This repository provides documentation and example scripts for using Aviso to receive data-availability notifications for Destination Earth (DestinE) Digital Twin data, with a focus on the Extremes Digital Twin (Extremes-DT).
- 1. What is Aviso?
- 2. Aviso in the Destination Earth Context
- 3. Requirements and Access
- 4. Installation
- 5. Service Endpoints and Connectivity
- 6. Running the Examples
- 7. Core Concepts
- 8. Catch-up and Historical Replay
- 9. Scope: Extremes Digital Twin Only
- 10. Examples in this Repository
- 11. Quotas, Throttling, and Best Practices
- 12. Troubleshooting
- 13. References
Aviso is an open-source notification system developed by ECMWF that broadcasts time-critical events across HPC and cloud systems, enabling event-driven workflows that span multiple domains.
In short, Aviso lets you:
- Subscribe to events you care about (e.g. "a new forecast step is ready").
- Define triggers that execute when a matching notification arrives — print, log, run a shell command, POST a CloudEvent, or call a Python function.
- React in near real time rather than polling for new data.
Aviso is the mechanism that informs users the moment new forecast data becomes available, so that downstream workflows — post-processing, visualisation, model coupling, alerting — can react immediately.
Aviso is developed and used extensively at ECMWF across internal operational workflows, so much of the upstream documentation is aimed at ECMWF-internal users. The examples and documentation in this repository are tailored specifically to Destination Earth. Generic pyaviso examples from the upstream docs may reference events, endpoints, or authentication methods that do not apply in the DestinE context.
Warning
Although Digital Twin data is produced on multiple EuroHPC systems (Mare Nostrum, LUMI, Leonardo), the Aviso server is currently deployed only on the LUMI Databridge and exposes notifications for the Extremes Digital Twin only. Climate DT notifications are not currently available through Aviso.
See Section 9 for details.
Note
Listening to Aviso notifications does not require authentication — the
current endpoint uses auth_type: none. However, downloading data via
Polytope (used in the Earthkit example) does require valid DESP
credentials.
To obtain DESP credentials:
- Register at the DestinE Platform.
- Apply for upgraded access as described in the access policy.
Once upgraded access is granted, run the desp-authentication.py script.
It will prompt for your DestinE username and password, then retrieve a
long-lived offline token from the Destination Earth Service Platform (DESP)
and store it at ~/.polytopeapirc. Re-authentication is only needed when that
token expires.
Python ≥ 3.6 is required.
Install all dependencies used in this repository:
pip install -r requirements.txtFor a minimal installation (listener only, no data download or plotting):
pip install pyaviso| Setting | Value |
|---|---|
| Aviso host | aviso.lumi.apps.dte.destination-earth.eu |
| Port | 443 (HTTPS) |
| Polytope host (data) | polytope.lumi.apps.dte.destination-earth.eu |
Verify connectivity before running the examples:
curl -v https://aviso.lumi.apps.dte.destination-earth.euIf you are on a corporate or institutional network, ensure outbound TCP 443 to the Aviso host is permitted by your firewall or proxy.
All scripts can be run directly from the repository root.
Real-time listener (echo trigger):
python3 aviso-extremes-dt.pyReplay from a past publish time, then continue live:
python3 aviso-extremes-dt-from-time.pyEnd-to-end Earthkit/Polytope workflow:
python3 aviso-extremes-dt-earthkit-example.pyOn startup you should see output similar to:
loaded config:
{'auth_type': 'none',
'configuration_engine': {'host': 'aviso.lumi.apps.dte.destination-earth.eu',
'https': True,
'port': 443},
'notification_engine': {'host': 'aviso.lumi.apps.dte.destination-earth.eu',
'https': True,
'port': 443},
'remote_schema': True,
'schema_parser': 'generic'}
Listening to /de/data/ at aviso.lumi.apps.dte.destination-earth.eu:443...
Notifications matching your filter will be printed to stdout as they arrive.
Stop with Ctrl + C.
Important
On every startup, Aviso checks the last notification it received and replays any that were missed before switching to real-time listening. On the very first run there is no prior state, so no historical notifications are returned. This catch-up behaviour ensures no notifications are lost across restarts or outages. See Section 8 for full details.
A listener is a Python dictionary that tells Aviso what to watch for and what to do when a match is found. It has three required parts:
event— the kind of event to listen for.request— a filter dictionary; only notifications whose metadata matches all keys are delivered.triggers— one or more actions to execute per matching notification.
listener = {
"event": "data", # always use "event": "data" for destine
"request": { ... }, # filter keys
"triggers": [{...}, ...], # one or more triggers
}
listeners_config = {"listeners": [listener]}For DestinE Digital Twin data, the event is always:
listener = {
"event": "data",
...
}The pyaviso schema also defines mars and dissemination events used in
ECMWF operational contexts — these are not relevant for DT data.
The aviso request dictionary uses keys from the MARS language. A notification is
delivered only when every specified key matches. The fewer keys you include,
the broader the subscription.
Do not confuse the aviso request dictionary with a polytope request dictionary. Although they are similar, the aviso request dictionary only accepts the following keys:
| Key | Typical value | Meaning |
|---|---|---|
class |
"d1" |
Destination Earth class |
expver |
"0001" |
Experiment version (operational) |
stream |
"oper", "wave" |
Data stream |
type |
"fc" |
Forecast |
levtype |
"sfc", "pl", "sol" |
Level type (surface, pressure levels, soil) |
date |
"YYYYMMDD" |
Forecast base date |
time |
"00" |
Forecast base time (only 00 available) |
step |
0, [0, 3, 6, ...] |
Forecast lead time in hours |
Each value can be a scalar or a list — for example,
"step": [0, 3, 6, 9, 12, 15, 18, 21, 24] matches any of those steps.
Note
Polytope request dictionaries accept additional keys (e.g., "param", "feature")
and follow different validation rules since they are processed by separate software.
Refer to the Polytope documentation
and polytope-examples
repository for details on building valid polytope requests.
Triggers define what happens when a notification matches. Multiple triggers per listener are supported; each runs as an independent process.
| Type | Use case |
|---|---|
echo |
Print to stdout — simplest, good for testing. |
log |
Append notifications to a log file. |
command |
Run a shell command; supports substitutions like ${request.step}. |
post |
Forward as a CloudEvent over HTTP or AWS SNS. |
function |
Call a Python function directly — used in the Earthkit example. |
Example trigger definitions:
# Echo (no extra config)
trigger = {"type": "echo"}
# Log to file
trigger = {"type": "log", "path": "aviso.log"}
# Shell command with request field substitution
trigger = {
"type": "command",
"command": "./process.sh --date ${request.date} --step ${request.step}",
}
# Python callback
def on_notification(notification): ...
trigger = {"type": "function", "function": on_notification}The dictionary received by a function trigger (and sent by the post trigger
as JSON) looks like this:
{
"event": "data",
"request": {
"class": "d1",
"expver": "0001",
"stream": "oper",
"type": "fc",
"levtype": "sfc",
"date": "20251104",
"time": "0000",
"step": "6",
},
}In a function trigger, the values from notification["request"] are typically
used to construct a Polytope or earthkit.data request to retrieve the data.
When nm.listen(...) is called, Aviso by default:
- Checks the last notification received (persisted locally per user).
- Replays any notifications missed since then.
- Switches to real-time listening.
After a reboot or transient outage, no notifications are lost. On the very first run there is no prior state, so no catch-up occurs.
To explicitly set a starting point, use from_date:
nm.listen(
listeners=listeners_config,
from_date=datetime(2025, 11, 4), #optional
to_date=datetime(2025, 11, 30), #optional
config=config,
)An optional to_date bounds the replay window; when set, Aviso exits after
processing the historical range instead of continuing into real-time.
Important
If using to_date, it should be set to a date in the past (not the current moment).
Aviso will exit once all notifications up to that time have been processed.
Warning
from_date and to_date refer to the wall-clock time when the notification was
published on the Aviso server—i.e., when the data producer announced that a product
was available. They are not the forecast base time (date + time in the request).
Example scenario:
Data for the forecast initialized at 2025-11-04 00 UTC typically becomes available
around 8:00–11:00 UTC that same day. However, due to queuing delays or system issues,
the corresponding notifications may be published several hours or even days later.
Correct approach:
- To replay all notifications for the
2025-11-04 00 UTCcycle, setfrom_dateto a time before the cycle started (e.g.,datetime(2025, 11, 3, 12)) and in the Polytope request dictionary filter to target the specific cycle:"date": "20251104", "time": "0000". - If you set
from_date=datetime(2025, 11, 4, 12), you will only capture notifications published from noon UTC onward, which may miss early steps and include products from prior forecast cycles. - When in doubt, set
from_dateconservatively early and rely on therequestfilter to select the correct forecasts.
The same principle applies to to_date: it bounds the publication-time window, not
the forecast base time window.
Notifications available through the endpoint used in these examples are limited to the Extremes Digital Twin:
class: d1,expver: 0001stream: oper(andwavefor the wave component)- 4 km global resolution; operational production began on 2023-12-11
Climate DT data is not available through Aviso at this time.
Consult the Extremes DT Data Catalogue to identify available variables, levels, and forecast steps before constructing your request filter.
| Script | Description |
|---|---|
aviso-extremes-dt.py |
Minimal real-time listener with the echo trigger. |
aviso-extremes-dt-from-time.py |
Replay from a given publish time, then continue listening live. |
aviso-extremes-dt-earthkit-example.py |
End-to-end workflow: notification → Polytope download → regrid → Europe map plot. |
aviso-extremes-dt-log.py |
Persist every notification to a structured JSON-lines log file. |
aviso-extremes-dt-multi-listener.py |
Two listeners in one process with different filters (surface vs. wave). |
aviso-extremes-dt-replay-window.py |
Replay a bounded historical window (from_date + to_date) and exit. |
aviso-extremes-dt-polytope-download.py |
Download GRIB data to disk for every matching step (no plotting). |
desp-authentication.py |
Obtain a DESP offline token and store it for Polytope access. |
- Rate limit: up to 50 requests/second to the Aviso server (may be adjusted under load).
- Concurrent operations: currently unlimited, but please be considerate.
- Use the narrowest request filter possible (
stream,levtype,step) to avoid triggering on irrelevant notifications. - In a
functiontrigger, do not block the main thread with lengthy operations. Hand off work to a queue or worker pool (e.g.concurrent.futures, Celery, or use theposttrigger to forward to an external system). - Each notification typically corresponds to one forecast step — download volume scales directly with how broad your filter is.
- For production deployments that should ignore catch-up replay, run
aviso listen --now(CLI) to reset the local state and listen only to new notifications.
| Symptom | Likely cause / fix |
|---|---|
Script prints Listening to /de/data/ … and no notifications arrive |
No matching data published since you started, or filter is too narrow. Try a from_date in the past. |
ConnectionError / TLS errors |
Outbound port 443 to aviso.lumi.apps.dte.destination-earth.eu is blocked by your network. |
| Polytope download fails with 401 / 403 | Refresh the DESP token by re-running desp-authentication.py. |
from_date replay returns fewer events than expected |
from_date is a publish time, not a forecast base time — see §8.1. |
| Catch-up replays the same notifications on every restart | The local state file is missing or being reset — check the Aviso config directory (default: ~/aviso/). |
- pyaviso documentation: https://pyaviso.readthedocs.io/en/latest/
- Aviso source code: https://github.com/ecmwf/aviso
- Destination Earth user guide: https://platform.destine.eu/services/documents-and-api/doc/?service_name=climate-dt-user-guide
- Polytope examples repository: https://github.com/destination-earth-digital-twins/polytope-examples
- Destination Earth: https://destination-earth.eu/
- DestinE / ECMWF Digital Twin Engine: https://destine.ecmwf.int/
- Extremes DT Data Catalogue: https://confluence.ecmwf.int/display/DDCZ/Extremes+DT+data+catalogue
- Earthkit: https://earthkit.readthedocs.io/
- CloudEvents specification: https://cloudevents.io/