Source code for pycif.plugins.models.lmdz_ico.io.native2inputs_adj

from __future__ import annotations

import datetime
from os import PathLike
from pathlib import Path
from typing import Any, Literal

import numpy as np
import pandas as pd
import xarray as xr

from .outputs import fetch_end
from .....utils.check.errclass import CifFileNotFoundError, CifValueError
from .....utils.hdf5 import _hdf5_lock


[docs] def native2inputs_adj( self, datastore: dict[tuple[str, str], dict[str | datetime.datetime, Any]], input_type: str, datei: datetime.datetime, datef: datetime.datetime, runsubdir: str | PathLike, mode: Literal["fwd", "tl", "adj"] = "fwd", onlyinit: bool = False, do_simu: bool = True, check_transforms: bool = False, **kwargs, ) -> dict[tuple[str, str], dict[str | datetime.datetime, Any]]: """Converts data at the model data resolution to model compatible input files. Args: self: the model Plugin input_type (str): one of 'fluxes', 'obs' datastore: data to convert if input_type == 'fluxes', a dictionary with flux maps if input_type == 'obs', a pandas dataframe with the observations datei, datef: date interval of the sub-simulation mode (str): running mode: one of 'fwd', 'adj' and 'tl' runsubdir (str): sub-directory for the current simulation workdir (str): the directory of the whole pycif simulation Notes: - LMDZ expects daily inputs; if the periods in the control vector are longer than one day, period values are uniformly de-aggregated to the daily scale; this is done with pandas function 'asfreq' and the option 'ffill' as 'forward-filling' See Pandas page for details: https://pandas.pydata.org/pandas-docs/stable/generated/pandas .DataFrame.asfreq.html """ runsubdir = Path(runsubdir) date = pd.to_datetime(runsubdir.name, format="%Y-%m-%d_%H-%M") ddi = min(datei, datef) # Fetch end concentrations of adjoint for chain simulation if input_type == "endconcs": datastore = fetch_end( self, datastore, datei, runsubdir, mode, onlyinit, check_transforms ) # Reading only output files related to given input_type if input_type in ("inicond", "flux", "prescrconcs", "prodloss3d"): for trid in datastore: (comp, spec) = trid if comp == "inicond": file_out = runsubdir.parent / "chain" / f"restart_{date:%Y-%m-%d-%H-00}.nc" else: file_out = runsubdir / f"{comp}_out.nc" if not file_out.is_file(): raise CifFileNotFoundError( f"LMDZ did not produce adjoint output file '{file_out}' for " f"'{comp}' in '{runsubdir}'" ) with _hdf5_lock: with xr.open_dataset(file_out) as ds: da = ds[spec] if self.grid == "regular": da = da.drop_vars(["lat", "lon"]) elif self.grid == "dynamico": da = da.rename({"cell": "lon"}).expand_dims("lat", axis=1) else: raise CifValueError(f"Unknown grid type '{self.grid}'") if input_type == "flux": da = da.expand_dims("lev", axis=1) expected_time = self.flux_input_dates[ddi][:-1].astype("datetime64") if not np.all(da.time.values == expected_time): raise CifValueError( f"Invalid time coordinate in LMDZ adjoint output file '{file_out}'" ) elif input_type == "inicond": da = da.expand_dims({"time": [date]}) else: # TODO: Is this ever reached ?? da["time"] = (["time"], self.input_dates[ddi][:-1]) # Check dimensions, wrong dimension order will cause errors on # # following transforms if da.dims != ("time", "lev", "lat", "lon"): raise CifValueError( f"Invalid dimensions in LMDZ adjoint output file '{file_out}'" ) datastore[trid]["data"][ddi]["adj_out"] = da return datastore