Powersheds is an open scientific software project for simulating river–reservoir cascades. It combines the performance of **Rust** 🦀 with a friendly **Python** 🐍 interface to model storage, pool elevation, head, releases, spills, routing lags, and power generation at hourly resolution. Designed for coupling with power-system models, simulations are driven by plant-level target power schedules and report realized generation after accounting for hydrologic and operational constraints.
## Getting started
...
- Built in Rust, exposed to Python via PyO3 for speed and usability
- Transparent, reproducible, and extensible for water–power studies
- End-to-end example (Cumberland River) with **R/Quarto** visualization 📊
## Description
...
## Installation
...
---
## Usage
...
⚠️ **Powersheds v0.1 is a beta (development) release. A full, documented release (v1.0) is scheduled for September 2026**.
## Support
...
---
## Contributing
...
## Features
## Authors and acknowledgment
...
🔁 Simple, flexible model set up using a "simulation order" specified for each object allows for cascading and parallel reservoir networks of any configuration and size
## License
...
🏞️ Three object types represented: reservoir, river (with lagged routing), and confluence
♒ Dynamic tracking of head informed by interpolation of storage-pool and release-tailwater elevation tables
📈 Dynamic release and power computation based on head-power-flow tables that can be easily constructed using efficiency curves specified for individual units at each plant (see offline, maximum efficiency DP model for unit commitment)
⚡ Realized hydropower and power violations determined after accounting for water availbility, pool level, and release constraints
⏩ Fast run times (~0.05s for an 8 reservoir network simulated over 168 hours) allow for thousands of simulations on desktop machines, paving the way for many interations within a model coupling environment, or for uncertainty analysis using probabilistic inflow forecasts
🧪 Open science ready: example workflows and typed inputs
---
## 📦 Installation
Powersheds ships as a Python package backed by a Rust extension module. Requires Python 3.12+.
Option A — Develop from source with maturin
1) Create and activate a virtual environment (recommended)
2) Install maturin and build in editable mode
```bash
pip install maturin==1.8.1
maturin develop
```
Option B — Build a wheel and install
```bash
pip install maturin==1.8.1
maturin build --release
pip install target/wheels/powersheds-*.whl
```
Using uv and uv.lock (recommended for examples) 🧰
The repository includes `uv.lock` to capture a reproducible Python environment for examples and docs.
```bash
# Install uv (https://docs.astral.sh/uv/) via pipx or your package manager
# pipx install uv
# Create a virtual environment and sync dependencies from pyproject + uv.lock
uv venv .venv --python 3.12
uv sync--frozen
# Activate the venv before running examples
# On Windows (PowerShell): .\.venv\Scripts\Activate.ps1
# On Unix shells: source .venv/bin/activate
```
Dependencies (pandas, numpy, scipy, matplotlib, PyYAML, pyarrow, etc.) are declared in `pyproject.toml` and will be installed automatically by `uv sync` or when installing a built wheel.
---
## 🚀 Quick Start in Python
```python
importpowersheds
# Minimal single-reservoir cascade (toy numbers for illustration)
cascade_data={
"reservoirs":{},
"rivers":{},
"confluences":{}
}
# Define a reservoir with 24 hours of inflow and power targets
cascade_data["reservoirs"]["DemoReservoir"]={
"object_type":"reservoir",
"simulation_order":1,
"downstream_object":"NA",# terminal object
"capacity":500.0,# Mm3
"initial_pool_elevation":200.0,
"min_power_pool":190.0,
"tailwater_elevation":150.0,
"max_release":2.0,# Mm3/hr
"min_release":0.0,# Mm3/hr
# storage–elevation (SET) curve
"set_storage":[0.0,250.0,500.0],
"set_elevation":[180.0,195.0,210.0],
# hourly inputs
"catchment_inflow":[0.2]*24,
"target_power":[50.0]*24,# MW
# head–power–flow (HPF) grid: cartesian product of H and P
# H (m): [40, 60]
# P (MW): [ 0, 50, 100]
# Q (m3/s): row-major values for each (H,P) pair
"hpf_h":[40,40,40,60,60,60],
"hpf_p":[0,50,100,0,50,100],
"hpf_q":[0,60,120,0,45,90],
}
results=powersheds.simulate_cascade(cascade_data)
res=results["DemoReservoir"]
print("release (Mm3/hr)",res.release[:5])
print("storage (Mm3)",res.storage[:5])
print("power (MW)",res.actual_power[:5])
```
#### Notes
- Power and releases are computed via bilinear interpolation over the HPF grid.
- Storage in Mm3; releases are volumetric in Mm3; head/elevations in meters; power in MW; HPF flows `hpf_q` in cumecs.
---
## 📚 Data Model
Pass a single `cascade_data` mapping with three sections: `reservoirs`, `rivers`, and `confluences`. Each contains a dictionary of named objects.
Reservoir
-`object_type`: "reservoir"
-`simulation_order`: integer; lower runs earlier
-`downstream_object`: name of downstream object or "NA"
-`capacity`: reservoir storage capacity (Mm3)
-`initial_pool_elevation` and `min_power_pool` (m)
-`tailwater_elevation` (m)
-`max_release` and `min_release` (Mm3/hr)
-`set_storage` and `set_elevation`: arrays for the storage–elevation curve
-`catchment_inflow`: list[float] length T
-`target_power`: list[float] length T (MW)
-`hpf_h`, `hpf_p`, `hpf_q`: equal-length arrays defining a regular grid of head (m), power (MW), and corresponding flow (m3/s). The arrays represent the cartesian product of unique head and power axes.
River
-`object_type`: "river"
-`simulation_order`
-`downstream_object`
-`lag`: integer (hours)
-`legacy_flows`: list[float] of length `lag` providing initial outflows
All timeseries share the same length (the number of timesteps in your inputs).
---
## 📊 Example: Cumberland River (R + Quarto)
The `examples/Cumberland` folder provides a full demo with data prep in Python and plotting in R via Quarto.
Contents
-`examples/Cumberland/script.qmd`: prepares inputs, runs the model, plots results
-`examples/Cumberland/cascade_config.yaml`: system topology and parameters
- CSV/Parquet files for inflows, targets, and curves loaded by the script
Run with Quarto/R (requires reticulate and a Python venv)
```r
# In R
reticulate::use_virtualenv(".venv",required=TRUE)
# Open and render the Quarto doc
# quarto render examples/Cumberland/script.qmd
```
---
## 🤝 Contributing
Contributions are welcome! Please open an issue to discuss ideas or submit a pull request. For substantial changes, include a short design note and relevant tests or example updates.
Suggested contributions:
- New example basins or datasets
- Additional object types or routing approaches
- Validation notebooks and benchmarks
---
## 📜 License
TBC
---
## 📣 Citation
TBC
---
## 🙏 Acknowledgments
- Supported by the U.S. Department of Energy’s Water Power Technologies Office as part of the Hydropower Coupler‑ready High‑performance Pliers (HydroCHiPPs) project
- Built with Rust + PyO3 for performance and ergonomy
- Thanks to the open-source scientific Python and Rust communities for foundational tools