Skip to content

Equipment-Specific Formulations

This chapter details the LP constraints for each equipment type in Novomodelo. While System Element Modeling Overview describes what each element is and its decision variables, this chapter contains the detailed mathematical constraints governing each equipment type’s behavior within the LP. The reading order is: System Element Modeling Overview → this chapter → LP Formulation.

For variable definitions and index sets, see Notation Conventions. For hydro production constraints specifically, see Hydro Production Function Models.

Each thermal plant has one marginal cost per MWh of generation, cjthc^{th}_j, and one generation column per block, gj,kg_{j,k}.

Decision Variables:

  • gj,kg_{j,k} = generation at thermal jj, block kk (MW)

Constraints:

Generation bounds:

G‾j≤gj,k≤Gˉj\underline{G}_j \leq g_{j,k} \leq \bar{G}_j

Both bounds are hard constraints with no slack variables — thermal dispatch is directly controllable (unlike hydro, which depends on exogenous inflows). The bounds may vary by stage and by block; cjthc^{th}_j varies by stage only.

Objective Contribution:

∑kτk cjth gj,k\sum_{k} \tau_k \, c^{th}_j \, g_{j,k}

Decision Variables:

  • fn,k+f^+_{n,k} = direct flow (source → target)
  • fn,k−f^-_{n,k} = reverse flow (target → source)

Bounds:

0≤fn,k+≤Fˉn+,0≤fn,k−≤Fˉn−0 \leq f^+_{n,k} \leq \bar{F}^+_n, \quad 0 \leq f^-_{n,k} \leq \bar{F}^-_n

Load Balance Contribution:

At source bus:

−fn,k++fn,k−-f^+_{n,k} + f^-_{n,k}

At target bus:

fn,k+−fn,k−f^+_{n,k} - f^-_{n,k}

The dispatch LP is lossless: both flow directions enter the bus balances with coefficient ±1. Transmission losses are a reported quantity computed after the solve, lossn,k=(1−ηn) (fn,k++fn,k−)\text{loss}_{n,k} = (1 - \eta_n)\,(f^+_{n,k} + f^-_{n,k}), where ηn\eta_n is the line’s reported efficiency (Notation Conventions). See System Element Modeling Overview for the network model.

Objective Contribution:

∑kτk⋅cnexch(fn,k++fn,k−)\sum_{k} \tau_k \cdot c^{exch}_n (f^+_{n,k} + f^-_{n,k})

Each contract is unidirectional — either an import or an export contract.

Decision Variables:

  • χc,k\chi_{c,k} = dispatched power for contract cc, block kk

Bounds:

C‾c≤χc,k≤Cˉc\underline{C}_c \leq \chi_{c,k} \leq \bar{C}_c

Load Balance Contribution:

At connected bus:

  • Import contracts (c∈Cimpc \in \mathcal{C}^{imp}): +χc,k+\chi_{c,k} (power entering the system)
  • Export contracts (c∈Cexpc \in \mathcal{C}^{exp}): −χc,k-\chi_{c,k} (power leaving the system)

Objective Contribution:

∑kτk∑c∈Cccctr⋅χc,k\sum_{k} \tau_k \sum_{c \in \mathcal{C}} c^{ctr}_c \cdot \chi_{c,k}

Because import prices (ccctrc^{ctr}_c) are positive and export prices are negative, this single summation naturally adds import costs and subtracts export revenue. The price sign is independent of the load-balance sign: an import column injects +χc,k+\chi_{c,k} and carries a positive (cost) price; an export column withdraws −χc,k-\chi_{c,k} and carries a negative (revenue) price.

Take-or-pay floor and lifecycle. A non-zero lower bound C‾c\underline{C}_c is a hard take-or-pay obligation: the LP must dispatch at least C‾c\underline{C}_c at the contract price, even when cheaper supply exists. Bounds [C‾c,Cˉc][\underline{C}_c, \bar{C}_c] and the price ccctrc^{ctr}_c may each vary by stage and, optionally, by block. Outside its commissioning window (System Element Modeling Overview §1) a contract’s column is pinned to zero. A contract is stateless: it carries no state variable and contributes nothing to the Benders cuts.

Pumping stations transfer water from a source reservoir to a destination reservoir, consuming electrical power in the process. The source-to-destination direction is a modeling choice (typically uphill / against the cascade); the formulation does not require any particular elevation relationship.

Decision Variables:

  • py,kp_{y,k} = pumped water flow at station yy, block kk (m³/s)

Bounds:

P‾y≤py,k≤Pˉy\underline{P}_y \leq p_{y,k} \leq \bar{P}_y

Both bounds are hard constraints and may vary by stage and, optionally, by block. Outside its commissioning window (System Element Modeling Overview §1) a pumping station’s flow column is pinned to zero, so the station moves no water and draws no power.

Power Consumption:

Py,kpump=ρypump⋅py,kP^{pump}_{y,k} = \rho^{pump}_y \cdot p_{y,k}

where ρypump\rho^{pump}_y is the power consumption rate (MW per m³/s).

Water Balance Impact:

The pumped flow leaves the source plant’s reservoir and enters the destination’s, converted to volume by the block conversion ζk=0.0036 τk\zeta_k = 0.0036\,\tau_k, with τk\tau_k the duration of block kk in hours:

  • On a chronological stage, block kk: −ζk py,k-\zeta_k \, p_{y,k} on the source plant’s block-kk water balance and +ζk py,k+\zeta_k \, p_{y,k} on the destination’s.
  • On a parallel stage each plant has one stage row: −∑k∈Kζk py,k-\sum_{k \in \mathcal{K}} \zeta_k \, p_{y,k} on the source’s and +∑k∈Kζk py,k+\sum_{k \in \mathcal{K}} \zeta_k \, p_{y,k} on the destination’s.

These are storage-change signs; in the all-terms row of LP Formulation §4 the same terms appear on the left with the opposite signs.

Load Balance Impact:

At connected bus: −Py,kpump=−ρypump⋅py,k-P^{pump}_{y,k} = -\rho^{pump}_y \cdot p_{y,k} (power consumed)

Objective Contribution: None

Hydro constraints are the most complex in the system. Rather than duplicating them here, the hydro formulation is split across two chapters:

  • Water balance, outflow, storage bounds, and soft constraints: See LP Formulation §4 (water balance), §6 (generation constraints), §7 (outflow constraints), §8 (variable bounds), §9 (constraint violation penalties).
  • Production function models (constant productivity, FPHA, linearized head): See Hydro Production Function Models.

A plant declares one or more unit groups, each on its own bus; groups sharing a bus form one (hydro, bus) cell, and turbined flow and generation are tracked per cell rather than per plant — storage, spillage, and diversion stay per plant. Turbined-flow and generation bounds (§8, §6 of LP Formulation) compose per cell from the cell’s member unit groups; a single-bus plant has exactly one cell and every formula reduces to the plant-level form.

For hydro decision variables and physical meaning, see System Element Modeling Overview §5.

Non-controllable sources (wind farms, solar plants, small run-of-river hydros) have an available generation the solver takes as given: drawn per scenario from the source’s availability model when it has one (Scenario Generation §5.4), and otherwise its stage’s available generation. The solver can only curtail generation below the available amount — it cannot dispatch upward beyond what nature provides.

Decision Variables:

  • gr,kncg^{nc}_{r,k} = generation at non-controllable source rr, block kk

Bounds (hard):

0≤gr,knc≤Ar,k,Ar,k=Gˉr ξr fr,k0 \leq g^{nc}_{r,k} \leq A_{r,k}, \qquad A_{r,k} = \bar{G}_r \, \xi_r \, f_{r,k}

where Gˉr\bar{G}_r is the installed capacity, ξr\xi_r the availability ratio of the stage and scenario (System Element Modeling Overview §6) and fr,kf_{r,k} the block factor of block kk (11 when none is given); a must-run source has gr,knc=Ar,kg^{nc}_{r,k} = A_{r,k}; a source without stochastic availability takes its stage’s available generation, the installed capacity unless the stage sets it, in place of Gˉrξr\bar{G}_r \xi_r.

Load Balance Contribution:

At connected bus: +gr,knc+g^{nc}_{r,k} (generation injected)

Objective Contribution:

−∑kτk∑r∈Rcrcurt gr,knc- \sum_{k} \tau_k \sum_{r \in \mathcal{R}} c^{curt}_r \, g^{nc}_{r,k}

The LP prices curtailment as a reward on dispatched generation. The term equals the curtailment penalty ∑kτk∑rcrcurtκr,k\sum_k \tau_k \sum_r c^{curt}_r \kappa_{r,k} minus the constant ∑kτk∑rcrcurtAr,k\sum_k \tau_k \sum_r c^{curt}_r A_{r,k}: it leads to the same decisions, and the stage objective can therefore be negative. A must-run source contributes the constant −τkcrcurtAr,k-\tau_k c^{curt}_r A_{r,k}.

The curtailment cost crcurtc^{curt}_r is a regularization penalty (Category 3 in the Penalty System), analogous to the spillage cost of a hydro — curtailment discards available “free” energy.

The methodology above defines every equipment type’s LP constraints; the tabs below cover how Novomodelo’s software surface configures, feeds, and reports on the four element types with dedicated configuration files — thermals, contracts, pumping stations, and non-controllable sources.

Novomodelo’s system/thermals.json, system/energy_contracts.json, system/pumping_stations.json, and system/non_controllable_sources.json files author the four equipment types that carry dedicated element configuration in this chapter. The equations these fields feed are in the sections above.

Thermals, non-controllable sources, pumping stations, and energy contracts all carry a required operational_start_date field:

FieldTypeDescription
operational_start_datestring (ISO-8601 date)Calendar date (YYYY-MM-DD) the entity enters the registry’s operational history. Provenance and the canonical (operational_start_date, id) ordering key (see Notation Conventions) — independent of the commissioning window below: it does not gate commissioning, and the window does not derive from it.

Thermals, lines, contracts, pumping stations, and non-controllable sources all carry the same optional pair of fields:

FieldTypeDescription
entry_stage_idinteger or nullStage index at which the entity enters service (inclusive). null means the entity is available from stage 0.
exit_stage_idinteger or nullStage index at which the entity is decommissioned. The commissioning window is half-open [entry_stage_id, exit_stage_id): the entity is active through exit_stage_id - 1, and from exit_stage_id onward its columns are pinned to [0, 0]. null means the entity is never decommissioned.

Outside its window, an entity’s decision columns remain present in the LP but are pinned to zero: it injects no power, withdraws no power, moves no water, and draws no bus power. An entry_stage_id past the last study stage is accepted: the entity then stays out of service for the whole study. See System Element Modeling Overview §1 for the shared methodology statement — methodology §3–4 above cover how the window interacts with contract take-or-pay bounds and pumping flow.

system/thermals.json — Thermal Plant Registry

Section titled “system/thermals.json — Thermal Plant Registry”

Thermal units are defined in system/thermals.json. The top-level object has a single key "thermals" containing an array of unit objects:

{
"thermals": [
{
"id": 0,
"name": "UTE1",
"bus_id": 0,
"operational_start_date": "1998-03-01",
"cost_per_mwh": 5.0,
"generation": { "min_mw": 0.0, "max_mw": 15.0 }
},
{
"id": 1,
"name": "Angra 1",
"bus_id": 0,
"operational_start_date": "1985-01-01",
"entry_stage_id": null,
"exit_stage_id": null,
"cost_per_mwh": 50.0,
"generation": { "min_mw": 0.0, "max_mw": 657.0 },
"anticipated_config": { "lead_stages": 2 }
}
]
}

Only id, name, bus_id, operational_start_date, cost_per_mwh, and generation are required; entry_stage_id, exit_stage_id, and anticipated_config are optional.

FieldTypeRequiredDescription
idintegerYesUnique non-negative identifier.
namestringYesHuman-readable plant name. Used in output files, validation messages, and log output.
bus_idintegerYesIdentifier of the electrical bus to which this unit’s generation is injected. Must match an id in buses.json.
operational_start_datestring (ISO-8601 date)YesCalendar date (YYYY-MM-DD) the unit enters the registry’s operational history — see Shared Operational Start Date above.
cost_per_mwhnumberYesMarginal cost of generation, in dollars per MWh. Must be non-negative.

The generation block sets the output limits, stored internally as min_generation_mw / max_generation_mw on the entity, and enforced as the hard generation bounds from methodology §1.1 in every stage LP.

"generation": { "min_mw": 0.0, "max_mw": 657.0 }
FieldTypeDescription
min_mwnumberMinimum electrical generation, in MW. A non-zero value is a must-run commitment: the solver must dispatch at least this much whenever the unit is in service (subject to the continuous-relaxation caveat in methodology §1.1).
max_mwnumberMaximum electrical generation (installed capacity), in MW.

Stage-varying bounds are supplied via constraints/thermal_bounds.parquet, which accepts sparse (thermal_id, stage_id) rows carrying min_generation_mw and/or max_generation_mw, optionally narrowed to one block via an optional block_id column; absent rows fall back to the base entity values. cost_per_mwh has no per-block variant — a row combining a non-null cost_per_mwh with a non-null block_id is rejected at validation, and a block_id row on a thermal declaring anticipated_config is rejected outright, since the delivered commitment is bounded at a single stage.

Anticipated Dispatch Configuration (anticipated_config)

Section titled “Anticipated Dispatch Configuration (anticipated_config)”

The optional anticipated_config block flags a thermal as anticipated, attaching the per-plant lead described in System Element Modeling Overview §4. The lead is given in exactly one of two mutually exclusive forms — a stage count or a physical duration:

"anticipated_config": { "lead_stages": 2 }
"anticipated_config": { "lead_time_hours": 720.0 }
FieldTypeDescription
lead_stagesintegerNumber of stages of dispatch anticipation, calendar-free. A value of 2 means the commitment for stage t must be decided at stage t - 2. Must be >= 1.
lead_time_hoursnumberPhysical anticipation lead in hours; must be finite and > 0. Each delivery stage’s commitment is decided at the stage containing the instant one lead before the delivery stage’s end (an instant on a stage boundary belongs to the earlier stage); a delivery whose instant is at or before the study start is decided before the study. When the instant falls inside the delivery stage itself, the delivery is not anticipated: the plant dispatches as an ordinary thermal at that stage, and the study setup logs a warning naming the plant and the stage.

Supplying both keys, or neither, is a load error. A lead that reaches past the study horizon — lead_stages exceeding the stage count, or lead_time_hours exceeding the summed horizon — is rejected at load unless the plant’s lead reaches a declared post-study stage (both lead modes are gated identically): such a commitment is delivered past the horizon, whether decided in the study or before it. With no post-study stage to reach, the plant can never deliver within the study horizon and the over-horizon lead is rejected. A lead_time_hours lead coarse enough that one decision stage would decide more than one delivery stage is rejected when the study is set up; a lead_stages lead never does this. See Post-Study Boundary & Chained Studies and post_study_stages.json for the boundary a post-horizon delivery is priced against.

An anticipated plant also declares, in initial_conditions.json’s past_anticipated_commitments, every commitment it decided before the study (System Element Modeling Overview §4, “Commitments decided before the study”). Each entry is a dated, delivery-anchored window — a committed MW rate held constant over [start_date, end_date):

{
"past_anticipated_commitments": [
{ "thermal_id": 2, "start_date": "2025-11-01", "end_date": "2025-12-01", "value_mw": 0.0 }
]
}
FieldTypeDescription
thermal_idintegerIdentifier of an anticipated thermal (must carry anticipated_config).
start_datestring (ISO-8601 date)Start of the commitment window (inclusive).
end_datestring (ISO-8601 date)End of the commitment window (exclusive); must be after start_date.
value_mwnumberCommitted MW rate held constant over the window.

A plant’s windows must tile every delivery stage the plant decided before the study, exactly — coverage 1.0, no gap, no overlap. Those stages may sit on either side of the horizon: the leading in-study delivery stages, and any post-horizon stages at which a commitment decided before the study is delivered (for the equivalent term in other planning tools, see the Glossary). A window may never cover a stage the study itself decides, and no single window may straddle the horizon end — split it there into an in-study and a post-study window. A stretch with no scheduled commitment still needs an explicit 0.0-rate window rather than a gap left to be inferred. How each value_mw is bounds-checked depends on where its window’s delivery stages fall. A window covering one or more in-study delivery stages has each covered, commissioning-active stage’s value_mw checked against that stage’s resolved generation box — the base [min_mw, max_mw] narrowed by any constraints/thermal_bounds.parquet stage/block override — within the solver’s feasibility tolerance. A purely post-horizon window (covering no in-study stage) instead checks each value_mw against the plant’s static [min_mw, max_mw] bounds, within the same tolerance. A value_mw outside its stage’s box is a load-time error, because the LP delivery equality on that stage could not otherwise be satisfied. The committed values are sunk: an in-study window seeds the ring slot of its delivery stage at the first stage, and that stage’s fish row pins the plant’s generation to it; a post-horizon window holds no slot, and when a boundary is loaded its state contribution is folded once, at load, into the intercept of every boundary cut. Neither enters the study objective, and a post-horizon window is echoed at its real delivery date in anticipated/fixed_deliveries.parquet.

An anticipated thermal whose lead delivers after the study horizon has two declaration surfaces, by who decides the commitment:

  • Decided in-study, delivered post-horizon — bounded and costed solely by a thermal_bounds[] cell in post_study_stages.json ({thermal_id, post_study_stage_index, cost_per_mwh, min_mw, max_mw}), one per reached post-study stage. The study decides the MW; the cell supplies its price and capability.
  • Decided before the study, delivered post-horizon — a past_anticipated_commitments window (above) extending past the horizon end.

An in-study decision is charged on its decision column at the cost_per_mwh of its thermal_bounds[] cell, discounted from the delivery stage, and the loaded boundary prices the commitment carried into the terminal state; with no policy.boundary declared in config.json, the study setup warns that the delivery prices at zero terminal value. A commitment decided before the study is sunk: its fuel enters no objective, and with a boundary loaded its state contribution enters the boundary cuts’ intercepts. Without a boundary, a post-horizon window with a non-zero rate raises the same setup warning; an all-zero window raises none. See Post-Study Boundary & Chained Studies for the formulation and Case Format for the file shapes.

Constraining Commitments via Generic Constraints

Section titled “Constraining Commitments via Generic Constraints”

The anticipated-commitment decision variable can be referenced in a generic constraint using the anticipated_decision(N) expression, where N is the thermal’s id — for example, to cap the MW level committed at each decision stage:

{
"constraints": [
{
"id": 1,
"name": "cap_ant_t1",
"expression": "anticipated_decision(2) <= 400.0",
"slack": { "enabled": false }
}
]
}

See Generic Constraints for the full authoring language — named expressions, the activation grid, and two-sided slack.

anticipated_decision(N) must reference a thermal carrying an anticipated_config block — referencing a non-anticipated thermal is a hard load-time error. thermal_generation(N) on an anticipated thermal is accepted but flagged with a semantic-ambiguity warning, since that expression reads the per-block delivered generation, not the forward commitment.

RuleError ClassDescription
Bus reference integrityReference errorEvery bus_id must match an id in buses.json.
Non-negative costSchema errorcost_per_mwh must be non-negative.
Generation bounds orderingPhysical feasibilitymin_mw must be <= max_mw.
Anticipated lead validityPhysical feasibilityWhen anticipated_config is present, lead_stages must be >= 1; a lead_stages above the study’s stage count, or a lead_time_hours above the summed horizon hours, is rejected unless the lead reaches a declared post-study stage.
Commitment-window tilingReference errorEvery anticipated thermal’s past_anticipated_commitments windows must tile every delivery stage the plant decided before the study exactly (coverage 1.0, no gap, no overlap) — in-study leading stages and post-horizon stages decided before the study alike — never a stage the study itself decides, and no window may straddle the horizon end.
Commitment thermal referenceReference errorEvery past_anticipated_commitments entry’s thermal_id must reference an anticipated thermal.
Commitment value boundsPhysical feasibilityEach past_anticipated_commitments window’s value_mw must lie within every covered delivery stage’s resolved generation box (in-study coverage) or the plant’s static [min_mw, max_mw] bounds (purely post-horizon), within solver tolerance. See Error Codes.
Post-study delivery boundsReference errorEvery post-study stage an anticipated thermal’s lead reaches must carry a matching post_study_stages.json thermal_bounds[] cell, and every post-study stage covered by a window for a commitment decided before the study must be tiled at coverage 1.0. See Error Codes for the exact failure modes.

system/non_controllable_sources.json — NCS Registry

Section titled “system/non_controllable_sources.json — NCS Registry”

Non-controllable sources (wind, solar, small run-of-river hydro) are defined in system/non_controllable_sources.json:

{
"non_controllable_sources": [
{
"id": 0,
"name": "Wind Farm A",
"bus_id": 1,
"operational_start_date": "2015-06-01",
"max_generation_mw": 100.0
}
]
}
FieldTypeRequiredDescription
idintegerYesUnique identifier.
namestringYesHuman-readable source name.
bus_idintegerYesBus to which this source’s generation is injected.
operational_start_datestring (ISO-8601 date)YesCalendar date (YYYY-MM-DD) the source enters the registry’s operational history — see Shared Operational Start Date above.
max_generation_mwnumberYesInstalled capacity, in MW. It scales the stochastic availability described in methodology §6 and does not cap the available generation.
allow_curtailmentbooleanNoDefaults to true (curtailable). Set false for must-run sources that pin generation to the realized availability every scenario — see System Element Modeling Overview §6 for when this is required (e.g. already-netted aggregate NCS totals).
curtailment_costnumber or nullNoEntity-level override of non_controllable_source.curtailment_cost in penalties.json.

The availability ratio comes from scenarios/non_controllable_stats.parquet (or scenarios/external_ncs_scenarios.parquet under the external scheme) and the block factors from scenarios/non_controllable_factors.json; a source with no availability model takes its stage’s available generation from constraints/ncs_bounds.parquet, max_generation_mw where no row applies — see the Inputs & Outputs tab.

system/pumping_stations.json — Pumping Station Registry

Section titled “system/pumping_stations.json — Pumping Station Registry”
{
"pumping_stations": [
{
"id": 0,
"name": "Bombeamento Serra da Mesa",
"bus_id": 10,
"operational_start_date": "2010-09-15",
"source_hydro_id": 3,
"destination_hydro_id": 5,
"consumption_mw_per_m3s": 0.5,
"flow": { "min_m3s": 0.0, "max_m3s": 150.0 }
}
]
}
FieldTypeRequiredDescription
idintegerYesUnique identifier.
namestringYesHuman-readable station name.
bus_idintegerYesBus from which electrical power is consumed.
operational_start_datestring (ISO-8601 date)YesCalendar date (YYYY-MM-DD) the station enters the registry’s operational history — see Shared Operational Start Date above.
source_hydro_idintegerYesHydro plant from whose reservoir water is extracted (see methodology §4). Must differ from destination_hydro_id — a station modeling a self-transfer (same id on both sides) is a load-time error, since it would silently cancel on one water-balance row while still drawing power. The station’s [entry_stage_id, exit_stage_id) window must lie inside the Operating stages of both its source and its destination hydro (not before entry, not at or after exit, and not Filling); otherwise the case is rejected (BusinessRuleViolation on system/pumping_stations.json), one error per offending side naming the first such stage. Zero pumping_bounds rows do not satisfy the rule.
destination_hydro_idintegerYesHydro plant into whose reservoir water is injected (see methodology §4).
consumption_mw_per_m3snumberYesPower consumption rate, in MW per m³/s (methodology §4).
flow.min_m3s / flow.max_m3snumberYesPumped-flow bounds, in m³/s (methodology §4).

Stage-varying bounds are supplied via constraints/pumping_bounds.parquet, which accepts sparse (pumping_station_id, stage_id) rows carrying min_m3s and/or max_m3s, optionally narrowed to one block via an optional block_id column; absent rows fall back to the base entity values.

system/energy_contracts.json — Contract Registry

Section titled “system/energy_contracts.json — Contract Registry”
{
"contracts": [
{
"id": 0,
"name": "Importação Argentina",
"bus_id": 5,
"operational_start_date": "2005-01-01",
"type": "import",
"price_per_mwh": 200.0,
"limits": { "min_mw": 0.0, "max_mw": 1000.0 }
},
{
"id": 1,
"name": "Exportação Uruguai",
"bus_id": 6,
"operational_start_date": "2020-04-01",
"type": "export",
"entry_stage_id": 1,
"exit_stage_id": 60,
"price_per_mwh": -150.0,
"limits": { "min_mw": 0.0, "max_mw": 500.0 }
}
]
}
FieldTypeRequiredDescription
idintegerYesUnique identifier.
namestringYesHuman-readable contract name.
bus_idintegerYesBus at which the contracted power is injected or withdrawn.
operational_start_datestring (ISO-8601 date)YesCalendar date (YYYY-MM-DD) the contract enters the registry’s operational history — see Shared Operational Start Date above.
typestringYes"import" or "export" — sets the load-balance sign and the import/export set membership from methodology §3.
price_per_mwhnumberYesContract price, in dollars per MWh. Positive for imports (cost), negative for exports (revenue) — see methodology §3.
limits.min_mw / limits.max_mwnumberYesDispatch bounds, in MW. A non-zero min_mw is the hard take-or-pay floor.

Stage-varying bounds and price are supplied via constraints/contract_bounds.parquet, which accepts sparse (contract_id, stage_id) rows carrying any combination of min_mw, max_mw, and price_per_mwh, optionally narrowed to one block via an optional block_id column; absent rows fall back to the base entity values. Unlike thermal cost_per_mwh, contract price_per_mwh is block-eligible — a study’s simulation cost path honors a per-block price override — because commitment is a stage-level decision for a thermal but a contract’s price can legitimately vary within a stage.

  • Notation Conventions — variable and set definitions (gjg_j, fnf_n, χc\chi_c, pyp_y, τk\tau_k, ζk\zeta_k)
  • System Element Modeling Overview — element descriptions, decision variables, and connections
  • LP Formulation — how equipment constraints integrate into the assembled LP; hydro water balance (§4), generation constraints (§6), variable bounds (§8)
  • Hydro Production Function Models — hydro-specific production function constraints (constant, FPHA, linearized head)
  • Penalty System — penalty taxonomy, regularization vs. violation costs
  • Block Formulation Variants — block structure within which equipment constraints operate
  • SDDP Algorithm — iterative algorithm that solves stage subproblems containing these equipment constraints