Skip to content

Generator commitment#

This file states the commitment rows of PyPSA's Generator. 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. Generator_status_carried_over is the state at the start of each snapshot, from the state the unit brought into the horizon at the first. Generator_com_ext_p_upper_big_m bounds an extendable committed unit by a large constant (big-M).

given:
  parameters:
    snapshot_weightings_objective: { dims: [snapshot] }
    Generator_p_nom: { dims: [scenario, generator] }
    Generator_p_nom_extendable: { dims: [generator], dtype: bool }
    Generator_p_min_pu: { dims: [scenario, snapshot, generator] }
    Generator_p_max_pu: { dims: [scenario, snapshot, generator] }
    Generator_p_nom_mod: { dims: [generator] }
    Generator_modules_installed: { dims: [scenario, generator] }
    Generator_p_min_pu_nonneg: { dims: [generator], dtype: bool }
    Generator_maintenance_pu: { dims: [scenario, generator] }
    period_weight_objective: { dims: [period] }
    Generator_active: { dims: [snapshot, generator], dtype: bool }
  variables:
    Generator_p: { dims: [scenario, snapshot, generator] }
    Generator_marginal_cost_piecewise: { dims: [scenario, snapshot, generator] }
    Generator_n_mod: { dims: [generator], domain: integer }
    Generator_maintenance_capacity: { dims: [scenario, snapshot, generator] }
    Generator_maintenance_status: { dims: [scenario, snapshot, generator] }
    Generator_p_nom_ext: { dims: [generator] }
  expressions:
    Generator_modules: { dims: [generator, scenario] }
    scenario_opex: { dims: [scenario] }
  conditions:
    Generator_marginal_cost_curve: { dims: [generator] }
    Generator_marginal_cost_as_lp: { dims: [] }
    Generator_committed: { dims: [generator, snapshot] }

dimensions:
  scenario:
    description: the futures dispatch is chosen in, each with a weight
  snapshot:
    description: dispatch periods
    dtype: datetime
    ordered: true
  generator:
    description: generating units, each on one bus
  period:
    description: investment periods. It is PyPSA's `investment_periods`
    dtype: int
    ordered: true
  breakpoint:
    description: >-
      the corners of a piecewise-linear curve, in order. A curve runs over the
      breakpoints its data has rows for, so curves may differ in length
    dtype: int
    ordered: true

relations:
  snapshot_period:
    description: the investment period a snapshot falls in
    key: snapshot
    values: period

parameters:
  Generator_marginal_cost_curve_p:
    description: >-
      the output at each breakpoint of a generator's piecewise `marginal_cost`
      curve: PyPSA's `p_pu` breakpoints times `p_nom`, data prep. A generator
      with no curve has no rows
    dims: [generator, breakpoint]
    missing: absent
  Generator_marginal_cost_curve_cost:
    description: >-
      the running cost at each breakpoint of a generator's piecewise
      `marginal_cost` curve: zero at the first, then each segment's width in
      output times the `marginal_cost` at its upper breakpoint, summed, data
      prep. It is linopy's `Slopes(...).to_breakpoints` (`piecewise.py:310-312`)
    dims: [generator, breakpoint]
    missing: absent
  Generator_committable:
    description: whether output is gated by an on/off status decision
    dims: [generator]
    dtype: bool
  Generator_min_up_time:
    description: least snapshots a unit stays on once started
    dims: [scenario, generator]
    dtype: int
  Generator_min_down_time:
    description: least snapshots a unit stays off once stopped
    dims: [scenario, generator]
    dtype: int
  Generator_status_initial:
    description: >-
      one where the unit was on before the first snapshot, zero where off.
      It is PyPSA's `up_time_before > 0` for a committable unit, and one for
      a unit 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, generator]
  Generator_up_time_brought_in:
    description: >-
      one while the up time a unit 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, generator]
  Generator_down_time_brought_in:
    description: >-
      one while the down time a unit 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, generator]
  Generator_start_up_cost:
    description: cost of one start in this snapshot
    dims: [scenario, snapshot, generator]
  Generator_shut_down_cost:
    description: cost of one stop in this snapshot
    dims: [scenario, snapshot, generator]
  Generator_stand_by_cost:
    description: cost of one snapshot spent on
    dims: [scenario, snapshot, generator]
  Generator_big_m:
    description: >-
      the bound a committed extendable generator'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 output a solve wants, it caps that output
    dims: [scenario, generator]
    missing: absent

variables:
  Generator_status:
    description: >-
      `Generator-status` — how much of a committable unit 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, generator]
    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_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_committed
    domain: integer
    bounds:
      lower: 0

expressions:
  Generator_previous_status:
    description: >-
      the commitment state a generator 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, generator]
    cases:
      opening_late: { when: "position(snapshot) > 0 AND NOT shift(Generator_active, along=snapshot, offset=1)", expression: 0 }
    otherwise: Generator_status_carried_over
  Generator_status_carried_over:
    description: >-
      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
    dims: [scenario, snapshot, generator]
    cases:
      opening: { when: "position(snapshot) == 0", expression: Generator_status_initial }
    otherwise: shift(Generator_status, along=snapshot, offset=1)
  Generator_commitment_opex:
    expression: >-
      sum(sum(((Generator_status * Generator_stand_by_cost) * snapshot_weightings_objective) * at(period_weight_objective, by=snapshot_period[period]), over=generator), over=snapshot)
      + sum(sum((Generator_start_up * Generator_start_up_cost) * at(period_weight_objective, by=snapshot_period[period]), over=generator), over=snapshot)
      + sum(sum((Generator_shut_down * Generator_shut_down_cost) * at(period_weight_objective, by=snapshot_period[period]), over=generator), over=snapshot)
    adds_to: scenario_opex

conditions:
  Generator_com_ext:
    description: >-
      a committable generator 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: >-
      Generator_committable
      AND Generator_p_nom_extendable
      AND NOT (Generator_p_nom_mod > 0)
      AND Generator_active

piecewise:
  Generator_marginal_cost_lp:
    description: >-
      `Generator-marginal_cost_piecewise` — the running cost of a generator
      with a curve is at least the curve at its output, stated as the line of
      each segment, where linopy chooses the segment lines
    along: breakpoint
    dims: [scenario, snapshot, generator]
    where: Generator_marginal_cost_as_lp AND Generator_marginal_cost_curve AND Generator_active AND Generator_marginal_cost_curve_p
    method: lp
    links:
      p: [Generator_p, Generator_marginal_cost_curve_p]
      cost: [Generator_marginal_cost_piecewise, Generator_marginal_cost_curve_cost, ">="]
  Generator_marginal_cost_incremental:
    description: >-
      `Generator-marginal_cost_piecewise` — the running cost of a generator
      with a curve is at least the curve at its output, stated by segments
      filled in order and switched by the status of a committable generator,
      where linopy chooses the incremental rows
    along: breakpoint
    dims: [scenario, snapshot, generator]
    where: NOT Generator_marginal_cost_as_lp AND Generator_marginal_cost_curve AND Generator_active AND Generator_marginal_cost_curve_p
    method: incremental
    activity: Generator_status
    links:
      p: [Generator_p, Generator_marginal_cost_curve_p]
      cost: [Generator_marginal_cost_piecewise, Generator_marginal_cost_curve_cost, ">="]

constraints:
  Generator_com_p_lower:
    description: "`Generator-com-p-lower` — a committed unit outputs at least its minimum. Off, it outputs at least nothing"
    dims: [scenario, snapshot, generator]
    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, it outputs at most nothing"
    dims: [scenario, snapshot, generator]
    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_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_committed
    expression: Generator_shut_down >= Generator_previous_status - Generator_status
  Generator_com_up_time:
    description: >-
      `Generator-com-up-time` — a unit started within its own minimum up time is
      still on. The up time it brought into the horizon counts as a start
      before the first snapshot
    dims: [scenario, snapshot, generator]
    where: Generator_committed AND Generator_min_up_time > 0
    expression: >-
      sum_back(Generator_start_up, along=snapshot, window=Generator_min_up_time)
      + Generator_up_time_brought_in <= Generator_status
  Generator_com_down_time:
    description: >-
      `Generator-com-down-time` — a unit stopped within its own minimum down time
      is still off, and so are the modules of a modular unit that stopped.
      The down time it brought into the horizon counts as one stop before the
      first snapshot, also where the unit is modular and all its modules
      stopped (`constraints.py:656-683`, PyPSA/PyPSA#2114)
    dims: [scenario, snapshot, generator]
    where: Generator_committed AND Generator_min_down_time > 0
    expression: >-
      sum_back(Generator_shut_down, along=snapshot, window=Generator_min_down_time)
      + Generator_down_time_brought_in <= Generator_modules - Generator_status
  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_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_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_com_ext
    expression: >-
      Generator_p >=
      Generator_p_min_pu * (Generator_p_nom_ext - Generator_maintenance_pu * Generator_maintenance_capacity)
      + Generator_big_m * Generator_status - Generator_big_m
  Generator_com_ext_p_lower_nonneg:
    description: >-
      `Generator-com-ext-p-lower-nonneg` — where no minimum-per-unit is
      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_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_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_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: >-
      `Generator-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, generator]
    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: >-
      `Generator-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, generator]
    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: >-
      `Generator-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, generator]
    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_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_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_committed AND Generator_p_nom_extendable AND Generator_p_nom_mod > 0
    expression: Generator_shut_down <= Generator_n_mod

assumptions:
  Generator_big_m_where_committed_and_extendable:
    holds: "Generator_big_m"
    where: "Generator_com_ext"
    description: >-
      a committable generator 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}\) Generator_active over \(\mathcal{G} \times \mathcal{T}\), data another file declares
\(\gamma\) Generator_maintenance_pu over \(\mathcal{G} \times \Xi\), data another file declares
\(\mathrm{N}^{\mathrm{fix}}\) Generator_modules_installed over \(\mathcal{G} \times \Xi\), data another file declares
\(\overline{\mathrm{p}}\) Generator_p_max_pu over \(\mathcal{G} \times \Xi \times \mathcal{T}\), data another file declares
\(\underline{\mathrm{p}}\) Generator_p_min_pu over \(\mathcal{G} \times \Xi \times \mathcal{T}\), data another file declares
\(\mathrm{nonneg}\) Generator_p_min_pu_nonneg over \(\mathcal{G}\), data another file declares
\(\mathrm{p}^{\mathrm{nom}}\) Generator_p_nom over \(\mathcal{G} \times \Xi\), data another file declares
\(\mathrm{ext}\) Generator_p_nom_extendable over \(\mathcal{G}\), data another file declares
\(\mathrm{p}^{\mathrm{mod}}\) Generator_p_nom_mod over \(\mathcal{G}\), 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^{\mathrm{nom}}\) Generator_maintenance_capacity over \(\mathcal{G} \times \Xi \times \mathcal{T}\)
\(\mu^{u}\) Generator_maintenance_status over \(\mathcal{G} \times \Xi \times \mathcal{T}\)
\(C^{\mathrm{curve}}\) Generator_marginal_cost_piecewise over \(\mathcal{G} \times \Xi \times \mathcal{T}\)
\(N\) Generator_n_mod over \(\mathcal{G}\)
\(p\) Generator_p over \(\mathcal{G} \times \Xi \times \mathcal{T}\)
\(P\) Generator_p_nom_ext over \(\mathcal{G}\)
\(\mathrm{M}\) Generator_modules over \(\mathcal{G} \times \Xi\), an expression another file defines
\(\mathit{scenario\_opex}\) scenario_opex over \(\Xi\), an expression this file adds Generator_commitment_opex to
\(\mathrm{on}^{\mathrm{com}}\) Generator_committed over \(\mathcal{G} \times \mathcal{T}\), a condition another file defines
\(\mathrm{lp}\) Generator_marginal_cost_as_lp (scalar), a condition another file defines
\(\mathrm{curve}\) Generator_marginal_cost_curve over \(\mathcal{G}\), a condition another file defines

Sets#

Symbol Meaning
\(\mathcal{B}\) index \(b\) — breakpoint — the corners of a piecewise-linear curve, in order. A curve runs over the breakpoints its data has rows for, so curves may differ in length
\(\mathcal{G}\) index \(g\) — generator — generating units, each on one bus
\(\mathcal{Y}\) index \(y\) — period with \(\mathrm{snapshot\_period}: \mathcal{T} \to \mathcal{Y}\) — investment periods. It is PyPSA's investment_periods
\(\Xi\) index \(\xi\) — scenario — the futures dispatch is chosen in, each with a weight
\(\mathcal{T}\) index \(t\) — snapshot with \(\mathrm{snapshot\_period}: \mathcal{T} \to \mathcal{Y}\) — dispatch periods

Parameters#

Symbol Meaning
\(\mathrm{M}\) Generator_big_m over \(\mathcal{G} \times \Xi\), absent where the data has no row — the bound a committed extendable generator'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 output a solve wants, it caps that output
\(\mathrm{com}\) Generator_committable over \(\mathcal{G}\) — whether output is gated by an on/off status decision
\(\mathrm{rest}\) Generator_down_time_brought_in over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — one while the down time a unit 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{C}^{\mathrm{curve}}\) Generator_marginal_cost_curve_cost over \(\mathcal{B} \times \mathcal{G}\), absent where the data has no row — the running cost at each breakpoint of a generator's piecewise marginal_cost curve: zero at the first, then each segment's width in output times the marginal_cost at its upper breakpoint, summed, data prep. It is linopy's Slopes(...).to_breakpoints (piecewise.py:310-312)
\(\mathrm{p}^{\mathrm{curve}}\) Generator_marginal_cost_curve_p over \(\mathcal{B} \times \mathcal{G}\), absent where the data has no row — the output at each breakpoint of a generator's piecewise marginal_cost curve: PyPSA's p_pu breakpoints times p_nom, data prep. A generator with no curve has no rows
\(\mathrm{DT}\) Generator_min_down_time over \(\mathcal{G} \times \Xi\) — least snapshots a unit stays off once stopped
\(\mathrm{UT}\) Generator_min_up_time over \(\mathcal{G} \times \Xi\) — least snapshots a unit stays on once started
\(\mathrm{c}^{\mathrm{dn}}\) Generator_shut_down_cost over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — cost of one stop in this snapshot
\(\mathrm{c}^{\mathrm{on}}\) Generator_stand_by_cost over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — cost of one snapshot spent on
\(\mathrm{c}^{\mathrm{up}}\) Generator_start_up_cost over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — cost of one start in this snapshot
\(\mathrm{u}^{0}\) Generator_status_initial over \(\mathcal{G} \times \Xi\) — one where the unit was on before the first snapshot, zero where off. It is PyPSA's up_time_before > 0 for a committable unit, and one for a unit 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}\) Generator_up_time_brought_in over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — one while the up time a unit 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}^{\mathrm{com,ext}}\) Generator_com_ext over \(\mathcal{G} \times \mathcal{T}\) — a committable generator 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}\) Generator_shut_down over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — Generator-shut_down — how much of a committable unit turns off this snapshot, capped as the status is
\(\mathit{up}\) Generator_start_up over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — Generator-start_up — how much of a committable unit turns on this snapshot, capped as the status is
\(u\) Generator_status over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — Generator-status — how much of a committable unit 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{Generator\_commitment\_opex}\) Generator_commitment_opex over \(\Xi\)
\(\overleftarrow{u}\) Generator_previous_status over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — the commitment state a generator 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}\) Generator_status_carried_over over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — 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

Notation#

Symbol Meaning
\(t \boxminus_{v} k\) edge=v — \(t-k\), and \(v\) past the edge
\(\mathrm{pos}(t)\) position() — the position of \(t\), counted from \(0\); \(t\) is the label

Conditions#

Generator_com_ext

\[ \mathrm{on}^{\mathrm{com,ext}}_{g,t} \iff \mathrm{com}_{g} \wedge \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \wedge \mathrm{on}_{g,t} \qquad \forall\, g \in \mathcal{G},\ t \in \mathcal{T} \]

Variable domains#

Generator_shut_down

\[ \mathit{dn}_{g,\xi,t} \ge 0, \mathit{dn}_{g,\xi,t} \in \mathbb{Z} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \]

Generator_start_up

\[ \mathit{up}_{g,\xi,t} \ge 0, \mathit{up}_{g,\xi,t} \in \mathbb{Z} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \]

Generator_status

\[ u_{g,\xi,t} \ge 0, u_{g,\xi,t} \in \mathbb{Z} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \]

Definitions#

Generator_commitment_opex

\[ \mathit{Generator\_commitment\_opex}_{\xi} = \sum_{t \in \mathcal{T}} \sum_{g \in \mathcal{G}} u_{g,\xi,t} \cdot \mathrm{c}^{\mathrm{on}}_{g,\xi,t} \cdot \mathrm{w}_{t} \cdot \mathrm{w}^{y}_{\mathrm{snapshot\_period}(t)} + \sum_{t \in \mathcal{T}} \sum_{g \in \mathcal{G}} \mathit{up}_{g,\xi,t} \cdot \mathrm{c}^{\mathrm{up}}_{g,\xi,t} \cdot \mathrm{w}^{y}_{\mathrm{snapshot\_period}(t)} + \sum_{t \in \mathcal{T}} \sum_{g \in \mathcal{G}} \mathit{dn}_{g,\xi,t} \cdot \mathrm{c}^{\mathrm{dn}}_{g,\xi,t} \cdot \mathrm{w}^{y}_{\mathrm{snapshot\_period}(t)} \qquad \forall\, \xi \in \Xi \]

Generator_previous_status

\[ \overleftarrow{u}_{g,\xi,t} = \begin{cases} 0 & \text{if } \mathrm{pos}(t) > 0 \wedge \neg \mathrm{on}_{g,t - 1} \\ \overleftarrow{u}^{\circ}_{g,\xi,t} & \text{otherwise} \end{cases} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \]

Generator_status_carried_over

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

Subject to#

Generator_com_down_time

\[ \sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{DT}} \mathit{dn}_{g,\xi,t'} + \mathrm{rest}_{g,\xi,t} \le \mathrm{M}_{g,\xi} - u_{g,\xi,t} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \wedge \mathrm{DT}_{g,\xi} > 0 \]

Generator_com_ext_p_lower

\[ p_{g,\xi,t} \ge \underline{\mathrm{p}}_{g,\xi,t} \cdot \left( P_{g} - \gamma_{g,\xi} \cdot \mu^{\mathrm{nom}}_{g,\xi,t} \right) + \mathrm{M}_{g,\xi} \cdot u_{g,\xi,t} - \mathrm{M}_{g,\xi} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{g,t} \]

Generator_com_ext_p_lower_nonneg

\[ p_{g,\xi,t} \ge 0 \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{g,t} \wedge \mathrm{nonneg}_{g} \]

Generator_com_ext_p_upper_big_m

\[ p_{g,\xi,t} \le \mathrm{M}_{g,\xi} \cdot u_{g,\xi,t} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{g,t} \]

Generator_com_ext_p_upper_cap

\[ p_{g,\xi,t} \le \overline{\mathrm{p}}_{g,\xi,t} \cdot \left( P_{g} - \gamma_{g,\xi} \cdot \mu^{\mathrm{nom}}_{g,\xi,t} \right) \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{g,t} \]

Generator_com_mod_p_lower

\[ p_{g,\xi,t} \ge \underline{\mathrm{p}}_{g,\xi,t} \cdot \mathrm{p}^{\mathrm{mod}}_{g} \cdot \left( u_{g,\xi,t} - \gamma_{g,\xi} \cdot \mu^{u}_{g,\xi,t} \right) \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \]

Generator_com_mod_p_upper

\[ p_{g,\xi,t} \le \overline{\mathrm{p}}_{g,\xi,t} \cdot \mathrm{p}^{\mathrm{mod}}_{g} \cdot \left( u_{g,\xi,t} - \gamma_{g,\xi} \cdot \mu^{u}_{g,\xi,t} \right) \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \]

Generator_com_p_lower

\[ p_{g,\xi,t} \ge \underline{\mathrm{p}}_{g,\xi,t} \cdot \mathrm{p}^{\mathrm{nom}}_{g,\xi} \cdot \left( u_{g,\xi,t} - \gamma_{g,\xi} \cdot \mu^{u}_{g,\xi,t} \right) \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \]

Generator_com_p_upper

\[ p_{g,\xi,t} \le \overline{\mathrm{p}}_{g,\xi,t} \cdot \mathrm{p}^{\mathrm{nom}}_{g,\xi} \cdot \left( u_{g,\xi,t} - \gamma_{g,\xi} \cdot \mu^{u}_{g,\xi,t} \right) \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \wedge \neg \mathrm{ext}_{g} \wedge \neg \left( \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \]

Generator_com_transition_shut_down

\[ \mathit{dn}_{g,\xi,t} \ge \overleftarrow{u}_{g,\xi,t} - u_{g,\xi,t} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \]

Generator_com_transition_start_up

\[ \mathit{up}_{g,\xi,t} \ge u_{g,\xi,t} - \overleftarrow{u}_{g,\xi,t} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \]

Generator_com_up_time

\[ \sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < \mathrm{UT}} \mathit{up}_{g,\xi,t'} + \mathrm{hold}_{g,\xi,t} \le u_{g,\xi,t} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \wedge \mathrm{UT}_{g,\xi} > 0 \]

Generator_shut_down_p_fixed_upper

\[ \mathit{dn}_{g,\xi,t} \le \mathrm{N}^{\mathrm{fix}}_{g,\xi} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \]

Generator_shut_down_p_nom_variable_upper

\[ \mathit{dn}_{g,\xi,t} \le N_{g} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \]

Generator_start_up_p_fixed_upper

\[ \mathit{up}_{g,\xi,t} \le \mathrm{N}^{\mathrm{fix}}_{g,\xi} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \]

Generator_start_up_p_nom_variable_upper

\[ \mathit{up}_{g,\xi,t} \le N_{g} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \]

Generator_status_p_fixed_upper

\[ u_{g,\xi,t} \le \mathrm{N}^{\mathrm{fix}}_{g,\xi} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \wedge \neg \left( \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \right) \]

Generator_status_p_nom_variable_upper

\[ u_{g,\xi,t} \le N_{g} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com}}_{g,t} \wedge \mathrm{ext}_{g} \wedge \mathrm{p}^{\mathrm{mod}}_{g} > 0 \]

Generator_marginal_cost_incremental

\[ C^{\mathrm{curve}}_{g,\xi,t} \ge \left( \begin{cases} u_{g,\xi,t} & \text{if } \mathrm{on}^{\mathrm{com}}_{g,t} \\ 1 & \text{otherwise} \end{cases} \cdot \left( \mathrm{p}^{\mathrm{curve}}_{\min \{ b \in \mathcal{B} \,:\, \neg \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \},g},\ \mathrm{C}^{\mathrm{curve}}_{\min \{ b \in \mathcal{B} \,:\, \neg \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \},g} \right) + \min(\begin{cases} u_{g,\xi,t} & \text{if } \mathrm{on}^{\mathrm{com}}_{g,t} \\ 1 & \text{otherwise} \end{cases},\ 1) \cdot \left( \mathrm{pwl}_{b \in \mathcal{B} \,:\, \neg \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined}}(\mathrm{p}^{\mathrm{curve}}_{b,g},\ \mathrm{C}^{\mathrm{curve}}_{b,g}) - \left( \mathrm{p}^{\mathrm{curve}}_{\min \{ b \in \mathcal{B} \,:\, \neg \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \},g},\ \mathrm{C}^{\mathrm{curve}}_{\min \{ b \in \mathcal{B} \,:\, \neg \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \},g} \right) \right) \right)(p_{g,\xi,t}) \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \]

Generator_marginal_cost_lp

\[ C^{\mathrm{curve}}_{g,\xi,t} \ge \mathrm{pwl}_{b \in \mathcal{B} \,:\, \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined}}(\mathrm{p}^{\mathrm{curve}}_{b,g},\ \mathrm{C}^{\mathrm{curve}}_{b,g})(p_{g,\xi,t}) \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \]

Assumptions#

Generator_big_m_where_committed_and_extendable

\[ \mathrm{M}_{g,\xi} \text{ is defined} \qquad \forall\, g \in \mathcal{G},\ \xi \in \Xi,\ t \in \mathcal{T} \,:\, \mathrm{on}^{\mathrm{com,ext}}_{g,t} \]

Generator_marginal_cost_incremental_complete

\[ \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \wedge \mathrm{C}^{\mathrm{curve}}_{b,g} \text{ is defined} \qquad \forall\, b \in \mathcal{B},\ g \in \mathcal{G},\ t \in \mathcal{T} \,:\, \neg \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \]

Generator_marginal_cost_incremental_contiguous

\[ \lvert \{ b \in \mathcal{B} \,:\, \neg \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \wedge \neg \left( \neg \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b - 1,g} \text{ is defined} \right) \} \rvert = 1 \qquad \forall\, g \in \mathcal{G},\ t \in \mathcal{T} \,:\, \lvert \{ b \in \mathcal{B} \,:\, \neg \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \} \rvert > 0 \]

Generator_marginal_cost_lp_complete

\[ \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \wedge \mathrm{C}^{\mathrm{curve}}_{b,g} \text{ is defined} \qquad \forall\, b \in \mathcal{B},\ g \in \mathcal{G},\ t \in \mathcal{T} \,:\, \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \]

Generator_marginal_cost_lp_increasing

\[ \mathrm{p}^{\mathrm{curve}}_{b \boxminus_{0} 1,g} < \mathrm{p}^{\mathrm{curve}}_{b,g} \qquad \forall\, b \in \mathcal{B},\ g \in \mathcal{G},\ t \in \mathcal{T} \,:\, \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \wedge \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b - 1,g} \text{ is defined} \]

Generator_marginal_cost_lp_curvature

\[ \left( \mathrm{C}^{\mathrm{curve}}_{b,g} - \mathrm{C}^{\mathrm{curve}}_{b \boxminus_{0} 1,g} \right) \cdot \left( \mathrm{p}^{\mathrm{curve}}_{b \boxplus_{0} 1,g} - \mathrm{p}^{\mathrm{curve}}_{b,g} \right) \le \left( \mathrm{C}^{\mathrm{curve}}_{b \boxplus_{0} 1,g} - \mathrm{C}^{\mathrm{curve}}_{b,g} \right) \cdot \left( \mathrm{p}^{\mathrm{curve}}_{b,g} - \mathrm{p}^{\mathrm{curve}}_{b \boxminus_{0} 1,g} \right) \qquad \forall\, b \in \mathcal{B},\ g \in \mathcal{G},\ t \in \mathcal{T} \,:\, \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \wedge \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b - 1,g} \text{ is defined} \wedge \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b + 1,g} \text{ is defined} \]

Generator_marginal_cost_lp_breakpoints

\[ \lvert \{ b \in \mathcal{B} \,:\, \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \} \rvert \ge 2 \qquad \forall\, g \in \mathcal{G},\ t \in \mathcal{T} \,:\, \lvert \{ b \in \mathcal{B} \,:\, \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \} \rvert > 0 \]

Generator_marginal_cost_lp_contiguous

\[ \lvert \{ b \in \mathcal{B} \,:\, \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \wedge \neg \left( \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b - 1,g} \text{ is defined} \right) \} \rvert = 1 \qquad \forall\, g \in \mathcal{G},\ t \in \mathcal{T} \,:\, \lvert \{ b \in \mathcal{B} \,:\, \mathrm{lp} \wedge \mathrm{curve}_{g} \wedge \mathrm{on}_{g,t} \wedge \mathrm{p}^{\mathrm{curve}}_{b,g} \text{ is defined} \} \rvert > 0 \]

See also