NWP forecasts — usage#
Request shape#
The NWP backend takes a variables mapping of model key → list of
parameters (the same shape as the GEE and STAC backends):
from earthlens.earthlens import EarthLens
lens = EarthLens(
data_source="nwp",
variables={"gfs": ["temperature_2m", "precipitation_acc"]},
start="2024-06-01",
end="2024-06-02", # cycle DATE range (inclusive)
lat_lim=[40, 45],
lon_lim=[-80, -75],
path="out/gfs",
steps=[0, 6, 12], # forecast lead times, in hours
mirror="aws", # cloud mirror (default "auto")
)
paths = lens.download() # one bbox-cropped COG per (cycle, step)
Each parameter name resolves through the catalog to the centre's
selector — a Herbie search regex (":TMP:2 m above ground:") for the
NOAA / ECMWF models, or a provider variable token ("T_2M") for DWD
ICON. See Catalog & install for the available models and
their parameter names.
Forecast steps: steps= vs horizon=#
| kwarg | meaning |
|---|---|
| (neither) | only the analysis step f000 — keeps a request small |
steps=[0, 6, 24] |
exactly these lead times (recommended — explicit) |
horizon=48 |
0..48 stepping on the model's step_cadence_h (e.g. every 3 h for GFS) |
A step beyond the model's horizon_h raises a ValueError. horizon=
expands on the model's published step cadence rather than blindly hourly,
so it won't request steps a coarse model never publishes. Any step the
model still doesn't carry on a given cycle is skipped per the
errors= policy below — it does not abort the download.
Partial availability (errors=)#
A (cycle, step) can be legitimately missing (the latest cycle isn't
published yet, or a model doesn't carry every step on every cycle).
download(errors=...) governs that:
errors= |
behaviour |
|---|---|
"warn" (default) |
log the miss, return the COGs that succeeded |
"skip" |
drop the miss silently |
"raise" |
abort the whole download on the first miss |
Ensemble members#
Ensemble models (gefs, ens) expose a members= axis parallel to steps=:
lens = EarthLens(
data_source="nwp",
variables={"gefs": ["temperature_2m"]},
start="2024-06-01", end="2024-06-01",
lat_lim=[40, 45], lon_lim=[-80, -75], path="out/gefs",
members=["mean", "1", "2", "3"], # mean + 3 perturbations
)
One COG is written per (cycle, step, member) (filename suffix _m{member}).
Without members=, only the model's default member is fetched (mean for
GEFS, control for ENS) — keeping a plain request bounded. GEFS members map to
Herbie's member= ("0"–"30", "mean"); ENS members map to ECMWF
type=pf + number= ("control" keeps the control forecast). Deterministic
models ignore members=. DWD/Météo-France ensembles (ICON-EPS, PEARP/PEAROME)
are a follow-on.
Cloud mirror selection#
mirror= chooses where the bytes come from:
mirror= |
NOAA (Herbie) | ECMWF Open Data |
|---|---|---|
"auto" (default) |
the catalog mirrors: order |
first known catalog mirror |
"aws" |
aws |
aws |
"gcp" |
google |
falls back to ecmwf |
"azure" |
azure |
azure |
"origin" |
nomads |
ecmwf |
Download mode: subset vs whole#
mode= controls how much of each GRIB2 is pulled off the wire before the
bbox crop:
mode= |
behaviour |
|---|---|
"subset" (default) |
fetch only the requested bands. For a model with a .idx byte-range index (idx: true — the NOAA / Herbie models) this downloads just the matching messages, cutting >99 % of the bytes. Models without an index already download the whole per-variable file. |
"whole" |
force a full-file download even for .idx-capable models, then crop. Useful when you want every variable/level from one cycle, or to work around an occasionally-stale .idx. |
lens = EarthLens(
data_source="nwp",
variables={"gfs": ["temperature_2m"]},
start="2024-06-01",
end="2024-06-01",
lat_lim=[40, 45],
lon_lim=[-80, -75],
path="out/gfs",
mode="whole", # full GFS file, then crop (default is "subset")
)
mode only changes behaviour for the .idx-capable NOAA / Herbie
centre — every other centre (ECMWF Open Data, DWD, ECCC, Météo-France)
is already whole-per-variable, so it accepts the flag and ignores it. The
default "subset" path is unchanged from earlier releases.
mode="zarr" is rejected with a ValueError: no NWP catalog row
carries a zarr_url, and Zarr sources (NWM, hrrrzarr) are separate
backends. A Zarr download mode is a documented follow-on, gated on a
future zarr_url catalog field (read through pyramids.zarr).
Output: bbox-cropped COGs#
Each (cycle, step) yields one Cloud-Optimized GeoTIFF named
{model}_{YYYYMMDDHH}_f{step:03d}.tif in path. The download fetches
the variable subset GRIB2 (the bandwidth win), reads it with
pyramids.grib.open_grib, crops it to your bbox (global 0–360° grids
are shifted to −180..180 when the bbox reaches into negative
longitudes), and writes the COG.
ICON-global grid
DWD's native ICON-global files are on an icosahedral grid, which does not crop as a regular lat/lon raster. The download path is correct, but for a croppable COG use a regular-lat/lon ICON product.
Antimeridian
A bounding box must satisfy longitude_min <= longitude_max, so a
box that crosses the 180°/−180° dateline (e.g. lon_lim=[170, -170])
cannot be expressed in a single request. Global 0–360° grids are
shifted to −180..180 when the bbox reaches negative longitudes, but a
true dateline-spanning AOI is not split — issue two requests (one per
side) and mosaic the results.
Aggregating the forecast stack#
Pass aggregate= to reduce the per-(cycle, step) COGs into windowed
composites (by valid time = cycle + step):
from earthlens.aggregate import AggregationConfig
lens = EarthLens(
data_source="nwp",
variables={"gfs": ["temperature_2m"]},
start="2024-06-01", end="2024-06-07",
lat_lim=[40, 45], lon_lim=[-80, -75], path="out/gfs",
steps=[0, 6, 12, 18],
)
paths = lens.download(
aggregate=AggregationConfig(freq="1D", op="mean"), # daily means
)
Aggregation requires a single model per request — different models have different native grids and cannot be co-registered into one stack. Issue one request per model.
Accumulated fields
precipitation_acc (and other *_acc fields) are accumulations
over a step-dependent window, not instantaneous values. Reducing
them across forecast steps by valid time (mean/sum) mixes
accumulation intervals and can give misleading totals — the backend
logs a warning when you do. Prefer the per-(cycle, step) COGs, or
de-accumulate before aggregating.