Source code for pycif.plugins.models.lmdz_old.chemistry.read_inputs


import datetime
import os
from typing import Any, Literal, TypeVar

import xarray as xr
import pandas as pd
import numpy as np
from .....utils.check.errclass import CifFileNotFoundError, CifNotImplementedError, CifValueError

# Aliases for type hinting
Model = Any
DataType = TypeVar("DataType", xr.DataArray, xr.Dataset)


[docs] def strip_spatial_coords(data: DataType) -> DataType: """Drop 'lev', 'lat' and 'lon' coordinates from a Dataset or DataArray to ensure taht futher operation between DataArrays will be index based (like numpy operation) and not coordinate based. Args: data (xr.DataArray or xr.Dataset) Returns: xr.DataArray or xr.Dataset: without spatial coordinates """ return data.drop_vars(['lev', 'lat', 'lon'], errors="ignore")
[docs] def read_inicond_file( self: Model, runsubdir: str, mode: Literal["fwd", "tl"] ) -> xr.DataArray: """Read LMDZ initial conditions file (.nc or .bin) for a given mode (fwd or tl) Args: self (Model): LMDZ model plugin runsubdir (str): sub simulation run directory mode (str): 'fwd' (forward) or 'tl' (tangent-linear) """ if mode == "fwd": file_basename = "start" elif mode == "tl": file_basename = "start_tl" else: raise CifValueError(f"unexpected mode '{mode}', should be 'fwd' or 'tl'") # Dimensions nspec = len(self.chemistry.acspecies.attributes) nlev = self.domain.nlev nlat = self.domain.nlat nlon = self.domain.nlon nc_file = os.path.join(runsubdir, f"{file_basename}.nc") if os.path.isfile(nc_file): inicond = xr.DataArray(data=np.zeros((nspec, nlev, nlat, nlon)), dims=['spec', 'lev', 'lat', 'lon']) # Reading initial conditions from NetCDF file with xr.open_dataset(nc_file) as ds: # Looping over active species for index, spec in enumerate(self.chemistry.acspecies.attributes): spec_plg = getattr(self.chemistry.acspecies, spec) var_name = f"q{spec_plg.restart_id:02d}" inicond[index, ...] = ds[var_name].values[0, ...] else: # Reading initial conditions from Fortran binary file bin_file = os.path.join(runsubdir, f"{file_basename}.bin") if not os.path.isfile(bin_file): raise CifFileNotFoundError("could not find initial condition file " f"'{nc_file}' or '{bin_file}'") # Version info lmdz_version = self.plugin.version if lmdz_version == "std": data = np.fromfile(bin_file, offset=4) elif lmdz_version == "acc": data = np.fromfile(bin_file) else: raise CifValueError(f"unexpected LMDZ version '{lmdz_version}'") data = data.reshape((nlon, nlat, nlev, nspec), order="F").T inicond = xr.DataArray(data=data, dims=['spec', 'lev', 'lat', 'lon']) return inicond
[docs] def chemfield_time_coord(ddi: datetime.datetime) -> xr.DataArray: """Build the daily time coordinate for a chemistry-field input dataset. Returns a 1-D DataArray of daily timestamps spanning the full calendar month that contains *ddi*. Args: ddi: any date within the target month. Returns: xr.DataArray: daily date range of length ``days_in_month``. """ di = pd.to_datetime(ddi) di = di if di.is_month_start else di - pd.offsets.MonthBegin() ntime = di.days_in_month time = xr.DataArray(data=pd.date_range(di, periods=ntime, freq='1D'), dims='time') return time
[docs] def read_kinetic( self: Model, ddi: datetime.datetime, runsubdir: str ) -> xr.Dataset: """Read the LMDZ-old kinetic (pressure/temperature) field from the run directory. Opens ``kinetic.nc``, renames dimensions to CIF conventions, and trims to the month's day count. Args: self: LMDZ-old model plugin instance. ddi: period start date. runsubdir: path to the period run directory. Returns: xr.Dataset with ``pmid`` and ``temp`` variables. """ # Version info lmdz_version = self.plugin.version # Coords time = chemfield_time_coord(ddi) ntime, = time.shape kinetic_file = os.path.join(runsubdir, "kinetic.nc") with xr.open_dataset(kinetic_file) as ds: ds = ds[['pmid', 'temp']].load() if lmdz_version == "acc": # Adding looping longitude ds = xr.concat([ds, ds.isel(lon=[0])], dim='lon') ds = ds.rename({'time_counter': 'time', 'presnivs': 'lev'}) ds = ds.isel(time=slice(None, ntime)) ds = strip_spatial_coords(ds) ds['time'] = time return ds
[docs] def read_prescr( self: Model, ddi: datetime.datetime, runsubdir: str ) -> xr.DataArray: """Read prescribed-concentration fields from the LMDZ-old run directory. Reads ``prescr.nc`` for all prescribed species and aligns time to the month containing *ddi*. Args: self: LMDZ-old model plugin instance. ddi: period start date. runsubdir: path to the period run directory. Returns: xr.DataArray of prescribed concentrations. """ # Version info lmdz_version = self.plugin.version # Coords time = chemfield_time_coord(ddi) # Dimensions nspec = len(self.chemistry.prescrconcs.attributes) ntime, = time.shape nlev = self.domain.nlev nlat = self.domain.nlat nlon = self.domain.nlon prescr = xr.DataArray(data=np.zeros((nspec, ntime, nlev, nlat, nlon)), dims=['spec', 'time', 'lev', 'lat', 'lon'], coords={'time': time}) # Looping over prescribed species for index, spec in enumerate(self.chemistry.prescrconcs.attributes): scale_file = os.path.join(runsubdir, f"mod_scale_{spec}.bin") if os.path.isfile(scale_file): raise CifNotImplementedError("scaling files are not implemented") prescr_file = os.path.join(runsubdir, f"prescr_{spec}.nc") with xr.open_dataset(prescr_file) as ds: data = ds[spec].values[:ntime, ...] if lmdz_version == "acc": data = np.concatenate([data, data[..., [0]]], axis=3) prescr[index, ...] = data return prescr