diff --git a/CHANGELOG.md b/CHANGELOG.md index 49291fad..8b0d31a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ it releases that version ([RELEASING.md](https://github.com/energy-models/mathsp ## Upcoming version +- docs(pypsa): the pypsa spec names its repeated row conditions as masks, and its topic files read them under `given: masks:` ([#825](https://github.com/energy-models/mathspec/pull/825)) +- feat(language): a where predicate is named once under `masks:`, and another file reads it under `given: masks:` ([#821](https://github.com/energy-models/mathspec/pull/821)) - feat(language)!: a construct that reads the order of a dimension needs the dimension declared ordered ([#793](https://github.com/energy-models/mathspec/pull/793)) - docs(pypsa): the pypsa reference targets master at 51986084, where the eight rungs that diverged now match ([#844](https://github.com/energy-models/mathspec/pull/844)) - feat(language)!: a call names the columns of a relation as relation[column], and a sum through a relation is a sum over the axes its join opens ([#664](https://github.com/energy-models/mathspec/pull/664)) diff --git a/docs/examples/pypsa.md b/docs/examples/pypsa.md index 33aa6c9a..dedac4e2 100644 --- a/docs/examples/pypsa.md +++ b/docs/examples/pypsa.md @@ -5752,6 +5752,27 @@ A plain `n.optimize()`, and its multi-period and stochastic classes, in one file | $`\mathit{Transformer\_angle\_sum}`$ | `Transformer_angle_sum` over $`\Xi \times \mathcal{T} \times \mathcal{C}`$ | | $`\mathit{Cycle\_angle\_sum}`$ | `Cycle_angle_sum` over $`\Xi \times \mathcal{T} \times \mathcal{C}`$ — the voltage angle differences around a cycle: every branch flow times its cycle weight, and every transformer phase shift | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{\mathrm{com,ext}}`$ | `Generator_com_ext` over $`\mathcal{T} \times \mathcal{G}`$ — a committable generator with an extendable, non-modular build that stands in the snapshot's period — PyPSA's `com-ext` rows, whose status is relaxed against the chosen build | +| $`\mathrm{on}^{\mathrm{mnt,ext}}`$ | `Generator_maint_ext` over $`\mathcal{T} \times \mathcal{G}`$ — a maintainable generator with an extendable build, unless it is committable and modular, that stands in the snapshot's period — the maintenance rows against the chosen build | +| $`\mathrm{on}^{\mathrm{com}}`$ | `Generator_committed` over $`\mathcal{T} \times \mathcal{G}`$ — a committable generator that stands in the snapshot's period — every unit-commitment row's set | +| $`\mathrm{prev}`$ | `Generator_ramps_from_previous` over $`\Xi \times \mathcal{T} \times \mathcal{G}`$ — a snapshot whose ramp reads a previous output — any snapshot but its period's first, and the horizon's first where the generator comes in off or carries an initial output | +| $`\mathrm{on}^{f,\mathrm{com,ext}}`$ | `Link_com_ext` over $`\mathcal{T} \times \mathcal{L}`$ — a committable link with an extendable, non-modular build that stands in the snapshot's period — PyPSA's `com-ext` rows, whose status is relaxed against the chosen build | +| $`\mathrm{on}^{f,\mathrm{mnt,ext}}`$ | `Link_maint_ext` over $`\mathcal{T} \times \mathcal{L}`$ — a maintainable link with an extendable build, unless it is committable and modular, that stands in the snapshot's period — the maintenance rows against the chosen build | +| $`\mathrm{on}^{f,\mathrm{com}}`$ | `Link_committed` over $`\mathcal{T} \times \mathcal{L}`$ — a committable link that stands in the snapshot's period — every unit-commitment row's set | +| $`\mathrm{prev}^{f}`$ | `Link_ramps_from_previous` over $`\Xi \times \mathcal{T} \times \mathcal{L}`$ — a snapshot whose ramp reads a previous output — any snapshot but its period's first, and the horizon's first where the link comes in off or carries an initial output | +| $`\mathrm{on}^{z,\mathrm{com,ext}}`$ | `Process_com_ext` over $`\mathcal{T} \times \mathcal{J}`$ — a committable process with an extendable, non-modular build that stands in the snapshot's period — PyPSA's `com-ext` rows, whose status is relaxed against the chosen build | +| $`\mathrm{on}^{z,\mathrm{mnt,ext}}`$ | `Process_maint_ext` over $`\mathcal{T} \times \mathcal{J}`$ — a maintainable process with an extendable build, unless it is committable and modular, that stands in the snapshot's period — the maintenance rows against the chosen build | +| $`\mathrm{on}^{z,\mathrm{com}}`$ | `Process_committed` over $`\mathcal{T} \times \mathcal{J}`$ — a committable process that stands in the snapshot's period — every unit-commitment row's set | +| $`\mathrm{prev}^{z}`$ | `Process_ramps_from_previous` over $`\Xi \times \mathcal{T} \times \mathcal{J}`$ — a snapshot whose ramp reads a previous output — any snapshot but its period's first, and the horizon's first where the process comes in off or carries an initial output | +| $`\mathrm{on}^{h,\mathrm{fix}}`$ | `StorageUnit_fix` over $`\mathcal{T} \times \mathcal{S}`$ — a storage unit with a fixed build that stands in the snapshot's period — PyPSA's `fix` rows | +| $`\mathrm{on}^{h,\mathrm{ext}}`$ | `StorageUnit_ext` over $`\mathcal{T} \times \mathcal{S}`$ — a storage unit with an extendable build that stands in the snapshot's period — PyPSA's `ext` rows | +| $`\mathrm{on}^{s,\mathrm{lossy}}`$ | `Line_lossy` over $`\mathcal{T} \times \mathcal{K}`$ — a line that stands in the snapshot's period, where the run models transmission losses | +| $`\mathrm{on}^{\sigma,\mathrm{lossy}}`$ | `Transformer_lossy` over $`\mathcal{T} \times \mathcal{M}`$ — a transformer that stands in the snapshot's period, where the run models transmission losses | + Upright is what the data supplies — a parameter such as $`\mathrm{Transformer\_phase\_shift\_varying}`$, a coordinate map, a label — and italic is what the solver chooses, such as $`\mathit{Transformer\_phase\_shift}`$. An index is italic too, being what a quantifier chooses, and a set is script. $`t \ominus k`$ denotes cyclic translation: index $`t-k`$ taken modulo the size of the dimension (`roll`). Plain $`t-k`$ (`shift`) has no wraparound — terms translated past the edge are simply absent. @@ -6154,12 +6175,12 @@ Z_{j} = \mathrm{z}^{\mathrm{nom,set}}_{j} \qquad \forall\, j \in \mathcal{J} \,: StorageUnit_fix_p_dispatch_lower: description: "`StorageUnit-fix-p_dispatch-lower` — dispatch is non-negative" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_dispatch >= 0 ``` ```math -h^{+}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{+}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{fix}}_{t,s} ``` ### `StorageUnit-fix-p_dispatch-upper` @@ -6170,12 +6191,12 @@ h^{+}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \ StorageUnit_fix_p_dispatch_upper: description: "`StorageUnit-fix-p_dispatch-upper` — a fixed unit dispatches at most its nominal power" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_dispatch <= StorageUnit_p_max_pu * StorageUnit_p_nom ``` ```math -h^{+}_{\xi,t,s} \le \overline{\mathrm{h}}_{\xi,t,s} \cdot \mathrm{h}^{\mathrm{nom}}_{\xi,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{+}_{\xi,t,s} \le \overline{\mathrm{h}}_{\xi,t,s} \cdot \mathrm{h}^{\mathrm{nom}}_{\xi,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{fix}}_{t,s} ``` ### `StorageUnit-fix-p_store-lower` @@ -6186,12 +6207,12 @@ h^{+}_{\xi,t,s} \le \overline{\mathrm{h}}_{\xi,t,s} \cdot \mathrm{h}^{\mathrm{no StorageUnit_fix_p_store_lower: description: "`StorageUnit-fix-p_store-lower` — storing is non-negative" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_store >= 0 ``` ```math -h^{-}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{-}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{fix}}_{t,s} ``` ### `StorageUnit-fix-p_store-upper` @@ -6204,12 +6225,12 @@ StorageUnit_fix_p_store_upper: `StorageUnit-fix-p_store-upper` — a fixed unit stores at most its nominal power, the minimum-per-unit column carrying that cap negated dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_store <= -StorageUnit_p_min_pu * StorageUnit_p_nom ``` ```math -h^{-}_{\xi,t,s} \le -\underline{\mathrm{h}}_{\xi,t,s} \cdot \mathrm{h}^{\mathrm{nom}}_{\xi,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{-}_{\xi,t,s} \le -\underline{\mathrm{h}}_{\xi,t,s} \cdot \mathrm{h}^{\mathrm{nom}}_{\xi,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{fix}}_{t,s} ``` ### `StorageUnit-fix-state_of_charge-lower` @@ -6220,12 +6241,12 @@ h^{-}_{\xi,t,s} \le -\underline{\mathrm{h}}_{\xi,t,s} \cdot \mathrm{h}^{\mathrm{ StorageUnit_fix_state_of_charge_lower: description: "`StorageUnit-fix-state_of_charge-lower` — charge is non-negative" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_state_of_charge >= 0 ``` ```math -\mathit{soc}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +\mathit{soc}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{fix}}_{t,s} ``` ### `StorageUnit-fix-state_of_charge-upper` @@ -6236,12 +6257,12 @@ StorageUnit_fix_state_of_charge_lower: StorageUnit_fix_state_of_charge_upper: description: "`StorageUnit-fix-state_of_charge-upper` — a fixed unit holds at most its hours at nominal power" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_state_of_charge <= StorageUnit_max_hours * StorageUnit_p_nom ``` ```math -\mathit{soc}_{\xi,t,s} \le \mathrm{T}^{h}_{\xi,s} \cdot \mathrm{h}^{\mathrm{nom}}_{\xi,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +\mathit{soc}_{\xi,t,s} \le \mathrm{T}^{h}_{\xi,s} \cdot \mathrm{h}^{\mathrm{nom}}_{\xi,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{fix}}_{t,s} ``` ### `Generator-com-p-lower` @@ -6252,12 +6273,12 @@ StorageUnit_fix_state_of_charge_upper: Generator_com_p_lower: description: "`Generator-com-p-lower` — a committed unit outputs at least its minimum; off, at least nothing" dims: [scenario, snapshot, generator] - where: Generator_committable AND not Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_p >= Generator_p_min_pu * Generator_p_nom * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) ``` ```math -p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{\xi,g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{\xi,g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` ### `Generator-com-p-upper` @@ -6268,12 +6289,12 @@ p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{nom}} Generator_com_p_upper: description: "`Generator-com-p-upper` — a committed unit outputs at most what is available; off, at most nothing" dims: [scenario, snapshot, generator] - where: Generator_committable AND not Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_p <= Generator_p_max_pu * Generator_p_nom * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) ``` ```math -p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{\xi,g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{\xi,g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` ### `Generator-com-transition-start-up` @@ -6284,12 +6305,12 @@ p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{nom}}_ 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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed expression: Generator_start_up >= Generator_status - Generator_previous_status ``` ```math -\mathit{up}_{\xi,t,g} \ge u_{\xi,t,g} - \overleftarrow{u}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +\mathit{up}_{\xi,t,g} \ge u_{\xi,t,g} - \overleftarrow{u}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` ### `Generator-com-transition-shut-down` @@ -6300,12 +6321,12 @@ Generator_com_transition_start_up: 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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed expression: Generator_shut_down >= Generator_previous_status - Generator_status ``` ```math -\mathit{dn}_{\xi,t,g} \ge \overleftarrow{u}_{\xi,t,g} - u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +\mathit{dn}_{\xi,t,g} \ge \overleftarrow{u}_{\xi,t,g} - u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` ### `Generator-com-up-time` @@ -6319,12 +6340,12 @@ Generator_com_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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_min_up_time > 0 AND position(snapshot) > 0 AND Generator_active + where: Generator_committed 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 ``` ```math -\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}} \mathit{up}_{\xi,t',g} \le u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{UT}_{\xi,g} > 0 \wedge \mathrm{pos}(t) > 0 \wedge \mathrm{on}_{t,g} +\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}} \mathit{up}_{\xi,t',g} \le u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{UT}_{\xi,g} > 0 \wedge \mathrm{pos}(t) > 0 ``` ### `Generator-com-down-time` @@ -6338,12 +6359,12 @@ Generator_com_down_time: time is still off. The first snapshot's share of the window is the brought-in down time's, which the must-stay-down mask carries dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_min_down_time > 0 AND position(snapshot) > 0 AND Generator_active + where: Generator_committed 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 ``` ```math -\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}} \mathit{dn}_{\xi,t',g} \le 1 - u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{DT}_{\xi,g} > 0 \wedge \mathrm{pos}(t) > 0 \wedge \mathrm{on}_{t,g} +\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}} \mathit{dn}_{\xi,t',g} \le 1 - u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{DT}_{\xi,g} > 0 \wedge \mathrm{pos}(t) > 0 ``` ### `Generator-com-status-min_up_time_must_stay_up` @@ -6354,12 +6375,12 @@ Generator_com_down_time: 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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_must_stay_up AND Generator_active + where: Generator_committed AND Generator_must_stay_up expression: Generator_status == 1 ``` ```math -u_{\xi,t,g} = 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{hold}_{\xi,t,g} \wedge \mathrm{on}_{t,g} +u_{\xi,t,g} = 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{hold}_{\xi,t,g} ``` ### `Generator-com-status-min_down_time_must_stay_up` @@ -6372,12 +6393,12 @@ Generator_com_status_must_stay_down: `Generator-com-status-min_down_time_must_stay_up` — a unit still serving the down time it brought in stays off; PyPSA names the row `_must_stay_up` dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_must_stay_down AND Generator_active + where: Generator_committed AND Generator_must_stay_down expression: Generator_status == 0 ``` ```math -u_{\xi,t,g} = 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{rest}_{\xi,t,g} \wedge \mathrm{on}_{t,g} +u_{\xi,t,g} = 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{rest}_{\xi,t,g} ``` ### `Generator-p-ramp_limit_up-run-bigM` @@ -6392,10 +6413,9 @@ Generator_p_ramp_limit_up_run_big_m: releases the row in the snapshot it turns on dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_up OR Generator_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_p - Generator_previous_p <= Generator_ramp_up_rate * Generator_p_nom_ext @@ -6403,7 +6423,7 @@ Generator_p_ramp_limit_up_run_big_m: ``` ```math -p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \widetilde{\mathrm{ru}}_{\xi,t,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot \overleftarrow{u}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \left( \mathrm{ru}_{\xi,t,g} \text{ is defined} \vee \mathrm{ru}^{\mathrm{up}}_{\xi,g} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \widetilde{\mathrm{ru}}_{\xi,t,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot \overleftarrow{u}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} \wedge \left( \mathrm{ru}_{\xi,t,g} \text{ is defined} \vee \mathrm{ru}^{\mathrm{up}}_{\xi,g} \text{ is defined} \right) \wedge \mathrm{prev}_{\xi,t,g} ``` ### `Generator-p-ramp_limit_up-start-bigM` @@ -6418,10 +6438,9 @@ Generator_p_ramp_limit_up_start_big_m: the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_up OR Generator_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_p - Generator_previous_p <= Generator_start_up_rate * Generator_p_nom_ext @@ -6429,7 +6448,7 @@ Generator_p_ramp_limit_up_start_big_m: ``` ```math -p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \widetilde{\mathrm{ru}}^{\mathrm{up}}_{\xi,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot \mathit{up}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \left( \mathrm{ru}_{\xi,t,g} \text{ is defined} \vee \mathrm{ru}^{\mathrm{up}}_{\xi,g} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \widetilde{\mathrm{ru}}^{\mathrm{up}}_{\xi,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot \mathit{up}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} \wedge \left( \mathrm{ru}_{\xi,t,g} \text{ is defined} \vee \mathrm{ru}^{\mathrm{up}}_{\xi,g} \text{ is defined} \right) \wedge \mathrm{prev}_{\xi,t,g} ``` ### `Generator-p-ramp_limit_down-run-bigM` @@ -6444,10 +6463,9 @@ Generator_p_ramp_limit_down_run_big_m: releases the row in the snapshot it turns off dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_down OR Generator_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_previous_p - Generator_p <= Generator_ramp_down_rate * Generator_p_nom_ext @@ -6455,7 +6473,7 @@ Generator_p_ramp_limit_down_run_big_m: ``` ```math -\overleftarrow{p}_{\xi,t,g} - p_{\xi,t,g} \le \widetilde{\mathrm{rd}}_{\xi,t,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \left( \mathrm{rd}_{\xi,t,g} \text{ is defined} \vee \mathrm{rd}^{\mathrm{dn}}_{\xi,g} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \right) \wedge \mathrm{on}_{t,g} +\overleftarrow{p}_{\xi,t,g} - p_{\xi,t,g} \le \widetilde{\mathrm{rd}}_{\xi,t,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} \wedge \left( \mathrm{rd}_{\xi,t,g} \text{ is defined} \vee \mathrm{rd}^{\mathrm{dn}}_{\xi,g} \text{ is defined} \right) \wedge \mathrm{prev}_{\xi,t,g} ``` ### `Generator-p-ramp_limit_down-shut-bigM` @@ -6470,10 +6488,9 @@ Generator_p_ramp_limit_down_shut_big_m: the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_down OR Generator_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_previous_p - Generator_p <= Generator_shut_down_rate * Generator_p_nom_ext @@ -6481,7 +6498,7 @@ Generator_p_ramp_limit_down_shut_big_m: ``` ```math -\overleftarrow{p}_{\xi,t,g} - p_{\xi,t,g} \le \widetilde{\mathrm{rd}}^{\mathrm{dn}}_{\xi,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot \mathit{dn}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \left( \mathrm{rd}_{\xi,t,g} \text{ is defined} \vee \mathrm{rd}^{\mathrm{dn}}_{\xi,g} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \right) \wedge \mathrm{on}_{t,g} +\overleftarrow{p}_{\xi,t,g} - p_{\xi,t,g} \le \widetilde{\mathrm{rd}}^{\mathrm{dn}}_{\xi,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot \mathit{dn}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} \wedge \left( \mathrm{rd}_{\xi,t,g} \text{ is defined} \vee \mathrm{rd}^{\mathrm{dn}}_{\xi,g} \text{ is defined} \right) \wedge \mathrm{prev}_{\xi,t,g} ``` ### `Generator-p_nom_modularity` @@ -6510,12 +6527,12 @@ Generator_com_ext_p_upper_cap: `Generator-com-ext-p-upper-cap` — a committed extendable unit outputs at most what is available of the chosen build, whatever its status dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext expression: Generator_p <= Generator_p_max_pu * (Generator_p_nom_ext - Generator_maintenance_pu * Generator_maintenance_capacity) ``` ```math -p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \left( P_{g} - \gamma_{\xi,g} \cdot \mu^{\mathrm{nom}}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \left( P_{g} - \gamma_{\xi,g} \cdot \mu^{\mathrm{nom}}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} ``` ### `Generator-com-ext-p-upper-bigM` @@ -6526,12 +6543,12 @@ p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \left( P_{g} - \gamma_{\xi Generator_com_ext_p_upper_big_m: description: "`Generator-com-ext-p-upper-bigM` — off, a unit outputs nothing; on, the big M is no bound" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext expression: Generator_p <= Generator_big_m * Generator_status ``` ```math -p_{\xi,t,g} \le \mathrm{M}_{\xi,g} \cdot u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \le \mathrm{M}_{\xi,g} \cdot u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} ``` ### `Generator-com-ext-p-lower` @@ -6544,7 +6561,7 @@ Generator_com_ext_p_lower: `Generator-com-ext-p-lower` — a committed extendable unit outputs at least its minimum of the chosen build; off, the big M releases the row dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext expression: >- Generator_p >= Generator_p_min_pu * (Generator_p_nom_ext - Generator_maintenance_pu * Generator_maintenance_capacity) @@ -6552,7 +6569,7 @@ Generator_com_ext_p_lower: ``` ```math -p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \left( P_{g} - \gamma_{\xi,g} \cdot \mu^{\mathrm{nom}}_{\xi,t,g} \right) + \mathrm{M}_{\xi,g} \cdot u_{\xi,t,g} - \mathrm{M}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \left( P_{g} - \gamma_{\xi,g} \cdot \mu^{\mathrm{nom}}_{\xi,t,g} \right) + \mathrm{M}_{\xi,g} \cdot u_{\xi,t,g} - \mathrm{M}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} ``` ### `Generator-com-ext-p-lower-nonneg` @@ -6566,14 +6583,12 @@ Generator_com_ext_p_lower_nonneg: negative, output is also plainly non-negative, a row the big-M lower cannot assert while the unit is off dims: [scenario, snapshot, generator] - where: >- - Generator_committable AND Generator_p_nom_extendable - AND Generator_p_min_pu_nonneg AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext AND Generator_p_min_pu_nonneg expression: Generator_p >= 0 ``` ```math -p_{\xi,t,g} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \mathrm{nonneg}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} \wedge \mathrm{nonneg}_{g} ``` ### `Generator-com-mod-p-lower` @@ -6586,12 +6601,12 @@ Generator_com_mod_p_lower: `Generator-com-mod-p-lower` — a committed modular unit outputs at least its minimum of one module, whether the build is fixed or a decision dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_p >= Generator_p_min_pu * Generator_p_nom_mod * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) ``` ```math -p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{mod}}_{g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{mod}}_{g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` ### `Generator-com-mod-p-upper` @@ -6604,12 +6619,12 @@ Generator_com_mod_p_upper: `Generator-com-mod-p-upper` — a committed modular unit outputs at most one module's share, whether the build is fixed or a decision dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_p <= Generator_p_max_pu * Generator_p_nom_mod * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) ``` ```math -p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{mod}}_{g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{mod}}_{g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` ### `Generator-status-p-fixed-upper` @@ -6623,12 +6638,12 @@ Generator_status_p_fixed_upper: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, generator] - where: Generator_committable AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) expression: Generator_status <= Generator_modules_installed ``` ```math -u_{\xi,t,g} \le \mathrm{N}^{\mathrm{fix}}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +u_{\xi,t,g} \le \mathrm{N}^{\mathrm{fix}}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` ### `Generator-start_up-p-fixed-upper` @@ -6642,12 +6657,12 @@ Generator_start_up_p_fixed_upper: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, generator] - where: Generator_committable AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) expression: Generator_start_up <= Generator_modules_installed ``` ```math -\mathit{up}_{\xi,t,g} \le \mathrm{N}^{\mathrm{fix}}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mathit{up}_{\xi,t,g} \le \mathrm{N}^{\mathrm{fix}}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` ### `Generator-shut_down-p-fixed-upper` @@ -6661,12 +6676,12 @@ Generator_shut_down_p_fixed_upper: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, generator] - where: Generator_committable AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) expression: Generator_shut_down <= Generator_modules_installed ``` ```math -\mathit{dn}_{\xi,t,g} \le \mathrm{N}^{\mathrm{fix}}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mathit{dn}_{\xi,t,g} \le \mathrm{N}^{\mathrm{fix}}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` ### `Generator-status-p_nom-variable-upper` @@ -6677,12 +6692,12 @@ Generator_shut_down_p_fixed_upper: Generator_status_p_nom_variable_upper: description: "`Generator-status-p_nom-variable-upper` — a modular unit is on only where a module is built" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 expression: Generator_status <= Generator_n_mod ``` ```math -u_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +u_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` ### `Generator-start_up-p_nom-variable-upper` @@ -6693,12 +6708,12 @@ u_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \ Generator_start_up_p_nom_variable_upper: description: "`Generator-start_up-p_nom-variable-upper` — a modular unit starts only where a module is built" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 expression: Generator_start_up <= Generator_n_mod ``` ```math -\mathit{up}_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +\mathit{up}_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` ### `Generator-shut_down-p_nom-variable-upper` @@ -6709,12 +6724,12 @@ Generator_start_up_p_nom_variable_upper: Generator_shut_down_p_nom_variable_upper: description: "`Generator-shut_down-p_nom-variable-upper` — a modular unit stops only where a module is built" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 expression: Generator_shut_down <= Generator_n_mod ``` ```math -\mathit{dn}_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +\mathit{dn}_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` ### `Generator-maint-event-count` @@ -6778,12 +6793,12 @@ Generator_maintcap_upper: `Generator-maintcap_upper` — the build taken off is at most the chosen build in maintenance, and at most the build less its floor out of it dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext expression: Generator_maintenance_capacity <= Generator_p_nom_ext - Generator_p_nom_min * (1 - Generator_maintenance) ``` ```math -\mu^{\mathrm{nom}}_{\xi,t,g} \le P_{g} - \underline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \left( 1 - \mu_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{\mathrm{nom}}_{\xi,t,g} \le P_{g} - \underline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \left( 1 - \mu_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{mnt,ext}}_{t,g} ``` ### `Generator-maintcap_upper_nommax` @@ -6794,12 +6809,12 @@ Generator_maintcap_upper: Generator_maintcap_upper_nommax: description: "`Generator-maintcap_upper_nommax` — out of maintenance, no build is taken off" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext expression: Generator_maintenance_capacity <= Generator_p_nom_max * Generator_maintenance ``` ```math -\mu^{\mathrm{nom}}_{\xi,t,g} \le \overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{\mathrm{nom}}_{\xi,t,g} \le \overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{mnt,ext}}_{t,g} ``` ### `Generator-maintcap_lower_nommax` @@ -6810,12 +6825,12 @@ Generator_maintcap_upper_nommax: Generator_maintcap_lower_nommax: description: "`Generator-maintcap_lower_nommax` — in maintenance, the whole chosen build is taken off" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext expression: Generator_maintenance_capacity >= Generator_p_nom_ext - Generator_p_nom_max * (1 - Generator_maintenance) ``` ```math -\mu^{\mathrm{nom}}_{\xi,t,g} \ge P_{g} - \overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \left( 1 - \mu_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{\mathrm{nom}}_{\xi,t,g} \ge P_{g} - \overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \left( 1 - \mu_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{mnt,ext}}_{t,g} ``` ### `Generator-maintcap_lower_nommin` @@ -6826,12 +6841,12 @@ Generator_maintcap_lower_nommax: Generator_maintcap_lower_nommin: description: "`Generator-maintcap_lower_nommin` — in maintenance, at least the floor of the build is taken off" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active AND Generator_p_nom_min > 0 + where: Generator_maint_ext AND Generator_p_nom_min > 0 expression: Generator_maintenance_capacity >= Generator_p_nom_min * Generator_maintenance ``` ```math -\mu^{\mathrm{nom}}_{\xi,t,g} \ge \underline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} \wedge \underline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} > 0 +\mu^{\mathrm{nom}}_{\xi,t,g} \ge \underline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{mnt,ext}}_{t,g} \wedge \underline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} > 0 ``` ### `Generator-maint-status-le-status` @@ -6842,12 +6857,12 @@ Generator_maintcap_lower_nommin: Generator_maint_status_le_status: description: "`Generator-maint-status-le-status` — the status in maintenance is at most the status" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maintainable AND Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_maintenance_status <= Generator_status ``` ```math -\mu^{u}_{\xi,t,g} \le u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \le u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` ### `Generator-maint-status-le-maint` @@ -6858,12 +6873,12 @@ Generator_maint_status_le_status: Generator_maint_status_le_maint: description: "`Generator-maint-status-le-maint` — out of maintenance, the status in maintenance is zero" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maintainable AND Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_maintenance_status <= Generator_maintenance ``` ```math -\mu^{u}_{\xi,t,g} \le \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \le \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` ### `Generator-maint-status-lb` @@ -6874,12 +6889,12 @@ Generator_maint_status_le_maint: Generator_maint_status_lb: description: "`Generator-maint-status-lb` — on and in maintenance, the status in maintenance is one" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maintainable AND Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_maintenance_status >= Generator_status + Generator_maintenance - 1 ``` ```math -\mu^{u}_{\xi,t,g} \ge u_{\xi,t,g} + \mu_{\xi,t,g} - 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \ge u_{\xi,t,g} + \mu_{\xi,t,g} - 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` ### `Generator-maint-modstatus-le-status` @@ -6890,12 +6905,12 @@ Generator_maint_status_lb: Generator_maint_modstatus_le_status: description: "`Generator-maint-modstatus-le-status` — the modules on in maintenance are at most the modules on" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_maintainable AND Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_maintenance_status <= Generator_status ``` ```math -\mu^{u}_{\xi,t,g} \le u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \le u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` ### `Generator-maint-modstatus-le-maint` @@ -6908,12 +6923,12 @@ Generator_maint_modstatus_le_maint: `Generator-maint-modstatus-le-maint` — out of maintenance, no module is on in maintenance; in it, at most the modules the build cap holds dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_maintainable AND Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_maintenance_status <= Generator_p_nom_max / Generator_p_nom_mod * Generator_maintenance ``` ```math -\mu^{u}_{\xi,t,g} \le \frac{\overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g}}{\mathrm{p}^{\mathrm{mod}}_{g}} \cdot \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \le \frac{\overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g}}{\mathrm{p}^{\mathrm{mod}}_{g}} \cdot \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` ### `Generator-maint-modstatus-lb` @@ -6924,12 +6939,12 @@ Generator_maint_modstatus_le_maint: Generator_maint_modstatus_lb: description: "`Generator-maint-modstatus-lb` — in maintenance, every module on is on in maintenance" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_maintainable AND Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_maintenance_status >= Generator_status - Generator_p_nom_max / Generator_p_nom_mod * (1 - Generator_maintenance) ``` ```math -\mu^{u}_{\xi,t,g} \ge u_{\xi,t,g} - \frac{\overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g}}{\mathrm{p}^{\mathrm{mod}}_{g}} \cdot \left( 1 - \mu_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \ge u_{\xi,t,g} - \frac{\overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g}}{\mathrm{p}^{\mathrm{mod}}_{g}} \cdot \left( 1 - \mu_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` ### `Link-com-p-lower` @@ -6940,12 +6955,12 @@ Generator_maint_modstatus_lb: Link_com_p_lower: description: "`Link-com-p-lower` — a committed link flows at least its minimum; off, at least nothing" dims: [scenario, snapshot, link] - where: Link_committable AND not Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_p >= Link_p_min_pu * Link_p_nom * (Link_status - Link_maintenance_pu * Link_maintenance_status) ``` ```math -f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{nom}}_{\xi,l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{nom}}_{\xi,l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` ### `Link-com-p-upper` @@ -6956,12 +6971,12 @@ f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{nom}} Link_com_p_upper: description: "`Link-com-p-upper` — a committed link flows at most what is available; off, at most nothing" dims: [scenario, snapshot, link] - where: Link_committable AND not Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_p <= Link_p_max_pu * Link_p_nom * (Link_status - Link_maintenance_pu * Link_maintenance_status) ``` ```math -f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{nom}}_{\xi,l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{nom}}_{\xi,l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` ### `Link-com-transition-start-up` @@ -6972,12 +6987,12 @@ f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{nom}}_ Link_com_transition_start_up: description: "`Link-com-transition-start-up` — turning on is a start, counted against the state the link carried into the snapshot" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed expression: Link_start_up >= Link_status - Link_previous_status ``` ```math -\mathit{up}^{f}_{\xi,t,l} \ge u^{f}_{\xi,t,l} - \overleftarrow{u}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +\mathit{up}^{f}_{\xi,t,l} \ge u^{f}_{\xi,t,l} - \overleftarrow{u}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` ### `Link-com-transition-shut-down` @@ -6988,12 +7003,12 @@ Link_com_transition_start_up: Link_com_transition_shut_down: description: "`Link-com-transition-shut-down` — turning off is a stop, counted against the state the link carried into the snapshot" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed expression: Link_shut_down >= Link_previous_status - Link_status ``` ```math -\mathit{dn}^{f}_{\xi,t,l} \ge \overleftarrow{u}^{f}_{\xi,t,l} - u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +\mathit{dn}^{f}_{\xi,t,l} \ge \overleftarrow{u}^{f}_{\xi,t,l} - u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` ### `Link-com-up-time` @@ -7007,12 +7022,12 @@ Link_com_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: [scenario, snapshot, link] - where: Link_committable AND Link_min_up_time > 0 AND position(snapshot) > 0 AND Link_active + where: Link_committed AND Link_min_up_time > 0 AND position(snapshot) > 0 expression: sum_back(Link_start_up, along=snapshot, window=Link_min_up_time) <= Link_status ``` ```math -\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}^{f}} \mathit{up}^{f}_{\xi,t',l} \le u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{UT}^{f}_{\xi,l} > 0 \wedge \mathrm{pos}(t) > 0 \wedge \mathrm{on}^{f}_{t,l} +\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}^{f}} \mathit{up}^{f}_{\xi,t',l} \le u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{UT}^{f}_{\xi,l} > 0 \wedge \mathrm{pos}(t) > 0 ``` ### `Link-com-down-time` @@ -7026,12 +7041,12 @@ Link_com_down_time: time is still off. The first snapshot's share of the window is the brought-in down time's, which the must-stay-down mask carries dims: [scenario, snapshot, link] - where: Link_committable AND Link_min_down_time > 0 AND position(snapshot) > 0 AND Link_active + where: Link_committed AND Link_min_down_time > 0 AND position(snapshot) > 0 expression: sum_back(Link_shut_down, along=snapshot, window=Link_min_down_time) <= 1 - Link_status ``` ```math -\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}^{f}} \mathit{dn}^{f}_{\xi,t',l} \le 1 - u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{DT}^{f}_{\xi,l} > 0 \wedge \mathrm{pos}(t) > 0 \wedge \mathrm{on}^{f}_{t,l} +\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}^{f}} \mathit{dn}^{f}_{\xi,t',l} \le 1 - u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{DT}^{f}_{\xi,l} > 0 \wedge \mathrm{pos}(t) > 0 ``` ### `Link-com-status-min_up_time_must_stay_up` @@ -7042,12 +7057,12 @@ Link_com_down_time: Link_com_status_must_stay_up: description: "`Link-com-status-min_up_time_must_stay_up` — a link still serving the up time it brought in stays on" dims: [scenario, snapshot, link] - where: Link_committable AND Link_must_stay_up AND Link_active + where: Link_committed AND Link_must_stay_up expression: Link_status == 1 ``` ```math -u^{f}_{\xi,t,l} = 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{hold}^{f}_{\xi,t,l} \wedge \mathrm{on}^{f}_{t,l} +u^{f}_{\xi,t,l} = 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{hold}^{f}_{\xi,t,l} ``` ### `Link-com-status-min_down_time_must_stay_up` @@ -7060,12 +7075,12 @@ Link_com_status_must_stay_down: `Link-com-status-min_down_time_must_stay_up` — a link still serving the down time it brought in stays off; PyPSA names the row `_must_stay_up` dims: [scenario, snapshot, link] - where: Link_committable AND Link_must_stay_down AND Link_active + where: Link_committed AND Link_must_stay_down expression: Link_status == 0 ``` ```math -u^{f}_{\xi,t,l} = 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{rest}^{f}_{\xi,t,l} \wedge \mathrm{on}^{f}_{t,l} +u^{f}_{\xi,t,l} = 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{rest}^{f}_{\xi,t,l} ``` ### `Link-p-ramp_limit_up-run-bigM` @@ -7079,11 +7094,7 @@ Link_p_ramp_limit_up_run_big_m: raises flow no faster than its limit of the chosen build; the big M releases the row in the snapshot it turns on dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) AND Link_ramps_from_previous expression: >- Link_p - Link_previous_p <= Link_ramp_up_rate * Link_p_nom_ext @@ -7091,7 +7102,7 @@ Link_p_ramp_limit_up_run_big_m: ``` ```math -f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \widetilde{\mathrm{ru}}^{f}_{\xi,t,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot \overleftarrow{u}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \left( \mathrm{ru}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{ru}^{f,\mathrm{up}}_{\xi,l} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \widetilde{\mathrm{ru}}^{f}_{\xi,t,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot \overleftarrow{u}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} \wedge \left( \mathrm{ru}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{ru}^{f,\mathrm{up}}_{\xi,l} \text{ is defined} \right) \wedge \mathrm{prev}^{f}_{\xi,t,l} ``` ### `Link-p-ramp_limit_up-start-bigM` @@ -7105,11 +7116,7 @@ Link_p_ramp_limit_up_start_big_m: committed extendable link ramps no further than its start-up ramp of the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) AND Link_ramps_from_previous expression: >- Link_p - Link_previous_p <= Link_start_up_rate * Link_p_nom_ext @@ -7117,7 +7124,7 @@ Link_p_ramp_limit_up_start_big_m: ``` ```math -f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \widetilde{\mathrm{ru}}^{f,\mathrm{up}}_{\xi,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot \mathit{up}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \left( \mathrm{ru}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{ru}^{f,\mathrm{up}}_{\xi,l} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \widetilde{\mathrm{ru}}^{f,\mathrm{up}}_{\xi,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot \mathit{up}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} \wedge \left( \mathrm{ru}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{ru}^{f,\mathrm{up}}_{\xi,l} \text{ is defined} \right) \wedge \mathrm{prev}^{f}_{\xi,t,l} ``` ### `Link-p-ramp_limit_down-run-bigM` @@ -7131,11 +7138,7 @@ Link_p_ramp_limit_down_run_big_m: lowers flow no faster than its limit of the chosen build; the big M releases the row in the snapshot it turns off dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) AND Link_ramps_from_previous expression: >- Link_previous_p - Link_p <= Link_ramp_down_rate * Link_p_nom_ext @@ -7143,7 +7146,7 @@ Link_p_ramp_limit_down_run_big_m: ``` ```math -\overleftarrow{f}_{\xi,t,l} - f_{\xi,t,l} \le \widetilde{\mathrm{rd}}^{f}_{\xi,t,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \left( \mathrm{rd}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{rd}^{f,\mathrm{dn}}_{\xi,l} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \right) \wedge \mathrm{on}^{f}_{t,l} +\overleftarrow{f}_{\xi,t,l} - f_{\xi,t,l} \le \widetilde{\mathrm{rd}}^{f}_{\xi,t,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} \wedge \left( \mathrm{rd}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{rd}^{f,\mathrm{dn}}_{\xi,l} \text{ is defined} \right) \wedge \mathrm{prev}^{f}_{\xi,t,l} ``` ### `Link-p-ramp_limit_down-shut-bigM` @@ -7157,11 +7160,7 @@ Link_p_ramp_limit_down_shut_big_m: a committed extendable link ramps no further than its shut-down ramp of the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) AND Link_ramps_from_previous expression: >- Link_previous_p - Link_p <= Link_shut_down_rate * Link_p_nom_ext @@ -7169,7 +7168,7 @@ Link_p_ramp_limit_down_shut_big_m: ``` ```math -\overleftarrow{f}_{\xi,t,l} - f_{\xi,t,l} \le \widetilde{\mathrm{rd}}^{f,\mathrm{dn}}_{\xi,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot \mathit{dn}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \left( \mathrm{rd}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{rd}^{f,\mathrm{dn}}_{\xi,l} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \right) \wedge \mathrm{on}^{f}_{t,l} +\overleftarrow{f}_{\xi,t,l} - f_{\xi,t,l} \le \widetilde{\mathrm{rd}}^{f,\mathrm{dn}}_{\xi,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot \mathit{dn}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} \wedge \left( \mathrm{rd}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{rd}^{f,\mathrm{dn}}_{\xi,l} \text{ is defined} \right) \wedge \mathrm{prev}^{f}_{\xi,t,l} ``` ### `Link-p_nom_modularity` @@ -7198,12 +7197,12 @@ Link_com_ext_p_upper_cap: `Link-com-ext-p-upper-cap` — a committed extendable link flows at most what is available of the chosen build, whatever its status dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext expression: Link_p <= Link_p_max_pu * (Link_p_nom_ext - Link_maintenance_pu * Link_maintenance_capacity) ``` ```math -f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \left( F_{l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,\mathrm{nom}}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \left( F_{l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,\mathrm{nom}}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} ``` ### `Link-com-ext-p-upper-bigM` @@ -7214,12 +7213,12 @@ f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \left( F_{l} - \gamma^{f}_ Link_com_ext_p_upper_big_m: description: "`Link-com-ext-p-upper-bigM` — off, a link flows nothing; on, the big M is no bound" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext expression: Link_p <= Link_big_m * Link_status ``` ```math -f_{\xi,t,l} \le \mathrm{M}^{f}_{\xi,l} \cdot u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \le \mathrm{M}^{f}_{\xi,l} \cdot u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} ``` ### `Link-com-ext-p-lower` @@ -7232,7 +7231,7 @@ Link_com_ext_p_lower: `Link-com-ext-p-lower` — a committed extendable link flows at least its minimum of the chosen build; off, the big M releases the row dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext expression: >- Link_p >= Link_p_min_pu * (Link_p_nom_ext - Link_maintenance_pu * Link_maintenance_capacity) @@ -7240,7 +7239,7 @@ Link_com_ext_p_lower: ``` ```math -f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \left( F_{l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,\mathrm{nom}}_{\xi,t,l} \right) + \mathrm{M}^{f}_{\xi,l} \cdot u^{f}_{\xi,t,l} - \mathrm{M}^{f}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \left( F_{l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,\mathrm{nom}}_{\xi,t,l} \right) + \mathrm{M}^{f}_{\xi,l} \cdot u^{f}_{\xi,t,l} - \mathrm{M}^{f}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} ``` ### `Link-com-ext-p-lower-nonneg` @@ -7254,14 +7253,12 @@ Link_com_ext_p_lower_nonneg: negative, flow is also plainly non-negative, a row the big-M lower cannot assert while the link is off dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable - AND Link_p_min_pu_nonneg AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext AND Link_p_min_pu_nonneg expression: Link_p >= 0 ``` ```math -f_{\xi,t,l} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{nonneg}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} \wedge \mathrm{nonneg}^{f}_{l} ``` ### `Link-com-mod-p-lower` @@ -7274,12 +7271,12 @@ Link_com_mod_p_lower: `Link-com-mod-p-lower` — a committed modular link flows at least its minimum of one module, whether the build is fixed or a decision dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_mod > 0 expression: Link_p >= Link_p_min_pu * Link_p_nom_mod * (Link_status - Link_maintenance_pu * Link_maintenance_status) ``` ```math -f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{mod}}_{l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{mod}}_{l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` ### `Link-com-mod-p-upper` @@ -7292,12 +7289,12 @@ Link_com_mod_p_upper: `Link-com-mod-p-upper` — a committed modular link flows at most one module's share, whether the build is fixed or a decision dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_mod > 0 expression: Link_p <= Link_p_max_pu * Link_p_nom_mod * (Link_status - Link_maintenance_pu * Link_maintenance_status) ``` ```math -f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{mod}}_{l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{mod}}_{l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` ### `Link-status-p-fixed-upper` @@ -7311,12 +7308,12 @@ Link_status_p_fixed_upper: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, link] - where: Link_committable AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) expression: Link_status <= Link_modules_installed ``` ```math -u^{f}_{\xi,t,l} \le \mathrm{N}^{f,\mathrm{fix}}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +u^{f}_{\xi,t,l} \le \mathrm{N}^{f,\mathrm{fix}}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` ### `Link-start_up-p-fixed-upper` @@ -7330,12 +7327,12 @@ Link_start_up_p_fixed_upper: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, link] - where: Link_committable AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) expression: Link_start_up <= Link_modules_installed ``` ```math -\mathit{up}^{f}_{\xi,t,l} \le \mathrm{N}^{f,\mathrm{fix}}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mathit{up}^{f}_{\xi,t,l} \le \mathrm{N}^{f,\mathrm{fix}}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` ### `Link-shut_down-p-fixed-upper` @@ -7349,12 +7346,12 @@ Link_shut_down_p_fixed_upper: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, link] - where: Link_committable AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) expression: Link_shut_down <= Link_modules_installed ``` ```math -\mathit{dn}^{f}_{\xi,t,l} \le \mathrm{N}^{f,\mathrm{fix}}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mathit{dn}^{f}_{\xi,t,l} \le \mathrm{N}^{f,\mathrm{fix}}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` ### `Link-status-p_nom-variable-upper` @@ -7365,12 +7362,12 @@ Link_shut_down_p_fixed_upper: Link_status_p_nom_variable_upper: description: "`Link-status-p_nom-variable-upper` — a modular link is on only where a module is built" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_extendable AND Link_p_nom_mod > 0 expression: Link_status <= Link_n_mod ``` ```math -u^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +u^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` ### `Link-start_up-p_nom-variable-upper` @@ -7381,12 +7378,12 @@ u^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ Link_start_up_p_nom_variable_upper: description: "`Link-start_up-p_nom-variable-upper` — a modular link starts only where a module is built" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_extendable AND Link_p_nom_mod > 0 expression: Link_start_up <= Link_n_mod ``` ```math -\mathit{up}^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +\mathit{up}^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` ### `Link-shut_down-p_nom-variable-upper` @@ -7397,12 +7394,12 @@ Link_start_up_p_nom_variable_upper: Link_shut_down_p_nom_variable_upper: description: "`Link-shut_down-p_nom-variable-upper` — a modular link stops only where a module is built" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_extendable AND Link_p_nom_mod > 0 expression: Link_shut_down <= Link_n_mod ``` ```math -\mathit{dn}^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +\mathit{dn}^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` ### `Link-maint-event-count` @@ -7466,12 +7463,12 @@ Link_maintcap_upper: `Link-maintcap_upper` — the build taken off is at most the chosen build in maintenance, and at most the build less its floor out of it dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext expression: Link_maintenance_capacity <= Link_p_nom_ext - Link_p_nom_min * (1 - Link_maintenance) ``` ```math -\mu^{f,\mathrm{nom}}_{\xi,t,l} \le F_{l} - \underline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \left( 1 - \mu^{f}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,\mathrm{nom}}_{\xi,t,l} \le F_{l} - \underline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \left( 1 - \mu^{f}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{mnt,ext}}_{t,l} ``` ### `Link-maintcap_upper_nommax` @@ -7482,12 +7479,12 @@ Link_maintcap_upper: Link_maintcap_upper_nommax: description: "`Link-maintcap_upper_nommax` — out of maintenance, no build is taken off" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext expression: Link_maintenance_capacity <= Link_p_nom_max * Link_maintenance ``` ```math -\mu^{f,\mathrm{nom}}_{\xi,t,l} \le \overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,\mathrm{nom}}_{\xi,t,l} \le \overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{mnt,ext}}_{t,l} ``` ### `Link-maintcap_lower_nommax` @@ -7498,12 +7495,12 @@ Link_maintcap_upper_nommax: Link_maintcap_lower_nommax: description: "`Link-maintcap_lower_nommax` — in maintenance, the whole chosen build is taken off" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext expression: Link_maintenance_capacity >= Link_p_nom_ext - Link_p_nom_max * (1 - Link_maintenance) ``` ```math -\mu^{f,\mathrm{nom}}_{\xi,t,l} \ge F_{l} - \overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \left( 1 - \mu^{f}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,\mathrm{nom}}_{\xi,t,l} \ge F_{l} - \overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \left( 1 - \mu^{f}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{mnt,ext}}_{t,l} ``` ### `Link-maintcap_lower_nommin` @@ -7514,12 +7511,12 @@ Link_maintcap_lower_nommax: Link_maintcap_lower_nommin: description: "`Link-maintcap_lower_nommin` — in maintenance, at least the floor of the build is taken off" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active AND Link_p_nom_min > 0 + where: Link_maint_ext AND Link_p_nom_min > 0 expression: Link_maintenance_capacity >= Link_p_nom_min * Link_maintenance ``` ```math -\mu^{f,\mathrm{nom}}_{\xi,t,l} \ge \underline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} \wedge \underline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} > 0 +\mu^{f,\mathrm{nom}}_{\xi,t,l} \ge \underline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{mnt,ext}}_{t,l} \wedge \underline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} > 0 ``` ### `Link-maint-status-le-status` @@ -7530,12 +7527,12 @@ Link_maintcap_lower_nommin: Link_maint_status_le_status: description: "`Link-maint-status-le-status` — the status in maintenance is at most the status" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_maintainable AND Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_maintenance_status <= Link_status ``` ```math -\mu^{f,u}_{\xi,t,l} \le u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \le u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` ### `Link-maint-status-le-maint` @@ -7546,12 +7543,12 @@ Link_maint_status_le_status: Link_maint_status_le_maint: description: "`Link-maint-status-le-maint` — out of maintenance, the status in maintenance is zero" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_maintainable AND Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_maintenance_status <= Link_maintenance ``` ```math -\mu^{f,u}_{\xi,t,l} \le \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \le \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` ### `Link-maint-status-lb` @@ -7562,12 +7559,12 @@ Link_maint_status_le_maint: Link_maint_status_lb: description: "`Link-maint-status-lb` — on and in maintenance, the status in maintenance is one" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_maintainable AND Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_maintenance_status >= Link_status + Link_maintenance - 1 ``` ```math -\mu^{f,u}_{\xi,t,l} \ge u^{f}_{\xi,t,l} + \mu^{f}_{\xi,t,l} - 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \ge u^{f}_{\xi,t,l} + \mu^{f}_{\xi,t,l} - 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` ### `Link-maint-modstatus-le-status` @@ -7578,12 +7575,12 @@ Link_maint_status_lb: Link_maint_modstatus_le_status: description: "`Link-maint-modstatus-le-status` — the modules on in maintenance are at most the modules on" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_maintainable AND Link_committed AND Link_p_nom_mod > 0 expression: Link_maintenance_status <= Link_status ``` ```math -\mu^{f,u}_{\xi,t,l} \le u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \le u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` ### `Link-maint-modstatus-le-maint` @@ -7596,12 +7593,12 @@ Link_maint_modstatus_le_maint: `Link-maint-modstatus-le-maint` — out of maintenance, no module is on in maintenance; in it, at most the modules the build cap holds dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_maintainable AND Link_committed AND Link_p_nom_mod > 0 expression: Link_maintenance_status <= Link_p_nom_max / Link_p_nom_mod * Link_maintenance ``` ```math -\mu^{f,u}_{\xi,t,l} \le \frac{\overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l}}{\mathrm{f}^{\mathrm{mod}}_{l}} \cdot \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \le \frac{\overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l}}{\mathrm{f}^{\mathrm{mod}}_{l}} \cdot \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` ### `Link-maint-modstatus-lb` @@ -7612,12 +7609,12 @@ Link_maint_modstatus_le_maint: Link_maint_modstatus_lb: description: "`Link-maint-modstatus-lb` — in maintenance, every module on is on in maintenance" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_maintainable AND Link_committed AND Link_p_nom_mod > 0 expression: Link_maintenance_status >= Link_status - Link_p_nom_max / Link_p_nom_mod * (1 - Link_maintenance) ``` ```math -\mu^{f,u}_{\xi,t,l} \ge u^{f}_{\xi,t,l} - \frac{\overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l}}{\mathrm{f}^{\mathrm{mod}}_{l}} \cdot \left( 1 - \mu^{f}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \ge u^{f}_{\xi,t,l} - \frac{\overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l}}{\mathrm{f}^{\mathrm{mod}}_{l}} \cdot \left( 1 - \mu^{f}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` ### `Process-com-p-lower` @@ -7628,12 +7625,12 @@ Link_maint_modstatus_lb: Process_com_p_lower: description: "`Process-com-p-lower` — a committed process runs at least its minimum; off, at least nothing" dims: [scenario, snapshot, process] - where: Process_committable AND not Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_p >= Process_p_min_pu * Process_p_nom * (Process_status - Process_maintenance_pu * Process_maintenance_status) ``` ```math -z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{nom}}_{\xi,j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{nom}}_{\xi,j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` ### `Process-com-p-upper` @@ -7644,12 +7641,12 @@ z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{nom}} Process_com_p_upper: description: "`Process-com-p-upper` — a committed process runs at most what is available; off, at most nothing" dims: [scenario, snapshot, process] - where: Process_committable AND not Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_p <= Process_p_max_pu * Process_p_nom * (Process_status - Process_maintenance_pu * Process_maintenance_status) ``` ```math -z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{nom}}_{\xi,j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{nom}}_{\xi,j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` ### `Process-com-transition-start-up` @@ -7660,12 +7657,12 @@ z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{nom}}_ Process_com_transition_start_up: description: "`Process-com-transition-start-up` — turning on is a start, counted against the state the process carried into the snapshot" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed expression: Process_start_up >= Process_status - Process_previous_status ``` ```math -\mathit{up}^{z}_{\xi,t,j} \ge u^{z}_{\xi,t,j} - \overleftarrow{u}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +\mathit{up}^{z}_{\xi,t,j} \ge u^{z}_{\xi,t,j} - \overleftarrow{u}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` ### `Process-com-transition-shut-down` @@ -7676,12 +7673,12 @@ Process_com_transition_start_up: Process_com_transition_shut_down: description: "`Process-com-transition-shut-down` — turning off is a stop, counted against the state the process carried into the snapshot" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed expression: Process_shut_down >= Process_previous_status - Process_status ``` ```math -\mathit{dn}^{z}_{\xi,t,j} \ge \overleftarrow{u}^{z}_{\xi,t,j} - u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +\mathit{dn}^{z}_{\xi,t,j} \ge \overleftarrow{u}^{z}_{\xi,t,j} - u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` ### `Process-com-up-time` @@ -7695,12 +7692,12 @@ Process_com_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: [scenario, snapshot, process] - where: Process_committable AND Process_min_up_time > 0 AND position(snapshot) > 0 AND Process_active + where: Process_committed AND Process_min_up_time > 0 AND position(snapshot) > 0 expression: sum_back(Process_start_up, along=snapshot, window=Process_min_up_time) <= Process_status ``` ```math -\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}^{z}} \mathit{up}^{z}_{\xi,t',j} \le u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{UT}^{z}_{\xi,j} > 0 \wedge \mathrm{pos}(t) > 0 \wedge \mathrm{on}^{z}_{t,j} +\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}^{z}} \mathit{up}^{z}_{\xi,t',j} \le u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{UT}^{z}_{\xi,j} > 0 \wedge \mathrm{pos}(t) > 0 ``` ### `Process-com-down-time` @@ -7714,12 +7711,12 @@ Process_com_down_time: time is still off. The first snapshot's share of the window is the brought-in down time's, which the must-stay-down mask carries dims: [scenario, snapshot, process] - where: Process_committable AND Process_min_down_time > 0 AND position(snapshot) > 0 AND Process_active + where: Process_committed AND Process_min_down_time > 0 AND position(snapshot) > 0 expression: sum_back(Process_shut_down, along=snapshot, window=Process_min_down_time) <= 1 - Process_status ``` ```math -\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}^{z}} \mathit{dn}^{z}_{\xi,t',j} \le 1 - u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{DT}^{z}_{\xi,j} > 0 \wedge \mathrm{pos}(t) > 0 \wedge \mathrm{on}^{z}_{t,j} +\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}^{z}} \mathit{dn}^{z}_{\xi,t',j} \le 1 - u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{DT}^{z}_{\xi,j} > 0 \wedge \mathrm{pos}(t) > 0 ``` ### `Process-com-status-min_up_time_must_stay_up` @@ -7730,12 +7727,12 @@ Process_com_down_time: Process_com_status_must_stay_up: description: "`Process-com-status-min_up_time_must_stay_up` — a process still serving the up time it brought in stays on" dims: [scenario, snapshot, process] - where: Process_committable AND Process_must_stay_up AND Process_active + where: Process_committed AND Process_must_stay_up expression: Process_status == 1 ``` ```math -u^{z}_{\xi,t,j} = 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{hold}^{z}_{\xi,t,j} \wedge \mathrm{on}^{z}_{t,j} +u^{z}_{\xi,t,j} = 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{hold}^{z}_{\xi,t,j} ``` ### `Process-com-status-min_down_time_must_stay_up` @@ -7748,12 +7745,12 @@ Process_com_status_must_stay_down: `Process-com-status-min_down_time_must_stay_up` — a process still serving the down time it brought in stays off; PyPSA names the row `_must_stay_up` dims: [scenario, snapshot, process] - where: Process_committable AND Process_must_stay_down AND Process_active + where: Process_committed AND Process_must_stay_down expression: Process_status == 0 ``` ```math -u^{z}_{\xi,t,j} = 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{rest}^{z}_{\xi,t,j} \wedge \mathrm{on}^{z}_{t,j} +u^{z}_{\xi,t,j} = 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{rest}^{z}_{\xi,t,j} ``` ### `Process-p-ramp_limit_up-run-bigM` @@ -7767,11 +7764,7 @@ Process_p_ramp_limit_up_run_big_m: raises internal power no faster than its limit of the chosen build; the big M releases the row in the snapshot it turns on dims: [scenario, snapshot, process] - where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) - AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + where: Process_com_ext AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) AND Process_ramps_from_previous expression: >- Process_p - Process_previous_p <= Process_ramp_up_rate * Process_p_nom_ext @@ -7779,7 +7772,7 @@ Process_p_ramp_limit_up_run_big_m: ``` ```math -z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \widetilde{\mathrm{ru}}^{z}_{\xi,t,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot \overleftarrow{u}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \left( \mathrm{ru}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{ru}^{z,\mathrm{up}}_{\xi,j} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \widetilde{\mathrm{ru}}^{z}_{\xi,t,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot \overleftarrow{u}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} \wedge \left( \mathrm{ru}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{ru}^{z,\mathrm{up}}_{\xi,j} \text{ is defined} \right) \wedge \mathrm{prev}^{z}_{\xi,t,j} ``` ### `Process-p-ramp_limit_up-start-bigM` @@ -7793,11 +7786,7 @@ Process_p_ramp_limit_up_start_big_m: committed extendable process ramps no further than its start-up ramp of the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, process] - where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) - AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + where: Process_com_ext AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) AND Process_ramps_from_previous expression: >- Process_p - Process_previous_p <= Process_start_up_rate * Process_p_nom_ext @@ -7805,7 +7794,7 @@ Process_p_ramp_limit_up_start_big_m: ``` ```math -z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \widetilde{\mathrm{ru}}^{z,\mathrm{up}}_{\xi,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot \mathit{up}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \left( \mathrm{ru}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{ru}^{z,\mathrm{up}}_{\xi,j} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \widetilde{\mathrm{ru}}^{z,\mathrm{up}}_{\xi,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot \mathit{up}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} \wedge \left( \mathrm{ru}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{ru}^{z,\mathrm{up}}_{\xi,j} \text{ is defined} \right) \wedge \mathrm{prev}^{z}_{\xi,t,j} ``` ### `Process-p-ramp_limit_down-run-bigM` @@ -7820,10 +7809,9 @@ Process_p_ramp_limit_down_run_big_m: releases the row in the snapshot it turns off dims: [scenario, snapshot, process] where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) + Process_com_ext AND (Process_ramp_limit_down OR Process_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + AND Process_ramps_from_previous expression: >- Process_previous_p - Process_p <= Process_ramp_down_rate * Process_p_nom_ext @@ -7831,7 +7819,7 @@ Process_p_ramp_limit_down_run_big_m: ``` ```math -\overleftarrow{z}_{\xi,t,j} - z_{\xi,t,j} \le \widetilde{\mathrm{rd}}^{z}_{\xi,t,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \left( \mathrm{rd}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{rd}^{z,\mathrm{dn}}_{\xi,j} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \right) \wedge \mathrm{on}^{z}_{t,j} +\overleftarrow{z}_{\xi,t,j} - z_{\xi,t,j} \le \widetilde{\mathrm{rd}}^{z}_{\xi,t,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} \wedge \left( \mathrm{rd}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{rd}^{z,\mathrm{dn}}_{\xi,j} \text{ is defined} \right) \wedge \mathrm{prev}^{z}_{\xi,t,j} ``` ### `Process-p-ramp_limit_down-shut-bigM` @@ -7846,10 +7834,9 @@ Process_p_ramp_limit_down_shut_big_m: the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, process] where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) + Process_com_ext AND (Process_ramp_limit_down OR Process_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + AND Process_ramps_from_previous expression: >- Process_previous_p - Process_p <= Process_shut_down_rate * Process_p_nom_ext @@ -7857,7 +7844,7 @@ Process_p_ramp_limit_down_shut_big_m: ``` ```math -\overleftarrow{z}_{\xi,t,j} - z_{\xi,t,j} \le \widetilde{\mathrm{rd}}^{z,\mathrm{dn}}_{\xi,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot \mathit{dn}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \left( \mathrm{rd}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{rd}^{z,\mathrm{dn}}_{\xi,j} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \right) \wedge \mathrm{on}^{z}_{t,j} +\overleftarrow{z}_{\xi,t,j} - z_{\xi,t,j} \le \widetilde{\mathrm{rd}}^{z,\mathrm{dn}}_{\xi,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot \mathit{dn}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} \wedge \left( \mathrm{rd}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{rd}^{z,\mathrm{dn}}_{\xi,j} \text{ is defined} \right) \wedge \mathrm{prev}^{z}_{\xi,t,j} ``` ### `Process-p_nom_modularity` @@ -7886,12 +7873,12 @@ Process_com_ext_p_upper_cap: `Process-com-ext-p-upper-cap` — a committed extendable process runs at most what is available of the chosen build, whatever its status dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext expression: Process_p <= Process_p_max_pu * (Process_p_nom_ext - Process_maintenance_pu * Process_maintenance_capacity) ``` ```math -z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \left( Z_{j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,\mathrm{nom}}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \left( Z_{j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,\mathrm{nom}}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} ``` ### `Process-com-ext-p-upper-bigM` @@ -7902,12 +7889,12 @@ z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \left( Z_{j} - \gamma^{z}_ Process_com_ext_p_upper_big_m: description: "`Process-com-ext-p-upper-bigM` — off, a process does not run; on, the big M is no bound" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext expression: Process_p <= Process_big_m * Process_status ``` ```math -z_{\xi,t,j} \le \mathrm{M}^{z}_{\xi,j} \cdot u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \le \mathrm{M}^{z}_{\xi,j} \cdot u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} ``` ### `Process-com-ext-p-lower` @@ -7920,7 +7907,7 @@ Process_com_ext_p_lower: `Process-com-ext-p-lower` — a committed extendable process runs at least its minimum of the chosen build; off, the big M releases the row dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext expression: >- Process_p >= Process_p_min_pu * (Process_p_nom_ext - Process_maintenance_pu * Process_maintenance_capacity) @@ -7928,7 +7915,7 @@ Process_com_ext_p_lower: ``` ```math -z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \left( Z_{j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,\mathrm{nom}}_{\xi,t,j} \right) + \mathrm{M}^{z}_{\xi,j} \cdot u^{z}_{\xi,t,j} - \mathrm{M}^{z}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \left( Z_{j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,\mathrm{nom}}_{\xi,t,j} \right) + \mathrm{M}^{z}_{\xi,j} \cdot u^{z}_{\xi,t,j} - \mathrm{M}^{z}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} ``` ### `Process-com-ext-p-lower-nonneg` @@ -7942,14 +7929,12 @@ Process_com_ext_p_lower_nonneg: negative, internal power is also plainly non-negative, a row the big-M lower cannot assert while the process is off dims: [scenario, snapshot, process] - where: >- - Process_committable AND Process_p_nom_extendable - AND Process_p_min_pu_nonneg AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext AND Process_p_min_pu_nonneg expression: Process_p >= 0 ``` ```math -z_{\xi,t,j} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{nonneg}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} \wedge \mathrm{nonneg}^{z}_{j} ``` ### `Process-com-mod-p-lower` @@ -7962,12 +7947,12 @@ Process_com_mod_p_lower: `Process-com-mod-p-lower` — a committed modular process runs at least its minimum of one module, whether the build is fixed or a decision dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_mod > 0 expression: Process_p >= Process_p_min_pu * Process_p_nom_mod * (Process_status - Process_maintenance_pu * Process_maintenance_status) ``` ```math -z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{mod}}_{j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{mod}}_{j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` ### `Process-com-mod-p-upper` @@ -7980,12 +7965,12 @@ Process_com_mod_p_upper: `Process-com-mod-p-upper` — a committed modular process runs at most one module's share, whether the build is fixed or a decision dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_mod > 0 expression: Process_p <= Process_p_max_pu * Process_p_nom_mod * (Process_status - Process_maintenance_pu * Process_maintenance_status) ``` ```math -z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{mod}}_{j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{mod}}_{j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` ### `Process-status-p-fixed-upper` @@ -7999,12 +7984,12 @@ Process_status_p_fixed_upper: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, process] - where: Process_committable AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) expression: Process_status <= Process_modules_installed ``` ```math -u^{z}_{\xi,t,j} \le \mathrm{N}^{z,\mathrm{fix}}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +u^{z}_{\xi,t,j} \le \mathrm{N}^{z,\mathrm{fix}}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` ### `Process-start_up-p-fixed-upper` @@ -8018,12 +8003,12 @@ Process_start_up_p_fixed_upper: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, process] - where: Process_committable AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) expression: Process_start_up <= Process_modules_installed ``` ```math -\mathit{up}^{z}_{\xi,t,j} \le \mathrm{N}^{z,\mathrm{fix}}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mathit{up}^{z}_{\xi,t,j} \le \mathrm{N}^{z,\mathrm{fix}}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` ### `Process-shut_down-p-fixed-upper` @@ -8037,12 +8022,12 @@ Process_shut_down_p_fixed_upper: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, process] - where: Process_committable AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) expression: Process_shut_down <= Process_modules_installed ``` ```math -\mathit{dn}^{z}_{\xi,t,j} \le \mathrm{N}^{z,\mathrm{fix}}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mathit{dn}^{z}_{\xi,t,j} \le \mathrm{N}^{z,\mathrm{fix}}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` ### `Process-status-p_nom-variable-upper` @@ -8053,12 +8038,12 @@ Process_shut_down_p_fixed_upper: Process_status_p_nom_variable_upper: description: "`Process-status-p_nom-variable-upper` — a modular process is on only where a module is built" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0 expression: Process_status <= Process_n_mod ``` ```math -u^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +u^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` ### `Process-start_up-p_nom-variable-upper` @@ -8069,12 +8054,12 @@ u^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ Process_start_up_p_nom_variable_upper: description: "`Process-start_up-p_nom-variable-upper` — a modular process starts only where a module is built" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0 expression: Process_start_up <= Process_n_mod ``` ```math -\mathit{up}^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +\mathit{up}^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` ### `Process-shut_down-p_nom-variable-upper` @@ -8085,12 +8070,12 @@ Process_start_up_p_nom_variable_upper: Process_shut_down_p_nom_variable_upper: description: "`Process-shut_down-p_nom-variable-upper` — a modular process stops only where a module is built" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0 expression: Process_shut_down <= Process_n_mod ``` ```math -\mathit{dn}^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +\mathit{dn}^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` ### `Process-maint-event-count` @@ -8154,12 +8139,12 @@ Process_maintcap_upper: `Process-maintcap_upper` — the build taken off is at most the chosen build in maintenance, and at most the build less its floor out of it dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext expression: Process_maintenance_capacity <= Process_p_nom_ext - Process_p_nom_min * (1 - Process_maintenance) ``` ```math -\mu^{z,\mathrm{nom}}_{\xi,t,j} \le Z_{j} - \underline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \left( 1 - \mu^{z}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,\mathrm{nom}}_{\xi,t,j} \le Z_{j} - \underline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \left( 1 - \mu^{z}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{mnt,ext}}_{t,j} ``` ### `Process-maintcap_upper_nommax` @@ -8170,12 +8155,12 @@ Process_maintcap_upper: Process_maintcap_upper_nommax: description: "`Process-maintcap_upper_nommax` — out of maintenance, no build is taken off" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext expression: Process_maintenance_capacity <= Process_p_nom_max * Process_maintenance ``` ```math -\mu^{z,\mathrm{nom}}_{\xi,t,j} \le \overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,\mathrm{nom}}_{\xi,t,j} \le \overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{mnt,ext}}_{t,j} ``` ### `Process-maintcap_lower_nommax` @@ -8186,12 +8171,12 @@ Process_maintcap_upper_nommax: Process_maintcap_lower_nommax: description: "`Process-maintcap_lower_nommax` — in maintenance, the whole chosen build is taken off" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext expression: Process_maintenance_capacity >= Process_p_nom_ext - Process_p_nom_max * (1 - Process_maintenance) ``` ```math -\mu^{z,\mathrm{nom}}_{\xi,t,j} \ge Z_{j} - \overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \left( 1 - \mu^{z}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,\mathrm{nom}}_{\xi,t,j} \ge Z_{j} - \overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \left( 1 - \mu^{z}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{mnt,ext}}_{t,j} ``` ### `Process-maintcap_lower_nommin` @@ -8202,12 +8187,12 @@ Process_maintcap_lower_nommax: Process_maintcap_lower_nommin: description: "`Process-maintcap_lower_nommin` — in maintenance, at least the floor of the build is taken off" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active AND Process_p_nom_min > 0 + where: Process_maint_ext AND Process_p_nom_min > 0 expression: Process_maintenance_capacity >= Process_p_nom_min * Process_maintenance ``` ```math -\mu^{z,\mathrm{nom}}_{\xi,t,j} \ge \underline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} \wedge \underline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} > 0 +\mu^{z,\mathrm{nom}}_{\xi,t,j} \ge \underline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{mnt,ext}}_{t,j} \wedge \underline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} > 0 ``` ### `Process-maint-status-le-status` @@ -8218,12 +8203,12 @@ Process_maintcap_lower_nommin: Process_maint_status_le_status: description: "`Process-maint-status-le-status` — the status in maintenance is at most the status" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_maintainable AND Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_maintenance_status <= Process_status ``` ```math -\mu^{z,u}_{\xi,t,j} \le u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \le u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` ### `Process-maint-status-le-maint` @@ -8234,12 +8219,12 @@ Process_maint_status_le_status: Process_maint_status_le_maint: description: "`Process-maint-status-le-maint` — out of maintenance, the status in maintenance is zero" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_maintainable AND Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_maintenance_status <= Process_maintenance ``` ```math -\mu^{z,u}_{\xi,t,j} \le \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \le \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` ### `Process-maint-status-lb` @@ -8250,12 +8235,12 @@ Process_maint_status_le_maint: Process_maint_status_lb: description: "`Process-maint-status-lb` — on and in maintenance, the status in maintenance is one" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_maintainable AND Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_maintenance_status >= Process_status + Process_maintenance - 1 ``` ```math -\mu^{z,u}_{\xi,t,j} \ge u^{z}_{\xi,t,j} + \mu^{z}_{\xi,t,j} - 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \ge u^{z}_{\xi,t,j} + \mu^{z}_{\xi,t,j} - 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` ### `Process-maint-modstatus-le-status` @@ -8266,12 +8251,12 @@ Process_maint_status_lb: Process_maint_modstatus_le_status: description: "`Process-maint-modstatus-le-status` — the modules on in maintenance are at most the modules on" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_maintainable AND Process_committed AND Process_p_nom_mod > 0 expression: Process_maintenance_status <= Process_status ``` ```math -\mu^{z,u}_{\xi,t,j} \le u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \le u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` ### `Process-maint-modstatus-le-maint` @@ -8284,12 +8269,12 @@ Process_maint_modstatus_le_maint: `Process-maint-modstatus-le-maint` — out of maintenance, no module is on in maintenance; in it, at most the modules the build cap holds dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_maintainable AND Process_committed AND Process_p_nom_mod > 0 expression: Process_maintenance_status <= Process_p_nom_max / Process_p_nom_mod * Process_maintenance ``` ```math -\mu^{z,u}_{\xi,t,j} \le \frac{\overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j}}{\mathrm{z}^{\mathrm{mod}}_{j}} \cdot \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \le \frac{\overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j}}{\mathrm{z}^{\mathrm{mod}}_{j}} \cdot \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` ### `Process-maint-modstatus-lb` @@ -8300,12 +8285,12 @@ Process_maint_modstatus_le_maint: Process_maint_modstatus_lb: description: "`Process-maint-modstatus-lb` — in maintenance, every module on is on in maintenance" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_maintainable AND Process_committed AND Process_p_nom_mod > 0 expression: Process_maintenance_status >= Process_status - Process_p_nom_max / Process_p_nom_mod * (1 - Process_maintenance) ``` ```math -\mu^{z,u}_{\xi,t,j} \ge u^{z}_{\xi,t,j} - \frac{\overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j}}{\mathrm{z}^{\mathrm{mod}}_{j}} \cdot \left( 1 - \mu^{z}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \ge u^{z}_{\xi,t,j} - \frac{\overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j}}{\mathrm{z}^{\mathrm{mod}}_{j}} \cdot \left( 1 - \mu^{z}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` ### `Line-fix-s-lower` @@ -8497,12 +8482,12 @@ s_{\xi,t,k} \le \frac{\overline{\delta}_{\xi,k} \cdot \frac{3.141592653589793}{1 Line_loss_upper: description: "`Line-loss_upper` — a line dissipates at most the loss at its rating" dims: [scenario, snapshot, line] - where: transmission_losses AND Line_active + where: Line_lossy expression: Line_loss <= Line_loss_max ``` ```math -\ell_{\xi,t,k} \le \overline{\ell}_{\xi,t,k} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{s}_{t,k} +\ell_{\xi,t,k} \le \overline{\ell}_{\xi,t,k} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K} \,:\, \mathrm{on}^{s,\mathrm{lossy}}_{t,k} ``` ### `Line-loss_tangents-{k}-1` @@ -8517,12 +8502,12 @@ Line_loss_tangents_forward: `k`, or one row stacked over its `secant` axis, and this block states them all over the segment dimension dims: [scenario, snapshot, line, segment] - where: transmission_losses AND Line_active + where: Line_lossy expression: Line_loss + Line_loss_slope * Line_s >= Line_loss_offset ``` ```math -\ell_{\xi,t,k} + \mathrm{a}_{\xi,t,k,b} \cdot s_{\xi,t,k} \ge \mathrm{b}_{\xi,t,k,b} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K},\ b \in \mathcal{B} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{s}_{t,k} +\ell_{\xi,t,k} + \mathrm{a}_{\xi,t,k,b} \cdot s_{\xi,t,k} \ge \mathrm{b}_{\xi,t,k,b} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K},\ b \in \mathcal{B} \,:\, \mathrm{on}^{s,\mathrm{lossy}}_{t,k} ``` ### `Line-loss_tangents-{k}--1` @@ -8535,12 +8520,12 @@ Line_loss_tangents_reverse: `Line-loss_tangents-{k}--1`, `Line-loss_secants-neg` — the same fan mirrored, the loss depending on the flow's magnitude dims: [scenario, snapshot, line, segment] - where: transmission_losses AND Line_active + where: Line_lossy expression: Line_loss - Line_loss_slope * Line_s >= Line_loss_offset ``` ```math -\ell_{\xi,t,k} - \mathrm{a}_{\xi,t,k,b} \cdot s_{\xi,t,k} \ge \mathrm{b}_{\xi,t,k,b} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K},\ b \in \mathcal{B} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{s}_{t,k} +\ell_{\xi,t,k} - \mathrm{a}_{\xi,t,k,b} \cdot s_{\xi,t,k} \ge \mathrm{b}_{\xi,t,k,b} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K},\ b \in \mathcal{B} \,:\, \mathrm{on}^{s,\mathrm{lossy}}_{t,k} ``` ### `Transformer-fix-s-lower` @@ -8771,12 +8756,12 @@ Transformer_v_ang_var_upper: Transformer_loss_upper: description: "`Transformer-loss_upper` — a transformer dissipates at most the loss at its rating" dims: [scenario, snapshot, transformer] - where: transmission_losses AND Transformer_active + where: Transformer_lossy expression: Transformer_loss <= Transformer_loss_max ``` ```math -\ell^{\sigma}_{\xi,t,m} \le \overline{\ell}^{\sigma}_{\xi,t,m} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{\sigma}_{t,m} +\ell^{\sigma}_{\xi,t,m} \le \overline{\ell}^{\sigma}_{\xi,t,m} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M} \,:\, \mathrm{on}^{\sigma,\mathrm{lossy}}_{t,m} ``` ### `Transformer-loss_tangents-{k}-1` @@ -8790,12 +8775,12 @@ Transformer_loss_tangents_forward: loss sits above every cut to its curve for flow one way, as a line's does, over the segment dimension dims: [scenario, snapshot, transformer, segment] - where: transmission_losses AND Transformer_active + where: Transformer_lossy expression: Transformer_loss + Transformer_loss_slope * Transformer_s >= Transformer_loss_offset ``` ```math -\ell^{\sigma}_{\xi,t,m} + \mathrm{a}^{\sigma}_{\xi,t,m,b} \cdot \sigma_{\xi,t,m} \ge \mathrm{b}^{\sigma}_{\xi,t,m,b} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M},\ b \in \mathcal{B} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{\sigma}_{t,m} +\ell^{\sigma}_{\xi,t,m} + \mathrm{a}^{\sigma}_{\xi,t,m,b} \cdot \sigma_{\xi,t,m} \ge \mathrm{b}^{\sigma}_{\xi,t,m,b} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M},\ b \in \mathcal{B} \,:\, \mathrm{on}^{\sigma,\mathrm{lossy}}_{t,m} ``` ### `Transformer-loss_tangents-{k}--1` @@ -8808,12 +8793,12 @@ Transformer_loss_tangents_reverse: `Transformer-loss_tangents-{k}--1`, `Transformer-loss_secants-neg` — the same fan mirrored, the loss depending on the flow's magnitude dims: [scenario, snapshot, transformer, segment] - where: transmission_losses AND Transformer_active + where: Transformer_lossy expression: Transformer_loss - Transformer_loss_slope * Transformer_s >= Transformer_loss_offset ``` ```math -\ell^{\sigma}_{\xi,t,m} - \mathrm{a}^{\sigma}_{\xi,t,m,b} \cdot \sigma_{\xi,t,m} \ge \mathrm{b}^{\sigma}_{\xi,t,m,b} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M},\ b \in \mathcal{B} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{\sigma}_{t,m} +\ell^{\sigma}_{\xi,t,m} - \mathrm{a}^{\sigma}_{\xi,t,m,b} \cdot \sigma_{\xi,t,m} \ge \mathrm{b}^{\sigma}_{\xi,t,m,b} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M},\ b \in \mathcal{B} \,:\, \mathrm{on}^{\sigma,\mathrm{lossy}}_{t,m} ``` ### `Line-fix-s-lower-security-for-{c}-outage-in-sub-network-{n}` @@ -9039,13 +9024,13 @@ Generator_p_ramp_limit_up: where: >- (Generator_ramp_limit_up OR Generator_ramp_limit_start_up) AND NOT (Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) + AND Generator_ramps_from_previous AND Generator_active expression: Generator_p - Generator_previous_p <= Generator_ramp_up_allowance ``` ```math -p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \Delta^{+}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \left( \mathrm{ru}_{\xi,t,g} \text{ is defined} \vee \mathrm{ru}^{\mathrm{up}}_{\xi,g} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \Delta^{+}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \left( \mathrm{ru}_{\xi,t,g} \text{ is defined} \vee \mathrm{ru}^{\mathrm{up}}_{\xi,g} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \right) \wedge \mathrm{prev}_{\xi,t,g} \wedge \mathrm{on}_{t,g} ``` ### `Generator-p-ramp_limit_down` @@ -9065,13 +9050,13 @@ Generator_p_ramp_limit_down: where: >- (Generator_ramp_limit_down OR Generator_ramp_limit_shut_down) AND NOT (Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) + AND Generator_ramps_from_previous AND Generator_active expression: Generator_previous_p - Generator_p <= Generator_ramp_down_allowance ``` ```math -\overleftarrow{p}_{\xi,t,g} - p_{\xi,t,g} \le \Delta^{-}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \left( \mathrm{rd}_{\xi,t,g} \text{ is defined} \vee \mathrm{rd}^{\mathrm{dn}}_{\xi,g} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \right) \wedge \mathrm{on}_{t,g} +\overleftarrow{p}_{\xi,t,g} - p_{\xi,t,g} \le \Delta^{-}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \left( \mathrm{rd}_{\xi,t,g} \text{ is defined} \vee \mathrm{rd}^{\mathrm{dn}}_{\xi,g} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \right) \wedge \mathrm{prev}_{\xi,t,g} \wedge \mathrm{on}_{t,g} ``` ### `Link-p-ramp_limit_up` @@ -9091,13 +9076,13 @@ Link_p_ramp_limit_up: where: >- (Link_ramp_limit_up OR Link_ramp_limit_start_up) AND NOT (Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) + AND Link_ramps_from_previous AND Link_active expression: Link_p - Link_previous_p <= Link_ramp_up_allowance ``` ```math -f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \Delta^{f,+}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \left( \mathrm{ru}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{ru}^{f,\mathrm{up}}_{\xi,l} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \Delta^{f,+}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \left( \mathrm{ru}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{ru}^{f,\mathrm{up}}_{\xi,l} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \right) \wedge \mathrm{prev}^{f}_{\xi,t,l} \wedge \mathrm{on}^{f}_{t,l} ``` ### `Link-p-ramp_limit_down` @@ -9117,13 +9102,13 @@ Link_p_ramp_limit_down: where: >- (Link_ramp_limit_down OR Link_ramp_limit_shut_down) AND NOT (Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) + AND Link_ramps_from_previous AND Link_active expression: Link_previous_p - Link_p <= Link_ramp_down_allowance ``` ```math -\overleftarrow{f}_{\xi,t,l} - f_{\xi,t,l} \le \Delta^{f,-}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \left( \mathrm{rd}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{rd}^{f,\mathrm{dn}}_{\xi,l} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \right) \wedge \mathrm{on}^{f}_{t,l} +\overleftarrow{f}_{\xi,t,l} - f_{\xi,t,l} \le \Delta^{f,-}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \left( \mathrm{rd}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{rd}^{f,\mathrm{dn}}_{\xi,l} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \right) \wedge \mathrm{prev}^{f}_{\xi,t,l} \wedge \mathrm{on}^{f}_{t,l} ``` ### `Process-p-ramp_limit_up` @@ -9143,13 +9128,13 @@ Process_p_ramp_limit_up: where: >- (Process_ramp_limit_up OR Process_ramp_limit_start_up) AND NOT (Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) + AND Process_ramps_from_previous AND Process_active expression: Process_p - Process_previous_p <= Process_ramp_up_allowance ``` ```math -z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \Delta^{z,+}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \left( \mathrm{ru}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{ru}^{z,\mathrm{up}}_{\xi,j} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \Delta^{z,+}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \left( \mathrm{ru}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{ru}^{z,\mathrm{up}}_{\xi,j} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \right) \wedge \mathrm{prev}^{z}_{\xi,t,j} \wedge \mathrm{on}^{z}_{t,j} ``` ### `Process-p-ramp_limit_down` @@ -9169,13 +9154,13 @@ Process_p_ramp_limit_down: where: >- (Process_ramp_limit_down OR Process_ramp_limit_shut_down) AND NOT (Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) + AND Process_ramps_from_previous AND Process_active expression: Process_previous_p - Process_p <= Process_ramp_down_allowance ``` ```math -\overleftarrow{z}_{\xi,t,j} - z_{\xi,t,j} \le \Delta^{z,-}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \left( \mathrm{rd}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{rd}^{z,\mathrm{dn}}_{\xi,j} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \right) \wedge \mathrm{on}^{z}_{t,j} +\overleftarrow{z}_{\xi,t,j} - z_{\xi,t,j} \le \Delta^{z,-}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \left( \mathrm{rd}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{rd}^{z,\mathrm{dn}}_{\xi,j} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \right) \wedge \mathrm{prev}^{z}_{\xi,t,j} \wedge \mathrm{on}^{z}_{t,j} ``` ### `StorageUnit-ext-p_dispatch-lower` @@ -9186,12 +9171,12 @@ Process_p_ramp_limit_down: StorageUnit_ext_p_dispatch_lower: description: "`StorageUnit-ext-p_dispatch-lower` — dispatch is non-negative" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_dispatch >= 0 ``` ```math -h^{+}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{+}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{ext}}_{t,s} ``` ### `StorageUnit-ext-p_dispatch-upper` @@ -9202,12 +9187,12 @@ h^{+}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \ StorageUnit_ext_p_dispatch_upper: description: "`StorageUnit-ext-p_dispatch-upper` — an extendable unit dispatches at most the chosen build" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_dispatch <= StorageUnit_p_max_pu * StorageUnit_p_nom_ext ``` ```math -h^{+}_{\xi,t,s} \le \overline{\mathrm{h}}_{\xi,t,s} \cdot H_{s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{+}_{\xi,t,s} \le \overline{\mathrm{h}}_{\xi,t,s} \cdot H_{s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{ext}}_{t,s} ``` ### `StorageUnit-ext-p_store-lower` @@ -9218,12 +9203,12 @@ h^{+}_{\xi,t,s} \le \overline{\mathrm{h}}_{\xi,t,s} \cdot H_{s} \qquad \forall\, StorageUnit_ext_p_store_lower: description: "`StorageUnit-ext-p_store-lower` — storing is non-negative" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_store >= 0 ``` ```math -h^{-}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{-}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{ext}}_{t,s} ``` ### `StorageUnit-ext-p_store-upper` @@ -9236,12 +9221,12 @@ StorageUnit_ext_p_store_upper: `StorageUnit-ext-p_store-upper` — an extendable unit stores at most the chosen build, the minimum-per-unit column carrying that cap negated dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_store <= -StorageUnit_p_min_pu * StorageUnit_p_nom_ext ``` ```math -h^{-}_{\xi,t,s} \le -\underline{\mathrm{h}}_{\xi,t,s} \cdot H_{s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{-}_{\xi,t,s} \le -\underline{\mathrm{h}}_{\xi,t,s} \cdot H_{s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{ext}}_{t,s} ``` ### `StorageUnit-ext-state_of_charge-lower` @@ -9252,12 +9237,12 @@ h^{-}_{\xi,t,s} \le -\underline{\mathrm{h}}_{\xi,t,s} \cdot H_{s} \qquad \forall StorageUnit_ext_state_of_charge_lower: description: "`StorageUnit-ext-state_of_charge-lower` — charge is non-negative" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_state_of_charge >= 0 ``` ```math -\mathit{soc}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +\mathit{soc}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{ext}}_{t,s} ``` ### `StorageUnit-ext-state_of_charge-upper` @@ -9268,12 +9253,12 @@ StorageUnit_ext_state_of_charge_lower: StorageUnit_ext_state_of_charge_upper: description: "`StorageUnit-ext-state_of_charge-upper` — an extendable unit holds at most its hours at the chosen build" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_state_of_charge <= StorageUnit_max_hours * StorageUnit_p_nom_ext ``` ```math -\mathit{soc}_{\xi,t,s} \le \mathrm{T}^{h}_{\xi,s} \cdot H_{s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +\mathit{soc}_{\xi,t,s} \le \mathrm{T}^{h}_{\xi,s} \cdot H_{s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{ext}}_{t,s} ``` ### `StorageUnit-ext-p_nom-lower` @@ -11716,19 +11701,19 @@ N_{g} \ge 0, N_{g} \in \mathbb{Z} \qquad \forall\, g \in \mathcal{G} \,:\, \math **`Generator_status`** ```math -u_{\xi,t,g} \ge 0, u_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +u_{\xi,t,g} \ge 0, u_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` **`Generator_start_up`** ```math -\mathit{up}_{\xi,t,g} \ge 0, \mathit{up}_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +\mathit{up}_{\xi,t,g} \ge 0, \mathit{up}_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` **`Generator_shut_down`** ```math -\mathit{dn}_{\xi,t,g} \ge 0, \mathit{dn}_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +\mathit{dn}_{\xi,t,g} \ge 0, \mathit{dn}_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` **`Generator_maintenance`** @@ -11746,13 +11731,13 @@ u_{\xi,t,g} \ge 0, u_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \ **`Generator_maintenance_capacity`** ```math -\mu^{\mathrm{nom}}_{\xi,t,g} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{\mathrm{nom}}_{\xi,t,g} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{mnt,ext}}_{t,g} ``` **`Generator_maintenance_status`** ```math -\mu^{u}_{\xi,t,g} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \right) \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \right) ``` **`Link_n_mod`** @@ -11764,19 +11749,19 @@ N^{f}_{l} \ge 0, N^{f}_{l} \in \mathbb{Z} \qquad \forall\, l \in \mathcal{L} \,: **`Link_status`** ```math -u^{f}_{\xi,t,l} \ge 0, u^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +u^{f}_{\xi,t,l} \ge 0, u^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` **`Link_start_up`** ```math -\mathit{up}^{f}_{\xi,t,l} \ge 0, \mathit{up}^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +\mathit{up}^{f}_{\xi,t,l} \ge 0, \mathit{up}^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` **`Link_shut_down`** ```math -\mathit{dn}^{f}_{\xi,t,l} \ge 0, \mathit{dn}^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +\mathit{dn}^{f}_{\xi,t,l} \ge 0, \mathit{dn}^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` **`Link_maintenance`** @@ -11794,13 +11779,13 @@ u^{f}_{\xi,t,l} \ge 0, u^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \ **`Link_maintenance_capacity`** ```math -\mu^{f,\mathrm{nom}}_{\xi,t,l} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,\mathrm{nom}}_{\xi,t,l} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{mnt,ext}}_{t,l} ``` **`Link_maintenance_status`** ```math -\mu^{f,u}_{\xi,t,l} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \right) ``` **`Process_n_mod`** @@ -11812,19 +11797,19 @@ N^{z}_{j} \ge 0, N^{z}_{j} \in \mathbb{Z} \qquad \forall\, j \in \mathcal{J} \,: **`Process_status`** ```math -u^{z}_{\xi,t,j} \ge 0, u^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +u^{z}_{\xi,t,j} \ge 0, u^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` **`Process_start_up`** ```math -\mathit{up}^{z}_{\xi,t,j} \ge 0, \mathit{up}^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +\mathit{up}^{z}_{\xi,t,j} \ge 0, \mathit{up}^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` **`Process_shut_down`** ```math -\mathit{dn}^{z}_{\xi,t,j} \ge 0, \mathit{dn}^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +\mathit{dn}^{z}_{\xi,t,j} \ge 0, \mathit{dn}^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` **`Process_maintenance`** @@ -11842,13 +11827,13 @@ u^{z}_{\xi,t,j} \ge 0, u^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \ **`Process_maintenance_capacity`** ```math -\mu^{z,\mathrm{nom}}_{\xi,t,j} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,\mathrm{nom}}_{\xi,t,j} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{mnt,ext}}_{t,j} ``` **`Process_maintenance_status`** ```math -\mu^{z,u}_{\xi,t,j} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \right) ``` **`Line_s`** @@ -11860,7 +11845,7 @@ s_{\xi,t,k} \in \mathbb{R} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k **`Line_loss`** ```math -\ell_{\xi,t,k} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{s}_{t,k} +\ell_{\xi,t,k} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K} \,:\, \mathrm{on}^{s,\mathrm{lossy}}_{t,k} ``` **`Transformer_s`** @@ -11872,7 +11857,7 @@ s_{\xi,t,k} \in \mathbb{R} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k **`Transformer_loss`** ```math -\ell^{\sigma}_{\xi,t,m} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{\sigma}_{t,m} +\ell^{\sigma}_{\xi,t,m} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M} \,:\, \mathrm{on}^{\sigma,\mathrm{lossy}}_{t,m} ``` **`Transformer_phase_shift`** diff --git a/docs/examples/pypsa/generator.md b/docs/examples/pypsa/generator.md index f2a67033..381b0a7e 100644 --- a/docs/examples/pypsa/generator.md +++ b/docs/examples/pypsa/generator.md @@ -222,6 +222,13 @@ expressions: expression: sum(Generator_sign * Generator_p, over=generator, by=Generator_bus[bus]) adds_to: Bus_injection +masks: + Generator_committed: + description: >- + a committable generator that stands in the snapshot's period — every + unit-commitment row's set + where: Generator_committable AND Generator_active + constraints: Generator_fix_p_lower: description: "`Generator-fix-p-lower` — a fixed generator outputs at least its minimum" @@ -374,6 +381,12 @@ assumptions: | $`\mathit{Generator\_additions}`$ | `Generator_additions` over $`\mathcal{Y} \times \mathcal{I}`$ | | $`\mathit{Generator\_injection}`$ | `Generator_injection` over $`\Xi \times \mathcal{T} \times \mathcal{N}`$ | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{\mathrm{com}}`$ | `Generator_committed` over $`\mathcal{T} \times \mathcal{G}`$ — a committable generator that stands in the snapshot's period — every unit-commitment row's set | + #### Subject to **`Generator_fix_p_lower`** @@ -498,6 +511,14 @@ p_{\xi,t,g} = \mathrm{p}^{\mathrm{set}}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ \mathit{Generator\_injection}_{\xi,t,n} = \sum_{g \in \mathcal{G} \,:\, \mathrm{Generator\_bus}(g) = n} \mathrm{sgn}_{g} \cdot p_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ n \in \mathcal{N} ``` +#### Masks + +**`Generator_committed`** + +```math +\mathrm{on}^{\mathrm{com}}_{t,g} \iff \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} +``` + #### Variable domains **`Generator_p`** diff --git a/docs/examples/pypsa/generator_commitment.md b/docs/examples/pypsa/generator_commitment.md index aa3ab50c..bc6be516 100644 --- a/docs/examples/pypsa/generator_commitment.md +++ b/docs/examples/pypsa/generator_commitment.md @@ -88,21 +88,21 @@ variables: the rows below cap at one, or at the module count where the build is modular dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed domain: integer bounds: lower: 0 Generator_start_up: description: "`Generator-start_up` — how much of a committable unit turns on this snapshot, capped as the status is" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed domain: integer bounds: lower: 0 Generator_shut_down: description: "`Generator-shut_down` — how much of a committable unit turns off this snapshot, capped as the status is" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed domain: integer bounds: lower: 0 @@ -128,6 +128,8 @@ given: Generator_p_nom_ext: { dims: [generator] } expressions: scenario_opex: { dims: [scenario] } + masks: + Generator_committed: { dims: [snapshot, generator] } expressions: Generator_previous_status: @@ -155,26 +157,38 @@ expressions: + sum(sum(Generator_shut_down * Generator_shut_down_cost, over=generator), over=snapshot) adds_to: scenario_opex +masks: + Generator_com_ext: + description: >- + a committable generator with an extendable, non-modular build that + stands in the snapshot's period — PyPSA's `com-ext` rows, whose status + is relaxed against the chosen build + where: >- + Generator_committable + AND Generator_p_nom_extendable + AND NOT (Generator_p_nom_mod > 0) + AND Generator_active + constraints: Generator_com_p_lower: description: "`Generator-com-p-lower` — a committed unit outputs at least its minimum; off, at least nothing" dims: [scenario, snapshot, generator] - where: Generator_committable AND not Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_p >= Generator_p_min_pu * Generator_p_nom * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) Generator_com_p_upper: description: "`Generator-com-p-upper` — a committed unit outputs at most what is available; off, at most nothing" dims: [scenario, snapshot, generator] - where: Generator_committable AND not Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_p <= Generator_p_max_pu * Generator_p_nom * (Generator_status - Generator_maintenance_pu * Generator_maintenance_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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed 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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed expression: Generator_shut_down >= Generator_previous_status - Generator_status Generator_com_up_time: description: >- @@ -182,7 +196,7 @@ constraints: 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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_min_up_time > 0 AND position(snapshot) > 0 AND Generator_active + where: Generator_committed 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: >- @@ -190,38 +204,38 @@ constraints: time is still off. The first snapshot's share of the window is the brought-in down time's, which the must-stay-down mask carries dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_min_down_time > 0 AND position(snapshot) > 0 AND Generator_active + where: Generator_committed 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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_must_stay_up AND Generator_active + where: Generator_committed AND Generator_must_stay_up expression: Generator_status == 1 Generator_com_status_must_stay_down: description: >- `Generator-com-status-min_down_time_must_stay_up` — a unit still serving the down time it brought in stays off; PyPSA names the row `_must_stay_up` dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_must_stay_down AND Generator_active + where: Generator_committed AND Generator_must_stay_down expression: Generator_status == 0 Generator_com_ext_p_upper_cap: description: >- `Generator-com-ext-p-upper-cap` — a committed extendable unit outputs at most what is available of the chosen build, whatever its status dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext expression: Generator_p <= Generator_p_max_pu * (Generator_p_nom_ext - Generator_maintenance_pu * Generator_maintenance_capacity) Generator_com_ext_p_upper_big_m: description: "`Generator-com-ext-p-upper-bigM` — off, a unit outputs nothing; on, the big M is no bound" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext expression: Generator_p <= Generator_big_m * Generator_status Generator_com_ext_p_lower: description: >- `Generator-com-ext-p-lower` — a committed extendable unit outputs at least its minimum of the chosen build; off, the big M releases the row dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext expression: >- Generator_p >= Generator_p_min_pu * (Generator_p_nom_ext - Generator_maintenance_pu * Generator_maintenance_capacity) @@ -232,23 +246,21 @@ constraints: negative, output is also plainly non-negative, a row the big-M lower cannot assert while the unit is off dims: [scenario, snapshot, generator] - where: >- - Generator_committable AND Generator_p_nom_extendable - AND Generator_p_min_pu_nonneg AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext AND Generator_p_min_pu_nonneg expression: Generator_p >= 0 Generator_com_mod_p_lower: description: >- `Generator-com-mod-p-lower` — a committed modular unit outputs at least its minimum of one module, whether the build is fixed or a decision dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_p >= Generator_p_min_pu * Generator_p_nom_mod * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) Generator_com_mod_p_upper: description: >- `Generator-com-mod-p-upper` — a committed modular unit outputs at most one module's share, whether the build is fixed or a decision dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_p <= Generator_p_max_pu * Generator_p_nom_mod * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) Generator_status_p_fixed_upper: description: >- @@ -256,7 +268,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, generator] - where: Generator_committable AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) expression: Generator_status <= Generator_modules_installed Generator_start_up_p_fixed_upper: description: >- @@ -264,7 +276,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, generator] - where: Generator_committable AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) expression: Generator_start_up <= Generator_modules_installed Generator_shut_down_p_fixed_upper: description: >- @@ -272,22 +284,22 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, generator] - where: Generator_committable AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) expression: Generator_shut_down <= Generator_modules_installed Generator_status_p_nom_variable_upper: description: "`Generator-status-p_nom-variable-upper` — a modular unit is on only where a module is built" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 expression: Generator_status <= Generator_n_mod Generator_start_up_p_nom_variable_upper: description: "`Generator-start_up-p_nom-variable-upper` — a modular unit starts only where a module is built" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 expression: Generator_start_up <= Generator_n_mod Generator_shut_down_p_nom_variable_upper: description: "`Generator-shut_down-p_nom-variable-upper` — a modular unit stops only where a module is built" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 expression: Generator_shut_down <= Generator_n_mod ``` @@ -344,6 +356,7 @@ constraints: | $`\mu^{u}`$ | `Generator_maintenance_status` over $`\Xi \times \mathcal{T} \times \mathcal{G}`$ | | $`P`$ | `Generator_p_nom_ext` over $`\mathcal{G}`$ | | $`\mathit{scenario\_opex}`$ | `scenario_opex` over $`\Xi`$, an expression this file adds `Generator_commitment_opex` to | +| $`\mathrm{on}^{\mathrm{com}}`$ | `Generator_committed` over $`\mathcal{T} \times \mathcal{G}`$, a mask another file defines | #### Definitions @@ -353,6 +366,12 @@ constraints: | $`\overleftarrow{u}^{\circ}`$ | `Generator_status_carried_over` over $`\Xi \times \mathcal{T} \times \mathcal{G}`$ — the state a generator carries over into a snapshot — the state it brought into the horizon at the first, the previous snapshot's after that | | $`\mathit{Generator\_commitment\_opex}`$ | `Generator_commitment_opex` over $`\Xi`$ | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{\mathrm{com,ext}}`$ | `Generator_com_ext` over $`\mathcal{T} \times \mathcal{G}`$ — a committable generator with an extendable, non-modular build that stands in the snapshot's period — PyPSA's `com-ext` rows, whose status is relaxed against the chosen build | + $`\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. #### Subject to @@ -360,121 +379,121 @@ $`\mathrm{pos}(t)`$ denotes where index $`t`$ sits along its dimension's own ord **`Generator_com_p_lower`** ```math -p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{\xi,g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{\xi,g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` **`Generator_com_p_upper`** ```math -p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{\xi,g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{nom}}_{\xi,g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` **`Generator_com_transition_start_up`** ```math -\mathit{up}_{\xi,t,g} \ge u_{\xi,t,g} - \overleftarrow{u}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +\mathit{up}_{\xi,t,g} \ge u_{\xi,t,g} - \overleftarrow{u}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` **`Generator_com_transition_shut_down`** ```math -\mathit{dn}_{\xi,t,g} \ge \overleftarrow{u}_{\xi,t,g} - u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +\mathit{dn}_{\xi,t,g} \ge \overleftarrow{u}_{\xi,t,g} - u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` **`Generator_com_up_time`** ```math -\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}} \mathit{up}_{\xi,t',g} \le u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{UT}_{\xi,g} > 0 \wedge \mathrm{pos}(t) > 0 \wedge \mathrm{on}_{t,g} +\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}} \mathit{up}_{\xi,t',g} \le u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{UT}_{\xi,g} > 0 \wedge \mathrm{pos}(t) > 0 ``` **`Generator_com_down_time`** ```math -\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}} \mathit{dn}_{\xi,t',g} \le 1 - u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{DT}_{\xi,g} > 0 \wedge \mathrm{pos}(t) > 0 \wedge \mathrm{on}_{t,g} +\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}} \mathit{dn}_{\xi,t',g} \le 1 - u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{DT}_{\xi,g} > 0 \wedge \mathrm{pos}(t) > 0 ``` **`Generator_com_status_must_stay_up`** ```math -u_{\xi,t,g} = 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{hold}_{\xi,t,g} \wedge \mathrm{on}_{t,g} +u_{\xi,t,g} = 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{hold}_{\xi,t,g} ``` **`Generator_com_status_must_stay_down`** ```math -u_{\xi,t,g} = 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{rest}_{\xi,t,g} \wedge \mathrm{on}_{t,g} +u_{\xi,t,g} = 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{rest}_{\xi,t,g} ``` **`Generator_com_ext_p_upper_cap`** ```math -p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \left( P_{g} - \gamma_{\xi,g} \cdot \mu^{\mathrm{nom}}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \left( P_{g} - \gamma_{\xi,g} \cdot \mu^{\mathrm{nom}}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} ``` **`Generator_com_ext_p_upper_big_m`** ```math -p_{\xi,t,g} \le \mathrm{M}_{\xi,g} \cdot u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \le \mathrm{M}_{\xi,g} \cdot u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} ``` **`Generator_com_ext_p_lower`** ```math -p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \left( P_{g} - \gamma_{\xi,g} \cdot \mu^{\mathrm{nom}}_{\xi,t,g} \right) + \mathrm{M}_{\xi,g} \cdot u_{\xi,t,g} - \mathrm{M}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \left( P_{g} - \gamma_{\xi,g} \cdot \mu^{\mathrm{nom}}_{\xi,t,g} \right) + \mathrm{M}_{\xi,g} \cdot u_{\xi,t,g} - \mathrm{M}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} ``` **`Generator_com_ext_p_lower_nonneg`** ```math -p_{\xi,t,g} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \mathrm{nonneg}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} \wedge \mathrm{nonneg}_{g} ``` **`Generator_com_mod_p_lower`** ```math -p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{mod}}_{g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \ge \underline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{mod}}_{g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` **`Generator_com_mod_p_upper`** ```math -p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{mod}}_{g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} \le \overline{\mathrm{p}}_{\xi,t,g} \cdot \mathrm{p}^{\mathrm{mod}}_{g} \cdot \left( u_{\xi,t,g} - \gamma_{\xi,g} \cdot \mu^{u}_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` **`Generator_status_p_fixed_upper`** ```math -u_{\xi,t,g} \le \mathrm{N}^{\mathrm{fix}}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +u_{\xi,t,g} \le \mathrm{N}^{\mathrm{fix}}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` **`Generator_start_up_p_fixed_upper`** ```math -\mathit{up}_{\xi,t,g} \le \mathrm{N}^{\mathrm{fix}}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mathit{up}_{\xi,t,g} \le \mathrm{N}^{\mathrm{fix}}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` **`Generator_shut_down_p_fixed_upper`** ```math -\mathit{dn}_{\xi,t,g} \le \mathrm{N}^{\mathrm{fix}}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mathit{dn}_{\xi,t,g} \le \mathrm{N}^{\mathrm{fix}}_{\xi,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` **`Generator_status_p_nom_variable_upper`** ```math -u_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +u_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` **`Generator_start_up_p_nom_variable_upper`** ```math -\mathit{up}_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +\mathit{up}_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` **`Generator_shut_down_p_nom_variable_upper`** ```math -\mathit{dn}_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +\mathit{dn}_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` #### Definitions @@ -497,23 +516,31 @@ u_{\xi,t,g} \le N_{g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \ \mathit{Generator\_commitment\_opex}_{\xi} = \sum_{t \in \mathcal{T}} \sum_{g \in \mathcal{G}} u_{\xi,t,g} \cdot \mathrm{c}^{\mathrm{on}}_{\xi,t,g} \cdot \mathrm{w}_{t} \cdot \mathrm{w}^{y}_{\mathrm{snapshot\_period}(t)} + \sum_{t \in \mathcal{T}} \sum_{g \in \mathcal{G}} \mathit{up}_{\xi,t,g} \cdot \mathrm{c}^{\mathrm{up}}_{\xi,t,g} + \sum_{t \in \mathcal{T}} \sum_{g \in \mathcal{G}} \mathit{dn}_{\xi,t,g} \cdot \mathrm{c}^{\mathrm{dn}}_{\xi,t,g} \qquad \forall\, \xi \in \Xi ``` +#### Masks + +**`Generator_com_ext`** + +```math +\mathrm{on}^{\mathrm{com,ext}}_{t,g} \iff \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} +``` + #### Variable domains **`Generator_status`** ```math -u_{\xi,t,g} \ge 0, u_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +u_{\xi,t,g} \ge 0, u_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` **`Generator_start_up`** ```math -\mathit{up}_{\xi,t,g} \ge 0, \mathit{up}_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +\mathit{up}_{\xi,t,g} \ge 0, \mathit{up}_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` **`Generator_shut_down`** ```math -\mathit{dn}_{\xi,t,g} \ge 0, \mathit{dn}_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +\mathit{dn}_{\xi,t,g} \ge 0, \mathit{dn}_{\xi,t,g} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` diff --git a/docs/examples/pypsa/generator_maintenance.md b/docs/examples/pypsa/generator_maintenance.md index 09f731f6..7e293796 100644 --- a/docs/examples/pypsa/generator_maintenance.md +++ b/docs/examples/pypsa/generator_maintenance.md @@ -82,9 +82,7 @@ variables: `Generator-maintenance_capacity` — the chosen build while in maintenance, zero otherwise: the product the `maintcap` rows linearize dims: [scenario, snapshot, generator] - where: >- - Generator_maintainable AND Generator_p_nom_extendable - AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext absence: zero bounds: lower: 0 @@ -95,8 +93,9 @@ variables: maintenance may also be off dims: [scenario, snapshot, generator] where: >- - Generator_maintainable AND Generator_committable - AND NOT (Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) AND Generator_active + Generator_maintainable + AND Generator_committed + AND NOT (Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) absence: zero bounds: lower: 0 @@ -113,6 +112,20 @@ given: variables: Generator_status: { dims: [scenario, snapshot, generator], domain: integer } Generator_p_nom_ext: { dims: [generator] } + masks: + Generator_committed: { dims: [snapshot, generator] } + +masks: + Generator_maint_ext: + description: >- + a maintainable generator with an extendable build, unless it is + committable and modular, that stands in the snapshot's period — the + maintenance rows against the chosen build + where: >- + Generator_maintainable + AND Generator_p_nom_extendable + AND NOT (Generator_committable AND Generator_p_nom_mod > 0) + AND Generator_active constraints: Generator_maint_event_count: @@ -138,54 +151,54 @@ constraints: `Generator-maintcap_upper` — the build taken off is at most the chosen build in maintenance, and at most the build less its floor out of it dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext expression: Generator_maintenance_capacity <= Generator_p_nom_ext - Generator_p_nom_min * (1 - Generator_maintenance) Generator_maintcap_upper_nommax: description: "`Generator-maintcap_upper_nommax` — out of maintenance, no build is taken off" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext expression: Generator_maintenance_capacity <= Generator_p_nom_max * Generator_maintenance Generator_maintcap_lower_nommax: description: "`Generator-maintcap_lower_nommax` — in maintenance, the whole chosen build is taken off" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext expression: Generator_maintenance_capacity >= Generator_p_nom_ext - Generator_p_nom_max * (1 - Generator_maintenance) Generator_maintcap_lower_nommin: description: "`Generator-maintcap_lower_nommin` — in maintenance, at least the floor of the build is taken off" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active AND Generator_p_nom_min > 0 + where: Generator_maint_ext AND Generator_p_nom_min > 0 expression: Generator_maintenance_capacity >= Generator_p_nom_min * Generator_maintenance Generator_maint_status_le_status: description: "`Generator-maint-status-le-status` — the status in maintenance is at most the status" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maintainable AND Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_maintenance_status <= Generator_status Generator_maint_status_le_maint: description: "`Generator-maint-status-le-maint` — out of maintenance, the status in maintenance is zero" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maintainable AND Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_maintenance_status <= Generator_maintenance Generator_maint_status_lb: description: "`Generator-maint-status-lb` — on and in maintenance, the status in maintenance is one" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maintainable AND Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_maintenance_status >= Generator_status + Generator_maintenance - 1 Generator_maint_modstatus_le_status: description: "`Generator-maint-modstatus-le-status` — the modules on in maintenance are at most the modules on" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_maintainable AND Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_maintenance_status <= Generator_status Generator_maint_modstatus_le_maint: description: >- `Generator-maint-modstatus-le-maint` — out of maintenance, no module is on in maintenance; in it, at most the modules the build cap holds dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_maintainable AND Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_maintenance_status <= Generator_p_nom_max / Generator_p_nom_mod * Generator_maintenance Generator_maint_modstatus_lb: description: "`Generator-maint-modstatus-lb` — in maintenance, every module on is on in maintenance" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_maintainable AND Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_maintenance_status >= Generator_status - Generator_p_nom_max / Generator_p_nom_mod * (1 - Generator_maintenance) assumptions: @@ -272,6 +285,13 @@ assumptions: | $`\overline{\mathrm{p}}^{\mathrm{nom}}`$ | `Generator_p_nom_max` over $`\Xi \times \mathcal{G}`$, data another file declares | | $`u`$ | `Generator_status` over $`\Xi \times \mathcal{T} \times \mathcal{G}`$ | | $`P`$ | `Generator_p_nom_ext` over $`\mathcal{G}`$ | +| $`\mathrm{on}^{\mathrm{com}}`$ | `Generator_committed` over $`\mathcal{T} \times \mathcal{G}`$, a mask another file defines | + +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{\mathrm{mnt,ext}}`$ | `Generator_maint_ext` over $`\mathcal{T} \times \mathcal{G}`$ — a maintainable generator with an extendable build, unless it is committable and modular, that stands in the snapshot's period — the maintenance rows against the chosen build | #### Subject to @@ -296,61 +316,69 @@ assumptions: **`Generator_maintcap_upper`** ```math -\mu^{\mathrm{nom}}_{\xi,t,g} \le P_{g} - \underline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \left( 1 - \mu_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{\mathrm{nom}}_{\xi,t,g} \le P_{g} - \underline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \left( 1 - \mu_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{mnt,ext}}_{t,g} ``` **`Generator_maintcap_upper_nommax`** ```math -\mu^{\mathrm{nom}}_{\xi,t,g} \le \overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{\mathrm{nom}}_{\xi,t,g} \le \overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{mnt,ext}}_{t,g} ``` **`Generator_maintcap_lower_nommax`** ```math -\mu^{\mathrm{nom}}_{\xi,t,g} \ge P_{g} - \overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \left( 1 - \mu_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{\mathrm{nom}}_{\xi,t,g} \ge P_{g} - \overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \left( 1 - \mu_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{mnt,ext}}_{t,g} ``` **`Generator_maintcap_lower_nommin`** ```math -\mu^{\mathrm{nom}}_{\xi,t,g} \ge \underline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} \wedge \underline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} > 0 +\mu^{\mathrm{nom}}_{\xi,t,g} \ge \underline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{mnt,ext}}_{t,g} \wedge \underline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} > 0 ``` **`Generator_maint_status_le_status`** ```math -\mu^{u}_{\xi,t,g} \le u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \le u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` **`Generator_maint_status_le_maint`** ```math -\mu^{u}_{\xi,t,g} \le \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \le \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` **`Generator_maint_status_lb`** ```math -\mu^{u}_{\xi,t,g} \ge u_{\xi,t,g} + \mu_{\xi,t,g} - 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \ge u_{\xi,t,g} + \mu_{\xi,t,g} - 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) ``` **`Generator_maint_modstatus_le_status`** ```math -\mu^{u}_{\xi,t,g} \le u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \le u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` **`Generator_maint_modstatus_le_maint`** ```math -\mu^{u}_{\xi,t,g} \le \frac{\overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g}}{\mathrm{p}^{\mathrm{mod}}_{g}} \cdot \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \le \frac{\overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g}}{\mathrm{p}^{\mathrm{mod}}_{g}} \cdot \mu_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 ``` **`Generator_maint_modstatus_lb`** ```math -\mu^{u}_{\xi,t,g} \ge u_{\xi,t,g} - \frac{\overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g}}{\mathrm{p}^{\mathrm{mod}}_{g}} \cdot \left( 1 - \mu_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \ge u_{\xi,t,g} - \frac{\overline{\mathrm{p}}^{\mathrm{nom}}_{\xi,g}}{\mathrm{p}^{\mathrm{mod}}_{g}} \cdot \left( 1 - \mu_{\xi,t,g} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 +``` + +#### Masks + +**`Generator_maint_ext`** + +```math +\mathrm{on}^{\mathrm{mnt,ext}}_{t,g} \iff \mathrm{mnt}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} ``` #### Variable domains @@ -370,13 +398,13 @@ assumptions: **`Generator_maintenance_capacity`** ```math -\mu^{\mathrm{nom}}_{\xi,t,g} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{t,g} +\mu^{\mathrm{nom}}_{\xi,t,g} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{mnt,ext}}_{t,g} ``` **`Generator_maintenance_status`** ```math -\mu^{u}_{\xi,t,g} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{com}_{g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \right) \wedge \mathrm{on}_{t,g} +\mu^{u}_{\xi,t,g} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{mnt}_{g} \wedge \mathrm{on}^{\mathrm{com}}_{t,g} \wedge \neg \left( \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \right) ``` #### Assumptions diff --git a/docs/examples/pypsa/generator_ramping.md b/docs/examples/pypsa/generator_ramping.md index 695390ee..df8d2711 100644 --- a/docs/examples/pypsa/generator_ramping.md +++ b/docs/examples/pypsa/generator_ramping.md @@ -67,6 +67,8 @@ given: Generator_previous_status: { dims: [scenario, snapshot, generator] } Generator_p_nom_effective: { dims: [scenario, generator] } Generator_p_nom_committed: { dims: [scenario, generator] } + masks: + Generator_com_ext: { dims: [snapshot, generator] } expressions: Generator_previous_p: @@ -139,6 +141,16 @@ expressions: * (Generator_previous_status - Generator_status) otherwise: Generator_ramp_down_rate * Generator_p_nom_effective +masks: + Generator_ramps_from_previous: + description: >- + a snapshot whose ramp reads a previous output — any snapshot but its + period's first, and the horizon's first where the generator comes in off + or carries an initial output + where: >- + position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND + (Generator_status_initial == 0 OR Generator_p_init)) + constraints: Generator_p_ramp_limit_up_run_big_m: description: >- @@ -147,10 +159,9 @@ constraints: releases the row in the snapshot it turns on dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_up OR Generator_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_p - Generator_previous_p <= Generator_ramp_up_rate * Generator_p_nom_ext @@ -162,10 +173,9 @@ constraints: the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_up OR Generator_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_p - Generator_previous_p <= Generator_start_up_rate * Generator_p_nom_ext @@ -177,10 +187,9 @@ constraints: releases the row in the snapshot it turns off dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_down OR Generator_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_previous_p - Generator_p <= Generator_ramp_down_rate * Generator_p_nom_ext @@ -192,10 +201,9 @@ constraints: the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_down OR Generator_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_previous_p - Generator_p <= Generator_shut_down_rate * Generator_p_nom_ext @@ -212,7 +220,7 @@ constraints: where: >- (Generator_ramp_limit_up OR Generator_ramp_limit_start_up) AND NOT (Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) + AND Generator_ramps_from_previous AND Generator_active expression: Generator_p - Generator_previous_p <= Generator_ramp_up_allowance Generator_p_ramp_limit_down: @@ -227,7 +235,7 @@ constraints: where: >- (Generator_ramp_limit_down OR Generator_ramp_limit_shut_down) AND NOT (Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) + AND Generator_ramps_from_previous AND Generator_active expression: Generator_previous_p - Generator_p <= Generator_ramp_down_allowance @@ -285,6 +293,7 @@ assumptions: | $`\overleftarrow{u}`$ | `Generator_previous_status` over $`\Xi \times \mathcal{T} \times \mathcal{G}`$, an expression another file defines | | $`\widetilde{\mathrm{p}}^{\mathrm{nom}}`$ | `Generator_p_nom_effective` over $`\Xi \times \mathcal{G}`$, an expression another file defines | | $`\widehat{\mathrm{p}}^{\mathrm{nom}}`$ | `Generator_p_nom_committed` over $`\Xi \times \mathcal{G}`$, an expression another file defines | +| $`\mathrm{on}^{\mathrm{com,ext}}`$ | `Generator_com_ext` over $`\mathcal{T} \times \mathcal{G}`$, a mask another file defines | #### Definitions @@ -298,6 +307,12 @@ assumptions: | $`\Delta^{+}`$ | `Generator_ramp_up_allowance` over $`\Xi \times \mathcal{T} \times \mathcal{G}`$ — how far a generator may raise output between two snapshots — its ramp limit of the build while it stays on, plus its start-up ramp in the snapshot it turns on | | $`\Delta^{-}`$ | `Generator_ramp_down_allowance` over $`\Xi \times \mathcal{T} \times \mathcal{G}`$ — how far a generator may lower output between two snapshots — its ramp limit of the build while it stays on, plus its shut-down ramp in the snapshot it turns off | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{prev}`$ | `Generator_ramps_from_previous` over $`\Xi \times \mathcal{T} \times \mathcal{G}`$ — a snapshot whose ramp reads a previous output — any snapshot but its period's first, and the horizon's first where the generator comes in off or carries an initial output | + $`\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. $`\mathrm{pos}_{\mathrm{relation}(t)}(t)`$ counts within the group a relation puts $`t`$ in: the subscript names the map, $`\mathcal{T}_{\mathrm{relation}(t)}`$ is the group it lands in, and that group has a first position of its own. @@ -307,37 +322,37 @@ $`\mathrm{pos}_{\mathrm{relation}(t)}(t)`$ counts within the group a relation pu **`Generator_p_ramp_limit_up_run_big_m`** ```math -p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \widetilde{\mathrm{ru}}_{\xi,t,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot \overleftarrow{u}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \left( \mathrm{ru}_{\xi,t,g} \text{ is defined} \vee \mathrm{ru}^{\mathrm{up}}_{\xi,g} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \widetilde{\mathrm{ru}}_{\xi,t,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot \overleftarrow{u}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} \wedge \left( \mathrm{ru}_{\xi,t,g} \text{ is defined} \vee \mathrm{ru}^{\mathrm{up}}_{\xi,g} \text{ is defined} \right) \wedge \mathrm{prev}_{\xi,t,g} ``` **`Generator_p_ramp_limit_up_start_big_m`** ```math -p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \widetilde{\mathrm{ru}}^{\mathrm{up}}_{\xi,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot \mathit{up}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \left( \mathrm{ru}_{\xi,t,g} \text{ is defined} \vee \mathrm{ru}^{\mathrm{up}}_{\xi,g} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \widetilde{\mathrm{ru}}^{\mathrm{up}}_{\xi,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot \mathit{up}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} \wedge \left( \mathrm{ru}_{\xi,t,g} \text{ is defined} \vee \mathrm{ru}^{\mathrm{up}}_{\xi,g} \text{ is defined} \right) \wedge \mathrm{prev}_{\xi,t,g} ``` **`Generator_p_ramp_limit_down_run_big_m`** ```math -\overleftarrow{p}_{\xi,t,g} - p_{\xi,t,g} \le \widetilde{\mathrm{rd}}_{\xi,t,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \left( \mathrm{rd}_{\xi,t,g} \text{ is defined} \vee \mathrm{rd}^{\mathrm{dn}}_{\xi,g} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \right) \wedge \mathrm{on}_{t,g} +\overleftarrow{p}_{\xi,t,g} - p_{\xi,t,g} \le \widetilde{\mathrm{rd}}_{\xi,t,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot u_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} \wedge \left( \mathrm{rd}_{\xi,t,g} \text{ is defined} \vee \mathrm{rd}^{\mathrm{dn}}_{\xi,g} \text{ is defined} \right) \wedge \mathrm{prev}_{\xi,t,g} ``` **`Generator_p_ramp_limit_down_shut_big_m`** ```math -\overleftarrow{p}_{\xi,t,g} - p_{\xi,t,g} \le \widetilde{\mathrm{rd}}^{\mathrm{dn}}_{\xi,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot \mathit{dn}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \left( \mathrm{rd}_{\xi,t,g} \text{ is defined} \vee \mathrm{rd}^{\mathrm{dn}}_{\xi,g} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \right) \wedge \mathrm{on}_{t,g} +\overleftarrow{p}_{\xi,t,g} - p_{\xi,t,g} \le \widetilde{\mathrm{rd}}^{\mathrm{dn}}_{\xi,g} \cdot P_{g} + \mathrm{M}_{\xi,g} - \mathrm{M}_{\xi,g} \cdot \mathit{dn}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{t,g} \wedge \left( \mathrm{rd}_{\xi,t,g} \text{ is defined} \vee \mathrm{rd}^{\mathrm{dn}}_{\xi,g} \text{ is defined} \right) \wedge \mathrm{prev}_{\xi,t,g} ``` **`Generator_p_ramp_limit_up`** ```math -p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \Delta^{+}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \left( \mathrm{ru}_{\xi,t,g} \text{ is defined} \vee \mathrm{ru}^{\mathrm{up}}_{\xi,g} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \right) \wedge \mathrm{on}_{t,g} +p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \Delta^{+}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \left( \mathrm{ru}_{\xi,t,g} \text{ is defined} \vee \mathrm{ru}^{\mathrm{up}}_{\xi,g} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \right) \wedge \mathrm{prev}_{\xi,t,g} \wedge \mathrm{on}_{t,g} ``` **`Generator_p_ramp_limit_down`** ```math -\overleftarrow{p}_{\xi,t,g} - p_{\xi,t,g} \le \Delta^{-}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \left( \mathrm{rd}_{\xi,t,g} \text{ is defined} \vee \mathrm{rd}^{\mathrm{dn}}_{\xi,g} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \right) \wedge \mathrm{on}_{t,g} +\overleftarrow{p}_{\xi,t,g} - p_{\xi,t,g} \le \Delta^{-}_{\xi,t,g} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \left( \mathrm{rd}_{\xi,t,g} \text{ is defined} \vee \mathrm{rd}^{\mathrm{dn}}_{\xi,g} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \right) \wedge \mathrm{prev}_{\xi,t,g} \wedge \mathrm{on}_{t,g} ``` #### Definitions @@ -384,6 +399,14 @@ p_{\xi,t,g} - \overleftarrow{p}_{\xi,t,g} \le \Delta^{+}_{\xi,t,g} \qquad \foral \Delta^{-}_{\xi,t,g} = \begin{cases} \widetilde{\mathrm{rd}}_{\xi,t,g} \cdot \widehat{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot u_{\xi,t,g} + \widetilde{\mathrm{rd}}^{\mathrm{dn}}_{\xi,g} \cdot \widehat{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} \cdot \left( \overleftarrow{u}_{\xi,t,g} - u_{\xi,t,g} \right) & \text{if } \mathrm{com}_{g} \\ \widetilde{\mathrm{rd}}_{\xi,t,g} \cdot \widetilde{\mathrm{p}}^{\mathrm{nom}}_{\xi,g} & \text{otherwise} \end{cases} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} ``` +#### Masks + +**`Generator_ramps_from_previous`** + +```math +\mathrm{prev}_{\xi,t,g} \iff \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{0}_{\xi,g} = 0 \vee \mathrm{p}^{0}_{\xi,g} \text{ is defined} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} +``` + #### Assumptions **`Generator_came_in_running_unless_committable`** diff --git a/docs/examples/pypsa/line.md b/docs/examples/pypsa/line.md index 345ff802..da780d87 100644 --- a/docs/examples/pypsa/line.md +++ b/docs/examples/pypsa/line.md @@ -175,7 +175,7 @@ variables: cost and held up by the cuts; absent, and zero in the balance, where the network is lossless dims: [scenario, snapshot, line] - where: transmission_losses AND Line_active + where: Line_lossy absence: zero bounds: lower: 0 @@ -243,6 +243,13 @@ expressions: expression: sum(Line_s * at(Line_cycle_weight, by=snapshot_period[period]), over=line) adds_to: Cycle_angle_sum +masks: + Line_lossy: + description: >- + a line that stands in the snapshot's period, where the run models + transmission losses + where: transmission_losses AND Line_active + constraints: Line_fix_s_lower: description: "`Line-fix-s-lower` — a fixed line carries at least the negative of its rating, the loss counted against it" @@ -307,7 +314,7 @@ constraints: Line_loss_upper: description: "`Line-loss_upper` — a line dissipates at most the loss at its rating" dims: [scenario, snapshot, line] - where: transmission_losses AND Line_active + where: Line_lossy expression: Line_loss <= Line_loss_max Line_loss_tangents_forward: description: >- @@ -316,14 +323,14 @@ constraints: `k`, or one row stacked over its `secant` axis, and this block states them all over the segment dimension dims: [scenario, snapshot, line, segment] - where: transmission_losses AND Line_active + where: Line_lossy expression: Line_loss + Line_loss_slope * Line_s >= Line_loss_offset Line_loss_tangents_reverse: description: >- `Line-loss_tangents-{k}--1`, `Line-loss_secants-neg` — the same fan mirrored, the loss depending on the flow's magnitude dims: [scenario, snapshot, line, segment] - where: transmission_losses AND Line_active + where: Line_lossy expression: Line_loss - Line_loss_slope * Line_s >= Line_loss_offset ``` @@ -403,6 +410,12 @@ constraints: | $`\mathit{Line\_injection}`$ | `Line_injection` over $`\Xi \times \mathcal{T} \times \mathcal{N}`$ | | $`\mathit{Line\_angle\_sum}`$ | `Line_angle_sum` over $`\Xi \times \mathcal{T} \times \mathcal{C}`$ | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{s,\mathrm{lossy}}`$ | `Line_lossy` over $`\mathcal{T} \times \mathcal{K}`$ — a line that stands in the snapshot's period, where the run models transmission losses | + #### Subject to **`Line_fix_s_lower`** @@ -474,19 +487,19 @@ s_{\xi,t,k} \le \frac{\overline{\delta}_{\xi,k} \cdot \frac{3.141592653589793}{1 **`Line_loss_upper`** ```math -\ell_{\xi,t,k} \le \overline{\ell}_{\xi,t,k} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{s}_{t,k} +\ell_{\xi,t,k} \le \overline{\ell}_{\xi,t,k} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K} \,:\, \mathrm{on}^{s,\mathrm{lossy}}_{t,k} ``` **`Line_loss_tangents_forward`** ```math -\ell_{\xi,t,k} + \mathrm{a}_{\xi,t,k,e} \cdot s_{\xi,t,k} \ge \mathrm{b}_{\xi,t,k,e} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K},\ e \in \mathcal{E} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{s}_{t,k} +\ell_{\xi,t,k} + \mathrm{a}_{\xi,t,k,e} \cdot s_{\xi,t,k} \ge \mathrm{b}_{\xi,t,k,e} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K},\ e \in \mathcal{E} \,:\, \mathrm{on}^{s,\mathrm{lossy}}_{t,k} ``` **`Line_loss_tangents_reverse`** ```math -\ell_{\xi,t,k} - \mathrm{a}_{\xi,t,k,e} \cdot s_{\xi,t,k} \ge \mathrm{b}_{\xi,t,k,e} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K},\ e \in \mathcal{E} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{s}_{t,k} +\ell_{\xi,t,k} - \mathrm{a}_{\xi,t,k,e} \cdot s_{\xi,t,k} \ge \mathrm{b}_{\xi,t,k,e} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K},\ e \in \mathcal{E} \,:\, \mathrm{on}^{s,\mathrm{lossy}}_{t,k} ``` #### Definitions @@ -539,6 +552,14 @@ s_{\xi,t,k} \le \frac{\overline{\delta}_{\xi,k} \cdot \frac{3.141592653589793}{1 \mathit{Line\_angle\_sum}_{\xi,t,c} = \sum_{k \in \mathcal{K}} s_{\xi,t,k} \cdot \mathrm{x}_{\mathrm{snapshot\_period}(t),k,c} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ c \in \mathcal{C} ``` +#### Masks + +**`Line_lossy`** + +```math +\mathrm{on}^{s,\mathrm{lossy}}_{t,k} \iff \mathrm{lossy} \wedge \mathrm{on}^{s}_{t,k} \qquad \forall\, t \in \mathcal{T},\ k \in \mathcal{K} +``` + #### Variable domains **`Line_s`** @@ -550,7 +571,7 @@ s_{\xi,t,k} \in \mathbb{R} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k **`Line_loss`** ```math -\ell_{\xi,t,k} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{s}_{t,k} +\ell_{\xi,t,k} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ k \in \mathcal{K} \,:\, \mathrm{on}^{s,\mathrm{lossy}}_{t,k} ``` **`Line_s_nom_ext`** diff --git a/docs/examples/pypsa/link.md b/docs/examples/pypsa/link.md index c41449a5..a43c4eea 100644 --- a/docs/examples/pypsa/link.md +++ b/docs/examples/pypsa/link.md @@ -274,6 +274,13 @@ expressions: + sum(Link_output_arrival, over=link_output, by=Link_output_bus[bus]) adds_to: Bus_injection +masks: + Link_committed: + description: >- + a committable link that stands in the snapshot's period — every + unit-commitment row's set + where: Link_committable AND Link_active + constraints: Link_fix_p_lower: description: "`Link-fix-p-lower` — a fixed link carries at least its minimum, negative for the other way" @@ -416,6 +423,12 @@ assumptions: | $`\mathit{Link\_additions}`$ | `Link_additions` over $`\mathcal{Y} \times \mathcal{I}`$ | | $`\mathit{Link\_injection}`$ | `Link_injection` over $`\Xi \times \mathcal{T} \times \mathcal{N}`$ | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{f,\mathrm{com}}`$ | `Link_committed` over $`\mathcal{T} \times \mathcal{L}`$ — a committable link that stands in the snapshot's period — every unit-commitment row's set | + $`t \ominus k`$ denotes cyclic translation: index $`t-k`$ taken modulo the size of the dimension (`roll`). Plain $`t-k`$ (`shift`) has no wraparound — terms translated past the edge are simply absent. $`t \boxminus_{v} k`$ denotes translation with $`v`$ standing where index $`t-k`$ leaves the dimension (`shift(edge=v)`), so the row at that boundary is built and carries $`v`$ rather than being dropped. @@ -540,6 +553,14 @@ f_{\xi,t,l} = \mathrm{f}^{\mathrm{set}}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ \mathit{Link\_injection}_{\xi,t,n} = -\left( \sum_{l \in \mathcal{L} \,:\, \mathrm{Link\_bus0}(l) = n} f_{\xi,t,l} \right) + \sum_{o \in \mathcal{O} \,:\, \mathrm{Link\_output\_bus}(o) = n} \overrightarrow{f}_{\xi,t,o} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ n \in \mathcal{N} ``` +#### Masks + +**`Link_committed`** + +```math +\mathrm{on}^{f,\mathrm{com}}_{t,l} \iff \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} \qquad \forall\, t \in \mathcal{T},\ l \in \mathcal{L} +``` + #### Variable domains **`Link_p`** diff --git a/docs/examples/pypsa/link_commitment.md b/docs/examples/pypsa/link_commitment.md index 9884826e..321b2d9f 100644 --- a/docs/examples/pypsa/link_commitment.md +++ b/docs/examples/pypsa/link_commitment.md @@ -88,21 +88,21 @@ variables: the rows below cap at one, or at the module count where the build is modular dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed domain: integer bounds: lower: 0 Link_start_up: description: "`Link-start_up` — how much of a committable link turns on this snapshot, capped as the status is" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed domain: integer bounds: lower: 0 Link_shut_down: description: "`Link-shut_down` — how much of a committable link turns off this snapshot, capped as the status is" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed domain: integer bounds: lower: 0 @@ -128,6 +128,8 @@ given: Link_p_nom_ext: { dims: [link] } expressions: scenario_opex: { dims: [scenario] } + masks: + Link_committed: { dims: [snapshot, link] } expressions: Link_previous_status: @@ -155,26 +157,38 @@ expressions: + sum(sum(Link_shut_down * Link_shut_down_cost, over=link), over=snapshot) adds_to: scenario_opex +masks: + Link_com_ext: + description: >- + a committable link with an extendable, non-modular build that stands in + the snapshot's period — PyPSA's `com-ext` rows, whose status is relaxed + against the chosen build + where: >- + Link_committable + AND Link_p_nom_extendable + AND NOT (Link_p_nom_mod > 0) + AND Link_active + constraints: Link_com_p_lower: description: "`Link-com-p-lower` — a committed link flows at least its minimum; off, at least nothing" dims: [scenario, snapshot, link] - where: Link_committable AND not Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_p >= Link_p_min_pu * Link_p_nom * (Link_status - Link_maintenance_pu * Link_maintenance_status) Link_com_p_upper: description: "`Link-com-p-upper` — a committed link flows at most what is available; off, at most nothing" dims: [scenario, snapshot, link] - where: Link_committable AND not Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_p <= Link_p_max_pu * Link_p_nom * (Link_status - Link_maintenance_pu * Link_maintenance_status) Link_com_transition_start_up: description: "`Link-com-transition-start-up` — turning on is a start, counted against the state the link carried into the snapshot" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed expression: Link_start_up >= Link_status - Link_previous_status Link_com_transition_shut_down: description: "`Link-com-transition-shut-down` — turning off is a stop, counted against the state the link carried into the snapshot" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed expression: Link_shut_down >= Link_previous_status - Link_status Link_com_up_time: description: >- @@ -182,7 +196,7 @@ constraints: 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: [scenario, snapshot, link] - where: Link_committable AND Link_min_up_time > 0 AND position(snapshot) > 0 AND Link_active + where: Link_committed AND Link_min_up_time > 0 AND position(snapshot) > 0 expression: sum_back(Link_start_up, along=snapshot, window=Link_min_up_time) <= Link_status Link_com_down_time: description: >- @@ -190,38 +204,38 @@ constraints: time is still off. The first snapshot's share of the window is the brought-in down time's, which the must-stay-down mask carries dims: [scenario, snapshot, link] - where: Link_committable AND Link_min_down_time > 0 AND position(snapshot) > 0 AND Link_active + where: Link_committed AND Link_min_down_time > 0 AND position(snapshot) > 0 expression: sum_back(Link_shut_down, along=snapshot, window=Link_min_down_time) <= 1 - Link_status Link_com_status_must_stay_up: description: "`Link-com-status-min_up_time_must_stay_up` — a link still serving the up time it brought in stays on" dims: [scenario, snapshot, link] - where: Link_committable AND Link_must_stay_up AND Link_active + where: Link_committed AND Link_must_stay_up expression: Link_status == 1 Link_com_status_must_stay_down: description: >- `Link-com-status-min_down_time_must_stay_up` — a link still serving the down time it brought in stays off; PyPSA names the row `_must_stay_up` dims: [scenario, snapshot, link] - where: Link_committable AND Link_must_stay_down AND Link_active + where: Link_committed AND Link_must_stay_down expression: Link_status == 0 Link_com_ext_p_upper_cap: description: >- `Link-com-ext-p-upper-cap` — a committed extendable link flows at most what is available of the chosen build, whatever its status dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext expression: Link_p <= Link_p_max_pu * (Link_p_nom_ext - Link_maintenance_pu * Link_maintenance_capacity) Link_com_ext_p_upper_big_m: description: "`Link-com-ext-p-upper-bigM` — off, a link flows nothing; on, the big M is no bound" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext expression: Link_p <= Link_big_m * Link_status Link_com_ext_p_lower: description: >- `Link-com-ext-p-lower` — a committed extendable link flows at least its minimum of the chosen build; off, the big M releases the row dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext expression: >- Link_p >= Link_p_min_pu * (Link_p_nom_ext - Link_maintenance_pu * Link_maintenance_capacity) @@ -232,23 +246,21 @@ constraints: negative, flow is also plainly non-negative, a row the big-M lower cannot assert while the link is off dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable - AND Link_p_min_pu_nonneg AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext AND Link_p_min_pu_nonneg expression: Link_p >= 0 Link_com_mod_p_lower: description: >- `Link-com-mod-p-lower` — a committed modular link flows at least its minimum of one module, whether the build is fixed or a decision dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_mod > 0 expression: Link_p >= Link_p_min_pu * Link_p_nom_mod * (Link_status - Link_maintenance_pu * Link_maintenance_status) Link_com_mod_p_upper: description: >- `Link-com-mod-p-upper` — a committed modular link flows at most one module's share, whether the build is fixed or a decision dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_mod > 0 expression: Link_p <= Link_p_max_pu * Link_p_nom_mod * (Link_status - Link_maintenance_pu * Link_maintenance_status) Link_status_p_fixed_upper: description: >- @@ -256,7 +268,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, link] - where: Link_committable AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) expression: Link_status <= Link_modules_installed Link_start_up_p_fixed_upper: description: >- @@ -264,7 +276,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, link] - where: Link_committable AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) expression: Link_start_up <= Link_modules_installed Link_shut_down_p_fixed_upper: description: >- @@ -272,22 +284,22 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, link] - where: Link_committable AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) expression: Link_shut_down <= Link_modules_installed Link_status_p_nom_variable_upper: description: "`Link-status-p_nom-variable-upper` — a modular link is on only where a module is built" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_extendable AND Link_p_nom_mod > 0 expression: Link_status <= Link_n_mod Link_start_up_p_nom_variable_upper: description: "`Link-start_up-p_nom-variable-upper` — a modular link starts only where a module is built" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_extendable AND Link_p_nom_mod > 0 expression: Link_start_up <= Link_n_mod Link_shut_down_p_nom_variable_upper: description: "`Link-shut_down-p_nom-variable-upper` — a modular link stops only where a module is built" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_extendable AND Link_p_nom_mod > 0 expression: Link_shut_down <= Link_n_mod ``` @@ -344,6 +356,7 @@ constraints: | $`\mu^{f,u}`$ | `Link_maintenance_status` over $`\Xi \times \mathcal{T} \times \mathcal{L}`$ | | $`F`$ | `Link_p_nom_ext` over $`\mathcal{L}`$ | | $`\mathit{scenario\_opex}`$ | `scenario_opex` over $`\Xi`$, an expression this file adds `Link_commitment_opex` to | +| $`\mathrm{on}^{f,\mathrm{com}}`$ | `Link_committed` over $`\mathcal{T} \times \mathcal{L}`$, a mask another file defines | #### Definitions @@ -353,6 +366,12 @@ constraints: | $`\overleftarrow{u}^{\circ f}`$ | `Link_status_carried_over` over $`\Xi \times \mathcal{T} \times \mathcal{L}`$ — the state a link carries over into a snapshot — the state it brought into the horizon at the first, the previous snapshot's after that | | $`\mathit{Link\_commitment\_opex}`$ | `Link_commitment_opex` over $`\Xi`$ | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{f,\mathrm{com,ext}}`$ | `Link_com_ext` over $`\mathcal{T} \times \mathcal{L}`$ — a committable link with an extendable, non-modular build that stands in the snapshot's period — PyPSA's `com-ext` rows, whose status is relaxed against the chosen build | + $`\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. #### Subject to @@ -360,121 +379,121 @@ $`\mathrm{pos}(t)`$ denotes where index $`t`$ sits along its dimension's own ord **`Link_com_p_lower`** ```math -f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{nom}}_{\xi,l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{nom}}_{\xi,l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` **`Link_com_p_upper`** ```math -f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{nom}}_{\xi,l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{nom}}_{\xi,l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` **`Link_com_transition_start_up`** ```math -\mathit{up}^{f}_{\xi,t,l} \ge u^{f}_{\xi,t,l} - \overleftarrow{u}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +\mathit{up}^{f}_{\xi,t,l} \ge u^{f}_{\xi,t,l} - \overleftarrow{u}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` **`Link_com_transition_shut_down`** ```math -\mathit{dn}^{f}_{\xi,t,l} \ge \overleftarrow{u}^{f}_{\xi,t,l} - u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +\mathit{dn}^{f}_{\xi,t,l} \ge \overleftarrow{u}^{f}_{\xi,t,l} - u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` **`Link_com_up_time`** ```math -\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}^{f}} \mathit{up}^{f}_{\xi,t',l} \le u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{UT}^{f}_{\xi,l} > 0 \wedge \mathrm{pos}(t) > 0 \wedge \mathrm{on}^{f}_{t,l} +\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}^{f}} \mathit{up}^{f}_{\xi,t',l} \le u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{UT}^{f}_{\xi,l} > 0 \wedge \mathrm{pos}(t) > 0 ``` **`Link_com_down_time`** ```math -\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}^{f}} \mathit{dn}^{f}_{\xi,t',l} \le 1 - u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{DT}^{f}_{\xi,l} > 0 \wedge \mathrm{pos}(t) > 0 \wedge \mathrm{on}^{f}_{t,l} +\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}^{f}} \mathit{dn}^{f}_{\xi,t',l} \le 1 - u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{DT}^{f}_{\xi,l} > 0 \wedge \mathrm{pos}(t) > 0 ``` **`Link_com_status_must_stay_up`** ```math -u^{f}_{\xi,t,l} = 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{hold}^{f}_{\xi,t,l} \wedge \mathrm{on}^{f}_{t,l} +u^{f}_{\xi,t,l} = 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{hold}^{f}_{\xi,t,l} ``` **`Link_com_status_must_stay_down`** ```math -u^{f}_{\xi,t,l} = 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{rest}^{f}_{\xi,t,l} \wedge \mathrm{on}^{f}_{t,l} +u^{f}_{\xi,t,l} = 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{rest}^{f}_{\xi,t,l} ``` **`Link_com_ext_p_upper_cap`** ```math -f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \left( F_{l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,\mathrm{nom}}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \left( F_{l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,\mathrm{nom}}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} ``` **`Link_com_ext_p_upper_big_m`** ```math -f_{\xi,t,l} \le \mathrm{M}^{f}_{\xi,l} \cdot u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \le \mathrm{M}^{f}_{\xi,l} \cdot u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} ``` **`Link_com_ext_p_lower`** ```math -f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \left( F_{l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,\mathrm{nom}}_{\xi,t,l} \right) + \mathrm{M}^{f}_{\xi,l} \cdot u^{f}_{\xi,t,l} - \mathrm{M}^{f}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \left( F_{l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,\mathrm{nom}}_{\xi,t,l} \right) + \mathrm{M}^{f}_{\xi,l} \cdot u^{f}_{\xi,t,l} - \mathrm{M}^{f}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} ``` **`Link_com_ext_p_lower_nonneg`** ```math -f_{\xi,t,l} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{nonneg}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} \wedge \mathrm{nonneg}^{f}_{l} ``` **`Link_com_mod_p_lower`** ```math -f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{mod}}_{l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \ge \underline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{mod}}_{l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` **`Link_com_mod_p_upper`** ```math -f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{mod}}_{l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} \le \overline{\mathrm{f}}_{\xi,t,l} \cdot \mathrm{f}^{\mathrm{mod}}_{l} \cdot \left( u^{f}_{\xi,t,l} - \gamma^{f}_{\xi,l} \cdot \mu^{f,u}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` **`Link_status_p_fixed_upper`** ```math -u^{f}_{\xi,t,l} \le \mathrm{N}^{f,\mathrm{fix}}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +u^{f}_{\xi,t,l} \le \mathrm{N}^{f,\mathrm{fix}}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` **`Link_start_up_p_fixed_upper`** ```math -\mathit{up}^{f}_{\xi,t,l} \le \mathrm{N}^{f,\mathrm{fix}}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mathit{up}^{f}_{\xi,t,l} \le \mathrm{N}^{f,\mathrm{fix}}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` **`Link_shut_down_p_fixed_upper`** ```math -\mathit{dn}^{f}_{\xi,t,l} \le \mathrm{N}^{f,\mathrm{fix}}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mathit{dn}^{f}_{\xi,t,l} \le \mathrm{N}^{f,\mathrm{fix}}_{\xi,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` **`Link_status_p_nom_variable_upper`** ```math -u^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +u^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` **`Link_start_up_p_nom_variable_upper`** ```math -\mathit{up}^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +\mathit{up}^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` **`Link_shut_down_p_nom_variable_upper`** ```math -\mathit{dn}^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +\mathit{dn}^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{ext}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` #### Definitions @@ -497,23 +516,31 @@ u^{f}_{\xi,t,l} \le N^{f}_{l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ \mathit{Link\_commitment\_opex}_{\xi} = \sum_{t \in \mathcal{T}} \sum_{l \in \mathcal{L}} u^{f}_{\xi,t,l} \cdot \mathrm{c}^{f,\mathrm{on}}_{\xi,t,l} \cdot \mathrm{w}_{t} \cdot \mathrm{w}^{y}_{\mathrm{snapshot\_period}(t)} + \sum_{t \in \mathcal{T}} \sum_{l \in \mathcal{L}} \mathit{up}^{f}_{\xi,t,l} \cdot \mathrm{c}^{f,\mathrm{up}}_{\xi,t,l} + \sum_{t \in \mathcal{T}} \sum_{l \in \mathcal{L}} \mathit{dn}^{f}_{\xi,t,l} \cdot \mathrm{c}^{f,\mathrm{dn}}_{\xi,t,l} \qquad \forall\, \xi \in \Xi ``` +#### Masks + +**`Link_com_ext`** + +```math +\mathrm{on}^{f,\mathrm{com,ext}}_{t,l} \iff \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} \qquad \forall\, t \in \mathcal{T},\ l \in \mathcal{L} +``` + #### Variable domains **`Link_status`** ```math -u^{f}_{\xi,t,l} \ge 0, u^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +u^{f}_{\xi,t,l} \ge 0, u^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` **`Link_start_up`** ```math -\mathit{up}^{f}_{\xi,t,l} \ge 0, \mathit{up}^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +\mathit{up}^{f}_{\xi,t,l} \ge 0, \mathit{up}^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` **`Link_shut_down`** ```math -\mathit{dn}^{f}_{\xi,t,l} \ge 0, \mathit{dn}^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +\mathit{dn}^{f}_{\xi,t,l} \ge 0, \mathit{dn}^{f}_{\xi,t,l} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` diff --git a/docs/examples/pypsa/link_maintenance.md b/docs/examples/pypsa/link_maintenance.md index 07a28342..c057d698 100644 --- a/docs/examples/pypsa/link_maintenance.md +++ b/docs/examples/pypsa/link_maintenance.md @@ -82,9 +82,7 @@ variables: `Link-maintenance_capacity` — the chosen build while in maintenance, zero otherwise: the product the `maintcap` rows linearize dims: [scenario, snapshot, link] - where: >- - Link_maintainable AND Link_p_nom_extendable - AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext absence: zero bounds: lower: 0 @@ -94,9 +92,7 @@ variables: otherwise: the product the `maint-status` rows linearize, so a unit in maintenance may also be off dims: [scenario, snapshot, link] - where: >- - Link_maintainable AND Link_committable - AND NOT (Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) AND Link_active + where: Link_maintainable AND Link_committed AND NOT (Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) absence: zero bounds: lower: 0 @@ -113,6 +109,20 @@ given: variables: Link_status: { dims: [scenario, snapshot, link], domain: integer } Link_p_nom_ext: { dims: [link] } + masks: + Link_committed: { dims: [snapshot, link] } + +masks: + Link_maint_ext: + description: >- + a maintainable link with an extendable build, unless it is committable + and modular, that stands in the snapshot's period — the maintenance rows + against the chosen build + where: >- + Link_maintainable + AND Link_p_nom_extendable + AND NOT (Link_committable AND Link_p_nom_mod > 0) + AND Link_active constraints: Link_maint_event_count: @@ -138,54 +148,54 @@ constraints: `Link-maintcap_upper` — the build taken off is at most the chosen build in maintenance, and at most the build less its floor out of it dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext expression: Link_maintenance_capacity <= Link_p_nom_ext - Link_p_nom_min * (1 - Link_maintenance) Link_maintcap_upper_nommax: description: "`Link-maintcap_upper_nommax` — out of maintenance, no build is taken off" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext expression: Link_maintenance_capacity <= Link_p_nom_max * Link_maintenance Link_maintcap_lower_nommax: description: "`Link-maintcap_lower_nommax` — in maintenance, the whole chosen build is taken off" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext expression: Link_maintenance_capacity >= Link_p_nom_ext - Link_p_nom_max * (1 - Link_maintenance) Link_maintcap_lower_nommin: description: "`Link-maintcap_lower_nommin` — in maintenance, at least the floor of the build is taken off" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active AND Link_p_nom_min > 0 + where: Link_maint_ext AND Link_p_nom_min > 0 expression: Link_maintenance_capacity >= Link_p_nom_min * Link_maintenance Link_maint_status_le_status: description: "`Link-maint-status-le-status` — the status in maintenance is at most the status" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_maintainable AND Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_maintenance_status <= Link_status Link_maint_status_le_maint: description: "`Link-maint-status-le-maint` — out of maintenance, the status in maintenance is zero" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_maintainable AND Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_maintenance_status <= Link_maintenance Link_maint_status_lb: description: "`Link-maint-status-lb` — on and in maintenance, the status in maintenance is one" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_maintainable AND Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_maintenance_status >= Link_status + Link_maintenance - 1 Link_maint_modstatus_le_status: description: "`Link-maint-modstatus-le-status` — the modules on in maintenance are at most the modules on" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_maintainable AND Link_committed AND Link_p_nom_mod > 0 expression: Link_maintenance_status <= Link_status Link_maint_modstatus_le_maint: description: >- `Link-maint-modstatus-le-maint` — out of maintenance, no module is on in maintenance; in it, at most the modules the build cap holds dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_maintainable AND Link_committed AND Link_p_nom_mod > 0 expression: Link_maintenance_status <= Link_p_nom_max / Link_p_nom_mod * Link_maintenance Link_maint_modstatus_lb: description: "`Link-maint-modstatus-lb` — in maintenance, every module on is on in maintenance" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_maintainable AND Link_committed AND Link_p_nom_mod > 0 expression: Link_maintenance_status >= Link_status - Link_p_nom_max / Link_p_nom_mod * (1 - Link_maintenance) assumptions: @@ -272,6 +282,13 @@ assumptions: | $`\overline{\mathrm{f}}^{\mathrm{nom}}`$ | `Link_p_nom_max` over $`\Xi \times \mathcal{L}`$, data another file declares | | $`u^{f}`$ | `Link_status` over $`\Xi \times \mathcal{T} \times \mathcal{L}`$ | | $`F`$ | `Link_p_nom_ext` over $`\mathcal{L}`$ | +| $`\mathrm{on}^{f,\mathrm{com}}`$ | `Link_committed` over $`\mathcal{T} \times \mathcal{L}`$, a mask another file defines | + +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{f,\mathrm{mnt,ext}}`$ | `Link_maint_ext` over $`\mathcal{T} \times \mathcal{L}`$ — a maintainable link with an extendable build, unless it is committable and modular, that stands in the snapshot's period — the maintenance rows against the chosen build | #### Subject to @@ -296,61 +313,69 @@ assumptions: **`Link_maintcap_upper`** ```math -\mu^{f,\mathrm{nom}}_{\xi,t,l} \le F_{l} - \underline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \left( 1 - \mu^{f}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,\mathrm{nom}}_{\xi,t,l} \le F_{l} - \underline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \left( 1 - \mu^{f}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{mnt,ext}}_{t,l} ``` **`Link_maintcap_upper_nommax`** ```math -\mu^{f,\mathrm{nom}}_{\xi,t,l} \le \overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,\mathrm{nom}}_{\xi,t,l} \le \overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{mnt,ext}}_{t,l} ``` **`Link_maintcap_lower_nommax`** ```math -\mu^{f,\mathrm{nom}}_{\xi,t,l} \ge F_{l} - \overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \left( 1 - \mu^{f}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,\mathrm{nom}}_{\xi,t,l} \ge F_{l} - \overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \left( 1 - \mu^{f}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{mnt,ext}}_{t,l} ``` **`Link_maintcap_lower_nommin`** ```math -\mu^{f,\mathrm{nom}}_{\xi,t,l} \ge \underline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} \wedge \underline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} > 0 +\mu^{f,\mathrm{nom}}_{\xi,t,l} \ge \underline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{mnt,ext}}_{t,l} \wedge \underline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} > 0 ``` **`Link_maint_status_le_status`** ```math -\mu^{f,u}_{\xi,t,l} \le u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \le u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` **`Link_maint_status_le_maint`** ```math -\mu^{f,u}_{\xi,t,l} \le \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \le \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` **`Link_maint_status_lb`** ```math -\mu^{f,u}_{\xi,t,l} \ge u^{f}_{\xi,t,l} + \mu^{f}_{\xi,t,l} - 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \ge u^{f}_{\xi,t,l} + \mu^{f}_{\xi,t,l} - 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) ``` **`Link_maint_modstatus_le_status`** ```math -\mu^{f,u}_{\xi,t,l} \le u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \le u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` **`Link_maint_modstatus_le_maint`** ```math -\mu^{f,u}_{\xi,t,l} \le \frac{\overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l}}{\mathrm{f}^{\mathrm{mod}}_{l}} \cdot \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \le \frac{\overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l}}{\mathrm{f}^{\mathrm{mod}}_{l}} \cdot \mu^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 ``` **`Link_maint_modstatus_lb`** ```math -\mu^{f,u}_{\xi,t,l} \ge u^{f}_{\xi,t,l} - \frac{\overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l}}{\mathrm{f}^{\mathrm{mod}}_{l}} \cdot \left( 1 - \mu^{f}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \ge u^{f}_{\xi,t,l} - \frac{\overline{\mathrm{f}}^{\mathrm{nom}}_{\xi,l}}{\mathrm{f}^{\mathrm{mod}}_{l}} \cdot \left( 1 - \mu^{f}_{\xi,t,l} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 +``` + +#### Masks + +**`Link_maint_ext`** + +```math +\mathrm{on}^{f,\mathrm{mnt,ext}}_{t,l} \iff \mathrm{mnt}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} \qquad \forall\, t \in \mathcal{T},\ l \in \mathcal{L} ``` #### Variable domains @@ -370,13 +395,13 @@ assumptions: **`Link_maintenance_capacity`** ```math -\mu^{f,\mathrm{nom}}_{\xi,t,l} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,\mathrm{nom}}_{\xi,t,l} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{mnt,ext}}_{t,l} ``` **`Link_maintenance_status`** ```math -\mu^{f,u}_{\xi,t,l} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{com}^{f}_{l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \right) \wedge \mathrm{on}^{f}_{t,l} +\mu^{f,u}_{\xi,t,l} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{mnt}^{f}_{l} \wedge \mathrm{on}^{f,\mathrm{com}}_{t,l} \wedge \neg \left( \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \right) ``` #### Assumptions diff --git a/docs/examples/pypsa/link_ramping.md b/docs/examples/pypsa/link_ramping.md index 3105ca16..41bc9db6 100644 --- a/docs/examples/pypsa/link_ramping.md +++ b/docs/examples/pypsa/link_ramping.md @@ -67,6 +67,8 @@ given: Link_p_nom_effective: { dims: [scenario, link] } Link_previous_status: { dims: [scenario, snapshot, link] } Link_p_nom_committed: { dims: [scenario, link] } + masks: + Link_com_ext: { dims: [snapshot, link] } expressions: Link_previous_p: @@ -139,6 +141,16 @@ expressions: * (Link_previous_status - Link_status) otherwise: Link_ramp_down_rate * Link_p_nom_effective +masks: + Link_ramps_from_previous: + description: >- + a snapshot whose ramp reads a previous output — any snapshot but its + period's first, and the horizon's first where the link comes in off or + carries an initial output + where: >- + position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND + (Link_status_initial == 0 OR Link_p_init)) + constraints: Link_p_ramp_limit_up_run_big_m: description: >- @@ -146,11 +158,7 @@ constraints: raises flow no faster than its limit of the chosen build; the big M releases the row in the snapshot it turns on dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) AND Link_ramps_from_previous expression: >- Link_p - Link_previous_p <= Link_ramp_up_rate * Link_p_nom_ext @@ -161,11 +169,7 @@ constraints: committed extendable link ramps no further than its start-up ramp of the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) AND Link_ramps_from_previous expression: >- Link_p - Link_previous_p <= Link_start_up_rate * Link_p_nom_ext @@ -176,11 +180,7 @@ constraints: lowers flow no faster than its limit of the chosen build; the big M releases the row in the snapshot it turns off dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) AND Link_ramps_from_previous expression: >- Link_previous_p - Link_p <= Link_ramp_down_rate * Link_p_nom_ext @@ -191,11 +191,7 @@ constraints: a committed extendable link ramps no further than its shut-down ramp of the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) AND Link_ramps_from_previous expression: >- Link_previous_p - Link_p <= Link_shut_down_rate * Link_p_nom_ext @@ -212,7 +208,7 @@ constraints: where: >- (Link_ramp_limit_up OR Link_ramp_limit_start_up) AND NOT (Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) + AND Link_ramps_from_previous AND Link_active expression: Link_p - Link_previous_p <= Link_ramp_up_allowance Link_p_ramp_limit_down: @@ -227,7 +223,7 @@ constraints: where: >- (Link_ramp_limit_down OR Link_ramp_limit_shut_down) AND NOT (Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) + AND Link_ramps_from_previous AND Link_active expression: Link_previous_p - Link_p <= Link_ramp_down_allowance @@ -285,6 +281,7 @@ assumptions: | $`\widetilde{\mathrm{f}}^{\mathrm{nom}}`$ | `Link_p_nom_effective` over $`\Xi \times \mathcal{L}`$, an expression another file defines | | $`\overleftarrow{u}^{f}`$ | `Link_previous_status` over $`\Xi \times \mathcal{T} \times \mathcal{L}`$, an expression another file defines | | $`\widehat{\mathrm{f}}^{\mathrm{nom}}`$ | `Link_p_nom_committed` over $`\Xi \times \mathcal{L}`$, an expression another file defines | +| $`\mathrm{on}^{f,\mathrm{com,ext}}`$ | `Link_com_ext` over $`\mathcal{T} \times \mathcal{L}`$, a mask another file defines | #### Definitions @@ -298,6 +295,12 @@ assumptions: | $`\Delta^{f,+}`$ | `Link_ramp_up_allowance` over $`\Xi \times \mathcal{T} \times \mathcal{L}`$ — how far a link may raise flow between two snapshots — its ramp limit of the build while it stays on, plus its start-up ramp in the snapshot it turns on | | $`\Delta^{f,-}`$ | `Link_ramp_down_allowance` over $`\Xi \times \mathcal{T} \times \mathcal{L}`$ — how far a link may lower flow between two snapshots — its ramp limit of the build while it stays on, plus its shut-down ramp in the snapshot it turns off | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{prev}^{f}`$ | `Link_ramps_from_previous` over $`\Xi \times \mathcal{T} \times \mathcal{L}`$ — a snapshot whose ramp reads a previous output — any snapshot but its period's first, and the horizon's first where the link comes in off or carries an initial output | + $`\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. $`\mathrm{pos}_{\mathrm{relation}(t)}(t)`$ counts within the group a relation puts $`t`$ in: the subscript names the map, $`\mathcal{T}_{\mathrm{relation}(t)}`$ is the group it lands in, and that group has a first position of its own. @@ -307,37 +310,37 @@ $`\mathrm{pos}_{\mathrm{relation}(t)}(t)`$ counts within the group a relation pu **`Link_p_ramp_limit_up_run_big_m`** ```math -f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \widetilde{\mathrm{ru}}^{f}_{\xi,t,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot \overleftarrow{u}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \left( \mathrm{ru}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{ru}^{f,\mathrm{up}}_{\xi,l} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \widetilde{\mathrm{ru}}^{f}_{\xi,t,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot \overleftarrow{u}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} \wedge \left( \mathrm{ru}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{ru}^{f,\mathrm{up}}_{\xi,l} \text{ is defined} \right) \wedge \mathrm{prev}^{f}_{\xi,t,l} ``` **`Link_p_ramp_limit_up_start_big_m`** ```math -f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \widetilde{\mathrm{ru}}^{f,\mathrm{up}}_{\xi,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot \mathit{up}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \left( \mathrm{ru}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{ru}^{f,\mathrm{up}}_{\xi,l} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \widetilde{\mathrm{ru}}^{f,\mathrm{up}}_{\xi,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot \mathit{up}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} \wedge \left( \mathrm{ru}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{ru}^{f,\mathrm{up}}_{\xi,l} \text{ is defined} \right) \wedge \mathrm{prev}^{f}_{\xi,t,l} ``` **`Link_p_ramp_limit_down_run_big_m`** ```math -\overleftarrow{f}_{\xi,t,l} - f_{\xi,t,l} \le \widetilde{\mathrm{rd}}^{f}_{\xi,t,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \left( \mathrm{rd}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{rd}^{f,\mathrm{dn}}_{\xi,l} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \right) \wedge \mathrm{on}^{f}_{t,l} +\overleftarrow{f}_{\xi,t,l} - f_{\xi,t,l} \le \widetilde{\mathrm{rd}}^{f}_{\xi,t,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot u^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} \wedge \left( \mathrm{rd}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{rd}^{f,\mathrm{dn}}_{\xi,l} \text{ is defined} \right) \wedge \mathrm{prev}^{f}_{\xi,t,l} ``` **`Link_p_ramp_limit_down_shut_big_m`** ```math -\overleftarrow{f}_{\xi,t,l} - f_{\xi,t,l} \le \widetilde{\mathrm{rd}}^{f,\mathrm{dn}}_{\xi,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot \mathit{dn}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \wedge \left( \mathrm{rd}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{rd}^{f,\mathrm{dn}}_{\xi,l} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \right) \wedge \mathrm{on}^{f}_{t,l} +\overleftarrow{f}_{\xi,t,l} - f_{\xi,t,l} \le \widetilde{\mathrm{rd}}^{f,\mathrm{dn}}_{\xi,l} \cdot F_{l} + \mathrm{M}^{f}_{\xi,l} - \mathrm{M}^{f}_{\xi,l} \cdot \mathit{dn}^{f}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com,ext}}_{t,l} \wedge \left( \mathrm{rd}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{rd}^{f,\mathrm{dn}}_{\xi,l} \text{ is defined} \right) \wedge \mathrm{prev}^{f}_{\xi,t,l} ``` **`Link_p_ramp_limit_up`** ```math -f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \Delta^{f,+}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \left( \mathrm{ru}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{ru}^{f,\mathrm{up}}_{\xi,l} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \right) \wedge \mathrm{on}^{f}_{t,l} +f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \Delta^{f,+}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \left( \mathrm{ru}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{ru}^{f,\mathrm{up}}_{\xi,l} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \right) \wedge \mathrm{prev}^{f}_{\xi,t,l} \wedge \mathrm{on}^{f}_{t,l} ``` **`Link_p_ramp_limit_down`** ```math -\overleftarrow{f}_{\xi,t,l} - f_{\xi,t,l} \le \Delta^{f,-}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \left( \mathrm{rd}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{rd}^{f,\mathrm{dn}}_{\xi,l} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \right) \wedge \mathrm{on}^{f}_{t,l} +\overleftarrow{f}_{\xi,t,l} - f_{\xi,t,l} \le \Delta^{f,-}_{\xi,t,l} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \left( \mathrm{rd}^{f}_{\xi,t,l} \text{ is defined} \vee \mathrm{rd}^{f,\mathrm{dn}}_{\xi,l} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{f}_{l} \wedge \mathrm{ext}^{f}_{l} \wedge \neg \left( \mathrm{f}^{\mathrm{mod}}_{l} > 0 \right) \right) \wedge \mathrm{prev}^{f}_{\xi,t,l} \wedge \mathrm{on}^{f}_{t,l} ``` #### Definitions @@ -384,6 +387,14 @@ f_{\xi,t,l} - \overleftarrow{f}_{\xi,t,l} \le \Delta^{f,+}_{\xi,t,l} \qquad \for \Delta^{f,-}_{\xi,t,l} = \begin{cases} \widetilde{\mathrm{rd}}^{f}_{\xi,t,l} \cdot \widehat{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot u^{f}_{\xi,t,l} + \widetilde{\mathrm{rd}}^{f,\mathrm{dn}}_{\xi,l} \cdot \widehat{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} \cdot \left( \overleftarrow{u}^{f}_{\xi,t,l} - u^{f}_{\xi,t,l} \right) & \text{if } \mathrm{com}^{f}_{l} \\ \widetilde{\mathrm{rd}}^{f}_{\xi,t,l} \cdot \widetilde{\mathrm{f}}^{\mathrm{nom}}_{\xi,l} & \text{otherwise} \end{cases} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} ``` +#### Masks + +**`Link_ramps_from_previous`** + +```math +\mathrm{prev}^{f}_{\xi,t,l} \iff \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{f,0}_{\xi,l} = 0 \vee \mathrm{f}^{0}_{\xi,l} \text{ is defined} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} +``` + #### Assumptions **`Link_came_in_running_unless_committable`** diff --git a/docs/examples/pypsa/process.md b/docs/examples/pypsa/process.md index 39a06c4e..19757167 100644 --- a/docs/examples/pypsa/process.md +++ b/docs/examples/pypsa/process.md @@ -245,6 +245,13 @@ expressions: sum(Process_output_arrival, over=process_output, by=Process_output_bus[bus]) adds_to: Bus_injection +masks: + Process_committed: + description: >- + a committable process that stands in the snapshot's period — every + unit-commitment row's set + where: Process_committable AND Process_active + constraints: Process_fix_p_lower: description: "`Process-fix-p-lower` — a fixed process runs at least its minimum, negative for the other way" @@ -381,6 +388,12 @@ assumptions: | $`\mathit{Process\_additions}`$ | `Process_additions` over $`\mathcal{Y} \times \mathcal{I}`$ | | $`\mathit{Process\_injection}`$ | `Process_injection` over $`\Xi \times \mathcal{T} \times \mathcal{N}`$ | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{z,\mathrm{com}}`$ | `Process_committed` over $`\mathcal{T} \times \mathcal{J}`$ — a committable process that stands in the snapshot's period — every unit-commitment row's set | + $`t \ominus k`$ denotes cyclic translation: index $`t-k`$ taken modulo the size of the dimension (`roll`). Plain $`t-k`$ (`shift`) has no wraparound — terms translated past the edge are simply absent. $`t \boxminus_{v} k`$ denotes translation with $`v`$ standing where index $`t-k`$ leaves the dimension (`shift(edge=v)`), so the row at that boundary is built and carries $`v`$ rather than being dropped. @@ -493,6 +506,14 @@ z_{\xi,t,j} = \mathrm{z}^{\mathrm{set}}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ \mathit{Process\_injection}_{\xi,t,n} = \sum_{r \in \mathcal{R} \,:\, \mathrm{Process\_output\_bus}(r) = n} \overrightarrow{z}_{\xi,t,r} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ n \in \mathcal{N} ``` +#### Masks + +**`Process_committed`** + +```math +\mathrm{on}^{z,\mathrm{com}}_{t,j} \iff \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} \qquad \forall\, t \in \mathcal{T},\ j \in \mathcal{J} +``` + #### Variable domains **`Process_p`** diff --git a/docs/examples/pypsa/process_commitment.md b/docs/examples/pypsa/process_commitment.md index b68a58d8..97d8f13f 100644 --- a/docs/examples/pypsa/process_commitment.md +++ b/docs/examples/pypsa/process_commitment.md @@ -88,21 +88,21 @@ variables: the rows below cap at one, or at the module count where the build is modular dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed domain: integer bounds: lower: 0 Process_start_up: description: "`Process-start_up` — how much of a committable process turns on this snapshot, capped as the status is" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed domain: integer bounds: lower: 0 Process_shut_down: description: "`Process-shut_down` — how much of a committable process turns off this snapshot, capped as the status is" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed domain: integer bounds: lower: 0 @@ -128,6 +128,8 @@ given: Process_p_nom_ext: { dims: [process] } expressions: scenario_opex: { dims: [scenario] } + masks: + Process_committed: { dims: [snapshot, process] } expressions: Process_previous_status: @@ -155,26 +157,38 @@ expressions: + sum(sum(Process_shut_down * Process_shut_down_cost, over=process), over=snapshot) adds_to: scenario_opex +masks: + Process_com_ext: + description: >- + a committable process with an extendable, non-modular build that stands + in the snapshot's period — PyPSA's `com-ext` rows, whose status is + relaxed against the chosen build + where: >- + Process_committable + AND Process_p_nom_extendable + AND NOT (Process_p_nom_mod > 0) + AND Process_active + constraints: Process_com_p_lower: description: "`Process-com-p-lower` — a committed process runs at least its minimum; off, at least nothing" dims: [scenario, snapshot, process] - where: Process_committable AND not Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_p >= Process_p_min_pu * Process_p_nom * (Process_status - Process_maintenance_pu * Process_maintenance_status) Process_com_p_upper: description: "`Process-com-p-upper` — a committed process runs at most what is available; off, at most nothing" dims: [scenario, snapshot, process] - where: Process_committable AND not Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_p <= Process_p_max_pu * Process_p_nom * (Process_status - Process_maintenance_pu * Process_maintenance_status) Process_com_transition_start_up: description: "`Process-com-transition-start-up` — turning on is a start, counted against the state the process carried into the snapshot" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed expression: Process_start_up >= Process_status - Process_previous_status Process_com_transition_shut_down: description: "`Process-com-transition-shut-down` — turning off is a stop, counted against the state the process carried into the snapshot" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed expression: Process_shut_down >= Process_previous_status - Process_status Process_com_up_time: description: >- @@ -182,7 +196,7 @@ constraints: 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: [scenario, snapshot, process] - where: Process_committable AND Process_min_up_time > 0 AND position(snapshot) > 0 AND Process_active + where: Process_committed AND Process_min_up_time > 0 AND position(snapshot) > 0 expression: sum_back(Process_start_up, along=snapshot, window=Process_min_up_time) <= Process_status Process_com_down_time: description: >- @@ -190,38 +204,38 @@ constraints: time is still off. The first snapshot's share of the window is the brought-in down time's, which the must-stay-down mask carries dims: [scenario, snapshot, process] - where: Process_committable AND Process_min_down_time > 0 AND position(snapshot) > 0 AND Process_active + where: Process_committed AND Process_min_down_time > 0 AND position(snapshot) > 0 expression: sum_back(Process_shut_down, along=snapshot, window=Process_min_down_time) <= 1 - Process_status Process_com_status_must_stay_up: description: "`Process-com-status-min_up_time_must_stay_up` — a process still serving the up time it brought in stays on" dims: [scenario, snapshot, process] - where: Process_committable AND Process_must_stay_up AND Process_active + where: Process_committed AND Process_must_stay_up expression: Process_status == 1 Process_com_status_must_stay_down: description: >- `Process-com-status-min_down_time_must_stay_up` — a process still serving the down time it brought in stays off; PyPSA names the row `_must_stay_up` dims: [scenario, snapshot, process] - where: Process_committable AND Process_must_stay_down AND Process_active + where: Process_committed AND Process_must_stay_down expression: Process_status == 0 Process_com_ext_p_upper_cap: description: >- `Process-com-ext-p-upper-cap` — a committed extendable process runs at most what is available of the chosen build, whatever its status dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext expression: Process_p <= Process_p_max_pu * (Process_p_nom_ext - Process_maintenance_pu * Process_maintenance_capacity) Process_com_ext_p_upper_big_m: description: "`Process-com-ext-p-upper-bigM` — off, a process does not run; on, the big M is no bound" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext expression: Process_p <= Process_big_m * Process_status Process_com_ext_p_lower: description: >- `Process-com-ext-p-lower` — a committed extendable process runs at least its minimum of the chosen build; off, the big M releases the row dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext expression: >- Process_p >= Process_p_min_pu * (Process_p_nom_ext - Process_maintenance_pu * Process_maintenance_capacity) @@ -232,23 +246,21 @@ constraints: negative, internal power is also plainly non-negative, a row the big-M lower cannot assert while the process is off dims: [scenario, snapshot, process] - where: >- - Process_committable AND Process_p_nom_extendable - AND Process_p_min_pu_nonneg AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext AND Process_p_min_pu_nonneg expression: Process_p >= 0 Process_com_mod_p_lower: description: >- `Process-com-mod-p-lower` — a committed modular process runs at least its minimum of one module, whether the build is fixed or a decision dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_mod > 0 expression: Process_p >= Process_p_min_pu * Process_p_nom_mod * (Process_status - Process_maintenance_pu * Process_maintenance_status) Process_com_mod_p_upper: description: >- `Process-com-mod-p-upper` — a committed modular process runs at most one module's share, whether the build is fixed or a decision dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_mod > 0 expression: Process_p <= Process_p_max_pu * Process_p_nom_mod * (Process_status - Process_maintenance_pu * Process_maintenance_status) Process_status_p_fixed_upper: description: >- @@ -256,7 +268,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, process] - where: Process_committable AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) expression: Process_status <= Process_modules_installed Process_start_up_p_fixed_upper: description: >- @@ -264,7 +276,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, process] - where: Process_committable AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) expression: Process_start_up <= Process_modules_installed Process_shut_down_p_fixed_upper: description: >- @@ -272,22 +284,22 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, process] - where: Process_committable AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) expression: Process_shut_down <= Process_modules_installed Process_status_p_nom_variable_upper: description: "`Process-status-p_nom-variable-upper` — a modular process is on only where a module is built" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0 expression: Process_status <= Process_n_mod Process_start_up_p_nom_variable_upper: description: "`Process-start_up-p_nom-variable-upper` — a modular process starts only where a module is built" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0 expression: Process_start_up <= Process_n_mod Process_shut_down_p_nom_variable_upper: description: "`Process-shut_down-p_nom-variable-upper` — a modular process stops only where a module is built" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0 expression: Process_shut_down <= Process_n_mod ``` @@ -344,6 +356,7 @@ constraints: | $`\mu^{z,u}`$ | `Process_maintenance_status` over $`\Xi \times \mathcal{T} \times \mathcal{J}`$ | | $`Z`$ | `Process_p_nom_ext` over $`\mathcal{J}`$ | | $`\mathit{scenario\_opex}`$ | `scenario_opex` over $`\Xi`$, an expression this file adds `Process_commitment_opex` to | +| $`\mathrm{on}^{z,\mathrm{com}}`$ | `Process_committed` over $`\mathcal{T} \times \mathcal{J}`$, a mask another file defines | #### Definitions @@ -353,6 +366,12 @@ constraints: | $`\overleftarrow{u}^{\circ z}`$ | `Process_status_carried_over` over $`\Xi \times \mathcal{T} \times \mathcal{J}`$ — the state a process carries over into a snapshot — the state it brought into the horizon at the first, the previous snapshot's after that | | $`\mathit{Process\_commitment\_opex}`$ | `Process_commitment_opex` over $`\Xi`$ | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{z,\mathrm{com,ext}}`$ | `Process_com_ext` over $`\mathcal{T} \times \mathcal{J}`$ — a committable process with an extendable, non-modular build that stands in the snapshot's period — PyPSA's `com-ext` rows, whose status is relaxed against the chosen build | + $`\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. #### Subject to @@ -360,121 +379,121 @@ $`\mathrm{pos}(t)`$ denotes where index $`t`$ sits along its dimension's own ord **`Process_com_p_lower`** ```math -z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{nom}}_{\xi,j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{nom}}_{\xi,j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` **`Process_com_p_upper`** ```math -z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{nom}}_{\xi,j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{nom}}_{\xi,j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` **`Process_com_transition_start_up`** ```math -\mathit{up}^{z}_{\xi,t,j} \ge u^{z}_{\xi,t,j} - \overleftarrow{u}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +\mathit{up}^{z}_{\xi,t,j} \ge u^{z}_{\xi,t,j} - \overleftarrow{u}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` **`Process_com_transition_shut_down`** ```math -\mathit{dn}^{z}_{\xi,t,j} \ge \overleftarrow{u}^{z}_{\xi,t,j} - u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +\mathit{dn}^{z}_{\xi,t,j} \ge \overleftarrow{u}^{z}_{\xi,t,j} - u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` **`Process_com_up_time`** ```math -\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}^{z}} \mathit{up}^{z}_{\xi,t',j} \le u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{UT}^{z}_{\xi,j} > 0 \wedge \mathrm{pos}(t) > 0 \wedge \mathrm{on}^{z}_{t,j} +\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}^{z}} \mathit{up}^{z}_{\xi,t',j} \le u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{UT}^{z}_{\xi,j} > 0 \wedge \mathrm{pos}(t) > 0 ``` **`Process_com_down_time`** ```math -\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}^{z}} \mathit{dn}^{z}_{\xi,t',j} \le 1 - u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{DT}^{z}_{\xi,j} > 0 \wedge \mathrm{pos}(t) > 0 \wedge \mathrm{on}^{z}_{t,j} +\sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}^{z}} \mathit{dn}^{z}_{\xi,t',j} \le 1 - u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{DT}^{z}_{\xi,j} > 0 \wedge \mathrm{pos}(t) > 0 ``` **`Process_com_status_must_stay_up`** ```math -u^{z}_{\xi,t,j} = 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{hold}^{z}_{\xi,t,j} \wedge \mathrm{on}^{z}_{t,j} +u^{z}_{\xi,t,j} = 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{hold}^{z}_{\xi,t,j} ``` **`Process_com_status_must_stay_down`** ```math -u^{z}_{\xi,t,j} = 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{rest}^{z}_{\xi,t,j} \wedge \mathrm{on}^{z}_{t,j} +u^{z}_{\xi,t,j} = 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{rest}^{z}_{\xi,t,j} ``` **`Process_com_ext_p_upper_cap`** ```math -z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \left( Z_{j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,\mathrm{nom}}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \left( Z_{j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,\mathrm{nom}}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} ``` **`Process_com_ext_p_upper_big_m`** ```math -z_{\xi,t,j} \le \mathrm{M}^{z}_{\xi,j} \cdot u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \le \mathrm{M}^{z}_{\xi,j} \cdot u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} ``` **`Process_com_ext_p_lower`** ```math -z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \left( Z_{j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,\mathrm{nom}}_{\xi,t,j} \right) + \mathrm{M}^{z}_{\xi,j} \cdot u^{z}_{\xi,t,j} - \mathrm{M}^{z}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \left( Z_{j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,\mathrm{nom}}_{\xi,t,j} \right) + \mathrm{M}^{z}_{\xi,j} \cdot u^{z}_{\xi,t,j} - \mathrm{M}^{z}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} ``` **`Process_com_ext_p_lower_nonneg`** ```math -z_{\xi,t,j} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{nonneg}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} \wedge \mathrm{nonneg}^{z}_{j} ``` **`Process_com_mod_p_lower`** ```math -z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{mod}}_{j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \ge \underline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{mod}}_{j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` **`Process_com_mod_p_upper`** ```math -z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{mod}}_{j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} \le \overline{\mathrm{z}}_{\xi,t,j} \cdot \mathrm{z}^{\mathrm{mod}}_{j} \cdot \left( u^{z}_{\xi,t,j} - \gamma^{z}_{\xi,j} \cdot \mu^{z,u}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` **`Process_status_p_fixed_upper`** ```math -u^{z}_{\xi,t,j} \le \mathrm{N}^{z,\mathrm{fix}}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +u^{z}_{\xi,t,j} \le \mathrm{N}^{z,\mathrm{fix}}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` **`Process_start_up_p_fixed_upper`** ```math -\mathit{up}^{z}_{\xi,t,j} \le \mathrm{N}^{z,\mathrm{fix}}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mathit{up}^{z}_{\xi,t,j} \le \mathrm{N}^{z,\mathrm{fix}}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` **`Process_shut_down_p_fixed_upper`** ```math -\mathit{dn}^{z}_{\xi,t,j} \le \mathrm{N}^{z,\mathrm{fix}}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mathit{dn}^{z}_{\xi,t,j} \le \mathrm{N}^{z,\mathrm{fix}}_{\xi,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` **`Process_status_p_nom_variable_upper`** ```math -u^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +u^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` **`Process_start_up_p_nom_variable_upper`** ```math -\mathit{up}^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +\mathit{up}^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` **`Process_shut_down_p_nom_variable_upper`** ```math -\mathit{dn}^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +\mathit{dn}^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{ext}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` #### Definitions @@ -497,23 +516,31 @@ u^{z}_{\xi,t,j} \le N^{z}_{j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ \mathit{Process\_commitment\_opex}_{\xi} = \sum_{t \in \mathcal{T}} \sum_{j \in \mathcal{J}} u^{z}_{\xi,t,j} \cdot \mathrm{c}^{z,\mathrm{on}}_{\xi,t,j} \cdot \mathrm{w}_{t} \cdot \mathrm{w}^{y}_{\mathrm{snapshot\_period}(t)} + \sum_{t \in \mathcal{T}} \sum_{j \in \mathcal{J}} \mathit{up}^{z}_{\xi,t,j} \cdot \mathrm{c}^{z,\mathrm{up}}_{\xi,t,j} + \sum_{t \in \mathcal{T}} \sum_{j \in \mathcal{J}} \mathit{dn}^{z}_{\xi,t,j} \cdot \mathrm{c}^{z,\mathrm{dn}}_{\xi,t,j} \qquad \forall\, \xi \in \Xi ``` +#### Masks + +**`Process_com_ext`** + +```math +\mathrm{on}^{z,\mathrm{com,ext}}_{t,j} \iff \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} \qquad \forall\, t \in \mathcal{T},\ j \in \mathcal{J} +``` + #### Variable domains **`Process_status`** ```math -u^{z}_{\xi,t,j} \ge 0, u^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +u^{z}_{\xi,t,j} \ge 0, u^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` **`Process_start_up`** ```math -\mathit{up}^{z}_{\xi,t,j} \ge 0, \mathit{up}^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +\mathit{up}^{z}_{\xi,t,j} \ge 0, \mathit{up}^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` **`Process_shut_down`** ```math -\mathit{dn}^{z}_{\xi,t,j} \ge 0, \mathit{dn}^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +\mathit{dn}^{z}_{\xi,t,j} \ge 0, \mathit{dn}^{z}_{\xi,t,j} \in \mathbb{Z} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` diff --git a/docs/examples/pypsa/process_maintenance.md b/docs/examples/pypsa/process_maintenance.md index 33bb93a0..ea2b545a 100644 --- a/docs/examples/pypsa/process_maintenance.md +++ b/docs/examples/pypsa/process_maintenance.md @@ -82,9 +82,7 @@ variables: `Process-maintenance_capacity` — the chosen build while in maintenance, zero otherwise: the product the `maintcap` rows linearize dims: [scenario, snapshot, process] - where: >- - Process_maintainable AND Process_p_nom_extendable - AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext absence: zero bounds: lower: 0 @@ -95,8 +93,9 @@ variables: maintenance may also be off dims: [scenario, snapshot, process] where: >- - Process_maintainable AND Process_committable - AND NOT (Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) AND Process_active + Process_maintainable + AND Process_committed + AND NOT (Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) absence: zero bounds: lower: 0 @@ -113,6 +112,20 @@ given: variables: Process_status: { dims: [scenario, snapshot, process], domain: integer } Process_p_nom_ext: { dims: [process] } + masks: + Process_committed: { dims: [snapshot, process] } + +masks: + Process_maint_ext: + description: >- + a maintainable process with an extendable build, unless it is + committable and modular, that stands in the snapshot's period — the + maintenance rows against the chosen build + where: >- + Process_maintainable + AND Process_p_nom_extendable + AND NOT (Process_committable AND Process_p_nom_mod > 0) + AND Process_active constraints: Process_maint_event_count: @@ -138,54 +151,54 @@ constraints: `Process-maintcap_upper` — the build taken off is at most the chosen build in maintenance, and at most the build less its floor out of it dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext expression: Process_maintenance_capacity <= Process_p_nom_ext - Process_p_nom_min * (1 - Process_maintenance) Process_maintcap_upper_nommax: description: "`Process-maintcap_upper_nommax` — out of maintenance, no build is taken off" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext expression: Process_maintenance_capacity <= Process_p_nom_max * Process_maintenance Process_maintcap_lower_nommax: description: "`Process-maintcap_lower_nommax` — in maintenance, the whole chosen build is taken off" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext expression: Process_maintenance_capacity >= Process_p_nom_ext - Process_p_nom_max * (1 - Process_maintenance) Process_maintcap_lower_nommin: description: "`Process-maintcap_lower_nommin` — in maintenance, at least the floor of the build is taken off" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active AND Process_p_nom_min > 0 + where: Process_maint_ext AND Process_p_nom_min > 0 expression: Process_maintenance_capacity >= Process_p_nom_min * Process_maintenance Process_maint_status_le_status: description: "`Process-maint-status-le-status` — the status in maintenance is at most the status" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_maintainable AND Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_maintenance_status <= Process_status Process_maint_status_le_maint: description: "`Process-maint-status-le-maint` — out of maintenance, the status in maintenance is zero" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_maintainable AND Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_maintenance_status <= Process_maintenance Process_maint_status_lb: description: "`Process-maint-status-lb` — on and in maintenance, the status in maintenance is one" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_maintainable AND Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_maintenance_status >= Process_status + Process_maintenance - 1 Process_maint_modstatus_le_status: description: "`Process-maint-modstatus-le-status` — the modules on in maintenance are at most the modules on" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_maintainable AND Process_committed AND Process_p_nom_mod > 0 expression: Process_maintenance_status <= Process_status Process_maint_modstatus_le_maint: description: >- `Process-maint-modstatus-le-maint` — out of maintenance, no module is on in maintenance; in it, at most the modules the build cap holds dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_maintainable AND Process_committed AND Process_p_nom_mod > 0 expression: Process_maintenance_status <= Process_p_nom_max / Process_p_nom_mod * Process_maintenance Process_maint_modstatus_lb: description: "`Process-maint-modstatus-lb` — in maintenance, every module on is on in maintenance" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_maintainable AND Process_committed AND Process_p_nom_mod > 0 expression: Process_maintenance_status >= Process_status - Process_p_nom_max / Process_p_nom_mod * (1 - Process_maintenance) assumptions: @@ -272,6 +285,13 @@ assumptions: | $`\mathrm{w}^{\mathrm{gen}}`$ | `snapshot_weightings_generators` over $`\mathcal{T}`$, data another file declares | | $`u^{z}`$ | `Process_status` over $`\Xi \times \mathcal{T} \times \mathcal{J}`$ | | $`Z`$ | `Process_p_nom_ext` over $`\mathcal{J}`$ | +| $`\mathrm{on}^{z,\mathrm{com}}`$ | `Process_committed` over $`\mathcal{T} \times \mathcal{J}`$, a mask another file defines | + +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{z,\mathrm{mnt,ext}}`$ | `Process_maint_ext` over $`\mathcal{T} \times \mathcal{J}`$ — a maintainable process with an extendable build, unless it is committable and modular, that stands in the snapshot's period — the maintenance rows against the chosen build | #### Subject to @@ -296,61 +316,69 @@ assumptions: **`Process_maintcap_upper`** ```math -\mu^{z,\mathrm{nom}}_{\xi,t,j} \le Z_{j} - \underline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \left( 1 - \mu^{z}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,\mathrm{nom}}_{\xi,t,j} \le Z_{j} - \underline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \left( 1 - \mu^{z}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{mnt,ext}}_{t,j} ``` **`Process_maintcap_upper_nommax`** ```math -\mu^{z,\mathrm{nom}}_{\xi,t,j} \le \overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,\mathrm{nom}}_{\xi,t,j} \le \overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{mnt,ext}}_{t,j} ``` **`Process_maintcap_lower_nommax`** ```math -\mu^{z,\mathrm{nom}}_{\xi,t,j} \ge Z_{j} - \overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \left( 1 - \mu^{z}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,\mathrm{nom}}_{\xi,t,j} \ge Z_{j} - \overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \left( 1 - \mu^{z}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{mnt,ext}}_{t,j} ``` **`Process_maintcap_lower_nommin`** ```math -\mu^{z,\mathrm{nom}}_{\xi,t,j} \ge \underline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} \wedge \underline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} > 0 +\mu^{z,\mathrm{nom}}_{\xi,t,j} \ge \underline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{mnt,ext}}_{t,j} \wedge \underline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} > 0 ``` **`Process_maint_status_le_status`** ```math -\mu^{z,u}_{\xi,t,j} \le u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \le u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` **`Process_maint_status_le_maint`** ```math -\mu^{z,u}_{\xi,t,j} \le \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \le \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` **`Process_maint_status_lb`** ```math -\mu^{z,u}_{\xi,t,j} \ge u^{z}_{\xi,t,j} + \mu^{z}_{\xi,t,j} - 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \ge u^{z}_{\xi,t,j} + \mu^{z}_{\xi,t,j} - 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) ``` **`Process_maint_modstatus_le_status`** ```math -\mu^{z,u}_{\xi,t,j} \le u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \le u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` **`Process_maint_modstatus_le_maint`** ```math -\mu^{z,u}_{\xi,t,j} \le \frac{\overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j}}{\mathrm{z}^{\mathrm{mod}}_{j}} \cdot \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \le \frac{\overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j}}{\mathrm{z}^{\mathrm{mod}}_{j}} \cdot \mu^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 ``` **`Process_maint_modstatus_lb`** ```math -\mu^{z,u}_{\xi,t,j} \ge u^{z}_{\xi,t,j} - \frac{\overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j}}{\mathrm{z}^{\mathrm{mod}}_{j}} \cdot \left( 1 - \mu^{z}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \ge u^{z}_{\xi,t,j} - \frac{\overline{\mathrm{z}}^{\mathrm{nom}}_{\xi,j}}{\mathrm{z}^{\mathrm{mod}}_{j}} \cdot \left( 1 - \mu^{z}_{\xi,t,j} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 +``` + +#### Masks + +**`Process_maint_ext`** + +```math +\mathrm{on}^{z,\mathrm{mnt,ext}}_{t,j} \iff \mathrm{mnt}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} \qquad \forall\, t \in \mathcal{T},\ j \in \mathcal{J} ``` #### Variable domains @@ -370,13 +398,13 @@ assumptions: **`Process_maintenance_capacity`** ```math -\mu^{z,\mathrm{nom}}_{\xi,t,j} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,\mathrm{nom}}_{\xi,t,j} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{mnt,ext}}_{t,j} ``` **`Process_maintenance_status`** ```math -\mu^{z,u}_{\xi,t,j} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{com}^{z}_{j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \right) \wedge \mathrm{on}^{z}_{t,j} +\mu^{z,u}_{\xi,t,j} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{mnt}^{z}_{j} \wedge \mathrm{on}^{z,\mathrm{com}}_{t,j} \wedge \neg \left( \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \right) ``` #### Assumptions diff --git a/docs/examples/pypsa/process_ramping.md b/docs/examples/pypsa/process_ramping.md index 65c24d92..eaeb5ea5 100644 --- a/docs/examples/pypsa/process_ramping.md +++ b/docs/examples/pypsa/process_ramping.md @@ -67,6 +67,8 @@ given: Process_p_nom_effective: { dims: [scenario, process] } Process_previous_status: { dims: [scenario, snapshot, process] } Process_p_nom_committed: { dims: [scenario, process] } + masks: + Process_com_ext: { dims: [snapshot, process] } expressions: Process_previous_p: @@ -139,6 +141,16 @@ expressions: * (Process_previous_status - Process_status) otherwise: Process_ramp_down_rate * Process_p_nom_effective +masks: + Process_ramps_from_previous: + description: >- + a snapshot whose ramp reads a previous output — any snapshot but its + period's first, and the horizon's first where the process comes in off + or carries an initial output + where: >- + position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND + (Process_status_initial == 0 OR Process_p_init)) + constraints: Process_p_ramp_limit_up_run_big_m: description: >- @@ -146,11 +158,7 @@ constraints: raises internal power no faster than its limit of the chosen build; the big M releases the row in the snapshot it turns on dims: [scenario, snapshot, process] - where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) - AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + where: Process_com_ext AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) AND Process_ramps_from_previous expression: >- Process_p - Process_previous_p <= Process_ramp_up_rate * Process_p_nom_ext @@ -161,11 +169,7 @@ constraints: committed extendable process ramps no further than its start-up ramp of the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, process] - where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) - AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + where: Process_com_ext AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) AND Process_ramps_from_previous expression: >- Process_p - Process_previous_p <= Process_start_up_rate * Process_p_nom_ext @@ -177,10 +181,9 @@ constraints: releases the row in the snapshot it turns off dims: [scenario, snapshot, process] where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) + Process_com_ext AND (Process_ramp_limit_down OR Process_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + AND Process_ramps_from_previous expression: >- Process_previous_p - Process_p <= Process_ramp_down_rate * Process_p_nom_ext @@ -192,10 +195,9 @@ constraints: the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, process] where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) + Process_com_ext AND (Process_ramp_limit_down OR Process_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + AND Process_ramps_from_previous expression: >- Process_previous_p - Process_p <= Process_shut_down_rate * Process_p_nom_ext @@ -212,7 +214,7 @@ constraints: where: >- (Process_ramp_limit_up OR Process_ramp_limit_start_up) AND NOT (Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) + AND Process_ramps_from_previous AND Process_active expression: Process_p - Process_previous_p <= Process_ramp_up_allowance Process_p_ramp_limit_down: @@ -227,7 +229,7 @@ constraints: where: >- (Process_ramp_limit_down OR Process_ramp_limit_shut_down) AND NOT (Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) + AND Process_ramps_from_previous AND Process_active expression: Process_previous_p - Process_p <= Process_ramp_down_allowance @@ -285,6 +287,7 @@ assumptions: | $`\widetilde{\mathrm{z}}^{\mathrm{nom}}`$ | `Process_p_nom_effective` over $`\Xi \times \mathcal{J}`$, an expression another file defines | | $`\overleftarrow{u}^{z}`$ | `Process_previous_status` over $`\Xi \times \mathcal{T} \times \mathcal{J}`$, an expression another file defines | | $`\widehat{\mathrm{z}}^{\mathrm{nom}}`$ | `Process_p_nom_committed` over $`\Xi \times \mathcal{J}`$, an expression another file defines | +| $`\mathrm{on}^{z,\mathrm{com,ext}}`$ | `Process_com_ext` over $`\mathcal{T} \times \mathcal{J}`$, a mask another file defines | #### Definitions @@ -298,6 +301,12 @@ assumptions: | $`\Delta^{z,+}`$ | `Process_ramp_up_allowance` over $`\Xi \times \mathcal{T} \times \mathcal{J}`$ — how far a process may raise internal power between two snapshots — its ramp limit of the build while it stays on, plus its start-up ramp in the snapshot it turns on | | $`\Delta^{z,-}`$ | `Process_ramp_down_allowance` over $`\Xi \times \mathcal{T} \times \mathcal{J}`$ — how far a process may lower internal power between two snapshots — its ramp limit of the build while it stays on, plus its shut-down ramp in the snapshot it turns off | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{prev}^{z}`$ | `Process_ramps_from_previous` over $`\Xi \times \mathcal{T} \times \mathcal{J}`$ — a snapshot whose ramp reads a previous output — any snapshot but its period's first, and the horizon's first where the process comes in off or carries an initial output | + $`\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. $`\mathrm{pos}_{\mathrm{relation}(t)}(t)`$ counts within the group a relation puts $`t`$ in: the subscript names the map, $`\mathcal{T}_{\mathrm{relation}(t)}`$ is the group it lands in, and that group has a first position of its own. @@ -307,37 +316,37 @@ $`\mathrm{pos}_{\mathrm{relation}(t)}(t)`$ counts within the group a relation pu **`Process_p_ramp_limit_up_run_big_m`** ```math -z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \widetilde{\mathrm{ru}}^{z}_{\xi,t,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot \overleftarrow{u}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \left( \mathrm{ru}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{ru}^{z,\mathrm{up}}_{\xi,j} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \widetilde{\mathrm{ru}}^{z}_{\xi,t,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot \overleftarrow{u}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} \wedge \left( \mathrm{ru}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{ru}^{z,\mathrm{up}}_{\xi,j} \text{ is defined} \right) \wedge \mathrm{prev}^{z}_{\xi,t,j} ``` **`Process_p_ramp_limit_up_start_big_m`** ```math -z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \widetilde{\mathrm{ru}}^{z,\mathrm{up}}_{\xi,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot \mathit{up}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \left( \mathrm{ru}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{ru}^{z,\mathrm{up}}_{\xi,j} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \widetilde{\mathrm{ru}}^{z,\mathrm{up}}_{\xi,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot \mathit{up}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} \wedge \left( \mathrm{ru}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{ru}^{z,\mathrm{up}}_{\xi,j} \text{ is defined} \right) \wedge \mathrm{prev}^{z}_{\xi,t,j} ``` **`Process_p_ramp_limit_down_run_big_m`** ```math -\overleftarrow{z}_{\xi,t,j} - z_{\xi,t,j} \le \widetilde{\mathrm{rd}}^{z}_{\xi,t,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \left( \mathrm{rd}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{rd}^{z,\mathrm{dn}}_{\xi,j} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \right) \wedge \mathrm{on}^{z}_{t,j} +\overleftarrow{z}_{\xi,t,j} - z_{\xi,t,j} \le \widetilde{\mathrm{rd}}^{z}_{\xi,t,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot u^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} \wedge \left( \mathrm{rd}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{rd}^{z,\mathrm{dn}}_{\xi,j} \text{ is defined} \right) \wedge \mathrm{prev}^{z}_{\xi,t,j} ``` **`Process_p_ramp_limit_down_shut_big_m`** ```math -\overleftarrow{z}_{\xi,t,j} - z_{\xi,t,j} \le \widetilde{\mathrm{rd}}^{z,\mathrm{dn}}_{\xi,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot \mathit{dn}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \wedge \left( \mathrm{rd}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{rd}^{z,\mathrm{dn}}_{\xi,j} \text{ is defined} \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \right) \wedge \mathrm{on}^{z}_{t,j} +\overleftarrow{z}_{\xi,t,j} - z_{\xi,t,j} \le \widetilde{\mathrm{rd}}^{z,\mathrm{dn}}_{\xi,j} \cdot Z_{j} + \mathrm{M}^{z}_{\xi,j} - \mathrm{M}^{z}_{\xi,j} \cdot \mathit{dn}^{z}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com,ext}}_{t,j} \wedge \left( \mathrm{rd}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{rd}^{z,\mathrm{dn}}_{\xi,j} \text{ is defined} \right) \wedge \mathrm{prev}^{z}_{\xi,t,j} ``` **`Process_p_ramp_limit_up`** ```math -z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \Delta^{z,+}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \left( \mathrm{ru}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{ru}^{z,\mathrm{up}}_{\xi,j} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \right) \wedge \mathrm{on}^{z}_{t,j} +z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \Delta^{z,+}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \left( \mathrm{ru}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{ru}^{z,\mathrm{up}}_{\xi,j} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \right) \wedge \mathrm{prev}^{z}_{\xi,t,j} \wedge \mathrm{on}^{z}_{t,j} ``` **`Process_p_ramp_limit_down`** ```math -\overleftarrow{z}_{\xi,t,j} - z_{\xi,t,j} \le \Delta^{z,-}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \left( \mathrm{rd}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{rd}^{z,\mathrm{dn}}_{\xi,j} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \right) \wedge \left( \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \right) \wedge \mathrm{on}^{z}_{t,j} +\overleftarrow{z}_{\xi,t,j} - z_{\xi,t,j} \le \Delta^{z,-}_{\xi,t,j} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \left( \mathrm{rd}^{z}_{\xi,t,j} \text{ is defined} \vee \mathrm{rd}^{z,\mathrm{dn}}_{\xi,j} \text{ is defined} \right) \wedge \neg \left( \mathrm{com}^{z}_{j} \wedge \mathrm{ext}^{z}_{j} \wedge \neg \left( \mathrm{z}^{\mathrm{mod}}_{j} > 0 \right) \right) \wedge \mathrm{prev}^{z}_{\xi,t,j} \wedge \mathrm{on}^{z}_{t,j} ``` #### Definitions @@ -384,6 +393,14 @@ z_{\xi,t,j} - \overleftarrow{z}_{\xi,t,j} \le \Delta^{z,+}_{\xi,t,j} \qquad \for \Delta^{z,-}_{\xi,t,j} = \begin{cases} \widetilde{\mathrm{rd}}^{z}_{\xi,t,j} \cdot \widehat{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot u^{z}_{\xi,t,j} + \widetilde{\mathrm{rd}}^{z,\mathrm{dn}}_{\xi,j} \cdot \widehat{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} \cdot \left( \overleftarrow{u}^{z}_{\xi,t,j} - u^{z}_{\xi,t,j} \right) & \text{if } \mathrm{com}^{z}_{j} \\ \widetilde{\mathrm{rd}}^{z}_{\xi,t,j} \cdot \widetilde{\mathrm{z}}^{\mathrm{nom}}_{\xi,j} & \text{otherwise} \end{cases} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} ``` +#### Masks + +**`Process_ramps_from_previous`** + +```math +\mathrm{prev}^{z}_{\xi,t,j} \iff \mathrm{pos}_{\mathrm{snapshot\_period}(t)}(t) > 0 \vee \mathrm{pos}(t) = 0 \wedge \left( \mathrm{u}^{z,0}_{\xi,j} = 0 \vee \mathrm{z}^{0}_{\xi,j} \text{ is defined} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} +``` + #### Assumptions **`Process_came_in_running_unless_committable`** diff --git a/docs/examples/pypsa/storage_unit.md b/docs/examples/pypsa/storage_unit.md index ca5e9944..ee22aaab 100644 --- a/docs/examples/pypsa/storage_unit.md +++ b/docs/examples/pypsa/storage_unit.md @@ -323,70 +323,82 @@ expressions: sum(StorageUnit_sign * (StorageUnit_p_dispatch - StorageUnit_p_store), over=storage_unit, by=StorageUnit_bus[bus]) adds_to: Bus_injection +masks: + StorageUnit_fix: + description: >- + a storage unit with a fixed build that stands in the snapshot's period — + PyPSA's `fix` rows + where: NOT StorageUnit_p_nom_extendable AND StorageUnit_active + StorageUnit_ext: + description: >- + a storage unit with an extendable build that stands in the snapshot's + period — PyPSA's `ext` rows + where: StorageUnit_p_nom_extendable AND StorageUnit_active + constraints: StorageUnit_fix_p_dispatch_lower: description: "`StorageUnit-fix-p_dispatch-lower` — dispatch is non-negative" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_dispatch >= 0 StorageUnit_fix_p_dispatch_upper: description: "`StorageUnit-fix-p_dispatch-upper` — a fixed unit dispatches at most its nominal power" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_dispatch <= StorageUnit_p_max_pu * StorageUnit_p_nom StorageUnit_fix_p_store_lower: description: "`StorageUnit-fix-p_store-lower` — storing is non-negative" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_store >= 0 StorageUnit_fix_p_store_upper: description: >- `StorageUnit-fix-p_store-upper` — a fixed unit stores at most its nominal power, the minimum-per-unit column carrying that cap negated dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_store <= -StorageUnit_p_min_pu * StorageUnit_p_nom StorageUnit_fix_state_of_charge_lower: description: "`StorageUnit-fix-state_of_charge-lower` — charge is non-negative" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_state_of_charge >= 0 StorageUnit_fix_state_of_charge_upper: description: "`StorageUnit-fix-state_of_charge-upper` — a fixed unit holds at most its hours at nominal power" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_state_of_charge <= StorageUnit_max_hours * StorageUnit_p_nom StorageUnit_ext_p_dispatch_lower: description: "`StorageUnit-ext-p_dispatch-lower` — dispatch is non-negative" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_dispatch >= 0 StorageUnit_ext_p_dispatch_upper: description: "`StorageUnit-ext-p_dispatch-upper` — an extendable unit dispatches at most the chosen build" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_dispatch <= StorageUnit_p_max_pu * StorageUnit_p_nom_ext StorageUnit_ext_p_store_lower: description: "`StorageUnit-ext-p_store-lower` — storing is non-negative" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_store >= 0 StorageUnit_ext_p_store_upper: description: >- `StorageUnit-ext-p_store-upper` — an extendable unit stores at most the chosen build, the minimum-per-unit column carrying that cap negated dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_store <= -StorageUnit_p_min_pu * StorageUnit_p_nom_ext StorageUnit_ext_state_of_charge_lower: description: "`StorageUnit-ext-state_of_charge-lower` — charge is non-negative" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_state_of_charge >= 0 StorageUnit_ext_state_of_charge_upper: description: "`StorageUnit-ext-state_of_charge-upper` — an extendable unit holds at most its hours at the chosen build" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_state_of_charge <= StorageUnit_max_hours * StorageUnit_p_nom_ext StorageUnit_ext_p_nom_lower: description: "`StorageUnit-ext-p_nom-lower` — the chosen build is at least its floor in every scenario" @@ -591,6 +603,13 @@ assumptions: | $`\mathit{StorageUnit\_additions}`$ | `StorageUnit_additions` over $`\mathcal{Y} \times \mathcal{I}`$ | | $`\mathit{StorageUnit\_injection}`$ | `StorageUnit_injection` over $`\Xi \times \mathcal{T} \times \mathcal{N}`$ | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{h,\mathrm{fix}}`$ | `StorageUnit_fix` over $`\mathcal{T} \times \mathcal{S}`$ — a storage unit with a fixed build that stands in the snapshot's period — PyPSA's `fix` rows | +| $`\mathrm{on}^{h,\mathrm{ext}}`$ | `StorageUnit_ext` over $`\mathcal{T} \times \mathcal{S}`$ — a storage unit with an extendable build that stands in the snapshot's period — PyPSA's `ext` rows | + $`t \ominus k`$ denotes cyclic translation: index $`t-k`$ taken modulo the size of the dimension (`roll`). Plain $`t-k`$ (`shift`) has no wraparound — terms translated past the edge are simply absent. $`t \ominus^{\mathrm{relation}(t)} k`$ denotes a translation counted inside the group a relation puts $`t`$ in (`shift(within=relation[c])`), so a term never crosses out of its own group. @@ -606,73 +625,73 @@ $`\lvert \mathcal{T} \rvert`$ denotes the size of the set being counted along, a **`StorageUnit_fix_p_dispatch_lower`** ```math -h^{+}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{+}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{fix}}_{t,s} ``` **`StorageUnit_fix_p_dispatch_upper`** ```math -h^{+}_{\xi,t,s} \le \overline{\mathrm{h}}_{\xi,t,s} \cdot \mathrm{h}^{\mathrm{nom}}_{\xi,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{+}_{\xi,t,s} \le \overline{\mathrm{h}}_{\xi,t,s} \cdot \mathrm{h}^{\mathrm{nom}}_{\xi,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{fix}}_{t,s} ``` **`StorageUnit_fix_p_store_lower`** ```math -h^{-}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{-}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{fix}}_{t,s} ``` **`StorageUnit_fix_p_store_upper`** ```math -h^{-}_{\xi,t,s} \le -\underline{\mathrm{h}}_{\xi,t,s} \cdot \mathrm{h}^{\mathrm{nom}}_{\xi,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{-}_{\xi,t,s} \le -\underline{\mathrm{h}}_{\xi,t,s} \cdot \mathrm{h}^{\mathrm{nom}}_{\xi,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{fix}}_{t,s} ``` **`StorageUnit_fix_state_of_charge_lower`** ```math -\mathit{soc}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +\mathit{soc}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{fix}}_{t,s} ``` **`StorageUnit_fix_state_of_charge_upper`** ```math -\mathit{soc}_{\xi,t,s} \le \mathrm{T}^{h}_{\xi,s} \cdot \mathrm{h}^{\mathrm{nom}}_{\xi,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +\mathit{soc}_{\xi,t,s} \le \mathrm{T}^{h}_{\xi,s} \cdot \mathrm{h}^{\mathrm{nom}}_{\xi,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{fix}}_{t,s} ``` **`StorageUnit_ext_p_dispatch_lower`** ```math -h^{+}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{+}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{ext}}_{t,s} ``` **`StorageUnit_ext_p_dispatch_upper`** ```math -h^{+}_{\xi,t,s} \le \overline{\mathrm{h}}_{\xi,t,s} \cdot H_{s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{+}_{\xi,t,s} \le \overline{\mathrm{h}}_{\xi,t,s} \cdot H_{s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{ext}}_{t,s} ``` **`StorageUnit_ext_p_store_lower`** ```math -h^{-}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{-}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{ext}}_{t,s} ``` **`StorageUnit_ext_p_store_upper`** ```math -h^{-}_{\xi,t,s} \le -\underline{\mathrm{h}}_{\xi,t,s} \cdot H_{s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +h^{-}_{\xi,t,s} \le -\underline{\mathrm{h}}_{\xi,t,s} \cdot H_{s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{ext}}_{t,s} ``` **`StorageUnit_ext_state_of_charge_lower`** ```math -\mathit{soc}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +\mathit{soc}_{\xi,t,s} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{ext}}_{t,s} ``` **`StorageUnit_ext_state_of_charge_upper`** ```math -\mathit{soc}_{\xi,t,s} \le \mathrm{T}^{h}_{\xi,s} \cdot H_{s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} +\mathit{soc}_{\xi,t,s} \le \mathrm{T}^{h}_{\xi,s} \cdot H_{s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ s \in \mathcal{S} \,:\, \mathrm{on}^{h,\mathrm{ext}}_{t,s} ``` **`StorageUnit_ext_p_nom_lower`** @@ -785,6 +804,20 @@ h^{-}_{\xi,t,s} = \mathrm{h}^{-,\mathrm{set}}_{\xi,t,s} \qquad \forall\, \xi \in \mathit{StorageUnit\_injection}_{\xi,t,n} = \sum_{s \in \mathcal{S} \,:\, \mathrm{StorageUnit\_bus}(s) = n} \mathrm{sgn}^{h}_{s} \cdot \left( h^{+}_{\xi,t,s} - h^{-}_{\xi,t,s} \right) \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ n \in \mathcal{N} ``` +#### Masks + +**`StorageUnit_fix`** + +```math +\mathrm{on}^{h,\mathrm{fix}}_{t,s} \iff \neg \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} \qquad \forall\, t \in \mathcal{T},\ s \in \mathcal{S} +``` + +**`StorageUnit_ext`** + +```math +\mathrm{on}^{h,\mathrm{ext}}_{t,s} \iff \mathrm{ext}^{h}_{s} \wedge \mathrm{on}^{h}_{t,s} \qquad \forall\, t \in \mathcal{T},\ s \in \mathcal{S} +``` + #### Variable domains **`StorageUnit_p_dispatch`** diff --git a/docs/examples/pypsa/transformer.md b/docs/examples/pypsa/transformer.md index a74e06dd..53ae70f0 100644 --- a/docs/examples/pypsa/transformer.md +++ b/docs/examples/pypsa/transformer.md @@ -183,7 +183,7 @@ variables: a line does; absent, and zero in the balance, where the network is lossless dims: [scenario, snapshot, transformer] - where: transmission_losses AND Transformer_active + where: Transformer_lossy absence: zero bounds: lower: 0 @@ -246,6 +246,13 @@ expressions: + sum(Transformer_phase_shift * at(Transformer_phase_shift_cycle_weight, by=snapshot_period[period]), over=transformer) adds_to: Cycle_angle_sum +masks: + Transformer_lossy: + description: >- + a transformer that stands in the snapshot's period, where the run models + transmission losses + where: transmission_losses AND Transformer_active + constraints: Transformer_fix_s_lower: description: "`Transformer-fix-s-lower` — a fixed transformer carries at least the negative of its rating, the loss counted against it" @@ -327,7 +334,7 @@ constraints: Transformer_loss_upper: description: "`Transformer-loss_upper` — a transformer dissipates at most the loss at its rating" dims: [scenario, snapshot, transformer] - where: transmission_losses AND Transformer_active + where: Transformer_lossy expression: Transformer_loss <= Transformer_loss_max Transformer_loss_tangents_forward: description: >- @@ -335,14 +342,14 @@ constraints: loss sits above every cut to its curve for flow one way, as a line's does, over the segment dimension dims: [scenario, snapshot, transformer, segment] - where: transmission_losses AND Transformer_active + where: Transformer_lossy expression: Transformer_loss + Transformer_loss_slope * Transformer_s >= Transformer_loss_offset Transformer_loss_tangents_reverse: description: >- `Transformer-loss_tangents-{k}--1`, `Transformer-loss_secants-neg` — the same fan mirrored, the loss depending on the flow's magnitude dims: [scenario, snapshot, transformer, segment] - where: transmission_losses AND Transformer_active + where: Transformer_lossy expression: Transformer_loss - Transformer_loss_slope * Transformer_s >= Transformer_loss_offset ``` @@ -415,6 +422,12 @@ constraints: | $`\mathit{Transformer\_injection}`$ | `Transformer_injection` over $`\Xi \times \mathcal{T} \times \mathcal{N}`$ | | $`\mathit{Transformer\_angle\_sum}`$ | `Transformer_angle_sum` over $`\Xi \times \mathcal{T} \times \mathcal{C}`$ | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{on}^{\sigma,\mathrm{lossy}}`$ | `Transformer_lossy` over $`\mathcal{T} \times \mathcal{M}`$ — a transformer that stands in the snapshot's period, where the run models transmission losses | + Upright is what the data supplies — a parameter such as $`\mathrm{Transformer\_phase\_shift\_varying}`$, a coordinate map, a label — and italic is what the solver chooses, such as $`\mathit{Transformer\_phase\_shift}`$. An index is italic too, being what a quantifier chooses, and a set is script. #### Subject to @@ -500,19 +513,19 @@ Upright is what the data supplies — a parameter such as $`\mathrm{Transformer\ **`Transformer_loss_upper`** ```math -\ell^{\sigma}_{\xi,t,m} \le \overline{\ell}^{\sigma}_{\xi,t,m} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{\sigma}_{t,m} +\ell^{\sigma}_{\xi,t,m} \le \overline{\ell}^{\sigma}_{\xi,t,m} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M} \,:\, \mathrm{on}^{\sigma,\mathrm{lossy}}_{t,m} ``` **`Transformer_loss_tangents_forward`** ```math -\ell^{\sigma}_{\xi,t,m} + \mathrm{a}^{\sigma}_{\xi,t,m,s} \cdot \sigma_{\xi,t,m} \ge \mathrm{b}^{\sigma}_{\xi,t,m,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M},\ s \in \mathcal{S} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{\sigma}_{t,m} +\ell^{\sigma}_{\xi,t,m} + \mathrm{a}^{\sigma}_{\xi,t,m,s} \cdot \sigma_{\xi,t,m} \ge \mathrm{b}^{\sigma}_{\xi,t,m,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M},\ s \in \mathcal{S} \,:\, \mathrm{on}^{\sigma,\mathrm{lossy}}_{t,m} ``` **`Transformer_loss_tangents_reverse`** ```math -\ell^{\sigma}_{\xi,t,m} - \mathrm{a}^{\sigma}_{\xi,t,m,s} \cdot \sigma_{\xi,t,m} \ge \mathrm{b}^{\sigma}_{\xi,t,m,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M},\ s \in \mathcal{S} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{\sigma}_{t,m} +\ell^{\sigma}_{\xi,t,m} - \mathrm{a}^{\sigma}_{\xi,t,m,s} \cdot \sigma_{\xi,t,m} \ge \mathrm{b}^{\sigma}_{\xi,t,m,s} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M},\ s \in \mathcal{S} \,:\, \mathrm{on}^{\sigma,\mathrm{lossy}}_{t,m} ``` #### Definitions @@ -541,6 +554,14 @@ Upright is what the data supplies — a parameter such as $`\mathrm{Transformer\ \mathit{Transformer\_angle\_sum}_{\xi,t,c} = \sum_{m \in \mathcal{M}} \sigma_{\xi,t,m} \cdot \mathrm{x}^{\sigma}_{\mathrm{snapshot\_period}(t),m,c} + \sum_{m \in \mathcal{M}} \vartheta_{t,m,c} + \sum_{m \in \mathcal{M}} \mathit{Transformer\_phase\_shift}_{\xi,t,m} \cdot \mathrm{Transformer\_phase\_shift\_cycle\_weight}_{\mathrm{snapshot\_period}(t),m,c} \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ c \in \mathcal{C} ``` +#### Masks + +**`Transformer_lossy`** + +```math +\mathrm{on}^{\sigma,\mathrm{lossy}}_{t,m} \iff \mathrm{lossy} \wedge \mathrm{on}^{\sigma}_{t,m} \qquad \forall\, t \in \mathcal{T},\ m \in \mathcal{M} +``` + #### Variable domains **`Transformer_s`** @@ -552,7 +573,7 @@ Upright is what the data supplies — a parameter such as $`\mathrm{Transformer\ **`Transformer_loss`** ```math -\ell^{\sigma}_{\xi,t,m} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M} \,:\, \mathrm{lossy} \wedge \mathrm{on}^{\sigma}_{t,m} +\ell^{\sigma}_{\xi,t,m} \ge 0 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ m \in \mathcal{M} \,:\, \mathrm{on}^{\sigma,\mathrm{lossy}}_{t,m} ``` **`Transformer_phase_shift`** diff --git a/docs/examples/pypsa_linearized_uc.md b/docs/examples/pypsa_linearized_uc.md index 2e8ba38f..e57606bf 100644 --- a/docs/examples/pypsa_linearized_uc.md +++ b/docs/examples/pypsa_linearized_uc.md @@ -463,7 +463,7 @@ Generator_status: ``` ```math -0 \le u_{\xi,t,g} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +0 \le u_{\xi,t,g} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` ### `Generator-start_up` @@ -479,7 +479,7 @@ Generator_start_up: ``` ```math -0 \le \mathit{up}_{\xi,t,g} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +0 \le \mathit{up}_{\xi,t,g} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` ### `Generator-shut_down` @@ -495,7 +495,7 @@ Generator_shut_down: ``` ```math -0 \le \mathit{dn}_{\xi,t,g} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{com}_{g} \wedge \mathrm{on}_{t,g} +0 \le \mathit{dn}_{\xi,t,g} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{on}^{\mathrm{com}}_{t,g} ``` ### `Link-status` @@ -514,7 +514,7 @@ Link_status: ``` ```math -0 \le u^{f}_{\xi,t,l} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +0 \le u^{f}_{\xi,t,l} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` ### `Link-start_up` @@ -530,7 +530,7 @@ Link_start_up: ``` ```math -0 \le \mathit{up}^{f}_{\xi,t,l} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +0 \le \mathit{up}^{f}_{\xi,t,l} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` ### `Link-shut_down` @@ -546,7 +546,7 @@ Link_shut_down: ``` ```math -0 \le \mathit{dn}^{f}_{\xi,t,l} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{com}^{f}_{l} \wedge \mathrm{on}^{f}_{t,l} +0 \le \mathit{dn}^{f}_{\xi,t,l} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ l \in \mathcal{L} \,:\, \mathrm{on}^{f,\mathrm{com}}_{t,l} ``` ### `Process-status` @@ -565,7 +565,7 @@ Process_status: ``` ```math -0 \le u^{z}_{\xi,t,j} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +0 \le u^{z}_{\xi,t,j} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` ### `Process-start_up` @@ -581,7 +581,7 @@ Process_start_up: ``` ```math -0 \le \mathit{up}^{z}_{\xi,t,j} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +0 \le \mathit{up}^{z}_{\xi,t,j} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` ### `Process-shut_down` @@ -597,7 +597,7 @@ Process_shut_down: ``` ```math -0 \le \mathit{dn}^{z}_{\xi,t,j} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{com}^{z}_{j} \wedge \mathrm{on}^{z}_{t,j} +0 \le \mathit{dn}^{z}_{\xi,t,j} \le 1 \qquad \forall\, \xi \in \Xi,\ t \in \mathcal{T},\ j \in \mathcal{J} \,:\, \mathrm{on}^{z,\mathrm{com}}_{t,j} ``` ### `Generator-com-p-before` diff --git a/docs/howto/compose.md b/docs/howto/compose.md index d1f2b50b..a6bcdf65 100644 --- a/docs/howto/compose.md +++ b/docs/howto/compose.md @@ -205,6 +205,7 @@ which each component pins at its own port. | any other declaration | one fragment declares it, and a second is refused | | an entry under `given:` | it is checked against the fragment that introduces the name, then folded into it. Its description fills the declaration where the introducer wrote none | | a given expression | the definition's body carries no dimension the reader's `dims` do not name | +| a given mask | the reader's `dims` name the dimensions the defining fragment's predicate reads, no more and no fewer | | a given entry no fragment introduces | it stays under `given:` until a host model provides it | | an expression with `adds_to:` | the sum it names is the body one fragment defines, if any, followed by every term by its name, in the order of the list. [Terms](../reference/language/declarations.md#terms) gives what is refused | | `objective` | one fragment sets it, and a second is refused. Several fragments contribute to it as terms of a sum the objective reads | diff --git a/docs/reference/language/declarations.md b/docs/reference/language/declarations.md index 3595828b..7a14dfc7 100644 --- a/docs/reference/language/declarations.md +++ b/docs/reference/language/declarations.md @@ -89,8 +89,8 @@ A pinned variable is still a variable. `given:` holds what this file reads and does not build: data under `parameters:`, columns under `variables:`, named expressions under -`expressions:`, and row families under `constraints:`. It takes those four keys -and no other. A file with a `given:` block loads and prints on its own. +`expressions:`, masks under `masks:`, and row families under `constraints:`. It +takes those five keys and no other. A file with a `given:` block loads and prints on its own. ### `given: parameters` @@ -230,7 +230,8 @@ constraints: There is no body. A [term](#terms) of this file may add to the name. This file reads the name as it reads a given variable: a quantity over the frame, of degree one. A `where` does not read it, because a mask is built before any -variable exists. A name declared under both `expressions:` and +variable exists. A predicate another file defines is read as a +[given mask](#given-masks) instead. A name declared under both `expressions:` and `given: expressions:` is refused. The typeset legend lists a given expression under _Given_. @@ -334,6 +335,47 @@ like any other. fragment '#2' adds 't' to 'injection', and 't' reads 'injection' back through 'x' of '#1', so the sum would define itself. A term may not read what reads its sum: write 't' from something else, or define 'x' without 'injection'. ``` +### `given: masks` + +A given mask is a [mask](named.md#masks) this file reads and another file +defines. + +```yaml +dimensions: + period: { dtype: int, ordered: true } + generator: { dtype: str } +given: + variables: + p: { dims: [period, generator] } + masks: + stands: + dims: [period, generator] + description: the generator stands in this period +parameters: + ramp: { dims: [generator] } +constraints: + ramp_up: + dims: [period, generator] + where: stands AND shift(stands, along=period, offset=1) + expression: p - shift(p, along=period, offset=1) <= ramp +``` + +| Field | | | +| ------------- | ------------------------------------------------------ | -------------- | +| `dims` | required. The dimensions the definer's predicate reads | | +| `description` | free text | default `null` | + +There is no predicate. A `where` reads the name as data over the frame, true +or false at each coordinate. A `where` may read a given mask where it may not +read a given expression, since a mask reads nothing a solve decides. In +arithmetic the name is refused, as a mask is. The typeset legend lists a given +mask under _Given_. + +[`merge`](../../howto/compose.md#a-library-of-components) folds a given mask +into the mask another fragment defines. The `dims` name the dimensions the +definer's predicate reads, no more and no fewer. A reading that names a +different set is refused, with both frames. + ## `constraints` One block is one rule. The name of the block is the name of the constraint. diff --git a/docs/reference/language/expressions.md b/docs/reference/language/expressions.md index 29f9ecb7..cdbcfb3c 100644 --- a/docs/reference/language/expressions.md +++ b/docs/reference/language/expressions.md @@ -73,7 +73,7 @@ a square. ## Name resolution One flat namespace covers dimensions, relations, parameters, variables, named -expressions, macros and the built-in operators. A collision is a load error that +expressions, masks, macros and the built-in operators. A collision is a load error that names both declarations, and nothing shadows anything. Position decides which kinds of name are legal: @@ -83,7 +83,7 @@ Position decides which kinds of name are legal: | expression (`p * cost`) | a variable, or a parameter whose values are numbers ([dtype](declarations.md#parameters)) | | dimension argument (`over=`, `along=`) | a dimension, or a list of them for `over=`. Beside `by=`, a name in `over=` is a column of the relation where it has one | | column argument (`by=`, `within=`) | columns of one relation, written `relation[column]` or `relation[column, …]` | -| `where` string | a parameter, variable, dimension or relation ([where strings](#where-strings)) | +| `where` string | a parameter, variable, dimension, relation or mask ([where strings](#where-strings)) | | `bounds.lower` / `bounds.upper` | a parameter name, or a number | | the `edge` key of `shift` | `'wrap'` in quotes, or a bare number | | `dual` argument (`dual(c)`) | a constraint. It resolves against the constraints alone ([named expressions](named.md#reading-a-constraints-dual)) | @@ -149,6 +149,7 @@ QUOTED ::= "'" chars "'" | '"' chars '"' | `name` (bare) | variable | The variable exists at this coordinate | | `name` (bare) | relation | A row exists, read at the relation's key. A relation may be [partial](relations.md#the-data-contract), and this selects the labels that do map | | `name` (bare) | dimension | A load error. It would be true everywhere | +| `name` (bare) | mask | The mask's predicate ([masks](named.md#masks)). A given mask is true or false at each coordinate, as a `bool` parameter is | | `name OP value` | parameter | Element-wise, and a null compares false | | `name OP value` | dimension | A filter on the frame's own coordinate column | | `name OP value`, `name[col] OP value` | relation | A filter on a value column, read at the relation's key. Name the column where the key determines several. A comparison reads one column | diff --git a/docs/reference/language/file.md b/docs/reference/language/file.md index d635d178..56b28ae5 100644 --- a/docs/reference/language/file.md +++ b/docs/reference/language/file.md @@ -5,8 +5,8 @@ SPDX-License-Identifier: CC-BY-4.0 # File shape -A spec file is a YAML mapping with **twelve declaration keys**, plus -`version` and `description`. Any subset of the twelve is accepted. +A spec file is a YAML mapping with **thirteen declaration keys**, plus +`version` and `description`. Any subset of the thirteen is accepted. | Key | | | ------------- | ------------------------------------------------------------------------------------------------- | @@ -18,6 +18,7 @@ A spec file is a YAML mapping with **twelve declaration keys**, plus | `constraints` | the rules those decisions obey | | `objective` | what is minimised or maximised | | `expressions` | named quantities, reusable in the math and readable after a solve ([named expressions](named.md)) | +| `masks` | named `where` predicates, read by name wherever a `where` string is ([masks](named.md#masks)) | | `macros` | templates that take arguments ([macros](named.md#macros)) | | `piecewise` | piecewise-linear curves ([piecewise](piecewise.md)) | | `sos` | special-ordered sets ([sos](piecewise.md#sos)) | diff --git a/docs/reference/language/index.md b/docs/reference/language/index.md index 59384fc7..c9c24bad 100644 --- a/docs/reference/language/index.md +++ b/docs/reference/language/index.md @@ -46,7 +46,7 @@ names the fix. | # | Rule | | | --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | -| 1 | A file has twelve declaration keys, plus `version` and `description`. An unknown key is refused, with the nearest valid key named. | [File shape](file.md) | +| 1 | A file has thirteen declaration keys, plus `version` and `description`. An unknown key is refused, with the nearest valid key named. | [File shape](file.md) | | 2 | Everything that can be checked without data is checked when the file loads. | [Errors](errors.md) | | 3 | Every name is declared once. A parameter and a dimension both called `snapshot` is refused. | [Names](expressions.md#name-resolution) | | 4 | Where a name may stand depends on what it is. A dimension follows `over=` or `along=`, and is never multiplied. | [Names](expressions.md#name-resolution) | diff --git a/docs/reference/language/named.md b/docs/reference/language/named.md index 30bbc682..76ebc2ca 100644 --- a/docs/reference/language/named.md +++ b/docs/reference/language/named.md @@ -3,11 +3,12 @@ SPDX-FileCopyrightText: mathspec contributors SPDX-License-Identifier: CC-BY-4.0 --> -# Named expressions and macros +# Named expressions, masks and macros An `expressions:` entry names a quantity once, for the math to read or for a -solve to report. A `macros:` entry is a template with arguments, substituted -before anything reads it. +solve to report. A `masks:` entry names a `where` predicate once, for every +`where:` that reads it. A `macros:` entry is a template with arguments, +substituted before anything reads it. ## `expressions` @@ -186,6 +187,84 @@ duals are opposite. A row that `c`'s `where:` deletes has no dual. +## `masks` + +A mask names a [`where` predicate](expressions.md#where-strings) once. A +`where:`, a `when:` or a `holds:` then reads it by name: + +```yaml +dimensions: + period: { dtype: int, ordered: true } + generator: { dtype: str } +parameters: + build_year: { dims: [generator] } + lifetime: { dims: [generator] } + period_year: { dims: [period] } + p_nom: { dims: [generator] } +masks: + stands: + where: build_year <= period_year AND period_year < build_year + lifetime + description: the generator stands in this period +variables: + p: + dims: [period, generator] + where: stands + bounds: { lower: 0, upper: p_nom } +constraints: + ramp_up: + dims: [period, generator] + where: stands AND shift(stands, along=period, offset=1) + expression: p - shift(p, along=period, offset=1) <= 0.5 * p_nom +``` + +The typeset document prints the mask's symbol where it is read. The symbol is +upright, as data is. The predicate prints once, under _Masks_, with ⟺ where an +expression prints =: + +```math +\mathrm{stands}_{e,g} \iff \mathrm{build\_year}_{g} \le \mathrm{period\_year}_{e} \wedge \mathrm{period\_year}_{e} < \mathrm{build\_year}_{g} + \mathrm{lifetime}_{g} \qquad \forall\, e \in \mathcal{E},\ g \in \mathcal{G} +``` + +```math +p_{e,g} - p_{e - 1,g} \le 0.5 \cdot \mathrm{p}^{\mathrm{nom}}_{g} \qquad \forall\, e \in \mathcal{E},\ g \in \mathcal{G} \,:\, \mathrm{stands}_{e,g} \wedge \mathrm{stands}_{e - 1,g} +``` + +| Field | | | +| ------------- | ----------------------------------------------- | -------------- | +| `where` | required. The predicate, in the `where` grammar | | +| `description` | free text | default `null` | + +An entry with no `description:` may be the bare `where` string. + +- **A bare mask name stands for its predicate.** It does so in a `where:`, a + `when:` and a `holds:`, under `NOT`, and inside `count`, `shift` and `at`. + The rows are the rows the predicate written out gives. +- **The predicate gives the frame.** A mask runs over the dimensions its + predicate reads, in the order `dimensions:` declares them, and the symbol + prints with those indices. A mask declares no `dims:`. A use over more + dimensions reads the mask as the same along the rest, and `shift` reads it + back along a dimension of its frame only. +- **A mask may read another mask**, and a named expression that reads data + only. A cycle is refused with its chain: + + ```text + Mask 'again': circular mask reference: again -> again + ``` + +- **A mask is never a number.** In arithmetic or in a comparison it is + refused: + + ```text + Named expression 'e': 'stands' is a mask, which is true or false where it is read, and not a number. Write it bare in the where — stands, or NOT stands — rather than comparing it or computing with it. + ``` + +- **A predicate that folds to `True` or `False` is refused**, since it names + every row or none. +- **A variable's own `where:` may not ask, through a mask, whether the + variable exists.** The loader refuses this as it refuses the bare name. + +Another file reads a mask under [`given: masks`](declarations.md#given-masks). + ## `macros` A macro is a template that takes arguments and is substituted into an expression diff --git a/docs/reference/notation.md b/docs/reference/notation.md index 0938e0d3..cb315bae 100644 --- a/docs/reference/notation.md +++ b/docs/reference/notation.md @@ -129,6 +129,13 @@ parameters: | $`\mathit{marginal\_price}`$ | `marginal_price` over $`\mathcal{T} \times \mathcal{B}`$ | | $`\mathrm{startup\_cost}`$ | `startup_cost` over $`\mathcal{T} \times \mathcal{G}`$ — what starting a unit in this snapshot costs, which the horizon's edge changes | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{dispatchable}`$ | `dispatchable` over $`\mathcal{G}`$ — a unit that dispatches and cannot be turned down | +| $`\mathrm{priced\_dispatch}`$ | `priced_dispatch` over $`\mathcal{G}`$ | + Upright is what the data supplies — a parameter such as $`\mathrm{p}^{\mathrm{max}}`$, a coordinate map, a label — and italic is what the solver chooses, such as $`p`$. An index is italic too, being what a quantifier chooses, and a set is script. $`t \ominus k`$ denotes cyclic translation: index $`t-k`$ taken modulo the size of the dimension (`roll`). Plain $`t-k`$ (`shift`) has no wraparound — terms translated past the edge are simply absent. @@ -793,6 +800,51 @@ expressions: \mathit{marginal\_price}_{t,b} = \lambda_{\mathrm{balance},t,b} \qquad \forall\, t \in \mathcal{T},\ b \in \mathcal{B} ``` +### Named masks + +#### Named mask + +a named predicate with its words: a use prints the symbol over the dims its predicate reads, the predicate prints once + +```yaml +masks: + dispatchable: + where: "p_max > 0 AND NOT is_flexible" +``` + +```math +\mathrm{dispatchable}_{g} \iff \mathrm{p}^{\mathrm{max}}_{g} > 0 \wedge \neg \mathrm{is\_flexible}_{g} \qquad \forall\, g \in \mathcal{G} +``` + +#### Mask that reads a mask + +the one-line form, reading another mask and a data-only entry + +```yaml +masks: + priced_dispatch: "dispatchable AND spend_cap > 0" +``` + +```math +\mathrm{priced\_dispatch}_{g} \iff \mathrm{dispatchable}_{g} \wedge \mathrm{spend}^{\mathrm{cap}}_{g} > 0 \qquad \forall\, g \in \mathcal{G} +``` + +#### Mask in a condition + +a mask as the where, beside a condition of the constraint's own + +```yaml +constraints: + masked: + dims: [snapshot, generator] + where: "priced_dispatch AND position(snapshot) > 0" + expression: p <= p_max +``` + +```math +p_{t,g} \le \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{priced\_dispatch}_{g} \wedge \mathrm{pos}(t) > 0 +``` + ### Shifts #### Cyclic and acyclic shift diff --git a/docs/reference/reading.md b/docs/reference/reading.md index 9677b2a9..fc6b5036 100644 --- a/docs/reference/reading.md +++ b/docs/reference/reading.md @@ -132,6 +132,12 @@ entry's expression, the same object that `program.expressions[name].expression` holds, and its value is the body's value. `children()` steps into the body, so a walk reads through it. +A `NamedMask` stands where a `masks:` entry is read. Its `body` is the entry's +predicate, the root of `program.masks[name].where`, and it is true where the +body is true. `where_children()` steps into the body, so `.atoms`, +`.names_read` and `.dims` read through it, and a consumer that builds rows +needs nothing from `program.masks`. + Every `where` arrives as a `Mask`. Its `.root` is the resolved predicate. The mask also answers four questions: @@ -166,11 +172,14 @@ stands at a mask's root or nowhere. A `Region`'s `when` is a `Mask` too. ## What a program does not build `program.given.parameters`, `program.given.variables`, -`program.given.expressions` and `program.given.constraints` name what the spec -reads and does not build ([given](language/declarations.md#given)). Every -other group is a build instruction. These four are names to look up in the -model this one is layered onto. An expression reads a given expression as a -`Variable` of that name, over the frame under `program.given.expressions`. +`program.given.expressions`, `program.given.masks` and +`program.given.constraints` name what the spec reads and does not build +([given](language/declarations.md#given)). Every other group is a build +instruction. These five are names to look up in the model this one is layered +onto. An expression reads a given expression as a `Variable` of that name, +over the frame under `program.given.expressions`. A `where` reads a given mask +as a `ParameterDefined` of that name: boolean data over the frame under +`program.given.masks`. An entry of `program.expressions` whose `adds_to` names a given expression is a term this file adds to it. The name is still one the program reads and does not build. @@ -197,7 +206,7 @@ layer.given.constraints['balance'].dims # ('snapshot', 'bus') The host model provides each name: it holds a column or a row family of that name. A consumer that builds the program checks that the host provides each name on the same frame, and refuses the program where it does not. A consumer -with no host refuses a program whose four groups are not all empty. `advice` +with no host refuses a program whose five groups are not all empty. `advice` returns one note of kind `given` per name ([what `advice` warns about](language/errors.md#what-advice-warns-about)). diff --git a/docs/reference/typeset.md b/docs/reference/typeset.md index 90b85ece..8db03f81 100644 --- a/docs/reference/typeset.md +++ b/docs/reference/typeset.md @@ -57,6 +57,9 @@ a flag. The [Python API](api.md#typesetting) gives each signature. `cases:` block, a [reported entry](language/named.md#reported-expressions) and a [term](language/declarations.md#terms) keep their definition line under either `inline_expressions` setting. +- A [mask](language/named.md#masks) prints its upright symbol where it is + read and its predicate once, under a **Masks** heading, with ⟺. It prints + by symbol under either `inline_expressions` setting. - Wherever the math moves an index, which every `shift` does, the document prints a line saying what that notation means. - A file that does not load does not print. @@ -80,8 +83,8 @@ dimension, parameter and variable. ## Printing one declaration on its own `typeset_declaration` returns the line the document prints for one named -expression, constraint, assumption or variable, with its quantifier and without -a document, a label, a number or math delimiters: +expression, mask, constraint, assumption, curve or variable, with its +quantifier and without a document, a label, a number or math delimiters: ```python ms.typeset_declaration('spec.yaml', 'spend', 'latex') @@ -103,7 +106,7 @@ A line on its own has no _Definitions_ section beside it, so the plain named expressions it uses are substituted. A cased expression prints by symbol, and a second call with its name prints its block. -A name that is none of the four kinds is refused with the near miss. A name +A name that is none of the six kinds is refused with the near miss. A name declared as two of them, such as a constraint and a variable, is refused too. ## Symbol tables diff --git a/examples/pypsa.yaml b/examples/pypsa.yaml index c51b7a09..cb533170 100644 --- a/examples/pypsa.yaml +++ b/examples/pypsa.yaml @@ -1453,21 +1453,21 @@ variables: the rows below cap at one, or at the module count where the build is modular dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed domain: integer bounds: lower: 0 Generator_start_up: description: "`Generator-start_up` — how much of a committable unit turns on this snapshot, capped as the status is" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed domain: integer bounds: lower: 0 Generator_shut_down: description: "`Generator-shut_down` — how much of a committable unit turns off this snapshot, capped as the status is" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed domain: integer bounds: lower: 0 @@ -1492,9 +1492,7 @@ variables: `Generator-maintenance_capacity` — the chosen build while in maintenance, zero otherwise: the product the `maintcap` rows linearize dims: [scenario, snapshot, generator] - where: >- - Generator_maintainable AND Generator_p_nom_extendable - AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext absence: zero bounds: lower: 0 @@ -1505,8 +1503,9 @@ variables: maintenance may also be off dims: [scenario, snapshot, generator] where: >- - Generator_maintainable AND Generator_committable - AND NOT (Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) AND Generator_active + Generator_maintainable + AND Generator_committed + AND NOT (Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) absence: zero bounds: lower: 0 @@ -1523,21 +1522,21 @@ variables: the rows below cap at one, or at the module count where the build is modular dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed domain: integer bounds: lower: 0 Link_start_up: description: "`Link-start_up` — how much of a committable link turns on this snapshot, capped as the status is" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed domain: integer bounds: lower: 0 Link_shut_down: description: "`Link-shut_down` — how much of a committable link turns off this snapshot, capped as the status is" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed domain: integer bounds: lower: 0 @@ -1562,9 +1561,7 @@ variables: `Link-maintenance_capacity` — the chosen build while in maintenance, zero otherwise: the product the `maintcap` rows linearize dims: [scenario, snapshot, link] - where: >- - Link_maintainable AND Link_p_nom_extendable - AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext absence: zero bounds: lower: 0 @@ -1574,9 +1571,7 @@ variables: otherwise: the product the `maint-status` rows linearize, so a unit in maintenance may also be off dims: [scenario, snapshot, link] - where: >- - Link_maintainable AND Link_committable - AND NOT (Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) AND Link_active + where: Link_maintainable AND Link_committed AND NOT (Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) absence: zero bounds: lower: 0 @@ -1593,21 +1588,21 @@ variables: the rows below cap at one, or at the module count where the build is modular dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed domain: integer bounds: lower: 0 Process_start_up: description: "`Process-start_up` — how much of a committable process turns on this snapshot, capped as the status is" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed domain: integer bounds: lower: 0 Process_shut_down: description: "`Process-shut_down` — how much of a committable process turns off this snapshot, capped as the status is" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed domain: integer bounds: lower: 0 @@ -1632,9 +1627,7 @@ variables: `Process-maintenance_capacity` — the chosen build while in maintenance, zero otherwise: the product the `maintcap` rows linearize dims: [scenario, snapshot, process] - where: >- - Process_maintainable AND Process_p_nom_extendable - AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext absence: zero bounds: lower: 0 @@ -1645,8 +1638,9 @@ variables: maintenance may also be off dims: [scenario, snapshot, process] where: >- - Process_maintainable AND Process_committable - AND NOT (Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) AND Process_active + Process_maintainable + AND Process_committed + AND NOT (Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) absence: zero bounds: lower: 0 @@ -1662,7 +1656,7 @@ variables: cost and held up by the cuts; absent, and zero in the balance, where the network is lossless dims: [scenario, snapshot, line] - where: transmission_losses AND Line_active + where: Line_lossy absence: zero bounds: lower: 0 @@ -1679,7 +1673,7 @@ variables: a line does; absent, and zero in the balance, where the network is lossless dims: [scenario, snapshot, transformer] - where: transmission_losses AND Transformer_active + where: Transformer_lossy absence: zero bounds: lower: 0 @@ -2520,6 +2514,127 @@ expressions: the voltage angle differences around a cycle: every branch flow times its cycle weight, and every transformer phase shift +masks: + Generator_com_ext: + description: >- + a committable generator with an extendable, non-modular build that + stands in the snapshot's period — PyPSA's `com-ext` rows, whose status + is relaxed against the chosen build + where: >- + Generator_committable + AND Generator_p_nom_extendable + AND NOT (Generator_p_nom_mod > 0) + AND Generator_active + Generator_maint_ext: + description: >- + a maintainable generator with an extendable build, unless it is + committable and modular, that stands in the snapshot's period — the + maintenance rows against the chosen build + where: >- + Generator_maintainable + AND Generator_p_nom_extendable + AND NOT (Generator_committable AND Generator_p_nom_mod > 0) + AND Generator_active + Generator_committed: + description: >- + a committable generator that stands in the snapshot's period — every + unit-commitment row's set + where: Generator_committable AND Generator_active + Generator_ramps_from_previous: + description: >- + a snapshot whose ramp reads a previous output — any snapshot but its + period's first, and the horizon's first where the generator comes in off + or carries an initial output + where: >- + position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND + (Generator_status_initial == 0 OR Generator_p_init)) + Link_com_ext: + description: >- + a committable link with an extendable, non-modular build that stands in + the snapshot's period — PyPSA's `com-ext` rows, whose status is relaxed + against the chosen build + where: >- + Link_committable + AND Link_p_nom_extendable + AND NOT (Link_p_nom_mod > 0) + AND Link_active + Link_maint_ext: + description: >- + a maintainable link with an extendable build, unless it is committable + and modular, that stands in the snapshot's period — the maintenance rows + against the chosen build + where: >- + Link_maintainable + AND Link_p_nom_extendable + AND NOT (Link_committable AND Link_p_nom_mod > 0) + AND Link_active + Link_committed: + description: >- + a committable link that stands in the snapshot's period — every + unit-commitment row's set + where: Link_committable AND Link_active + Link_ramps_from_previous: + description: >- + a snapshot whose ramp reads a previous output — any snapshot but its + period's first, and the horizon's first where the link comes in off or + carries an initial output + where: >- + position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND + (Link_status_initial == 0 OR Link_p_init)) + Process_com_ext: + description: >- + a committable process with an extendable, non-modular build that stands + in the snapshot's period — PyPSA's `com-ext` rows, whose status is + relaxed against the chosen build + where: >- + Process_committable + AND Process_p_nom_extendable + AND NOT (Process_p_nom_mod > 0) + AND Process_active + Process_maint_ext: + description: >- + a maintainable process with an extendable build, unless it is + committable and modular, that stands in the snapshot's period — the + maintenance rows against the chosen build + where: >- + Process_maintainable + AND Process_p_nom_extendable + AND NOT (Process_committable AND Process_p_nom_mod > 0) + AND Process_active + Process_committed: + description: >- + a committable process that stands in the snapshot's period — every + unit-commitment row's set + where: Process_committable AND Process_active + Process_ramps_from_previous: + description: >- + a snapshot whose ramp reads a previous output — any snapshot but its + period's first, and the horizon's first where the process comes in off + or carries an initial output + where: >- + position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND + (Process_status_initial == 0 OR Process_p_init)) + StorageUnit_fix: + description: >- + a storage unit with a fixed build that stands in the snapshot's period — + PyPSA's `fix` rows + where: NOT StorageUnit_p_nom_extendable AND StorageUnit_active + StorageUnit_ext: + description: >- + a storage unit with an extendable build that stands in the snapshot's + period — PyPSA's `ext` rows + where: StorageUnit_p_nom_extendable AND StorageUnit_active + Line_lossy: + description: >- + a line that stands in the snapshot's period, where the run models + transmission losses + where: transmission_losses AND Line_active + Transformer_lossy: + description: >- + a transformer that stands in the snapshot's period, where the run models + transmission losses + where: transmission_losses AND Transformer_active + constraints: Generator_fix_p_lower: description: "`Generator-fix-p-lower` — a fixed generator outputs at least its minimum" @@ -2639,54 +2754,54 @@ constraints: StorageUnit_fix_p_dispatch_lower: description: "`StorageUnit-fix-p_dispatch-lower` — dispatch is non-negative" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_dispatch >= 0 StorageUnit_fix_p_dispatch_upper: description: "`StorageUnit-fix-p_dispatch-upper` — a fixed unit dispatches at most its nominal power" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_dispatch <= StorageUnit_p_max_pu * StorageUnit_p_nom StorageUnit_fix_p_store_lower: description: "`StorageUnit-fix-p_store-lower` — storing is non-negative" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_store >= 0 StorageUnit_fix_p_store_upper: description: >- `StorageUnit-fix-p_store-upper` — a fixed unit stores at most its nominal power, the minimum-per-unit column carrying that cap negated dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_store <= -StorageUnit_p_min_pu * StorageUnit_p_nom StorageUnit_fix_state_of_charge_lower: description: "`StorageUnit-fix-state_of_charge-lower` — charge is non-negative" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_state_of_charge >= 0 StorageUnit_fix_state_of_charge_upper: description: "`StorageUnit-fix-state_of_charge-upper` — a fixed unit holds at most its hours at nominal power" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_state_of_charge <= StorageUnit_max_hours * StorageUnit_p_nom Generator_com_p_lower: description: "`Generator-com-p-lower` — a committed unit outputs at least its minimum; off, at least nothing" dims: [scenario, snapshot, generator] - where: Generator_committable AND not Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_p >= Generator_p_min_pu * Generator_p_nom * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) Generator_com_p_upper: description: "`Generator-com-p-upper` — a committed unit outputs at most what is available; off, at most nothing" dims: [scenario, snapshot, generator] - where: Generator_committable AND not Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_p <= Generator_p_max_pu * Generator_p_nom * (Generator_status - Generator_maintenance_pu * Generator_maintenance_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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed 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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed expression: Generator_shut_down >= Generator_previous_status - Generator_status Generator_com_up_time: description: >- @@ -2694,7 +2809,7 @@ constraints: 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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_min_up_time > 0 AND position(snapshot) > 0 AND Generator_active + where: Generator_committed 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: >- @@ -2702,19 +2817,19 @@ constraints: time is still off. The first snapshot's share of the window is the brought-in down time's, which the must-stay-down mask carries dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_min_down_time > 0 AND position(snapshot) > 0 AND Generator_active + where: Generator_committed 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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_must_stay_up AND Generator_active + where: Generator_committed AND Generator_must_stay_up expression: Generator_status == 1 Generator_com_status_must_stay_down: description: >- `Generator-com-status-min_down_time_must_stay_up` — a unit still serving the down time it brought in stays off; PyPSA names the row `_must_stay_up` dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_must_stay_down AND Generator_active + where: Generator_committed AND Generator_must_stay_down expression: Generator_status == 0 Generator_p_ramp_limit_up_run_big_m: description: >- @@ -2723,10 +2838,9 @@ constraints: releases the row in the snapshot it turns on dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_up OR Generator_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_p - Generator_previous_p <= Generator_ramp_up_rate * Generator_p_nom_ext @@ -2738,10 +2852,9 @@ constraints: the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_up OR Generator_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_p - Generator_previous_p <= Generator_start_up_rate * Generator_p_nom_ext @@ -2753,10 +2866,9 @@ constraints: releases the row in the snapshot it turns off dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_down OR Generator_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_previous_p - Generator_p <= Generator_ramp_down_rate * Generator_p_nom_ext @@ -2768,10 +2880,9 @@ constraints: the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_down OR Generator_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_previous_p - Generator_p <= Generator_shut_down_rate * Generator_p_nom_ext @@ -2786,19 +2897,19 @@ constraints: `Generator-com-ext-p-upper-cap` — a committed extendable unit outputs at most what is available of the chosen build, whatever its status dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext expression: Generator_p <= Generator_p_max_pu * (Generator_p_nom_ext - Generator_maintenance_pu * Generator_maintenance_capacity) Generator_com_ext_p_upper_big_m: description: "`Generator-com-ext-p-upper-bigM` — off, a unit outputs nothing; on, the big M is no bound" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext expression: Generator_p <= Generator_big_m * Generator_status Generator_com_ext_p_lower: description: >- `Generator-com-ext-p-lower` — a committed extendable unit outputs at least its minimum of the chosen build; off, the big M releases the row dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext expression: >- Generator_p >= Generator_p_min_pu * (Generator_p_nom_ext - Generator_maintenance_pu * Generator_maintenance_capacity) @@ -2809,23 +2920,21 @@ constraints: negative, output is also plainly non-negative, a row the big-M lower cannot assert while the unit is off dims: [scenario, snapshot, generator] - where: >- - Generator_committable AND Generator_p_nom_extendable - AND Generator_p_min_pu_nonneg AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext AND Generator_p_min_pu_nonneg expression: Generator_p >= 0 Generator_com_mod_p_lower: description: >- `Generator-com-mod-p-lower` — a committed modular unit outputs at least its minimum of one module, whether the build is fixed or a decision dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_p >= Generator_p_min_pu * Generator_p_nom_mod * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) Generator_com_mod_p_upper: description: >- `Generator-com-mod-p-upper` — a committed modular unit outputs at most one module's share, whether the build is fixed or a decision dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_p <= Generator_p_max_pu * Generator_p_nom_mod * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) Generator_status_p_fixed_upper: description: >- @@ -2833,7 +2942,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, generator] - where: Generator_committable AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) expression: Generator_status <= Generator_modules_installed Generator_start_up_p_fixed_upper: description: >- @@ -2841,7 +2950,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, generator] - where: Generator_committable AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) expression: Generator_start_up <= Generator_modules_installed Generator_shut_down_p_fixed_upper: description: >- @@ -2849,22 +2958,22 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, generator] - where: Generator_committable AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) expression: Generator_shut_down <= Generator_modules_installed Generator_status_p_nom_variable_upper: description: "`Generator-status-p_nom-variable-upper` — a modular unit is on only where a module is built" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 expression: Generator_status <= Generator_n_mod Generator_start_up_p_nom_variable_upper: description: "`Generator-start_up-p_nom-variable-upper` — a modular unit starts only where a module is built" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 expression: Generator_start_up <= Generator_n_mod Generator_shut_down_p_nom_variable_upper: description: "`Generator-shut_down-p_nom-variable-upper` — a modular unit stops only where a module is built" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 expression: Generator_shut_down <= Generator_n_mod Generator_maint_event_count: description: "`Generator-maint-event-count` — a maintainable generator holds its number of maintenance events over the horizon" @@ -2889,74 +2998,74 @@ constraints: `Generator-maintcap_upper` — the build taken off is at most the chosen build in maintenance, and at most the build less its floor out of it dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext expression: Generator_maintenance_capacity <= Generator_p_nom_ext - Generator_p_nom_min * (1 - Generator_maintenance) Generator_maintcap_upper_nommax: description: "`Generator-maintcap_upper_nommax` — out of maintenance, no build is taken off" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext expression: Generator_maintenance_capacity <= Generator_p_nom_max * Generator_maintenance Generator_maintcap_lower_nommax: description: "`Generator-maintcap_lower_nommax` — in maintenance, the whole chosen build is taken off" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext expression: Generator_maintenance_capacity >= Generator_p_nom_ext - Generator_p_nom_max * (1 - Generator_maintenance) Generator_maintcap_lower_nommin: description: "`Generator-maintcap_lower_nommin` — in maintenance, at least the floor of the build is taken off" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active AND Generator_p_nom_min > 0 + where: Generator_maint_ext AND Generator_p_nom_min > 0 expression: Generator_maintenance_capacity >= Generator_p_nom_min * Generator_maintenance Generator_maint_status_le_status: description: "`Generator-maint-status-le-status` — the status in maintenance is at most the status" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maintainable AND Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_maintenance_status <= Generator_status Generator_maint_status_le_maint: description: "`Generator-maint-status-le-maint` — out of maintenance, the status in maintenance is zero" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maintainable AND Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_maintenance_status <= Generator_maintenance Generator_maint_status_lb: description: "`Generator-maint-status-lb` — on and in maintenance, the status in maintenance is one" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maintainable AND Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_maintenance_status >= Generator_status + Generator_maintenance - 1 Generator_maint_modstatus_le_status: description: "`Generator-maint-modstatus-le-status` — the modules on in maintenance are at most the modules on" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_maintainable AND Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_maintenance_status <= Generator_status Generator_maint_modstatus_le_maint: description: >- `Generator-maint-modstatus-le-maint` — out of maintenance, no module is on in maintenance; in it, at most the modules the build cap holds dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_maintainable AND Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_maintenance_status <= Generator_p_nom_max / Generator_p_nom_mod * Generator_maintenance Generator_maint_modstatus_lb: description: "`Generator-maint-modstatus-lb` — in maintenance, every module on is on in maintenance" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_maintainable AND Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_maintenance_status >= Generator_status - Generator_p_nom_max / Generator_p_nom_mod * (1 - Generator_maintenance) Link_com_p_lower: description: "`Link-com-p-lower` — a committed link flows at least its minimum; off, at least nothing" dims: [scenario, snapshot, link] - where: Link_committable AND not Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_p >= Link_p_min_pu * Link_p_nom * (Link_status - Link_maintenance_pu * Link_maintenance_status) Link_com_p_upper: description: "`Link-com-p-upper` — a committed link flows at most what is available; off, at most nothing" dims: [scenario, snapshot, link] - where: Link_committable AND not Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_p <= Link_p_max_pu * Link_p_nom * (Link_status - Link_maintenance_pu * Link_maintenance_status) Link_com_transition_start_up: description: "`Link-com-transition-start-up` — turning on is a start, counted against the state the link carried into the snapshot" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed expression: Link_start_up >= Link_status - Link_previous_status Link_com_transition_shut_down: description: "`Link-com-transition-shut-down` — turning off is a stop, counted against the state the link carried into the snapshot" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed expression: Link_shut_down >= Link_previous_status - Link_status Link_com_up_time: description: >- @@ -2964,7 +3073,7 @@ constraints: 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: [scenario, snapshot, link] - where: Link_committable AND Link_min_up_time > 0 AND position(snapshot) > 0 AND Link_active + where: Link_committed AND Link_min_up_time > 0 AND position(snapshot) > 0 expression: sum_back(Link_start_up, along=snapshot, window=Link_min_up_time) <= Link_status Link_com_down_time: description: >- @@ -2972,19 +3081,19 @@ constraints: time is still off. The first snapshot's share of the window is the brought-in down time's, which the must-stay-down mask carries dims: [scenario, snapshot, link] - where: Link_committable AND Link_min_down_time > 0 AND position(snapshot) > 0 AND Link_active + where: Link_committed AND Link_min_down_time > 0 AND position(snapshot) > 0 expression: sum_back(Link_shut_down, along=snapshot, window=Link_min_down_time) <= 1 - Link_status Link_com_status_must_stay_up: description: "`Link-com-status-min_up_time_must_stay_up` — a link still serving the up time it brought in stays on" dims: [scenario, snapshot, link] - where: Link_committable AND Link_must_stay_up AND Link_active + where: Link_committed AND Link_must_stay_up expression: Link_status == 1 Link_com_status_must_stay_down: description: >- `Link-com-status-min_down_time_must_stay_up` — a link still serving the down time it brought in stays off; PyPSA names the row `_must_stay_up` dims: [scenario, snapshot, link] - where: Link_committable AND Link_must_stay_down AND Link_active + where: Link_committed AND Link_must_stay_down expression: Link_status == 0 Link_p_ramp_limit_up_run_big_m: description: >- @@ -2992,11 +3101,7 @@ constraints: raises flow no faster than its limit of the chosen build; the big M releases the row in the snapshot it turns on dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) AND Link_ramps_from_previous expression: >- Link_p - Link_previous_p <= Link_ramp_up_rate * Link_p_nom_ext @@ -3007,11 +3112,7 @@ constraints: committed extendable link ramps no further than its start-up ramp of the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) AND Link_ramps_from_previous expression: >- Link_p - Link_previous_p <= Link_start_up_rate * Link_p_nom_ext @@ -3022,11 +3123,7 @@ constraints: lowers flow no faster than its limit of the chosen build; the big M releases the row in the snapshot it turns off dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) AND Link_ramps_from_previous expression: >- Link_previous_p - Link_p <= Link_ramp_down_rate * Link_p_nom_ext @@ -3037,11 +3134,7 @@ constraints: a committed extendable link ramps no further than its shut-down ramp of the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) AND Link_ramps_from_previous expression: >- Link_previous_p - Link_p <= Link_shut_down_rate * Link_p_nom_ext @@ -3056,19 +3149,19 @@ constraints: `Link-com-ext-p-upper-cap` — a committed extendable link flows at most what is available of the chosen build, whatever its status dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext expression: Link_p <= Link_p_max_pu * (Link_p_nom_ext - Link_maintenance_pu * Link_maintenance_capacity) Link_com_ext_p_upper_big_m: description: "`Link-com-ext-p-upper-bigM` — off, a link flows nothing; on, the big M is no bound" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext expression: Link_p <= Link_big_m * Link_status Link_com_ext_p_lower: description: >- `Link-com-ext-p-lower` — a committed extendable link flows at least its minimum of the chosen build; off, the big M releases the row dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext expression: >- Link_p >= Link_p_min_pu * (Link_p_nom_ext - Link_maintenance_pu * Link_maintenance_capacity) @@ -3079,23 +3172,21 @@ constraints: negative, flow is also plainly non-negative, a row the big-M lower cannot assert while the link is off dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable - AND Link_p_min_pu_nonneg AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext AND Link_p_min_pu_nonneg expression: Link_p >= 0 Link_com_mod_p_lower: description: >- `Link-com-mod-p-lower` — a committed modular link flows at least its minimum of one module, whether the build is fixed or a decision dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_mod > 0 expression: Link_p >= Link_p_min_pu * Link_p_nom_mod * (Link_status - Link_maintenance_pu * Link_maintenance_status) Link_com_mod_p_upper: description: >- `Link-com-mod-p-upper` — a committed modular link flows at most one module's share, whether the build is fixed or a decision dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_mod > 0 expression: Link_p <= Link_p_max_pu * Link_p_nom_mod * (Link_status - Link_maintenance_pu * Link_maintenance_status) Link_status_p_fixed_upper: description: >- @@ -3103,7 +3194,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, link] - where: Link_committable AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) expression: Link_status <= Link_modules_installed Link_start_up_p_fixed_upper: description: >- @@ -3111,7 +3202,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, link] - where: Link_committable AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) expression: Link_start_up <= Link_modules_installed Link_shut_down_p_fixed_upper: description: >- @@ -3119,22 +3210,22 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, link] - where: Link_committable AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) expression: Link_shut_down <= Link_modules_installed Link_status_p_nom_variable_upper: description: "`Link-status-p_nom-variable-upper` — a modular link is on only where a module is built" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_extendable AND Link_p_nom_mod > 0 expression: Link_status <= Link_n_mod Link_start_up_p_nom_variable_upper: description: "`Link-start_up-p_nom-variable-upper` — a modular link starts only where a module is built" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_extendable AND Link_p_nom_mod > 0 expression: Link_start_up <= Link_n_mod Link_shut_down_p_nom_variable_upper: description: "`Link-shut_down-p_nom-variable-upper` — a modular link stops only where a module is built" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_extendable AND Link_p_nom_mod > 0 expression: Link_shut_down <= Link_n_mod Link_maint_event_count: description: "`Link-maint-event-count` — a maintainable link holds its number of maintenance events over the horizon" @@ -3159,74 +3250,74 @@ constraints: `Link-maintcap_upper` — the build taken off is at most the chosen build in maintenance, and at most the build less its floor out of it dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext expression: Link_maintenance_capacity <= Link_p_nom_ext - Link_p_nom_min * (1 - Link_maintenance) Link_maintcap_upper_nommax: description: "`Link-maintcap_upper_nommax` — out of maintenance, no build is taken off" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext expression: Link_maintenance_capacity <= Link_p_nom_max * Link_maintenance Link_maintcap_lower_nommax: description: "`Link-maintcap_lower_nommax` — in maintenance, the whole chosen build is taken off" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext expression: Link_maintenance_capacity >= Link_p_nom_ext - Link_p_nom_max * (1 - Link_maintenance) Link_maintcap_lower_nommin: description: "`Link-maintcap_lower_nommin` — in maintenance, at least the floor of the build is taken off" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active AND Link_p_nom_min > 0 + where: Link_maint_ext AND Link_p_nom_min > 0 expression: Link_maintenance_capacity >= Link_p_nom_min * Link_maintenance Link_maint_status_le_status: description: "`Link-maint-status-le-status` — the status in maintenance is at most the status" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_maintainable AND Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_maintenance_status <= Link_status Link_maint_status_le_maint: description: "`Link-maint-status-le-maint` — out of maintenance, the status in maintenance is zero" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_maintainable AND Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_maintenance_status <= Link_maintenance Link_maint_status_lb: description: "`Link-maint-status-lb` — on and in maintenance, the status in maintenance is one" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_maintainable AND Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_maintenance_status >= Link_status + Link_maintenance - 1 Link_maint_modstatus_le_status: description: "`Link-maint-modstatus-le-status` — the modules on in maintenance are at most the modules on" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_maintainable AND Link_committed AND Link_p_nom_mod > 0 expression: Link_maintenance_status <= Link_status Link_maint_modstatus_le_maint: description: >- `Link-maint-modstatus-le-maint` — out of maintenance, no module is on in maintenance; in it, at most the modules the build cap holds dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_maintainable AND Link_committed AND Link_p_nom_mod > 0 expression: Link_maintenance_status <= Link_p_nom_max / Link_p_nom_mod * Link_maintenance Link_maint_modstatus_lb: description: "`Link-maint-modstatus-lb` — in maintenance, every module on is on in maintenance" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_maintainable AND Link_committed AND Link_p_nom_mod > 0 expression: Link_maintenance_status >= Link_status - Link_p_nom_max / Link_p_nom_mod * (1 - Link_maintenance) Process_com_p_lower: description: "`Process-com-p-lower` — a committed process runs at least its minimum; off, at least nothing" dims: [scenario, snapshot, process] - where: Process_committable AND not Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_p >= Process_p_min_pu * Process_p_nom * (Process_status - Process_maintenance_pu * Process_maintenance_status) Process_com_p_upper: description: "`Process-com-p-upper` — a committed process runs at most what is available; off, at most nothing" dims: [scenario, snapshot, process] - where: Process_committable AND not Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_p <= Process_p_max_pu * Process_p_nom * (Process_status - Process_maintenance_pu * Process_maintenance_status) Process_com_transition_start_up: description: "`Process-com-transition-start-up` — turning on is a start, counted against the state the process carried into the snapshot" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed expression: Process_start_up >= Process_status - Process_previous_status Process_com_transition_shut_down: description: "`Process-com-transition-shut-down` — turning off is a stop, counted against the state the process carried into the snapshot" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed expression: Process_shut_down >= Process_previous_status - Process_status Process_com_up_time: description: >- @@ -3234,7 +3325,7 @@ constraints: 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: [scenario, snapshot, process] - where: Process_committable AND Process_min_up_time > 0 AND position(snapshot) > 0 AND Process_active + where: Process_committed AND Process_min_up_time > 0 AND position(snapshot) > 0 expression: sum_back(Process_start_up, along=snapshot, window=Process_min_up_time) <= Process_status Process_com_down_time: description: >- @@ -3242,19 +3333,19 @@ constraints: time is still off. The first snapshot's share of the window is the brought-in down time's, which the must-stay-down mask carries dims: [scenario, snapshot, process] - where: Process_committable AND Process_min_down_time > 0 AND position(snapshot) > 0 AND Process_active + where: Process_committed AND Process_min_down_time > 0 AND position(snapshot) > 0 expression: sum_back(Process_shut_down, along=snapshot, window=Process_min_down_time) <= 1 - Process_status Process_com_status_must_stay_up: description: "`Process-com-status-min_up_time_must_stay_up` — a process still serving the up time it brought in stays on" dims: [scenario, snapshot, process] - where: Process_committable AND Process_must_stay_up AND Process_active + where: Process_committed AND Process_must_stay_up expression: Process_status == 1 Process_com_status_must_stay_down: description: >- `Process-com-status-min_down_time_must_stay_up` — a process still serving the down time it brought in stays off; PyPSA names the row `_must_stay_up` dims: [scenario, snapshot, process] - where: Process_committable AND Process_must_stay_down AND Process_active + where: Process_committed AND Process_must_stay_down expression: Process_status == 0 Process_p_ramp_limit_up_run_big_m: description: >- @@ -3262,11 +3353,7 @@ constraints: raises internal power no faster than its limit of the chosen build; the big M releases the row in the snapshot it turns on dims: [scenario, snapshot, process] - where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) - AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + where: Process_com_ext AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) AND Process_ramps_from_previous expression: >- Process_p - Process_previous_p <= Process_ramp_up_rate * Process_p_nom_ext @@ -3277,11 +3364,7 @@ constraints: committed extendable process ramps no further than its start-up ramp of the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, process] - where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) - AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + where: Process_com_ext AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) AND Process_ramps_from_previous expression: >- Process_p - Process_previous_p <= Process_start_up_rate * Process_p_nom_ext @@ -3293,10 +3376,9 @@ constraints: releases the row in the snapshot it turns off dims: [scenario, snapshot, process] where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) + Process_com_ext AND (Process_ramp_limit_down OR Process_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + AND Process_ramps_from_previous expression: >- Process_previous_p - Process_p <= Process_ramp_down_rate * Process_p_nom_ext @@ -3308,10 +3390,9 @@ constraints: the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, process] where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) + Process_com_ext AND (Process_ramp_limit_down OR Process_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + AND Process_ramps_from_previous expression: >- Process_previous_p - Process_p <= Process_shut_down_rate * Process_p_nom_ext @@ -3326,19 +3407,19 @@ constraints: `Process-com-ext-p-upper-cap` — a committed extendable process runs at most what is available of the chosen build, whatever its status dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext expression: Process_p <= Process_p_max_pu * (Process_p_nom_ext - Process_maintenance_pu * Process_maintenance_capacity) Process_com_ext_p_upper_big_m: description: "`Process-com-ext-p-upper-bigM` — off, a process does not run; on, the big M is no bound" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext expression: Process_p <= Process_big_m * Process_status Process_com_ext_p_lower: description: >- `Process-com-ext-p-lower` — a committed extendable process runs at least its minimum of the chosen build; off, the big M releases the row dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext expression: >- Process_p >= Process_p_min_pu * (Process_p_nom_ext - Process_maintenance_pu * Process_maintenance_capacity) @@ -3349,23 +3430,21 @@ constraints: negative, internal power is also plainly non-negative, a row the big-M lower cannot assert while the process is off dims: [scenario, snapshot, process] - where: >- - Process_committable AND Process_p_nom_extendable - AND Process_p_min_pu_nonneg AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext AND Process_p_min_pu_nonneg expression: Process_p >= 0 Process_com_mod_p_lower: description: >- `Process-com-mod-p-lower` — a committed modular process runs at least its minimum of one module, whether the build is fixed or a decision dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_mod > 0 expression: Process_p >= Process_p_min_pu * Process_p_nom_mod * (Process_status - Process_maintenance_pu * Process_maintenance_status) Process_com_mod_p_upper: description: >- `Process-com-mod-p-upper` — a committed modular process runs at most one module's share, whether the build is fixed or a decision dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_mod > 0 expression: Process_p <= Process_p_max_pu * Process_p_nom_mod * (Process_status - Process_maintenance_pu * Process_maintenance_status) Process_status_p_fixed_upper: description: >- @@ -3373,7 +3452,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, process] - where: Process_committable AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) expression: Process_status <= Process_modules_installed Process_start_up_p_fixed_upper: description: >- @@ -3381,7 +3460,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, process] - where: Process_committable AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) expression: Process_start_up <= Process_modules_installed Process_shut_down_p_fixed_upper: description: >- @@ -3389,22 +3468,22 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, process] - where: Process_committable AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) expression: Process_shut_down <= Process_modules_installed Process_status_p_nom_variable_upper: description: "`Process-status-p_nom-variable-upper` — a modular process is on only where a module is built" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0 expression: Process_status <= Process_n_mod Process_start_up_p_nom_variable_upper: description: "`Process-start_up-p_nom-variable-upper` — a modular process starts only where a module is built" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0 expression: Process_start_up <= Process_n_mod Process_shut_down_p_nom_variable_upper: description: "`Process-shut_down-p_nom-variable-upper` — a modular process stops only where a module is built" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0 expression: Process_shut_down <= Process_n_mod Process_maint_event_count: description: "`Process-maint-event-count` — a maintainable process holds its number of maintenance events over the horizon" @@ -3429,54 +3508,54 @@ constraints: `Process-maintcap_upper` — the build taken off is at most the chosen build in maintenance, and at most the build less its floor out of it dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext expression: Process_maintenance_capacity <= Process_p_nom_ext - Process_p_nom_min * (1 - Process_maintenance) Process_maintcap_upper_nommax: description: "`Process-maintcap_upper_nommax` — out of maintenance, no build is taken off" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext expression: Process_maintenance_capacity <= Process_p_nom_max * Process_maintenance Process_maintcap_lower_nommax: description: "`Process-maintcap_lower_nommax` — in maintenance, the whole chosen build is taken off" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext expression: Process_maintenance_capacity >= Process_p_nom_ext - Process_p_nom_max * (1 - Process_maintenance) Process_maintcap_lower_nommin: description: "`Process-maintcap_lower_nommin` — in maintenance, at least the floor of the build is taken off" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active AND Process_p_nom_min > 0 + where: Process_maint_ext AND Process_p_nom_min > 0 expression: Process_maintenance_capacity >= Process_p_nom_min * Process_maintenance Process_maint_status_le_status: description: "`Process-maint-status-le-status` — the status in maintenance is at most the status" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_maintainable AND Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_maintenance_status <= Process_status Process_maint_status_le_maint: description: "`Process-maint-status-le-maint` — out of maintenance, the status in maintenance is zero" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_maintainable AND Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_maintenance_status <= Process_maintenance Process_maint_status_lb: description: "`Process-maint-status-lb` — on and in maintenance, the status in maintenance is one" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_maintainable AND Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_maintenance_status >= Process_status + Process_maintenance - 1 Process_maint_modstatus_le_status: description: "`Process-maint-modstatus-le-status` — the modules on in maintenance are at most the modules on" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_maintainable AND Process_committed AND Process_p_nom_mod > 0 expression: Process_maintenance_status <= Process_status Process_maint_modstatus_le_maint: description: >- `Process-maint-modstatus-le-maint` — out of maintenance, no module is on in maintenance; in it, at most the modules the build cap holds dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_maintainable AND Process_committed AND Process_p_nom_mod > 0 expression: Process_maintenance_status <= Process_p_nom_max / Process_p_nom_mod * Process_maintenance Process_maint_modstatus_lb: description: "`Process-maint-modstatus-lb` — in maintenance, every module on is on in maintenance" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_maintainable AND Process_committed AND Process_p_nom_mod > 0 expression: Process_maintenance_status >= Process_status - Process_p_nom_max / Process_p_nom_mod * (1 - Process_maintenance) Line_fix_s_lower: description: "`Line-fix-s-lower` — a fixed line carries at least the negative of its rating, the loss counted against it" @@ -3541,7 +3620,7 @@ constraints: Line_loss_upper: description: "`Line-loss_upper` — a line dissipates at most the loss at its rating" dims: [scenario, snapshot, line] - where: transmission_losses AND Line_active + where: Line_lossy expression: Line_loss <= Line_loss_max Line_loss_tangents_forward: description: >- @@ -3550,14 +3629,14 @@ constraints: `k`, or one row stacked over its `secant` axis, and this block states them all over the segment dimension dims: [scenario, snapshot, line, segment] - where: transmission_losses AND Line_active + where: Line_lossy expression: Line_loss + Line_loss_slope * Line_s >= Line_loss_offset Line_loss_tangents_reverse: description: >- `Line-loss_tangents-{k}--1`, `Line-loss_secants-neg` — the same fan mirrored, the loss depending on the flow's magnitude dims: [scenario, snapshot, line, segment] - where: transmission_losses AND Line_active + where: Line_lossy expression: Line_loss - Line_loss_slope * Line_s >= Line_loss_offset Transformer_fix_s_lower: description: "`Transformer-fix-s-lower` — a fixed transformer carries at least the negative of its rating, the loss counted against it" @@ -3639,7 +3718,7 @@ constraints: Transformer_loss_upper: description: "`Transformer-loss_upper` — a transformer dissipates at most the loss at its rating" dims: [scenario, snapshot, transformer] - where: transmission_losses AND Transformer_active + where: Transformer_lossy expression: Transformer_loss <= Transformer_loss_max Transformer_loss_tangents_forward: description: >- @@ -3647,14 +3726,14 @@ constraints: loss sits above every cut to its curve for flow one way, as a line's does, over the segment dimension dims: [scenario, snapshot, transformer, segment] - where: transmission_losses AND Transformer_active + where: Transformer_lossy expression: Transformer_loss + Transformer_loss_slope * Transformer_s >= Transformer_loss_offset Transformer_loss_tangents_reverse: description: >- `Transformer-loss_tangents-{k}--1`, `Transformer-loss_secants-neg` — the same fan mirrored, the loss depending on the flow's magnitude dims: [scenario, snapshot, transformer, segment] - where: transmission_losses AND Transformer_active + where: Transformer_lossy expression: Transformer_loss - Transformer_loss_slope * Transformer_s >= Transformer_loss_offset Line_fix_s_lower_security: description: >- @@ -3775,7 +3854,7 @@ constraints: where: >- (Generator_ramp_limit_up OR Generator_ramp_limit_start_up) AND NOT (Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) + AND Generator_ramps_from_previous AND Generator_active expression: Generator_p - Generator_previous_p <= Generator_ramp_up_allowance Generator_p_ramp_limit_down: @@ -3790,7 +3869,7 @@ constraints: where: >- (Generator_ramp_limit_down OR Generator_ramp_limit_shut_down) AND NOT (Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) + AND Generator_ramps_from_previous AND Generator_active expression: Generator_previous_p - Generator_p <= Generator_ramp_down_allowance Link_p_ramp_limit_up: @@ -3805,7 +3884,7 @@ constraints: where: >- (Link_ramp_limit_up OR Link_ramp_limit_start_up) AND NOT (Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) + AND Link_ramps_from_previous AND Link_active expression: Link_p - Link_previous_p <= Link_ramp_up_allowance Link_p_ramp_limit_down: @@ -3820,7 +3899,7 @@ constraints: where: >- (Link_ramp_limit_down OR Link_ramp_limit_shut_down) AND NOT (Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) + AND Link_ramps_from_previous AND Link_active expression: Link_previous_p - Link_p <= Link_ramp_down_allowance Process_p_ramp_limit_up: @@ -3835,7 +3914,7 @@ constraints: where: >- (Process_ramp_limit_up OR Process_ramp_limit_start_up) AND NOT (Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) + AND Process_ramps_from_previous AND Process_active expression: Process_p - Process_previous_p <= Process_ramp_up_allowance Process_p_ramp_limit_down: @@ -3850,40 +3929,40 @@ constraints: where: >- (Process_ramp_limit_down OR Process_ramp_limit_shut_down) AND NOT (Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) + AND Process_ramps_from_previous AND Process_active expression: Process_previous_p - Process_p <= Process_ramp_down_allowance StorageUnit_ext_p_dispatch_lower: description: "`StorageUnit-ext-p_dispatch-lower` — dispatch is non-negative" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_dispatch >= 0 StorageUnit_ext_p_dispatch_upper: description: "`StorageUnit-ext-p_dispatch-upper` — an extendable unit dispatches at most the chosen build" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_dispatch <= StorageUnit_p_max_pu * StorageUnit_p_nom_ext StorageUnit_ext_p_store_lower: description: "`StorageUnit-ext-p_store-lower` — storing is non-negative" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_store >= 0 StorageUnit_ext_p_store_upper: description: >- `StorageUnit-ext-p_store-upper` — an extendable unit stores at most the chosen build, the minimum-per-unit column carrying that cap negated dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_store <= -StorageUnit_p_min_pu * StorageUnit_p_nom_ext StorageUnit_ext_state_of_charge_lower: description: "`StorageUnit-ext-state_of_charge-lower` — charge is non-negative" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_state_of_charge >= 0 StorageUnit_ext_state_of_charge_upper: description: "`StorageUnit-ext-state_of_charge-upper` — an extendable unit holds at most its hours at the chosen build" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_state_of_charge <= StorageUnit_max_hours * StorageUnit_p_nom_ext StorageUnit_ext_p_nom_lower: description: "`StorageUnit-ext-p_nom-lower` — the chosen build is at least its floor in every scenario" diff --git a/examples/pypsa/generator.yaml b/examples/pypsa/generator.yaml index f55ccf07..6cd7ca87 100644 --- a/examples/pypsa/generator.yaml +++ b/examples/pypsa/generator.yaml @@ -215,6 +215,13 @@ expressions: expression: sum(Generator_sign * Generator_p, over=generator, by=Generator_bus[bus]) adds_to: Bus_injection +masks: + Generator_committed: + description: >- + a committable generator that stands in the snapshot's period — every + unit-commitment row's set + where: Generator_committable AND Generator_active + constraints: Generator_fix_p_lower: description: "`Generator-fix-p-lower` — a fixed generator outputs at least its minimum" diff --git a/examples/pypsa/generator_commitment.yaml b/examples/pypsa/generator_commitment.yaml index ab87bf2c..f325c324 100644 --- a/examples/pypsa/generator_commitment.yaml +++ b/examples/pypsa/generator_commitment.yaml @@ -81,21 +81,21 @@ variables: the rows below cap at one, or at the module count where the build is modular dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed domain: integer bounds: lower: 0 Generator_start_up: description: "`Generator-start_up` — how much of a committable unit turns on this snapshot, capped as the status is" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed domain: integer bounds: lower: 0 Generator_shut_down: description: "`Generator-shut_down` — how much of a committable unit turns off this snapshot, capped as the status is" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed domain: integer bounds: lower: 0 @@ -121,6 +121,8 @@ given: Generator_p_nom_ext: { dims: [generator] } expressions: scenario_opex: { dims: [scenario] } + masks: + Generator_committed: { dims: [snapshot, generator] } expressions: Generator_previous_status: @@ -148,26 +150,38 @@ expressions: + sum(sum(Generator_shut_down * Generator_shut_down_cost, over=generator), over=snapshot) adds_to: scenario_opex +masks: + Generator_com_ext: + description: >- + a committable generator with an extendable, non-modular build that + stands in the snapshot's period — PyPSA's `com-ext` rows, whose status + is relaxed against the chosen build + where: >- + Generator_committable + AND Generator_p_nom_extendable + AND NOT (Generator_p_nom_mod > 0) + AND Generator_active + constraints: Generator_com_p_lower: description: "`Generator-com-p-lower` — a committed unit outputs at least its minimum; off, at least nothing" dims: [scenario, snapshot, generator] - where: Generator_committable AND not Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_p >= Generator_p_min_pu * Generator_p_nom * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) Generator_com_p_upper: description: "`Generator-com-p-upper` — a committed unit outputs at most what is available; off, at most nothing" dims: [scenario, snapshot, generator] - where: Generator_committable AND not Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_p <= Generator_p_max_pu * Generator_p_nom * (Generator_status - Generator_maintenance_pu * Generator_maintenance_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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed 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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_active + where: Generator_committed expression: Generator_shut_down >= Generator_previous_status - Generator_status Generator_com_up_time: description: >- @@ -175,7 +189,7 @@ constraints: 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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_min_up_time > 0 AND position(snapshot) > 0 AND Generator_active + where: Generator_committed 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: >- @@ -183,38 +197,38 @@ constraints: time is still off. The first snapshot's share of the window is the brought-in down time's, which the must-stay-down mask carries dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_min_down_time > 0 AND position(snapshot) > 0 AND Generator_active + where: Generator_committed 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: [scenario, snapshot, generator] - where: Generator_committable AND Generator_must_stay_up AND Generator_active + where: Generator_committed AND Generator_must_stay_up expression: Generator_status == 1 Generator_com_status_must_stay_down: description: >- `Generator-com-status-min_down_time_must_stay_up` — a unit still serving the down time it brought in stays off; PyPSA names the row `_must_stay_up` dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_must_stay_down AND Generator_active + where: Generator_committed AND Generator_must_stay_down expression: Generator_status == 0 Generator_com_ext_p_upper_cap: description: >- `Generator-com-ext-p-upper-cap` — a committed extendable unit outputs at most what is available of the chosen build, whatever its status dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext expression: Generator_p <= Generator_p_max_pu * (Generator_p_nom_ext - Generator_maintenance_pu * Generator_maintenance_capacity) Generator_com_ext_p_upper_big_m: description: "`Generator-com-ext-p-upper-bigM` — off, a unit outputs nothing; on, the big M is no bound" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext expression: Generator_p <= Generator_big_m * Generator_status Generator_com_ext_p_lower: description: >- `Generator-com-ext-p-lower` — a committed extendable unit outputs at least its minimum of the chosen build; off, the big M releases the row dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext expression: >- Generator_p >= Generator_p_min_pu * (Generator_p_nom_ext - Generator_maintenance_pu * Generator_maintenance_capacity) @@ -225,23 +239,21 @@ constraints: negative, output is also plainly non-negative, a row the big-M lower cannot assert while the unit is off dims: [scenario, snapshot, generator] - where: >- - Generator_committable AND Generator_p_nom_extendable - AND Generator_p_min_pu_nonneg AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_com_ext AND Generator_p_min_pu_nonneg expression: Generator_p >= 0 Generator_com_mod_p_lower: description: >- `Generator-com-mod-p-lower` — a committed modular unit outputs at least its minimum of one module, whether the build is fixed or a decision dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_p >= Generator_p_min_pu * Generator_p_nom_mod * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) Generator_com_mod_p_upper: description: >- `Generator-com-mod-p-upper` — a committed modular unit outputs at most one module's share, whether the build is fixed or a decision dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_p <= Generator_p_max_pu * Generator_p_nom_mod * (Generator_status - Generator_maintenance_pu * Generator_maintenance_status) Generator_status_p_fixed_upper: description: >- @@ -249,7 +261,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, generator] - where: Generator_committable AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) expression: Generator_status <= Generator_modules_installed Generator_start_up_p_fixed_upper: description: >- @@ -257,7 +269,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, generator] - where: Generator_committable AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) expression: Generator_start_up <= Generator_modules_installed Generator_shut_down_p_fixed_upper: description: >- @@ -265,20 +277,20 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, generator] - where: Generator_committable AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_committed AND NOT (Generator_p_nom_extendable AND Generator_p_nom_mod > 0) expression: Generator_shut_down <= Generator_modules_installed Generator_status_p_nom_variable_upper: description: "`Generator-status-p_nom-variable-upper` — a modular unit is on only where a module is built" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 expression: Generator_status <= Generator_n_mod Generator_start_up_p_nom_variable_upper: description: "`Generator-start_up-p_nom-variable-upper` — a modular unit starts only where a module is built" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 expression: Generator_start_up <= Generator_n_mod Generator_shut_down_p_nom_variable_upper: description: "`Generator-shut_down-p_nom-variable-upper` — a modular unit stops only where a module is built" dims: [scenario, snapshot, generator] - where: Generator_committable AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0 expression: Generator_shut_down <= Generator_n_mod diff --git a/examples/pypsa/generator_maintenance.yaml b/examples/pypsa/generator_maintenance.yaml index 39721243..c42dd07d 100644 --- a/examples/pypsa/generator_maintenance.yaml +++ b/examples/pypsa/generator_maintenance.yaml @@ -75,9 +75,7 @@ variables: `Generator-maintenance_capacity` — the chosen build while in maintenance, zero otherwise: the product the `maintcap` rows linearize dims: [scenario, snapshot, generator] - where: >- - Generator_maintainable AND Generator_p_nom_extendable - AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext absence: zero bounds: lower: 0 @@ -88,8 +86,9 @@ variables: maintenance may also be off dims: [scenario, snapshot, generator] where: >- - Generator_maintainable AND Generator_committable - AND NOT (Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) AND Generator_active + Generator_maintainable + AND Generator_committed + AND NOT (Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) absence: zero bounds: lower: 0 @@ -106,6 +105,20 @@ given: variables: Generator_status: { dims: [scenario, snapshot, generator], domain: integer } Generator_p_nom_ext: { dims: [generator] } + masks: + Generator_committed: { dims: [snapshot, generator] } + +masks: + Generator_maint_ext: + description: >- + a maintainable generator with an extendable build, unless it is + committable and modular, that stands in the snapshot's period — the + maintenance rows against the chosen build + where: >- + Generator_maintainable + AND Generator_p_nom_extendable + AND NOT (Generator_committable AND Generator_p_nom_mod > 0) + AND Generator_active constraints: Generator_maint_event_count: @@ -131,54 +144,54 @@ constraints: `Generator-maintcap_upper` — the build taken off is at most the chosen build in maintenance, and at most the build less its floor out of it dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext expression: Generator_maintenance_capacity <= Generator_p_nom_ext - Generator_p_nom_min * (1 - Generator_maintenance) Generator_maintcap_upper_nommax: description: "`Generator-maintcap_upper_nommax` — out of maintenance, no build is taken off" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext expression: Generator_maintenance_capacity <= Generator_p_nom_max * Generator_maintenance Generator_maintcap_lower_nommax: description: "`Generator-maintcap_lower_nommax` — in maintenance, the whole chosen build is taken off" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maint_ext expression: Generator_maintenance_capacity >= Generator_p_nom_ext - Generator_p_nom_max * (1 - Generator_maintenance) Generator_maintcap_lower_nommin: description: "`Generator-maintcap_lower_nommin` — in maintenance, at least the floor of the build is taken off" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_p_nom_extendable AND NOT (Generator_committable AND Generator_p_nom_mod > 0) AND Generator_active AND Generator_p_nom_min > 0 + where: Generator_maint_ext AND Generator_p_nom_min > 0 expression: Generator_maintenance_capacity >= Generator_p_nom_min * Generator_maintenance Generator_maint_status_le_status: description: "`Generator-maint-status-le-status` — the status in maintenance is at most the status" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maintainable AND Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_maintenance_status <= Generator_status Generator_maint_status_le_maint: description: "`Generator-maint-status-le-maint` — out of maintenance, the status in maintenance is zero" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maintainable AND Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_maintenance_status <= Generator_maintenance Generator_maint_status_lb: description: "`Generator-maint-status-lb` — on and in maintenance, the status in maintenance is one" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) AND Generator_active + where: Generator_maintainable AND Generator_committed AND NOT Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) expression: Generator_maintenance_status >= Generator_status + Generator_maintenance - 1 Generator_maint_modstatus_le_status: description: "`Generator-maint-modstatus-le-status` — the modules on in maintenance are at most the modules on" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_maintainable AND Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_maintenance_status <= Generator_status Generator_maint_modstatus_le_maint: description: >- `Generator-maint-modstatus-le-maint` — out of maintenance, no module is on in maintenance; in it, at most the modules the build cap holds dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_maintainable AND Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_maintenance_status <= Generator_p_nom_max / Generator_p_nom_mod * Generator_maintenance Generator_maint_modstatus_lb: description: "`Generator-maint-modstatus-lb` — in maintenance, every module on is on in maintenance" dims: [scenario, snapshot, generator] - where: Generator_maintainable AND Generator_committable AND Generator_p_nom_mod > 0 AND Generator_active + where: Generator_maintainable AND Generator_committed AND Generator_p_nom_mod > 0 expression: Generator_maintenance_status >= Generator_status - Generator_p_nom_max / Generator_p_nom_mod * (1 - Generator_maintenance) assumptions: diff --git a/examples/pypsa/generator_ramping.yaml b/examples/pypsa/generator_ramping.yaml index 86453e53..6f021f28 100644 --- a/examples/pypsa/generator_ramping.yaml +++ b/examples/pypsa/generator_ramping.yaml @@ -60,6 +60,8 @@ given: Generator_previous_status: { dims: [scenario, snapshot, generator] } Generator_p_nom_effective: { dims: [scenario, generator] } Generator_p_nom_committed: { dims: [scenario, generator] } + masks: + Generator_com_ext: { dims: [snapshot, generator] } expressions: Generator_previous_p: @@ -132,6 +134,16 @@ expressions: * (Generator_previous_status - Generator_status) otherwise: Generator_ramp_down_rate * Generator_p_nom_effective +masks: + Generator_ramps_from_previous: + description: >- + a snapshot whose ramp reads a previous output — any snapshot but its + period's first, and the horizon's first where the generator comes in off + or carries an initial output + where: >- + position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND + (Generator_status_initial == 0 OR Generator_p_init)) + constraints: Generator_p_ramp_limit_up_run_big_m: description: >- @@ -140,10 +152,9 @@ constraints: releases the row in the snapshot it turns on dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_up OR Generator_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_p - Generator_previous_p <= Generator_ramp_up_rate * Generator_p_nom_ext @@ -155,10 +166,9 @@ constraints: the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_up OR Generator_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_p - Generator_previous_p <= Generator_start_up_rate * Generator_p_nom_ext @@ -170,10 +180,9 @@ constraints: releases the row in the snapshot it turns off dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_down OR Generator_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_previous_p - Generator_p <= Generator_ramp_down_rate * Generator_p_nom_ext @@ -185,10 +194,9 @@ constraints: the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, generator] where: >- - Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0) + Generator_com_ext AND (Generator_ramp_limit_down OR Generator_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) - AND Generator_active + AND Generator_ramps_from_previous expression: >- Generator_previous_p - Generator_p <= Generator_shut_down_rate * Generator_p_nom_ext @@ -205,7 +213,7 @@ constraints: where: >- (Generator_ramp_limit_up OR Generator_ramp_limit_start_up) AND NOT (Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) + AND Generator_ramps_from_previous AND Generator_active expression: Generator_p - Generator_previous_p <= Generator_ramp_up_allowance Generator_p_ramp_limit_down: @@ -220,7 +228,7 @@ constraints: where: >- (Generator_ramp_limit_down OR Generator_ramp_limit_shut_down) AND NOT (Generator_committable AND Generator_p_nom_extendable AND NOT (Generator_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Generator_status_initial == 0 OR Generator_p_init))) + AND Generator_ramps_from_previous AND Generator_active expression: Generator_previous_p - Generator_p <= Generator_ramp_down_allowance diff --git a/examples/pypsa/line.yaml b/examples/pypsa/line.yaml index 2eb5750a..66c5138a 100644 --- a/examples/pypsa/line.yaml +++ b/examples/pypsa/line.yaml @@ -168,7 +168,7 @@ variables: cost and held up by the cuts; absent, and zero in the balance, where the network is lossless dims: [scenario, snapshot, line] - where: transmission_losses AND Line_active + where: Line_lossy absence: zero bounds: lower: 0 @@ -236,6 +236,13 @@ expressions: expression: sum(Line_s * at(Line_cycle_weight, by=snapshot_period[period]), over=line) adds_to: Cycle_angle_sum +masks: + Line_lossy: + description: >- + a line that stands in the snapshot's period, where the run models + transmission losses + where: transmission_losses AND Line_active + constraints: Line_fix_s_lower: description: "`Line-fix-s-lower` — a fixed line carries at least the negative of its rating, the loss counted against it" @@ -300,7 +307,7 @@ constraints: Line_loss_upper: description: "`Line-loss_upper` — a line dissipates at most the loss at its rating" dims: [scenario, snapshot, line] - where: transmission_losses AND Line_active + where: Line_lossy expression: Line_loss <= Line_loss_max Line_loss_tangents_forward: description: >- @@ -309,12 +316,12 @@ constraints: `k`, or one row stacked over its `secant` axis, and this block states them all over the segment dimension dims: [scenario, snapshot, line, segment] - where: transmission_losses AND Line_active + where: Line_lossy expression: Line_loss + Line_loss_slope * Line_s >= Line_loss_offset Line_loss_tangents_reverse: description: >- `Line-loss_tangents-{k}--1`, `Line-loss_secants-neg` — the same fan mirrored, the loss depending on the flow's magnitude dims: [scenario, snapshot, line, segment] - where: transmission_losses AND Line_active + where: Line_lossy expression: Line_loss - Line_loss_slope * Line_s >= Line_loss_offset diff --git a/examples/pypsa/link.yaml b/examples/pypsa/link.yaml index 08fe4389..54d6665d 100644 --- a/examples/pypsa/link.yaml +++ b/examples/pypsa/link.yaml @@ -267,6 +267,13 @@ expressions: + sum(Link_output_arrival, over=link_output, by=Link_output_bus[bus]) adds_to: Bus_injection +masks: + Link_committed: + description: >- + a committable link that stands in the snapshot's period — every + unit-commitment row's set + where: Link_committable AND Link_active + constraints: Link_fix_p_lower: description: "`Link-fix-p-lower` — a fixed link carries at least its minimum, negative for the other way" diff --git a/examples/pypsa/link_commitment.yaml b/examples/pypsa/link_commitment.yaml index 51f9c736..3583a238 100644 --- a/examples/pypsa/link_commitment.yaml +++ b/examples/pypsa/link_commitment.yaml @@ -81,21 +81,21 @@ variables: the rows below cap at one, or at the module count where the build is modular dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed domain: integer bounds: lower: 0 Link_start_up: description: "`Link-start_up` — how much of a committable link turns on this snapshot, capped as the status is" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed domain: integer bounds: lower: 0 Link_shut_down: description: "`Link-shut_down` — how much of a committable link turns off this snapshot, capped as the status is" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed domain: integer bounds: lower: 0 @@ -121,6 +121,8 @@ given: Link_p_nom_ext: { dims: [link] } expressions: scenario_opex: { dims: [scenario] } + masks: + Link_committed: { dims: [snapshot, link] } expressions: Link_previous_status: @@ -148,26 +150,38 @@ expressions: + sum(sum(Link_shut_down * Link_shut_down_cost, over=link), over=snapshot) adds_to: scenario_opex +masks: + Link_com_ext: + description: >- + a committable link with an extendable, non-modular build that stands in + the snapshot's period — PyPSA's `com-ext` rows, whose status is relaxed + against the chosen build + where: >- + Link_committable + AND Link_p_nom_extendable + AND NOT (Link_p_nom_mod > 0) + AND Link_active + constraints: Link_com_p_lower: description: "`Link-com-p-lower` — a committed link flows at least its minimum; off, at least nothing" dims: [scenario, snapshot, link] - where: Link_committable AND not Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_p >= Link_p_min_pu * Link_p_nom * (Link_status - Link_maintenance_pu * Link_maintenance_status) Link_com_p_upper: description: "`Link-com-p-upper` — a committed link flows at most what is available; off, at most nothing" dims: [scenario, snapshot, link] - where: Link_committable AND not Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_p <= Link_p_max_pu * Link_p_nom * (Link_status - Link_maintenance_pu * Link_maintenance_status) Link_com_transition_start_up: description: "`Link-com-transition-start-up` — turning on is a start, counted against the state the link carried into the snapshot" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed expression: Link_start_up >= Link_status - Link_previous_status Link_com_transition_shut_down: description: "`Link-com-transition-shut-down` — turning off is a stop, counted against the state the link carried into the snapshot" dims: [scenario, snapshot, link] - where: Link_committable AND Link_active + where: Link_committed expression: Link_shut_down >= Link_previous_status - Link_status Link_com_up_time: description: >- @@ -175,7 +189,7 @@ constraints: 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: [scenario, snapshot, link] - where: Link_committable AND Link_min_up_time > 0 AND position(snapshot) > 0 AND Link_active + where: Link_committed AND Link_min_up_time > 0 AND position(snapshot) > 0 expression: sum_back(Link_start_up, along=snapshot, window=Link_min_up_time) <= Link_status Link_com_down_time: description: >- @@ -183,38 +197,38 @@ constraints: time is still off. The first snapshot's share of the window is the brought-in down time's, which the must-stay-down mask carries dims: [scenario, snapshot, link] - where: Link_committable AND Link_min_down_time > 0 AND position(snapshot) > 0 AND Link_active + where: Link_committed AND Link_min_down_time > 0 AND position(snapshot) > 0 expression: sum_back(Link_shut_down, along=snapshot, window=Link_min_down_time) <= 1 - Link_status Link_com_status_must_stay_up: description: "`Link-com-status-min_up_time_must_stay_up` — a link still serving the up time it brought in stays on" dims: [scenario, snapshot, link] - where: Link_committable AND Link_must_stay_up AND Link_active + where: Link_committed AND Link_must_stay_up expression: Link_status == 1 Link_com_status_must_stay_down: description: >- `Link-com-status-min_down_time_must_stay_up` — a link still serving the down time it brought in stays off; PyPSA names the row `_must_stay_up` dims: [scenario, snapshot, link] - where: Link_committable AND Link_must_stay_down AND Link_active + where: Link_committed AND Link_must_stay_down expression: Link_status == 0 Link_com_ext_p_upper_cap: description: >- `Link-com-ext-p-upper-cap` — a committed extendable link flows at most what is available of the chosen build, whatever its status dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext expression: Link_p <= Link_p_max_pu * (Link_p_nom_ext - Link_maintenance_pu * Link_maintenance_capacity) Link_com_ext_p_upper_big_m: description: "`Link-com-ext-p-upper-bigM` — off, a link flows nothing; on, the big M is no bound" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext expression: Link_p <= Link_big_m * Link_status Link_com_ext_p_lower: description: >- `Link-com-ext-p-lower` — a committed extendable link flows at least its minimum of the chosen build; off, the big M releases the row dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext expression: >- Link_p >= Link_p_min_pu * (Link_p_nom_ext - Link_maintenance_pu * Link_maintenance_capacity) @@ -225,23 +239,21 @@ constraints: negative, flow is also plainly non-negative, a row the big-M lower cannot assert while the link is off dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable - AND Link_p_min_pu_nonneg AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_com_ext AND Link_p_min_pu_nonneg expression: Link_p >= 0 Link_com_mod_p_lower: description: >- `Link-com-mod-p-lower` — a committed modular link flows at least its minimum of one module, whether the build is fixed or a decision dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_mod > 0 expression: Link_p >= Link_p_min_pu * Link_p_nom_mod * (Link_status - Link_maintenance_pu * Link_maintenance_status) Link_com_mod_p_upper: description: >- `Link-com-mod-p-upper` — a committed modular link flows at most one module's share, whether the build is fixed or a decision dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_mod > 0 expression: Link_p <= Link_p_max_pu * Link_p_nom_mod * (Link_status - Link_maintenance_pu * Link_maintenance_status) Link_status_p_fixed_upper: description: >- @@ -249,7 +261,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, link] - where: Link_committable AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) expression: Link_status <= Link_modules_installed Link_start_up_p_fixed_upper: description: >- @@ -257,7 +269,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, link] - where: Link_committable AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) expression: Link_start_up <= Link_modules_installed Link_shut_down_p_fixed_upper: description: >- @@ -265,20 +277,20 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, link] - where: Link_committable AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) AND Link_active + where: Link_committed AND NOT (Link_p_nom_extendable AND Link_p_nom_mod > 0) expression: Link_shut_down <= Link_modules_installed Link_status_p_nom_variable_upper: description: "`Link-status-p_nom-variable-upper` — a modular link is on only where a module is built" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_extendable AND Link_p_nom_mod > 0 expression: Link_status <= Link_n_mod Link_start_up_p_nom_variable_upper: description: "`Link-start_up-p_nom-variable-upper` — a modular link starts only where a module is built" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_extendable AND Link_p_nom_mod > 0 expression: Link_start_up <= Link_n_mod Link_shut_down_p_nom_variable_upper: description: "`Link-shut_down-p_nom-variable-upper` — a modular link stops only where a module is built" dims: [scenario, snapshot, link] - where: Link_committable AND Link_p_nom_extendable AND Link_p_nom_mod > 0 AND Link_active + where: Link_committed AND Link_p_nom_extendable AND Link_p_nom_mod > 0 expression: Link_shut_down <= Link_n_mod diff --git a/examples/pypsa/link_maintenance.yaml b/examples/pypsa/link_maintenance.yaml index aefd65cd..4388395b 100644 --- a/examples/pypsa/link_maintenance.yaml +++ b/examples/pypsa/link_maintenance.yaml @@ -75,9 +75,7 @@ variables: `Link-maintenance_capacity` — the chosen build while in maintenance, zero otherwise: the product the `maintcap` rows linearize dims: [scenario, snapshot, link] - where: >- - Link_maintainable AND Link_p_nom_extendable - AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext absence: zero bounds: lower: 0 @@ -87,9 +85,7 @@ variables: otherwise: the product the `maint-status` rows linearize, so a unit in maintenance may also be off dims: [scenario, snapshot, link] - where: >- - Link_maintainable AND Link_committable - AND NOT (Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) AND Link_active + where: Link_maintainable AND Link_committed AND NOT (Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) absence: zero bounds: lower: 0 @@ -106,6 +102,20 @@ given: variables: Link_status: { dims: [scenario, snapshot, link], domain: integer } Link_p_nom_ext: { dims: [link] } + masks: + Link_committed: { dims: [snapshot, link] } + +masks: + Link_maint_ext: + description: >- + a maintainable link with an extendable build, unless it is committable + and modular, that stands in the snapshot's period — the maintenance rows + against the chosen build + where: >- + Link_maintainable + AND Link_p_nom_extendable + AND NOT (Link_committable AND Link_p_nom_mod > 0) + AND Link_active constraints: Link_maint_event_count: @@ -131,54 +141,54 @@ constraints: `Link-maintcap_upper` — the build taken off is at most the chosen build in maintenance, and at most the build less its floor out of it dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext expression: Link_maintenance_capacity <= Link_p_nom_ext - Link_p_nom_min * (1 - Link_maintenance) Link_maintcap_upper_nommax: description: "`Link-maintcap_upper_nommax` — out of maintenance, no build is taken off" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext expression: Link_maintenance_capacity <= Link_p_nom_max * Link_maintenance Link_maintcap_lower_nommax: description: "`Link-maintcap_lower_nommax` — in maintenance, the whole chosen build is taken off" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active + where: Link_maint_ext expression: Link_maintenance_capacity >= Link_p_nom_ext - Link_p_nom_max * (1 - Link_maintenance) Link_maintcap_lower_nommin: description: "`Link-maintcap_lower_nommin` — in maintenance, at least the floor of the build is taken off" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_p_nom_extendable AND NOT (Link_committable AND Link_p_nom_mod > 0) AND Link_active AND Link_p_nom_min > 0 + where: Link_maint_ext AND Link_p_nom_min > 0 expression: Link_maintenance_capacity >= Link_p_nom_min * Link_maintenance Link_maint_status_le_status: description: "`Link-maint-status-le-status` — the status in maintenance is at most the status" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_maintainable AND Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_maintenance_status <= Link_status Link_maint_status_le_maint: description: "`Link-maint-status-le-maint` — out of maintenance, the status in maintenance is zero" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_maintainable AND Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_maintenance_status <= Link_maintenance Link_maint_status_lb: description: "`Link-maint-status-lb` — on and in maintenance, the status in maintenance is one" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) AND Link_active + where: Link_maintainable AND Link_committed AND NOT Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) expression: Link_maintenance_status >= Link_status + Link_maintenance - 1 Link_maint_modstatus_le_status: description: "`Link-maint-modstatus-le-status` — the modules on in maintenance are at most the modules on" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_maintainable AND Link_committed AND Link_p_nom_mod > 0 expression: Link_maintenance_status <= Link_status Link_maint_modstatus_le_maint: description: >- `Link-maint-modstatus-le-maint` — out of maintenance, no module is on in maintenance; in it, at most the modules the build cap holds dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_maintainable AND Link_committed AND Link_p_nom_mod > 0 expression: Link_maintenance_status <= Link_p_nom_max / Link_p_nom_mod * Link_maintenance Link_maint_modstatus_lb: description: "`Link-maint-modstatus-lb` — in maintenance, every module on is on in maintenance" dims: [scenario, snapshot, link] - where: Link_maintainable AND Link_committable AND Link_p_nom_mod > 0 AND Link_active + where: Link_maintainable AND Link_committed AND Link_p_nom_mod > 0 expression: Link_maintenance_status >= Link_status - Link_p_nom_max / Link_p_nom_mod * (1 - Link_maintenance) assumptions: diff --git a/examples/pypsa/link_ramping.yaml b/examples/pypsa/link_ramping.yaml index 4dd25f7c..66db2331 100644 --- a/examples/pypsa/link_ramping.yaml +++ b/examples/pypsa/link_ramping.yaml @@ -60,6 +60,8 @@ given: Link_p_nom_effective: { dims: [scenario, link] } Link_previous_status: { dims: [scenario, snapshot, link] } Link_p_nom_committed: { dims: [scenario, link] } + masks: + Link_com_ext: { dims: [snapshot, link] } expressions: Link_previous_p: @@ -132,6 +134,16 @@ expressions: * (Link_previous_status - Link_status) otherwise: Link_ramp_down_rate * Link_p_nom_effective +masks: + Link_ramps_from_previous: + description: >- + a snapshot whose ramp reads a previous output — any snapshot but its + period's first, and the horizon's first where the link comes in off or + carries an initial output + where: >- + position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND + (Link_status_initial == 0 OR Link_p_init)) + constraints: Link_p_ramp_limit_up_run_big_m: description: >- @@ -139,11 +151,7 @@ constraints: raises flow no faster than its limit of the chosen build; the big M releases the row in the snapshot it turns on dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) AND Link_ramps_from_previous expression: >- Link_p - Link_previous_p <= Link_ramp_up_rate * Link_p_nom_ext @@ -154,11 +162,7 @@ constraints: committed extendable link ramps no further than its start-up ramp of the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_up OR Link_ramp_limit_start_up) AND Link_ramps_from_previous expression: >- Link_p - Link_previous_p <= Link_start_up_rate * Link_p_nom_ext @@ -169,11 +173,7 @@ constraints: lowers flow no faster than its limit of the chosen build; the big M releases the row in the snapshot it turns off dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) AND Link_ramps_from_previous expression: >- Link_previous_p - Link_p <= Link_ramp_down_rate * Link_p_nom_ext @@ -184,11 +184,7 @@ constraints: a committed extendable link ramps no further than its shut-down ramp of the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, link] - where: >- - Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0) - AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) - AND Link_active + where: Link_com_ext AND (Link_ramp_limit_down OR Link_ramp_limit_shut_down) AND Link_ramps_from_previous expression: >- Link_previous_p - Link_p <= Link_shut_down_rate * Link_p_nom_ext @@ -205,7 +201,7 @@ constraints: where: >- (Link_ramp_limit_up OR Link_ramp_limit_start_up) AND NOT (Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) + AND Link_ramps_from_previous AND Link_active expression: Link_p - Link_previous_p <= Link_ramp_up_allowance Link_p_ramp_limit_down: @@ -220,7 +216,7 @@ constraints: where: >- (Link_ramp_limit_down OR Link_ramp_limit_shut_down) AND NOT (Link_committable AND Link_p_nom_extendable AND NOT (Link_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Link_status_initial == 0 OR Link_p_init))) + AND Link_ramps_from_previous AND Link_active expression: Link_previous_p - Link_p <= Link_ramp_down_allowance diff --git a/examples/pypsa/process.yaml b/examples/pypsa/process.yaml index fa9ee966..6271553e 100644 --- a/examples/pypsa/process.yaml +++ b/examples/pypsa/process.yaml @@ -238,6 +238,13 @@ expressions: sum(Process_output_arrival, over=process_output, by=Process_output_bus[bus]) adds_to: Bus_injection +masks: + Process_committed: + description: >- + a committable process that stands in the snapshot's period — every + unit-commitment row's set + where: Process_committable AND Process_active + constraints: Process_fix_p_lower: description: "`Process-fix-p-lower` — a fixed process runs at least its minimum, negative for the other way" diff --git a/examples/pypsa/process_commitment.yaml b/examples/pypsa/process_commitment.yaml index 134c82a9..c1751f27 100644 --- a/examples/pypsa/process_commitment.yaml +++ b/examples/pypsa/process_commitment.yaml @@ -81,21 +81,21 @@ variables: the rows below cap at one, or at the module count where the build is modular dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed domain: integer bounds: lower: 0 Process_start_up: description: "`Process-start_up` — how much of a committable process turns on this snapshot, capped as the status is" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed domain: integer bounds: lower: 0 Process_shut_down: description: "`Process-shut_down` — how much of a committable process turns off this snapshot, capped as the status is" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed domain: integer bounds: lower: 0 @@ -121,6 +121,8 @@ given: Process_p_nom_ext: { dims: [process] } expressions: scenario_opex: { dims: [scenario] } + masks: + Process_committed: { dims: [snapshot, process] } expressions: Process_previous_status: @@ -148,26 +150,38 @@ expressions: + sum(sum(Process_shut_down * Process_shut_down_cost, over=process), over=snapshot) adds_to: scenario_opex +masks: + Process_com_ext: + description: >- + a committable process with an extendable, non-modular build that stands + in the snapshot's period — PyPSA's `com-ext` rows, whose status is + relaxed against the chosen build + where: >- + Process_committable + AND Process_p_nom_extendable + AND NOT (Process_p_nom_mod > 0) + AND Process_active + constraints: Process_com_p_lower: description: "`Process-com-p-lower` — a committed process runs at least its minimum; off, at least nothing" dims: [scenario, snapshot, process] - where: Process_committable AND not Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_p >= Process_p_min_pu * Process_p_nom * (Process_status - Process_maintenance_pu * Process_maintenance_status) Process_com_p_upper: description: "`Process-com-p-upper` — a committed process runs at most what is available; off, at most nothing" dims: [scenario, snapshot, process] - where: Process_committable AND not Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_p <= Process_p_max_pu * Process_p_nom * (Process_status - Process_maintenance_pu * Process_maintenance_status) Process_com_transition_start_up: description: "`Process-com-transition-start-up` — turning on is a start, counted against the state the process carried into the snapshot" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed expression: Process_start_up >= Process_status - Process_previous_status Process_com_transition_shut_down: description: "`Process-com-transition-shut-down` — turning off is a stop, counted against the state the process carried into the snapshot" dims: [scenario, snapshot, process] - where: Process_committable AND Process_active + where: Process_committed expression: Process_shut_down >= Process_previous_status - Process_status Process_com_up_time: description: >- @@ -175,7 +189,7 @@ constraints: 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: [scenario, snapshot, process] - where: Process_committable AND Process_min_up_time > 0 AND position(snapshot) > 0 AND Process_active + where: Process_committed AND Process_min_up_time > 0 AND position(snapshot) > 0 expression: sum_back(Process_start_up, along=snapshot, window=Process_min_up_time) <= Process_status Process_com_down_time: description: >- @@ -183,38 +197,38 @@ constraints: time is still off. The first snapshot's share of the window is the brought-in down time's, which the must-stay-down mask carries dims: [scenario, snapshot, process] - where: Process_committable AND Process_min_down_time > 0 AND position(snapshot) > 0 AND Process_active + where: Process_committed AND Process_min_down_time > 0 AND position(snapshot) > 0 expression: sum_back(Process_shut_down, along=snapshot, window=Process_min_down_time) <= 1 - Process_status Process_com_status_must_stay_up: description: "`Process-com-status-min_up_time_must_stay_up` — a process still serving the up time it brought in stays on" dims: [scenario, snapshot, process] - where: Process_committable AND Process_must_stay_up AND Process_active + where: Process_committed AND Process_must_stay_up expression: Process_status == 1 Process_com_status_must_stay_down: description: >- `Process-com-status-min_down_time_must_stay_up` — a process still serving the down time it brought in stays off; PyPSA names the row `_must_stay_up` dims: [scenario, snapshot, process] - where: Process_committable AND Process_must_stay_down AND Process_active + where: Process_committed AND Process_must_stay_down expression: Process_status == 0 Process_com_ext_p_upper_cap: description: >- `Process-com-ext-p-upper-cap` — a committed extendable process runs at most what is available of the chosen build, whatever its status dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext expression: Process_p <= Process_p_max_pu * (Process_p_nom_ext - Process_maintenance_pu * Process_maintenance_capacity) Process_com_ext_p_upper_big_m: description: "`Process-com-ext-p-upper-bigM` — off, a process does not run; on, the big M is no bound" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext expression: Process_p <= Process_big_m * Process_status Process_com_ext_p_lower: description: >- `Process-com-ext-p-lower` — a committed extendable process runs at least its minimum of the chosen build; off, the big M releases the row dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext expression: >- Process_p >= Process_p_min_pu * (Process_p_nom_ext - Process_maintenance_pu * Process_maintenance_capacity) @@ -225,23 +239,21 @@ constraints: negative, internal power is also plainly non-negative, a row the big-M lower cannot assert while the process is off dims: [scenario, snapshot, process] - where: >- - Process_committable AND Process_p_nom_extendable - AND Process_p_min_pu_nonneg AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_com_ext AND Process_p_min_pu_nonneg expression: Process_p >= 0 Process_com_mod_p_lower: description: >- `Process-com-mod-p-lower` — a committed modular process runs at least its minimum of one module, whether the build is fixed or a decision dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_mod > 0 expression: Process_p >= Process_p_min_pu * Process_p_nom_mod * (Process_status - Process_maintenance_pu * Process_maintenance_status) Process_com_mod_p_upper: description: >- `Process-com-mod-p-upper` — a committed modular process runs at most one module's share, whether the build is fixed or a decision dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_mod > 0 expression: Process_p <= Process_p_max_pu * Process_p_nom_mod * (Process_status - Process_maintenance_pu * Process_maintenance_status) Process_status_p_fixed_upper: description: >- @@ -249,7 +261,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, process] - where: Process_committable AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) expression: Process_status <= Process_modules_installed Process_start_up_p_fixed_upper: description: >- @@ -257,7 +269,7 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, process] - where: Process_committable AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) expression: Process_start_up <= Process_modules_installed Process_shut_down_p_fixed_upper: description: >- @@ -265,20 +277,20 @@ constraints: place, an explicit row as PyPSA writes it: one where the build is not modular, and the fixed build's whole count of modules where it is dims: [scenario, snapshot, process] - where: Process_committable AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) AND Process_active + where: Process_committed AND NOT (Process_p_nom_extendable AND Process_p_nom_mod > 0) expression: Process_shut_down <= Process_modules_installed Process_status_p_nom_variable_upper: description: "`Process-status-p_nom-variable-upper` — a modular process is on only where a module is built" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0 expression: Process_status <= Process_n_mod Process_start_up_p_nom_variable_upper: description: "`Process-start_up-p_nom-variable-upper` — a modular process starts only where a module is built" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0 expression: Process_start_up <= Process_n_mod Process_shut_down_p_nom_variable_upper: description: "`Process-shut_down-p_nom-variable-upper` — a modular process stops only where a module is built" dims: [scenario, snapshot, process] - where: Process_committable AND Process_p_nom_extendable AND Process_p_nom_mod > 0 AND Process_active + where: Process_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0 expression: Process_shut_down <= Process_n_mod diff --git a/examples/pypsa/process_maintenance.yaml b/examples/pypsa/process_maintenance.yaml index af1beb1c..444353e6 100644 --- a/examples/pypsa/process_maintenance.yaml +++ b/examples/pypsa/process_maintenance.yaml @@ -75,9 +75,7 @@ variables: `Process-maintenance_capacity` — the chosen build while in maintenance, zero otherwise: the product the `maintcap` rows linearize dims: [scenario, snapshot, process] - where: >- - Process_maintainable AND Process_p_nom_extendable - AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext absence: zero bounds: lower: 0 @@ -88,8 +86,9 @@ variables: maintenance may also be off dims: [scenario, snapshot, process] where: >- - Process_maintainable AND Process_committable - AND NOT (Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) AND Process_active + Process_maintainable + AND Process_committed + AND NOT (Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) absence: zero bounds: lower: 0 @@ -106,6 +105,20 @@ given: variables: Process_status: { dims: [scenario, snapshot, process], domain: integer } Process_p_nom_ext: { dims: [process] } + masks: + Process_committed: { dims: [snapshot, process] } + +masks: + Process_maint_ext: + description: >- + a maintainable process with an extendable build, unless it is + committable and modular, that stands in the snapshot's period — the + maintenance rows against the chosen build + where: >- + Process_maintainable + AND Process_p_nom_extendable + AND NOT (Process_committable AND Process_p_nom_mod > 0) + AND Process_active constraints: Process_maint_event_count: @@ -131,54 +144,54 @@ constraints: `Process-maintcap_upper` — the build taken off is at most the chosen build in maintenance, and at most the build less its floor out of it dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext expression: Process_maintenance_capacity <= Process_p_nom_ext - Process_p_nom_min * (1 - Process_maintenance) Process_maintcap_upper_nommax: description: "`Process-maintcap_upper_nommax` — out of maintenance, no build is taken off" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext expression: Process_maintenance_capacity <= Process_p_nom_max * Process_maintenance Process_maintcap_lower_nommax: description: "`Process-maintcap_lower_nommax` — in maintenance, the whole chosen build is taken off" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active + where: Process_maint_ext expression: Process_maintenance_capacity >= Process_p_nom_ext - Process_p_nom_max * (1 - Process_maintenance) Process_maintcap_lower_nommin: description: "`Process-maintcap_lower_nommin` — in maintenance, at least the floor of the build is taken off" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_p_nom_extendable AND NOT (Process_committable AND Process_p_nom_mod > 0) AND Process_active AND Process_p_nom_min > 0 + where: Process_maint_ext AND Process_p_nom_min > 0 expression: Process_maintenance_capacity >= Process_p_nom_min * Process_maintenance Process_maint_status_le_status: description: "`Process-maint-status-le-status` — the status in maintenance is at most the status" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_maintainable AND Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_maintenance_status <= Process_status Process_maint_status_le_maint: description: "`Process-maint-status-le-maint` — out of maintenance, the status in maintenance is zero" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_maintainable AND Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_maintenance_status <= Process_maintenance Process_maint_status_lb: description: "`Process-maint-status-lb` — on and in maintenance, the status in maintenance is one" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) AND Process_active + where: Process_maintainable AND Process_committed AND NOT Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) expression: Process_maintenance_status >= Process_status + Process_maintenance - 1 Process_maint_modstatus_le_status: description: "`Process-maint-modstatus-le-status` — the modules on in maintenance are at most the modules on" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_maintainable AND Process_committed AND Process_p_nom_mod > 0 expression: Process_maintenance_status <= Process_status Process_maint_modstatus_le_maint: description: >- `Process-maint-modstatus-le-maint` — out of maintenance, no module is on in maintenance; in it, at most the modules the build cap holds dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_maintainable AND Process_committed AND Process_p_nom_mod > 0 expression: Process_maintenance_status <= Process_p_nom_max / Process_p_nom_mod * Process_maintenance Process_maint_modstatus_lb: description: "`Process-maint-modstatus-lb` — in maintenance, every module on is on in maintenance" dims: [scenario, snapshot, process] - where: Process_maintainable AND Process_committable AND Process_p_nom_mod > 0 AND Process_active + where: Process_maintainable AND Process_committed AND Process_p_nom_mod > 0 expression: Process_maintenance_status >= Process_status - Process_p_nom_max / Process_p_nom_mod * (1 - Process_maintenance) assumptions: diff --git a/examples/pypsa/process_ramping.yaml b/examples/pypsa/process_ramping.yaml index af2e36e6..7fb3d216 100644 --- a/examples/pypsa/process_ramping.yaml +++ b/examples/pypsa/process_ramping.yaml @@ -60,6 +60,8 @@ given: Process_p_nom_effective: { dims: [scenario, process] } Process_previous_status: { dims: [scenario, snapshot, process] } Process_p_nom_committed: { dims: [scenario, process] } + masks: + Process_com_ext: { dims: [snapshot, process] } expressions: Process_previous_p: @@ -132,6 +134,16 @@ expressions: * (Process_previous_status - Process_status) otherwise: Process_ramp_down_rate * Process_p_nom_effective +masks: + Process_ramps_from_previous: + description: >- + a snapshot whose ramp reads a previous output — any snapshot but its + period's first, and the horizon's first where the process comes in off + or carries an initial output + where: >- + position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND + (Process_status_initial == 0 OR Process_p_init)) + constraints: Process_p_ramp_limit_up_run_big_m: description: >- @@ -139,11 +151,7 @@ constraints: raises internal power no faster than its limit of the chosen build; the big M releases the row in the snapshot it turns on dims: [scenario, snapshot, process] - where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) - AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + where: Process_com_ext AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) AND Process_ramps_from_previous expression: >- Process_p - Process_previous_p <= Process_ramp_up_rate * Process_p_nom_ext @@ -154,11 +162,7 @@ constraints: committed extendable process ramps no further than its start-up ramp of the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, process] - where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) - AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + where: Process_com_ext AND (Process_ramp_limit_up OR Process_ramp_limit_start_up) AND Process_ramps_from_previous expression: >- Process_p - Process_previous_p <= Process_start_up_rate * Process_p_nom_ext @@ -170,10 +174,9 @@ constraints: releases the row in the snapshot it turns off dims: [scenario, snapshot, process] where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) + Process_com_ext AND (Process_ramp_limit_down OR Process_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + AND Process_ramps_from_previous expression: >- Process_previous_p - Process_p <= Process_ramp_down_rate * Process_p_nom_ext @@ -185,10 +188,9 @@ constraints: the chosen build; the big M releases the row everywhere else dims: [scenario, snapshot, process] where: >- - Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0) + Process_com_ext AND (Process_ramp_limit_down OR Process_ramp_limit_shut_down) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) - AND Process_active + AND Process_ramps_from_previous expression: >- Process_previous_p - Process_p <= Process_shut_down_rate * Process_p_nom_ext @@ -205,7 +207,7 @@ constraints: where: >- (Process_ramp_limit_up OR Process_ramp_limit_start_up) AND NOT (Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) + AND Process_ramps_from_previous AND Process_active expression: Process_p - Process_previous_p <= Process_ramp_up_allowance Process_p_ramp_limit_down: @@ -220,7 +222,7 @@ constraints: where: >- (Process_ramp_limit_down OR Process_ramp_limit_shut_down) AND NOT (Process_committable AND Process_p_nom_extendable AND NOT (Process_p_nom_mod > 0)) - AND (position(snapshot, within=snapshot_period[period]) > 0 OR (position(snapshot) == 0 AND (Process_status_initial == 0 OR Process_p_init))) + AND Process_ramps_from_previous AND Process_active expression: Process_previous_p - Process_p <= Process_ramp_down_allowance diff --git a/examples/pypsa/storage_unit.yaml b/examples/pypsa/storage_unit.yaml index d5118e1b..d838e891 100644 --- a/examples/pypsa/storage_unit.yaml +++ b/examples/pypsa/storage_unit.yaml @@ -316,70 +316,82 @@ expressions: sum(StorageUnit_sign * (StorageUnit_p_dispatch - StorageUnit_p_store), over=storage_unit, by=StorageUnit_bus[bus]) adds_to: Bus_injection +masks: + StorageUnit_fix: + description: >- + a storage unit with a fixed build that stands in the snapshot's period — + PyPSA's `fix` rows + where: NOT StorageUnit_p_nom_extendable AND StorageUnit_active + StorageUnit_ext: + description: >- + a storage unit with an extendable build that stands in the snapshot's + period — PyPSA's `ext` rows + where: StorageUnit_p_nom_extendable AND StorageUnit_active + constraints: StorageUnit_fix_p_dispatch_lower: description: "`StorageUnit-fix-p_dispatch-lower` — dispatch is non-negative" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_dispatch >= 0 StorageUnit_fix_p_dispatch_upper: description: "`StorageUnit-fix-p_dispatch-upper` — a fixed unit dispatches at most its nominal power" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_dispatch <= StorageUnit_p_max_pu * StorageUnit_p_nom StorageUnit_fix_p_store_lower: description: "`StorageUnit-fix-p_store-lower` — storing is non-negative" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_store >= 0 StorageUnit_fix_p_store_upper: description: >- `StorageUnit-fix-p_store-upper` — a fixed unit stores at most its nominal power, the minimum-per-unit column carrying that cap negated dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_p_store <= -StorageUnit_p_min_pu * StorageUnit_p_nom StorageUnit_fix_state_of_charge_lower: description: "`StorageUnit-fix-state_of_charge-lower` — charge is non-negative" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_state_of_charge >= 0 StorageUnit_fix_state_of_charge_upper: description: "`StorageUnit-fix-state_of_charge-upper` — a fixed unit holds at most its hours at nominal power" dims: [scenario, snapshot, storage_unit] - where: not StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_fix expression: StorageUnit_state_of_charge <= StorageUnit_max_hours * StorageUnit_p_nom StorageUnit_ext_p_dispatch_lower: description: "`StorageUnit-ext-p_dispatch-lower` — dispatch is non-negative" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_dispatch >= 0 StorageUnit_ext_p_dispatch_upper: description: "`StorageUnit-ext-p_dispatch-upper` — an extendable unit dispatches at most the chosen build" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_dispatch <= StorageUnit_p_max_pu * StorageUnit_p_nom_ext StorageUnit_ext_p_store_lower: description: "`StorageUnit-ext-p_store-lower` — storing is non-negative" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_store >= 0 StorageUnit_ext_p_store_upper: description: >- `StorageUnit-ext-p_store-upper` — an extendable unit stores at most the chosen build, the minimum-per-unit column carrying that cap negated dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_p_store <= -StorageUnit_p_min_pu * StorageUnit_p_nom_ext StorageUnit_ext_state_of_charge_lower: description: "`StorageUnit-ext-state_of_charge-lower` — charge is non-negative" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_state_of_charge >= 0 StorageUnit_ext_state_of_charge_upper: description: "`StorageUnit-ext-state_of_charge-upper` — an extendable unit holds at most its hours at the chosen build" dims: [scenario, snapshot, storage_unit] - where: StorageUnit_p_nom_extendable AND StorageUnit_active + where: StorageUnit_ext expression: StorageUnit_state_of_charge <= StorageUnit_max_hours * StorageUnit_p_nom_ext StorageUnit_ext_p_nom_lower: description: "`StorageUnit-ext-p_nom-lower` — the chosen build is at least its floor in every scenario" diff --git a/examples/pypsa/transformer.yaml b/examples/pypsa/transformer.yaml index 24400869..88f8b3c0 100644 --- a/examples/pypsa/transformer.yaml +++ b/examples/pypsa/transformer.yaml @@ -176,7 +176,7 @@ variables: a line does; absent, and zero in the balance, where the network is lossless dims: [scenario, snapshot, transformer] - where: transmission_losses AND Transformer_active + where: Transformer_lossy absence: zero bounds: lower: 0 @@ -239,6 +239,13 @@ expressions: + sum(Transformer_phase_shift * at(Transformer_phase_shift_cycle_weight, by=snapshot_period[period]), over=transformer) adds_to: Cycle_angle_sum +masks: + Transformer_lossy: + description: >- + a transformer that stands in the snapshot's period, where the run models + transmission losses + where: transmission_losses AND Transformer_active + constraints: Transformer_fix_s_lower: description: "`Transformer-fix-s-lower` — a fixed transformer carries at least the negative of its rating, the loss counted against it" @@ -320,7 +327,7 @@ constraints: Transformer_loss_upper: description: "`Transformer-loss_upper` — a transformer dissipates at most the loss at its rating" dims: [scenario, snapshot, transformer] - where: transmission_losses AND Transformer_active + where: Transformer_lossy expression: Transformer_loss <= Transformer_loss_max Transformer_loss_tangents_forward: description: >- @@ -328,12 +335,12 @@ constraints: loss sits above every cut to its curve for flow one way, as a line's does, over the segment dimension dims: [scenario, snapshot, transformer, segment] - where: transmission_losses AND Transformer_active + where: Transformer_lossy expression: Transformer_loss + Transformer_loss_slope * Transformer_s >= Transformer_loss_offset Transformer_loss_tangents_reverse: description: >- `Transformer-loss_tangents-{k}--1`, `Transformer-loss_secants-neg` — the same fan mirrored, the loss depending on the flow's magnitude dims: [scenario, snapshot, transformer, segment] - where: transmission_losses AND Transformer_active + where: Transformer_lossy expression: Transformer_loss - Transformer_loss_slope * Transformer_s >= Transformer_loss_offset diff --git a/examples/symbols/pypsa.yaml b/examples/symbols/pypsa.yaml index 45d80c05..3e4629c9 100644 --- a/examples/symbols/pypsa.yaml +++ b/examples/symbols/pypsa.yaml @@ -328,6 +328,22 @@ names: Line_active: '\mathrm{on}^{s}' Process_active: '\mathrm{on}^{z}' Transformer_active: '\mathrm{on}^{\sigma}' + Generator_committed: '\mathrm{on}^{\mathrm{com}}' + Generator_com_ext: '\mathrm{on}^{\mathrm{com,ext}}' + Generator_maint_ext: '\mathrm{on}^{\mathrm{mnt,ext}}' + Generator_ramps_from_previous: '\mathrm{prev}' + Link_committed: '\mathrm{on}^{f,\mathrm{com}}' + Link_com_ext: '\mathrm{on}^{f,\mathrm{com,ext}}' + Link_maint_ext: '\mathrm{on}^{f,\mathrm{mnt,ext}}' + Link_ramps_from_previous: '\mathrm{prev}^{f}' + Process_committed: '\mathrm{on}^{z,\mathrm{com}}' + Process_com_ext: '\mathrm{on}^{z,\mathrm{com,ext}}' + Process_maint_ext: '\mathrm{on}^{z,\mathrm{mnt,ext}}' + Process_ramps_from_previous: '\mathrm{prev}^{z}' + StorageUnit_fix: '\mathrm{on}^{h,\mathrm{fix}}' + StorageUnit_ext: '\mathrm{on}^{h,\mathrm{ext}}' + Line_lossy: '\mathrm{on}^{s,\mathrm{lossy}}' + Transformer_lossy: '\mathrm{on}^{\sigma,\mathrm{lossy}}' Generator_capital_weight: '\mathrm{W}' Link_capital_weight: '\mathrm{W}^{f}' StorageUnit_capital_weight: '\mathrm{W}^{h}' diff --git a/mkdocs.yml b/mkdocs.yml index 01b0df5b..1d4d4822 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -53,7 +53,7 @@ nav: - Dimensions: reference/language/dimensions.md - Relations: reference/language/relations.md - Expressions: reference/language/expressions.md - - Named expressions and macros: reference/language/named.md + - Named expressions, masks and macros: reference/language/named.md - Operators: reference/language/operators.md - Piecewise curves and SOS: reference/language/piecewise.md - Assumptions: reference/language/assumptions.md diff --git a/schema/mathspec.schema.json b/schema/mathspec.schema.json index 4016f4be..35de691f 100644 --- a/schema/mathspec.schema.json +++ b/schema/mathspec.schema.json @@ -289,7 +289,7 @@ }, "GivenBlock": { "additionalProperties": false, - "description": "What this file reads and does not build, by kind. Closed at the four kinds.", + "description": "What this file reads and does not build, by kind. Closed at the five kinds.", "properties": { "constraints": { "additionalProperties": { @@ -307,6 +307,14 @@ "title": "Expressions", "type": "object" }, + "masks": { + "additionalProperties": { + "$ref": "#/$defs/GivenMaskBlock" + }, + "default": {}, + "title": "Masks", + "type": "object" + }, "parameters": { "additionalProperties": { "$ref": "#/$defs/GivenParameterBlock" @@ -387,6 +395,36 @@ "title": "GivenExpressionBlock", "type": "object" }, + "GivenMaskBlock": { + "additionalProperties": false, + "description": "A mask this file reads and another file defines.\n\nThe frame is all this file states, and it names the dims the definer's\npredicate reads. A where reads the name as data over that frame, true or false at each coordinate, since a mask reads nothing\na solve decides: the predicate is the definer's.", + "properties": { + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "dims": { + "items": { + "type": "string" + }, + "title": "Dims", + "type": "array" + } + }, + "required": [ + "dims" + ], + "title": "GivenMaskBlock", + "type": "object" + }, "GivenParameterBlock": { "additionalProperties": false, "description": "Data this file reads and another file declares.\n\nIt says what a [`ParameterBlock`][] says, because the frame and the\ndtype are all a parameter declaration holds: a where compares against the\ndtype, and the dim rules read the frame.", @@ -511,6 +549,40 @@ "title": "MacroBlock", "type": "object" }, + "MaskBlock": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A named predicate: one where string, read wherever a ``where:``, a ``when:`` or a ``holds:`` names it.\n\nWritten in YAML as a bare where string, or as a mapping once it carries a\n``description:``, and serialised back to whichever form it was written in::\n\n masks:\n committable: \"Generator_committable AND Generator_active\"\n stands:\n where: build_year <= period_year AND period_year < build_year + lifetime\n description: the generator stands in this period\n\nA bare mask name in a where string stands for its predicate, inside\n``count``, ``shift`` and ``at`` too. The mask's frame is the dims its\npredicate reads, so it declares none. A mask is a predicate, so it is\nnever a value in an expression.", + "properties": { + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "where": { + "title": "Where", + "type": "string" + } + }, + "required": [ + "where" + ], + "title": "MaskBlock", + "type": "object" + }, + { + "type": "string" + } + ] + }, "ObjectiveBlock": { "additionalProperties": false, "description": "A declared objective function.", @@ -879,7 +951,7 @@ }, "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, - "description": "The declared math \u2014 one YAML file, or one dict, validated. Nothing here has seen data.\n\nA ``Spec`` that exists has passed the whole language: constructing one by\nany route \u2014 ``to_spec``, [`model_validate`][], the constructor \u2014 runs\nevery load-time check, expression pass included, and raises\n[`LanguageError`][] on a spec the language refuses.\nHolding one is the proof, so nothing downstream checks it again.\n\nThe API is the twelve declaration sections plus ``version`` and\n``description``, three ways back out \u2014 [`to_dict`][] for the spec as\ndata, [`to_yaml`][] for the file a reviewer reads, [`expand`][] for the\nspec with its formulations written out as plain rows \u2014 and [`program`][], the\nspec typed, which every reader after load walks. Everything else on this\nclass is pydantic's, not a contract this package keeps.", + "description": "The declared math \u2014 one YAML file, or one dict, validated. Nothing here has seen data.\n\nA ``Spec`` that exists has passed the whole language: constructing one by\nany route \u2014 ``to_spec``, [`model_validate`][], the constructor \u2014 runs\nevery load-time check, expression pass included, and raises\n[`LanguageError`][] on a spec the language refuses.\nHolding one is the proof, so nothing downstream checks it again.\n\nThe API is the thirteen declaration sections plus ``version`` and\n``description``, three ways back out \u2014 [`to_dict`][] for the spec as\ndata, [`to_yaml`][] for the file a reviewer reads, [`expand`][] for the\nspec with its formulations written out as plain rows \u2014 and [`program`][], the\nspec typed, which every reader after load walks. Everything else on this\nclass is pydantic's, not a contract this package keeps.", "properties": { "assumptions": { "additionalProperties": { @@ -930,6 +1002,7 @@ "default": { "constraints": {}, "expressions": {}, + "masks": {}, "parameters": {}, "variables": {} } @@ -942,6 +1015,14 @@ "title": "Macros", "type": "object" }, + "masks": { + "additionalProperties": { + "$ref": "#/$defs/MaskBlock" + }, + "default": {}, + "title": "Masks", + "type": "object" + }, "objective": { "anyOf": [ { diff --git a/src/mathspec/_expression_resolver.py b/src/mathspec/_expression_resolver.py index b9343737..6b7d1f74 100644 --- a/src/mathspec/_expression_resolver.py +++ b/src/mathspec/_expression_resolver.py @@ -191,6 +191,9 @@ def _name(self, node: NameNode) -> Expression | None: """ if node.name in self.formals: return None + if node.name in self.ns.masks or node.name in self.ns.given_masks: + self.errors.append(mask_compared(node.name, self.context)) + return None if node.name in self.ns.bodies: try: return cast('Expression', self.ns.named(node.name, self.context)) @@ -790,6 +793,14 @@ def _vacates(offset: int | str) -> bool: return offset != 0 +def mask_compared(name: str, context: str) -> str: + """The refusal for a mask written where a number goes: a side of a comparison, or arithmetic.""" + return ( + f"{context}: '{name}' is a mask, which is true or false where it is read, and not a number. " + f'Write it bare in the where — {name}, or NOT {name} — rather than comparing it or computing with it.' + ) + + def _named_offset_edge_message(name: str) -> str: """Why a named offset must say what the vacated positions contribute. diff --git a/src/mathspec/_where_resolver.py b/src/mathspec/_where_resolver.py index d3c95689..8fb2f08a 100644 --- a/src/mathspec/_where_resolver.py +++ b/src/mathspec/_where_resolver.py @@ -26,7 +26,7 @@ literal_number, nodes, ) -from mathspec._expression_resolver import ExpressionResolver +from mathspec._expression_resolver import ExpressionResolver, mask_compared from mathspec._where_parser import ( ColumnNode, UnresolvedComparisonNode, @@ -35,7 +35,7 @@ UnresolvedWhereNode, ) from mathspec.dimensions import dims_of, join_dims -from mathspec.errors import DimensionError, LanguageError, did_you_mean, prefixed, unordered +from mathspec.errors import DimensionError, LanguageError, SchemaError, did_you_mean, prefixed, unordered from mathspec.expansion import expand from mathspec.program import ( Add, @@ -51,6 +51,7 @@ JoinedPredicate, Mask, Multiply, + NamedMask, Negate, Not, Or, @@ -108,7 +109,7 @@ def _expressions(self) -> ExpressionResolver: def where(self, node: Predicate | UnresolvedWhereNode) -> Predicate | UnresolvedWhereNode: """One predicate node typed, or returned unresolved with its refusal appended.""" - if isinstance(node, BooleanLiteral | TypedPredicate): + if isinstance(node, BooleanLiteral | TypedPredicate | NamedMask): return node if isinstance(node, NameNode): return self._where_name(node) @@ -126,13 +127,24 @@ def where(self, node: Predicate | UnresolvedWhereNode) -> Predicate | Unresolved return Or(self._child(node.left), self._child(node.right)) assert_never(node) + @property + def masks(self) -> frozenset[str]: + """Every mask this file names, defined or given.""" + return self.ns.masks | self.ns.given_masks + def _child(self, node: Predicate | UnresolvedWhereNode) -> Predicate: """A connective's child, typed as resolved: an unresolved one survives only with its refusal appended.""" return cast('Predicate', self.where(node)) def _where_name(self, node: NameNode) -> Predicate | UnresolvedWhereNode: - """A bare name: a parameter's or relation's definedness, or a variable's existence.""" + """A bare name: a mask's predicate, a parameter's or relation's definedness, or a variable's existence.""" ns, context = self.ns, self.context + if node.name in ns.masks: + try: + return ns.mask(node.name, context) + except SchemaError as e: + self.errors.append(str(e)) + return node kind = ns.kind(node.name) if kind is None: self.errors.append(ns.unknown(node.name, context, allow_dims=True)) @@ -304,6 +316,10 @@ def _comparison(self, node: UnresolvedComparisonNode) -> Predicate | UnresolvedW """ if isinstance(node.left, FunctionCallNode) and node.left.name == 'position': return self._position(node.left, node) + compared = [name for side in (node.left, node.right) if (name := _side_name_or_none(side)) is not None] + if masked := [name for name in compared if name in self.masks]: + self.errors.append(mask_compared(masked[0], self.context)) + return node plain = self._plain(node) if plain is None: return self._expression_comparison(node) @@ -614,6 +630,11 @@ class _Plain(NamedTuple): quoted: bool +def _side_name_or_none(side: ArithmeticNode | ColumnNode | KeywordNode) -> str | None: + """The bare name a side spells, or ``None`` — a quoted label is no name.""" + return side.name if isinstance(side, NameNode) else None + + def _side_name(side: ArithmeticNode | ColumnNode) -> str | None: """The name a side of a where-comparison spells — bare or ``relation[column]`` — or ``None`` where it is arithmetic.""" if isinstance(side, NameNode): diff --git a/src/mathspec/advising.py b/src/mathspec/advising.py index 9f8ffbac..ccb7092a 100644 --- a/src/mathspec/advising.py +++ b/src/mathspec/advising.py @@ -69,6 +69,7 @@ def _given(program: Program) -> list[Advice]: for name in given.expressions ), *(Advice('given', name, _given_note('row family', name)) for name in given.constraints), + *(Advice('given', name, _given_note('mask', name)) for name in given.masks), ] diff --git a/src/mathspec/composition.py b/src/mathspec/composition.py index 77047cd0..d6968068 100644 --- a/src/mathspec/composition.py +++ b/src/mathspec/composition.py @@ -47,8 +47,9 @@ reads its own sum through another fragment is refused, both named. * **A given declaration is folded** into the declaration that introduces the name, once the reader is checked to say the same as the introducer or less. - A given expression's body may carry no dimension its reader does not state, - and a name read as one kind and introduced as another is refused. A + A given expression's body may carry no dimension its reader does not + state. A given mask states exactly the dims its definer's predicate reads. + A name read as one kind and introduced as another is refused. A reader's description fills a declaration its owner left undescribed. Two fragments that both read a name have to read it over one frame, as a set. What no fragment introduces stays under ``given:`` until a host model @@ -121,6 +122,7 @@ 'variables': 'given variable', 'constraints': 'given constraint', 'expressions': 'given expression', + 'masks': 'given mask', } #: What a fragment, a base or a patch may be given as. @@ -588,21 +590,25 @@ def _fits( """Refuse a reading that says more than the declaration it folds into. A reading states the frame its introducer declares, and every other field - it writes is the introducer's. A given expression is the one kind whose - frame may be wider than the composed body: a body over fewer dimensions - broadcasts, and the composed load refuses a row it would repeat. + it writes is the introducer's. A mask declares no frame, so its reader + states the dims the predicate reads. A given expression is the one kind + whose frame may be wider than the composed body: a body over fewer + dimensions broadcasts, and the composed load refuses a row it would repeat. """ stated = set(cast('list[str]', _mapping(reading)['dims'])) if kind == 'expressions': frame = set(_definer_frame(loaded, key)) fits = frame <= stated + elif kind == 'masks': + frame = set(next(spec.program.masks[key].dims for spec in loaded.values() if key in spec.program.masks)) + fits = frame == stated else: frame = set(cast('list[str]', _mapping(introduced)['dims'])) fits = frame == stated fields = {f: v for f, v in _mapping(_claims(reading)).items() if f != 'dims'} if fits and all(_mapping(introduced).get(f) == v for f, v in fields.items()): return - how = f'over {sorted(frame)}' if kind == 'expressions' else f'as {introduced!r}' + how = f'over {sorted(frame)}' if kind in ('expressions', 'masks') else f'as {introduced!r}' raise LanguageError( f"fragment '{_reader_of(read, kind, key)}' reads the {GIVEN_KINDS[kind]} {key!r} as {reading!r}, where " f"'{_author_of(read, kind, key)}' introduces it {how}. A given declaration says the same as the " @@ -624,7 +630,7 @@ def _definer_frame(loaded: Mapping[str, Spec], key: str) -> frozenset[str]: return frozenset(frame) -READ_KINDS = ('parameters', 'variables', 'expressions') +READ_KINDS = ('parameters', 'variables', 'expressions', 'masks') def _same_kind(read: Mapping[str, dict[str, object]], merged: Mapping[str, object], kind: str, key: str) -> None: diff --git a/src/mathspec/exclusivity.py b/src/mathspec/exclusivity.py index d1ae8559..893b0cf5 100644 --- a/src/mathspec/exclusivity.py +++ b/src/mathspec/exclusivity.py @@ -31,6 +31,7 @@ ExpressionComparison, JoinedPredicate, Mask, + NamedMask, Negate, Not, Or, @@ -486,6 +487,8 @@ def _evaluate(node: Predicate, cell: dict[Subject, Cell], grid: _Grid) -> bool: return _evaluate(left, cell, grid) and _evaluate(right, cell, grid) case Or(left=left, right=right): return _evaluate(left, cell, grid) or _evaluate(right, cell, grid) + case NamedMask(body=body): + return _evaluate(body, cell, grid) case _: assert_never(node) diff --git a/src/mathspec/lowering.py b/src/mathspec/lowering.py index fdf18100..4ec69f24 100644 --- a/src/mathspec/lowering.py +++ b/src/mathspec/lowering.py @@ -32,6 +32,7 @@ GivenTargets, Link, Mask, + MaskDeclaration, Named, ObjectiveDeclaration, Parameter, @@ -51,6 +52,7 @@ resolve_expression, resolve_expression_text, resolve_where_text, + self_existence, ) from mathspec.validation import emitted_name_errors, reference_errors @@ -128,9 +130,20 @@ def lower(schema: Spec) -> Program: terms = _terms(entries, schema, ns, errors) + masks: dict[str, MaskDeclaration] = {} + for mname, mdef in schema.masks.items(): + named_mask, refusals = ns.mask_entry(mname) + errors.extend(refusals) + if named_mask is not None: + body = Mask(named_mask.body) + frame = tuple(d for d in schema.dimensions if d in body.dims) + masks[mname] = MaskDeclaration(body, frame, mdef.description) + variables = {} for vname, vdef in schema.variables.items(): where = resolve_where_text(vdef.where, ns, f"Variable '{vname}'", errors, self_variable=vname) + if where is not None and (refusal := self_existence(where, vname, f"Variable '{vname}'")) is not None: + errors.append(refusal) if vdef.domain == 'binary': lower_bound, upper_bound = Constant(0.0), Constant(1.0) else: @@ -231,6 +244,7 @@ def lower(schema: Spec) -> Program: ) for name, entry in entries.items() }, + masks=masks, given=GivenTargets( parameters={ name: ParameterDeclaration(tuple(g.dims), g.dtype, g.description) @@ -245,6 +259,7 @@ def lower(schema: Spec) -> Program: expressions={ name: GivenDeclaration(tuple(g.dims), g.description) for name, g in schema.given.expressions.items() }, + masks={name: GivenDeclaration(tuple(g.dims), g.description) for name, g in schema.given.masks.items()}, ), description=schema.description, ) diff --git a/src/mathspec/program.py b/src/mathspec/program.py index 307a03bf..6ab7e62e 100644 --- a/src/mathspec/program.py +++ b/src/mathspec/program.py @@ -66,8 +66,10 @@ 'JoinedPredicate', 'Link', 'Mask', + 'MaskDeclaration', 'Multiply', 'Named', + 'NamedMask', 'Negate', 'Not', 'ObjectiveDeclaration', @@ -722,6 +724,9 @@ class GivenTargets: constraints: Mapping[str, GivenDeclaration] = Sealed({}) #: Named expressions the host model defines, by name. expressions: Mapping[str, GivenDeclaration] = Sealed({}) + #: Masks the host model defines, by name. A where reads one as the data it + #: is: a [`ParameterDefined`][] of a boolean over its frame. + masks: Mapping[str, GivenDeclaration] = Sealed({}) def __post_init__(self) -> None: for f in fields(self): @@ -729,7 +734,7 @@ def __post_init__(self) -> None: def __bool__(self) -> bool: """Whether the program reads anything it does not build.""" - return bool(self.parameters or self.variables or self.constraints or self.expressions) + return bool(self.parameters or self.variables or self.constraints or self.expressions or self.masks) @dataclass(frozen=True) @@ -798,6 +803,22 @@ class ExpressionDeclaration: adds_to: str | None = None +@dataclass(frozen=True) +class MaskDeclaration: + """A named predicate — a ``masks:`` entry, read wherever a ``where``, a ``when`` or a ``holds`` names it. + + It builds nothing of its own: every use stands as a [`NamedMask`][] with + this predicate under it, so a consumer reading a mask never looks the + name up here. + """ + + where: Mask + #: The frame the mask is read over: the dims its predicate carries, in the + #: order ``dimensions:`` declares them. + dims: tuple[str, ...] + description: str | None = None + + @dataclass(frozen=True) class Link: """One link of a ``piecewise:`` block: an expression tied to the breakpoints a values parameter holds. @@ -1023,6 +1044,9 @@ class Program: #: named expression is outside the language is refused by every verb that #: reads the file rather than only by the one that reads the expression. expressions: Mapping[str, ExpressionDeclaration] = Sealed({}) + #: Declared ``masks:``. Each use stands as a [`NamedMask`][] where it is + #: read, so a consumer building rows needs nothing from this group. + masks: Mapping[str, MaskDeclaration] = Sealed({}) #: What this program reads and does not build ([`GivenTargets`][]), for a host model to provide. given: GivenTargets = GivenTargets() #: What the file as a whole is, as its ``description:`` says. @@ -1385,6 +1409,20 @@ class Or: right: Predicate +@dataclass(frozen=True) +class NamedMask: + """A use of a ``masks:`` entry, standing where its name was written, with the entry's predicate under it. + + Its truth is its body's: a consumer steps through it, as + [`where_children`][] does. It is kept as a node rather than written in so + the typesetter can print the symbol where the name stood and define it + once, as [`Named`][] does for an expression. + """ + + name: str + body: Predicate + + #: Every predicate resolution has typed: it names a declaration and the kind is #: settled. Resolution passes these straight through, having nothing left to #: decide about them. @@ -1403,20 +1441,21 @@ class Or: | JoinedPredicate ) -#: The boolean connectives — the only where nodes carrying other where nodes, -#: and so the only place a walk over a predicate recurses. The grammar builds -#: these classes directly, over leaves still unresolved, so a pre-resolution -#: tree shares them — the transient impurity resolution normalizes away. +#: The boolean connectives — with [`NamedMask`][], the only where nodes +#: carrying other where nodes, and so the places a walk over a predicate +#: recurses. The grammar builds these classes directly, over leaves still +#: unresolved, so a pre-resolution tree shares them — the transient impurity +#: resolution normalizes away. Connective = Not | And | Or #: Every resolved predicate node. The parser's ``Unresolved*`` nodes are not members: they live with the #: grammar in [`mathspec._where_parser`][], and resolution rewrites them away #: before anything here is asked. -Predicate = BooleanLiteral | TypedPredicate | Connective +Predicate = BooleanLiteral | TypedPredicate | Connective | NamedMask def where_children(where: Predicate) -> tuple[Predicate, ...]: - """The predicates under *where* — a connective's operands, and nothing under a leaf. + """The predicates under *where* — a connective's operands, a named mask's body, and nothing under a leaf. What every walk over a predicate recurses through, as [`children`][] is for an expression. A leaf has nothing under it whether or not it is @@ -1426,6 +1465,8 @@ def where_children(where: Predicate) -> tuple[Predicate, ...]: return (where.operand,) if isinstance(where, (And, Or)): return (where.left, where.right) + if isinstance(where, NamedMask): + return (where.body,) return () @@ -1439,7 +1480,7 @@ def _atoms(where: Predicate) -> Iterator[TypedPredicate]: """ if isinstance(where, TypedPredicate): yield where - elif isinstance(where, BooleanLiteral | Connective): + elif isinstance(where, BooleanLiteral | Connective | NamedMask): for child in where_children(where): yield from _atoms(child) else: @@ -1556,7 +1597,8 @@ def _fold(node: Predicate) -> Predicate: none, ``NOT True`` is ``False`` and ``NOT NOT X`` is ``X``. What survives is a predicate over data, or the one literal the whole mask reduces to — the invariant [`Mask`][] applies at construction, so it holds wherever - a mask is built. + a mask is built. A [`NamedMask`][] is left whole: its body was folded + when the entry was read, and refused there where it folded to a literal. """ if isinstance(node, Not): operand = _fold(node.operand) diff --git a/src/mathspec/resolution.py b/src/mathspec/resolution.py index 97c25001..9200d75e 100644 --- a/src/mathspec/resolution.py +++ b/src/mathspec/resolution.py @@ -41,9 +41,11 @@ Expression, Mask, Named, + NamedMask, Predicate, Region, RelationDeclaration, + VariableDefined, carries_variable, ) @@ -53,7 +55,7 @@ from mathspec._expression_parser import ComparisonOperator, ParsedNode from mathspec._where_parser import ParsedWhere from mathspec.program import DeclaredDtype - from mathspec.spec import ExpressionBlock, Spec + from mathspec.spec import ExpressionBlock, MaskBlock, Spec #: What a name a file may write turns out to be. Answered by @@ -70,12 +72,15 @@ class Namespace: __slots__ = ( '_loading', + '_masks', '_named', 'bodies', 'constraints', 'dimensions', 'dtypes', + 'given_masks', 'leaf_dims', + 'masks', 'parameters', 'relations', 'schema', @@ -91,7 +96,12 @@ def __init__(self, schema: Spec) -> None: parameters = {**schema.parameters, **schema.given.parameters} self.variables = frozenset(variables) self.bodies = frozenset(schema.expressions) - self.parameters = frozenset(parameters) + #: The declared masks, read by name wherever a where string is. + self.masks = frozenset(schema.masks) + #: The masks this file reads under ``given:``. A where reads each as + #: the boolean data it is to this file, so each is a parameter here too. + self.given_masks = frozenset(schema.given.masks) + self.parameters = frozenset(parameters) | self.given_masks self.dimensions = frozenset(schema.dimensions) #: The declared constraint names, off the flat namespace: a bare name #: never reaches them, so a spec may name a constraint after a variable. @@ -101,6 +111,7 @@ def __init__(self, schema: Spec) -> None: #: what a where comparison checks its literal against. self.dtypes: dict[str, DeclaredDtype] = { **{p: pd.dtype for p, pd in parameters.items()}, + **{m: cast('DeclaredDtype', 'bool') for m in schema.given.masks}, **{d: dd.dtype for d, dd in schema.dimensions.items()}, } #: relation name -> its columns and key, as declared. @@ -112,14 +123,18 @@ def __init__(self, schema: Spec) -> None: #: each leaf a where names, the way a relation leaf carries ``over``. self.leaf_dims: dict[str, tuple[str, ...]] = { **{p: tuple(pd.dims) for p, pd in parameters.items()}, + **{m: tuple(md.dims) for m, md in schema.given.masks.items()}, **{v: tuple(vd.dims or ()) for v, vd in variables.items()}, } #: named expression -> its resolved node, or ``None``, and its refusals; #: filled the first time anything reads the name. self._named: dict[str, tuple[Named | None, tuple[str, ...]]] = {} - #: The named expressions waiting to be resolved, the one asked for - #: first — each above the entries it reads, so it is a cycle's chain. + #: The named expressions and masks waiting to be resolved, the one + #: asked for first — each above the entries it reads, so it is a cycle's chain. self._loading: list[str] = [] + #: mask -> its resolved node, or ``None``, and its refusals; filled + #: the first time anything reads the name. + self._masks: dict[str, tuple[NamedMask | None, tuple[str, ...]]] = {} def named(self, name: str, context: str) -> Named: """The ``expressions:`` entry *name* as the node that stands where its name is written. @@ -138,12 +153,39 @@ def named(self, name: str, context: str) -> Named: raise SchemaError(msg) return node - def cycle(self, name: str, context: str, through: Iterable[str] = ()) -> str | None: + def cycle(self, name: str, context: str, through: Iterable[str] = (), *, noun: str = 'expression') -> str | None: """The refusal for reading *name* while it is being resolved, or ``None``; *through* names the macros the read went through.""" if name not in self._loading: return None chain = ' -> '.join([*self._loading[self._loading.index(name) :], *through, name]) - return f'{context}: circular expression reference: {chain}' + return f'{context}: circular {noun} reference: {chain}' + + def mask(self, name: str, context: str) -> NamedMask: + """The ``masks:`` entry *name* as the node that stands where its name is written. + + Resolved under the entry's own context the first time it is asked + for, and read from then on, so a fault in it is reported once. + + Raises: + SchemaError: The entry reads itself, or does not load. + """ + if (refusal := self.cycle(name, context, noun='mask')) is not None: + raise SchemaError(refusal) + node, _ = self.mask_entry(name) + if node is None: + msg = f"{context}: mask '{name}' does not load. Its refusal is listed with it." + raise SchemaError(msg) + return node + + def mask_entry(self, name: str) -> tuple[NamedMask | None, tuple[str, ...]]: + """The ``masks:`` entry *name* resolved, or ``None``, with every refusal it earned.""" + if name not in self._masks: + self._loading.append(name) + errors: list[str] = [] + node = _mask(name, self.schema.masks[name], self, errors) + self._loading.pop() + self._masks[name] = (node, tuple(errors)) + return self._masks[name] def named_entry(self, name: str) -> tuple[Named | None, tuple[str, ...]]: """The ``expressions:`` entry *name* resolved, or ``None``, with every refusal it earned. @@ -232,7 +274,12 @@ def unknown(self, name: str, context: str, *, allow_dims: bool, formals: Iterabl """ shown: list[tuple[str, Iterable[str]]] = [('Formals', formals)] if formals else [] shown += ( - [('Parameters', self.parameters), ('Dimensions', self.dimensions), ('Relations', self.relations)] + [ + ('Parameters', self.parameters), + ('Dimensions', self.dimensions), + ('Relations', self.relations), + *([('Masks', self.masks)] if self.masks else []), + ] if allow_dims else [('Variables', self.variables), ('Parameters', self.parameters)] ) @@ -467,6 +514,42 @@ def _named(name: str, block: ExpressionBlock, ns: Namespace, errors: list[str]) return Named(name, Cases((*regions, left_over))) +def _mask(name: str, block: MaskBlock, ns: Namespace, errors: list[str]) -> NamedMask | None: + """One ``masks:`` entry as the node every use of it holds, or ``None`` once anything in it failed. + + A predicate the connectives decide is refused, since a name for every row + or for none narrows nothing a where could not say plainly. + """ + context = f"Mask '{name}'" + body = resolve_where_text(block.where, ns, context, errors) + if body is None: + return None + if isinstance(body, BooleanLiteral): + errors.append( + f'{context}: the predicate {block.where!r} folds to {str(body.value).lower()}, so the mask ' + f'{"admits every row" if body.value else "admits no row"} and names nothing a where needs. ' + f'Delete it, or write the predicate over the data.' + ) + return None + return NamedMask(name, body) + + +def self_existence(where: Predicate, variable: str, context: str) -> str | None: + """The refusal for a variable whose own where asks, through a mask, whether it exists — or ``None``. + + The where resolver refuses the bare name; a mask's predicate is resolved + under its own name, so the check runs again on the tree with every mask's + predicate in it. + """ + if not any(isinstance(atom, VariableDefined) and atom.name == variable for atom in Mask(where).atoms): + return None + return ( + f"{context}: variable '{variable}' asks whether it exists in its own where, through a mask, which " + f'nothing can answer — the mask is what decides where it exists. Test a parameter, or another ' + f'variable declared before it.' + ) + + def _constant_arm(context: str, *, value: bool) -> str: """The refusal for a case arm whose mask the connectives already decided. diff --git a/src/mathspec/spec.py b/src/mathspec/spec.py index b9398eed..098e7d99 100644 --- a/src/mathspec/spec.py +++ b/src/mathspec/spec.py @@ -361,8 +361,22 @@ class GivenExpressionBlock(_StrictBlock): description: str | None = None +class GivenMaskBlock(_StrictBlock): + """A mask this file reads and another file defines. + + The frame is all this file states, and it names the dims the definer's + predicate reads. A where reads the name as data over that frame, true or false at each coordinate, since a mask reads nothing + a solve decides: the predicate is the definer's. + """ + + _label: ClassVar[str] = 'a given mask declaration' + + dims: list[str] + description: str | None = None + + class GivenBlock(_StrictBlock): - """What this file reads and does not build, by kind. Closed at the four kinds.""" + """What this file reads and does not build, by kind. Closed at the five kinds.""" _label: ClassVar[str] = 'a given block' @@ -374,10 +388,12 @@ class GivenBlock(_StrictBlock): constraints: dict[str, GivenConstraintBlock] = {} #: Named expressions another file defines ([`GivenExpressionBlock`][]). expressions: dict[str, GivenExpressionBlock] = {} + #: Masks another file defines ([`GivenMaskBlock`][]). + masks: dict[str, GivenMaskBlock] = {} def __bool__(self) -> bool: """Whether the file reads anything it does not build.""" - return bool(self.parameters or self.variables or self.constraints or self.expressions) + return bool(self.parameters or self.variables or self.constraints or self.expressions or self.masks) class ConstraintBlock(_StrictBlock): @@ -567,6 +583,48 @@ def _as_written(self) -> str | dict[str, object]: return written +class MaskBlock(_StrictBlock): + """A named predicate: one where string, read wherever a ``where:``, a ``when:`` or a ``holds:`` names it. + + Written in YAML as a bare where string, or as a mapping once it carries a + ``description:``, and serialised back to whichever form it was written in:: + + masks: + committable: "Generator_committable AND Generator_active" + stands: + where: build_year <= period_year AND period_year < build_year + lifetime + description: the generator stands in this period + + A bare mask name in a where string stands for its predicate, inside + ``count``, ``shift`` and ``at`` too. The mask's frame is the dims its + predicate reads, so it declares none. A mask is a predicate, so it is + never a value in an expression. + """ + + _label: ClassVar[str] = 'a mask declaration' + + #: The predicate, in the where grammar. + where: str + description: str | None = None + + @model_validator(mode='before') + @classmethod + def _from_string(cls, data: object) -> object: + return {'where': data} if isinstance(data, str) else data + + @classmethod + @override + def __get_pydantic_json_schema__(cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler) -> dict[str, object]: + """The published schema admits the bare string the one-line form is written as.""" + return _also_written_as(core_schema, handler, {'type': 'string'}) + + @model_serializer + def _as_written(self) -> str | dict[str, object]: + if self.description is None: + return self.where + return {'where': self.where, 'description': self.description} + + class AssumptionBlock(_StrictBlock): """What the spec assumes of its data: a predicate every coordinate it is checked at has to satisfy. @@ -825,7 +883,7 @@ class Spec(_StrictBlock): [`LanguageError`][] on a spec the language refuses. Holding one is the proof, so nothing downstream checks it again. - The API is the twelve declaration sections plus ``version`` and + The API is the thirteen declaration sections plus ``version`` and ``description``, three ways back out — [`to_dict`][] for the spec as data, [`to_yaml`][] for the file a reviewer reads, [`expand`][] for the spec with its formulations written out as plain rows — and [`program`][], the @@ -852,6 +910,7 @@ class is pydantic's, not a contract this package keeps. constraints: dict[str, ConstraintBlock] = {} objective: ObjectiveBlock | None = None expressions: dict[str, ExpressionBlock] = {} + masks: dict[str, MaskBlock] = {} macros: dict[str, MacroBlock] = {} piecewise: dict[str, PiecewiseBlock] = {} sos: dict[str, SosBlock] = {} @@ -995,7 +1054,7 @@ def _names_are_names(self) -> Spec: Read off the spec's own mappings rather than a list of sections, so a section added later cannot be forgotten here — every mapping a Spec - carries is keyed by a declaration name. ``given:`` nests its four + carries is keyed by a declaration name. ``given:`` nests its five mappings one level down, so they are read off [`GivenBlock`][] the same way. """ diff --git a/src/mathspec/typesetting/__init__.py b/src/mathspec/typesetting/__init__.py index 6a85ebd8..ffe441de 100644 --- a/src/mathspec/typesetting/__init__.py +++ b/src/mathspec/typesetting/__init__.py @@ -176,8 +176,8 @@ def typeset_declaration( """Render one declaration as the bare line the document prints for it. The line the whole-spec render prints for it — a named expression's - definition, a constraint, an assumption, a ``piecewise:`` curve, or a - variable's domain, quantifier included — + or a mask's definition, a constraint, an assumption, a ``piecewise:`` + curve, or a variable's domain, quantifier included — with no document, label, equation number or math delimiters around it, for a math context the caller lays out: a docstring, a table cell. A line on its own has no Definitions section beside it, so the plain named @@ -186,8 +186,8 @@ def typeset_declaration( Args: spec: Anything [`mathspec.to_spec`][] accepts, or a [`Program`][]. - name: A named expression, constraint, assumption, ``piecewise:`` - block or variable the spec declares. + name: A named expression, mask, constraint, assumption, + ``piecewise:`` block or variable the spec declares. fmt: What spells the math — a key of [`FORMATS`][]. symbols: How names print; see [`typeset`][]. inline_expressions: Substitute the plain named expressions the line uses, so it @@ -201,7 +201,7 @@ def typeset_declaration( Raises: ValueError: *fmt* names no format. LanguageError: A spec that does not compile; it does not print. - SchemaError: *name* is declared as none of the five, as two — a + SchemaError: *name* is declared as none of the six, as two — a constraint may share a variable's name — or under ``given:``, which prints in the legend rather than as a line; or a symbol table entry names nothing in the spec. @@ -213,6 +213,7 @@ def typeset_declaration( 'variable': given.variables, 'expression': given.expressions, 'constraint': given.constraints, + 'mask': given.masks, } given_kind = next((kind for kind, group in givens.items() if name in group), None) if given_kind is not None: diff --git a/src/mathspec/typesetting/format.py b/src/mathspec/typesetting/format.py index fe888114..00193b6e 100644 --- a/src/mathspec/typesetting/format.py +++ b/src/mathspec/typesetting/format.py @@ -32,6 +32,7 @@ 'plus', 'minus', 'equal', + 'iff', 'le', 'ge', 'lt', @@ -66,7 +67,8 @@ #: Every operator a walk can emit, by the name the walk uses for it, with its #: LaTeX spelling first and its Typst spelling second — one row per operator, -#: so no format can be missing one. ``such_that`` is the colon in +#: so no format can be missing one. ``iff`` is the ⟺ a mask is defined +#: with, since a predicate is not a value to equate. ``such_that`` is the colon in #: "∀ t ∈ T : condition", ``times`` sits between sets in the legend, #: ``maps_to`` is the → in a coordinate map, ``curve`` and ``hull`` are the two #: sets a ``piecewise:`` block states its links lie on, and the three @@ -78,6 +80,7 @@ 'plus': ('+', '+'), 'minus': ('-', '-'), 'equal': ('=', '='), + 'iff': (r'\iff', 'arrow.l.r.double.long'), 'le': (r'\le', '<='), 'ge': (r'\ge', '>='), 'lt': ('<', '<'), diff --git a/src/mathspec/typesetting/legend.py b/src/mathspec/typesetting/legend.py index 5a7c0e1c..28d24451 100644 --- a/src/mathspec/typesetting/legend.py +++ b/src/mathspec/typesetting/legend.py @@ -111,6 +111,8 @@ def masks(mask: Mask) -> None: expressions(entry.expression) for curve in program.piecewise.values(): expressions(*(link.expression for link in curve.links)) + for mask in program.masks.values(): + masks(mask.where) for declaration in (*program.constraints.values(), *program.variables.values()): if declaration.where is not None: masks(declaration.where) @@ -133,7 +135,7 @@ def _op(self, name: OperatorName) -> str: return self.format.operators[name] def glossaries(self, noticed: Noticed, defined: Iterable[str]) -> list[tuple[str, list[Entry]]]: - """The sets, parameters, variables, given declarations and definitions, each with its symbol, its dims and its description. + """The sets, parameters, variables, given declarations, definitions and masks, each with its symbol, its dims and its description. *defined* names the expressions that print under their own symbol, so a legend row stands exactly where a symbol does. @@ -189,6 +191,14 @@ def glossaries(self, noticed: Noticed, defined: Iterable[str]) -> list[tuple[str ) for g, block in program.given.constraints.items() ), + *( + self._entry( + self.symbols.name[g], + f'{fmt.mono(g)}{self._over(list(block.dims))}, a mask another file defines', + block.description, + ) + for g, block in program.given.masks.items() + ), ] shown = set(defined) definitions = [ @@ -196,12 +206,17 @@ def glossaries(self, noticed: Noticed, defined: Iterable[str]) -> list[tuple[str for e, block in program.expressions.items() if e in shown ] + masks = [ + self._entry(self.symbols.name[m], f'{fmt.mono(m)}{self._over(list(block.dims))}', block.description) + for m, block in program.masks.items() + ] groups = ( ('Sets', sets), ('Parameters', parameters), ('Variables', variables), ('Given', given), ('Definitions', definitions), + ('Masks', masks), ) return [(title, entries) for title, entries in groups if entries] diff --git a/src/mathspec/typesetting/symbols.py b/src/mathspec/typesetting/symbols.py index da15d756..30346672 100644 --- a/src/mathspec/typesetting/symbols.py +++ b/src/mathspec/typesetting/symbols.py @@ -104,7 +104,8 @@ class Symbols: Attributes: overridden: Names the table spelled; the convention note quotes only derived symbols. - name: Each parameter's, variable's and expression's symbol. + name: Each parameter's, variable's, expression's and mask's symbol. + A mask is upright, being read off the data as a parameter is. constraint: Each constraint's symbol, the subscript ``dual(c)`` prints λ against. Off the flat namespace, like the constraints themselves — a spec may name a constraint after a variable, so this is its @@ -146,6 +147,8 @@ def symbols_for(program: Program, fmt: Format, table: SymbolTable) -> Symbols: *program.given.variables, *program.expressions, *program.given.expressions, + *program.masks, + *program.given.masks, ) declared = frozenset(names) @@ -291,6 +294,8 @@ def _declared(program: Program) -> set[str]: | set(program.given.variables) | set(program.expressions) | set(program.given.expressions) + | set(program.masks) + | set(program.given.masks) | set(program.constraints) | set(program.given.constraints) ) diff --git a/src/mathspec/typesetting/walk.py b/src/mathspec/typesetting/walk.py index 11c2bf44..1e0aa369 100644 --- a/src/mathspec/typesetting/walk.py +++ b/src/mathspec/typesetting/walk.py @@ -35,6 +35,7 @@ Mask, Multiply, Named, + NamedMask, Negate, Not, Or, @@ -539,9 +540,12 @@ def _where(self, node: Predicate, ctx: _Context) -> tuple[str, int]: assert not node.value, 'an always-true mask is folded away or refused before anything prints it' return self._op('false'), _ATOM + if isinstance(node, NamedMask): + return ctx.indexed(self.symbols.name[node.name], list(self.program.masks[node.name].dims)), _ATOM + if isinstance(node, ParameterDefined): indexed = ctx.indexed(self.symbols.name[node.name], list(node.dims)) - if self._parameters[node.name].dtype == 'bool': + if node.name in self.program.given.masks or self._parameters[node.name].dtype == 'bool': return indexed, _ATOM return f'{indexed} {self.format.prose(" is defined")}', comparison @@ -664,6 +668,7 @@ def equations(self) -> list[tuple[str, list[Line]]]: ('Objective', self._objective()), ('Subject to', self._constraints()), ('Definitions', self._definitions()), + ('Masks', [self.mask(name) for name in self.program.masks]), ('Variable domains', self._variables()), ('Assumptions', self._assumptions()), ] @@ -749,21 +754,38 @@ def definition(self, name: str) -> Line: condition=self._quantifier(frame, ''), ) + def mask(self, name: str) -> Line: + """The line defining one mask, ``symbol ⟺ predicate`` over its frame. + + A mask is a predicate rather than a value, so it is defined with ⟺ + where an expression is defined with =; every use prints the symbol. + """ + entry = self.program.masks[name] + frame = list(entry.dims) + ctx = self._context(frame) + return Line( + label=name, + left=ctx.indexed(self.symbols.name[name], frame), + right=f'{self._op("iff")} {self._predicate(entry.where.root, ctx)}', + condition=self._quantifier(frame, ''), + ) + def line(self, name: str) -> Line: - """The one line *name* prints as: a named expression, a constraint, an assumption, a curve, or a variable's domain. + """The one line *name* prints as: a named expression, a mask, a constraint, an assumption, a curve, or a variable's domain. An assumption is looked up where the document prints it from, so a condition a curve's method states is a line a reader can ask for before the curve is written out. Raises: - SchemaError: *name* is declared as none of the five, or as two — a + SchemaError: *name* is declared as none of the six, or as two — a constraint may share a variable's name, and one line prints one of them. """ program = self.program kinds = { 'named expression': (program.expressions, self.definition), + 'mask': (program.masks, self.mask), 'constraint': (program.constraints, self._constraint), 'assumption': (program.assumptions, self._assumption), 'curve': (program.piecewise, self._piecewise), @@ -773,7 +795,7 @@ def line(self, name: str) -> Line: if not found: everything = {n for group, _ in kinds.values() for n in group} msg = ( - f"'{name}' is not a named expression, constraint, assumption, curve or variable. " + f"'{name}' is not a named expression, mask, constraint, assumption, curve or variable. " f'{did_you_mean(name, everything)}' ) raise SchemaError(msg) diff --git a/src/mathspec/validation.py b/src/mathspec/validation.py index 32f3bc56..12b76bee 100644 --- a/src/mathspec/validation.py +++ b/src/mathspec/validation.py @@ -108,6 +108,8 @@ def _flat_namespace(schema: Spec) -> list[tuple[str, Iterable[str]]]: ('given variable', schema.given.variables), ('named expression', schema.expressions), ('given expression', schema.given.expressions), + ('mask', schema.masks), + ('given mask', schema.given.masks), ('macro', schema.macros), ] @@ -155,6 +157,7 @@ def _frame_dimensions(schema: Spec) -> Iterator[str]: *(('Given variable', name, g.dims) for name, g in schema.given.variables.items()), *(('Given expression', name, g.dims) for name, g in schema.given.expressions.items()), *(('Given constraint', name, g.dims) for name, g in schema.given.constraints.items()), + *(('Given mask', name, g.dims) for name, g in schema.given.masks.items()), *(('Constraint', name, c.dims) for name, c in schema.constraints.items()), *(('Named expression', name, e.dims or []) for name, e in schema.expressions.items()), ] diff --git a/tests/test_canonical.py b/tests/test_canonical.py index 54edf0e3..c5d25364 100644 --- a/tests/test_canonical.py +++ b/tests/test_canonical.py @@ -149,6 +149,7 @@ def test_the_declarations_of_a_section_are_sorted_by_name(): 'constraints', 'objective', 'expressions', + 'masks', 'macros', 'piecewise', 'sos', diff --git a/tests/test_given.py b/tests/test_given.py index 732f1e93..ba355282 100644 --- a/tests/test_given.py +++ b/tests/test_given.py @@ -48,11 +48,11 @@ } -def test_given_holds_four_kinds_and_refuses_a_fifth(): +def test_given_holds_five_kinds_and_refuses_a_sixth(): """The section is closed, so a kind nobody has admitted yet is the schema's own refusal.""" with pytest.raises(LanguageError) as raised: to_spec({**SUPPLY, 'given': {'macros': {'twice': {'params': ['x'], 'template': '2 * x'}}}}) - assert 'Valid keys: constraints, expressions, parameters, variables' in str(raised.value), ( + assert 'Valid keys: constraints, expressions, masks, parameters, variables' in str(raised.value), ( 'the refusal names what the block takes' ) diff --git a/tests/test_masks.py b/tests/test_masks.py new file mode 100644 index 00000000..4ea9a1f6 --- /dev/null +++ b/tests/test_masks.py @@ -0,0 +1,306 @@ +# SPDX-FileCopyrightText: mathspec Contributors +# +# SPDX-License-Identifier: MIT + +"""A mask is a named predicate: written once under ``masks:``, read by name wherever a where string is (#803). + +A use stands as a `NamedMask` with the predicate under it, so every question +asked of a mask is asked of the predicate, and the typesetter still prints the +name. Another file reads a mask under ``given: masks:`` as the boolean data it +is to that file, and `merge` folds the reading into the definition. +""" + +from __future__ import annotations + +import copy +from typing import Any + +import pytest + +from mathspec import FORMATS, LanguageError, advice, merge, override, to_spec, typeset, typeset_declaration +from mathspec.program import Mask, NamedMask, ParameterDefined, VariableDefined +from tests.fixtures import SMALL_MODEL, varied + +#: A unit stands in a period between its build year and its retirement, and +#: two families of rows read that test: the core's capacity and a ramp. +DIMS: dict[str, Any] = {'period': {'dtype': 'int', 'ordered': True}, 'generator': {'dtype': 'str'}} +FRAME = ['period', 'generator'] +STANDS = 'build_year <= period_year AND period_year < build_year + lifetime' + +CORE: dict[str, Any] = { + 'dimensions': DIMS, + 'parameters': { + 'build_year': {'dims': ['generator']}, + 'lifetime': {'dims': ['generator']}, + 'period_year': {'dims': ['period']}, + 'p_nom': {'dims': ['generator']}, + }, + 'masks': {'stands': {'where': STANDS, 'description': 'the unit stands in this period'}}, + 'variables': {'p': {'dims': FRAME, 'where': 'stands', 'bounds': {'lower': 0}}}, + 'constraints': {'cap': {'dims': FRAME, 'where': 'stands', 'expression': 'p <= p_nom'}}, + 'objective': {'sense': 'minimize', 'expression': 'sum(p, over=[period, generator])'}, +} + +RAMPING: dict[str, Any] = { + 'dimensions': DIMS, + 'given': {'variables': {'p': {'dims': FRAME}}, 'masks': {'stands': {'dims': FRAME}}}, + 'parameters': {'ramp': {'dims': ['generator']}}, + 'constraints': { + 'ramp_up': { + 'dims': FRAME, + 'where': 'stands AND shift(stands, along=period, offset=1)', + 'expression': 'p - shift(p, along=period, offset=1) <= ramp', + } + }, +} + + +def test_a_mask_stands_where_its_name_is_written_with_its_predicate_under_it(): + program = to_spec(CORE).program + where = program.constraints['cap'].where + assert where is not None and isinstance(where.root, NamedMask), 'the use keeps the name the file wrote' + assert where.root.body == program.masks['stands'].where.root, 'and carries the predicate the entry declares' + assert program.masks['stands'].dims == ('period', 'generator') + assert where.names_read == {'build_year', 'lifetime', 'period_year'}, 'a mask reads what its predicate reads' + assert where.dims == {'period', 'generator'} + + +@pytest.mark.parametrize( + 'patch', + [ + pytest.param({'constraints.cap.where': 'NOT stands'}, id='under-a-connective'), + pytest.param({'constraints.cap.where': 'stands AND shift(stands, along=period, offset=1)'}, id='in-shift'), + pytest.param( + {'assumptions.lives': {'holds': 'lifetime > 0', 'where': 'stands'}}, id='as-an-assumption-s-where' + ), + pytest.param({'assumptions.ever': 'count(stands, over=period) >= 1'}, id='in-count'), + pytest.param( + { + 'expressions.share': { + 'dims': FRAME, + 'cases': {'standing': {'when': 'stands', 'expression': 'p_nom'}}, + 'otherwise': 0, + } + }, + id='as-a-when', + ), + pytest.param({'masks.old': 'stands AND lifetime > 30', 'constraints.cap.where': 'old'}, id='in-another-mask'), + pytest.param( + {'expressions.half': 'p_nom / 2', 'masks.big': 'half > 10', 'constraints.cap.where': 'big'}, + id='reading-a-data-only-expression', + ), + pytest.param({'masks.stands': STANDS}, id='the-one-line-form'), + ], +) +def test_a_mask_is_read_wherever_a_where_string_is(patch): + spec = to_spec(varied(CORE, **patch)) + assert to_spec(spec.to_yaml()).program == spec.program, 'the file reads back to the same program' + + +def test_the_one_line_form_writes_back_as_written(): + spec = to_spec(varied(CORE, **{'masks.stands': STANDS})) + assert spec.to_dict()['masks'] == {'stands': STANDS}, 'a mask with no words is one where string' + + +def test_a_mask_declares_no_frame(): + """A declared frame wider than the predicate printed a use over an index no quantifier binds. + + ``young`` over ``[period, generator]`` read only ``lifetime``. A use over + ``[generator]`` loaded, and every format printed ``young`` with a period + index under a quantifier over generators alone. The predicate gives a mask + its frame, so the closed schema now refuses ``dims:`` on a mask. + """ + young = { + 'masks.young': {'dims': FRAME, 'where': 'lifetime > 10'}, + 'constraints.total': {'dims': ['generator'], 'where': 'young', 'expression': 'sum(p, over=period) <= p_nom'}, + } + with pytest.raises(LanguageError, match=r"masks\.young: unknown key 'dims' in a mask declaration"): + to_spec(varied(CORE, **young)) + + +def test_a_variable_that_asks_through_a_mask_whether_it_exists_is_refused(): + """The where resolver refused the bare name only: a mask's predicate is read under the mask's own name.""" + with pytest.raises(LanguageError, match=r"variable 'p' asks whether it exists in its own where, through a mask"): + to_spec(varied(CORE, **{'masks.live': 'p', 'variables.p.where': 'live'})) + + +def test_a_mask_may_ask_whether_another_variable_exists(): + spec = to_spec(varied(CORE, **{'masks.dispatched': 'p', 'constraints.cap.where': 'dispatched'})) + where = spec.program.constraints['cap'].where + assert where is not None and VariableDefined('p', ('period', 'generator')) in where.atoms + + +@pytest.mark.parametrize( + ('patch', 'says'), + [ + pytest.param({'masks': {'a': 'b', 'b': 'a'}}, 'circular mask reference: a -> b -> a', id='a-cycle'), + pytest.param({'masks': {'a': 'a'}}, 'circular mask reference: a -> a', id='a-mask-reading-itself'), + pytest.param({'masks': {'a': 'True'}}, 'folds to true, so the mask admits every row', id='always-true'), + pytest.param({'masks': {'a': 'NOT True'}}, 'folds to false, so the mask admits no row', id='always-false'), + pytest.param( + {'masks': {'a': 'c > 0'}, 'expressions.e': 'c * a'}, + "'a' is a mask, which is true or false where it is read, and not a number", + id='a-mask-in-arithmetic', + ), + pytest.param( + {'masks': {'a': 'c > 0'}, 'variables.p.where': 'a == 1'}, + "'a' is a mask, which is true or false where it is read, and not a number", + id='a-mask-compared', + ), + pytest.param( + {'given.masks.m': {'dims': ['g']}, 'expressions.e': 'c * m'}, + "'m' is a mask, which is true or false where it is read, and not a number", + id='a-given-mask-in-arithmetic', + ), + pytest.param( + {'given.expressions.ge': {'dims': ['g']}, 'masks': {'a': 'ge > 0'}}, + "where references the given expression 'ge'", + id='a-mask-reading-a-given-expression', + ), + pytest.param({'masks': {'c': 'flag'}}, "Mask 'c' collides with the parameter", id='a-name-taken'), + pytest.param({'masks': {'a': 'nope'}}, "Mask 'a': 'nope' not found", id='an-unknown-name'), + pytest.param( + {'masks': {'a': 'c > 0'}, 'variables.r.where': 'a'}, + "outside the frame ['h']", + id='a-use-over-a-frame-the-predicate-leaves', + ), + ], +) +def test_what_a_mask_may_not_be_is_refused_at_load(patch, says): + with pytest.raises(LanguageError, match=None) as raised: + to_spec(varied(SMALL_MODEL, **patch)) + assert says in str(raised.value), 'the refusal names the mask and what to write instead' + + +def test_cases_split_by_a_mask_are_proved_apart_through_its_predicate(): + """The overlap check evaluates a when on a grid of the data it reads, and a mask's data is its predicate's.""" + case = { + 'dims': ['g'], + 'cases': {'on': {'when': 'a', 'expression': 1}, 'off': {'when': 'flag', 'expression': 2}}, + 'otherwise': 0, + } + with pytest.raises(LanguageError, match=r"cases 'on' and 'off' both claim the value where flag is true"): + to_spec(varied(SMALL_MODEL, **{'masks.a': 'flag', 'expressions.e': case})) + apart = varied(case, **{'cases.off.when': 'NOT a'}) + assert to_spec(varied(SMALL_MODEL, **{'masks.a': 'flag', 'expressions.e': apart})) + + +# --------------------------------------------------------------------------- +# another file's mask +# --------------------------------------------------------------------------- + + +def test_a_fragment_reads_another_file_s_mask_as_boolean_data(): + """#803's ramp read `active == 1` under `given: expressions:`, which a where may not read.""" + program = to_spec(RAMPING).program + where = program.constraints['ramp_up'].where + assert where is not None and ParameterDefined('stands', ('period', 'generator')) in where.atoms, ( + 'alone, the reading is data the host provides' + ) + assert sorted(program.given.masks) == ['stands'] + + +def test_check_notes_the_mask_a_fragment_reads(): + """Every other given kind had a note saying who provides it, and a given mask had none.""" + notes = {(note.kind, note.subject): note.text for note in advice(RAMPING)} + assert "mask 'stands' is read here and declared elsewhere" in notes[('given', 'stands')] + + +@pytest.mark.parametrize( + 'fragments', + [ + pytest.param([CORE, RAMPING], id='the-definer-first'), + pytest.param([RAMPING, CORE], id='the-reader-first'), + ], +) +def test_merge_folds_the_reading_into_the_mask_another_fragment_defines(fragments): + program = merge(fragments).program + assert not program.given, 'the reading is spent once the definer is in the composition' + where = program.constraints['ramp_up'].where + assert where is not None and isinstance(where.conjuncts[0], NamedMask), 'the composed row reads the mask by name' + + +@pytest.mark.parametrize( + ('reader', 'says'), + [ + pytest.param( + varied(RAMPING, **{'given.masks.stands.dims': ['generator'], 'constraints.ramp_up.where': 'stands'}), + "reads the given mask 'stands'", + id='a-reader-that-states-less-than-the-frame', + ), + pytest.param( + varied( + RAMPING, + **{'given.masks': {}, 'given.parameters.stands': {'dims': FRAME, 'dtype': 'bool'}}, + ), + "reads 'stands' as a given parameter, where '#1' introduces it under 'masks:'", + id='a-mask-read-as-a-parameter', + ), + ], +) +def test_a_reading_the_definer_does_not_answer_is_refused(reader, says): + with pytest.raises(LanguageError) as raised: + merge([CORE, reader]) + assert says in str(raised.value) + + +def test_a_reader_that_states_a_frame_wider_than_the_mask_s_is_refused(): + """A reader stated a superset of the predicate's dims, and the merge took it. + + A mask's frame is the dims its predicate reads, which no file chooses, so + a wider reading only defers the failure to the composed load. + """ + definer = varied(CORE, **{'masks.stands': 'lifetime > 0'}) + reader = varied(RAMPING, **{'constraints.ramp_up.where': 'stands'}) + with pytest.raises(LanguageError) as raised: + merge([definer, reader]) + assert "reads the given mask 'stands' as {'dims': ['period', 'generator']}" in str(raised.value), ( + 'the refusal names the frame the reader states' + ) + assert "introduces it over ['generator']" in str(raised.value), "and the frame the definer's predicate reads" + + +def test_two_fragments_that_define_one_mask_collide(): + other = {'dimensions': DIMS, 'parameters': {'retired': {'dims': ['generator'], 'dtype': 'bool'}}} + with pytest.raises(LanguageError, match=r"fragments '#1' and '#2' both declare the mask 'stands'"): + merge([CORE, {**other, 'masks': {'stands': 'NOT retired'}}]) + + +def test_a_patch_rewrites_a_mask_s_predicate(): + laid = override(CORE, [{'masks': {'stands': {'where': 'lifetime > 0'}}}]) + assert laid.masks['stands'].where == 'lifetime > 0' + assert laid.masks['stands'].description == 'the unit stands in this period', 'a patch says only what it changes' + + +# --------------------------------------------------------------------------- +# typesetting +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize('fmt', sorted(FORMATS)) +def test_a_mask_prints_its_symbol_where_it_is_read_and_its_predicate_once(fmt): + printed = typeset(CORE, fmt) + defined = typeset_declaration(CORE, 'stands', fmt) + assert printed.count(FORMATS[fmt].operators['iff']) == 1, 'the predicate prints once, as the definition' + assert defined.split()[0].replace('\\_', '_').count('stands') == 1, 'the definition opens with the symbol' + assert FORMATS[fmt].operators['iff'] in defined, 'a predicate is defined with iff, not equated' + assert 'the unit stands in this period' in printed, 'the legend carries its words' + + +@pytest.mark.parametrize('fmt', sorted(FORMATS)) +def test_a_given_mask_prints_in_the_legend_and_no_line_of_its_own(fmt): + assert 'a mask another file defines' in typeset(RAMPING, fmt) + with pytest.raises(LanguageError, match=r"'stands' is a given mask, and a given declaration prints no line"): + typeset_declaration(RAMPING, 'stands', fmt) + + +def test_the_fixtures_are_never_mutated(): + before = copy.deepcopy((CORE, RAMPING)) + merge([CORE, RAMPING]) + assert before == (CORE, RAMPING) + + +def test_a_mask_with_no_declared_frame_takes_the_dims_its_predicate_reads(): + spec = to_spec(varied(SMALL_MODEL, **{'masks.a': 'c > 0 AND flag'})) + assert spec.program.masks['a'].dims == ('g',) + assert spec.program.masks['a'].where == Mask(spec.program.masks['a'].where.root), 'the predicate is folded' diff --git a/tests/test_validation.py b/tests/test_validation.py index 0a46cd35..f7b17a6a 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -2369,6 +2369,15 @@ def test_an_infinite_bound_is_refused_with_the_null_that_opens_a_side(side, valu 't', id='position', ), + pytest.param({'masks.first': 'position(t) == 0'}, 't', id='a-mask-predicate'), + pytest.param( + { + 'masks.lit': 'on', + 'constraints.c': {'dims': ['t'], 'where': 'shift(lit, along=t, offset=1)', 'expression': 'x <= 0'}, + }, + 't', + id='a-shifted-mask', + ), pytest.param( {'piecewise.curve': {'over': 'bp', 'links': [['x', 'bp_x'], ['y', 'bp_y']]}}, 'bp', diff --git a/tests/typesetting/golden/latex.out b/tests/typesetting/golden/latex.out index 3815c78b..da63aa76 100644 --- a/tests/typesetting/golden/latex.out +++ b/tests/typesetting/golden/latex.out @@ -67,6 +67,12 @@ \item[{$\mathrm{startup\_cost}$}] \texttt{startup\_cost} over $\mathcal{T} \times \mathcal{G}$ --- what starting a unit in this snapshot costs, which the horizon's edge changes \end{description} +\paragraph{Masks} +\begin{description} +\item[{$\mathrm{dispatchable}$}] \texttt{dispatchable} over $\mathcal{G}$ --- a unit that dispatches and cannot be turned down +\item[{$\mathrm{priced\_dispatch}$}] \texttt{priced\_dispatch} over $\mathcal{G}$ +\end{description} + \noindent Upright is what the data supplies --- a parameter such as $\mathrm{p}^{\mathrm{max}}$, a coordinate map, a label --- and italic is what the solver chooses, such as $p$. An index is italic too, being what a quantifier chooses, and a set is script. \noindent $t \ominus k$ denotes cyclic translation: index $t-k$ taken modulo the size of the dimension (\texttt{roll}). Plain $t-k$ (\texttt{shift}) has no wraparound --- terms translated past the edge are simply absent. @@ -136,6 +142,7 @@ \text{run\_start} && \mathit{slack}_{t} & \le \mathrm{load}_{t,b} && \forall\, t \in \mathcal{T},\ b \in \mathcal{B} \,:\, \mathrm{load}_{t,b} \text{ is defined} \wedge \neg \left( \mathrm{load}_{t - 1,b} \text{ is defined} \right) \\ \text{zoned} && \theta_{b} & \le \mathrm{budget} && \forall\, b \in \mathcal{B} \,:\, \mathrm{zone\_cap}_{\mathrm{zone\_of}(b)} \text{ is defined} \\ \text{capped} && p_{t,g} & \le \mathrm{p}^{\mathrm{max}}_{g} && \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{spend}^{\mathrm{cap}}_{g} > 0 \vee \neg \mathrm{is\_flexible}_{g} \\ +\text{masked} && p_{t,g} & \le \mathrm{p}^{\mathrm{max}}_{g} && \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{priced\_dispatch}_{g} \wedge \mathrm{pos}(t) > 0 \\ \text{fuel\_curve} && \left( p_{t,g},\ \mathit{fuel}_{t,g},\ \mathit{heat}_{t,g} \right) & \in \mathit{on}_{t,g} \cdot \mathrm{pwl}_{a \in \mathcal{A}}(\mathrm{bp\_x}_{g,a},\ \mathrm{bp\_y}_{g,a},\ \mathrm{bp\_heat}_{g,a}) && \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \\ \text{cost\_curve} && \mathit{op\_cost}_{t,g} & \ge \begin{cases} \mathit{warm}_{t,g} & \text{if } \mathrm{is\_flexible}_{g} \\ 1 & \text{otherwise} \end{cases} \cdot \mathrm{pwl}_{a \in \mathcal{A} \,:\, \mathrm{bp\_run}_{g,a}}(\mathrm{bp\_x}_{g,a},\ \mathrm{bp\_y}_{g,a})(p_{t,g}) && \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \\ \text{hull\_curve} && \left( p_{t,g},\ \mathit{fuel}_{t,g} \right) & \in \mathrm{conv}_{a \in \mathcal{A}}(\mathrm{bp\_x}_{g,a},\ \mathrm{bp\_heat}_{g,a}) && \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \\ @@ -154,6 +161,12 @@ \text{startup\_cost} && \mathrm{startup\_cost}_{t,g} & = \begin{cases} \mathrm{cost}_{g} & \text{if } \mathrm{pos}(t) = 0 \\ \mathrm{cost}_{g} \cdot 2 & \text{if } \mathrm{pos}(t) > 0 \wedge \mathrm{season\_of}(t) = \text{'}\mathrm{winter}\text{'} \\ 0 & \text{otherwise} \end{cases} && \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \end{align} +\paragraph{Masks} +\begin{align} +\text{dispatchable} && \mathrm{dispatchable}_{g} & \iff \mathrm{p}^{\mathrm{max}}_{g} > 0 \wedge \neg \mathrm{is\_flexible}_{g} && \forall\, g \in \mathcal{G} \\ +\text{priced\_dispatch} && \mathrm{priced\_dispatch}_{g} & \iff \mathrm{dispatchable}_{g} \wedge \mathrm{spend}^{\mathrm{cap}}_{g} > 0 && \forall\, g \in \mathcal{G} +\end{align} + \paragraph{Variable domains} \begin{align} \text{p} && \mathrm{p}^{\mathrm{min}}_{g} \le p_{t,g} & \le \mathrm{p}^{\mathrm{max}}_{g} && \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{p}^{\mathrm{max}}_{g} > 0 \wedge \neg \mathrm{is\_flexible}_{g} \vee \mathrm{p}^{\mathrm{min}}_{g} > 0 \\ diff --git a/tests/typesetting/golden/markdown.out b/tests/typesetting/golden/markdown.out index 879bbe67..1be024bb 100644 --- a/tests/typesetting/golden/markdown.out +++ b/tests/typesetting/golden/markdown.out @@ -66,6 +66,13 @@ every character a notation escapes, set as text: link\_to, 100% & \#1 costs \$5 | $`\mathit{marginal\_price}`$ | `marginal_price` over $`\mathcal{T} \times \mathcal{B}`$ | | $`\mathrm{startup\_cost}`$ | `startup_cost` over $`\mathcal{T} \times \mathcal{G}`$ — what starting a unit in this snapshot costs, which the horizon's edge changes | +#### Masks + +| Symbol | Meaning | +|---|---| +| $`\mathrm{dispatchable}`$ | `dispatchable` over $`\mathcal{G}`$ — a unit that dispatches and cannot be turned down | +| $`\mathrm{priced\_dispatch}`$ | `priced_dispatch` over $`\mathcal{G}`$ | + Upright is what the data supplies — a parameter such as $`\mathrm{p}^{\mathrm{max}}`$, a coordinate map, a label — and italic is what the solver chooses, such as $`p`$. An index is italic too, being what a quantifier chooses, and a set is script. $`t \ominus k`$ denotes cyclic translation: index $`t-k`$ taken modulo the size of the dimension (`roll`). Plain $`t-k`$ (`shift`) has no wraparound — terms translated past the edge are simply absent. @@ -376,6 +383,12 @@ p_{t,g} \le \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g p_{t,g} \le \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{spend}^{\mathrm{cap}}_{g} > 0 \vee \neg \mathrm{is\_flexible}_{g} ``` +**`masked`** + +```math +p_{t,g} \le \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{priced\_dispatch}_{g} \wedge \mathrm{pos}(t) > 0 +``` + **`fuel_curve`** ```math @@ -450,6 +463,20 @@ p_{t,g} \le \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \mathrm{startup\_cost}_{t,g} = \begin{cases} \mathrm{cost}_{g} & \text{if } \mathrm{pos}(t) = 0 \\ \mathrm{cost}_{g} \cdot 2 & \text{if } \mathrm{pos}(t) > 0 \wedge \mathrm{season\_of}(t) = \text{'}\mathrm{winter}\text{'} \\ 0 & \text{otherwise} \end{cases} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} ``` +#### Masks + +**`dispatchable`** + +```math +\mathrm{dispatchable}_{g} \iff \mathrm{p}^{\mathrm{max}}_{g} > 0 \wedge \neg \mathrm{is\_flexible}_{g} \qquad \forall\, g \in \mathcal{G} +``` + +**`priced_dispatch`** + +```math +\mathrm{priced\_dispatch}_{g} \iff \mathrm{dispatchable}_{g} \wedge \mathrm{spend}^{\mathrm{cap}}_{g} > 0 \qquad \forall\, g \in \mathcal{G} +``` + #### Variable domains **`p`** diff --git a/tests/typesetting/golden/model.yaml b/tests/typesetting/golden/model.yaml index 259a4b74..81cef789 100644 --- a/tests/typesetting/golden/model.yaml +++ b/tests/typesetting/golden/model.yaml @@ -159,6 +159,12 @@ expressions: winter: { when: "position(snapshot) > 0 and season_of == 'winter'", expression: cost * 2 } otherwise: 0 +masks: + dispatchable: # a named predicate with its words: a use prints the symbol over the dims its predicate reads, the predicate prints once + where: "p_max > 0 AND NOT is_flexible" + description: a unit that dispatches and cannot be turned down + priced_dispatch: "dispatchable AND spend_cap > 0" # the one-line form, reading another mask and a data-only entry + constraints: budgeted: # names the plain expression: its symbol prints here, its definition once below dims: [snapshot] @@ -327,6 +333,10 @@ constraints: dims: [snapshot, generator] where: "spend_cap > 0 OR NOT is_flexible" expression: p <= p_max + masked: # a mask as the where, beside a condition of the constraint's own + dims: [snapshot, generator] + where: "priced_dispatch AND position(snapshot) > 0" + expression: p <= p_max assumptions: # what the data has to satisfy for the answer to mean anything bounds_do_not_cross: "p_min <= p_max" # two parameters, which is arithmetic like any other diff --git a/tests/typesetting/golden/typst.out b/tests/typesetting/golden/typst.out index 8f245ca9..261e376e 100644 --- a/tests/typesetting/golden/typst.out +++ b/tests/typesetting/golden/typst.out @@ -55,6 +55,10 @@ every character a notation escapes, set as text: link\_to, 100% & \#1 costs \$5 / $italic("marginal_price")$: `marginal_price` over $cal(T) times cal(B)$ / $upright("startup_cost")$: `startup_cost` over $cal(T) times cal(G)$ --- what starting a unit in this snapshot costs, which the horizon's edge changes +== Masks +/ $upright("dispatchable")$: `dispatchable` over $cal(G)$ --- a unit that dispatches and cannot be turned down +/ $upright("priced_dispatch")$: `priced_dispatch` over $cal(G)$ + Upright is what the data supplies --- a parameter such as $upright("p")^(upright("max"))$, a coordinate map, a label --- and italic is what the solver chooses, such as $p$. An index is italic too, being what a quantifier chooses, and a set is script. $t minus.o k$ denotes cyclic translation: index $t-k$ taken modulo the size of the dimension (`roll`). Plain $t-k$ (`shift`) has no wraparound --- terms translated past the edge are simply absent. @@ -123,6 +127,7 @@ $ upright("budgeted") & italic("spend")_(t) & <= upright("budget") & forall t in upright("run_start") & italic("slack")_(t) & <= upright("load")_(t,b) & forall t in cal(T), b in cal(B) colon upright("load")_(t,b) upright(" is defined") and not (upright("load")_(t - 1,b) upright(" is defined")) \ upright("zoned") & theta_(b) & <= upright("budget") & forall b in cal(B) colon upright("zone_cap")_(upright("zone_of")(b)) upright(" is defined") \ upright("capped") & p_(t,g) & <= upright("p")^(upright("max"))_(g) & forall t in cal(T), g in cal(G) colon upright("spend")^(upright("cap"))_(g) > 0 or not upright("is_flexible")_(g) \ + upright("masked") & p_(t,g) & <= upright("p")^(upright("max"))_(g) & forall t in cal(T), g in cal(G) colon upright("priced_dispatch")_(g) and upright("pos")(t) > 0 \ upright("fuel_curve") & (p_(t,g), italic("fuel")_(t,g), italic("heat")_(t,g)) & in italic("on")_(t,g) dot upright("pwl")_(a in cal(A))(upright("bp_x")_(g,a), upright("bp_y")_(g,a), upright("bp_heat")_(g,a)) & forall t in cal(T), g in cal(G) \ upright("cost_curve") & italic("op_cost")_(t,g) & >= cases(italic("warm")_(t,g) & upright("if ") upright("is_flexible")_(g), 1 & upright("otherwise")) dot upright("pwl")_(a in cal(A) colon upright("bp_run")_(g,a))(upright("bp_x")_(g,a), upright("bp_y")_(g,a))(p_(t,g)) & forall t in cal(T), g in cal(G) \ upright("hull_curve") & (p_(t,g), italic("fuel")_(t,g)) & in upright("conv")_(a in cal(A))(upright("bp_x")_(g,a), upright("bp_heat")_(g,a)) & forall t in cal(T), g in cal(G) \ @@ -139,6 +144,11 @@ $ upright("spend_cap") & upright("spend")^(upright("cap"))_(g) & = upright("cost upright("marginal_price") & italic("marginal_price")_(t,b) & = lambda_(upright("balance"),t,b) & forall t in cal(T), b in cal(B) \ upright("startup_cost") & upright("startup_cost")_(t,g) & = cases(upright("cost")_(g) & upright("if ") upright("pos")(t) = 0, upright("cost")_(g) dot 2 & upright("if ") upright("pos")(t) > 0 and upright("season_of")(t) = upright("'winter'"), 0 & upright("otherwise")) & forall t in cal(T), g in cal(G) $ +== Masks +#set math.equation(numbering: "(1)") +$ upright("dispatchable") & upright("dispatchable")_(g) & arrow.l.r.double.long upright("p")^(upright("max"))_(g) > 0 and not upright("is_flexible")_(g) & forall g in cal(G) \ + upright("priced_dispatch") & upright("priced_dispatch")_(g) & arrow.l.r.double.long upright("dispatchable")_(g) and upright("spend")^(upright("cap"))_(g) > 0 & forall g in cal(G) $ + == Variable domains #set math.equation(numbering: "(1)") $ upright("p") & upright("p")^(upright("min"))_(g) <= p_(t,g) & <= upright("p")^(upright("max"))_(g) & forall t in cal(T), g in cal(G) colon upright("p")^(upright("max"))_(g) > 0 and not upright("is_flexible")_(g) or upright("p")^(upright("min"))_(g) > 0 \ diff --git a/tests/typesetting/test_declaration.py b/tests/typesetting/test_declaration.py index 005eecce..3213c376 100644 --- a/tests/typesetting/test_declaration.py +++ b/tests/typesetting/test_declaration.py @@ -161,7 +161,7 @@ def test_a_substituted_term_that_opens_with_a_minus_prints_as_a_subtraction(): pytest.param( PLAIN, 'spent', - r"'spent' is not a named expression, constraint, assumption, curve or variable.*spend", + r"'spent' is not a named expression, mask, constraint, assumption, curve or variable.*spend", id='a-near-miss', ), pytest.param(PLAIN, 'objective', r"'objective' is not a named expression", id='the-objective-has-no-name'), diff --git a/tools/gallery.py b/tools/gallery.py index 538eca3a..131755f6 100644 --- a/tools/gallery.py +++ b/tools/gallery.py @@ -113,6 +113,8 @@ def symbols_for(model: Spec, table_path: Path = LIBRARY_SYMBOLS) -> dict[str, An *given.variables, *given.expressions, *given.constraints, + *model.masks, + *given.masks, } return { 'notation': table['notation'], diff --git a/tools/notation.py b/tools/notation.py index a4411206..2d15d25e 100644 --- a/tools/notation.py +++ b/tools/notation.py @@ -51,7 +51,7 @@ #: and ``parameters`` are absent on purpose: they declare no equation, and what #: they print is the legend, which the page shows once rather than a row at a #: time. -BLOCKS = ('objective', 'constraints', 'expressions', 'variables', 'piecewise', 'sos', 'assumptions') +BLOCKS = ('objective', 'constraints', 'expressions', 'masks', 'variables', 'piecewise', 'sos', 'assumptions') #: The page's sections in the order of the language reference, and in each the #: fixture's declarations under the construct they show. The declaration name @@ -114,6 +114,11 @@ 'lcoe': 'Reported expression', 'marginal_price': 'Dual of a constraint', }, + 'Named masks': { + 'dispatchable': 'Named mask', + 'priced_dispatch': 'Mask that reads a mask', + 'masked': 'Mask in a condition', + }, 'Shifts': { 'ramp': 'Cyclic and acyclic shift', 'edges': 'Filled and forward shifts', diff --git a/tools/pypsa_split.py b/tools/pypsa_split.py index 17639c0f..e6b5b58f 100644 --- a/tools/pypsa_split.py +++ b/tools/pypsa_split.py @@ -18,7 +18,9 @@ with `adds_to:`, and the fragment reads that hub under `given:`. One fragment reads each hub without adding to it, with its description, so the terms always have a reader, and a new component is one new fragment. The reader of -`total_cost` also sets the objective. +`total_cost` also sets the objective. A mask goes to the topic its name +names, as any declaration does, and a fragment that reads another topic's +mask reads it under `given: masks:`. `merge` then writes each hub as the file does, so `check` is one comparison: the merged fragments and the one file have one canonical form. @@ -46,7 +48,7 @@ from collections.abc import Mapping SOURCE = Path('examples/pypsa.yaml') -SECTIONS = ('dimensions', 'relations', 'parameters', 'variables', 'expressions', 'constraints', 'assumptions') +SECTIONS = ('dimensions', 'relations', 'parameters', 'variables', 'expressions', 'masks', 'constraints', 'assumptions') FRAME = ('dimensions', 'relations') IDENT = re.compile(r'[A-Za-z_][A-Za-z0-9_]*') HEADER = '# SPDX-FileCopyrightText: mathspec Contributors\n#\n# SPDX-License-Identifier: MIT\n' @@ -221,8 +223,12 @@ def keys(self) -> list[Key]: return list(self.blocks) def frames(self) -> dict[str, tuple[str, ...]]: - """The frame of every named expression, read off the one file.""" - return {name: e.dims for name, e in to_spec(self.data).program.expressions.items()} + """The frame of every named expression and mask, read off the one file.""" + program = to_spec(self.data).program + return { + **{name: e.dims for name, e in program.expressions.items()}, + **{name: m.dims for name, m in program.masks.items()}, + } def home(self, name: str) -> str: """The fragment that reads the hub *name* without adding to it, and carries its description.""" @@ -255,7 +261,7 @@ def fragments(model: Model) -> dict[str, str]: given = {model.key(n) for n in read if model.key(n)[0] not in FRAME and model.key(n) not in mine} stated = { **{n: model.data[s][n]['dims'] for s, n in given if s in ('parameters', 'variables')}, - **{n: list(frames[n]) for s, n in given if s == 'expressions'}, + **{n: list(frames[n]) for s, n in given if s in ('expressions', 'masks')}, } frame_reads = read | set().union(*(model.names_in(dims) for dims in stated.values())) frame = {model.key(n) for n in frame_reads if model.key(n)[0] in FRAME} @@ -319,7 +325,7 @@ def _fragment( #: The kinds a fragment reads under `given:`, and the fields of the source #: declaration each restates beside the frame. -GIVEN_KINDS = {'parameters': ('dtype',), 'variables': ('domain',), 'expressions': ()} +GIVEN_KINDS = {'parameters': ('dtype',), 'variables': ('domain',), 'expressions': (), 'masks': ()} def _term_block(block: str, hub: str) -> str: