Skip to content

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:

National scale example model overview
National scale example model 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 layout of a supply technology
The layout of a supply technology which has an infinite source, a carrier conversion efficiency ($flow_{eff}^{out}$), and a constraint on its maximum built $flow_{cap}$ (which puts an upper limit on $flow_{out}$).

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)
The layout of a supply technology which makes use of a storage buffer and parasitic efficiency
The layout of a supply technology which makes use of a storage buffer and parasitic efficiency.

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:

A storage node with inflow and outflow efficiencies and losses from the stored carrier
A storage node with $flow_{eff}^{in}$, $flow_{eff}^{out}$, and $storage_{loss}$.
  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:

A demand technology, directing an inflow to a sink outside the system boundary
A demand technology, directing a $flow_{in}$ to a sink outside the system boundary.

Power demand is a technology like any other. We will associate an actual demand time series with the demand technology at each node separately.

  demand_power:
    name: "Power demand"
    color: "#072486"
    base_tech: demand
    carrier_in: power

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:

A transmission technology with the options for flow efficiency and flow capacity
A transmission technology with the options for flow efficiency ($flow_{eff}^{out}$ and $flow_{eff}^{in}$) and flow capacity ($flow_{cap}$).
  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:

Nodes and their technologies in the example model
Nodes and their technologies in the example model.

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 our techs.yaml file. The technologies listed here must have been defined under the techs key.
  • It also overrides some options for both demand_power and ccgt. For ccgt, it simply sets a node-specific maximum capacity constraint. For demand_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 any sink option in the definition of the demand_power technology. Instead, this is done directly via a node-specific override. For this node, the file demand-1.csv is loaded and the column demand is taken (the text after the colon). If no column is specified, Calliope will assume that the column name matches the location name region1.
  • 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.

node_groups:
  csp_regions:
    techs:
      csp:

Where to go next

To try loading and solving the model yourself, move on to the accompanying notebook here. You can also find a list of all the example models available in Calliope here.