GCC Code Coverage Report


./
File: src/XpertMass/Ionizable.cpp
Date: 2024-08-24 11:26:06
Lines:
118/201
58.7%
Functions:
15/19
78.9%
Branches:
94/256
36.7%

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 /////////////////////// Qt includes
35 #include <QtGlobal>
36
37
38 /////////////////////// Local includes
39 #include "Ionizable.hpp"
40 #include "PolChemDef.hpp"
41
42
43 namespace MsXpS
44 {
45
46 namespace libXpertMass
47 {
48
49 /*!
50 \class MsXpS::libXpertMass::Ionizable
51 \inmodule libXpertMass
52 \ingroup PolChemDefBuildingdBlocks
53 \inheaderfile Ionizable.hpp
54
55 \brief The Ionizable class provides abstractions to work with entities that
56 have masses (base class \l Ponderable) and that can be ionized.
57
58 An Ionizable is a \l Ponderable that might undergo ionization, using either its
59 own member \l IonizeRule or an IonizeRule passed as argument to one of its
60 functions. An Ionizable \e is also a \l PolChemDefEntity, as it has to know at
61 each instant what polymer chemistry definition it is based upon. The ionization
62 status of the Ionizable can be checked at each moment and a call to an ionizing
63 function will first trigger deionization if the Ionizable is already ionized.
64 The main members of the Ionizable class are the following:
65
66 \list
67 \li a IonizeRule;
68 \li a boolean member stating if the entity has actually been
69 ionized;
70 \endlist
71
72 Upon creation of an Ionizable (without use of the copy constructor), all the
73 data required for the full qualification of the new instance should be passed to
74 the constructor. Default parameters ensure that the Ionizable is set to a
75 consistent status (that is its IonizeRule member is \e invalid and that its
76 m_isIonized flag is false).
77
78 The caller is responsible for seeding correct and consistent values into the
79 constructor for proper operation of the class instances.
80
81 For the ionization to be actually performed, and the masses to be effectively
82 updated to account for that ionization, the Ionizable instance must be
83 ionize()d.
84
85 It is possible to deionize() an Ionizable instance as it is possible to reionize
86 the instance with another IonizeRule, which will replace the member IonizeRule
87 if the reionization succeeds. The deionize() call effects the state of the
88 Ionizable instance only if the m_isIonized boolean is true.
89 */
90
91
92 /*!
93 \variable MsXpS::libXpertMass::Ionizable::m_ionizeRule
94
95 \brief The ionization rule that defines the way to ionize this Ionizable.
96
97 \sa IonizeRule
98 */
99
100 /*!
101 \variable MsXpS::libXpertMass::Ionizable::m_isIonized
102
103 \brief Tells if this Ionizable has undergone an ionization.
104 */
105
106
107 /*!
108 \brief Constructs an Ionizable.
109
110 The Ionizable instance will be an ionized entity if \a is_ionized is true. The
111 \a ponderable's mono and avg masses must thus be masses which take
112 (is_ionized is true) or do not take (is_ionized is false) into account the data
113 located in \a ionize_rule. If \a is_ionized is true, the \a ionize_rule is
114 validated and the level of the ionization must not be 0. If one of these two
115 tests fails, this is an error and the program aborts.
116
117 \a pol_chem_def_csp Polymer chemistry definition (cannot be nullptr);
118
119 \a name Name of this Ionizable (defaults to "NOT_SET");
120
121 \a ponderable \l Ponderable (mono and avg masses);
122
123 \a ionize_rule \l IonizeRule (defaults to IonizeRule(), that is,
124 an invalid IonizeRule).
125
126 \a is_ionized Tells if the Ionizable to be constructed should be considered
127 as an ionized chemical entity.
128 */
129 152 Ionizable::Ionizable(PolChemDefCstSPtr pol_chem_def_csp,
130 const QString &name,
131 const Ponderable &ponderable,
132 const IonizeRule &ionize_rule,
133 152 bool is_ionized)
134 : PolChemDefEntity(pol_chem_def_csp, name),
135 Ponderable(ponderable),
136 152 m_ionizeRule(ionize_rule),
137
3/6
✓ Branch 2 taken 152 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 152 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 152 times.
✗ Branch 10 not taken.
152 m_isIonized(is_ionized)
138 {
139
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 132 times.
152 if(m_isIonized)
140 {
141 // If the Ionizable is ionized, then that means that its
142 // IonizeRule should validate and also that the ionization
143 // level should not be 0.
144
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 20 times.
20 if(!m_ionizeRule.isValid())
145 qFatal("The member IonizeRule is not valid.");
146
147
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 20 times.
20 if(m_ionizeRule.level() == 0)
148 qFatal("The member IonizeRule has no charge.");
149 }
150 152 }
151
152
153 /*!
154 \brief Constructs an Ionizable as a copy of \a other.
155
156 No assumption should be made as to the status of the created Ionizable. The
157 caller should characterize the ionization status of the Ionizable with
158 isIonized().
159 */
160 12 Ionizable::Ionizable(const Ionizable &other)
161 : PolChemDefEntity(other),
162 Ponderable(other),
163 12 m_ionizeRule(other.m_ionizeRule),
164
2/4
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
12 m_isIonized(other.m_isIonized)
165 {
166 12 }
167
168
169 /*!
170 \brief Destructs this Ionizable.
171 */
172 328 Ionizable::~Ionizable()
173 {
174 328 }
175
176
177 /*!
178 \brief Assigns \a other to this Ionizable.
179
180 Return A reference to this Ionizable instance.
181 */
182 Ionizable &
183 4 Ionizable::operator=(const Ionizable &other)
184 {
185
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(&other == this)
186 return *this;
187
188 4 PolChemDefEntity::operator=(other);
189 4 Ponderable::operator=(other);
190
191 4 m_ionizeRule = other.m_ionizeRule;
192 4 m_isIonized = other.m_isIonized;
193
194 4 return *this;
195 }
196
197
198 /*!
199 \brief Returns true if \c this Ionizable is identical to \a other, false
200 otherwise.
201 */
202 bool
203 12 Ionizable::operator==(const Ionizable &other) const
204 {
205
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(this == &other)
206 return true;
207
208
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(m_mono != other.m_mono)
209 return false;
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(m_avg != other.m_avg)
211 return false;
212
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if(m_ionizeRule != other.m_ionizeRule)
213 return false;
214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(m_isIonized != other.m_isIonized)
215 return false;
216
217 12 return true;
218 }
219
220 /*!
221 \brief Returns true if this Ionizable is different than \a other, false
222 otherwise.
223
224 Returns the negation of operator==(other).
225 */
226 bool
227 4 Ionizable::operator!=(const Ionizable &other) const
228 {
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(this == &other)
230 return false;
231
232 4 return !operator==(other);
233 }
234
235 /*!
236 \brief Returns a constant reference to this IonizeRule.
237 */
238 const IonizeRule &
239 Ionizable::ionizeRule() const
240 {
241 return m_ionizeRule;
242 }
243
244
245 /*!
246 \brief Returns a pointer to this IonizeRule.
247 */
248 IonizeRule *
249 92 Ionizable::ionizeRule()
250 {
251 92 return &m_ionizeRule;
252 }
253
254
255 /*!
256 \brief Returns true if this Ionizable is ionized, false otherwise.
257
258 The ionization status is returned on the basis of the m_isIonized member
259 boolean value.
260 */
261 bool
262 32 Ionizable::isIonized() const
263 {
264 32 return m_isIonized;
265 }
266
267 /*!
268 \brief Returns the ionization charge of this Ionizable.
269
270 The charge is returned as a positive number (0 allowed) or as -1 if the
271 IonizeRule is not valid or if m_isIonized is false.
272
273 \note The charge is returned as the compound product of the IonizeRule's
274 m_charge and m_level members.
275 */
276 int
277 44 Ionizable::charge() const
278 {
279 // If the member ionizeRule is not valid, then return -1, that is
280 // inform the caller that something is not correct in the
281 // "workflow".
282
283
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 40 times.
44 if(!m_ionizeRule.isValid())
284 4 return -1;
285
286 // Now, the ionizeRule is correct, but the ionizable does not
287 // advertise that it has been ionized. In this case, its charge is
288 // 0.
289
290
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 32 times.
40 if(!m_isIonized)
291 8 return 0;
292
293 // Finally, the ionizable is effectively
294 // ionized, that is, its ionizeRule is valid and it advertises that
295 // it is ionized, in which case we can return its charge.
296
297 32 return (m_ionizeRule.charge() * m_ionizeRule.level());
298 }
299
300
301 /*!
302 \brief Sets the charge of this Ionizable to \a charge.
303
304 The charge of an ionizable is the compound product of m_charge and m_level in
305 its \l IonizeRule member. The value passed as \a charge is \e that compound
306 product. Thus, the member IonizeRule is updated to reflect the new \c charge
307 using the following code:
308
309 \code
310 int level = charge / m_ionizeRule.charge();
311 m_ionizeRule.setLevel(level);
312 \endcode
313
314 Only the level value of IonizeRule is changed to reflect the change of the \a
315 charge because the chemistry of the ionization rule itself must not be changed.
316
317 The following logic is applied:
318
319 \list 1
320 \li
321 If the member ionizeRule is not valid, this function returns -1 because it
322 does not make sense to try to change the charge of an Ionizable if its member
323 IonizeRule is not valid.
324 \li This Ionizable is first deionized if it is ionized. If the deionization
325 fails -1 is returned.
326 \li This Ionizable is ionized with the level value in its member IonizeRule. If
327 that ionization fails, -1 is returned.
328 \endlist
329
330 At this point all the process went fine and 1 is returned.
331 */
332 int
333 Ionizable::setCharge(int charge)
334 {
335 if(!m_ionizeRule.isValid())
336 return -1;
337
338 // If *this Ionizable is ionized, first deionize it with its own
339 // m_ionizeRule, so that we get back to M, as we would say in
340 // front of a mass spectrometer.
341
342 if(m_isIonized)
343 {
344 if(!deionize())
345 return -1;
346 else
347 // Make clear that at this point we are not ionized.
348 m_isIonized = false;
349 }
350
351 // At this point, if charge is 0, then we are done, and we can
352 // return a success.
353 if(!charge)
354 return 1;
355
356 // At this point we can compute what IonizeRule's level should
357 // be
358 //(taking into account its charge, that is its unitary charge) so
359 // that the total charge is identical to the parameter.
360
361 int level = charge / m_ionizeRule.charge();
362
363 m_ionizeRule.setLevel(level);
364
365 if(ionize() == -1)
366 return -1;
367
368 // Return 1 because we know we changed something:
369 return 1;
370 }
371
372
373 /*!
374 \brief Ionizes this Ionizable.
375
376 Ionization is performed on this Ionizable using the member IonizeRule.
377
378 The following logic is applied:
379
380 \list 1
381 \li If the member ionizeRule is not valid, this function returns -1 because it
382 does not make sense to try to change the charge of an Ionizable if its member
383 IonizeRule is not valid.
384 \li If this Ionizable is ionized, return 0 (already ionized, then nothing to
385 do).
386 \li The mass status of this Ionizable (\l Ponderable base class) is stored
387 for later comparison.
388 \li The ionization process is carried out and the new mass status is compared
389 to the older one. If the new status differs from the previous one, than 1 is
390 returned, otherwise -1 is returned.
391 \endlist
392 */
393 int
394 8 Ionizable::ionize()
395 {
396
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 if(!m_ionizeRule.isValid())
397 return -1;
398
399
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(m_isIonized)
400 return 0;
401
402 // At this point perform the ionization proper.
403
404
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 Formula formula(m_ionizeRule.formula());
405
406 // qDebug() << "The formula right before ionizing:" << formula.toString();
407
408
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 Ponderable temp(*this);
409
410 // qDebug() << "Before ionization:\n"
411 // << "Ionizable's charge / level:" << m_ionizeRule.charge() << "/"
412 // << m_ionizeRule.level() << " -- "
413 // << "Ionizable's whole charge:"
414 // << m_ionizeRule.charge() * m_ionizeRule.level() << " -- "
415 // << "Mono:" << temp.mono() << "Avg:" << temp.avg() << "\n\n";
416
417 8 double localMono = 0;
418 8 double localAvg = 0;
419
420 IsotopicDataCstSPtr isotopic_data_csp =
421
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 mcsp_polChemDef->getIsotopicDataCstSPtr();
422
423 // Note the times(localIonizeRule.level()) param to call below.
424
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
8 if(!formula.accountMasses(
425
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 isotopic_data_csp, &localMono, &localAvg, m_ionizeRule.level()))
426 return -1;
427
428 // qDebug() << qSetRealNumberPrecision(6)
429 // << "Right after accounting formula masses:" << localAvg << "-"
430 // << localMono;
431
432 // OK, the accounting of the masses worked ok, we can update the
433 // values in the mono and avg params(note that we know that the
434 // charge and level values of m_ionizeRule cannot be <= 0 because
435 // otherwise the m_ionizRule would not have validated.
436
437
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 int ionCharge = m_ionizeRule.charge() * m_ionizeRule.level();
438
439 // qDebug() << "The ion charge:" << ionCharge;
440
441 // qDebug() << qSetRealNumberPrecision(6) << "m_mono:" << m_mono
442 // << "m_avg:" << m_avg;
443
444 8 m_mono += localMono;
445 8 m_mono = m_mono / ionCharge;
446
447 8 m_avg += localAvg;
448 8 m_avg = m_avg / ionCharge;
449
450 // Of course, we now are ionized.
451 8 m_isIonized = true;
452
453 // qDebug() << "After ionization:\n"
454 // << "Ionizable's charge / level:" << m_ionizeRule.charge() << "/"
455 // << m_ionizeRule.level() << " -- "
456 // << "Ionizable's whole charge:"
457 // << m_ionizeRule.charge() * m_ionizeRule.level() << " -- "
458 // << "Mono:" << m_mono << "Avg:" << m_avg << "\n\n";
459
460 // If something changed in the masses, then return 1, otherwise
461 // return 0.
462
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
8 if(temp != static_cast<Ponderable>(*this))
463 {
464 8 return 1;
465 }
466
467 return 0;
468 8 }
469
470 /*!
471 \brief Ionizes this Ionizable using \a ionize_rule.
472
473 The following logic is applied:
474
475 \list 1
476 \li
477 If \a ionize_rule is not valid, this function returns -1 because it
478 does not make sense to try to change the charge of an Ionizable if that
479 ionization rule is not valid.
480 \li If this Ionizable is ionized, first \l{deionize()} it. If this step fails,
481 returns -1.
482 \li The ionization process is carried out. If the process is successful and the
483 status of this Ionizable has changed, 1 is
484 returned, else 0 is returned. If the process is not successful -1 is returned.
485 \endlist
486
487 \sa ionize()
488 */
489 int
490 16 Ionizable::ionize(const IonizeRule &ionize_rule)
491 {
492
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
16 if(!ionize_rule.isValid())
493 {
494 qDebug() << "The IonizeRule is not valid, cannot ionize, returning -1.";
495 return -1;
496 }
497
498 // qDebug() << "The new IonizeRule:" << ionize_rule.toString();
499
500 // If *this Ionizable is ionized, first deionize it with its own
501 // m_ionizeRule, so that we get back to M, as we would say in
502 // front of a mass spectrometer.
503
504
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 Ponderable temp(*this);
505
506
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
16 if(m_isIonized)
507 {
508 // qDebug() << "This Ionizable is ionized right before deionization:"
509 // << toString();
510
511
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
12 if(!deionize())
512 {
513 qDebug() << "Failed to deionize the Ionizable.";
514 return -1;
515 }
516 else
517 // Make clear that at this point we are not ionized.
518 12 m_isIonized = false;
519
520 // qDebug() << "This Ionizable right after deionization:" << toString();
521 }
522 else
523 {
524 // qDebug() << "This Ionizable is not ionized:" << toString();
525 }
526
527 // At this point perform the ionization proper using 'ionize_rule'.
528
529
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
16 Formula formula(ionize_rule.formula());
530
531 // qDebug() << "The new ionization formula is:" << formula.toString();
532
533 16 double localMono = 0;
534 16 double localAvg = 0;
535
536 IsotopicDataCstSPtr isotopic_data_csp =
537
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 mcsp_polChemDef->getIsotopicDataCstSPtr();
538
539 // Note the times(ionizeRule.level()) param to call below.
540
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
16 if(!formula.accountMasses(
541
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 isotopic_data_csp, &localMono, &localAvg, ionize_rule.level()))
542 return -1;
543
544 // qDebug() << qSetRealNumberPrecision(6)
545 // << "Right after accounting the ionization masses:" << localMono
546 // << "-" << localAvg;
547
548 // OK, the accounting of the masses worked ok, we can update the
549 // values in the mono and avg params(note that we know that the
550 // charge and level values of ionizeRule cannot be <= 0 because
551 // otherwise the ionizRule would not have validated.
552
553 // qDebug()
554 // << qSetRealNumberPrecision(6)
555 // << "The ionizable masses before addition of the masses due to
556 // ionization:"
557 // << m_mono << "-" << m_avg;
558
559 16 m_mono += localMono;
560 16 m_avg += localAvg;
561
562 // qDebug() << qSetRealNumberPrecision(6)
563 // << "Right after having added the ionization masses:" << m_mono <<
564 // "-"
565 // << m_avg;
566
567 // If the ionization rule is for actually ionizing, then perform
568 // the division.
569
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 if(ionize_rule.level())
570 {
571
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
16 m_mono = m_mono / (ionize_rule.charge() * ionize_rule.level());
572
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
16 m_avg = m_avg / (ionize_rule.charge() * ionize_rule.level());
573
574 // qDebug() << qSetRealNumberPrecision(6)
575 // << "And now true m/z values:" << m_mono << "-" << m_avg;
576
577 16 m_isIonized = true;
578 }
579 else
580 {
581 m_isIonized = false;
582 }
583
584 // Update the internal IonizeRule.
585
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 m_ionizeRule = ionize_rule;
586
587 // qDebug() << __FILE__ << __LINE__
588 // << "After ionization: Mono:" << *mono << "Avg:" << avg;
589
590 // If something changed in the masses, then return 1, otherwise
591 // return 0.
592
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 if(temp != *this)
593 16 return 1;
594
595 return 0;
596 16 }
597
598
599 /*!
600 \brief Ionizes the \a ionizable using \a ionize_rule.
601
602 The following logic is applied:
603
604 \list 1
605 \li If \a ionize_rule is not valid, this function returns -1 because it
606 does not make sense to try to change the charge of an Ionizable if that
607 ionization rule is not valid.
608 \li If \a ionizable is ionized, first \l{deionize()} it. If this step fails,
609 returns -1.
610 \li The ionization process is carried out. If the process is successful, 1 is
611 returned, otherwise -1 is returned.
612 \endlist
613
614 \sa ionize()
615 */
616 int
617 Ionizable::ionize(Ionizable *ionizable, const IonizeRule &ionize_rule)
618 {
619 if(!ionize_rule.isValid())
620 return -1;
621
622 // If *this Ionizable is ionized, first deionize it with its own
623 // m_ionizeRule, so that we get back to M, as we would say in
624 // front of a mass spectrometer.
625
626 if(ionizable->m_isIonized)
627 {
628 if(!ionizable->deionize())
629 return -1;
630 else
631 // Make clear that at this point we are not ionized.
632 ionizable->m_isIonized = false;
633 }
634
635 // At this point perform the ionization proper using 'ionizeRule'.
636
637 Formula formula(ionizable->ionizeRule()->formula());
638
639 Ponderable temp(*ionizable);
640
641 double localMono = 0;
642 double localAvg = 0;
643
644 IsotopicDataCstSPtr isotopic_data_csp =
645 ionizable->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr();
646
647 // Note the times(ionizeRule.level()) param to call below.
648 if(!formula.accountMasses(
649 isotopic_data_csp, &localMono, &localAvg, ionize_rule.level()))
650 return -1;
651
652 // OK, the accounting of the masses worked ok, we can update the
653 // values in the mono and avg params(note that we know that the
654 // charge and level values of ionizeRule cannot be <= 0 because
655 // otherwise the ionizRule would not have validated.
656
657 ionizable->m_mono += localMono;
658 ionizable->m_avg += localAvg;
659
660 // If the ionization rule is for actually ionizing, then perform
661 // the division.
662 if(ionize_rule.level())
663 {
664 ionizable->m_mono =
665 ionizable->m_mono / (ionize_rule.charge() * ionize_rule.level());
666
667 ionizable->m_avg =
668 ionizable->m_avg / (ionize_rule.charge() * ionize_rule.level());
669
670 ionizable->m_isIonized = true;
671 }
672 else
673 {
674 ionizable->m_isIonized = false;
675 }
676
677 // Update the internal IonizeRule.
678 ionizable->m_ionizeRule = ionize_rule;
679
680 // qDebug() << __FILE__ << __LINE__
681 // << "After ionization: Mono:" << *mono << "Avg:" << avg;
682
683 // If something changed in the masses, then return 1, otherwise
684 // return 0.
685 if(temp != *ionizable)
686 return 1;
687
688 return 0;
689 }
690
691
692 /*!
693 \brief Deionizes this Ionizable.
694
695 This Ionizable is deionized using its member m_ionizeRule.
696 The following logic is applied:
697
698 \list 1
699 \li If this Ionizable is not ionized, this function returns 0.
700 \li If the member ionizeRule is not valid, this function returns -1 because it
701 does not make sense to try to change the ionization of an Ionizable if its
702 member IonizeRule is not valid.
703 \li
704 \li The deionization process is carried out. If the process is successful, 1 is
705 returned, otherwise -1 is returned.
706 \endlist
707 */
708 int
709 24 Ionizable::deionize()
710 {
711
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if(!m_isIonized)
712 {
713 // The Ionizable is not ionized, nothing to do, return true.
714 return 0;
715 }
716
717
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 Ponderable temp(*this);
718
719 // qDebug() << "This Ionizable before deionizing:" << toString();
720
721 // At this point we know the Ionizable is ionized, thus it is an
722 // error that m_ionizeRule is not valid.
723
724
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
24 if(!m_ionizeRule.isValid())
725 return -1;
726
727 // Now, reverse the usual M+zH/z(proteins) stuff.
728
729
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
24 double localMono = m_mono * abs(m_ionizeRule.charge() * m_ionizeRule.level());
730
731 // qDebug() << qSetRealNumberPrecision(10)
732 // << "Set to a variable the m_mono * ion charge:" << localMono;
733
734
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
24 double localAvg = m_avg * abs(m_ionizeRule.charge() * m_ionizeRule.level());
735
736 // qDebug() << qSetRealNumberPrecision(10)
737 // << "Set to a variable the m_avg * ion charge:" << localAvg;
738
739
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
24 Formula formula(m_ionizeRule.formula());
740
741 // qDebug() << "The member IonizeRule formula:" << formula.toString();
742
743 IsotopicDataCstSPtr isotopic_data_csp =
744
1/2
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
24 mcsp_polChemDef->getIsotopicDataCstSPtr();
745
746 // Note the negated 'times'(- m_ionizeRule.level())param to call
747 // below so that we revert the chemical action that led level
748 // times to the ionization of the analyte. Note that we do not
749 // have any compound(level * charge) because we are dealing with
750 // matter here, not charges, thus only 'level' is to be taken into
751 // consideration.
752
753
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 24 times.
24 if(!formula.accountMasses(
754
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 isotopic_data_csp, &localMono, &localAvg, -m_ionizeRule.level()))
755 return -1;
756
757 // qDebug()
758 // << qSetRealNumberPrecision(10)
759 // << "After having removed the ionization formula masses from the "
760 // "previously computed (M+z) mono and avg masses: we now get M masses:"
761 // << localMono << "-" << localAvg;
762
763 // At this point we can update the member masses;
764
765 24 m_mono = localMono;
766 24 m_avg = localAvg;
767
768 24 m_isIonized = false;
769
770 // If something changed in the masses, then return 1, otherwise
771 // return 0.
772
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
24 if(temp != *this)
773 24 return 1;
774
775 return 0;
776 24 }
777
778 /*!
779 \brief Returns the molecular mass (either monoisotopic or average, depending
780 on \a mass_type)
781
782 A copy of this Ionizable is first made, then it is deionized and its mass is
783 returned.
784 */
785 double
786 8 Ionizable::molecularMass(MassType mass_type)
787 {
788
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 Ionizable temp(*this);
789
790
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 if(!temp.deionize())
791 return -1;
792
793
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 return temp.mass(mass_type);
794 8 }
795
796 /*!
797 \brief Validates this Ionizable.
798
799 If this Ionizable is actually in an ionized state, then the member IonizeRule
800 is validated. Returns true if the IonizeRule validates successfully.
801
802 If this Ionizable is not in an ionized state, the member IonizeRule
803 is not validated and this function returns true.
804
805 The idea is that an Ionizable may well be in a state that is not ionized,
806 in which case checking the validity of the member IonizeRule is not
807 meaningful.
808 */
809 bool
810 8 Ionizable::validate()
811 {
812
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if(!PolChemDefEntity::validate())
813 return false;
814
815 // If this Ionizable is ionized, then it is an error if the
816 // m_ionizeRule is not valid !
817
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
8 if(m_isIonized && !m_ionizeRule.isValid())
818 return false;
819
820 8 return true;
821 }
822
823
824 /*!
825 \brief Calculates the masses of this Ionizable.
826
827 If this Ionizable is ionized, the masses of the \l Ponderable base class are
828 let unchanged. If this Ionizable is not ionized and the IonizeRule is valid,
829 perform the ionization, which calculates the masses.
830
831 This function returns false if the member IonizeRule is not valid or if the
832 ionization failed, true otherwise.
833
834 \sa ionize()
835 */
836 bool
837 Ionizable::calculateMasses()
838 {
839 if(!Ponderable::calculateMasses())
840 return false;
841
842 if(m_isIonized)
843 {
844 // Because the Ionizable is ionized, we have nothing more to
845 // do.
846
847 return true;
848 }
849 else
850 {
851 // The Ionizable is not ionized. If the IonizeRule is valid,
852 // then we just ionize it.
853 if(m_ionizeRule.isValid())
854 {
855 if(ionize() == -1)
856 return false;
857 else
858 return true;
859 }
860 }
861
862 // We should not be here.
863 return false;
864 }
865
866 /*!
867 \brief Returns a string describing this Ionizable.
868 */
869 QString
870 4 Ionizable::toString()
871 {
872 4 QString text;
873
874
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 text += m_ionizeRule.toString();
875
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 text += "\n";
876
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 text += Ponderable::monoString(6);
877
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 text += "-";
878
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 text += Ponderable::avgString(6);
879
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 text += "\n";
880
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 text += (m_isIonized ? "ionized" : "not ionized");
881
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 text += "\n";
882
883 4 return text;
884 }
885
886 } // namespace libXpertMass
887
888 } // namespace MsXpS
889