Skip to content

Repository files navigation

RainReserve

Size rainwater storage from the dry spells your averages hide.

CI License: MIT Python 3.11+

RainReserve is a local, deterministic CLI for early-stage rainwater-storage planning. Give it daily rainfall, a roof catchment, first-flush loss, and daily demand. It replays every day, exposes every litre collected or lost, and selects the smallest capacity on your declared grid that meets all reliability targets.

It is not a one-storm barrel calculator and not a dashboard shell. Its source of truth is a daily mass-balance ledger that can be reviewed, diffed, and rerun offline.

What it produces

  • summary.json — selected capacity, targets, totals, reliability, and mass-balance error.
  • daily.csv — rainfall, collection, first flush, overflow, demand, service, shortfall, and storage.
  • capacities.csv — every tested tank capacity and why it passed or failed.
  • report.svg — a standalone storage/shortfall chart suitable for issues and documents.
  • report.html — a script-free, standalone review report with the same evidence.

Invalid inputs fail before an output directory is created. A valid project that cannot meet its targets up to the configured maximum returns evidence and exit code 1, rather than pretending the largest tank worked.

Quick start

git clone https://github.com/KanadeK/rainreserve.git
cd rainreserve
uv sync --locked --dev

uv run --no-sync rainreserve validate examples/garden/project.json
uv run --no-sync rainreserve size examples/garden/project.json --out build/garden

Or install a wheel from the latest GitHub Release:

python -m pip install .\rainreserve-0.1.0-py3-none-any.whl
rainreserve demo --out rainreserve-demo

Input contract

project.json declares the model and points to a continuous daily CSV:

{
  "schema_version": 1,
  "name": "Workshop garden",
  "rainfall_csv": "rainfall.csv",
  "catchment": {
    "area_m2": 42,
    "runoff_coefficient": 0.82,
    "first_flush_l": 18
  },
  "demand": { "default_daily_l": 55 },
  "sizing": {
    "min_capacity_l": 250,
    "max_capacity_l": 4000,
    "step_l": 250,
    "target_day_reliability": 0.9,
    "target_volume_reliability": 0.95,
    "max_shortfall_run_days": 4
  },
  "simulation": { "initial_storage_l": 0 }
}

rainfall.csv requires date,rain_mm. demand_l is optional and overrides the default on that day:

date,rain_mm,demand_l
2026-01-01,4.2,
2026-01-02,0,35
2026-01-03,11.8,

Dates must be unique, ordered, and continuous. Add dry days as 0; RainReserve never guesses what a missing date means.

Commands and exit codes

rainreserve validate PROJECT
rainreserve simulate PROJECT --capacity-l LITRES --out DIRECTORY
rainreserve size PROJECT --out DIRECTORY
rainreserve demo --out DIRECTORY
Exit Meaning
0 Valid input; requested simulation or sizing target succeeded.
1 Valid project, but no tested capacity met every sizing target.
2 Invalid JSON/CSV/CLI input; no report directory was created.

Boundary errors include stable codes such as [DATE_GAP], [CSV_HEADER], and [PROJECT_RANGE] so automation can preserve the actual failure instead of matching vague prose.

The model

One millimetre of rain on one square metre is one litre before losses. For each day RainReserve:

  1. multiplies rainfall by catchment area and runoff coefficient;
  2. subtracts first flush once at the start of each consecutive rain event;
  3. adds captured water and records overflow above capacity;
  4. serves that day's demand and records any shortfall;
  5. carries closing storage into the next day.

The release gate verifies this invariant for every run:

initial storage + captured = served + overflow + ending storage

Capacity sizing tests the configured grid in ascending order. A capacity passes only when day reliability, volume reliability, and longest consecutive shortfall all meet their targets.

Acceptance

The same gate runs locally and in GitHub Actions on Linux and Windows:

uv run --no-sync python scripts/check.py

It runs formatting, lint, strict typing, branch coverage, vulnerability audit, real CLI success and failure fixtures, deterministic demo generation, wheel/sdist inspection, and a clean wheel install.

If it fails, follow docs/repair.md. The short version is: run the first failing command printed by the gate, fix that cause, then rerun the single gate once code has changed.

Scope and safety

RainReserve is planning evidence. It does not decide potable-water safety, treatment, mosquito or contamination controls, structural loads, overflow routing, pumps, pipe sizes, permits, or code compliance. Historical rainfall does not guarantee future rainfall. Have the physical system and any health-critical use reviewed by qualified local professionals.

Runtime is network-free and the tool never uploads project data.

Why this project

Public rainwater examples commonly stop at a one-off formula or an exploratory notebook. RainReserve packages the useful continuous daily mass balance into a reproducible CLI with explicit input contracts, capacity-search evidence, stable failure behavior, and installable release artifacts.

Development

uv sync --locked --dev
uv run --no-sync pytest -q
uv run --no-sync ruff check .
uv run --no-sync mypy src tests scripts

See docs/spec.md for the v0.1 contract and CHANGELOG.md for release notes. Contributions are welcome through focused issues and pull requests.

License

MIT © KanadeK

About

Auditable, offline rainwater storage sizing from daily rainfall and demand.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages