Skip to content

Calliope in fragments#

Calliope states its math in YAML: a base, modes laid over it, and examples of math a modeller adds. This is all of it, from Calliope v0.7.0 (src/calliope/math/ and docs/user_defined_math/examples/), as files under examples/calliope/. The port record lists every Calliope block with how it is stated here, and what mathspec is missing where the port is not one block for one.

The math is Calliope's, copyright Calliope contributors, under the Apache License 2.0. The files under examples/calliope/ are changed from it and are under the same licence. The port record says what changed.

Calliope composes its math by overriding: a mode or an example restates a base block whole to change it. Here, three kinds of file do that work, and the PyPSA split uses the first two.

  • A base fragment is one topic of Calliope's base math. merge composes the fragments into one spec.
  • An extension adds to the base: new rows, new decisions, and a term to a sum the base declares. merge composes it with the base. Where Calliope restates system_balance, cost_investment, cost_operation_fixed or the objective to add one term, the extension adds the term and no base file changes.
  • A variant changes what the base states: a mode, or an example that rewrites a base row. override lays it over the composition. A variant is a patch, not a spec, and it prints as the declarations it writes.

Compose a model#

from pathlib import Path

import mathspec as ms

here = Path('examples/calliope')
base = sorted(here.glob('*.yaml'))

plan = ms.merge(base)
milp = ms.override(ms.merge([*base, here / 'extensions/milp.yaml']), [here / 'variants/milp.yaml'])
operate = ms.override(plan, [here / 'variants/operate.yaml'])
spores = ms.override(plan, [here / 'variants/spores.yaml'])
clustered = ms.override(plan, [here / 'variants/storage_inter_cluster.yaml'])

A mode and an extension compose as Calliope's do: the MILP fragment and its patch, then operate mode and its MILP half, then an example.

fuel = ms.merge([*base, here / 'extensions/fuel_dist.yaml'])
chp = ms.override(ms.merge([*base, here / 'extensions/chp_htp.yaml']), [here / 'variants/chp_htp.yaml'])

What a file needs beside it#

A fragment reads what it does not declare under given, and loads and prints alone. A model needs the file that declares each name a fragment reads, and merge keeps what no file declares under given:.

  • settings, balance, flows and cost are the core. They read one another, and the four compose with nothing left under given:.
  • conversion and transmission read flows alone. storage, export, feasibility and reporting read the core, and reporting reads export.
  • area, demand and supply read one another. A source or a sink per unit of area reads area_use, and area_use is built where a source or a sink is per unit of area. Compose the three together. Without demand and supply, area keeps sink_unit and source_unit under given:.
  • supply_storage couples supply and storage, and needs both.
  • extensions/milp.yaml reads the capacities of storage, area and supply. piecewise_linear_costs, piecewise_linear_efficiency and uptime_downtime_limits read the units it builds, so they need it.
  • piecewise_linear_costs and sos2_piecewise_linear_costs both declare piecewise_cost_investment, so merge refuses the two together. The CHP variants both rewrite balance_conversion. Calliope's examples are alternatives in the same way.

Conventions#

  • Names are Calliope's. A declaration Calliope names is spelled as Calliope spells it, and its description opens with that name. A sub-expression Calliope writes as $name is a named expression. A name Calliope has no word for, such as a term of a sum, carries the name of the file that adds it.
  • A parameter's default is data preparation. Calliope reads a missing value as the parameter's default: in arithmetic, and as not given in a where:. Here a missing row reads as 0 and as false. Where a default is not zero and the math reads the value, data prep fills it, and the description says so. A bound has a row wherever its variable has one, so data prep fills its default too. Where the math reads the parameter only behind a where: that tests it, the description says "given only where set".
  • A variable with a mask is zero outside it. Calliope's default: 0 is absence: zero, so a sum of costs keeps its terms where one of them is masked.
  • A parameter carries the dimensions Calliope's examples give it. The timesteps are added where Calliope resamples the parameter.

The sums#

Sum Over Declared in The terms, by the fragment that adds each
cost_investment nodes, techs, costs cost cost_investment_area_use, flows_cost_investment, cost_investment_purchase, piecewise_cost_investment_term, cost_investment_piecewise, cost_investment_storage_cap, cost_investment_source_cap
carrier_flow nodes, carriers, timesteps balance export_carrier_flow, feasibility_carrier_flow, flows_carrier_flow, fuel_dist_carrier_flow
cost_operation_variable nodes, techs, costs, timesteps cost export_cost_operation_variable, flows_cost_operation_variable, supply_cost_operation_variable
system_cost nothing: one number settings cost_of_techs, fuel_dist_system_cost
cost_operation_fixed nodes, techs, costs cost flows_cost_operation_fixed, cost_month_peak_charge
penalty nothing: one number settings unmet_demand_penalty

The fragments#

Fragment Parameters Variables Constraints Reads Adds to
area 5 1 3 5 cost_investment
balance 0 0 1 2
conversion 0 0 1 4
cost 4 0 0 4 system_cost
demand 4 0 3 5
export 4 1 1 5 carrier_flow, cost_operation_variable
feasibility 0 2 0 6 carrier_flow, penalty
flows 22 3 7 7 carrier_flow, cost_investment, cost_operation_variable, cost_operation_fixed
reporting 0 0 0 6
settings 4 0 0 0
storage 10 2 6 7 cost_investment
supply 10 2 6 10 cost_investment, cost_operation_variable
supply_storage 0 0 1 7
transmission 0 0 2 5
annual_energy_balance 5 0 5 4
chp_htp 4 0 6 6
demand_share_per_timestep_decision 2 1 3 2
fuel_dist 4 1 3 4 carrier_flow, system_cost
max_time_varying 1 0 1 3
milp 11 4 26 22 cost_investment
monthly_peak_flow_charge 2 1 1 4 cost_operation_fixed
net_import_share 1 0 3 4
piecewise_linear_costs 2 1 1 3 cost_investment
piecewise_linear_efficiency 2 0 1 3
share_all_timesteps 2 0 2 2
share_per_timestep 2 0 2 2
sos2_piecewise_linear_costs 2 2 1 2 cost_investment
uptime_downtime_limits 4 0 4 6
urban_scale_chp 1 0 2 5