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
344// This m/z value is used to create bins phased with another previously
345// performed integration. Note that the lowerAnchor m/z value might be
346// smaller than the m_smallestMz value. See the bins creation function.
347void
349{
350 if(m_lowerAnchorMz == value)
351 return;
352
353 m_lowerAnchorMz = value;
355}
356
357void
359{
360 if(value == m_lowerAnchorMz)
361 return;
362
363 m_lowerAnchorMz = value;
365}
366
367double
372
373// This m/z value is used to create bins phased with another previously
374// performed integration. Note that the upperAnchor m/z value might be
375// greater than the m_greatestMz value. See the bins creation function.
376void
378{
379 if(m_upperAnchorMz == value)
380 return;
381
382 m_upperAnchorMz = value;
384}
385
386void
388{
389 if(value == m_upperAnchorMz)
390 return;
391
392 m_upperAnchorMz = value;
394}
395
396double
401
402void
403MzIntegrationParams::setMzValues(double smallest, double greatest)
404{
405 setSmallestMz(smallest);
406 setGreatestMz(greatest);
407}
408
409void
412{
413 if(m_binningType == binning_type)
414 return;
415
416 m_binningType = binning_type;
417 emit binningTypeChanged();
418}
419
425
426int
431
432void
434{
435 if(m_binSizeModel == bin_size_model_p)
436 return;
437
438 m_binSizeModel = bin_size_model_p;
439
440 if(m_binSizeModel == nullptr)
442
443 emit binSizeModelChanged();
444}
445
451
452void
454{
455 if(m_binSizeDivisor == divisor)
456 return;
457
458 m_binSizeDivisor = divisor;
460}
461
462int
467
468void
470{
471 if(m_decimalPlaces == decimal_places)
472 return;
473
474 m_decimalPlaces = decimal_places;
476}
477
478void
480{
481 if(m_removeZeroValDataPoints == removeOrNot)
482 return;
483
484 m_removeZeroValDataPoints = removeOrNot;
486}
487
488bool
493
494//! Reset the instance to default values.
495void
497{
498 // Each function handles the emission of the corresponding changed
499 // notification.
500 setSmallestMz(std::numeric_limits<double>::max());
501 setGreatestMz(std::numeric_limits<double>::min());
507}
508
509bool
511{
512 int errors = 0;
513
515 {
516 // qDebug() << "m_smallestMz:" << m_smallestMz;
517 // qDebug() << "smallest is max:" << (m_smallestMz ==
518 // std::numeric_limits<double>::max());
519 errors += (m_smallestMz == std::numeric_limits<double>::max() ? 1 : 0);
520
521 // qDebug() << "m_greatestMz:" << m_greatestMz;
522 // qDebug() << "greatest is min:" << (m_greatestMz ==
523 // std::numeric_limits<double>::min());
524 errors += (m_greatestMz == std::numeric_limits<double>::min() ? 1 : 0);
525 }
526
527 if(errors)
528 {
529 qCritical() << "The m/z integration parameters are invalid.";
530 }
531
532 return !errors;
533}
534
535bool
537{
538 return (m_smallestMz < std::numeric_limits<double>::max()) &&
539 (m_greatestMz >= std::numeric_limits<double>::min());
540}
541
542std::vector<double>
544{
545 // qDebug() << "mp_precision:" << mp_precision->toString();
546
547 std::vector<double> bins;
548
550 {
551 // If no binning is to be performed, fine.
552 return bins;
553 }
555 {
556 // Use only data in the MzIntegrationParams member data.
557 return createArbitraryBins();
558 }
560 {
561 qFatal() << "Programming error. "
562 "Please use the createBins(pappso::MassSpectrumCstSPtr "
563 "mass_spectrum_csp) overload.";
564 }
565
566 return bins;
567}
568
569std::vector<double>
571{
572 // qDebug();
573
574 std::vector<double> bins;
575
577 {
578 // If no binning is to be performed, fine.
579 return bins;
580 }
582 {
583 // Use only data in the MzIntegrationParams member data.
584 return createArbitraryBins();
585 }
587 {
588 // qDebug();
589
590 // Use the first spectrum to perform the data-based bins
591
592 return createDataBasedBins(mass_spectrum_csp);
593 }
594
595 return bins;
596}
597
598std::vector<double>
600{
601 // Now starts the tricky stuff. Depending on the binning size model, we need
602 // to take diverse actions.
603
604 if(!isValid())
605 qFatal() << "Programming error. The MzIntegrationParams::BinningLogic is "
606 "not valid, cannot "
607 "create bins.";
608
609 // qDebug() << "Binning logic:" << toString();
610
611 double min_mz = m_smallestMz;
612 double max_mz = m_greatestMz;
613
614 // qDebug() << " Target min_mz:" << min_mz << "and max_mz:" << max_mz;
615
616 double bin_size;
617
618 // We will need to account for the bin size divisor, that is set to 6 as the
619 // default because that is the empirical observation that it gives the most
620 // nicely shaped peaks.
621 Q_ASSERT(m_binSizeDivisor > 0);
622
623 bin_size = m_binSizeModel->delta(min_mz) / m_binSizeDivisor;
624
625 // qDebug() << QString::asprintf(
626 //"binSize is the precision delta for min_mz: %.6f\n", binSize);
627
628 // Only compute the decimal places if they were not configured already.
629 if(m_decimalPlaces == -1)
630 {
631 // qDebug() << "Now checking how many decimal places are needed.";
632
633 // We want as many decimal places as there are 0s between the integral
634 // part of the double and the first non-0 cipher. For example, if
635 // binSize is 0.004, zero decimals is 2 and m_decimalPlaces is set to 3,
636 // because we want decimals up to 4 included.
637
639
640 // qDebug() << "With binSize" << bin_size
641 // << " m_decimalPlaces was computed to be:" << m_decimalPlaces;
642 }
643 // else
644 // qDebug() << "m_decimalPlaces: " << m_decimalPlaces;
645
646 // Now that we have defined the value of m_decimalPlaces, let's use that
647 // value.
648
649 double first_mz = ceil((min_mz * std::pow(10, m_decimalPlaces)) - 0.49) /
650 pow(10, m_decimalPlaces);
651 double last_mz =
652 ceil((max_mz * pow(10, m_decimalPlaces)) - 0.49) / pow(10, m_decimalPlaces);
653
654 // qDebug() << qSetRealNumberPrecision(6)
655 // << "After having accounted for the decimals, new min/max values:"
656 // << "Very first data point:" << first_mz << "Very last data point
657 // to reach:" << last_mz;
658
659 // Instantiate the vector of mz double_s that we'll feed with the bins.
660
661 std::vector<double> bins;
662 double previous_mz_bin;
663 double current_mz;
664
665 // There is a gotcha here: if the user has set the m_lowerAnchorMz value,
666 // then that means that we want to start phasing the bins to that value.
667 if(m_lowerAnchorMz != std::numeric_limits<double>::max())
668 {
669 // qDebug() << qSetRealNumberPrecision(6) << "a lower anchor was set at"
670 // << m_lowerAnchorMz;
671
672 double lower_anchor_mz =
673 ceil((m_lowerAnchorMz * std::pow(10, m_decimalPlaces)) - 0.49) /
674 pow(10, m_decimalPlaces);
675
676 // qDebug() << qSetRealNumberPrecision(6)
677 // << "after decimals accounting, becomes:" << lower_anchor_mz;
678
679 // There are two situations:
680
681 if(lower_anchor_mz <= first_mz)
682 {
683 // qDebug()
684 // << "lower anchor m/z is smaller than min_mz, min_mz becomes
685 // lower_anchor_mz;";
686
687 // 1. If m_lowerAnchorMz is smaller than m_smallestMz, then we need to
688 // use the former value as the start value for the bin calculations.
689 // We do not compute specific lower anchor-related bins, first_mz just
690 // becomes lower_anchor_mz.
691
692 first_mz = lower_anchor_mz;
693 }
694 else
695 {
696 // lower_anchor_mz > first_mz
697
698 // qDebug() << "Special case where we need to prepend bins.";
699
700 // 2. If m_lowerAnchorMz is greater than the m_smallestMz, then we
701 // need to compute in reverse the bins so that the bins are phased
702 // with that m_anchorMz value. Note that at then end of the
703 // computation of these lower anchor-related bins, the normal course
704 // of operation will start at first_mz, which is still
705 // lower_anchor_mz.
706
707 QList<double> prepended_bins;
708
709 previous_mz_bin = lower_anchor_mz;
710
711 while(previous_mz_bin > first_mz)
712 {
713 current_mz =
714 previous_mz_bin -
715 (m_binSizeModel->delta(previous_mz_bin) / m_binSizeDivisor);
716
717 // Now apply on the obtained mz value the decimals that were
718 // either set or computed earlier.
719
720 double current_rounded_mz =
721 ceil((current_mz * pow(10, m_decimalPlaces)) - 0.49) /
722 pow(10, m_decimalPlaces);
723
724 // qDebug() << QString::asprintf(
725 //"current_mz: %.6f and current_rounded_mz: %.6f and
726 // previous_mzBin "
727 //": % .6f\n ",
728 // current_mz,
729 // current_rounded_mz,
730 // previous_mz_bin);
731
732 // If rounding makes the new value identical to the previous one,
733 // then that means that we need to decrease roughness.
734
735 if(current_rounded_mz == previous_mz_bin)
736 {
738
739 current_rounded_mz =
740 ceil((current_mz * pow(10, m_decimalPlaces)) - 0.49) /
741 pow(10, m_decimalPlaces);
742
743 // qDebug().noquote()
744 //<< "Had to increment decimal places by one while creating
745 // the bins " "in MzIntegrationParams::BinningType::ARBITRARY
746 // mode..";
747 }
748
749 // qDebug() << qSetRealNumberPrecision(6) << "Prepending bin"
750 // << current_rounded_mz;
751
752 prepended_bins.prepend(current_rounded_mz);
753
754 // Use the local_mz value for the storage of the previous mz bin.
755 previous_mz_bin = current_rounded_mz;
756 }
757 // End of
758 // while(previous_mz_bin > anchor_mz)
759
760 // Now set the bins to the vector that we'll use later.
761 bins.assign(prepended_bins.begin(), prepended_bins.end());
762
763 // #if 0
764
765 QString bins_with_delta = binsToStringWithDeltas(bins);
766
767 QString file_name =
768 "/tmp/prependedMassSpecBins.txt-at-" +
769 QDateTime::currentDateTime().toString("yyyyMMdd-HH-mm-ss");
770
771 // qDebug() << "Writing the list of prepended bins setup in the "
772 // "mass spectrum in file "
773 // << file_name;
774
775 Q_ASSERT(Utils::writeToFile(bins_with_delta, file_name));
776
777 // #endif
778 }
779 }
780 // End of
781 // if(m_lowerAnchorMz != std::numeric_limits<double>::max())
782
783 // At this point, either we have already set some bins because
784 // anchor_mz was greater than min_mz, or we start fresh.
785
786 // We have the same gotcha as for the lower anchor m/z value, this
787 // time with the upper anchor m/z value:
788
789 if(m_upperAnchorMz != std::numeric_limits<double>::max())
790 {
791 // qDebug() << qSetRealNumberPrecision(6) << "an upper anchor was set at"
792 // << m_upperAnchorMz;
793
794 double upper_anchor_mz =
795 ceil((m_upperAnchorMz * std::pow(10, m_decimalPlaces)) - 0.49) /
796 pow(10, m_decimalPlaces);
797
798 // qDebug() << qSetRealNumberPrecision(6)
799 // << "after decimals accounting, becomes:" << upper_anchor_mz;
800
801 if(upper_anchor_mz > last_mz)
802 {
803 // qDebug() << "since upper anchor m/z is greater than last_mz,
804 // last_mz becomes "
805 // "upper_anchor_mz.";
806
807 last_mz = upper_anchor_mz;
808 }
809 }
810
811 // Store that very first value for later use in the loop.
812 // The bins are nothing more than:
813 //
814 // 1. The first mz (that is the smallest mz value found in all the spectra
815 // 2. A sequence of mz values corresponding to that first mz value
816 // incremented by the bin size.
817
818 // Seed the root of the bin vector with the first mz value rounded above as
819 // requested. that first mz value is actually anchor_mz.
820 previous_mz_bin = first_mz;
821
822 bins.push_back(previous_mz_bin);
823
824 // Now continue adding mz values until we have reached the end of the
825 // spectrum, that is the max_mz value, as converted using the decimals to
826 // last_mz.
827
828 // debugCount value used below for debugging purposes.
829 // int debugCount = 0;
830
831 while(previous_mz_bin <= last_mz)
832 {
833 // qDebug() << "Now starting the bin creation loop.";
834
835 // Calculate dynamically the precision delta according to the current mz
836 // value.
837
838 // double precision_delta = mp_precision->delta(previous_mz_bin);
839 // qDebug() << "precision_delta: " << precision_delta;
840
841 // In certain circumstances, the bin size is not enough to properly render
842 // hyper-high resolution data (like the theoretical isotopic cluster data
843 // generated in silico). In that case, the bin size, computed using the
844 // precision object, is divided by the m_binSizeDivisor, which normally is
845 // set to 6 as the default because that is the empirical observation that
846 // it gives the most nicely shaped peaks.
847
848 current_mz = previous_mz_bin +
849 (m_binSizeModel->delta(previous_mz_bin) / m_binSizeDivisor);
850
851 // qDebug() << QString::asprintf(
852 //"previous_mzBin: %.6f and current_mz: %.6f\n",
853 // previous_mz_bin,
854 // current_mz);
855
856 // Now apply on the obtained mz value the decimals that were either set
857 // or computed earlier.
858
859 double current_rounded_mz =
860 ceil((current_mz * pow(10, m_decimalPlaces)) - 0.49) /
861 pow(10, m_decimalPlaces);
862
863 // qDebug() << QString::asprintf(
864 //"current_mz: %.6f and current_rounded_mz: %.6f and previous_mzBin "
865 //": % .6f\n ",
866 // current_mz,
867 // current_rounded_mz,
868 // previous_mz_bin);
869
870 // If rounding makes the new value identical to the previous one, then
871 // that means that we need to decrease roughness.
872
873 if(current_rounded_mz == previous_mz_bin)
874 {
876
877 current_rounded_mz =
878 ceil((current_mz * pow(10, m_decimalPlaces)) - 0.49) /
879 pow(10, m_decimalPlaces);
880
881 // qDebug().noquote()
882 //<< "Had to increment decimal places by one while creating the bins "
883 //"in MzIntegrationParams::BinningType::ARBITRARY mode..";
884 }
885
886 bins.push_back(current_rounded_mz);
887
888 // Use the local_mz value for the storage of the previous mz bin.
889 previous_mz_bin = current_rounded_mz;
890 }
891
892#if 0
893
894 QString bins_with_delta = binsToStringWithDelta(bins);
895
896 QString file_name = "/tmp/massSpecArbitraryBins.txt-at-" +
897 QDateTime::currentDateTime().toString("yyyyMMdd-HH-mm-ss");
898
899 qDebug() << "Writing the list of bins setup in the mass spectrum in file " << file_name;
900
901 Q_ASSERT(Utils::writeToFile(bins_with_delta, file_name));
902
903#endif
904
905 // qDebug() << "Prepared " << bins.size() << "arbitrary bins starting with mz"
906 // << bins.front()
907 // << "ending with mz" << bins.back();
908
909 return bins;
910}
911
912std::vector<double>
914 pappso::MassSpectrumCstSPtr mass_spectrum_csp)
915{
916 // The mass spectrum passed as parameters has intrinsic bins in it. These bins
917 // are nothing else than the succession of m/z values in it.
918
919 // The very first thing to do is replicate that spectrum into a local copy
920 // of bins (simple double values), and during that step recompute the m/z
921 // value (x value) according to the decimals settings in this instance, if
922 // that is required (that is m_decimalPlaces != -1).
923
924 QList<double> bins;
925 QList<double> deltas;
926 QList<double> resolutions;
927
928 double left_mz_value = 0;
929 double right_mz_value = 0;
930 double mz_delta = 0;
931 double resolution = 0;
932
933 // We need three bins to compute a number of values.
934 if(mass_spectrum_csp->size() < 3)
935 {
936 std::vector<double> bins_vector(bins.constBegin(), bins.constEnd());
937 return bins_vector;
938 }
939
940 // Make sure the spectrum is sorted, as this function takes for granted
941 // that the DataPoint instances are sorted in ascending x (== mz) value
942 // order.
943 pappso::MassSpectrum mass_spectrum_copy = *mass_spectrum_csp;
944 mass_spectrum_copy.sortMz();
945
946 std::vector<pappso::DataPoint>::const_iterator iterator_const =
947 mass_spectrum_copy.cbegin();
948
949 // We need to see the first value
950 left_mz_value = iterator_const->x;
951 if(m_decimalPlaces != -1)
952 left_mz_value = ceil((left_mz_value * pow(10, m_decimalPlaces)) - 0.49) /
953 pow(10, m_decimalPlaces);
954
955 qDebug() << qSetRealNumberPrecision(6)
956 << "left_mz_value in the template mass spectrum:" << left_mz_value;
957
958 bins.append(left_mz_value);
959 // No delta nor resolution can be computed for the first m/z
960 // because their computation requires to compare the second m/z with
961 // the first m/z, and in turn for all the remaining m/z values.
962 deltas.append(0.0);
963 resolutions.append(0.0);
964
965 // We used the first bin, go to the next!
966 ++iterator_const;
967 while(iterator_const != mass_spectrum_copy.cend())
968 {
969 right_mz_value = iterator_const->x;
970
971 qDebug() << qSetRealNumberPrecision(6)
972 << "right_mz_value:" << right_mz_value;
973
974 if(m_decimalPlaces != -1)
975 right_mz_value =
976 ceil((right_mz_value * pow(10, m_decimalPlaces)) - 0.49) /
977 pow(10, m_decimalPlaces);
978
979 bins.append(right_mz_value);
980
981 mz_delta = right_mz_value - left_mz_value;
982 deltas.append(mz_delta);
983 Q_ASSERT(mz_delta != 0.0);
984
985 resolution = right_mz_value / mz_delta;
986 resolutions.append(resolution);
987
988 left_mz_value = right_mz_value;
989 ++iterator_const;
990 }
991
992 // At this point we have the bins that match those in the template mass
993 // spectrum.
994
995 // #if 0
996
997 // We want to check them.
998 std::vector<double> bins_vector(bins.constBegin(), bins.constEnd());
999
1000 QString bins_with_delta = binsToStringWithDeltas(bins_vector);
1001
1002 QString file_name =
1003 "/tmp/massSpecDataBasedTemplateBinsWithDeltas.txt-at-" +
1004 QDateTime::currentDateTime().toString("yyyyMMdd-HH-mm-ss");
1005
1006 // qDebug() << "Writing the list of bins setup in the mass spectrum in file "
1007 // << file_name;
1008
1009 Q_ASSERT(Utils::writeToFile(bins_with_delta, file_name));
1010
1011 // #endif
1012
1013 // Done, we now have bins and deltas in between each bin and its following
1014 // bin.
1015
1016 // Now, there are specificities:
1017 //
1018 // If the user has set m_lowerAnchorMz and/or m_upperAnchorMz, then we
1019 // still have to work to do. That is because the user wants us to keep
1020 // the binning as found in the template mass spectrum passed as parameter, but
1021 // they also want that the m/z range of the bins be potentially larger, if
1022 // either m_lowerAnchorMz is smaller than the first bin value or if
1023 // m_upperAnchorMz is larger than the last bin value, or BOTH.
1024
1025 // The difficulty here is that we need to know the logic that has been used
1026 // in the first place for the generation of the bins in the template mass
1027 // spectrum.
1028
1029 // Some metrics:
1030
1031 // We could check if the bins have the same width all along the m/z range.
1032
1033 // Delta should be constant if the bins were calculated with dalton logic.
1034
1035 bool uniform_deltas = false;
1036 std::size_t deltas_count = deltas.size();
1037 // We cannot really take the first delta because it is 0.00000.
1038 double first_delta = deltas.at(1);
1039 double middle_delta = deltas.at(deltas_count / 2);
1040 double last_delta = deltas.at(deltas_count - 1);
1041
1042 if(first_delta == middle_delta && middle_delta == last_delta)
1043 uniform_deltas = true;
1044
1045 // qDebug() << "Deltas are uniform or not?" << uniform_deltas;
1046 // qDebug() << qSetRealNumberPrecision(6) << "First delta:" << first_delta
1047 // << "Middle delta:" << middle_delta << "Last delta:" << last_delta;
1048
1049 // Resolution should be constant if the bins were calculated with resolution
1050 // logic. Remember : Res = m/z / Delta m/z.
1051 bool uniform_resolutions = false;
1052 std::size_t resolutions_count = resolutions.size();
1053 // We cannot really take the first resolution because it is 0.00000.
1054 double first_resolution = resolutions.at(01);
1055 double middle_resolution = resolutions.at(resolutions_count / 2);
1056 double last_resolution = resolutions.at(resolutions_count - 1);
1057
1058 if(first_resolution == middle_resolution &&
1059 middle_resolution == last_resolution)
1060 uniform_resolutions = true;
1061
1062 // qDebug() << "Deltas are uniform or not?" << uniform_resolutions;
1063 // qDebug() << qSetRealNumberPrecision(6) << "First resolution:" <<
1064 // first_resolution
1065 // << "Middle resolution:" << middle_resolution << "Last resolution:"
1066 // << last_resolution;
1067
1068 // Now that we have the metric about the bins, we can start checking the other
1069 // parameters of the creation of bins:
1070 // There are two pairs of values in this instance:
1071 // m_smallestMz vs m_greatestMz
1072 // and
1073 // m_lowerAnchorMz vs m_upperAnchorMz
1074
1075 // Which one of these pairs should we account for, or both ?
1076
1077 // In theory, m_smallestMz and m_greatestMz were designed for the arbitrary
1078 // binning method, but it is imaginable that one would want to combine to and
1079 // existing spectrum a new spectrum that is larger than the existing
1080 // one, either on the left or on the right.
1081
1082 // Then, there is the other pair: m_lowerAnchorMz vs m_upperAnchorMz. In the
1083 // end what could be done is to take the smallest of all the values for the
1084 // left end of the integration bins and the greatest for the right end of the
1085 // integration bins.
1086
1087 double first_bin_mz = bins.first();
1088 double last_bin_mz = bins.last();
1089
1090 double smallest_mz = std::min(m_smallestMz, m_lowerAnchorMz);
1091 double greatest_mz = std::max(m_greatestMz, m_upperAnchorMz);
1092
1093 // qDebug() << qSetRealNumberPrecision(6) << "smallest_mz:" << smallest_mz
1094 // << "greatest_mz:" << greatest_mz;
1095
1096 // Now we can check if the [smallest_mz -- greatest_mz] m/z value range
1097 // extends farther on the left or on the right or both compared to the bins
1098 // range.
1099
1100 // Now that we have the real smallest and greatest m/z values that might be
1101 // std::max() and std::min() respectively if not set by the user. So all we
1102 // have to do is check them against the first and last bin values.
1103
1104 // The list of prepended bins on the left of the existing bins. By starting
1105 // with first_bin_mz, we maintain the phase with the bins in the mass spectrum
1106 // passed as parameter.
1107
1108 if(smallest_mz < first_bin_mz)
1109 {
1110 // qDebug() << "A starting m/z value is smaller than the first bin in the
1111 // "
1112 // "spectrum";
1113
1114 // We will have to craft bins to the left of first_bin_mz up to
1115 // smallest_mz. There are going to be different strategies depending on
1116 // what we discovered above. We will prepend the newly created bins to the
1117 // bins QList (thanks QList).
1118
1119 double bin_mz = first_bin_mz;
1120
1121 if(uniform_deltas)
1122 {
1123 // That is the simplest situation.
1124
1125 while(bin_mz > smallest_mz)
1126 {
1127 double new_bin_mz = bin_mz - first_delta;
1128 bins.prepend(new_bin_mz);
1129 bin_mz = new_bin_mz;
1130 }
1131 }
1132 else if(uniform_resolutions)
1133 {
1134 // Remember: Res = m/z / Delta m/z.
1135
1136 while(bin_mz > smallest_mz)
1137 {
1138 double new_bin_mz = bin_mz - (bin_mz / first_resolution);
1139 bins.prepend(new_bin_mz);
1140 bin_mz = new_bin_mz;
1141 }
1142 }
1143 }
1144
1145 // Now make sure we append any necessary bin (same logic as above).
1146 // By starting with last_bin_mz, we maintain the phase with the bins in the
1147 // mass spectrum passed as parameter.
1148
1149 if(greatest_mz > last_bin_mz)
1150 {
1151 // qDebug() << "A stopping m/z value is greater than the last bin in the "
1152 // "spectrum";
1153
1154 // Craft bins to the right of the bins m/z range, that is append
1155 // new bins.
1156
1157 double bin_mz = last_bin_mz;
1158
1159 if(uniform_deltas)
1160 {
1161 // That is the simplest situation.
1162
1163 while(bin_mz < greatest_mz)
1164 {
1165 double new_bin_mz = bin_mz + first_delta;
1166 bins.append(new_bin_mz);
1167 bin_mz = new_bin_mz;
1168 }
1169 }
1170 else if(uniform_resolutions)
1171 {
1172 // Remember: Res = m/z / Delta m/z.
1173
1174 while(bin_mz > smallest_mz)
1175 {
1176 double new_bin_mz = bin_mz + (bin_mz / first_resolution);
1177 bins.append(new_bin_mz);
1178 bin_mz = new_bin_mz;
1179 }
1180 }
1181 }
1182
1183 // At this point we should have bins over the full range of m/z values
1184 // required by the user and in phase with those transmitted via the
1185 // mass spectrum argument.
1186
1187 // Convert to std::vector<double> and return.
1188 std::vector<double> full_bins_vector(bins.constBegin(), bins.constEnd());
1189
1190#if 0
1191
1192 bins_with_delta = binsToStringWithDeltas(full_bins_vector);
1193
1194 file_name = "/tmp/massSpecDataBasedFullBinsWithDeltas.txt-at-" +
1195 QDateTime::currentDateTime().toString("yyyyMMdd-HH-mm-ss");
1196
1197 qDebug() << "Writing the list of bins setup in the mass spectrum in file "
1198 << file_name;
1199
1200 Q_ASSERT(Utils::writeToFile(bins_with_delta, file_name));
1201
1202#endif
1203
1204 // qDebug() << "Prepared " << bins.size() << "data-based bins starting with
1205 // mz"
1206 // << bins.front() << "ending with mz" << bins.back();
1207
1208 return full_bins_vector;
1209}
1210
1211std::vector<double>
1213 pappso::MassSpectrumCstSPtr mass_spectrum_csp)
1214{
1215 // The bins must be calculated using those in mass_spectrum_csp as a template.
1216
1217 // qDebug();
1218
1219 std::vector<double> bins;
1220
1221 if(mass_spectrum_csp->size() < 2)
1222 return bins;
1223
1224 // Make sure the spectrum is sorted, as this function takes for granted
1225 // that the DataPoint instances are sorted in ascending x (== mz) value
1226 // order.
1227 pappso::MassSpectrum sorted_mass_spectrum = *mass_spectrum_csp;
1228 sorted_mass_spectrum.sortMz();
1229
1230 double min_mz = m_smallestMz;
1231
1232 // qDebug() << "The min_mz:" << min_mz;
1233
1234 if(m_decimalPlaces != -1)
1235 min_mz = ceil((min_mz * pow(10, m_decimalPlaces)) - 0.49) /
1236 pow(10, m_decimalPlaces);
1237
1238 // Two values for the definition of a MassSpectrumBin.
1239
1240 // The first value of the mz range that defines the bin. This value is part
1241 // of the bin.
1242 double start_mz_in = min_mz;
1243
1244 // The second value of the mz range that defines the bin. This value is
1245 // *not* part of the bin.
1246 double end_mz_out;
1247
1248 std::vector<pappso::DataPoint>::const_iterator it =
1249 sorted_mass_spectrum.begin();
1250
1251 double prev_mz = it->x;
1252
1253 if(m_decimalPlaces != -1)
1254 prev_mz = ceil((prev_mz * pow(10, m_decimalPlaces)) - 0.49) /
1255 pow(10, m_decimalPlaces);
1256
1257 ++it;
1258
1259 while(it != sorted_mass_spectrum.end())
1260 {
1261 double next_mz = it->x;
1262
1263 if(m_decimalPlaces != -1)
1264 next_mz = ceil((next_mz * pow(10, m_decimalPlaces)) - 0.49) /
1265 pow(10, m_decimalPlaces);
1266
1267 double step = next_mz - prev_mz;
1268 end_mz_out = start_mz_in + step;
1269
1270 if(m_decimalPlaces != -1)
1271 end_mz_out = ceil((end_mz_out * pow(10, m_decimalPlaces)) - 0.49) /
1272 pow(10, m_decimalPlaces);
1273
1274 // The data point that is crafted has a 0 y-value. The binning must
1275 // indeed not create artificial intensity data.
1276
1277 // qDebug() << "Pushing back bin:" << start_mz_in << end_mz_out;
1278
1279 bins.push_back(start_mz_in);
1280
1281 // Prepare next bin
1282 start_mz_in = end_mz_out;
1283
1284 // Update prev_mz to be the current one for next iteration.
1285 prev_mz = next_mz;
1286
1287 // Now go to the next DataPoint instance.
1288 ++it;
1289 }
1290
1291#if 0
1292
1293 QString fileName = "/tmp/massSpecDataBasedBins.txt";
1294
1295 qDebug() << "Writing the list of bins setup in the "
1296 "mass spectrum in file "
1297 << fileName;
1298
1299 QFile file(fileName);
1300 file.open(QIODevice::WriteOnly);
1301
1302 QTextStream fileStream(&file);
1303
1304 for(auto &&bin : m_bins)
1305 fileStream << QString("[%1-%2]\n")
1306 .arg(bin.startMzIn, 0, 'f', 10)
1307 .arg(bin.endMzOut, 0, 'f', 10);
1308
1309 fileStream.flush();
1310 file.close();
1311
1312 qDebug() << "elements."
1313 << "starting with mz" << m_bins.front().startMzIn << "ending with mz"
1314 << m_bins.back().endMzOut;
1315
1316#endif
1317
1318 return bins;
1319}
1320
1321// This is for documentation, not for outputting the string used in the
1322// settings that are saved on disk.
1323QString
1324MzIntegrationParams::toString(int offset, const QString &spacer) const
1325{
1326 // The space-containing string that reflects the offset at which
1327 // new text lines should be added to start with.
1328 QString offset_lead;
1329
1330 for(int iter = 0; iter < offset; ++iter)
1331 offset_lead += spacer;
1332
1333 QString text = offset_lead;
1334 text += "m/z integration parameters:\n";
1335
1336 QString new_lead = QString("%1%2").arg(offset_lead, spacer);
1337
1338 text += new_lead;
1339 if(m_smallestMz != std::numeric_limits<double>::max())
1340 text.append(
1341 QString::asprintf("Smallest (first) m/z: %.6f\n", m_smallestMz));
1342
1343 text += new_lead;
1344 if(m_greatestMz != std::numeric_limits<double>::min())
1345 text.append(QString::asprintf("Greatest (last) m/z: %.6f\n", m_greatestMz));
1346
1347 text += new_lead;
1348 if(m_lowerAnchorMz != std::numeric_limits<double>::max())
1349 text.append(QString::asprintf("Lower anchor m/z: %.6f\n", m_lowerAnchorMz));
1350
1351 text += new_lead;
1352 if(m_upperAnchorMz != std::numeric_limits<double>::max())
1353 text.append(QString::asprintf("Upper anchor m/z: %.6f\n", m_upperAnchorMz));
1354
1355 text += new_lead;
1356 text += QString("Remove 0-val data points: %1\n")
1357 .arg(m_removeZeroValDataPoints ? "true" : "false");
1358
1359 text += new_lead;
1360 text.append("Binning logic:\n");
1361
1362 new_lead += spacer;
1363
1364 text += new_lead;
1365 text.append(
1366 QString("Binning type:%1\n").arg(::pappso::binningTypeMap[m_binningType]));
1367
1368 text += new_lead;
1369 text.append(QString("Bin size model: %1\n").arg(m_binSizeModel->toString()));
1370
1371 text += new_lead;
1372 text.append(QString("Bin size divisor: %2\n").arg(m_binSizeDivisor));
1373
1374 text += new_lead;
1375 text.append(QString("Decimal places: %1\n").arg(m_decimalPlaces));
1376
1377 return text;
1378}
1379
1380// This version is used to craft the string that enables the initialization.
1381QString
1383{
1384 QString text;
1385
1386 // In the string for saving settings, we do not ouput the m/z values.
1387 text.append(
1388 QString("Binning type:%1\n").arg(::pappso::binningTypeMap[m_binningType]));
1389 text.append(QString("Bin size model: %1\n").arg(m_binSizeModel->toString()));
1390 text.append(QString("Bin size divisor: %2\n").arg(m_binSizeDivisor));
1391 text.append(QString("Decimal places:%1\n").arg(m_decimalPlaces));
1392 text.append(
1393 QString("Remove 0-val data points:%1\n").arg(m_removeZeroValDataPoints));
1394
1395 // qDebug().noquote() << "Returning text:\n" << text;
1396
1397 return text;
1398}
1399
1400QString
1402 const std::vector<double> bins) const
1403{
1404 QString bins_with_delta;
1405 double previous_bin_value = 0;
1406
1407 for(auto &&value : bins)
1408 {
1409 double delta = value - previous_bin_value;
1410 bins_with_delta += QString("%1 - %2\n")
1411 .arg(value, 0, 'f', m_decimalPlaces)
1412 .arg(delta, 0, 'f', m_decimalPlaces);
1413 previous_bin_value = value;
1414 }
1415
1416 return bins_with_delta;
1417}
1418
1419void
1421{
1422 // qDebug() << "registerJsConstructor for MzIntegrationParams to QJSEngine.";
1423
1424 if(engine == nullptr)
1425 {
1426 qFatal() << "Cannot register class: engine is null";
1427 }
1428
1429 QJSValue pappso_root_property;
1430 QJSValue pappso_enums_property;
1431
1432 if(engine->globalObject().hasProperty("pappso"))
1433 {
1434 qDebug() << "Global object property 'pappso' already exists.";
1435 pappso_root_property = engine->globalObject().property("pappso");
1436
1437 if(pappso_root_property.hasProperty("Enums"))
1438 {
1439 pappso_enums_property = pappso_root_property.property("Enums");
1440 }
1441 else
1442 {
1443 pappso_enums_property = engine->newObject();
1444 pappso_root_property.setProperty("Enums", pappso_enums_property);
1445 }
1446 }
1447 else
1448 {
1449 qDebug() << "Global object property 'pappso' not found.";
1450 pappso_root_property = engine->newObject();
1451 pappso_enums_property = engine->newObject();
1452 pappso_root_property.setProperty("Enums", pappso_enums_property);
1453 engine->globalObject().setProperty("pappso", pappso_root_property);
1454 }
1455
1456 QJSValue enumObject;
1457
1458 // First the BinningType enum
1459 enumObject = engine->newObject();
1460
1461 enumObject.setProperty(
1462 "NONE", static_cast<int>(pappso::MzIntegrationParams::BinningType::NONE));
1463 enumObject.setProperty(
1464 "DATA_BASED",
1466 enumObject.setProperty(
1467 "ARBITRARY",
1469
1470 pappso_enums_property.setProperty("BinningType", enumObject);
1471
1472 // Second the InitializationResult enum
1473 enumObject = engine->newObject();
1474 enumObject.setProperty(
1475 "DEFAULT",
1476 static_cast<int>(
1478 enumObject.setProperty(
1479 "fromSettingsBinSizeModelPartial",
1482 enumObject.setProperty(
1483 "fromSettingsBinSizeModelFull",
1484 static_cast<int>(
1486 enumObject.setProperty(
1487 "fromSettingsFull",
1489
1490 pappso_enums_property.setProperty("InitializationResult", enumObject);
1491
1492 // Finally gegister the class meta object as a constructor for the class
1493 QJSValue jsMetaObject =
1494 engine->newQMetaObject(&MzIntegrationParams::staticMetaObject);
1495 engine->globalObject().setProperty("MzIntegrationParams", jsMetaObject);
1496}
1497
1498} // 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 InitializationResult initialize(const QString &text)
std::vector< double > createDataBasedBinsOld(pappso::MassSpectrumCstSPtr massSpectrum)
Q_INVOKABLE double getLowerAnchorMz() const
Q_INVOKABLE void setBinSizeModel(pappso::PrecisionPtr bin_size_model_p)
Q_INVOKABLE void setBinSizeDivisor(int divisor)
std::vector< double > createArbitraryBins()
Q_INVOKABLE double getUpperAnchorMz() 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 void updateLowerAnchorMz(double value)
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 void setUpperAnchorMz(double value)
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 void updateUpperAnchorMz(double value)
Q_INVOKABLE double getGreatestMz() const
Q_INVOKABLE void setLowerAnchorMz(double value)
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 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