Commit e0c2af74 authored by Turner, Sean's avatar Turner, Sean
Browse files

readme

parent 1df519df
Loading
Loading
Loading
Loading
+226 −20
Original line number Diff line number Diff line
# Powersheds 🏞️🔌
# 🔌🏞️ Powersheds
## *---A Fast, Open Hydropower Cascade Simulator*

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

🔌 Coupler-ready, Python API: simple dict/dataclass inputs; rich timeseries outputs

🧪 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
import powersheds

# 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

Confluence

- `object_type`: "confluence"
- `simulation_order`
- `downstream_object`

---

## 🧮 API Overview

Python entry point

```python
powersheds.simulate_cascade(cascade_data: dict) -> dict[str, Result]
```

Returned per-object results:

- Reservoir: `storage`, `pool_elevation`, `head`, `release`, `spill`, `catchment_inflow`, `total_inflow`, `target_release`, `target_power`, `actual_power`
- River: `inflow`, `outflow` (with travel-time lag)
- Confluence: `inflow`, `outflow` (no lag)

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
## Project status
...
 No newline at end of file