Source code for isogroup.base.misc

from __future__ import annotations
import re
from isocor.base import LabelledChemical

[docs] class Misc: """ Miscellaneous utility functions for isotope labelling analysis. """ @staticmethod def _parse_strtracer(str_tracer:str) -> tuple[str, int]: """Parse the tracer code. :param str_tracer: tracer code (e.g. "13C") Returns: tuple - (str) tracer element (e.g. "C") - (int) tracer index in :py:attr:`~data_isotopes` """ try: tracer = re.search(r'(\d*)([A-Z][a-z]*)', str_tracer) count = int(tracer.group(1)) tracer_el = tracer.group(2) except (ValueError, AttributeError): raise ValueError("Invalid tracer code: '{}'." " Please check your inputs.".format(str_tracer)) best_diff = float("inf") idx_tracer = None unexpected_msg = "Unexpected tracer code. Are you sure this isotope is "\ "in data_isotopes? '{}'".format(str_tracer) assert tracer_el in LabelledChemical.DEFAULT_ISODATA, unexpected_msg for i, mass in enumerate(LabelledChemical.DEFAULT_ISODATA[tracer_el]["mass"]): test_diff = abs(mass - count) if test_diff < best_diff: best_diff = test_diff idx_tracer = i assert best_diff < 0.5, unexpected_msg return (tracer_el, idx_tracer)
[docs] @staticmethod def get_atomic_mass(element: str) -> float | None: """ Returns the atomic mass of the given element. :param element: Chemical element symbol (e.g. "C", "H", "N", "O"). """ if element in LabelledChemical.DEFAULT_ISODATA: return LabelledChemical.DEFAULT_ISODATA[element]["mass"][0] return None
[docs] @staticmethod def calculate_mzshift(tracer: str) -> float: """ Calculate the m/z shift for a given tracer (e.g. "13C"). :param tracer: Tracer code (e.g. "13C"). """ tracer_element, tracer_idx = Misc._parse_strtracer(tracer) # Mass of the tracer isotope tracer_mass = LabelledChemical.DEFAULT_ISODATA[tracer_element]["mass"][tracer_idx] # Mass of the most abundant natural isotope natural_mass = LabelledChemical.DEFAULT_ISODATA[tracer_element]["mass"][0] mz_shift = tracer_mass - natural_mass return mz_shift
[docs] @staticmethod def get_max_isotopologues_for_mz(mz: float, tracer_element: str) -> int: """ Returns the maximum number of isotopologues to consider based on the m/z value. This is a placeholder function and should be replaced with actual logic as needed. :param mz: Mass-to-charge ratio of the feature. :param tracer_element: Tracer element symbol (e.g. "C", "N"). """ element_mass = float(Misc.get_atomic_mass(tracer_element)) if element_mass is None: raise ValueError(f"Unknown tracer element: {tracer_element}") if tracer_element == "C": factor = 0.7 # Approximation, empiric fraction based on the Seven Golden Rules elif tracer_element == "N": factor = 0.2 elif tracer_element == "O": factor = 0.3 else: raise NotImplementedError(f"Tracer {tracer_element} not implemented yet.") return max(1, int(factor * (mz / element_mass)))
[docs] def calculate_isotopologue_index(candidate_mz:float, base_mz:float, mzshift_tracer:float) -> int: """ Calculate the theoretical isotopologue index based on m/z values. :param candidate_mz: m/z of the candidate isotopologue. :param base_mz: m/z of the base (unlabeled) feature. :param mzshift_tracer: m/z shift corresponding to the tracer. """ return round((candidate_mz - base_mz) / mzshift_tracer)