Metadata-Version: 2.4
Name: epwtools
Version: 0.3.0
Summary: Python SDK and CLI for epw.tools — generate EPW/DDY weather files from MERRA-2, ERA5 and CMIP6
Project-URL: Homepage, https://epw.tools
Project-URL: Documentation, https://epw.tools/#tab=api
Project-URL: Repository, https://github.com/epwtools/epwtools-python
Project-URL: Bug Tracker, https://github.com/epwtools/epwtools-python/issues
License: Proprietary
Keywords: ashrae,building-simulation,climate,cmip6,energyplus,eplus,epw,future-climate,tmy,weather
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: requests>=2.30
Requires-Dist: rich>=13
Requires-Dist: typer>=0.12
Provides-Extra: aio
Requires-Dist: httpx>=0.27; extra == 'aio'
Provides-Extra: dev
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: responses>=0.25; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Description-Content-Type: text/markdown

# EPW.tools

**Every weather file you'll ever need.**

A web application + API for generating high-quality weather files for building
energy simulation, solar design, climate research, agriculture, insurance
and more.

---

## What it does

EPW.tools fuses measured station observations with global reanalysis and
satellite-derived solar data to produce dense, internally consistent weather
files in over a dozen formats.

| Layer | Source |
|---|---|
| Backbone | ERA5 (ECMWF) and MERRA-2 (NASA) reanalysis |
| Stations | NOAA ISD via Meteostat (~3 500 curated stations worldwide) |
| Solar | NREL NSRDB / PSM v3 where available, Zhang–Huang fallback |
| Future climate | NASA NEX-GDDP-CMIP6 with Belcher 2005 morphing |
| Design conditions | ASHRAE Handbook of Fundamentals |

Measured station data takes priority; small gaps are interpolated; longer
gaps are filled from reanalysis. Every file passes a battery of
physical-plausibility checks before release.

See [RESOURCES.md](RESOURCES.md) for the full source list and references.

---

## Python SDK

The `epwtools` Python package provides a typed client for the REST API plus a
`weatherproof` CLI:

```bash
pip install epwtools
```

### Quick start

```python
from epwtools import EPWClient

client = EPWClient(api_key="your-key-here")  # or set EPWTOOLS_API_KEY

# Single-year AMY
epw = client.generate(lat=40.71, lon=-74.01, year=2023, name="NewYork")

# Typical Meteorological Year
epw = client.tmy(lat=51.50, lon=-0.12, start=2005, end=2024, name="London")

# Future climate (CMIP6, SSP2-4.5, 2050)
epw = client.future(lat=48.85, lon=2.35, scenario="ssp245", horizon=2050)

# Multi-GCM ensemble (one EPW per CMIP6 model — for uncertainty bands)
epws = client.ensemble(lat=48.85, lon=2.35, scenario="ssp245", horizon=2050)

# Climate stress-test for a portfolio (no credits consumed)
table = client.portfolio_stress_test(
    [{"lat": 40.71, "lon": -74.01, "label": "NYC"},
     {"lat": 51.50, "lon": -0.12,  "label": "London"}],
    scenario="ssp245", horizon=2050,
)
```

### Real-time progress (SSE)

```python
resp = client._post("/generate/individual", json={...})
for ev in client.stream(resp["job_id"]):
    if ev["type"] == "log":
        print("  ", ev["line"])
    elif ev["type"] == "status" and ev["status"] == "complete":
        break
```

### Endpoint namespaces

The client exposes every API endpoint under five thin namespaces — use them
when you want to reach a specific endpoint that isn't covered by the
high-level generation methods:

```python
client.tools.uhi_adjust("base.epw", ucz="UCZ-2")
client.tools.koppen("file.epw")              # → {"koppen": "Cfa", ...}
client.tools.solar_calibrate("file.epw")     # PSM3 blend (US)
client.tools.gap_fill("partial.epw")
client.tools.epw_diff("a.epw", "b.epw")      # → hourly delta stats

client.epw.wind_rose(job_id, "out.epw")      # 16 dirs × 6 speed bins
client.epw.heatmap(job_id, "out.epw", variable="dry_bulb")
client.epw.psychro(job_id, "out.epw")

client.reports.solar("file.epw")
client.reports.viticulture("file.epw")       # GDD, Huglin, Winkler
client.reports.datacenter("file.epw")        # free-cooling hours
client.reports.insurance("file.epw", heat_threshold=38)

client.data.cities()                         # ~3,000 curated cities
client.data.stations_isd(40.7, -74.0)        # NOAA ISD nearby
client.data.cmip6_models()                   # available GCMs
client.data.design_conditions(40.7, -74.0)   # ASHRAE for nearest station

client.jobs.get(job_id)
client.jobs.cancel(job_id)
client.jobs.csv(job_id, "out.epw")           # download CSV alt
```

### Async client (for portfolio workloads)

```bash
pip install 'epwtools[aio]'
```

```python
import asyncio
from epwtools import AsyncEPWClient

async def main():
    async with AsyncEPWClient(api_key="...", max_concurrency=8) as client:
        paths = await asyncio.gather(*[
            client.generate(lat=lat, lon=lon, year=2023, name=label)
            for (lat, lon, label) in PORTFOLIO
        ])
    return paths

asyncio.run(main())
```

### CLI

```bash
weatherproof generate --lat 39.74 --lon -104.98 --year 2022 --name Denver
weatherproof tmy      --lat 40.71 --lon -74.01 --start 2005 --end 2024
weatherproof future   --lat 48.85 --lon 2.35   --scenario ssp245 --horizon 2050
weatherproof export   myfile.epw --format xlsx
```

The CLI honours `EPWTOOLS_API_KEY` and `EPWTOOLS_API_URL` env vars (or pass
`--api-url`). Self-hosted backends work too — just point `EPWTOOLS_API_URL`
at your instance.

---

## Features

### Generate
- Single-year and multi-year EPW files for any lat/lon
- Composite-year files that blend multiple seasons
- TMY selection via Sandia / Finkelstein–Schafer
- Future-climate morphing through 2100 from CMIP6 scenarios
- Ensemble runs across GCMs
- Batch processing from a CSV of locations

### Modify
- Altitude / micro-climate correction (lapse rate, barometric, wind log-law)
- Monthly solar calibration against reference data
- Urban heat-island morphing
- Wildfire smoke / atmospheric attenuation overlay

### Inspect
- Visualise any EPW (psychrometric, hourly, monthly)
- Validate against ASHRAE / EnergyPlus range checks
- Köppen–Geiger and ASHRAE / IECC climate-zone classification
- Heating, cooling and billing degree-day reports
- Independent solar sanity check (Zhang–Huang 2002)

### Output formats
EPW, DDY, STAT, WEA (Radiance), CLM (ESP-r), PVSyst .met, CSV, JSON,
Excel, Parquet, NetCDF (CF-1.8), and a one-click `.zip` bundle.

---

## Project structure

```
epw.tools/
├── backend/                  # FastAPI + pandas + xarray
│   ├── app.py                # API routes + job management
│   ├── methods.py            # Core methods
│   ├── ssl_utils.py
│   ├── resources/            # Static lookup data (design conditions, etc.)
│   ├── outputs/              # Generated files (auto-created)
│   └── requirements.txt
├── frontend/                 # React + Vite + Tailwind
│   ├── src/
│   │   ├── App.jsx
│   │   ├── api/client.js
│   │   ├── hooks/
│   │   └── components/
│   └── package.json
├── run_backend.py            # Convenience entry point
├── RESOURCES.md              # Data sources & methodology
└── README.md
```

---

## Setup

### Quick start (5 minutes)

Generate a single-year EPW for any lat/lon from the command line:

```bash
git clone https://github.com/your-org/epwtools.git
cd epwtools
python3 -m venv .venv && source .venv/bin/activate
pip install -r backend/requirements.txt
python run_backend.py &                     # backend on :8000
sleep 3

# Submit a job: New York City, 2022, with default fusion + QC
curl -X POST http://localhost:8000/api/generate/individual \
  -H "Content-Type: application/json" \
  -d '{"lat":40.7,"lon":-74.0,"year":2022,"location_name":"NYC"}'
# → returns {"job_id":"abc-..."}

# Poll until status=="completed", then download:
curl -s http://localhost:8000/api/jobs/abc-.../download/NYC_2022.epw -o nyc.epw
```

The full UI is at `http://localhost:5173` after `cd frontend && npm install && npm run dev`.

### Compare two EPWs (e.g. baseline vs UHI-morphed):

```bash
python tools/epw_diff.py base.epw morphed.epw --summary
# {"row_count":8760,"max_dT_dry_bulb":4.0,"mean_dT_dry_bulb":1.85,...}
```

### Backend

```bash
python3 -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install -r backend/requirements.txt
python run_backend.py            # → http://localhost:8000
```

### Frontend

```bash
cd frontend
npm install
npm run dev                      # → http://localhost:5173
```

The Vite dev server proxies `/api` requests to the backend automatically.

---

## API overview

| Method | Endpoint | Description |
|---|---|---|
| `POST` | `/api/generate/individual`       | Single-year EPW job |
| `POST` | `/api/generate/multi-year`       | Multi-year EPW job |
| `POST` | `/api/generate/composite`        | Composite-year EPW |
| `POST` | `/api/generate/tmy`              | TMY (Sandia / Finkelstein–Schafer) |
| `POST` | `/api/generate/future`           | Future-climate (CMIP6 morphing) |
| `POST` | `/api/generate/ensemble`         | Multi-GCM ensemble |
| `POST` | `/api/generate/metered`          | Raw measured-data download |
| `POST` | `/api/generate/csv-batch`        | Bulk job from CSV |
| `POST` | `/api/tools/altitude-correct`    | Apply lapse / pressure / wind correction |
| `POST` | `/api/tools/solar-calibrate`     | Monthly solar calibration |
| `POST` | `/api/tools/billing-dd`          | Heating / cooling / billing DD |
| `POST` | `/api/tools/koppen`              | Köppen–Geiger classification |
| `POST` | `/api/tools/solar-check`         | Zhang–Huang solar cross-check |
| `POST` | `/api/tools/export-format/{fmt}` | Export EPW to alternative format |
| `GET`  | `/api/jobs/{job_id}`             | Poll job status / logs / result |
| `GET`  | `/api/jobs/{job_id}/stream`      | SSE stream for real-time logs |
| `GET`  | `/api/jobs/{job_id}/download/{filename}` | Download generated file |

---

## License

Proprietary. All rights reserved.
