Cantera  2.0
Elements.cpp
Go to the documentation of this file.
1 /**
2  * @file Elements.cpp
3  * This file contains a database of atomic weights.
4  */
5 
6 // Copyright 2003 California Institute of Technology
7 
9 #include "cantera/base/xml.h"
10 #include "cantera/base/ctml.h"
13 
14 using namespace ctml;
15 using namespace std;
16 
17 #include <cstdlib>
18 
19 namespace Cantera
20 {
21 
22 
23 /*! Database for atomic molecular weights
24  * Values are taken from the 1989 Standard Atomic Weights, CRC
25  *
26  * awTable[] is a static function with scope limited to this file.
27  * It can only be referenced via the LookupWtElements() function.
28  *
29  * units = kg / kg-mol (or equivalently gm / gm-mol)
30  *
31  * This structure was picked because it's simple, compact, and extensible.
32  */
33 struct awData {
34  char name[4]; //!< Null Terminated name, First letter capitalized
35  double atomicWeight; //!< atomic weight in kg / kg-mol
36 };
37 
38 /*!
39  * @var static struct awData aWTable[]
40  * \brief aWTable is a vector containing the atomic weights database.
41  *
42  * The size of the table is given by the initial instantiation.
43  */
44 static struct awData aWTable[] = {
45  {"H", 1.00794},
46  {"D", 2.0 },
47  {"Tr", 3.0 },
48  {"He", 4.002602},
49  {"Li", 6.941 },
50  {"Be", 9.012182},
51  {"B", 10.811 },
52  {"C", 12.011 },
53  {"N", 14.00674},
54  {"O", 15.9994 },
55  {"F", 18.9984032},
56  {"Ne", 20.1797 },
57  {"Na", 22.98977},
58  {"Mg", 24.3050 },
59  {"Al", 26.98154},
60  {"Si", 28.0855 },
61  {"P", 30.97376},
62  {"S", 32.066 },
63  {"Cl", 35.4527 },
64  {"Ar", 39.948 },
65  {"K", 39.0983 },
66  {"Ca", 40.078 },
67  {"Sc", 44.95591},
68  {"Ti", 47.88 },
69  {"V", 50.9415 },
70  {"Cr", 51.9961 },
71  {"Mn", 54.9381 },
72  {"Fe", 55.847 },
73  {"Co", 58.9332 },
74  {"Ni", 58.69 },
75  {"Cu", 63.546 },
76  {"Zn", 65.39 },
77  {"Ga", 69.723 },
78  {"Ge", 72.61 },
79  {"As", 74.92159},
80  {"Se", 78.96 },
81  {"Br", 79.904 },
82  {"Kr", 83.80 },
83  {"Rb", 85.4678 },
84  {"Sr", 87.62 },
85  {"Y", 88.90585},
86  {"Zr", 91.224 },
87  {"Nb", 92.90638},
88  {"Mo", 95.94 },
89  {"Tc", 97.9072 },
90  {"Ru", 101.07 },
91  {"Rh", 102.9055 },
92  {"Pd", 106.42 },
93  {"Ag", 107.8682 },
94  {"Cd", 112.411 },
95  {"In", 114.82 },
96  {"Sn", 118.710 },
97  {"Sb", 121.75 },
98  {"Te", 127.6 },
99  {"I", 126.90447},
100  {"Xe", 131.29 },
101  {"Cs", 132.90543},
102  {"Ba", 137.327 },
103  {"La", 138.9055 },
104  {"Ce", 140.115 },
105  {"Pr", 140.90765},
106  {"Nd", 144.24 },
107  {"Pm", 144.9127 },
108  {"Sm", 150.36 },
109  {"Eu", 151.965 },
110  {"Gd", 157.25 },
111  {"Tb", 158.92534},
112  {"Dy", 162.50 },
113  {"Ho", 164.93032},
114  {"Er", 167.26 },
115  {"Tm", 168.93421},
116  {"Yb", 173.04 },
117  {"Lu", 174.967 },
118  {"Hf", 178.49 },
119  {"Ta", 180.9479 },
120  {"W", 183.85 },
121  {"Re", 186.207 },
122  {"Os", 190.2 },
123  {"Ir", 192.22 },
124  {"Pt", 195.08 },
125  {"Au", 196.96654},
126  {"Hg", 200.59 },
127  {"Ti", 204.3833 },
128  {"Pb", 207.2 },
129  {"Bi", 208.98037},
130  {"Po", 208.9824 },
131  {"At", 209.9871 },
132  {"Rn", 222.0176 },
133  {"Fr", 223.0197 },
134  {"Ra", 226.0254 },
135  {"Ac", 227.0279 },
136  {"Th", 232.0381 },
137  {"Pa", 231.03588},
138  {"U", 238.0508 },
139  {"Np", 237.0482 },
140  {"Pu", 244.0482 }
141 };
142 
143 
144 // Static function to look up an atomic weight
145 /*
146  * This static function looks up the argument string in the
147  * database above and returns the associated molecular weight.
148  * The data are from the periodic table.
149  *
150  * Note: The idea behind this function is to provide a unified
151  * source for the element atomic weights. This helps to
152  * ensure that mass is conserved.
153  *
154  * @param s String, Only the first 3 characters are significant
155  *
156  * @return
157  * Return value contains the atomic weight of the element
158  * If a match for the string is not found, a value of -1.0 is
159  * returned.
160  *
161  * @exception CanteraError
162  * If a match is not found, a CanteraError is thrown as well
163  */
164 doublereal Elements::LookupWtElements(const std::string& ename)
165 {
166  int num = sizeof(aWTable) / sizeof(struct awData);
167  string s3 = ename.substr(0,3);
168  for (int i = 0; i < num; i++) {
169  if (s3 == aWTable[i].name) {
170  return (aWTable[i].atomicWeight);
171  }
172  }
173  throw CanteraError("LookupWtElements", "element not found");
174  return -1.0;
175 }
176 
177 doublereal LookupWtElements(const std::string& ename)
178 {
179  int num = sizeof(aWTable) / sizeof(struct awData);
180  string s3 = ename.substr(0,3);
181  for (int i = 0; i < num; i++) {
182  if (s3 == aWTable[i].name) {
183  return (aWTable[i].atomicWeight);
184  }
185  }
186  throw CanteraError("LookupWtElements", "element not found");
187  return -1.0;
188 }
189 
190 
191 
192 
193 
194 //! Exception class to indicate a fixed set of elements.
195 /*!
196  * This class is used to warn the user when the number of elements
197  * are changed after at least one species is defined.
198  */
200 {
201 public:
202  //! Constructor for class
203  /*!
204  * @param func Function where the error occurred.
205  */
206  ElementsFrozen(string func)
207  : CanteraError(func,
208  "elements cannot be added after species.") {}
209 };
210 
211 /*
212  * Elements Class Constructor
213  * We initialize all internal variables to zero here.
214  */
215 Elements::Elements() :
216  m_mm(0),
217  m_elementsFrozen(false),
218  m_elem_type(0),
219  numSubscribers(0)
220 {
221 }
222 
223 /*
224  * Elements Class Destructor
225  * If the number of subscribers is not zero, through an error.
226  * A logic problem has occurred.
227  *
228  * @exception CanteraError
229  */
231 {
232  if (numSubscribers != 0) {
233  throw CanteraError("~Elements", "numSubscribers not zero");
234  }
235 }
236 
238  m_mm(0),
239  m_elementsFrozen(false),
240  numSubscribers(0)
241 {
242  *this = operator=(right);
243 }
244 
246 {
247  if (&right == this) {
248  return *this;
249  }
250 
251  m_mm = right.m_mm;
256  m_entropy298 = right.m_entropy298;
257  m_elem_type = right.m_elem_type;
258  numSubscribers = 0;
259 
260  return *this;
261 }
262 
263 
264 
265 /*
266  * freezeElements():
267  *
268  * Set the freeze flag. This is a prerequesite to other
269  * activivities, i.e., this is done before species are defined.
270  */
272 {
273  m_elementsFrozen = true;
274 }
275 
276 /*
277  * elementIndex():
278  *
279  * Index of element named \c name. The index is an integer
280  * assigned to each element in the order it was added,
281  * beginning with 0 for the first element. If \c name is not
282  * the name of an element in the set, then the value -1 is
283  * returned.
284  *
285  */
286 int Elements::elementIndex(std::string name) const
287 {
288  for (int i = 0; i < m_mm; i++) {
289  if (m_elementNames[i] == name) {
290  return i;
291  }
292  }
293  return -1;
294 }
295 
296 /*
297  *
298  * Name of the element with index \c m. @param m Element
299  * index. If m < 0 or m >= nElements() an exception is thrown.
300  */
301 string Elements::elementName(int m) const
302 {
303  if (m < 0 || m >= nElements()) {
304  throw CanteraError("Elements::elementName()", "out of bounds: " + int2str(m) + " " + int2str(nElements()));
305  }
306  return m_elementNames[m];
307 }
308 
309 
310 doublereal Elements::entropyElement298(int m) const
311 {
313  "Elements::entropy298",
314  "Entropy at 298 K of element is unknown");
315  AssertTrace(m >= 0 && m < m_mm);
316  return (m_entropy298[m]);
317 }
318 //====================================================================================================================
319 //! Return the element constraint type
320 /*!
321  * Possible types include:
322  *
323  * CT_ELEM_TYPE_TURNEDOFF -1
324  * CT_ELEM_TYPE_ABSPOS 0
325  * CT_ELEM_TYPE_ELECTRONCHARGE 1
326  * CT_ELEM_TYPE_CHARGENEUTRALITY 2
327  * CT_ELEM_TYPE_LATTICERATIO 3
328  * CT_ELEM_TYPE_KINETICFROZEN 4
329  * CT_ELEM_TYPE_SURFACECONSTRAINT 5
330  * CT_ELEM_TYPE_OTHERCONSTRAINT 6
331  *
332  * The default is CT_ELEM_TYPE_ABSPOS
333  */
334 int Elements::elementType(int m) const
335 {
336  return m_elem_type[m];
337 }
338 //====================================================================================================================
339 // Change the element type of the mth constraint
340 /*
341  * Reassigns an element type
342  *
343  * @param m Element index
344  * @param elem_type New elem type to be assigned
345  *
346  * @return Returns the old element type
347  */
348 int Elements::changeElementType(int m, int elem_type)
349 {
350  int old = m_elem_type[m];
351  m_elem_type[m] = elem_type;
352  return old;
353 }
354 //====================================================================================================================
355 /*
356  *
357  * Add an element to the current set of elements in the current object.
358  * @param symbol symbol string
359  * @param weight atomic weight in kg/kmol.
360  *
361  * The default weight is a special value, which will cause the
362  * routine to look up the actual weight via a string lookup.
363  *
364  * There are two interfaces to this routine. The XML interface
365  * looks up the required parameters for the regular interface
366  * and then calls the base routine.
367  */
368 void Elements::
369 addElement(const std::string& symbol, doublereal weight)
370 {
371  if (weight == -12345.0) {
372  weight = LookupWtElements(symbol);
373  if (weight < 0.0) {
374  throw ElementsFrozen("addElement");
375  }
376  }
377  if (m_elementsFrozen) {
378  throw ElementsFrozen("addElement");
379  return;
380  }
381  m_atomicWeights.push_back(weight);
382  m_elementNames.push_back(symbol);
383  if (symbol == "E") {
385  } else {
386  m_elem_type.push_back(CT_ELEM_TYPE_ABSPOS);
387  }
388 
389  m_mm++;
390 }
391 //===========================================================================================================
392 void Elements::
394 {
395  doublereal weight = atof(e["atomicWt"].c_str());
396  string symbol = e["name"];
397  addElement(symbol, weight);
398 }
399 //===========================================================================================================
400 /*
401  * addUniqueElement():
402  *
403  * Add a unique element to the set. This routine will not allow
404  * duplicate elements to be input.
405  *
406  * @param symbol symbol string
407  * @param weight atomic weight in kg/kmol.
408  *
409  *
410  * The default weight is a special value, which will cause the
411  * routine to look up the actual weight via a string lookup.
412  */
413 void Elements::
414 addUniqueElement(const std::string& symbol,
415  doublereal weight, int atomicNumber, doublereal entropy298,
416  int elem_type)
417 {
418  if (weight == -12345.0) {
419  weight = LookupWtElements(symbol);
420  if (weight < 0.0) {
421  throw ElementsFrozen("addElement");
422  }
423  }
424  /*
425  * First decide if this element has been previously added
426  * by conducting a string search. If it unique, add it to
427  * the list.
428  */
429  int ifound = 0;
430  int i = 0;
431  for (vector<string>::const_iterator it = m_elementNames.begin();
432  it < m_elementNames.end(); ++it, ++i) {
433  if (*it == symbol) {
434  ifound = 1;
435  break;
436  }
437  }
438  if (!ifound) {
439  if (m_elementsFrozen) {
440  throw ElementsFrozen("addElement");
441  return;
442  }
443  m_atomicWeights.push_back(weight);
444  m_elementNames.push_back(symbol);
445  m_atomicNumbers.push_back(atomicNumber);
446  m_entropy298.push_back(entropy298);
447  if (symbol == "E") {
449  } else {
450  m_elem_type.push_back(elem_type);
451  }
452  m_mm++;
453  } else {
454  if (m_atomicWeights[i] != weight) {
455  throw CanteraError("AddUniqueElement",
456  "Duplicate Elements (" + symbol + ") have different weights");
457  }
458  }
459 }
460 
461 /*
462  * @todo call addUniqueElement(symbol, weight) instead of
463  * addElement.
464  */
465 void Elements::
467 {
468  doublereal weight = 0.0;
469  if (e.hasAttrib("atomicWt")) {
470  weight = atof(stripws(e["atomicWt"]).c_str());
471  }
472  int anum = 0;
473  if (e.hasAttrib("atomicNumber")) {
474  anum = atoi(stripws(e["atomicNumber"]).c_str());
475  }
476  string symbol = e["name"];
477  doublereal entropy298 = ENTROPY298_UNKNOWN;
478  if (e.hasChild("entropy298")) {
479  XML_Node& e298Node = e.child("entropy298");
480  if (e298Node.hasAttrib("value")) {
481  entropy298 = atofCheck(stripws(e298Node["value"]).c_str());
482  }
483  }
484  if (weight != 0.0) {
485  addUniqueElement(symbol, weight, anum, entropy298);
486  } else {
487  addUniqueElement(symbol);
488  }
489 }
490 
491 // True if freezeElements has been called.
493 {
494  return m_elementsFrozen;
495 }
496 
497 /*
498  * clear()
499  *
500  * Remove all elements from the structure.
501  */
503 {
504  m_mm = 0;
505  m_atomicWeights.resize(0);
506  m_elementNames.resize(0);
507  m_entropy298.resize(0);
508  m_elem_type.resize(0);
509  m_elementsFrozen = false;
510 }
511 
512 /*
513  * ready():
514  *
515  * True if the elements have been frozen
516  */
517 bool Elements::ready() const
518 {
519  return (m_elementsFrozen);
520 }
521 
522 
524 {
525 
526  // get the declared element names
527  if (! phase.hasChild("elementArray")) {
528  throw CanteraError("Elements::addElementsFromXML",
529  "phase xml node doesn't have \"elementArray\" XML Node");
530  }
531  XML_Node& elements = phase.child("elementArray");
532  vector<string> enames;
533  getStringArray(elements, enames);
534 
535  // // element database defaults to elements.xml
536  string element_database = "elements.xml";
537  if (elements.hasAttrib("datasrc")) {
538  element_database = elements["datasrc"];
539  }
540 
541  XML_Node* doc = get_XML_File(element_database);
542  XML_Node* dbe = &doc->child("ctml/elementData");
543 
544  XML_Node& root = phase.root();
545  XML_Node* local_db = 0;
546  if (root.hasChild("ctml")) {
547  if (root.child("ctml").hasChild("elementData")) {
548  local_db = &root.child("ctml/elementData");
549  }
550  }
551 
552  int nel = static_cast<int>(enames.size());
553  int i;
554  string enm;
555  XML_Node* e = 0;
556  for (i = 0; i < nel; i++) {
557  e = 0;
558  if (local_db) {
559  //writelog("looking in local database.");
560  e = local_db->findByAttr("name",enames[i]);
561  //if (!e) writelog(enames[i]+" not found.");
562  }
563  if (!e) {
564  e = dbe->findByAttr("name",enames[i]);
565  }
566  if (e) {
567  addUniqueElement(*e);
568  } else {
569  throw CanteraError("addElementsFromXML","no data for element "
570  +enames[i]);
571  }
572  }
573 
574 }
575 
576 /*
577  * subscribe(), unsubscribe(), and reportSubscriptions():
578  *
579  * Handles setting and reporting the number of subscriptions to this
580  * object.
581  */
583 {
584  ++numSubscribers;
585 }
587 {
588  --numSubscribers;
589  return numSubscribers;
590 }
592 {
593  return numSubscribers;
594 }
595 
596 /********************* GLOBAL STATIC SECTION **************************/
597 /*
598  * We keep track of a vector of pointers to element objects.
599  * Initially there are no Elements objects. Whenever one is created,
600  * the pointer to that object is added onto this list.
601  */
602 vector<Elements*> Elements::Global_Elements_List;
603 /***********************************************************************/
604 }