Skip to main content

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.


AI assistants (MCP server)

EPW.tools ships a Model Context Protocol server, so AI assistants (Claude Desktop, Cursor, and other MCP-capable agents) can generate weather files directly — no browser. Tools exposed: generate_epw, generate_tmy, generate_future_climate, generate_forecast, get_nearest_stations, get_job_result.

Add this to your MCP client config (one line, nothing to clone — it uses uv to run on demand):

{
  "mcpServers": {
    "epw-tools": {
      "command": "uvx",
      "args": ["--from", "epwtools[mcp]", "epw-tools-mcp"],
      "env": { "EPWTOOLS_API_KEY": "epw_your_key_here" }
    }
  }
}

Prefer pip? pip install 'epwtools[mcp]', then set the command to epw-tools-mcp. The API base defaults to the public backend (https://epw.tools/api); override with EPWTOOLS_API_BASE for a self-hosted instance. Get an API key at epw.tools — most generated files use one credit (a future-climate file uses three), and new accounts start with free credits.


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.

Release files for epwtools 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for epwtools 0.4.0
File Size Uploaded
epwtools-0.4.0.tar.gz 32.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for epwtools 0.4.0
File Interpreter ABI Platform
epwtools-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 67.5 kB

Release files / epwtools-0.4.0.tar.gz

Download URL epwtools-0.4.0.tar.gz
Size 32.5 kB
Tags Source
SHA-256 checksum
How to use checksums
50aeafe748d1023079239b0720471e0f13e5a363d4141a17f8b6b7bdfdc6eb36
BLAKE2b-256 checksum
How to use checksums
facee92a7ed5b2419876b84c0ef1b23f3f312261a0ccec1d0d4574b413c9b810
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release files / epwtools-0.4.0-py3-none-any.whl

Download URL epwtools-0.4.0-py3-none-any.whl
Size 34.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
95a01b142203398032fd03dcfb80129030c43d043d7452b4aee72320df251a95
BLAKE2b-256 checksum
How to use checksums
45b1cd6e5bd9a311750f932f73865168149f0b76e18a626e716ed134bc0d15f8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.1

2 release files

0.3.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page