"Bash(git -C \"C:\\\\Users\\\\snk\\\\powersheds\" commit -m \"$\\(cat <<''EOF''\nadd CLAUDE.md for Claude Code guidance\n\nProvides build commands, architecture overview, and key concepts\nfor future Claude Code instances working in this repository.\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
"Bash(git -C C:/Users/snk/powersheds commit -m \"$\\(cat <<''EOF''\nHandle infeasible power targets with symmetric clamping\n\nWhen target power falls outside the feasible range at a given head,\nthe HPF interpolation now clamps to the nearest feasible boundary\n\\(min or max\\) rather than returning NaN. This maintains roundtrip\nconsistency between P→Q and Q→P functions.\n\nChanges:\n- src/helpers.rs: Implement symmetric clamping for infeasible power,\n use linear scan for P index lookup \\(binary search unreliable with\n approx_eq\\), remove duplicate actual_power calculation in lib.rs\n- tests/test_hpf.py: Update tolerances for flat regions \\(~2 MW corner,\n ~1.5 MW edge\\), filter clamping cases from roundtrip tests, increase\n performance thresholds for linear scan algorithm\n\nFixes NaN values in target_release for Cumberland example simulation.\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Powersheds is a hydropower cascade simulator combining Rust performance with a Python interface. It simulates river-reservoir cascades at hourly resolution, computing storage, pool elevation, head, releases, spills, routing lags, and power generation driven by plant-level power schedules. The simulation is driven by hourly inflows (total inflows for headwater reservoirs; incremental/side inflows for in-network reservoirs) and target hourly power generation time series for each hydropower dam. The purpose of the model is computation of infeasible power generation, as measured by violations in power generation after accounting for water-related constraints in the simulation.
**Status**: v0.1 beta; v1.0 scheduled for September 2026.
## Confidentiality
The `examples/private/` directory contains proprietary data for private hydropower systems. This directory is excluded from version control via `.gitignore`. **Never** commit, push, or include any data, file paths, reservoir names, parameter values, or other details from `examples/private/` in the public repository — this includes CLAUDE.md, test fixtures, regression baselines, commit messages, PR descriptions, generated outputs (diagnostics HTML, plots, notebook cell outputs), and any other artifacts.
## Build Commands
```bash
# Create virtual environment (recommended)
uv venv .venv --python 3.12
uv sync--frozen
# Activate: .\.venv\Scripts\Activate.ps1 (Windows) or source .venv/bin/activate (Unix)
# Build and install in editable mode (after activating venv)
maturin develop
# Build release wheel
maturin build --release
pip install target/wheels/powersheds-*.whl
```
After any Rust code changes, run `maturin develop` to recompile.
## Architecture
**Rust-Python Bridge**: Core simulator written in Rust for speed, exposed to Python via PyO3.
-`python/powersheds/__init__.py` - Package init, re-exports from `_lib`
-`python/powersheds/diagnostics.py` - Interactive diagnostic visualization (see Diagnostics section)
**Three Object Types**:
-**Reservoir**: Pool elevation from storage-elevation curves, head calculation (with optional dynamic tailwater), release/power via bilinear HPF interpolation, storage/capacity constraints
-**River**: Lag-based flow routing with legacy flows for initialization
-**Confluence**: Instantaneous flow merging (no lag)
**Simulation Order**: Objects execute in user-specified order (lower `simulation_order` runs first), enabling flexible cascade topologies.
**Key Algorithm Flow** (per timestep):
1. Sort objects by simulation order
2. For each reservoir: interpolate pool elevation → compute head (with dynamic tailwater iteration if TW table provided) → bilinear HPF lookup for target release → constrain release → update storage → compute actual power
3. Rivers delay flows by `lag` hours; confluences pass through immediately
4. Upstream releases/spills become downstream inflows
**Dynamic Tailwater**: When `set_tw_outflow`/`set_tw_elevation` are provided, tailwater elevation is computed from total outflow via a rating curve, using fixed-point iteration (tolerance=0.001m, max_iter=10, damping=0.5). When empty, the static `tailwater_elevation` scalar is used (legacy behavior).
## Units
- Storage: Mm³ (Million Cubic Meters)
- Releases: Mm³/hr
- Head/Elevations: meters
- Power: MW
- HPF flows (`hpf_q`): m³/s (cumecs)
- TW outflow (`set_tw_outflow`): m³/s (cumecs)
## Diagnostics
The `powersheds.diagnostics` module provides interactive visualization for simulation results. Requires the `viz` optional dependency: `uv pip install -e ".[viz]"`
-`cascade_diagnostics(results, cascade_data=None, ...)` → Plotly `go.Figure` with dropdown reservoir selector, three linked time series panels (flows with release constraint band, elevation with pool/tailwater/head, power with infeasible shading). Pass `cascade_data` to enable min/max release bands on the flows panel.
-`network_diagram(cascade_data, ...)` → matplotlib `Figure` with publication-quality static network diagram (trapezoid reservoir nodes, Bezier edges, lag labels)
-`save_diagnostics_html(results, output_path, ...)` → writes standalone HTML file
**Internal helpers** (prefixed with `_`):
-`_build_topology(cascade_data)` - Extract nodes/edges from CascadeData
-`tests/test_cascade.py` - Full cascade integration (13 tests): output structure, multi-reservoir routing, simulation ordering, mass balance, physical invariants, Cumberland NaN check
-`tests/test_simulate_timestep.py` - Per-timestep reservoir constraints (10 tests): below power pool, min/max release, insufficient water, spill, mass balance, unconstrained power equality
-`tests/fixtures/cumberland_baseline.json` - Golden baseline for regression testing
-`tests/TEST_DOCUMENTATION.md` - Detailed documentation of every test
**Known Limitations**:
- Some HPF tables have flat regions where multiple P values produce the same Q. In these regions, the reverse function (Q->P) cannot uniquely determine P, resulting in roundtrip errors up to ~1 MW. This is a physical limitation of the data, not a bug.
## Dependencies
-**Build**: maturin 1.9.4 (Rust-to-Python bridge)
-**Rust**: PyO3 0.23.3
-**Python**: pandas, numpy, scipy, matplotlib, PyYAML, pyarrow (pinned in `uv.lock`)