def is_valid_formula_adduct(formula: str, adduct: str): if adduct and adduct not in ('[M]+', '[M]-'): formula += adduct try: cpyMSpec.isotopePattern(formula) return True except Exception: return False
def ion_centroids(self, sf, adduct): """ Args ---- sf : str adduct: str Returns ------- : list of tuples """ try: pyisocalc.parseSumFormula(sf + adduct) # tests is the sf and adduct compatible iso_pattern = isotopePattern(str(sf + adduct)) iso_pattern.addCharge(int(self.charge)) fwhm = self.sigma * SIGMA_TO_FWHM resolving_power = iso_pattern.masses[0] / fwhm instrument_model = InstrumentModel('tof', resolving_power) centr = iso_pattern.centroids(instrument_model) mzs = np.array(centr.masses) ints = 100. * np.array(centr.intensities) mzs, ints = self._trim(mzs, ints, ISOTOPIC_PEAK_N) return mzs, ints except Exception as e: logger.warning('%s %s - %s', sf, adduct, e) return None, None
def get_isotope_pattern(sf, resolving_power=100000, instrument_type='tof', at_mz=None, cutoff_perc=0.1, charge=None, pts_per_mz=10000, **kwargs): # layer of compatibility with the original pyMSpec.pyisocalc module if charge is None: charge = 1 cutoff = cutoff_perc / 100.0 abs_charge = max(1, abs(charge)) p = isotopePattern(str(sf), cutoff / 10.0) p.addCharge(charge) mzs = np.arange( min(p.masses) / abs_charge - 1, max(p.masses) / abs_charge + 1, 1.0 / pts_per_mz) instr = InstrumentModel(instrument_type, resolving_power) intensities = np.asarray(p.envelope(instr)(mzs * abs_charge)) intensities *= 100.0 / intensities.max() ms = MassSpectrum() ms.add_spectrum(mzs, intensities) p = ProfileSpectrum(mzs, intensities).centroids(5) p.removeIntensitiesBelow(cutoff) p.sortByMass() ms.add_centroids(p.masses, np.array(p.intensities)) return ms
def centroids(self, formula): """ Args ----- formula : str Returns ----- list[tuple] """ try: pyisocalc.parseSumFormula( formula) # tests that formula is parsable iso_pattern = isotopePattern(str(formula)) iso_pattern.addCharge(int(self.charge)) fwhm = self.sigma * SIGMA_TO_FWHM resolving_power = iso_pattern.masses[0] / fwhm instrument_model = InstrumentModel('tof', resolving_power) centr = iso_pattern.centroids(instrument_model) mzs_ = np.array(centr.masses) ints_ = 100. * np.array(centr.intensities) mzs_, ints_ = self._trim(mzs_, ints_, self.n_peaks) n = len(mzs_) mzs = np.zeros(self.n_peaks) mzs[:n] = np.array(mzs_) ints = np.zeros(self.n_peaks) ints[:n] = ints_ return mzs, ints except Exception as e: logger.warning('%s - %s', formula, e) return None, None
def get_centroid_peaks( formula: str, adduct: Optional[str], charge: int, min_abundance: float, instrument_model: cpyMSpec.InstrumentModel, ) -> List[Tuple[float, float]]: if adduct and adduct not in ('[M]+', '[M]-'): formula += adduct iso_pattern = cpyMSpec.isotopePattern(formula) if charge: iso_pattern.addCharge(charge) try: centr = iso_pattern.centroids(instrument_model, min_abundance=min_abundance) return sorted(zip(centr.masses, centr.intensities), key=lambda pair: -pair[1]) except Exception as ex: # iso_pattern.centroids may raise an exception: # Exception: b'the result contains no peaks, make min_abundance lower!' # If this happens, just return the most intense uncentroided theoretical peak. if 'min_abundance' in str(ex): return [(iso_pattern.masses[np.argmax(iso_pattern.intensities)], 1.0)] if 'total number of' in str(ex) and 'less than zero' in str(ex): # Invalid molecule due to adduct removing non-existent elements raise Exception(f'Adduct could not be applied: {formula}{adduct}') raise
def ion_centroids(self, sf, adduct): """ Args ---- sf : str adduct: str Returns ------- : list of tuples """ try: pyisocalc.parseSumFormula( sf + adduct) # tests is the sf and adduct compatible iso_pattern = isotopePattern(str(sf + adduct)) iso_pattern.addCharge(int(self.charge)) fwhm = self.sigma * SIGMA_TO_FWHM resolving_power = iso_pattern.masses[0] / fwhm instrument_model = InstrumentModel('tof', resolving_power) centr = iso_pattern.centroids(instrument_model) mzs = np.array(centr.masses) ints = 100. * np.array(centr.intensities) mzs, ints = self._trim(mzs, ints, ISOTOPIC_PEAK_N) return mzs, ints except Exception as e: logger.warning('%s %s - %s', sf, adduct, e) return None, None
def get_mono_mz(formula: str, adduct: Optional[str], charge: int): if adduct and adduct not in ('[M]+', '[M]-'): formula += adduct iso_pattern = cpyMSpec.isotopePattern(formula) if charge: iso_pattern.addCharge(charge) return iso_pattern.masses[np.argmax(iso_pattern.intensities)]
def on_get(self, req, res, ion, instr, res_power, at_mz, charge): try: isotopes = isotopePattern(ion) isotopes.addCharge(int(charge)) instrument = InstrumentModel(instr, float(res_power), float(at_mz)) centroids = Centroids(isotopes, instrument) self.on_success(res, centroids.spectrum_chart()) except Exception as e: LOG.warning('(%s, %s, %s, %s, %s) - %s', ion, instr, res_power, at_mz, charge, e)
def test_centroiding(f, resolution): p = isotopePattern(f, 0.9999999) instr = InstrumentModel('tof', resolution) p1 = p.centroids(instr, points_per_fwhm=500) min_mz = min(p1.masses) max_mz = max(p1.masses) step = 5e-5 n_pts = int((max_mz + 2 - min_mz) / step) mzs = [min_mz - 1 + step * i for i in range(n_pts)] p2 = ProfileSpectrum(mzs, p.envelope(instr)(mzs)).centroids(window_size=15) assert_patterns_almost_equal(p1, p2, instr)
def test_thresholding(f, threshold): instr = InstrumentModel('tof', 100000) p = isotopePattern(f).centroids(instr, min_abundance=threshold) for a in p.intensities: assert a >= threshold
def mz(formula): return isotopePattern(formula).masses[0]
def isotopePattern(self, sum_formula, adduct, charge=None): if charge is None: charge = -1 if self.polarity == 'Negative' else +1 p = isotopePattern(sum_formula + adduct) p.addCharge(charge) return p
def test_duplicate_peaks(): p = cpyMSpec.isotopePattern("OBr") assert len(p.masses) == len(np.unique(p.masses))
def test_doubling(e): m1 = isotopePattern(e).masses[0] m2 = isotopePattern(e + "2").masses[0] assert abs(2 * m1 - m2) < 1e-5
def test_invalid_sum_formula(): for sf in ["UnknownElements", "C2HzO3", "C5:H2", "C2(H3O", "H2O.2"]: with pytest.raises(Exception): isotopePattern(sf)
def test_unsorted_envelope_input(): with pytest.raises(Exception): isotopePattern("C5H7O12").envelope(InstrumentModel('tof', 100000))([3, 1, 2])
def test_too_few_points(): with pytest.raises(Exception): isotopePattern("C2H5OH").centroids(InstrumentModel('tof', 100000), points_per_fwhm=3)
def generate(ion, instr, res_power, at_mz, charge): isotopes = isotopePattern(ion) isotopes.addCharge(int(charge)) instrument = InstrumentModel(instr, float(res_power), float(at_mz)) centroids = Centroids(isotopes, instrument) return centroids.spectrum_chart()
def test_invalid_min_abundance(): with pytest.raises(Exception): isotopePattern("C5H5N5O").centroids(InstrumentModel('tof', 100000), min_abundance=1)
def test_negative_element_number(): with pytest.raises(Exception): isotopePattern('C2HBrClF3+H-H2O') with pytest.raises(Exception): isotopePattern('C2HBrClF3-H2')
from cpyMSpec import isotopePattern import pytest formulas = ["H", "O", "C", "S", "P", "N", "Fe", "Na", "K"] spectra = [isotopePattern(f) for f in formulas] @pytest.mark.parametrize("s1", spectra) @pytest.mark.parametrize("s2", spectra) def test_addition(s1, s2): s = s1 + s2 assert sorted(s1.masses + s2.masses) == s.sortedByMass().masses assert sorted(s1.intensities + s2.intensities) == s.sortedByIntensity().intensities[::-1] @pytest.mark.parametrize("s", spectra) def test_scaling(s): s2 = s * 42 assert max(s.intensities) == 1 assert max(s2.intensities) == 42
def test_too_many_combinations(): for sf in ["Ru36", "H100000"]: with pytest.raises(Exception): isotopePattern(sf)