libatomprobe
Library for Atom Probe Tomography (APT) computation
abundance.cpp
Go to the documentation of this file.
1 /*
2  * abundance.cpp : module to load, manipulate and compute natural abundance information
3  * Copyright (C) 2015 D Haley
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <iostream>
20 #include <cstdlib>
21 #include <stack>
22 #include <map>
23 #include <cmath>
24 #include <limits>
25 #include <algorithm>
26 #include <numeric>
27 
31 #include "atomprobe/helper/misc.h"
33 
34 
35 #include "helper/helpFuncs.h"
36 
38 
39 namespace AtomProbe
40 {
41 
42 
43 using std::vector;
44 using std::pair;
45 using std::make_pair;
46 using std::map;
47 using std::string;
48 
49 const char *ABUNDANCE_ERROR[] = { "Unable to read abundance data (opening file)",
50  "Unable to create XML reader.",
51  "Bad property found in XML file",
52  "XML document did not match expected layout (DTD validation)",
53  "Unable to find required node during parse",
54  "Root node missing, expect <atomic-mass-table>!",
55  "Found incorrect root node. Expected <atomic-mass-table>"
56 };
57 
58 
59 const char *AbundanceData::getErrorText(size_t errorCode)
60 {
61  ASSERT(errorCode < ABUNDANCE_ERROR_ENUM_END);
62  return ABUNDANCE_ERROR[errorCode];
63 }
64 
66 {
67  size_t v=0;
68  for(size_t ui=0;ui<isotopeData.size();ui++)
69  v+=isotopeData[ui].size();
70 
71  return v;
72 }
73 
75 {
76  return isotopeData.size();
77 }
78 
79 size_t AbundanceData::open(const char *file, bool strict)
80 {
81 
82  xmlDocPtr doc;
83  xmlParserCtxtPtr context;
84 
85  context =xmlNewParserCtxt();
86 
87  if(!context)
88  return ABUNDANCE_ERR_NO_CONTEXT;
89 
90  //Open the XML file
91  doc = xmlCtxtReadFile(context, file, NULL, XML_PARSE_DTDVALID| XML_PARSE_NOENT | XML_PARSE_NONET);
92 
93  if(!doc)
94  {
95  xmlFreeParserCtxt(context);
96  return ABUNDANCE_ERR_BAD_DOC;
97  }
98  else
99  {
100  //Check for context validity
101  if(!context->valid)
102  {
103  if(strict)
104  {
105  xmlFreeDoc(doc);
106  xmlFreeParserCtxt(context);
107  return ABUNDANCE_ERROR_FAILED_VALIDATION;
108  }
109  }
110  }
111 
112  try
113  {
114 
115  //retrieve root node
116  std::stack<xmlNodePtr> nodeStack;
117  xmlNodePtr nodePtr = xmlDocGetRootElement(doc);
118 
119  if(!nodePtr)
120  throw ABUNDANCE_ERROR_MISSING_ROOT_NODE;
121 
122  //This *should* be an abundance file
123  if(xmlStrcmp(nodePtr->name, (const xmlChar *)"atomic-mass-table"))
124  throw ABUNDANCE_ERROR_WRONG_ROOT_NODE;
125 
126  nodeStack.push(nodePtr);
127 
128  nodePtr=nodePtr->xmlChildrenNode;
129  while(!XMLHelpFwdToElem(nodePtr,"entry"))
130  {
131  ISOTOPE_ENTRY curIsoData;
132 
133  if(XMLHelpGetProp(curIsoData.symbol,nodePtr,"symbol"))
134  throw ABUNDANCE_ERROR_BAD_VALUE;
135 
136  if(XMLHelpGetProp(curIsoData.atomicNumber,nodePtr,"atomic-number"))
137  throw ABUNDANCE_ERROR_BAD_VALUE;
138 
139 
140  nodeStack.push(nodePtr);
141  nodePtr=nodePtr->xmlChildrenNode;
142 
143  //Move to natural-abundance child
144  if(XMLHelpFwdToElem(nodePtr,"natural-abundance"))
145  throw ABUNDANCE_ERROR_MISSING_NODE;
146 
147  nodePtr=nodePtr->xmlChildrenNode;
148 
149  vector<ISOTOPE_ENTRY> curIsotopes;
150  //TODO: value checking
151  while(!XMLHelpFwdToElem(nodePtr,"isotope"))
152  {
153  //Spin to mass node
154  if(XMLHelpGetProp(curIsoData.massNumber,nodePtr,"mass-number"))
155  throw ABUNDANCE_ERROR_MISSING_NODE;
156 
157  nodeStack.push(nodePtr);
158  nodePtr=nodePtr->xmlChildrenNode;
159 
160  //Spin to mass node
161  if(XMLHelpFwdToElem(nodePtr,"mass"))
162  throw ABUNDANCE_ERROR_MISSING_NODE;
163 
164  if(XMLHelpGetProp(curIsoData.mass,nodePtr,"value"))
165  throw ABUNDANCE_ERROR_BAD_VALUE;
166 
167  if(XMLHelpGetProp(curIsoData.massError,nodePtr,"error"))
168  throw ABUNDANCE_ERROR_BAD_VALUE;
169  else
170  curIsoData.massError=0;
171 
172 
173  //spin to abundance node
174  if(XMLHelpFwdToElem(nodePtr,"abundance"))
175  throw ABUNDANCE_ERROR_MISSING_NODE;
176 
177  if(XMLHelpGetProp(curIsoData.abundance,nodePtr,"value"))
178  throw ABUNDANCE_ERROR_BAD_VALUE;
179 
180  if(XMLHelpGetProp(curIsoData.abundanceError,nodePtr,"error"))
181  throw ABUNDANCE_ERROR_BAD_VALUE;
182  else
183  curIsoData.abundanceError=0;
184 
185  curIsotopes.push_back(curIsoData);
186 
187  nodePtr=nodeStack.top();
188  nodePtr=nodePtr->next;
189  nodeStack.pop();
190  }
191 
192  isotopeData.push_back(curIsotopes);
193  curIsotopes.clear();
194 
195  nodePtr=nodeStack.top();
196  nodeStack.pop();
197  nodePtr=nodePtr->next;
198 
199  }
200  }
201  catch( int &excep)
202  {
203  xmlFreeDoc(doc);
204  xmlFreeParserCtxt(context);
205  return excep;
206  }
207 
208  xmlFreeDoc(doc);
209  xmlFreeParserCtxt(context);
210  return 0;
211 }
212 
213 
214 size_t AbundanceData::symbolIndex(const char *symbol, bool caseSensitive) const
215 {
216  ASSERT(isotopeData.size());
217  if(!caseSensitive)
218  {
219  for(size_t ui=0;ui<isotopeData.size();ui++)
220  {
221  if(isotopeData[ui].size() && lowercase(isotopeData[ui][0].symbol) == lowercase(symbol) )
222  return ui;
223  }
224  }
225  else
226  {
227  for(size_t ui=0;ui<isotopeData.size();ui++)
228  {
229  if(isotopeData[ui].size() && isotopeData[ui][0].symbol == symbol)
230  return ui;
231  }
232  }
233 
234  return (size_t)-1;
235 }
236 
237 
238 void AbundanceData::isotopeIndex(size_t elemIn, float mass ,size_t &isoIndex) const
239 {
240  ASSERT(isotopeData.size());
241  for(size_t uj=0;uj<isotopeData[elemIn].size();uj++)
242  {
243  if(isotopeData[elemIn][uj].mass == mass)
244  {
245  isoIndex=uj;
246  return;
247  }
248  }
249 
250  isoIndex=(size_t)-1;
251  return;
252 }
253 
254 void AbundanceData::isotopeIndex(size_t elem, float mass, float tolerance, size_t &isoIndex) const
255 {
256  ASSERT(isotopeData.size());
257  for(size_t uj=0;uj<isotopeData[elem].size();uj++)
258  {
259  if(fabs(isotopeData[elem][uj].mass - mass) < tolerance)
260  {
261  isoIndex=uj;
262  return;
263  }
264  }
265 
266  isoIndex=(size_t)-1;
267  return;
268 }
269 
270 
271 const ISOTOPE_ENTRY &AbundanceData::isotope(size_t elemIdx,size_t isotopeIdx) const
272 {
273  ASSERT(isotopeData.size());
274  return isotopeData[elemIdx][isotopeIdx];
275 }
276 
277 void AbundanceData::generateIsotopeDist(const vector<size_t> &elementIdx,
278  const vector<size_t> &frequency,
279  vector<pair<float,float> > &massDist) const
280 {
281  ASSERT(isotopeData.size());
282  ASSERT(frequency.size() == elementIdx.size());
283  //Search out the given isotopes, and compute the peaks
284  // that would be seen for this isotope combination
285 
286  //map of vectors for the available isotopes
287  map<unsigned int, vector< pair<float,float> > > isotopeMassDist;
288 
289  //For each isotope, retrieve its (mass,probability dist) values
290  // placing into a map
291  //--
292  for(size_t ui=0;ui<elementIdx.size();ui++)
293  {
294  size_t curIso;
295  curIso = elementIdx[ui];
296 
297  //If we have seen isotope before, move on
298  if(isotopeMassDist.find(curIso) != isotopeMassDist.end())
299  continue;
300 
301  vector<pair<float,float> > thisIsotope;
302  for(size_t uj=0;uj<isotopeData[curIso].size();uj++)
303  {
304  //Compute the probability of this isotope's presence in
305  // the sampled concentration
306  thisIsotope.push_back(make_pair(isotopeData[curIso][uj].mass,
307  isotopeData[curIso][uj].abundance));
308  }
309 
310  isotopeMassDist[curIso] = thisIsotope;
311  thisIsotope.clear();
312 
313  }
314  //--
315 
316 
317  //Given the isotopes we have, permute the mass spectra
318  vector<pair<float,float> > peakProbs;
319  for(size_t ui=0;ui<elementIdx.size();ui++) //Loop over element type
320  {
321 
322  for(size_t repeat=0;repeat<frequency[ui];repeat++) //Loop over the numberof times element occurs
323  {
324  vector< pair<float,float> >::const_iterator isoBegin,isoEnd;
325  isoBegin = isotopeMassDist[elementIdx[ui]].begin();
326  isoEnd = isotopeMassDist[elementIdx[ui]].end();
327 
328  vector<pair<float,float> > newProbs;
329  //If this is the very first item in our list,
330  // simply push on the value, rather than modifying the
331  // distribution
332  if(peakProbs.empty())
333  {
334  //The masses will be added to, and the probabilities multiplied
335  for(vector<pair<float,float> >::const_iterator it=isoBegin;
336  it!=isoEnd;++it)
337  peakProbs.push_back(*it);
338  }
339  else
340  {
341  const unsigned int peakProbSize = peakProbs.size();
342  newProbs.resize(peakProbSize*isotopeMassDist[elementIdx[ui]].size());
343  unsigned int offset=0;
344 
345  for(size_t uj=0;uj<peakProbSize;uj++) //Loop over mass distribution
346  {
347 
348  //Convolve it with other peaks
349  //The masses will be added to, and the probabilities multiplied
350  for(vector<pair<float,float> >::const_iterator it=isoBegin;
351  it!=isoEnd;++it)
352  {
353  pair<float,float> newMass;
354 
355  newMass.first=peakProbs[uj].first+it->first;
356  newMass.second=peakProbs[uj].second*it->second;
357  newProbs[offset]=newMass;
358  offset++;
359  }
360 
361  }
362 
363  peakProbs.swap(newProbs);
364  newProbs.clear();
365  }
366  }
367  }
368 
369 
370  float tolerance=sqrt(std::numeric_limits<float>::epsilon());
371 
372  vector<bool> killPeaks(peakProbs.size(),false);
373  const unsigned int peakProbSize=peakProbs.size();
374 #pragma omp parallel for
375  //Find the non-unique peaks and sum them
376  for(size_t ui=0;ui<peakProbSize;ui++)
377  {
378  for(size_t uj=ui+1;uj<peakProbSize;uj++)
379  {
380  if(fabs(peakProbs[ui].first-peakProbs[uj].first) <tolerance &&
381  peakProbs[uj].second > 0.0f)
382  {
383 
384  peakProbs[ui].second+=peakProbs[uj].second;
385  peakProbs[uj].second=0.0f;
386  killPeaks[uj]=true;
387  }
388  }
389  }
390 
391  vectorMultiErase(peakProbs,killPeaks);
392  massDist.swap(peakProbs);
393 }
394 
395 void AbundanceData::generateGroupedIsotopeDist(const vector<size_t> &elementIdx,
396  const vector<size_t> &frequency, vector<pair<float,float> > &massDist,
397  float massTolerance) const
398 {
399  //First, generate the isotope distribtion
400  generateIsotopeDist(elementIdx,frequency, massDist);
401 
402  //We can't group zero or 1 results
403  if(massDist.size() < 2)
404  return;
405 
406  //Now sort the isotope distribution by mass
407  ComparePairFirst cmp;
408  std::sort(massDist.begin(),massDist.end(),cmp);
409 
410  //Now we have to accumulate overlapping entries.
411 
412  vector<vector<pair<float,float> > > overlappingEntries;
413 
414  vector<pair<float,float> > curGroup;
415  curGroup.push_back(massDist[0]);
416 
417  float threshPos=massDist[0].first + massTolerance;
418  for(unsigned int ui=1;ui<massDist.size(); ui++)
419  {
420  if(massDist[ui].first > threshPos)
421  {
422  //Terminate the current group
423  overlappingEntries.push_back(curGroup);
424  curGroup.clear();
425 
426 
427  }
428  curGroup.push_back(massDist[ui]);
429  threshPos=massDist[ui].first + massTolerance;
430  }
431 
432  overlappingEntries.push_back(curGroup);
433 
434  massDist.clear();
435  //OK, so now we have each overlap set as a group, average each
436  // group's mass to provide a good mass estimate, but sum the abundance intensities
437  for(unsigned int ui=0;ui<overlappingEntries.size(); ui++)
438  {
439  vector<float> x,y;
440 
441  for(unsigned int uj=0;uj<overlappingEntries[ui].size();uj++)
442  {
443  x.push_back(overlappingEntries[ui][uj].first);
444  y.push_back(overlappingEntries[ui][uj].second);
445  }
446 
447  float wMean;
448  wMean=weightedMean(x,y);
449 
450  float summedAbundance;
451  summedAbundance=std::accumulate(y.begin(),y.end(),0.0f);
452 
453  massDist.push_back(make_pair(wMean,summedAbundance));
454 
455 
456  }
457 
458 
459 
460 
461 }
462 
463 float AbundanceData::abundanceBetweenLimits( const std::vector<size_t> &elemIdx,
464  const std::vector<size_t> &frequency,
465  float massStart, float massEnd) const
466 {
467  vector<pair<float,float> > massDist;
468  generateIsotopeDist(elemIdx,frequency,massDist);
469  float abundance=0;
470  for(const auto &isotope : massDist)
471  {
472  if(isotope.first >=massStart && isotope.first < massEnd)
473  abundance+=isotope.second;
474 
475 
476  }
477 
478  ASSERT(abundance >=0 && abundance <=1);
479  return abundance;
480 }
481 
482 size_t AbundanceData::getMajorIsotopeFromElemIdx(size_t elementIdx) const
483 {
484  ASSERT(elementIdx < isotopeData.size());
485  const std::vector<ISOTOPE_ENTRY> &curIsotopes= isotopeData[elementIdx];
486 
487  ASSERT(isotopeData.size());
488 
489  size_t maxIdx=0;
490  float maxV=curIsotopes[0].abundance;
491  for(size_t ui=1; ui<curIsotopes.size();ui++)
492  {
493  if(curIsotopes[ui].abundance > maxV )
494  {
495  maxIdx=ui;
496  maxV=curIsotopes[ui].abundance;
497  }
498  }
499 
500  return maxIdx;
501 }
502 
503 unsigned int AbundanceData::getAtomicNumber(size_t elementIdx) const
504 {
505  ASSERT(elementIdx < isotopeData.size());
506  const std::vector<ISOTOPE_ENTRY> &curIsotopes= isotopeData[elementIdx];
507 
508 #ifdef DEBUG
509  for(size_t ui=1;ui<curIsotopes.size();ui++)
510  {
511  ASSERT(curIsotopes[ui].atomicNumber == curIsotopes[0].atomicNumber);
512  }
513 #endif
514  return curIsotopes[0].atomicNumber;
515 }
516 
517 void AbundanceData::generateSingleAtomDist(size_t atomIdx, unsigned int repeatCount,
518  std::vector<std::pair<float,float> > &massDist) const
519 {
520  massDist.clear();
521 
522  //Obtain the single Isotope distribution
523  vector<pair<float,float> > singleDist;
524  const std::vector<ISOTOPE_ENTRY> &curIso=isotopeData[atomIdx];
525  for(size_t ui=0;ui<curIso.size();ui++)
526  {
527  singleDist.push_back(make_pair(curIso[ui].mass,curIso[ui].abundance));
528  }
529 
530  //Duplicate the single dist for the first atom
531  massDist=singleDist;
532 
533  //For the number of elements, branch the distribution
534  // out from this using the single mass distribution to continually
535  // create new members
536  for(size_t ui=1;ui<repeatCount;ui++)
537  {
538  vector<pair<float,float> > newDist;
539  //Add the new element to the multiDist
540  for(size_t uj=0;uj<singleDist.size();uj++)
541  {
542  for(size_t uk=0;uk<massDist.size();uk++)
543  {
544  pair<float,float> newMass;
545  newMass.first = massDist[uk].first + singleDist[uj].first;
546  newMass.second = massDist[uk].second*singleDist[uj].second;
547  newDist.push_back(newMass);
548  }
549  }
550  massDist.swap(newDist);
551  newDist.clear();
552  }
553 
554 
555 }
556 
557 
558 
559 void AbundanceData::getSymbolIndices(const vector<string> &symbols,vector<size_t> &indices) const
560 {
561  indices.resize(symbols.size());
562  for(size_t ui=0;ui<symbols.size(); ui++)
563  indices[ui]=symbolIndex(symbols[ui].c_str());
564 
565 }
566 
567 void AbundanceData::getSymbolIndices(const vector<pair<string,size_t> > &symbols,
568  vector<pair<size_t,unsigned int >> &indices) const
569 {
570  //Extract first
571  vector<string> sV;
572  vector<size_t> iV;
573  sV.resize(symbols.size()); iV.resize(symbols.size());
574  for(unsigned int ui=0;ui<symbols.size(); ui++)
575  {
576  sV[ui]= symbols[ui].first;
577  iV[ui]= symbols[ui].second;
578  }
579 
580  vector<size_t> indicesV;
581  getSymbolIndices(sV,indicesV);
582 
583  //Repackage into pair
584  indices.resize(sV.size());
585  for(unsigned int ui=0; ui<indices.size();ui++)
586  {
587  indices[ui].first = indicesV[ui]; //Index
588  indices[ui].second = iV[ui]; //Payload
589  }
590 
591 }
592 
593 std::string AbundanceData::elementNames(size_t start,size_t end, char separator) const
594 {
595  ASSERT(start < end && isotopeData.size());
596  std::string retStr,sep;
597  sep=separator;
598  for(size_t ui=start; ui<end; ui++)
599  {
600  size_t symbolIdx;
601  symbolIdx=symbolIdxFromAtomicNumber(ui);
602  if(symbolIdx !=(size_t)-1)
603  retStr+=elementName(symbolIdx) + sep;
604  }
605 
606  return retStr;
607 }
608 
609 size_t AbundanceData::symbolIdxFromAtomicNumber(size_t atomicNumber) const
610 {
611  for(size_t ui=0;ui<isotopeData.size(); ui++)
612  {
613  if(isotopeData[ui].size() &&
614  isotopeData[ui][0].atomicNumber == atomicNumber)
615  return ui;
616  }
617 
618  return (size_t)-1;
619 }
620 
621 const std::vector<ISOTOPE_ENTRY> &AbundanceData::isotopes(size_t offset) const
622 {
623  ASSERT(offset < isotopeData.size()); return isotopeData[offset];
624 }
625 
626 float AbundanceData::getNominalMass(size_t elementIdx) const
627 {
628 
629  const vector<ISOTOPE_ENTRY> &isoDat=isotopeData[elementIdx];
630 
631  float total=0;
632  for(size_t ui=0;ui<isoDat.size();ui++)
633  {
634  total+=isoDat[ui].abundance*isoDat[ui].mass;
635  }
636 
637  //return mean
638  return total;
639 
640 }
641 
642 size_t AbundanceData::getNearestCharge(const vector<pair<size_t,size_t> > &element,float targetMass,size_t maxCharge) const
643 {
644  //find the nearest isotope
645  vector<pair<float,float> > isotopeDist;
646 
647  vector<size_t> elemIdx, elemFreq;
648  elemIdx.resize(element.size());
649  elemFreq.resize(element.size());
650 
651  for(size_t ui=0;ui<element.size();ui++)
652  {
653  elemIdx[ui] = element[ui].first;
654  elemFreq[ui] = element[ui].second;
655  }
656 
657  generateIsotopeDist(elemIdx,elemFreq,isotopeDist);
658 
659  float minDelta=std::numeric_limits<float>::max();
660  //this is the position of the isotope in the dist
661  size_t bestCharge=0;
662  for(size_t curCharge=1;curCharge<maxCharge; curCharge++)
663  {
664  for(size_t ui=0;ui<isotopeDist.size();ui++)
665  {
666  float curDelta;
667  curDelta=fabs(isotopeDist[ui].first/curCharge-targetMass);
668  if( curDelta< minDelta)
669  {
670  minDelta = curDelta;
671  bestCharge=curCharge;
672  }
673  }
674 
675  }
676 
677  return bestCharge;
678 }
679 
680 #ifdef DEBUG
681 void AbundanceData::checkErrors() const
682 {
683  //Ensure all isotopes sum to 1-ish
684  // Rounding errors limit our correctness here.
685  for(size_t ui=0;ui<isotopeData.size();ui++)
686  {
687  if(!isotopeData[ui].size())
688  continue;
689 
690  float sum;
691  sum=0.0f;
692  for(size_t uj=0; uj<isotopeData[ui].size();uj++)
693  {
694  sum+=isotopeData[ui][uj].abundance;
695  }
696 
697  ASSERT(fabs(sum -1.0f) < 0.000001);
698 
699  }
700 
701 
702  //Ensure Ti has 5 isotopes (original data file was missing)
703  ASSERT(isotopeData[symbolIndex("Ti")].size() == 5);
704 }
705 
706 bool AbundanceData::runUnitTests()
707 {
708  AbundanceData massTable;
709  TEST(massTable.open("../data/naturalAbundance.xml") == 0,"load table");
710  //FIXME: Getting the isotope dis
711 
712  size_t ironIndex=massTable.symbolIndex("Fe");
713  TEST(ironIndex != (size_t)-1,"symbol lookup");
714 
715  //Generate the mass peak dist for iron
716  vector<size_t> elements;
717  vector<size_t> concentrations;
718  elements.push_back(ironIndex);
719  concentrations.push_back(1);
720 
721  std::vector<std::pair<float,float> > massDist;
722  massTable.generateIsotopeDist(elements,concentrations,massDist);
723 
724  TEST(massDist.size() == 4, "Iron has 4 isotopes");
725 
726 
727  elements.clear();
728  massDist.clear();
729 
730  //Check the isotope dist (grouped) for Mo2
731  //==========
732  size_t molyIdx = massTable.symbolIndex("Mo");
733  concentrations.clear();
734  //Mo2
735  elements.push_back(molyIdx);
736  concentrations.push_back(2);
737 
738 
739  massTable.generateGroupedIsotopeDist(elements,concentrations,massDist,0.005f);
740 
741  // AJL Reports the following
742  const float MO2_ISOTOPES[15][2] ={
743  {183.8136,0.022},
744  {185.8119,0.0275},
745  {186.8126,0.0473},
746  {187.811,0.0581},
747  {188.8119,0.0578},
748  {189.8111,0.1278},
749  {190.8108,0.0708},
750  {191.8118,0.1315},
751  {192.811,0.1087},
752  {193.8115,0.1074},
753  {194.8124,0.0768},
754  {195.8117,0.0904},
755  {196.8135,0.0184},
756  {197.8129,0.0465},
757  {199.8149,0.0093},
758  };
759 
760  ComparePairFirst cmp;
761  std::sort(massDist.begin(),massDist.end(),cmp);
762 
763  const unsigned int NUM_ISOTOPES_MO2=15;
764  TEST(massDist.size() == NUM_ISOTOPES_MO2,"Mo2 dist test");
765 
766  for(unsigned int ui=0;ui<NUM_ISOTOPES_MO2; ui++)
767  {
768  float m,i;
769  m=MO2_ISOTOPES[ui][0];
770  i=MO2_ISOTOPES[ui][1];
771 
772  float dm,di;
773  dm = fabs(m-massDist[ui].first);
774  di = fabs(i-massDist[ui].second);
775  TEST(dm < 0.01,"Mo2 Isotope position");
776  TEST(di < 0.01,"Mo2 Isotope intensity");
777 
778  }
779  //==========
780 
781 
782  return true;
783 
784 }
785 #endif
786 }
void isotopeIndex(size_t elem, float mass, size_t &isotopeIdx) const
Obtain the atom ID (first) and the isotope value(second).
Definition: abundance.cpp:238
size_t symbolIndex(const char *symbol, bool caseSensitive=true) const
Return the element&#39;s position in table, starting from 0.
Definition: abundance.cpp:214
size_t atomicNumber
Definition: abundance.h:46
unsigned int XMLHelpFwdToElem(xmlNodePtr &node, const char *nodeName)
Definition: XMLHelper.cpp:41
float massError
Definition: abundance.h:48
std::string elementNames(size_t start, size_t end, char separator=',') const
Obtain a delimited list of element names, using the (optional) supplied separator, over the specified [start,end) range.
Definition: abundance.cpp:593
Definition: abundance.h:42
void generateSingleAtomDist(size_t atomIdx, unsigned int repeatCount, std::vector< std::pair< float, float > > &massDist) const
Obtain the mass distribution from a single isotope that repeats. Output is not grouped.
Definition: abundance.cpp:517
void generateIsotopeDist(const std::vector< size_t > &elementIdx, const std::vector< size_t > &frequency, std::vector< std::pair< float, float > > &massDist) const
Compute the mass-probability distribution for the grouped set of ions.
Definition: abundance.cpp:277
void vectorMultiErase(std::vector< T > &vec, const std::vector< bool > &wantKill)
Remove elements from the vector, without preserving order.
Definition: misc.h:112
float getNominalMass(size_t elementIdx) const
Obtain the isotope weighted mass for the given element.
Definition: abundance.cpp:626
size_t massNumber
Definition: abundance.h:45
#define TEST(f, g)
Definition: aptAssert.h:49
void getSymbolIndices(const std::vector< std::string > &symbols, std::vector< size_t > &indices) const
Return a vector of symbol indices.
Definition: abundance.cpp:559
const char * ABUNDANCE_ERROR[]
Definition: abundance.cpp:49
float abundanceBetweenLimits(const std::vector< size_t > &elemIdx, const std::vector< size_t > &frequency, float massStart, float massEnd) const
Obtain the fractional abundance between two limits for the given species [start,end) ...
Definition: abundance.cpp:463
std::string elementName(size_t elemIdx) const
Obtain the name of an element from its index.
Definition: abundance.h:107
T weightedMean(const std::vector< T > &values, const std::vector< T > &weight)
Definition: misc.h:52
void generateGroupedIsotopeDist(const std::vector< size_t > &elementIdx, const std::vector< size_t > &frequency, std::vector< std::pair< float, float > > &massDist, float massTolerance) const
As per generateIsotopeDist, however, this convenience groups the distribution to limit the effect of ...
Definition: abundance.cpp:395
size_t open(const char *file, bool strict=false)
Attempt to open the abundance data file, return 0 on success, nonzero on failure. ...
Definition: abundance.cpp:79
float abundance
Definition: abundance.h:49
unsigned int XMLHelpGetProp(T &prop, xmlNodePtr node, std::string propName)
Definition: XMLHelper.h:87
Class to load abundance information for natural isotopes.
Definition: abundance.h:54
std::string lowercase(std::string s)
Return a lowercase version for a given string.
const std::vector< ISOTOPE_ENTRY > & isotopes(size_t offset) const
Return the isotope at a given position in the table. Offset must exist.
Definition: abundance.cpp:621
size_t getMajorIsotopeFromElemIdx(size_t elementIdx) const
Obtain the most prominent isotope&#39;s index from the element index.
Definition: abundance.cpp:482
#define ASSERT(f)
float mass
Definition: abundance.h:47
const ISOTOPE_ENTRY & isotope(size_t elementIdx, size_t isotopeIdx) const
Obtain a reference to a particular isotope, using the element&#39;s index in the table, and the isotope index.
Definition: abundance.cpp:271
size_t numElements() const
Return the number of elements in the loaded table.
Definition: abundance.cpp:74
size_t numIsotopes() const
Return the number of isotopes in the loaded table.
Definition: abundance.cpp:65
std::string symbol
Definition: abundance.h:44
float abundanceError
Definition: abundance.h:50
unsigned int getAtomicNumber(size_t elemIdx) const
Obtain the atomic number for the given element, by element index.
Definition: abundance.cpp:503
size_t getNearestCharge(const std::vector< std::pair< size_t, size_t > > &molecule, float targetMass, size_t maxCharge) const
Obtain the closest charge state for the given mass and species group.
Definition: abundance.cpp:642
size_t symbolIdxFromAtomicNumber(size_t atomicNumber) const
Return the symbol index (offset in vector) for the given atomic number, -1 if it cannot be found...
Definition: abundance.cpp:609
static const char * getErrorText(size_t errorCode)
Obtain a human-readable error message from the open( ) call.
Definition: abundance.cpp:59