Skip to content

AROME (Météo-France) GRIB enrichment: condensate species over the AROME domain #529

Description

@roznet

Why

The meteofrance model slot is today served only by Open-Meteo's meteofrance_seamless, which exposes no condensate and no vertical velocity for Météo-France. Worse, seamless silently blends: AROME 0.025° inside the AROME footprint, ARPEGE outside, with nothing marking the seam (verified — at Paris, seamless T850 = AROME 21.2 °C while ARPEGE reads 21.8 °C).

AROME is a convection-permitting model (2.5 km / 1.3 km) whose physics is Meso-NH with ICE3 five-species microphysics; only its ALADIN-NH dynamical core descends from the ARPEGE/IFS family. So on the fields that matter for icing and convection it is genuinely independent of the ECMWF IFS we already ingest.

What this buys us is geographic, and bounded. Measured against the ICON-D2 domain:

route D2 coverage AROME coverage
London → Cannes 61/61 61/61
London → Paris 61/61 61/61
London → Brest 53/61 61/61
London → Dublin 37/61 61/61
Cannes → Barcelona 11/61 61/61

46 % of AROME's footprint lies outside ICON-D2 — Ireland, Brittany and western France, Iberia, the western Med. Core corridors are already D2-covered, so AROME is gap-filling plus a second, independent convection-permitting opinion where the two overlap. It is not a prerequisite for the icing win; that comes from the near-free ICON-D2 species work.

ARPEGE was evaluated and deliberately excluded: it shares the IFS codebase with ECMWF (co-developed since 1987), adds nothing convective (no reflectivity, no lightning; its CAPE_INS we already have via Open-Meteo), and is the least independent model we could add alongside the ECMWF we already ingest.

Source — verified live 2026-07-30

Public OVH mirror, no API key required:

https://meteofrance-pnt.s3.rbx.io.cloud.ovh.net/pnt/{RUN}/arome/0025/{PKG}/arome__0025__{PKG}__{RANGE}__{RUN}.grib2
  • RUN format 2026-07-30T12:00:00Z; RANGE00H06H, 07H12H, 13H18H, 19H24H, 25H30H, 31H36H, 37H42H, 43H48H, 49H51H
  • 8 runs/day (00,03,06,09,12,15,18,21 Z), horizon 51 h, publish ≈ +3 h (12Z first group landed 15:13 Z)
  • Accept-Ranges: bytes confirmed — HTTP 206 works mid-file, so per-message byte-range extraction is available if volume ever demands it (demonstrated: a single 88 KB message fetched by offset and decoded standalone)
  • 1.3 km stream lives at .../arome/001/{PKG}/arome__001__{PKG}__{HH}H__{RUN}.grib2, hourly
  • Run/resource discovery: the data.gouv.fr dataset API paquets-arome-resolution-0-025deg enumerates every resource URL

⚠️ Do not use object.data.gouv.fr/meteofrance-pnt — that is the URL in Open-Meteo's source and it is stale, newest run 2026-05-11.

⚠️ Do not use the public-api.meteofrance.fr package endpoint: it requires a key and does not support range requests.

Packages

pkg contents size / 6 h group
IP2 clwc, ciwc, crwc, cswc, graupel (cat1/num201), cc — 24 levels 100–1000 hPa, hourly 146 MB
IP4 tke (24 levels) + 3-D reflectivity (cat16/num192, 16 levels 200–925 hPa) 68 MB
001/SP2 column-max reflectivity (cat16/num193), CAPE_INS, tgrp, lcc/mcc/hcc, tirf — 1.3 km hourly ~1.0 MB for the reflectivity message

Pressure levels: 100,125,150,175,200,225,250,275,300,350,400,450,500,550,600,650,700,750,800,850,900,925,950,1000.
Measured per-message: clwc 0.181 MB, ciwc 0.146 MB, cc 0.126 MB, crwc 0.122 MB.

shortNames

clwc, ciwc, cc are already in _VAR_MAP (src/weatherbrief/fetch/grib/decode.py:32-42) from the ECMWF IFS path — they map straight through. crwc/cswc are standard eccodes names. Graupel and both reflectivity fields are Météo-France local parameters that eccodes reports as unknown; select them by (discipline, parameterCategory, parameterNumber) — the same technique already used for HRRR's CIMIXR (see the unknown note in the HRRR var map in decode.py).

Identification is corroborated by physics: ICE3 carries exactly five prognostic condensate species (cloud droplets, rain, ice, snow, graupel), and IP2 contains exactly clwc+ciwc+crwc+cswc+one unknown — hence cat1/num201 = graupel.

Domain gate — mandatory prerequisite

AROME's array is 717×1121 regular lat/lon over 37.5–55.4 N / −12–16 E, but only 82.8 % of cells are finite. The valid region is a bowed quadrilateral that narrows southward: west edge −11.5° at 53 N → −8.4° at 39 N, and at 55.38 N only −0.5…4.5. It is not an axis-aligned box, and not a clean Lambert conformal rectangle either (fitting one leaves 61 km of edge scatter).

Required:

  • Generate a data-derived per-latitude [west, east] row table from a CONSTANT or SP1 GRIB, checked in as data, with a regeneration script so it can be refreshed if MF changes the domain.
  • Implement route_in_arome_domain(route_points) -> bool, mirroring route_in_icon_eu_domain() (src/weatherbrief/fetch/grib/icon_eu_fetch.py:448) — all-or-nothing, false if any route point is outside.

Note: the same predicate is needed by the separate narrow-only Open-Meteo model-gating work (limiting the meteofrance slot to routes fully inside AROME rather than merely touching France). Build it once, in a place both can import.

Implementation sketch

  1. src/weatherbrief/fetch/grib/arome_fetch.py, mirroring icon_eu_fetch.py: a frozen config dataclass (cycles, publish delay, horizon, packages, levels), run finder that HEAD-probes back through cycles, package URL builder, flight-window → package-group mapping, the domain predicate, and cache-key helpers.
  2. Decode: extend _VAR_MAP with crwcrain_water_kg_kg, cswcsnow_water_kg_kg; add an MF-local parameter map keyed on (0,1,201) → graupel, (0,16,192) → 3-D reflectivity, (0,16,193) → column-max reflectivity.
  3. Orchestrator: add _prepare_arome / _prefetch_arome_data / _decode_and_merge_arome alongside the ICON trio in src/weatherbrief/fetch/grib/__init__.py (anchors: _prepare_icon_eu :3836, _prefetch_icon_eu_data :3962, _decode_and_merge_icon_eu :4281), keyed to ModelSource.METEOFRANCE (models/analysis.py:449), and record grib_sources["meteofrance"] = "arome:mf".
  4. Freshness: add SOURCE_REGISTRY["arome:mf"] in src/weatherbrief/fetch/freshness/registry.py, following the icon_d2:dwd entry at :307 — 8 cycles, ~3 h publish delay, 51 h horizon.
  5. Skip paths: reuse the existing grib_skip_reasons mechanism (the "out_of_domain" reason string already exists — see _prepare_icon_eu's docstring). Skip when the route is not fully inside the domain, or when the flight window exceeds the 51 h horizon; degrade silently to Open-Meteo.

Scope

In: IP2 condensate + cc; the domain gate and row table; freshness registration; merging clwc/ciwc/cc into the enrichment path so meteofrance carries real condensate; window/domain skip handling.

Deferred to follow-ups (no consumer exists yet — do not build speculatively):

Dependency

#530 adds rain_water_kg_kg / snow_water_kg_kg / graupel_water_kg_kg to PressureLevelData and their consumers. Land that first and this issue just feeds them. If this lands first, add the fields here.

Acceptance criteria

  • A briefing on a route fully inside AROME shows meteofrance with non-null cloud_liquid_water_kg_kg / ice_mixing_ratio_kg_kg at cruise levels.
  • A route partially outside (e.g. anything reaching south of ~38 N or west of the bowed edge) records the skip diagnostic and falls back to Open-Meteo without error.
  • Flight window beyond 51 h → skipped with an explicit reason.
  • Domain-predicate unit tests against verified points: inside — London 51.5/−0.4, Paris 48.9/2.4, Berlin 52.5/13.4, Marseille 43.5/5.2; outside — 37.55/2.0, 45.0/−11.95, 45.0/15.95, 56.0/−3.2.
  • Prefetch is capped to the flight window's package groups — never all 9.
  • designs/weather-engine-specs.md updated with the new source, bucket paths and variable table (it already documents GFS/ICON-EU/ECMWF this way).

Risks

  • Volume. IP2 alone is 146 MB per 6 h group — roughly 584 MB per run to cover 0–24 h, at 8 runs/day. GRIB precache is currently disabled in production (WB_GRIB_PRECACHE_ENABLED=false, after an OOM killed a user briefing). Do not enable AROME prefetch on the droplet without a memory plan; the home-compute offload path is the natural host for this ingestion.
  • Reflectivity units. MF's parameter description labels RFLCTVT as "Derived radar reflectivity backscatter from rain" with unit m6 m-3, but measured values span 0–62 (peak 62.2 at 1.3 km, 57 dBZ at 650 hPa in the 3-D field) which reads as dBZ. Confirm at decode before any threshold is set on it.
  • Domain drift. The row table is derived from data; if MF changes the AROME domain the table silently goes stale. The regeneration script and a periodic assertion (finite-fraction ≈ 82.8 %) guard this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions