Skip to content

Commit 45ab1f6

Browse files
authored
Merge pull request #52 from mattchdt/38-add-support-for-pearpege
38 add support for pearpege
2 parents 95e15d6 + 0989df9 commit 45ab1f6

File tree

5 files changed

+186
-23
lines changed

5 files changed

+186
-23
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.2.3] - November, 2025
8+
9+
### Features
10+
11+
* Support for Météo-France ensemble model `PE-ARPEGE` (#38)
12+
713
## [0.2.2] - August, 2025
814

915
### Features

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ Create an account on [the Météo-France API portal](https://portail-api.meteofr
5050

5151
Meteole allows you to retrieve forecasts for a wide range of weather indicators. Here's how to get started:
5252

53-
| Characteristics | AROME | AROME-PE | ARPEGE | AROME INSTANTANE | PIAF |
54-
|------------------|----------------------------|----------------------------|-----------------------------|--------------------------------| -------------------------------|
55-
| Resolution | 1.3 km | 2.8 km | 10 km | 1.3 km | 1.3 km |
56-
| Update Frequency | Every 3 hours | Every 6 hours | Every 6 hours | Every 1 hour | Every 10 minutes |
57-
| Forecast Range | Every hour, up to 51 hours | Every hour, up to 51 hours | Every hour, up to 114 hours | Every 15 minutes, up to 360 minutes | Every 5 minutes, up to 195 minutes |
58-
| Numbers of scenarios | 1 | 25 | 1 | 1 | 1 |
59-
60-
The AromePE model is an ensemble model. Instead of making a single forecast of the most likely weather, a set (or ensemble) of forecasts is produced. This set of forecasts aims to give an indication of the range of possible future states of the atmosphere ([from Wikipedia](https://en.wikipedia.org/wiki/Ensemble_forecasting)). It provides 25 scenarios of the possible weather parameters instead of one for the standard determinist models.
53+
| Characteristics | AROME | AROME-PE | ARPEGE | ARPEGE-PE | AROME INSTANTANE | PIAF |
54+
|------------------|----------------------------|----------------------------|-----------------------------|--------------------------------| -------------------------------| -------------------------------|
55+
| Resolution | 1.3 km | 2.8 km | 10 km | 10 km | 1.3 km | 1.3 km |
56+
| Update Frequency | Every 3 hours | Every 6 hours | Every 6 hours | Every 6 hours | Every 1 hour | Every 10 minutes |
57+
| Forecast Range | Every hour, up to 51 hours | Every hour, up to 51 hours | Every hour, up to 114 hours | Every hour up to 48 hours, then every 3 hours up to 114 hours | Every 15 minutes, up to 360 minutes | Every 5 minutes, up to 195 minutes |
58+
| Numbers of scenarios | 1 | 25 | 1 | 35 | 1 | 1 |
59+
60+
The AromePE and ArpegePE models are ensemble models. Instead of making a single forecast of the most likely weather, a set (or ensemble) of forecasts is produced. This set of forecasts aims to give an indication of the range of possible future states of the atmosphere ([from Wikipedia](https://en.wikipedia.org/wiki/Ensemble_forecasting)). It provides several scenarios of the possible weather parameters instead of one for the standard determinist models.
6161

6262
*note : the date of the run cannot be more than 4 days in the past. Consequently, change the date of the run in the example below.*
6363

docs/pages/how_to.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,14 @@ client.get_vignette()
4949

5050
Meteole allows you to retrieve forecasts for a wide range of weather indicators. Here's how to get started with AROME, AROME-PE, AROME INSTANTANE, ARPEGE or PIAF:
5151

52-
| Characteristics | AROME | AROME-PE | ARPEGE | AROME INSTANTANE | PIAF |
53-
|------------------|----------------------------|----------------------------|-----------------------------|--------------------------------| -------------------------------|
54-
| Resolution | 1.3 km | 2.8 km | 10 km | 1.3 km | 1.3 km |
55-
| Update Frequency | Every 3 hours | Every 6 hours | Every 6 hours | Every 1 hour | Every 10 minutes |
56-
| Forecast Range | Every hour, up to 51 hours | Every hour, up to 51 hours | Every hour, up to 114 hours | Every 15 minutes, up to 360 minutes | Every 5 minutes, up to 195 minutes |
57-
| Numbers of scenarios | 1 | 25 | 1 | 1 | 1 |
58-
59-
The AromePE model is an ensemble model. Instead of making a single forecast of the most likely weather, a set (or ensemble) of forecasts is produced. This set of forecasts aims to give an indication of the range of possible future states of the atmosphere ([from Wikipedia](https://en.wikipedia.org/wiki/Ensemble_forecasting)). It provides 25 scenarios of the possible weather parameters instead of one for the standard determinist models.
52+
| Characteristics | AROME | AROME-PE | ARPEGE | ARPEGE-PE | AROME INSTANTANE | PIAF |
53+
|------------------|----------------------------|----------------------------|-----------------------------|--------------------------------| -------------------------------| -------------------------------|
54+
| Resolution | 1.3 km | 2.8 km | 10 km | 10 km | 1.3 km | 1.3 km |
55+
| Update Frequency | Every 3 hours | Every 6 hours | Every 6 hours | Every 6 hours | Every 1 hour | Every 10 minutes |
56+
| Forecast Range | Every hour, up to 51 hours | Every hour, up to 51 hours | Every hour, up to 114 hours | Every hour up to 48 hours, then every 3 hours up to 114 hours | Every 15 minutes, up to 360 minutes | Every 5 minutes, up to 195 minutes |
57+
| Numbers of scenarios | 1 | 25 | 1 | 35 | 1 | 1 |
58+
59+
The AromePE and ArpegePE models are ensemble models. Instead of making a single forecast of the most likely weather, a set (or ensemble) of forecasts is produced. This set of forecasts aims to give an indication of the range of possible future states of the atmosphere ([from Wikipedia](https://en.wikipedia.org/wiki/Ensemble_forecasting)). It provides several scenarios of the possible weather parameters instead of one for the standard determinist models.
6060

6161
*note : the date of the run cannot be more than 4 days in the past. Consequently, change the date of the run in the example below.*
6262

src/meteole/_arpege_ensemble.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
from typing import Any, final
5+
6+
from meteole.clients import BaseClient, MeteoFranceClient
7+
from meteole.forecast import WeatherForecast
8+
9+
logger = logging.getLogger(__name__)
10+
11+
AVAILABLE_ARPEGE_TERRITORY: list[str] = ["EUROPE", "GLOBE"]
12+
13+
ARPEGE_INSTANT_INDICATORS: list[str] = [
14+
"GEOMETRIC_HEIGHT__GROUND_OR_WATER_SURFACE",
15+
"ISOTHERMAL_LEVEL_TW27315__GROUND_OR_WATER_SURFACE",
16+
"ISOTHERMAL_LEVEL_TW27415__GROUND_OR_WATER_SURFACE",
17+
"ISOTHERMAL_LEVEL_TW27465__GROUND_OR_WATER_SURFACE",
18+
"ISOTHERMAL_LEVEL_T27315__GROUND_OR_WATER_SURFACE",
19+
"CLOUD_BASE_HEIGHT__GROUND_OR_WATER_SURFACE",
20+
"CONVECTIVE_AVAILABLE_POTENTIAL_ENERGY_MAX__GROUND_OR_WATER_SURFACE",
21+
"CONVECTIVE_AVAILABLE_POTENTIAL_ENERGY_MODEL__GROUND_OR_WATER_SURFACE",
22+
"INHIBITION_CONVECTIVE__GROUND_OR_WATER_SURFACE",
23+
"TOTAL_WATER_VAPOUR__GROUND_OR_WATER_SURFACE",
24+
"DIVERGENCE__ISOBARIC_SURFACE",
25+
"RELATIVE_HUMIDITY__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND",
26+
"RELATIVE_HUMIDITY__ISOBARIC_SURFACE",
27+
"PLANETARY_BOUNDARY_LAYER_HEIGHT__GROUND_OR_WATER_SURFACE",
28+
"LIGHTNING_DENSITY_MEAN_3H__GROUND_OR_WATER_SURFACE",
29+
"CONVECTIVE_AVAILABLE_POTENTIAL_ENERGY_MEAN_LAYER__GROUND_OR_WATER_SURFACE",
30+
"LOW_CLOUD_COVER__GROUND_OR_WATER_SURFACE",
31+
"CONVECTIVE_CLOUD_COVER__GROUND_OR_WATER_SURFACE",
32+
"HIGH_CLOUD_COVER__GROUND_OR_WATER_SURFACE",
33+
"MEDIUM_CLOUD_COVER__GROUND_OR_WATER_SURFACE",
34+
"TOTAL_CLOUD_COVER__GROUND_OR_WATER_SURFACE",
35+
"PRESSURE__GROUND_OR_WATER_SURFACE",
36+
"PRESSURE__MEAN_SEA_LEVEL",
37+
"POTENTIAL_TEMPERATURE__POTENTIAL_VORTICITY_SURFACE_1500",
38+
"POTENTIAL_TEMPERATURE__POTENTIAL_VORTICITY_SURFACE_2000",
39+
"MAXIMUM_TEMPERATURE__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND",
40+
"MINIMUM_TEMPERATURE__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND",
41+
"PSEUDO_ADIABATIC_POTENTIAL_TEMPERATURE__ISOBARIC_SURFACE",
42+
"TEMPERATURE__GROUND_OR_WATER_SURFACE",
43+
"TEMPERATURE__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND",
44+
"TEMPERATURE__ISOBARIC_SURFACE",
45+
"U_COMPONENT_OF_WIND_GUST__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND",
46+
"U_COMPONENT_OF_WIND__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND",
47+
"U_COMPONENT_OF_WIND__ISOBARIC_SURFACE",
48+
"U_COMPONENT_OF_WIND__POTENTIAL_VORTICITY_SURFACE_1500",
49+
"U_COMPONENT_OF_WIND__POTENTIAL_VORTICITY_SURFACE_2000",
50+
"MINIMUM_VISIBILITY_180_PRECIPITATING_HYDROMETEORS__GROUND_OR_WATER_SURFACE",
51+
"MINIMUM_VISIBILITY_PRECIPITATING_HYDROMETEORS__GROUND_OR_WATER_SURFACE",
52+
"MINIMUM_VISIBILITY_180_NON_PRECIPITATING_HYDROMETEORS__GROUND_OR_WATER_SURFACE",
53+
"MINIMUM_VISIBILITY_NON_PRECIPITATING_HYDROMETEORS__GROUND_OR_WATER_SURFACE",
54+
"VERTICAL_VELOCITY_PRESSURE__ISOBARIC_SURFACE",
55+
"V_COMPONENT_OF_WIND_GUST__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND",
56+
"V_COMPONENT_OF_WIND__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND",
57+
"V_COMPONENT_OF_WIND__ISOBARIC_SURFACE",
58+
"V_COMPONENT_OF_WIND__POTENTIAL_VORTICITY_SURFACE_1500",
59+
"V_COMPONENT_OF_WIND__POTENTIAL_VORTICITY_SURFACE_2000",
60+
"GEOPOTENTIAL__ISOBARIC_SURFACE",
61+
"GEOPOTENTIAL__POTENTIAL_VORTICITY_SURFACE_1500",
62+
"GEOPOTENTIAL__POTENTIAL_VORTICITY_SURFACE_2000",
63+
]
64+
65+
ARPEGE_OTHER_INDICATORS: list[str] = [
66+
"TOTAL_WATER_PRECIPITATION__GROUND_OR_WATER_SURFACE",
67+
"LIGHTNING_DENSITY_CUMULATED__GROUND_OR_WATER_SURFACE",
68+
"TOTAL_SNOW_PRECIPITATION__GROUND_OR_WATER_SURFACE",
69+
"TOTAL_PRECIPITATION__GROUND_OR_WATER_SURFACE",
70+
]
71+
72+
73+
@final
74+
class ArpegePEForecast(WeatherForecast):
75+
"""Access the ARPEGE numerical weather forecast data from Meteo-France API.
76+
77+
Doc:
78+
- https://portail-api.meteofrance.fr/web/fr/api/pe-arpege
79+
80+
Attributes:
81+
territory: Covered area (e.g., FRANCE, EUROPE, ...).
82+
precision: Precision value of the forecast.
83+
capabilities: DataFrame containing details on all available coverage ids.
84+
"""
85+
86+
MODEL_NAME: str = "pearpege"
87+
BASE_ENTRY_POINT: str = "wcs/MF-NWP-GLOBAL-PEARP"
88+
MODEL_TYPE: str = "ENSEMBLE"
89+
ENSEMBLE_NUMBERS: int = 35
90+
DEFAULT_TERRITORY: str = "EUROPE"
91+
RELATION_TERRITORY_TO_PREC_ARPEGE: dict[str, float] = {"EUROPE": 0.1, "GLOBE": 0.25}
92+
CLIENT_CLASS: type[BaseClient] = MeteoFranceClient
93+
INDICATORS: list[str] = ARPEGE_INSTANT_INDICATORS + ARPEGE_OTHER_INDICATORS
94+
INSTANT_INDICATORS: list[str] = ARPEGE_INSTANT_INDICATORS
95+
96+
def __init__(
97+
self,
98+
client: BaseClient | None = None,
99+
*,
100+
territory: str = "EUROPE",
101+
**kwargs: Any,
102+
):
103+
"""Initializes an ArpegeForecast object.
104+
105+
The `precision` of the forecast is inferred from the specified `territory`.
106+
107+
Args:
108+
territory: The ARPEGE territory to fetch. Defaults to "EUROPE".
109+
api_key: The API key for authentication. Defaults to None.
110+
token: The API token for authentication. Defaults to None.
111+
application_id: The Application ID for authentication. Defaults to None.
112+
113+
Notes:
114+
- See `MeteoFranceClient` for additional details on the parameters `api_key`, `token`,
115+
and `application_id`.
116+
- Available territories are listed in the `AVAILABLE_TERRITORY` constant.
117+
"""
118+
super().__init__(
119+
client=client,
120+
territory=territory,
121+
precision=self.RELATION_TERRITORY_TO_PREC_ARPEGE[territory],
122+
**kwargs,
123+
)
124+
125+
def _validate_parameters(self) -> None:
126+
"""Check the territory and the precision parameters.
127+
128+
Raise:
129+
ValueError: At least, one parameter is not good.
130+
"""
131+
if self.territory not in AVAILABLE_ARPEGE_TERRITORY:
132+
raise ValueError(f"The parameter precision must be in {AVAILABLE_ARPEGE_TERRITORY}")

src/meteole/forecast.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,17 @@ def _get_data_single_forecast(
557557

558558
df: pd.DataFrame = self._grib_bytes_to_df(grib_binary, temp_dir=temp_dir)
559559

560+
if self.MODEL_NAME == "pearpege":
561+
# for unclear reasons, the pearpege API does not accept lat, long
562+
# parameters unlike the other models API.
563+
# So we retrieve all the domain and then filter the results
564+
# for the desired lat, long in _get_data_single_forecast
565+
df = df.loc[
566+
(df["latitude"] <= lat[1])
567+
& (df["latitude"] >= lat[0])
568+
& (df["longitude"] <= long[1])
569+
& (df["longitude"] >= long[0])
570+
]
560571
# Drop and rename columns
561572
df.drop(columns=["surface", "valid_time"], errors="ignore", inplace=True)
562573
df.rename(
@@ -647,19 +658,33 @@ def _get_coverage_file(
647658
else:
648659
url = f"{self._model_base_path}/{self._entry_point.replace('xxx', f'{ensemble_number:03}')}/GetCoverage"
649660

650-
params = {
651-
"service": "WCS",
652-
"version": "2.0.1",
653-
"coverageid": coverage_id,
654-
"format": "application/wmo-grib",
655-
"subset": [
661+
if self.MODEL_NAME == "pearpege":
662+
# for unclear reasons, the pearpege API does not accept lat, long
663+
# parameters unlike the other models API.
664+
# So we retrieve all the domain and then filter the results
665+
# for the desired lat, long in _get_data_single_forecast
666+
subset = [
667+
*([f"pressure({pressure})"] if pressure is not None else []),
668+
*([f"height({height})"] if height is not None else []),
669+
f"time({forecast_horizon_in_seconds})",
670+
]
671+
else:
672+
subset = [
656673
*([f"pressure({pressure})"] if pressure is not None else []),
657674
*([f"height({height})"] if height is not None else []),
658675
f"time({forecast_horizon_in_seconds})",
659676
f"lat({lat[0]},{lat[1]})",
660677
f"long({long[0]},{long[1]})",
661-
],
678+
]
679+
680+
params = {
681+
"service": "WCS",
682+
"version": "2.0.1",
683+
"coverageid": coverage_id,
684+
"format": "application/wmo-grib",
685+
"subset": subset,
662686
}
687+
663688
response = self._client.get(url, params=params)
664689

665690
return response.content

0 commit comments

Comments
 (0)