Hydro Plant Files
This page documents system/hydros.json, the hydro plant registry. The files that supply a plant’s production model, reservoir geometry and tailrace curves are documented on Production Model Files. The other case files are listed in the Case Format overview.
system/hydros.json
Section titled “system/hydros.json”Methodology: System Element Modeling Overview · Hydro Production Function Models · Penalty System
Hydro plant registry. Each entry defines a complete hydro plant with reservoir, turbine, and optional cascade linkage.
Key fields:
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
hydros[].id | integer | Yes | — | — | Plant identifier (integer, unique) |
hydros[].name | string | Yes | — | — | Human-readable plant name |
hydros[].unit_groups | array | Yes | — | — | Turbine groups partitioning the plant’s generation envelope, each on its own bus (see below); an absent, null, or empty array is rejected |
hydros[].downstream_id | integer | null | No | — | — | Downstream plant ID in the cascade; null = tailwater |
hydros[].travel_time_hours | number | null | No | null | h | Water travel time [hours] on the main cascade arc to downstream_id; null or 0.0 = instantaneous. When present and strictly positive, the release is delivered downstream after this delay and carried as in-transit Bellman state (see System Element Modeling Overview §5 and State Augmentation §6). Requires initial_conditions.past_defluences seeding. |
hydros[].operational_start_date | string | Yes | — | — | Required on every entity; see Shared entity fields. |
hydros[].entry_stage_id | integer | null | No | null | — | Stage when plant enters service; null = always exists |
hydros[].exit_stage_id | integer | null | No | null | — | Stage when plant is decommissioned; null = never |
hydros[].reservoir | object | Yes | — | — | min_storage_hm3 and max_storage_hm3 (both >= 0) — the plant’s physical, stage-invariant range: stored-energy outputs, the hydro_useful_volume_* bound fold and the min_storage/max_storage/max_stored_energy computed tags read it; constraints/hydro_bounds.parquet storage overrides tighten only the per-stage operative bounds of the LP storage variable |
hydros[].outflow | object | Yes | — | — | min_outflow_m3s and max_outflow_m3s total outflow bounds; max_outflow_m3s is optional |
hydros[].generation | object | Yes | — | — | Generation model: model, turbine flow bounds, generation MW bounds |
hydros[].generation.model | string | Yes | — | — | One of "constant_productivity", "linearized_head", "fpha". "linearized_head" is a reserved name that resolves to constant productivity (Hydro Production Function Models §3). |
hydros[].specific_productivity_mw_per_m3s_per_m | number | null | No | null | MW/(m³/s)/m | Specific productivity ρ_esp [MW/(m³/s)/m]. Required for FPHA hydros that derive ρ_eq from VHA geometry; on any hydro with system/hydro_geometry.parquet rows it also feeds the useful-range mean evaluator (per-stage overrides in system/hydro_energy_productivity.parquet). |
hydros[].tailrace | object | null | No | — | — | Tailrace model: "polynomial" or "piecewise" |
hydros[].hydraulic_losses | object | null | No | — | — | Head loss model: "factor" or "constant" |
hydros[].efficiency | object | null | No | — | — | Turbine efficiency model: "constant" |
hydros[].evaporation | object | null | No | — | — | Evaporation config: coefficients_mm (12 values) and optional reference_volumes_hm3 |
hydros[].diversion | object | null | No | — | — | Diversion channel: downstream_id and max_flow_m3s |
hydros[].filling | object | null | No | — | — | Filling config: start_stage_id and filling_min_rate_m3s |
hydros[].penalties | object | null | No | — | — | Entity-level hydro penalty overrides (all fields optional, fall back to global) |
Sub-object fields: the keys inside each sub-object listed above.
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
hydros[].reservoir.min_storage_hm3 | number | Yes | — | hm³ | Dead volume; >= 0 and <= max_storage_hm3. |
hydros[].reservoir.max_storage_hm3 | number | Yes | — | hm³ | Maximum storage; >= 0. |
hydros[].outflow.min_outflow_m3s | number | Yes | — | m³/s | Minimum total outflow; >= 0. |
hydros[].outflow.max_outflow_m3s | number | null | No | — | m³/s | null or absent means no upper bound; when set, >= min_outflow_m3s. |
hydros[].generation.min_turbined_m3s | number | Yes | — | m³/s | Minimum turbined flow; >= 0. |
hydros[].generation.max_turbined_m3s | number | Yes | — | m³/s | Maximum turbined flow; >= 0 and >= min_turbined_m3s. |
hydros[].generation.min_generation_mw | number | Yes | — | MW | Minimum generation. |
hydros[].generation.max_generation_mw | number | Yes | — | MW | Maximum generation; >= min_generation_mw. |
hydros[].tailrace.type | string | Yes | — | — | One of "polynomial", "piecewise". Selects the tailrace curve form. |
hydros[].tailrace.coefficients | array | Conditional | — | — | Required when type is "polynomial". Tailrace level as a polynomial in the total outflow, coefficients in ascending powers. |
hydros[].tailrace.points[].outflow_m3s | number | Yes | — | m³/s | Total outflow at the point. The points array is required when type is "piecewise". |
hydros[].tailrace.points[].height_m | number | Yes | — | m | Tailrace level at the point’s outflow. |
hydros[].hydraulic_losses.type | string | Yes | — | — | One of "factor", "constant". Selects the head-loss form. |
hydros[].hydraulic_losses.value | number | Conditional | — | — | Required when type is "factor". A fraction of the gross head, forebay level minus tailrace level. |
hydros[].hydraulic_losses.value_m | number | Conditional | — | m | Required when type is "constant". Head loss, independent of flow and head. |
hydros[].efficiency.type | string | Yes | — | — | One of "constant". Selects the efficiency form. |
hydros[].efficiency.value | number | Yes | — | — | Turbine efficiency as a fraction. |
hydros[].evaporation.coefficients_mm | array | Yes | — | mm/month | Exactly 12 values, January first. A plant declaring them needs system/hydro_geometry.parquet rows. |
hydros[].evaporation.reference_volumes_hm3 | array | null | No | null | hm³ | Exactly 12 linearization volumes, January first. Each is finite and inside [min_storage_hm3, max_storage_hm3]. When null or absent the linearization uses the midpoint of that range. |
hydros[].diversion.downstream_id | integer | Yes | — | — | Plant receiving the diverted water. |
hydros[].diversion.max_flow_m3s | number | Yes | — | m³/s | Channel capacity. |
hydros[].filling.start_stage_id | integer | Yes | — | — | Study stage id at which filling starts. |
hydros[].filling.filling_min_rate_m3s | number | No | 0.0 | m³/s | Minimum filling rate, >= 0; 0.0 is passive filling. |
The generation object accepts no productivity value; a plant’s productivity
comes from system/hydro_production_models.json or
system/hydro_energy_productivity.parquet. Computed FPHA requires the
tailrace, hydraulic_losses and efficiency objects and system/hydro_geometry.parquet rows.
hydros[].unit_groups[] — each group carries its own bus, so one plant
can inject generation onto several electrical buses:
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
hydros[].unit_groups[].id | integer | Yes | — | — | Group identifier: unique within the plant (not globally) |
hydros[].unit_groups[].name | string | Yes | — | — | Human-readable group name |
hydros[].unit_groups[].bus_id | integer | Yes | — | — | Bus where this group’s generation is injected |
hydros[].unit_groups[].min_turbined_m3s | number | Yes | — | m³/s | Minimum turbined flow for this group [m³/s] |
hydros[].unit_groups[].max_turbined_m3s | number | Yes | — | m³/s | Maximum turbined flow for this group [m³/s] |
hydros[].unit_groups[].min_generation_mw | number | Yes | — | MW | Minimum generation for this group [MW] |
hydros[].unit_groups[].max_generation_mw | number | Yes | — | MW | Maximum generation for this group [MW] |
The plant is partitioned into (hydro, bus) cells — one cell per distinct
bus_id among its unit groups. The LP holds one turbine (turbined-flow)
column and one FPHA generation column per cell, and each cell’s generation
injects at that cell’s bus; two groups sharing a bus share that cell’s
columns, so their individual split is undetermined rather than unimplemented
— every equally optimal split has no dual to distinguish it. A plant whose
groups all declare the same bus_id has a single cell.
Stage-varying, optionally per-block overrides on a group’s four declared
bounds are supplied via
constraints/hydro_unit_group_bounds.parquet.
Per-cell simulation output is
simulation/hydro_bus_generation/;
simulation/hydros/ reports each plant’s total.
All fields within hydros[].penalties are optional. When a field is absent the
global default from penalties.json is used. The following fields are supported:
| Name | Type | Required | Default | Units | Description |
|---|---|---|---|---|---|
hydros[].penalties.spillage_cost | number | null | No | null | USD/(m³/s·h) | Spillage penalty (USD/(m³/s·h)). |
hydros[].penalties.turbined_cost | number | null | No | null | USD/(m³/s·h) | Turbined flow regularization cost; applied to every hydro’s turbine column in the LP objective (USD/(m³/s·h)). |
hydros[].penalties.diversion_cost | number | null | No | null | USD/(m³/s·h) | Diversion flow penalty (USD/(m³/s·h)). |
hydros[].penalties.storage_violation_below_cost | number | null | No | null | USD/hm³ | Storage below-minimum violation penalty (USD/hm³). |
hydros[].penalties.filling_target_violation_cost | number | null | No | null | USD/hm³ | Filling target violation penalty (USD/hm³). |
hydros[].penalties.turbined_violation_below_cost | number | null | No | null | USD/(m³/s·h) | Turbined flow below-minimum violation penalty (USD/(m³/s·h)). |
hydros[].penalties.outflow_violation_below_cost | number | null | No | null | USD/(m³/s·h) | Total outflow below-minimum violation penalty (USD/(m³/s·h)). |
hydros[].penalties.outflow_violation_above_cost | number | null | No | null | USD/(m³/s·h) | Total outflow above-maximum violation penalty (USD/(m³/s·h)). |
hydros[].penalties.generation_violation_below_cost | number | null | No | null | USD/MWh | Generation below-minimum violation penalty (USD/MWh). |
hydros[].penalties.evaporation_violation_cost | number | null | No | null | USD/(m³/s·h) | Symmetric evaporation violation penalty (USD/(m³/s·h)). Supplies both directional evaporation costs of this plant; see Directional evaporation and withdrawal costs. |
hydros[].penalties.water_withdrawal_violation_cost | number | null | No | null | USD/(m³/s·h) | Symmetric water withdrawal violation penalty (USD/(m³/s·h)). Supplies both directional withdrawal costs of this plant; see Directional evaporation and withdrawal costs. |
hydros[].penalties.inflow_nonnegativity_cost | number | null | No | null | USD/(m³/s·h) | Override global inflow non-negativity penalty cost for this plant (USD/(m³/s·h)). |
Validation:
entry_stage_idmust be less thanexit_stage_idwhen both are set.- A plant with
fillingmust declareentry_stage_id, withfilling.start_stage_idbefore it, and noexit_stage_id;filling.start_stage_idmust be a declared study stage. - Its
filling_storageseed ininitial_conditions.jsonmust lie in[0, min_storage_hm3), and be0when filling starts at afilling.start_stage_idgreater than0(the declared id, so with study stage ids above0a filling that starts at the first study stage also needs a0seed);filling_min_rate_m3s >= 0. - A filling plant whose
entry_stage_idis at or past the study horizon draws a warning: it fills throughout and never operates in the study. - The filling schedule must reach the dead volume; see Load-time penalty checks.
- Unit-group ids are unique within the plant, and each group’s minimum turbined flow and generation must not exceed its maximum.
- Each group’s
min_turbined_m3sandmax_turbined_m3smust be>= 0. - The groups’ maxima must sum to at most the plant’s maxima and their minima to at least the plant’s minima (turbined flow and generation, each checked against the declared values, not against per-stage overrides).