Source code for pycif.plugins.models.lagrangian.io.inputs.inicond_ad

import datetime
import glob
import os
from logging import debug

import numpy as np
import pandas as pd

from ......utils.parallel import thread
from ...utils.read import read_flexpart_gridinit
from ......utils.check.errclass import CifError


[docs] def inicond_contribution_ad( self, mode, dataobs, fp_header_init, spec, ddi ): """Compute the adjoint initial-condition sensitivity from FLEXPART fields. Reads initial-condition sensitivity grids and weights them by ``adj_out`` departures from *dataobs*, accumulating the result into the in-memory initial-condition sensitivity store indexed by period and grid cell. Args: self: Lagrangian model plugin instance. mode (str): ``'adj'``. dataobs: CIF observation data-store (contains ``'adj_out'``). fp_header_init: FLEXPART initial-condition sensitivity header. spec (str): species name. ddi (datetime): sub-simulation period start. """ inicond = {"spec": self.datainicond[("inicond", spec)][ddi]["spec"]} nlat, nlon = self.domain.zlat.shape nz = (fp_header_init.outheight != 0.).sum() ini_dates = pd.DatetimeIndex( self.datainicond[("inicond", spec)][ddi]["spec"].time.values ).to_pydatetime() ndates =len(ini_dates) # Execute parallel threads nthreads = self.nthreads thread_intervals = np.linspace(0, len(dataobs), nthreads + 1).astype(int) @thread def thread_function(ithread): inicond_sensit_tmp = np.zeros((ndates, nz, nlat, nlon)) for obs_i in range(thread_intervals[ithread], thread_intervals[ithread + 1]): process_obs_row( self, dataobs, ithread, fp_header_init, ini_dates, inicond, inicond_sensit_tmp, obs_i ) return inicond_sensit_tmp inicond_sensit = sum(thread_function(range(nthreads))) # Flush inicond self.datainicond[("inicond", spec)][ddi]["spec"] = None return inicond_sensit
[docs] def process_obs_row( self, dataobs, ithread, fp_header_init, ini_dates, inicond, inicond_sensit_tmp, obs_i ): """Compute the adjoint initial-condition sensitivity for a single observation row. Reads the initial-condition sensitivity field for *obs_i* and weights it by ``adj_out`` from *dataobs*, accumulating the result into *inicond_sensit_tmp*. Args: self: Lagrangian model plugin instance. dataobs: CIF observation data-store for the period. ithread (int): thread index. fp_header_init: FLEXPART initial-condition sensitivity header. ini_dates: list of initial-condition sensitivity dates. inicond (dict): initial-condition arrays. inicond_sensit_tmp: thread-local accumulator for the sensitivity. obs_i (int): row index into the observation data-store. """ row = dataobs.iloc[obs_i]["metadata"] row_main = dataobs.iloc[obs_i]["maindata"] station = row.station network = row.network subdir = row.date.strftime(self.footprint_dir_format) # Translate station name if needed if hasattr(self, "station_name_dict"): station = self.dict_station_name[station.upper()].upper() # Infer folder structure runsubdir_init = os.path.join( self.run_dir_bg, self.footprint_stat_subdir_format.format( stat=station.upper(), network=network), subdir ) release_date = row.date - pd.to_timedelta(self.release_shift) file_date = release_date.strftime('%Y%m%d%H%M%S') file_name = self.file_ini_format.format( date=file_date, stat=station.upper(), network=network) list_valid = glob.glob(os.path.join(runsubdir_init, file_name)) if list_valid == []: debug(f"WARNING: file not found: {os.path.join(runsubdir_init, file_name)}") return elif len(list_valid) > 1: raise CifError( f"Multiple files fit the specified format {self.file_ini_format}. " f"This can be related to the use of a wildcard... " f"Please check your yml" ) file_name = os.path.basename(list_valid[0]) debug(f"Thread #{ithread}: Reading {file_name} for station {station}") grid_init = read_flexpart_gridinit( runsubdir_init, file_name, fp_header_init) # Normalize grid_init to make sure that total sensitivity is 1 grid_init /= grid_init.sum() # Multiply 3-D sensitivity to background concentrations # WARNING: do not deal with temporal and vertical dimension yet nz = (fp_header_init.outheight != 0.).sum() ini_sensit = grid_init.T.reshape(nz, -1) inds_inicond = np.argmin( (np.array( [row.date - pd.to_timedelta(self.backward_trajdays)] )[:, np.newaxis] - ini_dates[np.newaxis, :] ) >= datetime.timedelta(0), axis=1) - 1 istartsensit = ( 0 if self.domain.zlon.size == self.domain.zlon_in.size else self.domain.zlon_in.size ) inicond_sensit_tmp[inds_inicond, :, 0, istartsensit:] \ += ini_sensit * row_main.adj_out