libpappsomspp
Library for mass spectrometry
Loading...
Searching...
No Matches
mzcborprecursorfinder.cpp
Go to the documentation of this file.
1/**
2 * \file pappsomspp/core/processing/cbor/mzcbor/copyreplace/mzcborprecursorfinder.cpp
3 * \date 2/10/2026
4 * \author Olivier Langella
5 * \brief read mzcbor stream or file, process it and write processed mzcbor output
6 */
7
8/*******************************************************************************
9 * Copyright (c) 2026 Olivier Langella <Olivier.Langella@cnrs.fr>.
10 *
11 * This file is part of PAPPSOms-tools.
12 *
13 * PAPPSOms-tools is free software: you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation, either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * PAPPSOms-tools is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with PAPPSOms-tools. If not, see <http://www.gnu.org/licenses/>.
25 *
26 ******************************************************************************/
27
28#include "filtercomplement.h"
41#include <memory>
44#include <algorithm>
45#include <qjsonarray.h>
46#include <qlogging.h>
47#include <qobject.h>
48namespace pappso
49{
50namespace cbor
51{
52namespace mzcbor
53{
55 const QJsonObject &json_params)
56 : MzcborCopyReplaceBase(cbor_output_p, "precursor_finder")
57{
58 m_parameters = json_params;
59
60
61 if(getParameterValue("ms1_tolerance_unit").toString() == "dalton")
62 {
65 }
66 else if(getParameterValue("ms1_tolerance_unit").toString() == "ppm")
67 {
70 }
71 else
72 {
73 throw pappso::ExceptionNotFound(QObject::tr("ms1_tolerance_unit %1 not known")
74 .arg(getParameterValue("ms1_tolerance_unit").toString()));
75 }
76
77 if(getParameterValue("ms2_tolerance_unit").toString() == "dalton")
78 {
81 }
82 else if(getParameterValue("ms2_tolerance_unit").toString() == "ppm")
83 {
86 }
87 else
88 {
89 throw pappso::ExceptionNotFound(QObject::tr("ms2_tolerance_unit %1 not known")
90 .arg(getParameterValue("ms2_tolerance_unit").toString()));
91 }
92
93 m_minimumComplementIon = getParameterValue("minimum_complement_ion").toInt();
94 m_intensityRatioThreshold = getParameterValue("intensity_ratio_threshold").toDouble();
95
96 m_ms2nMostIntense = getParameterValue("ms2_max_peaks").toInt();
97
99 QJsonArray charge_arr = getParameterValue("potential_charge_states").toArray();
100 for(auto charge : charge_arr)
101 {
102 m_potentialPrecursorChargeStateList.push_back(charge.toInt());
103 }
104}
108
109
110QJsonValue
112{
113 QJsonValue value;
114 if(m_parameters.contains(name))
115 {
116 return m_parameters.value(name);
117 }
118 else
119 {
120 throw pappso::ExceptionNotFound(QObject::tr("parameter %1 not defined precursor finder JSON "
121 "parameters")
122 .arg(name));
123 }
124 return value;
125}
126
127void
129{
130 m_rtStart = rt_start;
131 m_rtStop = rt_stop;
132}
133void
135 QcborSpectrum &cbor_spectrum)
136{
137 if(cbor_spectrum.getMsLevel() == 1)
138 {
139 QString old_ms1_id = cbor_spectrum.getId();
140 m_spectrumMs1List.push_back({old_ms1_id, cbor_spectrum});
142 {
143 m_spectrumMs1List.pop_front();
144 }
145 QString new_ms1_id = spectrum_tube.push_back(cbor_spectrum);
146
147 m_ms1idOldToNew.insert({old_ms1_id, new_ms1_id});
148 }
149 else
150 {
151 double rt = cbor_spectrum.getRetentionTimeInSeconds();
152 if((rt > m_rtStart) && (rt < m_rtStop))
153 {
154
155 std::vector<QcborSpectrumPrecursor> precursor_list = cbor_spectrum.getPrecursorList();
156 if(precursor_list.size() == 1)
157 {
158 //"spectrumRef":"controllerType=0 controllerNumber=1 scan=2711",
159 QString spectrum_ref = precursor_list.at(0).getSpectrumRef();
160
161 auto it_ms1 =
162 std::find_if(m_spectrumMs1List.begin(),
163 m_spectrumMs1List.end(),
164 [spectrum_ref](const std::pair<QString, QcborSpectrum> &pair) {
165 return pair.first == spectrum_ref;
166 });
167 if(it_ms1 != m_spectrumMs1List.end())
168 {
169
170 // replace with new spectrum_ref
171 QString new_spectrum_ref = m_ms1idOldToNew.at(spectrum_ref);
172
173
174 QcborSpectrumPrecursor precursor = precursor_list.at(0);
175
176 pappso::MzRange original_ms1_peak_range(
177 precursor.getSelectedIonList().at(0).getMz(), m_ms1Tolerance);
178 // qWarning() << "original precursor charge="
179 // << precursor.getSelectedIonList().at(0).getChargeState();
180 precursor.setSpectrumRef(new_spectrum_ref);
181 std::vector<QcborSpectrumPrecursor> new_precursor_list;
182 new_precursor_list.push_back(precursor);
183 cbor_spectrum.setPrecursorList(new_precursor_list);
184
185
186 std::vector<QcborSpectrum> spectrum_list =
187 findPotentialPrecursorIonData(it_ms1->second, cbor_spectrum, precursor);
188
189 int i = 0;
190 bool report_original_spectrum = true;
191 for(QcborSpectrum &spectrum : spectrum_list)
192 {
193 QcborSpectrumPrecursor candidate_precursor =
194 spectrum.getPrecursorList().at(0);
195
196 // qWarning() << "candidate_precursor charge="
197 // <<
198 // candidate_precursor.getSelectedIonList().at(0).getChargeState();
199 if(candidate_precursor.getSelectedIonList().at(0).getChargeState() ==
200 precursor.getSelectedIonList().at(0).getChargeState())
201 {
202 if(original_ms1_peak_range.contains(
203 candidate_precursor.getSelectedIonList().at(0).getMz()))
204 {
205 report_original_spectrum = false;
206 }
207 }
208
209 QString new_id = spectrum_tube.push_back(spectrum);
210 // qWarning() << " id=" << new_id << " candidate_precursor charge="
211 // << spectrum.getPrecursorList()
212 // .at(0)
213 // .getSelectedIonList()
214 // .at(0)
215 // .getChargeState();
216 i++;
217 }
218 if(report_original_spectrum)
219 {
220 QString new_id = spectrum_tube.push_back(cbor_spectrum);
221 // qWarning() << "orig id=" << new_id
222 // << " spectrum_list.size()=" << spectrum_list.size()
223 // << " candidate_precursor charge="
224 // << cbor_spectrum.getPrecursorList()
225 // .at(0)
226 // .getSelectedIonList()
227 // .at(0)
228 // .getChargeState();
229 }
230 }
231 else
232 {
234 QObject::tr("ERROR: precursor ref %1 not found for current spectrum %2")
235 .arg(spectrum_ref)
236 .arg(cbor_spectrum.getId()));
237 }
238 }
239 else
240 {
241 qDebug() << cbor_spectrum.getId();
243 QObject::tr("ERROR: precursor_list.size() != 1 (%1)").arg(precursor_list.size()));
244 }
245 }
246 }
247}
248
249
250std::vector<QcborSpectrum>
252 const QcborSpectrum &ms2_spectrum,
253 const QcborSpectrumPrecursor &precursor)
254{
255
256 pappso::TraceSPtr ms1_trace_sp;
257
258 auto it_cache_ms1_trace =
259 std::find_if(m_cacheMs1Trace.cbegin(),
260 m_cacheMs1Trace.cend(),
261 [ms1_spectrum](const auto &item) { return item.first == &ms1_spectrum; });
262
263 if(it_cache_ms1_trace == m_cacheMs1Trace.end())
264 {
265 ms1_trace_sp = std::make_shared<Trace>();
266 ms1_spectrum.decodeTrace(*ms1_trace_sp.get());
267 m_cacheMs1Trace.push_back({&ms1_spectrum, ms1_trace_sp});
269 {
270 m_cacheMs1Trace.pop_front();
271 }
272 }
273 else
274 {
275 ms1_trace_sp = it_cache_ms1_trace->second;
276 }
277 pappso::Trace ms1_trace(*ms1_trace_sp.get());
278 pappso::Trace ms2_trace;
279 ms2_spectrum.decodeTrace(ms2_trace);
280
281 // MS2 filters
285 // END MS2 filters
286
287
288 std::vector<QcborSpectrum> potential_precursors;
289 double precursor_intensity = 0;
290 std::vector<SelectedIon> selected_ion_list = precursor.getSelectedIonList();
291 if(selected_ion_list.size() != 1)
292 {
293 throw pappso::PappsoException("ERROR: selected_ion_list.size() != 1");
294 }
295 bool is_ok = false;
296 precursor_intensity = selected_ion_list.at(0).getIntensity(&is_ok);
297 if(!is_ok)
298 {
300 QObject::tr("ERROR: precursor intensity not found in spectrum %1")
301 .arg(ms2_spectrum.getId()));
302 precursor_intensity = 0;
303 }
304
305 qDebug();
306 FilterMs1WindowSelection window(precursor);
307 qDebug();
308 window.filter(ms1_trace);
309 pappso::FilterHighPass(precursor_intensity * m_intensityRatioThreshold).filter(ms1_trace);
310
311 for(const pappso::DataPoint &data_point : ms1_trace)
312 {
314 {
315 double intensity = data_point.y;
316 double mz = data_point.x;
317
318 FilterComplement complement(mz * (double)charge, m_ms2Tolerance);
319 pappso::Trace ms2(ms2_trace);
320 complement.filter(ms2);
321 if(ms2.size() > (std::size_t)m_minimumComplementIon)
322 {
323 QcborSpectrum new_ms2(ms2_spectrum);
324
325 QcborSpectrumPrecursor copy_precursor(precursor);
326 copy_precursor.setSelectedIon(mz, intensity, charge);
327
328 std::vector<QcborSpectrumPrecursor> precursor_list;
329 precursor_list.push_back(copy_precursor);
330
331 qDebug() << copy_precursor;
332 new_ms2.setPrecursorList(precursor_list);
333
334 qDebug() << new_ms2;
335 potential_precursors.push_back(new_ms2);
336 }
337 }
338 }
339
340
341 return potential_precursors;
342}
343
344} // namespace mzcbor
345} // namespace cbor
346} // namespace pappso
try to detect complementary ions and assign maximum intensity of both elements
pappso::Trace & filter(pappso::Trace &data_points) const override
try to detect complementary ions and assign maximum intensity of both elements
pappso::Trace & filter(pappso::Trace &data_points) const override
Trace & filter(Trace &data_points) const override
get all the datapoints and remove different isotope and add their intensity and change to charge = 1 ...
keep N datapoints form the greatest intensities to the lowest
Definition filterpass.h:96
Trace & filter(Trace &data_points) const override
remove datapoints below or equal to the given Y value (intensity)
Definition filterpass.h:58
Trace & filter(Trace &data_points) const override
Trace & filter(Trace &trace) const override
bool contains(pappso_double) const
Definition mzrange.cpp:115
static PrecisionPtr getPpmInstance(pappso_double value)
get a ppm precision pointer
static PrecisionPtr getDaltonInstance(pappso_double value)
get a Dalton precision pointer
A simple container of DataPoint instances.
Definition trace.h:152
overrides QCborStreamWriter base class to provide convenient functions
MzcborCopyReplaceBase(CborStreamWriter *cbor_output_p, const QString &operation)
std::vector< QcborSpectrum > findPotentialPrecursorIonData(const QcborSpectrum &ms1_spectrum, const QcborSpectrum &ms2_spectrum, const QcborSpectrumPrecursor &precursor)
void setRetentionTimeRangeInSeconds(double rt_start, double rt_stop)
virtual void processQcborSpectrum(SpectrumTube &spectrum_tube, QcborSpectrum &cbor_spectrum)
std::deque< std::pair< QString, QcborSpectrum > > m_spectrumMs1List
MzcborPrecursorFinder(CborStreamWriter *cbor_output_p, const QJsonObject &json_params)
std::vector< std::int8_t > m_potentialPrecursorChargeStateList
std::deque< std::pair< const QcborSpectrum *, TraceSPtr > > m_cacheMs1Trace
QJsonValue getParameterValue(const QString &name) const
std::vector< SelectedIon > getSelectedIonList() const
void setSelectedIon(double mz, double intensity, int charge)
void decodeTrace(pappso::Trace &trace) const
std::vector< QcborSpectrumPrecursor > getPrecursorList() const
void setPrecursorList(std::vector< QcborSpectrumPrecursor > &precursor_list)
const QString push_back(QcborSpectrum &spectrum)
read mzcbor stream or file, process it and write processed mzcbor output
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition aa.cpp:39
std::shared_ptr< Trace > TraceSPtr
Definition trace.h:139
PSI precursor object for mzML/mzCBOR.
Low level CBOR map to store an mzCBOR Spectrum object.
Low level CBOR map to store an mzCBOR Spectrum object.