Skip to content

LP Formulation

This chapter presents the complete stage subproblem LP for the Novomodelo SDDP solver: the objective function with its cost taxonomy, all constraint families, slack/penalty variables, and the Benders cut interface to the future cost function. It uses the parallel blocks formulation by default.

Reading order: System Element Modeling Overview → Equipment-Specific Formulations → this chapter → State Augmentation

For what each physical element represents and its decision variables, see System Element Modeling Overview. For variable naming conventions and index sets, see Notation Conventions.

Given its incoming state, the stage LP minimizes the objective below subject to the rows of every family it carries, each written in its parallel-stage form with the symbols of its owning section, ⋯\cdots standing for terms that section writes in full; the in-transit bucket and commitment-ring families are named in one line.

min⁡  Cresource+Crecourse+Cviolation+Cregularization+dt→t+1 θzh=bh,m(t)+∑ℓ=1Phψm(t),ℓ ah,ℓ+σm(t) εt(1)vh−vhin−ζ zh+∑k∈Kζk(qh,k+sh,k+uh,k)+⋯=−ζ rh(2)∑h∈Hbgh,b,k+∑j∈Tbgj,k+⋯+∑s∈Sbδb,k,s−ϵb,k=Db,k(4)gh,b,k≤λh,b(γ0m+γvm vhavg+γsm sh,k)+γqm qh,b,k(5)eh−γv,hev2 (vhin+vh)−σhe++σhe−=γ0,hev(6)qh,k+sh,k+σh,ko−≥O‾h(7)qh,k+sh,k−σh,ko+≤Oˉh(8)qh,b,k+σh,b,kq−≥Q‾h,b(9)gh,b,k+σh,b,kg−≥G‾h,b(10)vh+σhfill≥Vttarget(11)vh+σhv−≥V‾h(12)in-transit bucket definition; commitment fish, deposit and carry(3), (13), (14), (15)b‾g≤∑eγg,e xe≤bˉg(16)θ≥β0,i+∑h∈Hβi,hv vh+⋯(17)\begin{aligned} & \min\; C^{resource} + C^{recourse} + C^{violation} + C^{regularization} + d_{t \to t+1}\,\theta \\ & z_h = b_{h,m(t)} + \sum_{\ell=1}^{P_h} \psi_{m(t),\ell} \, a_{h,\ell} + \sigma_{m(t)} \, \varepsilon_t \quad (1) \\ & v_h - v^{in}_h - \zeta \, z_h + \sum_{k \in \mathcal{K}} \zeta_k \big( q_{h,k} + s_{h,k} + u_{h,k} \big) + \cdots = -\zeta \, r_h \quad (2) \\ & \sum_{h \in \mathcal{H}_b} g_{h,b,k} + \sum_{j \in \mathcal{T}_b} g_{j,k} + \cdots + \sum_{s \in \mathcal{S}_b} \delta_{b,k,s} - \epsilon_{b,k} = D_{b,k} \quad (4) \\ & g_{h,b,k} \leq \lambda_{h,b} \big( \gamma^m_0 + \gamma^m_v \, v^{avg}_h + \gamma^m_s \, s_{h,k} \big) + \gamma^m_q \, q_{h,b,k} \quad (5) \\ & e_h - \tfrac{\gamma^{ev}_{v,h}}{2} \, \big( v^{in}_h + v_h \big) - \sigma^{e+}_h + \sigma^{e-}_h = \gamma^{ev}_{0,h} \quad (6) \\ & q_{h,k} + s_{h,k} + \sigma^{o-}_{h,k} \geq \underline{O}_h \quad (7) \\ & q_{h,k} + s_{h,k} - \sigma^{o+}_{h,k} \leq \bar{O}_h \quad (8) \\ & q_{h,b,k} + \sigma^{q-}_{h,b,k} \geq \underline{Q}_{h,b} \quad (9) \\ & g_{h,b,k} + \sigma^{g-}_{h,b,k} \geq \underline{G}_{h,b} \quad (10) \\ & v_h + \sigma^{fill}_h \geq V^{\text{target}}_t \quad (11) \\ & v_h + \sigma^{v-}_h \geq \underline{V}_h \quad (12) \\ & \text{in-transit bucket definition; commitment fish, deposit and carry} \quad (3),\ (13),\ (14),\ (15) \\ & \underline{b}_g \leq \sum_{e} \gamma_{g,e} \, x_e \leq \bar{b}_g \quad (16) \\ & \theta \geq \beta_{0,i} + \sum_{h \in \mathcal{H}} \beta^v_{i,h} \, v_h + \cdots \quad (17) \end{aligned}

The table lists the families in the order of the stage LP’s rows, with N=∣H∣N = \lvert\mathcal{H}\rvert hydros and ∣K∣\lvert\mathcal{K}\rvert blocks at the stage.

No.Row familyRows per stageSection
1Realized-inflow definitionNN, one per hydro§5
2Water balanceNN on a parallel stage, N∣K∣N \lvert\mathcal{K}\rvert on a chronological stage§4
3In-transit bucket definitionone per receiving plant and maturity lag reached at the stageState Augmentation — Bucket definition rows
4Load balance∣B∣ ∣K∣\lvert\mathcal{B}\rvert \, \lvert\mathcal{K}\rvert, one per bus and block§3
5FPHA planesone per plane, (hydro, bus) cell and block of each FPHA hydro; none while the hydro is PreFilling or in its filling window§6
6Evaporationone per evaporating hydro on a parallel stage, one per evaporating hydro and block on a chronological stage; none while the hydro is PreFilling§4 Evaporation Row
7Minimum outflowN∣K∣N \lvert\mathcal{K}\rvert, one per hydro and block§7
8Maximum outflowN∣K∣N \lvert\mathcal{K}\rvert, one per hydro and block§7
9Minimum turbined flowone per (hydro, bus) cell and block§8
10Minimum generationone per (hydro, bus) cell and block§6
11Filling floorone per filling hydro at each stage of its filling window§8
12Soft dead-volume floorone per filling hydro at each stage from its entry stage on§8
13Commitment fishone per anticipated thermal whose delivery at the stage was decided at an earlier stage or before the studyState Augmentation — Ring Rows
14Commitment depositone per anticipated thermal that decides at the stage a commitment for a delivery inside the study or the declared post-study calendar at which the plant is commissionedState Augmentation — Ring Rows
15Commitment carryone per ring slot holding a later delivery, decided at an earlier stage or before the study, inside the study or the declared post-study calendarState Augmentation — Ring Rows
16Generic constraintsone per active bound and block it covers, or one stage-level row for a block-independent bound§10
17Benders cutsone per active cut; one per resident cut under Dynamic Cut Selection§11

Incoming-state pinning and the lifecycle pins are column bounds, not rows: each incoming-state column is fixed at its trial value by equal bounds (State Augmentation §2), and each column a lifecycle phase freezes is fixed by its bounds (§8).

Penalty System owns the cost categories, their units, their priority ordering, the ordering checks and the resolution cascade; the table lists the cost symbols of each category as this page writes them.

CategoryCost symbols
Thermal and contract costscjthc^{th}_j, ccctrc^{ctr}_c
Recourse slackscb,sdefc^{def}_{b,s}, cbexcc^{exc}_b, chinfc^{inf}_h
Constraint violation penaltieschsv−c^{sv-}_h, chfillc^{fill}_h, chtv−c^{tv-}_h, chov−c^{ov-}_h, chov+c^{ov+}_h, chgv−c^{gv-}_h, chev+c^{ev+}_h, chev−c^{ev-}_h, chwv+c^{wv+}_h, chwv−c^{wv-}_h
Regularization costschspillc^{spill}_h, chtcc^{tc}_h, chdivc^{div}_h, crcurtc^{curt}_r, cnexchc^{exch}_n

Pumping carries no cost term; the power a pumping station draws enters the load balance of its bus (§3, Equipment-Specific Formulations §4).

The complete stage objective is:

min⁡  Cresource⏟thermal, contracts+Crecourse⏟deficit, excess, inflow non-negativity+Cviolation⏟constraint slacks+Cregularization⏟spillage, exchange, ...+dt→t+1 θ\min \; \underbrace{C^{resource}}_{\text{thermal, contracts}} + \underbrace{C^{recourse}}_{\text{deficit, excess, inflow non-negativity}} + \underbrace{C^{violation}}_{\text{constraint slacks}} + \underbrace{C^{regularization}}_{\text{spillage, exchange, ...}} + d_{t \to t+1} \, \theta

where the per-block terms are weighted by the block duration τk\tau_k and the stage-level terms, namely the storage-violation slacks on end-of-stage storage, the inflow non-negativity and withdrawal slacks, the evaporation slacks of a parallel stage, the slacks of a stage-level generic constraint (§10) and the commitment cost of an anticipated thermal, apply once per stage (§2); the per-block part of each component is:

Ccomponent=∑k∈Kτk⋅(cost terms for component)C^{component} = \sum_{k \in \mathcal{K}} \tau_k \cdot (\text{cost terms for component})

The coefficient of θ\theta is the one-step discount factor dt→t+1d_{t \to t+1} of Discount Rate Formulation, equal to 1 at a zero rate.

min⁡∑k∈Kτk[∑j∈Tcjthgj,k⏟Thermal cost+∑c∈Cccctrχc,k⏟Contract cost\min \sum_{k \in \mathcal{K}} \tau_k \Bigg[ \underbrace{\sum_{j \in \mathcal{T}} c^{th}_j g_{j,k}}_{\text{Thermal cost}} + \underbrace{\sum_{c \in \mathcal{C}} c^{ctr}_c \chi_{c,k}}_{\text{Contract cost}} +∑b∈B∑s∈Sbcb,sdefδb,k,s⏟Deficit (piecewise)+∑b∈Bcbexcϵb,k⏟Excess + \underbrace{\sum_{b \in \mathcal{B}} \sum_{s \in \mathcal{S}_b} c^{def}_{b,s} \delta_{b,k,s}}_{\text{Deficit (piecewise)}} + \underbrace{\sum_{b \in \mathcal{B}} c^{exc}_b \epsilon_{b,k}}_{\text{Excess}} +∑h∈Hchspillsh,k+∑h∈Hchtcqh,k+∑h∈Hchdivuh,k⏟Hydro regularization + \underbrace{\sum_{h \in \mathcal{H}} c^{spill}_h s_{h,k} + \sum_{h \in \mathcal{H}} c^{tc}_h q_{h,k} + \sum_{h \in \mathcal{H}} c^{div}_h u_{h,k}}_{\text{Hydro regularization}} −∑r∈Rcrcurt gr,knc⏟Curtailment (regularization)+∑n∈Lcnexch(fn,k++fn,k−)⏟Exchange (regularization) - \underbrace{\sum_{r \in \mathcal{R}} c^{curt}_r \, g^{nc}_{r,k}}_{\text{Curtailment (regularization)}} + \underbrace{\sum_{n \in \mathcal{L}} c^{exch}_n (f^+_{n,k} + f^-_{n,k})}_{\text{Exchange (regularization)}} +Constraint violation penalties⏟See §9] + \underbrace{\text{Constraint violation penalties}}_{\text{See }\S 9} \Bigg] +∑h∈H[chsv−σhv−+chfillσhfill]⏟Storage violations (not per-block)+∑h∈HchinfHt σhinf⏟Inflow non-negativity (not per-block)+  dt→t+1 θ+ \underbrace{\sum_{h \in \mathcal{H}} \Big[ c^{sv-}_h \sigma^{v-}_h + c^{fill}_h \sigma^{fill}_h \Big]}_{\text{Storage violations (not per-block)}} + \underbrace{\sum_{h \in \mathcal{H}} c^{inf}_h H_t \, \sigma^{inf}_h}_{\text{Inflow non-negativity (not per-block)}} + \; d_{t \to t+1} \, \theta

Wherever the inflow non-negativity slack enters the water balance of hh (§4), it supplies water to that balance at chinfHtc^{inf}_h H_t per ζ\zeta hm³, so on a parallel stage the value of one more hm³ of water at hh is at most chinfHt/ζc^{inf}_h H_t / \zeta. On a chronological stage the slack enters every block row with its ζk\zeta_k, and the bound holds for the duration-weighted average, with weights wkw_k, of the block values, not for each block. The bound is one-sided: it caps the value of water, not a negative value of surplus water. Penalty System — Penalty Priority Ordering converts the inflow cost to $/MWh and orders it below deficit, so that the LP takes slack water before it sheds load. On a parallel stage the storage cut coefficient βhv\beta^v_h read at the stage inherits the bound, −βhv≤chinfHt/ζ-\beta^v_h \le c^{inf}_h H_t / \zeta, where vhinv^{in}_h enters only the water balance; an FPHA plane, the evaporation row or a generic constraint that reads vhinv^{in}_h adds its own terms.

Anticipated thermals (State Augmentation §5) add one stage-level commitment-cost term per plant deciding a commitment at the stage — not a per-block term: the commitment column priced at the plant’s unit cost and hours of the delivery stage and discounted from it (State Augmentation — Objective contributions). Their per-block generation carries no thermal cost at a stage where the plant has a fish row (State Augmentation — Ring Rows), which ties it to the committed rate; at any other stage it is priced like any thermal.

Each hydro plant hh is partitioned into one or more (hydro, bus) cells — one cell per distinct bus among the plant’s declared unit groups (see System Element Modeling Overview §5). Bh\mathcal{B}_h denotes the set of buses hosting one of hh‘s cells; Hb\mathcal{H}_b denotes the hydros with a cell at bus bb (i.e. b∈Bhb \in \mathcal{B}_h). gh,b,kg_{h,b,k} is the generation of hydro hh‘s cell at bus bb, block kk — the quantity that actually injects at bb. A plant whose groups share a single bus has ∣Bh∣=1|\mathcal{B}_h| = 1, and gh,b,kg_{h,b,k} collapses to the single-cell gh,kg_{h,k} used everywhere else in this chapter.

For each bus b∈Bb \in \mathcal{B} and block k∈Kk \in \mathcal{K}:

∑h∈Hbgh,b,k+∑j∈Tbgj,k+∑r∈Rbgr,knc+∑c∈Cbimpχc,k\sum_{h \in \mathcal{H}_b} g_{h,b,k} + \sum_{j \in \mathcal{T}_b} g_{j,k} + \sum_{r \in \mathcal{R}_b} g^{nc}_{r,k} + \sum_{c \in \mathcal{C}^{imp}_b} \chi_{c,k} +∑n:target=bfn,k++∑n:source=bfn,k−+ \sum_{n: \text{target}=b} f^+_{n,k} + \sum_{n: \text{source}=b} f^-_{n,k} −∑n:source=bfn,k+−∑n:target=bfn,k−−∑c∈Cbexpχc,k−∑y∈Pbρypumppy,k+∑s∈Sbδb,k,s−ϵb,k=Db,k- \sum_{n: \text{source}=b} f^+_{n,k} - \sum_{n: \text{target}=b} f^-_{n,k} - \sum_{c \in \mathcal{C}^{exp}_b} \chi_{c,k} - \sum_{y \in \mathcal{P}_b} \rho^{pump}_y p_{y,k} + \sum_{s \in \mathcal{S}_b} \delta_{b,k,s} - \epsilon_{b,k} = D_{b,k}

Dual variable: πb,klb\pi^{lb}_{b,k} (marginal cost of energy at bus bb, block kk, in $/MW; divide by τk\tau_k for $/MWh — see Variable Units Convention)

For the physical meaning of each element in the balance, see System Element Modeling Overview.

Every hydro h∈Hh \in \mathcal{H} has one water-balance row on a parallel stage and one per block on a chronological stage (Block Formulation Variants); every LP variable is on the left-hand side.

vh−vhin−bh,1in−ζ(zh+σhinf+σhw−−σhw+−eh)+∑k∈Kζk(qh,k+sh,k+uh,k−∑h′∈Uhνh′,t,0 (qh′,k+sh′,k)−∑h′: div=huh′,k+∑y: src=hpy,k−∑y: dest=hpy,k)=−ζ rh\begin{aligned} & v_h - v^{in}_h - b^{\mathrm{in}}_{h,1} - \zeta \big( z_h + \sigma^{inf}_h + \sigma^{w-}_h - \sigma^{w+}_h - e_h \big) \\ & \quad + \sum_{k \in \mathcal{K}} \zeta_k \Big( q_{h,k} + s_{h,k} + u_{h,k} - \sum_{h' \in \mathcal{U}_h} \nu_{h',t,0} \, (q_{h',k} + s_{h',k}) - \sum_{h':\,\text{div}=h} u_{h',k} \\ & \qquad + \sum_{y:\,\text{src}=h} p_{y,k} - \sum_{y:\,\text{dest}=h} p_{y,k} \Big) = -\zeta \, r_h \end{aligned}

For each block k∈Kk \in \mathcal{K}, with vh,0=vhinv_{h,0} = v^{in}_h and vh,∣K∣=vhv_{h,\lvert\mathcal{K}\rvert} = v_h:

vh,k−vh,k−1−ϕh,k bh,1in−ζk(zh+σhinf+σhw−−σhw+−eh,k)+ζk(qh,k+sh,k+uh,k)−∑h′∈Uh∑k′≤kνh′,tk′→k ζk′ (qh′,k′+sh′,k′)−ζk∑h′: div=huh′,k+ζk(∑y: src=hpy,k−∑y: dest=hpy,k)=−ζk rh\begin{aligned} & v_{h,k} - v_{h,k-1} - \phi_{h,k} \, b^{\mathrm{in}}_{h,1} - \zeta_k \big( z_h + \sigma^{inf}_h + \sigma^{w-}_h - \sigma^{w+}_h - e_{h,k} \big) + \zeta_k \big( q_{h,k} + s_{h,k} + u_{h,k} \big) \\ & \quad - \sum_{h' \in \mathcal{U}_h} \sum_{k' \le k} \nu^{k' \to k}_{h',t} \, \zeta_{k'} \, (q_{h',k'} + s_{h',k'}) - \zeta_k \sum_{h':\,\text{div}=h} u_{h',k} + \zeta_k \Big( \sum_{y:\,\text{src}=h} p_{y,k} - \sum_{y:\,\text{dest}=h} p_{y,k} \Big) = -\zeta_k \, r_h \end{aligned}
  • vhv_h and vhinv^{in}_h = outgoing and incoming storage; vhinv^{in}_h is pinned to the trial value by its column bounds (State Augmentation §2). On a chronological stage vh,kv_{h,k} is the storage at the end of block kk
  • bh,1inb^{\mathrm{in}}_{h,1} = in-transit volume maturing at this stage on the travel-time arcs into hh, in hm³ and therefore outside the conversion (State Augmentation §6); a chronological stage spreads it over its blocks by the arrival density ϕh,k\phi_{h,k}, ∑kϕh,k=1\sum_k \phi_{h,k} = 1. Absent when no travel-time arc enters hh
  • zh=ahz_h = a_h = realized incremental inflow (§5), so block kk of a chronological stage receives the share wk ζ σm(t) εtw_k\,\zeta\,\sigma_{m(t)}\,\varepsilon_t of the innovation
  • σhinf\sigma^{inf}_h = inflow non-negativity slack, present under the penalty-based methods, which adds water (see Inflow Non-Negativity Solution Methods)
  • rhr_h, σhw−\sigma^{w-}_h, σhw+\sigma^{w+}_h = signed stage-level withdrawal target (rh<0r_h < 0 adds water) and its stage-level under- and over-delivery slacks; the realized withdrawal is Rh=rh−σhw−+σhw+R_h = r_h - \sigma^{w-}_h + \sigma^{w+}_h, with the slack caps in §9
  • ehe_h / eh,ke_{h,k} = signed net evaporation of a hydro with an evaporation model (a negative value is net rainfall on the lake and adds water): one stage-level column on a parallel stage of any block count, one per block on a chronological stage, each a bounded column tied to storage by its evaporation row (see Evaporation Row)
  • qh,k=∑b∈Bhqh,b,kq_{h,k} = \sum_{b \in \mathcal{B}_h} q_{h,b,k}, sh,ks_{h,k}, uh,ku_{h,k} = the plant’s own turbined, spilled and diverted flow in block kk; every (hydro, bus) cell’s turbined column carries the same coefficient, and spillage and diversion are single per-plant columns
  • Upstream release: only the turbined and spilled flow of each h′∈Uhh' \in \mathcal{U}_h reaches hh; a plant’s diverted flow reaches only its diversion target (the div\text{div} sum)
  • νh′,t,0=(Ht−Δh′tt)+/Ht\nu_{h',t,0} = (H_t - \Delta^{tt}_{h'})^+ / H_t = same-stage share of the release of h′h' on a parallel stage, with HtH_t the duration of stage tt and Δh′tt\Delta^{tt}_{h'} the travel time of h′h'‘s main cascade arc (00 when none is declared, so the share is 11); the remaining 1−νh′,t,01 - \nu_{h',t,0} is deposited into the in-transit buckets (State Augmentation §6)
  • νh′,tk′→k\nu^{k' \to k}_{h',t} = within-stage routing share on a chronological stage: the fraction of h′h'‘s block-k′k' release that reaches hh‘s block-kk row in the same stage, k≥k′k \ge k'. Without a travel time νh′,tk→k=1\nu^{k \to k}_{h',t} = 1 and νh′,tk′→k=0\nu^{k' \to k}_{h',t} = 0 for k′<kk' < k; the rest of the release is deposited into the buckets (State Augmentation §6)
  • py,kp_{y,k} = pumped flow of station yy, out of its source plant’s row and into its destination plant’s row (see Equipment-Specific Formulations); on a parallel stage every block’s pumped flow enters the single stage row
  • ζ=0.0036∑k∈Kτk\zeta = 0.0036 \sum_{k \in \mathcal{K}} \tau_k and ζk=0.0036 τk=wk ζ\zeta_k = 0.0036\,\tau_k = w_k\,\zeta = stage and block flow-to-volume conversions, with τk\tau_k the duration of block kk in hours and wk=τk/∑k′∈Kτk′w_k = \tau_k / \sum_{k' \in \mathcal{K}} \tau_{k'} its weight, so ∑kζk=ζ\sum_k \zeta_k = \zeta

A PreFilling hydro (see Lifecycle Phases for the phase) has the frozen identity row vh−vhin=0v_h - v^{in}_h = 0, and on a chronological stage vh,k−vh,k−1=0v_{h,k} - v_{h,k-1} = 0 per block, with right-hand side 00. Its local inflow zhz_h, the releases of its upstream plants and the flows diverted into it enter the row of the first non-PreFilling plant downstream with the coefficients they would carry on the PreFilling plant’s own row (−ζ-\zeta on zhz_h on a parallel stage and −ζk-\zeta_k on each block row of a chronological stage, −ζk-\zeta_k on every block-kk flow) and no travel-time share, and its withdrawal target moves to that plant’s right-hand side. Its maturing in-transit volume bh,1inb^{\mathrm{in}}_{h,1} enters the same row, with coefficient −1-1 on a parallel stage and −ϕh,k-\phi_{h,k} on block kk‘s row of a chronological stage, ϕh,k\phi_{h,k} being the PreFilling plant’s own arrival density. With no such plant downstream the water leaves the system.

Summing a chronological stage’s block rows over k∈Kk \in \mathcal{K} telescopes the storage terms to vh−vhinv_h - v^{in}_h, returns bh,1inb^{\mathrm{in}}_{h,1} (∑kϕh,k=1\sum_k \phi_{h,k} = 1), and returns the parallel coefficients of zhz_h, σhinf\sigma^{inf}_h, σhw−\sigma^{w-}_h, σhw+\sigma^{w+}_h and the right-hand side (∑kζk=ζ\sum_k \zeta_k = \zeta); the per-block flows keep their ζk\zeta_k. The sum has the parallel row’s form with two mode differences: ∑kζk eh,k\sum_k \zeta_k\,e_{h,k} in place of ζ eh\zeta\,e_h, and each upstream block-k′k' release carrying its own same-stage share ∑k≥k′νh′,tk′→k\sum_{k \ge k'} \nu^{k' \to k}_{h',t} in place of νh′,t,0\nu_{h',t,0}, whose duration-weighted average over k′k' is νh′,t,0\nu_{h',t,0}: ∑k′∈Kwk′∑k≥k′νh′,tk′→k=νh′,t,0\sum_{k' \in \mathcal{K}} w_{k'} \sum_{k \ge k'} \nu^{k' \to k}_{h',t} = \nu_{h',t,0}. With one block, or no travel time on the arc, the shares coincide.

Dual variable: πhwb\pi^{wb}_h for the parallel row; on a chronological stage each block row has its own dual πh,kwb\pi^{wb}_{h,k} (water value — captures the marginal value of incoming storage as seen through the hydro balance, but is not used directly as a cut coefficient; the cut coefficient comes from the reduced cost of the pinned incoming-storage column, see State Augmentation §2 and Cut Management)

Every hydro with an evaporation model has an evaporation row that ties its net evaporation to storage. A parallel stage, of any block count, has one row per such hydro, on the stage’s incoming and outgoing storage:

eh−γv,hev2 (vhin+vh)−σhe++σhe−=γ0,heve_h - \tfrac{\gamma^{ev}_{v,h}}{2} \, \big( v^{in}_h + v_h \big) - \sigma^{e+}_h + \sigma^{e-}_h = \gamma^{ev}_{0,h}

A chronological stage has one row per block k∈Kk \in \mathcal{K}, on the block’s own storages, with vh,0=vhinv_{h,0} = v^{in}_h and vh,∣K∣=vhv_{h,\lvert\mathcal{K}\rvert} = v_h:

eh,k−γv,hev2 (vh,k−1+vh,k)−σh,ke++σh,ke−=γ0,heve_{h,k} - \tfrac{\gamma^{ev}_{v,h}}{2} \, \big( v_{h,k-1} + v_{h,k} \big) - \sigma^{e+}_{h,k} + \sigma^{e-}_{h,k} = \gamma^{ev}_{0,h}
  • γv,hev\gamma^{ev}_{v,h} and γ0,hev\gamma^{ev}_{0,h} = slope and intercept, at a reference volume, of the tangent of the evaporation flux of hh: the evaporation coefficient of the stage’s calendar month times the reservoir surface area from its area–volume curve, converted to a monthly-average rate in m³/s. Both are recomputed at every stage; the row’s target is the tangent evaluated at the average of the two storages the row reads. The flux, and so ehe_h or eh,ke_{h,k}, can be negative (net rainfall on the lake)
  • ehe_h / eh,ke_{h,k} = net evaporation (Row Terms above), bounded symmetrically about zero by a fixed safety margin times ∣γ0,hev+γv,hevVˉh∣\lvert \gamma^{ev}_{0,h} + \gamma^{ev}_{v,h} \bar{V}_h \rvert, the magnitude of the target at the maximum storage Vˉh\bar{V}_h, recomputed at every stage; it is not a free column. It enters the parallel water-balance row as ζ eh\zeta \, e_h and the block-kk row of a chronological stage as ζk eh,k\zeta_k \, e_{h,k}
  • σhe+\sigma^{e+}_h / σh,ke+\sigma^{e+}_{h,k} and σhe−\sigma^{e-}_h / σh,ke−\sigma^{e-}_{h,k} = evaporation above and below the target, priced at chev+c^{ev+}_h and chev−c^{ev-}_h: the row sets the evaporation to the target plus σe+\sigma^{e+} minus σe−\sigma^{e-}. On a parallel stage the one pair is priced over the stage hours HtH_t, on a chronological stage each block’s pair over its duration τk\tau_k (§9)

With one block the parallel and chronological forms coincide, rows and pricing alike. A PreFilling hydro has no evaporation row.

For each hydro h∈Hh \in \mathcal{H}, the LP includes an auxiliary variable zhz_h representing the total realized inflow (m³/s) at the current stage. These variables are defined by equality constraints that combine the deterministic base, lag contributions, and stochastic noise:

zh=bh,m(t)+∑ℓ=1Phψm(t),ℓ⋅ah,ℓ+σm(t)⋅εtz_h = b_{h,m(t)} + \sum_{\ell=1}^{P_h} \psi_{m(t),\ell} \cdot a_{h,\ell} + \sigma_{m(t)} \cdot \varepsilon_t

where:

  • zhz_h = LP variable representing the realized inflow for hydro hh (free column, zero cost)
  • bh,m(t)b_{h,m(t)} = deterministic base (precomputed from seasonal means and AR coefficients — see PAR(p) Inflow Model §2.4)
  • ψm(t),ℓ\psi_{m(t),\ell} = original-unit AR coefficients (constraint matrix entries, set once at LP construction); for a hydro with the annual component the sum runs over every lag slot (ℓ≤Pmax⁡\ell \le P^{\max}) and each of these coefficients also carries the annual term (PAR(p) Inflow Model §7.4)
  • ah,ℓa_{h,\ell} = LP variables for lagged inflows (state variables, fixed by State Augmentation §4)
  • σm(t)⋅εt\sigma_{m(t)} \cdot \varepsilon_t = noise innovation (patched into the constraint RHS per scenario)

The z-inflow variable zhz_h then enters the water balance constraint (§4) in place of the raw inflow term aha_h, and its primal value after solving gives the realized inflow for reporting and simulation extraction.

The z-inflow columns sit between the leading state columns and the incoming storage columns, and their constraint rows form the first equality block (LP Layout and Scaling §1). The RHS is patched per scenario with bh,m(t)+σm(t)⋅εtb_{h,m(t)} + \sigma_{m(t)} \cdot \varepsilon_t, where εt\varepsilon_t is the effective noise (possibly clamped for inflow non-negativity — see Inflow Non-Negativity Solution Methods). They are not pinned state columns and carry no cut coefficient of their own; the cut row multiplies the lag-1 coefficient by zhz_h.

Constraint count: NN total constraints, where N=∣H∣N = |\mathcal{H}| is the number of hydros, whatever their lifecycle phase.

Novomodelo supports two production models, in increasing order of complexity. A third model name, linearized head, is a reserved alias that resolves to constant productivity in every phase — see Hydro Production Function Models §3. The model can vary by stage or season per hydro.

Both models are evaluated per cell (h,b)(h, b) (§3) rather than per plant; for a single-cell plant the per-cell constraint is the plant-level constraint.

Constant Productivity Model (for each cell (h,b)(h, b) of hydro h∈Hconsth \in \mathcal{H}^{const}, block kk):

gh,b,k=ρh⋅qh,b,kg_{h,b,k} = \rho_h \cdot q_{h,b,k}

Constant-productivity hydros carry no separate generation column: gh,b,kg_{h,b,k} is this direct multiple of the cell’s own turbined-flow column, so a cell’s generation bound is enforced by folding it into that column’s bound (§8) rather than by a bound on gh,b,kg_{h,b,k} itself.

FPHA Model (for each plane m∈Mhm \in \mathcal{M}_h, cell (h,b)(h, b) of hydro h∈Hfphah \in \mathcal{H}^{fpha}, block kk):

gh,b,k≤λh,b(γ0m+γvm⋅vhavg+γsm⋅sh,k)+γqm⋅qh,b,kg_{h,b,k} \leq \lambda_{h,b} \big( \gamma^m_0 + \gamma^m_v \cdot v^{avg}_h + \gamma^m_s \cdot s_{h,k} \big) + \gamma^m_q \cdot q_{h,b,k}

where vhavg=(vhin+vh)/2v^{avg}_h = (v^{in}_h + v_h)/2 is the average storage during the stage, with vhinv^{in}_h being the incoming storage LP variable (State Augmentation §2) and vhv_h the end-of-stage storage on a parallel stage; on a chronological stage the row of block kk reads that block’s own average (vh,k−1+vh,k)/2(v_{h,k-1} + v_{h,k})/2 (Block Formulation Variants §2.4) — a single plant-level quantity shared by every cell, since storage is not partitioned. λh,b\lambda_{h,b} is cell (h,b)(h,b)‘s apportionment share of plant hh‘s declared turbine capacity,

λh,b=∑u ∈ (h,b)Qˉu∑u ∈ hQˉu\lambda_{h,b} = \frac{\sum_{u \,\in\, (h,b)} \bar{Q}_u}{\sum_{u \,\in\, h} \bar{Q}_u}

(the ratio of the cell’s own unit groups’ declared maximum turbined flow to the plant’s total, 00 when the plant’s total is 00), so that ∑b∈Bhλh,b=1\sum_{b \in \mathcal{B}_h} \lambda_{h,b} = 1 for every plant with positive declared turbine capacity. Only the plane’s flow-independent part — the intercept γ0m\gamma^m_0, the storage term, and the spillage term — is apportioned by λh,b\lambda_{h,b}; the flow coefficient γqm\gamma^m_q stays on the cell’s own qh,b,kq_{h,b,k} unscaled, because it alone is homogeneous in the cell partition (summing the per-cell rows at fixed vhavgv^{avg}_h, sh,ks_{h,k}, and ∑bqh,b,k\sum_b q_{h,b,k} recovers the plant-level bound this replaces). A single-cell plant with positive declared turbine capacity has λh,b=1\lambda_{h,b} = 1 exactly, so its row is the plant-level row.

For a plant with positive declared turbine capacity the shares sum to 11, so the per-cell rows of each plane sum to that plane’s plant-level row (above). A sum of minima is at most the minimum of the sums, so at the same vhavgv^{avg}_h, sh,ks_{h,k} and total turbined flow the cap the per-cell rows put on the plant’s generation ∑b∈Bhgh,b,k\sum_{b \in \mathcal{B}_h} g_{h,b,k}, on the left, is at most the plant-level envelope, the minimum over the planes of the plant-level rows, on the right:

∑b∈Bhmin⁡m(λh,b(γ0m+γvmvhavg+γsmsh,k)+γqmqh,b,k)  ≤  min⁡m(γ0m+γvmvhavg+γsmsh,k+γqm∑bqh,b,k)\sum_{b \in \mathcal{B}_h} \min_{m} \Big( \lambda_{h,b}\big(\gamma^m_0 + \gamma^m_v v^{avg}_h + \gamma^m_s s_{h,k}\big) + \gamma^m_q q_{h,b,k} \Big) \;\le\; \min_{m} \Big( \gamma^m_0 + \gamma^m_v v^{avg}_h + \gamma^m_s s_{h,k} + \gamma^m_q \sum_{b} q_{h,b,k} \Big)

Equality holds when the same plane attains the minimum in every cell, in particular when the cells turbine in proportion to their shares λh,b\lambda_{h,b}. For λh,b>0\lambda_{h,b} > 0 the minimum of cell (h,b)(h,b) is λh,b\lambda_{h,b} times the plant-level envelope at the flow qh,b,k/λh,bq_{h,b,k}/\lambda_{h,b}, so when every share is positive the display is Jensen’s inequality for the concave envelope with the weights λh,b\lambda_{h,b}. A cell with λh,b=0\lambda_{h,b} = 0 keeps only its γqm qh,b,k\gamma^m_q \, q_{h,b,k} term, and the display still holds by the sum-of-minima argument.

Generation Bounds (per cell (h,b)(h, b), block kk — the FPHA generation column’s own bound; a constant-productivity cell has no such column, so its generation cap is folded into the turbined-flow bound below instead):

G‾h,b−σh,b,kg−≤gh,b,k≤Gˉh,b\underline{G}_{h,b} - \sigma^{g-}_{h,b,k} \leq g_{h,b,k} \leq \bar{G}_{h,b} G‾h,b=∑u ∈ (h,b)G‾u,Gˉh,b=min⁡ ⁣(∑u ∈ (h,b)Gˉu,  Gˉh)\underline{G}_{h,b} = \sum_{u \,\in\, (h,b)} \underline{G}_u, \qquad \bar{G}_{h,b} = \min\!\Big( \sum_{u \,\in\, (h,b)} \bar{G}_u,\ \ \bar{G}_h \Big)

Generation bounds are user-defined (declared per unit group, not derived from turbined flow). The lower bound is soft, with one slack σh,b,kg−\sigma^{g-}_{h,b,k} per cell — priced at the plant’s own penalty chgv−c^{gv-}_h at full magnitude on every cell of a split plant, never divided by cell count; a constant-productivity cell’s floor couples this same slack to ρh⋅qh,b,k\rho_h \cdot q_{h,b,k} rather than to a generation column. The upper bound’s plain sum over the cell’s own unit groups closes against the plant’s own resolved maximum Gˉh\bar{G}_h — a bounds override may never raise a cell above it. See System Element Modeling Overview §5. Matches the per-cell σh,b,kg−\sigma^{g-}_{h,b,k} defined in Notation Conventions §4.3.

For details on the FPHA construction and production function model variants, see Hydro Production Function Models.

Outflow Definition (per hydro hh, block kk):

oh,k=qh,k+sh,ko_{h,k} = q_{h,k} + s_{h,k}

Outflow Bounds (with slacks for soft enforcement):

O‾h−σh,ko−≤oh,k≤Oˉh+σh,ko+\underline{O}_h - \sigma^{o-}_{h,k} \leq o_{h,k} \leq \bar{O}_h + \sigma^{o+}_{h,k}

8. Variable Bounds and Minimum Constraints

Section titled “8. Variable Bounds and Minimum Constraints”
V‾h≤vh≤Vˉh\underline{V}_h \leq v_h \leq \bar{V}_h

The dead volume V‾h\underline{V}_h is a hard lower bound for every hydro except two cases: a hydro with a filling configuration has a floor of 00 in every phase, the per-stage filling floor below taking its place while it fills, and from its entry stage on the dead volume returns as the soft floor vh+σhv−≥V‾hv_h + \sigma^{v-}_h \geq \underline{V}_h, the only storage-below-minimum slack of the LP, priced above deficit; a hydro without a filling configuration has a floor of 00 while PreFilling, where its frozen identity (§4) holds the storage at its incoming value. On a chronological stage the block-end storages vh,kv_{h,k} carry the same column bounds, and the soft floor applies to the end-of-stage storage vhv_h only. The upper bound is hard; excess water leaves through spillage.

Filling floors (for filling hydros, at every filling stage tt, from the filling start stage up to, not including, the entry stage):

vh+σhfill≥Vttarget,Vttarget=min⁡ ⁣(V‾h,L−∑t′=t+1Lζt′ ratet′, V‾h,t)v_h + \sigma^{fill}_h \geq V^{\text{target}}_t, \qquad V^{\text{target}}_t = \min\!\Big( \underline{V}_{h,L} - \sum_{t'=t+1}^{L} \zeta_{t'} \, \text{rate}_{t'},\ \underline{V}_{h,t} \Big)

V‾h,t\underline{V}_{h,t} is the dead volume in force at stage tt (a stage may override it), LL is the last filling stage, the stage before the entry stage, and ratet′\text{rate}_{t'} the minimum accumulation rate of stage t′t', which ζt′\zeta_{t'} converts into hm³. The floor at stage tt is the dead volume of stage LL minus the accumulation the schedule still owes after tt, never above the dead volume of stage tt itself; it reaches V‾h,L\underline{V}_{h,L} at LL. Every filling stage carries its floor. The slack σhfill\sigma^{fill}_h is priced at chfillc^{fill}_h, which Novomodelo expects below deficit (Penalty System — Penalty Ordering Validation checks it as given). See Penalty System §6.

Turbined Flow Bounds (per cell (h,b)(h, b), block kk)

Section titled “Turbined Flow Bounds (per cell (h,b)(h, b)(h,b), block kkk)”
Q‾h,b−σh,b,kq−≤qh,b,k≤Qˉh,b\underline{Q}_{h,b} - \sigma^{q-}_{h,b,k} \leq q_{h,b,k} \leq \bar{Q}_{h,b}

The lower bound is soft, with one slack σh,b,kq−\sigma^{q-}_{h,b,k} per cell, priced at the plant’s own penalty chtv−c^{tv-}_h at full magnitude on every cell — never divided by cell count; it is the plain sum of the cell’s own unit groups’ resolved minimum turbined flow, Q‾h,b=∑u ∈ (h,b)Q‾u\underline{Q}_{h,b} = \sum_{u \,\in\, (h,b)} \underline{Q}_u. The upper bound is hard and closes against the plant’s own resolved maximum, never raised above it:

Qˉh,b=min⁡ ⁣(∑u ∈ (h,b)fold(u),  Qˉh)\bar{Q}_{h,b} = \min\!\left( \sum_{u \,\in\, (h,b)} \mathrm{fold}(u),\ \ \bar{Q}_h \right)

where fold(u)=Qˉu\mathrm{fold}(u) = \bar{Q}_u for an FPHA hydro (turbined flow and generation are independent columns there) and fold(u)=min⁡(Qˉu, Gˉu/ρh)\mathrm{fold}(u) = \min(\bar{Q}_u,\ \bar{G}_u / \rho_h) for a constant-productivity hydro — each group’s own flow cap and MW-implied flow cap must be folded before summing across the cell, since min⁡\min does not distribute over a sum of groups that bind on different sides. This is the mechanism that enforces a constant-productivity cell’s generation cap (§6): there is no separate generation column to bound directly. Matches the per-cell σh,b,kq−\sigma^{q-}_{h,b,k} defined in Notation Conventions §4.3.

Diversion Flow Bounds (per hydro hh, block kk)

Section titled “Diversion Flow Bounds (per hydro hhh, block kkk)”
0≤uh,k≤Uˉh0 \leq u_{h,k} \leq \bar{U}_h

The lower bound is 00 unless a minimum diversion flow is set for the stage or for the block, which replaces it. Both bounds are hard. Diversion cost is a regularization term (see §1), not a violation penalty.

Spillage Bounds (per hydro hh, block kk)

Section titled “Spillage Bounds (per hydro hhh, block kkk)”

Spillage sh,ks_{h,k} is bounded below by 00 and above by +∞+\infty unless a minimum or maximum spillage is set for the stage or for the block, which replaces the bound. Both bounds are hard. While the hydro is PreFilling, spillage is fixed at [0,0][0, 0] (Lifecycle Phases).

Pumping Flow Bounds (per station yy, block kk)

Section titled “Pumping Flow Bounds (per station yyy, block kkk)”
P‾y≤py,k≤Pˉy\underline{P}_y \leq p_{y,k} \leq \bar{P}_y

Both bounds are hard.

At each stage a hydro is in exactly one lifecycle phase, set by its commissioning window and, for a filling hydro (a hydro with a filling configuration), by its filling start; every other entity is either in service (inside its commissioning window) or out of service.

PhaseApplies atStorage row and floorTurbined flow and generationSpillageDiversionInflow, upstream releases and withdrawalOperational floors
PreFillingA hydro without a filling configuration: every stage outside its commissioning window, before its entry stage or from its exit stage on. A filling hydro: every stage before its filling startFrozen identity vh−vhin=0v_h - v^{in}_h = 0, per block on a chronological stage, with right-hand side 00 (§4); floor 00; no evaporation rowqh,b,kq_{h,b,k} fixed at [0,0][0, 0] on every cell; no FPHA rows and no generation columnsh,ks_{h,k} fixed at [0,0][0, 0], whatever its boundsuh,ku_{h,k} fixed at [0,0][0, 0]The local inflow, the releases of the upstream plants, the flows diverted in and the in-transit volume maturing into the plant enter the row of the first non-PreFilling plant downstream, and the withdrawal target moves to that plant’s right-hand side; with no such plant the water leaves the systemThe minimum-outflow, minimum-turbined-flow and minimum-generation rows are present; with turbined flow, spillage and generation at 00, a positive minimum falls wholly on its slack, at its penalty (§9)
FillingA filling hydro: every stage from its filling start up to, but not including, its entry stageWater balance of §4, with the evaporation row of an evaporating hydro; floor 00 and the per-stage filling floor vh+σhfill≥Vttargetv_h + \sigma^{fill}_h \geq V^{\text{target}}_t of §8 Storage Boundsqh,b,kq_{h,b,k} fixed at [0,0][0, 0] on every cell; no FPHA rows and no generation columnWithin its boundsuh,ku_{h,k} fixed at [0,0][0, 0]On the plant’s own row (§4)The same rows are present; a positive minimum turbined flow or minimum generation falls wholly on its slack, and spillage can meet a positive minimum outflow
OperatingA hydro without a filling configuration: every stage inside its commissioning window, so every stage when it declares none. A filling hydro: every stage from its entry stage on; it has no exit stageWater balance of §4, with the evaporation row of an evaporating hydro; the hard dead-volume floor V‾h\underline{V}_h for a hydro without a filling configuration, and for a filling hydro floor 00 with the soft dead-volume floor vh+σhv−≥V‾hv_h + \sigma^{v-}_h \geq \underline{V}_hTurbined flow within the Turbined Flow Bounds above; generation by the production model of §6Within its boundsWithin the Diversion Flow Bounds above for a hydro without a filling configuration; fixed at [0,0][0, 0] for a filling hydroOn the plant’s own row (§4)The same rows are present; the minimum turbined flow is met by the cell’s turbined flow, the minimum generation by its generation, and the minimum outflow by turbined flow plus spillage, with the slack covering any shortfall at its penalty (§9)

Outside its commissioning window a thermal’s generation, a line’s flow in each direction, a non-controllable source’s generation, a pumping station’s pumped flow and a contract’s power are fixed at [0,0][0, 0] in every block; an anticipated thermal’s commitment is gated by its delivery stage in addition, and is opened only when the plant is in service at that delivery stage, whatever its status at the decision stage (State Augmentation §5).

The per-block constraint violation penalties in the objective (referenced from §2) are:

∑k∈Kτk∑h∈H[chtv−∑b∈Bhσh,b,kq−+chov−σh,ko−+chov+σh,ko++chgv−∑b∈Bhσh,b,kg−]\sum_{k \in \mathcal{K}} \tau_k \sum_{h \in \mathcal{H}} \Big[ c^{tv-}_h \sum_{b \in \mathcal{B}_h} \sigma^{q-}_{h,b,k} + c^{ov-}_h \sigma^{o-}_{h,k} + c^{ov+}_h \sigma^{o+}_{h,k} + c^{gv-}_h \sum_{b \in \mathcal{B}_h} \sigma^{g-}_{h,b,k} \Big]

The turbined- and generation-minimum slacks are per cell — each of a split plant’s cells carries its own slack column and its own row, priced at the plant’s penalty at full magnitude, never divided across cells. The outflow slacks stay per plant: outflow has no per-cell column to attribute a floor to. The evaporation slacks are per plant too, stage-level on a parallel stage and per block on a chronological stage (Evaporation Row).

+∑h∈HHt⋅(chwv−σhw−+chwv+σhw+)+ \sum_{h \in \mathcal{H}} H_t \cdot \bigl(c^{wv-}_h \sigma^{w-}_h + c^{wv+}_h \sigma^{w+}_h\bigr) +∑h∈HHt⋅(chev+σhe++chev−σhe−)    (parallel stage),+∑k∈Kτk∑h∈H(chev+σh,ke++chev−σh,ke−)    (chronological stage)+ \sum_{h \in \mathcal{H}} H_t \cdot \bigl(c^{ev+}_h \sigma^{e+}_h + c^{ev-}_h \sigma^{e-}_h\bigr) \;\; \text{(parallel stage)}, \qquad + \sum_{k \in \mathcal{K}} \tau_k \sum_{h \in \mathcal{H}} \bigl(c^{ev+}_h \sigma^{e+}_{h,k} + c^{ev-}_h \sigma^{e-}_{h,k}\bigr) \;\; \text{(chronological stage)}

where Ht=∑kτkH_t = \sum_k \tau_k is the total stage duration in hours, and the evaporation sums run over the hydros that model evaporation. Withdrawal violation slacks (σhw−\sigma^{w-}_h, σhw+\sigma^{w+}_h) are stage-level (not per-block) and bidirectional: σhw−\sigma^{w-}_h penalizes under-delivery (the realized withdrawal Rh=rh−σhw−+σhw+R_h = r_h - \sigma^{w-}_h + \sigma^{w+}_h falls short of the target), and σhw+\sigma^{w+}_h penalizes over-delivery. The withdrawal target rhr_h is signed (§4), and the slack bounds ensure the realized withdrawal cannot flip sign relative to the target:

  • rh>0r_h > 0 (scheduled removal): σhw−≤rh\sigma^{w-}_h \leq r_h (under-delivery slack capped at the target magnitude; floors Rh≥0R_h \geq 0), σhw+\sigma^{w+}_h unbounded.
  • rh<0r_h < 0 (scheduled inter-basin return/addition): σhw+≤∣rh∣\sigma^{w+}_h \leq |r_h| (over-delivery slack capped at ∣rh∣|r_h|; caps Rh≤0R_h \leq 0), σhw−\sigma^{w-}_h unbounded.
  • rh=0r_h = 0: both slacks are pinned to zero.

This cap guards a degenerate case: an unbounded under-delivery slack would let a run-of-river plant “un-withdraw” past its target and inject phantom water into the reservoir.

User-defined linear constraints (per constraint g∈Gg \in \mathcal{G}) take a two-sided interval form:

b‾g  ≤  ∑eγg,e⋅xe  ≤  bˉg\underline{b}_g \;\leq\; \sum_{e} \gamma_{g,e} \cdot x_e \;\leq\; \bar{b}_g

where xex_e is a quantity of the stage LP that the generic-constraint variable catalog exposes — a column such as storage, a flow, a generation or a deficit, or a fixed combination of columns such as the inflow of a hydro (below).

A term on the turbined flow or the generation of a plant split across buses may address one (hydro, bus) cell or all of the plant’s cells at once (§3).

A term may also address a plant’s useful volume vh−Vhminv_h - V^{min}_h — its storage above the physical minimum VhminV^{min}_h. It is realised on the storage variable itself by shifting both endpoints by the same amount,

b‾g≤γg,e (vh−Vhmin)+⋯≤bˉg  ⟺  b‾g+γg,eVhmin≤γg,e vh+⋯≤bˉg+γg,eVhmin,\underline{b}_g \le \gamma_{g,e}\,(v_h - V^{min}_h) + \dots \le \bar{b}_g \iff \underline{b}_g + \gamma_{g,e}V^{min}_h \le \gamma_{g,e}\,v_h + \dots \le \bar{b}_g + \gamma_{g,e}V^{min}_h,

so it adds no LP variable; an absent endpoint stays absent, and a negative γg,e\gamma_{g,e} lowers both endpoints.

Either endpoint, b‾g\underline{b}_g or bˉg\bar{b}_g, may be absent — never both — with an absent side read as the corresponding infinity. This single interval subsumes every shape a constraint can take: a present b‾g\underline{b}_g alone is a floor, a present bˉg\bar{b}_g alone is a cap, b‾g=bˉg\underline{b}_g = \bar{b}_g is an equality, and both present together bound a two-sided band. A constraint’s shape — floor, cap, equality or band — follows from which endpoints are present and, when both are, whether they coincide.

The coefficients γg,e\gamma_{g,e} may be either literal numeric values or named scalar parameters, exactly as for any other coefficient in the LP.

Each endpoint composes independently from up to two pieces, summed together: a numeric base that may itself vary by stage (and, for the finest-grained parameter kind below, by block within the stage), plus an optional affine remainder layered on top of it — a constant plus a weighted sum of named scalar parameters. An endpoint carrying neither piece is simply absent; a base alone, a remainder alone, or their sum are all valid, so a single endpoint can combine a scheduled floor or cap that already varies by stage with a further parameter-driven adjustment on top.

A named scalar parameter takes one of five kinds: one value for every stage; one per stage; one per season; a quantity computed from a plant’s geometry and energy conversion, §5 of Hydro Production Function Models; or one per stage and block.

Resolution happens once at LP-build time, so the LP coefficients and endpoints are still numeric at solve time — the parameter mechanism does not introduce LP-variable coupling between constraints. Methodology relevance: it lets the corpus express ramping limits, capacity caps, and operator-imposed quotas that vary by stage, season, or block without authoring a separate constraint per stage. Coefficient and endpoint values can therefore be stage- and block-varying constants, not just literal numbers.

A constraint may optionally carry a slack, penalized per unit of violation, so the row relaxes at a cost instead of forcing infeasibility. A one-sided row — only b‾g\underline{b}_g or only bˉg\bar{b}_g present — carries a single slack column relaxing that one endpoint. A two-sided row — both b‾g\underline{b}_g and bˉg\bar{b}_g present — carries two independent slack columns, σggc+\sigma^{gc+}_g relaxing the floor upward and σggc−\sigma^{gc-}_g relaxing the cap downward: a single column cannot represent both “how far below the floor” and “how far above the cap” without conflating the two directions.

The reported violation is the signed net σggc+−σggc−\sigma^{gc+}_g - \sigma^{gc-}_g: positive when the row sits below its floor, negative when it sits above its cap, matching the sign of the underlying deviation rather than reading as an unsigned magnitude. The objective charges both columns, σggc++σggc−\sigma^{gc+}_g + \sigma^{gc-}_g, which coincides with the net’s magnitude whenever only one direction is active — the case at any optimum, since paying for both directions on the same row at once is strictly dominated by paying for neither of the excess.

Row materialization: a constraint bound declared for every block of the stage over a block-independent expression — one whose every term references a stock variable (incoming storage vhinv^{in}_h, outgoing storage vhv_h, evaporation (the stage-level ehe_h on a parallel stage, a named block’s eh,ke_{h,k} on a chronological stage), or an anticipated-thermal commitment) — is materialized as a single stage-level row priced by the total stage hours HtH_t, since per-block rows would be identical, provided its coefficients and endpoints are block-independent too: a coefficient or an endpoint drawn from the per-(stage, block) parameter kind above varies within the stage, which forces the per-block row set even when the expression alone would otherwise qualify for the collapse. A bound declared for every block on a block-level expression, or a bound declared for one block, still produces one row per relevant block. This is an LP row-count optimization that is cost- and parity-neutral.

See Generic Constraints for the authoring grammar, the activation grid, and the per-file field tables this formulation implements.

A hydro-inflow term reads the inflow Ih,kI_{h,k} of hydro hh in block kk, a rate in m³/s built from LP columns:

Ih,k=zh+∑h′: div=huh′,k+∑h′∈Uhoh′→h,karr+ϕh,kζk bh,1in+∑h′∈Uhpre(t)(zh′+∑h′′: div=h′uh′′,k+∑h′′∈Uh′oh′′,k+ϕh′,kζk bh′,1in)I_{h,k} = z_h + \sum_{h':\,\text{div}=h} u_{h',k} + \sum_{h' \in \mathcal{U}_h} o^{arr}_{h' \to h,k} + \frac{\phi_{h,k}}{\zeta_k} \, b^{\mathrm{in}}_{h,1} + \sum_{h' \in \mathcal{U}^{pre}_h(t)} \Big( z_{h'} + \sum_{h'':\,\text{div}=h'} u_{h'',k} + \sum_{h'' \in \mathcal{U}_{h'}} o_{h'',k} + \frac{\phi_{h',k}}{\zeta_k} \, b^{\mathrm{in}}_{h',1} \Big)

Each upstream release is credited to block kk by the travel time of its arc:

oh′→h,karr={oh′,karc without a travel timeνh′,t,0 oh′,ktravel-time arc, parallel stage∑k′≤kνh′,tk′→k ζk′ζk oh′,k′travel-time arc, chronological stageo^{arr}_{h' \to h,k} = \begin{cases} o_{h',k} & \text{arc without a travel time} \\ \nu_{h',t,0} \, o_{h',k} & \text{travel-time arc, parallel stage} \\ \sum_{k' \le k} \nu^{k' \to k}_{h',t} \, \dfrac{\zeta_{k'}}{\zeta_k} \, o_{h',k'} & \text{travel-time arc, chronological stage} \end{cases}
  • oh′,k=qh′,k+sh′,ko_{h',k} = q_{h',k} + s_{h',k} = turbined plus spilled outflow of h′h' (§7), credited with the same-stage share νh′,t,0\nu_{h',t,0} or the within-stage shares νh′,tk′→k\nu^{k' \to k}_{h',t} of §4
  • ϕh,k\phi_{h,k} = arrival density of State Augmentation §6 on a chronological stage and τk/Ht\tau_k / H_t on a parallel stage: the share of the maturing in-transit volume bh,1inb^{\mathrm{in}}_{h,1} that arrives in block kk, which the division by ζk\zeta_k turns into a rate; the maturing term is absent when no travel-time arc enters hh
  • Uhpre(t)\mathcal{U}^{pre}_h(t) = PreFilling plants at stage tt whose first non-PreFilling downstream plant is hh (see PreFilling Pass-Through); the local inflow of each, the flows diverted into it and the releases of its upstream plants enter whole, with no travel-time share, and its maturing in-transit volume enters as the rate ϕh′,kζkbh′,1in\frac{\phi_{h',k}}{\zeta_k} b^{\mathrm{in}}_{h',1}, by its own arrival density

The term excludes pumping, the inflow non-negativity slack, the plant’s own outflows, evaporation and withdrawal.

For a hydro that is not PreFilling at stage tt, the term mirrors the inflow terms of its water balance (§4) listed below. On a chronological stage ζkIh,k\zeta_k I_{h,k} equals the inflow terms of block kk‘s water-balance row: the local inflow, the flows diverted in, the credited releases, the maturing transit volume and the PreFilling pass-through. On a parallel stage ∑kζkIh,k\sum_{k} \zeta_k I_{h,k} equals the same terms of the stage row, and only the duration-weighted stage total matches the balance; the split across blocks is a convention.

For a PreFilling hydro the term reads the same columns, with Uhpre(t)\mathcal{U}^{pre}_h(t) empty, and they match no row of hh, whose row is the frozen identity (PreFilling Pass-Through): its local inflow, the flows diverted into it, the releases of its upstream plants and its maturing transit volume enter the row of the first non-PreFilling plant downstream, or leave the system when there is none (Delayed-arrival water-balance entry).

Ih,kI_{h,k} reads per-block columns, so a bound without a block expands to one row per block (see row materialization in §10).

For each cut ii in the stage LP, that is each active cut or, under Dynamic Cut Selection, each cut resident in the solve (Cut Management §8):

θ≥β0,i+∑h∈Hβi,hv⋅vh+∑h∈H(βi,h,1lag⋅zh+∑ℓ=2Pmax⁡βi,h,ℓlag⋅ah,ℓ−1)\theta \geq \beta_{0,i} + \sum_{h \in \mathcal{H}} \beta^v_{i,h} \cdot v_h + \sum_{h \in \mathcal{H}} \Big( \beta^{lag}_{i,h,1} \cdot z_h + \sum_{\ell=2}^{P^{\max}} \beta^{lag}_{i,h,\ell} \cdot a_{h,\ell-1} \Big)

where:

  • β0,i\beta_{0,i} = cut intercept (RHS)
  • βi,hv\beta^v_{i,h} = coefficient for storage state variable, present when the cut keeps the storage dimensions
  • βi,h,ℓlag\beta^{lag}_{i,h,\ell} = coefficient for the inflow lag ℓ\ell of the next stage’s incoming state, multiplied by the realized inflow zhz_h for ℓ=1\ell = 1 and by the incoming lag ah,ℓ−1a_{h,\ell-1} for ℓ≥2\ell \ge 2; present when the cut keeps the inflow-lag dimensions

When anticipated thermals are present, the cut carries one coefficient per commitment-ring slot (State Augmentation §5), read from the same reduced-cost mechanism; when travel-time arcs are present, it likewise carries one coefficient per in-transit bucket dimension (State Augmentation §6). Unlike the storage and lag coefficients, the ring-slot and bucket coefficients are always part of the cut projection (State Augmentation §7).

The future-cost variable θ\theta has lower bound 00 at every stage, which presumes Vt+1≥0V_{t+1} \ge 0. Stage objectives can be negative: export contracts earn revenue (§2), and the curtailment term of §2 is a reward: it is never positive, and it is constant for a must-run source. Where Vt+1V_{t+1} is negative the bound lies above it, so the cut approximation need not lie below Vt+1V_{t+1} and the lower bound is not guaranteed. At the last stage of the finite horizon, with no terminal boundary loaded, θ\theta is 00 (VT+1=0V_{T+1} = 0); with a terminal boundary loaded, θ\theta keeps the floor and the imported cuts bound it from below, so the terminal function is the larger of 00 and the imported cuts.

Cuts live in an append-only pool at stable slot indices: every cut ever generated is retained for the lifetime of the run, and only the active subset is baked into each iteration’s stage template. Deactivation excludes a cut from each iteration’s stage-template rebake rather than mutating any row; the persistent lower-bound LP is append-only (its rows are never removed, so the lower bound stays monotone). Slot indices stay stable, so reactivation — re-baking the cut into the template at the same slot — is exact. Dynamic Cut Selection deactivates no cut itself: each solve loads a resident subset of the active cuts and grows it with the omitted candidate cuts its solution violates. A per-stage cap on the number of active cuts, when set, deactivates cuts once it is exceeded, under every selection method.

For cut coefficient derivation, aggregation, and selection strategies, see Cut Management.