
Security News
Deno Goes the Way of the Dinosaur
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.
epwtools
Advanced tools
Python SDK and CLI for epw.tools — generate EPW/DDY weather files from MERRA-2, ERA5 and CMIP6
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.
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.
The epwtools Python package provides a typed client for the REST API plus a
weatherproof CLI:
pip install epwtools
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,
)
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
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
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())
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.
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_design_conditions, get_nearest_stations, get_job_result.
Every generate_* tool takes an optional save_to folder: the EPW, DDY and STAT are
saved there under one base name, ready for EnergyPlus or OpenStudio.
get_design_conditions returns heating and cooling design conditions in one of two
modes that are never mixed: ashrae_method (the ASHRAE Handbook method, replicated
and validated) or enhanced (values at the site, for today's climate, with 90 %
ranges). Values are free and need no key; the ASHRAE-method DDY is free and the Enhanced
DDY costs 1 credit.
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.
OpenStudio-MCP lets an AI assistant build and simulate EnergyPlus models, but it needs a weather file on disk. Run both servers and EPW.tools supplies the weather for any location, year or future climate:
{
"mcpServers": {
"epw-tools": {
"command": "uvx",
"args": ["--from", "epwtools[mcp]", "epw-tools-mcp"],
"env": { "EPWTOOLS_API_KEY": "epw_your_key_here" }
},
"openstudio-mcp": {
"command": "docker",
"args": ["run", "--rm", "-i",
"-v", "/path/to/weather:/inputs:ro", "-v", "/path/to/runs:/runs",
"nrel/openstudio-mcp:v1.2.1", "openstudio-mcp"]
}
}
}
Then ask, for example:
generate_epw(..., save_to="/path/to/weather"),
then OpenStudio-MCP's create_new_building(weather_file="/inputs/Lugano_2023.epw").generate_future_climate(..., save_to=...).get_design_conditions(mode="enhanced", save_to="/path/to/weather", ddy_name="Lugano_2023") replaces the DDY before the
location is set.EPW, DDY, STAT, WEA (Radiance), CLM (ESP-r), PVSyst .met, CSV, JSON,
Excel, Parquet, NetCDF (CF-1.8), and a one-click .zip bundle.
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
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.
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,...}
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
cd frontend
npm install
npm run dev # → http://localhost:5173
The Vite dev server proxies /api requests to the backend automatically.
| 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 |
Proprietary. All rights reserved.
FAQs
Python SDK and CLI for epw.tools — generate EPW/DDY weather files from MERRA-2, ERA5 and CMIP6
We found that epwtools demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.

Security News
Anthropic’s free OSS Scanner uses Mythos and other Claude models to find open source vulnerabilities and send maintainers reports with proposed fixes.

Security News
On Front Lines’ Early Adopters: CISO podcast, Andrew Becherer shares lessons from Datadog, weighs the long-term costs of AI-built tools, and explains how founders can actually gain a CISO’s trust.