Getting Started

Once you have installed smif, the quickest way to get started is to use the included sample project. You can make a new directory and copy the sample project files there by running:

$ mkdir sample_project
$ cd sample_project
$ smif setup
$ ls
config/ data/ models/ planning/ results/ smif.log

On the command line, from within the project directory, type the following command to list the available model runs:

$ smif list
20170918_energy_water_short
20170918_energy_water

To run a model run, type the following command:

$ smif run 20170918_energy_water
Model run complete

Note that the -d directory flag can be used to point to the project folder, so you can run smif commands explicitly:

$ smif list -d ~/projects/smif_sample_project/
...

Project Configuration

There are three layers of configuration in order to use the simulation modelling integration framework to conduct system-of-system modelling.

A project is the highest level container which holds all the elements required to run models, configure simulation models and define system-of-system models.

The basic folder structure looks like this:

/config
    project.yml
    /sector_models
        energy_demand.yml
        water_supply.yml
    /sos_models
        energy_water.yml
    /sos_model_runs
        20170918_energy_water.yml
        20170918_energy_water_short.yml
/data
    /initial_conditions
        energy_demand_existing.yml
        energy_supply_existing.yml
    /interval_definitions
        hourly.csv
        annual.csv
    /interventions
        energy_demand.yml
    /narratives
        energy_demand_high_tech.yml
        central_planning.yml
    /region_definitions
        lad.shp
    /scenarios
        population_high.csv
/models
    energy_demand.py
    water_supply.py
/results
    /20170918_energy_water_short
        /energy_demand
        /water_supply

The folder structure is divided into a config subfolder and a data subfolder.

The Configuration Folder

This folder holds configuration and metadata on simulation models, system-of-system models and model runs.

The Project File

This file holds all the project configuration.

project.yml
name: Test Project
scenario_sets:
- description: Growth in UK population
  facets:
  - name: population
    description: ''
  name: population
- description: Rainfall in the UK
  facets:
  - name: raininess
    description: ''
  name: rainfall
narrative_sets:
- description: Describes the evolution of technology
  name: technology
interval_definitions:
- description: ''
  filename: annual_intervals.csv
  name: annual
narratives:
- description: High penetration of SMART technology on the demand side
  filename: high_tech_dsm.yml
  name: High Tech Demand Side Management
  narrative_set: technology
region_definitions:
- description: ''
  filename: uk_nations_shp/regions.shp
  name: national
- description: ''
  filename: oxfordshire/regions.geojson
  name: oxfordshire
scenarios:
- description: ''
  facets:
  - filename: raininess.csv
    name: raininess
    spatial_resolution: national
    temporal_resolution: annual
    units: litre
  name: Central Rainfall
  scenario_set: rainfall
- description: ''
  facets:
  - filename: population_low.csv
    name: population
    spatial_resolution: national
    temporal_resolution: annual
    units: people
  name: Central Population (Low)
  scenario_set: population
- description: ''
  facets:
  - filename: population_med.csv
    name: population
    spatial_resolution: national
    temporal_resolution: annual
    units: people
  name: Central Population (Medium)
  scenario_set: population
- description: ''
  facets:
  - filename: population_high.csv
    name: population
    spatial_resolution: national
    temporal_resolution: annual
    units: people
  name: Central Population (High)
  scenario_set: population
units: units.txt

We’ll step through this configuration file section by section.

The first line gives the project name, a unique identifier for this project.

name: Test Project

One section lists the scenario sets. These give the categories into which scenarios are collected.

scenario_sets:
- description: Growth in UK population
  facets:
  - name: population
    description: ''
  name: population

Narrative sets collect together the categories into which narrative files are collected.

narrative_sets:
- description: Describes the evolution of technology
  name: technology

Region definitions list the collection of region files and the mapping to a unique name which can be used in scenarios and sector models. Region definitions define the spatial resolution of data.

region_definitions:
- description: ''
  filename: uk_nations_shp/regions.shp
  name: national

Interval definitions list the collection of interval files and the mapping to a unique name which can be used in scenarios and sector models. Interval definitions define the temporal resolution of data.

interval_definitions:
- description: ''
  filename: annual_intervals.csv
  name: annual

Unit definitions references a file containing custom units, not included in the Pint library default unit register (e.g. non-SI units).

units: units.txt

The scenarios section lists the scenarios and corresponding collections of data associated with scenarios.

scenarios:
- description: ''
  facets:
  - filename: raininess.csv
    name: raininess
    spatial_resolution: national
    temporal_resolution: annual
    units: litre
  name: Central Rainfall
  scenario_set: rainfall

The narratives section lists the narratives and mapping to one or more narrative files

narratives:
- description: High penetration of SMART technology on the demand side
  filename: high_tech_dsm.yml
  name: High Tech Demand Side Management
  narrative_set: technology

A Simulation Model File

A simulation model file contains all the configuration data necessary for smif to run the model, and link the model to data sources and sinks. This file also contains a list of parameters, the ‘knobs and dials’ the user wishes to expose to smif which can be adjusted in narratives. Intervention files and initial condition files contain the collections of data that are needed to expose the model to smif’s decision making functionality.

name: water_supply   # model name for internal/logging reference
description: 'Simulates the optimal operation of the UK water
supply system'
path: models/water_supply.py   # path to python file
classname: WaterSupplySectorModel # implements smif.SectorModel
inputs:
- name: raininess
  spatial_resolution: national
  temporal_resolution: annual
  units: Ml
- name: population
  spatial_resolution: national
  temporal_resolution: annual
  units: people
- name: water_demand
  spatial_resolution: national
  temporal_resolution: annual
  units: Ml
outputs:
- name: cost
  spatial_resolution: national
  temporal_resolution: annual
  units: million GBP
- name: energy_demand
  spatial_resolution: national
  temporal_resolution: annual
  units: kWh
- name: water
  spatial_resolution: national
  temporal_resolution: annual
  units: Ml
interventions:
  - water_supply.yml
initial_conditions:
  - water_supply_oxford.yml
  - reservoirs.yml
parameters:
- name: clever_water_meter_savings
  description: The savings from smart water meters
  absolute_range: (0, 100)
  suggested_range: (3, 10)
  default_value: 3
  units: '%'
- name: per_capita_water_demand
  description: The assumed per capita demand for water
  absolute_range: (0, 1.5)
  suggested_range: (1, 1.3)
  default_value: 1.1
  units: 'liter/person'

Inputs

Define the collection of inputs required from external sources to run the model. Inputs are defined with a name, spatial resolution, temporal-resolution and units.

inputs:
- name: raininess
  spatial_resolution: national
  temporal_resolution: annual
  units: Ml
- name: population
  spatial_resolution: national
  temporal_resolution: annual
  units: people
Input Attributes
Attribute Type Notes
name string A unique name within the input defintions
spatial_resolution string References an entry in the region definitions
temporal_resolution string References an entry in the interval definitions
units string References an entry in the unit definitions

Outputs

Define the collection of output model parameters used for the purpose of metrics, for accounting purposes, such as operational cost and emissions, or as the source of a dependency in another model.

outputs:
- name: cost
  spatial_resolution: national
  temporal_resolution: annual
  units: million GBP
- name: energy_demand
  spatial_resolution: national
  temporal_resolution: annual
  units: kWh
Output Attributes
Attribute Type Notes
name string A unique name within the output definitions
spatial_resolution string References an entry in the region definitions
temporal_resolution string References an entry in the interval definitions
units string References an entry in the unit definitions

Parameters

parameters:
- name: clever_water_meter_savings
  description: The savings from smart water meters
  absolute_range: (0, 100)
  suggested_range: (3, 10)
  default_value: 3
  units: '%'
Parameter Attributes
Attribute Type Notes
name string A unique name within the simulation model
description string Include sources of assumptions around default value
absolute_range tuple Raises an error if bounds exceeded
suggested_range tuple Provides a hint to a user as to sensible ranges
default_value float The default value for the parameter
units string  

A System-of-System Model File

A system-of-systems model collects together scenario sets and simulation models. Users define dependencies between scenario and simulation models.

name: energy_water
description: 'The future supply and demand of energy and water for the UK'
scenario_sets: # Select 0 or more of the scenario sets
- population
- rainfall
narrative_sets: []
sector_models: # Select 1 or more of the sector models
- water_supply
- energy_demand
dependencies:
- source_model: rainfall
  source_model_output: raininess
  sink_model: water_supply
  sink_model_input: raininess
- source_model: population
  source_model_output: population
  sink_model: water_supply
  sink_model_input: population
- source_model: water_supply
  source_model_output: energy_demand
  sink_model: energy_demand
  sink_model_input: energy_demand
- source_model: population
  source_model_output: population
  sink_model: energy_demand
  sink_model_input: population
- source_model: energy_demand
  source_model_output: water_demand
  sink_model: water_supply
  sink_model_input: water_demand
max_iterations: 100
convergence_absolute_tolerance: 1e-05
convergence_relative_tolerance: 1e-05

Scenario Sets

Scenario sets are the categories in which scenario data are organised. Choosing a scenario set at this points allows different scenario data to be chosen in model runs which share the same system-of-systems model configuration defintion.

scenario_sets: # Select 0 or more of the scenario sets
- population
- rainfall
Scenario Sets Attributes
Attribute Type Notes
names list A list of scenario set names

Simulation Models

This section contains a list of pre-configured simulation models which exist in the current project.

narrative_sets: []
sector_models: # Select 1 or more of the sector models
- water_supply
Simulation Models Attributes
Attribute Type Notes
names list A list of simulation model names

Dependencies

In this section, dependencies are defined between sources and sinks.

- energy_demand
dependencies:
- source_model: rainfall
  source_model_output: raininess
  sink_model: water_supply
  sink_model_input: raininess
- source_model: population
  source_model_output: population
  sink_model: water_supply
Dependency Attributes
Attribute Type Notes
source_model string The source model of the data
source_model_output string The output in the source model
sink_model string The model which depends on the source
sink_model_input string The input which should receive the data

A Model Run File

A model run brings together a system-of-systems model definition with timesteps over which planning takes place, and a choice of scenarios and narratives to population the placeholder scenario sets in the system-of-systems model.

name: 20170918_energy_water
description: Combined energy and water under central scenario
stamp: "2017-09-18T12:53:23+00:00"
timesteps:
- 2010
- 2015
- 2020
sos_model: energy_water
decision_module: ''
scenarios:
  population: Central Population (Medium)
  rainfall: Central Rainfall
narratives:
  technology:
  - High Tech Demand Side Management

Timesteps

A list of timesteps define the years in which planning takes place, and the simulation models are executed.

timesteps:
- 2010
- 2015
- 2020
Timestep Attributes
Attribute Type Notes
timesteps list A list of integer years

Scenarios

For each scenario set available in the contained system-of-systems model, one scenario should be chosen.

scenarios:
  population: Central Population (Medium)
  rainfall: Central Rainfall
Model Run Scenario Attributes
Attribute Type Notes
scenarios list A list of tuples of scenario sets and scenarios

Narratives

For each narrative set available in the project, zero or more available narratives should be chosen.

narratives:
  technology:
  - High Tech Demand Side Management

Note that narrative files override the values of parameters in specific simulation models. Selecting a narrative file which overrides parameters in an absent simulation model will have no effect.

Model Run Narrative Attributes
Attribute Type Notes
scenarios list A list of mappings between narrative sets and list of narrative files

Data Folder

This folder holds data like information to define the spatial and temporal resolution of data, as well as exogenous environmental data held in scenarios.

Initial Conditions

- name: Kielder Water
  location: England
  capacity:
    value: 500
    units: ML
  operational_lifetime:
    value: 300
    units: years
  economic_lifetime:
    value: 150
    units: years
  capital_cost:
    value: 15
    units: million £
  build_date: 1975
  current_level:
    value: 3
    units: Ml
    is_state: true

Interval definitions

The attribution of hours in a year to the temporal resolution used in the sectoral model.

Within-year time intervals are specified in yaml files, and as for regions, specified in the *.yml file in the project/data/intervals folder.

This links a unique name with the definitions of the intervals in a yaml file. The data in the file specify the mapping of model timesteps to durations within a year (assume modelling 365 days: no extra day in leap years, no leap seconds)

Use ISO 8601 [1] duration format to specify periods:

P[n]Y[n]M[n]DT[n]H[n]M[n]S

For example:

id,start,end
1,PT0H,PT8760H

In this example, the interval with id 1 begins in the first hour of the year and ends in the last hour of the year. This represents one, year-long interval.

Interval Attributes
Attribute Type Notes
id string The unique identifier used by the simulation model
start_hour string Period since beginning of year
end_hour string Period since beginning of year

Region definitions

Define the set of unique regions which are used within the model as polygons. The spatial resolution of the model may be implicit, and even a national model needs to have a national region defined. Inputs and outputs are assigned a model-specific geography from this list allowing automatic conversion from and to these geographies.

Model region files are stored in project/data/region_defintions.

The file format must be possible to parse with GDAL, and must contain an attribute “name” to use as an identifier for the region.

The sets of geographic regions are specified in the project configuration file using a region_definitions attributes as shown below:

  - name: raininess
  name: rainfall
narrative_sets:
- description: Describes the evolution of technology
  name: technology
interval_definitions:
- description: ''

This links a name, used elsewhere in the configuration with inputs, outputs and scenarios with a file containing the geographic data.

Interventions

Interventions are the atomic units which comprise the infrastructure systems in the simulation models. Interventions can represent physical assets such as pipes, and lines (edges in a network) or power stations and reservoirs (nodes in a network). Interventions can also represent intangibles which affects the operation of a system, such as a policy.

An exhaustive list of the interventions (often infrastructure assets) should be defined. These are represented internally in the system-of-systems model, collected into a gazateer and allow the framework to reason on infrastructure assets across all sectors. Interventions are instances of Intervention and are held in InterventionRegister. Interventions include investments in assets, supply side efficiency improvements, but not demand side management (these are incorporated in the strategies).

Define all possible interventions in an *.yml file in the project/data/interventions For example:

- name: small_pumping_station
  location: Oxford
  capacity:
    value: 50
    units: ML/day
  operational_lifetime:
    value: 150
    units: years
  economic_lifetime:
    value: 50
    units: years
  capital_cost:
    value: 5
    units: million £

Narratives

A narrative file contains references to 0 or more parameters defined in the simulation models, as well as special global parameters. Whereas model parameters are available only to individual simulation models, global parameters are available across all models. Use global paramaters for system-wide constants, such as emission coefficients, exchange rates, conversion factors etc.

Value specified in the narrative file override the default values specified in the simulation model configuration. If more than one narrative file is selected in the sos model configuration, then values in later files override values in earlier files.

energy_demand:
  smart_meter_savings: 8
water_supply:
  clever_water_meter_savings: 8
  per_capita_water_demand: 1.2

Scenarios

The scenarios: section of the project configuration file allows you to define static sources for simulation model dependencies.

In the case of the example project file shown earlier, the scenarios section lists the scenarios and corresponding collections of data associated with the scenario sets:

  narrative_set: technology
- description: ''
  filename: uk_nations_shp/regions.shp
  name: national
- description: ''
  filename: oxfordshire/regions.geojson
  name: oxfordshire
scenarios:
- description: ''
  facets:
  - filename: raininess.csv
    name: raininess
    spatial_resolution: national
    temporal_resolution: annual
    units: litre
  name: Central Rainfall
  scenario_set: rainfall
- description: ''
  facets:

The data files are stored in the project/data/scenarios folder.

The metadata required to define a particular scenario are shown in the table below. It is possible to associate a number of different data sets with the same scenario, so that, for example, choosing the High Population scenario allows users to access both population count and density data in the same or different spatial and temporal resolutions.

Scenario Attributes
Attribute Type Notes
name string  
description string  
scenario_set string  
parameters list  

Scenario Parameters

The filename in the parameters section within the scenario definition points to a comma-seperated-values file stored in the project/data/scenarios folder. For example:

year,region,interval,value
2010,England,1,52000000
2010,Scotland,1,5100000
2010,Wales,1,2900000
2015,England,1,53000000
2015,Scotland,1,5300000
2015,Wales,1,3000000
2020,England,1,54000000
2020,Scotland,1,5500000
2020,Wales,1,3200000

For each entry in the scenario parameters list, the following metadata is required:

Scenario Parameter Attributes
Attribute Type Notes
name string  
spatial_resolution string  
temporal_resolution string  
units string  
filename string  

Wrapping a Sector Model: Overview

In addition to collecting the configuration data listed above, to integrate a new sector model into the system-of-systems model it is necessary to write a Python wrapper function. The template class smif.model.sector_model.SectorModel enables a user to write a script which runs the wrapped model, passes in parameters and writes out results.

The wrapper acts as an interface between the simulation modelling integration framework and the simulation model, keeping all the code necessary to implement the conversion of data types in one place.

In particular, the wrapper must take the smif formatted data, which includes inputs, parameters, state and pass this data into the wrapped model. After the simulate() has run, results from the sector model must be formatted and passed back into smif.

The handling of data is aided through the use of a set of methods provided by smif.data_layer.data_handle.DataHandle, namely:

and

In this section, we describe the process necessary to correctly write this wrapper function, referring to the example project included with the package.

It is difficult to provide exhaustive details for every type of sector model implementation - our decision to leave this largely up to the user is enabled by the flexibility afforded by python. The wrapper can write to a database or structured text file before running a model from a command line prompt, or import a python sector model and pass in parameters values directly. As such, what follows is a recipe of components from which you can construct a wrapper to full integrate your simulation model within smif.

For help or feature requests, please raise issues at the github repository [2] and we will endeavour to provide assistance as resources allow.

Example Wrapper

Here’s a reproduction of the example wrapper in the sample project included within smif. In this case, the wrapper doesn’t actually call or run a separate model, but demonstrates calls to the data handler methods necessary to pass data into an external model, and send results back to smif.

    """Energy model
    """
    def initialise(self, initial_conditions):
        pass

    def simulate(self, data):

        # Get the current timestep

        now = data.current_timestep
        self.logger.info("EDMWrapper received inputs in %s",
                         now)

        # Demonstrates how to get the value for a model parameter
        parameter_value = data.get_parameter('smart_meter_savings')
        self.logger.info('Smart meter savings: %s', parameter_value)

        # Demonstrates how to get the value for a model input
        # (defaults to the current time period)
        current_energy_demand = data.get_data('energy_demand')
        self.logger.info("Current energy demand in %s is %s",
                         now, current_energy_demand)

        # Demonstrates how to get the value for a model input from the base
        # timeperiod
        base_energy_demand = data.get_base_timestep_data('energy_demand')
        base_year = data.base_timestep
        self.logger.info("Base year energy demand in %s was %s", base_year,
                         base_energy_demand)

        # Demonstrates how to get the value for a model input from the previous
        # timeperiod
        if now > base_year:
            prev_energy_demand = data.get_previous_timestep_data('energy_demand')
            prev_year = data.previous_timestep
            self.logger.info("Previous energy demand in %s was %s",
                             prev_year, prev_energy_demand)

        # Pretend to call the 'energy model'
        # This code prints out debug logging messages for each input
        # defined in the energy_demand configuration
        for name in self.inputs.names:
            time_intervals = self.inputs[name].get_interval_names()
            regions = self.inputs[name].get_region_names()
            for i, region in enumerate(regions):
                for j, interval in enumerate(time_intervals):
                    self.logger.info(
                        "%s %s %s",
                        interval,
                        region,
                        data.get_data(name)[i, j])

        # Write pretend results to data handler
        data.set_results("cost", np.ones((3, 1)) * 3)
        data.set_results("water_demand", np.ones((3, 1)) * 3)

        self.logger.info("EDMWrapper produced outputs in %s",
                         now)

    def extract_obj(self, results):
        return 0

The key methods in the SectorModel class which need to be overridden are:

The wrapper should be written in a python file, e.g. water_supply.py. The path to the location of this file should be entered in the sector model configuration of the project. (see A Simulation Model File above).

Wrapping a Sector Model: Simulate

The most common workflow that will need to be implemented in the simulate method is:

  1. Retrieve model input and parameter data from the data handler
  2. Write or pass this data to the wrapped model
  3. Run the model
  4. Retrieve results from the model
  5. Write results back to the data handler

Accessing model parameter data

Use the get_parameter() or get_parameters() method as shown in the example:

parameter_value = data.get_parameter('smart_meter_savings')

Note that the name argument passed to the get_parameter() is that which is defined in the sector model configuration file.

Accessing model input data for the current year

The method get_data() allows a user to get the value for any model input that has been defined in the sector model’s configuration. In the example, the option year argument is omitted, and it defaults to fetching the data for the current timestep.

current_energy_demand = data.get_data('energy_demand')

Accessing model input data for the base year

To access model input data from the timestep prior to the current timestep, you can use the following argument:

base_energy_demand = data.get_base_timestep_data('energy_demand')

Accessing model input data for a previous year

To access model input data from the timestep prior to the current timestep, you can use the following argument:

    prev_energy_demand = data.get_previous_timestep_data('energy_demand')

Passing model data directly to a Python model

If the wrapped model is a python script or package, then the wrapper can import and instantiate the model, passing in data directly.

instance = ExampleWaterSupplySimulationModel()
instance.raininess = raininess
instance.number_of_treatment_plants = number_of_treatment_plants
instance.reservoir_level = reservoir_level

water, cost = instance.run()
data.set_results('water', np.ones((3, 1)) * water / 3)
data.set_results("cost", np.ones((3, 1)) * cost / 3)

In this example, the example water supply simulation model is instantiated within the simulate method, data is written to properties of the instantiated class and the run() method of the simulation model is called. Finally, (dummy) results are written back to the data handler using the set_results() method.

Alternatively, the wrapper could call the model via the command line (see below).

Passing model data in as a command line argument

If the model is fairly simple, or requires a parameter value or input data to be passed as an argument on the command line, use the methods provided by subprocess to call out to the model from the wrapper:

parameter = data.get_parameter('command_line_argument')
arguments = ['path/to/model/executable',
             '-my_argument={}'.format(parameter)]
output = subprocess.run(arguments, check=True)

Writing data to a text file

Again, the exact implementation of writing data to a text file for subsequent reading into the wrapped model will differ on a case-by-case basis. In the following example, we write some data to a comma-separated-values (.csv) file:

with open(path_to_data_file, 'w') as open_file:
    fieldnames = ['year', 'PETROL', 'DIESEL', 'LPG',
                  'ELECTRICITY', 'HYDROGEN', 'HYBRID']
    writer = csv.DictWriter(open_file, fieldnames)
    writer.writeheader()

    now = data.current_timestep
    base_year_enum = RelativeTimestep.BASE

    base_price_set = {
        'year': base_year_enum.resolve_relative_to(now, data.timesteps),
        'PETROL': data.get_data('petrol_price', base_year_enum),
        'DIESEL': data.get_data('diesel_price', base_year_enum),
        'LPG': data.get_data('lpg_price', base_year_enum),
        'ELECTRICITY': data.get_data('electricity_price', base_year_enum),
        'HYDROGEN': data.get_data('hydrogen_price', base_year_enum),
        'HYBRID': data.get_data('hybrid_price', base_year_enum)
    }

    current_price_set = {
        'year': now,
        'PETROL': data.get_data('petrol_price'),
        'DIESEL': data.get_data('diesel_price'),
        'LPG': data.get_data('lpg_price'),
        'ELECTRICITY': data.get_data('electricity_price'),
        'HYDROGEN': data.get_data('hydrogen_price'),
        'HYBRID': data.get_data('hybrid_price')
    }

    writer.writerow(base_price_set)
    writer.writerow(current_price_set)

Writing data to a database

The exact implementation of writing input and parameter data will differ on a case-by-case basis. In the following example, we write model inputs energy_demand to a postgreSQL database table ElecLoad using the psycopg2 library [3]

def simulate(self, data):

    # Open a connection to the database
    conn = psycopg2.connect("dbname=vagrant user=vagrant")
    # Open a cursor to perform database operations
    cur = conn.cursor()

    # Returns a numpy array whose dimensions are defined by the interval and
    # region definitions
    elec_data = data.get_data('electricity_demand')

    # Build the SQL string
    sql = """INSERT INTO "ElecLoad" (Year, Interval, BusID, ElecLoad)
             VALUES (%s, %s, %s, %s)"""

    # Get the time interval definitions associated with the input
    time_intervals = self.inputs[name].get_interval_names()
    # Get the region definitions associated with the input
    regions = self.inputs[name].get_region_names()
    # Iterate over the regions and intervals (columns and rows) of the numpy
    # array holding the energy demand data and write each value into the table
    for i, region in enumerate(regions):
        for j, interval in enumerate(time_intervals):
            # This line calls out to a helper method which associates
            # electricity grid bus bars to energy demand regions
            bus_number = get_bus_number(region)
            # Build the tuple to write to the table
            insert_data = (data.current_timestep,
                           interval,
                           bus_number,
                           data[i, j])
            cur.execute(sql, insert_data)

    # Make the changes to the database persistent
    conn.commit()

    # Close communication with the database
    cur.close()
    conn.close()

Writing model results to the data handler

Writing results back to the data handler is as simple as calling the set_results() method:

data.set_results("cost", np.array([[1.23, 1.543, 2.355]])

The expected format of the data is a 2-dimensional numpy array with the dimensions described by the tuple (len(regions), len(intervals)) as defined in the model’s output configuration. Results are expected to be set for each of the model outputs defined in the output configuration and a warning is raised if these are not present at runtime.

The interval definitions associated with the output can be interrogated from within the SectorModel class using self.outputs[name].get_interval_names() and the regions using self.outputs[name].get_region_names() and these can then be used to compose the numpy array.