Skip to content

Weather Downloads

The WeatherDownloader downloads EPW and DDY weather files from climate.onebuilding.org with automatic caching.

Prefer the shell?

The idfkit tmy CLI wraps this API for interactive use. Pass --download DIR to fetch the EPW/DDY/STAT bundle for a station without writing any Python.

Basic Usage

from idfkit.weather import StationIndex, WeatherDownloader

# Find a station
index = StationIndex.load()
station = index.search("chicago ohare")[0].station

# Download weather files
downloader = WeatherDownloader()
files = downloader.download(station)

print(f"EPW: {files.epw}")
print(f"DDY: {files.ddy}")

Download by Filename

If you have the canonical EPW filename, download directly without a manual station lookup:

from idfkit.weather import WeatherDownloader

downloader = WeatherDownloader()

# Download directly by EPW filename — no manual station lookup needed
epw = downloader.get_epw_by_filename("USA_IL_Chicago.Ohare.Intl.AP.725300_TMYx.2009-2023")
ddy = downloader.get_ddy_by_filename("USA_IL_Chicago.Ohare.Intl.AP.725300_TMYx.2009-2023")

print(f"EPW: {epw}")
print(f"DDY: {ddy}")

Selective Extraction

By default download() extracts the full bundle and requires both an EPW and a DDY to be present. Pass only={".epw"} (or any iterable of suffixes) to extract just the files you need — useful when you only want the EPW for an annual run, or when iterating over thousands of stations and you want to skip the STAT file entirely.

# Pull just the EPW out of the bundle — skip DDY and STAT extraction.
files = downloader.download(station, only={".epw"})

print(f"EPW: {files.epw}")
assert files.ddy is None
assert files.stat is None

# Pass an iterable of any-case suffixes; ".EPW" and "epw" both match ".epw".
both = downloader.download(station, only=[".epw", ".ddy"])
assert both.epw is not None
assert both.ddy is not None

When only= is set, download() returns a PartialWeatherFiles whose epw, ddy, and stat fields are each Path | None — see below.

WeatherFiles

The default download() call returns a WeatherFiles object:

Attribute Type Description
epw Path Path to the EPW file
ddy Path Path to the DDY file
stat Path | None Path to the STAT file (may be None)
zip_path Path Path to the original downloaded ZIP archive
station WeatherStation The station this download corresponds to
files = downloader.download(station)

# Use for simulation
from idfkit.simulation import simulate

result = simulate(model, files.epw)

# Use for design days
from idfkit.weather import DesignDayManager

ddm = DesignDayManager(files.ddy)

PartialWeatherFiles

download(station, only=...) returns a PartialWeatherFiles instead. Same shape, but epw, ddy, and stat are all optional — only the suffixes you asked for are populated:

Attribute Type Description
epw Path | None Path to the EPW file, or None if not requested
ddy Path | None Path to the DDY file, or None if not requested
stat Path | None Path to the STAT file, or None if not requested
zip_path Path Path to the original downloaded ZIP archive
station WeatherStation The station this download corresponds to

Caching

Downloaded files are cached locally to avoid redundant downloads:

# First download: fetches from internet
files1 = downloader.download(station)

# Second download: instant from cache
files2 = downloader.download(station)

assert files1.epw == files2.epw  # Same cached file

Cache Location

Default locations by platform:

Platform Default Path
Linux ~/.cache/idfkit/weather/files/
macOS ~/Library/Caches/idfkit/weather/files/
Windows %LOCALAPPDATA%\idfkit\cache\weather\files\

Custom Cache Directory

from pathlib import Path

downloader = WeatherDownloader(cache_dir=Path("/data/weather_cache"))

Clear Cache

import shutil

shutil.rmtree(downloader.cache_dir)

Download Process

The downloader:

  1. Checks if files are already cached
  2. Downloads the ZIP file from the station's URL
  3. Extracts the requested files (full bundle by default; subset when only= is given)
  4. Stores in the cache directory
  5. Returns paths to the extracted files

Error Handling

from idfkit.weather import WeatherDownloader

downloader = WeatherDownloader()

try:
    files = downloader.download(station)
except Exception as e:
    print(f"Download failed: {e}")

Common errors:

  • Network connectivity issues
  • Invalid station URL
  • Server temporarily unavailable

Offline Usage

Once files are cached, no network is needed:

# Pre-download files while online
downloader = WeatherDownloader()
for station in my_stations:
    downloader.download(station)

# Later, offline usage works
files = downloader.download(station)  # From cache

Batch Downloads

Download files for multiple stations:

from idfkit.weather import StationIndex, WeatherDownloader

index = StationIndex.load()
downloader = WeatherDownloader()

# Download for multiple cities
cities = ["chicago", "new york", "los angeles", "houston"]
weather_files = {}

for city in cities:
    station = index.search(city)[0].station
    files = downloader.download(station)
    weather_files[city] = files
    print(f"Downloaded: {station.display_name}")

File Format Details

EPW (EnergyPlus Weather)

  • Hourly weather data for a typical meteorological year
  • Contains temperature, humidity, solar radiation, wind, etc.
  • Used by simulate() for annual simulations

DDY (Design Day)

  • ASHRAE design day conditions
  • Contains SizingPeriod:DesignDay objects
  • Used for HVAC sizing calculations

Integration Example

Complete workflow:

from idfkit import load_idf
from idfkit.weather import (
    StationIndex,
    WeatherDownloader,
    DesignDayManager,
    geocode,
)
from idfkit.simulation import simulate

# Load model
model = load_idf("building.idf")

# Find station near project site
index = StationIndex.load()
lat, lon = geocode("123 Main St, Chicago, IL")
station = index.nearest(lat, lon)[0].station

# Download weather files
downloader = WeatherDownloader()
files = downloader.download(station)

# Apply design days
ddm = DesignDayManager(files.ddy)
ddm.apply_to_model(model, heating="99.6%", cooling="1%")

# Run simulation
result = simulate(model, files.epw, design_day=True)
print(f"Success: {result.success}")

See Also