Last updated on Feb 20, 2026.

Source code for netsse.model.era5

# -*- coding: utf-8 -*-
"""
Retrieve and process sea state information from the **ERA5 dataset**.

.. dropdown:: Copyright (C) 2023-2026 Technical University of Denmark, R.E.G. Mounet
    :color: primary
    :icon: law

    *This code is part of the NetSSE software.*

    NetSSE is free software: you can redistribute it and/or modify it under
    the terms of the GNU General Public License as published by the Free
    Software Foundation, either version 3 of the License, or (at your
    option) any later version.

    NetSSE is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    for more details.

    You should have received a copy of the GNU General Public License along
    with this program.  If not, see https://www.gnu.org/licenses/.

    To credit the author, users are encouraged to use below reference:

    .. code-block:: text

        Mounet, R. E. G., & Nielsen, U. D. NetSSE: An open-source Python package
        for network-based sea state estimation from ships, buoys, and other
        observation platforms (version 2.2). Technical University of Denmark,
        GitLab. February 2026. https://doi.org/10.11583/DTU.26379811.

*Last updated on 20-02-2026 by R.E.G. Mounet*

"""

import sys
from pathlib import Path
import cdsapi
import pandas as pd
import numpy as np


[docs] ERA5_PARAMS = { "swh1": { "name": "Significant wave height of first swell partition", "unit": "m", "CDS_name": "significant_wave_height_of_first_swell_partition", "shortName": "swh1", "paramId": 140121, }, "mwd1": { "name": "Mean wave direction of first swell partition", "unit": "degrees", "CDS_name": "mean_wave_direction_of_first_swell_partition", "shortName": "mwd1", "paramId": 140122, }, "mwp1": { "name": "Mean wave period of first swell partition", "unit": "s", "CDS_name": "mean_wave_period_of_first_swell_partition", "shortName": "mwp1", "paramId": 140123, }, "swh2": { "name": "Significant wave height of second swell partition", "unit": "m", "CDS_name": "significant_wave_height_of_second_swell_partition", "shortName": "swh2", "paramId": 140124, }, "mwd2": { "name": "Mean wave direction of second swell partition", "unit": "degrees", "CDS_name": "mean_wave_period_of_second_swell_partition", "shortName": "mwd2", "paramId": 140125, }, "mwp2": { "name": "Mean wave period of second swell partition", "unit": "s", "CDS_name": "mean_wave_period_of_second_swell_partition", "shortName": "mwp2", "paramId": 140126, }, "swh3": { "name": "Significant wave height of third swell partition", "unit": "m", "CDS_name": "significant_wave_height_of_third_swell_partition", "shortName": "swh3", "paramId": 140127, }, "mwd3": { "name": "Mean wave direction of third swell partition", "unit": "degrees", "CDS_name": "mean_wave_direction_of_third_swell_partition", "shortName": "mwd3", "paramId": 140128, }, "mwp3": { "name": "Mean wave period of third swell partition", "unit": "s", "CDS_name": "mean_wave_period_of_third_swell_partition", "shortName": "mwp3", "paramId": 140129, }, "wss": { "name": "Wave Spectral Skewness", "unit": "dimensionless", "CDS_name": "wave_spectral_skewness", "shortName": "wss", "paramId": 140207, }, "wstar": { "name": "Free convective velocity over the oceans", "unit": "m s**-1", "CDS_name": "free_convective_velocity_over_the_oceans", "shortName": "wstar", "paramId": 140208, }, "rhoao": { "name": "Air density over the oceans", "unit": "kg m**-3", "CDS_name": "air_density_over_the_oceans", "shortName": "rhoao", "paramId": 140209, }, "phiaw": { "name": "Normalized energy flux into waves", "unit": "dimensionless", "CDS_name": "normalized_energy_flux_into_waves", "shortName": "phiaw", "paramId": 140211, }, "phioc": { "name": "Normalized energy flux into ocean", "unit": "dimensionless", "CDS_name": "normalized_energy_flux_into_ocean", "shortName": "phioc", "paramId": 140212, }, "tauoc": { "name": "Normalized stress into ocean", "unit": "dimensionless", "CDS_name": "normalized_stress_into_ocean", "shortName": "tauoc", "paramId": 140214, }, "ust": { "name": "U-component stokes drift", "unit": "m s**-1", "CDS_name": "u_component_stokes_drift", "shortName": "ust", "paramId": 140215, }, "vst": { "name": "V-component stokes drift", "unit": "m s**-1", "CDS_name": "v_component_stokes_drift", "shortName": "vst", "paramId": 140216, }, "tmax": { "name": "Period corresponding to maximum individual wave height", "unit": "s", "CDS_name": "period_corresponding_to_maximum_individual_wave_height", "shortName": "tmax", "paramId": 140217, }, "hmax": { "name": "Maximum individual wave height", "unit": "m", "CDS_name": "maximum_individual_wave_height", "shortName": "hmax", "paramId": 140218, }, "wmb": { "name": "Model bathymetry", "unit": "m", "CDS_name": "model_bathymetry", "shortName": "wmb", "paramId": 140219, }, "mp1": { "name": "Mean wave period based on first moment", "unit": "s", "CDS_name": "mean_wave_period_based_on_first_moment", "shortName": "mp1", "paramId": 140220, }, "mp2": { "name": "Mean zero-crossing wave period", "unit": "s", "CDS_name": "mean_zero_crossing_wave_period", "shortName": "mp2", "paramId": 140221, }, "wdw": { "name": "Wave spectral directional width", "unit": "Radians", "CDS_name": "wave_spectral_directional_width", "shortName": "wdw", "paramId": 140222, }, "p1ww": { "name": "Mean wave period based on first moment for wind waves", "unit": "s", "CDS_name": "mean_wave_period_based_on_first_moment_for_wind_waves", "shortName": "p1ww", "paramId": 140223, }, "p2ww": { "name": "Mean wave period based on second moment for wind waves", "unit": "s", "CDS_name": "mean_wave_period_based_on_second_moment_for_wind_waves", "shortName": "p2ww", "paramId": 140224, }, "dwww": { "name": "Wave spectral directional width for wind waves", "unit": "Radians", "CDS_name": "wave_spectral_directional_width_for_wind_waves", "shortName": "dwww", "paramId": 140225, }, "p1ps": { "name": "Mean wave period based on first moment for swell", "unit": "s", "CDS_name": "mean_wave_period_based_on_first_moment_for_swell", "shortName": "p1ps", "paramId": 140226, }, "p2ps": { "name": "Mean wave period based on second moment for swell", "unit": "s", "CDS_name": "mean_wave_period_based_on_second_moment_for_wind_waves", "shortName": "p2ps", "paramId": 140227, }, "dwps": { "name": "Wave spectral directional width for swell", "unit": "Radians", "CDS_name": "wave_spectral_directional_width_for_swell", "shortName": "dwps", "paramId": 140228, }, "swh": { "name": "Significant height of combined wind waves and swell", "unit": "m", "CDS_name": "significant_height_of_combined_wind_waves_and_swell", "shortName": "swh", "paramId": 140229, }, "mwd": { "name": "Mean wave direction", "unit": "degrees", "CDS_name": "mean_wave_direction", "shortName": "mwd", "paramId": 140230, }, "pp1d": { "name": "Peak wave period", "unit": "s", "CDS_name": "peak_wave_period", "shortName": "pp1d", "paramId": 140231, }, "mwp": { "name": "Mean wave period", "unit": "s", "CDS_name": "mean_wave_period", "shortName": "mwp", "paramId": 140232, }, "cdww": { "name": "Coefficient of drag with waves", "unit": "dimensionless", "CDS_name": "coefficient_of_drag_with_waves", "shortName": "cdww", "paramId": 140233, }, "shww": { "name": "Significant height of wind waves", "unit": "m", "CDS_name": "significant_height_of_wind_waves", "shortName": "shww", "paramId": 140234, }, "mdww": { "name": "Mean direction of wind waves", "unit": "degrees", "CDS_name": "mean_direction_of_wind_waves", "shortName": "mdww", "paramId": 140235, }, "mpww": { "name": "Mean period of wind waves", "unit": "s", "CDS_name": "mean_period_of_wind_waves", "shortName": "mpww", "paramId": 140236, }, "shts": { "name": "Significant height of total swell", "unit": "m", "CDS_name": "significant_height_of_total_swell", "shortName": "shts", "paramId": 140237, }, "mdts": { "name": "Mean direction of total swell", "unit": "degrees", "CDS_name": "mean_direction_of_total_swell", "shortName": "mdts", "paramId": 140238, }, "mpts": { "name": "Mean period of total swell", "unit": "s", "CDS_name": "mean_period_of_total_swell", "shortName": "mpts", "paramId": 140239, }, "msqs": { "name": "Mean square slope of waves", "unit": "dimensionless", "CDS_name": "mean_square_slope_of_waves", "shortName": "msqs", "paramId": 140244, }, "wind": { "name": "10 metre wind speed", "unit": "m s**-1", "CDS_name": "ocean_surface_stress_equivalent_10m_neutral_wind_speed", "shortName": "wind", "paramId": 140245, }, "dwi": { "name": "10 metre wind direction", "unit": "degrees", "CDS_name": "ocean_surface_stress_equivalent_10m_neutral_wind_direction", "shortName": "dwi", "paramId": 140249, }, "wsk": { "name": "Wave spectral kurtosis", "unit": "dimensionless", "CDS_name": "wave_spectral_kurtosis", "shortName": "wsk", "paramId": 140252, }, "bfi": { "name": "Benjamin-Feir index", "unit": "dimensionless", "CDS_name": "benjamin_feir_index", "shortName": "bfi", "paramId": 140253, }, "wsp": { "name": "Wave spectral peakedness", "unit": "dimensionless", "CDS_name": "wave_spectral_peakedness", "shortName": "wsp", "paramId": 140254, }, }
[docs] def retrieve_dirwavespec(index_month=0, folder_output="../Output/"): """Retrieve directional wave spectrum data from the ERA5 dataset (Hersbach et al., 2020, 2023). The function retrieves data from the ERA5 dataset for a specific period of time and geographical area. The latter must be specified in a dedicated `'ERA5_request.csv' <https://gitlab.gbar.dtu.dk/regmo/NetSSE/-/blob/wip/docs/examples/ERA5_request.csv>`_ file at the location path given in ``folder_output`` informing the spatial and temporal coverage of the data to be retrieved. The content of the file must follow the example below (including the header line!): .. code-block:: text ,Year,Month,Day_start,Day_end,NrDays,Longitude_min,Longitude_max,Latitude_min,Latitude_max 0,2007,9,12,25,30,-55.0,-5.0,46.0,50.0 1,2007,10,12,26,31,-55.0,-5.0,45.0,50.0 There can be as many lines as needed in the ERA5 request file, each line corresponding to a single retrieval request of data located all within the same month. The function retrieves the directional wave spectrum data for the request line having the index ``index_month`` in the file. The default value for ``index_month`` is ``0``, which means to submit the first request in the request file. The data is retrieved for the month and year specified in the `'Month'` and `'Year'` columns of the request file; e.g., September 2007 in the first request contained in the above example). The retrieval period covers from the start day at 00:00 to the end day at 23:00; e.g., from 12 September 2007 at 00:00 to 25 September 2007 at 23:00 in the first request contained in the above example. Longitudes are specified in degrees East and latitudes in degrees North. The data is retrieved for the geographical area defined by the minimum and maximum longitudes and latitudes; e.g., from 46.0 to 50.0 degrees North and from -55.0 to -5.0 degrees East in the first request contained in the above example. The spatial resolution is fixed to 0.5 degrees in both latitude and longitude. The data is retrieved using the Copernicus Climate Data Store (CDS) API. The user must have a valid CDS API key to access the data. The downloaded data is stored in a NetCDF file at the location path specified in the variable ``folder_output``. The default value for ``folder_output`` is ``'../Output/'``. References ---------- 1. Hersbach, H., Bell, B., Berrisford, P., Hirahara, S., HorĂĄnyi, A., Muñoz-Sabater, J., Nicolas, J., Peubey, C., Radu, R., Schepers, D., Simmons, A., Soci, C., Abdalla, S., Abellan, X., Balsamo, G., Bechtold, P., Biavati, G., Bidlot, J., Bonavita, M., De Chiara, G., Dahlgren, P., Dee, D., Diamantakis, M., Dragani, R., Flemming, J., Forbes, R., Fuentes, M., Geer, A., Haimberger, L., Healy, S., Hogan, R. J., HĂłlm, E., JaniskovĂĄ, M., Keeley, S., Laloyaux, P., Lopez, P., Lupu, C., Radnoti, G., de Rosnay, P., Rozum, I., Vamborg, F., Villaume, S., and ThĂ©paut, J. N. The ERA5 global reanalysis. Quarterly Journal of the Royal Meteorological Society 146, 730 (2020), 1999–2049, DOI: 10.1002/qj.3803. 2. Hersbach, H., Bell, B., Berrisford, P., Biavati, G., HorĂĄnyi, A., Muñoz Sabater, J., Nicolas, J., Peubey, C., Radu, R., Rozum, I., Schepers, D., Simmons, A., Soci, C., Dee, D., ThĂ©paut, J-N. (2023): ERA5 hourly data on single levels from 1940 to present. Copernicus Climate Change Service (C3S) Climate Data Store (CDS), DOI: 10.24381/cds.adbb2d47 (Accessed on 26-02-2025). See Also -------- retrieve_params : Function to retrieve wave parameter data from the ERA5 dataset. Example ------- To retrieve the directional wave spectrum data for the month with index 1 in the file `'ERA5_request.csv'` stored in the folder `'../Output/'`, run: >>> retrieve_dirwavespec(index_month=1,folder_output='../Output/') >>> # Output: >>> # Retrieving ERA5 data for the month with index 1... >>> # Selected dates: 2007-10-12/to/2007-10-26 >>> # Selected area: 50.0/-55.0/45.0/-5.0 >>> # The downloaded file is stored at '../Output/ERA5_2D_spectra_2007-10.nc'. """ try: c = cdsapi.Client() except Exception: sys.exit( "The CDS API client could not be initiated. Please check your CDS API key." ) # Import the spatial boundaries for the specific month: input_file = Path(folder_output + "ERA5_request.csv") if not input_file.is_file(): sys.exit( 'The file "ERA5_request.csv" is missing from the folder "%s".' % folder_output ) # Build the date and area strings: df_selec_areas = pd.read_csv(input_file) selec_month = df_selec_areas.loc[index_month] year = int(selec_month.loc["Year"]) month = int(selec_month.loc["Month"]) day_start, day_end, minlon_month, maxlon_month, minlat_month, maxlat_month = ( np.int32( selec_month.loc[ [ "Day_start", "Day_end", "Longitude_min", "Longitude_max", "Latitude_min", "Latitude_max", ] ] ) ) date_str = "%04.0f-%02.0f-%02.0f/to/%04.0f-%02.0f-%02.0f" % ( year, month, day_start, year, month, day_end, ) area_str = "%s/%s/%s/%s" % (maxlat_month, minlon_month, minlat_month, maxlon_month) # Define the output file: filename_output = folder_output + "ERA5_2D_spectra_%04.0f-%02.0f.nc" % (year, month) print("Retrieving ERA5 data for the month with index %s..." % index_month) print("Selected dates: ", date_str) print("Selected area: ", area_str) # Retrieve the directional wave spectrum data: c.retrieve( "reanalysis-era5-complete", { "class": "ea", "date": date_str, "direction": "1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24", "area": area_str, "grid": "0.5/0.5", "expver": "1", "frequency": "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", "param": "251.140", "stream": "wave", "time": "00/to/23/by/1", "type": "an", "format": "netcdf", }, filename_output, ) print("The downloaded file is stored at %s." % filename_output)
[docs] def retrieve_params(index_request=0, folder_output="../Output/"): """Retrieve wave parameter data from the ERA5 dataset (Hersbach et al., 2020, 2023). The function retrieves data from the ERA5 dataset for a specific period of time and geographical area. The latter must be specified in a dedicated `'ERA5_request_params.csv' <https://gitlab.gbar.dtu.dk/regmo/NetSSE/-/blob/wip/docs/examples/ERA5_request_params.csv>`_ file at the location path given in ``folder_output`` informing the spatial and temporal coverage of the data to be retrieved. The content of the file must follow the example below (including the header line!): .. code-block:: text ,Year,Month_start,Month_end,Longitude_min,Longitude_max,Latitude_min,Latitude_max,Var1,Var2,Var3,Var4,Var5,Var6,Var7,Var8 0,2020,1,3,0.5,5.5,55.0,58.5,swh,mp1,p1ps,p1ww,pp1d,shts,shww,wmb 1,2020,4,6,0.5,5.5,55.0,58.5,swh,mp1,p1ps,p1ww,pp1d,shts,shww,wmb There can be as many lines as needed in the ERA5 request file, each line corresponding to a single retrieval request of data located all within the same year. The function retrieves the parameter data for the request line having the index ``index_request`` in the file. The default value for ``index_request`` is ``0``, which means to submit the first request in the request file. The data is retrieved for the year specified in the `'Year'` column of the request file. It is possible to specify which months should be downloaded using the `'Month_start'` and `'Month_end'` columns; e.g., January-March 2020 in the first request contained in the above example). The retrieval period covers from the first day of the start month at 00:00 to the last day of the end month at 23:00; e.g., from 1 January 2020 at 00:00 to 31 March 2020 at 23:00 in the first request contained in the above example. Longitudes are specified in degrees East and latitudes in degrees North. The data is retrieved for the geographical area defined by the minimum and maximum longitudes and latitudes; e.g., from 55.0 to 58.5 degrees North and from 0.5 to 5.5 degrees East in the first request contained in the above example. The spatial resolution is fixed to 0.5 degrees in both latitude and longitude. The parameters to be retrieved are specified in the columns `'VarX'` of the request file. The available wave parameters are listed in `Table 7 of the ERA5 documentation <https://confluence.ecmwf.int/display/CKB/ERA5%3A+data+documentation#heading-Table7waveparametersinstantaneous>`_. The short names of the parameters can be used in the request file; e.g., `'swh'` for significant height of combined wind waves and swell, `'mp1'` for mean wave period based on first moment, etc. The number of parameters that can be downloaded at once is unlimited, and as many columns named `'VarX` can be added as needed in the request file. In the above example, both requests retrieve 8 parameters. .. warning:: For the request to be validated by the Copernicus Climate Data Store (CDS) API, the request size should not exceed a certain limit set by the CDS. If the request is too large, it is recommended to split it into smaller requests by reducing the number of months and/or the geographical area covered in each request. The present function will check the request size and compare it to a conservative reference set by the NetSSE developers. If the request exceeds this reference limit, the function will display a warning message and advise the user to modify the request parameters accordingly. The data is retrieved using the Copernicus Climate Data Store (CDS) API. The user must have a valid CDS API key to access the data. The downloaded data is stored in a NetCDF file at the location path specified in the variable ``folder_output``. The default value for ``folder_output`` is ``'../Output/'``. References ---------- 1. Hersbach, H., Bell, B., Berrisford, P., Hirahara, S., HorĂĄnyi, A., Muñoz-Sabater, J., Nicolas, J., Peubey, C., Radu, R., Schepers, D., Simmons, A., Soci, C., Abdalla, S., Abellan, X., Balsamo, G., Bechtold, P., Biavati, G., Bidlot, J., Bonavita, M., De Chiara, G., Dahlgren, P., Dee, D., Diamantakis, M., Dragani, R., Flemming, J., Forbes, R., Fuentes, M., Geer, A., Haimberger, L., Healy, S., Hogan, R. J., HĂłlm, E., JaniskovĂĄ, M., Keeley, S., Laloyaux, P., Lopez, P., Lupu, C., Radnoti, G., de Rosnay, P., Rozum, I., Vamborg, F., Villaume, S., and ThĂ©paut, J. N. The ERA5 global reanalysis. Quarterly Journal of the Royal Meteorological Society 146, 730 (2020), 1999–2049, DOI: 10.1002/qj.3803. 2. Hersbach, H., Bell, B., Berrisford, P., Biavati, G., HorĂĄnyi, A., Muñoz Sabater, J., Nicolas, J., Peubey, C., Radu, R., Rozum, I., Schepers, D., Simmons, A., Soci, C., Dee, D., ThĂ©paut, J-N. (2023): ERA5 hourly data on single levels from 1940 to present. Copernicus Climate Change Service (C3S) Climate Data Store (CDS), DOI: 10.24381/cds.adbb2d47 (Accessed on 26-02-2025). See Also -------- retrieve_dirwavespec : Function to retrieve directional wave spectrum data from the ERA5 dataset. Example ------- To retrieve the wave parameter data for the month with index 1 in the file `'ERA5_request_params.csv'` stored in the folder `'../Output/'`, run: >>> retrieve_wave_params(index_request=1,folder_output='../Output/') >>> # Output: >>> # Retrieving ERA5 data for the request with index 1... >>> # Selected dates: 2020-01-01/to/2020-03-31 >>> # Selected area: 58.5/0.5/55.0/5.5 >>> # The downloaded file is stored at '../Output/ERA5_params_2020-01to03.zip'. """ try: c = cdsapi.Client() except Exception: sys.exit( "The CDS API client could not be initiated. Please check your CDS API key." ) # Import the spatial boundaries for the specific month: input_file = Path(folder_output + "ERA5_request_params.csv") if not input_file.is_file(): sys.exit( 'The file "ERA5_request_params.csv" is missing from the folder "%s".' % folder_output ) # Build the time lists: df_selec_areas = pd.read_csv(input_file) selec_request = df_selec_areas.loc[index_request] if ( "Month_start" in df_selec_areas.columns and "Month_end" in df_selec_areas.columns ): month_start = selec_request.loc["Month_start"] month_end = selec_request.loc["Month_end"] else: month_start = 1 month_end = 12 year, minlon, maxlon, minlat, maxlat = np.array( selec_request.loc[ [ "Year", "Longitude_min", "Longitude_max", "Latitude_min", "Latitude_max", ] ].values ) year = [str(year)] months = ["%02.0f" % m for m in np.arange(month_start, month_end + 1)] Nmonths = len(months) days = ["%02.0f" % d for d in np.arange(1, 32)] Ndays = len(days) * Nmonths # Define the geographical area: Nlon = int((maxlon - minlon) // 0.5) + 1 Nlat = int((maxlat - minlat) // 0.5) + 1 Npoints = Nlon * Nlat area = [maxlat, minlon, minlat, maxlon] area_str = "%s/%s/%s/%s" % (maxlat, minlon, minlat, maxlon) # Build the variables list: var_list = [] for col in df_selec_areas.columns: if "Var" in col: var_name = str(selec_request.loc[col]) if len(var_name) < 6: var_name = ERA5_PARAMS[var_name]["CDS_name"] var_list.append(var_name) print("Variable list to retrieve: ", var_list) Nvars = len(var_list) # Check the request size: request_size = Npoints * Ndays * Nvars * 24 conversion_factor = ( 141 / 2000 ) # conservative conversion factor set by NetSSE developers estimated_ERA5_size = request_size * conversion_factor reference_ERA5_size = 121e3 if estimated_ERA5_size > reference_ERA5_size: print( "WARNING: The request size is estimated to be %1.2e, which would exceed the ERA5 limit cost of %1.2e." % (estimated_ERA5_size, reference_ERA5_size) ) print( "Please consider reducing the number of months and/or the geographical area covered in the request to avoid potential rejection by the CDS API." ) return # Define the output file: filename_output = folder_output + "ERA5_params_%s-%02.0fto%02.0f.nc" % ( year[0], month_start, month_end, ) print("Retrieving ERA5 data for the request with index %s..." % index_request) print("Selected year: ", year[0]) print("Selected months: ", months) print("Selected area: ", area_str) # Retrieve the wave parameter data: dataset = "reanalysis-era5-single-levels" request = { "product_type": ["reanalysis"], "variable": var_list, "year": year, "month": months, "day": days, "time": "00/to/23/by/1", "data_format": "netcdf", "download_format": "unarchived", "area": area, } c.retrieve(dataset, request, filename_output) print("The downloaded data is stored at %s." % filename_output)
if __name__ == "__main__":
[docs] index_request = int(sys.argv[1])
retrieve_params(index_request=index_request, folder_output="../ERA5_data/") # if __name__ == "__main__": # index_month = int(sys.argv[1]) # retrieve_dirwavespec(index_month=index_month)