Source code for wavespectra.input.era5

"""Read ERA5 2D Wave Spectra NetCDF files"""

import numpy as np
import xarray as xr
from xarray.backends import BackendEntrypoint

from wavespectra.core.utils import create_frequencies
from wavespectra.core.attributes import attrs, set_spec_attributes


MAPPING = {
    "time": attrs.TIMENAME,
    "valid_time": attrs.TIMENAME,
    "frequency": attrs.FREQNAME,
    "frequencyNumber": attrs.FREQNAME,
    "direction": attrs.DIRNAME,
    "directionNumber": attrs.DIRNAME,
    "d2fd": attrs.SPECNAME,
    "longitude": attrs.LONNAME,
    "latitude": attrs.LATNAME,
}


[docs] def read_era5(filename_or_fileglob, chunks={}, f0=0.03453, df=1.1, as_site=False): """Read Spectra from ECMWF ERA5 netCDF format. Args: - filename_or_fileglob (str, list, filelike): filename, fileglob specifying multiple files, or filelike object to read. - chunks (dict): chunk sizes for dimensions in dataset. By default dataset is loaded using single chunk for all dimensions (see xr.open_mfdataset documentation). - f0 (float): First frequency value in Hz (e.g., 0.03453) - df (float): Multiplicative increment between frequencies. - as_site (bool): If True locations are defined by 1D site dimension. Returns: - dset (SpecDataset): spectra dataset object read from netcdf file. Note: - This reader also supports ECMWF spectra from the operational forecast. - Documentation describing how to construct the spectral grid can be found at https://www.ecmwf.int/en/forecasts/documentation-and-support/2d-wave-spectra. - If file is large to fit in memory, consider specifying chunks for 'time' and/or 'latitude/longitude' dims. """ dset = xr.open_dataset(filename_or_fileglob).chunk(chunks) return from_era5(dset, f0=f0, df=df, as_site=as_site)
def from_era5(dset, f0=0.03453, df=1.1, as_site=False): """Format ERA5 netcdf dataset to receive wavespectra accessor. Args: - dset (xr.Dataset): Dataset created from a ERA5 netcdf file. - f0 (float): First frequency value in Hz (e.g., 0.03453) - df (float): Multiplicative increment between frequencies. - as_site (bool): If True locations are defined by 1D site dimension. Returns: - Formated dataset with the SpecDataset accessor in the `spec` namespace. """ mapping = {k: v for k, v in MAPPING.items() if k != v and k in dset.variables} dset = dset.rename(mapping).reset_coords(drop=True) # Convert ERA5 format to wavespectra format dset[attrs.SPECNAME] = 10 ** dset[attrs.SPECNAME] * np.pi / 180 dset[attrs.SPECNAME] = dset[attrs.SPECNAME].fillna(0) # Assign the logarithmic frequency grid dset[attrs.FREQNAME] = create_frequencies(f0, dset[attrs.FREQNAME].size, df) # Assign the coming-from direction grid dd = 360 / dset[attrs.DIRNAME].size dset[attrs.DIRNAME] = ((np.arange(0, 360, dd) + dd / 2) + 180) % 360 # Stack lat/lon dimensions if as_site is True if as_site: dset = ( dset.stack(site=[attrs.LATNAME, attrs.LONNAME], create_index=False) .reset_coords() .transpose(attrs.TIMENAME, attrs.SITENAME, attrs.FREQNAME, attrs.DIRNAME) ) # Setting standard attributes set_spec_attributes(dset) return dset class ERA5BackendEntrypoint(BackendEntrypoint): """ERA5 backend engine.""" def open_dataset( self, filename_or_obj, *, drop_variables=None, f0=0.03453, df=1.1, as_site=False, ): return read_era5(filename_or_obj, f0=f0, df=df) def guess_can_open(self, filename_or_obj): return False description = "Open ERA5 netcdf spectra files as a wavespectra dataset." url = "https://github.com/wavespectra/wavespectra"