Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude/skills/docs-writing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ not need, in three groups:
and `Program`: an engine such as specsolve, a renderer, a checker.
- **Contributing** is for someone who changes mathspec itself.
- **Proofs of concept** holds the notation page, which renders the typesetting
test spec, and the PyPSA pages. The PyPSA pages stay in `docs/examples/`,
where `tools/gallery.py` writes them.
test spec, and the PyPSA and Calliope pages. Those stay in
`docs/examples/`, where `tools/gallery.py` writes them.

A page in Development keeps the folder of its kind.

Expand Down
5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ docs/examples/library/generator.md
docs/examples/library/load.md
docs/examples/library/composed.md
docs/examples/pypsa/*.md
docs/examples/calliope/*.md
docs/examples/calliope/extensions/*.md
docs/examples/calliope/variants/*.md
# The port record is written by hand, so prettier keeps its tables.
!docs/examples/calliope/port.md

# The PyPSA reference scripts write this file; prettier would reformat what
# they stamp, and the two would fight over it exactly as above.
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ it releases that version ([RELEASING.md](https://github.com/energy-models/mathsp

## Upcoming version

- docs(calliope): all of calliope's math is a set of fragments that merge, with its modes laid over them as patches ([#770](https://github.com/energy-models/mathspec/pull/770))
- fix(language): dual(c) is the rate at which the optimal objective rises with the right side of c, so an equality has a sign too ([#751](https://github.com/energy-models/mathspec/pull/751))
- fix(language): a macro formal written inside a list takes the name the call binds to it ([#779](https://github.com/energy-models/mathspec/pull/779))
- feat(language): a sum names several dimensions in one over= list ([#778](https://github.com/energy-models/mathspec/pull/778))
Expand Down
178 changes: 178 additions & 0 deletions docs/examples/calliope/area.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<!--
SPDX-FileCopyrightText: Calliope contributors
SPDX-FileCopyrightText: mathspec contributors
SPDX-License-Identifier: CC-BY-4.0 AND Apache-2.0
-->

# Area

One of the base fragments of [Calliope in fragments](index.md). Area use, its limits, its tie to flow capacity, and its cost.

<!-- gallery:begin -->
```yaml
dimensions:
nodes:
description: Calliope's `nodes` — the places technologies stand at
techs:
description: Calliope's `techs` — technologies
carriers:
description: Calliope's `carriers` — energy and commodity carriers
costs:
description: Calliope's `costs` — cost classes, such as monetary and CO2

parameters:
area_use_min:
description: "`area_use_min` — least area use. Calliope's default is 0, and data prep fills it"
dims: [nodes, techs]
area_use_max:
description: "`area_use_max` — most area use. Calliope's default is `.inf`, and data prep fills it"
dims: [nodes, techs]
area_use_per_flow_cap:
description: "`area_use_per_flow_cap` — area use per unit of flow capacity; given only where set"
dims: [nodes, techs]
available_area:
description: "`available_area` — the area every technology at a node may use; given only where set"
dims: [nodes]
cost_area_use:
description: "`cost_area_use` — the cost of one unit of area use"
dims: [nodes, techs, costs]

variables:
area_use:
description: >-
`area_use` — the area a technology uses. Calliope builds it where
`area_use_min` is given at all; the least area use is data here, so
it is built where that is above zero
dims: [nodes, techs]
where: area_use_min > 0 OR area_use_max OR area_use_per_flow_cap OR sink_unit == per_area OR source_unit == per_area
bounds: { lower: area_use_min, upper: area_use_max }
absence: zero

expressions:
cost_investment_area_use:
description: "`cost_investment_area_use` — the investment cost of area use"
expression: cost_area_use * area_use

given:
parameters:
flow_cap_max: { dims: [nodes, techs] }
sink_unit: { dims: [nodes, techs], dtype: str }
source_unit: { dims: [nodes, techs], dtype: str }
variables:
flow_cap: { dims: [nodes, techs, carriers] }
expressions:
cost_investment: { dims: [nodes, techs, costs], term: cost_investment_area_use }

constraints:
force_zero_area_use:
description: "`force_zero_area_use` — a technology with no flow capacity uses no area"
dims: [nodes, techs]
where: area_use AND flow_cap_max == 0
expression: area_use == 0
area_use_per_flow_capacity:
description: "`area_use_per_flow_capacity` — area use follows flow capacity, where set"
dims: [nodes, techs, carriers]
where: flow_cap AND area_use AND area_use_per_flow_cap
expression: area_use == flow_cap * area_use_per_flow_cap
area_use_capacity_per_loc:
description: >-
`area_use_capacity_per_loc` — the technologies at a node use at most
its available area. Calliope's `where: area_use` over a node reads as
any technology there using area
dims: [nodes]
where: count(area_use, over=techs) >= 1 AND available_area
expression: sum(area_use, over=techs) <= available_area

assumptions:
unbounded_area_use_cost:
description: Calliope's `unbounded_area_use_cost` — a negative area cost needs a finite maximum
holds: NOT cost_area_use < 0 OR area_use_max
```

#### Sets

| Symbol | Meaning |
|---|---|
| $`\mathcal{N}`$ | index $`n`$ — `nodes` — Calliope's `nodes` — the places technologies stand at |
| $`\mathcal{I}`$ | index $`i`$ — `techs` — Calliope's `techs` — technologies |
| $`\mathcal{C}`$ | index $`c`$ — `carriers` — Calliope's `carriers` — energy and commodity carriers |
| $`\mathcal{K}`$ | index $`k`$ — `costs` — Calliope's `costs` — cost classes, such as monetary and CO2 |

#### Parameters

| Symbol | Meaning |
|---|---|
| $`\mathrm{area\_use\_min}`$ | `area_use_min` over $`\mathcal{N} \times \mathcal{I}`$ — `area_use_min` — least area use. Calliope's default is 0, and data prep fills it |
| $`\mathrm{area\_use\_max}`$ | `area_use_max` over $`\mathcal{N} \times \mathcal{I}`$ — `area_use_max` — most area use. Calliope's default is `.inf`, and data prep fills it |
| $`\mathrm{area\_use\_per\_flow\_cap}`$ | `area_use_per_flow_cap` over $`\mathcal{N} \times \mathcal{I}`$ — `area_use_per_flow_cap` — area use per unit of flow capacity; given only where set |
| $`\mathrm{available\_area}`$ | `available_area` over $`\mathcal{N}`$ — `available_area` — the area every technology at a node may use; given only where set |
| $`\mathrm{cost\_area\_use}`$ | `cost_area_use` over $`\mathcal{N} \times \mathcal{I} \times \mathcal{K}`$ — `cost_area_use` — the cost of one unit of area use |

#### Variables

| Symbol | Meaning |
|---|---|
| $`\mathit{area\_use}`$ | `area_use` over $`\mathcal{N} \times \mathcal{I}`$ — `area_use` — the area a technology uses. Calliope builds it where `area_use_min` is given at all; the least area use is data here, so it is built where that is above zero |

#### Given

| Symbol | Meaning |
|---|---|
| $`\mathrm{flow\_cap\_max}`$ | `flow_cap_max` over $`\mathcal{N} \times \mathcal{I}`$, data another file declares |
| $`\mathrm{sink\_unit}`$ | `sink_unit` over $`\mathcal{N} \times \mathcal{I}`$, data another file declares |
| $`\mathrm{source\_unit}`$ | `source_unit` over $`\mathcal{N} \times \mathcal{I}`$, data another file declares |
| $`\mathit{flow\_cap}`$ | `flow_cap` over $`\mathcal{N} \times \mathcal{I} \times \mathcal{C}`$ |
| $`\mathit{cost\_investment}`$ | `cost_investment` over $`\mathcal{N} \times \mathcal{I} \times \mathcal{K}`$, an expression this file adds `cost_investment_area_use` to |

#### Definitions

| Symbol | Meaning |
|---|---|
| $`\mathit{cost\_investment\_area\_use}`$ | `cost_investment_area_use` over $`\mathcal{N} \times \mathcal{I} \times \mathcal{K}`$ — `cost_investment_area_use` — the investment cost of area use |

Upright is what the data supplies — a parameter such as $`\mathrm{area\_use\_min}`$, a coordinate map, a label — and italic is what the solver chooses, such as $`\mathit{area\_use}`$. An index is italic too, being what a quantifier chooses, and a set is script.

#### Subject to

**`force_zero_area_use`**

```math
\mathit{area\_use}_{n,i} = 0 \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I} \,:\, \mathit{area\_use}_{n,i} \text{ exists} \wedge \mathrm{flow\_cap\_max}_{n,i} = 0
```

**`area_use_per_flow_capacity`**

```math
\mathit{area\_use}_{n,i} = \mathit{flow\_cap}_{n,i,c} \cdot \mathrm{area\_use\_per\_flow\_cap}_{n,i} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ c \in \mathcal{C} \,:\, \mathit{flow\_cap}_{n,i,c} \text{ exists} \wedge \mathit{area\_use}_{n,i} \text{ exists} \wedge \mathrm{area\_use\_per\_flow\_cap}_{n,i} \text{ is defined}
```

**`area_use_capacity_per_loc`**

```math
\sum_{i \in \mathcal{I}} \mathit{area\_use}_{n,i} \le \mathrm{available\_area}_{n} \qquad \forall\, n \in \mathcal{N} \,:\, \lvert \{ i \in \mathcal{I} \,:\, \mathit{area\_use}_{n,i} \text{ exists} \} \rvert \ge 1 \wedge \mathrm{available\_area}_{n} \text{ is defined}
```

#### Definitions

**`cost_investment_area_use`**

```math
\mathit{cost\_investment\_area\_use}_{n,i,k} = \mathrm{cost\_area\_use}_{n,i,k} \cdot \mathit{area\_use}_{n,i} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ k \in \mathcal{K}
```

#### Variable domains

**`area_use`**

```math
\mathrm{area\_use\_min}_{n,i} \le \mathit{area\_use}_{n,i} \le \mathrm{area\_use\_max}_{n,i} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I} \,:\, \mathrm{area\_use\_min}_{n,i} > 0 \vee \mathrm{area\_use\_max}_{n,i} \text{ is defined} \vee \mathrm{area\_use\_per\_flow\_cap}_{n,i} \text{ is defined} \vee \mathrm{sink\_unit}_{n,i} = \text{'}\mathrm{per\_area}\text{'} \vee \mathrm{source\_unit}_{n,i} = \text{'}\mathrm{per\_area}\text{'}
```

#### Assumptions

**`unbounded_area_use_cost`**

```math
\neg \left( \mathrm{cost\_area\_use}_{n,i,k} < 0 \right) \vee \mathrm{area\_use\_max}_{n,i} \text{ is defined} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ k \in \mathcal{K}
```
<!-- gallery:end -->
91 changes: 91 additions & 0 deletions docs/examples/calliope/balance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<!--
SPDX-FileCopyrightText: Calliope contributors
SPDX-FileCopyrightText: mathspec contributors
SPDX-License-Identifier: CC-BY-4.0 AND Apache-2.0
-->

# The balance

One of the base fragments of [Calliope in fragments](index.md). Calliope's `system_balance`: at each node, in each time step, a carrier's production equals its consumption. The file declares the sum `carrier_flow` empty, and every file that moves a carrier adds its term.

<!-- gallery:begin -->
```yaml
dimensions:
nodes:
description: Calliope's `nodes` — the places technologies stand at
techs:
description: Calliope's `techs` — technologies
carriers:
description: Calliope's `carriers` — energy and commodity carriers
timesteps:
description: Calliope's `timesteps` — time steps, in order
dtype: datetime

given:
parameters:
carrier_in:
description: whether a technology consumes a carrier at a node
dims: [nodes, techs, carriers]
dtype: bool
carrier_out:
description: whether a technology produces a carrier at a node
dims: [nodes, techs, carriers]
dtype: bool

expressions:
carrier_flow:
description: >-
what every technology and every other file puts into a node's carrier,
less what it takes out
dims: [nodes, carriers, timesteps]
empty: true

constraints:
system_balance:
description: >-
`system_balance` — at every node, in every time step, a carrier's
production equals its consumption. Built where a technology at the
node produces or consumes the carrier
dims: [nodes, carriers, timesteps]
where: count(carrier_in, over=techs) >= 1 OR count(carrier_out, over=techs) >= 1
expression: carrier_flow == 0
```

#### Sets

| Symbol | Meaning |
|---|---|
| $`\mathcal{N}`$ | index $`n`$ — `nodes` — Calliope's `nodes` — the places technologies stand at |
| $`\mathcal{I}`$ | index $`i`$ — `techs` — Calliope's `techs` — technologies |
| $`\mathcal{C}`$ | index $`c`$ — `carriers` — Calliope's `carriers` — energy and commodity carriers |
| $`\mathcal{T}`$ | index $`t`$ — `timesteps` — Calliope's `timesteps` — time steps, in order |

#### Given

| Symbol | Meaning |
|---|---|
| $`\mathrm{carrier\_in}`$ | `carrier_in` over $`\mathcal{N} \times \mathcal{I} \times \mathcal{C}`$, data another file declares — whether a technology consumes a carrier at a node |
| $`\mathrm{carrier\_out}`$ | `carrier_out` over $`\mathcal{N} \times \mathcal{I} \times \mathcal{C}`$, data another file declares — whether a technology produces a carrier at a node |

#### Definitions

| Symbol | Meaning |
|---|---|
| $`\mathit{carrier\_flow}`$ | `carrier_flow` over $`\mathcal{N} \times \mathcal{C} \times \mathcal{T}`$ — what every technology and every other file puts into a node's carrier, less what it takes out |

#### Subject to

**`system_balance`**

```math
\mathit{carrier\_flow}_{n,c,t} = 0 \qquad \forall\, n \in \mathcal{N},\ c \in \mathcal{C},\ t \in \mathcal{T} \,:\, \lvert \{ i \in \mathcal{I} \,:\, \mathrm{carrier\_in}_{n,i,c} \} \rvert \ge 1 \vee \lvert \{ i \in \mathcal{I} \,:\, \mathrm{carrier\_out}_{n,i,c} \} \rvert \ge 1
```

#### Definitions

**`carrier_flow`**

```math
\mathit{carrier\_flow}_{n,c,t} = \cdots \qquad \forall\, n \in \mathcal{N},\ c \in \mathcal{C},\ t \in \mathcal{T}
```
<!-- gallery:end -->
65 changes: 65 additions & 0 deletions docs/examples/calliope/conversion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<!--
SPDX-FileCopyrightText: Calliope contributors
SPDX-FileCopyrightText: mathspec contributors
SPDX-License-Identifier: CC-BY-4.0 AND Apache-2.0
-->

# Conversion

One of the base fragments of [Calliope in fragments](index.md). Calliope's `balance_conversion`, alone: a conversion technology puts out what it takes in. It declares nothing, and reads everything it needs.

<!-- gallery:begin -->
```yaml
dimensions:
nodes:
description: Calliope's `nodes` — the places technologies stand at
techs:
description: Calliope's `techs` — technologies
carriers:
description: Calliope's `carriers` — energy and commodity carriers
timesteps:
description: Calliope's `timesteps` — time steps, in order
dtype: datetime

given:
parameters:
base_tech: { dims: [techs], dtype: str }
include_storage: { dims: [nodes, techs], dtype: bool }
expressions:
flow_out_inc_eff: { dims: [nodes, techs, carriers, timesteps] }
flow_in_inc_eff: { dims: [nodes, techs, carriers, timesteps] }

constraints:
balance_conversion:
description: "`balance_conversion` — a conversion technology puts out, before its losses, what it takes in after them"
dims: [nodes, techs, timesteps]
where: base_tech == 'conversion' AND NOT include_storage
expression: sum(flow_out_inc_eff, over=carriers) == sum(flow_in_inc_eff, over=carriers)
```

#### Sets

| Symbol | Meaning |
|---|---|
| $`\mathcal{N}`$ | index $`n`$ — `nodes` — Calliope's `nodes` — the places technologies stand at |
| $`\mathcal{I}`$ | index $`i`$ — `techs` — Calliope's `techs` — technologies |
| $`\mathcal{C}`$ | index $`c`$ — `carriers` — Calliope's `carriers` — energy and commodity carriers |
| $`\mathcal{T}`$ | index $`t`$ — `timesteps` — Calliope's `timesteps` — time steps, in order |

#### Given

| Symbol | Meaning |
|---|---|
| $`\mathrm{base\_tech}`$ | `base_tech` over $`\mathcal{I}`$, data another file declares |
| $`\mathrm{include\_storage}`$ | `include_storage` over $`\mathcal{N} \times \mathcal{I}`$, data another file declares |
| $`\mathit{flow\_out\_inc\_eff}`$ | `flow_out_inc_eff` over $`\mathcal{N} \times \mathcal{I} \times \mathcal{C} \times \mathcal{T}`$, an expression another file defines |
| $`\mathit{flow\_in\_inc\_eff}`$ | `flow_in_inc_eff` over $`\mathcal{N} \times \mathcal{I} \times \mathcal{C} \times \mathcal{T}`$, an expression another file defines |

#### Subject to

**`balance_conversion`**

```math
\sum_{c \in \mathcal{C}} \mathit{flow\_out\_inc\_eff}_{n,i,c,t} = \sum_{c \in \mathcal{C}} \mathit{flow\_in\_inc\_eff}_{n,i,c,t} \qquad \forall\, n \in \mathcal{N},\ i \in \mathcal{I},\ t \in \mathcal{T} \,:\, \mathrm{base\_tech}_{i} = \text{'}\mathrm{conversion}\text{'} \wedge \neg \mathrm{include\_storage}_{n,i}
```
<!-- gallery:end -->
Loading
Loading