dwdopen
Python library to access DWD Open Data numerical weather prediction (NWP) data: discover what data is currently offered, define what you want, and download it.
Still in "pre-alpha", not everything works yet. See Not yet implemented.
Install
Requires Python 3.12+.
pip install git+https://github.com/MuffinCompiler/dwdopen@v0.1.0
Quickstart
from dwdopen import DWD
with DWD() as dwd:
icon_eu = dwd.nwp.model("icon-eu")
query = icon_eu.select(parameters=["T_2M", "PMSL"], steps="6h")
query.download("forecast.grib2")
Nothing touches the network until a call needs availability information.
Investigate your request before you download
resolve() freezes a query against one run and hands back a plan you can inspect before
any download happens:
plan = icon_eu.select(parameters="U", level_type="pressure").resolve()
print(plan)
# ResolvedRequest(run=2026-09-23T06:00:00+00:00, 1860 assets, 1.5 GB, U,
# on pressure (100), 20 levels, steps 0h..120h)
print(plan.assets[0])
# Asset(U, 50 hPa, 0h, 723.4 KB)
plan.download("u.grib2")
A plan never switches to a newer run later, so what you inspected is what you get.
Discovery
dwd.nwp.models() # every model DWD currently publishes
icon_eu.parameters() # every parameter of one model
icon_eu.parameter("T").level_types # (pressure (100), model (150))
icon_eu.levels("T", "pressure") # available level values
Everything comes from the live catalogue, so a parameter DWD adds should show up without a new dwdopen release.
Selecting
Forecast steps
Steps are durations, ICON-D2 publishes precipitation every 15 minutes and ICON-D2-RUC every 5.
from dwdopen import Between, Every, hours, minutes
steps="6h" # exactly this step
steps=["0h", "3h", "6h"] # exactly these
steps=Every("0h", "48h", "3h") # interval, inclusive at both ends
steps=Between("0h", "48h") # whatever exists in the interval
steps="all" # everything published
steps=hours(0, 6, 12, 18) # plain numbers, if that is what you have
steps=hours(range(0, 121, 3)) # or any iterable of them
steps=minutes(0, 15, 30) # for the sub-hourly models
hours() and minutes() turn a single number into a single duration, so they compose
with the others too: Every(hours(0), hours(48), hours(3)).
Every names exact steps, so a missing one makes the request incomplete. Between and
"all" ask for whatever is there and can never be incomplete.
Note that DWD publishes data incrementally on the server, so a run might just be halfway
available. If you use Between, you might just get what's already available, not the full
data.
Ensemble members
The -eps models publish each member separately. Members are plain integers; how many
there are differs per model (40 for ICON-EPS and ICON-EU-EPS, 20 for ICON-D2-EPS, 10 for
ICON-ART-EPS):
eps.select(parameters="T_2M", members=[1, 2, 5])
eps.select(parameters="T_2M", members=Between(1, 10))
eps.select(parameters="T_2M") # every member
Leaving members out takes all of them, the same as levels and steps.
Ensembles publish a reduced level set: icon-eu-eps T has 3 pressure levels
and 3 model levels, against 20 and 74 for deterministic ICON-EU. Check the output of
model.levels() to see what is actually there.
Vertical levels
icon_eu.select(parameters="T", level_type="pressure", levels=[850, 500])
icon_eu.select(parameters="T", level_type="model", levels=Between(60, 74))
icon_eu.select(parameters="T_SO", level_type="soil", levels="all")
level_type may be left out only while the selection is unambiguous. T is available on
both pressure and model levels, so omitting it raises AmbiguousSelectionError rather
than guessing. A 2-D field such as T_2M needs no level type at all. Time-invariant fields
need no level type either. These are published under every run with a single step.
HHL is the exception, being model half-level heights:
icon_d2.select(parameters="HSURF", steps="0h") # no level type
icon_d2.select(parameters="HHL", level_type="model", levels="all")
Downloading
query.download("forecast.grib2") # one combined file
query.download("members/", combine="member") # one file per ensemble member
query.download("forecast/", combine="none") # one file per message
query.download("f.grib2", temp_dir="/scratch") # partial downloads before combining elsewhere
combine="all" concatenates the messages into a single GRIB2 file, which is valid
because GRIB2 messages are self-delimiting. You can also provide just a directory in
this case and the name is generated automatically.
plan = query.resolve()
plan.suggested_name() # 'icon-eu_2026-09-24T0600_PMSL+T_2M_0h-24h.grib2'
plan.download("/home/weather/") # writes that name into the directory
Messages are sorted time-major: every field of one forecast step together, steps ascending. Some programs like CDO require grib files to be sorted by time to work correctly.
Downloads run concurrently, are written to a temporary name and renamed into place, so a partial file is never mistaken for a finished one. Several processes may write the same directory at once.
Already downloaded files are skipped, a second run of the same selection transfers nothing:
A file counts as done when its size matches the catalogue and it starts with GRIB
and ends with 7777. For combine="all" this works when the name was generated, because
the generated name ends in a hash of the plan and so describes that exact selection.
DownloadResult.assets_skipped says how many were already there, which is worth
checking in a scheduled job. Nothing is kept between runs. A partial download is discarded,
so an interrupted job never leaves files behind.
temp_dir must be on the same filesystem as the destination, because publishing a
finished file is a rename and a rename cannot cross filesystems.
Logging
The library logs through the standard dwdopen logger. It never prompts or reads from
stdin as the library is meant to run unattended.
import logging
logging.basicConfig(level=logging.INFO)
INFO reports what is about to be downloaded and how large it is. WARNING covers
retries and mixed level types.
Not yet implemented
- Resume and skip existing files. A failed run currently re-downloads from scratch.
- Ensemble members** (
-epsmodels, thee/<NN>/path segment). -
combine="member"andcombine="parameter". - Listing cache. Every call re-reads the catalogue today.
- Feedback of download progress (bar, text, visual?)
- ICON-ART wavelengths (the
wvl1segment). - A command-line interface.
- Automatic parameter choice for
Model.latest_run(), which needsparameter=at the moment.
License
MIT. See LICENSE.
Forecast data itself is provided by Deutscher Wetterdienst (DWD) under its own terms of use.
Metadata
Release files for dwdopen 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| dwdopen-0.2.1.tar.gz | 57.0 kB | Without provenance | Details for dwdopen-0.2.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| dwdopen-0.2.1-py3-none-any.whl | Python 3 | none | any | Details for dwdopen-0.2.1-py3-none-any.whl |
Total release size: 101.1 kB
Release files / dwdopen-0.2.1.tar.gz
| Download URL | dwdopen-0.2.1.tar.gz |
|---|---|
| Size | 57.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8079ad82e5daa2439ab1cbbc0491bbab4dce24d1ee1f8af94a09fc111ea68c8f
|
|
BLAKE2b-256 checksum How to use checksums |
8517c42c138b9c50f0c510ad946ec6f1d5a19d92727de353c66e313d464fb4ff
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / dwdopen-0.2.1-py3-none-any.whl
| Download URL | dwdopen-0.2.1-py3-none-any.whl |
|---|---|
| Size | 44.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c743a27a962c89c1849ffc619168b77401c252b1246040bda0087f233e6a6c55
|
|
BLAKE2b-256 checksum How to use checksums |
2dabbdbed188f5c740e5d4c90de00b9c68df24ae25f5e7db1d4dd2c0093105d8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Security for dwdopen 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.