Pywr · git:20260817.6859167 · 2026-08-17 · sha256 52438d623fd858fe
Pywr git:20260817.6859167A
Immutable. This exact content never changes and is served at /api/v1/blob/52438d623fd858fe.
---
name: pywr
description: >-
Pywr 1.30.0. Covers Generalised directed-network resource allocation, primarily
water-resource systems; Supply-demand balance solved each timestep as a linear program
over node costs and flow constraints; Reservoir/storage operation: storage continuity,
control curves, min/max volume, regulated release…. Use when the task involves running,
configuring, calibrating or interpreting Pywr.
---
> **MANDATORY EXECUTION POLICY** — READ BEFORE PROCEEDING
>
> You MUST run the **actual model binary or package** described in this document.
> If the model fails to import, compile, or execute, you MUST:
> 1. Check `diagnostics/triplets.yaml` for a matching error pattern
> 2. Try the fix suggested in the triplet's `remedy` section
> 3. If still failing, report the error to the user with full details
>
> You MUST NOT substitute a simplified Python formula, regression equation,
> or hand-coded approximation in place of the real model. Doing so produces
> scientifically invalid results and defeats the purpose of the KI.
>
> Before starting, run: `python preflight_check.py` (in this KI directory)
> to verify that the model binary/package and required data are available.
>
> **DEBUGGING PROTOCOL** — When something goes wrong, follow this order:
> 1. **Check triplets** — `diagnostics/triplets.yaml` may already cover this error
> 2. **Read official docs** — The model's own documentation for expected formats/units
> 3. **Find working examples** — Check `outputs/` or the model's shipped test data
> 4. **Fix the tool** — With knowledge of what "correct" looks like
>
> Do NOT write custom debug scripts. The answers are in the docs and examples.
---
## Forcing Data
Pywr requires time-varying forcing input files that drive the water allocation model:
| Forcing Variable | Source | Format | Units |
|-----------------|--------|--------|-------|
| Reservoir inflow | VIC runoff+baseflow or observed discharge | CSV timeseries | m3/s |
| Irrigation demand | DSSAT crop water stress or prescribed schedule | JSON parameter | m3/s |
| Municipal/industrial demand | Prescribed or population-scaled | JSON parameter | m3/s |
| Precipitation (optional) | For direct-rainfall reservoirs | CSV timeseries | mm/day |
All forcing input files must cover the simulation period with daily timestep. Missing days cause Pywr to raise `DataFrameKeysError`.
## Configuration
Pywr models are configured through a single JSON model file assembled by `assemble_pywr_model.py`. Key configuration settings:
| Setting | Location | Default | Notes |
|---------|----------|---------|-------|
| Simulation period | `metadata.minimum_version` + `timestepper` | — | `start`, `end`, `timestep` (days) |
| Solver | `solver.name` | `glpk` | Only GLPK supported in this KI |
| Node costs | Each node's `cost` parameter | varies | More negative = higher priority |
| Control curve | `parameters` section | — | Monthly target storage levels (fraction) |
| Demand profiles | `parameters` section | — | `monthlyprofile` or `dataframe` type |
The JSON config file is the only configuration needed — no `.ini`, `.cfg`, or namelist files.
## Data Preparation
### Input data
**Input Source**: Pywr takes inflow time series from upstream models (VIC) or observations.
- `convert_vic_to_inflow.py` — Converts VIC/CaMa-Flood discharge to Pywr inflow DataFrame
- `convert_obs_to_inflow.py` — Converts observed discharge to Pywr inflow format
- `find_dams_in_basin.py` — Finds dams from GRanD database within basin
- `build_reservoir_properties.py` — Builds reservoir properties (capacity, area, elevation) from GRanD/HydroLAKES
**Data Validation Reference**: See `data_ki/GRanD/SKILL.md` for dam/reservoir database documentation.
See `data_ki/HydroLAKES/SKILL.md` for lake morphometry documentation.
---
# Pywr Knowledge Infrastructure — Agent Entry Point
**Package**: `pywr-knowledge-infrastructure` v1.0.0
**Model**: Pywr 1.30.0 (Python Water Resources Framework)
**Domain**: Reservoir operations, water resources management, flood control, water supply
**Role in HydroCraft**: Fills the reservoir operations gap — couples with VIC (inflow), CaMa-Flood (regulated routing), DSSAT (irrigation demand), DLBreach (dam-break trigger)
---
## What This Enables
An AI agent can autonomously:
1. Find dams within any basin using the GRanD global database (7,320 dams)
2. Build reservoir physical properties from GRanD data (capacity, area-volume, dead storage)
3. Convert VIC model output to reservoir inflow timeseries
4. Generate operating rules (flood control, irrigation, hydropower) with regional flood seasons
5. Assemble and run a complete Pywr water allocation model
6. Inject regulated releases back into CaMa-Flood for downstream routing
7. Detect overtopping events and trigger DLBreach dam-break analysis
## Quick Reference
| Component | What | Where |
|-----------|------|-------|
| **Python package** | `pywr` 1.30.0 | `KISSPATH_HOME/桌面/test/hydro-claude/python_env/bin/python3` |
| **Solver** | GLPK (GNU Linear Programming Kit) | System library |
| **Dam database** | GRanD_allocated.csv (7,320 dams) | `model/cmf_v420_pkg/map/data/GRanD_allocated.csv` |
| **Input format** | JSON model file + CSV timeseries | Generated by assemble_pywr_model.py |
| **VIC results** | Daily flux files | `outputs/{run}/vic_result/` |
| **CaMa results** | Daily discharge NetCDF | `model/cmf_v420_pkg/out/{run}/` |
## Pipeline Overview (8 Stages)
```
S1 Verify Pywr ──> S2 Find Dams ──> S3 Reservoir Properties
│
v
S4 VIC->Inflow ──> S5 Operating Rules
│ │
v │
S6 Demands ──────────> S7 Assemble Model
│
v
S8 Run + Plot + CaMa Inject
```
## Tools Reference
| Stage | Tool ID | Script | Purpose |
|-------|---------|--------|---------|
| S1 | `verify_pywr_installation` | `tools/s1_installation/verify_pywr_installation.py` | Check Pywr + GLPK + dependencies |
| S2 | `find_dams_in_basin` | `tools/s2_dam_inventory/find_dams_in_basin.py` | Search GRanD for dams in basin |
| S3 | `build_reservoir_properties` | `tools/s3_reservoir_properties/build_reservoir_properties.py` | Compute volumes, area-volume curve |
| S4 | `convert_vic_to_inflow` | `tools/s4_inflow/convert_vic_to_inflow.py` | VIC runoff+baseflow to m3/s inflow |
| S5 | `create_operating_rules` | `tools/s5_operating_rules/create_operating_rules.py` | Monthly control curves, release rules |
| S6 | `create_demand_nodes` | `tools/s6_demands/create_demand_nodes.py` | Environmental, irrigation, municipal demands |
| S7 | `assemble_pywr_model` | `tools/s7_assembly/assemble_pywr_model.py` | Generate complete Pywr JSON |
| S8 | `run_pywr` | `tools/s8_execution/run_pywr.py` | Run model, extract results |
| S8 | `plot_reservoir_operations` | `tools/s8_execution/plot_reservoir_operations.py` | Multi-panel visualization |
| S8 | `inject_releases_to_cama` | `tools/s8_execution/inject_releases_to_cama.py` | Regulated flow -> CaMa runoff |
| S8 | `check_overtopping` | `tools/s8_execution/check_overtopping.py` | DLBreach trigger evaluation |
## GRanD Dam Database
The Global Reservoir and Dam (GRanD) database is at:
```
model/cmf_v420_pkg/map/data/GRanD_allocated.csv
```
| Stat | Count |
|------|-------|
| Total dams | 7,320 |
| China | 1,571 |
| Huaihe basin | ~68 |
| Has height data | ~90% |
| Has capacity data | 100% |
Key columns: `ID, lat_alloc, lon_alloc, DamName, RiverName, CAP_MCM, DAM_HGT_M, YEAR, ELEV_MASL, area_alloc`
## HydroCraft Coupling Summary
| Coupling | Direction | Unit Conversion | Tool |
|----------|-----------|-----------------|------|
| VIC -> Pywr | runoff+baseflow (mm/day) -> inflow (m3/s) | `* cell_area_m2 / (1000 * 86400)` | `convert_vic_to_inflow` |
| Pywr -> CaMa | release (m3/s) -> runoff (mm/day) | `* 86400 / cell_area_m2 * 1000` | `inject_releases_to_cama` |
| DSSAT -> Pywr | irrigation need (mm) -> demand (m3/s) | `* area_m2 / (1000 * 86400 * efficiency)` | `create_demand_nodes` |
| Pywr -> DLBreach | overtopping (m3) -> breach trigger | excess volume + inflow hydrograph | `check_overtopping` |
## Critical Domain Knowledge
1. **Pywr uses GLPK linear programming.** Every timestep is an independent LP problem that optimally allocates water. The solver minimizes total cost across the network. Negative costs attract water (storage fills); positive costs repel water (storage empties).
2. **Node types are lowercase.** `storage`, `input`, `output`, `link`, `catchment`. Using `Storage` or `Input` causes a parse error.
3. **Parameter types are lowercase.** `constant`, `monthlyprofile`, `dataframe`, `controlcurve`. Using `MonthlyProfile` causes a parse error.
4. **MCM to m3: multiply by 1e6.** GRanD CAP_MCM is in million cubic meters. Pywr volumes are in m3. Forgetting `* 1e6` makes the reservoir 1 million times too small.
5. **VIC mm/day to m3/s requires two conversions.** Divide by 1000 (mm to m) AND divide by 86400 (day to seconds). Missing either produces inflow off by orders of magnitude.
6. **Dataframe CSV paths are resolved relative to model JSON location.** Use absolute paths in the `url` field to avoid FileNotFoundError.
7. **Control curves are essential for realistic operation.** Without a ControlCurveParameter, the LP solver either fills to max or empties to min with no intermediate regulation. The control curve provides monthly target levels.
8. **Environmental flow has highest priority (most negative cost).** In Pywr's cost-based allocation, more negative cost = higher priority. Environmental flow: -1000, municipal: -900, irrigation: -800, industrial: -700, spill: +1000.
9. **Spill path is mandatory.** Every storage node must have a spill/overflow path to avoid InfeasibleError when inflow exceeds demand + storage capacity.
10. **CaMa injection replaces, not adds.** `inject_releases_to_cama.py` replaces the VIC runoff at the dam cell with Pywr release. It does NOT add to existing runoff. This is correct because the release already includes the upstream catchment water.
## Python Environment
Pywr is installed in a separate environment from HydroCraft's main venv:
```bash
# Pywr-capable Python:
KISSPATH_HOME/桌面/test/hydro-claude/python_env/bin/python3
# HydroCraft main venv (does NOT have Pywr):
KISSPATH_PYTHON_ENV/bin/python
```
For tools that need both Pywr AND HydroCraft dependencies (geopandas, xarray, etc.), use the Pywr environment which has all dependencies.
## Error Handling
When errors occur, look up symptoms in `diagnostics/triplets.yaml` (15 triplets). Key triplets:
- **dt_pywr_001**: GLPK not found — install libglpk-dev
- **dt_pywr_005**: Inflow all zeros — wrong VIC flux file format or upstream cells
- **dt_pywr_009**: InfeasibleError — demands exceed supply, add spill path or reduce demands
- **dt_pywr_011**: CaMa injection negative runoff — cell area unit error
- **dt_pywr_014**: MCM to m3 mismatch — forgot `* 1e6` in capacity conversion
- **dt_pywr_015**: VIC mm/day to m3/s conversion — forgot `/1000` or `/86400`
## Quick Start Example
```bash
PYWR_PYTHON="KISSPATH_HOME/桌面/test/hydro-claude/python_env/bin/python3"
TOOLS="models/Pywr/knowledge_infrastructure/tools"
GRAND="model/cmf_v420_pkg/map/data/GRanD_allocated.csv"
# 1. Verify installation
$PYWR_PYTHON $TOOLS/s1_installation/verify_pywr_installation.py
# 2. Find dams in basin
$PYWR_PYTHON $TOOLS/s2_dam_inventory/find_dams_in_basin.py \
--shp data/shp/bengbu_shp/bengbu_clip.shp \
--grand $GRAND --output /tmp/dams.json
# 3. Build reservoir properties (pick first dam)
$PYWR_PYTHON $TOOLS/s3_reservoir_properties/build_reservoir_properties.py \
--dam_json /tmp/dams.json --dam_index 0 --output /tmp/reservoir.json
# 4. Convert VIC to inflow
$PYWR_PYTHON $TOOLS/s4_inflow/convert_vic_to_inflow.py \
--vic_dir outputs/bengbu_1980-1990_025deg/vic_result \
--grid_nc outputs/bengbu_1980-1990_025deg/vic_temp/grid/grid_bengbu_1980-1990_025deg_025deg.nc \
--dam_lat 31.43 --dam_lon 115.92 --method all \
--start_year 1980 --end_year 1990 --output /tmp/inflow.csv
# 5. Create operating rules
$PYWR_PYTHON $TOOLS/s5_operating_rules/create_operating_rules.py \
--reservoir_json /tmp/reservoir.json --purpose flood_control \
--region china_east --output /tmp/rules.json
# 6. Create demand nodes
$PYWR_PYTHON $TOOLS/s6_demands/create_demand_nodes.py \
--type environmental --mean_annual_flow_m3s 150.0 \
--output /tmp/demands.json
# 7. Assemble model
$PYWR_PYTHON $TOOLS/s7_assembly/assemble_pywr_model.py \
--reservoir_json /tmp/reservoir.json --inflow_csv /tmp/inflow.csv \
--rules_json /tmp/rules.json --demands_json /tmp/demands.json \
--start_date 1980-01-01 --end_date 1990-12-31 \
--output /tmp/model.json
# 8. Run model
$PYWR_PYTHON $TOOLS/s8_execution/run_pywr.py \
--model /tmp/model.json --output_dir /tmp/pywr_results/
# 9. Plot results
$PYWR_PYTHON $TOOLS/s8_execution/plot_reservoir_operations.py \
--storage_csv /tmp/pywr_results/storage_timeseries.csv \
--release_csv /tmp/pywr_results/release_timeseries.csv \
--inflow_csv /tmp/inflow.csv --rules_json /tmp/rules.json \
--output /tmp/pywr_results/operations_plot.png \
--title "Huaihe Basin Reservoir"
```
---
*This knowledge infrastructure was built using the Knowledge Dissection Toolkit v1.0 (Jianyun Zhang Research Group, Hohai University).*