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 urban-scale example model

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.

Supply node

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)
More complex node, with resource storage

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
        charge_rate: 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:

Transmission node

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
        charge_rate: 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 charge rate (charge_rate) of 4, which in turn limits the storage capacity. The charge rate is the charge/discharge rate / storage capacity (a.k.a the battery resevoir). 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:

Demand node

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 technologies. 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):

Transmission node

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

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 our techs.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 and ccgt. For the latter, it simply sets a location-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 resource option in the definition of the demand_power technology. Instead, this is done directly via a location-specific override. For this location, 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-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.

Notebook

Calliope National Scale Example Model

In [1]:
import calliope

# We increase logging verbosity
calliope.set_log_level('INFO')
In [2]:
model = calliope.examples.national_scale()
[2018-10-04 16:26:05] INFO: Model: initialising
[2018-10-04 16:26:06] INFO: Model: preprocessing stage 1 (model_run)
[2018-10-04 16:26:06] INFO: Model: preprocessing stage 2 (model_data)
[2018-10-04 16:26:07] INFO: Model: preprocessing complete. Time since start: 0:00:01.335305
In [3]:
# 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
Out[3]:
<xarray.Dataset>
Dimensions:                    (carriers: 1, coordinates: 2, costs: 1, loc_carriers: 5, loc_techs: 15, loc_techs_area: 3, loc_techs_finite_resource: 5, loc_techs_investment_cost: 7, loc_techs_non_conversion: 15, loc_techs_om_cost: 12, loc_techs_store: 4, loc_techs_supply_plus: 3, loc_techs_transmission: 8, locs: 5, techs: 6, timesteps: 120)
Coordinates:
  * costs                      (costs) object 'monetary'
  * techs                      (techs) object 'free_transmission' ...
  * loc_techs_store            (loc_techs_store) object 'region1-1::csp' ...
  * coordinates                (coordinates) object 'lat' 'lon'
  * loc_techs_non_conversion   (loc_techs_non_conversion) object 'region2::ac_transmission:region1' ...
  * loc_techs_area             (loc_techs_area) object 'region1-1::csp' ...
  * loc_techs_investment_cost  (loc_techs_investment_cost) object 'region1-1::csp' ...
  * loc_techs_transmission     (loc_techs_transmission) object 'region2::ac_transmission:region1' ...
  * loc_techs_finite_resource  (loc_techs_finite_resource) object 'region1::demand_power' ...
  * timesteps                  (timesteps) datetime64[ns] 2005-01-01 ...
  * carriers                   (carriers) object 'power'
  * loc_carriers               (loc_carriers) object 'region1::power' ...
  * loc_techs_om_cost          (loc_techs_om_cost) object 'region1::free_transmission:region1-1' ...
  * locs                       (locs) object 'region2' 'region1-2' 'region1' ...
  * loc_techs                  (loc_techs) object 'region2::ac_transmission:region1' ...
  * loc_techs_supply_plus      (loc_techs_supply_plus) object 'region1-1::csp' ...
Data variables:
    resource_area_max          (loc_techs_area) float64 inf inf inf
    resource_eff               (loc_techs_finite_resource) float64 nan nan ...
    energy_prod                (loc_techs) float64 1.0 1.0 1.0 nan 1.0 1.0 ...
    energy_ramping             (loc_techs) float64 nan nan nan nan nan nan ...
    resource                   (loc_techs_finite_resource, timesteps) float64 -2.528e+04 ...
    force_resource             (loc_techs_finite_resource) float64 1.0 1.0 ...
    lifetime                   (loc_techs) float64 25.0 25.0 nan nan nan nan ...
    energy_cap_max             (loc_techs) float64 1e+04 1e+04 inf nan inf ...
    resource_unit              (loc_techs_finite_resource) <U15 'energy' ...
    charge_rate                (loc_techs_store) int64 1 4 1 1
    energy_eff                 (loc_techs) float64 0.85 0.85 1.0 nan 1.0 1.0 ...
    energy_con                 (loc_techs) float64 1.0 1.0 1.0 1.0 1.0 1.0 ...
    parasitic_eff              (loc_techs_supply_plus) float64 0.9 0.9 0.9
    storage_loss               (loc_techs_store) float64 0.002 0.0 0.002 0.002
    storage_cap_max            (loc_techs_store) float64 6.14e+05 inf ...
    reserve_margin             (carriers) float64 nan
    cost_depreciation_rate     (costs, loc_techs_investment_cost) float64 0.1102 ...
    cost_resource_area         (costs, loc_techs_investment_cost) float64 200.0 ...
    cost_energy_cap            (costs, loc_techs_investment_cost) float64 1e+03 ...
    cost_resource_cap          (costs, loc_techs_investment_cost) float64 200.0 ...
    cost_om_prod               (costs, loc_techs_om_cost) float64 0.0 0.002 ...
    cost_om_con                (costs, loc_techs_om_cost) float64 nan nan ...
    cost_storage_cap           (costs, loc_techs_investment_cost) float64 50.0 ...
    lookup_remotes             (loc_techs_transmission) <U36 'region1::ac_transmission:region2' ...
    loc_coordinates            (coordinates, locs) int64 40 39 40 39 41 -8 ...
    colors                     (techs) <U7 '#6783E3' '#8465A9' '#E37A72' ...
    inheritance                (techs) <U12 'transmission' 'transmission' ...
    names                      (techs) <U26 'Local power transmission' ...
    energy_cap_max_systemwide  (techs) float64 nan nan 1e+05 nan nan nan
    lookup_loc_carriers        (loc_carriers) <U221 'region1::ccgt::power,region1::free_transmission:region1-2::power,region1::demand_power::power,region1::free_transmission:region1-1::power,region1::ac_transmission:region2::power,region1::free_transmission:region1-3::power' ...
    lookup_loc_techs           (loc_techs_non_conversion) <U43 'region2::ac_transmission:region1::power' ...
    lookup_loc_techs_area      (locs) <U14 '' 'region1-2::csp' '' ...
    timestep_resolution        (timesteps) float64 1.0 1.0 1.0 1.0 1.0 1.0 ...
    timestep_weights           (timesteps) float64 1.0 1.0 1.0 1.0 1.0 1.0 ...
    max_demand_timesteps       (carriers) datetime64[ns] 2005-01-05T16:00:00
Attributes:
    model.calliope_version:            0.6.3
    model.name:                        National-scale example model
    model.subset_time:                 ['2005-01-01', '2005-01-05']
    model.timeseries_dateformat:       %Y-%m-%d %H:%M:%S
    run.backend:                       pyomo
    run.bigM:                          1000000.0
    run.cyclic_storage:                True
    run.ensure_feasibility:            True
    run.mode:                          plan
    run.objective:                     minmax_cost_optimization
    run.objective_options.cost_class:  monetary
    run.objective_options.sense:       minimize
    run.operation.use_cap_results:     False
    run.solver:                        glpk
    run.zero_threshold:                1e-10
    calliope_version:                  0.6.3
    applied_overrides:                 
    scenario:                          None
    defaults:                          available_area: null\ncarrier_ratios: ...
    allow_operate_mode:                1
In [4]:
# Individual data variables can be accessed easily, `to_pandas()` reformats the data to look nicer
model.inputs.resource.to_pandas()
Out[4]:
timesteps 2005-01-01 00:00:00 2005-01-01 01:00:00 2005-01-01 02:00:00 2005-01-01 03:00:00 2005-01-01 04:00:00 2005-01-01 05:00:00 2005-01-01 06:00:00 2005-01-01 07:00:00 2005-01-01 08:00:00 2005-01-01 09:00:00 ... 2005-01-05 14:00:00 2005-01-05 15:00:00 2005-01-05 16:00:00 2005-01-05 17:00:00 2005-01-05 18:00:00 2005-01-05 19:00:00 2005-01-05 20:00:00 2005-01-05 21:00:00 2005-01-05 22:00:00 2005-01-05 23:00:00
loc_techs_finite_resource
region1::demand_power -25284.480 -24387.440 -23730.656 -23123.040 -23119.600 -23683.280 -24364.720 -25249.968000 -26090.208000 -26870.464000 ... -37078.160000 -38203.12000 -39033.520 -38631.008 -36990.800 -35330.832 -33623.456 -31341.168 -29390.624 -28132.928
region2::demand_power -2254.098 -2131.148 -2090.164 -2131.148 -2172.132 -2172.132 -2213.114 -2295.082000 -2459.016000 -2459.016000 ... -2295.082000 -2459.01600 -2909.836 -2868.852 -2786.886 -2745.902 -2622.950 -2459.016 -2254.098 -2295.082
region1-1::csp 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.021060 0.263805 0.434037 ... 0.322062 0.07927 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000
region1-3::csp 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000000 0.000000 0.026837 ... 0.118691 0.00000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000
region1-2::csp 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.009056 0.096755 0.245351 ... 0.000000 0.00000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000

5 rows × 120 columns

In [5]:
# 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()
Out[5]:
timesteps 2005-01-01 00:00:00 2005-01-01 01:00:00 2005-01-01 02:00:00 2005-01-01 03:00:00 2005-01-01 04:00:00 2005-01-01 05:00:00 2005-01-01 06:00:00 2005-01-01 07:00:00 2005-01-01 08:00:00 2005-01-01 09:00:00 ... 2005-01-05 14:00:00 2005-01-05 15:00:00 2005-01-05 16:00:00 2005-01-05 17:00:00 2005-01-05 18:00:00 2005-01-05 19:00:00 2005-01-05 20:00:00 2005-01-05 21:00:00 2005-01-05 22:00:00 2005-01-05 23:00:00
techs
csp 0.000 0.000 0.00 0.000 0.000 0.000 0.000 0.030116 0.36056 0.706225 ... 0.440753 0.07927 0.000 0.00 0.000 0.000 0.000 0.000 0.000 0.00
demand_power -27538.578 -26518.588 -25820.82 -25254.188 -25291.732 -25855.412 -26577.834 -27545.050000 -28549.22400 -29329.480000 ... -39373.242000 -40662.13600 -41943.356 -41499.86 -39777.686 -38076.734 -36246.406 -33800.184 -31644.722 -30428.01

2 rows × 120 columns

In [6]:
# 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()
[2018-10-04 16:26:07] INFO: Backend: starting model run
[2018-10-04 16:26:09] INFO: Backend: model generated. Time since start: 0:00:03.356609
[2018-10-04 16:26:09] INFO: Backend: sending model to solver
[2018-10-04 16:26:11] INFO: Backend: solver finished running. Time since start: 0:00:05.309883
[2018-10-04 16:26:11] INFO: Backend: loaded results
[2018-10-04 16:26:11] INFO: Backend: generated solution array. Time since start: 0:00:05.408545
[2018-10-04 16:26:11] INFO: Postprocessing: started
[2018-10-04 16:26:11] INFO: Postprocessing: All values < 1e-10 set to 0 in carrier_prod, carrier_con, cost, storage, cost_var, capacity_factor
[2018-10-04 16:26:11] INFO: Postprocessing: Model was feasible, deleting unmet_demand variable
[2018-10-04 16:26:11] INFO: Postprocessing: ended. Time since start: 0:00:05.636784
In [7]:
# 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
Out[7]:
<xarray.Dataset>
Dimensions:                     (carriers: 1, costs: 1, loc_tech_carriers_con: 11, loc_tech_carriers_prod: 13, loc_techs: 15, loc_techs_area: 3, loc_techs_cost: 13, loc_techs_investment_cost: 7, loc_techs_om_cost: 12, loc_techs_store: 4, loc_techs_supply_plus: 3, techs: 6, timesteps: 120)
Coordinates:
  * costs                       (costs) object 'monetary'
  * techs                       (techs) object 'free_transmission' ...
  * loc_techs_store             (loc_techs_store) object 'region1-1::csp' ...
  * loc_techs_cost              (loc_techs_cost) object 'region1::free_transmission:region1-1' ...
  * loc_techs_area              (loc_techs_area) object 'region1-1::csp' ...
  * loc_techs_investment_cost   (loc_techs_investment_cost) object 'region1-1::csp' ...
  * timesteps                   (timesteps) datetime64[ns] 2005-01-01 ...
  * loc_tech_carriers_con       (loc_tech_carriers_con) object 'region1-1::free_transmission:region1::power' ...
  * carriers                    (carriers) <U5 'power'
  * loc_techs_om_cost           (loc_techs_om_cost) object 'region1::free_transmission:region1-1' ...
  * loc_tech_carriers_prod      (loc_tech_carriers_prod) object 'region1-1::csp::power' ...
  * loc_techs                   (loc_techs) object 'region2::ac_transmission:region1' ...
  * loc_techs_supply_plus       (loc_techs_supply_plus) object 'region1-1::csp' ...
Data variables:
    energy_cap                  (loc_techs) float64 3.23e+03 3.23e+03 9e+03 ...
    carrier_prod                (loc_tech_carriers_prod, timesteps) float64 0.0 ...
    carrier_con                 (loc_tech_carriers_con, timesteps) float64 0.0 ...
    cost                        (costs, loc_techs_cost) float64 0.0 ...
    resource_area               (loc_techs_area) float64 1.304e+05 0.0 8.486e+03
    storage_cap                 (loc_techs_store) float64 2.443e+05 ...
    storage                     (loc_techs_store, timesteps) float64 0.0 0.0 ...
    resource_con                (loc_techs_supply_plus, timesteps) float64 0.0 ...
    resource_cap                (loc_techs_supply_plus) float64 6.722e+04 ...
    cost_var                    (costs, loc_techs_om_cost, timesteps) float64 0.0 ...
    cost_investment             (costs, loc_techs_investment_cost) float64 9.317e+04 ...
    capacity_factor             (loc_tech_carriers_prod, timesteps) float64 0.0 ...
    systemwide_capacity_factor  (techs, carriers) float64 nan nan 0.9469 ...
    systemwide_levelised_cost   (carriers, techs, costs) float64 nan nan ...
    total_levelised_cost        (carriers, costs) float64 0.05361
Attributes:
    model.calliope_version:            0.6.3
    model.name:                        National-scale example model
    model.subset_time:                 ['2005-01-01', '2005-01-05']
    model.timeseries_dateformat:       %Y-%m-%d %H:%M:%S
    run.backend:                       pyomo
    run.bigM:                          1000000.0
    run.cyclic_storage:                True
    run.ensure_feasibility:            True
    run.mode:                          plan
    run.objective:                     minmax_cost_optimization
    run.objective_options.cost_class:  monetary
    run.objective_options.sense:       minimize
    run.operation.use_cap_results:     False
    run.solver:                        glpk
    run.zero_threshold:                1e-10
    calliope_version:                  0.6.3
    applied_overrides:                 
    scenario:                          None
    defaults:                          available_area: null\ncarrier_ratios: ...
    allow_operate_mode:                1
    termination_condition:             optimal
    solution_time:                     3.724045
    time_finished:                     2018-10-04 16:26:11
In [8]:
# 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()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 120 entries, 2005-01-01 00:00:00 to 2005-01-05 23:00:00
Data columns (total 9 columns):
ac_transmission:region1        120 non-null float64
ac_transmission:region2        120 non-null float64
battery                        120 non-null float64
ccgt                           120 non-null float64
csp                            120 non-null float64
free_transmission:region1      120 non-null float64
free_transmission:region1-1    120 non-null float64
free_transmission:region1-2    120 non-null float64
free_transmission:region1-3    120 non-null float64
dtypes: float64(9)
memory usage: 9.4 KB
In [9]:
# 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()
Out[9]:
techs ac_transmission:region1 ac_transmission:region2 battery ccgt csp free_transmission:region1 free_transmission:region1-1 free_transmission:region1-2 free_transmission:region1-3
timesteps
2005-01-01 00:00:00 2254.098 0.0 0.0 27936.360000 0.0 0.0 0.0 0.0 0.0
2005-01-01 01:00:00 2131.148 0.0 0.0 26894.672941 0.0 0.0 0.0 0.0 0.0
2005-01-01 02:00:00 2090.164 0.0 0.0 26189.672471 0.0 0.0 0.0 0.0 0.0
2005-01-01 03:00:00 2131.148 0.0 0.0 25630.272941 0.0 0.0 0.0 0.0 0.0
2005-01-01 04:00:00 2172.132 0.0 0.0 25675.049412 0.0 0.0 0.0 0.0 0.0
In [10]:
# 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()
In [11]:
# plot.capacities gives a graphical view of the non-timeseries variables, both input and output
model.plot.capacity()
In [12]:
# We can also examine total technology costs
# notice all the NaN values which appear when seperating loc::techs to locs and techs. 
# Any NaN value means we never considered that combination of `loc` and `tech` for the variable

costs =  model.get_formatted_array('cost').loc[{'costs': 'monetary'}].to_pandas()
costs
Out[12]:
techs ac_transmission:region1 ac_transmission:region2 battery ccgt csp free_transmission:region1 free_transmission:region1-1 free_transmission:region1-2 free_transmission:region1-3
locs
region1 NaN 487.527365 NaN 170306.866419 NaN NaN 0.0 0.0 0.0
region1-1 NaN NaN NaN NaN 94592.427594 0.0 NaN NaN NaN
region1-2 NaN NaN NaN NaN 0.000000 0.0 NaN NaN NaN
region1-3 NaN NaN NaN NaN 9013.121389 0.0 NaN NaN NaN
region2 1067.172474 NaN 1581.363398 NaN NaN NaN NaN NaN NaN
In [13]:
# We can examine levelized costs for each location and technology

lcoes = model.results.systemwide_levelised_cost.loc[{'carriers': 'power', 'costs':'monetary'}].to_pandas()
lcoes
Out[13]:
techs
free_transmission         NaN
ac_transmission           NaN
ccgt                 0.049961
csp                  0.142719
demand_power              NaN
battery              0.100035
dtype: float64
In [14]:
# We can just plot this directly using calliope analysis functionality
model.plot.capacity(array='systemwide_levelised_cost')
In [15]:
# Plotting a map of locations and transmission lines is easily possible
# In our example, the system is purely hypothetical and the resulting map
# gives us little useful information...

model.plot.transmission()
In [16]:
# Transmission plots give us a static picture of installed capacity along links. 
# If we want timeseries data on energy flows at locations and along links
# we can use energy flow plotting. We only show every 4th hour here, to improve loading speed.

model.plot.flows(timestep_cycle=4)
In [17]:
# `cufflinks` allows for easy plotly plots to be 
# produced from a pandas DataFrame

##
# ATTENTION: To run this final part of the tutorial,
# you need to run `pip install cufflinks`
##

import cufflinks
cufflinks.go_offline()
In [18]:
# We might like to plot the costs table from further above,
# which is outside the scope of internal calliope analysis functions

# But by using cufflinks, we can plot directly from our pandas DataFrame

# Note that the colours are not correct for our technologies here,
# as they come from the default colour theme applied by cufflinks

costs.iplot(kind='bar')

See the Calliope documentation for more details on setting up and running a Calliope model.

Previous: Tutorials | Next: Tutorial 2: urban scale