Hydro Production Function Models
Este conteúdo não está disponível em sua língua ainda.
Purpose
Section titled “Purpose”This spec defines the hydro generation constraint models supported by Cobre, which relate turbined flow and reservoir storage to electrical output. Two models are available during training (policy construction): constant productivity and FPHA. A third model — linearized head — is available only during simulation (policy evaluation) as a higher-fidelity enhancement. The choice among training models 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 elements. For variable definitions see notation conventions; for LP integration see LP formulation; for hydro element descriptions see system elements.
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 below 1), from the hydro object’s
efficiencyfield - = 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 resolved at load time.
LP treatment: 1 equality constraint per hydro per block. The generation variable is fully determined by — no free generation variable is needed. 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 (Função de Produção Hidrelétrica Aproximada)
Section titled “2. FPHA (Função de Produção Hidrelétrica Aproximada)”For accurate modeling of hydroelectric generation, FPHA (Aproximate Hydropower 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 Cobre 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 Mapping
Section titled “2.1 Notation Mapping”This section uses consistent notation with the LP formulation. The following table maps Cobre symbols to equivalent Portuguese terminology for practitioners familiar with DECOMP/DESSEM/NEWAVE:
| Cobre | Portuguese (practitioner reference) | Description | Units |
|---|---|---|---|
| FPH | Hydro production function | MW | |
| Reservoir storage | hm³ | ||
| Turbined flow | m³/s | ||
| / | Spillage | m³/s | |
| GH | Hydro generation | MW | |
| (montante) | Forebay (upstream) level | m | |
| (jusante) | Tailrace (downstream) level | m | |
| (líquida) | Net head | m | |
| (perda hidráulica) | Hydraulic losses | m | |
| Total downstream outflow | m³/s |
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
- (MW·s/m⁴), with constant efficiency per plant (default 1.0)
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, Cobre approximates with a set of linear hyperplanes.
2.3 Topology Functions
Section titled “2.3 Topology Functions”Cobre 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 volume-height curve in hydro_geometry.parquet. For storage where :
The downstream water level depends on total outflow. Three representations are supported:
Polynomial model (type: "polynomial"):
Piecewise-linear model (type: "piecewise"): 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. C0 continuity between segments is enforced to ~1 mm. 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 specific productivity (MW per (m³/s · m)) converts hydraulic power to electrical power:
so the exact production is in MW. Cobre uses constant efficiency per plant.
FPHA plants author directly — not a single scalar productivity . The equivalent productivity at the reference operating point is derived from 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 outer approximation of the exact surface. Each hyperplane defines an upper bound on generation:
Physical interpretation of coefficients:
| Coefficient | Sign | Meaning |
|---|---|---|
| > 0 | Intercept (MW at zero storage, flow, spillage) | |
| ≥ 0 | Higher storage → higher forebay → more generation | |
| ≥ 0 | More turbined flow → more generation | |
| ≤ 0 | More spillage → higher tailrace → less net head |
Source of hyperplanes: planes are either pre-computed (read from fpha_hyperplanes.parquet) 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:
volume_discretization_pointsuniformly spaced values spanning the fitting window (default 5 points). The window is set by the optionalfitting_windowblock insidefpha_config(absolutevolume_min_hm3/volume_max_hm3or percentile bounds), defaulting to the plant’s full forebay storage range. It is distinct fromreference_volume(§5.1), which fixes an operating point, not this window. - Flow axis:
turbine_discretization_pointsuniformly spaced values spanning (default 5 points). 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.
Run-of-river plants: when the plant has a single fitting volume (), two volume samples ~1% of useful storage apart are synthesized to keep the 3-D hull non-degenerate. The resulting residual is then snapped to exactly 0, enforcing the correct 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 . The result is a concave outer approximation (the smallest concave function lying above ); 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 αFPHA\alpha_{FPHA}αFPHA 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 exact 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. This is why (the old intercept-only shrink).
- 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 exact over the spill grid is computed. A warning is emitted (in canonical plant/stage order) when it exceeds 5% — typically indicating a strongly non-concave surface that no single can track well.
Precomputed input: when using source: "precomputed", hyperplanes are read directly from fpha_hyperplanes.parquet. That file retains a kappa column (defaulting to 1.0); for precomputed planes it is still applied as an intercept-only scale () and is validated to lie in . Only the computed path retired — there the whole-affine correction above replaces it.
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 (in the current default: own spillage ) over 9 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). Near-zero slopes of either sign are snapped to exactly 0 to protect LP column scaling from near-zero structural coefficients.
Default lateral axis: own spillage (). The more general composition (upstream defluences, post-incremental inflows with participation factors) is a deferred future extension.
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, configured via an fpha_plane_reduction block:
- Angle (
method: angle, tolerance in degrees): merge consecutive pairs whose normal-vector angle satisfies (strict). Fully deterministic from coefficients. - Distance (
method: distance, tolerance in percent + sample count): merge consecutive pairs whose normalised mean-squared generation difference . 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.
Off by default: no reduction is applied unless an fpha_plane_reduction block is present in the production-models config.
2.9 LP Integration
Section titled “2.9 LP Integration”Final FPHA Constraint
Section titled “Final FPHA Constraint”For each hydro using FPHA, block , and plane :
The coefficients are already -scaled — there is no separate pre-scaling step. These are hard constraints — no slack variables. Feasibility is ensured through the turbined_cost regularization mechanism (see section 2.10).
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 LP Formulation §4a) and is end-of-stage storage. Both are LP variables, so appears in the FPHA constraint with coefficient . The LP solver automatically accounts for this in the reduced cost of the pinned column.
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 bounded by (user-defined bounds from
hydros.json) - FPHA constraints (one per plane ) provide upper bounds relating generation to storage, flow, and spillage
- The optimizer maximizes generation subject to FPHA constraints
- At optimum, generation lies on one of the FPHA hyperplane facets
Key insight: Because minimizing cost includes maximizing hydro generation (which has zero fuel cost), the optimizer naturally pushes generation to the FPHA surface boundary. The turbined_cost regularization (section 2.10) ensures the solution lies on the boundary rather than at an interior point.
2.10 Turbined Cost
Section titled “2.10 Turbined Cost”A small regularization cost is applied to the turbined flow variable of every hydro in the objective:
This cost must satisfy for each plant. The rule serves two purposes.
For hydros using the FPHA production model, the regularization keeps the solver on the FPHA surface boundary rather than at an interior point: without it, the optimizer could find degenerate solutions where turbined flow and spillage are both artificially high (with net generation unchanged), because the FPHA surface has a flat region where increasing and simultaneously can maintain the same . The penalty making every unit of turbined flow carry a small additional cost collapses the degenerate interior region.
For hydros using constant productivity, the same regularization is applied uniformly so that the LP tie-breaks (turbined, spillage) decompositions deterministically. Applying it to constant-productivity plants — not to FPHA plants alone — keeps every turbine column priced, so the dispatch stays well-determined even on cases that mix the two production models. Plants using linearized_head (simulation-only, see section 3) are not subject to this regularization during training because training uses only constant_productivity and fpha; during simulation, the cost is irrelevant because no policy is being constructed.
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 the FPHA constraint (via , section 2.9), the FPHA hyperplane duals 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 LP Formulation §4a) makes its reduced cost capture the total sensitivity automatically — the LP solver propagates the FPHA contribution through .
The cut coefficient for storage is simply the reduced cost of the pinned column:
This dual implicitly includes the water balance contribution (), the FPHA contribution (), and any generic constraint contributions — all resolved by the LP solver without explicit dual combination.
For the complete cut coefficient computation, see cut management.
Model Transition Considerations
Section titled “Model Transition Considerations”When a hydro transitions between production models across stages:
| Transition | Cut Interpretation | Action |
|---|---|---|
| Constant → FPHA | Cuts at stage use constant model | Cut valid but conservative |
| FPHA → Constant | Stage backward pass uses constant | May overestimate value |
| FPHA → FPHA (different params) | Parameters change | Cuts remain valid if conservative |
Recommendation: When using stage-dependent FPHA configuration, ensure the FPHA at stage is at least as conservative as stage for cut validity.
3. Linearized Head Model (Simulation-Only Enhancement)
Section titled “3. Linearized Head Model (Simulation-Only Enhancement)”An intermediate model between constant productivity and full FPHA that captures first-order head variation with storage:
where:
- are linearization coefficients derived from
- (normalization at reference volume)
3.1 Why Simulation-Only
Section titled “3.1 Why Simulation-Only”The product is a bilinear term — both and are LP variables. To maintain LP linearity, the standard approach fixes from the previous SDDP iteration (or from a reference volume on the first iteration), converting the constraint to a linear equality. However, this means the LP constraint coefficients change between iterations: the effective productivity is different after each forward pass updates the storage trajectory.
This violates a foundational assumption of SDDP: each stage must have a fixed LP structure across all iterations. Benders cuts generated under one linearization point encode dual information about a specific LP. When the LP changes (because changed), previously generated cuts are not guaranteed to be valid — they may cut off the true optimal solution or produce inconsistent value function approximations. This breaks the convergence guarantees of the algorithm.
During simulation, linearized head is safe because simulation executes a single forward pass through the policy — there are no cuts being accumulated, no convergence to verify. The model provides a higher-fidelity generation estimate than constant productivity without the preprocessing cost of fitting FPHA hyperplanes.
3.2 Simulation Use Case
Section titled “3.2 Simulation Use Case”The linearized head model fills a practical gap in the simulation step:
- More accurate than constant productivity: Captures how reservoir level affects generation — important for plants with significant head variation that are modeled with
constant_productivityduring training for computational reasons - Cheaper than FPHA: Requires only the Volume-Height-Area curve (
hydro_geometry.parquet), no hyperplane fitting - Single constraint: One equality constraint per hydro per block, compared to inequality constraints for FPHA
Typical use: plants where full FPHA accuracy is justified for near-term training stages but far-future stages use constant_productivity during training, then linearized_head during simulation for improved analytics.
3.3 Data Requirements
Section titled “3.3 Data Requirements”productivity_mw_per_m3s from hydros.json plus hydro_geometry.parquet for the Volume-Height-Area curve.
4. Model Selection Guidelines
Section titled “4. Model Selection Guidelines”Training (Policy Construction)
Section titled “Training (Policy Construction)”Only constant_productivity and fpha are valid during training. The linearized head model is excluded because it changes the LP between iterations (see section 3.1).
| Scenario | Recommended Model | Rationale |
|---|---|---|
| High-head storage reservoirs | FPHA | Significant head variation (>20%) |
| Large storage variation plants | FPHA | Operating across wide volume range |
| Run-of-river plants | FPHA or Constant | Hull now supports run-of-river |
| 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)”All three models are available during simulation. The linearized head model is particularly useful as a simulation-only upgrade for plants that used constant_productivity during training:
| 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 trained with constant, significant head variation | Linearized head | Better analytics without FPHA fitting cost |
| Post-optimization validation | Compare all models | Verify approximation quality |
The production model may vary by stage or by season per hydro, configured via the stage_ranges and seasonal selection modes in hydro_production_models.json.
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 — Cobre reduces each plant’s production model to a small set of per-(hydro, stage) scalars at a representative operating point. 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 ρeq\rho_{eq}ρeq”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 active generation model at stage :
| Generation model | derivation |
|---|---|
constant_productivity | A per-(hydro, stage) numeric value authored by the case — see “Authoring sources” below. |
linearized_head | A per-(hydro, stage) numeric value authored by the case — same resolution as constant_productivity. |
fpha | , where is the net head computed from the VHA geometry (section 2.3) at the reference point. FPHA hydros do not author a separate scalar in the production-models input — it is derived. A parquet-level override is still accepted (see “FPHA override path”). |
Reference volume: the reference operating volume is declared by a single reference_volume field in the production-model config entry (stage_range or seasonal). The field takes exactly one of two forms: an absolute volume_hm3 value, or a percentile (fraction of useful volume between and ); when absent it defaults to 65% of useful volume. This single field 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 by the optional fitting_window block inside fpha_config (§2.6.1), defaulting to the plant’s full forebay storage range.
The reference flow is typically set at installed turbine capacity.
Authoring sources for non-FPHA hydros
Section titled “Authoring sources for non-FPHA hydros”Two complementary inputs supply for constant_productivity and linearized_head plants:
- Range-level productivity in the hydro production models input. Each
stage_rangeorseasonalentry may carry a singleproductivity_mw_per_m3svalue that applies to every stage the entry covers. This is the natural authoring shape for “this productivity is the same for the next five stages” — declarative, low-volume. - Per-stage productivity in the hydro energy productivity input (a per-row table indexed by
(hydro_id, stage_id)). Each row may name a specific stage or carry a per-hydro default (nostage_idset). This is the natural authoring shape for “this productivity changes every stage” — tabular, high-volume.
Either source — but not both for the same (hydro, stage) — may supply the value. The contract is symmetric with the generic-constraint authoring contract: a declarative JSON file owns model selection plus range-level values, and a tabular parquet file owns per-stage numerical refinement.
Resolution order
Section titled “Resolution order”For a non-FPHA hydro at study stage , the value of is resolved in this order:
- A per-stage row in the energy-productivity input whose
stage_idmatches exactly. - A per-hydro default row in the energy-productivity input (no
stage_idset) for hydro . - The
productivity_mw_per_m3svalue from the matchingstage_rangeorseasonalentry in the production-models input.
The first three options are mutually exclusive at load time (see “Conflict and coverage” below), so this ordering is descriptive rather than a precedence in the sense of “earlier source overrides later”; it is the order in which the resolver consults sources, stopping at the first hit.
Conflict and coverage
Section titled “Conflict and coverage”Two load-time invariants are enforced:
- Conflict: when the per-stage parquet row (or the per-hydro default) and the production-models JSON entry both supply a value for the same , the case is rejected at load time. The error names both files and the offending .
- Coverage: when neither source supplies a value for some study stage of a non-FPHA hydro , the case is rejected at load time. The error names the offending pair.
A coverage failure never reaches the dispatch pipeline — it is caught alongside the other case-validation rules. This is the same boundary discipline used elsewhere in the load pipeline: structural problems surface as load-time errors, never as deeper dispatch-time panics.
FPHA override path
Section titled “FPHA override path”For FPHA hydros, is derived from VHA geometry and at the reference operating point. The per-stage energy-productivity input remains available as an override: if a row supplies a value for an FPHA , it replaces the derived value. The production-models JSON file does not accept productivity_mw_per_m3s for FPHA — that field is rejected at parse time. FPHA hydros are therefore exempt from the non-FPHA conflict and coverage rules.
Zero as planned-outage marker
Section titled “Zero as planned-outage marker”A resolved is accepted for non-FPHA hydros and a resolved is accepted for FPHA hydros. Both are interpreted as a planned outage for the affected stage: the LP uses these scalars as multipliers (never divisors), so zero productivity produces zero generation cleanly without any divide-by-zero or feasibility hazard. The same relaxation applies to the parquet override, the parquet column, and the JSON range-level productivity. Negative values are still rejected at load time as nonsensical.
This relaxation lets real-world cases mark a plant as out-of-service for specific stages without the case author needing to remove it from the system definition, restructure the cascade, or work around a strict-positivity check.
5.2 Accumulated Cascade Productivity
Section titled “5.2 Accumulated Cascade Productivity ρacum\rho_{acum}ρacum”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 elements for the cascade topology). Plants with no downstream successors have . The accumulation is per-stage because each summand can vary by stage.
5.3 Inflow and Storage in Energy Units
Section titled “5.3 Inflow and Storage in Energy Units”converts 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):
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.
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.4 Why a Scalar Reduction Exists at All
Section titled “5.4 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; linearized head is bilinear in . None of these 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 one number per (hydro, stage), at one operating point, and that number is what the cascade-summation, ENA, and EARM formulas above can consume uniformly. The LP continues to enforce the full production model — the scalar reduction is for accounting, not for dispatch.
6. Data Requirements Summary
Section titled “6. Data Requirements Summary”| Data Source | Required Fields | Used For |
|---|---|---|
| Hydro plant entity | tailrace (polynomial, piecewise, or piecewise-quartic families) | 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) | for derivation (§5.1) |
| 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 sections 1 and 3 (§5.1 authoring source 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 | override (§5.1 authoring source 2 + FPHA override path) |
| Hydro geometry | volume_hm3, height_m | interpolation (§2.3) |
| Pre-fitted FPHA planes | (plus kappa column retained for back-compat, defaults to 1.0) | Optional alternative to in-process fitting |
For non-FPHA hydros, the per-(hydro, stage) productivity coefficient is resolved from exactly one of the two authoring sources listed in §5.1: range-level productivity in the production-models input, or per-stage productivity in the energy-productivity input. Supplying a value from both for the same is rejected at load time; supplying neither for any study stage of a non-FPHA hydro is also rejected at load time.
For FPHA hydros, the production-models input does not accept a productivity scalar — is derived from VHA geometry and unless the energy-productivity input supplies an override.
Implementation in Cobre
Section titled “Implementation in Cobre”The methodology above defines the hydro production models; the tabs below cover how Cobre’s software surface configures, feeds, and reports on them.
Cobre’s system/hydros.json and system/hydro_production_models.json files
author the entities and production-model selection the methodology above
describes. This tab is the field-level configuration reference; the equations
these fields feed are in the sections above.
system/hydros.json — Hydro Plant Registry
Section titled “system/hydros.json — Hydro Plant Registry”Hydro plants are defined in system/hydros.json. The top-level object has a
single key "hydros" containing an array of plant objects:
{ "hydros": [ { "id": 1, "name": "UHE Tucuruí", "bus_id": 0, "downstream_id": null, "operational_start_date": "1984-11-22", "entry_stage_id": null, "exit_stage_id": null, "reservoir": { "min_storage_hm3": 50.0, "max_storage_hm3": 45000.0 }, "outflow": { "min_outflow_m3s": 1000.0, "max_outflow_m3s": 100000.0 }, "generation": { "model": "constant_productivity", "min_turbined_m3s": 500.0, "max_turbined_m3s": 22500.0, "min_generation_mw": 0.0, "max_generation_mw": 8370.0 }, "tailrace": { "type": "polynomial", "coefficients": [5.0, 0.001] }, "hydraulic_losses": { "type": "factor", "value": 0.03 }, "efficiency": { "type": "constant", "value": 0.93 }, "evaporation": { "coefficients_mm": [ 80.0, 75.0, 70.0, 65.0, 60.0, 55.0, 60.0, 65.0, 70.0, 75.0, 80.0, 85.0 ] }, "diversion": { "downstream_id": 2, "max_flow_m3s": 200.0 }, "filling": { "start_stage_id": 48, "filling_min_rate_m3s": 100.0 }, "penalties": { "spillage_cost": 0.01, "diversion_cost": 0.1, "turbined_cost": 0.05, "storage_violation_below_cost": 10000.0, "filling_target_violation_cost": 6000.0, "turbined_violation_below_cost": 500.0, "outflow_violation_below_cost": 500.0, "outflow_violation_above_cost": 500.0, "generation_violation_below_cost": 1000.0, "evaporation_violation_cost": 5000.0, "water_withdrawal_violation_cost": 1000.0 } } ]}Only id, name, bus_id, downstream_id, operational_start_date,
reservoir, outflow, and generation are required. All other top-level keys (tailrace,
hydraulic_losses, efficiency, evaporation, diversion, filling,
penalties) are optional and default to off when absent.
Core Fields
Section titled “Core Fields”| Field | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Unique non-negative identifier. Must be unique across all hydro plants. Referenced by initial_conditions.json and by other plants via downstream_id. |
name | string | Yes | Human-readable plant name. Used in output files, validation messages, and log output. |
bus_id | integer | Yes | Identifier of the electrical bus to which this plant’s generation is injected. Must match an id in buses.json. |
downstream_id | integer or null | Yes | Identifier of the plant that receives this plant’s outflow. null means the plant is at the bottom of its cascade — outflow leaves the system. |
operational_start_date | string (ISO-8601 date) | Yes | Calendar date (YYYY-MM-DD) the plant enters the registry’s operational history. Provenance and the canonical (operational_start_date, id) ordering key (see Notation Conventions) — independent of entry_stage_id/exit_stage_id below, which alone gate commissioning. |
entry_stage_id | integer or null | No | Stage index at which the plant enters service (inclusive). null means the plant is available from stage 0. |
exit_stage_id | integer or null | No | Stage index at which the plant is decommissioned. The commissioning window is half-open [entry_stage_id, exit_stage_id): the plant is active through exit_stage_id - 1, and outside the window it is not in service. For a hydro the out-of-service state is PreFilling — turbine, spillage, and diversion pinned to zero, storage frozen, and natural inflow routed past the site (see Implementation notes and system elements §5) — rather than a bare column zero-pin. null means the plant is never decommissioned. |
Reservoir
Section titled “Reservoir”Storage is tracked in hm³ (cubic hectometres; 1 hm³ = 10⁶ m³).
"reservoir": { "min_storage_hm3": 0.0, "max_storage_hm3": 1000.0}| Field | Type | Description |
|---|---|---|
min_storage_hm3 | number | Minimum operational storage (dead volume). Water below this level cannot reach the turbine intakes. For plants that can empty completely, use 0.0. |
max_storage_hm3 | number | Maximum operational storage (flood control level). Must be strictly greater than min_storage_hm3. |
Outflow Constraints
Section titled “Outflow Constraints”Total outflow equals turbined flow plus spillage.
"outflow": { "min_outflow_m3s": 0.0, "max_outflow_m3s": 50.0}| Field | Type | Description |
|---|---|---|
min_outflow_m3s | number | Minimum total outflow required at all times [m³/s]. Set to the ecological flow requirement or minimum riparian right. Use 0.0 if there is no minimum requirement. |
max_outflow_m3s | number or null | Maximum total outflow [m³/s]. null means no upper bound on outflow. |
When the solver cannot meet the minimum-outflow bound, a violation slack is
added at the cost of outflow_violation_below_cost in the penalties block.
Generation Models
Section titled “Generation Models”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 | Phase availability | Description |
|---|---|---|---|
| Constant productivity | "constant_productivity" | Training + simulation | power = productivity * turbined_flow. Independent of reservoir head. Productivity is supplied per stage range or season in system/hydro_production_models.json. |
| FPHA | "fpha" | Training + simulation | Piecewise-linear envelope of the nonlinear production function. Head-dependent. Configured via hydro_production_models.json — see below. |
| Linearized head | "linearized_head" | Simulation-only | Head-dependent productivity linearized around a reference volume. Rejected if selected during training — see methodology §3.1 for why the bilinear term breaks the fixed-LP-structure requirement SDDP depends on. |
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).
Cascade Topology (downstream_id)
Section titled “Cascade Topology (downstream_id)”The downstream_id field creates a directed chain of hydro plants: water
released from an upstream plant (turbined or spilled) enters the downstream
plant’s reservoir in the same stage. To model a three-plant cascade where
plant 0 flows into plant 1, which flows into plant 2:
{ "id": 0, "downstream_id": 1, ... }{ "id": 1, "downstream_id": 2, ... }{ "id": 2, "downstream_id": null, ... }The downstream graph is validated to be acyclic — no chain of
downstream_id references may return to a plant already in the chain. Plants
with downstream_id: null are tailwater plants; each connected component of
the cascade graph must have exactly one tailwater plant.
Water Travel Time (travel_time_hours)
Section titled “Water Travel Time (travel_time_hours)”The optional travel_time_hours field declares how long a release takes to
travel down the main cascade arc to downstream_id.
{ "id": 0, "downstream_id": 1, "travel_time_hours": 48.0 }| Field | Type | Required | Description |
|---|---|---|---|
travel_time_hours | number or null | No | Travel time on the main cascade arc, in hours. When present, strictly positive, and the plant has a downstream_id, the outflow is delivered to the downstream reservoir after this delay instead of in the same stage. Absent, null, or 0.0 means an instantaneous transfer. |
When an arc is declared, Cobre carries the water in transit as additional
Bellman state (see LP formulation §5d and
system elements §5). Travel time applies to the main
cascade arc only — diversion and pumping transfers are always instantaneous.
Declaring an arc requires seeding the pre-study releases already in transit via
initial_conditions.past_defluences, and the downstream plant must be
operating over the arrival window — see Implementation notes.
Advanced Fields
Section titled “Advanced Fields”These fields enable more detailed physical modeling and are all optional.
Tailrace model — the tailrace block models downstream water level as a
function of total outflow; when absent, tailrace elevation is zero. Two
entity-level variants are supported:
"tailrace": { "type": "polynomial", "coefficients": [5.0, 0.001] }coefficients is an ascending-power polynomial: coefficients[0] is the
height at zero outflow (m), coefficients[1] the coefficient for Q¹, and so
on.
"tailrace": { "type": "piecewise", "points": [ { "outflow_m3s": 0.0, "height_m": 3.0 }, { "outflow_m3s": 5000.0, "height_m": 4.5 } ]}Points must be sorted in ascending outflow_m3s order; the solver
interpolates linearly between them. A third, file-level source — the optional
per-plant piecewise-quartic tailrace curves with backwater families (methodology
§2.3.1) — overrides the entity-level model for any plant that has rows in
that file.
Hydraulic losses — the hydraulic_losses block models head loss in the
penstock; absent means lossless.
"hydraulic_losses": { "type": "factor", "value": 0.03 }value is a dimensionless fraction (e.g. 0.03 = 3% of net head) for
"factor", or a fixed metres value (value_m) for "constant".
Efficiency model — the efficiency block scales hydraulic power to
electrical power; absent means 100% efficiency. Only "constant" is
supported:
"efficiency": { "type": "constant", "value": 0.93 }Evaporation — the evaporation block models net water flux at the
reservoir surface; absent means no evaporation. Coefficients are signed:
positive values are net evaporative loss, negative values are net rainfall
input.
"evaporation": { "coefficients_mm": [ 80.0, 75.0, 70.0, 65.0, 60.0, 55.0, 60.0, 65.0, 70.0, 75.0, 80.0, 85.0 ], "reference_volumes_hm3": [ 15000, 12000, 10000, 8000, 6000, 5000, 5500, 7000, 9000, 11000, 13000, 14500 ]}| Field | Type | Required | Description |
|---|---|---|---|
coefficients_mm | array | Yes | Exactly 12 values, one per calendar month (index 0 = January). mm/month; may be negative. |
reference_volumes_hm3 | array | No | Exactly 12 linearization reference volumes [hm³], one per month, within [min_storage_hm3, max_storage_hm3]. Absent defaults to the storage range midpoint. |
A hydro that declares coefficients_mm but has no usable area-volume curve to
convert it into a flux — see Implementation notes for exactly what “no usable
curve” means and what Cobre does about it.
Diversion channel — the diversion block models a diversion that routes
flow directly to a downstream plant’s reservoir, bypassing turbines and
spillways; absent means no diversion.
"diversion": { "downstream_id": 2, "max_flow_m3s": 200.0 }Filling configuration — the filling block enables a commissioning fill
period, during which the reservoir accumulates water toward min_storage_hm3
before the plant can generate.
"filling": { "start_stage_id": 48, "filling_min_rate_m3s": 100.0}| Field | Description |
|---|---|
start_stage_id | Stage index at which filling begins (inclusive); filling runs through the stage before entry_stage_id. |
filling_min_rate_m3s | Per-stage minimum accumulation rate [m³/s]: anchors a per-stage minimum end-of-stage storage target that ramps to min_storage_hm3 by the last filling stage. It is not an applied inflow and not a cap — natural inflow and upstream cascade releases still flow through the ordinary water balance; there is no retention or impound mechanism that diverts inflow into storage. |
The target trajectory, its soft-floor penalty, and the load-time filling-sufficiency check are covered in Penalty System §6.
Penalties — the penalties block overrides the global defaults from
penalties.json for one plant; when absent, the plant uses the global
values.
"penalties": { "spillage_cost": 0.01, "diversion_cost": 0.1, "turbined_cost": 0.05, "storage_violation_below_cost": 10000.0, "filling_target_violation_cost": 6000.0, "turbined_violation_below_cost": 500.0, "outflow_violation_below_cost": 500.0, "outflow_violation_above_cost": 500.0, "generation_violation_below_cost": 1000.0, "evaporation_violation_cost": 5000.0, "water_withdrawal_violation_cost": 1000.0, "water_withdrawal_violation_pos_cost": 1200.0, "water_withdrawal_violation_neg_cost": 800.0, "evaporation_violation_pos_cost": 5000.0, "evaporation_violation_neg_cost": 5000.0, "inflow_nonnegativity_cost": 1000.0}When present, the block must supply every field; the directional
*_pos_cost / *_neg_cost fields override the symmetric default for their
direction only. For the full field list, units, and the priority ordering
across penalty categories, see Penalty System.
Three-tier resolution cascade — penalty values are resolved from the most specific to the most general source:
- Stage-level override (stage-specific penalty files, when present)
- Entity-level override (the
penaltiesblock inside the plant’s JSON object) - Global default (the
hydrosection ofpenalties.json, which must always be present and complete)
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.
"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) |
plane_id | INT32 | Yes | Plane index within this hydro |
gamma_0 | DOUBLE | Yes | Intercept coefficient (MW) |
gamma_v | DOUBLE | Yes | Volume coefficient (MW/hm³). Must be non-negative (>= 0); run-of-river plants have gamma_v = 0. |
gamma_q | DOUBLE | Yes | Turbined flow coefficient (MW per m³/s) |
gamma_s | DOUBLE | Yes | Spillage coefficient (MW per m³/s). Must be <= 0. |
kappa | DOUBLE? | 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 | DOUBLE? | No | Minimum volume where this plane is valid (hm³) |
valid_v_max_hm3 | DOUBLE? | No | Maximum volume where this plane is valid (hm³) |
valid_q_max_m3s | DOUBLE? | No | Maximum turbined flow where this plane is valid (m³/s) |
source: "computed" — hyperplanes are fitted at runtime from the plant’s
physical geometry. Cobre 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 alpha_FPHA 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 at least 1 row in system/hydro_geometry.parquet for
the plant (a single row is valid for run-of-river plants; linearized_head
still needs at least 2 rows because it fits a head slope in volume — that
constraint does not apply to FPHA).
"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. Must be >= 2. |
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 alpha_FPHA 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 currently 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 operating range [min_storage_hm3, max_storage_hm3]. |
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": 5.0, "volume_max_percentile": 95.0 }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 | Maximum relative MSE distance (fraction) to treat two planes as coincident. 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.
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.
{ "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 |
|---|---|---|
| Bus reference integrity | Reference error | Every bus_id must match an id in buses.json. |
| Downstream reference integrity | Reference error | Every non-null downstream_id must match an id in hydros.json. |
| Cascade acyclicity | Topology error | The directed graph of downstream_id links must be acyclic. |
| Storage bounds ordering | Physical feasibility | min_storage_hm3 must be less than max_storage_hm3. |
| Outflow bounds ordering | Physical feasibility | When max_outflow_m3s is present, it must be >= min_outflow_m3s. |
| Turbine bounds ordering | Physical feasibility | min_turbined_m3s must be <= max_turbined_m3s. |
| Generation bounds consistency | Physical feasibility | min_generation_mw must be <= max_generation_mw. |
| Initial conditions completeness | Reference error | Every hydro plant must have exactly one entry in initial_conditions.json (storage or filling_storage, not both). |
| Evaporation array length | Schema error | coefficients_mm must have exactly 12 values; reference_volumes_hm3, when present, must also have exactly 12 values within [min_storage_hm3, max_storage_hm3]. |
| FPHA geometry coverage | Dimensional error | Every fpha plant must have at least 1 row in system/hydro_geometry.parquet (a single row is valid for run-of-river plants); every linearized_head plant needs at least 2 rows. |
| 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 — it names each file and its role, 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 Directory Format and Output Format pages).
Inputs
Section titled “Inputs”| File | Role |
|---|---|
system/hydros.json | Hydro plant registry — core fields, reservoir, outflow, generation model, and the advanced blocks (see the 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 or linearized_head, and for the evaporation area-volume conversion. |
system/hydro_energy_productivity.parquet | Per-(hydro, stage) equivalent-productivity overrides — the tabular authoring source for §5.1’s resolution order, and the FPHA override path. |
system/fpha_hyperplanes.parquet | Pre-fitted FPHA hyperplane coefficients, read when a plant’s fpha_config.source is "precomputed". |
initial_conditions.json (hydro rows) | Per-hydro initial state: storage or filling_storage (exactly one), optional past_inflows for AR lag seeding, and optional past_defluences seeding the pre-study releases in transit on a declared travel-time arc. |
For the complete field-by-field schema of each file above, see the Case Directory Format reference page in the Reference corpus.
Outputs
Section titled “Outputs”| File | Role |
|---|---|
output/hydro_models/fpha_hyperplanes.parquet | Fitted hyperplane coefficients when fpha_config.source is "computed" — 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 Implementation notes for the round-trip workflow. |
| 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. |
simulation/in_transit/ | Per-(stage, downstream hydro, maturity lag) in-transit water volumes on declared travel-time arcs — five columns (stage_id, hydro_id, lag, in_transit_volume_hm3, delayed_arrival_hm3). Written only when a travel-time arc is declared; absent (byte-neutral) otherwise. |
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 Cobre 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.
The 3-D convex hull’s upper-envelope facets are corrected by a single
least-squares alpha_FPHA scalar. That correction scales the whole affine
plane — intercept and both slopes together — not just the intercept; this
is why it replaces (rather than equals) the retired kappa shrink, which
only touched the intercept and now survives solely on the precomputed input
path (validated in (0, 1]).
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. 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.
Fit-quality warning
Section titled “Fit-quality warning”After the full pipeline, Cobre 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:
Warning: hydro 'UHE Example' stage 3 FPHA fit deviation = 6.2 % (> 5 %).Consider increasing discretization points or narrowing the fitting window.The warning is informational — the run continues with the fitted planes. A
high deviation typically indicates a strongly non-concave production surface
that no single alpha_FPHA 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 with a warning, not a hard error
Section titled “Evaporation: disabled with a warning, not a hard error”A hydro that declares evaporation.coefficients_mm but has no usable
area-volume curve — no rows in system/hydro_geometry.parquet, or every
area_km2 value is zero — does not fail to load. Evaporation is instead
disabled (zero flux) for that hydro, with a warning naming the plant,
because a zero surface area yields zero evaporative flux regardless of the
authored coefficients. Non-finite coefficients and a stage with no
season_id (which evaporation needs to select a calendar month) remain hard
load-time errors — only the missing-area case is downgraded to a warning. 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 natural inflow is routed past the site to the first active downstream plant (or the network sink). 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 system elements §5 for the full lifecycle 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. - Proxy from
past_inflows. When a plant lacks the requiredpast_defluenceshistory, Cobre derives a proxy seed from itspast_inflowsand logs a caveat; when neither is available the study is rejected. - Downstream must be operating. A release into a downstream plant that is still PreFilling/Filling, or before its commissioning entry stage, over the arrival window is rejected — the water would arrive at a reservoir not yet present in the dispatch.
- Zero travel time. A zero (or absent) travel time is treated as undeclared — an instantaneous transfer, adding no bucket and requiring no seed.
The horizon limitation (in-transit water maturing past the last stage is dropped, not credited to terminal storage) is covered in LP formulation §5d.
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.
Cross-References
Section titled “Cross-References”- Notation conventions — variable and set definitions (, , , , )
- System elements — hydro plant element description, decision variables, Variable Units Convention
- LP formulation — how production constraints integrate into the assembled LP
- Penalty system —
turbined_costregularization, penalty priority ordering - Cut management — Benders cut generation affected by FPHA dual variables
- Equipment formulations — thermal and hydro equipment constraint patterns