Source code for pycif.plugins.controlvects.standard.utils.get_physical

import numpy as np
import pandas as pd
from scipy import ndimage
from logging import debug, warning
from .....plugins.transforms.system.fromcontrol.utils.scalemaps \
    import map2scale, vmap2vaggreg
from .....utils.dataarrays.reindex import reindex
from .....utils.check.errclass import CifError


[docs] def get_physical(controlvect, tracer, comp, trcr, **kwargs): debug(f"Loading physical values for {comp}/{trcr}. This can take a while") trid = (comp, trcr) datavect = controlvect.datavect # Scale xb if prescribed in Yaml xb_scale = getattr(tracer, "xb_scale", 1.0) # Filling with defined value if prescribed xb_value = getattr(tracer, "xb_value", 0.0) # Raise warning here if merged_dates was not created if not hasattr(tracer, "merged_dates"): warning( f"WARNING! 'merged_dates' was not initialized for {comp} / {trcr}. " f"This could be due to the tracer not being used in your observation " f"operator pipe line. Please check your yaml and your pipeline." ) return np.zeros(tracer.dim), np.zeros(tracer.dim) merged_dates = tracer.merged_dates nb_inputs = np.zeros(tracer.dim) xb = np.zeros(tracer.dim) std = np.zeros(tracer.dim) for di in tracer.input_dates: debug(f"Loading physical values for {di}") # Skip if empty period if len(tracer.input_dates[di]) == 0: continue # Turn dates to datetime for consistency in read # TODO: This should be standardized in the future dates2read = [ [x.to_pydatetime() for x in row] for row in tracer.input_dates[di].itertuples(index=False, name=None) ] inputs = tracer.read( trcr, tracer.varname, dates2read, tracer.input_files[di], comp_type=comp, tracer=tracer, model=controlvect.model, ddi=di, **kwargs ) # Reprojecting inputs and scaling factors to the same merged # time steps outdates = pd.to_datetime( np.unique(np.array(tracer.merged_dates[di]).flatten()) ) if len(outdates) == 1: outdates = outdates.append(outdates) # Reindex inputs to merged dates inputs = reindex( inputs, levels={"time": outdates[:-1]}, ) # Corresponding indexes in control vector and inputs control_indexes = \ pd.Series(range(tracer.ndates), index=tracer.dates).reindex( outdates, method="ffill") # Find indexes in the control vector corresponding to each data # periods period_dates = merged_dates[di] for k, period in period_dates.iterrows(): # Either take the corresponding slice of time, # or take the exact date # if the control variable is on a time stamp try: dd0, dd1 = period mask = (outdates[:-1] >= dd0) & (outdates[:-1] < dd1) # If period is a time stamp, i.e., dd0 = dd1, adapt the mask if dd0 == dd1: raise CifError("Test what happens here") mask = outdates == dd0 except TypeError: raise CifError("Test what happens here") dd0 = period mask = outdates == dd0 control_slice = control_indexes.iloc[k] # If control_slice is nan, # it means that the date in the sensitivity # is not in the control vector, hence skipping if np.isnan(control_slice): continue # Reference matrix with error values errtype = getattr(tracer, "errtype", "") err_scale = tracer.err_scale err_value = getattr(tracer, "err_value", 0) errdata = err_scale * np.abs(inputs[mask].values) + err_value if errtype == "max": errdata = ndimage.maximum_filter(errdata, size=3) # If errtype is 'avg', prescribes uniform uncertainties elif errtype == "avg": errdata[:] = errdata.mean() / 2 # It errtype is "threshold", put all errors below the threshold # (in absolute value) to 0 elif errtype == "threshold": errdata = np.where( errdata <= tracer.err_threshold, tracer.err_threshold, errdata ) # If inputs are below a certain value, put to zero the error elif errtype == "threshold_input": errdata = np.where( np.abs(inputs[mask].values) <= tracer.err_threshold, 0, errdata ) # Temporal aggregation vdata = np.sum(inputs[mask], axis=0).values[np.newaxis] vcounter = np.sum(np.ones_like(inputs[mask]), axis=0)[np.newaxis] verrdata = np.sum(errdata, axis=0)[np.newaxis] # Vertical aggregation depending on vertical stacks vaggreg = vmap2vaggreg(vdata, tracer, tracer.domain, trid) vcounteraggreg = vmap2vaggreg( vcounter, tracer, tracer.domain, trid) verraggreg = vmap2vaggreg(verrdata, tracer, tracer.domain, trid) # Horizontal aggregation depending on regions xstack = map2scale(vaggreg, tracer, tracer.domain, region_scale_area=True) xcounter = map2scale(vcounteraggreg, tracer, tracer.domain, region_scale_area=True) xerr = map2scale(verraggreg, tracer, tracer.domain, region_scale_area=True) # Putting flatten values into xb and std xb[int(control_slice) * tracer.hresoldim * tracer.vresoldim: int(control_slice + 1) * tracer.hresoldim * tracer.vresoldim ] += xstack.flatten() * xb_scale + xb_value std[int(control_slice) * tracer.hresoldim * tracer.vresoldim: int(control_slice + 1) * tracer.hresoldim * tracer.vresoldim ] += np.abs(xerr).flatten() # Keep in memory how much inputs values were used for each date nb_inputs[int(control_slice) * tracer.hresoldim * tracer.vresoldim: int(control_slice + 1) * tracer.hresoldim * tracer.vresoldim ] += xcounter.flatten() # Averaging temporally according to nb inputs xb /= nb_inputs std /= nb_inputs xb = np.where(nb_inputs == 0, 0, xb) std = np.where(nb_inputs == 0, 0, std) # TODO: generalize for regions and aggregated temporal resolutions return xb, std