1 /* massXpert - the true massist's program. 2 -------------------------------------- 3 Copyright(C) 2006,2007 Filippo Rusconi 4 5 http://www.massxpert.org/massXpert 6 7 This file is part of the massXpert project. 8 9 The massxpert project is the successor to the "GNU polyxmass" 10 project that is an official GNU project package(see 11 www.gnu.org). The massXpert project is not endorsed by the GNU 12 project, although it is released ---in its entirety--- under the 13 GNU General Public License. A huge part of the code in massXpert 14 is actually a C++ rewrite of code in GNU polyxmass. As such 15 massXpert was started at the Centre National de la Recherche 16 Scientifique(FRANCE), that granted me the formal authorization to 17 publish it under this Free Software License. 18 19 This software is free software; you can redistribute it and/or 20 modify it under the terms of the GNU General Public 21 License version 3, as published by the Free Software Foundation. 22 23 24 This software is distributed in the hope that it will be useful, 25 but WITHOUT ANY WARRANTY; without even the implied warranty of 26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 27 General Public License for more details. 28 29 You should have received a copy of the GNU General Public License 30 along with this software; if not, write to the 31 32 Free Software Foundation, Inc., 33 34 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 35 */ 36 37 38 /////////////////////// Std includes 39 #include <cmath> 40 41 42 /////////////////////// Local includes 43 #include "pkaPhPi.hpp" 44 45 46 namespace massXpert 47 { 48 PkaPhPi(Polymer & polymer,CalcOptions & calcOptions,QList<Monomer * > * monomerList,QList<Modif * > * modifList)49 PkaPhPi::PkaPhPi(Polymer &polymer, 50 CalcOptions &calcOptions, 51 QList<Monomer *> *monomerList, 52 QList<Modif *> *modifList) 53 : m_polymer(polymer), 54 m_calcOptions(calcOptions), 55 mpa_monomerList(monomerList), 56 mpa_modifList(modifList) 57 { 58 m_ph = 7; 59 m_pi = 7; 60 m_positiveCharges = 0; 61 m_negativeCharges = 0; 62 63 mp_aborted = 0; 64 mp_progress = 0; 65 } 66 67 ~PkaPhPi()68 PkaPhPi::~PkaPhPi() 69 { 70 if (mpa_monomerList) 71 { 72 while(!mpa_monomerList->isEmpty()) 73 delete mpa_monomerList->takeFirst(); 74 75 delete mpa_monomerList; 76 mpa_monomerList = 0; 77 } 78 79 if (mpa_modifList) 80 { 81 while(!mpa_modifList->isEmpty()) 82 delete mpa_modifList->takeFirst(); 83 84 delete mpa_modifList; 85 86 mpa_modifList = 0; 87 } 88 89 } 90 91 92 void setPh(double ph)93 PkaPhPi::setPh(double ph) 94 { 95 Q_ASSERT(ph > 0 && ph < 14); 96 97 m_ph = ph; 98 } 99 100 101 double ph()102 PkaPhPi::ph() 103 { 104 return m_ph; 105 } 106 107 108 double pi()109 PkaPhPi::pi() 110 { 111 return m_pi; 112 } 113 114 115 double positiveCharges()116 PkaPhPi::positiveCharges() 117 { 118 return m_positiveCharges; 119 } 120 121 122 double negativeCharges()123 PkaPhPi::negativeCharges() 124 { 125 return m_negativeCharges; 126 } 127 128 129 void setCalcOptions(const CalcOptions & calcOptions)130 PkaPhPi::setCalcOptions(const CalcOptions &calcOptions) 131 { 132 m_calcOptions = calcOptions; 133 } 134 135 136 void setMonomerList(QList<Monomer * > * monomerList)137 PkaPhPi::setMonomerList(QList<Monomer *> *monomerList) 138 { 139 Q_ASSERT(monomerList); 140 141 mpa_monomerList = monomerList; 142 } 143 144 145 void setModifList(QList<Modif * > * modifList)146 PkaPhPi::setModifList(QList<Modif *> *modifList) 147 { 148 Q_ASSERT(modifList); 149 150 mpa_modifList = modifList; 151 } 152 153 154 int calculateCharges()155 PkaPhPi::calculateCharges() 156 { 157 int processedChemicalGroups = 0; 158 159 m_positiveCharges = 0; 160 m_negativeCharges = 0; 161 162 // We of course need monomers ! Instead, we may not need modifs. 163 if (!mpa_monomerList) 164 return -1; 165 166 int polymerSize = m_polymer.size(); 167 168 // The general scheme is : 169 170 // Get the list of the coordinates of the different region 171 // selections. For each first monomer and end monomer of a given 172 // region selection, check if the the region is an oligomer or a 173 // residual chain(m_selectionType of CalcOptions); act 174 // accordingly. Also, check for each selection region if it 175 // encompasses the polymer left/right end. If the left/right end 176 // modifications are to be taken into account, act accordingly. 177 178 const CoordinateList &coordinateList = m_calcOptions.coordinateList(); 179 180 for (int iter = 0; iter < coordinateList.size(); ++iter) 181 { 182 Coordinates *coordinates = coordinateList.at(iter); 183 184 int startIndex = coordinates->start(); 185 int endIndex = coordinates->end(); 186 187 bool leftMostCoordinates = 188 coordinateList.isLeftMostCoordinates(coordinates); 189 bool rightMostCoordinates = 190 coordinateList.isRightMostCoordinates(coordinates); 191 192 for(int jter = startIndex; jter < endIndex + 1; ++jter) 193 { 194 const Monomer *seqMonomer = m_polymer.at(jter); 195 196 // qDebug() << __FILE__ << __LINE__ 197 // << "-- Monomer:" << seqMonomer->name() 198 // << "position:" << jter + 1; 199 200 // Find a monomer by the same code in our list of monomers 201 // that have been fed with chemical group data. Note that 202 // all the monomers in a given sequence must not 203 // necessarily have a counterpart in the local list of 204 // monoemers. For example, there might be cases in which a 205 // given monomer might not bring any charge whatsoever. 206 207 int index = Monomer::isCodeInList(seqMonomer->code(), 208 *mpa_monomerList); 209 if (index == -1) 210 return -1; 211 212 const Monomer *monomer = mpa_monomerList->at(index); 213 Q_ASSERT(monomer); 214 215 // A monomer can have multiple such "CHEMICAL_GROUP" 216 // properties. Indeed, for example for proteins, a monomer 217 // might have three such chemical groups(and thus three 218 // Prop objects): one for the alpha NH2, one for the alpha 219 // COOH and one for a residual chain chemical group, like 220 // epsilon NH2 for lysine. 221 222 for (int kter = 0; kter < monomer->propList().size(); ++kter) 223 { 224 Prop *prop = monomer->propList().at(kter); 225 226 if(prop->name() != "CHEMICAL_GROUP") 227 continue; 228 229 // qDebug() << __FILE__ << __LINE__ 230 // << "Monomer has property CHEMICAL_GROUP..."; 231 232 // Get the chemical group out of the property. 233 234 ChemicalGroup *chemicalGroup = 235 static_cast<ChemicalGroup *>(prop->data()); 236 237 if(chemicalGroup->polRule() & MXP_CHEMGROUP_LEFT_TRAPPED) 238 { 239 // qDebug() << __FILE__ << __LINE__ 240 // << "... that is MXP_CHEMGROUP_LEFT_TRAPPED"; 241 242 // The chemical group we are dealing with is trapped 243 // when the monomer is polymerized on the left end, that 244 // is if the monomer is not the left end monomer of the 245 // sequence being analyzed. 246 247 // Thus we only can take it into account if one of 248 // two conditions are met: 249 250 // 1. The monomer is the left end monomer of the 251 // whole polymer sequence. 252 253 // 2. The monomer is the left end monomer of the 254 // region selection AND the selection type is 255 // oligomers(thus it does not get polymerized to 256 // the previous selection region). 257 258 if (jter > 0) 259 { 260 // Clearly we are not dealing with the left 261 // end of the polymer, so check if we have to 262 // account for this chemical group or not. 263 264 if(!leftMostCoordinates) 265 { 266 // The current Coordinates is not the 267 // left-most Coordinates in the 268 // CoordinateList, thus we cannot consider 269 // it to be the "left end coordinates" of 270 // the CoordinateList. Just continue 271 // without exploring any more. 272 continue; 273 } 274 if(jter == startIndex) 275 { 276 // The current monomer is the first 277 // monomer of Coordinates. We only take 278 // into account the chemical group if each 279 // Coordinates is to be considered an 280 // oligomer. 281 282 if (m_calcOptions.selectionType() != 283 SELECTION_TYPE_OLIGOMERS) 284 continue; 285 } 286 } 287 } 288 289 if(chemicalGroup->polRule() & MXP_CHEMGROUP_RIGHT_TRAPPED) 290 { 291 // qDebug() << __FILE__ << __LINE__ 292 // << "... that is MXP_CHEMGROUP_RIGHT_TRAPPED"; 293 294 // See explanations above. 295 296 if (jter < polymerSize - 1) 297 { 298 // Clearly, we are not dealing with the right 299 // end of the polymer. 300 301 if(!rightMostCoordinates) 302 { 303 // The current Coordinates is not the 304 // right-most Coordinates of the 305 // CoordinateList, thus we cannot consider 306 // it to be the "right end coordinates" of 307 // the CoordinateList. Just continue 308 // without exploring anymore. 309 continue; 310 } 311 if(jter == endIndex) 312 { 313 // The current monomer is the last monomer 314 // of Coordinates. We only take into 315 // account the chemical group if each 316 // Coordinates is to be considered an 317 // oligomer(and not a residual chain). 318 319 if (m_calcOptions.selectionType() != 320 SELECTION_TYPE_OLIGOMERS) 321 continue; 322 } 323 } 324 } 325 326 if(iter == 0 && 327 m_calcOptions.polymerEntities() & 328 MXT_POLYMER_CHEMENT_LEFT_END_MODIF) 329 { 330 // We are iterating in the monomer that is at the 331 // beginning of the polymer sequence. We should 332 // test if the chemical group we are dealing with 333 // right now has a special rule for the left end 334 // of the polymer sequence region. 335 336 int ret = 337 accountPolymerEndModif(MXT_POLYMER_CHEMENT_LEFT_END_MODIF, 338 *chemicalGroup); 339 if (ret >= 0) 340 { 341 // qDebug() << __FILE__ << __LINE__ 342 // << "Accounted for left end modif."; 343 344 processedChemicalGroups += ret; 345 continue; 346 } 347 } 348 349 if(iter == polymerSize -1 && 350 m_calcOptions.polymerEntities() & 351 MXT_POLYMER_CHEMENT_RIGHT_END_MODIF) 352 { 353 int ret = 354 accountPolymerEndModif(MXT_POLYMER_CHEMENT_RIGHT_END_MODIF, 355 *chemicalGroup); 356 if (ret >= 0) 357 { 358 // qDebug() << __FILE__ << __LINE__ 359 // << "Accounted for right end modif."; 360 361 processedChemicalGroups += ret; 362 continue; 363 } 364 } 365 366 if(m_calcOptions.monomerEntities() & 367 MXT_MONOMER_CHEMENT_MODIF && 368 seqMonomer->isModified()) 369 { 370 int ret = accountMonomerModif(*seqMonomer, *chemicalGroup); 371 if (ret >= 0) 372 { 373 // qDebug() << __FILE__ << __LINE__ 374 // << "Accounted for monomer modif."; 375 376 processedChemicalGroups += ret; 377 continue; 378 } 379 } 380 381 double charge = 382 calculateChargeRatio(chemicalGroup->pka(), 383 chemicalGroup->isAcidCharged()); 384 385 // qDebug() << __FILE__ << __LINE__ 386 // << "Charge:" << charge; 387 388 if(charge < 0) 389 m_negativeCharges += charge; 390 else if (charge > 0) 391 m_positiveCharges += charge; 392 393 // qDebug() << __FILE__ << __LINE__ 394 // << "Pos =" << m_positiveCharges 395 // << "Neg = " << m_negativeCharges; 396 397 ++processedChemicalGroups; 398 } 399 // End of 400 // for (int kter = 0; kter < monomer->propList().size(); ++kter) 401 402 // qDebug() << __FILE__ << __LINE__ 403 // << "End dealing with Monomer:" << seqMonomer->name() 404 // << "position:" << jter + 1; 405 } 406 // End of 407 // for (int jter = startIndex; jter < endIndex + 1; ++jter) 408 409 // qDebug() << __FILE__ << __LINE__ 410 // << "End dealing with Coordinates"; 411 } 412 // End of 413 // for (int iter = 0; iter < coordinateList.size(); ++iter) 414 415 // We have finished processing all the Coordinates in the list. 416 417 return processedChemicalGroups; 418 } 419 420 421 int calculatePi()422 PkaPhPi::calculatePi() 423 { 424 int processedChemicalGroups = 0; 425 int iteration = 0; 426 427 double netCharge = 0; 428 double firstCharge = 0; 429 double thirdCharge = 0; 430 431 // We of course need monomers ! Instead, we may not need modifs. 432 if (!mpa_monomerList) 433 { 434 m_pi = 0; 435 m_ph= 0; 436 437 return -1; 438 } 439 440 m_ph = 0; 441 442 while(true) 443 { 444 // qDebug() << "Current pH being tested:" << m_ph; 445 446 processedChemicalGroups = calculateCharges(); 447 448 if(processedChemicalGroups == -1) 449 { 450 qDebug() << "Failed to calculate net charge for pH" << m_ph; 451 452 m_pi = 0; 453 m_ph= 0; 454 455 return -1; 456 } 457 458 netCharge = m_positiveCharges + m_negativeCharges; 459 460 // Note that if the 0.01 tested_ph step is enough to switch the 461 // net charge from one excess value to another excess value in 462 // the opposite direction, we'll enter an infinite loop. 463 // 464 // The evidence for such loop is that every other two measures, 465 // the net charge of the polymer sequence will be the same. 466 // 467 // Here we test this so that we can break the loop. 468 469 470 ++iteration; 471 472 if(iteration == 1) 473 { 474 firstCharge = netCharge; 475 } 476 else if (iteration == 3) 477 { 478 thirdCharge = netCharge; 479 480 if (firstCharge == thirdCharge) 481 break; 482 483 iteration = 0; 484 485 firstCharge = netCharge; 486 } 487 488 // At this point we have to test the net charge: 489 490 if(netCharge >= -0.1 && netCharge <= 0.1) 491 { 492 // qDebug() << "Breaking loop with netCharge:" << netCharge; 493 494 break; 495 } 496 497 if(netCharge > 0) 498 { 499 // The test ph is too low. 500 501 m_ph += 0.01; 502 // qDebug() << "Set new pH m_ph += 0.01:" << m_ph 503 // << "netCharge:" << netCharge; 504 505 continue; 506 } 507 508 if(netCharge < 0) 509 { 510 // The test ph is too high. 511 512 m_ph -= 0.01; 513 // qDebug() << "Set new pH m_ph -= 0.01:" << m_ph 514 // << "netCharge:" << netCharge; 515 516 continue; 517 } 518 } 519 // End of 520 // while(true) 521 522 // At this point m_pi is m_ph. 523 524 m_pi = m_ph; 525 // qDebug() << "pi is:" << m_pi; 526 527 528 return processedChemicalGroups; 529 } 530 531 532 double calculateChargeRatio(double pka,bool acidCharged)533 PkaPhPi::calculateChargeRatio(double pka, bool acidCharged) 534 { 535 double aOverAh = 0; 536 537 if (pka < 0) 538 return 0; 539 if (pka > 14) 540 return 0; 541 542 if (m_ph < 0) 543 return 0; 544 if (m_ph > 14) 545 return 0; 546 547 548 // Example with pKa = 4.25(Glu) ; pH = 4.16, thus we are more 549 // acidic than pKa, we expect AH to be greater than A by a small 550 // margin. 551 552 aOverAh =(double) pow(10,(m_ph - pka)); 553 // aOverAh = 0.81283051616409951(confirmed manually) 554 555 if (aOverAh < 1) 556 { 557 /* The solution contains more acid forms(AH) than basic forms 558 (A). 559 */ 560 if(acidCharged) 561 return(1 - aOverAh); 562 else 563 // The acid is not charged, that is, it is a COOH. 564 // AH = 1 - A 565 // A = aOverAh.AH 566 // A = aOverAh.(1-A) 567 // A = aOverAh - aOverAh.A 568 // A(1+aOverAh) = aOverAh 569 // A = aOverAh /(1+aOverAh), for us this is 570 // A = 0.81283 / 1.81283 = 0.448 571 572 // And not - aOverAh, that is - aOverAh. 573 574 // Below seems faulty(20071204) 575 // return(- aOverAh); 576 577 // Tentative correction(20071204) 578 return(-(aOverAh /(1 + aOverAh))); 579 } 580 581 else if (aOverAh > 1) 582 { 583 /* The solution contains more basic forms(A) than acid forms 584 (AH). 585 */ 586 if(acidCharged) 587 return(1 / aOverAh); 588 else 589 return(-(1 -(1 / aOverAh))); 590 } 591 else if (aOverAh == 1) 592 { 593 /* The solution contains as many acid forms(AH) as basic forms 594 (H). 595 */ 596 if(acidCharged) 597 return(aOverAh / 2); 598 else 599 return(- aOverAh / 2); 600 } 601 else 602 Q_ASSERT(0); 603 604 return 0; 605 } 606 607 608 int accountPolymerEndModif(int endModif,const ChemicalGroup & group)609 PkaPhPi::accountPolymerEndModif(int endModif, 610 const ChemicalGroup &group) 611 { 612 QString modifName; 613 ChemicalGroupRule *rule = 0; 614 615 int count = 0; 616 617 // Get the name of the modification of the polymer(if any) and get 618 // the rule dealing with that polymer modification(if any). 619 620 if (endModif == MXT_POLYMER_CHEMENT_LEFT_END_MODIF) 621 { 622 modifName = m_polymer.leftEndModif().name(); 623 624 rule = group.findRule("LE_PLM_MODIF", modifName); 625 626 // Remember a chemical group is defined like this: 627 628 // <mnmchemgroup> 629 // <name>N-term NH2</name> 630 // <pka>9.6</pka> 631 // <acidcharged>TRUE</acidcharged> 632 // <polrule>left_trapped</polrule> 633 // <chemgrouprule> 634 // <entity>LE_PLM_MODIF</entity> 635 // <name>Acetylation</name> 636 // <outcome>LOST</outcome> 637 // </chemgrouprule> 638 // </mnmchemgroup> 639 640 } 641 else if (endModif == MXT_POLYMER_CHEMENT_RIGHT_END_MODIF) 642 { 643 modifName = m_polymer.rightEndModif().name(); 644 645 rule = group.findRule("RE_PLM_MODIF", modifName); 646 } 647 else 648 Q_ASSERT(0); 649 650 651 // The polymer might not be modified, and also the chemical group 652 // passed as parameter might not contain any rule about any polymer 653 // modification. In that case we just have nothing to do. 654 655 if (modifName.isEmpty()) 656 { 657 if(rule) 658 { 659 double charge = calculateChargeRatio(group.pka(), 660 group.isAcidCharged()); 661 if (charge < 0) 662 m_negativeCharges += charge; 663 else if (charge > 0) 664 m_positiveCharges += charge; 665 666 return ++count; 667 } 668 else 669 { 670 // The polymer end was NOT modified and the chemical group 671 // was NOT eligible for a polymer end modification. This 672 // means that we do not have to process it, and we return -1 673 // so that the caller function knows we did not do anything 674 // and that this chemical group should continue to undergo 675 // analysis without skipping it. 676 677 return -1; 678 } 679 } 680 // End of 681 // if (modifName.isEmpty()) 682 683 if (!rule) 684 { 685 // This chemical group was not "designed" to receive any polymer 686 // end modification, so we have nothing to do with it and we 687 // return -1 so that the caller function knows we did not do 688 // anything and that this chemical group should continue to 689 // undergo analysis without skipping it. 690 691 return -1; 692 } 693 694 // At this point we know that the chemical group 'group' we are 695 // analyzing is eligible for a polymer left end modification and 696 // that it is indeed modified with a correcct modification. So we 697 // have a rule for it. Let's continue the analysis. 698 699 // Apparently the rule has data matching the ones we are looking 700 // for. At this point we should now what action to take for this 701 // group. 702 703 if (rule->outcome() == MXP_CHEMGROUP_RULE_LOST) 704 { 705 // We do not use the current chemical group 'group' because the 706 // polymer end's modification has abolished it. 707 } 708 else if (rule->outcome() == MXP_CHEMGROUP_RULE_PRESERVED) 709 { 710 double charge = calculateChargeRatio(group.pka(), 711 group.isAcidCharged()); 712 if(charge < 0) 713 m_negativeCharges += charge; 714 else if (charge > 0) 715 m_positiveCharges += charge; 716 717 return ++count; 718 } 719 else 720 Q_ASSERT(0); 721 722 // Whatever we should do with the left/right end monomer's chemgroup, 723 // we should take into account the modification itself that might 724 // have brought chemgroup(s) worth calculating their intrinsic 725 // charges! 726 727 // Find a modif object in the local list of modif objects, that has 728 // the same name as the modification with which the left/right end 729 // of the polymer is modified. We'll see what chemgroup(s) this 730 // modification brings to the polymer sequence. 731 732 int index = Modif::isNameInList(modifName, *mpa_modifList); 733 734 if (index == -1) 735 { 736 // qDebug() << __FILE__ << __LINE__ 737 // << "Information: following modif not in local list:" 738 // << modifName; 739 740 return count; 741 } 742 743 const Modif *modif = mpa_modifList->at(index); 744 Q_ASSERT(modif); 745 746 for (int jter = 0; jter < modif->propList().size(); ++jter) 747 { 748 Prop *prop = modif->propList().at(jter); 749 750 if(prop->name() != "CHEMICAL_GROUP") 751 continue; 752 753 // Get the chemical group out of the property. 754 755 const ChemicalGroup *chemicalGroup = 756 static_cast<const ChemicalGroup *>(prop->data()); 757 758 double charge = 759 calculateChargeRatio(chemicalGroup->pka(), 760 chemicalGroup->isAcidCharged()); 761 if(charge < 0) 762 m_negativeCharges += charge; 763 else if (charge > 0) 764 m_positiveCharges += charge; 765 766 ++count; 767 } 768 769 return count; 770 } 771 772 773 int accountMonomerModif(const Monomer & monomer,ChemicalGroup & group)774 PkaPhPi::accountMonomerModif(const Monomer &monomer, 775 ChemicalGroup &group) 776 { 777 QString modifName; 778 ChemicalGroupRule *rule = 0; 779 780 int count = 0; 781 782 // For each modification in the monomer, make the accounting work. 783 784 Q_ASSERT(mpa_modifList); 785 Q_ASSERT(mpa_modifList->size()); 786 787 for (int iter = 0; iter < monomer.modifList()->size(); ++iter) 788 { 789 Modif *iterModif = monomer.modifList()->at(iter); 790 791 // Get the name of the modification of the monomer(if any) and get 792 // the rule dealing with that monomer modification(if any). 793 794 modifName = iterModif->name(); 795 796 rule = group.findRule("MONOMER_MODIF", modifName); 797 798 if(modifName.isEmpty()) 799 { 800 // The monomer does not seem to be modified. However, we still 801 // have to make sure that the chemgroup that we were parsing is 802 // actually a chemgroup suitable for a modif. If this chemgroup 803 // was actually suitable for a monomer modif, but it is not 804 // effectively modified, that means that we have to calculate 805 // the charge for the non-modified chemgroup... 806 807 if (rule) 808 { 809 double charge = calculateChargeRatio(group.pka(), 810 group.isAcidCharged()); 811 if(charge < 0) 812 m_negativeCharges += charge; 813 else if (charge > 0) 814 m_positiveCharges += charge; 815 816 return ++count; 817 } 818 else 819 { 820 // The current monomer was NOT modified, and the chemgroup 821 // was NOT eligible for a monomer modification. This means 822 // that we do not have to process it, and we return -1 so 823 // that the caller function knows we did not do anything and 824 // that this chemgroup should continue to undergo analysis 825 // without skipping it. 826 827 return -1; 828 } 829 } 830 // End of 831 // if (modifName.isEmpty()) 832 833 if(!rule) 834 { 835 // This chemgroup was not "designed" to receive any 836 // modification, so we have nothing to do with it, and we return 837 // -1 to let the caller know that its processing should be 838 // continued in the caller's function space. 839 840 return -1; 841 } 842 843 // At this point, we know that the chemgroup we are analyzing is 844 // eligible for a modification and that we have a rule for it. Let's 845 // continue the analysis: 846 847 // Apparently, a rule object has member data matching the ones we 848 // were looking for. At this point we should know what action to 849 // take for this chemgroup. 850 851 if(rule->outcome() == MXP_CHEMGROUP_RULE_LOST) 852 { 853 // We do not use the current chemical group 'group' because the 854 // monomer modification has abolished it. 855 } 856 else if (rule->outcome() == MXP_CHEMGROUP_RULE_PRESERVED) 857 { 858 double charge = calculateChargeRatio(group.pka(), 859 group.isAcidCharged()); 860 if (charge < 0) 861 m_negativeCharges += charge; 862 else if (charge > 0) 863 m_positiveCharges += charge; 864 865 return ++count; 866 } 867 else 868 Q_ASSERT(0); 869 870 // Whatever we should do with this monomer's chemgroup, we should 871 // take into account the modification itself that might have brought 872 // chemgroup(s) worth calculating their intrinsic charges! 873 874 // Find a modif object in the local list of modif objects, that has 875 // the same name as the modification with which the monomer is 876 // modified. We'll see what chemgroup(s) this modification brings to 877 // the polymer sequence. 878 879 int index = Modif::isNameInList(modifName, *mpa_modifList); 880 881 if(index == -1) 882 { 883 // qDebug() << __FILE__ << __LINE__ 884 // << "Information: following modif not in local list:" 885 // << modifName; 886 887 return count; 888 } 889 890 Modif *modif = mpa_modifList->at(index); 891 Q_ASSERT(modif); 892 893 for(int jter = 0; jter < modif->propList().size(); ++jter) 894 { 895 Prop *prop = modif->propList().at(jter); 896 897 if (prop->name() != "CHEMICAL_GROUP") 898 continue; 899 900 // Get the chemical group out of the property. 901 902 const ChemicalGroup *chemicalGroup = 903 static_cast<const ChemicalGroup *>(prop->data()); 904 905 double charge = 906 calculateChargeRatio(chemicalGroup->pka(), 907 chemicalGroup->isAcidCharged()); 908 if (charge < 0) 909 m_negativeCharges += charge; 910 else if (charge > 0) 911 m_positiveCharges += charge; 912 913 ++count; 914 } 915 } 916 917 return count; 918 } 919 920 } // namespace massXpert 921