Source code for pycif.plugins.obsoperators.standard.transforms.utils.add_default

import copy
import numpy as np
import tracemalloc

from ......utils.classes.setup import Setup
from ......utils.classes.transforms import Transform
from ......utils.mappers import safe_deepcopy
from logging import debug

from . import connect_pipes
from . import propagate_attributes
from . import init_default_transformations
from ......utils.check.errclass import CifTypeError


[docs] def add_default( self, transforms, yml_dict, position="last", index=0, init=False, mapper={}, transform_type="state", backup_comps={}, ref_transform="", precursor=None, successor=None, transform_id=None, do_pipe_entry=False ): # Check usage of precursor and successor assert ( (precursor is None or type(precursor) == dict) and (successor is None or type(successor) == dict) ) debug("Adding the following transform:") debug(yml_dict) # Initialize 'default_index' is necessary if not hasattr(transforms, 'default_index'): transforms.default_index = 0 # Create a random name to identify the current transformation if transform_id is not None: new_id = transform_id elif 'plugin' in yml_dict and 'name' in yml_dict['plugin']: plg = yml_dict['plugin'] new_id = f"{plg['name']}_{plg.get('version', 'std')}_{transforms.default_index:05d}" else: new_id = f"default_{transforms.default_index:05d}" transforms.default_index += 1 new_transf = Setup.from_dict({new_id: yml_dict}) Setup.load_setup(new_transf, level=1) new_transf = getattr(new_transf, new_id) # Attach transform id to the transform itself for self identification new_transf.transform_id = new_id # Update overall transform pipe with current transform setattr(transforms, new_id, new_transf) # Initializes mapper if init: transf_mapper_loc = new_transf.ini_mapper( backup_comps=backup_comps, transforms_order=transforms.attributes, ref_transform=ref_transform, precursor=precursor, successor=successor, general_mapper=mapper, all_transforms=transforms, transform_name=new_id ) # Clean input dates to make sure they are of proper format transf_mapper_loc = new_transf.clean_input_dates(transf_mapper_loc) # Deep-copy the mapper to avoid issues with references transf_mapper_loc = safe_deepcopy(transf_mapper_loc) new_transf.mapper = transf_mapper_loc mapper[new_id] = transf_mapper_loc update_successors_precursors( new_id, precursor, successor, transf_mapper_loc, mapper ) generate_internal_pipe(new_id, mapper) if position in ["last", "end"]: transforms.attributes.append(new_id) elif position in ["start", "first"]: transforms.attributes.insert(0, new_id) elif position == "index": transforms.attributes.insert(index, new_id) # Check memory if requested if self.monitor_memory: current, peak = tracemalloc.get_traced_memory() debug(f"Current memory usage is {current / 1024 ** 2}MB; " f"Peak was {peak / 1024 ** 2}MB") # Connecting new transformation to the rest of the pipeline connect_pipes.connect_pipes(transforms, mapper, new_id) # Propagate attributes backward and forward propagate_attributes.propagate_attributes( self, transforms, mapper, new_id, backup_comps=backup_comps ) # Initialize pipe_entry if no precursor if do_pipe_entry: init_pipe_entry( self, transforms, backup_comps, mapper, new_id) # Now initialize default transformations # with precursors and successors if any mismatch init_default_transformations.init_default_transformations( self, transforms, backup_comps, mapper, new_id, do_pipe_entry=do_pipe_entry ) return new_transf, new_id
[docs] def init_pipe_entry( self, all_transforms, backup_comps, mapper, transform ): debug(f"Initializing entry point for {transform}") # Initialize required input variables self.required_inputs = getattr(self, "required_inputs", {}) self.missing = getattr(self, "missing", {}) self.init_missing = getattr(self, "init_missing", {}) # Save required inputs and missing inputs ignore_missing = hasattr(self, "init_inputs") init_inputs = getattr(self, "init_inputs", None) # Add default transformation "fromcontrol" when no predecessor is available # Also include "unit_conversion" if required in configuration transf_mapper = mapper[transform] precursors = transf_mapper["precursors"] for trid in precursors: # Skip if there are precursors if precursors[trid]: continue prm = trid[1] cmp = trid[0] # Save required inputs for debug dumping if cmp not in self.required_inputs: self.required_inputs[cmp] = {} self.required_inputs[cmp][prm] = {"available": True} # Fetch component from backup if necessary # It is used to replace components names components = self.datavect.components comps = components.attributes cmp_in = cmp if cmp in comps else backup_comps.get(cmp, None) if cmp_in is None or not hasattr(components, cmp_in): self.required_inputs[cmp][prm] = {"available": False} if transform not in self.missing: self.missing[transform] = {} if cmp not in self.missing[transform]: self.missing[transform][cmp] = [] self.missing[transform][cmp].append(prm) # Check if missing parameter is needed for initialization if ignore_missing: if cmp in init_inputs.components.attributes: list_params = getattr(init_inputs.components, cmp) if list_params == []: missing_params = self.missing[transform][cmp] else: missing_params = [ prm for prm in self.missing[transform][cmp] if prm in list_params ] if missing_params != []: if transform not in self.init_missing: self.init_missing[transform] = {} self.init_missing[transform][cmp] = missing_params continue cmp_plg = getattr(components, cmp_in) # Skip if component is observation and not data if getattr(cmp_plg, "isobs", False): continue # Fetch parameters # If no parameters, handle the component as a whole if not hasattr(cmp_plg, "parameters"): params = cmp_plg parameters = [""] else: params = cmp_plg.parameters parameters = params.attributes[:] param = getattr(params, prm, cmp_plg) if not hasattr(params, prm): self.required_inputs[cmp][prm]["from_component"] = True # TODO: split from control into from control and from datavect # control variables are initialized with from control and if need # to read data, from datavect should be added as precursor ind_transform = all_transforms.attributes.index(transform) yml_dict = { "plugin": { "name": "fromcontrol", "version": "std", "type": "transform", "newplg": True, }, "component": [cmp], "parameter": [prm], "orig_parameter_plg": param, "orig_component_plg": cmp_plg, } ref_successor = {(cmp, prm): transform} new_transf, fromcontrol_id = add_default( self, all_transforms, yml_dict, position="index", index=ind_transform, mapper=mapper, init=True, backup_comps=backup_comps, # successor=ref_successor, do_pipe_entry=True ) # Rescaling if any if hasattr(param, "unit_conversion"): unit_conv = getattr(param, "unit_conversion") yml_dict = { "plugin": { "name": "unit_conversion", "version": "std", "type": "transform", }, "component": [cmp], "parameter": [prm], "orig_parameter_plg": param, "orig_component_plg": cmp_plg, **{attr: getattr(unit_conv, attr) for attr in getattr(unit_conv, "attributes", []) if attr != "plugin"} } ref_precursor = {(cmp, prm): fromcontrol_id} ref_successor = mapper[fromcontrol_id]["successors"] new_transf, new_id = add_default( self, all_transforms, yml_dict, position="index", index=ind_transform + 1, mapper=mapper, init=True, backup_comps=backup_comps, precursor=ref_precursor, successor=ref_successor )
[docs] def update_successors_precursors( new_id, precursors2add, successor2add, transf_mapper_loc, mapper ): # Force precursors if "precursors" not in transf_mapper_loc: transf_mapper_loc["precursors"] = {} for trid in transf_mapper_loc["inputs"]: if trid not in transf_mapper_loc["precursors"]: transf_mapper_loc["precursors"][trid] = [] if precursors2add is None: continue if trid not in precursors2add: continue if type(precursors2add[trid]) == str: transf_mapper_loc["precursors"][trid].append( precursors2add[trid] ) elif type(precursors2add[trid]) == list: transf_mapper_loc["precursors"][trid].extend( precursors2add[trid] ) else: raise CifTypeError( f"Unexpexted type ({type(precursors2add[trid])} " f"for precursors {precursors2add[trid]}) " f"for transform {new_id}" ) # Force successors if "successors" not in transf_mapper_loc: transf_mapper_loc["successors"] = {} for trid in transf_mapper_loc["outputs"]: if trid not in transf_mapper_loc["successors"]: transf_mapper_loc["successors"][trid] = [] if successor2add is None: continue if trid not in successor2add: continue if type(successor2add[trid]) == str: transf_mapper_loc["successors"][trid].append( successor2add[trid] ) elif type(successor2add[trid]) == list: transf_mapper_loc["successors"][trid].extend( successor2add[trid] ) else: raise CifTypeError( f"Unexpexted type ({type(successor2add[trid])} " f"for precursors {successor2add[trid]}) " f"for transform {new_id}" ) # If both precursors and successors are specified, hence update corresponding pipe if successor2add is None or precursors2add is None: return for trid in successor2add: # if trid not in both precursor and successor, can't force pipe if trid not in precursors2add: continue # Loop over precursors and successors successor_trid = [successor2add[trid]] if type(successor2add[trid]) == str \ else successor2add[trid] precursor_trid = [precursors2add[trid]] if type(precursors2add[trid]) == str \ else precursors2add[trid] for successor_tmp, precursor_tmp in zip(successor_trid, precursor_trid): # if there was no direct pipeline, can't force it # TODO: check whether this can reveal an error? if trid not in mapper[precursor_tmp]["successors"]: continue if trid not in mapper[successor_tmp]["precursors"]: continue if successor_tmp not in mapper[precursor_tmp]["successors"][trid]: continue if precursor_tmp not in mapper[successor_tmp]["precursors"][trid]: continue mapper[precursor_tmp]["successors"][trid].remove( successor_tmp) mapper[precursor_tmp]["successors"][trid].append(new_id) mapper[successor_tmp]["precursors"][trid].remove( precursor_tmp) mapper[successor_tmp]["precursors"][trid].append(new_id)
[docs] def generate_internal_pipe(new_id, mapper): transf_mapper = mapper[new_id] # Generate the attribute outputs2inputs if not provided # Then all inputs are supposed to be linked to all outputs if "outputs2inputs" not in transf_mapper: transf_mapper["outputs2inputs"] = { trout: copy.deepcopy(list(transf_mapper["inputs"].keys())) for trout in transf_mapper["outputs"] } # Now generate the corresponding path forwards transf_mapper["inputs2outputs"] = \ Transform.generate_inputs2outputs(transf_mapper)