Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python

Présentation

La vapeur d’eau intégrée, également appelée vapeur d’eau totale de colonne ou eau précipitable, correspond à la quantité de vapeur d’eau intégrée verticalement dans une colonne atmosphérique. C’est une variable importante pour la correction atmosphérique, les études des nuages, l’analyse météorologique et les applications de télédétection satellitaire.

Dans ce tutoriel, nous montrons comment télécharger la vapeur d’eau totale de colonne ERA5 avec Python, lire les données avec xarray, puis créer des cartes de qualité publication sur les États-Unis continentaux.

ERA5 est la réanalyse atmosphérique de cinquième génération de l’ECMWF. Le Copernicus Climate Data Store fournit des données horaires ERA5 sur niveaux simples à partir de 1940. La variable utilisée ici est total_column_water_vapour, disponible en unités kg m-2. (Climate Data Store)

Une interprétation utile est :

1
1 kg m-2  1 mm deau précipitable

Donc, si ERA5 indique :

1
tcwv = 25 kg m-2

cela correspond approximativement à :

1
25 mm = 2.5 cm deau précipitable

Qu’est-ce que la vapeur d’eau totale de colonne ?

La vapeur d’eau totale de colonne, souvent abrégée TCWV, représente la quantité de vapeur d’eau contenue dans une colonne atmosphérique verticale allant de la surface jusqu’au sommet de l’atmosphère. Elle est aussi couramment appelée :

Nom Signification
TCWV Total column water vapor
IWV Integrated water vapor
PWV Precipitable water vapor
PW Precipitable water

ERA5 fournit cette quantité sous forme d’une variable maillée nommée :

1
total_column_water_vapour

Dans le fichier NetCDF téléchargé, le nom court de la variable est généralement :

1
tcwv

Les unités sont :

1
kg m-2

La documentation Copernicus définit la vapeur d’eau totale de colonne comme la quantité totale de vapeur d’eau dans une colonne s’étendant de la surface jusqu’au sommet de l’atmosphère, avec des unités kg m-2. (Climate Data Store)

Pourquoi utiliser ERA5 ?

ERA5 est utile parce qu’elle fournit des champs atmosphériques horaires, globalement complets. Pour les applications de télédétection satellitaire, la TCWV peut être utilisée comme indicateur de premier ordre de l’absorption atmosphérique, en particulier dans les régions spectrales infrarouges affectées par la vapeur d’eau.

Par exemple, en télédétection des feux, la vapeur d’eau affecte la transmittance atmosphérique près de la fenêtre sensible aux feux autour de 3.7–4.0 µm. La TCWV seule ne constitue pas une solution complète de transfert radiatif, mais c’est une première variable utile à explorer lorsque l’on construit un workflow de correction atmosphérique.

Prérequis

Installez les paquets Python nécessaires :

1
pip install cdsapi xarray netCDF4 matplotlib cartopy

Ou avec conda :

1
conda install -c conda-forge cdsapi xarray netcdf4 matplotlib cartopy

Vous avez également besoin d’un compte Copernicus Climate Data Store.

Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python
Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python

Après avoir créé votre compte, configurez vos identifiants CDS API dans (configuration CDSAPI) :

1
~/.cdsapirc

Le fichier devrait ressembler à ceci :

1
2
url: https://cds.climate.copernicus.eu/api
key: YOUR_PERSONAL_ACCESS_TOKEN

Vous devez également accepter les conditions de licence du jeu de données ERA5 sur niveaux simples dans l’interface web du CDS. Sinon, les requêtes Python peuvent échouer avec une erreur telle que :

1
required licences not accepted

Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python
Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python

Télécharger la vapeur d’eau totale de colonne ERA5 pour une journée complète

L’exemple suivant télécharge la vapeur d’eau totale de colonne horaire sur un domaine approximatif couvrant les États-Unis continentaux pour le 8 août 2019.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import cdsapi

dataset = "reanalysis-era5-single-levels"

request = {
    "product_type": ["reanalysis"],
    "variable": ["total_column_water_vapour"],
    "year": ["2019"],
    "month": ["08"],
    "day": ["08"],
    "time": [
        "00:00", "01:00", "02:00", "03:00",
        "04:00", "05:00", "06:00", "07:00",
        "08:00", "09:00", "10:00", "11:00",
        "12:00", "13:00", "14:00", "15:00",
        "16:00", "17:00", "18:00", "19:00",
        "20:00", "21:00", "22:00", "23:00",
    ],
    "data_format": "netcdf",
    "download_format": "unarchived",

    # Boîte englobante : Nord, Ouest, Sud, Est
    "area": [50.0, -130.0, 24.0, -65.0],
}

client = cdsapi.Client()
client.retrieve(dataset, request).download("era5_tcwv_conus_20190808.nc")

Le champ area utilise l’ordre :

1
[North, West, South, East]

Pour cet exemple :

1
[50.0, -130.0, 24.0, -65.0]

signifie :

Limite Valeur
Nord 50.0°N
Ouest 130.0°W
Sud 24.0°N
Est 65.0°W

Cela couvre les États-Unis continentaux avec une certaine marge.

Ouvrir le fichier NetCDF ERA5 avec xarray

1
2
3
4
5
import xarray as xr

ds = xr.open_dataset("era5_tcwv_conus_20190808.nc")

print(ds)

Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python
Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python

La variable TCWV est généralement nommée :

1
tcwv = ds["tcwv"]

Inspectez la variable :

1
2
3
4
5
tcwv = ds["tcwv"]

print(tcwv)
print(tcwv.attrs)
print(tcwv.coords)

Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python
Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python

Selon la sortie CDS, la coordonnée temporelle peut être nommée valid_time ou time. Le code suivant la détecte automatiquement :

1
2
3
time_name = "valid_time" if "valid_time" in tcwv.coords else "time"

print("Coordonnée temporelle :", time_name)

Tracer une carte horaire de TCWV

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import matplotlib.pyplot as plt

tcwv0 = tcwv.isel({time_name: 0})

plt.figure(figsize=(10, 6))

tcwv0.plot(
    cmap="viridis",
    cbar_kwargs={"label": "TCWV (kg m$^{-2}$ / mm)"}
)

plt.title(f"Vapeur d’eau totale de colonne ERA5 sur CONUS\n{tcwv0[time_name].values}")
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.show()

Cela donne une carte rapide de la vapeur d’eau totale de colonne pour la première heure téléchargée.

Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python
Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python

Carte de qualité publication avec côtes, frontières et États

Pour obtenir des cartes plus propres, utilisez cartopy.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature

tcwv0 = tcwv.isel({time_name: 0})

fig = plt.figure(figsize=(11, 6.5))

ax = fig.add_axes(
    [0.07, 0.12, 0.78, 0.76],
    projection=ccrs.PlateCarree()
)

cax = fig.add_axes([0.88, 0.18, 0.025, 0.62])

p = tcwv0.plot(
    ax=ax,
    transform=ccrs.PlateCarree(),
    cmap="viridis",
    add_colorbar=False
)

cbar = fig.colorbar(p, cax=cax)
cbar.set_label("Vapeur d’eau totale de colonne (kg m$^{-2}$ / mm)")

ax.add_feature(cfeature.COASTLINE, linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linewidth=0.8)
ax.add_feature(cfeature.STATES, linewidth=0.35, edgecolor="white", alpha=0.8)

ax.set_extent([-130, -65, 24, 50], crs=ccrs.PlateCarree())

gl = ax.gridlines(
    draw_labels=True,
    linewidth=0.3,
    alpha=0.4,
    linestyle="--"
)

gl.top_labels = False
gl.right_labels = False

ax.set_title(
    f"Vapeur d’eau totale de colonne ERA5 sur CONUS\n{tcwv0[time_name].values}",
    fontsize=13
)

plt.show()

Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python
Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python

La position de la barre de couleurs est contrôlée manuellement avec :

1
cax = fig.add_axes([0.88, 0.18, 0.025, 0.62])

Les valeurs signifient :

1
[left, bottom, width, height]

Donc, pour rendre la barre de couleurs plus courte, réduisez la hauteur :

1
cax = fig.add_axes([0.88, 0.22, 0.025, 0.50])

Tracer la TCWV moyenne journalière

Comme le fichier contient 24 champs horaires, nous pouvons calculer la moyenne journalière :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
tcwv_daily_mean = tcwv.mean(dim=time_name)

plt.figure(figsize=(10, 6))

tcwv_daily_mean.plot(
    cmap="viridis",
    cbar_kwargs={"label": "TCWV moyenne journalière (kg m$^{-2}$ / mm)"}
)

plt.title("Moyenne journalière ERA5 de la vapeur d’eau totale de colonne sur CONUS\n2019-08-08")
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.show()

Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python
Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python

Une version de qualité publication :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
fig = plt.figure(figsize=(11, 6.5))

ax = fig.add_axes(
    [0.07, 0.12, 0.78, 0.76],
    projection=ccrs.PlateCarree()
)

cax = fig.add_axes([0.88, 0.18, 0.025, 0.62])

p = tcwv_daily_mean.plot(
    ax=ax,
    transform=ccrs.PlateCarree(),
    cmap="viridis",
    add_colorbar=False
)

cbar = fig.colorbar(p, cax=cax)
cbar.set_label("TCWV moyenne journalière (kg m$^{-2}$ / mm)")

ax.add_feature(cfeature.COASTLINE, linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linewidth=0.8)
ax.add_feature(cfeature.STATES, linewidth=0.35, edgecolor="white", alpha=0.8)

ax.set_extent([-130, -65, 24, 50], crs=ccrs.PlateCarree())

gl = ax.gridlines(
    draw_labels=True,
    linewidth=0.3,
    alpha=0.4,
    linestyle="--"
)

gl.top_labels = False
gl.right_labels = False

ax.set_title(
    "Moyenne journalière ERA5 de la vapeur d’eau totale de colonne sur CONUS\n2019-08-08",
    fontsize=13
)

plt.show()

Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python
Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python

Convertir la TCWV en eau précipitable en centimètres

La TCWV ERA5 est exprimée en kg m-2, ce qui est numériquement équivalent à des millimètres d’eau précipitable.

Par conséquent :

1
2
tcwv_mm = tcwv
tcwv_cm = tcwv / 10.0

Vous pouvez ajouter des métadonnées :

1
2
3
tcwv_cm = tcwv / 10.0
tcwv_cm.attrs["long_name"] = "Vapeur d’eau totale de colonne"
tcwv_cm.attrs["units"] = "cm"

Puis tracer :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
tcwv0_cm = tcwv_cm.isel({time_name: 0})

plt.figure(figsize=(10, 6))

tcwv0_cm.plot(
    cmap="viridis",
    cbar_kwargs={"label": "TCWV (cm)"}
)

plt.title(f"Vapeur d’eau totale de colonne ERA5 sur CONUS\n{tcwv0_cm[time_name].values}")
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.show()

Tracer une série temporelle moyenne sur le domaine

Pour voir l’évolution horaire sur le domaine :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
tcwv_mean = tcwv.mean(dim=["latitude", "longitude"])

plt.figure(figsize=(8, 4))

tcwv_mean.plot(marker="o")

plt.title("Vapeur d’eau totale de colonne ERA5 moyenne sur le domaine")
plt.ylabel("TCWV (kg m$^{-2}$ / mm)")
plt.xlabel("Temps")
plt.grid(True, alpha=0.3)
plt.show()

Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python
Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python

Vous pouvez également tracer le minimum, la moyenne et le maximum :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
tcwv_min = tcwv.min(dim=["latitude", "longitude"])
tcwv_mean = tcwv.mean(dim=["latitude", "longitude"])
tcwv_max = tcwv.max(dim=["latitude", "longitude"])

plt.figure(figsize=(8, 4))

plt.plot(tcwv[time_name], tcwv_min, marker="o", label="min")
plt.plot(tcwv[time_name], tcwv_mean, marker="o", label="mean")
plt.plot(tcwv[time_name], tcwv_max, marker="o", label="max")

plt.title("Statistiques de la TCWV ERA5 sur le domaine")
plt.ylabel("TCWV (kg m$^{-2}$ / mm)")
plt.xlabel("Temps")
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python
Comment télécharger la vapeur d’eau intégrée depuis ERA5 avec Python

Interpoler la TCWV vers une latitude et une longitude spécifiques

Pour les applications satellitaires, nous avons souvent besoin de la valeur de TCWV à l’emplacement d’une détection de feu, d’un pixel nuageux ou d’un point de validation.

Exemple de point :

1
2
lat = 45.8326
lon = -120.5

Sélectionnez l’heure ERA5 la plus proche :

1
2
3
import pandas as pd

target_time = pd.Timestamp("2019-08-08T20:15:00")

Puis interpolez :

1
2
3
4
5
6
7
8
tcwv_at_time = tcwv.sel({time_name: target_time}, method="nearest")

tcwv_point = tcwv_at_time.interp(
    latitude=lat,
    longitude=lon
)

print(float(tcwv_point.values))

Pour plusieurs points stockés dans un DataFrame pandas :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import pandas as pd
import xarray as xr

# Exemple de DataFrame
df = pd.DataFrame({
    "latitude": [40.0, 42.5, 45.0],
    "longitude": [-120.0, -115.0, -110.0],
    "acq_date_time": [
        "2019-08-08T18:20:00",
        "2019-08-08T19:45:00",
        "2019-08-08T20:10:00",
    ],
})

df["acq_date_time"] = pd.to_datetime(df["acq_date_time"])

tcwv_values = []

for _, row in df.iterrows():
    tcwv_at_time = tcwv.sel(
        {time_name: row["acq_date_time"]},
        method="nearest"
    )

    value = tcwv_at_time.interp(
        latitude=row["latitude"],
        longitude=row["longitude"]
    )

    tcwv_values.append(float(value.values))

df["tcwv_kg_m2"] = tcwv_values
df["tcwv_cm"] = df["tcwv_kg_m2"] / 10.0

print(df)

Optionnel : créer un proxy de transmittance à 3.9 µm de premier ordre

La TCWV n’est pas équivalente à la transmittance atmosphérique. Cependant, pour les applications liées aux feux satellitaires, elle peut être utilisée pour construire un proxy simple de premier ordre.

Une approximation de base de type Beer-Lambert est :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import numpy as np

def clear_sky_transmittance_3p9_from_tcwv(
    tcwv_kg_m2,
    view_zenith_deg,
    a=0.04,
    b=0.06
):
    """
    Transmittance claire approximative de premier ordre à 3.9 µm
    à partir de la vapeur d’eau totale de colonne ERA5.

    Parameters
    ----------
    tcwv_kg_m2 : float or array-like
        Vapeur d’eau totale de colonne ERA5 en kg m-2.
        Numériquement équivalente aux mm d’eau précipitable.

    view_zenith_deg : float or array-like
        Angle zénithal de visée du satellite en degrés.

    a, b : float
        Coefficients empiriques. Ils devraient à terme être ajustés
        ou remplacés par une table de correspondance de transfert radiatif.

    Returns
    -------
    T : float or array-like
        Transmittance atmosphérique approximative entre 0 et 1.
    """

    tcwv_cm = tcwv_kg_m2 / 10.0
    mu = np.cos(np.deg2rad(view_zenith_deg))

    tau = a + b * tcwv_cm

    return np.exp(-tau / mu)

Exemple :

1
2
3
4
5
6
7
8
9
view_zenith_deg = 35.0

T_clear = clear_sky_transmittance_3p9_from_tcwv(
    tcwv_kg_m2=tcwv,
    view_zenith_deg=view_zenith_deg
)

T_clear.attrs["long_name"] = "Transmittance claire approximative à 3.9 µm"
T_clear.attrs["units"] = "1"

Tracez le premier pas de temps :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
T0 = T_clear.isel({time_name: 0})

plt.figure(figsize=(10, 6))

T0.plot(
    cmap="viridis",
    vmin=0,
    vmax=1,
    cbar_kwargs={"label": "Transmittance approximative à 3.9 µm"}
)

plt.title(f"Transmittance claire approximative à 3.9 µm\nVZA = {view_zenith_deg}°")
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.show()

Il ne s’agit que d’un proxy de premier ordre. Une approche physiquement plus robuste consisterait à utiliser RTTOV, MODTRAN ou libRadtran avec les profils verticaux, l’altitude de surface, la géométrie de visée et la fonction de réponse spectrale du capteur.

Remarques importantes

La TCWV ERA5 est utile, mais elle ne doit pas être confondue avec la transmittance atmosphérique complète.

Quantité Signification
TCWV Quantité de vapeur d’eau dans la colonne atmosphérique
Transmittance Fraction du rayonnement transmise à travers l’atmosphère
Épaisseur optique nuageuse Atténuation optique par les nuages
Profondeur optique des aérosols Atténuation optique par les aérosols ou la fumée

Pour la télédétection des feux, en particulier près de 3.7–4.0 µm, la vapeur d’eau n’est qu’une partie du problème de correction atmosphérique. Une correction complète dépend aussi des profils verticaux d’humidité et de température, de l’angle de visée, de la pression, de l’altitude, des aérosols et de la fonction de réponse spectrale du capteur.

Conclusion

Dans ce tutoriel, nous avons téléchargé la vapeur d’eau totale de colonne ERA5 pour une journée complète sur CONUS, ouvert les données avec xarray, tracé des cartes horaires et journalières, puis extrait des valeurs de TCWV à des emplacements spécifiques.

C’est une première étape utile vers des workflows de correction atmosphérique. Pour les applications de feux satellitaires, la TCWV peut aider à expliquer les variations spatiales et temporelles de l’absorption par la vapeur d’eau et peut être utilisée comme entrée de premier ordre pour estimer la transmittance claire à 3.9 µm.

L’étape suivante consiste à combiner la TCWV avec la géométrie de visée satellitaire et à comparer la transmittance approximative obtenue à un produit de référence, ou à remplacer l’approximation empirique par un modèle de transfert radiatif.

Références

Image

of