Skip to content

Commit

Permalink
Updates following review.
Browse files Browse the repository at this point in the history
  • Loading branch information
brynpickering committed Jan 8, 2024
1 parent 8402646 commit 37f2173
Show file tree
Hide file tree
Showing 9 changed files with 342 additions and 165 deletions.
63 changes: 6 additions & 57 deletions docs/custom_math/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,7 @@ A decision variable in Calliope math looks like this:

```yaml
variables:
storage_cap:
description: "The upper limit on carriers that can be stored by a `supply_plus` or `storage` technology in any timestep."
unit: carrier_unit
foreach: [nodes, techs]
where: "include_storage=True"
domain: real # optional; defaults to real.
bounds:
min: storage_cap_min
max: storage_cap_max
active: true
--8<-- "src/calliope/math/base.yaml:variable"
```

1. It needs a unique name (`storage_cap` in the example above).
Expand Down Expand Up @@ -54,25 +45,8 @@ For instance, total costs are global expressions as the cost associated with a t
To not clutter the objective function with all combinations of variables and parameters, we define a separate global expression:

```yaml
cost:
description: "The total annualised costs of a technology, including installation and operation costs."
unit: cost
foreach: [nodes, techs, costs]
where: "cost_investment OR cost_var"
equations:
- expression: $cost_investment + $cost_var_sum
sub_expressions:
cost_investment:
- where: "cost_investment"
expression: cost_investment
- where: "NOT cost_investment"
expression: "0"
cost_var_sum:
- where: "cost_var"
expression: sum(cost_var, over=timesteps)
- where: "NOT cost_var"
expression: "0"
active: true
global_expressions:
--8<-- "src/calliope/math/base.yaml:expression"
```

Global expressions are by no means necessary to include, but can make more complex linear expressions easier to keep track of and can reduce post-processing requirements.
Expand All @@ -95,16 +69,8 @@ This includes limits on things like the maximum area use of tech (there's only s
Here is an example:

```yaml
set_storage_initial:
description: "Fix the relationship between carrier stored in a `storage` technology at the start and end of the whole model period."
foreach: [nodes, techs]
where: "storage AND storage_initial AND config.cyclic_storage=True"
equations:
- expression: storage[timesteps=$final_step] * ((1 - storage_loss) ** timestep_resolution[timesteps=$final_step]) == storage_initial * storage_cap
slices:
final_step:
- expression: get_val_at_index(timesteps=-1)
active: true
constraints:
--8<-- "src/calliope/math/base.yaml:constraint"
```

1. It needs a unique name (`set_storage_initial` in the above example).
Expand All @@ -123,24 +89,7 @@ With your constrained decision variables and a global expression that binds thes

```yaml
objectives:
minmax_cost_optimisation:
description: >
Minimise the total cost of installing and operation all technologies in the system.
If multiple cost classes are present (e.g., monetary and co2 emissions), the weighted sum of total costs is minimised.
Cost class weights can be defined in the top-level parameter `objective_cost_class`.
equations:
- where: "any(cost, over=[nodes, techs, costs])"
expression: sum(sum(cost, over=[nodes, techs]) * objective_cost_class, over=costs) + $unmet_demand
- where: "NOT any(cost, over=[nodes, techs, costs])"
expression: $unmet_demand
sub_expressions:
unmet_demand:
- where: "config.ensure_feasibility=True"
expression: sum(sum(unmet_demand - unused_supply, over=[carriers, nodes]) * timestep_weights, over=timesteps) * bigM
- where: "NOT config.ensure_feasibility=True"
expression: "0"
sense: minimise
active: true
--8<-- "src/calliope/math/base.yaml:objective"
```

1. It needs a unique name.
Expand Down
8 changes: 7 additions & 1 deletion docs/custom_math/customise.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@ When defining your model, you can reference any number of YAML files containing
```yaml
config:
init:
custom_math: [my_new_math_1.md, my_new_math_2.md]
custom_math: [my_new_math_1.yaml, my_new_math_2.yaml]
```

You can also define a mixture of your custom math and the [inbuilt math][inbuilt-math]:

```yaml
config:
init:
custom_math: [my_new_math_1.yaml, storage_inter_cluster, my_new_math_2.md]
```

## Writing your own math documentation

You can write your model's mathematical formulation to view it in a rich-text format (as we do for our [inbuilt math][inbuilt-math] in this documentation).
Expand Down
2 changes: 1 addition & 1 deletion docs/custom_math/examples/annual_energy_balance.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ constraints:
description: >
Limit total flow into the system from a particular source.
NOTE: this only works for supply_plus technologies.
For `supply` technologies you will need to convert `flow_out` to `flow_in` using `energy_eff` and limit that.
For `supply` technologies you will need to convert `flow_out` to `flow_in` using `energy_eff` and limit that.
foreach: [techs]
where: source_use AND annual_source_max
equations:
Expand Down
10 changes: 7 additions & 3 deletions docs/custom_math/examples/chp_htp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ constraints:
chp_backpressure_line_min:
description: >
Set the backpressure line as a lower bound for electricity generation in combined heat and power plants with extraction turbines.
Set the backpressure line as a lower bound for electricity generation in
combined heat and power plants with extraction turbines.
`power_to_heat_ratio` is also referred to as the `backpressure ratio` or simply `cb`.
foreach: [nodes, techs, timesteps]
where: turbine_type=extraction
Expand All @@ -114,7 +115,9 @@ constraints:
# ~~
chp_backpressure_line_max:
description: >
Set the backpressure line as a lower bound for heat generation in combined heat and power plants without extraction turbines, but with the option to divert fuel to use in direct heat generation (e.g., via a boiler).
Set the backpressure line as a lower bound for heat generation in
combined heat and power plants without extraction turbines,
but with the option to divert fuel to use in direct heat generation (e.g., via a boiler).
`power_to_heat_ratio` is also referred to as the `backpressure ratio` or simply `cb`.
foreach: [nodes, techs, timesteps]
where: turbine_type=backpressure AND boiler_eff
Expand All @@ -123,7 +126,8 @@ constraints:

chp_divert_fuel_to_boiler:
description: >
Divert fuel input from use in combined heat and power generation to be used in direct heat generation (e.g., via a boiler).
Divert fuel input from use in combined heat and power generation to be used in
direct heat generation (e.g., via a boiler).
`heat_eff` is the boiler efficiency.
`power_to_heat_ratio` is also referred to as the `backpressure ratio` or simply `cb`.
foreach: [nodes, techs, timesteps]
Expand Down
14 changes: 10 additions & 4 deletions docs/custom_math/examples/demand_share_per_timestep_decision.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ variables:

constraints:
demand_share_per_timestep_decision_main_min:
description: Limit the lower bound of a technology's outflow to the share of demand that the model has decided it will meet.
description: >-
Limit the lower bound of a technology's outflow to the share of demand that the model has decided it will meet.
foreach: [nodes, techs, timesteps]
where: demand_share_per_timestep_decision
equations:
Expand All @@ -56,13 +57,16 @@ constraints:
- expression: demand_share_carrier # assigned as a top-level parameter

demand_share_per_timestep_decision_main_max:
description: Limit the upper bound of a technology's outflow to the share of demand that the model has decided it will meet.
description: >-
Limit the upper bound of a technology's outflow to
the share of demand that the model has decided it will meet.
foreach: [nodes, techs, timesteps]
where: demand_share_per_timestep_decision
equations:
- expression: >
flow_out[carriers=$carrier] <=
(1 + $relaxation) * select_from_lookup_arrays(sink_equals, techs=decide_demand_share) * demand_share_per_timestep_decision
(1 + $relaxation) * select_from_lookup_arrays(sink_equals, techs=decide_demand_share)
* demand_share_per_timestep_decision
sub_expressions:
relaxation: *relaxation_component
slices: *slice
Expand All @@ -71,7 +75,9 @@ constraints:
# to a specified share of carrier demand (here, 50% = 0.5 of electricity demand).

demand_share_per_timestep_decision_sum:
description: Limit the total share of demand that can be met by technologies controlled by the `demand_share_per_timestep_decision` variable.
description: >-
Limit the total share of demand that can be met by technologies controlled by
the `demand_share_per_timestep_decision` variable.
foreach: [nodes, timesteps]
where: demand_share_per_timestep_decision AND demand_share_limit
equations:
Expand Down
21 changes: 12 additions & 9 deletions docs/custom_math/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@ As of Calliope version 0.7, all of the internal math used to build optimisation
The same syntax used for the [inbuilt math](https://github.com/calliope-project/calliope/tree/main/calliope/math) can be used to define custom math.
So, if you want to introduce new constraints, decision variables, or objectives, you can do so as part of the collection of YAML files describing your model.

In brief, components of the math formulation are stored under named keys and contain information on
In brief, components of the math formulation are stored under named keys and contain information on:

* the sets over which they will be generated (e.g., for each technology, node, timestep, ...),
* the conditions under which they will be built in any specific model (e.g., if no `storage` technologies exist in the model, decision variables and constraints associated with them will not be built),
* and their math expression(s).
* the conditions under which they will be built in any specific model (e.g., if no `storage` technologies exist in the model, decision variables and constraints associated with them will not be built), and
* their math expression(s).

In this section, we will describe the [math components][math-components] and the [formulation syntax][math-syntax] in more detail.
Whenever we refer to a "math component" it could be a:

- decision variable (something you want the optimisation model to decide on the value of).
- global expression (a mixture of decision variables and input parameters glued together with math).
- constraint (a way to limit the upper/lower bound of a decision variable using other decision variables/parameters/global expressions).
- objective (the expression whose value you want to minimise/maximise in the optimisation).
* decision variable (something you want the optimisation model to decide on the value of).
* global expression (a mixture of decision variables and input parameters glued together with math).
* constraint (a way to limit the upper/lower bound of a decision variable using other decision variables/parameters/global expressions).
* objective (the expression whose value you want to minimise/maximise in the optimisation).

We recommend you first get an overview of the available [math components][math-components], then review the math formulation [syntax][math-syntax], before looking at how to add your own [custom math][customise] and browsing the [gallery of custom math examples][examples].
A full reference for the allowed key-value pairs in your custom math YAML file is available in the [reference section of the documentation][math_schema].
We recommend you first get an overview of the available [math components][math-components],
then review the math formulation [syntax][math-syntax],
before looking at how to add your own [custom math][introducing-custom-math-to-your-model] and browsing the [gallery of custom math examples][examples].
A full reference for the allowed key-value pairs in your custom math YAML file is available in the [reference section of the documentation][math-formulation-schema].

!!! note

Expand Down
6 changes: 3 additions & 3 deletions docs/custom_math/syntax.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Math syntax

This page provides an overview of the syntax available to formulate math components. reference for the allowed key-value pairs in your custom math YAML file is available in the [reference section of the documentation][math_schema].
This page provides an overview of the syntax available to formulate math components. reference for the allowed key-value pairs in your custom math YAML file is available in the [reference section of the documentation][math-formulation-schema].

## foreach lists

Expand All @@ -27,7 +27,7 @@ When checking the existence of an input parameter it is possible to first sum it
- If you want to apply a constraint across all `nodes` and `techs`, but only for node+tech combinations where the `flow_out_eff` parameter has been defined, you would include `flow_out_eff`.
- If you want to apply a constraint over `techs` and `timesteps`, but only for combinations where the `source_max` parameter has at least one `node` with a value defined, you would include `any(resource, over=nodes)`. (1)

1. I'm a [helper function][helper-functions]; read more below!
1. `any` is a [helper function][helper-functions]; read more below!

1. Checking the value of a configuration option or an input parameter.
Checks can use any of the operators: `>`, `<`, `=`, `<=`, `>=`.
Expand All @@ -38,7 +38,7 @@ Configuration options are any that are defined in `config.build`, where you can
- If you want to apply a constraint only if the configuration option `config.build.cyclic_storage` is _True_, you would include `config.cyclic_storage=True` (`True`/`False` is case insensitive).
- If you want to apply a constraint across all `nodes` and `techs`, but only where the `flow_eff` parameter is less than 0.5, you would include `flow_eff<0.5`.
- If you want to apply a constraint only for the first timestep in your timeseries, you would include `timesteps=get_val_at_index(dim=timesteps, idx=0)`. (1)
- If you want to apply a constraint only for the last timestep in your timeseries, you would include `timesteps=get_val_at_index(dim=timesteps, idx=-1)`. (1)
- If you want to apply a constraint only for the last timestep in your timeseries, you would include `timesteps=get_val_at_index(dim=timesteps, idx=-1)`.

1. `get_val_at_index` is a [helper function][helper-functions]; read more below!

Expand Down
Loading

0 comments on commit 37f2173

Please sign in to comment.