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