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.
mergecomposes the fragments into one spec. - An extension adds to the base: new rows, new decisions, and a term to a
sum the base declares.
mergecomposes it with the base. Where Calliope restatessystem_balance,cost_investment,cost_operation_fixedor 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.
overridelays 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,flowsandcostare the core. They read one another, and the four compose with nothing left undergiven:.conversionandtransmissionreadflowsalone.storage,export,feasibilityandreportingread the core, andreportingreadsexport.area,demandandsupplyread one another. A source or a sink per unit of area readsarea_use, andarea_useis built where a source or a sink is per unit of area. Compose the three together. Withoutdemandandsupply,areakeepssink_unitandsource_unitundergiven:.supply_storagecouplessupplyandstorage, and needs both.extensions/milp.yamlreads the capacities ofstorage,areaandsupply.piecewise_linear_costs,piecewise_linear_efficiencyanduptime_downtime_limitsread the units it builds, so they need it.piecewise_linear_costsandsos2_piecewise_linear_costsboth declarepiecewise_cost_investment, somergerefuses the two together. The CHP variants both rewritebalance_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
$nameis 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 awhere:. Here a missing row reads as0and 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 awhere:that tests it, the description says "given only where set". - A variable with a mask is zero outside it. Calliope's
default: 0isabsence: 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 |