libpappsomspp
Library for mass spectrometry
Loading...
Searching...
No Matches
mzintegrationparams.cpp
Go to the documentation of this file.
1/* BEGIN software license
2 *
3 * msXpertSuite - mass spectrometry software suite
4 * -----------------------------------------------
5 * Copyright(C) 2009,...,2018 Filippo Rusconi
6 *
7 * http://www.msxpertsuite.org
8 *
9 * This file is part of the msXpertSuite project.
10 *
11 * The msXpertSuite project is the successor of the massXpert project. This
12 * project now includes various independent modules:
13 *
14 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
15 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
16 *
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29 *
30 * END software license
31 */
32
33/////////////////////// StdLib includes
34#include <cmath>
35#include <map>
36
37/////////////////////// Qt includes
38#include <QDateTime>
39#include <QDebug>
40#include <QFile>
41#include <QString>
42#include <QJSEngine>
43
44/////////////////////// pappsomspp includes
47
48/////////////////////// Local includes
50
51/*
52int mzIntegrationParamsMetaTypeId =
53 qRegisterMetaType<pappso::MzIntegrationParams>("pappso::MzIntegrationParams");
54int mzIntegrationParamsPtrMetaTypeId =
55 qRegisterMetaType<pappso::MzIntegrationParams *>("pappso::MzIntegrationParams
56*");*/
57
58namespace pappso
59{
60
61//! Map relating the BinningType to a textual representation
62std::map<MzIntegrationParams::BinningType, QString> binningTypeMap{
66
68getBinningTypeFromString(const QString &text)
69{
70 std::map<MzIntegrationParams::MzIntegrationParams::BinningType,
71 QString>::const_iterator the_iterator_const =
72 std::find_if(
73 binningTypeMap.begin(),
74 binningTypeMap.end(),
75 [text](const std::pair<MzIntegrationParams::BinningType, QString> &pair) {
76 return pair.second == text;
77 });
78
79 if(the_iterator_const != binningTypeMap.end())
80 return the_iterator_const->first;
81
83}
84
85MzIntegrationParams::MzIntegrationParams(QObject *parent): QObject(parent)
86{
87}
88
89MzIntegrationParams::MzIntegrationParams(const QString &text, QObject *parent)
90 : QObject(parent)
91{
92 initialize(text);
93}
94
96 double min_mz,
97 double max_mz,
99 pappso::PrecisionPtr bin_size_model,
100 int bin_size_divisor,
101 int decimal_places,
102 bool remove_zero_val_data_points,
103 QObject *parent)
104 : QObject(parent),
105 m_smallestMz(min_mz),
106 m_greatestMz(max_mz),
107 m_binningType(binning_type),
108 m_binSizeModel(bin_size_model),
109 m_binSizeDivisor(bin_size_divisor),
110 m_decimalPlaces(decimal_places),
111 m_removeZeroValDataPoints(remove_zero_val_data_points)
112{
113 if(m_binSizeModel == nullptr)
115
116 // qDebug() << "MetaObject?" << this->metaObject()->className();
117}
118
122
124MzIntegrationParams::clone(QObject *parent) const
125{
126 MzIntegrationParams *mz_integration_params_p =
134 parent);
135
136 return mz_integration_params_p;
137}
138
141{
142 // qDebug() << "Initializing MzIntegrationParams using text:" << text;
143
145
146 if(text.isEmpty())
147 return initialization_result;
148
149 reset();
150
151 // Expected text: "Smallest (first) m/z:294.725158\nGreatest (last)
152 // m/z:2055.002453\nDecimal places:-1\nBinning type:2\nBin size model:0.05
153 // dalton\nBin size divisor:1\nRemove 0-val data points:1\n"
154
155 // In order to consider that the parameters were correctly read, we need
156 // some of the members to effectively be set from the settings values.
157
158 bool binning_type_set = false;
159 bool decimal_places_set = false;
160 bool bin_size_model_set = false;
161 bool bin_size_divisor_set = false;
162 bool remove_zero_data_points_set = false;
163
164 QStringList string_list = text.split("\n");
165
166 for(int iter = 0; iter < string_list.size(); ++iter)
167 {
168 QString iter_string = string_list.at(iter);
169
170 // qDebug() << "Iterating in string:" << iter_string;
171
172 if(iter_string.contains("Binning type:"))
173 {
175 getBinningTypeFromString(iter_string.split(':').last()));
176 binning_type_set = true;
177 }
178 else if(iter_string.contains("Bin size model:"))
179 {
181 PrecisionFactory::fromString(iter_string.split(':').last()));
182 bin_size_model_set = true;
183 }
184 else if(iter_string.contains("Bin size divisor:"))
185 {
186 setBinSizeDivisor(iter_string.split(':').last().toInt());
187 bin_size_divisor_set = true;
188 }
189 else if(iter_string.contains("Decimal places:"))
190 {
191 setDecimalPlaces(iter_string.split(':').last().toInt());
192 decimal_places_set = true;
193 }
194 else if(iter_string.contains("Remove 0-val data points:"))
195 {
196 setRemoveZeroValDataPoints(iter_string.split(':').last().toInt());
197 remove_zero_data_points_set = true;
198 }
199 }
200
201 // qDebug() << "At this point the initialization is done and this"
202 // " MzIntegrationParams instance is: "
203 // << toString();
204
205 if(binning_type_set)
206 initialization_result |= InitializationResult::BINNING_TYPE;
207 if(bin_size_model_set)
208 initialization_result |= InitializationResult::BIN_SIZE_MODEL;
209 if(bin_size_divisor_set)
210 initialization_result |= InitializationResult::BIN_SIZE_DIVISOR;
211 if(decimal_places_set)
212 initialization_result |= InitializationResult::DECIMAL_PLACES;
213
214 if(remove_zero_data_points_set)
215 initialization_result |= InitializationResult::REMOVE_ZERO_DATA_POINTS;
216
217 return initialization_result;
218}
219
220void
222 double max_mz,
224 pappso::PrecisionPtr bin_size_model,
225 int bin_size_divisor,
226 int decimal_places,
227 bool remove_zero_val_data_points,
228 QObject *parent)
229{
230 setSmallestMz(min_mz);
231 setGreatestMz(max_mz);
232 setBinningType(binning_type);
233 setBinSizeModel(bin_size_model);
234 setBinSizeDivisor(bin_size_divisor);
235 setDecimalPlaces(decimal_places);
236 setRemoveZeroValDataPoints(remove_zero_val_data_points);
237
238 setParent(parent);
239}
240
241void
255
256// For use in JavaScript.
257void
259 QObject *parent)
260{
261 Q_ASSERT(other_p != nullptr);
262 initialize(*other_p, parent);
263}
264
265// Initialize this MzIntegrationParams using other but only for members that
266// were effectively set upon reading from a text string (see initialize
267// (QString)).
268void
270 InitializationResult initialization_results)
271{
272 if(static_cast<bool>(
273 initialization_results &
276
277 if(static_cast<bool>(
278 initialization_results &
281
282 if(static_cast<bool>(
283 initialization_results &
286
287 if(static_cast<bool>(
288 initialization_results &
291}
292
293void
295{
296 if(m_smallestMz != value)
297 m_smallestMz = value;
298
299 emit smallestMzChanged();
300}
301
302void
304{
305 if(value == m_smallestMz)
306 return;
307
308 m_smallestMz = value;
309 emit smallestMzChanged();
310}
311
312double
317
318void
320{
321 if(m_greatestMz == value)
322 return;
323
324 m_greatestMz = value;
325 emit greatestMzChanged();
326}
327
328void
330{
331 if(value > m_greatestMz)
332 {
333 m_greatestMz = value;
334 emit greatestMzChanged();
335 }
336}
337
338double
343
344void
345MzIntegrationParams::setMzValues(double smallest, double greatest)
346{
347 setSmallestMz(smallest);
348 setGreatestMz(greatest);
349}
350
351void
354{
355 if(m_binningType == binning_type)
356 return;
357
358 m_binningType = binning_type;
359 emit binningTypeChanged();
360}
361
367
368
369int
374
375void
377{
378 if(m_binSizeModel == bin_size_model_p)
379 return;
380
381 m_binSizeModel = bin_size_model_p;
382
383 if(m_binSizeModel == nullptr)
385
386 emit binSizeModelChanged();
387}
388
394
395void
397{
398 if(m_binSizeDivisor == divisor)
399 return;
400
401 m_binSizeDivisor = divisor;
403}
404
405int
410
411void
417
418double
423
424void
426{
427 if(m_decimalPlaces == decimal_places)
428 return;
429
430 m_decimalPlaces = decimal_places;
432}
433
434void
436{
437 if(m_removeZeroValDataPoints == removeOrNot)
438 return;
439
440 m_removeZeroValDataPoints = removeOrNot;
442}
443
444bool
449
450//! Reset the instance to default values.
451void
453{
454 // Each function handles the emission of the corresponding changed
455 // notification.
456 setSmallestMz(std::numeric_limits<double>::max());
457 setGreatestMz(std::numeric_limits<double>::min());
463}
464
465bool
467{
468 int errors = 0;
469
471 {
472 // qDebug() << "m_smallestMz:" << m_smallestMz;
473 // qDebug() << "smallest is max:" << (m_smallestMz ==
474 // std::numeric_limits<double>::max());
475 errors += (m_smallestMz == std::numeric_limits<double>::max() ? 1 : 0);
476
477 // qDebug() << "m_greatestMz:" << m_greatestMz;
478 // qDebug() << "greatest is min:" << (m_greatestMz ==
479 // std::numeric_limits<double>::min());
480 errors += (m_greatestMz == std::numeric_limits<double>::min() ? 1 : 0);
481 }
482
483 if(errors)
484 {
485 qCritical() << "The m/z integration parameters are invalid.";
486 }
487
488 return !errors;
489}
490
491bool
493{
494 return (m_smallestMz < std::numeric_limits<double>::max()) &&
495 (m_greatestMz >= std::numeric_limits<double>::min());
496}
497
498std::vector<double>
500{
501 // qDebug() << "mp_precision:" << mp_precision->toString();
502
503 std::vector<double> bins;
504
506 {
507 // If no binning is to be performed, fine.
508 return bins;
509 }
511 {
512 // Use only data in the MzIntegrationParams member data.
513 return createArbitraryBins();
514 }
516 {
517 qFatal() << "Programming error. "
518 "Please use the createBins(pappso::MassSpectrumCstSPtr "
519 "mass_spectrum_csp) overload.";
520 }
521
522 return bins;
523}
524
525std::vector<double>
527{
528 // qDebug();
529
530 std::vector<double> bins;
531
533 {
534 // If no binning is to be performed, fine.
535 return bins;
536 }
538 {
539 // Use only data in the MzIntegrationParams member data.
540 return createArbitraryBins();
541 }
543 {
544 // qDebug();
545
546 // Use the first spectrum to perform the data-based bins
547
548 return createDataBasedBins(mass_spectrum_csp);
549 }
550
551 return bins;
552}
553
554std::vector<double>
556{
557 // Now starts the tricky stuff. Depending on the binning size model, we need
558 // to take diverse actions.
559
560 if(!isValid())
561 qFatal() << "Programming error. The MzIntegrationParams::BinningLogic is "
562 "not valid, cannot create bins.";
563
564 // qDebug() << "Binning logic:" << toString();
565
566 qDebug() << qSetRealNumberPrecision(6) << "The smallest and greatest m/z values:" << m_smallestMz
567 << "-" << m_greatestMz;
568
569 // Compute the number of decimals required to faithfully account for the
570 // precision needed to represent the binned data. We do this by computing
571 // some sort of bin size and checking how many decimals we need to described it.
572
573 // We cannot have a smallest m/z that is 0 because that impairs the computation of the
574 // bins (see below). In case the m_smallestMz value is 0 we craft one temporary value
575 // by computing half of the range:
576
577 // In certain circumstances, the bin size is not enough to properly render
578 // hyper-high resolution data (like the theoretical isotopic cluster data
579 // generated in silico). In that case, the bin size, computed using the
580 // precision object, is divided by the m_binSizeDivisor, which normally is
581 // set to 6 as the default because that is the empirical observation that
582 // it gives the most nicely shaped peaks.
583 Q_ASSERT(m_binSizeDivisor >= 1);
584
585
586 // Instantiate the vector of mz double_s that we'll feed with the bins.
587 std::vector<double> bins;
588
589 double bin_size;
590
591 if(!m_smallestMz)
592 {
594 {
595 // The caller has intelligently set an indicative bin size. Use it.
596 bin_size = m_indicativeBinSize;
597 qDebug() << qSetRealNumberPrecision(6)
598 << "The indicative bin size is being used:" << bin_size;
599 }
600 else
601 {
602 double half_way_mz = 0;
603 half_way_mz = (m_greatestMz - m_smallestMz) / 2;
604
605 qDebug() << " Target min_mz:" << m_smallestMz << "and max_mz:" << m_greatestMz
606 << "half_way_mz:" << half_way_mz;
607 bin_size = m_binSizeModel->delta(half_way_mz) / m_binSizeDivisor;
608 qDebug() << qSetRealNumberPrecision(6)
609 << "The m/z range half way strategy is being used for bin size:" << bin_size;
610 }
611 }
612 else
613 {
614 bin_size = m_binSizeModel->delta(m_smallestMz) / m_binSizeDivisor;
615 qDebug() << qSetRealNumberPrecision(6) << "The normally computed bin size:" << bin_size;
616 }
617
618 qDebug() << qSetRealNumberPrecision(6) << "The bin size was computed to be" << bin_size;
619
620 // Only compute the decimal places if they were not configured already.
621 if(m_decimalPlaces == -1)
622 {
623 // qDebug() << "Now checking how many decimal places are needed.";
624
625 // We want as many decimal places as there are 0s between the integral
626 // part of the double and the first non-0 cipher. For example, if
627 // binSize is 0.004, zero decimals is 2 and m_decimalPlaces is set to 3,
628 // because we want decimals up to 4 included.
629
631
632 qDebug() << "With binSize" << bin_size
633 << " m_decimalPlaces was computed to be:" << m_decimalPlaces;
634 }
635
636 qDebug() << "m_decimalPlaces: " << m_decimalPlaces;
637
638 // Now that we have defined the value of m_decimalPlaces, let's use that
639 // value.
640 double first_mz =
642 double last_mz =
644
645 qDebug() << qSetRealNumberPrecision(10)
646 << "After having accounted for the decimals, new min/max values:"
647 << "Very first data point to start from:" << first_mz
648 << "Very last data point to reach: " << last_mz;
649
650 double previous_mz_bin;
651 double current_mz;
652
653 // Store that very first value for later use in the loop.
654 // The bins are nothing more than:
655 //
656 // 1. The first mz (that is the smallest mz value)
657 // 2. A sequence of mz values corresponding to that first mz value
658 // incremented by the bin size calculated each time using the precision
659 // facility (res, ppm, dalton logic).
660
661 bins.push_back(first_mz);
662
663 // The previous_mz_bin cannot be 0, otherwise the m_binSizeModel->delta() will
664 // return 0 and we won't be able to create bins.
665
666 if(!first_mz)
667 {
668 bins.push_back(bin_size);
669 previous_mz_bin = bin_size;
670 }
671 else
672 {
673 previous_mz_bin = first_mz;
674 }
675
676 // qDebug() << qSetRealNumberPrecision(6) << "Pushed back first rounded mz:" << previous_mz_bin;
677
678 // Now continue adding mz values until we have reached the end of the
679 // spectrum, that is the max_mz value, as converted using the decimals to
680 // last_mz.
681
682 // debugCount value used below for debugging purposes.
683 // int debugCount = 0;
684
685 while(previous_mz_bin <= last_mz)
686 {
687 // The size of bin is calculated dynamically using the
688 // precision object that computes it with its specifications:
689 // res or ppm or dalton.
690 current_mz = previous_mz_bin + m_binSizeModel->delta(previous_mz_bin) / m_binSizeDivisor;
691
692 // qDebug() << qSetRealNumberPrecision(6) << "previous mz bin: " << previous_mz_bin
693 // << "and current mz: " << current_mz;
694
695 // Now apply on the obtained mz value the decimals that were either set
696 // or computed earlier.
697
698 double current_rounded_mz = Utils::roundValueToDecimalPlaces(current_mz,
700 /*round_up*/ true);
701
702 // qDebug() << qSetRealNumberPrecision(50)
703 // << "after rounding, current mz becomes: " << current_rounded_mz;
704
705 // If rounding makes the new value identical to the previous one, then
706 // that means that we need to decrease roughness.
707
708 if(current_rounded_mz == previous_mz_bin)
709 {
711
712 qDebug() << "It was required to increment decimal places to" << m_decimalPlaces;
713 current_rounded_mz = Utils::roundValueToDecimalPlaces(current_mz,
715 /*round_up*/ true);
716
717 qDebug().noquote() << "Because current rounded mz is equal to previous mz bin, we had to "
718 "increment decimal places by one while creating the bins "
719 "in MzIntegrationParams::BinningType::ARBITRARY mode.";
720 }
721
722 bins.push_back(current_rounded_mz);
723
724 // qDebug() << qSetRealNumberPrecision(50)
725 // << "Pushed back current rounded mz:" << current_rounded_mz;
726
727
728 // Use the local_mz value for the storage of the previous mz bin.
729 previous_mz_bin = current_rounded_mz;
730 }
731
732 // #if 0
733
734 QString bins_with_delta = binsToStringWithDeltas(bins);
735
736 QString file_name = "/tmp/massSpecArbitraryBins.txt-at-" +
737 QDateTime::currentDateTime().toString("yyyyMMdd-HH-mm-ss");
738
739 qDebug() << "Writing the list of bins setup in the mass spectrum in file " << file_name;
740
741 Q_ASSERT(Utils::writeToFile(bins_with_delta, file_name));
742
743 // #endif
744
745 qDebug() << "Prepared " << bins.size() << "arbitrary bins starting with mz" << bins.front()
746 << "ending with mz" << bins.back();
747
748 return bins;
749}
750
751std::vector<double>
753 pappso::MassSpectrumCstSPtr mass_spectrum_csp)
754{
755 // The mass spectrum passed as parameters has intrinsic bins in it. These bins
756 // are nothing else than the succession of m/z values in it.
757
758 // The very first thing to do is replicate that spectrum into a local copy
759 // of bins (simple double values), and during that step recompute the m/z
760 // value (x value) according to the decimals settings in this instance, if
761 // that is required (that is m_decimalPlaces != -1).
762
763 QList<double> bins;
764 QList<double> deltas;
765 QList<double> resolutions;
766
767 double left_mz_value = 0;
768 double right_mz_value = 0;
769 double mz_delta = 0;
770 double resolution = 0;
771
772 // We need three bins to compute a number of values.
773 if(mass_spectrum_csp->size() < 3)
774 {
775 std::vector<double> bins_vector(bins.constBegin(), bins.constEnd());
776 return bins_vector;
777 }
778
779 // Make sure the spectrum is sorted, as this function takes for granted
780 // that the DataPoint instances are sorted in ascending x (== mz) value
781 // order.
782 pappso::MassSpectrum mass_spectrum_copy = *mass_spectrum_csp;
783 mass_spectrum_copy.sortMz();
784
785 std::vector<pappso::DataPoint>::const_iterator iterator_const =
786 mass_spectrum_copy.cbegin();
787
788 // We need to see the first value
789 left_mz_value = iterator_const->x;
790
791 if(m_decimalPlaces != -1)
792 left_mz_value =
793 Utils::roundValueToDecimalPlaces(left_mz_value, m_decimalPlaces, /*round_up*/ true);
794
795 qDebug() << qSetRealNumberPrecision(6)
796 << "left_mz_value in the template mass spectrum:" << left_mz_value;
797
798 bins.append(left_mz_value);
799 // No delta nor resolution can be computed for the first m/z
800 // because their computation requires to compare the second m/z with
801 // the first m/z, and in turn for all the remaining m/z values.
802 deltas.append(0.0);
803 resolutions.append(0.0);
804
805 // We used the first bin, go to the next!
806 ++iterator_const;
807 while(iterator_const != mass_spectrum_copy.cend())
808 {
809 right_mz_value = iterator_const->x;
810
811 qDebug() << qSetRealNumberPrecision(6)
812 << "right_mz_value:" << right_mz_value;
813
814 if(m_decimalPlaces != -1)
815 right_mz_value =
816 Utils::roundValueToDecimalPlaces(right_mz_value, m_decimalPlaces, /*round_up*/ true);
817
818 bins.append(right_mz_value);
819
820 mz_delta = right_mz_value - left_mz_value;
821 deltas.append(mz_delta);
822 Q_ASSERT(mz_delta != 0.0);
823
824 resolution = right_mz_value / mz_delta;
825 resolutions.append(resolution);
826
827 left_mz_value = right_mz_value;
828 ++iterator_const;
829 }
830
831 // At this point we have the bins that match those in the template mass
832 // spectrum.
833
834 // #if 0
835
836 // We want to check them.
837 std::vector<double> bins_vector(bins.constBegin(), bins.constEnd());
838
839 QString bins_with_delta = binsToStringWithDeltas(bins_vector);
840
841 QString file_name =
842 "/tmp/massSpecDataBasedTemplateBinsWithDeltas.txt-at-" +
843 QDateTime::currentDateTime().toString("yyyyMMdd-HH-mm-ss");
844
845 // qDebug() << "Writing the list of bins setup in the mass spectrum in file "
846 // << file_name;
847
848 Q_ASSERT(Utils::writeToFile(bins_with_delta, file_name));
849
850 // #endif
851
852 // Done, we now have bins and deltas in between each bin and its following
853 // bin.
854
855 // Now, there are specificities:
856 //
857 // If the user has set m_smallestMz and/or m_greatestMz, then we
858 // still have to work to do. That is because the user wants us to keep
859 // the binning as found in the template mass spectrum passed as parameter, but
860 // they also want that the m/z range of the bins be potentially larger, if
861 // either m_smallestMz is smaller than the first bin value or if
862 // m_greatestMz is larger than the last bin value, or BOTH.
863
864 // The difficulty here is that we need to know the logic that has been used
865 // in the first place for the generation of the bins in the template mass
866 // spectrum.
867
868 // Some metrics:
869
870 // We could check if the bins have the same width all along the m/z range.
871
872 // Delta should be constant if the bins were calculated with dalton logic.
873
874 bool uniform_deltas = false;
875 std::size_t deltas_count = deltas.size();
876 // We cannot really take the first delta because it is 0.00000.
877 double first_delta = deltas.at(1);
878 double middle_delta = deltas.at(deltas_count / 2);
879 double last_delta = deltas.at(deltas_count - 1);
880
881 if(first_delta == middle_delta && middle_delta == last_delta)
882 uniform_deltas = true;
883
884 // qDebug() << "Deltas are uniform or not?" << uniform_deltas;
885 // qDebug() << qSetRealNumberPrecision(6) << "First delta:" << first_delta
886 // << "Middle delta:" << middle_delta << "Last delta:" << last_delta;
887
888 // Resolution should be constant if the bins were calculated with resolution
889 // logic. Remember : Res = m/z / Delta m/z.
890 bool uniform_resolutions = false;
891 std::size_t resolutions_count = resolutions.size();
892 // We cannot really take the first resolution because it is 0.00000.
893 double first_resolution = resolutions.at(01);
894 double middle_resolution = resolutions.at(resolutions_count / 2);
895 double last_resolution = resolutions.at(resolutions_count - 1);
896
897 if(first_resolution == middle_resolution &&
898 middle_resolution == last_resolution)
899 uniform_resolutions = true;
900
901 // qDebug() << "Deltas are uniform or not?" << uniform_resolutions;
902 // qDebug() << qSetRealNumberPrecision(6) << "First resolution:" <<
903 // first_resolution
904 // << "Middle resolution:" << middle_resolution << "Last resolution:"
905 // << last_resolution;
906
907 // Now that we have the metric about the bins, we can start checking the other
908 // parameters of the creation of bins:
909 // m_smallestMz vs m_greatestMz
910
911 // In theory, m_smallestMz and m_greatestMz were designed for the arbitrary
912 // binning method, but it is imaginable that one would want to combine to an
913 // existing spectrum a new spectrum that is larger than the existing
914 // one, either on the left or on the right.
915
916 double first_bin_mz = bins.first();
917 double last_bin_mz = bins.last();
918
919 // qDebug() << qSetRealNumberPrecision(6) << "smallest_mz:" << m_smallestMz
920 // << "greatest_mz:" << m_greatestMz;
921
922 // Now we can check if the [m_smallestMz -- m_greatestMz] m/z value range
923 // extends farther on the left or on the right or both compared to the bins
924 // range.
925
926 // Now that we have the real smallest and greatest m/z values that might be
927 // std::max() and std::min() respectively if not set by the user. So all we
928 // have to do is check them against the first and last bin values.
929
930 // The list of prepended bins on the left of the existing bins. By starting
931 // with first_bin_mz, we maintain the phase with the bins in the mass spectrum
932 // passed as parameter.
933
934 if(m_smallestMz < first_bin_mz)
935 {
936 // qDebug() << "A starting m/z value is smaller than the first bin in the
937 // "
938 // "spectrum";
939
940 // We will have to craft bins to the left of first_bin_mz up to
941 // smallest_mz. There are going to be different strategies depending on
942 // what we discovered above. We will prepend the newly created bins to the
943 // bins QList (thanks QList).
944
945 double bin_mz = first_bin_mz;
946
947 if(uniform_deltas)
948 {
949 // That is the simplest situation.
950
951 while(bin_mz > m_smallestMz)
952 {
953 double new_bin_mz = bin_mz - first_delta;
954 bins.prepend(new_bin_mz);
955 bin_mz = new_bin_mz;
956 }
957 }
958 else if(uniform_resolutions)
959 {
960 // Remember: Res = m/z / Delta m/z.
961
962 while(bin_mz > m_smallestMz)
963 {
964 double new_bin_mz = bin_mz - (bin_mz / first_resolution);
965 bins.prepend(new_bin_mz);
966 bin_mz = new_bin_mz;
967 }
968 }
969 }
970
971 // Now make sure we append any necessary bin (same logic as above).
972 // By starting with last_bin_mz, we maintain the phase with the bins in the
973 // mass spectrum passed as parameter.
974
975 if(m_greatestMz > last_bin_mz)
976 {
977 // qDebug() << "A stopping m/z value is greater than the last bin in the "
978 // "spectrum";
979
980 // Craft bins to the right of the bins m/z range, that is append
981 // new bins.
982
983 double bin_mz = last_bin_mz;
984
985 if(uniform_deltas)
986 {
987 // That is the simplest situation.
988
989 while(bin_mz < m_greatestMz)
990 {
991 double new_bin_mz = bin_mz + first_delta;
992 bins.append(new_bin_mz);
993 bin_mz = new_bin_mz;
994 }
995 }
996 else if(uniform_resolutions)
997 {
998 // Remember: Res = m/z / Delta m/z.
999
1000 while(bin_mz > m_greatestMz)
1001 {
1002 double new_bin_mz = bin_mz + (bin_mz / first_resolution);
1003 bins.append(new_bin_mz);
1004 bin_mz = new_bin_mz;
1005 }
1006 }
1007 }
1008
1009 // At this point we should have bins over the full range of m/z values
1010 // required by the user and in phase with those transmitted via the
1011 // mass spectrum argument.
1012
1013 // Convert to std::vector<double> and return.
1014 std::vector<double> full_bins_vector(bins.constBegin(), bins.constEnd());
1015
1016#if 0
1017
1018 bins_with_delta = binsToStringWithDeltas(full_bins_vector);
1019
1020 file_name = "/tmp/massSpecDataBasedFullBinsWithDeltas.txt-at-" +
1021 QDateTime::currentDateTime().toString("yyyyMMdd-HH-mm-ss");
1022
1023 qDebug() << "Writing the list of bins setup in the mass spectrum in file "
1024 << file_name;
1025
1026 Q_ASSERT(Utils::writeToFile(bins_with_delta, file_name));
1027
1028#endif
1029
1030 // qDebug() << "Prepared " << bins.size() << "data-based bins starting with
1031 // mz"
1032 // << bins.front() << "ending with mz" << bins.back();
1033
1034 return full_bins_vector;
1035}
1036
1037std::vector<double>
1039 pappso::MassSpectrumCstSPtr mass_spectrum_csp)
1040{
1041 // The bins must be calculated using those in mass_spectrum_csp as a template.
1042
1043 // qDebug();
1044
1045 std::vector<double> bins;
1046
1047 if(mass_spectrum_csp->size() < 2)
1048 return bins;
1049
1050 // Make sure the spectrum is sorted, as this function takes for granted
1051 // that the DataPoint instances are sorted in ascending x (== mz) value
1052 // order.
1053 pappso::MassSpectrum sorted_mass_spectrum = *mass_spectrum_csp;
1054 sorted_mass_spectrum.sortMz();
1055
1056 double min_mz = m_smallestMz;
1057
1058 // qDebug() << "The min_mz:" << min_mz;
1059
1060 if(m_decimalPlaces != -1)
1061 min_mz = ceil((min_mz * pow(10, m_decimalPlaces)) - 0.49) /
1062 pow(10, m_decimalPlaces);
1063
1064 // Two values for the definition of a MassSpectrumBin.
1065
1066 // The first value of the mz range that defines the bin. This value is part
1067 // of the bin.
1068 double start_mz_in = min_mz;
1069
1070 // The second value of the mz range that defines the bin. This value is
1071 // *not* part of the bin.
1072 double end_mz_out;
1073
1074 std::vector<pappso::DataPoint>::const_iterator it =
1075 sorted_mass_spectrum.begin();
1076
1077 double prev_mz = it->x;
1078
1079 if(m_decimalPlaces != -1)
1080 prev_mz = ceil((prev_mz * pow(10, m_decimalPlaces)) - 0.49) /
1081 pow(10, m_decimalPlaces);
1082
1083 ++it;
1084
1085 while(it != sorted_mass_spectrum.end())
1086 {
1087 double next_mz = it->x;
1088
1089 if(m_decimalPlaces != -1)
1090 next_mz = ceil((next_mz * pow(10, m_decimalPlaces)) - 0.49) /
1091 pow(10, m_decimalPlaces);
1092
1093 double step = next_mz - prev_mz;
1094 end_mz_out = start_mz_in + step;
1095
1096 if(m_decimalPlaces != -1)
1097 end_mz_out = ceil((end_mz_out * pow(10, m_decimalPlaces)) - 0.49) /
1098 pow(10, m_decimalPlaces);
1099
1100 // The data point that is crafted has a 0 y-value. The binning must
1101 // indeed not create artificial intensity data.
1102
1103 // qDebug() << "Pushing back bin:" << start_mz_in << end_mz_out;
1104
1105 bins.push_back(start_mz_in);
1106
1107 // Prepare next bin
1108 start_mz_in = end_mz_out;
1109
1110 // Update prev_mz to be the current one for next iteration.
1111 prev_mz = next_mz;
1112
1113 // Now go to the next DataPoint instance.
1114 ++it;
1115 }
1116
1117#if 0
1118
1119 QString fileName = "/tmp/massSpecDataBasedBins.txt";
1120
1121 qDebug() << "Writing the list of bins setup in the "
1122 "mass spectrum in file "
1123 << fileName;
1124
1125 QFile file(fileName);
1126 file.open(QIODevice::WriteOnly);
1127
1128 QTextStream fileStream(&file);
1129
1130 for(auto &&bin : m_bins)
1131 fileStream << QString("[%1-%2]\n")
1132 .arg(bin.startMzIn, 0, 'f', 10)
1133 .arg(bin.endMzOut, 0, 'f', 10);
1134
1135 fileStream.flush();
1136 file.close();
1137
1138 qDebug() << "elements."
1139 << "starting with mz" << m_bins.front().startMzIn << "ending with mz"
1140 << m_bins.back().endMzOut;
1141
1142#endif
1143
1144 return bins;
1145}
1146
1147// This is for documentation, not for outputting the string used in the
1148// settings that are saved on disk.
1149QString
1150MzIntegrationParams::toString(int offset, const QString &spacer) const
1151{
1152 // The space-containing string that reflects the offset at which
1153 // new text lines should be added to start with.
1154 QString offset_lead;
1155
1156 for(int iter = 0; iter < offset; ++iter)
1157 offset_lead += spacer;
1158
1159 QString text = offset_lead;
1160 text += "m/z integration parameters:\n";
1161
1162 QString new_lead = QString("%1%2").arg(offset_lead, spacer);
1163
1164 text += new_lead;
1165 if(m_smallestMz != std::numeric_limits<double>::max())
1166 text.append(
1167 QString::asprintf("Smallest (first) m/z: %.6f\n", m_smallestMz));
1168
1169 text += new_lead;
1170 if(m_greatestMz != std::numeric_limits<double>::min())
1171 text.append(QString::asprintf("Greatest (last) m/z: %.6f\n", m_greatestMz));
1172
1173 text += new_lead;
1174 text += QString("Remove 0-val data points: %1\n")
1175 .arg(m_removeZeroValDataPoints ? "true" : "false");
1176
1177 text += new_lead;
1178 text.append("Binning logic:\n");
1179
1180 new_lead += spacer;
1181
1182 text += new_lead;
1183 text.append(
1184 QString("Binning type:%1\n").arg(::pappso::binningTypeMap[m_binningType]));
1185
1186 text += new_lead;
1187 text.append(QString("Bin size model: %1\n").arg(m_binSizeModel->toString()));
1188
1189 text += new_lead;
1190 text.append(QString("Bin size divisor: %2\n").arg(m_binSizeDivisor));
1191
1192 text += new_lead;
1193 text.append(QString("Decimal places: %1\n").arg(m_decimalPlaces));
1194
1195 return text;
1196}
1197
1198// This version is used to craft the string that enables the initialization.
1199QString
1201{
1202 QString text;
1203
1204 // In the string for saving settings, we do not ouput the m/z values.
1205 text.append(
1206 QString("Binning type:%1\n").arg(::pappso::binningTypeMap[m_binningType]));
1207 text.append(QString("Bin size model: %1\n").arg(m_binSizeModel->toString()));
1208 text.append(QString("Bin size divisor: %2\n").arg(m_binSizeDivisor));
1209 text.append(QString("Decimal places:%1\n").arg(m_decimalPlaces));
1210 text.append(
1211 QString("Remove 0-val data points:%1\n").arg(m_removeZeroValDataPoints));
1212
1213 // qDebug().noquote() << "Returning text:\n" << text;
1214
1215 return text;
1216}
1217
1218QString
1220 const std::vector<double> bins) const
1221{
1222 QString bins_with_delta;
1223 double previous_bin_value = 0;
1224
1225 for(auto &&value : bins)
1226 {
1227 double delta = value - previous_bin_value;
1228 bins_with_delta += QString("%1 - %2\n")
1229 .arg(value, 0, 'f', m_decimalPlaces)
1230 .arg(delta, 0, 'f', m_decimalPlaces);
1231 previous_bin_value = value;
1232 }
1233
1234 return bins_with_delta;
1235}
1236
1237void
1239{
1240 // qDebug() << "registerJsConstructor for MzIntegrationParams to QJSEngine.";
1241
1242 if(engine == nullptr)
1243 {
1244 qFatal() << "Cannot register class: engine is null";
1245 }
1246
1247 QJSValue pappso_root_property;
1248 QJSValue pappso_enums_property;
1249
1250 if(engine->globalObject().hasProperty("pappso"))
1251 {
1252 qDebug() << "Global object property 'pappso' already exists.";
1253 pappso_root_property = engine->globalObject().property("pappso");
1254
1255 if(pappso_root_property.hasProperty("Enums"))
1256 {
1257 pappso_enums_property = pappso_root_property.property("Enums");
1258 }
1259 else
1260 {
1261 pappso_enums_property = engine->newObject();
1262 pappso_root_property.setProperty("Enums", pappso_enums_property);
1263 }
1264 }
1265 else
1266 {
1267 qDebug() << "Global object property 'pappso' not found.";
1268 pappso_root_property = engine->newObject();
1269 pappso_enums_property = engine->newObject();
1270 pappso_root_property.setProperty("Enums", pappso_enums_property);
1271 engine->globalObject().setProperty("pappso", pappso_root_property);
1272 }
1273
1274 QJSValue enumObject;
1275
1276 // First the BinningType enum
1277 enumObject = engine->newObject();
1278
1279 enumObject.setProperty(
1280 "NONE", static_cast<int>(pappso::MzIntegrationParams::BinningType::NONE));
1281 enumObject.setProperty(
1282 "DATA_BASED",
1284 enumObject.setProperty(
1285 "ARBITRARY",
1287
1288 pappso_enums_property.setProperty("BinningType", enumObject);
1289
1290 // Second the InitializationResult enum
1291 enumObject = engine->newObject();
1292 enumObject.setProperty(
1293 "DEFAULT",
1294 static_cast<int>(
1296 enumObject.setProperty(
1297 "fromSettingsBinSizeModelPartial",
1300 enumObject.setProperty(
1301 "fromSettingsBinSizeModelFull",
1302 static_cast<int>(
1304 enumObject.setProperty(
1305 "fromSettingsFull",
1307
1308 pappso_enums_property.setProperty("InitializationResult", enumObject);
1309
1310 // Finally gegister the class meta object as a constructor for the class
1311 QJSValue jsMetaObject =
1312 engine->newQMetaObject(&MzIntegrationParams::staticMetaObject);
1313 engine->globalObject().setProperty("MzIntegrationParams", jsMetaObject);
1314}
1315
1316} // namespace pappso
Class to represent a mass spectrum.
void sortMz()
Sort the DataPoint instances of this spectrum.
The MzIntegrationParams class provides the parameters definining how m/z integrations must be perform...
Q_INVOKABLE int getDecimalPlaces() const
Q_INVOKABLE MzIntegrationParams(QObject *parent=nullptr)
Q_INVOKABLE void updateGreatestMz(double value)
Q_INVOKABLE int getBinSizeDivisor() const
Q_INVOKABLE void setIndicativeBinSize(double value)
Q_INVOKABLE InitializationResult initialize(const QString &text)
std::vector< double > createDataBasedBinsOld(pappso::MassSpectrumCstSPtr massSpectrum)
Q_INVOKABLE void setBinSizeModel(pappso::PrecisionPtr bin_size_model_p)
Q_INVOKABLE void setBinSizeDivisor(int divisor)
std::vector< double > createArbitraryBins()
Q_INVOKABLE double getIndicativeBinSize() const
Q_INVOKABLE void setGreatestMz(double value)
Q_INVOKABLE void setSmallestMz(double value)
Q_INVOKABLE QString binsToStringWithDeltas(const std::vector< double > bins) const
Q_INVOKABLE bool isRemoveZeroValDataPoints() const
Q_INVOKABLE double getSmallestMz() const
pappso::PrecisionPtr m_binSizeModel
Q_INVOKABLE bool isValid() const
Q_INVOKABLE BinningType getBinningType() const
Q_INVOKABLE pappso::PrecisionPtr getBinSizeModel() const
Q_INVOKABLE bool hasValidMzRange() const
@ DATA_BASED
binning based on mass spectral data
@ ARBITRARY
binning based on arbitrary bin size value
static void registerJsConstructor(QJSEngine *engine)
Q_INVOKABLE void updateSmallestMz(double value)
Q_INVOKABLE MzIntegrationParams * clone(QObject *parent=nullptr) const
Q_INVOKABLE void setMzValues(double smallest, double greatest)
Q_INVOKABLE void setBinningType(BinningType binningType)
Q_INVOKABLE void reset()
Reset the instance to default values.
std::vector< double > createDataBasedBins(pappso::MassSpectrumCstSPtr massSpectrum)
Q_INVOKABLE QString toString() const
Q_INVOKABLE void setDecimalPlaces(int decimal_places)
Q_INVOKABLE std::vector< double > createBins()
Q_INVOKABLE void setRemoveZeroValDataPoints(bool removeOrNot=true)
Q_INVOKABLE double getGreatestMz() const
static PrecisionPtr getResInstance(pappso_double value)
get a resolution precision pointer
static PrecisionPtr fromString(const QString &str)
get a precision pointer from a string
Definition precision.cpp:80
static PrecisionPtr getPpmInstance(pappso_double value)
get a ppm precision pointer
static double roundValueToDecimalPlaces(double value, int decimal_places, bool round_up=true)
Definition utils.cpp:371
static bool writeToFile(const QString &text, const QString &file_name)
Definition utils.cpp:216
static int zeroDecimalsInValue(pappso_double value)
Determine the number of zero decimals between the decimal point and the first non-zero decimal.
Definition utils.cpp:102
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition aa.cpp:39
MzIntegrationParams::BinningType getBinningTypeFromString(const QString &text)
std::map< MzIntegrationParams::BinningType, QString > binningTypeMap
Map relating the BinningType to a textual representation.
std::shared_ptr< const MassSpectrum > MassSpectrumCstSPtr
const PrecisionBase * PrecisionPtr
Definition precision.h:122