National Scale Example Model¶
This example consists of two possible power supply technologies, a power demand at two nodes, the possibility for battery storage at one of the nodes, and a transmission technology linking the two.
The diagram below gives an overview:
We distinguish between model configuration (the options provided to Calliope to do its work) and the model definition (your representation of a physical system in YAML).
Model configuration¶
The model configuration file model.yaml
is the place to tell Calliope about how to interpret the model definition and how to build and solve your model.
It does not contain much data, but the scaffolding with which to construct and run your model.
config:
init:
name: National-scale example model
# What version of Calliope this model is intended for
calliope_version: 0.7.0
# Time series data path - can either be a path relative to this file, or an absolute path
time_data_path: "timeseries_data"
time_subset: ["2005-01-01", "2005-01-05"] # Subset of timesteps
build:
ensure_feasibility: true # Switches on the "unmet demand" constraint
mode: plan # Choices: plan, operate
solve:
solver: cbc
zero_threshold: 1e-10 # Any value coming out of the backend that is smaller than this (due to floating point errors, probably) will be set to zero
Model definition¶
Indexed parameters¶
Before we dive into the technologies and nodes in the model, we have defined some parameters that are independent of both of these:
parameters:
objective_cost_weights:
data: 1
index: [monetary]
dims: costs
# `bigM` sets the scale of unmet demand, which cannot be too high, otherwise the optimisation will not converge
bigM: 1e6
Neither of these parameters is strictly necessary to define.
They have defaults assigned to them (see the model definition schema in the reference
section of the documentation).
However, we have included them in here as examples.
objective_cost_weights
can be used to weight different cost classes in the objective function
(e.g., if we had co2_emissions
as well as monetary
costs).
bigM
(see "Big M method" on Wikipedia) is used to formulate certain types of constraints and should be a large number,
but not so large that it causes numerical trouble.
bigM
is dimensionless, while objective_cost_weights
is indexed over the costs
dimension.
You will see this same parameter
definition structure elsewhere in the model definition as we index certain parameters over other dimensions.
Supply 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 source.
Its only constraints are the cost of built capacity (flow_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: "#E37A72"
base_tech: supply
inherit: cost_dim_setter
carrier_out: power
flow_out_eff: 0.5
flow_cap_max: 40000 # kW
flow_cap_max_systemwide: 100000 # kW
flow_ramping: 0.8
lifetime: 25
cost_flow_cap.data: 750 # USD per kW
cost_flow_in.data: 0.02 # USD per kWh
There are a few things to note.
First, ccgt
defines essential information:
a name, a color (given as an HTML color code, for later visualisation), its base_tech, supply
, and its carrier_out, power
.
It has set itself up as a power supply technology.
This is followed by the definition of parameters to use to constrain the technology's contribution to the system and costs.
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 or EUR for costs. There is nothing preventing the use of other units, but yt is the responsibility of the modeler to ensure that units are correct and consistent.
The second technology is csp
(concentrating solar power), and serves as an example of a complex supply technology making use of:
- a finite source based on time series data
- inbuilt storage
- plant-internal losses (
parasitic_eff
)
This definition in the example model's configuration is more verbose:
csp:
name: "Concentrating solar power"
color: "#F9CF22"
base_tech: supply
inherit: cost_dim_setter
carrier_out: power
source_unit: per_area
include_storage: True
storage_cap_max: 614033
flow_cap_per_storage_cap_max: 1
storage_loss: 0.002
flow_out_eff: 0.4
flow_out_parasitic_eff: 0.9
area_use_max: .inf
flow_cap_max: 10000
lifetime: 25
cost_storage_cap.data: 50
cost_area_use.data: 200
cost_source_cap.data: 200
cost_flow_cap.data: 1000
cost_flow_out.data: 0.002
Again, csp
has the definitions for name, color, base_tech, and carrier_out.
Its constraining parameters are more numerous, it defines:
- a maximum storage capacity (
storage_cap_max
) - an hourly storage loss rate (
storage_loss
), - a constraint that flow capacity can be no greater than storage capacity (
flow_cap_per_storage_cap_max
).
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 source 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 source to carrier (power):
- storage capacity
- source collector area
- source conversion capacity
- carrier conversion capacity
- variable operational and maintenance costs
Interlude: inheriting from technology groups¶
You will notice that the above technologies inherit cost_dim_setter
.
Inheritance allows us to avoid excessive repetition in our model definition.
In this case, cost_dim_setter
defines the dimension and index of costs, allowing us to keep our definition of technology costs to only defining data
.
By defining data
, the technologies override the null
setting applied by cost_dim_setter
.
We also use it to set the interest_rate
for all technologies, which will be used to annualise any investment costs each technology defines.
Technologies can inherit from anything defined in tech_groups
, while nodes can inherit from anything in node_groups
.
items in [tech/node]_groups
can also inherit from each other, so you can create inheritance chains.
cost_dim_setter
looks like this:
tech_groups:
cost_dim_setter:
cost_flow_cap:
data: null
index: monetary
dims: costs
cost_flow_in:
data: null
index: monetary
dims: costs
cost_flow_out:
data: null
index: monetary
dims: costs
cost_storage_cap:
data: null
index: monetary
dims: costs
cost_area_use:
data: null
index: monetary
dims: costs
cost_source_cap:
data: null
index: monetary
dims: costs
cost_interest_rate:
data: 0.10
index: monetary
dims: costs
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: "#3B61E3"
base_tech: storage
inherit: cost_dim_setter
carrier_in: power
carrier_out: power
flow_cap_max: 1000 # kW
storage_cap_max: .inf
flow_cap_per_storage_cap_max: 4
# 0.95 * 0.95 = 0.9025 round trip efficiency
flow_out_eff: 0.95
flow_in_eff: 0.95
storage_loss: 0 # No loss over time assumed
lifetime: 25
cost_storage_cap.data: 200 # USD per kWh storage capacity
The constraints give a maximum installed generation capacity for battery storage.
This is combined with a maximum ratio of flow capacity to storage capacity (flow_cap_per_storage_cap_max
),
which effectively limits the storage capacity.
The ratio is the charge/discharge rate / storage capacity (a.k.a the battery reservoir
).
In the case of a storage technology, flow_in_eff
applies on charging and flow_out_eff
on discharging.
In addition, storage technologies can lose stored carrier 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:
Power demand is a technology like any other. We will associate an actual demand time series with the demand technology at each node separately.
What remains to set up is a simple transmission technology. Transmission technologies look different to other technologies, as they link the carrier at one location to the carrier at another:
region1_to_region2:
from: region1
to: region2
name: "AC power transmission"
color: "#8465A9"
base_tech: transmission
inherit: cost_dim_setter
carrier_in: power
carrier_out: power
flow_out_eff: 0.85
lifetime: 25
cost_flow_cap.data: 200
cost_flow_out.data: 0.002
flow_cap_max: 10000
region1_to_region1_1:
from: region1
to: region1_1
inherit: free_transmission
region1_to_region1_2:
from: region1
to: region1_2
inherit: free_transmission
region1_to_region1_3:
from: region1
to: region1_3
inherit: free_transmission
ac_transmission
has an efficiency of 0.85, so a loss during transmission of 0.15, as well as some cost definitions.
free_transmission
allows local power transmission from any of the csp facilities to the nearest location.
As the name suggests, it applies no cost or efficiency losses to this transmission.
We can see that those technologies which rely on free_transmission
inherit a lot of this information from elsewhere in the model definition.
free_transmission
is defined in tech_groups
, which makes it inheritable.
free_transmission:
name: "Local power transmission"
color: "#6783E3"
carrier_in: power
carrier_out: power
base_tech: transmission
Nodes¶
In order to translate the model requirements shown in this section's introduction into a model definition, five nodes are used: region1
, region2
, region1_1
, region1_2
, and region1_3
.
The technologies are set up at these nodes as follows:
Locations and their technologies in the example model
Let's now look at the first location definition:
region1:
latitude: 40
longitude: -2
techs:
demand_power:
ccgt:
flow_cap_max: 30000 # increased to ensure no unmet_demand in first timestep
There are several things to note here:
- The node specifies a dictionary of technologies that it allows (
techs
), with each key of the dictionary referring to the name of technologies defined in ourtechs.yaml
file. The technologies listed here must have been defined under thetechs
key. - It also overrides some options for both
demand_power
andccgt
. Forccgt
, it simply sets a node-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 anysink
option in the definition of thedemand_power
technology. Instead, this is done directly via a node-specific override. For this node, the filedemand-1.csv
is loaded and the columndemand
is taken (the text after the colon). If no column is specified, Calliope will assume that the column name matches the location nameregion1
. - Coordinates are defined by latitude and longitude, which will be used to calculate distance of transmission lines (unless we specify otherwise later on). They can also be used for geospatial visualisations.
The remaining nodes look similar:
region2:
latitude: 40
longitude: -8
techs:
demand_power:
battery:
region1_1:
inherit: csp_regions
latitude: 41
longitude: -2
region1_2:
inherit: csp_regions
latitude: 39
longitude: -1
region1_3:
inherit: csp_regions
latitude: 39
longitude: -2
region2
is very similar to region1
, except that it does not include the ccgt
technology.
The three region1-
locations are defined together using the node group csp_regions
, except for their geospatial coordinates.
They allow only the csp
technology, this allows us to model three possible sites for CSP plants.