CoreMS 4.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- corems/__init__.py +63 -0
- corems/chroma_peak/__init__.py +0 -0
- corems/chroma_peak/calc/ChromaPeakCalc.py +480 -0
- corems/chroma_peak/calc/__init__.py +0 -0
- corems/chroma_peak/calc/subset.py +196 -0
- corems/chroma_peak/factory/__init__.py +0 -0
- corems/chroma_peak/factory/chroma_peak_classes.py +1178 -0
- corems/encapsulation/__init__.py +0 -0
- corems/encapsulation/constant.py +1283 -0
- corems/encapsulation/factory/__init__.py +0 -0
- corems/encapsulation/factory/parameters.py +392 -0
- corems/encapsulation/factory/processingSetting.py +1244 -0
- corems/encapsulation/input/__init__.py +0 -0
- corems/encapsulation/input/parameter_from_json.py +579 -0
- corems/encapsulation/output/__init__.py +0 -0
- corems/encapsulation/output/parameter_to_dict.py +142 -0
- corems/encapsulation/output/parameter_to_json.py +332 -0
- corems/mass_spectra/__init__.py +0 -0
- corems/mass_spectra/calc/GC_Calc.py +84 -0
- corems/mass_spectra/calc/GC_Deconvolution.py +558 -0
- corems/mass_spectra/calc/GC_RI_Calibration.py +44 -0
- corems/mass_spectra/calc/MZSearch.py +249 -0
- corems/mass_spectra/calc/SignalProcessing.py +656 -0
- corems/mass_spectra/calc/__init__.py +0 -0
- corems/mass_spectra/calc/lc_calc.py +5790 -0
- corems/mass_spectra/calc/lc_calc_operations.py +1127 -0
- corems/mass_spectra/factory/GC_Class.py +791 -0
- corems/mass_spectra/factory/__init__.py +0 -0
- corems/mass_spectra/factory/chromat_data.py +51 -0
- corems/mass_spectra/factory/lc_class.py +2721 -0
- corems/mass_spectra/input/__init__.py +0 -0
- corems/mass_spectra/input/andiNetCDF.py +200 -0
- corems/mass_spectra/input/boosterHDF5.py +216 -0
- corems/mass_spectra/input/brukerSolarix.py +153 -0
- corems/mass_spectra/input/brukerSolarix_utils.py +73 -0
- corems/mass_spectra/input/corems_hdf5.py +1710 -0
- corems/mass_spectra/input/massList.py +133 -0
- corems/mass_spectra/input/mzml.py +668 -0
- corems/mass_spectra/input/parserbase.py +239 -0
- corems/mass_spectra/input/rawFileReader.py +1839 -0
- corems/mass_spectra/output/__init__.py +0 -0
- corems/mass_spectra/output/export.py +2800 -0
- corems/mass_spectrum/__init__.py +0 -0
- corems/mass_spectrum/calc/AutoRecalibration.py +237 -0
- corems/mass_spectrum/calc/Calibration.py +602 -0
- corems/mass_spectrum/calc/CalibrationCalc.py +253 -0
- corems/mass_spectrum/calc/KendrickGroup.py +239 -0
- corems/mass_spectrum/calc/MassErrorPrediction.py +436 -0
- corems/mass_spectrum/calc/MassSpectrumCalc.py +303 -0
- corems/mass_spectrum/calc/MeanResolvingPowerFilter.py +212 -0
- corems/mass_spectrum/calc/NoiseCalc.py +371 -0
- corems/mass_spectrum/calc/NoiseCalc_Bayes.py +93 -0
- corems/mass_spectrum/calc/PeakPicking.py +994 -0
- corems/mass_spectrum/calc/__init__.py +0 -0
- corems/mass_spectrum/factory/MassSpectrumClasses.py +1753 -0
- corems/mass_spectrum/factory/__init__.py +0 -0
- corems/mass_spectrum/input/__init__.py +0 -0
- corems/mass_spectrum/input/baseClass.py +531 -0
- corems/mass_spectrum/input/boosterHDF5.py +161 -0
- corems/mass_spectrum/input/coremsHDF5.py +475 -0
- corems/mass_spectrum/input/massList.py +402 -0
- corems/mass_spectrum/input/numpyArray.py +133 -0
- corems/mass_spectrum/output/__init__.py +0 -0
- corems/mass_spectrum/output/export.py +841 -0
- corems/molecular_formula/__init__.py +0 -0
- corems/molecular_formula/calc/MolecularFormulaCalc.py +885 -0
- corems/molecular_formula/calc/__init__.py +0 -0
- corems/molecular_formula/factory/MolecularFormulaFactory.py +895 -0
- corems/molecular_formula/factory/__init__.py +0 -0
- corems/molecular_formula/input/__init__.py +0 -0
- corems/molecular_formula/input/masslist_ref.py +355 -0
- corems/molecular_id/__init__.py +0 -0
- corems/molecular_id/calc/ClusterFilter.py +251 -0
- corems/molecular_id/calc/MolecularFilter.py +122 -0
- corems/molecular_id/calc/SpectralSimilarity.py +632 -0
- corems/molecular_id/calc/__init__.py +0 -0
- corems/molecular_id/calc/math_distance.py +1637 -0
- corems/molecular_id/data/FAMES_REF.msp +980 -0
- corems/molecular_id/data/PNNLMetV20191015.msp +157267 -0
- corems/molecular_id/factory/EI_SQL.py +650 -0
- corems/molecular_id/factory/MolecularLookupTable.py +914 -0
- corems/molecular_id/factory/__init__.py +0 -0
- corems/molecular_id/factory/classification.py +884 -0
- corems/molecular_id/factory/lipid_molecular_metadata.py +50 -0
- corems/molecular_id/factory/molecularSQL.py +827 -0
- corems/molecular_id/factory/spectrum_search_results.py +119 -0
- corems/molecular_id/input/__init__.py +0 -0
- corems/molecular_id/input/nistMSI.py +148 -0
- corems/molecular_id/search/__init__.py +0 -0
- corems/molecular_id/search/compoundSearch.py +214 -0
- corems/molecular_id/search/database_interfaces.py +1527 -0
- corems/molecular_id/search/findOxygenPeaks.py +330 -0
- corems/molecular_id/search/lcms_spectral_search.py +348 -0
- corems/molecular_id/search/molecularFormulaSearch.py +1117 -0
- corems/molecular_id/search/priorityAssignment.py +723 -0
- corems/ms_peak/__init__.py +0 -0
- corems/ms_peak/calc/MSPeakCalc.py +1010 -0
- corems/ms_peak/calc/__init__.py +0 -0
- corems/ms_peak/factory/MSPeakClasses.py +542 -0
- corems/ms_peak/factory/__init__.py +0 -0
- corems/transient/__init__.py +0 -0
- corems/transient/calc/TransientCalc.py +362 -0
- corems/transient/calc/__init__.py +0 -0
- corems/transient/factory/TransientClasses.py +457 -0
- corems/transient/factory/__init__.py +0 -0
- corems/transient/input/__init__.py +0 -0
- corems/transient/input/brukerSolarix.py +461 -0
- corems/transient/input/midasDatFile.py +172 -0
- corems-4.0.0.dist-info/METADATA +475 -0
- corems-4.0.0.dist-info/RECORD +171 -0
- corems-4.0.0.dist-info/WHEEL +5 -0
- corems-4.0.0.dist-info/licenses/LICENSE +22 -0
- corems-4.0.0.dist-info/top_level.txt +4 -0
- examples/archive/scripts/CoreMS_tutorial.py +94 -0
- examples/archive/scripts/DI HR-MS Halogens Bruker.py +196 -0
- examples/archive/scripts/DI HR-MS MassList.py +385 -0
- examples/archive/scripts/GC-MS MetabRef.py +213 -0
- examples/archive/scripts/GC-MS NetCDF.py +217 -0
- examples/archive/scripts/HR-MS Thermo Raw 21T.py +136 -0
- examples/archive/scripts/LC-ICPMS_metal_peaks.py +297 -0
- examples/archive/scripts/LCMS-Thermo.py +460 -0
- examples/archive/scripts/LCMS_isotopes.py +283 -0
- examples/archive/scripts/MSParams_example.py +21 -0
- examples/archive/scripts/Molecular Formula Data Aggreation.py +84 -0
- examples/archive/scripts/Single Mz Search.py +69 -0
- examples/test_notebooks.py +145 -0
- ext_lib/ChemstationMSFileReader.dll +0 -0
- ext_lib/ChemstationMSFileReader.xml +126 -0
- ext_lib/RawFileReaderLicense.doc +0 -0
- ext_lib/ThermoFisher.CommonCore.BackgroundSubtraction.dll +0 -0
- ext_lib/ThermoFisher.CommonCore.BackgroundSubtraction.xml +2307 -0
- ext_lib/ThermoFisher.CommonCore.Data.dll +0 -0
- ext_lib/ThermoFisher.CommonCore.Data.xml +28974 -0
- ext_lib/ThermoFisher.CommonCore.MassPrecisionEstimator.dll +0 -0
- ext_lib/ThermoFisher.CommonCore.MassPrecisionEstimator.xml +241 -0
- ext_lib/ThermoFisher.CommonCore.RawFileReader.dll +0 -0
- ext_lib/ThermoFisher.CommonCore.RawFileReader.xml +31174 -0
- ext_lib/__init__.py +0 -0
- ext_lib/dotnet/OpenMcdf.Extensions.dll +0 -0
- ext_lib/dotnet/OpenMcdf.dll +0 -0
- ext_lib/dotnet/OpenMcdf.xml +1154 -0
- ext_lib/dotnet/System.IO.FileSystem.AccessControl.dll +0 -0
- ext_lib/dotnet/System.IO.FileSystem.AccessControl.xml +506 -0
- ext_lib/dotnet/System.Security.AccessControl.dll +0 -0
- ext_lib/dotnet/System.Security.AccessControl.xml +2043 -0
- ext_lib/dotnet/System.Security.Principal.Windows.dll +0 -0
- ext_lib/dotnet/System.Security.Principal.Windows.xml +1011 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.BackgroundSubtraction.dll +0 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.BackgroundSubtraction.xml +2307 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.Data.dll +0 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.Data.xml +29148 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.MassPrecisionEstimator.dll +0 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.MassPrecisionEstimator.xml +241 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.RawFileReader.dll +0 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.RawFileReader.xml +31492 -0
- ext_lib/version +18 -0
- support_code/atom_parsers/AtomsDescription.py +359 -0
- support_code/atom_parsers/CreateAtomsDescription.py +56 -0
- support_code/nmdc/filefinder.py +90 -0
- support_code/nmdc/lipidomics/lipidomics_workflow.py +748 -0
- support_code/nmdc/lipidomics/manifest_examples.py +49 -0
- support_code/nmdc/metabolomics/gcms_workflow.py +158 -0
- support_code/nmdc/metabolomics/lcms_metabolomics_targeted_search.py +59 -0
- support_code/nmdc/metabolomics/lcms_metabolomics_workflow.py +248 -0
- support_code/nmdc/metabolomics/metabolomics_collection.py +628 -0
- support_code/nmdc/metadata/dms_api.py +42 -0
- support_code/nmdc/nom/archived_scripts/nmdc_metadata_gen.py +288 -0
- support_code/nmdc/nom/archived_scripts/nom_grow_workflow.py +209 -0
- support_code/nmdc/nom/nom_workflow.py +312 -0
- support_code/windows_only_importers/BrukerCompassXtract.py +180 -0
- support_code/windows_only_importers/ThermoMSFileReader.py +405 -0
|
@@ -0,0 +1,885 @@
|
|
|
1
|
+
__author__ = "Yuri E. Corilo"
|
|
2
|
+
__date__ = "Jun 24, 2019"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
import IsoSpecPy
|
|
8
|
+
from numpy import array, exp, isnan, nextafter, power
|
|
9
|
+
|
|
10
|
+
# this is to handle both versions of IsoSpecPy, 2.0.2 and 2.2.2
|
|
11
|
+
# TODO in a future release remove support for legacy isospecpy
|
|
12
|
+
from packaging import version
|
|
13
|
+
|
|
14
|
+
from corems.encapsulation.constant import Atoms, Labels
|
|
15
|
+
from corems.encapsulation.factory.parameters import MSParameters
|
|
16
|
+
from corems.molecular_id.calc.SpectralSimilarity import SpectralSimilarity
|
|
17
|
+
|
|
18
|
+
isospec_version = IsoSpecPy.__version__
|
|
19
|
+
if version.parse(isospec_version) > version.parse("2.0.2"):
|
|
20
|
+
legacy_isospec = False
|
|
21
|
+
else:
|
|
22
|
+
legacy_isospec = True
|
|
23
|
+
if legacy_isospec:
|
|
24
|
+
from IsoSpecPy import IsoSpecPy
|
|
25
|
+
|
|
26
|
+
warnings.warn(
|
|
27
|
+
f"IsoSpecPy version {isospec_version} is installed, and support is deprecated. Please update to 2.2.2",
|
|
28
|
+
DeprecationWarning,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class MolecularFormulaCalc:
|
|
33
|
+
"""Class of calculations related to molecular formula
|
|
34
|
+
|
|
35
|
+
This class is not intended to be used directly, but rather to be inherited by other classes in the molecular_formula/factory module like MolecularFormula, MolecularFormulaIsotopologue, and LCMSLibRefMolecularFormula
|
|
36
|
+
|
|
37
|
+
Attributes
|
|
38
|
+
----------
|
|
39
|
+
mz_calc : float
|
|
40
|
+
The m/z value of the molecular formula.
|
|
41
|
+
neutral_mass : float
|
|
42
|
+
The neutral mass of the molecular formula.
|
|
43
|
+
ion_charge : int
|
|
44
|
+
The ion charge of the molecular formula.
|
|
45
|
+
_external_mz : float
|
|
46
|
+
The externally provided m/z value of the molecular formula.
|
|
47
|
+
_d_molecular_formula : dict
|
|
48
|
+
The dictionary representation of the molecular formula.
|
|
49
|
+
_mspeak_parent : object
|
|
50
|
+
The parent MS peak object associated with the molecular formula.
|
|
51
|
+
_assignment_mass_error : float
|
|
52
|
+
The mass error of the molecular formula.
|
|
53
|
+
|
|
54
|
+
Methods
|
|
55
|
+
-------
|
|
56
|
+
* _calc_resolving_power_low_pressure(B, T)
|
|
57
|
+
Calculate the resolving power at low pressure.
|
|
58
|
+
* _calc_resolving_power_high_pressure(B, T)
|
|
59
|
+
Calculate the resolving power at high pressure.
|
|
60
|
+
* _adduct_mz(adduct_atom, ion_charge)
|
|
61
|
+
Get the m/z value of an adducted ion version of the molecular formula.
|
|
62
|
+
* _protonated_mz(ion_charge)
|
|
63
|
+
Get the m/z value of a protonated or deprotonated ion version of the molecular formula.
|
|
64
|
+
* _radical_mz(ion_charge)
|
|
65
|
+
Get the m/z value of a radical ion version of the molecular formula.
|
|
66
|
+
* _neutral_mass()
|
|
67
|
+
Get the neutral mass of the molecular formula.
|
|
68
|
+
* _calc_mz()
|
|
69
|
+
Get the m/z value of the molecular formula.
|
|
70
|
+
* _calc_assignment_mass_error(method='ppm')
|
|
71
|
+
Calculate the mass error of the molecular formula.
|
|
72
|
+
* _calc_mz_confidence(mean=0)
|
|
73
|
+
Calculate the m/z confidence of the molecular formula.
|
|
74
|
+
* _calc_isotopologue_confidence()
|
|
75
|
+
Calculate the isotopologue confidence of the molecular formula.
|
|
76
|
+
* normalize_distance(dist, dist_range)
|
|
77
|
+
Normalize the distance value.
|
|
78
|
+
* subtract_formula(formula_obj, formated=True)
|
|
79
|
+
Subtract a formula from the current formula object.
|
|
80
|
+
* _calc_average_mz_score()
|
|
81
|
+
Calculate the average m/z error score of the molecular formula identification, including the isotopologues.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def _calc_resolving_power_low_pressure(self, B, T):
|
|
85
|
+
"""
|
|
86
|
+
Calculate the resolving power at low pressure.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
B : float
|
|
91
|
+
Magnetic Strength (Testa).
|
|
92
|
+
T : float
|
|
93
|
+
Transient time (seconds).
|
|
94
|
+
|
|
95
|
+
"""
|
|
96
|
+
return (1.274 * 10000000 * B * T) * (1 / self.mz_calc)
|
|
97
|
+
|
|
98
|
+
def _calc_resolving_power_high_pressure(self, B, T):
|
|
99
|
+
"""
|
|
100
|
+
Calculate the resolving power at high pressure.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
B : float
|
|
105
|
+
Magnetic Strength (Testa).
|
|
106
|
+
T : float
|
|
107
|
+
Transient time (seconds).
|
|
108
|
+
|
|
109
|
+
"""
|
|
110
|
+
return (2.758 * 10000000 * B * T) * (1 / self.mz_calc)
|
|
111
|
+
|
|
112
|
+
def _adduct_mz(self, adduct_atom, ion_charge):
|
|
113
|
+
"""Get the m/z value of an adducted ion version of the molecular formula.
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
adduct_atom : str
|
|
118
|
+
The adduct atom.
|
|
119
|
+
ion_charge : int
|
|
120
|
+
The ion charge.
|
|
121
|
+
|
|
122
|
+
"""
|
|
123
|
+
return (
|
|
124
|
+
self.neutral_mass
|
|
125
|
+
+ (Atoms.atomic_masses.get(adduct_atom))
|
|
126
|
+
+ (ion_charge * -1 * Atoms.electron_mass)
|
|
127
|
+
) / abs(ion_charge)
|
|
128
|
+
|
|
129
|
+
def _protonated_mz(self, ion_charge):
|
|
130
|
+
"""Get the m/z value of a protonated or deprotonated ion version of the molecular formula.
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
ion_charge : int
|
|
135
|
+
The ion charge.
|
|
136
|
+
"""
|
|
137
|
+
return (
|
|
138
|
+
self.neutral_mass
|
|
139
|
+
+ (ion_charge * Atoms.atomic_masses.get("H"))
|
|
140
|
+
+ (ion_charge * -1 * Atoms.electron_mass)
|
|
141
|
+
) / abs(ion_charge)
|
|
142
|
+
|
|
143
|
+
def _radical_mz(self, ion_charge):
|
|
144
|
+
"""Get the m/z value of a radical ion version of the molecular formula.
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
ion_charge : int
|
|
149
|
+
The ion charge.
|
|
150
|
+
"""
|
|
151
|
+
return (self.neutral_mass + (ion_charge * -1 * Atoms.electron_mass)) / abs(
|
|
152
|
+
ion_charge
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def _neutral_mass(self):
|
|
156
|
+
"""Get the neutral mass of the molecular formula."""
|
|
157
|
+
|
|
158
|
+
mass = 0
|
|
159
|
+
|
|
160
|
+
for each_atom in self._d_molecular_formula.keys():
|
|
161
|
+
if each_atom != Labels.ion_type and each_atom != "HC":
|
|
162
|
+
try:
|
|
163
|
+
mass = mass + Atoms.atomic_masses[
|
|
164
|
+
each_atom
|
|
165
|
+
] * self._d_molecular_formula.get(each_atom)
|
|
166
|
+
|
|
167
|
+
except:
|
|
168
|
+
print(Labels.ion_type, each_atom)
|
|
169
|
+
|
|
170
|
+
return mass
|
|
171
|
+
|
|
172
|
+
def _calc_mz(self):
|
|
173
|
+
"""Get the m/z value of the molecular formula, based on the ion charge and ion type."""
|
|
174
|
+
|
|
175
|
+
if self.ion_charge is not None:
|
|
176
|
+
if self._external_mz:
|
|
177
|
+
return self._external_mz
|
|
178
|
+
|
|
179
|
+
else:
|
|
180
|
+
ion_type = self._d_molecular_formula.get(Labels.ion_type)
|
|
181
|
+
|
|
182
|
+
if ion_type == Labels.protonated_de_ion:
|
|
183
|
+
return self.protonated_mz
|
|
184
|
+
|
|
185
|
+
elif ion_type == Labels.radical_ion or ion_type == Labels.adduct_ion:
|
|
186
|
+
return self.radical_mz
|
|
187
|
+
|
|
188
|
+
elif ion_type == Labels.neutral:
|
|
189
|
+
return self.neutral_mass
|
|
190
|
+
|
|
191
|
+
elif self.ion_charge == 0:
|
|
192
|
+
return self.neutral_mass
|
|
193
|
+
|
|
194
|
+
else:
|
|
195
|
+
# formula is probably ion form used for bruker ref list
|
|
196
|
+
return self.neutral_mass
|
|
197
|
+
|
|
198
|
+
else:
|
|
199
|
+
raise Exception("Please set ion charge first")
|
|
200
|
+
|
|
201
|
+
def _calc_assignment_mass_error(self, method="ppm"):
|
|
202
|
+
"""Calculate the mass error of the molecular formula, based on the experimental m/z and the calculated m/z.
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
method : str, optional
|
|
207
|
+
The method to calculate the mass error, by default 'ppm', but can be 'ppb'
|
|
208
|
+
|
|
209
|
+
Raises
|
|
210
|
+
------
|
|
211
|
+
Exception
|
|
212
|
+
If the method is not 'ppm' or 'ppb'.
|
|
213
|
+
Exception
|
|
214
|
+
If there is no ms peak associated with the molecular formula instance.
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
if method == "ppm":
|
|
218
|
+
multi_factor = 1000000
|
|
219
|
+
|
|
220
|
+
elif method == "ppb":
|
|
221
|
+
multi_factor = 1000000
|
|
222
|
+
|
|
223
|
+
else:
|
|
224
|
+
raise Exception(
|
|
225
|
+
"method needs to be ppm or ppb, you have entered %s" % method
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
if self._mspeak_parent.mz_exp:
|
|
229
|
+
self._assignment_mass_error = (
|
|
230
|
+
(self._mspeak_parent.mz_exp - self.mz_calc) / self.mz_calc
|
|
231
|
+
) * multi_factor
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
(self._mspeak_parent.mz_exp - self.mz_calc) / self.mz_calc
|
|
235
|
+
) * multi_factor
|
|
236
|
+
|
|
237
|
+
else:
|
|
238
|
+
raise Exception(
|
|
239
|
+
"No ms peak associated with the molecular formula instance %s", self
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
def _calc_mz_confidence(self, mean=0):
|
|
243
|
+
"""Calculate the m/z confidence of the molecular formula, based on the experimental m/z and the calculated m/z.
|
|
244
|
+
|
|
245
|
+
Parameters
|
|
246
|
+
----------
|
|
247
|
+
mean : int, optional
|
|
248
|
+
The mean of the m/z error, by default 0
|
|
249
|
+
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
# predicted std not set, using 0.3
|
|
253
|
+
if not self._mspeak_parent.predicted_std:
|
|
254
|
+
self._mspeak_parent.predicted_std = 1.66
|
|
255
|
+
|
|
256
|
+
# print( self._mspeak_parent.predicted_std)
|
|
257
|
+
|
|
258
|
+
return exp(
|
|
259
|
+
-1
|
|
260
|
+
* (
|
|
261
|
+
power((self.mz_error - mean), 2)
|
|
262
|
+
/ (2 * power(self._mspeak_parent.predicted_std, 2))
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
def _calc_isotopologue_confidence(self):
|
|
267
|
+
"""Calculate the isotopologue confidence of the molecular formula, based on the isotopologue similarity.
|
|
268
|
+
|
|
269
|
+
Returns
|
|
270
|
+
-------
|
|
271
|
+
float
|
|
272
|
+
The isotopologue confidence of the molecular formula.
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
if self.is_isotopologue:
|
|
276
|
+
# confidence of isotopologue is pure mz error
|
|
277
|
+
# TODO add more features here
|
|
278
|
+
|
|
279
|
+
mformula_index = self.mono_isotopic_formula_index
|
|
280
|
+
mspeak_index = self.mspeak_index_mono_isotopic
|
|
281
|
+
|
|
282
|
+
mspeak = self._mspeak_parent._ms_parent[mspeak_index]
|
|
283
|
+
|
|
284
|
+
expected_isotopologues = mspeak[mformula_index].expected_isotopologues
|
|
285
|
+
|
|
286
|
+
mono_mz = mspeak[mformula_index].mz_calc
|
|
287
|
+
mono_abundance = mspeak.abundance
|
|
288
|
+
|
|
289
|
+
else:
|
|
290
|
+
mono_mz = self.mz_calc
|
|
291
|
+
mono_abundance = self._mspeak_parent.abundance
|
|
292
|
+
|
|
293
|
+
expected_isotopologues = self.expected_isotopologues
|
|
294
|
+
# has isotopologues based on current dinamic range
|
|
295
|
+
|
|
296
|
+
if expected_isotopologues:
|
|
297
|
+
dict_mz_abund_ref = {"mz": [mono_mz], "abundance": [mono_abundance]}
|
|
298
|
+
|
|
299
|
+
# get reference data
|
|
300
|
+
for mf in expected_isotopologues:
|
|
301
|
+
dict_mz_abund_ref["abundance"].append(mf.abundance_calc)
|
|
302
|
+
dict_mz_abund_ref["mz"].append(mf.mz_calc)
|
|
303
|
+
|
|
304
|
+
dict_mz_abund_exp = {mono_mz: mono_abundance}
|
|
305
|
+
|
|
306
|
+
# get experimental data
|
|
307
|
+
for mf in expected_isotopologues:
|
|
308
|
+
# molecular formula has been assigned to a peak
|
|
309
|
+
if mf._mspeak_parent:
|
|
310
|
+
# stores mspeak abundance
|
|
311
|
+
dict_mz_abund_exp[mf.mz_calc] = mf._mspeak_parent.abundance
|
|
312
|
+
|
|
313
|
+
else:
|
|
314
|
+
# fill missing mz with abundance 0 and mz error score of 0
|
|
315
|
+
dict_mz_abund_exp[mf.mz_calc] = nextafter(0, 1)
|
|
316
|
+
|
|
317
|
+
distance = SpectralSimilarity(
|
|
318
|
+
dict_mz_abund_exp, dict_mz_abund_ref
|
|
319
|
+
).manhattan_distance()
|
|
320
|
+
correlation = 1 - self.normalize_distance(distance, [0, 2])
|
|
321
|
+
# correlation = dwt_correlation(dict_mz_abund_exp, dict_mz_abund_ref)
|
|
322
|
+
# correlation = cosine_correlation(dict_mz_abund_exp, dict_mz_abund_ref)
|
|
323
|
+
|
|
324
|
+
if correlation == 1:
|
|
325
|
+
print(dict_mz_abund_exp, dict_mz_abund_ref)
|
|
326
|
+
if isnan(correlation):
|
|
327
|
+
# print(dict_mz_abund_exp, dict_mz_abund_ref)
|
|
328
|
+
correlation = 0.00001
|
|
329
|
+
|
|
330
|
+
else:
|
|
331
|
+
# no isotopologue expected giving a correlation score of 0.0 but it needs optimization
|
|
332
|
+
correlation = 0.0
|
|
333
|
+
|
|
334
|
+
return correlation
|
|
335
|
+
|
|
336
|
+
def normalize_distance(self, dist, dist_range):
|
|
337
|
+
"""
|
|
338
|
+
Normalize the distance value.
|
|
339
|
+
|
|
340
|
+
Parameters
|
|
341
|
+
----------
|
|
342
|
+
dist : float
|
|
343
|
+
The distance value to be normalized.
|
|
344
|
+
dist_range : list
|
|
345
|
+
The range of the distance value.
|
|
346
|
+
|
|
347
|
+
"""
|
|
348
|
+
result = (dist - dist_range[0]) / (dist_range[1] - dist_range[0])
|
|
349
|
+
|
|
350
|
+
if result < 0:
|
|
351
|
+
result = 0.0
|
|
352
|
+
elif result > 1:
|
|
353
|
+
result = 1.0
|
|
354
|
+
|
|
355
|
+
return result
|
|
356
|
+
|
|
357
|
+
def subtract_formula(self, formula_obj, formated=True):
|
|
358
|
+
"""Subtract a formula from the current formula object
|
|
359
|
+
|
|
360
|
+
Parameters
|
|
361
|
+
----------
|
|
362
|
+
formula_obj : MolecularFormula
|
|
363
|
+
MolecularFormula object to be subtracted from the current formula object
|
|
364
|
+
formated : bool, optional
|
|
365
|
+
If True, returns the formula in string format, by default True
|
|
366
|
+
|
|
367
|
+
"""
|
|
368
|
+
subtraction = {}
|
|
369
|
+
for atom, value in self.to_dict().items():
|
|
370
|
+
if atom != Labels.ion_type:
|
|
371
|
+
if formula_obj.get(atom):
|
|
372
|
+
# value_subtraction = value - formula_obj.get(atom)
|
|
373
|
+
if value - formula_obj.get(atom) > 0:
|
|
374
|
+
subtraction[atom] = value - formula_obj.get(atom)
|
|
375
|
+
else:
|
|
376
|
+
subtraction[atom] = value
|
|
377
|
+
if formated:
|
|
378
|
+
SUB = str.maketrans("0123456789", "₀₁₂₃₄₅₆₇₈₉")
|
|
379
|
+
SUP = str.maketrans("0123456789", "⁰¹²³⁴⁵⁶⁷⁸⁹")
|
|
380
|
+
else:
|
|
381
|
+
SUB = str.maketrans("0123456789", "0123456789")
|
|
382
|
+
SUP = str.maketrans("0123456789", "0123456789")
|
|
383
|
+
formula_srt = ""
|
|
384
|
+
for atom in Atoms.atoms_order:
|
|
385
|
+
if atom in subtraction.keys():
|
|
386
|
+
formula_srt += atom.translate(SUP) + str(
|
|
387
|
+
int(subtraction.get(atom))
|
|
388
|
+
).translate(SUB)
|
|
389
|
+
|
|
390
|
+
return formula_srt
|
|
391
|
+
|
|
392
|
+
def _calc_average_mz_score(self):
|
|
393
|
+
"""Calculate the average m/z error score of the molecular formula identification, including the isotopologues."""
|
|
394
|
+
if self.is_isotopologue:
|
|
395
|
+
# confidence of isotopologue is pure mz error
|
|
396
|
+
# TODO add more features here
|
|
397
|
+
|
|
398
|
+
mformula_index = self.mono_isotopic_formula_index
|
|
399
|
+
mspeak_index = self.mspeak_index_mono_isotopic
|
|
400
|
+
|
|
401
|
+
mspeak = self._mspeak_parent._ms_parent[mspeak_index]
|
|
402
|
+
|
|
403
|
+
expected_isotopologues = mspeak[mformula_index].expected_isotopologues
|
|
404
|
+
|
|
405
|
+
else:
|
|
406
|
+
expected_isotopologues = self.expected_isotopologues
|
|
407
|
+
# has isotopologues based on current dinamic range
|
|
408
|
+
|
|
409
|
+
accumulated_mz_score = [self.mz_error_score]
|
|
410
|
+
|
|
411
|
+
if expected_isotopologues:
|
|
412
|
+
for mf in expected_isotopologues:
|
|
413
|
+
# molecular formula has been assigned to a peak
|
|
414
|
+
if mf._mspeak_parent:
|
|
415
|
+
# stores mspeak abundance
|
|
416
|
+
accumulated_mz_score.append(mf.mz_error_score)
|
|
417
|
+
else:
|
|
418
|
+
# fill missing mz with abundance 0 and mz error score of 0
|
|
419
|
+
accumulated_mz_score.append(0.0)
|
|
420
|
+
|
|
421
|
+
average_mz_score = sum(accumulated_mz_score) / len(accumulated_mz_score)
|
|
422
|
+
|
|
423
|
+
if isnan(average_mz_score):
|
|
424
|
+
average_mz_score = 0.0
|
|
425
|
+
|
|
426
|
+
return average_mz_score
|
|
427
|
+
|
|
428
|
+
def _calc_confidence_score(self):
|
|
429
|
+
"""Calculate the confidence score of the molecular formula identification, including the isotopologues."""
|
|
430
|
+
|
|
431
|
+
### Assumes random mass error, i.e, spectrum has to be calibrated and with zero mean
|
|
432
|
+
#### TODO: Add spectral similarity
|
|
433
|
+
|
|
434
|
+
## Parameters
|
|
435
|
+
# ----------
|
|
436
|
+
#### mz_exp:
|
|
437
|
+
#### Experimental m/z
|
|
438
|
+
#### predicted_std:
|
|
439
|
+
#### Standart deviation calculated from Resolving power optimization or constant set by User
|
|
440
|
+
|
|
441
|
+
isotopologue_correlation = self.isotopologue_similarity
|
|
442
|
+
average_mz_score = self.average_mz_error_score
|
|
443
|
+
# add monoisotopic peak mz error score
|
|
444
|
+
|
|
445
|
+
# calculate score with higher weight for mass error
|
|
446
|
+
# score = power(((isotopologue_correlation) * (power(average_mz_score,3))),1/4)
|
|
447
|
+
a = self._mspeak_parent._ms_parent.molecular_search_settings.mz_error_score_weight
|
|
448
|
+
b = self._mspeak_parent._ms_parent.molecular_search_settings.isotopologue_score_weight
|
|
449
|
+
|
|
450
|
+
score = (isotopologue_correlation * b) + (average_mz_score * a)
|
|
451
|
+
|
|
452
|
+
# if round(average_mz_score,2) == 0.00:
|
|
453
|
+
# print(a,b, average_mz_score, isotopologue_correlation, score, isotopologue_correlation*b)
|
|
454
|
+
|
|
455
|
+
return score
|
|
456
|
+
|
|
457
|
+
def _calc_abundance_error(self, method="percentile"):
|
|
458
|
+
"""Calculate the abundance error of the molecular formula, based on the experimental abundance and the calculated abundance.
|
|
459
|
+
|
|
460
|
+
Parameters
|
|
461
|
+
----------
|
|
462
|
+
method : str, optional
|
|
463
|
+
The method to calculate the abundance error, by default 'percentile', but can be 'ppm' or 'ppb'
|
|
464
|
+
|
|
465
|
+
Returns
|
|
466
|
+
-------
|
|
467
|
+
float
|
|
468
|
+
The abundance error of the molecular formula.
|
|
469
|
+
|
|
470
|
+
Raises
|
|
471
|
+
------
|
|
472
|
+
Exception
|
|
473
|
+
If isotopologues were not calculated.
|
|
474
|
+
"""
|
|
475
|
+
|
|
476
|
+
mult_factor = 100
|
|
477
|
+
|
|
478
|
+
iso_abundance = self._mspeak_parent.abundance
|
|
479
|
+
mono_abundance = self._mspeak_parent._ms_parent[
|
|
480
|
+
self.mspeak_index_mono_isotopic
|
|
481
|
+
].abundance
|
|
482
|
+
|
|
483
|
+
if self.prob_ratio:
|
|
484
|
+
theor_abundance = mono_abundance * self.prob_ratio
|
|
485
|
+
# self.parent need to have a MassSpecPeak associated with the MolecularFormula class
|
|
486
|
+
return ((theor_abundance - iso_abundance) / theor_abundance) * mult_factor
|
|
487
|
+
|
|
488
|
+
else:
|
|
489
|
+
raise Exception("Please calc_isotopologues")
|
|
490
|
+
|
|
491
|
+
def _calc_area_error(self, method="percentile"):
|
|
492
|
+
"""Calculate the area error of the molecular formula, based on the experimental area and the calculated area.
|
|
493
|
+
|
|
494
|
+
Parameters
|
|
495
|
+
----------
|
|
496
|
+
method : str, optional
|
|
497
|
+
The method to calculate the area error, by default 'percentile', but can be 'ppm' or 'ppb'
|
|
498
|
+
|
|
499
|
+
Returns
|
|
500
|
+
-------
|
|
501
|
+
float
|
|
502
|
+
The area error of the molecular formula.
|
|
503
|
+
|
|
504
|
+
Raises
|
|
505
|
+
------
|
|
506
|
+
Exception
|
|
507
|
+
If isotopologues were not calculated.
|
|
508
|
+
"""
|
|
509
|
+
|
|
510
|
+
mult_factor = 100
|
|
511
|
+
|
|
512
|
+
iso_area = self._mspeak_parent.area
|
|
513
|
+
mono_area = self._mspeak_parent._ms_parent[self.mspeak_index_mono_isotopic].area
|
|
514
|
+
|
|
515
|
+
if self.prob_ratio:
|
|
516
|
+
if mono_area and iso_area:
|
|
517
|
+
# exp_ratio = iso_area/mono_area
|
|
518
|
+
|
|
519
|
+
area_calc = mono_area * self.prob_ratio
|
|
520
|
+
|
|
521
|
+
# self.parent need to have a MassSpecPeak associated with the MolecularFormula class
|
|
522
|
+
return ((area_calc - iso_area) / area_calc) * mult_factor
|
|
523
|
+
# return ((self.prob_ratio - exp_ratio )/self.prob_ratio)*mult_factor
|
|
524
|
+
|
|
525
|
+
else:
|
|
526
|
+
# centroid mass spectrum
|
|
527
|
+
return 0
|
|
528
|
+
else:
|
|
529
|
+
raise Exception("Please calc_isotopologues")
|
|
530
|
+
|
|
531
|
+
def _calc_aromaticity_index_mod(self):
|
|
532
|
+
"""Calculate the modified aromaticity index of the molecular formula.
|
|
533
|
+
|
|
534
|
+
Returns
|
|
535
|
+
-------
|
|
536
|
+
float
|
|
537
|
+
The aromaticity index of the molecular formula.
|
|
538
|
+
|
|
539
|
+
Notes
|
|
540
|
+
-----
|
|
541
|
+
Source Koch and Dittmar, 2006 https://doi.org/10.1002/rcm.2386
|
|
542
|
+
corrected in https://doi.org/10.1002/rcm.7433
|
|
543
|
+
"""
|
|
544
|
+
# Prepare empty dictionary to store the number of atoms of each element
|
|
545
|
+
ai_es = {"C": 0, "H": 0, "O": 0, "N": 0, "S": 0}
|
|
546
|
+
|
|
547
|
+
# Count the number of atoms of each element in the molecular formula, inclusive of isotopes
|
|
548
|
+
for element in ai_es:
|
|
549
|
+
elements_w_iso = [element] + Atoms.isotopes.get(element)[1]
|
|
550
|
+
for element_w_iso in elements_w_iso:
|
|
551
|
+
if element_w_iso in self._d_molecular_formula:
|
|
552
|
+
ai_es[element] += self._d_molecular_formula[element_w_iso]
|
|
553
|
+
|
|
554
|
+
ai_n = (
|
|
555
|
+
1
|
|
556
|
+
+ ai_es["C"]
|
|
557
|
+
- (0.5 * ai_es["O"])
|
|
558
|
+
- ai_es["S"]
|
|
559
|
+
- (0.5 * (ai_es["N"] + ai_es["H"]))
|
|
560
|
+
)
|
|
561
|
+
ai_d = ai_es["C"] - (0.5 * ai_es["O"]) - ai_es["N"] - ai_es["S"]
|
|
562
|
+
|
|
563
|
+
ai = ai_n / ai_d
|
|
564
|
+
|
|
565
|
+
if ai < 0:
|
|
566
|
+
ai = 0
|
|
567
|
+
if ai > 1:
|
|
568
|
+
ai = 1
|
|
569
|
+
|
|
570
|
+
return ai
|
|
571
|
+
|
|
572
|
+
def _calc_aromaticity_index(self):
|
|
573
|
+
"""Calculate the aromaticity index of the molecular formula.
|
|
574
|
+
|
|
575
|
+
Returns
|
|
576
|
+
-------
|
|
577
|
+
float
|
|
578
|
+
The aromaticity index of the molecular formula.
|
|
579
|
+
|
|
580
|
+
Notes
|
|
581
|
+
-----
|
|
582
|
+
Source Koch and Dittmar, 2006 https://doi.org/10.1002/rcm.2386
|
|
583
|
+
corrected in https://doi.org/10.1002/rcm.7433
|
|
584
|
+
"""
|
|
585
|
+
# Prepare empty dictionary to store the number of atoms of each element
|
|
586
|
+
ai_es = {"C": 0, "H": 0, "O": 0, "N": 0, "S": 0}
|
|
587
|
+
|
|
588
|
+
# Count the number of atoms of each element in the molecular formula, inclusive of isotopes
|
|
589
|
+
for element in ai_es:
|
|
590
|
+
elements_w_iso = [element] + Atoms.isotopes.get(element)[1]
|
|
591
|
+
for element_w_iso in elements_w_iso:
|
|
592
|
+
if element_w_iso in self._d_molecular_formula:
|
|
593
|
+
ai_es[element] += self._d_molecular_formula[element_w_iso]
|
|
594
|
+
|
|
595
|
+
ai_n = (
|
|
596
|
+
1
|
|
597
|
+
+ ai_es["C"]
|
|
598
|
+
- (ai_es["O"])
|
|
599
|
+
- ai_es["S"]
|
|
600
|
+
- (0.5 * (ai_es["N"] + ai_es["H"]))
|
|
601
|
+
)
|
|
602
|
+
ai_d = ai_es["C"] - (ai_es["O"]) - ai_es["N"] - ai_es["S"]
|
|
603
|
+
|
|
604
|
+
ai = ai_n / ai_d
|
|
605
|
+
|
|
606
|
+
if ai < 0:
|
|
607
|
+
ai = 0
|
|
608
|
+
if ai > 1:
|
|
609
|
+
ai = 1
|
|
610
|
+
|
|
611
|
+
return ai
|
|
612
|
+
|
|
613
|
+
def _calc_nosc(self):
|
|
614
|
+
"""Calculate the average nominal oxidation state of carbon
|
|
615
|
+
|
|
616
|
+
Returns
|
|
617
|
+
-------
|
|
618
|
+
float
|
|
619
|
+
The average nominal oxidation state of carbon
|
|
620
|
+
|
|
621
|
+
Notes
|
|
622
|
+
-----
|
|
623
|
+
Source LaRowe and Van Cappellen, 2011 https://doi.org/10.1016/j.gca.2011.01.020
|
|
624
|
+
"""
|
|
625
|
+
# Prepare empty dictionary to store the number of atoms of each element
|
|
626
|
+
nosc_es = {"C": 0, "H": 0, "O": 0, "N": 0, "S": 0, "P": 0}
|
|
627
|
+
|
|
628
|
+
# Count the number of atoms of each element in the molecular formula, inclusive of isotopes
|
|
629
|
+
for element in nosc_es:
|
|
630
|
+
elements_w_iso = [element] + Atoms.isotopes.get(element)[1]
|
|
631
|
+
for element_w_iso in elements_w_iso:
|
|
632
|
+
if element_w_iso in self._d_molecular_formula:
|
|
633
|
+
nosc_es[element] += self._d_molecular_formula[element_w_iso]
|
|
634
|
+
|
|
635
|
+
nosc = (
|
|
636
|
+
-(
|
|
637
|
+
(
|
|
638
|
+
4 * nosc_es["C"]
|
|
639
|
+
+ nosc_es["H"]
|
|
640
|
+
- 3 * nosc_es["N"]
|
|
641
|
+
- 2 * nosc_es["O"]
|
|
642
|
+
+ 5 * nosc_es["P"]
|
|
643
|
+
- 2 * nosc_es["S"]
|
|
644
|
+
)
|
|
645
|
+
/ nosc_es["C"]
|
|
646
|
+
)
|
|
647
|
+
+ 4
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
# If nosc is infinite or negative infinity, set it to nan
|
|
651
|
+
if nosc == float("inf") or nosc == float("-inf"):
|
|
652
|
+
nosc = float("nan")
|
|
653
|
+
|
|
654
|
+
return nosc
|
|
655
|
+
|
|
656
|
+
@property
|
|
657
|
+
def dbe_ai(self):
|
|
658
|
+
"""Calculate the double bond equivalent (DBE) of the molecular formula, based on the number of carbons, hydrogens, and oxygens."""
|
|
659
|
+
|
|
660
|
+
carbons = self._d_molecular_formula.get("C")
|
|
661
|
+
hydrogens = self._d_molecular_formula.get("H")
|
|
662
|
+
oxygens = self._d_molecular_formula.get("O")
|
|
663
|
+
return 1 + (((2 * carbons) - hydrogens - (2 * oxygens)) * 0.5)
|
|
664
|
+
|
|
665
|
+
def _calc_dbe(self):
|
|
666
|
+
"""Calculate the double bond equivalent (DBE) of the molecular formula"""
|
|
667
|
+
|
|
668
|
+
individual_dbe = 0
|
|
669
|
+
|
|
670
|
+
for atom in self._d_molecular_formula.keys():
|
|
671
|
+
if atom != Labels.ion_type:
|
|
672
|
+
n_atom = int(self._d_molecular_formula.get(atom))
|
|
673
|
+
|
|
674
|
+
clean_atom = "".join([i for i in atom if not i.isdigit()])
|
|
675
|
+
|
|
676
|
+
if self._mspeak_parent:
|
|
677
|
+
valencia = self._mspeak_parent._ms_parent.molecular_search_settings.used_atom_valences.get(
|
|
678
|
+
clean_atom
|
|
679
|
+
)
|
|
680
|
+
else:
|
|
681
|
+
valencia = MSParameters.molecular_search.used_atom_valences.get(
|
|
682
|
+
clean_atom
|
|
683
|
+
)
|
|
684
|
+
# valencia = Atoms.atoms_covalence.get(atom)
|
|
685
|
+
|
|
686
|
+
if type(valencia) is tuple:
|
|
687
|
+
valencia = valencia[0]
|
|
688
|
+
if valencia > 0:
|
|
689
|
+
# print atom, valencia, n_atom, individual_dbe
|
|
690
|
+
individual_dbe = individual_dbe + (n_atom * (valencia - 2))
|
|
691
|
+
else:
|
|
692
|
+
continue
|
|
693
|
+
|
|
694
|
+
dbe = 1 + (0.5 * individual_dbe)
|
|
695
|
+
|
|
696
|
+
if self.ion_type == Labels.adduct_ion:
|
|
697
|
+
dbe = dbe + 0.5
|
|
698
|
+
|
|
699
|
+
return dbe
|
|
700
|
+
|
|
701
|
+
def _calc_kmd(self, dict_base):
|
|
702
|
+
"""Calculate the Kendrick mass defect (KMD) of the molecular formula, based on the monoisotopic mass and the Kendrick mass.
|
|
703
|
+
|
|
704
|
+
Parameters
|
|
705
|
+
----------
|
|
706
|
+
dict_base : dict
|
|
707
|
+
The dictionary of the base formula, e.g. {'C':1, 'H':2}
|
|
708
|
+
|
|
709
|
+
Returns
|
|
710
|
+
-------
|
|
711
|
+
tuple
|
|
712
|
+
The tuple of the KMD, Kendrick mass, and nominal Kendrick mass.
|
|
713
|
+
"""
|
|
714
|
+
mass = 0
|
|
715
|
+
for atom in dict_base.keys():
|
|
716
|
+
mass = mass + Atoms.atomic_masses.get(atom) * dict_base.get(atom)
|
|
717
|
+
|
|
718
|
+
kendrick_mass = (int(mass) / mass) * self.mz_calc
|
|
719
|
+
|
|
720
|
+
nominal_km = int(kendrick_mass)
|
|
721
|
+
|
|
722
|
+
kmd = (nominal_km - kendrick_mass) * 100
|
|
723
|
+
|
|
724
|
+
# kmd = (nominal_km - km) * 1
|
|
725
|
+
kmd = round(kmd, 0)
|
|
726
|
+
|
|
727
|
+
return kmd, kendrick_mass, nominal_km
|
|
728
|
+
|
|
729
|
+
def _cal_isotopologues(
|
|
730
|
+
self, formula_dict, min_abundance, current_abundance, ms_dynamic_range
|
|
731
|
+
):
|
|
732
|
+
"""Calculate the isotopologues for a given molecular formula.
|
|
733
|
+
|
|
734
|
+
Parameters
|
|
735
|
+
----------
|
|
736
|
+
formula_dict : dict
|
|
737
|
+
The dictionary of the molecular formula. Example: {'C':10, 'H', 20, 'O', 2}
|
|
738
|
+
min_abundance : float
|
|
739
|
+
The minimum abundance.
|
|
740
|
+
current_abundance : float
|
|
741
|
+
The current monoisotopic abundance.
|
|
742
|
+
ms_dynamic_range : float
|
|
743
|
+
The dynamic range.
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
Notes
|
|
747
|
+
-----
|
|
748
|
+
This is the primary function to look for isotopologues based on a monoisotopic molecular formula.
|
|
749
|
+
It needs to be expanded to include the calculation of resolving power and plot the results.
|
|
750
|
+
Use this function at runtime during the molecular identification algorithm only when a positive ID is observed to the monoisotopic ion.
|
|
751
|
+
Use this function to simulate mass spectrum (needs resolving power calculation to be fully operational).
|
|
752
|
+
It might break when adding non-conventional atoms (not yet tested).
|
|
753
|
+
This function employs the IsoSpecPy library https://github.com/MatteoLacki/IsoSpec.
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
"""
|
|
757
|
+
|
|
758
|
+
# last update on 05-26-2020, Yuri E. Corilo
|
|
759
|
+
|
|
760
|
+
# updated it to reflect min possible mass peak abundance
|
|
761
|
+
cut_off_to_IsoSpeccPy = 1 - (1 / ms_dynamic_range)
|
|
762
|
+
|
|
763
|
+
# print("cut_off_to_IsoSpeccPy", cut_off_to_IsoSpeccPy, current_abundance, min_abundance, ms_dynamic_range)
|
|
764
|
+
# print(cut_off_to_IsoSpeccPy)
|
|
765
|
+
atoms_labels = (
|
|
766
|
+
atom
|
|
767
|
+
for atom in formula_dict.keys()
|
|
768
|
+
if atom != Labels.ion_type and atom != "H"
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
atoms_count = []
|
|
772
|
+
masses_list_tuples = []
|
|
773
|
+
props_list_tuples = []
|
|
774
|
+
all_atoms_list = []
|
|
775
|
+
|
|
776
|
+
for atom_label in atoms_labels:
|
|
777
|
+
if Atoms.isotopes.get(atom_label)[1][0] is None:
|
|
778
|
+
"This atom_label has no heavy isotope"
|
|
779
|
+
atoms_count.append(formula_dict.get(atom_label))
|
|
780
|
+
mass = Atoms.atomic_masses.get(atom_label)
|
|
781
|
+
prop = Atoms.isotopic_abundance.get(atom_label)
|
|
782
|
+
masses_list_tuples.append([mass])
|
|
783
|
+
props_list_tuples.append([prop])
|
|
784
|
+
all_atoms_list.append(atom_label)
|
|
785
|
+
|
|
786
|
+
else:
|
|
787
|
+
isotopes_label_list = Atoms.isotopes.get(atom_label)[1]
|
|
788
|
+
|
|
789
|
+
if len(isotopes_label_list) > 1:
|
|
790
|
+
"This atom_label has two or more heavy isotope"
|
|
791
|
+
isotopos_labels = [i for i in isotopes_label_list]
|
|
792
|
+
else:
|
|
793
|
+
"This atom_label only has one heavy isotope"
|
|
794
|
+
isotopos_labels = [isotopes_label_list[0]]
|
|
795
|
+
|
|
796
|
+
# all_atoms_list.extend(isotopos_labels)
|
|
797
|
+
isotopos_labels = [atom_label] + isotopos_labels
|
|
798
|
+
|
|
799
|
+
all_atoms_list.extend(isotopos_labels)
|
|
800
|
+
|
|
801
|
+
masses = [
|
|
802
|
+
Atoms.atomic_masses.get(atom_label)
|
|
803
|
+
for atom_label in isotopos_labels
|
|
804
|
+
]
|
|
805
|
+
props = [
|
|
806
|
+
Atoms.isotopic_abundance.get(atom_label)
|
|
807
|
+
for atom_label in isotopos_labels
|
|
808
|
+
]
|
|
809
|
+
|
|
810
|
+
atoms_count.append(formula_dict.get(atom_label))
|
|
811
|
+
masses_list_tuples.append(masses)
|
|
812
|
+
props_list_tuples.append(props)
|
|
813
|
+
if legacy_isospec:
|
|
814
|
+
iso = IsoSpecPy.IsoSpec(
|
|
815
|
+
atoms_count,
|
|
816
|
+
masses_list_tuples,
|
|
817
|
+
props_list_tuples,
|
|
818
|
+
cut_off_to_IsoSpeccPy,
|
|
819
|
+
)
|
|
820
|
+
conf = iso.getConfs()
|
|
821
|
+
masses = conf[0]
|
|
822
|
+
probs = exp(conf[1])
|
|
823
|
+
molecular_formulas = conf[2]
|
|
824
|
+
# print('conf', conf)
|
|
825
|
+
# print('probs', conf[1])
|
|
826
|
+
else:
|
|
827
|
+
# This syntax in IsoSpecPy 2.2.2 yields the same information as the legacy approach
|
|
828
|
+
iso = IsoSpecPy.IsoTotalProb(
|
|
829
|
+
atomCounts=atoms_count,
|
|
830
|
+
isotopeMasses=masses_list_tuples,
|
|
831
|
+
isotopeProbabilities=props_list_tuples,
|
|
832
|
+
prob_to_cover=cut_off_to_IsoSpeccPy,
|
|
833
|
+
get_confs=True,
|
|
834
|
+
)
|
|
835
|
+
masses = list(iso.masses)
|
|
836
|
+
probs = array(list(iso.probs))
|
|
837
|
+
confs = list(iso.confs)
|
|
838
|
+
|
|
839
|
+
molecular_formulas = []
|
|
840
|
+
for x in confs:
|
|
841
|
+
tmplist = []
|
|
842
|
+
for y in x:
|
|
843
|
+
tmplist.extend(list(y))
|
|
844
|
+
molecular_formulas.append(tmplist)
|
|
845
|
+
|
|
846
|
+
new_formulas = []
|
|
847
|
+
|
|
848
|
+
for isotopologue_index in range(len(iso)):
|
|
849
|
+
# skip_mono_isotopic
|
|
850
|
+
|
|
851
|
+
formula_list = molecular_formulas[isotopologue_index]
|
|
852
|
+
new_formula_dict = dict(zip(all_atoms_list, formula_list))
|
|
853
|
+
new_formula_dict[Labels.ion_type] = formula_dict.get(Labels.ion_type)
|
|
854
|
+
if formula_dict.get("H"):
|
|
855
|
+
new_formula_dict["H"] = formula_dict.get("H")
|
|
856
|
+
|
|
857
|
+
new_formulas.append({x: y for x, y in new_formula_dict.items() if y != 0})
|
|
858
|
+
|
|
859
|
+
# formula_dict in new_formulas check if monoisotopic is being returned
|
|
860
|
+
if new_formulas: # and formula_dict in new_formulas:
|
|
861
|
+
# print(conf)
|
|
862
|
+
# print(new_formulas)
|
|
863
|
+
# print(atoms_count)
|
|
864
|
+
# print(all_atoms_list)
|
|
865
|
+
# print(masses_list_tuples)
|
|
866
|
+
# print(props_list_tuples)
|
|
867
|
+
# find where monoisotopic is
|
|
868
|
+
index_mono = new_formulas.index(formula_dict)
|
|
869
|
+
# calculate ratio iso/mono
|
|
870
|
+
probs = list(probs / probs[index_mono])
|
|
871
|
+
|
|
872
|
+
# delete the monoisotopic
|
|
873
|
+
del probs[index_mono]
|
|
874
|
+
del new_formulas[index_mono]
|
|
875
|
+
|
|
876
|
+
# print('probs_exp', probs)
|
|
877
|
+
for formulas, prob in zip(new_formulas, probs):
|
|
878
|
+
theor_abundance = current_abundance * prob
|
|
879
|
+
if theor_abundance > min_abundance:
|
|
880
|
+
# print(prob, theor_abundance, current_abundance)
|
|
881
|
+
yield (formulas, prob)
|
|
882
|
+
# return zip(new_formulas, probs )
|
|
883
|
+
|
|
884
|
+
# else:
|
|
885
|
+
# return []
|