Unit commitment#
This spec adds an on/off decision per unit and a start-up ramp to least-cost
dispatch. Read previous_status first, then ramp_up.
previous_status is the state that a unit carries into a snapshot, and the file
states its three regimes once, under
cases:. ramp_up reads
previous_status as it reads a parameter, so one inequality covers a running
unit and a starting unit. The typesetter prints the cases once, under
Definitions below.
description: >-
Unit commitment with a start-up ramp, the formulation `cases:` exists for.
The state a unit carries into a snapshot has three regimes — a unit that is
never off, the first snapshot, and every later one — and writing them at the
constraint would fork `ramp_up` three ways. With the regimes named once, the
inequality is written once.
dimensions:
snapshot: { dtype: int, description: dispatch periods, ordered: true }
generator: { description: generating units }
parameters:
committable: { dims: [generator], dtype: bool, description: whether the unit may be switched off }
status_initial: { dims: [generator], description: whether the unit was running before the horizon }
capacity: { dims: [generator], description: installed capacity }
min_output: { dims: [generator], description: output floor while running }
ramp_limit: { dims: [generator], description: how far output may move between snapshots while running }
start_up_limit: { dims: [generator], description: how far it may move in the snapshot it starts in }
load: { dims: [snapshot], description: demand to be met }
cost: { dims: [generator], description: marginal cost }
variables:
dispatch:
description: output of a generator in a snapshot
dims: [snapshot, generator]
bounds: { lower: 0, upper: capacity }
status:
description: whether the unit is running in a snapshot
dims: [snapshot, generator]
domain: binary
expressions:
previous_status:
description: the commitment state a unit carries into a snapshot
dims: [snapshot, generator]
cases:
always_on:
when: "not committable"
expression: 1
boundary:
when: "committable and position(snapshot) == 0"
expression: status_initial
otherwise: shift(status, along=snapshot, offset=1)
constraints:
power_balance:
dims: [snapshot]
expression: sum(dispatch, over=generator) == load
upper:
description: a unit that is not running produces nothing
dims: [snapshot, generator]
expression: dispatch <= status * capacity
lower:
description: and one that is running produces at least its floor
dims: [snapshot, generator]
expression: dispatch >= status * min_output
ramp_up:
description: >-
one inequality for both regimes — a unit already running is held to
`ramp_limit`, a unit starting up to `start_up_limit`.
dims: [snapshot, generator]
expression: >-
dispatch - shift(dispatch, along=snapshot, offset=1, edge=0)
<= ramp_limit * previous_status + start_up_limit * (1 - previous_status)
assumptions:
output_floor_fits_under_the_cap:
holds: "min_output <= capacity"
where: "committable"
description: >-
`lower` and `upper` hold one dispatch between them, so a floor above the
cap makes a running unit infeasible rather than expensive. A unit that
cannot be switched off is held to its floor in every snapshot, so the
check is the committable ones'.
objective:
sense: minimize
expression: sum(dispatch * cost)
Unit commitment with a start-up ramp, the formulation cases: exists for. The state a unit carries into a snapshot has three regimes — a unit that is never off, the first snapshot, and every later one — and writing them at the constraint would fork ramp_up three ways. With the regimes named once, the inequality is written once.
Sets#
| Symbol | Meaning |
|---|---|
| \(\mathcal{T}\) | index \(t\) — snapshot — dispatch periods |
| \(\mathcal{G}\) | index \(g\) — generator — generating units |
Parameters#
| Symbol | Meaning |
|---|---|
| \(\mathrm{committable}\) | committable over \(\mathcal{G}\) — whether the unit may be switched off |
| \(\mathrm{status}^{\mathrm{initial}}\) | status_initial over \(\mathcal{G}\) — whether the unit was running before the horizon |
| \(\mathrm{capacity}\) | capacity over \(\mathcal{G}\) — installed capacity |
| \(\mathrm{min\_output}\) | min_output over \(\mathcal{G}\) — output floor while running |
| \(\mathrm{ramp\_limit}\) | ramp_limit over \(\mathcal{G}\) — how far output may move between snapshots while running |
| \(\mathrm{start\_up\_limit}\) | start_up_limit over \(\mathcal{G}\) — how far it may move in the snapshot it starts in |
| \(\mathrm{load}\) | load over \(\mathcal{T}\) — demand to be met |
| \(\mathrm{cost}\) | cost over \(\mathcal{G}\) — marginal cost |
Variables#
| Symbol | Meaning |
|---|---|
| \(\mathit{dispatch}\) | dispatch over \(\mathcal{T} \times \mathcal{G}\) — output of a generator in a snapshot |
| \(\mathit{status}\) | status over \(\mathcal{T} \times \mathcal{G}\) — whether the unit is running in a snapshot |
Definitions#
| Symbol | Meaning |
|---|---|
| \(\mathit{previous\_status}\) | previous_status over \(\mathcal{T} \times \mathcal{G}\) — the commitment state a unit carries into a snapshot |
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 |
Variable domains#
dispatch
status
Definitions#
previous_status
Objective#
Subject to#
power_balance
upper
lower
ramp_up
Assumptions#
output_floor_fits_under_the_cap
Regenerate with pixi run python -m tools.gallery.