Production Model Files
These five files, all under system/, hold the per-plant tables and settings behind hydro generation: reservoir
geometry, the production model used at each stage, energy-productivity overrides, precomputed FPHA hyperplanes, and
tailrace-level curves. Each row or entry names a plant of
system/hydros.json by hydro_id. The
File summary maps every case file to its page.
system/hydro_geometry.parquet
Section titled “system/hydro_geometry.parquet”Methodology: Hydro Production Function Models §2.3
Volume-Height-Area (VHA) curves for hydro reservoirs. Required for a
computed-FPHA plant (source: "computed") and for a plant that declares
evaporation coefficients. 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.
The table is also read by the useful-range mean evaluator
(Hydro Production Function Models §5.3)
for any hydro, whatever its generation model, that has a specific
productivity (the system/hydros.json entity field or a
system/hydro_energy_productivity.parquet override). For such a plant, a table
that fails to build, or a non-positive mean equivalent head, rejects the case at
novomodelo validate (see
GenericConstraintValidationError).
Author the table to span reservoir.min_storage_hm3 … reservoir.max_storage_hm3:
when the table is narrower, the mean is taken over the table’s own extent.
4 columns, all non-nullable; rows may appear in any order.
Multiple rows per hydro_id together constitute the VHA curve for that plant.
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
hydro_id | Int32 | Yes | — | — | Hydro plant ID |
volume_hm3 | Float64 | Yes | — | hm³ | Total reservoir volume at this point (hm³). Non-negative and finite. |
height_m | Float64 | Yes | — | m | Reservoir surface elevation at this volume (m). Non-negative and finite. |
area_km2 | Float64 | Yes | — | km² | Water surface area at this volume (km²). Non-negative and finite. |
Validation: all four columns must be present with the correct types. volume_hm3,
height_m, and area_km2 must be non-negative and finite. Within each plant,
volume_hm3 must be strictly increasing and height_m and area_km2
non-decreasing, each within a relative tolerance.
system/hydro_production_models.json
Section titled “system/hydro_production_models.json”Methodology: Hydro Production Function Models · §2.8 Similar-Hyperplane Reduction
Per-hydro production function assignment. The file needs an entry for every
plant whose generation.model in system/hydros.json is "fpha". For any
other plant, each study stage takes its productivity from exactly one of
productivity_mw_per_m3s here or equivalent_productivity_mw_per_m3s in
system/hydro_energy_productivity.parquet, so a plant whose productivity the
Parquet file supplies for every study stage needs no entry.
The file contains a "production_models" array. Each entry configures one hydro
plant and is identified by a unique hydro_id. Results are loaded in
hydro_id-ascending order regardless of declaration order.
Top-level structure:
{ "$schema": "https://docs.novomodelo.invalid/schemas/production_models.schema.json", "production_models": [ ... ]}fpha_plane_reduction (optional top-level block). A sibling of the
production_models array — not a per-hydro field — that trims the computed-FPHA
hyperplane set after fitting. When the key is absent, no reduction is applied
(off by default). It is a tagged object keyed by method; unknown fields are
rejected, so a tolerance field that belongs to the other method is rejected.
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
fpha_plane_reduction | object | null | No | — | — | Optional block that trims the computed-FPHA hyperplane set after fitting; absent means no reduction is applied. |
fpha_plane_reduction.method | string | Yes | — | — | One of "angle", "distance". The literal string "angle" takes tolerance_deg; the literal string "distance" takes tolerance_pct and n_samples. |
fpha_plane_reduction.tolerance_deg | number | Conditional | — | ° | Required when method is "angle". Merge tolerance in degrees; finite and in [0.0, 90.0]. |
fpha_plane_reduction.tolerance_pct | number | Conditional | — | % | Required when method is "distance". Distance tolerance (percentage); finite, >= 0.0. |
fpha_plane_reduction.n_samples | integer | Conditional | — | — | Required when method is "distance". Sample count; integer >= 1. |
{ "production_models": [ ... ], "fpha_plane_reduction": { "method": "angle", "tolerance_deg": 5.0 }}The distance form is { "method": "distance", "tolerance_pct": 0.5, "n_samples": 64 }.
Per-hydro entry fields:
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
production_models[].hydro_id | integer | Yes | — | — | Hydro plant ID. Must be unique within the file. |
production_models[].selection_mode | string | Yes | — | — | One of "stage_ranges", "seasonal". How the model variant is chosen per stage. |
stage_ranges mode. The model for each stage is determined by the first
matching [start_stage_id, end_stage_id] range. end_stage_id may be null
to mean “until end of horizon”.
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
production_models[].stage_ranges | array | Conditional | — | — | Required when selection_mode is "stage_ranges". One entry per stage range. |
production_models[].stage_ranges[].start_stage_id | integer | Yes | — | — | First stage (inclusive) to which this entry applies |
production_models[].stage_ranges[].end_stage_id | integer | null | No | — | — | Last stage (inclusive); null or absent means open-ended |
production_models[].stage_ranges[].model | string | Yes | — | — | Model name: "constant_productivity", "linearized_head", or "fpha" |
production_models[].stage_ranges[].fpha_config | object | null | No | — | — | Source and fit settings of an "fpha" entry; see FPHA config fields below. An "fpha" entry without it reads the rows of system/fpha_hyperplanes.parquet, as source: "precomputed" does. |
production_models[].stage_ranges[].reference_volume | object | null | No | — | — | Reference operating volume V_ref, a sibling of fpha_config (not nested). Set exactly one of volume_hm3 (absolute, hm³, > 0.0) or percentile (a fraction of the operating range, [0.0, 1.0]); both or neither is rejected. Absent ⇒ the case-wide default fraction. Applies to any plant in either selection mode. See reference-volume fields below. |
production_models[].stage_ranges[].productivity_mw_per_m3s | number | null | No | — | MW/(m³/s) | Finite and >= 0 when present (0 marks a planned-outage stage); rejected on "fpha". Optional for constant_productivity and linearized_head — when omitted, supply the value via system/hydro_energy_productivity.parquet. Exactly one source per (hydro, stage) is required; both is rejected at load time. |
seasonal mode. The model for a stage is determined by its season_id.
Stages whose season is not listed use default_model.
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
production_models[].default_model | string | Conditional | — | — | Required when selection_mode is "seasonal". Fallback model name for unlisted seasons |
production_models[].seasons | array | Conditional | — | — | Required when selection_mode is "seasonal". Array of season overrides: season_id, model, optional fpha_config, reference_volume, productivity_mw_per_m3s |
production_models[].seasons[] entry fields:
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
production_models[].seasons[].season_id | integer | Yes | — | — | Season index (0-based, matching the stages.json season map). |
production_models[].seasons[].model | string | Yes | — | — | Model name: "constant_productivity", "linearized_head", or "fpha" |
production_models[].seasons[].fpha_config | object | null | No | — | — | As production_models[].stage_ranges[].fpha_config. |
production_models[].seasons[].fpha_config.source | string | Yes | — | — | As production_models[].stage_ranges[].fpha_config.source. |
production_models[].seasons[].fpha_config.volume_discretization_points | integer | null | No | — | — | As production_models[].stage_ranges[].fpha_config.volume_discretization_points. |
production_models[].seasons[].fpha_config.turbine_discretization_points | integer | null | No | — | — | As production_models[].stage_ranges[].fpha_config.turbine_discretization_points. |
production_models[].seasons[].fpha_config.spillage_discretization_points | integer | null | No | — | — | As production_models[].stage_ranges[].fpha_config.spillage_discretization_points. |
production_models[].seasons[].fpha_config.max_planes_per_hydro | integer | null | No | — | — | As production_models[].stage_ranges[].fpha_config.max_planes_per_hydro. |
production_models[].seasons[].fpha_config.fitting_window | object | null | No | — | — | As production_models[].stage_ranges[].fpha_config.fitting_window. |
production_models[].seasons[].fpha_config.fitting_window.volume_min_hm3 | number | null | No | — | hm³ | As production_models[].stage_ranges[].fpha_config.fitting_window.volume_min_hm3. |
production_models[].seasons[].fpha_config.fitting_window.volume_max_hm3 | number | null | No | — | hm³ | As production_models[].stage_ranges[].fpha_config.fitting_window.volume_max_hm3. |
production_models[].seasons[].fpha_config.fitting_window.volume_min_percentile | number | null | No | — | — | As production_models[].stage_ranges[].fpha_config.fitting_window.volume_min_percentile. |
production_models[].seasons[].fpha_config.fitting_window.volume_max_percentile | number | null | No | — | — | As production_models[].stage_ranges[].fpha_config.fitting_window.volume_max_percentile. |
production_models[].seasons[].reference_volume | object | null | No | — | — | As production_models[].stage_ranges[].reference_volume. |
production_models[].seasons[].reference_volume.volume_hm3 | number | null | No | — | hm³ | As production_models[].stage_ranges[].reference_volume.volume_hm3. |
production_models[].seasons[].reference_volume.percentile | number | null | No | — | — | As production_models[].stage_ranges[].reference_volume.percentile. |
production_models[].seasons[].productivity_mw_per_m3s | number | null | No | — | MW/(m³/s) | As production_models[].stage_ranges[].productivity_mw_per_m3s. |
reference_volume fields (optional sibling of fpha_config):
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
production_models[].stage_ranges[].reference_volume.volume_hm3 | number | null | No | — | hm³ | Absolute reference volume [hm³]; finite and > 0.0. Mutually exclusive with percentile. |
production_models[].stage_ranges[].reference_volume.percentile | number | null | No | — | — | Reference volume as a fraction of the [V_min, V_max] band; finite and in [0.0, 1.0]. Mutually exclusive with volume_hm3. |
The reference operating volume V_ref feeds the FPHA backwater (downstream forebay) level and the energy-equivalent productivity ρ_eq. It is the single source of truth for V_ref: when absent, the case-wide default fraction is used.
fpha_config fields:
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
production_models[].stage_ranges[].fpha_config.source | string | Yes | — | — | "precomputed" or "computed" |
production_models[].stage_ranges[].fpha_config.volume_discretization_points | integer | null | No | — | — | Number of volume grid points, at least 2; 1, or a fitting volume range that is a single point, selects the single-volume (run-of-river) fit; absent or null uses 5 |
production_models[].stage_ranges[].fpha_config.turbine_discretization_points | integer | null | No | — | — | Number of turbine-flow grid points; at least 2; absent or null uses 5 |
production_models[].stage_ranges[].fpha_config.spillage_discretization_points | integer | null | No | — | — | Validated (>= 2); does not add a spillage axis to the fit — accepted for input round-tripping (see Hydro Production Function Models — Configure); absent or null uses 5 |
production_models[].stage_ranges[].fpha_config.max_planes_per_hydro | integer | null | No | — | — | Validated (>= 1); the fit keeps every upper-envelope facet and does not trim to this count; only fpha_plane_reduction reduces the set (see Hydro Production Function Models — Configure); absent or null uses 10 |
production_models[].stage_ranges[].fpha_config.fitting_window | object | null | No | — | — | Volume range restriction for hyperplane computation; absent or null uses the full forebay range of the plant’s system/hydro_geometry.parquet volume–height curve |
source: "precomputed" means the hyperplanes are loaded from
system/fpha_hyperplanes.parquet. source: "computed" means Novomodelo derives
them from system/hydro_geometry.parquet; in this case hydro_geometry.parquet
must be present and the computed planes are automatically written to
output/hydro_models/fpha_hyperplanes.parquet.
A plant with any source: "computed" entry fits planes for every study stage, so every stage must resolve to an entry
that carries an fpha_config. A constant_productivity or linearized_head range, a season left to default_model,
or a stage that no entry covers is rejected when the case is set up. To use FPHA in some stages and another model in
the rest, give every FPHA entry source: "precomputed".
fitting_window fields. Absolute bounds (volume_min_hm3, volume_max_hm3)
and percentile bounds (volume_min_percentile, volume_max_percentile) are
mutually exclusive per bound — set at most one of volume_min_hm3 and
volume_min_percentile, and at most one of volume_max_hm3 and
volume_max_percentile.
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
production_models[].stage_ranges[].fpha_config.fitting_window.volume_min_hm3 | number | null | No | — | hm³ | Explicit minimum volume for fitting (hm³) |
production_models[].stage_ranges[].fpha_config.fitting_window.volume_max_hm3 | number | null | No | — | hm³ | Explicit maximum volume for fitting (hm³) |
production_models[].stage_ranges[].fpha_config.fitting_window.volume_min_percentile | number | null | No | — | — | Minimum as a percentile of the operating range (0–1) |
production_models[].stage_ranges[].fpha_config.fitting_window.volume_max_percentile | number | null | No | — | — | Maximum as a percentile of the operating range (0–1) |
Example — hydro 0 uses computed FPHA for stages 0–24 and computed FPHA on a finer grid from stage 25:
{ "$schema": "https://docs.novomodelo.invalid/schemas/production_models.schema.json", "production_models": [ { "hydro_id": 0, "selection_mode": "stage_ranges", "stage_ranges": [ { "start_stage_id": 0, "end_stage_id": 24, "model": "fpha", "fpha_config": { "source": "computed", "volume_discretization_points": 7, "turbine_discretization_points": 15 } }, { "start_stage_id": 25, "end_stage_id": null, "model": "fpha", "fpha_config": { "source": "computed", "volume_discretization_points": 11, "turbine_discretization_points": 21 } } ] } ]}Example — hydro 5 uses FPHA in season 0, linearized_head in all other seasons:
{ "production_models": [ { "hydro_id": 5, "selection_mode": "seasonal", "default_model": "linearized_head", "seasons": [ { "season_id": 0, "model": "fpha", "fpha_config": { "source": "precomputed" } } ] } ]}system/hydro_energy_productivity.parquet
Section titled “system/hydro_energy_productivity.parquet”Methodology: Hydro Production Function Models §5
Optional per-plant, per-stage overrides for the energy-conversion preprocessing
layer. Rows with stage_id = NULL act as per-hydro defaults and apply to all
stages not covered by a stage-specific row. Each override column reaches a
different set of consumers:
equivalent_productivity_mw_per_m3s— replaces ρ_eq outright in both energy-conversion evaluators (the reference-point value and the useful-range mean value), and therefore theequivalent_productivity/integrated_equivalent_productivitycomputed tags and theequivalent_productivity_mw_per_m3s/integrated_equivalent_productivity_mw_per_m3ssimulation output columns.specific_productivity_mw_per_m3s_per_m— the ρ_esp that multiplies the net head in both evaluators’ head-derived ρ_eq (the reference-point value of an FPHA plant; the useful-range mean value of any plant with VHA geometry), and thespecific_productivitycomputed tag. It is resolved per(hydro, stage)as stage-specific row →stage_id = NULLrow → the entity’sspecific_productivity_mw_per_m3s_per_minsystem/hydros.json.reference_outflow_m3s— reaches thereference_turbinecomputed tag only. It does not change the net head: both evaluators always evaluate the head at the plant’smax_turbined_m3s.
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
hydro_id | Int32 | Yes | — | — | Hydro plant identifier |
stage_id | Int32 (nullable) | Yes | — | — | Stage; NULL means “applies to all stages” |
equivalent_productivity_mw_per_m3s | Float64 (nullable) | Yes | — | MW/(m³/s) | Direct ρ_eq override [MW/(m³/s)]; finite and >= 0.0 (0.0 marks a planned outage at a stage not using the "fpha" model) |
reference_outflow_m3s | Float64 (nullable) | Yes | — | m³/s | Q_ref override [m³/s]; finite and >= 0.0 |
specific_productivity_mw_per_m3s_per_m | Float64 (nullable) | Yes | — | MW/(m³/s)/m | ρ_esp override [MW/(m³/s)/m]; finite and >= 0.0 (0.0 is accepted and makes the head-derived equivalent productivity zero) |
Validation:
- All five columns must be present; a file missing any of them is rejected.
hydro_idmust not be null.equivalent_productivity_mw_per_m3s, when set, must be finite and >= 0.0;0.0is accepted; at a stage that does not use the"fpha"model it marks a planned outage.reference_outflow_m3s, when set, must be finite and >= 0.0.specific_productivity_mw_per_m3s_per_m, when set, must be finite and >= 0.0;0.0is accepted; it makes the head-derived equivalent productivity zero and is not a planned-outage marker.- A row where all three override columns are NULL is accepted.
- Duplicate
(hydro_id, stage_id)pairs are rejected during case build. - The reference operating volume V_ref is declared per
(plant, stage)byreference_volumeinsystem/hydro_production_models.json; areference_volume_hm3column in this file is ignored, with a warning.
system/fpha_hyperplanes.parquet
Section titled “system/fpha_hyperplanes.parquet”Methodology: Hydro Production Function Models §2.5
Pre-computed FPHA hyperplane coefficients for hydros configured with
fpha_config.source: "precomputed". When absent, only "computed" source is
available.
11 columns; rows may appear in any order. A null stage_id means the plane is
valid for all stages of that hydro. One row per hyperplane; at least 1 plane is
required per (hydro_id, stage_id) group.
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
hydro_id | Int32 | Yes | — | — | Hydro plant ID |
stage_id | Int32 (nullable) | No | — | — | Stage the plane applies to. null = valid for all stages |
plane_id | Int32 | Yes | — | — | Plane index within this hydro (and stage) |
gamma_0 | Float64 | Yes | — | MW | Intercept coefficient (MW) |
gamma_v | Float64 | Yes | — | MW/hm³ | Volume coefficient (MW/hm³). >= 0 (0 for a run-of-river plant). |
gamma_q | Float64 | Yes | — | MW/(m³/s) | Turbined flow coefficient (MW per m³/s). >= 0; each stage’s planes need at least one with > 0. |
gamma_s | Float64 | Yes | — | MW/(m³/s) | Spillage coefficient (MW per m³/s). <= 0. |
kappa | Float64 (nullable) | No | 1.0 | — | Intercept-only correction factor (gamma_0 is multiplied by it) in (0, 1]; defaults to 1.0 when absent or null. |
valid_v_min_hm3 | Float64 (nullable) | No | — | hm³ | Volume range minimum where this plane is valid (hm³) |
valid_v_max_hm3 | Float64 (nullable) | No | — | hm³ | Volume range maximum where this plane is valid (hm³) |
valid_q_max_m3s | Float64 (nullable) | No | — | m³/s | Maximum turbined flow where this plane is valid (m³/s) |
Validation: required columns (hydro_id, plane_id, gamma_0, gamma_v,
gamma_q, gamma_s) must be present with the correct types. Optional columns
that are present must also have the correct types. Every row must have
gamma_v >= 0 and gamma_s <= 0 at load, and once the file holds any row,
every FPHA plant with turbine capacity, computed included, needs rows in it (the
computed fit does not read them). Each stage a plant runs with precomputed FPHA
uses the rows carrying its stage_id, else the all-stage (stage_id null)
rows, and must have one of the two; each row a stage uses must have gamma_q
>= 0 and kappa in (0, 1], at least one with gamma_q > 0, when the study is set up.
The file produced by output/hydro_models/fpha_hyperplanes.parquet (written when
source: "computed" is used) has this exact same 11-column schema. It loads unedited as precomputed input: its flat cap plane (gamma_q = 0) is accepted, and a plant without turbine capacity needs no rows.
system/tailrace_curves.parquet
Section titled “system/tailrace_curves.parquet”Methodology: Hydro Production Function Models §2.3.1
Optional piecewise-quartic tailrace-level curves that replace the entity-level
tailrace model for any plant that has rows in this file. When a plant has rows
here, the computed-FPHA pipeline evaluates its tailrace level from these
piecewise-quartic curves — selecting the segment by downstream flow and
interpolating between backwater families at the downstream plant’s stage
reference level — instead of the tailrace model declared in hydros.json.
Plants without a row in this file keep their existing tailrace model; the file
is inert (silently skipped) when absent from the case directory.
Rows may appear in any order. A complete curve for one backwater family
consists of multiple rows sharing (hydro_id, family_id).
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
hydro_id | Int32 | Yes | — | — | Plant whose tailrace this describes |
family_id | Int32 | Yes | — | — | Family index within the plant (sequential grouping key) |
downstream_reference_level_m | Float64 (nullable) | Yes | — | m | Downstream reservoir reference level keying this family (m). null when the plant has a single family and no backwater dependency. |
segment_id | Int32 | Yes | — | — | Piece index within the family |
outflow_min_m3s | Float64 | Yes | — | m³/s | Segment lower validity bound (m³/s). Non-negative. |
outflow_max_m3s | Float64 | Yes | — | m³/s | Segment upper validity bound (m³/s). Non-negative, >= outflow_min_m3s. |
coefficient_0 | Float64 | Yes | — | — | Degree-0 polynomial coefficient. Any sign. |
coefficient_1 | Float64 | Yes | — | — | Degree-1 polynomial coefficient. Any sign. |
coefficient_2 | Float64 | Yes | — | — | Degree-2 polynomial coefficient. Any sign. |
coefficient_3 | Float64 | Yes | — | — | Degree-3 polynomial coefficient. Any sign. |
coefficient_4 | Float64 | Yes | — | — | Degree-4 polynomial coefficient. Any sign. |
The quartic is evaluated as coefficient_0 + coefficient_1*x + coefficient_2*x² + coefficient_3*x³ + coefficient_4*x⁴ where x is the downstream outflow in m³/s. Higher-degree coefficients are routinely negative in source data; all signs are accepted.
Validation rules:
- All eleven columns must be present with the types listed above.
outflow_min_m3sandoutflow_max_m3smust be non-negative and finite.outflow_max_m3s >= outflow_min_m3s(segments are non-inverted).coefficient_0throughcoefficient_4must be finite.downstream_reference_level_m, when non-null, must be non-negative and finite.