Skip to content

The composed model#

What the surface, generators and loads make together:

import math_spec as ms

model = ms.merge({'surface': 'surface.yaml', 'generator': 'generator.yaml', 'load': 'load.yaml'})
spec = ms.to_spec(model)

The file below is spec.to_yaml() — no fragment holds it, and nothing in the repository commits it. Port_p is one declaration here: each fragment read it under given_variables, and merging folded those into the surface's own.

The objective is the generator's, carried as it was written, because it is the only fragment that priced anything. A second priced fragment would have its term summed with this one.

The math under the file has a tab per formulation. As composed is the model above. With commitment lays variants/commitment.yaml over it with override, which makes the generator a committed unit:

spec = ms.to_spec(ms.override(model, {'commitment': 'variants/commitment.yaml'}))

A patch is refused on its own, because it edits declarations it does not declare. So the model it lands on is the only place its math exists, and the tab prints the patch beside that math.

version: 0
dimensions:
  snapshot:
    dtype: datetime
    description: dispatch periods
  bus:
    dtype: str
    description: network nodes
  port:
    dtype: str
    description: the connections components make, one label per connection
  generator:
    dtype: str
    description: generating units, each on one port
  load:
    dtype: str
    description: demands, each on one port
relations:
  Port_bus:
    key: port
    value: bus
  Generator_port:
    key: generator
    value: port
  Load_port:
    key: load
    value: port
parameters:
  Generator_p_nom:
    dims:
    - generator
    dtype: float
    description: nominal power
  Generator_marginal_cost:
    dims:
    - generator
    dtype: float
    description: cost of one unit of output
  Load_p_set:
    dims:
    - snapshot
    - load
    dtype: float
    description: '`Load-p_set` — what a load takes in a snapshot'
variables:
  Port_p:
    dims:
    - snapshot
    - port
    domain: continuous
    absence: undefined
    description: what a port puts into its bus in a snapshot, negative for a withdrawal
  Generator_p:
    dims:
    - snapshot
    - generator
    bounds:
      lower: 0.0
      upper: Generator_p_nom
    domain: continuous
    absence: undefined
    description: '`Generator-p` — what a generator produces in a snapshot'
constraints:
  Bus_nodal_balance:
    dims:
    - snapshot
    - bus
    expression: sum(Port_p, by=Port_bus) == 0
    description: '`Bus-nodal_balance` — what the ports on a bus put in nets to nothing'
  Generator_injection:
    dims:
    - snapshot
    - generator
    expression: at(Port_p, by=Generator_port) == Generator_p
    description: 'what a generator produces is what its port injects. No PyPSA row
      stands for this: PyPSA writes the generator into the balance instead'
  Load_withdrawal:
    dims:
    - snapshot
    - load
    expression: at(Port_p, by=Load_port) == -Load_p_set
    description: 'what a load takes is what its port withdraws. No PyPSA row stands
      for this: PyPSA writes the load into the balance instead'
objective:
  sense: minimize
  expression: sum(Generator_p * Generator_marginal_cost)

Sets#

Symbol Meaning
\(\mathcal{T}\) index \(t\) — snapshot — dispatch periods
\(\mathcal{N}\) index \(n\) — bus with \(\mathrm{Port\_bus}: \mathcal{J} \to \mathcal{N}\) — network nodes
\(\mathcal{J}\) index \(j\) — port with \(\mathrm{Port\_bus}: \mathcal{J} \to \mathcal{N},\ \mathrm{Generator\_port}: \mathcal{G} \to \mathcal{J},\ \mathrm{Load\_port}: \mathcal{D} \to \mathcal{J}\) — the connections components make, one label per connection
\(\mathcal{G}\) index \(g\) — generator with \(\mathrm{Generator\_port}: \mathcal{G} \to \mathcal{J}\) — generating units, each on one port
\(\mathcal{D}\) index \(d\) — load with \(\mathrm{Load\_port}: \mathcal{D} \to \mathcal{J}\) — demands, each on one port

Parameters#

Symbol Meaning
\(\mathrm{p}^{\mathrm{nom}}\) Generator_p_nom over \(\mathcal{G}\) — nominal power
\(\mathrm{c}\) Generator_marginal_cost over \(\mathcal{G}\) — cost of one unit of output
\(\mathrm{load}\) Load_p_set over \(\mathcal{T} \times \mathcal{D}\) — Load-p_set — what a load takes in a snapshot

Variables#

Symbol Meaning
\(f\) Port_p over \(\mathcal{T} \times \mathcal{J}\) — what a port puts into its bus in a snapshot, negative for a withdrawal
\(p\) Generator_p over \(\mathcal{T} \times \mathcal{G}\) — Generator-p — what a generator produces in a snapshot

Objective#

\[ \min \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} p_{t,g} \cdot \mathrm{c}_{g} \]

Subject to#

Bus_nodal_balance

\[ \sum_{j \in \mathcal{J} \,:\, \mathrm{Port\_bus}(j) = n} f_{t,j} = 0 \qquad \forall\, t \in \mathcal{T},\ n \in \mathcal{N} \]

Generator_injection

\[ f_{t,\mathrm{Generator\_port}(g)} = p_{t,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

Load_withdrawal

\[ f_{t,\mathrm{Load\_port}(d)} = -\mathrm{load}_{t,d} \qquad \forall\, t \in \mathcal{T},\ d \in \mathcal{D} \]

Variable domains#

Port_p

\[ f_{t,j} \in \mathbb{R} \qquad \forall\, t \in \mathcal{T},\ j \in \mathcal{J} \]

Generator_p

\[ 0 \le p_{t,g} \le \mathrm{p}^{\mathrm{nom}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]
variants/commitment.yaml
parameters:
  Generator_p_min_pu: { dims: [generator], description: "least output, per unit of nominal power" }
variables:
  Generator_status:
    dims: [snapshot, generator]
    domain: binary
    description: "`Generator-status` — whether a unit is on in a snapshot"
  Generator_p: { bounds: { upper: .inf } }
constraints:
  Generator_com_p_upper:
    description: "`Generator-com-p-upper` — a committed unit outputs at most its nominal power; off, at most nothing"
    dims: [snapshot, generator]
    expression: Generator_p <= Generator_p_nom * Generator_status
  Generator_com_p_lower:
    description: "`Generator-com-p-lower` — a committed unit outputs at least its minimum; off, at least nothing"
    dims: [snapshot, generator]
    expression: Generator_p >= Generator_p_min_pu * Generator_p_nom * Generator_status

Sets#

Symbol Meaning
\(\mathcal{T}\) index \(t\) — snapshot — dispatch periods
\(\mathcal{N}\) index \(n\) — bus with \(\mathrm{Port\_bus}: \mathcal{J} \to \mathcal{N}\) — network nodes
\(\mathcal{J}\) index \(j\) — port with \(\mathrm{Port\_bus}: \mathcal{J} \to \mathcal{N},\ \mathrm{Generator\_port}: \mathcal{G} \to \mathcal{J},\ \mathrm{Load\_port}: \mathcal{D} \to \mathcal{J}\) — the connections components make, one label per connection
\(\mathcal{G}\) index \(g\) — generator with \(\mathrm{Generator\_port}: \mathcal{G} \to \mathcal{J}\) — generating units, each on one port
\(\mathcal{D}\) index \(d\) — load with \(\mathrm{Load\_port}: \mathcal{D} \to \mathcal{J}\) — demands, each on one port

Parameters#

Symbol Meaning
\(\mathrm{p}^{\mathrm{nom}}\) Generator_p_nom over \(\mathcal{G}\) — nominal power
\(\mathrm{c}\) Generator_marginal_cost over \(\mathcal{G}\) — cost of one unit of output
\(\mathrm{load}\) Load_p_set over \(\mathcal{T} \times \mathcal{D}\) — Load-p_set — what a load takes in a snapshot
\(\underline{\mathrm{p}}\) Generator_p_min_pu over \(\mathcal{G}\) — least output, per unit of nominal power

Variables#

Symbol Meaning
\(f\) Port_p over \(\mathcal{T} \times \mathcal{J}\) — what a port puts into its bus in a snapshot, negative for a withdrawal
\(p\) Generator_p over \(\mathcal{T} \times \mathcal{G}\) — Generator-p — what a generator produces in a snapshot
\(u\) Generator_status over \(\mathcal{T} \times \mathcal{G}\) — Generator-status — whether a unit is on in a snapshot

Objective#

\[ \min \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} p_{t,g} \cdot \mathrm{c}_{g} \]

Subject to#

Bus_nodal_balance

\[ \sum_{j \in \mathcal{J} \,:\, \mathrm{Port\_bus}(j) = n} f_{t,j} = 0 \qquad \forall\, t \in \mathcal{T},\ n \in \mathcal{N} \]

Generator_injection

\[ f_{t,\mathrm{Generator\_port}(g)} = p_{t,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

Load_withdrawal

\[ f_{t,\mathrm{Load\_port}(d)} = -\mathrm{load}_{t,d} \qquad \forall\, t \in \mathcal{T},\ d \in \mathcal{D} \]

Generator_com_p_upper

\[ p_{t,g} \le \mathrm{p}^{\mathrm{nom}}_{g} \cdot u_{t,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

Generator_com_p_lower

\[ p_{t,g} \ge \underline{\mathrm{p}}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \cdot u_{t,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

Variable domains#

Port_p

\[ f_{t,j} \in \mathbb{R} \qquad \forall\, t \in \mathcal{T},\ j \in \mathcal{J} \]

Generator_p

\[ p_{t,g} \ge 0 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

Generator_status

\[ u_{t,g} \in \{0, 1\} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]