cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
mole_species.cpp
Go to the documentation of this file.
1 /* This file is part of Cloudy and is copyright (C)1978-2017 by Gary J. Ferland and
2  * others. For conditions of distribution and use see copyright notice in license.txt */
3 /*CO_Init called from cdInit to initialize co routines */
4 /*CO_update_chem_rates update rate coefficients, only temp part - in mole_co_etc.c */
5 #include "cdstd.h"
6 #include "cddefines.h"
7 #include "deuterium.h"
8 #include "elementnames.h"
9 #include "h2.h"
10 #include "hmi.h"
11 #include "dense.h"
12 #include "grainvar.h"
13 #include "trace.h"
14 #include "abund.h"
15 #include "mole_priv.h"
16 #include "mole.h"
17 #include "parse_species.h"
18 /*lint -e778 constant expression evaluates to 0 in operation '-' */
19 
20 /* CO_update_chem_rates update rate coefficients, only temp part - in
21  * mole_co_etc.c called in conv_base before any chemistry or
22  * ionization is done */
23 
25 
26 STATIC void read_species_file( string filename, bool lgCreateIsotopologues = true );
27 STATIC void newelement(const char label[], int ipion);
28 STATIC void newisotope( const count_ptr<chem_element> &el, int massNumberA,
29  realnum mass_amu, double frac );
31 // newspecies is overloaded. The first one just calls the second with the additional lgCreateIsotopologues set true
32 STATIC molecule *newspecies(const char label[], enum spectype type,
33  enum mole_state state, realnum form_enthalpy);
34 STATIC molecule *newspecies(const char label[], enum spectype type,
35  enum mole_state state, realnum form_enthalpy, bool lgCreateIsotopologues);
36 STATIC bool isactive(const molecule &mol);
37 STATIC bool ispassive(const molecule &mol);
38 STATIC void SetIsotopeFractions( const vector<bool>& lgResolveNelem );
39 
40 namespace mole_priv {
41  map <string,count_ptr<molecule> > spectab;
42  map <string,count_ptr<mole_reaction> > reactab;
43  map <string,count_ptr<chem_element> > elemtab;
44  map <string,count_ptr<mole_reaction> > functab;
45 }
46 
47 map <string,size_t > nuclidetab;
48 typedef map<string,size_t >::iterator chem_nuclide_i;
49 
56 vector<molecule *> groupspecies;
57 
58 
59 namespace
60 {
61  class MoleCmp : public binary_function<const count_ptr<molecule>,
62  const count_ptr<molecule>,bool>
63  {
64  public:
65  bool operator()(const count_ptr<molecule> &mol1,
66  const count_ptr<molecule> &mol2) const
67  {
68  return mol1->compare(*mol2) < 0;
69  }
70  bool operator()(const molecule *mol1, const molecule *mol2) const
71  {
72  return mol1->compare(*mol2) < 0;
73  }
74  };
75 }
76 
77 void t_mole_global::sort(t_mole_global::MoleculeList::iterator start,
78  t_mole_global::MoleculeList::iterator end)
79 {
80  std::sort(start,end,MoleCmp());
81 }
83 {
84  std::sort(start,end,MoleCmp());
85 }
86 
87 STATIC void SetIsotopeFractions( const vector<bool>& lgResolveNelem )
88 {
89  DEBUG_ENTRY( "SetIsotopeFractions()" );
90 
91  for( int ielm = 0; ielm < LIMELM; ielm++ )
92  {
93  long Z = ielm;
94  for( int j = 0; j < abund.IsoAbn[ielm].getNiso(); j++ )
95  {
96  long A = abund.IsoAbn[ielm].getAiso( j );
97  realnum mass = abund.IsoAbn[ielm].getMass( A );
98  realnum frac = abund.IsoAbn[ielm].getAbn( A );
99 
100  if( (unsigned)Z <= lgResolveNelem.size() && lgResolveNelem[Z] )
101  newisotope( element_list[Z], A, mass, frac );
102  // always do this to continue history of predicting 13CO
103  else if( Z==ipCARBON )
104  newisotope( element_list[Z], A, mass, frac );
105  }
106  }
107 
108  return;
109 }
110 
112 {
113  DEBUG_ENTRY( "mole_global::make_species()" );
114 
115  long int i;
116  molecule *sp;
117 
118  null_element = new chem_element(0,"") ;
119  null_nuclide = new( chem_nuclide );
120  null_molezone = new( molezone );
121  null_molezone->den = 0.;
122 
123  /* set up concordance of elemental species to external Cloudy indices */
124  for (i=0;i<LIMELM;i++)
125  {
127  }
128 
129  // flip this to treat deuterium
130  if( deut.lgElmtOn )
131  lgTreatIsotopes[ipHYDROGEN] = true;
132 
133  // read and define isotopes, set default fractionations
135 
136 
138  {
139  SetDeuteriumFractionation( element_list[ipHYDROGEN]->isotopes[2]->frac );
140  SetGasPhaseDeuterium( dense.gas_phase[ipHYDROGEN] );
142  }
143 
144  for( long nelem=0; nelem<LIMELM; ++nelem )
145  {
146  realnum mass_amu = MeanMassOfElement( element_list[nelem] );
147  //define generic mean-abundance isotopes
148  newisotope( element_list[nelem], -1, mass_amu, 1.0 );
149  }
150 
151  /* set up properties of molecular species -- chemical formulae,
152  array indices, elementary components (parsed from formula),
153  status within CO network, location of stored value external
154  to CO network (must be floating point). */
155 
156  /* Sizes of different parts of network are calculated by increments in newspecies */
158  /* Enthalpies of formation taken from
159  * >>refer mol Le Teuff, Y. E., Millar, T. J., & Markwick, A. J.,2000, A&AS, 146, 157
160  */
161 
162  /* Zero density pseudo-species to return when molecule is switched off */
163  null_mole = newspecies(" ",OTHER,MOLE_NULL, 0.);
164  null_mole->index = -1;
165 
166  read_species_file( "chem_species.dat" );
167 
169  {
170  /* What are formation enthalpies of COgrn, H2Ogrn, OHgrn? For
171  present, take grn as standard state, and neglect adsorption enthalpy.
172 
173  -- check e.g. http://www.arxiv.org/abs/astro-ph/0702322 for CO adsorption energy.
174  */
175  if (0)
176  {
177  read_species_file( "chem_species_grn.dat" );
178  }
179  else
180  {
181  (void) newspecies("COgrn ",MOLECULE,MOLE_ACTIVE, -113.8f);
182  (void) newspecies("H2Ogrn",MOLECULE,MOLE_ACTIVE, -238.9f);
183  (void) newspecies("OHgrn ",MOLECULE,MOLE_ACTIVE, 38.4f);
184  //sp = newspecies("Hgrn ",MOLECULE,MOLE_ACTIVE, 216.f);
185  }
186  }
187 
188  /* Add passive species to complete network */
189  sp = newspecies("e- ",OTHER,MOLE_PASSIVE, 0.0f);
190  sp->charge = -1; sp->mole_mass = (realnum)ELECTRON_MASS; /* Augment properties for this non-molecular species */
191  (void) newspecies("grn ",OTHER,MOLE_PASSIVE, 0.0f);
192  (void) newspecies("PHOTON",OTHER,MOLE_PASSIVE, 0.0f);
193  (void) newspecies("CRPHOT",OTHER,MOLE_PASSIVE, 0.0f);
194  (void) newspecies("CRP ",OTHER,MOLE_PASSIVE, 0.0f);
195 
197  (void) newspecies("H- ",MOLECULE,MOLE_ACTIVE, 143.2f);
199  {
200  (void) newspecies("H2* ",MOLECULE,MOLE_ACTIVE,
201  h2.ENERGY_H2_STAR * KJMOL1CM);
202  }
203 
204  if( deut.lgElmtOn )
205  {
206  read_species_file( "chem_species_deuterium.dat", false );
207  }
208 
209  /* Add species for all other elements and their first ions -- couple to network at least via H- */
210  for( ChemNuclideList::iterator atom = nuclide_list.begin();
211  atom != nuclide_list.end(); ++atom)
212  {
213  if ( !(*atom)->lgHasLinkedIon() )
214  continue;
215  long int nelem = (*atom)->el->Z-1;
216 
217  for( long ion=0; ion<=nelem+1; ion++ )
218  {
220  char temp[CHARS_ION_STAGE+1];
221  if( ion==0 )
222  temp[0] = '\0';
223  else if( ion==1 )
224  sprintf( temp, "+" );
225  else
226  sprintf( temp, "+%ld", ion );
227  sprintf( str, "%s%s", (*atom)->label().c_str(), temp );
228  if (findspecies(str) == null_mole)
229  {
230  sp = newspecies(str,MOLECULE,MOLE_ACTIVE, 0.f);
231  fixit("populate these in a local update");
232 #if 0
233  if( sp != NULL )
234  {
235  sp->levels = NULL;
236  sp->numLevels = 0;
237  }
238 #endif
239  }
240  }
241  }
242 
243  return;
244 }
245 
247 {
248  DEBUG_ENTRY( "mole_make_list()" );
249 
250  /* Create linear list of species and populate it... */
252 
253  /* ...first active species */
254  long int i = 0;
255  for (molecule_i p = mole_priv::spectab.begin(); p != mole_priv::spectab.end(); ++p)
256  {
257  if (isactive(*(p->second)))
258  mole_global.list[i++] = p->second;
259  }
260  ASSERT (i == mole_global.num_calc);
261 
262  /* Sort list into a standard ordering */
264 
265  for (molecule_i p = mole_priv::spectab.begin(); p != mole_priv::spectab.end(); ++p)
266  {
267  if (ispassive(*(p->second)))
268  mole_global.list[i++] = p->second;
269  }
270  ASSERT (i == mole_global.num_total);
271 
272  /* Set molecule indices to order of list just created */
273  for(i=0;i<mole_global.num_total;i++)
274  {
275  mole_global.list[i]->index = i;
276  }
277 
278  for(i=0;i<mole_global.num_total;i++)
279  {
280  if( !mole_global.list[i]->parentLabel.empty() )
281  {
282  long parentIndex = findspecies( mole_global.list[i]->parentLabel.c_str() )->index;
283  mole_global.list[i]->parentIndex = parentIndex;
284  }
285  else
286  mole_global.list[i]->parentIndex = -1;
287  }
288 
289  /* Register the atomic ladders */
290  for(i=0;i<mole_global.num_total;i++)
291  {
292  molecule *sp = &(*mole_global.list[i]);
293  if (sp->isMonatomic())
294  {
295  ASSERT( (int)sp->nNuclide.size() == 1 );
296  count_ptr<chem_nuclide> atom = sp->nNuclide.begin()->first;
297  ASSERT(sp->charge <= atom->el->Z);
298  if(sp->charge >= 0 && sp->lgGas_Phase)
299  {
300  atom->ipMl[sp->charge] = i;
301  }
302  }
303  }
304 
305  return;
306 
307 }
308 
309 STATIC void read_species_file( string filename, bool lgCreateIsotopologues )
310 {
311  DEBUG_ENTRY( "read_species_file()" );
312 
313  fstream ioDATA;
314  open_data( ioDATA, filename.c_str(), mode_r );
315  string line;
316 
317  while( getline( ioDATA,line ) )
318  {
319  if( line.empty() )
320  break;
321  if( line[0] == '#' )
322  continue;
323  istringstream iss( line );
324  string species;
325  double formation_enthalpy;
326  iss >> species;
327  iss >> formation_enthalpy;
328  ASSERT( iss.eof() );
329  newspecies( species.c_str(), MOLECULE,MOLE_ACTIVE, formation_enthalpy, lgCreateIsotopologues );
330  //fprintf( ioQQQ, "DEBUGGG read_species_file %s\t%f\n", species.c_str(), formation_enthalpy );
331  }
332 
333  return;
334 }
335 /*lint +e778 constant expression evaluates to 0 in operation '-' */
336 
339  vector< int >& numNuclides,
340  string atom_old,
341  string atom_new,
342  string embellishments,
343  vector<string>& newLabels )
344 {
345  DEBUG_ENTRY( "create_isotopologues()" );
346 
347  fixit("make sure atom_new and atom_old are isotopes");
348  fixit("make sure atom_new is not already present");
349 
350  //for( ChemNuclideList::iterator it = atoms.begin(); it != atoms.end(); ++it )
351  for( unsigned position = 0; position < atoms.size(); ++position )
352  {
353  string newLabel;
354  create_isotopologues_one_position( position, atoms, numNuclides, atom_old, atom_new, embellishments, newLabel );
355  if( !newLabel.empty() )
356  newLabels.push_back( newLabel );
357  }
358 
359  return;
360 }
361 
363  unsigned position,
365  vector< int >& numNuclides,
366  string atom_old,
367  string atom_new,
368  string embellishments,
369  string& newLabel )
370 {
371  DEBUG_ENTRY( "create_isotopologues_one_position()" );
372 
373  stringstream str;
374  if( atoms[position]->label() == atom_old )
375  {
376  for( unsigned i=0; i<position; ++i )
377  {
378  str << atoms[i]->label();
379  if( numNuclides[i]>1 )
380  str << numNuclides[i];
381  }
382 
383  if( numNuclides[position] > 1 )
384  {
385  str << atom_old;
386  if( numNuclides[position] > 2 )
387  str << numNuclides[position]-1;
388  }
389 
390  if( position+1 == atoms.size() )
391  str << atom_new;
392 
393  for( unsigned i=position+1; i<atoms.size(); ++i )
394  {
395  if( i==position+1 )
396  {
397  // remove adjacent duplicates
398  if( atom_new == atoms[i]->label() )
399  {
400  str << atoms[i]->label();
401  ASSERT( numNuclides[i] + 1 > 1 );
402  str << numNuclides[i] + 1;
403  }
404  else
405  {
406  str << atom_new;
407  str << atoms[i]->label();
408  if( numNuclides[i] > 1 )
409  str << numNuclides[i];
410  }
411  }
412  else
413  {
414  str << atoms[i]->label();
415  if( numNuclides[i] > 1 )
416  str << numNuclides[i];
417  }
418  }
419 
420  // add on charge, grn, and excitation embellishments
421  str << embellishments;
422 
423  newLabel = str.str();
424  //fprintf( ioQQQ, "DEBUGGG create_isotopologues_one_position %s\n", newLabel.c_str() );
425  }
426 
427  return;
428 }
429 
430 /* Fill element linking structure */
431 STATIC void newelement(const char label[], int ipion)
432 {
433  char *s;
434 
435  DEBUG_ENTRY( "newelement()" );
436 
437  /* Create private workspace for label; copy and strip trailing whitespace */
438  int len = strlen(label)+1;
439  auto_vec<char> mylab_v(new char[len]);
440  char *mylab = mylab_v.data();
441  strncpy(mylab,label,len);
442  s = strchr(mylab,' ');
443  if (s)
444  *s = '\0';
445 
446  int exists = (mole_priv::elemtab.find(mylab) != mole_priv::elemtab.end());
447  if (!exists)
448  {
449  count_ptr<chem_element> element(new chem_element(ipion+1,mylab));
450  mole_priv::elemtab[element->label] = element;
451  element_list.push_back(element);
452  }
453  return;
454 }
455 
456 /* Fill isotope lists */
457 STATIC void newisotope( const count_ptr<chem_element>& el, int massNumberA,
458  realnum mass_amu, double frac )
459 {
460 
461  DEBUG_ENTRY( "newisotope()" );
462 
463  ASSERT( massNumberA < 3 * LIMELM && ( massNumberA > 0 || massNumberA == -1 ) );
464  ASSERT( mass_amu < 3. * LIMELM && mass_amu > 0. );
465  ASSERT( frac <= 1. + FLT_EPSILON && frac >= 0. );
466 
468  isotope->A = massNumberA;
469  isotope->mass_amu = mass_amu;
470  isotope->frac = frac;
471  isotope->ipMl.resize(el->Z+1);
472  isotope->el = el.get_ptr();
473  for (long int ion = 0; ion < el->Z+1; ion++)
474  isotope->ipMl[ion] = -1; /* Chemical network species indices not yet defined */
475 
476  //int exists = (mole_priv::nuclidetab.find( isotope->label() ) != mole_priv::nuclidetab.end());
477  nuclidetab[ isotope->label() ] = nuclide_list.size();
478  nuclide_list.push_back(isotope);
479  // register with 'parent' element
480  el->isotopes[massNumberA] = isotope;
481 }
482 
484 {
485  DEBUG_ENTRY( "MeanMassOfElement()" );
486 
487  // if no isotopes have been defined, just use the mean mass stored elsewhere
488  if( el->isotopes.size()==0 )
489  return dense.AtomicWeight[el->Z-1];
490 
491  realnum averageMass = 0., fracsum = 0.;
492  for( isotopes_i it = el->isotopes.begin(); it != el->isotopes.end(); ++it )
493  {
494  fracsum += it->second->frac;
495  averageMass += it->second->mass_amu * it->second->frac;
496  }
497  ASSERT( fp_equal( fracsum, realnum(1.) ) );
498 
499  return averageMass;
500 }
501 
503  const char label[],
504  enum spectype type,
505  enum mole_state state,
506  realnum form_enthalpy)
507 {
508  return newspecies( label, type, state, form_enthalpy, true);
509 }
510 
511 /* Parse species string to find constituent atoms, charge etc. */
513  const char label[],
514  enum spectype type,
515  enum mole_state state,
516  realnum form_enthalpy,
517  bool lgCreateIsotopologues )/* formation enthalpy at 0K */
518 {
519  DEBUG_ENTRY( "newspecies()" );
520 
521  int exists;
522  ChemNuclideList nuclidesLeftToRight;
523  vector< int > numNuclides;
524  string embellishments;
525  char *s;
526  count_ptr<molecule> mol(new molecule);
527 
528  mol->parentLabel.clear();
529  mol->isEnabled = true;
530  mol->charge = 0;
531  mol->lgExcit = false;
532  mol->mole_mass = 0.0;
533  mol->state = state;
534  mol->lgGas_Phase = true;
535  mol->form_enthalpy = form_enthalpy;
536  mol->groupnum = -1;
537  mol->n_react = 0;
538 
539  /* Create private workspace for label; copy and strip trailing whitespace */
540  int len = strlen(label)+1;
541  auto_vec<char> mylab_v(new char[len]);
542  char *mylab = mylab_v.data();
543  strncpy(mylab,label,len);
544  s = strchr(mylab,' ');
545  if (s)
546  *s = '\0';
547  mol->label = mylab;
548 
549  if(type == MOLECULE)
550  {
551  if( parse_species_label( mylab, nuclidesLeftToRight, numNuclides, embellishments, mol->lgExcit, mol->charge, mol->lgGas_Phase ) == false )
552  return NULL;
553 
554  for( unsigned i = 0; i < nuclidesLeftToRight.size(); ++i )
555  {
556  mol->nNuclide[ nuclidesLeftToRight[i] ] += numNuclides[i];
557  mol->mole_mass += numNuclides[i] * nuclidesLeftToRight[i]->mass_amu * (realnum)ATOMIC_MASS_UNIT;
558  }
559  }
560 
561  // we also kill H- if molecules are disabled. This is less than ideal,
562  // but physically motivated by the fact that one of the strongest H- sinks
563  // involves formation of H2 (disabled by "no molecules"), while one the
564  // fastest sources is e- recombination (which would still be allowed).
565  if ( (mol->n_nuclei() > 1 || (mol->isMonatomic() && mol->charge==-1) ) && mole_global.lgNoMole)
566  {
567  if( trace.lgTraceMole )
568  fprintf(ioQQQ,"No species %s as molecules off\n",label);
569  return NULL;
570  }
571 
572  if (mol->n_nuclei() > 1 && mole_global.lgNoHeavyMole)
573  {
574  for( nNucs_ri it=mol->nNuclide.rbegin(); it != mol->nNuclide.rend(); --it )
575  {
576  if( it->first->el->Z-1 != ipHYDROGEN )
577  {
578  ASSERT( it->second > 0 );
579  if( trace.lgTraceMole )
580  fprintf(ioQQQ,"No species %s as heavy molecules off\n",label);
581  return NULL;
582  }
583  }
584  }
585 
586  if (speciesOff(mol->label))
587  {
588  fprintf( ioQQQ,"Species %s disabled\n",mol->label.c_str() );
589  return NULL;
590  }
591 
592  /* Insert species into hash table */
593  exists = (mole_priv::spectab.find(mol->label) != mole_priv::spectab.end());
594  if( exists )
595  {
596  fprintf( ioQQQ,"Warning: duplicate species %s - using first one\n",
597  mol->label.c_str() );
598  return NULL;
599  }
600  else
601  mole_priv::spectab[mol->label] = mol;
602 
603  // all map entries should have strictly positive number of nuclei
604  for( nNucs_i it=mol->nNuclide.begin(); it != mol->nNuclide.end(); ++it )
605  ASSERT( it->second > 0 );
606 
607  if (state != MOLE_NULL)
609  if (state == MOLE_ACTIVE)
611 
612  // this is a special case to always treat 13CO (as has long been done)
613  if( lgCreateIsotopologues && type==MOLECULE && mol->label.compare("CO")==0 )
614  {
615  molecule *sp = newspecies( "^13CO", MOLECULE, mol->state, mol->form_enthalpy, false );
616  sp->parentLabel = mol->label;
617  }
618 
619  // create singly-substituted isotopologues
620  if( lgCreateIsotopologues && type==MOLECULE && !mol->isMonatomic() )
621  {
622  for( nNucs_i it1 = mol->nNuclide.begin(); it1 != mol->nNuclide.end(); ++it1 )
623  {
624  for( map<int, count_ptr<chem_nuclide> >::iterator it2 = it1->first->el->isotopes.begin();
625  it2 != it1->first->el->isotopes.end(); ++it2 )
626  {
627  // we don't want to create ^1H isotopologues (only substitute D for H)
628  if( it1->first->el->Z-1 == ipHYDROGEN && it2->second->A != 2 )
629  continue;
630  if( !mole_global.lgTreatIsotopes[it1->first->el->Z-1] )
631  continue;
632  if( it2->second->lgMeanAbundance() )
633  continue;
634  vector<string> isoLabs;
635  create_isotopologues( nuclidesLeftToRight, numNuclides, it1->first->label(), it2->second->label(), embellishments, isoLabs );
636  //fprintf( ioQQQ, " DEBUGGG %10s isotopologues of %10s:", it1->first->label().c_str(), mol->label.c_str() );
637  //for( vector<string>::iterator lab = isoLabs.begin(); lab != isoLabs.end(); ++ lab )
638  // fprintf( ioQQQ, " %10s", lab->c_str() );
639  //fprintf( ioQQQ, "\n" );
640  for( vector<string>::iterator newLabel = isoLabs.begin(); newLabel != isoLabs.end(); ++newLabel )
641  {
642  molecule *sp = newspecies( newLabel->c_str(), MOLECULE, mol->state, mol->form_enthalpy, false );
643  // D is special -- don't set parentLabel
644  if( sp!=NULL && it2->second->A != 2 )
645  sp->parentLabel = mol->label;
646  }
647  }
648  }
649  }
650 
651  return &(*mol);
652 }
653 bool parse_species_label( const char label[], ChemNuclideList &nuclidesLeftToRight, vector<int> &numNuclides, string &embellishments )
654 {
655  bool lgExcit, lgGas_Phase;
656  int charge;
657  bool lgOK = parse_species_label( label, nuclidesLeftToRight, numNuclides, embellishments, lgExcit, charge, lgGas_Phase );
658  return lgOK;
659 }
660 bool parse_species_label( const char label[], ChemNuclideList &nuclidesLeftToRight, vector<int> &numNuclides, string &embellishments,
661  bool &lgExcit, int &charge, bool &lgGas_Phase )
662 {
663  long int i, n, ipAtom;
664  char thisAtom[CHARS_ISOTOPE_SYM];
666  const char *s;
667  int iend = strlen(label);
668 
669  /* Excitation... */
670  s = strpbrk(label,"*");
671  if(s)
672  {
673  lgExcit = true;
674  embellishments = s;
675  iend = s-label;
676  }
677 
678  /* ...Charge */
679  s = strpbrk(label,"+-");
680  if(s)
681  {
682  if(isdigit(*(s+1)))
683  n = atoi(s+1);
684  else
685  n = 1;
686  if(*s == '+')
687  charge = n;
688  else
689  charge = -n;
690  embellishments = s + embellishments;
691  iend = s-label;
692  }
693  /* ...Grain */
694  s = strstr(label,"grn");
695  if(s)
696  {
697  lgGas_Phase = false;
698  embellishments = s + embellishments;
699  iend = s-label;
700  }
701  else
702  {
703  lgGas_Phase = true;
704  }
705  //fprintf( ioQQQ, "DEBUGGG parse_species_label %s\t%d\t%s\t%d\t%s\t\n",
706  // label, (int)strlen(label), label, iend, embellishments.c_str() );
707 
708  /* Now analyse chemical formula */
709  i = 0;
710  while (i < iend && label[i] != ' ' && label[i] != '*')
711  {
712  ipAtom = 0;
713  /* Select next nuclide in species, matches regexp (^\d+)?[A-Z][a-z]? */
714  /* look for isotope prefix */
715  if(label[i]=='^')
716  {
717  thisAtom[ipAtom++] = label[i++];
718  ASSERT( isdigit(label[i]) );
719  thisAtom[ipAtom++] = label[i++];
720  if(isdigit(label[i]))
721  {
722  thisAtom[ipAtom++] = label[i++];
723  }
724  }
725  // should be first character of an element symbol
726  thisAtom[ipAtom++] = label[i++];
727  if( i<iend && islower(label[i]))
728  {
729  thisAtom[ipAtom++] = label[i++];
730  }
731  thisAtom[ipAtom] = '\0';
732  ASSERT(ipAtom <= CHARS_ISOTOPE_SYM);
733 
734  atom = findnuclide(thisAtom);
735  if(atom.get_ptr() == NULL)
736  {
737  fprintf(stderr,"Did not recognize atom at %s in \"%s \"[%ld]\n",thisAtom,label,i);
738  exit(-1);
739  }
740  if(!dense.lgElmtOn[atom->el->Z-1])
741  {
742  if( trace.lgTraceMole )
743  fprintf(ioQQQ,"No species %s as element %s off\n",label,atom->el->label.c_str() );
744  return false;
745  }
746 
747  if(i < iend && isdigit(label[i])) /* If there is >1 of this atom */
748  {
749  n = 0;
750  do {
751  n = 10*n+(long int)(label[i]-'0');
752  i++;
753  } while (i < iend && isdigit(label[i]));
754  }
755  else
756  {
757  n = 1;
758  }
759  nuclidesLeftToRight.push_back( atom );
760  numNuclides.push_back( n );
761  }
762 
763  return true;
764 }
765 STATIC bool isactive(const molecule &mol)
766 {
767  DEBUG_ENTRY( "isactive()" );
768  return mol.state == MOLE_ACTIVE;
769 }
770 STATIC bool ispassive(const molecule &mol)
771 {
772 
773  DEBUG_ENTRY( "ispassive()" );
774  return mol.state == MOLE_PASSIVE;
775 }
776 
777 bool lgDifferByExcitation( const molecule &mol1, const molecule &mol2 )
778 {
779  if( mol1.label == mol2.label + "*" )
780  return true;
781  else if( mol2.label == mol1.label + "*" )
782  return true;
783  else
784  return false;
785 }
786 
787 molecule *findspecies(const char buf[])
788 {
789  DEBUG_ENTRY( "findspecies()" );
790 
791  // strip string of the first space and anything after it
792  string s;
793  for (const char *pb = buf; *pb && *pb != ' '; ++pb)
794  {
795  s += *pb;
796  }
797 
798  const molecule_i p = mole_priv::spectab.find(s);
799 
800  if(p != mole_priv::spectab.end())
801  return &(*p->second);
802  else
803  return null_mole;
804 }
805 
806 molecule *findspecies_validate(const char buf[])
807 {
808  DEBUG_ENTRY( "findspecies_validate()" );
809 
810  // strip string of the first space and anything after it
811  string s;
812  for (const char *pb = buf; *pb && *pb != ' '; ++pb)
813  {
814  s += *pb;
815  }
816 
817  const molecule_i p = mole_priv::spectab.find(s);
818 
819  if(p != mole_priv::spectab.end())
820  return &(*p->second);
821  else
822  {
823  fprintf(ioQQQ," PROBLEM specified species, '%s', is not valid or disabled.\n", s.c_str() );
824  fprintf(ioQQQ," The SAVE SPECIES LABELS command will generate a list of species. Species names are case sensitive.\n");
825  cdEXIT( EXIT_FAILURE );
826  }
827 }
828 
829 molezone *findspecieslocal(const char buf[])
830 {
831  DEBUG_ENTRY( "findspecieslocal()" );
832 
833  molecule *sp = findspecies(buf);
834 
835  if(sp != null_mole)
836  return &mole.species[ sp->index ];
837  else
838  return null_molezone;
839 }
840 
842 {
843  DEBUG_ENTRY( "findspecieslocal_validate()" );
844 
845  molecule *sp = findspecies_validate(buf);
846 
847  return &mole.species[ sp->index ];
848 }
849 
851 {
852  chem_nuclide_i p;
853 
854  DEBUG_ENTRY( "findnuclide()" );
855 
856  p = nuclidetab.find(buf);
857 
858  if(p != nuclidetab.end())
859  return nuclide_list[p->second];
860  else
861  return count_ptr<chem_nuclide>(NULL);
862 }
863 
865 {
866  int i;
867  double den_times_area, den_grains, adsorbed_density;
868 
869  DEBUG_ENTRY( "mole_update_species_cache()" );
870 
871  enum { DEBUG_MOLE = false };
872 
873  /* For rates that are dependent on grain physics. This includes grain density,
874  cross sectional area, and dust temperature of each constituent. Note that
875 
876  gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3
877 
878  is the integrated projected grain surface area per cm^3 of gas for each grain size bin */
879 
880  /* >>chng 06 feb 28, turn off this rate when no grain molecules */
881  /* >>chng 06 dec 05 rjrw: do this in newreact rather than rate */
882  if( gv.lgDustOn() )
883  {
884  den_times_area = den_grains = 0.0;
885  for( size_t nd=0; nd < gv.bin.size(); nd++ )
886  {
887  /* >>chng 06 mar 04, update expression for projected grain surface area, PvH */
888  den_times_area += gv.bin[nd]->IntArea/4.*gv.bin[nd]->cnv_H_pCM3;
889  den_grains += gv.bin[nd]->cnv_GR_pCM3;
890  }
891 
892  adsorbed_density = 0.0;
893  for (i=0;i<mole_global.num_total;i++)
894  {
895  if( !mole_global.list[i]->lgGas_Phase && mole_global.list[i]->isIsotopicTotalSpecies() )
896  adsorbed_density += mole.species[i].den;
897  }
898 
899  mole.grain_area = den_times_area;
900  mole.grain_density = den_grains;
901 
902  double mole_cs = 1e-15;
903  if (4*den_times_area <= mole_cs*adsorbed_density)
904  mole.grain_saturation = 1.0;
905  else
906  mole.grain_saturation = ((mole_cs*adsorbed_density)/(4.*den_times_area));
907  }
908  else
909  {
910  mole.grain_area = 0.0;
911  mole.grain_density = 0.0;
912  mole.grain_saturation = 1.0;
913  }
914 
915  if (DEBUG_MOLE)
916  fprintf(ioQQQ,"Dust: %f %f %f\n",
918 
919  for (i=0;i<mole_global.num_total;i++)
920  {
921  if(mole.species[i].location != NULL)
922  {
923  ASSERT( mole_global.list[i]->isIsotopicTotalSpecies() );
924  mole.species[i].den = *(mole.species[i].location);
925  if (DEBUG_MOLE)
926  fprintf(ioQQQ,"Updating %s: %15.8g\n",mole_global.list[i]->label.c_str(),mole.species[i].den);
927  }
928  }
929 
931 }
932 
934 // Finding the total atom density from MoleMap.molElems w
935 {
936  int i;
937 
938  /* These two updates should together maintain the abundance invariant */
939 
940  // Assert invariant
942 
943  // Update total of non-ladder species
946 
947  /* charge on molecules */
948  mole.elec = 0.;
949  for(i=0;i<mole_global.num_calc;i++)
950  {
951  if (mole.species[i].location == NULL && mole_global.list[i]->isIsotopicTotalSpecies())
952  mole.elec += mole.species[i].den*mole_global.list[i]->charge;
953  }
954 
955  // Update ionization ladder species
956  realnum delta = 0.0;
957  long ncpt = 0;
958 
959  for (i=0;i<mole_global.num_total;i++)
960  {
961  if(mole.species[i].location && mole_global.list[i]->state == MOLE_ACTIVE)
962  {
963  realnum new_pop = mole.species[i].den;
964 
965  if( mole_global.list[i]->isMonatomic() )
966  {
967  realnum old_pop = *(mole.species[i].location);
968  long nelem = mole_global.list[i]->nNuclide.begin()->first->el->Z-1;
969  realnum frac = (new_pop-old_pop)/SDIV(new_pop+old_pop+1e-8*
970  dense.gas_phase[nelem]);
971  delta += frac*frac;
972  ++ncpt;
973  }
974 
975  //if( iteration >= 3 && nzone >= 100 )
976  // fprintf( ioQQQ, "DEBUGGG mole_return_ %i\t%s\t%.12e\t%.12e\t%.12e\t%.12e\t%li\n",
977  // i, mole_global.list[i]->label.c_str(), new_pop, old_pop, frac, delta, ncpt );
978  *(mole.species[i].location) = new_pop;
979  }
980  }
981 
982  // Assert invariant
984  return ncpt>0 ? sqrt(delta/ncpt) : 0.f;
985 }
986 
988 {
989  DEBUG_ENTRY( "t_mole_local::set_isotope_abundances()" );
990 
991  // loop over unresolved elements
992  for(ChemNuclideList::iterator atom = nuclide_list.begin(); atom != nuclide_list.end(); ++atom)
993  {
994  if ( !(*atom)->lgMeanAbundance() )
995  continue;
996 
997  // loop over all isotopes of each element
998  for( isotopes_i it = (*atom)->el->isotopes.begin(); it != (*atom)->el->isotopes.end(); ++it )
999  {
1000  // skip mean-abundance "isotopes"
1001  if( it->second->lgMeanAbundance() )
1002  continue;
1003 
1004  // loop over all ions of the isotope
1005  for( unsigned long ion=0; ion<it->second->ipMl.size(); ++ion )
1006  {
1007  if( it->second->ipMl[ion] != -1 &&
1008  (species[ it->second->ipMl[ion] ].location == NULL ) && (*atom)->ipMl[ion] != -1 )
1009  {
1010  species[ it->second->ipMl[ion] ].den = species[ (*atom)->ipMl[ion] ].den * it->second->frac;
1011  }
1012  }
1013  }
1014  }
1015 
1016  return;
1017 }
1018 
1020 {
1021  DEBUG_ENTRY( "t_mole_local::set_ion_locations()" );
1022 
1023  for( ChemNuclideList::iterator atom = nuclide_list.begin();
1024  atom != nuclide_list.end(); ++atom)
1025  {
1026  if ( !(*atom)->lgHasLinkedIon() )
1027  continue;
1028  long nelem = (*atom)->el->Z-1;
1029  for( long ion=0; ion<nelem+2; ++ion )
1030  {
1031  long mole_index = (*atom)->ipMl[ion];
1032  // element not enabled if index is -1
1033  if( mole_index != -1 )
1034  {
1035  ASSERT( mole_index < mole_global.num_total );
1036  species[mole_index].location = &(dense.xIonDense[nelem][ion]);
1037  dense.ionMole[nelem][ion] = &species[mole_index];
1038  }
1039  }
1040  }
1041 
1042  if( deut.lgElmtOn )
1043  {
1044  findspecieslocal("D")->location = &(deut.xIonDense[0]);
1045  findspecieslocal("D+")->location = &(deut.xIonDense[1]);
1046  }
1047 }
1048 
1050 {
1051  DEBUG_ENTRY( "total_molecule_deut()" );
1052 
1053  double total = 0.;
1054 
1055  if( !deut.lgElmtOn )
1056  return;
1057 
1058  for (long int i=0;i<mole_global.num_calc;++i)
1059  {
1060  if (mole.species[i].location == NULL && mole_global.list[i]->isIsotopicTotalSpecies() )
1061  {
1062  for( molecule::nNucsMap::iterator atom=mole_global.list[i]->nNuclide.begin();
1063  atom != mole_global.list[i]->nNuclide.end(); ++atom)
1064  {
1065  long int nelem = atom->first->el->Z-1;
1066  if( nelem==0 && atom->first->A==2 )
1067  {
1068  total += mole.species[i].den*atom->second;
1069  }
1070  }
1071  }
1072  }
1073 
1074  total_f = (realnum)total;
1075 
1076  return;
1077 }
1079 {
1080  DEBUG_ENTRY( "total_molecule_elems()" );
1081 
1082  /* now set total density of each element locked in molecular species */
1083  for ( long int nelem=ipHYDROGEN;nelem<LIMELM; ++nelem )
1084  {
1085  total[nelem] = 0.;
1086  }
1087  for (long int i=0;i<mole_global.num_calc;++i)
1088  {
1089  if (mole.species[i].location == NULL && mole_global.list[i]->isIsotopicTotalSpecies() )
1090  {
1091  for( molecule::nNucsMap::iterator atom=mole_global.list[i]->nNuclide.begin();
1092  atom != mole_global.list[i]->nNuclide.end(); ++atom)
1093  {
1094  ASSERT( atom->second > 0 );
1095  long int nelem = atom->first->el->Z-1;
1096  if( atom->first->lgMeanAbundance() )
1097  total[nelem] += (realnum) mole.species[i].den*atom->second;
1098  }
1099  }
1100  }
1101 }
1103 {
1104  long int i;
1105  realnum total;
1106 
1107  DEBUG_ENTRY( "total_molecules()" );
1108 
1109  total = 0.;
1110  for (i=0;i<mole_global.num_calc;++i)
1111  {
1112  if (mole.species[i].location == NULL && mole_global.list[i]->isIsotopicTotalSpecies())
1113  total += (realnum) mole.species[i].den;
1114  }
1115  return total;
1116 }
1118 {
1119  long int i;
1120  realnum total;
1121 
1122  DEBUG_ENTRY( "total_molecules_gasphase()" );
1123 
1124  total = 0.;
1125  for (i=0;i<mole_global.num_calc;++i)
1126  {
1127  if (mole_global.list[i]->lgGas_Phase && mole.species[i].location == NULL && mole_global.list[i]->isIsotopicTotalSpecies())
1128  total += (realnum) mole.species[i].den;
1129  }
1130  return total;
1131 }
1132 /*lint +e778 const express eval to 0 */
1133 /*lint +e725 expect positive indentation */
1134 
1136 {
1137  long int i, j;
1138  /* Neutrals and positive ions will be treated as single species inside
1139  molecular equilibrium solver, to facilitate coupling with ionization
1140  solvers */
1141  DEBUG_ENTRY ("mole_make_groups()" );
1142  if (mole_global.num_calc == 0)
1143  {
1144  groupspecies.resize(0);
1146  return;
1147  }
1149  for (i=0,j=0;i<mole_global.num_calc;i++)
1150  {
1151  if( mole_global.list[i]->isIsotopicTotalSpecies() && ( !mole_global.list[i]->isMonatomic() || mole_global.list[i]->charge <= 0 || ! mole_global.list[i]->lgGas_Phase ) )
1152  {
1153  /* Compound molecules and negative ions are represented individually */
1154  mole_global.list[i]->groupnum = j;
1155  groupspecies[j++] = &(*mole_global.list[i]);
1156  }
1157  else
1158  {
1159  /* All positive ions are collapsed into single macrostate (i.e. H+ -> H) */
1160  /* Need to increase constant if higher ions are included */
1161  ASSERT (mole_global.list[i]->charge < LIMELM+1);
1162  ASSERT (mole_global.list[i]->groupnum == -1);
1163  }
1164  }
1167 
1168  for (i=0;i<mole_global.num_calc;i++)
1169  {
1170  if (mole_global.list[i]->groupnum == -1)
1171  {
1172  if( mole_global.list[i]->isMonatomic() && mole_global.list[i]->isIsotopicTotalSpecies() )
1173  {
1174  for( nNucs_i it = mole_global.list[i]->nNuclide.begin(); it != mole_global.list[i]->nNuclide.end(); ++it )
1175  {
1176  ASSERT( it->second > 0 );
1177  mole_global.list[i]->groupnum = mole_global.list[it->first->ipMl[0]]->groupnum;
1178  break;
1179  }
1180  }
1181  else
1182  {
1183  ASSERT( !mole_global.list[i]->isIsotopicTotalSpecies() );
1184  mole_global.list[i]->groupnum = mole_global.list[ mole_global.list[i]->parentIndex ]->groupnum;
1185  }
1186  }
1187 
1188  ASSERT( mole_global.list[i]->groupnum != -1);
1189  }
1190 
1191  return;
1192 }
1193 
map< string, size_t >::iterator chem_nuclide_i
t_mole_global mole_global
Definition: mole.cpp:7
double grain_area
Definition: mole.h:457
nNucsMap nNuclide
Definition: mole.h:157
static void sort(MoleculeList::iterator start, MoleculeList::iterator end)
FILE * open_data(const char *fname, const char *mode, access_scheme scheme)
Definition: cpu.cpp:751
const double ENERGY_H2_STAR
Definition: h2_priv.h:449
chem_element * el
Definition: mole.h:47
molecule::nNucsMap::iterator nNucs_i
Definition: mole.h:242
realnum getMass(int Anum)
Definition: abund.h:86
bool lgElmtOn
Definition: deuterium.h:21
molecule * null_mole
int n_nuclei(void) const
Definition: mole.h:161
vector< bool > lgTreatIsotopes
Definition: mole.h:359
int num_total
Definition: mole.h:362
mole_state
Definition: mole.h:19
realnum total_molecules(void)
int num_calc
Definition: mole.h:362
map< int, count_ptr< chem_nuclide > >::iterator isotopes_i
Definition: mole.h:39
void make_species(void)
void total_molecule_deut(realnum &total)
map< string, count_ptr< molecule > >::iterator molecule_i
Definition: mole_priv.h:40
bool lgLeiden_Keep_ipMH2s
Definition: hmi.h:219
bool lgTraceMole
Definition: trace.h:55
void updateXMolecules()
Definition: dense.cpp:24
const string label
Definition: mole.h:32
void mole_make_list(void)
map< string, count_ptr< mole_reaction > > reactab
STATIC molecule * newspecies(const char label[], enum spectype type, enum mole_state state, realnum form_enthalpy)
molezone * ionMole[LIMELM][LIMELM+1]
Definition: dense.h:137
bool speciesOff(const string &label)
FILE * ioQQQ
Definition: cddefines.cpp:7
vector< count_ptr< chem_element > > ChemElementList
Definition: mole.h:126
map< string, size_t > nuclidetab
molezone * findspecieslocal(const char buf[])
Definition: mole.h:378
bool lgGas_Phase
Definition: mole.h:160
void InitDeuteriumIonization()
Definition: deuterium.cpp:29
ChemNuclideList nuclide_list
Definition: mole.h:142
bool parse_species_label(const char label[], ChemNuclideList &atomsLeftToRight, vector< int > &numAtoms, string &embellishments)
int A
Definition: mole.h:48
int num_compacted
Definition: mole.h:362
realnum mass_amu
Definition: mole.h:51
molezone * findspecieslocal_validate(const char buf[])
t_dense dense
Definition: global.cpp:15
realnum form_enthalpy
Definition: mole.h:189
double grain_density
Definition: mole.h:457
t_elementnames elementnames
Definition: elementnames.cpp:5
Definition: mole.h:19
vector< int > ipMl
Definition: mole.h:50
ChemElementList element_list
bool isEnabled
Definition: mole.h:153
int getAiso(int iso)
Definition: abund.h:79
double xIonDense[LIMELM][LIMELM+1]
Definition: dense.h:135
element_type * data() const
Definition: cddefines.h:1209
t_trace trace
Definition: trace.cpp:5
bool fp_equal(sys_float x, sys_float y, int n=3)
Definition: cddefines.h:858
t_abund abund
Definition: abund.cpp:5
t_isotope IsoAbn[LIMELM]
Definition: abund.h:213
STATIC realnum MeanMassOfElement(const count_ptr< chem_element > &el)
molecule::nNucsMap::reverse_iterator nNucs_ri
Definition: mole.h:243
bool lgLeidenHack
Definition: mole.h:334
const ios_base::openmode mode_r
Definition: cpu.h:267
map< int, count_ptr< chem_nuclide > > isotopes
Definition: mole.h:33
STATIC bool isactive(const molecule &mol)
#define STATIC
Definition: cddefines.h:118
map< string, count_ptr< chem_element > > elemtab
string parentLabel
Definition: mole.h:147
STATIC void read_species_file(string filename, bool lgCreateIsotopologues=true)
int groupnum
Definition: mole.h:194
chem_nuclide * null_nuclide
t_mole_local mole
Definition: mole.cpp:8
molecule * findspecies(const char buf[])
void create_isotopologues_one_position(unsigned position, ChemNuclideList &atoms, vector< int > &numAtoms, string atom_old, string atom_new, string embellishments, string &newLabel)
t_atoms atoms
Definition: atoms.cpp:5
int getNiso()
Definition: abund.h:66
float realnum
Definition: cddefines.h:124
valarray< class molezone > species
Definition: mole.h:468
#define EXIT_FAILURE
Definition: cddefines.h:168
enum mole_state state
Definition: mole.h:193
count_ptr< chem_nuclide > findnuclide(const char buf[])
STATIC void SetIsotopeFractions(const vector< bool > &lgResolveNelem)
#define cdEXIT(FAIL)
Definition: cddefines.h:484
int index
Definition: mole.h:194
realnum total_molecules_gasphase(void)
map< string, count_ptr< mole_reaction > > functab
const int Z
Definition: mole.h:31
STATIC void newelement(const char label[], int ipion)
realnum mole_return_cached_species(const GroupMap &MoleMap)
bool lgExcit
Definition: mole.h:159
diatomics h2("h2", 4100.,&hmi.H2_total, Yan_H2_CS)
int n_react
Definition: mole.h:195
bool exists(const molecule *m)
Definition: mole.h:258
void SetDeuteriumFractionation(const realnum &frac)
Definition: deuterium.cpp:57
void SetGasPhaseDeuterium(const realnum &Hdensity)
Definition: deuterium.cpp:65
t_state state
Definition: state.cpp:18
double xIonDense[2]
Definition: deuterium.h:23
bool lgElmtOn[LIMELM]
Definition: dense.h:160
char chElementSym[LIMELM][CHARS_ELEMENT_SYM]
Definition: elementnames.h:25
int compare(const molecule &mol2) const
Definition: mole.h:211
realnum gas_phase[LIMELM]
Definition: dense.h:76
vector< count_ptr< chem_nuclide > > ChemNuclideList
Definition: mole.h:124
realnum AtomicWeight[LIMELM]
Definition: dense.h:80
#define ASSERT(exp)
Definition: cddefines.h:617
T * get_ptr() const
Definition: count_ptr.h:49
void set_isotope_abundances(void)
STATIC void start(long, realnum[], realnum[], long, long[], realnum *, int *)
realnum getAbn(int Anum)
Definition: abund.h:121
Definition: abund.h:39
const int LIMELM
Definition: cddefines.h:307
bool isMonatomic(void) const
Definition: mole.h:182
int charge
Definition: mole.h:158
double den
Definition: mole.h:421
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:729
STATIC void newisotope(const count_ptr< chem_element > &el, int massNumberA, realnum mass_amu, double frac)
bool lgGrain_mole_deplete
Definition: mole.h:356
bool lgDifferByExcitation(const molecule &mol1, const molecule &mol2)
vector< GrainBin * > bin
Definition: grainvar.h:585
void mole_update_species_cache(void)
STATIC bool ispassive(const molecule &mol)
t_deuterium deut
Definition: deuterium.cpp:7
string label
Definition: mole.h:156
double elec
Definition: mole.h:460
realnum mole_mass
Definition: mole.h:190
int fprintf(const Output &stream, const char *format,...)
Definition: service.cpp:1217
MoleculeList list
Definition: mole.h:365
vector< molecule * > groupspecies
double frac
Definition: mole.h:52
spectype
sys_float SDIV(sys_float x)
Definition: cddefines.h:1006
const int ipCARBON
Definition: cddefines.h:353
string label(void) const
Definition: mole.h:66
bool lgNoHeavyMole
Definition: mole.h:328
#define fixit(a)
Definition: cddefines.h:416
bool lgElemsConserved(void)
Definition: dense.cpp:119
GrainVar gv
Definition: grainvar.cpp:5
t_hmi hmi
Definition: hmi.cpp:5
double frac(double d)
void set_ion_locations()
bool lgNoMole
Definition: mole.h:325
bool lgDustOn() const
Definition: grainvar.h:477
const int ipHYDROGEN
Definition: cddefines.h:348
void mole_make_groups(void)
map< string, count_ptr< molecule > > spectab
chem_element * null_element
void total_molecule_elems(realnum total[LIMELM])
molecule * findspecies_validate(const char buf[])
molezone * null_molezone
double * location
Definition: mole.h:411
void create_isotopologues(ChemNuclideList &atoms, vector< int > &numAtoms, string atom_old, string atom_new, string embellishments, vector< string > &newLabels)
double grain_saturation
Definition: mole.h:457
void updateXMolecules()
Definition: deuterium.cpp:16