Generators#
This file states 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 each of these sums: primary_energy, operational_limit, tech_capacity_expansion, scenario_opex, total_cost, Carrier_additions and Bus_injection. Generator_p_nom_effective is the build the limits use: the chosen one where the generator is extendable, the given one otherwise.
given:
parameters:
snapshot_weightings_objective: { dims: [snapshot] }
Generator_marginal_cost_curve_p: { dims: [generator, breakpoint] }
Generator_marginal_cost_curve_cost: { dims: [generator, breakpoint] }
Generator_committable: { dims: [generator], dtype: bool }
Generator_maintenance_pu: { dims: [scenario, generator] }
scenario_weight: { dims: [scenario] }
CVaR_omega: { dims: [] }
period_weight_objective: { dims: [period] }
variables:
Generator_maintenance: { dims: [scenario, snapshot, generator] }
Generator_maintenance_capacity: { dims: [scenario, snapshot, generator] }
expressions:
energy_limit_weight: { dims: [snapshot] }
GlobalConstraint_energy_weight: { dims: [global_constraint, scenario, snapshot] }
primary_energy: { dims: [global_constraint, scenario] }
operational_limit: { dims: [global_constraint, scenario] }
tech_capacity_expansion: { dims: [global_constraint] }
scenario_opex: { dims: [scenario] }
total_cost: { dims: [] }
Carrier_additions: { dims: [carrier, period] }
Bus_injection: { dims: [bus, scenario, snapshot] }
dimensions:
scenario:
description: the futures dispatch is chosen in, each with a weight
snapshot:
description: dispatch periods
dtype: datetime
ordered: true
bus:
description: network nodes
generator:
description: generating units, each on one bus
global_constraint:
description: PyPSA's `GlobalConstraint` rows, one label per declared limit
period:
description: investment periods. It is PyPSA's `investment_periods`
dtype: int
ordered: true
carrier:
description: energy carriers, what a growth limit is set per
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
Generator_carrier:
description: the carrier a generator converts from
key: generator
values: carrier
Generator_bus:
description: the bus a generator sits on
key: generator
values: bus
parameters:
Generator_p_nom:
description: nominal power
dims: [scenario, generator]
Generator_p_nom_extendable:
description: whether the nominal power is a decision
dims: [generator]
dtype: bool
Generator_p_min_pu:
description: least output, per unit of nominal power
dims: [scenario, snapshot, generator]
Generator_p_max_pu:
description: "most output, per unit of nominal power: an availability profile"
dims: [scenario, snapshot, generator]
Generator_marginal_cost:
description: cost of one unit of output
dims: [scenario, snapshot, generator]
Generator_marginal_cost_quadratic:
description: cost of the square of one unit of output
dims: [scenario, snapshot, generator]
Generator_sign:
description: >-
the sign output enters its bus's balance with. It is PyPSA's `sign`, `1`
unless given, `-1` for a unit that draws power. PyPSA refuses one that
differs by scenario (`constants.py:46`)
dims: [generator]
Generator_p_nom_mod:
description: the module size a build comes in whole numbers of. No value means the build is continuous
dims: [generator]
missing: absent
Generator_modules_installed:
description: >-
how many whole modules a committable build has in place, data prep. It
is `Generator_p_nom / Generator_p_nom_mod` where a fixed build is
modular, and one where it is not. PyPSA refuses a fixed modular build
whose nominal power is not a whole number of modules
dims: [scenario, generator]
Generator_p_min_pu_nonneg:
description: >-
true where none of the generator's own minimums-per-unit is negative. It
is PyPSA's per-unit `(p_min_pu >= 0).all()` over every snapshot and
scenario, data prep
dims: [generator]
dtype: bool
Generator_active:
description: whether a generator is active in a snapshot's period. It is PyPSA's `active`, from build year and lifetime, data prep
dims: [snapshot, generator]
dtype: bool
Generator_active_asset:
description: >-
whether a generator is one of PyPSA's `active_assets`, by its `active`
flag alone, without build year and lifetime. A unit that is active in no
snapshot of the horizon still counts. PyPSA refuses one that differs by
scenario (`constants.py:54`)
dims: [generator]
dtype: bool
Generator_capital_weight:
description: the sum of period weights a generator is active in. It is PyPSA's `active * period_weighting`, summed, data prep
dims: [generator]
Generator_first_active:
description: >-
one in the first period a generator is active in, zero elsewhere, data prep.
PyPSA takes `active & (active.cumsum() == 1)` (`global_constraints.py:278`)
dims: [period, generator]
Generator_p_set:
description: a given output schedule. A generator without one has no row here
dims: [scenario, snapshot, generator]
missing: absent
Generator_p_nom_min:
description: least nominal power an extendable generator may be built at
dims: [scenario, generator]
Generator_p_nom_max:
description: most nominal power an extendable generator may be built at
dims: [scenario, generator]
Generator_capital_cost:
description: >-
cost of one unit of nominal power for the modelled horizon (PyPSA's
`periodized_cost`), data prep. It is `overnight_cost` as an annuity over
`lifetime` at `discount_rate`, times `nyears`, where `overnight_cost` is
given, and `capital_cost` where it is not. Both add `fom_cost`
(`components.py:1157-1178`, `costs.py:102-203`)
dims: [scenario, generator]
Generator_p_nom_set:
description: a given nominal power for an extendable generator. One without a value has no row here
dims: [generator]
missing: absent
Generator_e_sum_min:
description: least energy over the horizon. It is minus infinity where no floor is meant
dims: [scenario, generator]
Generator_e_sum_max:
description: "most energy over the horizon: a fuel or emission budget in energy terms. It is infinity where no cap is meant"
dims: [scenario, generator]
Generator_primary_energy_weight:
description: >-
the constrained attribute per unit of energy at the bus: the carrier's
`co2_emissions` over the generator's efficiency at the snapshot, data
prep. A generator of an unweighted carrier has no row
dims: [scenario, global_constraint, snapshot, generator]
missing: absent
Generator_operational_limit_weight:
description: one where the generator is in the row's set, data prep. One outside it has no row
dims: [scenario, global_constraint, generator]
missing: absent
Generator_tech_capacity_weight:
description: >-
one where the generator is in the row's carrier-and-bus set, data prep.
One outside it, or one that is not active in the row's
`investment_period`, has no row
dims: [global_constraint, generator]
missing: absent
variables:
Generator_p:
description: "`Generator-p` — output of a generator in a snapshot"
dims: [scenario, snapshot, generator]
where: Generator_active
Generator_marginal_cost_piecewise:
description: >-
`Generator-marginal_cost_piecewise` — the running cost of a generator
that pays along a curve, held above the curve
dims: [scenario, snapshot, generator]
where: Generator_marginal_cost_curve AND Generator_active
Generator_n_mod:
description: "`Generator-n_mod` — how many modules of an extendable modular build. A unit that is not active has none"
dims: [generator]
where: Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active_asset
domain: integer
bounds:
lower: 0
Generator_p_nom_ext:
description: >-
`Generator-p_nom` — nominal power where it is a decision, for a
generator that is active. A fixed build is the parameter of the same
PyPSA name
dims: [generator]
where: Generator_p_nom_extendable AND Generator_active_asset
expressions:
Generator_p_nom_effective:
description: "the build a generator's limits are taken against: the chosen one where it is extendable, the given one otherwise"
dims: [scenario, generator]
cases:
extendable: { when: Generator_p_nom_extendable, expression: Generator_p_nom_ext }
otherwise: Generator_p_nom
Generator_modules:
description: >-
how many modules a committed unit's status counts: the chosen number
where the build is extendable and modular, and the modules installed
otherwise (`constraints.py:656-661`)
dims: [scenario, generator]
cases:
chosen: { when: Generator_p_nom_extendable AND Generator_p_nom_mod > 0, expression: Generator_n_mod }
otherwise: Generator_modules_installed
Generator_p_nom_committed:
description: >-
the build a committed unit's ramp rows are taken against: one module
where the build is modular, since its status counts modules, and the
given build otherwise (`constraints.py:1131-1134`)
dims: [scenario, generator]
cases:
modular_build: { when: "Generator_p_nom_mod > 0", expression: Generator_p_nom_mod }
otherwise: Generator_p_nom
Generator_primary_energy:
expression: >-
sum(sum((Generator_p * GlobalConstraint_energy_weight) * Generator_primary_energy_weight, over=snapshot), over=generator)
adds_to: primary_energy
Generator_operational_limit:
expression: >-
sum(sum((Generator_p * GlobalConstraint_energy_weight) * Generator_operational_limit_weight, over=snapshot), over=generator)
adds_to: operational_limit
Generator_tech_capacity_expansion:
expression: sum(Generator_p_nom_ext * Generator_tech_capacity_weight, over=generator)
adds_to: tech_capacity_expansion
Generator_running_cost:
description: >-
what a generator's output costs in a snapshot: the value read off its
piecewise curve where it has one, and its linear `marginal_cost`
otherwise. A curve replaces the linear cost, as PyPSA drops the unit
from that term (`optimize.py:333-348`)
dims: [scenario, snapshot, generator]
cases:
curve: { when: Generator_marginal_cost_curve, expression: Generator_marginal_cost_piecewise }
otherwise: Generator_p * Generator_marginal_cost
Generator_opex:
expression: >-
sum(sum((Generator_running_cost * snapshot_weightings_objective) * at(period_weight_objective, by=snapshot_period[period]), over=generator), over=snapshot)
+ sum(sum((((Generator_p * Generator_p) * Generator_marginal_cost_quadratic) * snapshot_weightings_objective) * at(period_weight_objective, by=snapshot_period[period]), over=generator), over=snapshot)
adds_to: scenario_opex
Generator_capex:
expression: sum(scenario_weight * Generator_p_nom_ext * Generator_capital_cost * Generator_capital_weight)
adds_to: total_cost
Generator_additions:
expression: >-
sum(Generator_p_nom_ext * Generator_first_active, over=generator, by=Generator_carrier[carrier])
adds_to: Carrier_additions
Generator_injection:
expression: sum(Generator_sign * Generator_p, over=generator, by=Generator_bus[bus])
adds_to: Bus_injection
conditions:
Generator_marginal_cost_curve:
description: >-
a generator that pays along a piecewise `marginal_cost` curve: one of
PyPSA's `active_assets` with curve data (`optimize.py:307`,
`piecewise.py:230-257`)
condition: count(Generator_marginal_cost_curve_p, over=breakpoint) > 0 AND Generator_active_asset
Generator_marginal_cost_curve_bends_down:
description: >-
a breakpoint where a generator's cost curve gets less steep: the
segment after it rises less per unit of output than the one before
condition: >-
Generator_marginal_cost_curve_p
AND shift(Generator_marginal_cost_curve_p, along=breakpoint, offset=1)
AND shift(Generator_marginal_cost_curve_p, along=breakpoint, offset=-1)
AND (Generator_marginal_cost_curve_cost - shift(Generator_marginal_cost_curve_cost, along=breakpoint, offset=1, edge=0))
* (shift(Generator_marginal_cost_curve_p, along=breakpoint, offset=-1, edge=0) - Generator_marginal_cost_curve_p)
> (shift(Generator_marginal_cost_curve_cost, along=breakpoint, offset=-1, edge=0) - Generator_marginal_cost_curve_cost)
* (Generator_marginal_cost_curve_p - shift(Generator_marginal_cost_curve_p, along=breakpoint, offset=1, edge=0))
Generator_marginal_cost_as_lp:
description: >-
linopy's choice of method for all generator cost curves at once
(`method="auto"`): the segment lines where every curve is convex and no
generator with a curve is committable, the incremental rows otherwise.
PyPSA gates the curves by `Generator-status` where one is committable,
and linopy states no segment lines under a gate (`optimize.py:314-318`,
`piecewise.py:200-224`)
condition: >-
count(Generator_marginal_cost_curve AND count(Generator_marginal_cost_curve_bends_down, over=breakpoint) > 0, over=generator) == 0
AND count(Generator_marginal_cost_curve AND Generator_committable, over=generator) == 0
Generator_committed:
description: >-
a committable generator that is active in the snapshot's period: every
unit-commitment row's set
condition: Generator_committable AND Generator_active
constraints:
Generator_fix_p_lower:
description: "`Generator-fix-p-lower` — a fixed generator outputs at least its minimum"
dims: [scenario, snapshot, generator]
where: not Generator_p_nom_extendable AND not Generator_committable AND Generator_active
expression: Generator_p >= Generator_p_min_pu * Generator_p_nom * (1 - Generator_maintenance_pu * Generator_maintenance)
Generator_fix_p_upper:
description: "`Generator-fix-p-upper` — a fixed generator outputs at most what is available"
dims: [scenario, snapshot, generator]
where: not Generator_p_nom_extendable AND not Generator_committable AND Generator_active
expression: Generator_p <= Generator_p_max_pu * Generator_p_nom * (1 - Generator_maintenance_pu * Generator_maintenance)
Generator_ext_p_lower:
description: "`Generator-ext-p-lower` — an extendable generator outputs at least its minimum of the chosen build"
dims: [scenario, snapshot, generator]
where: Generator_p_nom_extendable AND not Generator_committable AND Generator_active
expression: Generator_p >= Generator_p_min_pu * (Generator_p_nom_ext - Generator_maintenance_pu * Generator_maintenance_capacity)
Generator_ext_p_upper:
description: "`Generator-ext-p-upper` — an extendable generator outputs at most what is available of the chosen build"
dims: [scenario, snapshot, generator]
where: Generator_p_nom_extendable AND not Generator_committable AND Generator_active
expression: Generator_p <= Generator_p_max_pu * (Generator_p_nom_ext - Generator_maintenance_pu * Generator_maintenance_capacity)
Generator_ext_p_nom_lower:
description: "`Generator-ext-p_nom-lower` — the chosen build is at least its floor in every scenario"
dims: [scenario, generator]
where: Generator_p_nom_extendable AND Generator_active_asset
expression: Generator_p_nom_ext >= Generator_p_nom_min
Generator_ext_p_nom_upper:
description: "`Generator-ext-p_nom-upper` — the chosen build is at most its cap in every scenario. A cap of infinity is no row"
dims: [scenario, generator]
where: Generator_p_nom_extendable AND Generator_active_asset AND Generator_p_nom_max
expression: Generator_p_nom_ext <= Generator_p_nom_max
Generator_p_nom_set:
description: "`Generator-p_nom_set` — the chosen build pinned, wherever a value is given"
dims: [generator]
where: Generator_p_nom_extendable AND Generator_active_asset AND Generator_p_nom_set
expression: Generator_p_nom_ext == Generator_p_nom_set
Generator_e_sum_min:
description: "`Generator-e_sum_min` — energy over the horizon, each period weighted by its years, is at least its floor. A floor of minus infinity is no row, and so is a generator active in no snapshot"
dims: [scenario, generator]
where: Generator_e_sum_min AND count(Generator_active, over=snapshot) > 0
expression: sum(Generator_p * energy_limit_weight, over=snapshot) >= Generator_e_sum_min
Generator_e_sum_max:
description: "`Generator-e_sum_max` — energy over the horizon, each period weighted by its years, is at most its budget. A budget of infinity is no row, and so is a generator active in no snapshot"
dims: [scenario, generator]
where: Generator_e_sum_max AND count(Generator_active, over=snapshot) > 0
expression: sum(Generator_p * energy_limit_weight, over=snapshot) <= Generator_e_sum_max
Generator_p_nom_modularity:
description: "`Generator-p_nom_modularity` — the chosen build is a whole number of modules"
dims: [generator]
where: Generator_p_nom_extendable AND Generator_p_nom_mod > 0 AND Generator_active_asset
expression: Generator_p_nom_ext == Generator_p_nom_mod * Generator_n_mod
Generator_p_set:
description: "`Generator-p_set` — output pinned to the given schedule, wherever one is given"
dims: [scenario, snapshot, generator]
where: Generator_p_set AND Generator_active
expression: Generator_p == Generator_p_set
assumptions:
Generator_marginal_cost_curve_on_a_fixed_build:
holds: "NOT Generator_p_nom_extendable AND Generator_p_nom > 0 AND Generator_p_nom < inf"
where: "Generator_marginal_cost_curve"
description: >-
a generator's cost curve is given per unit of a fixed, positive, finite
`p_nom`, which scales it to output. PyPSA refuses an extendable unit and
a zero, missing or infinite `p_nom` (`piecewise.py:284-304`), and takes
a negative one, which the file does not state
Generator_marginal_cost_quadratic_without_risk_preference:
holds: "Generator_marginal_cost_quadratic == 0"
where: "CVaR_omega"
description: >-
a quadratic cost puts a square into every `CVaR-excess` row. PyPSA
refuses quadratic costs under any risk preference, `omega = 0` included
(`optimize.py:506-513`). In data prep you write a `CVaR_omega` row only
where a risk preference is set
Given#
| Symbol | Meaning |
|---|---|
| \(\omega\) | CVaR_omega (scalar), data another file declares |
| \(\mathrm{com}\) | Generator_committable over \(\mathcal{G}\), data another file declares |
| \(\gamma\) | Generator_maintenance_pu over \(\mathcal{G} \times \Xi\), data another file declares |
| \(\mathrm{C}^{\mathrm{curve}}\) | Generator_marginal_cost_curve_cost over \(\mathcal{B} \times \mathcal{G}\), data another file declares |
| \(\mathrm{p}^{\mathrm{curve}}\) | Generator_marginal_cost_curve_p over \(\mathcal{B} \times \mathcal{G}\), data another file declares |
| \(\mathrm{w}^{y}\) | period_weight_objective over \(\mathcal{Y}\), data another file declares |
| \(\pi\) | scenario_weight over \(\Xi\), data another file declares |
| \(\mathrm{w}\) | snapshot_weightings_objective over \(\mathcal{T}\), data another file declares |
| \(\mu\) | Generator_maintenance over \(\mathcal{G} \times \Xi \times \mathcal{T}\) |
| \(\mu^{\mathrm{nom}}\) | Generator_maintenance_capacity over \(\mathcal{G} \times \Xi \times \mathcal{T}\) |
| \(\mathit{Bus\_injection}\) | Bus_injection over \(\mathcal{N} \times \Xi \times \mathcal{T}\), an expression this file adds Generator_injection to |
| \(\mathit{Carrier\_additions}\) | Carrier_additions over \(\mathcal{I} \times \mathcal{Y}\), an expression this file adds Generator_additions to |
| \(\mathit{w}^{\mathrm{gc}}\) | GlobalConstraint_energy_weight over \(\mathcal{L} \times \Xi \times \mathcal{T}\), an expression another file defines |
| \(\mathit{w}^{E}\) | energy_limit_weight over \(\mathcal{T}\), an expression another file defines |
| \(\mathit{operational\_limit}\) | operational_limit over \(\mathcal{L} \times \Xi\), an expression this file adds Generator_operational_limit to |
| \(\mathit{primary\_energy}\) | primary_energy over \(\mathcal{L} \times \Xi\), an expression this file adds Generator_primary_energy to |
| \(\mathit{scenario\_opex}\) | scenario_opex over \(\Xi\), an expression this file adds Generator_opex to |
| \(\mathit{tech\_capacity\_expansion}\) | tech_capacity_expansion over \(\mathcal{L}\), an expression this file adds Generator_tech_capacity_expansion to |
| \(\mathit{total\_cost}\) | total_cost (scalar), an expression this file adds Generator_capex to |
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{N}\) | index \(n\) — bus with \(\mathrm{Generator\_bus}: \mathcal{G} \to \mathcal{N}\) — network nodes |
| \(\mathcal{I}\) | index \(i\) — carrier with \(\mathrm{Generator\_carrier}: \mathcal{G} \to \mathcal{I}\) — energy carriers, what a growth limit is set per |
| \(\mathcal{G}\) | index \(g\) — generator with \(\mathrm{Generator\_bus}: \mathcal{G} \to \mathcal{N},\ \mathrm{Generator\_carrier}: \mathcal{G} \to \mathcal{I}\) — generating units, each on one bus |
| \(\mathcal{L}\) | index \(l\) — global_constraint — PyPSA's GlobalConstraint rows, one label per declared limit |
| \(\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{on}\) | Generator_active over \(\mathcal{G} \times \mathcal{T}\) — whether a generator is active in a snapshot's period. It is PyPSA's active, from build year and lifetime, data prep |
| \(\mathrm{act}\) | Generator_active_asset over \(\mathcal{G}\) — whether a generator is one of PyPSA's active_assets, by its active flag alone, without build year and lifetime. A unit that is active in no snapshot of the horizon still counts. PyPSA refuses one that differs by scenario (constants.py:54) |
| \(\mathrm{c}^{\mathrm{cap}}\) | Generator_capital_cost over \(\mathcal{G} \times \Xi\) — cost of one unit of nominal power for the modelled horizon (PyPSA's periodized_cost), data prep. It is overnight_cost as an annuity over lifetime at discount_rate, times nyears, where overnight_cost is given, and capital_cost where it is not. Both add fom_cost (components.py:1157-1178, costs.py:102-203) |
| \(\mathrm{W}\) | Generator_capital_weight over \(\mathcal{G}\) — the sum of period weights a generator is active in. It is PyPSA's active * period_weighting, summed, data prep |
| \(\overline{\mathrm{E}}\) | Generator_e_sum_max over \(\mathcal{G} \times \Xi\) — most energy over the horizon: a fuel or emission budget in energy terms. It is infinity where no cap is meant |
| \(\underline{\mathrm{E}}\) | Generator_e_sum_min over \(\mathcal{G} \times \Xi\) — least energy over the horizon. It is minus infinity where no floor is meant |
| \(\mathrm{new}\) | Generator_first_active over \(\mathcal{G} \times \mathcal{Y}\) — one in the first period a generator is active in, zero elsewhere, data prep. PyPSA takes active & (active.cumsum() == 1) (global_constraints.py:278) |
| \(\mathrm{c}\) | Generator_marginal_cost over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — cost of one unit of output |
| \(\mathrm{c}^{(2)}\) | Generator_marginal_cost_quadratic over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — cost of the square of one unit of output |
| \(\mathrm{N}^{\mathrm{fix}}\) | Generator_modules_installed over \(\mathcal{G} \times \Xi\) — how many whole modules a committable build has in place, data prep. It is Generator_p_nom / Generator_p_nom_mod where a fixed build is modular, and one where it is not. PyPSA refuses a fixed modular build whose nominal power is not a whole number of modules |
| \(\mathrm{b}\) | Generator_operational_limit_weight over \(\mathcal{G} \times \mathcal{L} \times \Xi\), absent where the data has no row — one where the generator is in the row's set, data prep. One outside it has no row |
| \(\overline{\mathrm{p}}\) | Generator_p_max_pu over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — most output, per unit of nominal power: an availability profile |
| \(\underline{\mathrm{p}}\) | Generator_p_min_pu over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — least output, per unit of nominal power |
| \(\mathrm{nonneg}\) | Generator_p_min_pu_nonneg over \(\mathcal{G}\) — true where none of the generator's own minimums-per-unit is negative. It is PyPSA's per-unit (p_min_pu >= 0).all() over every snapshot and scenario, data prep |
| \(\mathrm{p}^{\mathrm{nom}}\) | Generator_p_nom over \(\mathcal{G} \times \Xi\) — nominal power |
| \(\mathrm{ext}\) | Generator_p_nom_extendable over \(\mathcal{G}\) — whether the nominal power is a decision |
| \(\overline{\mathrm{p}}^{\mathrm{nom}}\) | Generator_p_nom_max over \(\mathcal{G} \times \Xi\) — most nominal power an extendable generator may be built at |
| \(\underline{\mathrm{p}}^{\mathrm{nom}}\) | Generator_p_nom_min over \(\mathcal{G} \times \Xi\) — least nominal power an extendable generator may be built at |
| \(\mathrm{p}^{\mathrm{mod}}\) | Generator_p_nom_mod over \(\mathcal{G}\), absent where the data has no row — the module size a build comes in whole numbers of. No value means the build is continuous |
| \(\mathrm{p}^{\mathrm{nom,set}}\) | Generator_p_nom_set over \(\mathcal{G}\), absent where the data has no row — a given nominal power for an extendable generator. One without a value has no row here |
| \(\mathrm{p}^{\mathrm{set}}\) | Generator_p_set over \(\mathcal{G} \times \Xi \times \mathcal{T}\), absent where the data has no row — a given output schedule. A generator without one has no row here |
| \(\mathrm{a}\) | Generator_primary_energy_weight over \(\mathcal{G} \times \mathcal{L} \times \Xi \times \mathcal{T}\), absent where the data has no row — the constrained attribute per unit of energy at the bus: the carrier's co2_emissions over the generator's efficiency at the snapshot, data prep. A generator of an unweighted carrier has no row |
| \(\mathrm{sgn}\) | Generator_sign over \(\mathcal{G}\) — the sign output enters its bus's balance with. It is PyPSA's sign, 1 unless given, -1 for a unit that draws power. PyPSA refuses one that differs by scenario (constants.py:46) |
| \(\mathrm{m}\) | Generator_tech_capacity_weight over \(\mathcal{G} \times \mathcal{L}\), absent where the data has no row — one where the generator is in the row's carrier-and-bus set, data prep. One outside it, or one that is not active in the row's investment_period, has no row |
Conditions#
| Symbol | Meaning |
|---|---|
| \(\mathrm{on}^{\mathrm{com}}\) | Generator_committed over \(\mathcal{G} \times \mathcal{T}\) — a committable generator that is active in the snapshot's period: every unit-commitment row's set |
| \(\mathrm{lp}\) | Generator_marginal_cost_as_lp (scalar) — linopy's choice of method for all generator cost curves at once (method="auto"): the segment lines where every curve is convex and no generator with a curve is committable, the incremental rows otherwise. PyPSA gates the curves by Generator-status where one is committable, and linopy states no segment lines under a gate (optimize.py:314-318, piecewise.py:200-224) |
| \(\mathrm{curve}\) | Generator_marginal_cost_curve over \(\mathcal{G}\) — a generator that pays along a piecewise marginal_cost curve: one of PyPSA's active_assets with curve data (optimize.py:307, piecewise.py:230-257) |
| \(\mathrm{bend}\) | Generator_marginal_cost_curve_bends_down over \(\mathcal{B} \times \mathcal{G}\) — a breakpoint where a generator's cost curve gets less steep: the segment after it rises less per unit of output than the one before |
Variables#
| Symbol | Meaning |
|---|---|
| \(C^{\mathrm{curve}}\) | Generator_marginal_cost_piecewise over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — Generator-marginal_cost_piecewise — the running cost of a generator that pays along a curve, held above the curve |
| \(N\) | Generator_n_mod over \(\mathcal{G}\) — Generator-n_mod — how many modules of an extendable modular build. A unit that is not active has none |
| \(p\) | Generator_p over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — Generator-p — output of a generator in a snapshot |
| \(P\) | Generator_p_nom_ext over \(\mathcal{G}\) — Generator-p_nom — nominal power where it is a decision, for a generator that is active. A fixed build is the parameter of the same PyPSA name |
Definitions#
| Symbol | Meaning |
|---|---|
| \(\mathit{Generator\_additions}\) | Generator_additions over \(\mathcal{I} \times \mathcal{Y}\) |
| \(\mathit{Generator\_capex}\) | Generator_capex (scalar) |
| \(\mathit{Generator\_injection}\) | Generator_injection over \(\mathcal{N} \times \Xi \times \mathcal{T}\) |
| \(\mathrm{M}\) | Generator_modules over \(\mathcal{G} \times \Xi\) — how many modules a committed unit's status counts: the chosen number where the build is extendable and modular, and the modules installed otherwise (constraints.py:656-661) |
| \(\mathit{Generator\_operational\_limit}\) | Generator_operational_limit over \(\mathcal{L} \times \Xi\) |
| \(\mathit{Generator\_opex}\) | Generator_opex over \(\Xi\) |
| \(\widehat{\mathrm{p}}^{\mathrm{nom}}\) | Generator_p_nom_committed over \(\mathcal{G} \times \Xi\) — the build a committed unit's ramp rows are taken against: one module where the build is modular, since its status counts modules, and the given build otherwise (constraints.py:1131-1134) |
| \(\widetilde{\mathrm{p}}^{\mathrm{nom}}\) | Generator_p_nom_effective over \(\mathcal{G} \times \Xi\) — the build a generator's limits are taken against: the chosen one where it is extendable, the given one otherwise |
| \(\mathit{Generator\_primary\_energy}\) | Generator_primary_energy over \(\mathcal{L} \times \Xi\) |
| \(C^{\mathrm{run}}\) | Generator_running_cost over \(\mathcal{G} \times \Xi \times \mathcal{T}\) — what a generator's output costs in a snapshot: the value read off its piecewise curve where it has one, and its linear marginal_cost otherwise. A curve replaces the linear cost, as PyPSA drops the unit from that term (optimize.py:333-348) |
| \(\mathit{Generator\_tech\_capacity\_expansion}\) | Generator_tech_capacity_expansion over \(\mathcal{L}\) |
Notation#
| Symbol | Meaning |
|---|---|
| \(t \boxminus_{v} k\) | edge=v — \(t-k\), and \(v\) past the edge |
Conditions#
Generator_committed
Generator_marginal_cost_as_lp
Generator_marginal_cost_curve
Generator_marginal_cost_curve_bends_down
Variable domains#
Generator_marginal_cost_piecewise
Generator_n_mod
Generator_p
Generator_p_nom_ext
Definitions#
Generator_additions
Generator_capex
Generator_injection
Generator_modules
Generator_operational_limit
Generator_opex
Generator_p_nom_committed
Generator_p_nom_effective
Generator_primary_energy
Generator_running_cost
Generator_tech_capacity_expansion
Subject to#
Generator_e_sum_max
Generator_e_sum_min
Generator_ext_p_lower
Generator_ext_p_nom_lower
Generator_ext_p_nom_upper
Generator_ext_p_upper
Generator_fix_p_lower
Generator_fix_p_upper
Generator_p_nom_modularity
Generator_p_nom_set
Generator_p_set
Assumptions#
Generator_marginal_cost_curve_on_a_fixed_build
Generator_marginal_cost_quadratic_without_risk_preference
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