Source code for pycif.plugins.datastreams.fluxes.lmdz_sflx.read

from logging import debug

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

from .....utils.hdf5 import _hdf5_lock


[docs] def read( self, name, varnames, dates, files, interpol_flx=False, tracer=None, model=None, **kwargs, ): """Read LMDZ sflx flux files and unflatten the vector grid into a pyCIF variable. For each date/file pair, rounds and normalizes the file's ``time`` values as in `fetch`, shifts years if the file is a fixed reference, finds the exact matching time index, and reshapes the flat vector flux back into a 2D ``(lat, lon)`` field: pole values (first/last vector entries) are replicated across all longitudes, and a wrap-around longitude column is appended to close the grid. Args: self: the model Plugin, providing ``domain`` (for ``nlon``/``nlat``). name: the name of the component varnames: name of the variable to read in the file dates: list of ``[start, end]`` date intervals to extract; only the start of each interval is used to match a time index. files: list of files matching `dates` interpol_flx (bool): Not currently applied (temporal interpolation logic is present but commented out); kept for interface consistency with other flux plugins. tracer: Unused directly, kept for interface consistency. model: Unused directly, kept for interface consistency. Returns: xr.DataArray: the flux data with dimensions ``(time, lev, lat, lon)``. """ debug(f"Reading LMDZ sflx files for {len(dates)} dates") # Reading fluxes for periods within the simulation window trcr_flx = [] trcr_dates = [] for dd, file_flx in zip(dates, files): debug(f"Decoding sflx file {file_flx} for date {dd[0]}") with _hdf5_lock: nc = xr.open_dataset(file_flx, decode_times=False) nlon = self.domain.nlon nlat = self.domain.nlat # Keeps only values for the corresponding date # Assumes monthly resolution times = xr.open_dataset(file_flx)["time"][:] # Round dates times = times.to_dataframe()["time"].dt.round("h").values # Check if monthly resolution freq = np.unique(np.diff(times)) times = pd.DatetimeIndex(times) if len(freq) != 1 and np.all(pd.TimedeltaIndex(np.diff(times)).days >= 28): times -= (times.day - 1) * pd.Timedelta("1D") + times.hour * pd.Timedelta( "1h" ) # Shift year if using a fixed file year_ref = times.year.min() if dd[0].year != year_ref: times = times + pd.DateOffset(years=dd[0].year - year_ref) ind_date = np.where( np.abs(times - np.datetime64(dd[0])) < np.timedelta64(1, "s") )[0][0] # Vector to map # Deals with polar boxes by sub-dividing them zonally # Also loops zonally for consistency with other call to gridded values with _hdf5_lock: flx = nc[varnames].values[ind_date, ...] flx0 = flx[0] flx1 = flx[-1] flx = flx[1:-1].reshape((nlat - 2, nlon - 1)) flx = np.append( flx, flx1[np.newaxis, np.newaxis] * np.ones((1, nlon - 1)), axis=0, ) flx = np.append( flx0[np.newaxis, np.newaxis] * np.ones((1, nlon - 1)), flx, axis=0, ) flx = np.append(flx, flx[:, np.newaxis, 0], axis=1) trcr_flx.append(flx) trcr_dates.append(dd[0]) # # Interpolating fluxes temporally between file values # if interpol_flx: # weights = [] # weights_inds = [] # for flx_file, flx, dd in zip(files, trcr_flx, dates): # inds = [ # k for k, flxx in enumerate(trcr_flx) if np.all(flx == flxx) # ] # w0 = dd - dates[inds[0]] # w1 = dates[min(inds[-1] + 1, len(dates) - 1)] - dd # dt = w1 + w0 # w0 = w0.total_seconds() / float(dt.total_seconds()) # w1 = w1.total_seconds() / float(dt.total_seconds()) # weights.append((w0, w1)) # weights_inds.append((inds[0], min(inds[-1] + 1, len(dates) - 1))) # # trcr_flx_interp = [] # for k, ((w0, w1), (i0, i1)) in enumerate(zip(weights, weights_inds)): # trcr_flx_interp.append(trcr_flx[i0] * w1 + trcr_flx[i1] * w0) # trcr_flx = trcr_flx_interp xmod = xr.DataArray( np.array(trcr_flx)[:, np.newaxis, ...], coords={"time": trcr_dates}, dims=("time", "lev", "lat", "lon"), ) return xmod