Skip to content

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.


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:

NameTypeRequiredDefaultUnitsDescription
hydros[].idintegerYes——Plant identifier (integer, unique)
hydros[].namestringYes——Human-readable plant name
hydros[].unit_groupsarrayYes——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_idinteger | nullNo——Downstream plant ID in the cascade; null = tailwater
hydros[].travel_time_hoursnumber | nullNonullhWater 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_datestringYes——Required on every entity; see Shared entity fields.
hydros[].entry_stage_idinteger | nullNonull—Stage when plant enters service; null = always exists
hydros[].exit_stage_idinteger | nullNonull—Stage when plant is decommissioned; null = never
hydros[].reservoirobjectYes——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[].outflowobjectYes——min_outflow_m3s and max_outflow_m3s total outflow bounds; max_outflow_m3s is optional
hydros[].generationobjectYes——Generation model: model, turbine flow bounds, generation MW bounds
hydros[].generation.modelstringYes——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_mnumber | nullNonullMW/(m³/s)/mSpecific 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[].tailraceobject | nullNo——Tailrace model: "polynomial" or "piecewise"
hydros[].hydraulic_lossesobject | nullNo——Head loss model: "factor" or "constant"
hydros[].efficiencyobject | nullNo——Turbine efficiency model: "constant"
hydros[].evaporationobject | nullNo——Evaporation config: coefficients_mm (12 values) and optional reference_volumes_hm3
hydros[].diversionobject | nullNo——Diversion channel: downstream_id and max_flow_m3s
hydros[].fillingobject | nullNo——Filling config: start_stage_id and filling_min_rate_m3s
hydros[].penaltiesobject | nullNo——Entity-level hydro penalty overrides (all fields optional, fall back to global)

Sub-object fields: the keys inside each sub-object listed above.

NameTypeRequiredDefaultUnitsDescription
hydros[].reservoir.min_storage_hm3numberYes—hm³Dead volume; >= 0 and <= max_storage_hm3.
hydros[].reservoir.max_storage_hm3numberYes—hm³Maximum storage; >= 0.
hydros[].outflow.min_outflow_m3snumberYes—m³/sMinimum total outflow; >= 0.
hydros[].outflow.max_outflow_m3snumber | nullNo—m³/snull or absent means no upper bound; when set, >= min_outflow_m3s.
hydros[].generation.min_turbined_m3snumberYes—m³/sMinimum turbined flow; >= 0.
hydros[].generation.max_turbined_m3snumberYes—m³/sMaximum turbined flow; >= 0 and >= min_turbined_m3s.
hydros[].generation.min_generation_mwnumberYes—MWMinimum generation.
hydros[].generation.max_generation_mwnumberYes—MWMaximum generation; >= min_generation_mw.
hydros[].tailrace.typestringYes——One of "polynomial", "piecewise". Selects the tailrace curve form.
hydros[].tailrace.coefficientsarrayConditional——Required when type is "polynomial". Tailrace level as a polynomial in the total outflow, coefficients in ascending powers.
hydros[].tailrace.points[].outflow_m3snumberYes—m³/sTotal outflow at the point. The points array is required when type is "piecewise".
hydros[].tailrace.points[].height_mnumberYes—mTailrace level at the point’s outflow.
hydros[].hydraulic_losses.typestringYes——One of "factor", "constant". Selects the head-loss form.
hydros[].hydraulic_losses.valuenumberConditional——Required when type is "factor". A fraction of the gross head, forebay level minus tailrace level.
hydros[].hydraulic_losses.value_mnumberConditional—mRequired when type is "constant". Head loss, independent of flow and head.
hydros[].efficiency.typestringYes——One of "constant". Selects the efficiency form.
hydros[].efficiency.valuenumberYes——Turbine efficiency as a fraction.
hydros[].evaporation.coefficients_mmarrayYes—mm/monthExactly 12 values, January first. A plant declaring them needs system/hydro_geometry.parquet rows.
hydros[].evaporation.reference_volumes_hm3array | nullNonullhm³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_idintegerYes——Plant receiving the diverted water.
hydros[].diversion.max_flow_m3snumberYes—m³/sChannel capacity.
hydros[].filling.start_stage_idintegerYes——Study stage id at which filling starts.
hydros[].filling.filling_min_rate_m3snumberNo0.0m³/sMinimum 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:

NameTypeRequiredDefaultUnitsDescription
hydros[].unit_groups[].idintegerYes——Group identifier: unique within the plant (not globally)
hydros[].unit_groups[].namestringYes——Human-readable group name
hydros[].unit_groups[].bus_idintegerYes——Bus where this group’s generation is injected
hydros[].unit_groups[].min_turbined_m3snumberYes—m³/sMinimum turbined flow for this group [m³/s]
hydros[].unit_groups[].max_turbined_m3snumberYes—m³/sMaximum turbined flow for this group [m³/s]
hydros[].unit_groups[].min_generation_mwnumberYes—MWMinimum generation for this group [MW]
hydros[].unit_groups[].max_generation_mwnumberYes—MWMaximum 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:

NameTypeRequiredDefaultUnitsDescription
hydros[].penalties.spillage_costnumber | nullNonullUSD/(m³/s·h)Spillage penalty (USD/(m³/s·h)).
hydros[].penalties.turbined_costnumber | nullNonullUSD/(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_costnumber | nullNonullUSD/(m³/s·h)Diversion flow penalty (USD/(m³/s·h)).
hydros[].penalties.storage_violation_below_costnumber | nullNonullUSD/hm³Storage below-minimum violation penalty (USD/hm³).
hydros[].penalties.filling_target_violation_costnumber | nullNonullUSD/hm³Filling target violation penalty (USD/hm³).
hydros[].penalties.turbined_violation_below_costnumber | nullNonullUSD/(m³/s·h)Turbined flow below-minimum violation penalty (USD/(m³/s·h)).
hydros[].penalties.outflow_violation_below_costnumber | nullNonullUSD/(m³/s·h)Total outflow below-minimum violation penalty (USD/(m³/s·h)).
hydros[].penalties.outflow_violation_above_costnumber | nullNonullUSD/(m³/s·h)Total outflow above-maximum violation penalty (USD/(m³/s·h)).
hydros[].penalties.generation_violation_below_costnumber | nullNonullUSD/MWhGeneration below-minimum violation penalty (USD/MWh).
hydros[].penalties.evaporation_violation_costnumber | nullNonullUSD/(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_costnumber | nullNonullUSD/(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_costnumber | nullNonullUSD/(m³/s·h)Override global inflow non-negativity penalty cost for this plant (USD/(m³/s·h)).

Validation:

  • entry_stage_id must be less than exit_stage_id when both are set.
  • A plant with filling must declare entry_stage_id, with filling.start_stage_id before it, and no exit_stage_id; filling.start_stage_id must be a declared study stage.
  • Its filling_storage seed in initial_conditions.json must lie in [0, min_storage_hm3), and be 0 when filling starts at a filling.start_stage_id greater than 0 (the declared id, so with study stage ids above 0 a filling that starts at the first study stage also needs a 0 seed); filling_min_rate_m3s >= 0.
  • A filling plant whose entry_stage_id is 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_m3s and max_turbined_m3s must 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).