Services de calculs#
Le projet “Outils facilitant les analyses des risques aux infrastructures posés par le climat” vise à faciliter l’estimation des probabilités d’aléas climatiques en climat futur. L’objectif principal du projet est de proposer une méthode (Huard et al., 2026) permettant d’inclure les principales incertitudes climatiques, et faire en sorte que les praticiens n’aient pas à faire des choix difficiles concernant la sélection de modèles climatiques ou de scénarios de GES.
Services de calculs offerts#
Dans le cadre du projet, différents services de calculs ont été développés. Ces services sont accessibles publiquement et gratuitement via le standard OGC API Processes:
compute-indicators-obsCalcule un indicateur climatique sur une série d’observations climatiques provenant d’une station météorologique donnée. Retourne un lien vers les résultats en format zarr.
compute-indicators-simCalcule des indicateurs climatiques sur une série de simulations climatiques (1950–2100) dont les biais par rapport à une station donnée a été corrigé. Retourne un lien vers les résultats en format zarr.
compute-hazard-thresholdsCalcule la probabilité de dépassement de seuils climatiques pour différents indicateurs à une station donnée. Retourne un fichier JSON.
Exemple de calcul d’un indicateur#
Pour clarifier le fonctionnement de ces services, le premier exemple lance le calcul des jours de chauffage observé à la station McTavish au centre-ville de Montréal. Les paramètres d’entrée pour le calcul sont les suivants:
- name (str):
Identifiant de l’indicateur xclim, voir la liste des indicateurs supportés ici, par exemple,
"heating_degree_days".- params (dict):
Paramètres de l’indicateur, par exemple,
{"thresh": "10 degC"}.- stations (dict):
Numéro de station pour toutes les variables nécessaires au calcul de l’indicateur, par exemple,
{"tas": "7024745"}.
Pour lancer les calculs, il faut simplement passer une commande au serveur avec les paramètres des calculs.
import requests
import json
process = "compute-indicators-obs"
headers = {"Content-Type": "application/json", "Prefer": "respond-sync"}
url = f"https://pavics.ouranos.ca/portail-ing-backend/processes/{process}/execution"
data = {"inputs":{
"name": "HEATING_DEGREE_DAYS",
"params": {"thresh": "10 degC"},
"stations": {"tas": "7024745"}
}}
resp = requests.post(url, json=data, headers=headers, timeout=60)
print(resp.headers["location"])
https://pavics.ouranos.ca/portail-ing-backend/jobs/a7b23106-05f3-11f1-8dc5-0242ac120025
On peut ensuite consulter le statut de la tâche en cours en suivant le lien ci-dessus. On accède au résultat de la tâche avec la commande suivante:
results = requests.get(resp.headers['location'] + "/results?f=json").json()
print(json.dumps(results, indent=2))
{
"id": "links",
"time": 0.010171710979193449,
"value": "https://minio.ouranos.ca/portail-ing/workspace/HEATING-DEGREE-DAYS_415b2471179aca9288146db300f20c4d_obs_tas7024745_xc0-53-2.zarr"
}
Le lien qui est retourné pointe vers un fichier zarr hébergé sur un serveur Minio, un service de stockage compatible avec le standard S3. Pour ouvrir et lire le fichier, on utilise les librairies s3fs pour accéder au systèmes de fichiers, et xarray pour lire le format Zarr:
import s3fs
import xarray as xr
dataurl = requests.utils.parse_url(results['value'])
fs = s3fs.S3FileSystem(
endpoint_url=f"{dataurl.scheme}://{dataurl.hostname}",
anon=True,
)
store = fs.get_mapper(dataurl.path)
ds = xr.open_zarr(store, decode_timedelta=False)
ds
<xarray.Dataset> Size: 3kB
Dimensions: (time: 182)
Coordinates:
* time (time) datetime64[ns] 1kB 1840-01-01 ... 2021-01-01
Data variables:
heating_degree_days (time) float64 1kB dask.array<chunksize=(182,), meta=np.ndarray>On peut aussi utiliser l’utilitaire mc, fourni par Minio pour télécharger directement le dossier zarr sur la machine locale (voir stockage). On l’ouvrira par la suite avec xr.open_zarr("<dossier.zarr>") dans un interpréteur python.
Exemple de calcul des probabilités de dépassement#
Le portail offre aussi un service de calcul “tout-en-un” qui réplique les résultats offert à l’onglet « Seuils climatiques » de l’interface web. L’idée est la même qu’à la section précédente, mais le résultat est un dictionnaire des probabilités de dépassement. Les données d’entrées nécessaires sont:
- indicators (List[Dict]):
Une liste d’indicateurs et de leur paramètres, c’est à dire une liste de dictionnaires avec les clefs
nameetparamstels que définies dans la section précédente.- stations (Dict):
Le même argument
stationsque précédemment.- hazards (List[List[Dict]]):
Pour chaque indicateur, une liste d’aléas, chacuns définis soit par une valeur de dépassement
X, soit par une période de retourT. Unedescriptionoptionnelle peut aussi être donnée.- analysis (Dict):
Certains paramètres modifiant l’analyse statistique. Tous sont optionnels.
Les options disponibles pour analysis sont:
- ref_period (tuple):
Le début et la fin de la période de référence (en années). Défaut :
(1991, 2020).- fut_period (tuple):
Le début et la fin de la période futur étudiée (en années). Défaut :
(2041, 2070).- dist (List[str]):
Le nom des distributions statistiques à utiliser pour chaque indicateurs. La valeur par défaut dépend de l’indicateur.
import requests
process = "compute-hazard-thresholds"
headers = {"Content-Type": "application/json", "Prefer": "respond-sync"}
url = f"https://pavics.ouranos.ca/portail-ing-backend/processes/{process}/execution"
data = {"inputs": {
"indicators": [
{
"name": "HEATING_DEGREE_DAYS",
"params": {"thresh": "10 degC"},
},
{
"name": "COOLING_DEGREE_DAYS",
"params": {"thresh": "25 degC"}
}
],
"stations": {"tas": "7024745"},
"analysis": {"fut_period": (2071, 2100)},
"hazards": [
[ # Pour HEATING_DEGREE_DAYS
{"description": "Usine A", "X": 4000},
{"description": "Usine B", "T": 20}
],
[ # Pour COOLING_DEGREE_DAYS
{"description": "Usine A", "X": 40},
{"description": "Usine B", "T": 2}
]
]
}}
resp = requests.post(url, json=data, headers=headers, timeout=60)
print(resp.headers["location"])
https://pavics.ouranos.ca/portail-ing-backend/jobs/a87bf874-05f3-11f1-8956-0242ac120025
Les résultats sont disponibles directement dans la réponse du serveur, en format JSON:
results = requests.get(resp.headers['location'] + "/results?f=json").json()
print(json.dumps(results, indent=2))
{
"value": {
"analysis": {
"fut_period": [
2071,
2100
],
"indicators": [
{
"bic": 390.2350617912555,
"distribution": "uniform",
"long_name": "Somme cumul\u00e9e des degr\u00e9s de temp\u00e9rature pour la temp\u00e9rature moyenne quotidienne sous 10 \u00b0C"
},
{
"bic": 228.36723565465556,
"distribution": "gamma",
"long_name": "Somme cumul\u00e9e des degr\u00e9s de temp\u00e9rature pour la temp\u00e9rature moyenne quotidienne au-dessus de 25 \u00b0C"
}
],
"level": 0.05,
"ref_period": [
1986,
2021
]
},
"hazards": [
[
{
"descr": "Usine A",
"fut_sf": 0.0,
"obs_sf": 0.0,
"obs_t": Infinity,
"ratio": NaN,
"ref_sf": 0.0,
"value": 4000,
"xid": "HEATING_DEGREE_DAYS(freq='YS-JAN', thresh='10 \u00b0C')"
},
{
"descr": "Usine B",
"fut_sf": 0.0067179381374612695,
"obs_sf": 0.05,
"obs_t": 20,
"ratio": -0.9629477968638572,
"ref_sf": 0.18131008600965554,
"value": 2525.6749999999997,
"xid": "HEATING_DEGREE_DAYS(freq='YS-JAN', thresh='10 \u00b0C')"
}
],
[
{
"descr": "Usine A",
"fut_sf": 0.8329032731449297,
"obs_sf": 0.06532667900772003,
"obs_t": 15.30768156577842,
"ratio": 7.748859862924245,
"ref_sf": 0.09520135036961691,
"value": 40,
"xid": "COOLING_DEGREE_DAYS(freq='YS-JAN', thresh='25 \u00b0C')"
},
{
"descr": "Usine B",
"fut_sf": 0.9887923036117812,
"obs_sf": 0.5,
"obs_t": 2,
"ratio": 0.9155449254035206,
"ref_sf": 0.51619374231251,
"value": 12.595725167075102,
"xid": "COOLING_DEGREE_DAYS(freq='YS-JAN', thresh='25 \u00b0C')"
}
]
]
}
}
Notez qu’il est préférable de lancer des requêtes en mode asynchrone afin d’éviter des problèmes de timeout si le serveur est sollicité:
headers = {"Content-Type": "application/json", "Prefer": "respond-async"}
Si les calculs ne sont pas démarrés, le serveur retourne le résultat suivant:
{'code': 'ResultNotReady',
'type': 'ResultNotReady',
'description': 'job accepted but not yet running'}