Python SDK and CLI for epw.tools — generate EPW/DDY weather files from MERRA-2, ERA5 and CMIP6
Project description
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 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:
pip install epwtools
Quick start
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)
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:
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)
pip install 'epwtools[aio]'
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
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:
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):
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
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
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file epwtools-0.3.1.tar.gz.
File metadata
- Download URL: epwtools-0.3.1.tar.gz
- Upload date:
- Size: 25.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f6887b75160ab44fac22b2df6b1e2f788830d4530cd4eff2b18f59f2f34afe2
|
|
| MD5 |
606a82e3a6da7f0fdb26627722d17745
|
|
| BLAKE2b-256 |
7723c1afc953aafd2dd40051b7ef2d9d6faf0c8042b216f71868a91869b364fe
|
Provenance
The following attestation bundles were made for epwtools-0.3.1.tar.gz:
Publisher:
publish-pypi.yml on carlobianchi89/epwtools
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
epwtools-0.3.1.tar.gz -
Subject digest:
8f6887b75160ab44fac22b2df6b1e2f788830d4530cd4eff2b18f59f2f34afe2 - Sigstore transparency entry: 1851764528
- Sigstore integration time:
-
Permalink:
carlobianchi89/epwtools@c2be8f9b0e9e8cee4a586d06187bb72ed10f3dbc -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/carlobianchi89
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@c2be8f9b0e9e8cee4a586d06187bb72ed10f3dbc -
Trigger Event:
push
-
Statement type:
File details
Details for the file epwtools-0.3.1-py3-none-any.whl.
File metadata
- Download URL: epwtools-0.3.1-py3-none-any.whl
- Upload date:
- Size: 28.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a85b47072d452e3dccff7899e742e7a8e2f61b40fbc1db2426e9b63f8230d9a0
|
|
| MD5 |
9489c17c3e5587badc3aaf3c674aedd4
|
|
| BLAKE2b-256 |
fcbfb36f54f4900244c56d537b8bef16906af2c5609ef890e2470cb18c7dcfcb
|
Provenance
The following attestation bundles were made for epwtools-0.3.1-py3-none-any.whl:
Publisher:
publish-pypi.yml on carlobianchi89/epwtools
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
epwtools-0.3.1-py3-none-any.whl -
Subject digest:
a85b47072d452e3dccff7899e742e7a8e2f61b40fbc1db2426e9b63f8230d9a0 - Sigstore transparency entry: 1851764643
- Sigstore integration time:
-
Permalink:
carlobianchi89/epwtools@c2be8f9b0e9e8cee4a586d06187bb72ed10f3dbc -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/carlobianchi89
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@c2be8f9b0e9e8cee4a586d06187bb72ed10f3dbc -
Trigger Event:
push
-
Statement type: