Skip to content

Rung 12: linearized unit commitment — the status a share in [0, 1], stated by pypsa_linearized_uc.yaml

One rung of the PyPSA corpus: the file pypsa.yaml projected onto what this network builds, attached to that network, and held to what PyPSA solves it to.

✔ Verified against pypsa 1.3.0 — objective 7775.0 on both sides; structure ✔ 21 constraints · 5 variables, name for name; size ✔ 128 rows · ✔ 44 columns · ✔ 288 nonzeros; duals ≠ 128 rows, 1 negated, Generator-com-status-min_up_time_must_stay_up off by 490.0 — degenerate with PyPSA's duplicated cap — a unit held on has status pinned at 1 by this row from below and by the variable bound and the cap row from above, and HiGHS may put the dual on any of them; Generator-status-p-fixed-upper off by 1447.5 — PyPSA bounds the status at 1 on the variable and writes the cap row as well, so a binding cap's dual may sit on the bound and leave the row at zero; the file states the row alone (rung 12, the linearized relaxation); model for model: 24 blocks equal, 0 documented splits, 3 recorded deviations.

Rows and columns, PyPSA against specsolve, name for name
row PyPSA specsolve
Bus-nodal_balance 8 8
Generator-com-down-time 6 6
Generator-com-p-before 3 3
Generator-com-p-current 3 3
Generator-com-p-lower 8 8
Generator-com-p-upper 8 8
Generator-com-partly-shut-down 3 3
Generator-com-partly-start-up 3 3
Generator-com-status-min_up_time_must_stay_up 2 2
Generator-com-transition-shut-down 8 8
Generator-com-transition-start-up 8 8
Generator-com-up-time 6 6
Generator-fix-p-lower 8 8
Generator-fix-p-upper 8 8
Generator-p-ramp_limit_down 7 7
Generator-p-ramp_limit_up 7 7
Generator-shut_down-p-fixed-upper 8 8
Generator-start_up-p-fixed-upper 8 8
Generator-status-p-fixed-upper 8 8
Link-fix-p-lower 4 4
Link-fix-p-upper 4 4
column PyPSA specsolve
Generator-p 16 16
Generator-shut_down 8 8
Generator-start_up 8 8
Generator-status 8 8
Link-p 4 4

The model

The same model, as math

The relaxed class of a plain n.optimize(): linearized_unit_commitment, stated on rung 1's transport surface in a file of its own. The status, its starts and its stops are shares in [0, 1] rather than binaries — a domain is the spec's, not the data's — and four rows PyPSA adds only under the keyword tighten the relaxation where a unit's start and stop cost the same. examples/pypsa.yaml stays the integer one.

Sets

Symbol Meaning
\(\mathcal{T}\) index \(t\) — snapshot — dispatch periods
\(\mathcal{N}\) index \(n\) — bus with \(\mathrm{Generator\_bus}: \mathcal{G} \to \mathcal{N},\ \mathrm{Link\_bus0}: \mathcal{L} \to \mathcal{N},\ \mathrm{Link\_output\_bus}: \mathcal{O} \to \mathcal{N},\ \mathrm{Load\_bus}: \mathcal{D} \to \mathcal{N}\) — network nodes
\(\mathcal{G}\) index \(g\) — generator with \(\mathrm{Generator\_bus}: \mathcal{G} \to \mathcal{N}\) — generating units, each on one bus
\(\mathcal{L}\) index \(l\) — link with \(\mathrm{Link\_bus0}: \mathcal{L} \to \mathcal{N},\ \mathrm{Link\_output\_link}: \mathcal{O} \to \mathcal{L}\) — controllable connections, each from one bus to the buses it delivers to
\(\mathcal{O}\) index \(o\) — link_output with \(\mathrm{Link\_output\_link}: \mathcal{O} \to \mathcal{L},\ \mathrm{Link\_output\_bus}: \mathcal{O} \to \mathcal{N}\) — a link's output ports, one label per port a link declares — PyPSA's bus1, bus2, … columns read long, so a link of any number of output ports is one term in the balance, data prep
\(\mathcal{D}\) index \(d\) — load with \(\mathrm{Load\_bus}: \mathcal{D} \to \mathcal{N}\) — demands, each on one bus

Parameters

Symbol Meaning
\(\mathrm{w}\) snapshot_weightings_objective over \(\mathcal{T}\) — PyPSA's snapshot_weightings.objective — hours a snapshot stands for in the cost
\(\mathrm{p}^{\mathrm{nom}}\) Generator_p_nom over \(\mathcal{G}\) — nominal power
\(\underline{\mathrm{p}}\) Generator_p_min_pu over \(\mathcal{T} \times \mathcal{G}\) — least output, per unit of nominal power
\(\overline{\mathrm{p}}\) Generator_p_max_pu over \(\mathcal{T} \times \mathcal{G}\) — most output, per unit of nominal power — an availability profile
\(\mathrm{c}\) Generator_marginal_cost over \(\mathcal{T} \times \mathcal{G}\) — cost of one unit of output
\(\mathrm{f}^{\mathrm{nom}}\) Link_p_nom over \(\mathcal{L}\) — nominal power
\(\underline{\mathrm{f}}\) Link_p_min_pu over \(\mathcal{T} \times \mathcal{L}\) — least flow, per unit of nominal power — negative for a link that carries both ways
\(\overline{\mathrm{f}}\) Link_p_max_pu over \(\mathcal{T} \times \mathcal{L}\) — most flow, per unit of nominal power
\(\eta\) Link_efficiency over \(\mathcal{O}\) — share of the flow that arrives at an output port, PyPSA's efficiency, efficiency2, … read long — negative where that port consumes rather than delivers
\(\mathrm{c}^{f}\) Link_marginal_cost over \(\mathcal{T} \times \mathcal{L}\) — cost of one unit of flow
\(\mathrm{load}\) Load_p_set over \(\mathcal{T} \times \mathcal{D}\) — demand
\(\mathrm{com}\) Generator_committable over \(\mathcal{G}\) — whether output is gated by an on/off status decision
\(\mathrm{ru}\) Generator_ramp_limit_up over \(\mathcal{G}\) — most a generator may raise its output between snapshots, per unit of nominal power; no value means no limit
\(\mathrm{rd}\) Generator_ramp_limit_down over \(\mathcal{G}\) — most a generator may lower its output between snapshots, per unit of nominal power; no value means no limit
\(\mathrm{ru}^{\mathrm{up}}\) Generator_ramp_limit_start_up over \(\mathcal{G}\) — most output in the snapshot a unit starts, per unit of nominal power
\(\mathrm{rd}^{\mathrm{dn}}\) Generator_ramp_limit_shut_down over \(\mathcal{G}\) — most output in the snapshot before a unit stops, per unit of nominal power
\(\mathrm{UT}\) Generator_min_up_time over \(\mathcal{G}\) — least snapshots a unit stays on once started
\(\mathrm{DT}\) Generator_min_down_time over \(\mathcal{G}\) — least snapshots a unit stays off once stopped
\(\mathrm{u}^{0}\) Generator_status_initial over \(\mathcal{G}\) — one where the unit was on before the first snapshot, zero where off — PyPSA's up_time_before > 0, data prep
\(\mathrm{hold}\) Generator_must_stay_up over \(\mathcal{T} \times \mathcal{G}\) — true while the up time a unit brought into the horizon still binds — data prep, since position() compares against a literal rather than a parameter
\(\mathrm{c}^{\mathrm{up}}\) Generator_start_up_cost over \(\mathcal{G}\) — cost of one start
\(\mathrm{c}^{\mathrm{dn}}\) Generator_shut_down_cost over \(\mathcal{G}\) — cost of one stop
\(\mathrm{c}^{\mathrm{on}}\) Generator_stand_by_cost over \(\mathcal{T} \times \mathcal{G}\) — cost of one snapshot spent on
\(\mathrm{tight}\) Generator_partly_tightened over \(\mathcal{G}\) — whether the four tightening rows below apply — PyPSA adds them only where a unit's start-up and shut-down costs are equal; two parameters cannot be compared in a where, so the equality is data prep

Variables

Symbol Meaning
\(p\) Generator_p over \(\mathcal{T} \times \mathcal{G}\) — Generator-p — output of a generator in a snapshot
\(f\) Link_p over \(\mathcal{T} \times \mathcal{L}\) — Link-p — PyPSA's p0, the flow measured at the Link_bus0 end: a positive value withdraws there and injects at every bus the link's output ports deliver to
\(u\) Generator_status over \(\mathcal{T} \times \mathcal{G}\) — Generator-status — how much of a committable unit is on, a share in [0, 1] rather than a binary: the relaxation linearized_unit_commitment solves
\(\mathit{up}\) Generator_start_up over \(\mathcal{T} \times \mathcal{G}\) — Generator-start_up — how much of a committable unit turns on this snapshot
\(\mathit{dn}\) Generator_shut_down over \(\mathcal{T} \times \mathcal{G}\) — Generator-shut_down — how much of a committable unit turns off this snapshot

Definitions

Symbol Meaning
\(\mathit{Generator\_previous\_status}\) Generator_previous_status over \(\mathcal{T} \times \mathcal{G}\) — the commitment state a generator carries into a snapshot — the state it brought into the horizon at the first, the previous snapshot's after that
\(\mathit{Generator\_previous\_p}\) Generator_previous_p over \(\mathcal{T} \times \mathcal{G}\) — the output a generator carries into a snapshot — nothing at the start of the horizon, which is why a unit that came in running carries no ramp row there

\(\mathrm{pos}(t)\) denotes where index \(t\) sits along its dimension's own order — the order shift steps along, not the order labels sort in — counted from \(0\). The index itself stays the coordinate, so \(t\) compares against labels and \(\mathrm{pos}(t)\) against positions.

Objective

\[ \min \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} p_{t,g} \cdot \mathrm{c}_{t,g} \cdot \mathrm{w}_{t} + \sum_{t \in \mathcal{T},\ l \in \mathcal{L}} f_{t,l} \cdot \mathrm{c}^{f}_{t,l} \cdot \mathrm{w}_{t} + \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} u_{t,g} \cdot \mathrm{c}^{\mathrm{on}}_{t,g} \cdot \mathrm{w}_{t} + \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} \mathit{up}_{t,g} \cdot \mathrm{c}^{\mathrm{up}}_{g} + \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} \mathit{dn}_{t,g} \cdot \mathrm{c}^{\mathrm{dn}}_{g} \]

Subject to

Generator_fix_p_lower

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

Generator_fix_p_upper

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

Link_fix_p_lower

\[ f_{t,l} \ge \underline{\mathrm{f}}_{t,l} \cdot \mathrm{f}^{\mathrm{nom}}_{l} \qquad \forall\, t \in \mathcal{T},\ l \in \mathcal{L} \]

Link_fix_p_upper

\[ f_{t,l} \le \overline{\mathrm{f}}_{t,l} \cdot \mathrm{f}^{\mathrm{nom}}_{l} \qquad \forall\, t \in \mathcal{T},\ l \in \mathcal{L} \]

Bus_nodal_balance

\[ \sum_{g \in \mathcal{G} \,:\, \mathrm{Generator\_bus}(g) = n} p_{t,g} - \left( \sum_{l \in \mathcal{L} \,:\, \mathrm{Link\_bus0}(l) = n} f_{t,l} \right) + \sum_{o \in \mathcal{O} \,:\, \mathrm{Link\_output\_bus}(o) = n} f_{t,\mathrm{Link\_output\_link}(o)} \cdot \eta_{o} = \sum_{d \in \mathcal{D} \,:\, \mathrm{Load\_bus}(d) = n} \mathrm{load}_{t,d} \qquad \forall\, t \in \mathcal{T},\ n \in \mathcal{N} \]

Generator_com_p_lower

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

Generator_com_p_upper

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

Generator_com_transition_start_up

\[ \mathit{up}_{t,g} \ge u_{t,g} - \mathit{Generator\_previous\_status}_{t,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \]

Generator_com_transition_shut_down

\[ \mathit{dn}_{t,g} \ge \mathit{Generator\_previous\_status}_{t,g} - u_{t,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \]

Generator_com_up_time

\[ \sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}} \mathit{up}_{t',g} \le u_{t,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{UT}_{g} > 0 \wedge \mathrm{pos}(t) > 0 \]

Generator_com_down_time

\[ \sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}} \mathit{dn}_{t',g} \le 1 - u_{t,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{DT}_{g} > 0 \wedge \mathrm{pos}(t) > 0 \]

Generator_com_status_must_stay_up

\[ u_{t,g} = 1 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{hold}_{t,g} \]

Generator_p_ramp_limit_up

\[ p_{t,g} - \mathit{Generator\_previous\_p}_{t,g} \le \mathrm{ru}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \cdot \mathit{Generator\_previous\_status}_{t,g} + \mathrm{ru}^{\mathrm{up}}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \cdot \left( u_{t,g} - \mathit{Generator\_previous\_status}_{t,g} \right) \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ru}_{g} \text{ is defined} \wedge \left( \mathrm{pos}(t) > 0 \vee \mathrm{u}^{0}_{g} = 0 \right) \]

Generator_p_ramp_limit_down

\[ \mathit{Generator\_previous\_p}_{t,g} - p_{t,g} \le \mathrm{rd}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \cdot u_{t,g} + \mathrm{rd}^{\mathrm{dn}}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \cdot \left( \mathit{Generator\_previous\_status}_{t,g} - u_{t,g} \right) \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{rd}_{g} \text{ is defined} \wedge \left( \mathrm{pos}(t) > 0 \vee \mathrm{u}^{0}_{g} = 0 \right) \]

Generator_status_p_fixed_upper

\[ u_{t,g} \le 1 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \]

Generator_start_up_p_fixed_upper

\[ \mathit{up}_{t,g} \le 1 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \]

Generator_shut_down_p_fixed_upper

\[ \mathit{dn}_{t,g} \le 1 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \]

Generator_com_p_before

\[ p_{t - 1,g} - \mathrm{rd}^{\mathrm{dn}}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \cdot u_{t - 1,g} - \left( \overline{\mathrm{p}}_{t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} - \mathrm{rd}^{\mathrm{dn}}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \right) \cdot \left( u_{t,g} - \mathit{up}_{t,g} \right) \le 0 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{tight}_{g} \]

Generator_com_p_current

\[ p_{t,g} - \overline{\mathrm{p}}_{t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \cdot u_{t,g} + \left( \overline{\mathrm{p}}_{t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} - \mathrm{ru}^{\mathrm{up}}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \right) \cdot \mathit{up}_{t,g} \le 0 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{tight}_{g} \wedge \mathrm{pos}(t) > 0 \]

Generator_com_partly_start_up

\[ p_{t,g} - p_{t - 1,g} - \left( \underline{\mathrm{p}}_{t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} + \mathrm{ru}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \right) \cdot u_{t,g} + \underline{\mathrm{p}}_{t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \cdot u_{t - 1,g} + \left( \underline{\mathrm{p}}_{t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} + \mathrm{ru}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} - \mathrm{ru}^{\mathrm{up}}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \right) \cdot \mathit{up}_{t,g} \le 0 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{tight}_{g} \]

Generator_com_partly_shut_down

\[ p_{t - 1,g} - p_{t,g} - \mathrm{rd}^{\mathrm{dn}}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \cdot u_{t - 1,g} + \left( \mathrm{rd}^{\mathrm{dn}}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} - \mathrm{rd}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \right) \cdot u_{t,g} - \left( \underline{\mathrm{p}}_{t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} + \mathrm{rd}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} - \mathrm{rd}^{\mathrm{dn}}_{g} \cdot \mathrm{p}^{\mathrm{nom}}_{g} \right) \cdot \mathit{up}_{t,g} \le 0 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{tight}_{g} \]

Definitions

Generator_previous_status

\[ \mathit{Generator\_previous\_status}_{t,g} = \begin{cases} \mathrm{u}^{0}_{g} & \text{if } \mathrm{pos}(t) = 0 \\ u_{t - 1,g} & \text{otherwise} \end{cases} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

Generator_previous_p

\[ \mathit{Generator\_previous\_p}_{t,g} = \begin{cases} 0 & \text{if } \mathrm{pos}(t) = 0 \\ p_{t - 1,g} & \text{otherwise} \end{cases} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

Variable domains

Generator_p

\[ p_{t,g} \in \mathbb{R} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

Link_p

\[ f_{t,l} \in \mathbb{R} \qquad \forall\, t \in \mathcal{T},\ l \in \mathcal{L} \]

Generator_status

\[ u_{t,g} \ge 0 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \]

Generator_start_up

\[ \mathit{up}_{t,g} \ge 0 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \]

Generator_shut_down

\[ \mathit{dn}_{t,g} \ge 0 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \]

The spec, differential/pypsa/rungs/rung_12_linearized_uc.yaml — the file projected onto what this rung builds:

description: 'The relaxed class of a plain `n.optimize()`: `linearized_unit_commitment`, stated on rung
  1''s transport surface in a file of its own. The status, its starts and its stops are shares in [0,
  1] rather than binaries — a domain is the spec''s, not the data''s — and four rows PyPSA adds only under
  the keyword tighten the relaxation where a unit''s start and stop cost the same. `examples/pypsa.yaml`
  stays the integer one.'
dimensions:
  snapshot: {description: dispatch periods, dtype: datetime}
  bus: {description: network nodes}
  generator: {description: 'generating units, each on one bus'}
  link: {description: 'controllable connections, each from one bus to the buses it delivers to'}
  link_output: {description: 'a link''s output ports, one label per port a link declares — PyPSA''s `bus1`,
      `bus2`, … columns read long, so a link of any number of output ports is one term in the balance,
      data prep'}
  load: {description: 'demands, each on one bus'}
relations:
  Generator_bus: {description: the bus a generator sits on, key: generator, values: bus}
  Link_bus0: {description: the bus a link leaves, key: link, values: bus}
  Link_output_link: {description: the link an output port belongs to, key: link_output, values: link}
  Link_output_bus: {description: 'the bus an output port delivers to — PyPSA''s `bus1`, `bus2`, … columns.
      A link of three output ports is three labels here rather than a third relation, so the file states
      any number of them', key: link_output, values: bus}
  Load_bus: {description: the bus a load sits on, key: load, values: bus}
parameters:
  snapshot_weightings_objective:
    description: PyPSA's `snapshot_weightings.objective` — hours a snapshot stands for in the cost
    dims: [snapshot]
  Generator_p_nom:
    description: nominal power
    dims: [generator]
  Generator_p_min_pu:
    description: least output, per unit of nominal power
    dims: [snapshot, generator]
  Generator_p_max_pu:
    description: most output, per unit of nominal power — an availability profile
    dims: [snapshot, generator]
  Generator_marginal_cost:
    description: cost of one unit of output
    dims: [snapshot, generator]
  Link_p_nom:
    description: nominal power
    dims: [link]
  Link_p_min_pu:
    description: least flow, per unit of nominal power — negative for a link that carries both ways
    dims: [snapshot, link]
  Link_p_max_pu:
    description: most flow, per unit of nominal power
    dims: [snapshot, link]
  Link_efficiency:
    description: share of the flow that arrives at an output port, PyPSA's `efficiency`, `efficiency2`,
      … read long — negative where that port consumes rather than delivers
    dims: [link_output]
  Link_marginal_cost:
    description: cost of one unit of flow
    dims: [snapshot, link]
  Load_p_set:
    description: demand
    dims: [snapshot, load]
  Generator_committable:
    description: whether output is gated by an on/off status decision
    dims: [generator]
    dtype: bool
  Generator_ramp_limit_up:
    description: most a generator may raise its output between snapshots, per unit of nominal power; no
      value means no limit
    dims: [generator]
  Generator_ramp_limit_down:
    description: most a generator may lower its output between snapshots, per unit of nominal power; no
      value means no limit
    dims: [generator]
  Generator_ramp_limit_start_up:
    description: most output in the snapshot a unit starts, per unit of nominal power
    dims: [generator]
  Generator_ramp_limit_shut_down:
    description: most output in the snapshot before a unit stops, per unit of nominal power
    dims: [generator]
  Generator_min_up_time:
    description: least snapshots a unit stays on once started
    dims: [generator]
    dtype: int
  Generator_min_down_time:
    description: least snapshots a unit stays off once stopped
    dims: [generator]
    dtype: int
  Generator_status_initial:
    description: one where the unit was on before the first snapshot, zero where off — PyPSA's `up_time_before
      > 0`, data prep
    dims: [generator]
    dtype: int
  Generator_must_stay_up:
    description: true while the up time a unit brought into the horizon still binds — data prep, since
      `position()` compares against a literal rather than a parameter
    dims: [snapshot, generator]
    dtype: bool
  Generator_start_up_cost:
    description: cost of one start
    dims: [generator]
  Generator_shut_down_cost:
    description: cost of one stop
    dims: [generator]
  Generator_stand_by_cost:
    description: cost of one snapshot spent on
    dims: [snapshot, generator]
  Generator_partly_tightened:
    description: whether the four tightening rows below apply — PyPSA adds them only where a unit's start-up
      and shut-down costs are equal; two parameters cannot be compared in a `where`, so the equality is
      data prep
    dims: [generator]
    dtype: bool
variables:
  Generator_p:
    description: '`Generator-p` — output of a generator in a snapshot'
    dims: [snapshot, generator]
  Link_p:
    description: '`Link-p` — PyPSA''s `p0`, the flow measured at the `Link_bus0` end: a positive value
      withdraws there and injects at every bus the link''s output ports deliver to'
    dims: [snapshot, link]
  Generator_status:
    description: '`Generator-status` — how much of a committable unit is on, a share in [0, 1] rather
      than a binary: the relaxation `linearized_unit_commitment` solves'
    dims: [snapshot, generator]
    where: Generator_committable
    bounds: {lower: 0}
  Generator_start_up:
    description: '`Generator-start_up` — how much of a committable unit turns on this snapshot'
    dims: [snapshot, generator]
    where: Generator_committable
    bounds: {lower: 0}
  Generator_shut_down:
    description: '`Generator-shut_down` — how much of a committable unit turns off this snapshot'
    dims: [snapshot, generator]
    where: Generator_committable
    bounds: {lower: 0}
constraints:
  Generator_fix_p_lower:
    description: '`Generator-fix-p-lower` — a generator outputs at least its minimum'
    dims: [snapshot, generator]
    where: not Generator_committable
    expression: Generator_p >= Generator_p_min_pu * Generator_p_nom
  Generator_fix_p_upper:
    description: '`Generator-fix-p-upper` — a generator outputs at most what is available'
    dims: [snapshot, generator]
    where: not Generator_committable
    expression: Generator_p <= Generator_p_max_pu * Generator_p_nom
  Link_fix_p_lower:
    description: '`Link-fix-p-lower` — a link carries at least its minimum, negative for the other way'
    dims: [snapshot, link]
    expression: Link_p >= Link_p_min_pu * Link_p_nom
  Link_fix_p_upper:
    description: '`Link-fix-p-upper` — a link carries at most its nominal power'
    dims: [snapshot, link]
    expression: Link_p <= Link_p_max_pu * Link_p_nom
  Bus_nodal_balance:
    description: '`Bus-nodal_balance` — what is generated at a bus, less what the links take away, plus
      what arrives over them after losses, meets the load there'
    dims: [snapshot, bus]
    expression: sum(Generator_p, by=Generator_bus, over=generator, into=bus) - sum(Link_p, by=Link_bus0,
      over=link, into=bus) + sum(at(Link_p, by=Link_output_link, over=link, into=link_output) * Link_efficiency,
      by=Link_output_bus, over=link_output, into=bus) == sum(Load_p_set, by=Load_bus, over=load, into=bus)
  Generator_com_p_lower:
    description: '`Generator-com-p-lower` — a committed unit outputs at least its minimum; off, at least
      nothing'
    dims: [snapshot, generator]
    where: Generator_committable
    expression: Generator_p >= Generator_p_min_pu * Generator_p_nom * Generator_status
  Generator_com_p_upper:
    description: '`Generator-com-p-upper` — a committed unit outputs at most what is available; off, at
      most nothing'
    dims: [snapshot, generator]
    where: Generator_committable
    expression: Generator_p <= Generator_p_max_pu * Generator_p_nom * Generator_status
  Generator_com_transition_start_up:
    description: '`Generator-com-transition-start-up` — turning on is a start, counted against the state
      the unit carried into the snapshot'
    dims: [snapshot, generator]
    where: Generator_committable
    expression: Generator_start_up >= Generator_status - Generator_previous_status
  Generator_com_transition_shut_down:
    description: '`Generator-com-transition-shut-down` — turning off is a stop, counted against the state
      the unit carried into the snapshot'
    dims: [snapshot, generator]
    where: Generator_committable
    expression: Generator_shut_down >= Generator_previous_status - Generator_status
  Generator_com_up_time:
    description: '`Generator-com-up-time` — a unit started within its own minimum up time is still on.
      The first snapshot''s share of the window is the brought-in up time''s, which the must-stay-up mask
      carries'
    dims: [snapshot, generator]
    where: Generator_committable AND Generator_min_up_time > 0 AND position(snapshot) > 0
    expression: sum_back(Generator_start_up, along=snapshot, window=Generator_min_up_time) <= Generator_status
  Generator_com_down_time:
    description: '`Generator-com-down-time` — a unit stopped within its own minimum down time is still
      off'
    dims: [snapshot, generator]
    where: Generator_committable AND Generator_min_down_time > 0 AND position(snapshot) > 0
    expression: sum_back(Generator_shut_down, along=snapshot, window=Generator_min_down_time) <= 1 - Generator_status
  Generator_com_status_must_stay_up:
    description: '`Generator-com-status-min_up_time_must_stay_up` — a unit still serving the up time it
      brought in stays on'
    dims: [snapshot, generator]
    where: Generator_committable AND Generator_must_stay_up
    expression: Generator_status == 1
  Generator_p_ramp_limit_up:
    description: '`Generator-p-ramp_limit_up` — a committed unit raises output no faster than its limit
      while it was already on, and no further than its start-up ramp in the snapshot it turns on. A unit
      that came into the horizon running brought an unknown output, so it carries no row at the first
      snapshot'
    dims: [snapshot, generator]
    where: Generator_committable AND Generator_ramp_limit_up AND (position(snapshot) > 0 OR Generator_status_initial
      == 0)
    expression: Generator_p - Generator_previous_p <= Generator_ramp_limit_up * Generator_p_nom * Generator_previous_status
      + Generator_ramp_limit_start_up * Generator_p_nom * (Generator_status - Generator_previous_status)
  Generator_p_ramp_limit_down:
    description: '`Generator-p-ramp_limit_down` — a committed unit lowers output no faster than its limit
      while it stays on, and no further than its shut-down ramp in the snapshot it turns off. A unit that
      came into the horizon running brought an unknown output, so it carries no row at the first snapshot'
    dims: [snapshot, generator]
    where: Generator_committable AND Generator_ramp_limit_down AND (position(snapshot) > 0 OR Generator_status_initial
      == 0)
    expression: Generator_previous_p - Generator_p <= Generator_ramp_limit_down * Generator_p_nom * Generator_status
      + Generator_ramp_limit_shut_down * Generator_p_nom * (Generator_previous_status - Generator_status)
  Generator_status_p_fixed_upper:
    description: '`Generator-status-p-fixed-upper` — a status is at most one, an explicit row as PyPSA
      writes it'
    dims: [snapshot, generator]
    where: Generator_committable
    expression: Generator_status <= 1
  Generator_start_up_p_fixed_upper:
    description: '`Generator-start_up-p-fixed-upper` — a start is at most one, an explicit row as PyPSA
      writes it'
    dims: [snapshot, generator]
    where: Generator_committable
    expression: Generator_start_up <= 1
  Generator_shut_down_p_fixed_upper:
    description: '`Generator-shut_down-p-fixed-upper` — a stop is at most one, an explicit row as PyPSA
      writes it'
    dims: [snapshot, generator]
    where: Generator_committable
    expression: Generator_shut_down <= 1
  Generator_com_p_before:
    description: '`Generator-com-p-before` — the output a unit had entering this snapshot fits the share
      of it still on, less the share it is shutting down at the shut-down ramp. The translated term vacates
      the first snapshot, as PyPSA''s `sns[1:]` does'
    dims: [snapshot, generator]
    where: Generator_committable AND Generator_partly_tightened
    expression: shift(Generator_p, along=snapshot, offset=1) - Generator_ramp_limit_shut_down * Generator_p_nom
      * shift(Generator_status, along=snapshot, offset=1) - (Generator_p_max_pu * Generator_p_nom - Generator_ramp_limit_shut_down
      * Generator_p_nom) * (Generator_status - Generator_start_up) <= 0
  Generator_com_p_current:
    description: '`Generator-com-p-current` — output fits the share on, and the share starting up only
      up to the start-up ramp'
    dims: [snapshot, generator]
    where: Generator_committable AND Generator_partly_tightened AND position(snapshot) > 0
    expression: Generator_p - Generator_p_max_pu * Generator_p_nom * Generator_status + (Generator_p_max_pu
      * Generator_p_nom - Generator_ramp_limit_start_up * Generator_p_nom) * Generator_start_up <= 0
  Generator_com_partly_start_up:
    description: '`Generator-com-partly-start-up` — raising output while a share is starting up is bounded
      by the ramp of the share on and the start-up ramp of the share coming on'
    dims: [snapshot, generator]
    where: Generator_committable AND Generator_partly_tightened
    expression: Generator_p - shift(Generator_p, along=snapshot, offset=1) - (Generator_p_min_pu * Generator_p_nom
      + Generator_ramp_limit_up * Generator_p_nom) * Generator_status + Generator_p_min_pu * Generator_p_nom
      * shift(Generator_status, along=snapshot, offset=1) + (Generator_p_min_pu * Generator_p_nom + Generator_ramp_limit_up
      * Generator_p_nom - Generator_ramp_limit_start_up * Generator_p_nom) * Generator_start_up <= 0
  Generator_com_partly_shut_down:
    description: '`Generator-com-partly-shut-down` — lowering output while a share is shutting down is
      bounded likewise, by the shut-down ramp'
    dims: [snapshot, generator]
    where: Generator_committable AND Generator_partly_tightened
    expression: shift(Generator_p, along=snapshot, offset=1) - Generator_p - Generator_ramp_limit_shut_down
      * Generator_p_nom * shift(Generator_status, along=snapshot, offset=1) + (Generator_ramp_limit_shut_down
      * Generator_p_nom - Generator_ramp_limit_down * Generator_p_nom) * Generator_status - (Generator_p_min_pu
      * Generator_p_nom + Generator_ramp_limit_down * Generator_p_nom - Generator_ramp_limit_shut_down
      * Generator_p_nom) * Generator_start_up <= 0
expressions:
  Generator_previous_status:
    description: the commitment state a generator carries into a snapshot — the state it brought into
      the horizon at the first, the previous snapshot's after that
    dims: [snapshot, generator]
    cases:
      opening: {when: position(snapshot) == 0, expression: Generator_status_initial}
    otherwise: shift(Generator_status, along=snapshot, offset=1)
  Generator_previous_p:
    description: the output a generator carries into a snapshot — nothing at the start of the horizon,
      which is why a unit that came in running carries no ramp row there
    dims: [snapshot, generator]
    cases:
      opening: {when: position(snapshot) == 0, expression: 0}
    otherwise: shift(Generator_p, along=snapshot, offset=1)
objective: {sense: minimize, description: 'operating cost by weighted snapshot, plus what starts, stops
    and standing by cost', expression: sum(Generator_p * Generator_marginal_cost * snapshot_weightings_objective)
    + sum(Link_p * Link_marginal_cost * snapshot_weightings_objective) + sum(Generator_status * Generator_stand_by_cost
    * snapshot_weightings_objective) + sum(Generator_start_up * Generator_start_up_cost) + sum(Generator_shut_down
    * Generator_shut_down_cost)}

The prep — every table the spec declares, from the network — and the solve:

from differential.pypsa.prep import relation, static, varying, weighting


def _link_ports(n: pypsa.Network) -> pd.DataFrame:
    """A link's output ports read long — one row per port a link declares, carrying the link, the bus it delivers to and its efficiency.

    PyPSA spells the ports across columns — ``bus1``/``efficiency``, ``bus2``/``efficiency2``, … — and a
    link declares a port by naming a bus in one, so a link of any port count is as many rows here and
    one term in the balance. The label is the link and the column the port came from.
    """
    links = n.static('Link')
    blank = pd.Series('', index=links.index, dtype=str)
    frames = []
    for port in ['1', *n.components.links.additional_ports]:
        suffix = '' if port == '1' else port
        buses = links.get(f'bus{port}', blank).astype(str)
        # `efficiency`, `delay` and `cyclic_delay` are PyPSA's unsuffixed attributes: port 1
        # spells them bare and every port after it takes the number
        efficiencies = links.get(f'efficiency{suffix}', pd.Series(1.0, index=links.index)).astype(float)
        delays = links.get(f'delay{suffix}', pd.Series(0, index=links.index)).fillna(0).astype(int)
        cyclic = links.get(f'cyclic_delay{suffix}', pd.Series(False, index=links.index)).fillna(False).astype(bool)
        frame = pd.DataFrame(
            keyed(links.index, 'link')
            | {
                'bus': buses.to_numpy(),
                'value': efficiencies.to_numpy(),
                'delay': delays.to_numpy(),
                'cyclic_delay': cyclic.to_numpy(),
                'port': int(port),
            }
        )
        frames.append(frame[buses.to_numpy() != ''])
    ports = pd.concat(frames, ignore_index=True).sort_values(['link', 'port'], kind='stable')
    ports['link_output'] = ports['link'] + '_bus' + ports['port'].astype(str)
    return ports.drop(columns='port').reset_index(drop=True)


def _must_stay_up(n: pypsa.Network) -> pd.DataFrame:
    """True while the up time a unit brought into the horizon still binds."""
    rows = []
    for name, g in n.generators.iterrows():
        if not g['committable'] or g['up_time_before'] <= 0:
            continue
        remaining = int(min(g['min_up_time'] - g['up_time_before'], len(n.snapshots)))
        rows.extend({'snapshot': t, 'generator': str(name), 'value': True} for t in timesteps(n)[: max(remaining, 0)])
    table = pd.DataFrame(rows, columns=['snapshot', 'generator', 'value'])
    return table.astype({'value': bool})


def _per_port(n: pypsa.Network, column: str, as_name: str | None = None) -> pd.DataFrame:
    """One column of the long port table keyed by ``link_output`` — what a port names, or what it carries.

    *as_name* is what the file calls it: a relation keeps its target dimension's
    own name, and every parameter over the ports lands under ``value``.
    """
    ports = _link_ports(n)
    keys = [key for key in ('scenario', 'link_output') if key in ports.columns]
    return ports[[*keys, column]].rename(columns={column: as_name or column})


n = build()  # the network from the PyPSA tab

sources = {
    'snapshot': pl.Series('snapshot', list(timesteps(n)), dtype=pl.Datetime('us')),
    'bus': pl.Series('bus', list(names(n.buses.index).astype(str)), dtype=pl.String),
    'generator': pl.Series('generator', list(names(generators.index).astype(str)), dtype=pl.String),
    'link': pl.Series('link', list(names(links.index).astype(str)), dtype=pl.String),
    'link_output': pl.Series('link_output', list(pd.unique(_link_ports(n)['link_output'])), dtype=pl.String),
    'load': pl.Series('load', list(names(loads.index).astype(str)), dtype=pl.String),
    'Generator_bus': relation(n, 'Generator', 'bus'),
    'Link_bus0': relation(n, 'Link', 'bus0'),
    'Link_output_link': _per_port(n, 'link'),
    'Link_output_bus': _per_port(n, 'bus'),
    'Load_bus': relation(n, 'Load', 'bus'),
    'snapshot_weightings_objective': weighting(n, 'objective'),
    'Generator_p_nom': static(n, 'Generator', 'p_nom'),
    'Generator_p_min_pu': varying(n, 'Generator', 'p_min_pu'),
    'Generator_p_max_pu': varying(n, 'Generator', 'p_max_pu'),
    'Generator_marginal_cost': varying(n, 'Generator', 'marginal_cost'),
    'Link_p_nom': static(n, 'Link', 'p_nom'),
    'Link_p_min_pu': varying(n, 'Link', 'p_min_pu'),
    'Link_p_max_pu': varying(n, 'Link', 'p_max_pu'),
    'Link_efficiency': _per_port(n, 'value'),
    'Link_marginal_cost': varying(n, 'Link', 'marginal_cost'),
    'Load_p_set': varying(n, 'Load', 'p_set'),
    'Generator_committable': static(n, 'Generator', 'committable'),
    'Generator_ramp_limit_up': static(n, 'Generator', 'ramp_limit_up').dropna(),
    'Generator_ramp_limit_down': static(n, 'Generator', 'ramp_limit_down').dropna(),
    'Generator_ramp_limit_start_up': static(n, 'Generator', 'ramp_limit_start_up').fillna({'value': 1.0}),
    'Generator_ramp_limit_shut_down': static(n, 'Generator', 'ramp_limit_shut_down').fillna({'value': 1.0}),
    'Generator_min_up_time': static(n, 'Generator', 'min_up_time'),
    'Generator_min_down_time': static(n, 'Generator', 'min_down_time'),
    'Generator_status_initial': pd.DataFrame(
            keyed(generators.index, 'generator')
            | {
                'value': (generators['up_time_before'] > 0).astype(int).to_numpy(),
            }
        ),
    'Generator_must_stay_up': _must_stay_up(n),
    'Generator_start_up_cost': static(n, 'Generator', 'start_up_cost'),
    'Generator_shut_down_cost': static(n, 'Generator', 'shut_down_cost'),
    'Generator_stand_by_cost': varying(n, 'Generator', 'stand_by_cost'),
    'Generator_partly_tightened': pd.DataFrame(
            keyed(generators.index, 'generator')
            | {
                'value': (generators['start_up_cost'] == generators['shut_down_cost']).to_numpy(),
            }
        ),
        **loss_fan(n, segments),
}

with sps.solve('differential/pypsa/rungs/rung_12_linearized_uc.yaml', sources) as solution:
    solution.objective  # 7775.0

The network, rung_12_linearized_uc.py in the corpus — the spine plus what this rung adds:

# SPDX-FileCopyrightText: mathspec Contributors
#
# SPDX-License-Identifier: MIT

"""Rung 12: linearized unit commitment — the status a share in [0, 1], stated by `pypsa_linearized_uc.yaml`."""

from __future__ import annotations

import spine

MODEL = 'pypsa_linearized_uc.yaml'
OPTIMIZE = {'linearized_unit_commitment': True}


def build():
    """The spine plus two committable units, one whose start and stop cost the same, so PyPSA tightens its relaxation."""
    n = spine.build()
    n.add(
        'Generator',
        'uc12',
        bus='north',
        committable=True,
        p_nom=50,
        marginal_cost=5,
        p_min_pu=0.4,
        min_up_time=3,
        min_down_time=2,
        up_time_before=1,
        ramp_limit_up=0.5,
        ramp_limit_down=0.5,
        ramp_limit_start_up=0.6,
        ramp_limit_shut_down=0.6,
        start_up_cost=100,
        shut_down_cost=100,
        stand_by_cost=5,
    )
    n.add(
        'Generator',
        'cold12',
        bus='south',
        committable=True,
        p_nom=30,
        marginal_cost=60,
        p_min_pu=0.3,
        min_up_time=2,
        min_down_time=1,
        up_time_before=0,
        ramp_limit_up=0.5,
        ramp_limit_down=0.5,
        ramp_limit_start_up=0.7,
        ramp_limit_shut_down=0.7,
        start_up_cost=80,
        shut_down_cost=40,
    )
    n.add('Load', 'swing12', bus='north', p_set=[25, 45, 45, 10])
    return n
n = build()
n.optimize(solver_name='highs')
n.objective  # 7775.0

The data

The tables this rung is the first to declare (35), as the prep produced them:

Generator_bus.csv

generator,bus
coal,north
cold12,south
gas,south
uc12,north

Generator_committable.csv

generator,value
coal,false
cold12,true
gas,false
uc12,true

Generator_marginal_cost.csv

snapshot,generator,value
2015-01-01T00:00:00.000000,coal,10.0
2015-01-01T00:00:00.000000,cold12,60.0
2015-01-01T00:00:00.000000,gas,30.0
2015-01-01T00:00:00.000000,uc12,5.0
2015-01-01T01:00:00.000000,coal,10.0
2015-01-01T01:00:00.000000,cold12,60.0
2015-01-01T01:00:00.000000,gas,30.0
2015-01-01T01:00:00.000000,uc12,5.0
2015-01-01T02:00:00.000000,coal,10.0
2015-01-01T02:00:00.000000,cold12,60.0
2015-01-01T02:00:00.000000,gas,30.0
2015-01-01T02:00:00.000000,uc12,5.0
2015-01-01T03:00:00.000000,coal,10.0
2015-01-01T03:00:00.000000,cold12,60.0
2015-01-01T03:00:00.000000,gas,30.0
2015-01-01T03:00:00.000000,uc12,5.0

Generator_min_down_time.csv

generator,value
coal,0
cold12,1
gas,0
uc12,2

Generator_min_up_time.csv

generator,value
coal,0
cold12,2
gas,0
uc12,3

Generator_must_stay_up.csv

snapshot,generator,value
2015-01-01T00:00:00.000000,uc12,true
2015-01-01T01:00:00.000000,uc12,true

Generator_p_max_pu.csv

snapshot,generator,value
2015-01-01T00:00:00.000000,coal,1.0
2015-01-01T00:00:00.000000,cold12,1.0
2015-01-01T00:00:00.000000,gas,1.0
2015-01-01T00:00:00.000000,uc12,1.0
2015-01-01T01:00:00.000000,coal,1.0
2015-01-01T01:00:00.000000,cold12,1.0
2015-01-01T01:00:00.000000,gas,1.0
2015-01-01T01:00:00.000000,uc12,1.0
2015-01-01T02:00:00.000000,coal,1.0
2015-01-01T02:00:00.000000,cold12,1.0
2015-01-01T02:00:00.000000,gas,1.0
2015-01-01T02:00:00.000000,uc12,1.0
2015-01-01T03:00:00.000000,coal,1.0
2015-01-01T03:00:00.000000,cold12,1.0
2015-01-01T03:00:00.000000,gas,1.0
2015-01-01T03:00:00.000000,uc12,1.0

Generator_p_min_pu.csv

snapshot,generator,value
2015-01-01T00:00:00.000000,coal,0.0
2015-01-01T00:00:00.000000,cold12,0.3
2015-01-01T00:00:00.000000,gas,0.0
2015-01-01T00:00:00.000000,uc12,0.4
2015-01-01T01:00:00.000000,coal,0.0
2015-01-01T01:00:00.000000,cold12,0.3
2015-01-01T01:00:00.000000,gas,0.0
2015-01-01T01:00:00.000000,uc12,0.4
2015-01-01T02:00:00.000000,coal,0.0
2015-01-01T02:00:00.000000,cold12,0.3
2015-01-01T02:00:00.000000,gas,0.0
2015-01-01T02:00:00.000000,uc12,0.4
2015-01-01T03:00:00.000000,coal,0.0
2015-01-01T03:00:00.000000,cold12,0.3
2015-01-01T03:00:00.000000,gas,0.0
2015-01-01T03:00:00.000000,uc12,0.4

Generator_p_nom.csv

generator,value
coal,100.0
cold12,30.0
gas,100.0
uc12,50.0

Generator_partly_tightened.csv

generator,value
coal,true
cold12,false
gas,true
uc12,true

Generator_ramp_limit_down.csv

generator,value
cold12,0.5
uc12,0.5

Generator_ramp_limit_shut_down.csv

generator,value
coal,1.0
cold12,0.7
gas,1.0
uc12,0.6

Generator_ramp_limit_start_up.csv

generator,value
coal,1.0
cold12,0.7
gas,1.0
uc12,0.6

Generator_ramp_limit_up.csv

generator,value
cold12,0.5
uc12,0.5

Generator_shut_down_cost.csv

generator,value
coal,0.0
cold12,40.0
gas,0.0
uc12,100.0

Generator_stand_by_cost.csv

snapshot,generator,value
2015-01-01T00:00:00.000000,coal,0.0
2015-01-01T00:00:00.000000,cold12,0.0
2015-01-01T00:00:00.000000,gas,0.0
2015-01-01T00:00:00.000000,uc12,5.0
2015-01-01T01:00:00.000000,coal,0.0
2015-01-01T01:00:00.000000,cold12,0.0
2015-01-01T01:00:00.000000,gas,0.0
2015-01-01T01:00:00.000000,uc12,5.0
2015-01-01T02:00:00.000000,coal,0.0
2015-01-01T02:00:00.000000,cold12,0.0
2015-01-01T02:00:00.000000,gas,0.0
2015-01-01T02:00:00.000000,uc12,5.0
2015-01-01T03:00:00.000000,coal,0.0
2015-01-01T03:00:00.000000,cold12,0.0
2015-01-01T03:00:00.000000,gas,0.0
2015-01-01T03:00:00.000000,uc12,5.0

Generator_start_up_cost.csv

generator,value
coal,0.0
cold12,80.0
gas,0.0
uc12,100.0

Generator_status_initial.csv

generator,value
coal,1
cold12,0
gas,1
uc12,1

Link_bus0.csv

link,bus
wire,north

Link_efficiency.csv

link_output,value
wire_bus1,0.9

Link_marginal_cost.csv

snapshot,link,value
2015-01-01T00:00:00.000000,wire,0.0
2015-01-01T01:00:00.000000,wire,0.0
2015-01-01T02:00:00.000000,wire,0.0
2015-01-01T03:00:00.000000,wire,0.0

Link_output_bus.csv

link_output,bus
wire_bus1,south

Link_output_link.csv

link_output,link
wire_bus1,wire

Link_p_max_pu.csv

snapshot,link,value
2015-01-01T00:00:00.000000,wire,1.0
2015-01-01T01:00:00.000000,wire,1.0
2015-01-01T02:00:00.000000,wire,1.0
2015-01-01T03:00:00.000000,wire,1.0

Link_p_min_pu.csv

snapshot,link,value
2015-01-01T00:00:00.000000,wire,-1.0
2015-01-01T01:00:00.000000,wire,-1.0
2015-01-01T02:00:00.000000,wire,-1.0
2015-01-01T03:00:00.000000,wire,-1.0

Link_p_nom.csv

link,value
wire,40.0

Load_bus.csv

load,bus
north_load,north
south_load,south
swing12,north

Load_p_set.csv

snapshot,load,value
2015-01-01T00:00:00.000000,north_load,30.0
2015-01-01T00:00:00.000000,south_load,40.0
2015-01-01T00:00:00.000000,swing12,25.0
2015-01-01T01:00:00.000000,north_load,30.0
2015-01-01T01:00:00.000000,south_load,40.0
2015-01-01T01:00:00.000000,swing12,45.0
2015-01-01T02:00:00.000000,north_load,30.0
2015-01-01T02:00:00.000000,south_load,40.0
2015-01-01T02:00:00.000000,swing12,45.0
2015-01-01T03:00:00.000000,north_load,30.0
2015-01-01T03:00:00.000000,south_load,40.0
2015-01-01T03:00:00.000000,swing12,10.0

bus.csv

bus
north
south

generator.csv

generator
coal
cold12
gas
uc12

link.csv

link
wire

link_output.csv

link_output
wire_bus1

load.csv

load
north_load
south_load
swing12

snapshot.csv

snapshot
2015-01-01T00:00:00.000000
2015-01-01T01:00:00.000000
2015-01-01T02:00:00.000000
2015-01-01T03:00:00.000000

snapshot_weightings_objective.csv

snapshot,value
2015-01-01T00:00:00.000000,2.0
2015-01-01T01:00:00.000000,1.5
2015-01-01T02:00:00.000000,2.5
2015-01-01T03:00:00.000000,3.0