Process commitment#
This file states the commitment rows of PyPSA's Process. It is one of the 25 whole specs of one topic each (fragments) that merge composes into the spec of examples/pypsa.yaml. It adds a term to the sum scenario_opex. Process_status_carried_over is the state at the start of each snapshot, from the state the unit brought into the horizon at the first. Process_com_ext_p_upper_big_m bounds an extendable committed unit by a large constant (big-M).
given:
parameters:
snapshot_weightings_objective: { dims: [snapshot] }
Process_p_nom: { dims: [scenario, process] }
Process_p_nom_extendable: { dims: [process], dtype: bool }
Process_p_min_pu: { dims: [scenario, snapshot, process] }
Process_p_max_pu: { dims: [scenario, snapshot, process] }
Process_p_nom_mod: { dims: [process] }
Process_modules_installed: { dims: [scenario, process] }
Process_p_min_pu_nonneg: { dims: [process], dtype: bool }
Process_maintenance_pu: { dims: [scenario, process] }
period_weight_objective: { dims: [period] }
Process_active: { dims: [snapshot, process], dtype: bool }
variables:
Process_p: { dims: [scenario, snapshot, process] }
Process_n_mod: { dims: [process], domain: integer }
Process_maintenance_capacity: { dims: [scenario, snapshot, process] }
Process_maintenance_status: { dims: [scenario, snapshot, process] }
Process_p_nom_ext: { dims: [process] }
expressions:
Process_modules: { dims: [process, scenario] }
scenario_opex: { dims: [scenario] }
conditions:
Process_committed: { dims: [process, snapshot] }
dimensions:
scenario:
description: the futures dispatch is chosen in, each with a weight
snapshot:
description: dispatch periods
dtype: datetime
ordered: true
process:
description: generalised multi-port converters, each with an internal power that every port draws or delivers at its own rate
period:
description: investment periods. It is PyPSA's `investment_periods`
dtype: int
ordered: true
maps:
snapshot:
period:
description: the investment period a snapshot falls in
refers_to: period
parameters:
Process_committable:
description: whether internal power is gated by an on/off status decision
dims: [process]
dtype: bool
Process_min_up_time:
description: least snapshots a process stays on once started
dims: [scenario, process]
dtype: int
Process_min_down_time:
description: least snapshots a process stays off once stopped
dims: [scenario, process]
dtype: int
Process_status_initial:
description: >-
one where the process was on before the first snapshot, zero where off.
It is PyPSA's `up_time_before > 0` for a committable process, and one for
a process that is not, whose ramp rows PyPSA starts from on
(`constraints.py:323`), data prep. Under a rolling horizon, it is the
status the previous window solved, a fraction where unit commitment is
linearised
dims: [scenario, process]
Process_up_time_brought_in:
description: >-
one while the up time a process brought into the horizon is still
running, zero after. It is PyPSA's start `up_time_before` snapshots
before the first, counted while `min_up_time` has not passed since
(`constraints.py:302-323`, `:677-678`), data prep. Under a rolling
horizon with linearised unit commitment, it is the fraction of the
start the previous window solved
dims: [scenario, snapshot, process]
Process_down_time_brought_in:
description: >-
one while the down time a process brought into the horizon is still
running, zero after. It is PyPSA's stop `down_time_before` snapshots
before the first, counted while `min_down_time` has not passed since
(`constraints.py:302-323`, `:677-678`), data prep. Under a rolling
horizon with linearised unit commitment, it is the fraction of the
stop the previous window solved
dims: [scenario, snapshot, process]
Process_start_up_cost:
description: cost of one start in this snapshot
dims: [scenario, snapshot, process]
Process_shut_down_cost:
description: cost of one stop in this snapshot
dims: [scenario, snapshot, process]
Process_stand_by_cost:
description: cost of one snapshot spent on
dims: [scenario, snapshot, process]
Process_big_m:
description: >-
the bound a committed extendable process's big-M rows release it by,
data prep. It is the build cap `p_nom_max` times the highest `p_max_pu`,
where the cap is finite and positive. Elsewhere it is
`committable_big_m` times the highest `p_max_pu`
(`components.py:1082-1152`). Without that keyword, it is ten times the
largest of the peak total load and the component's largest finite
`p_nom` and `p_nom_max`. Where there is none of them, it is 1e6. Below
the internal power a solve wants, it caps that internal power
dims: [scenario, process]
missing: absent
variables:
Process_status:
description: >-
`Process-status` — how much of a committable process is on. It is an
integer the rows below cap at one, or at the module count where the
build is modular
dims: [scenario, snapshot, process]
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_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_committed
domain: integer
bounds:
lower: 0
expressions:
Process_previous_status:
description: >-
the commitment state a process carries into a snapshot. It is off at the
first snapshot it is active in past the first of the horizon. PyPSA reads a
status it did not build as off there (`constraints.py:391`). It is the
state carried over otherwise
dims: [scenario, snapshot, process]
cases:
opening_late: { when: "position(snapshot) > 0 AND NOT shift(Process_active, along=snapshot, offset=1)", expression: 0 }
otherwise: Process_status_carried_over
Process_status_carried_over:
description: >-
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
dims: [scenario, snapshot, process]
cases:
opening: { when: "position(snapshot) == 0", expression: Process_status_initial }
otherwise: shift(Process_status, along=snapshot, offset=1)
Process_commitment_opex:
expression: >-
sum(sum(((Process_status * Process_stand_by_cost) * snapshot_weightings_objective) * at(period_weight_objective, by=snapshot[period]), over=process), over=snapshot)
+ sum(sum((Process_start_up * Process_start_up_cost) * at(period_weight_objective, by=snapshot[period]), over=process), over=snapshot)
+ sum(sum((Process_shut_down * Process_shut_down_cost) * at(period_weight_objective, by=snapshot[period]), over=process), over=snapshot)
adds_to: scenario_opex
conditions:
Process_com_ext:
description: >-
a committable process with an extendable, non-modular build that is active
in the snapshot's period. These are PyPSA's `com-ext` rows, whose status
is relaxed against the chosen build
condition: >-
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, it runs at least nothing"
dims: [scenario, snapshot, process]
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, it runs at most nothing"
dims: [scenario, snapshot, process]
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_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_committed
expression: Process_shut_down >= Process_previous_status - Process_status
Process_com_up_time:
description: >-
`Process-com-up-time` — a process started within its own minimum up time is
still on. The up time it brought into the horizon counts as a start
before the first snapshot
dims: [scenario, snapshot, process]
where: Process_committed AND Process_min_up_time > 0
expression: >-
sum_back(Process_start_up, along=snapshot, window=Process_min_up_time)
+ Process_up_time_brought_in <= Process_status
Process_com_down_time:
description: >-
`Process-com-down-time` — a process stopped within its own minimum down time
is still off, and so are the modules of a modular process that stopped.
The down time it brought into the horizon counts as one stop before the
first snapshot, also where the process is modular and all its modules
stopped (`constraints.py:656-683`, PyPSA/PyPSA#2114)
dims: [scenario, snapshot, process]
where: Process_committed AND Process_min_down_time > 0
expression: >-
sum_back(Process_shut_down, along=snapshot, window=Process_min_down_time)
+ Process_down_time_brought_in <= Process_modules - Process_status
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_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_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_com_ext
expression: >-
Process_p >=
Process_p_min_pu * (Process_p_nom_ext - Process_maintenance_pu * Process_maintenance_capacity)
+ Process_big_m * Process_status - Process_big_m
Process_com_ext_p_lower_nonneg:
description: >-
`Process-com-ext-p-lower-nonneg` — where no minimum-per-unit is
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_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_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_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: >-
`Process-status-p-fixed-upper` — a status is at most the modules in
place, an explicit row as PyPSA writes it. The bound is 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_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: >-
`Process-start_up-p-fixed-upper` — a start is at most the modules in
place, an explicit row as PyPSA writes it. The bound is 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_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: >-
`Process-shut_down-p-fixed-upper` — a stop is at most the modules in
place, an explicit row as PyPSA writes it. The bound is 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_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_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_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_committed AND Process_p_nom_extendable AND Process_p_nom_mod > 0
expression: Process_shut_down <= Process_n_mod
assumptions:
Process_big_m_where_committed_and_extendable:
holds: "Process_big_m"
where: "Process_com_ext"
description: >-
a committable process with an extendable, non-modular build has
the big-M its rows release it by — PyPSA computes one for each
(`get_committable_big_m_values`)
Given#
| Symbol | Meaning |
|---|---|
| \(\mathrm{on}^{z}\) | Process_active over \(\mathcal{J} \times \mathcal{T}\), data another file declares |
| \(\gamma^{z}\) | Process_maintenance_pu over \(\mathcal{J} \times \Xi\), data another file declares |
| \(\mathrm{N}^{z,\mathrm{fix}}\) | Process_modules_installed over \(\mathcal{J} \times \Xi\), data another file declares |
| \(\overline{\mathrm{z}}\) | Process_p_max_pu over \(\mathcal{J} \times \Xi \times \mathcal{T}\), data another file declares |
| \(\underline{\mathrm{z}}\) | Process_p_min_pu over \(\mathcal{J} \times \Xi \times \mathcal{T}\), data another file declares |
| \(\mathrm{nonneg}^{z}\) | Process_p_min_pu_nonneg over \(\mathcal{J}\), data another file declares |
| \(\mathrm{z}^{\mathrm{nom}}\) | Process_p_nom over \(\mathcal{J} \times \Xi\), data another file declares |
| \(\mathrm{ext}^{z}\) | Process_p_nom_extendable over \(\mathcal{J}\), data another file declares |
| \(\mathrm{z}^{\mathrm{mod}}\) | Process_p_nom_mod over \(\mathcal{J}\), data another file declares |
| \(\mathrm{w}^{y}\) | period_weight_objective over \(\mathcal{Y}\), data another file declares |
| \(\mathrm{w}\) | snapshot_weightings_objective over \(\mathcal{T}\), data another file declares |
| \(\mu^{z,\mathrm{nom}}\) | Process_maintenance_capacity over \(\mathcal{J} \times \Xi \times \mathcal{T}\) |
| \(\mu^{z,u}\) | Process_maintenance_status over \(\mathcal{J} \times \Xi \times \mathcal{T}\) |
| \(N^{z}\) | Process_n_mod over \(\mathcal{J}\) |
| \(z\) | Process_p over \(\mathcal{J} \times \Xi \times \mathcal{T}\) |
| \(Z\) | Process_p_nom_ext over \(\mathcal{J}\) |
| \(\mathrm{M}^{z}\) | Process_modules over \(\mathcal{J} \times \Xi\), an expression another file defines |
| \(\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{J} \times \mathcal{T}\), a condition another file defines |
Sets#
| Symbol | Meaning |
|---|---|
| \(\mathcal{Y}\) | index \(y\) — period with \(\mathrm{period}: \mathcal{T} \to \mathcal{Y}\) — investment periods. It is PyPSA's investment_periods |
| \(\mathcal{J}\) | index \(j\) — process — generalised multi-port converters, each with an internal power that every port draws or delivers at its own rate |
| \(\Xi\) | index \(\xi\) — scenario — the futures dispatch is chosen in, each with a weight |
| \(\mathcal{T}\) | index \(t\) — snapshot with \(\mathrm{period}: \mathcal{T} \to \mathcal{Y}\) — dispatch periods |
Parameters#
| Symbol | Meaning |
|---|---|
| \(\mathrm{M}^{z}\) | Process_big_m over \(\mathcal{J} \times \Xi\), absent where the data has no row — the bound a committed extendable process's big-M rows release it by, data prep. It is the build cap p_nom_max times the highest p_max_pu, where the cap is finite and positive. Elsewhere it is committable_big_m times the highest p_max_pu (components.py:1082-1152). Without that keyword, it is ten times the largest of the peak total load and the component's largest finite p_nom and p_nom_max. Where there is none of them, it is 1e6. Below the internal power a solve wants, it caps that internal power |
| \(\mathrm{com}^{z}\) | Process_committable over \(\mathcal{J}\) — whether internal power is gated by an on/off status decision |
| \(\mathrm{rest}^{z}\) | Process_down_time_brought_in over \(\mathcal{J} \times \Xi \times \mathcal{T}\) — one while the down time a process brought into the horizon is still running, zero after. It is PyPSA's stop down_time_before snapshots before the first, counted while min_down_time has not passed since (constraints.py:302-323, :677-678), data prep. Under a rolling horizon with linearised unit commitment, it is the fraction of the stop the previous window solved |
| \(\mathrm{DT}^{z}\) | Process_min_down_time over \(\mathcal{J} \times \Xi\) — least snapshots a process stays off once stopped |
| \(\mathrm{UT}^{z}\) | Process_min_up_time over \(\mathcal{J} \times \Xi\) — least snapshots a process stays on once started |
| \(\mathrm{c}^{z,\mathrm{dn}}\) | Process_shut_down_cost over \(\mathcal{J} \times \Xi \times \mathcal{T}\) — cost of one stop in this snapshot |
| \(\mathrm{c}^{z,\mathrm{on}}\) | Process_stand_by_cost over \(\mathcal{J} \times \Xi \times \mathcal{T}\) — cost of one snapshot spent on |
| \(\mathrm{c}^{z,\mathrm{up}}\) | Process_start_up_cost over \(\mathcal{J} \times \Xi \times \mathcal{T}\) — cost of one start in this snapshot |
| \(\mathrm{u}^{z,0}\) | Process_status_initial over \(\mathcal{J} \times \Xi\) — one where the process was on before the first snapshot, zero where off. It is PyPSA's up_time_before > 0 for a committable process, and one for a process that is not, whose ramp rows PyPSA starts from on (constraints.py:323), data prep. Under a rolling horizon, it is the status the previous window solved, a fraction where unit commitment is linearised |
| \(\mathrm{hold}^{z}\) | Process_up_time_brought_in over \(\mathcal{J} \times \Xi \times \mathcal{T}\) — one while the up time a process brought into the horizon is still running, zero after. It is PyPSA's start up_time_before snapshots before the first, counted while min_up_time has not passed since (constraints.py:302-323, :677-678), data prep. Under a rolling horizon with linearised unit commitment, it is the fraction of the start the previous window solved |
Conditions#
| Symbol | Meaning |
|---|---|
| \(\mathrm{on}^{z,\mathrm{com,ext}}\) | Process_com_ext over \(\mathcal{J} \times \mathcal{T}\) — a committable process with an extendable, non-modular build that is active in the snapshot's period. These are PyPSA's com-ext rows, whose status is relaxed against the chosen build |
Variables#
| Symbol | Meaning |
|---|---|
| \(\mathit{dn}^{z}\) | Process_shut_down over \(\mathcal{J} \times \Xi \times \mathcal{T}\) — Process-shut_down — how much of a committable process turns off this snapshot, capped as the status is |
| \(\mathit{up}^{z}\) | Process_start_up over \(\mathcal{J} \times \Xi \times \mathcal{T}\) — Process-start_up — how much of a committable process turns on this snapshot, capped as the status is |
| \(u^{z}\) | Process_status over \(\mathcal{J} \times \Xi \times \mathcal{T}\) — Process-status — how much of a committable process is on. It is an integer the rows below cap at one, or at the module count where the build is modular |
Definitions#
| Symbol | Meaning |
|---|---|
| \(\mathit{Process\_commitment\_opex}\) | Process_commitment_opex over \(\Xi\) |
| \(\overleftarrow{u}^{z}\) | Process_previous_status over \(\mathcal{J} \times \Xi \times \mathcal{T}\) — the commitment state a process carries into a snapshot. It is off at the first snapshot it is active in past the first of the horizon. PyPSA reads a status it did not build as off there (constraints.py:391). It is the state carried over otherwise |
| \(\overleftarrow{u}^{\circ z}\) | Process_status_carried_over over \(\mathcal{J} \times \Xi \times \mathcal{T}\) — 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 |
Notation#
| Symbol | Meaning |
|---|---|
| \(\mathrm{pos}(t)\) | position() — the position of \(t\), counted from \(0\); \(t\) is the label |
Conditions#
Process_com_ext
Variable domains#
Process_shut_down
Process_start_up
Process_status
Definitions#
Process_commitment_opex
Process_previous_status
Process_status_carried_over
Subject to#
Process_com_down_time
Process_com_ext_p_lower
Process_com_ext_p_lower_nonneg
Process_com_ext_p_upper_big_m
Process_com_ext_p_upper_cap
Process_com_mod_p_lower
Process_com_mod_p_upper
Process_com_p_lower
Process_com_p_upper
Process_com_transition_shut_down
Process_com_transition_start_up
Process_com_up_time
Process_shut_down_p_fixed_upper
Process_shut_down_p_nom_variable_upper
Process_start_up_p_fixed_upper
Process_start_up_p_nom_variable_upper
Process_status_p_fixed_upper
Process_status_p_nom_variable_upper
Assumptions#
Process_big_m_where_committed_and_extendable
See also
- PyPSA in 25 files, for how the fragments compose
- PyPSA in one file, for the same spec as one file, rung by rung