Skip to content

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.


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.

NameTypeRequiredDefaultUnitsDescription
hydro_idInt32Yes——Hydro plant ID
volume_hm3Float64Yes—hm³Total reservoir volume at this point (hm³). Non-negative and finite.
height_mFloat64Yes—mReservoir surface elevation at this volume (m). Non-negative and finite.
area_km2Float64Yes—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.


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.

NameTypeRequiredDefaultUnitsDescription
fpha_plane_reductionobject | nullNo——Optional block that trims the computed-FPHA hyperplane set after fitting; absent means no reduction is applied.
fpha_plane_reduction.methodstringYes——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_degnumberConditional—°Required when method is "angle". Merge tolerance in degrees; finite and in [0.0, 90.0].
fpha_plane_reduction.tolerance_pctnumberConditional—%Required when method is "distance". Distance tolerance (percentage); finite, >= 0.0.
fpha_plane_reduction.n_samplesintegerConditional——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:

NameTypeRequiredDefaultUnitsDescription
production_models[].hydro_idintegerYes——Hydro plant ID. Must be unique within the file.
production_models[].selection_modestringYes——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”.

NameTypeRequiredDefaultUnitsDescription
production_models[].stage_rangesarrayConditional——Required when selection_mode is "stage_ranges". One entry per stage range.
production_models[].stage_ranges[].start_stage_idintegerYes——First stage (inclusive) to which this entry applies
production_models[].stage_ranges[].end_stage_idinteger | nullNo——Last stage (inclusive); null or absent means open-ended
production_models[].stage_ranges[].modelstringYes——Model name: "constant_productivity", "linearized_head", or "fpha"
production_models[].stage_ranges[].fpha_configobject | nullNo——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_volumeobject | nullNo——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_m3snumber | nullNo—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.

NameTypeRequiredDefaultUnitsDescription
production_models[].default_modelstringConditional——Required when selection_mode is "seasonal". Fallback model name for unlisted seasons
production_models[].seasonsarrayConditional——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:

NameTypeRequiredDefaultUnitsDescription
production_models[].seasons[].season_idintegerYes——Season index (0-based, matching the stages.json season map).
production_models[].seasons[].modelstringYes——Model name: "constant_productivity", "linearized_head", or "fpha"
production_models[].seasons[].fpha_configobject | nullNo——As production_models[].stage_ranges[].fpha_config.
production_models[].seasons[].fpha_config.sourcestringYes——As production_models[].stage_ranges[].fpha_config.source.
production_models[].seasons[].fpha_config.volume_discretization_pointsinteger | nullNo——As production_models[].stage_ranges[].fpha_config.volume_discretization_points.
production_models[].seasons[].fpha_config.turbine_discretization_pointsinteger | nullNo——As production_models[].stage_ranges[].fpha_config.turbine_discretization_points.
production_models[].seasons[].fpha_config.spillage_discretization_pointsinteger | nullNo——As production_models[].stage_ranges[].fpha_config.spillage_discretization_points.
production_models[].seasons[].fpha_config.max_planes_per_hydrointeger | nullNo——As production_models[].stage_ranges[].fpha_config.max_planes_per_hydro.
production_models[].seasons[].fpha_config.fitting_windowobject | nullNo——As production_models[].stage_ranges[].fpha_config.fitting_window.
production_models[].seasons[].fpha_config.fitting_window.volume_min_hm3number | nullNo—hm³As production_models[].stage_ranges[].fpha_config.fitting_window.volume_min_hm3.
production_models[].seasons[].fpha_config.fitting_window.volume_max_hm3number | nullNo—hm³As production_models[].stage_ranges[].fpha_config.fitting_window.volume_max_hm3.
production_models[].seasons[].fpha_config.fitting_window.volume_min_percentilenumber | nullNo——As production_models[].stage_ranges[].fpha_config.fitting_window.volume_min_percentile.
production_models[].seasons[].fpha_config.fitting_window.volume_max_percentilenumber | nullNo——As production_models[].stage_ranges[].fpha_config.fitting_window.volume_max_percentile.
production_models[].seasons[].reference_volumeobject | nullNo——As production_models[].stage_ranges[].reference_volume.
production_models[].seasons[].reference_volume.volume_hm3number | nullNo—hm³As production_models[].stage_ranges[].reference_volume.volume_hm3.
production_models[].seasons[].reference_volume.percentilenumber | nullNo——As production_models[].stage_ranges[].reference_volume.percentile.
production_models[].seasons[].productivity_mw_per_m3snumber | nullNo—MW/(m³/s)As production_models[].stage_ranges[].productivity_mw_per_m3s.

reference_volume fields (optional sibling of fpha_config):

NameTypeRequiredDefaultUnitsDescription
production_models[].stage_ranges[].reference_volume.volume_hm3number | nullNo—hm³Absolute reference volume [hm³]; finite and > 0.0. Mutually exclusive with percentile.
production_models[].stage_ranges[].reference_volume.percentilenumber | nullNo——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:

NameTypeRequiredDefaultUnitsDescription
production_models[].stage_ranges[].fpha_config.sourcestringYes——"precomputed" or "computed"
production_models[].stage_ranges[].fpha_config.volume_discretization_pointsinteger | nullNo——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_pointsinteger | nullNo——Number of turbine-flow grid points; at least 2; absent or null uses 5
production_models[].stage_ranges[].fpha_config.spillage_discretization_pointsinteger | nullNo——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_hydrointeger | nullNo——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_windowobject | nullNo——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.

NameTypeRequiredDefaultUnitsDescription
production_models[].stage_ranges[].fpha_config.fitting_window.volume_min_hm3number | nullNo—hm³Explicit minimum volume for fitting (hm³)
production_models[].stage_ranges[].fpha_config.fitting_window.volume_max_hm3number | nullNo—hm³Explicit maximum volume for fitting (hm³)
production_models[].stage_ranges[].fpha_config.fitting_window.volume_min_percentilenumber | nullNo——Minimum as a percentile of the operating range (0–1)
production_models[].stage_ranges[].fpha_config.fitting_window.volume_max_percentilenumber | nullNo——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" }
}
]
}
]
}

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 the equivalent_productivity / integrated_equivalent_productivity computed tags and the equivalent_productivity_mw_per_m3s / integrated_equivalent_productivity_mw_per_m3s simulation 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 the specific_productivity computed tag. It is resolved per (hydro, stage) as stage-specific row → stage_id = NULL row → the entity’s specific_productivity_mw_per_m3s_per_m in system/hydros.json.
  • reference_outflow_m3s — reaches the reference_turbine computed tag only. It does not change the net head: both evaluators always evaluate the head at the plant’s max_turbined_m3s.
NameTypeRequiredDefaultUnitsDescription
hydro_idInt32Yes——Hydro plant identifier
stage_idInt32 (nullable)Yes——Stage; NULL means “applies to all stages”
equivalent_productivity_mw_per_m3sFloat64 (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_m3sFloat64 (nullable)Yes—m³/sQ_ref override [m³/s]; finite and >= 0.0
specific_productivity_mw_per_m3s_per_mFloat64 (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_id must not be null.
  • equivalent_productivity_mw_per_m3s, when set, must be finite and >= 0.0; 0.0 is 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.0 is 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) by reference_volume in system/hydro_production_models.json; a reference_volume_hm3 column in this file is ignored, with a warning.

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.

NameTypeRequiredDefaultUnitsDescription
hydro_idInt32Yes——Hydro plant ID
stage_idInt32 (nullable)No——Stage the plane applies to. null = valid for all stages
plane_idInt32Yes——Plane index within this hydro (and stage)
gamma_0Float64Yes—MWIntercept coefficient (MW)
gamma_vFloat64Yes—MW/hm³Volume coefficient (MW/hm³). >= 0 (0 for a run-of-river plant).
gamma_qFloat64Yes—MW/(m³/s)Turbined flow coefficient (MW per m³/s). >= 0; each stage’s planes need at least one with > 0.
gamma_sFloat64Yes—MW/(m³/s)Spillage coefficient (MW per m³/s). <= 0.
kappaFloat64 (nullable)No1.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_hm3Float64 (nullable)No—hm³Volume range minimum where this plane is valid (hm³)
valid_v_max_hm3Float64 (nullable)No—hm³Volume range maximum where this plane is valid (hm³)
valid_q_max_m3sFloat64 (nullable)No—m³/sMaximum 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.


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).

NameTypeRequiredDefaultUnitsDescription
hydro_idInt32Yes——Plant whose tailrace this describes
family_idInt32Yes——Family index within the plant (sequential grouping key)
downstream_reference_level_mFloat64 (nullable)Yes—mDownstream reservoir reference level keying this family (m). null when the plant has a single family and no backwater dependency.
segment_idInt32Yes——Piece index within the family
outflow_min_m3sFloat64Yes—m³/sSegment lower validity bound (m³/s). Non-negative.
outflow_max_m3sFloat64Yes—m³/sSegment upper validity bound (m³/s). Non-negative, >= outflow_min_m3s.
coefficient_0Float64Yes——Degree-0 polynomial coefficient. Any sign.
coefficient_1Float64Yes——Degree-1 polynomial coefficient. Any sign.
coefficient_2Float64Yes——Degree-2 polynomial coefficient. Any sign.
coefficient_3Float64Yes——Degree-3 polynomial coefficient. Any sign.
coefficient_4Float64Yes——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_m3s and outflow_max_m3s must be non-negative and finite.
  • outflow_max_m3s >= outflow_min_m3s (segments are non-inverted).
  • coefficient_0 through coefficient_4 must be finite.
  • downstream_reference_level_m, when non-null, must be non-negative and finite.