libpappsomspp
Library for mass spectrometry
Loading...
Searching...
No Matches
timsframesmsrunreader.cpp
Go to the documentation of this file.
1/**
2 * \file pappsomspp/msrun/private/timsmsrunreader.h
3 * \date 05/09/2019
4 * \author Olivier Langella
5 * \brief MSrun file reader for native Bruker TimsTOF raw data
6 */
7
8/*******************************************************************************
9 * Copyright (c) 2019 Olivier Langella <Olivier.Langella@u-psud.fr>.
10 *
11 * This file is part of the PAPPSOms++ library.
12 *
13 * PAPPSOms++ 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++ 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++. If not, see <http://www.gnu.org/licenses/>.
25 *
26 ******************************************************************************/
27
33#include <QDebug>
34
35using namespace pappso;
36
41
46
47
49TimsFramesMsRunReader::massSpectrumSPtr([[maybe_unused]] std::size_t spectrum_index)
50{
52 QObject::tr("Not yet implemented in TimsFramesMsRunReader %1.\n").arg(__LINE__));
53
55}
56
57
60{
61 return msp_timsData->getMassSpectrumCstSPtrByGlobalScanIndex(spectrum_index);
62}
63
64
67 bool want_binary_data) const
68{
69
70 QualifiedMassSpectrum mass_spectrum;
71
72 msp_timsData->getQualifiedMassSpectrumByGlobalScanIndex(
73 getMsRunId(), mass_spectrum, spectrum_index, want_binary_data);
74 return mass_spectrum;
75}
76
77
78void
80{
81 // qDebug() << "Reading the spectrum collection with no specific
82 // configuration.";
83 MsRunReadConfig config;
84 readSpectrumCollection2(config, handler);
85}
86
87
88void
91{
92 // qDebug().noquote() << "Reading the spectrum collection with this "
93 // "specific configuration:"
94 // << config.toString();
95
96 std::vector<std::size_t> subset_of_tims_frame_ids;
97
98
99 bool asked_ion_mobility_scan_num_range = false;
100
101 quint32 mobility_scan_num_range_begin = std::numeric_limits<quint32>::max();
102 quint32 mobility_scan_num_range_end = std::numeric_limits<quint32>::max();
103 quint32 mobility_scan_num_range_width = std::numeric_limits<quint32>::max();
104
105 double mobility_one_over_k0 = std::numeric_limits<double>::max();
106 double mobility_one_over_k0_range_begin = std::numeric_limits<double>::max();
107 double mobility_one_over_k0_range_end = std::numeric_limits<double>::max();
108
111 {
112 mobility_scan_num_range_begin =
114 mobility_scan_num_range_end =
116
117 // We need the range width below.
118 mobility_scan_num_range_width =
119 mobility_scan_num_range_end + 1 - mobility_scan_num_range_begin;
120
121 asked_ion_mobility_scan_num_range = true;
122
123 // Be sure to check in the frames loop below that the user might
124 // have asked for an ion mobility range but on the basis of the 1/K0 unit.
125 }
126
127 const std::vector<FrameIdDescr> &frame_id_descr_list = msp_timsData->getFrameIdDescrList();
128
129 // Just for the feedback to the user.
130 std::size_t scan_count = 0;
131
132 for(auto const &frame_record : msp_timsData->getTimsFrameRecordList())
133 {
134 if(handler.shouldStop())
135 {
136 // qDebug() << "The operation was cancelled. Breaking the loop.";
137 throw ExceptionInterrupted(QObject::tr("Reading timsTOF data cancelled by the user."));
138 }
139
140 if(frame_record.frame_id == 0)
141 continue;
142
143 if(!config.acceptRetentionTimeInSeconds(frame_record.frame_time))
144 continue;
145
146 std::size_t ms_level = 2;
147 if(frame_record.msms_type == 0)
148 ms_level = 1;
149
150 if(!config.acceptMsLevel(ms_level))
151 continue;
152
153 // FIXME: this might be the place where to actually get the list of
154 // precursors' m/z and charge values.
155
156 subset_of_tims_frame_ids.push_back(frame_record.frame_id);
157
158 if(mobility_scan_num_range_width != std::numeric_limits<int>::max())
159 {
160 scan_count += mobility_scan_num_range_width;
161 }
162 else
163 {
164 scan_count += frame_id_descr_list[frame_record.frame_id].m_scanCount;
165 }
166 }
167
168 // At this point, we have a subset of frame records.
169 std::size_t frame_count = subset_of_tims_frame_ids.size();
170 qDebug() << "The number of retained RT range- and MS level-matching frames : " << frame_count;
171
172 // Inform the handler of the spectrum list so that it can handle feedback to
173 // the user.
174
175 // FIXME:
176 // Either we document the number of frames (because we assume we will
177 // flatten them all)...
178 handler.spectrumListHasSize(frame_count);
179 // Or we document the number of actual scans because we might not flatten
180 // all the frames.
181 // handler.spectrumListHasSize(scan_count);
182
183 // Check for m/z range selection
184 bool asked_mz_range = false;
185 double mz_range_begin = -1;
186 double mz_range_end = -1;
187
189 {
190 asked_mz_range = true;
191
192 mz_range_begin = config.getParameterValue(MsRunReadConfigParameter::MzRangeBegin).toDouble();
193 mz_range_end = config.getParameterValue(MsRunReadConfigParameter::MzRangeEnd).toDouble();
194
195 // qDebug() << "The m/z range asked is: " << mz_range_begin << "--"
196 // << mz_range_end;
197 }
198
199 // Check for m/z resolution downgrading
200 // The idea is that we merge a number of mz indices into a single index,
201 // which is essentially an increase of the m/z bin size, and therefore
202 // of the resolution/definition of the mass spectrum.
203 std::size_t mz_index_merge_window = 0;
205 {
206 mz_index_merge_window =
208
209 // qDebug() << "mz_index_merge_window=" << mz_index_merge_window;
210 }
211
212 // The scan index is the index of the scan in the *whole* mass data file, it
213 // is a sequential number of scans over all the frames.
214 std::size_t scan_index = 0; // iterate in each spectrum
215
216 for(std::size_t tims_frame_id : subset_of_tims_frame_ids)
217 {
218 if(handler.shouldStop())
219 {
220 // qDebug() << "The operation was cancelled. Breaking the loop.";
221 throw ExceptionInterrupted(QObject::tr("Reading timsTOF data cancelled by the user."));
222 }
223
224 // qDebug() << "tims_frame_id=" << tims_frame_id;
225
226 const FrameIdDescr &current_frame_record = frame_id_descr_list[tims_frame_id];
227 // qDebug() << "tims_frame_id=" << tims_frame_id;
228
229 // FIXME: from an outsider point of view, cumulated size does not
230 // convey the notion of sequential scan number.
231
232 scan_index = current_frame_record.m_globalScanIndex;
233
234 TimsFrameCstSPtr tims_frame_csp = msp_timsData->getTimsFrameCstSPtrCached(tims_frame_id);
235
236 // If the user wants to select 1/Ko values in a given range, we need to
237 // compute the ion mobility scan value starting from that 1/Ko value in
238 // *each* frame. Note that the computed mobility_scan_num_begin and
239 // mobility_scan_num_end would override thoses possibly set with
240 // TimsFramesMsRunReader_mobility_index_begin/end above.
241
243 .isNull() &&
245 {
246 mobility_one_over_k0_range_begin =
248 .toDouble();
249
250 mobility_one_over_k0_range_end =
252 .toDouble();
253
254 mobility_scan_num_range_begin =
255 tims_frame_csp.get()->getScanIndexFromOneOverK0(mobility_one_over_k0_range_begin);
256
257 mobility_scan_num_range_end =
258 tims_frame_csp.get()->getScanIndexFromOneOverK0(mobility_one_over_k0_range_end);
259
260 asked_ion_mobility_scan_num_range = true;
261 }
262
263 // Now that we know if the user has asked for an ion mobility range,
264 // either using scan indices or 1/K0 values, we need to double check the
265 // range borders.
266 quint32 count_of_mobility_scans = tims_frame_csp->getTotalNumberOfScans();
267
268 if(asked_ion_mobility_scan_num_range)
269 {
270 if(mobility_scan_num_range_end > (count_of_mobility_scans - 1))
271 {
272 mobility_scan_num_range_end = count_of_mobility_scans - 1;
273 }
274 }
275 else
276 {
277 mobility_scan_num_range_begin = 0;
278 mobility_scan_num_range_end = count_of_mobility_scans - 1;
279 }
280
281 // Now that we know the mobility index range, if we did not set the
282 // mobility one over K0 because that was not the unit used by
283 // the caller, then we can compute these values and set them
284 // later to the qualified mass spectrum parameters.
285 if(mobility_one_over_k0_range_begin == std::numeric_limits<double>::max())
286 mobility_one_over_k0_range_begin =
287 tims_frame_csp->getOneOverK0Transformation(mobility_scan_num_range_begin);
288 if(mobility_one_over_k0_range_end == std::numeric_limits<double>::max())
289 mobility_one_over_k0_range_end =
290 tims_frame_csp->getOneOverK0Transformation(mobility_scan_num_range_end);
291
292 mobility_scan_num_range_width =
293 mobility_scan_num_range_end + 1 - mobility_scan_num_range_begin;
294
295 // We want to provide the inverse mobility for the scan that sits in the
296 // middle of the defined range or the whole range if none is defined..
297 mobility_one_over_k0 = tims_frame_csp.get()->getScanIndexFromOneOverK0(
298 mobility_scan_num_range_begin + (mobility_scan_num_range_width / 2));
299
300 // Now, with or without the peak list, we have to craft a qualified mass
301 // spectrum that will hold all the data about the data in it.
302 QualifiedMassSpectrum qualified_mass_spectrum;
303
304 MassSpectrumId spectrum_id;
305
306 spectrum_id.setSpectrumIndex(tims_frame_id);
307 spectrum_id.setMsRunId(getMsRunId());
308
309 // Can be modified to add bits that might help our case
310 spectrum_id.setNativeId(QString("frame_id=%1 global_scan_index=%2 im_scan_range_begin=%3 "
311 "im_scan_range_end=%4")
312 .arg(tims_frame_id)
313 .arg(scan_index)
314 .arg(mobility_scan_num_range_begin)
315 .arg(mobility_scan_num_range_end));
316
317 qualified_mass_spectrum.setMassSpectrumId(spectrum_id);
318
319 // We want to document the retention time!
320
321 qualified_mass_spectrum.setRtInSeconds(tims_frame_csp.get()->getRtInSeconds());
322
323 // We do want to document the ms level of the spectrum and possibly
324 // the precursor's m/z and charge.
325 unsigned int frame_ms_level = tims_frame_csp.get()->getMsLevel();
326 qualified_mass_spectrum.setMsLevel(frame_ms_level);
327
328
329 // Arrival time at half the range.
330
331 qualified_mass_spectrum.setDtInMilliSeconds(tims_frame_csp.get()->getDriftTimeInMilliseconds(
332 mobility_scan_num_range_begin + (mobility_scan_num_range_width / 2)));
333
334 // 1/K0
335 qDebug() << "mobility_one_over_k0:" << mobility_one_over_k0
336 << "mobility_one_over_k0_range_begin:" << mobility_one_over_k0_range_begin
337 << "mobility_one_over_k0_range_end" << mobility_one_over_k0_range_end;
338
339 if(mobility_one_over_k0 == std::numeric_limits<double>::max() ||
340 mobility_one_over_k0_range_begin == std::numeric_limits<double>::max() ||
341 mobility_one_over_k0_range_end == std::numeric_limits<double>::max())
342 throw(
343 ExceptionNotPossible("Not possible that mobility_one_over_k0 and its "
344 "range are undefined."));
345
347 mobility_one_over_k0);
348 qualified_mass_spectrum.setParameterValue(
349 QualifiedMassSpectrumParameter::IonMobOneOverK0Begin, mobility_one_over_k0_range_begin);
351 mobility_one_over_k0_range_end);
352
353 // qDebug() << "mobility_scan_num_range_begin:"
354 // << mobility_scan_num_range_begin
355 // << "mobility_scan_num_range_end:" <<
356 // mobility_scan_num_range_end;
357
358 if(mobility_scan_num_range_begin == std::numeric_limits<quint32>::max() ||
359 mobility_scan_num_range_end == std::numeric_limits<quint32>::max())
360 throw(
361 ExceptionNotPossible("Not possible that mobility_scan_num_range values are undefined."));
362
364 mobility_scan_num_range_begin +
365 (mobility_scan_num_range_width / 2));
366 qualified_mass_spectrum.setParameterValue(
368 mobility_scan_num_range_begin);
369 qualified_mass_spectrum.setParameterValue(
371
372 qualified_mass_spectrum.setParameterValue(
374 static_cast<qlonglong>(tims_frame_csp->getTotalNumberOfScans()));
375
376
377 Trace trace;
378
379 if(config.needPeakList())
380 {
381 // Provide these two variables for the function below to fill in the
382 // values. We will need them later.
383 quint32 min_mz_index_out = 0;
384 quint32 max_mz_index_out = 0;
385
386 if(asked_mz_range)
387 {
389 mz_index_merge_window,
390 mz_range_begin,
391 mz_range_end,
392 mobility_scan_num_range_begin,
393 mobility_scan_num_range_end,
394 min_mz_index_out,
395 max_mz_index_out);
396 }
397 else
398 {
399 trace = tims_frame_csp->combineScansToTraceWithDowngradedMzResolution(
400 mz_index_merge_window,
401 mobility_scan_num_range_begin,
402 mobility_scan_num_range_end,
403 min_mz_index_out,
404 max_mz_index_out);
405 }
406
407 // qDebug() << "Got min_mz_index_out:" << min_mz_index_out;
408 // qDebug() << "Got max_mz_index_out:" << max_mz_index_out;
409
410 qualified_mass_spectrum.setParameterValue(
412 qualified_mass_spectrum.setParameterValue(
414
415 qualified_mass_spectrum.setMassSpectrumSPtr(std::make_shared<MassSpectrum>(trace));
416 qualified_mass_spectrum.setEmptyMassSpectrum(false);
417 }
418 else
419 {
420 qualified_mass_spectrum.setEmptyMassSpectrum(true);
421 }
422
423 handler.setQualifiedMassSpectrum(qualified_mass_spectrum);
424 }
425}
426
427
428void
430 [[maybe_unused]] SpectrumCollectionHandlerInterface &handler,
431 [[maybe_unused]] unsigned int ms_level)
432{
433 qDebug();
434}
435
436
437std::size_t
439{
440 return msp_timsData->getTotalScanCount();
441}
442
443
444Trace
446{
447
448 // We want to compute the TIC chromatogram, not load the chromatogram that
449 // is located in the SQL database.
450 //
451 // For this, we need to iterated into the frames and ask for MS1 spectra
452 // only. msp_timsData has that information:
453 //
454 // std::vector<FrameIdDescr> m_frameIdDescrList;
455 //
456 // and
457
458 // struct FrameIdDescr
459 // {
460 // std::size_t m_frameId; // frame id
461 // std::size_t m_size; // frame size (number of TOF scans in frame)
462 // std::size_t m_cumulSize; // cumulative size
463 // };
464
465 Trace tic_chromatogram;
466
467 const std::vector<FrameIdDescr> frame_descr_list = msp_timsData->getFrameIdDescrList();
468
469 for(FrameIdDescr frame_id_descr : frame_descr_list)
470 {
471 TimsFrameCstSPtr tims_frame_csp =
472 msp_timsData->getTimsFrameCstSPtrCached(frame_id_descr.m_frameId);
473 std::size_t scan_begin = 0;
474 std::size_t scan_end = tims_frame_csp->getTotalNumberOfScans() - 1;
475
476 // By convention, a TIC chromatogram is only performed using MS1
477 // spectra.
478 if(tims_frame_csp->getMsLevel() == 1)
479 {
480
481 // Retention times are in seconds in the Bruker world.
482 double rt = tims_frame_csp->getRtInSeconds();
483
484 tic_chromatogram.append(
485 DataPoint(rt, tims_frame_csp->cumulateScanRangeIntensities(scan_begin, scan_end)));
486 }
487 else
488 continue;
489 }
490
491 return tic_chromatogram;
492}
493
494
495std::size_t
497 [[maybe_unused]])
498{
500 QObject::tr("%1 %2 %3 not implemented").arg(__FILE__).arg(__FUNCTION__).arg(__LINE__));
501}
void setNativeId(const QString &native_id)
void setMsRunId(MsRunIdCstSPtr other)
void setSpectrumIndex(std::size_t index)
const QVariant getParameterValue(MsRunReadConfigParameter parameter) const
bool acceptMsLevel(std::size_t ms_level) const
bool acceptRetentionTimeInSeconds(double retention_time_in_seconds) const
const MsRunIdCstSPtr & getMsRunId() const
Class representing a fully specified mass spectrum.
void setDtInMilliSeconds(pappso_double rt)
Set the drift time in milliseconds.
void setMassSpectrumId(const MassSpectrumId &iD)
Set the MassSpectrumId.
void setMsLevel(uint ms_level)
Set the mass spectrum level.
void setParameterValue(QualifiedMassSpectrumParameter parameter, const QVariant &value)
void setMassSpectrumSPtr(MassSpectrumSPtr massSpectrum)
Set the MassSpectrumSPtr.
void setRtInSeconds(pappso_double rt)
Set the retention time in seconds.
void setEmptyMassSpectrum(bool is_empty_mass_spectrum)
interface to collect spectrums from the MsRunReader class
virtual void setQualifiedMassSpectrum(const QualifiedMassSpectrum &spectrum)=0
virtual std::size_t getTotalNumberOfScans() const
get the number of scans contained in this frame each scan represents an ion mobility slice
double getDriftTimeInMilliseconds(std::size_t scan_index) const
get drift time of a scan number in milliseconds
std::size_t getScanIndexFromOneOverK0(double one_over_k0) const
get the scan number from a given 1/Ko mobility value
double getRtInSeconds() const
unsigned int getMsLevel() const
double getOneOverK0Transformation(std::size_t scan_index) const
get 1/K0 value of a given scan (mobility value)
virtual Trace combineScansToTraceWithDowngradedMzResolution(std::size_t mzindex_merge_window, std::size_t scanNumBegin, std::size_t scanNumEnd, quint32 &mz_minimum_index, quint32 &mz_maximum_index) const override
cumulate spectrum given a scan number range need the binary file The intensities are normalized with ...
virtual quint64 cumulateScanRangeIntensities(std::size_t scanNumBegin, std::size_t scanNumEnd) const override
...
virtual Trace combineScansToTraceWithDowngradedMzResolution2(std::size_t mz_index_merge_window, double mz_range_begin, double mz_range_end, std::size_t mobility_scan_begin, std::size_t mobility_scan_end, quint32 &mz_minimum_index_out, quint32 &mz_maximum_index_out) const override
cumulate spectrum given a scan number range need the binary file The intensities are normalized with ...
virtual std::size_t spectrumListSize() const override
get the totat number of spectrum conained in the MSrun data file
virtual void readSpectrumCollection(SpectrumCollectionHandlerInterface &handler) override
function to visit an MsRunReader and get each Spectrum in a spectrum collection handler
virtual MassSpectrumCstSPtr massSpectrumCstSPtr(std::size_t spectrum_index) override
virtual QualifiedMassSpectrum qualifiedMassSpectrum(std::size_t spectrum_index, bool want_binary_data=true) const override
get a QualifiedMassSpectrum class given its scan number
virtual std::size_t spectrumStringIdentifier2SpectrumIndex(const QString &spectrum_identifier) override
if possible, get the spectrum index given a string identifier throw a not found exception if spectrum...
virtual MassSpectrumSPtr massSpectrumSPtr(std::size_t spectrum_index) override
get a MassSpectrumSPtr class given its spectrum index
virtual void readSpectrumCollection2(const MsRunReadConfig &config, SpectrumCollectionHandlerInterface &handler) override
TimsFramesMsRunReader(MsRunIdCstSPtr &msrun_id_csp)
virtual void readSpectrumCollectionByMsLevel(SpectrumCollectionHandlerInterface &handler, unsigned int ms_level) override
function to visit an MsRunReader and get each Spectrum in a spectrum collection handler by Ms Levels
TimsMsRunReaderBase(MsRunIdCstSPtr &msrun_id_csp)
A simple container of DataPoint instances.
Definition trace.h:148
size_t append(const DataPoint &data_point)
appends a datapoint and return new size
Definition trace.cpp:623
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition aa.cpp:39
std::shared_ptr< const MsRunId > MsRunIdCstSPtr
Definition msrunid.h:46
std::shared_ptr< const MassSpectrum > MassSpectrumCstSPtr
@ TimsFrameMzIndexBegin
Bruker's timsTOF mz index frame start range.
@ TimsFrameScansCount
Bruker's timsTOF frame's total ion mobility slots.
@ TimsFrameMzIndexEnd
Bruker's timsTOF mz index frame end range.
std::shared_ptr< MassSpectrum > MassSpectrumSPtr
std::shared_ptr< const TimsFrame > TimsFrameCstSPtr
Definition timsframe.h:44
std::size_t m_globalScanIndex
Definition timsdata.h:61