Tutorials¶
Before going through these tutorials, it is recommended to have a brief look at the components section to become familiar with the terminology and modeling approach used.
The tutorials are based on the built-in example models, they explain the key steps necessary to set up and run simple models. Refer to the other parts of the documentation for more detailed information on configuring and running more complex models.
The built-in examples are simple on purpose, to show the key components of a Calliope model.
The first part of the tutorial builds a model for part of a national grid, exhibiting the following Calliope functionality:
- Use of supply, supply_plus, demand, storage and transmission technologies
- Nested locations
- Multiple cost types
The second part of the tutorial builds a model for part of a district network, exhibiting the following Calliope functionality:
- Use of supply, demand, conversion, conversion_plus, and transmission technologies
- Use of multiple energy carriers
- Revenue generation, by carrier export
Tutorial 1: national scale¶
This example consists of two possible power supply technologies, a power demand at two locations, the possibility for battery storage at one of the locations, and a transmission technology linking the two. The diagram below gives an overview:
Supply-side technologies¶
The example model defines two power supply technologies.
The first is ccgt
(combined-cycle gas turbine), which serves as an example of a simple technology with an infinite resource. Its only constraints are the cost of built capacity (e_cap
) and a constraint on its maximum built capacity.
The definition of this technology in the example model’s configuration looks as follows:
ccgt:
name: 'Combined cycle gas turbine'
color: '#FDC97D'
stack_weight: 200
parent: supply
carrier_out: power
constraints:
r: inf
e_eff: 0.5
e_cap.max: 40000 # kW
costs:
monetary:
e_cap: 750 # USD per kW
om_fuel: 0.02 # USD per kWh
There are a few things to note. First, ccgt
defines a name, a color (given as an HTML color code), and a stack_weight. These are used by the built-in analysis tools when analyzing model results. Second, it specifies its parent, supply
, and its carrier_out, power
, thus setting itself up as a power supply technology. This is followed by the definition of constraints and costs (the only cost class used is monetary, but this is where other “costs”, such as emissions, could be defined).
Note
There are technically no restrictions on the units used in model definitions. Usually, the units will be kW and kWh, alongside a currency like USD for costs. It is the responsibility of the modeler to ensure that units are correct and consistent. Some of the analysis functionality in the analysis
module assumes that kW and kWh are used when drawing figure and axis labels, but apart from that, there is nothing preventing the use of other units.
The second technology is csp
(concentrating solar power), and serves as an example of a complex supply_plus technology making use of:
- a finite resource based on time series data
- built-in storage
- plant-internal losses (
p_eff
)
This definition in the example model’s configuration is more verbose:
csp:
name: 'Concentrating solar power'
color: '#99CB48'
stack_weight: 100
parent: supply_plus
carrier_out: power
constraints:
use_s_time: true
s_time.max: 24
s_loss: 0.002
r: file # Will look for `csp_r.csv` in data directory
e_eff: 0.4
p_eff: 0.9
r_area.max: inf
e_cap.max: 10000
costs:
monetary:
s_cap: 50
r_area: 200
r_cap: 200
e_cap: 1000
om_var: 0.002
depreciation:
monetary:
interest: 0.12
Again, csp
has the definitions for name, color, stack_weight, parent, and carrier_out. Its constraints are more numerous: it defines a maximum storage time (s_time.max
), an hourly storage loss rate (s_loss
), then specifies that its resource should be read from a file (more on that below). It also defines a carrier conversion efficiency of 0.4 and a parasitic efficiency of 0.9 (i.e., an internal loss of 0.1). Finally, the resource collector area and the installed carrier conversion capacity are constrained to a maximum.
The costs are more numerous as well, and include monetary costs for all relevant components along the conversion from resource to carrier (power): storage capacity, resource collector area, resource conversion capacity, energy conversion capacity, and variable operational and maintenance costs. Finally, it also overrides the default value for the monetary interest rate.
Storage technologies¶
The second location allows a limited amount of battery storage to be deployed to better balance the system. This technology is defined as follows:
battery:
name: 'Battery storage'
color: '#DC5CE5'
parent: storage
carrier: power
constraints:
e_cap.max: 1000 # kW
s_cap.max: inf
c_rate: 4
e_eff: 0.95 # 0.95 * 0.95 = 0.9025 round trip efficiency
s_loss: 0 # No loss over time assumed
costs:
monetary:
s_cap: 200 # USD per kWh storage capacity
The contraints give a maximum installed generation capacity for battery storage together with a charge rate (C-rate) of 4, which in turn limits the storage capacity. In the case of a storage technology, e_eff
applies twice: on charging and discharging. In addition, storage technologies can lose stored energy over time – in this case, we set this loss to zero.
Other technologies¶
Three more technologies are needed for a simple model. First, a definition of power demand and unmet power demand:
demand_power:
name: 'Power demand'
parent: demand
carrier: power
unmet_demand_power:
name: 'Unmet power demand'
parent: unmet_demand
carrier: power
Power demand is a technology like any other. We will associate an actual demand time series with the demand technology later. The parent of unmet_demand_power
, unmet_demand
, is a special kind of supply technology with an unlimited resource but very high cost. It allows a model to remain mathematically feasible even if insufficient supply is available to meet demand, and model results can easily be examined to verify whether there was any unmet demand. There is no requirement to include such a technology in a model, but it is useful to do so, since in its absence, an infeasible model would cause the solver to end with an error, returning no results for Calliope to analyze.
What remains to set up is a simple transmission technology:
ac_transmission:
name: 'AC power transmission'
parent: transmission
carrier: power
constraints:
e_eff: 0.85
costs:
monetary:
e_cap: 200
om_var: 0.002
ac_transmission
has an efficiency of 0.85, so a loss during transmission of 0.15, as well as some cost definitions.
Transmission technologies (like conversion technologies) look different than other nodes, as they link the carrier at one location to the carrier at another (or, in the case of conversion, one carrier to another at the same location). The following figure illustrates this for the example model’s transmission technology:
Locations¶
In order to translate the model requirements shown in this section’s introduction into a model definition, five locations are used: r1
, r2
, csp1
, csp2
, and csp3
.
The technologies are set up in these locations as follows:
Let’s now look at the first location definition:
locations:
region1:
techs: ['demand_power', 'unmet_demand_power', 'ccgt']
override:
demand_power:
x_map: 'region1: demand'
constraints:
r: file=demand-1.csv
r_scale_to_peak: -40000
ccgt:
constraints:
e_cap.max: 30000 # increased to ensure no unmet_demand in first timestep
There are several things to note here:
- The location specifies a list of technologies that it allows (
techs
). Note that technologies listed here must have been defined elsewhere in the model configuration. - It also overrides some options for both
demand_power
andccgt
. For the latter, it simply sets a location-specific maximum capacity constraint. Fordemand_power
, the options set here are related to reading the demand time series from a CSV file. CSV is a simple text-based format that stores tables by comma-separated rows. Note that we did not define anyr
option in the definition of thedemand_power
technology. Instead, this is done directly via a location-specific override. For this location, the filedemand-1.csv
is loaded, and the demand is then scaled such that the demand peak is at the given value. Note that in Calliope, a supply is positive and a demand is negative, so the peak demand is actually a negative value. Finally, thex_map
option allows us to read a CSV file with a single column named “demand” and tell Calliope to load data from that column for regionr1
. This is necessary unless the column name(s) in the CSV file already correspond to the location names defined in the model configuration.
The remaining location definitions look like this:
region2:
techs: ['demand_power', 'unmet_demand_power', 'battery']
override:
demand_power:
x_map: 'region2: demand'
constraints:
r: file=demand-2.csv
r_scale_to_peak: -5000
region1-1,region1-2,region1-3:
within: region1
techs: ['csp']
r2
is very similar to r1
, except that it does not allow the ccgt
technology. The three csp
locations are defined together, i.e. they each get the exact same configuration. They are within
the location r1
and allow only the csp
technology, this allows us to model three possible sites for CSP plants within r1
.
Locations that do not specify a within
are implicitly at the topmost level. Transmission between locations at the topmost level can only take place if transmission links are defined between them. On the other hand, locations which are specified as within
another location can automatically and without any losses transmit energy to and from their parent location. In other words, a topmost location and all its contained locations together are implicitly assumed to be on a “copperplate” together. That means there are no transmission constraints and no transmission losses between these locations. Balancing of supply and demand takes place only at the topmost level.
For transmission technologies, the model also needs to know which top-level locations can be linked, and this is set up in the model configuration as follows:
links:
region1,region2:
ac_transmission:
constraints:
e_cap.max: 10000
Tutorial 2: urban scale¶
This example consists of two possible sources of electricity, one possible source of heat, and one possible source of simultaneous heat and electricity. There are three locations, each describing a building, with transmission links between them. The diagram below gives an overview:
Supply technologies¶
This example model defines three supply technologies.
The first two are national_gas
and national_grid
, referring to the supply of gas
(natural gas) and power
(electricity), respectively, from the national distribution system. These ‘inifinitely’ available national commodities can become energy carriers in the system, with the cost of their purchase being considered at supply, not conversion.
The definition of these technologies in the example model’s configuration looks as follows:
##-GRID SUPPLY-##
supply_grid_power:
name: 'National grid import'
parent: supply
carrier: power
constraints:
r: inf
e_cap.max: 2000
costs:
monetary:
e_cap: 15
om_fuel: 0.1 # 10p/kWh electricity price #ppt
supply_gas:
name: 'Natural gas import'
parent: supply
carrier: gas
constraints:
r: inf
e_cap.max: 2000
costs:
monetary:
e_cap: 1
om_fuel: 0.025 # 2.5p/kWh gas price #ppt
The final supply technology is pv
(solar photovoltaic power), which serves as a inflexible supply technology. It is simple to define, other than having a time-dependant resource availablity, loaded from file. Additionally, it is constrained by available area, which is the rooftop area of the locations in this example.
The definition of this technology in the example model’s configuration looks as follows:
##-Renewables-##
pv:
name: 'Solar photovoltaic power'
color: '#99CB48'
stack_weight: 100
parent: supply
export: true
carrier_out: power
constraints:
r: file # Will look for `pv_r.csv` in data directory - already accounted for panel efficiency
e_eff: 0.85
e_cap.max: 250
r_area.max: 1500
costs:
monetary:
e_cap: 1350
Conversion technologies¶
The example model defines two conversion technologies.
The first is boiler
(natural gas boiler), which serves as an example of a simple conversion technology with one input carrier and one output carrier. Its only constraints are the cost of built capacity (e_cap
) and a constraint on its maximum built capacity.
The definition of this technology in the example model’s configuration looks as follows:
# Conversion
boiler:
name: 'Natural gas boiler'
stack_weight: 100
parent: conversion
carrier_out: heat
carrier_in: gas
constraints:
e_cap.max: 600
e_eff: 0.85
There are a few things to note. First, boiler
defines a name, a color (given as an HTML color code), and a stack_weight. These are used by the built-in analysis tools when analyzing model results. Second, it specifies its parent, conversion
, its carrier_in gas
, and its carrier_out heat
, thus setting itself up as a gasto heat conversion technology. This is followed by the definition of constraints and costs (the only cost class used is monetary, but this is where other “costs”, such as emissions, could be defined).
The second technology is chp
(combined heat and power), and serves as an example of a possible conversion_plus technology making use of two output carriers.
This definition in the example model’s configuration is more verbose:
# Conversion_plus
chp:
name: 'Combined heat and power'
stack_weight: 100
parent: conversion_plus
export: true
primary_carrier: power
carrier_in: gas
carrier_out: power
carrier_out_2:
heat: 0.8
constraints:
e_cap.max: 1300
e_eff: 0.405
costs:
monetary:
e_cap: 750
om_var: 0.004 # .4p/kWh for 4500 operating hours/year
Again, chp
has the definitions for name, color, stack_weight, parent, and carrier_in. Its constraints are no more numerous: it still only defines a carrier conversion efficiency and maximum carrier conversion capacity.
Demand technologies¶
Electricity and heat demand, and their unmet_demand counterparts are defined here:
##-DEMAND-##
demand_power:
name: 'Electrical demand'
parent: demand
carrier: power
unmet_demand_power:
name: 'Unmet electrical demand'
parent: unmet_demand
carrier: power
demand_heat:
name: 'Heat demand'
parent: demand
carrier: heat
unmet_demand_heat:
name: 'Unmet heat demand'
parent: unmet_demand
Electricity and heat demand are a technologies like any other. We will associate an actual demand time series with each demand technology later. The parent of unmet_demand_power
and unmet_demand_heat
, unmet_demand
, is a special kind of supply technology with an unlimited resource but very high cost. It allows a model to remain mathematically feasible even if insufficient supply is available to meet demand, and model results can easily be examined to verify whether there was any unmet demand. There is no requirement to include such a technology in a model, but it is useful to do so, since in its absence, an infeasible model would cause the solver to end with an error, returning no results for Calliope to analyze.
Transmission technologies¶
In this district, electricity and heat can be transmitted between two locations. Gas is made available in each location without consideration of transmission.
##-DISTRIBUTION-##
power_lines:
name: 'Electrical power distribution'
parent: transmission
carrier: power
constraints:
e_cap.max: 2000
e_eff: 0.98
costs_per_distance:
monetary:
e_cap: 0.01
heat_pipes:
name: 'District heat distribution'
parent: transmission
carrier: heat
constraints:
e_cap.max: 2000
constraints_per_distance:
e_loss: 0.025
costs_per_distance:
monetary:
power_lines
has an efficiency of 0.95, so a loss during transmission of 0.05. heat_pipes
has a loss rate per unit distance of 2.5%/km. Over the distance between the two locations of 0.5km, this translates to 1.25% loss rate.
Locations¶
In order to translate the model requirements shown in this section’s introduction into a model definition, four locations are used: X1
, X2
, X3
, and N1
.
The technologies are set up in these locations as follows:
Let’s now look at the first location definition:
locations:
X1:
techs: ['chp', 'pv',
'supply_grid_power', 'supply_gas',
'demand_power', 'demand_heat',
'unmet_demand_power', 'unmet_demand_heat']
available_area: 500
override:
demand_power.constraints.r: file=demand_power.csv
demand_heat.constraints.r: file=demand_heat.csv
supply_grid_power.costs.monetary.e_cap: 100 # cost of transformers
There are several things to note here:
- The location specifies a list of technologies that it allows (
techs
). Note that technologies listed here must have been defined elsewhere in the model configuration. - It also overrides some options for
demand_power
andccgt
. For the latter, it simply sets a location-specific maximum capacity constraint. Fordemand_power
, the options set here are related to reading the demand time series from a CSV file. CSV is a simple text-based format that stores tables by comma-separated rows. Note that we did not define anyr
option in the definition of thedemand_power
technology. Instead, this is done directly via a location-specific override. For this location, the filedemand-1.csv
is loaded, and the demand is then scaled such that the demand peak is at the given value. Note that in Calliope, a supply is positive and a demand is negative, so the peak demand is actually a negative value. Finally, thex_map
option allows us to read a CSV file with a single column named “demand” and tell Calliope to load data from that column for regionr1
. This is necessary unless the column name(s) in the CSV file already correspond to the location names defined in the model configuration.
The remaining location definitions look like this:
X2:
techs: ['boiler', 'pv',
'supply_gas',
'demand_power', 'demand_heat',
'unmet_demand_power', 'unmet_demand_heat'
]
available_area: 1300
override:
demand_power.constraints.r: file=demand_power.csv
demand_heat.constraints.r: file=demand_heat.csv
boiler.costs.monetary.e_cap: 43.1 # different boiler costs
pv.costs.monetary:
om_var: -0.0203 # revenue for just producing electricity
export: -0.0491 # FIT return for PV export
X3:
techs: ['boiler', 'pv',
'supply_gas',
'demand_power', 'demand_heat',
'unmet_demand_power', 'unmet_demand_heat'
]
available_area: 900
override:
demand_power.constraints.r: file=demand_power.csv
demand_heat.constraints.r: file=demand_heat.csv
boiler.costs.monetary.e_cap: 78 # different boiler costs
pv:
constraints:
e_cap.max: 50 # changing tariff structure below 50kW
costs.monetary:
om_fixed: -80.5 # reimbursement per kWp from FIT
N1: # location for branching heat transmission network
techs: ['heat_pipes']
X2
and X3
are very similar to X1
, except that they do not connect to the national grid, nor do they contain the chp
technology.
N1
differs to the others by virtue of containing no technologies. It acts as a branching station for the heat network, allowing connections to one or both of X2
and X3
without double counting the pipeline from X1
to N1
. Its definition look like this:
N1: # location for branching heat transmission network
techs: ['heat_pipes']
For transmission technologies, the model also needs to know which top-level locations can be linked, and this is set up in the model configuration as follows:
links:
X1,X2:
power_lines:
distance: 0.5
X1,X3:
power_lines:
distance: 0.6
X1,N1:
heat_pipes:
distance: 0.25
N1,X2:
heat_pipes:
distance: 0.25
N1,X3:
heat_pipes:
distance: 0.35
Revenue by export¶
Defined for both PV and CHP, there is the option to accrue revenue in the system by exporting electricity. This export is considered as a removal of the energy carrier power
from the system, in exchange for negative cost (i.e. revenue). To allow this, export: true
has been given under both technology definitions and an export
value given under costs.
The revenue from PV export varies depending on location, emulating the different feed-in tariff structures in the UK for commercial and domestic properties. In domestic properties, the revenue is generated by simply having the installation (per kW installed capacity), as export is not metered. Export is metered in commercial properties, thus revenue is generated directly from export (per kWh exported). The revenue generated by CHP depends on the electricity grid wholesale price per kWh, being 80% of that. These revenue possibilities are reflected in the technologies’ and locations’ definitions.
Files that define the model¶
For all Calliope models, including the examples discussed above, the model definitions in through YAML files, which are simple human-readable text files (YAML is a human readable data serialization format). They are stored with a .yaml
(or .yml
) extension. See YAML configuration file format for details.
Typically, we want to collect all files belonging to a model inside a model directory. In the national-scale example describe above, the layout of that directory, which also includes the time series data in CSV format, is as follows (+
denotes directories, -
files):
+ example_model
+ model_config
+ data
- csp_r.csv
- demand-1.csv
- demand-2.csv
- set_t.csv
- locations.yaml
- model.yaml
- techs.yaml
- run.yaml
The urban-scale example follows a similar layout. A complete listing of the files in all example models is available in Built-in example models.
Inside the data
directory, time series are stored as CSV files (their location is configured inside model.yaml
). At a minimum, a model must always have a set_t.csv
file which defines the model’s timesteps. For more details on this and on time series data more generally, refer to Using time series data.
The three files locations.yaml
, model.yaml
, and techs.yaml
together are the model definition, and have been described above. There is one more YAML file, however: run.yaml
. This tells Calliope how to run the model given by the model definition, and will be described next. To run a model in Calliope, these two basic components – a model definition and a run configuration – are always required.
The run configuration¶
At its most basic, the run configuration simply specifies which model to run, which mode to run it in, and what solver to use. These three options are the required minimum. In the case of the example models, we also specify some output options. The output options only apply when the calliope run
command-line tool is used to run the model (see below). In the national-scale example:
name: "Test run" # Run name -- distinct from model name!
model: 'model_config/model.yaml'
output: # Only used if run via the 'calliope run' command-line tool
format: csv # Choices: netcdf, csv
path: 'Output' # Will be created if it doesn't exist
mode: plan # Choices: plan, operate
solver: glpk
To speed up model runs, the national-scale example model’s run configuration also specifies a time subset:
subset_t: ['2005-01-01', '2005-01-05'] # Subset of timesteps
The included time series is hourly for a full year. The subset_t
setting runs the model over only a subset of five days.
The full run.yaml
file includes additional options, none of which are relevant for this tutorial. See the full file listing for the national-scale example and the section on the run configuration for more details on the available options.
Plan vs. operate¶
A Calliope model can either be run in planning mode (mode: plan
) or operational mode (mode: operate
). In planning mode, an optimization problem is solved to design an energy system that satisfies the given constraints.
In operational mode, all max
constraints (such as e_cap.max
) are treated as fixed rather than as upper bounds. The resulting, fully defined energy system is then operated with a receding horizon control approach. The results are returned in exactly the same format as for planning mode results.
To specify a runnable operational model, capacities for all technologies at all locations would have to be defined. This can be done by specifying e_cap.equals
. In the absence of e_cap.equals
, e_cap.max
is assumed to be fixed.
In this tutorial section, we are only demonstrating the planning mode.
Running a model and analyzing results¶
Running interactively¶
The most straightforward way to run a Calliope model is to do so in an interactive Python session.
An example which also demonstrates some of the analysis possibilities after running a model is given in the following Jupyter notebook, based on the national-scale example model. Note that you can download and run this notebook on your own machine (if both Calliope and the Jupyter Notebook are installed):
Running with the command-line tool¶
Another way to run a Calliope model is to use the command-line tool calliope run
. First, we create a new copy of the built-in national-scale example model, by using calliope new
:
$ calliope new testmodel
Note
By default, calliope new
uses the national-scale example model as a template. To use a different template, you can specify the example model to use, e.g.: --template=UrbanScale
.
This creates a new directory, testmodel
, in the current working directory. We can now run this model:
$ calliope run testmodel/run.yaml
Because of the output options set in run.yaml
, model results will be stored as a set of CSV files in the directory Output
. Saving CSV files is an easy way to get results in a format suitable for further processing with other tools. In order to make use of Calliope’s analysis functionality, results should be saved as a single NetCDF file instead, which comes with improved performance and handling.
See Running the model for more on how to run a model and then retrieve results from it. See Analyzing results for more details on analyzing results, including the built-in functionality to read results from either CSV or NetCDF files, making them available for further analysis as described above (Running interactively).
Previous: Components to build a model | Next: Model formulation