GCC Code Coverage Report


source/XpertMassCore/src/
File: source/XpertMassCore/src/Oligomer.cpp
Date: 2025-11-20 01:41:33
Lines:
172/315
54.6%
Functions:
36/56
64.3%
Branches:
73/212
34.4%

Line Branch Exec Source
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
34 /////////////////////// Local includes
35 #include "MsXpS/libXpertMassCore/Oligomer.hpp"
36 #include "MsXpS/libXpertMassCore/Polymer.hpp"
37 #include "MsXpS/libXpertMassCore/Ionizer.hpp"
38
39 namespace MsXpS
40 {
41 namespace libXpertMassCore
42 {
43
44
45 /*!
46 \class MsXpS::libXpertMassCore::Oligomer
47 \inmodule libXpertMassCore
48 \ingroup PolChemDefBuildingdBlocks
49 \inheaderfile Oligomer.hpp
50
51 \brief The Oligomer class provides abstractions to work with
52 an oligomer molecule (peptide or oligonucleotide, for example).
53
54 The notion of an \l Oligomer is that it is part of a \l Polymer and is thus
55 defined by a range of \l Monomer indices in this \l Polymer (the \l
56 IndexRangeCollection).
57
58 The start index cannot be less than 0 nor greater than the size of the polymer
59 minus one, and the end index follows the same rule.
60
61 An \l Oligomer is also characterized by a monoisotopic
62 mass and an average mass.
63
64 The ionization of the Oligomer is handled by the member \l Ionizer instance.
65
66 All computations about an oligomer (fragmentation, composition, for
67 example, isoelectric point, ...) can only be performed by referring
68 to the sequence of its "enclosing" \l Polymer.
69 */
70
71 /*!
72 \variable MsXpS::libXpertMassCore::Oligomer::mcsp_polymer
73
74 \brief The \l Polymer instance of which this Oligomer is part.
75 */
76
77 /*!
78 \variable MsXpS::libXpertMassCore::Oligomer::m_name
79
80 \brief The name of the Oligomer.
81 */
82
83 /*!
84 \variable MsXpS::libXpertMassCore::Oligomer::m_description
85
86 \brief The description of the Oligomer.
87 */
88
89 /*!
90 \variable MsXpS::libXpertMassCore::Oligomer::m_isModified
91
92 \brief Tells if the Oligomer is modified.
93 */
94
95 /*!
96 \variable MsXpS::libXpertMassCore::Oligomer::m_ionizer
97
98 \brief The Ionizer instance that drives the ionization of the Oligomer.
99 */
100
101 /*!
102 \variable MsXpS::libXpertMassCore::Oligomer::m_calcOptions
103
104 \brief The CalcOptions instance that drives the mass and formula calculations.
105 */
106
107 /*!
108 \variable MsXpS::libXpertMassCore::Oligomer::m_mono
109
110 \brief The monoisotopic mass.
111 */
112
113 /*!
114 \variable MsXpS::libXpertMassCore::Oligomer::m_avg
115
116 \brief The average mass.
117 */
118
119 /*!
120 \variable MsXpS::libXpertMassCore::Oligomer::m_crossLinks
121
122 \brief The container of CrossLink instances formed in the Oligomer sequence.
123 */
124
125 /*!
126 \variable MsXpS::libXpertMassCore::Oligomer::m_partialCleavage
127
128 \brief Stores the partial cleavage out of which the Oligomer has been obtained
129 if that process was a (bio)-chemical cleavage.
130 */
131
132 /*!
133 \variable MsXpS::libXpertMassCore::Oligomer::m_formula
134
135 \brief Stores the formula of the Oligomer.
136 */
137
138
139 /*!
140 \typedef MsXpS::libXpertMassCore::OligomerSPtr
141 \relates Oligomer
142
143 Synonym for std::shared_ptr<Oligomer>.
144 */
145
146 /*!
147 \typedef MsXpS::libXpertMassCore::OligomerCstSPtr
148 \relates Oligomer
149
150 Synonym for std::shared_ptr<const Oligomer>.
151 */
152
153 /*!
154 \brief Constructs an Oligomer.
155
156 The Oligomer instance is constructed with these arguments:
157
158 \list
159
160 \li \a polymer_cqsp The \l Polymer instance that encloses this Oligomer.
161
162 \li \a name The name of this Oligomer, used to intialize the Ionizable base
163 class
164
165 \li \a description The description of this Oligomer (m_description)
166
167 \li \a is_modified Tells if the Oligomer is modified
168
169 \li \a ionizer The Ionizer used to ionize this Oligomer
170
171 \li \a calc_options Used to initialize the m_calcOptions member
172
173 \endlist
174 */
175 736 Oligomer::Oligomer(PolymerCstQSPtr polymer_cqsp,
176 const QString &name,
177 const QString &description,
178 bool is_modified,
179 const Ionizer &ionizer,
180 736 const CalcOptions &calc_options)
181 1472 : mcsp_polymer(polymer_cqsp),
182
1/2
✓ Branch 0 taken 736 times.
✗ Branch 1 not taken.
736 m_name(name),
183
1/2
✓ Branch 0 taken 736 times.
✗ Branch 1 not taken.
736 m_description(description),
184 736 m_isModified(is_modified),
185
1/2
✓ Branch 1 taken 736 times.
✗ Branch 2 not taken.
736 m_ionizer(ionizer),
186
3/6
✓ Branch 1 taken 736 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 736 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 736 times.
✗ Branch 8 not taken.
1472 m_calcOptions(calc_options)
187 {
188 736 qDebug() << "Allocating new Oligomer with other calc options:"
189 << calc_options.toString()
190 << "and this calc options:" << m_calcOptions.toString();
191 736 }
192
193 /*!
194 \brief Constructs the Oligomer as a copy of \a other.
195 */
196 1909 Oligomer::Oligomer(const Oligomer &other)
197 : PropListHolder(other),
198 3818 mcsp_polymer(other.mcsp_polymer),
199
1/2
✓ Branch 0 taken 1909 times.
✗ Branch 1 not taken.
1909 m_name(other.m_name),
200
1/2
✓ Branch 0 taken 1909 times.
✗ Branch 1 not taken.
1909 m_description(other.m_description),
201 1909 m_isModified(other.m_isModified),
202
1/2
✓ Branch 1 taken 1909 times.
✗ Branch 2 not taken.
1909 m_ionizer(other.m_ionizer),
203
1/2
✓ Branch 1 taken 1909 times.
✗ Branch 2 not taken.
1909 m_calcOptions(other.m_calcOptions),
204 1909 m_mono(other.m_mono),
205 1909 m_avg(other.m_avg),
206
1/2
✓ Branch 1 taken 1909 times.
✗ Branch 2 not taken.
1909 m_crossLinks(other.m_crossLinks),
207 1909 m_partialCleavage(other.m_partialCleavage),
208
2/4
✓ Branch 1 taken 1909 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1909 times.
✗ Branch 5 not taken.
3818 m_formula(other.m_formula)
209 {
210 1909 }
211
212 /*!
213 \brief Destructs this Oligomer.
214 */
215 5290 Oligomer::~Oligomer()
216 {
217 // qDebug() << "~Oligomer()";
218 5290 }
219
220 //////////////// THE POLYMER /////////////////////
221 /*!
222 \brief Set the \l{PolChemDef} \l{mcsp_polymer} member to \a polymer_cqsp.
223
224 \sa getPolymerCstSPtr()
225 */
226 void
227 Oligomer::setPolymerCstSPtr(PolymerCstQSPtr polymer_cqsp)
228 {
229 mcsp_polymer = polymer_cqsp;
230 }
231
232 /*!
233 \brief Returns the polymer.
234
235 \sa getPolymerCstSPtr()
236 */
237 const PolymerCstQSPtr &
238 15 Oligomer::getPolymerCstSPtr() const
239 {
240 15 return mcsp_polymer;
241 }
242
243 //////////////// THE NAME /////////////////////
244
245 /*!
246 \brief Sets the \a name.
247 */
248 void
249 1941 Oligomer::setName(const QString &name)
250 {
251 1941 m_name = name;
252 1941 }
253
254 /*!
255 \brief Returns the name.
256 */
257 QString
258 1674 Oligomer::getName() const
259 {
260
1/2
✓ Branch 0 taken 1674 times.
✗ Branch 1 not taken.
1674 return m_name;
261 }
262
263 //////////////// THE DESCRIPTION /////////////////////
264
265 /*!
266 \brief Sets the \a description.
267 */
268 void
269 Oligomer::setDescription(const QString &description)
270 {
271 m_description = description;
272 }
273
274 /*!
275 \brief Returns the description.
276 */
277 QString
278 45 Oligomer::getDescription() const
279 {
280
1/2
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
45 return m_description;
281 }
282
283 //////////////// MODIFICATION STATUS /////////////////////
284
285 /*!
286 \brief Sets the modification status to \a is_modified.
287 */
288 void
289 Oligomer::setModified(bool is_modified)
290 {
291 m_isModified = is_modified;
292 }
293
294 /*!
295 \brief Returns the chemical modification status of this Oligomer.
296
297 If \a deep is true, the member Polymer must exist (mcsp_polymer cannot be
298 nullptr) because the polymer itself is asked for the count of Monomer instances
299 being modified. The sequence ranges of the polymer that are surveyed are those
300 in this Oligomer's IndexRangeCollection. Upon checking the result, if the count
301 of modified Monomer instances is not 0, then m_isModified is set to true,
302 otherwise it is set to false. That m_isModified value is then returned.
303
304 If \a deep is false, the value of m_isModified is immediately returned.
305
306 \sa Polymer::hasModifiedMonomer, Monomer::isModified()
307 */
308 bool
309 47 Oligomer::isModified(bool deep /*false*/) const
310 {
311 // Either we truly go to the polymer instance and check if the oligomer is
312 // modified or we just ask for the member datum, that might have been set,
313 // for example, during creation of the oligomer in the Cleaver::cleave()
314 // function. We need the possibility to ask for the member datum because
315 // there are circumstances where the oligomer exists and not the original
316 // polymer (for example if the polymer sequence is edited while a set of
317 // cleavage oligomers is displayed in the cleavage dialog. When the
318 // tableviewmodel needs to refresh the contents of the cells, it crashes
319 // because the polymer has been edited and one monomer is missing from the
320 // sequence of the oligomer as it had been configured in the first place.
321
322
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 45 times.
47 if(deep)
323 {
324 2 m_isModified = modifiedMonomerCount() != 0 ? true : false;
325 }
326
327 47 return m_isModified;
328 }
329
330 /*!
331 \brief Return the count of the \l{Polymer}'s \l{Sequence}'s \l{Monomer}
332 instances that are modified.
333 \sa Polymer::modifiedMonomerCount
334 */
335 std::size_t
336 6 Oligomer::modifiedMonomerCount() const
337 {
338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if(mcsp_polymer == nullptr)
339 qFatal("Programming error. The pointer cannot be nullptr.");
340
341 6 return mcsp_polymer->modifiedMonomerCount(
342 6 m_calcOptions.getIndexRangeCollectionCstRef());
343 }
344
345 //////////////// THE IONIZER /////////////////////
346
347 /*!
348 \brief Sets \a ionizer to the member datum.
349 */
350 void
351 1604 Oligomer::setIonizer(const Ionizer &ionizer)
352 {
353 1604 m_ionizer = ionizer;
354 1604 }
355
356 /*!
357 \brief Returns a const reference to the Ionizer.
358 */
359 const Ionizer &
360 4 Oligomer::getIonizerCstRef() const
361 {
362 4 return m_ionizer;
363 }
364
365 /*!
366 \brief Returns a reference to the Ionizer.
367 */
368 Ionizer &
369 9 Oligomer::getIonizerRef()
370 {
371 9 return m_ionizer;
372 }
373
374 /*!
375 \brief Returns the result of the ionization process.
376
377 The new masses, if a change occurred, are updated in the member m_mono and
378 m_avg variables.
379 */
380 Enums::IonizationOutcome
381 1586 Oligomer::ionize()
382 {
383 1586 return m_ionizer.ionize(m_mono, m_avg);
384 }
385
386 /*!
387 \brief Returns the result of the deionization process.
388
389 The new masses, if a change occurred, are updated in the member m_mono and
390 m_avg variables.
391 */
392 Enums::IonizationOutcome
393 3 Oligomer::deionize()
394 {
395 3 return m_ionizer.deionize(m_mono, m_avg);
396 }
397
398 /*!
399 \brief Sets \a mono and \a avg to the masses of unionized analyte.
400
401 The member Ionizer is used to first deionize the \a mono and \a avg masses (on
402 copy data, if the Ionizer reports that the analyte is ionized).
403
404 \a mono and \a avg are then set to the masses of this polymer instance in an
405 unionized status.
406
407 Nothing is modified in this Oligomer instance.
408
409 Returns the process outcome.
410 */
411 Enums::IonizationOutcome
412 Oligomer::molecularMasses(double &mono, double &avg) const
413 {
414 double temp_mono = m_mono;
415 double temp_avg = m_avg;
416
417 Enums::IonizationOutcome ionization_outcome =
418 m_ionizer.deionize(temp_mono, temp_avg);
419
420 if(ionization_outcome == Enums::IonizationOutcome::FAILED)
421 {
422 qCritical() << "Failed to deionize the analyte.";
423 return ionization_outcome;
424 }
425
426 mono = temp_mono;
427 avg = temp_avg;
428
429 return ionization_outcome;
430 }
431
432 //////////////// THE SEQUENCE RANGES /////////////////////
433
434 /*!
435 \brief Sets \a index_ranges to the member datum.
436 */
437 void
438 Oligomer::setIndexRanges(const IndexRangeCollection &index_ranges)
439 {
440 m_calcOptions.getIndexRangeCollectionRef().initialize(index_ranges);
441 }
442
443 /*!
444 \brief Sets \a index_range to the member datum as the sole range in the
445 member IndexRange container.
446 */
447 void
448 Oligomer::setIndexRange(const IndexRange &index_range)
449 {
450 m_calcOptions.getIndexRangeCollectionRef().clear();
451 m_calcOptions.getIndexRangeCollectionRef().setIndexRange(index_range);
452 }
453
454 /*!
455 \brief Returns a const reference to the IndexRangeCollection.
456 */
457 const IndexRangeCollection &
458 12 Oligomer::getIndexRangeCollectionCstRef() const
459 {
460 12 return m_calcOptions.getIndexRangeCollectionCstRef();
461 }
462
463 /*!
464 \brief Returns a reference to the IndexRangeCollection.
465 */
466 IndexRangeCollection &
467 51 Oligomer::getIndexRangeCollectionRef()
468 {
469 51 return m_calcOptions.getIndexRangeCollectionRef();
470 }
471
472 /*!
473 \brief Sets the \a start_index and \a stop_index indices to the first
474 IndexRange instance of the member IndexRangeCollection container.
475 */
476 void
477 Oligomer::setStartAndStopIndices(std::size_t start_index,
478 std::size_t stop_index)
479 {
480 if(!m_calcOptions.getIndexRangeCollectionRef().size())
481 {
482 m_calcOptions.getIndexRangeCollectionRef().setIndexRange(start_index,
483 stop_index);
484 }
485 else
486 {
487 m_calcOptions.getIndexRangeCollectionRef()
488 .getRangesRef()
489 .front()
490 ->m_start = start_index;
491 m_calcOptions.getIndexRangeCollectionRef()
492 .getRangesRef()
493 .front()
494 ->m_stop = stop_index;
495 }
496 }
497
498 /*!
499 \brief Sets \a start_index as the the first IndexRange instance'start value
500 of the member IndexRangeCollection container.
501 */
502 void
503 Oligomer::setStartIndex(int start_index)
504 {
505 if(!m_calcOptions.getIndexRangeCollectionRef().size())
506 {
507 m_calcOptions.getIndexRangeCollectionRef().setIndexRange(start_index, 0);
508 }
509 else
510 {
511 m_calcOptions.getIndexRangeCollectionRef()
512 .getRangesRef()
513 .front()
514 ->m_start = start_index;
515 }
516 }
517
518 /*!
519 \brief Returns the start index, or 0 (\a ok is set to false) if the
520 IndexRangeCollection member is empty.
521
522 The returned start index is that of the first IndexRange instance in the
523 IndexRangeCollection member (\a ok is set to true).
524 */
525 qsizetype
526 33 Oligomer::startIndex(bool &ok) const
527 {
528
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
33 if(!m_calcOptions.getIndexRangeCollectionCstRef().size())
529 {
530 ok = false;
531 return 0;
532 }
533
534 33 ok = true;
535 33 return m_calcOptions.getIndexRangeCollectionCstRef()
536 33 .getRangesCstRef()
537 33 .front()
538 33 ->m_start;
539 }
540
541 /*!
542 \brief Sets \a stop_index as the the first IndexRange instance'stop value
543 of the member IndexRangeCollection container.
544 */
545 void
546 Oligomer::setStopIndex(int stop_index)
547 {
548 if(!m_calcOptions.getIndexRangeCollectionRef().size())
549 {
550 m_calcOptions.getIndexRangeCollectionRef().setIndexRange(0, stop_index);
551 }
552 else
553 {
554 m_calcOptions.getIndexRangeCollectionRef()
555 .getRangesRef()
556 .front()
557 ->m_stop = stop_index;
558 }
559 }
560
561 /*!
562 \brief Returns the stop index, or 0 (\a ok is set to false) if the
563 IndexRangeCollection member is empty.
564
565 The returned stop index is that of the first IndexRange instance in the
566 IndexRangeCollection member (\a ok is set to true).
567 */
568 qsizetype
569 45 Oligomer::stopIndex(bool &ok) const
570 {
571
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 45 times.
45 if(!m_calcOptions.getIndexRangeCollectionCstRef().size())
572 {
573 ok = false;
574 return 0;
575 }
576
577 45 ok = true;
578 45 return m_calcOptions.getIndexRangeCollectionCstRef()
579 45 .getRangesCstRef()
580 45 .front()
581 45 ->m_stop;
582 }
583
584 //////////////// THE CALCULATION OPTIONS /////////////////////
585
586 /*!
587 \brief Sets \a calc_options to the member datum.
588 */
589 void
590 Oligomer::setCalcOptions(const CalcOptions &calc_options)
591 {
592 m_calcOptions.initialize(calc_options);
593 }
594
595 /*!
596 \brief Returns the CalcOptions member.
597 */
598 const CalcOptions &
599 23 Oligomer::getCalcOptionsCstRef() const
600 {
601 23 return m_calcOptions;
602 }
603
604 /*!
605 \brief Returns a reference to the CalcOptions member.
606 */
607 CalcOptions &
608 54 Oligomer::getCalcOptionsRef()
609 {
610 54 return m_calcOptions;
611 }
612
613 //////////////// THE MASSES /////////////////////
614 /*!
615 \brief Set the mass qualified using \a mass_type to the \a mass value.
616
617 \sa setMasses()
618 */
619 void
620 Oligomer::setMass(Enums::MassType mass_type, double mass)
621 {
622 if(mass_type == Enums::MassType::MONO)
623 m_mono = mass;
624 else if(mass_type == Enums::MassType::AVG)
625 m_avg = mass;
626 else
627 qFatalStream() << "The mass_type needs to be either MONO or AVG.";
628 }
629
630 /*!
631 \brief Sets \a mono and \a avg masses to this Oligomer.
632
633 \sa setMass()
634 */
635 void
636 208 Oligomer::setMasses(double mono, double avg)
637 {
638 208 m_mono = mono;
639 208 m_avg = avg;
640 208 }
641
642 /*!
643 \brief Returns the Oligomer's mass of the type defined by \a mass_type.
644 */
645 double
646 128 Oligomer::getMass(Enums::MassType mass_type) const
647 {
648
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 if(mass_type != Enums::MassType::MONO && mass_type != Enums::MassType::AVG)
649 qFatalStream() << "The mass_type needs to be either MONO or AVG.";
650
651
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 64 times.
128 if(mass_type == Enums::MassType::MONO)
652 64 return m_mono;
653
654 64 return m_avg;
655 }
656
657 /*!
658 \brief Returns a reference to the Oligomer's mass of the type defined by \a
659 mass_type.
660 */
661 double &
662 258 Oligomer::getMassRef(Enums::MassType mass_type)
663 {
664
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 258 times.
258 if(mass_type != Enums::MassType::MONO && mass_type != Enums::MassType::AVG)
665 qFatalStream() << "The mass_type needs to be either MONO or AVG.";
666
667
2/2
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 129 times.
258 if(mass_type == Enums::MassType::MONO)
668 129 return m_mono;
669
670 129 return m_avg;
671 }
672
673 //////////////// THE CROSS-LINKS /////////////////////
674 /*!
675 \brief Returns a const reference to the CrossLink container.
676 */
677 const std::vector<CrossLinkSPtr> &
678 Oligomer::getCrossLinksCstRef() const
679 {
680 return m_crossLinks;
681 }
682
683 /*!
684 \brief Returns a reference to the CrossLink container.
685 */
686 std::vector<CrossLinkSPtr> &
687 Oligomer::getCrossLinksRef()
688 {
689 return m_crossLinks;
690 }
691
692 /*!
693 \brief Adds the \a cross_link_sp CrossLink to this Oligomer's container of
694 \l{CrossLink} instances.
695
696 Returns true if the CrossLink was added succesfully, or false \a
697 cross_link_sp was already in the container.
698 */
699 bool
700 44 Oligomer::addCrossLink(CrossLinkSPtr cross_link_sp)
701 {
702
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if(cross_link_sp == nullptr || cross_link_sp.get() == nullptr)
703 qFatalStream() << "Programming error. The pointer cannot be nullptr.";
704
705 // Add the cross-link only if it does not exist already. Return true
706 // only if the crossLink has been added.
707
708 44 std::vector<CrossLinkSPtr>::const_iterator the_iterator_cst =
709
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 22 times.
44 std::find_if(m_crossLinks.cbegin(),
710 m_crossLinks.cend(),
711 46 [&cross_link_sp](const CrossLinkSPtr &iter_cross_link_sp) {
712
6/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 6 times.
✓ Branch 10 taken 6 times.
✓ Branch 11 taken 9 times.
✓ Branch 12 taken 13 times.
✓ Branch 13 taken 9 times.
46 return iter_cross_link_sp == cross_link_sp;
713 });
714
715
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 22 times.
44 if(the_iterator_cst == m_crossLinks.end())
716 {
717 22 m_crossLinks.push_back(cross_link_sp);
718 22 return true;
719 }
720
721 return false;
722 }
723
724 //////////////// THE PARTIAL CLEAVAGE /////////////////////
725 /*!
726 \brief Set the member \l m_partialCleavage to \a partial_cleavage.
727 */
728 void
729 428 Oligomer::setPartialCleavage(std::size_t partial_cleavage)
730 {
731 428 m_partialCleavage = partial_cleavage;
732 428 }
733
734 /*!
735 \brief Returns the m_partialCleavage member.
736 */
737 std::size_t
738 Oligomer::getPartialCleavage() const
739 {
740 return m_partialCleavage;
741 }
742
743 //////////////// THE FORMULA /////////////////////
744 /*!
745 \brief Sets the formula to \a formula.
746 */
747 void
748 Oligomer::setFormula(const Formula &formula)
749 {
750 m_formula = formula;
751 }
752
753 /*!
754 \brief Sets the formula to \a formula_string.
755 */
756 void
757 Oligomer::setFormula(const QString &formula_string)
758 {
759 m_formula = Formula(formula_string);
760 }
761
762 /*!
763 \brief Returns a constant reference to the formula.
764 */
765 const Formula &
766 43 Oligomer::getFormulaCstRef() const
767 {
768 43 return m_formula;
769 }
770
771 /*!
772 \brief Returns a reference to the formula.
773 */
774 Formula &
775 2418 Oligomer::getFormulaRef()
776 {
777 2418 return m_formula;
778 }
779
780 //////////////// THE MONOMERS /////////////////////
781 /*!
782 \brief Returns the Monomer in the member Polymer that is located at the index
783 that has the start value of the first IndexRange object in the member
784 IndexRangeCollection container.
785
786 If the Polymer is not avaialable, or if the start
787 index could not be obtained, nullptr is returned.
788
789 If the index is out of bounds, that is a fatal error.
790 */
791 MonomerSPtr
792 1 Oligomer::getLeftEndMonomerCstSPtr() const
793 {
794
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(mcsp_polymer == nullptr)
795 {
796 qCritical() << "The polymer is not available.";
797 return nullptr;
798 }
799
800 1 bool ok = false;
801 1 std::size_t index = startIndex(ok);
802
803
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(!ok)
804 return nullptr;
805
806
807
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if(index >= mcsp_polymer->getSequenceCstRef().size())
808 qFatalStream() << "Programming error. Index is out of bounds.";
809
810 1 return mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(index);
811 }
812
813 /*!
814 \brief Returns the Monomer in the member Polymer that is located at the index
815 that has the stop value of the first IndexRange object in the member
816 IndexRangeCollection container.
817
818 If the Polymer is not avaialable, or if the stop
819 index could not be obtained, nullptr is returned.
820
821 If the index is out of bounds, that is a fatal error.
822 */
823 MonomerSPtr
824 13 Oligomer::getRightEndMonomerCstSPtr() const
825 {
826
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if(mcsp_polymer == nullptr)
827 {
828 qCritical() << "The polymer is not available.";
829 return nullptr;
830 }
831
832 13 bool ok = false;
833 13 std::size_t index = stopIndex(ok);
834
835
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if(!ok)
836 return nullptr;
837
838
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
13 if(index >= mcsp_polymer->getSequenceCstRef().size())
839 qFatalStream() << "Programming error. Index is out of bounds.";
840
841 13 return mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(index);
842 }
843
844 /*!
845 \brief Returns the Monomer in the member Polymer that is located at the \a
846 index.
847
848 If the Polymer is not available, nullptr is returned. If \a index is not
849 encompassed by the member IndexRangeCollection container, or if that container
850 is empty, nullptr is returned.
851
852 If \a index is out of bounds, that is a fatal error.
853 */
854 MonomerSPtr
855 3 Oligomer::getMonomerCstSPtrAt(std::size_t index) const
856 {
857
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(mcsp_polymer == nullptr)
858 {
859 qCritical() << "The polymer is not available.";
860 return nullptr;
861 }
862
863
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if(!m_calcOptions.getIndexRangeCollectionCstRef().size())
864 return nullptr;
865
866
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if(!m_calcOptions.getIndexRangeCollectionCstRef().encompassIndex(index))
867 {
868 qCritical() << "Asking for Monomer at index not encompassed by member "
869 "IndexRangeCollection.";
870 }
871
872
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if(index >= mcsp_polymer->getSequenceCstRef().size())
873 qFatalStream() << "Programming error. Index is out of bounds.";
874
875 3 return mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(index);
876 }
877
878 //////////////// OPERATORS /////////////////////
879 /*!
880 \brief Assigns \a other to this Oligomer instance.
881 */
882 Oligomer &
883 Oligomer::operator=(const Oligomer &other)
884 {
885 if(&other == this)
886 return *this;
887
888 PropListHolder::operator=(other);
889
890 mcsp_polymer = other.mcsp_polymer;
891 m_name = other.m_name;
892 m_description = other.m_description;
893 m_isModified = other.m_isModified;
894 m_ionizer = other.m_ionizer;
895 m_partialCleavage = other.m_partialCleavage;
896 m_calcOptions.initialize(other.m_calcOptions);
897 m_mono = other.m_mono;
898 m_avg = other.m_avg;
899 m_formula = other.m_formula;
900 m_crossLinks.assign(other.m_crossLinks.cbegin(), other.m_crossLinks.cend());
901
902 return *this;
903 }
904
905 //////////////// ELEMENTAL CALCULATION FUNCTIONS /////////////////////
906
907 /*!
908 \brief Returns the elemental composition of this Oligomer instance.
909
910 The computation of the elemental composition is performed as configured in \a
911 calc_options and using the ionization described in \a ionizer.
912 */
913 QString
914 428 Oligomer::elementalComposition(const CalcOptions &calc_options,
915 const Ionizer &ionizer) const
916 {
917 428 qDebug().noquote()
918 << "Calculating the elemental composition of Oligomer using "
919 "CalcOptions:"
920 << calc_options.toString() << "and Ionizer:" << ionizer.toString()
921 << "that has internal formula:" << m_formula.getActionFormula();
922
923
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 428 times.
428 if(calc_options.getSelectionType() == Enums::SelectionType::RESIDUAL_CHAINS)
924 qInfo() << "Enums::SelectionType::RESIDUAL_CHAINS";
925 else
926
1/2
✓ Branch 2 taken 428 times.
✗ Branch 3 not taken.
428 qInfo() << "Enums::SelectionType::OLIGOMERS";
927
928 428 qDebug() << "Going to call Polymer::elementalComposition() with CalcOptions:"
929 << calc_options.toString() << "and ionizer:" << ionizer.toString();
930
931 428 return mcsp_polymer->elementalComposition(calc_options, ionizer);
932 }
933
934 /*!
935 \brief Returns the elemental composition of this Oligomer instance.
936 */
937 QString
938 428 Oligomer::elementalComposition() const
939 {
940 428 return elementalComposition(m_calcOptions, m_ionizer);
941 }
942
943 //////////////// MASS CALCULATION FUNCTIONS /////////////////////
944 /*!
945 \brief Calculates the monoisotopic and average masses.
946
947 The calculation is performed by computing the mono and avg masses
948 of the sequence stretch as described by the set of start and top indices (member
949 IndexRangeCollection object) in the polymer sequence.
950
951 The member CalcOptions (m_calcOptions) and the member Ionizer
952 (m_ionizer) are used.
953
954 The m_mono and m_avg member objects are reset to 0.0 before the calculations.
955
956 Returns true if calculations were successful, false otherwise.
957 */
958 bool
959 442 Oligomer::calculateMasses()
960 {
961 442 return calculateMasses(m_calcOptions, m_ionizer);
962 }
963
964 /*!
965 \brief Calculates the monoisotopic and average masses.
966
967 The calculation is performed by computing the mono and avg masses
968 of the sequence stretch as described by the start and end indices in
969 the polymer sequence. The calculations are configured by \a calc_options
970 and the ionization is defined in \a ionizer.
971
972 The m_mono and m_avg member objects are reset to 0.0 before the calculations.
973
974 The member Ionizer is not set to \a ionizer. This is so that a whole set
975 of ionization computations can be performed without touching the internal
976 Ionizer.
977
978 Returns true if calculations were successful, false otherwise.
979 */
980 bool
981 517 Oligomer::calculateMasses(const CalcOptions &calc_options,
982 const Ionizer &ionizer)
983 {
984 517 qDebug().noquote() << "Going to calculate masses with calculation options:"
985 << calc_options.toString()
986 << "and ionizer:" << ionizer.toString();
987
988 517 CalcOptions local_calc_options(calc_options);
989
990 // The coordinates of the oligomer are the following:
991
992 // MAMISGMSGRKAS
993
994 // For a tryptic peptide obtained from protein above, we'd have
995
996 // MAMISGMSGR, that is in the oligomer coordinates:
997
998 // [0] MAMISGMSGR [9]
999
1000 // When computing the mass of the oligomer, we have to do a
1001
1002 // for (iter == [0] ; iter < [9 + 1]; ++iter)
1003
1004 // Which is why we increment add 1 to m_endIndex in the function below.
1005
1006 // A polymer might be something made of more than one residual chain
1007 // in case it is a cross-linked oligomer. Compute the mass fore each
1008 // residual chain, without accounting for the cross-links...
1009
1010 517 m_mono = 0;
1011 517 m_avg = 0;
1012
1013 // An oligomer is characterized by a reference polymer and a
1014 // IndexRangeCollection object that documents the ranges of that Polymer
1015 // Sequence. That IndexRangeCollection object is in the calc_options object
1016 // passed as reference. With it we can call the Polymer::accountMasses
1017 // function. This is why we need to first copy the member IndexRangeCollection
1018 // object in it.
1019
1020 // local_calc_options.setIndexRanges(m_calcOptions.getIndexRangeCollectionCstRef());
1021
1022 517 qDebug() << "Index ranges inside calculation options:"
1023 << local_calc_options.getIndexRangeCollectionRef().indicesAsText();
1024
1025 // We do not want to take into account the cross-links because
1026 // we'll be doing this here and because it cannot work if the
1027 // cross-links are taken into account from the polymer.
1028
1029
1/2
✓ Branch 1 taken 517 times.
✗ Branch 2 not taken.
517 int flags = static_cast<int>(local_calc_options.getMonomerEntities());
1030 // qDebug() << "flags:" << flags
1031 // << chemicalEntityMap[static_cast<Enums::ChemicalEntity>(flags)];
1032
1033 517 flags &= ~static_cast<int>(Enums::ChemicalEntity::CROSS_LINKER);
1034 // qDebug() << "flags:" << flags
1035 // << chemicalEntityMap[static_cast<Enums::ChemicalEntity>(flags)];
1036
1037
1/2
✓ Branch 1 taken 517 times.
✗ Branch 2 not taken.
517 local_calc_options.setMonomerEntities(
1038 static_cast<Enums::ChemicalEntity>(flags));
1039
1040 // flags = static_cast<int>(local_calc_options.getPolymerEntities());
1041 // qDebug() << "flags:" << flags
1042 // << chemicalEntityMap[static_cast<Enums::ChemicalEntity>(flags)];
1043
1044
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 511 times.
517 if(!Polymer::accountMasses(
1045
1/2
✓ Branch 1 taken 517 times.
✗ Branch 2 not taken.
517 mcsp_polymer.get(), local_calc_options, m_mono, m_avg))
1046 {
1047
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 qCritical() << "Failed accounting masses of Polymer.";
1048 6 return false;
1049 }
1050
1051 511 qDebug() << "20250522 After the enclosing polymer accounted masses (prior to "
1052 "cross-links), mono mass is:"
1053 << m_mono << "avg mass is:" << m_avg;
1054
1055 // At this point, we have added the mass of each constituent
1056 // oligomer's residual chain (with or without end caps, depending
1057 // on the configuration). Let's deal with the cross-links, if
1058 // so is required.
1059
1060
3/4
✓ Branch 1 taken 511 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 231 times.
✓ Branch 4 taken 280 times.
511 if(static_cast<int>(calc_options.getMonomerEntities()) &
1061 static_cast<int>(Enums::ChemicalEntity::CROSS_LINKER))
1062 {
1063 231 qDebug() << "As per configuration, now accounting "
1064 "the cross-links.";
1065
1066
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 231 times.
253 for(const CrossLinkSPtr &cross_link_sp : m_crossLinks)
1067 {
1068 22 qDebug() << "Now iterating in one of the cross-links of the Oligomer:"
1069 << cross_link_sp->toString();
1070
1071 22 qDebug()
1072 << "Checking if Index ranges:"
1073 << local_calc_options.getIndexRangeCollectionRef().indicesAsText()
1074 << "fully encompass the cross-link.";
1075
1076 // Only account the cross-link if it is entirely encompassed
1077 // by the index range.
1078 22 std::size_t in_count = 0;
1079 22 std::size_t out_count = 0;
1080
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 Enums::CrossLinkEncompassed cross_link_encompassed =
1081
2/4
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
22 cross_link_sp->isEncompassedByIndexRangeCollection(
1082 local_calc_options.getIndexRangeCollectionCstRef(),
1083 in_count,
1084 out_count);
1085
1086
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if(cross_link_encompassed == Enums::CrossLinkEncompassed::FULLY)
1087 {
1088
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 cross_link_sp->accountMasses(m_mono, m_avg);
1089
1090 qDebug() << "After accounting fully encompassed cross-link:"
1091 << cross_link_sp->getCrossLinkerCstSPtr()->getName()
1092 << "mono mass is:" << m_mono << "avg mass is:" << m_avg;
1093 }
1094 }
1095 }
1096
1097 // If the ionizer is valid use it.
1098
3/4
✓ Branch 1 taken 511 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 27 times.
✓ Branch 4 taken 484 times.
511 if(ionizer.isValid())
1099 {
1100
2/4
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 27 times.
27 if(ionizer.ionize(m_mono, m_avg) == Enums::IonizationOutcome::FAILED)
1101 {
1102 qCritical() << "Failed to ionize the Oligomer.";
1103 return false;
1104 }
1105 }
1106
1107 qDebug() << "Coming out from the calculateMasses function:"
1108 << "mono mass is:" << m_mono << "avg mass is:" << m_avg;
1109
1110 return true;
1111 517 }
1112
1113 /*!
1114 \brief Returns the size of this Oligomer.
1115
1116 The size is computed by adding the length of all the regions of the
1117 enclosing Polymer as documented in the Coordinates instances in the member
1118 coordinateList.
1119 */
1120 std::size_t
1121 Oligomer::size()
1122 {
1123 std::size_t sum = 0;
1124
1125 foreach(const IndexRange *item,
1126 m_calcOptions.getIndexRangeCollectionCstRef().getRangesCstRef())
1127 sum += (item->m_stop - item->m_start + 1);
1128
1129 return sum;
1130 }
1131
1132 /*!
1133 \brief Returns true if this Oligomer spans at least one region of the
1134 enclosing polymer that contains a Monomer at \a index, false otherwise.
1135 */
1136 bool
1137 3344 Oligomer::encompasses(std::size_t index) const
1138 {
1139
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3344 times.
3344 if(mcsp_polymer == nullptr)
1140 {
1141 qCritical() << "The polymer is not available.";
1142 return false;
1143 }
1144
1145
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 3344 times.
3344 if(index >= mcsp_polymer->getSequenceCstRef().size())
1146 qFatalStream() << "Programming error. Index is out of bounds.";
1147
1148 3344 return m_calcOptions.getIndexRangeCollectionCstRef().encompassIndex(index);
1149 }
1150
1151 /*!
1152 \brief Returns true if this Oligomer spans at least one region of the
1153 enclosing Polymer that contains the Monomer \a monomer_crp, false otherwise.
1154
1155 The search is performed by comparing pointers, thus the Monomer to be search \e
1156 is \a monomer_crp.
1157 */
1158 bool
1159 3344 Oligomer::encompasses(MonomerCstRPtr monomer_crp) const
1160 {
1161 3344 bool ok = false;
1162 3344 std::size_t index =
1163 3344 mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_crp, ok);
1164
1165 3344 return encompasses(index);
1166 }
1167
1168 /*!
1169 \brief Returns true if this Oligomer spans at least one region of the
1170 enclosing Polymer that contains the Monomer \a monomer_sp, false otherwise.
1171
1172 The search is performed by comparing pointers, thus the Monomer to be search \e
1173 is \a monomer_sp.
1174 */
1175 bool
1176 Oligomer::encompasses(MonomerSPtr monomer_sp) const
1177 {
1178
1179 bool ok = false;
1180 std::size_t index =
1181 mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_sp, ok);
1182
1183 return encompasses(index);
1184 }
1185
1186 /*!
1187 \brief Returns a string documenting this Oligomer instance.
1188 */
1189 QString
1190 Oligomer::toString() const
1191 {
1192 QString text =
1193 QString(
1194 "name: %1 - desc.: %2 - index ranges: %3 - mono: %4 - avg: "
1195 "%5 - stored formula :%6 - ionizer: %7 - calc. elem. comp. : %8\n")
1196 .arg(m_name)
1197 .arg(m_description)
1198 .arg(m_calcOptions.getIndexRangeCollectionCstRef().positionsAsText())
1199 .arg(m_mono)
1200 .arg(m_avg)
1201 .arg(m_formula.getActionFormula())
1202 .arg(m_ionizer.toString())
1203 .arg(elementalComposition());
1204
1205 return text;
1206 }
1207
1208 } // namespace libXpertMassCore
1209 } // namespace MsXpS
1210