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:
Overview of the built-in national-scale example model¶
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 (energy_cap
) and a constraint on its maximum built capacity.
The layout of a supply node, in this case ccgt
, which has an infinite resource, a carrier conversion efficiency (\(energy_{eff}\)), and a constraint on its maximum built \(energy_{cap}\) (which puts an upper limit on \(energy_{prod}\)).¶
The definition of this technology in the example model’s configuration looks as follows:
ccgt:
essentials:
name: 'Combined cycle gas turbine'
color: '#E37A72'
parent: supply
carrier_out: power
constraints:
resource: inf
energy_eff: 0.5
energy_cap_max: 40000 # kW
energy_cap_max_systemwide: 100000 # kW
energy_ramping: 0.8
lifetime: 25
costs:
monetary:
interest_rate: 0.10
energy_cap: 750 # USD per kW
om_con: 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 parent, supply
, and its carrier_out, power
. It has set 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 (
parasitic_eff
)
The layout of a more complex node, in this case csp
, which makes use of most node-level functionality available.¶
This definition in the example model’s configuration is more verbose:
csp:
essentials:
name: 'Concentrating solar power'
color: '#F9CF22'
parent: supply_plus
carrier_out: power
constraints:
storage_cap_max: 614033
energy_cap_per_storage_cap_max: 1
storage_loss: 0.002
resource: file=csp_resource.csv
resource_unit: energy_per_area
energy_eff: 0.4
parasitic_eff: 0.9
resource_area_max: inf
energy_cap_max: 10000
lifetime: 25
costs:
monetary:
interest_rate: 0.10
storage_cap: 50
resource_area: 200
resource_cap: 200
energy_cap: 1000
om_prod: 0.002
Again, csp
has the definitions for name, color, parent, and carrier_out. Its constraints are more numerous: it defines a maximum storage capacity (storage_cap_max
), an hourly storage loss rate (storage_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:
A storage node with an \(energy_{eff}\) and \(storage_{loss}\).¶
battery:
essentials:
name: 'Battery storage'
color: '#3B61E3'
parent: storage
carrier: power
constraints:
energy_cap_max: 1000 # kW
storage_cap_max: inf
energy_cap_per_storage_cap_max: 4
energy_eff: 0.95 # 0.95 * 0.95 = 0.9025 round trip efficiency
storage_loss: 0 # No loss over time assumed
lifetime: 25
costs:
monetary:
interest_rate: 0.10
storage_cap: 200 # USD per kWh storage capacity
The contraints give a maximum installed generation capacity for battery storage together with a maximum ratio of energy capacity to storage capacity (energy_cap_per_storage_cap_max
) of 4, which in turn 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, energy_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:
A simple demand node.¶
demand_power:
essentials:
name: 'Power demand'
color: '#072486'
parent: demand
carrier: power
Power demand is a technology like any other. We will associate an actual demand time series with the demand technology later.
What remains to set up is a simple transmission technology. 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):
A simple transmission node with an \(energy_{eff}\).¶
ac_transmission:
essentials:
name: 'AC power transmission'
color: '#8465A9'
parent: transmission
carrier: power
constraints:
energy_eff: 0.85
lifetime: 25
costs:
monetary:
interest_rate: 0.10
energy_cap: 200
om_prod: 0.002
free_transmission:
essentials:
name: 'Local power transmission'
color: '#6783E3'
parent: transmission
carrier: power
constraints:
energy_cap_max: inf
energy_eff: 1.0
costs:
monetary:
om_prod: 0
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.
Locations¶
In order to translate the model requirements shown in this section’s introduction into a model definition, five locations are used: region-1
, region-2
, region1-1
, region1-2
, and region1-3
.
The technologies are set up in these locations as follows:
Locations and their technologies in the example model¶
Let’s now look at the first location definition:
region1:
coordinates: {lat: 40, lon: -2}
techs:
demand_power:
constraints:
resource: file=demand-1.csv:demand
ccgt:
constraints:
energy_cap_max: 30000 # increased to ensure no unmet_demand in first timestep
There are several things to note here:
The location 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. 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 anyresource
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 columndemand
is taken (the text after the colon). If no column is specified, Calliope will assume that the column name matches the location nameregion1-1
. Note that in Calliope, a supply is positive and a demand is negative, so the stored CSV data will be negative.Coordinates are defined by latitude (
lat
) and longitude (lon
), which will be used to calculate distance of transmission lines (unless we specify otherwise later on) and for location-based visualisation.
The remaining location definitions look like this:
region2:
coordinates: {lat: 40, lon: -8}
techs:
demand_power:
constraints:
resource: file=demand-2.csv:demand
battery:
region1-1.coordinates: {lat: 41, lon: -2}
region1-2.coordinates: {lat: 39, lon: -1}
region1-3.coordinates: {lat: 39, lon: -2}
region1-1, region1-2, region1-3:
techs:
csp:
region2
is very similar to region1
, except that it does not allow the ccgt
technology. The three region1-
locations are defined together, except for their location coordinates, i.e. they each get the exact same configuration. They allow only the csp
technology, this allows us to model three possible sites for CSP plants.
For transmission technologies, the model also needs to know which locations can be linked, and this is set up in the model configuration as follows:
region1,region2:
techs:
ac_transmission:
constraints:
energy_cap_max: 10000
region1,region1-1:
techs:
free_transmission:
region1,region1-2:
techs:
free_transmission:
region1,region1-3:
techs:
free_transmission:
We are able to override constraints for transmission technologies at this point, such as the maximum capacity of the specific region1
to region2
link shown here.
Running the model¶
We now take you through running the model in a Jupyter notebook, which is included fully below. To download and run the notebook yourself, you can find it here. You will need to have Calliope installed.
Calliope National Scale Example Model¶
import calliope
# We increase logging verbosity
calliope.set_log_level('INFO')
model = calliope.examples.national_scale()
# Model inputs can be viewed at `model.inputs`.
# Variables are indexed over any combination of `techs`, `locs`, `carriers`, `costs` and `timesteps`,
# although `techs`, `locs`, and `carriers` are often concatenated.
# e.g. `ccgt`, `region1`, `power` -> `region1::ccgt::power`
model.inputs
# Individual data variables can be accessed easily, `to_pandas()` reformats the data to look nicer
model.inputs.resource.to_pandas()
# To reformat the array, deconcatenating loc_techs / loc_tech_carriers, you can use model.get_formatted_array()
# You can then apply loc/tech/carrier only operations, like summing information over locations:
model.get_formatted_array('resource').sum('locs').to_pandas()
# Solve the model. Results are loaded into `model.results`.
# By including logging (see package importing), we can see the timing of parts of the run, as well as the solver's log
model.run()
# Model results are held in the same structure as model inputs.
# The results consist of the optimal values for all decision variables, including capacities and carrier flow
# There are also results, like system capacity factor and levelised costs, which are calculated in postprocessing
# before being added to the results Dataset
model.results
# We can sum power output over all locations and turn the result into a pandas DataFrame
df_power = model.get_formatted_array('carrier_prod').loc[{'carriers':'power'}].sum('locs').to_pandas().T
#The information about the dataframe tells us about the amount of data it holds in the index and in each column
df_power.info()
# Using .head() to see the first few rows of power generation and demand
# Note: power output in ac_transmission:region1 is power received by the high voltage line at any location connected to `r1`
df_power.head()
# We can plot this by using the timeseries plotting functionality.
# The top-left dropdown gives us the chance to scroll through other timeseries data too.
model.plot.timeseries()