Add a cascading hydropower system¶
Goal. Introduce a multi-station hydropower cascade (e.g. a new river basin) into a scenario, with per-station reservoir physics and inter-station water-delay routing.
This is the heaviest "add a tech" recipe because hydropower stations
are first-class techs in PREP-SHOT (not aggregated). Each station is
its own row in tech_registry.csv AND has its own reservoir-
parameter rows across ~10 reservoir-specific files. See the
shipped Lower Mekong scenario (examples/southeast_asia, 57
stations) and the Thailand scenario (examples/thailand, 13
stations) for fully-worked examples.
Steps¶
1. Pick station slugs. No spaces; dots stripped. Example for a 3-station cascade:
Upper_Dam, Middle_Dam, Lower_Dam
2. Register each as a hydro tech. In tech_registry.csv:
tech,name,carrier,is_storage
Upper_Dam,Upper Dam,hydro,False
Middle_Dam,Middle Dam,hydro,False
Lower_Dam,Lower Dam,hydro,False
The carrier='hydro' is special-cased; the model loads the
reservoir-physics constraint stack for these techs.
3. Map each station to a zone. In reservoir_zone.csv:
tech,unit,zone
Upper_Dam,zone_code,BA1
Middle_Dam,zone_code,BA1
Lower_Dam,zone_code,BA2
4. Populate per-station reservoir parameters. Each of these files needs one row per station:
reservoir_capacity_max.csv (MW, generator nameplate)
reservoir_capacity_min.csv (MW, usually 0)
reservoir_outflow_max.csv (m^3/s)
reservoir_outflow_min.csv (m^3/s, environmental flow)
reservoir_generation_flow_max.csv (m^3/s through turbines)
reservoir_head.csv (m, designed)
reservoir_coefficient.csv (efficiency, typically ~8.5)
5. Time-varying inputs. reservoir_inflow.csv needs an entry
per (station, year, month, hour); same for
reservoir_storage_max.csv and reservoir_storage_min.csv
(though these are usually constant within a month). Endpoint
volumes go in reservoir_initial_storage_level.csv and
reservoir_final_storage_level.csv.
6. Cascade routing. Define water travel time between connected
stations in reservoir_water_delay_time.csv:
upstream_tech,downstream_tech,delay
Upper_Dam,Middle_Dam,2
Middle_Dam,Lower_Dam,4
Delay is in hours. Stations with no downstream entry are terminal (water leaves the modeled system).
7. Piecewise functions. Two more files describe the non-linear hydropower physics, both in long-format with per-station rows:
reservoir_forebay_level_volume_function.csv-- forebay elevation as a function of reservoir volume.reservoir_tailrace_level_discharge_function.csv-- tailrace elevation as a function of total discharge.
PREP-SHOT linearizes these inside the head-iteration loop.
8. Existing capacity (optional). If the cascade is partly already built:
tech,zone,commission_year,unit,capacity
Upper_Dam,BA1,2020,MW,150
Middle_Dam,BA1,2020,MW,200
9. Per-tech files. Don't forget the eight non-reservoir per-tech files (lifetime, fuel_price, emission_factor, fixed_OM_cost, variable_OM_cost, investment_cost, ramp_up, ramp_down) -- each station needs a row, even if mostly zeros (no fuel, no emissions).
Verify¶
import xarray as xr
ds = xr.open_dataset("output/year.nc")
cascade = ["Upper_Dam", "Middle_Dam", "Lower_Dam"]
print("Installed:")
print(ds["install"].sel(tech=cascade).to_pandas())
print("\nGeneration (sum over time):")
print(ds["gen"].sel(tech=cascade).sum(["hour", "month", "zone"]).to_pandas())
# Reservoir trajectories (only when isinflow=true)
print("\nGeneration flow (sum over hours, by month, year):")
print(ds["genflow"].sel(station=cascade).sum("hour").to_pandas())
Common pitfalls¶
Slug consistency. The same station name must appear in every CSV that mentions it:
tech_registry,reservoir_zone, all thereservoir_*files, and the per-tech files. Mistyping one breaks the load.Cascade direction.
upstream_techis always the station the water leaves;downstream_techis the next station downstream. Reversing them silently produces nonsense.Inflow data shape.
reservoir_inflow.csvis per-(station, year, month, hour). With confighour=8760, month=1, that's 8760 rows per station -- big but unavoidable. Reduce hour count during development.Constraints can make the model infeasible. If
reservoir_outflow_minexceedsreservoir_outflow_maxat any station, or if the demanded generation exceedscapacity_maxfor too many hours, the LP will be reported infeasible by HiGHS with no slack indication. Check your bounds.
Looking at examples/southeast_asia/input/ is the fastest way to
see what a complete 57-station cascade dataset looks like.