Source code for pycif.plugins.transforms.complex.isotopes.obsvect2native

import numpy as np
import pandas as pd


[docs] def obsvect2native( transf, y0, mapper, mod_input, ddi, ddf, mode, runsubdir, workdir, **kwargs ): """Split obs-vector observations into two isotopologue sub-species. Expands each matched observation into two rows — one per output isotopologue — by duplicating the original DataFrame and assigning the appropriate output parameter names. In adjoint mode, propagates ``obs_incr`` from the (δ, total) space back to the individual isotopologue increments using the forward concentrations saved in ``transf.fwd_data[ddi]``: * **Reference increment** (denominator): :math:`\\partial J / \\partial [ref] = -[iso]/[ref]^2 \\cdot incr \\cdot 1000 / R_{std}` * **Isotopologue increment** (numerator): :math:`\\partial J / \\partial [iso] = 1/[ref] \\cdot incr \\cdot 1000 / R_{std}` Args: transf (Plugin): isotopes plugin instance (carries ``parameters_in``, ``parameters_out``, ``fwd_data``). y0 (pd.DataFrame): obs-vector DataFrame; extended and reordered in-place. mapper (dict): transform mapper. mod_input (str): component type (e.g. ``'obs'``). ddi (datetime): sub-simulation start date. ddf (datetime): sub-simulation end date. mode (str): ``'fwd'``, ``'tl'``, or ``'adj'``. runsubdir (str): unused. workdir (str): unused. **kwargs: unused. Returns: pd.DataFrame: updated obs-vector DataFrame sorted by ``indorig`` and ``parameter``. """ y0.loc[:, "indorig"] = np.arange(len(y0)) y0.loc[:, "parameter_ref"] = y0["parameter"] inputs = transf.parameters_in.names outputs = transf.parameters_out.names unit = transf.unit r_std = transf.parameters_in.standard iso_mass = transf.parameters_out.iso_mass spec_mass = transf.parameters_out.spec_mass # Just splitting into two sub-species in the dataframe mask = y0["parameter"].str.lower().isin([s.lower() for s in inputs]) df = y0.loc[mask].copy() df = pd.concat([df.assign(parameter=iso.lower()) for iso in outputs]) # Computing adjoint if needed if mode == "adj": mask_tot = df["parameter_ref"] == inputs[1].lower() mask_iso = df["parameter_ref"] == inputs[0].lower() mask_spec_ref = (df["parameter"] == outputs[0].lower()) & mask_iso mask_isotopologue = ( df["parameter"] == outputs[1].lower() ) & mask_iso incr = 0 * df["obs_incr"] incr_spec_ref = df.loc[mask_spec_ref, "obs_incr"] incr_isotopologue = df.loc[mask_isotopologue, "obs_incr"] # Fetch simulation from previous forward isotopologue = transf.fwd_data[ddi]["isotopologue_iso"] spec_ref = transf.fwd_data[ddi]["spec_ref_iso"] incr.loc[mask_tot] = df.loc[mask_tot, "obs_incr"] incr.loc[mask_spec_ref] = ( -isotopologue / spec_ref ** 2 * incr_spec_ref * 1000 / r_std ) incr.loc[mask_isotopologue] = ( 1 / spec_ref * incr_isotopologue * 1000 / r_std ) df.loc[:, "obs_incr"] = incr y0 = pd.concat([df, y0.loc[~mask]]) y0 = y0.sort_values(['indorig', 'parameter']) return y0