Hydro Production Function Models
Purpose
Section titled “Purpose”This chapter defines the hydro generation constraint models supported by Novomodelo, which relate turbined flow and reservoir storage to electrical output. Two production functions shape dispatch: constant productivity and FPHA. A third model name — linearized head — is a reserved alias for constant productivity (see §3). The choice between constant productivity and FPHA trades off accuracy vs. computational cost, and can vary by stage per hydro.
All decision variables use rate units (MW, m³/s) — see the Variable Units Convention in System Element Modeling Overview. For variable definitions see Notation Conventions; for LP integration see LP Formulation; for hydro element descriptions see System Element Modeling Overview.
1. Constant Productivity Model
Section titled “1. Constant Productivity Model”The simplest model assumes a linear relationship:
where (MW per m³/s) is the hydro productivity for stage :
with:
- = turbine efficiency (a dimensionless factor in )
- = reference net head (meters), evaluated at the reference storage level and varying by stage
Per-stage productivity: the productivity coefficient is authored per (hydro, stage) rather than per plant. A plant can therefore carry a stage-varying constant productivity — useful when the reference head differs between near-term and far-future stages of the same study, or when the constant model is being used as a coarse approximation that needs different operating points across the horizon. Section 5.1 describes how that per-(hydro, stage) value is supplied.
LP treatment: constant productivity adds no generation column and no production row: the turbined-flow column of each (hydro, bus) cell (§2.9) enters its bus’s load balance with coefficient in every block, so holds by construction. Simple and fast, but ignores head variation with storage within a stage.
Data requirements: a per-stage productivity scalar per hydro plant. No geometry or hyperplane data needed.
2. FPHA (Approximate Hydroelectric Production Function)
Section titled “2. FPHA (Approximate Hydroelectric Production Function)”For accurate modeling of hydroelectric generation, FPHA (Approximate Hydroelectric Production Function) captures the nonlinear relationship between storage, flow, spillage, and generation through a piecewise-linear approximation. The approach follows the piecewise-linear hydro production model of Diniz & Maceira (2008); that model is four-dimensional (forebay/head, turbined flow, and tailrace/spillage), whereas Novomodelo fits a reduced variant over storage and turbined flow at spillage = 0, capturing the spillage effect through the per-plane lateral-flow secant of §2.7 rather than an explicit spillage axis.
2.1 Notation
Section titled “2.1 Notation”This section uses the notation of the LP Formulation; for equivalent terms in other planning tools, see the Glossary.
2.2 Exact Production Function
Section titled “2.2 Exact Production Function”The exact hydroelectric production function relates generation to the operating state:
where:
- = reservoir storage volume (hm³)
- = turbined flow (m³/s)
- = spillage flow (m³/s)
- = net head (m), clamped to
- = turbine efficiency (section 1), constant per plant; the factor (MW·s/m⁴) converts hydraulic power to electrical power
The net head is computed as:
clamped to , where:
- = forebay (upstream reservoir) level as function of storage
- = tailrace (downstream channel) level as function of total outflow
- = hydraulic head losses (from the factor or constant model)
Why linearization is needed: is nonlinear in due to the bilinear product , nonlinear topology functions and , and flow-dependent hydraulic losses. For LP formulation, Novomodelo approximates with a set of linear hyperplanes.
2.3 Topology Functions
Section titled “2.3 Topology Functions”Novomodelo uses tabular data with linear interpolation for the forebay curve — more transparent and easier to validate against surveyed data than polynomial fits.
The upstream water level is read from the plant’s volume-height curve. For storage where :
The downstream water level depends on total outflow. Three representations are supported:
Polynomial model:
Piecewise-linear model: tabular breakpoints with linear interpolation between points.
Piecewise-quartic families (exact tailrace): an optional per-plant tailrace table provides piecewise degree-4 polynomial segments (evaluated via Horner’s method), grouped into backwater families keyed by the downstream reservoir’s reference forebay level — see §2.3.1.
Total downstream flow in LP: (turbined flow + spillage). For FPHA fitting, spillage is fixed at when building the generation cloud; the lateral-flow secant (§2.7) captures the spillage correction per plane.
2.3.1 Piecewise-Quartic Tailrace Families (Backwater Coupling)
Section titled “2.3.1 Piecewise-Quartic Tailrace Families (Backwater Coupling)”When plants are hydraulically close, plant ‘s tailrace level depends on the downstream reservoir’s forebay. An optional per-plant tailrace table provides piecewise-quartic tailrace segments: the flow domain is split into contiguous segments, each a degree-4 polynomial
evaluated via Horner’s method for numerical stability. Consecutive segments are contiguous in outflow and agree in level at their shared boundary (C0 continuity) to calibration precision (Implementation notes, “Computed-FPHA fitting behavior”). These segments are grouped into backwater families keyed by the downstream reservoir’s reference forebay level (, in metres). At fitting time, the active family is linearly interpolated by the downstream plant’s resolved stage reference level — clamped to the calibrated level range, never extrapolated. Plants with a single keyless family (no backwater coupling) evaluate that family directly regardless of the downstream level. Plants without a tailrace table use the entity-level polynomial or piecewise-linear tailrace.
Hydraulic Losses
Section titled “Hydraulic Losses hlossh_{loss}hloss”Two models are supported:
Factor model — proportional to gross head:
where is a small dimensionless fraction of the gross head.
Constant model — fixed head loss:
2.4 Productivity
Section titled “2.4 Productivity”The exact production function converts hydraulic power to electrical power with the factor (MW per (m³/s · m)), so in MW. Novomodelo uses constant efficiency per plant. The specific productivity , in the same unit, is a separate plant input that the exact production function and the FPHA fit do not use.
An FPHA plant has no single scalar productivity . The fitted planes follow from and the topology functions alone, while the equivalent productivity at the reference operating point is derived from a specific productivity supplied for the plant (overridable per stage) and the VHA geometry; the derivation is documented in section 5.1.
2.5 FPHA Hyperplanes
Section titled “2.5 FPHA Hyperplanes”The FPHA approximation replaces the nonlinear production function with a set of linear hyperplanes that form a concave approximation of the exact surface (an outer approximation of the capped cloud before the correction of §2.6.3). Each hyperplane defines an upper bound on generation:
The plane is fitted once per plant (§2.1–§2.8); a plant split across buses evaluates it per (hydro, bus) cell at LP-integration time, apportioning its flow-independent terms by each cell’s share of the plant’s turbine capacity — see §2.9 for the exact per-cell form.
Physical interpretation of coefficients:
| Coefficient | Sign | Meaning |
|---|---|---|
| — | Intercept (MW at zero storage, flow, spillage); its sign is not validated | |
| ≥ 0 | Higher storage → higher forebay → more generation | |
| ≥ 0 (> 0 on at least one plane per stage for precomputed planes) | More turbined flow → more generation | |
| ≤ 0 | More spillage → higher tailrace → less net head |
Source of hyperplanes: planes are either pre-computed (supplied with the case) or computed from topology data during preprocessing. The computed fit is described in §2.6.
2.6 Computed FPHA Fitting Pipeline
Section titled “2.6 Computed FPHA Fitting Pipeline”The computed-FPHA path produces hyperplanes from topology data in four stages: convex-hull fit → correction → lateral-flow secant → optional plane reduction. The fit is resolved per production-model entry (per season or stage range), so planes can differ across the horizon for the same plant. Results are expanded to per-stage hyperplane rows.
2.6.1 Grid and Cloud
Section titled “2.6.1 Grid and Cloud”The fitter evaluates the exact production function on a uniform two-dimensional grid over the fitting window, with spillage and lateral flow . The grid has:
- Volume axis: a configurable number of uniformly spaced values spanning the fitting window . The window lies within the plant’s forebay storage range (the full range unless narrowed) and is distinct from the reference volume (§5.1), which fixes an operating point, not this window. The point counts and the window are set on the Configure tab.
- Flow axis: a configurable number of uniformly spaced values spanning . The axis starts at , where generation is zero; this zero-flow column anchors the lower closure of the cloud and eliminates the need for any synthetic closing point.
Each cloud point is capped at the plant’s installed capacity . Spillage is not a cloud dimension — it is fixed at zero throughout.
Plants without turbine capacity: when the flow axis collapses onto , where generation is zero, so the cloud carries no production and no plane survives the hull. Such a plant is modelled with zero productivity at every stage instead of a plane set; the implementation notes give the capacity threshold and how the plant is reported.
Run-of-river plants: when the plant has a single fitting volume (, or a single volume sample requested), two nearby volume samples are synthesized to keep the 3-D hull non-degenerate (Implementation notes, “Run-of-river support”). The resulting is snapped to exactly 0 when its magnitude is negligible, enforcing the run-of-river semantics ().
Determinism: the cloud points and the hull output are canonically sorted, so the fitted hyperplanes are bit-identical regardless of input ordering and MPI rank count.
2.6.2 Convex Hull
Section titled “2.6.2 Convex Hull”The 3-D convex hull of the cloud is computed via the qhull library. The upper-envelope facets — those whose outward normal has a positive generation component — are selected. Each is read as . Before the correction of §2.6.3, the result is a concave outer approximation (the smallest concave function lying above at the capped cloud points); non-concave regions of fall inside the hull and their facets drop out. Near-parallel coplanar facets arising from hull triangulation are deduplicated by exact coefficient comparison.
2.6.3 Least-Squares Correction
Section titled “2.6.3 Least-Squares kFPHAk_{FPHA}kFPHA Correction”The raw hull envelope is optimistic where is non-concave and pessimistic where it is concave. A single scalar corrects the bias by minimising the mean-squared error between and the capped production over the spill grid:
Key properties:
- The regression uses the pointwise minimum over the raw hull planes as , because the LP applies planes as for every , so the binding cap is the minimum, not the maximum.
- The regression is over the spill grid only — adding a spillage axis would pull toward the larger-deviation spill region and degrade the no-spill operating region.
- scales the whole affine function ( alike), not just the intercept. It therefore differs from the factor of precomputed planes, which scales the intercept only.
- may be greater or less than 1 (an MSE balance, not a one-sided shrink). Validation requires . A degenerate denominator (all-zero production) yields the neutral .
- Validation also requires , , .
Fit-quality diagnostic: after the full pipeline, the relative mean-absolute-deviation of the emitted min-envelope vs the capped production over the spill grid is computed. A warning is emitted (in canonical plant/stage order) when it exceeds a threshold (Implementation notes, “Fit-quality warning”) — typically indicating a strongly non-concave surface that no single can track well.
Precomputed input: precomputed planes may carry an intercept factor (Configure tab) that scales each plane’s intercept only (), unlike the whole-affine . The computed path does not use ; its correction plays that role.
The figure below shows a synthetic plant with normalized axes: storage, flow and generation are fractions of , and . It slices the fit at one grid volume, which the example takes as the reference volume of §5.1, and plots five series against turbined flow: the cloud points at that volume; the exact production capped at ; the raw hull , the minimum over the hull planes, which lies on or above every cloud point; the corrected envelope , which rescales the hull by the single scalar of this section; and the dashed line of §5.1, which, as the example sets to , meets the uncapped at , above the capped curve where the capacity binds.
2.7 Lateral-Flow Secant
Section titled “2.7 Lateral-Flow Secant”Spillage raises the tailrace level, lowering net head and generation. The coefficient for each plane is fit by a per-plane 1-D ordinary-least-squares secant of generation vs lateral flow (the plant’s own spillage ) over evenly spaced samples of , evaluated at the plane’s representative (active-maximum) operating point:
where MLT is the long-term mean inflow (m³/s). The representative operating point for each plane is the spill grid point where that plane is the active (tightest) upper bound and attains the largest generation — the operating region the plane actually governs. The secant samples the uncapped production function (not clipped at installed capacity ), so the spillage sensitivity is read from the raw head curve and is not flattened wherever the capacity ceiling binds — unlike the cloud and the regression (§2.6), which both use the capacity-capped output.
Sign: (more lateral flow raises the tailrace, reduces generation). A slope that is non-negative or negligibly negative is snapped to exactly 0 (Implementation notes, “Computed-FPHA fitting behavior”) to protect LP column scaling from near-zero structural coefficients.
Lateral axis: the plant’s own spillage (); upstream releases and post-incremental inflows do not enter it.
2.8 Similar-Hyperplane Reduction
Section titled “2.8 Similar-Hyperplane Reduction”An optional post-fit step merges consecutive near-parallel or near-coincident planes into their mean hyperplane to shrink the LP. Two mutually-exclusive methods are supported:
- Angle: merge consecutive pairs whose normal-vector angle satisfies (strict), with and the tolerance in degrees. Fully deterministic from coefficients.
- Distance: merge consecutive pairs whose normalised mean-squared generation difference , where is the largest value of the exact production (uncapped) over the fitting grid, with the tolerance in percent and EQM estimated over sampled operating points. Uses a deterministically-seeded PRNG (seeded from stable plant/stage/plane-pair identity, never from wall clock or MPI rank), so results are bit-identical across input ordering and rank count.
Origin-plane invariant: the plane through the origin (, generating zero power at zero turbining) is never merged. This guarantees the zero-generation floor.
Reduction is optional and applies only when selected (Configure tab).
2.9 LP Integration
Section titled “2.9 LP Integration”A hydro plant is partitioned into one or more (hydro, bus) cells — one per distinct bus among its declared unit groups (see System Element Modeling Overview §5) — and FPHA is evaluated per cell, not per plant: a plant’s fitted hyperplane set (§2.1–§2.8) is fitted once per plant and consumed by every one of its cells, apportioned as described below. For a single-cell plant the per-cell constraint is the plant-level constraint.
Final FPHA Constraint
Section titled “Final FPHA Constraint”For each cell of a hydro using FPHA, block , and plane :
Every variable sits on the left with its plane coefficient negated, and the apportioned intercept is the row’s upper bound; the row is the cap .
The coefficients are already -scaled — there is no separate pre-scaling step. These are hard constraints — no slack variables.
apportions the plane’s flow-independent part — the intercept , the storage term, and the spillage term — by cell ‘s share of the plant’s declared turbine capacity:
( when the plant’s declared total is ), a study-time constant derived from each unit group’s declared maximum turbined flow — never recomputed per stage or block, and never re-derived from a resolved per-stage override, which would double-count an availability derate already enforced on the flow path by each cell’s own column bounds. It satisfies , and a single-cell plant has exactly. The flow coefficient stays on the cell’s own unscaled — it is the only term homogeneous in the cell partition, which is what makes at fixed , , and recover the plant-level bound this per-cell form replaces.
Average Storage Computation
Section titled “Average Storage Computation”The average storage over the stage:
where is the incoming storage LP variable (pinned to via column bounds — see State Augmentation §2) and is end-of-stage storage — a single plant-level quantity shared by every cell, since storage is not partitioned. Both are LP variables, so appears in every cell’s FPHA constraint with coefficient on the left-hand side, as does , on a parallel stage; on a chronological stage, in block 1’s rows, and in block ‘s. The LP solver automatically accounts for this in the reduced cost of the pinned column.
On a chronological stage the row of block uses that block’s own average storage in place of , with (see Block Formulation Variants).
Generation as Independent Variable
Section titled “Generation as Independent Variable”When using FPHA, the generation variable is not directly computed from turbined flow. Instead:
- Generation is a free LP variable per cell, bounded by , where sums the cell’s own unit groups’ declared maximum generation and closes against the plant’s own resolved maximum (see LP Formulation §6)
- FPHA constraints (one per plane , per cell) provide upper bounds relating generation to storage, flow, and spillage
- Where generation has value, the optimizer raises it until an FPHA plane or a tighter bound such as binds
- At an optimum where generation has value, each cell’s generation lies on one of its FPHA hyperplane facets or on a tighter bound, such as
Key insight: Because minimizing cost includes maximizing hydro generation (which has zero fuel cost), the optimizer naturally pushes generation to the FPHA surface boundary or to a tighter bound such as . Where generation has no value, turbined flow beyond what the planes convert produces nothing, and the turbined cost of section 2.10 decides between turbining that water and spilling it; on a binding plane with , spillage still lowers the plane through (section 2.7), which the LP weighs against that cost.
2.10 Turbined Cost
Section titled “2.10 Turbined Cost”A small regularization cost is charged on the turbined flow of every cell of every hydro, whatever its production model:
Where generation has no value, the LP is indifferent between turbining water without generating from it and spilling it; a cost above the spillage cost, , tips it toward spilling. On a binding plane with , more turbined flow adds no generation, but each unit of spillage lowers the plane by (sections 2.7 and 2.9), so the LP spills that water only where exceeds the value of the generation lost. A hydro using FPHA requires (Penalty System — Category 3).
For the full penalty taxonomy and priority ordering, see Penalty System.
2.11 Impact on Benders Cuts
Section titled “2.11 Impact on Benders Cuts”The FPHA formulation affects water value computation. Because the incoming storage variable appears in every one of the plant’s cells’ FPHA constraints (via , each apportioned by that cell’s , section 2.9) — on a parallel stage; on a chronological stage, in block 1’s rows — the FPHA hyperplane duals of every cell contribute to the marginal value of incoming storage. However, the implementation does not require manually combining duals from the water balance and FPHA constraints. Instead, pinning to by column bounds (see State Augmentation §2) makes its reduced cost capture the total sensitivity automatically — the LP solver propagates every cell’s FPHA contribution through the single shared .
The cut coefficient for storage is simply the reduced cost of the pinned column:
This reduced cost implicitly includes the water balance contribution (that of the row reading : on a parallel stage, on a chronological stage), the FPHA contribution summed over every cell of the plant (, with summed over the blocks whose rows read : every block on a parallel stage, block 1 on a chronological stage), the dual of the evaporation row (LP Formulation — Evaporation Row) weighted by for a hydro with an evaporation model (block 1’s row only on a chronological stage), and any generic constraint contributions — all resolved by the LP solver without explicit dual combination.
For the complete cut coefficient computation, see Cut Management.
Production Model and Cut Validity
Section titled “Production Model and Cut Validity”A production model that varies by stage or season is part of the model the policy is trained on: its cuts lower-approximate that model’s cost-to-go whichever production model each stage uses (Cut Management — when bounds and certificates hold). A policy simulated under a different production model than the one it was trained on is a heuristic, with no bound guarantee: the trained lower bound need not bound the simulated model’s optimal value, and the gap between the two certifies nothing.
3. Linearized Head Model (Reserved — Resolves to Constant Productivity)
Section titled “3. Linearized Head Model (Reserved — Resolves to Constant Productivity)”Linearized head is a reserved production-model name: it is equivalent to constant productivity (§1) in every phase — training (backward and forward passes) and simulation alike — and selecting it applies no head correction and imposes no phase restriction.
Generation is therefore with the plant’s per-(hydro, stage) productivity (§5.1). It still requires the plant’s productivity input.
4. Model Selection Guidelines
Section titled “4. Model Selection Guidelines”Training (Policy Construction)
Section titled “Training (Policy Construction)”The substantive choice during training is between constant productivity and FPHA (linearized head is an alias for the former, §3). That choice trades accuracy against computational cost:
| Scenario | Recommended Model | Rationale |
|---|---|---|
| High-head storage reservoirs | FPHA | Significant head variation |
| Large storage variation plants | FPHA | Operating across wide volume range |
| Run-of-river plants | FPHA or Constant | The hull fit handles run-of-river plants (§2.6.1) |
| Initial algorithm testing | Constant productivity | Fast iteration, debug focus |
| Near-term stages | FPHA | Accuracy for operational decisions |
| Far-future stages | Constant productivity | Computational efficiency |
Simulation (Policy Evaluation)
Section titled “Simulation (Policy Evaluation)”During simulation, constant productivity and FPHA behave as they do in training, and linearized head resolves to constant productivity (§3):
| Scenario | Recommended Model | Rationale |
|---|---|---|
| Plants trained with FPHA | FPHA | Consistency with training model |
| Plants trained with constant, low head variation | Constant productivity | No benefit from head correction |
| Plants selecting linearized head | Constant productivity (equivalent) | Linearized head resolves to the same constant-productivity source (§3); no head correction is applied |
| Post-optimization validation | Compare all models | Verify approximation quality |
Simulating a plant under another production model than the one it was trained with evaluates the policy heuristically (§2.11).
The production model may vary by stage or by season per hydro (Configure tab).
5. Energy-Conversion Quantities
Section titled “5. Energy-Conversion Quantities”The three production models of sections 1–3 describe how generation depends on the operating state. For accounting purposes — natural-inflow energy (ENA), stored reservoir energy (EARM), and per-stage MW/MWh reporting — Novomodelo reduces each plant’s production model to a small set of per-(hydro, stage) scalars produced by two evaluators: one evaluated at a representative reference operating point (§5.1–§5.2), and one averaged over the plant’s physical storage range (§5.3). These scalars are computed once at study setup and reused on every stage of every scenario.
5.1 Equivalent Productivity
Section titled “5.1 Equivalent Productivity”The equivalent productivity (MW per m³/s) is the single-scalar productivity that the plant would carry at the reference operating point . The derivation depends on the plant’s declared generation model, not on the model a stage selects:
| Generation model | derivation |
|---|---|
| Constant productivity | A per-(hydro, stage) numeric value authored by the case (see below). |
| Linearized head | A per-(hydro, stage) numeric value authored by the case, supplied as for constant productivity. |
| FPHA | , where is the net head computed from the VHA geometry (section 2.3) at the reference point and is resolved per stage: a per-stage override, else a per-plant default override, else the plant’s own value. FPHA hydros author no separate scalar — it is derived, and an override is accepted (see below). |
Reference volume: the reference operating volume is declared once per production-model entry (a stage range or a season), either as an absolute volume or as a fraction of the useful volume between and ; when absent a default fraction of the useful volume applies (Configure tab). This single declaration is the source of truth for the derivation above and for the reservoir reference level at which the computed-FPHA piecewise-quartic tailrace families are interpolated (§2.3.1 — a plant’s reference volume sets the downstream forebay level seen by the plant immediately upstream). It does not set the computed-FPHA volume fitting window : that window is configured separately on the Configure tab, defaulting to the plant’s full forebay storage range (§2.6.1).
The reference flow at which both evaluators compute the net head is the plant’s installed turbined-flow capacity.
Authoring: a non-FPHA plant’s equivalent productivity is authored per hydro and stage. An FPHA plant’s equivalent productivity is derived as in the table above and may be overridden; the override replaces the derived value in both evaluators, the reference-point value here and the useful-range mean value of §5.3. A zero equivalent productivity at a stage where the plant does not use the FPHA model is a planned outage: the plant generates nothing at that stage. At a stage that uses the FPHA model, generation follows the fitted planes, not this scalar. The authoring sources, their resolution order and the load-time checks are on the Configure tab.
5.2 Accumulated Cascade Productivity
Section titled “5.2 Accumulated Cascade Productivity”The accumulated productivity (MW per m³/s) is the energy that one m³/s of incremental inflow into plant contributes once it is routed through plant and every plant downstream of along the cascade:
The sum is taken in topological order over the cascade (see System Element Modeling Overview for the cascade topology). Plants with no downstream successors have . The accumulation is per-stage because each summand can vary by stage.
5.3 Useful-Range Mean Productivity
Section titled “5.3 Useful-Range Mean Productivity”The reference-point scalars of §5.1–§5.2 value water at a single operating volume. A quantity that values the whole stored volume — stored energy, or a floor on it — needs a productivity representative of every volume the reservoir can occupy. The second evaluator supplies it by averaging the forebay level over the plant’s physical storage range.
Physical storage range. Each plant carries a physical storage range : a stage-invariant property of the plant (its dead-volume floor and its full-reservoir ceiling). It is distinct from the per-stage operative bounds on the storage variable, which may be tighter at a given stage (for example under a flood-control ceiling). Every quantity in this subsection and in §5.4 reads the physical range, never the operative bounds, so an active operative restriction never truncates a stored-energy value.
Mean forebay level. The forebay level is averaged over the physical range:
Because is the piecewise-linear volume–height curve of §2.3, this integral is evaluated exactly (a composite trapezoid over the curve’s breakpoints), not approximated by quadrature.
Mean equivalent productivity. With the net-head function of §5.1, evaluated from a forebay level and the reference flow :
It is the forebay level that is averaged, not the net head: the tailrace level and the hydraulic losses are then evaluated at exactly as the reference-point evaluator evaluates them. The two evaluators therefore differ only in the forebay level they feed to the same net-head function.
Resolution order. For each (hydro , stage ):
- An explicit equivalent-productivity override replaces both evaluators with the same value, so equals the override.
- Otherwise, a collapsed physical range (), or a plant without volume–height geometry or without a specific productivity , gives : a zero-width range reduces the average to a point evaluation.
- Otherwise, is the integrated value above.
The integrating evaluator is gated by the plant’s geometry, not by its generation model: it applies to any plant with volume–height geometry and a specific productivity, whether its LP production model is FPHA or constant productivity. A non-positive mean net head is rejected at study setup. The collapsed-range rule is how a run-of-river plant falls out of the data: with a collapsed range it behaves identically under either evaluator.
Mean accumulated productivity. follows from the mean own terms by the same downstream recurrence as §5.2:
Maximum stored energy. The energy content of the plant’s full useful volume is
Its unit is the raw product MW/(m³/s) × hm³ — not MWh (no hm³-to-hours factor is applied).
Security-curve identity. A security curve is a per-stage floor on stored energy expressed as a fraction of the maximum stored energy. Written on the outgoing storage with both sides on the mean evaluator,
states “at least a fraction of the useful volume, in energy terms”. The per-plant coefficient cancels only because both sides use the same (mean) evaluator and the same raw unit; mixing the reference-point coefficient on one side with on the other, or converting one side to MWh alone, would not reduce to the useful-volume fraction. How such a row is authored is described in LP Formulation §10.
5.4 Inflow and Storage in Energy Units
Section titled “5.4 Inflow and Storage in Energy Units”and convert hydraulic quantities to energy units that downstream reporting expects:
Incremental inflow energy (MW):
This is the rate-form natural energy inflow in MW. Stagewise energy (MWh) is recovered by multiplying by block duration in hours.
Stored reservoir energy (MWh):
where is the physical minimum storage of §5.3 (not the stage’s operative lower bound) and is the useful-range mean accumulated productivity. The conversion factor converts hm³ to m³ and seconds to hours so that storage in hm³ multiplied by productivity in MW/(m³/s) yields MWh; no stage duration enters, so EARM is independent of stage length.
Stored reservoir energy, power form (MW):
for either the initial or the final value, where the divisor is the stage’s total block hours — the same divisor for every block of the stage.
EARM and ENA use different evaluators on purpose: ENA values an inflow at the plant’s representative operating point, while EARM values the stored volume over the whole range it can occupy.
These quantities do not enter the LP — they are accounting outputs derived from the LP solution. Their methodology relevance is that they make the production model auditable in the same energy units used by the load forecast and the cost objective.
5.5 Why a Scalar Reduction Exists at All
Section titled “5.5 Why a Scalar Reduction Exists at All”The full FPHA production function (section 2) is multi-dimensional and concave; constant productivity is a scalar but per-plant. Neither can be summed across a cascade or scaled by inflow without a reference operating point. The energy-conversion scalars resolve this: each model is reduced to two numbers per (hydro, stage) — one from the reference-point evaluator, one from the useful-range mean evaluator — and those numbers are what the cascade-summation, ENA, EARM, and maximum-stored-energy formulas above can consume uniformly. The LP’s production model does not use these scalars; it continues to enforce the full production model. They are accounting quantities that may also be used as coefficients of user-authored generic constraints, such as the security curve of §5.3 (see LP Formulation §10).
Implementation in Novomodelo
Section titled “Implementation in Novomodelo”The methodology above defines the hydro production models; the tabs below cover how Novomodelo’s software surface configures, feeds, and reports on them.
Novomodelo’s system/hydro_production_models.json file authors the production-model
selection the methodology above describes, and the generation block of
system/hydros.json supplies each plant’s default model and its turbine and
generation bounds. The equations these fields feed are in the sections above (Hydro Production Function Models §1–§5).
The rest of the plant registry is on
System Element Modeling Overview — Configure.
system/hydros.json — generation block
Section titled “system/hydros.json — generation block”The generation block configures the turbine model for dispatch purposes and
provides the default production function used when no
hydro_production_models.json entry lists this plant. All variants share
turbine bounds (min_turbined_m3s, max_turbined_m3s) and generation bounds
(min_generation_mw, max_generation_mw); the model key selects the
production function.
"generation": { "model": "constant_productivity", "min_turbined_m3s": 0.0, "max_turbined_m3s": 50.0, "min_generation_mw": 0.0, "max_generation_mw": 50.0}| Field | Type | Description |
|---|---|---|
model | string | Production function variant. See the model table below. |
min_turbined_m3s | number | Minimum turbined flow [m³/s]. Non-zero values model a minimum stable turbine operation. |
max_turbined_m3s | number | Maximum turbined flow (installed turbine capacity) [m³/s]. |
min_generation_mw | number | Minimum electrical generation [MW]. |
max_generation_mw | number | Maximum electrical generation (installed capacity) [MW]. |
Available production function models:
| Model | model value | Description |
|---|---|---|
| Constant productivity | "constant_productivity" | power = productivity * turbined_flow. Independent of reservoir head. Productivity is supplied per stage range or season in system/hydro_production_models.json. |
| FPHA | "fpha" | Piecewise-linear envelope of the nonlinear production function. Head-dependent. Configured via hydro_production_models.json — see below. |
| Linearized head | "linearized_head" | Reserved model name; it resolves to the same constant-productivity source as constant_productivity (see methodology §3). Requires the plant’s productivity input. |
Every model applies in training and in simulation.
For most initial studies, constant_productivity is the natural choice. The
productivity coefficient encodes the plant’s average efficiency and net head:
for a plant with 80 m net head and 90% efficiency, the theoretical
productivity is approximately 9.81 × 80 × 0.90 / 1000 ≈ 0.706 MW/(m³/s).
system/hydro_production_models.json — Production Model Selection
Section titled “system/hydro_production_models.json — Production Model Selection”This optional file maps each hydro plant to a production-model selection
strategy, overriding generation.model from hydros.json for the stages or
seasons it lists. Two selection strategies are supported.
stage_ranges — assigns a model to each contiguous stage interval:
{ "production_models": [ { "hydro_id": 1, "selection_mode": "stage_ranges", "stage_ranges": [ { "start_stage_id": 0, "end_stage_id": null, "model": "fpha", "fpha_config": { "source": "precomputed" } } ] } ]}seasonal — assigns a model by season index, with a fallback for seasons
not listed:
{ "production_models": [ { "hydro_id": 1, "selection_mode": "seasonal", "default_model": "constant_productivity", "seasons": [ { "season_id": 0, "model": "fpha", "fpha_config": { "source": "computed", "volume_discretization_points": 7, "turbine_discretization_points": 7 } } ] } ]}Season indices are 0-based and match the season map in stages.json.
reference_volume
Section titled “reference_volume”Each stage range or season entry may carry an optional reference_volume
sibling of fpha_config (not nested inside it), declaring the reference
operating volume the equivalent-productivity derivation and the computed-FPHA
tailrace backwater interpolation consume. Set exactly one of:
volume_hm3— an absolute value in hm³ (finite,> 0.0).percentile— a fraction in[0.0, 1.0]of the plant’s operating range.
When reference_volume is omitted entirely, the reference volume defaults to
0.65 of the operating band (equivalent to { "percentile": 0.65 }).
"reference_volume": { "percentile": 0.65 }Hyperplane Sources
Section titled “Hyperplane Sources”When a plant is configured with model: "fpha", fpha_config.source selects
where the hyperplane coefficients come from.
source: "precomputed" — hyperplanes are loaded directly from
system/fpha_hyperplanes.parquet:
"fpha_config": { "source": "precomputed" }No additional fpha_config fields are needed; discretization and fitting
options are ignored. The parquet schema:
| Column | Type | Required | Description |
|---|---|---|---|
hydro_id | Int32 | Yes | Hydro plant identifier |
stage_id | Int32 | No | Stage the plane applies to (null = all stages; an absent column means the same) |
plane_id | Int32 | Yes | Plane index within this hydro |
gamma_0 | Float64 | Yes | Intercept coefficient (MW) |
gamma_v | Float64 | Yes | Volume coefficient (MW/hm³). Must be non-negative (>= 0); run-of-river plants have gamma_v = 0. |
gamma_q | Float64 | Yes | Turbined flow coefficient (MW per m³/s). Must be non-negative (>= 0); each stage a plant runs on precomputed planes needs at least one plane with gamma_q > 0, checked when the study is set up. |
gamma_s | Float64 | Yes | Spillage coefficient (MW per m³/s). Must be <= 0. |
kappa | Float64 | No | Intercept-only correction factor, defaulting to 1.0; validated in (0, 1]. Applies only to precomputed planes — the computed path does not use kappa (see Implementation notes). |
valid_v_min_hm3 | Float64 | No | Minimum volume where this plane is valid (hm³) |
valid_v_max_hm3 | Float64 | No | Maximum volume where this plane is valid (hm³) |
valid_q_max_m3s | Float64 | No | Maximum turbined flow where this plane is valid (m³/s) |
source: "computed" — hyperplanes are fitted at runtime from the plant’s
physical geometry. Novomodelo evaluates the exact production function on a
(volume, turbined-flow) grid at spillage = 0 — the flow axis starts at
q = 0, whose zero-flow column anchors the lower closure, so there is no
synthetic closing point and no spillage axis in the cloud — takes the 3-D
convex hull of the resulting cloud with vendored qhull, applies a
least-squares correction that scales the whole affine plane
(intercept and slopes together, not just the intercept), and fits a per-plane
lateral-flow secant. Fits are resolved independently per stage range or
season. Run-of-river plants (a single fitting volume) are supported: the
fitter synthesizes two nearby volume samples to keep the hull non-degenerate,
then snaps the resulting gamma_v to exactly 0. See Implementation notes
for the full fitting-pipeline summary and methodology §2.6–§2.7 for the
derivation.
This source requires tailrace, hydraulic_losses, and efficiency models
in hydros.json, plus geometry rows in system/hydro_geometry.parquet for the
plant. An FPHA-configured plant needs at least 1 row — a single row is valid for
run-of-river plants (a constant forebay, so gamma_v = 0). A plant configured
with linearized_head needs at least 2 rows when the file holds any row; that higher minimum is a load-time
validation rule keyed on the configured model name, not a runtime head fit —
linearized_head resolves to constant productivity (methodology §3).
"fpha_config": { "source": "computed", "volume_discretization_points": 5, "turbine_discretization_points": 5, "spillage_discretization_points": 5, "max_planes_per_hydro": 10, "fitting_window": null}All fields except source are optional:
| Field | Default | Description |
|---|---|---|
volume_discretization_points | 5 | Number of volume grid points for fitting. 1 selects the single-volume fit of a run-of-river plant; otherwise it must be >= 2. With a single fitting volume the fitter synthesizes two nearby volume samples and snaps gamma_v to exactly 0 (see Implementation notes). |
turbine_discretization_points | 5 | Number of turbined-flow grid points. Must be >= 2. |
spillage_discretization_points | 5 | Validated (must be >= 2) but does not add a spillage axis to the fitting grid — the cloud and the regression fix spillage at 0, and the lateral-flow secant sweeps its own sample range independently (§2.7). The field is retained for input round-tripping. |
max_planes_per_hydro | 10 | Accepted and validated (>= 1) but does not trim the plane set — the hull fitter emits exactly the upper-envelope facets. |
fitting_window | null | Optional volume range for fitting; absent uses the full forebay range of the plant’s system/hydro_geometry.parquet volume-height curve. |
fitting_window restricts which portion of the operating range builds the
grid — useful when the plant rarely operates near one extreme:
"fitting_window": { "volume_min_hm3": 1000.0, "volume_max_hm3": 40000.0 }"fitting_window": { "volume_min_percentile": 0.05, "volume_max_percentile": 0.95 }Absolute (_hm3) and percentile (_percentile) bounds are mutually
exclusive per bound (min and max may use different modes). For the 5%
fit-quality warning emitted after fitting, and for the round-trip parquet
export of computed planes, see Implementation notes.
Plane Reduction (fpha_plane_reduction)
Section titled “Plane Reduction (fpha_plane_reduction)”The optional file-level fpha_plane_reduction block merges near-parallel or
near-coincident FPHA planes after fitting. Off by default (absent = no
reduction); applied uniformly to every plant in the file.
Angle method — merges planes whose normal vectors are within
tolerance_deg degrees:
"fpha_plane_reduction": { "method": "angle", "tolerance_deg": 2.0 }| Field | Required | Description |
|---|---|---|
method | Yes | Must be "angle". |
tolerance_deg | Yes | Maximum angle between plane normals to merge them. Finite, in [0.0, 90.0]. |
Distance method — merges planes whose sampled mean-squared distance stays
within tolerance_pct, using n_samples sample points:
"fpha_plane_reduction": { "method": "distance", "tolerance_pct": 0.01, "n_samples": 200 }| Field | Required | Description |
|---|---|---|
method | Yes | Must be "distance". |
tolerance_pct | Yes | Tolerance, in percent, on the normalised mean-squared generation difference: two planes merge when that difference, as a fraction, is below tolerance_pct / 100. Finite, >= 0.0. |
n_samples | Yes | Number of sample points used to estimate the distance. Must be >= 1. |
Supplying a field belonging to the other method is a load-time error. The origin plane (zero generation at zero turbining) is never merged. The distance method’s sample draws are deterministically seeded — bit-identical across input ordering and rank count.
Per-Range and Per-Season Productivity
Section titled “Per-Range and Per-Season Productivity”For constant_productivity and linearized_head hydros, the equivalent
productivity for each (hydro, stage) pair is supplied by exactly one of two
sources:
system/hydro_production_models.json— an inlineproductivity_mw_per_m3sfield on the matchingstage_rangeorseasonalentry. Natural for “this productivity is constant for the next five stages.”system/hydro_energy_productivity.parquet— a row in theequivalent_productivity_mw_per_m3scolumn; a row withstage_idrefines a single stage, a row withstage_id = nullis a per-hydro default. Natural for per-stage numerical refinement.
As with the generic-constraint inputs, the JSON file owns model selection and range-level values, and the parquet file owns per-stage numerical refinement.
Resolution order at load time:
- Parquet stage-specific row (exact
stage_idmatch). - Parquet per-hydro default row (
stage_id = null). - JSON
productivity_mw_per_m3son the matching stage range or season.
Supplying a value from both sources for the same (hydro, stage) is a
load-time error; supplying neither is also a load-time error. The conflict
error names both files and the offending (hydro_id, stage_id) and asks for
the value from exactly one source; the coverage error names the pair and both
files.
A hydro whose generation.model is fpha is outside both checks: its
equivalent productivity is derived from the VHA geometry and the specific
productivity (methodology §5.1). An equivalent_productivity_mw_per_m3s row
for such a hydro (stage-specific, else the per-hydro default) is an override that
replaces the derived value for both evaluators — the reference-point value
(§5.1) and the useful-range mean value (§5.3).
In system/hydro_energy_productivity.parquet,
equivalent_productivity_mw_per_m3s and
specific_productivity_mw_per_m3s_per_m must each be finite and non-negative
when set: 0 is accepted and a negative value is rejected at load. A 0
equivalent productivity at a stage that does not use the "fpha" model marks a
planned-outage stage, as
0.0 does in the JSON field below. A 0 specific productivity zeroes the head-derived equivalent productivity and does not stop generation.
{ "start_stage_id": 12, "end_stage_id": 24, "model": "constant_productivity", "productivity_mw_per_m3s": 0.72}| Field | Type | Required | Description |
|---|---|---|---|
productivity_mw_per_m3s | number | Optional (non-FPHA) | Finite and non-negative (>= 0.0) when present; 0.0 marks a planned-outage stage. Rejected on FPHA — FPHA derives productivity from VHA geometry, not a scalar coefficient. |
Load-Time Validation Rules
Section titled “Load-Time Validation Rules”| Rule | Error Class | Description |
|---|---|---|
| FPHA geometry coverage | Dimensional error | When system/hydro_geometry.parquet holds any row, every fpha plant must have at least 1 row of its own (a single row is valid for run-of-river plants) and every linearized_head plant at least 2 rows; with no row in the file this rule does not run. |
| FPHA plane coverage | Dimensional error | Every (hydro_id, stage_id) group in system/fpha_hyperplanes.parquet must have at least 1 plane. |
| FPHA coefficient signs | Semantic error | gamma_v must be non-negative (>= 0; run-of-river plants have gamma_v = 0); gamma_s must be non-positive. |
| Geometry monotonicity | Semantic error | volume_hm3 must be strictly increasing; height_m and area_km2 must be non-decreasing. |
This is a topic-scoped index of the files hydro production touches and of the fields the methodology reads from them — it names each file and its role and maps fields to the quantities they feed; it does not repeat their field-by-field schemas. The exhaustive, field-by-field case-directory and output reference is owned by the Reference corpus (Case Format and Output Format pages).
Inputs
Section titled “Inputs”| File | Role |
|---|---|
system/hydros.json | Hydro plant registry — core fields, reservoir, outflow, generation model, the required unit_groups bus partition, and the advanced blocks (see the System Element Modeling Overview Configure tab). |
system/hydro_production_models.json | Optional per-hydro production-model selection (stage_ranges / seasonal), FPHA fitting configuration, and plane reduction. |
system/hydro_geometry.parquet | Volume-Height-Area (VHA) curves — required for any hydro using computed FPHA and for the evaporation area-volume conversion; when the file holds any row, every FPHA plant needs at least 1 row of its own and every linearized_head plant at least 2; also read, for any plant with a specific productivity, by the useful-range mean evaluator (Hydro Production Function Models §5.3). |
system/hydro_energy_productivity.parquet | Per-(hydro, stage) overrides: equivalent productivity (both evaluators), specific productivity (both evaluators’ head term and the specific_productivity tag), reference turbined flow (the reference_turbine tag only) — the tabular authoring source of the Configure tab’s resolution order, and the FPHA override. |
system/tailrace_curves.parquet | Optional piecewise-quartic tailrace-level curves — tailrace_level(outflow) per (hydro_id, family_id, segment_id) — feeding the computed-FPHA fit. A hydro with no curve uses its entity-level tailrace model. |
system/fpha_hyperplanes.parquet | Pre-fitted FPHA hyperplane coefficients, read when a plant’s fpha_config.source is "precomputed". |
constraints/hydro_unit_group_bounds.parquet | Optional stage-varying, optionally per-block overrides of a unit group’s four declared bounds (see the System Element Modeling Overview Configure tab). Rows are keyed by (hydro_id, hydro_unit_group_id, stage_id, block_id?), where hydro_unit_group_id matches a group’s own id in hydros.json; a group with no override row reads its declared value. |
constraints/hydro_bounds.parquet | Optional stage-varying (and optionally per-block) overrides of a hydro plant’s own bounds (maximum turbined flow, storage, outflow, maximum generation, diversion, spillage, filling rate, withdrawal). Its min_turbined_m3s and min_generation_mw columns reach no LP row: the minimum turbined-flow and minimum-generation rows read the unit groups’ resolved minima. Keyed by (hydro_id, stage_id) with an optional block_id; an absent/null cell falls back to the declared base value. Distinct from the per-unit-group hydro_unit_group_bounds.parquet above — a separate, plant-level file (see the System Element Modeling Overview Configure tab). |
initial_conditions.json (hydro rows) | Per-hydro initial state: storage or filling_storage (exactly one), and optional past_defluences seeding the pre-study releases in transit on a declared travel-time arc. AR-lag seeding for the PAR inflow model derives from scenarios/inflow_history.parquet and recent_observations instead — see the PAR(p) Inflow Model Inputs & Outputs tab. |
For the complete field-by-field schema of each file above, see the Case Format reference page in the Reference corpus.
Data requirements by quantity
Section titled “Data requirements by quantity”Each row names a data source, the fields the methodology above reads from it, and the quantity those fields feed:
| Data source | Fields | Used for |
|---|---|---|
| Hydro plant entity | tailrace (polynomial or piecewise) | computation |
| Hydro plant entity | hydraulic_losses (factor or constant) | computation |
| Hydro plant entity | efficiency (constant) | Turbine efficiency |
| Hydro plant entity | specific_productivity_mw_per_m3s_per_m (FPHA, and the useful-range mean evaluator for any model) | for derivation (§5.1) and (§5.3) |
| Hydro plant entity | Cascade topology (downstream pointer) | topological sum (§5.2) |
| Hydro production models input | Range-level productivity per stage_range / seasonal entry (non-FPHA, optional) | for §1 and §3 (§5.1) |
| Hydro production models input | reference_volume per stage_range / seasonal entry (absolute or percentile) | for the energy-conversion reduction (§5.1) and the tailrace backwater reference level (§2.3.1) |
| Hydro production models input | FPHA fitting_window per stage_range / seasonal entry (absolute or percentile bounds, optional) | Computed-FPHA volume grid (§2.6.1) |
| Hydro production models input | FPHA fitting configuration and optional fpha_plane_reduction block | Grid sizes, plane reduction method (§2.6–§2.8) |
| Hydro energy productivity input | Per-(hydro, stage) equivalent_productivity_mw_per_m3s and specific productivity | for both evaluators: the authoring source of a non-FPHA plant, the override of an FPHA plant (§5.1); for both evaluators’ head-derived value (§5.1, §5.3) |
| Hydro geometry | volume_hm3, height_m | interpolation (§2.3); mean forebay level (§5.3) |
| Pre-fitted FPHA planes | gamma_0, gamma_v, gamma_q, gamma_s (plus the optional kappa intercept factor, default 1.0) | Optional alternative to in-process fitting |
Outputs
Section titled “Outputs”| File | Role |
|---|---|
hydro_models/fpha_hyperplanes.parquet | Fitted hyperplane coefficients when fpha_config.source is "computed", written only when at least one plant has fitted planes (for example, a computed-FPHA plant with no turbine capacity has none) — the same schema as the system/fpha_hyperplanes.parquet input, so it round-trips: copy it into system/ and switch source to "precomputed" to reuse a prior fit without refitting. See the Implementation notes tab, “Round-trip parquet export”, for the workflow. |
hydro_models/evaporation_models.parquet | Fitted per-hydro evaporation-model coefficients — written whenever any evaporation model was built (not gated on an exports flag; absent when no plant declares evaporation). |
hydro_models/fpha_deviation_points.parquet | Per-plane FPHA fit-deviation sample points — a fitting diagnostic. Written only when the exports.fpha_deviation_points flag is enabled (default off) and at least one deviation point exists; absent otherwise. |
| Hydro generation and storage output columns | Per-(stage, block, hydro) dispatch results (turbined flow, spillage, generation, storage) written alongside every other entity’s output rows — the plant total, unchanged by bus-partitioning. |
Energy-conversion columns of simulation/hydros/ | The reference-point (equivalent_productivity_mw_per_m3s, accumulated_productivity_mw_per_m3s) and useful-range mean (integrated_*) productivities, incremental_inflow_energy_mw, and stored energy in MWh and MW (stored_energy_{initial,final}_{mwh,mw}) — see §5 above and the Output Format reference. |
simulation/hydro_bus_generation/ | Per-(stage, block, hydro, bus) cell dispatch results — turbined_m3s, generation_mw, generation_mwh; bus_id always non-null. See Implementation notes for the exact-sum caveat on the generation_mw column. |
simulation/in_transit/ | Per-(stage, downstream hydro, maturity lag) in-transit water volumes on declared travel-time arcs, including the delayed arrival that enters the receiving plant’s water balance; written only when a travel-time arc is declared (schema: Simulation Output). |
simulation/transit_seed/ | Scenario-level rolling release-window seed for a declared travel-time arc — five columns (scenario_id, hydro_id, start_date, end_date, value_m3s), carrying no stage/node prefix. Written only when a travel-time arc is declared; absent otherwise. Feeds a continuing run’s own initial_conditions.past_defluences (study chaining). |
For the complete output schema (columns, types, file layout), see the Output Format reference page in the Reference corpus.
Non-normative software behavior for the hydro production models — what Novomodelo does at runtime, beyond the equations above. This tab references the methodology body for the derivations rather than restating them.
Computed-FPHA fitting behavior
Section titled “Computed-FPHA fitting behavior”The computed path (§2.6–§2.7 above) evaluates the exact production function
on a (volume, turbined-flow) grid at spillage fixed to 0. The flow axis
starts at q = 0; that zero-flow column is generation-zero by construction
and anchors the lower closure of the cloud, so the fitter needs neither a
synthetic closing point nor a spillage axis in the cloud itself — spillage
sensitivity is captured separately by the per-plane lateral-flow secant
(§2.7), not by adding a third grid dimension. That secant uses
nine evenly spaced samples of the lateral flow.
The 3-D convex hull’s upper-envelope facets are corrected by a single
least-squares scalar. That correction scales the whole affine
plane — intercept and both slopes together — not just the intercept, unlike
the kappa factor of precomputed planes, which scales the intercept only
(validated in (0, 1]).
Lateral-flow slopes above -1e-10 — a near-zero negative residual or any
positive one — are set to exactly 0, so no near-zero coefficient reaches
the spillage column.
When any plant uses computed FPHA, the families of system/tailrace_curves.parquet are checked as the
production models are built: consecutive segments of a family must meet (the previous
outflow_max_m3s and the next outflow_min_m3s differ by at most 1e-6 m³/s) and give the same
level at the shared boundary, to within the larger of 0.001 m and 1e-4 of the larger of the two
levels. A gap, an overlap or a level mismatch beyond these bounds is an error that names the plant by
id and the boundary.
Run-of-river support
Section titled “Run-of-river support”A plant whose fitting volume range collapses to a single point (constant
forebay) is fit by synthesizing two nearby volume samples so the 3-D hull
stays non-degenerate, then snapping the resulting gamma_v residual to
exactly 0 when its magnitude is at most 1e-6 MW per hm³. This enforces the correct
run-of-river semantics — generation on
these planes does not vary with storage — and is why run-of-river plants are
supported on the computed path rather than rejected.
The two samples sit either side of that single volume, each offset by the
larger of half a percent of the plant’s useful storage and 1 hm³, so a
plant with no useful storage still gets two distinct samples; when the
volume-height curve spans a range of volumes, both samples are clamped into
it.
FPHA without turbine capacity
Section titled “FPHA without turbine capacity”A plant that requests FPHA, computed (fpha_config.source: "computed" in
any of its production-model entries) or precomputed with no row in
system/fpha_hyperplanes.parquet, and declares max_turbined_m3s at or below
1e-9 is not fitted; it is modelled with zero productivity at every stage
(§2.6.1 above). The computed-FPHA prerequisites apply
to a computed plant: tailrace, hydraulic_losses and efficiency in hydros.json and
geometry rows in system/hydro_geometry.parquet; a missing one is a load-time
error.
A warning names the plant; novomodelo validate and novomodelo run both print it:
WARN hydro UHE1 (id=0) requests FPHA but has no turbine capacity (max_turbined_m3s = 0); modeling it with zero productivityEvery MPI rank prepares the production models, so a multi-rank run prints the warning once per rank.
The plant counts as constant productivity: in n_constant of
training/hydro_models.json, in its no_turbine_capacity list, in
n_constant of the Python hydro_models summary, and in
the constant count of the CLI Production: line. It is absent from
n_fpha, total_planes and fpha_details, from
n_fpha_computed_from_geometry and n_fpha_precomputed_hyperplanes in
training/model_provenance.json, and from
hydro_models/fpha_hyperplanes.parquet.
Fit-quality warning
Section titled “Fit-quality warning”After the full pipeline, Novomodelo compares the fitted min-envelope against the
exact production function on the spillage-0 grid and computes their
relative mean-absolute deviation. When it exceeds 5%, a warning is logged
naming the plant and stage:
FPHA fit for hydro UHE Example (stage 3) deviates 6.2% from the exact production function (mean |Δ| 4.1 MW, max 9.7 MW); the convex-hull approximation is poor here — typically a strongly non-concave production surface that no single α correction can trackThe warning is informational — the run continues with the fitted planes. A
high deviation typically indicates a strongly non-concave production surface
that no single scalar can track well; narrowing fitting_window
or raising the discretization point counts are the usual remedies.
Determinism guarantees
Section titled “Determinism guarantees”The fitting cloud points and the hull output are canonically sorted, so
fitted hyperplanes are bit-identical regardless of input ordering and MPI
rank count. The fpha_plane_reduction distance method’s sampled draws are
seeded from stable plant/stage/plane-pair identity — never wall clock or MPI
rank — so its merges are equally reproducible.
Evaporation: disabled on zero area, rejected on missing geometry
Section titled “Evaporation: disabled on zero area, rejected on missing geometry”A hydro that declares evaporation.coefficients_mm needs an area-volume
curve — the rows in system/hydro_geometry.parquet — to linearize its
evaporative flux, and the two ways that curve can be absent are handled
differently:
- No geometry rows at all is a hard load-time error. A hydro that
declares
evaporation.coefficients_mmbut contributes no rows tosystem/hydro_geometry.parquetis rejected at load, naming the plant: the evaporation linearization requires area-volume curve data and none is present. - Rows present but every
area_km2is zero is disabled with a warning. The curve exists but describes zero surface area throughout, so evaporative flux is identically zero regardless of the authored coefficients. Novomodelo disables evaporation (zero flux) for that hydro and logs a warning naming the plant rather than failing the run.
Two conditions raised while building the per-stage linearization remain hard load-time errors: a stage whose total block duration is not positive, and a computed evaporation coefficient — volume slope or intercept — that is not finite (the signature of a degenerate area-volume curve). The calendar month that selects each stage’s monthly evaporation coefficient is taken from that stage’s start date. See Penalty System — Signed Net Evaporation for the water-balance treatment of the resulting flux.
Commissioning: the PreFilling state
Section titled “Commissioning: the PreFilling state”A hydro’s entry_stage_id/exit_stage_id commissioning window is honored even for a non-filling hydro (one with no filling block). At any stage outside its window — before entry_stage_id, or from exit_stage_id onward — the plant is PreFilling: the site is not in service, so its turbine, spillage, and diversion columns are pinned to zero, its storage is frozen by the identity , and its local inflow, the upstream releases, the flows diverted in and the in-transit water maturing into it are routed past the site to the first downstream plant that is not PreFilling (or leaves the modelled system if there is none). Unlike the generic outside-window zero-pin used for the other entity types, the river is never trapped or duplicated at a site that is not in service. See LP Formulation — Lifecycle Phases for every phase and Penalty System for the spillage-frozen-versus-costed distinction.
Water travel time: in-transit seed and validation
Section titled “Water travel time: in-transit seed and validation”When a plant declares a travel-time arc, the releases already in transit at the
study start must be supplied so the first stages’ delayed-arrival buckets are
seeded rather than starting empty. This history lives in
initial_conditions.past_defluences: windowed pre-study release records (a
hydro_id, a release window, and an average rate in m³/s) for the plants whose
outflow feeds a declared arc.
Load-time validation of a declared arc:
- History depth. The
past_defluenceswindows must cover the arc’s in-transit span at study start — the interval[start_0 − travel_time, start_0)ending at the study start. A gap, or no windows at all, is a load-time error, and no window may be future-dated past the study start — there is no proxy or fallback seed from any other source. - Downstream must be operating. A release at a stage before the downstream
plant’s
entry_stage_id— while it is PreFilling or Filling — is rejected; the check covers each in-study release stage, the first included, so the downstream plant must have entered service by the first study stage. - Heterogeneous travel time under chronological blocks. When two or more
declared arcs feed the same downstream plant with differing
travel_time_hoursand at least one study stage runs in chronological block mode, the case is rejected at load with aNotImplementederror, whose message reads “unsupported in v1”, naming the downstream plant and the conflicting arcs; align the arcs’travel_time_hours, or keep every study stage in parallel block mode. Equal travel times into a confluence, or a study with no chronological stage, are unaffected. See Error Codes —NotImplemented. - Zero, absent, or invalid travel time. A zero or absent
travel_time_hoursis treated as undeclared — an instantaneous transfer that creates no cross-stage arc, adds no in-transit bucket, and needs nopast_defluencesseed. An explicit0.0additionally logs a model-quality warning that it is being read as undeclared, so a deliberate zero is flagged while an omitted field stays silent. A negative or non-finitetravel_time_hoursis a hard load-time error. See Error Codes. - Model-quality warnings (non-blocking). Two further conditions on a
declared arc are reported as
warning:lines afterValid case: …; the case still loads and runs. See Error Codes —ModelQuality.- Negligible travel time. When
travel_time_hoursis below 1 % of every study stage’s length (travel_time_hours / stage hours < 0.01on every stage), the arc carries a negligible cross-stage fraction; the warning suggests not declaring it. - Longer than the remaining study horizon. When
travel_time_hoursexceeds the remaining study horizon from some stage, releases from that stage onward never arrive within the study, and the warning names the first such stage as the point from which the arc is economically inert. The in-transit sizing stays safe.
- Negligible travel time. When
In-transit water that matures into a plant at a stage where the plant is PreFilling enters the row of the first non-PreFilling plant downstream, and leaves the modelled system only when no such plant exists.
Horizon limitation. Without a loaded terminal boundary, in-transit water that would mature after the last stage is dropped; with one, it is carried to the terminal stage and priced there (State Augmentation — Horizon limitation, Post-Study Boundary & Chained Studies).
Bus-partitioned hydro: cell collapse and the generation-sum caveat
Section titled “Bus-partitioned hydro: cell collapse and the generation-sum caveat”A plant’s unit_groups partition it into one cell per distinct bus_id:
the LP carries one turbine column and one FPHA-generation column per cell.
When every group shares one bus, the plant collapses to a single cell — the
same LP shape, byte-identical, as a plant declaring no groups beyond the
required one. Same-bus groups sharing a cell’s turbine and generation
columns is exact for the box constraints (each group’s own bounds fold
independently before summing into the cell), because every group on a plant
shares the same production model and productivity coefficients — there is no
per-group productivity field.
The simulation/hydro_bus_generation/ output sums bit-exactly to the plant’s
own turbined_m3s on every model, since both are a plain sum of the same
per-cell turbine columns. generation_mw sums exactly only for FPHA plants
and single-cell plants: for ConstantProductivity/LinearizedHead, the
plant-level row computes (Σ_c turbined_c) × ρ — sum the cells’ turbined
flow first, then multiply once — while each bus-cell row computes
turbined_c × ρ independently. The two expressions are equal in exact
arithmetic, but floating-point multiplication does not distribute over
addition bit-for-bit, so the plant total need not equal the sum of the
cell rows to the last bit once a plant spans more than one cell. FPHA
plants are exempt because both the plant row and the cell rows sum the
same underlying per-cell FPHA generation columns the same way; a
single-cell plant is exempt because there is only one term either way.
Round-trip parquet export
Section titled “Round-trip parquet export”When hyperplanes are fitted at runtime (source: "computed"), the fitted
coefficients are automatically written to
output/hydro_models/fpha_hyperplanes.parquet using the same schema as the
system/fpha_hyperplanes.parquet input. To reuse a prior fit without
refitting on a subsequent run, copy this output file into system/ and
change source to "precomputed" in hydro_production_models.json.
A plant with no turbine capacity has no rows in the exported file and needs
none: copied to system/, the file loads unedited, and the plant stays at
zero productivity.
Cross-References
Section titled “Cross-References”- Notation Conventions — variable and set definitions (, , , , )
- System Element Modeling Overview — hydro plant element description, decision variables, Variable Units Convention
- LP Formulation — how production constraints integrate into the assembled LP
- Penalty System — turbined-cost regularization, penalty priority ordering
- Cut Management — Benders cut generation affected by FPHA dual variables
- Equipment-Specific Formulations — thermal and hydro equipment constraint patterns