Source code for microcalorimetry.math.rmemeas_extras
# -*- coding: utf-8 -*-
"""
This module contains functions intended to operate directly on RMEMeas objects,
instead of operating on the underlying datasets though propagation.
"""
from rmellipse.uobjects import RMEMeas
import numpy as np
import xarray as xr
__all__ = ['categorize_by']
[docs]
def categorize_by(
meas: RMEMeas,
category: str,
combine_uncategorized: bool = True,
uncategorized_name: str = 'uncategorized',
deg: bool = False,
rad: bool = False,
verbose: bool = False) -> 'RMEMeas':
"""
Group linear uncertainty mechanisms into categories.
Mechanisms sharing a common category will be combined quadratically
into a single mechanism. For example, if the 'Type' category is
selected, all the mechanisms with the 'Type' = 'A' designation in
the covcats attribute will be quadratically combined.
This function does NOT preserve the DOF's or the category information
of the uncertainty mechanisms after categorization, and is recommended
to be used for debugging and presentation purposes only. It is NOT
recommended for categorized RMEMeas objects to be saved.
Parameters
----------
meas: RMEMeas
Measurement to group uncertainty mechanisms.
category : str
String identifying the category of uncertainty mechanism.
combine_uncategorized: bool,
If True, mechanisms that do not contain 'category' will all be
combined into a single mechanism called uncategorized_name.
uncategorized_name:str,
String to call all uncategorized mechanisms IF they are being
combined. The default is 'uncategorized'.
deg: bool,
If true, calculates minimum angle differences for uncertainties
as degrees.
rad: bool,
If true, calculates minimum angle differences for uncertainties
as radians.
verbose : bool, optional
Print information about grouping. The default is False.
Returns
-------
out : RMEMeas
Measurement.
"""
assert not all([rad, deg])
def sum_reexpand(x, k, nominal, deg, rad):
if deg or rad:
if deg:
const = 180
else:
const = np.pi
temp = (((x.values - nominal.values) + const) % (2*const) - const)**2
else:
temp = (x.values - nominal.values)**2
temp = np.sum(temp, axis=0)**.5 + nominal.values
out = nominal.copy()
out.values = temp
out = out.assign_coords(umech_id=[k])
return out
new = [meas.cov.sel(umech_id=['nominal'])]
nominal = new[0]
# do stuff
covcats = meas.covcats.copy()
ind = covcats.values == ''
covcats.values[ind] = 'Uncategorized'
groupings = np.unique(covcats.loc[:, category])
groups = {g: list(covcats.umech_id[covcats.loc[:, category] == g].values) for g in groupings}
try:
unused_locs = groups.pop('Uncategorized')
ind = groupings == 'Uncategorized'
groupings = np.delete(groupings, ind)
except KeyError:
unused_locs = []
for g in groupings:
new.append(sum_reexpand(meas.cov.sel(umech_id=groups[g]), g, nominal, deg, rad))
# if combining, pool them into a single mecanism
if len(unused_locs) > 0 and combine_uncategorized:
new.append(sum_reexpand(meas.cov.sel(umech_id=unused_locs), uncategorized_name, nominal, deg, rad))
if verbose:
print('un mapped umech id being added to ungrouped.')
print(unused_locs)
# else, get the unused mechanisms, and copy their covariance categories
elif len(unused_locs) > 0 and not combine_uncategorized:
new.append(meas.cov.sel(umech_id=unused_locs))
# concatenate things
new_cov = xr.concat(new, 'umech_id')
if meas.mc is None:
new_mc = None
else:
new_mc = meas.mc.copy()
out = RMEMeas(name=meas.name, cov=new_cov, mc=new_mc)
return out