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 import RMEMeas, AnnotatedArray
import numpy as np
import xarray as xr
from fnmatch import fnmatch

__all__ = ['categorize_by', 'DEFAULT_ORIGIN_FILTER']

"""Mapping of (keys) glob patterns of covcats origins that should be renamed to their values."""
DEFAULT_ORIGIN_FILTER = {
    '*DC*Trace*': 'DC Measurement Accuracy',
    '*DC*Datasheet*': 'DC Measurement Accuracy',
    'Datasheet':'DC Measurement Accuracy',
    '*[rR]epeat*':'Repeatability',
    'Type IV':'DC Measurement Accuracy',
    'Eff':'Calibration Standard Eff Definition',
    '*DC*Noise*': 'DC Measurement Noise',
    '*DC*Repeat*': 'DC Measurement Noise',
    '*Noise*': 'DC Measurement Noise',
    '*Short Population': 'S-Parameters',  # exists from dc measurements of some S-parameter standards.
    '*Cable Bend*': 'S-Parameters',
    '*Airline*': 'S-Parameters',
    '*VNA*': 'S-Parameters',
    'uncategorized':'Other'
    # '*Correction*': 'Correction Factor',
}


[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, apply_origin_filter: dict = DEFAULT_ORIGIN_FILTER, ) -> '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. apply_origin_filter : bool, optional If True, regroup origins by applying a filter that renames origins according to pattern matching. 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) ** 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) if apply_origin_filter: for u in out.umech_id: out.assign_categories([u], ['Origin'], [u]) for pattern, new_origin in apply_origin_filter.items(): update = [u for u in out.umech_id if fnmatch(u, pattern)] # print(pattern, update) n = len(update) out.assign_categories(update, ['Origin'] * n, [new_origin] * n) out = categorize_by( meas=out, category='Origin', combine_uncategorized=False, uncategorized_name='uncategorized', deg=deg, rad=rad, verbose=False, apply_origin_filter={}, ) return out