Elements.cpp

Go to the documentation of this file.
00001 /**
00002  *  @file Elements.cpp
00003  *  Declaration file for class, Elements, which contains the elements that
00004  *  make up species (see \ref phases and \link Cantera::Elements Elements\endlink).
00005  *
00006  *  This file contains the definitions for functions in the class Elements.
00007  *  It also contains a database of atomic weights.
00008  */
00009 
00010 /****************************************************************************
00011  *  $RCSfile: Elements.cpp,v $
00012  *  $Author: hkmoffa $
00013  *  $Date: 2010-02-09 15:24:11 -0500 (Tue, 09 Feb 2010) $
00014  *  $Revision: 398 $
00015  *
00016  *
00017  ****************************************************************************/
00018 //  Copyright 2003  California Institute of Technology
00019 
00020 
00021 #ifdef WIN32
00022 #pragma warning(disable:4786)
00023 #endif
00024 
00025 #include "Elements.h"
00026 #include "xml.h"
00027 #include "ctml.h"
00028 #include "ctexceptions.h"
00029 #include "stringUtils.h"
00030 
00031 using namespace ctml;
00032 using namespace std;
00033 
00034 #ifdef USE_DGG_CODE
00035 #include <map>
00036 #endif
00037 #include <cstdlib>
00038 
00039 namespace Cantera {
00040 
00041     
00042   /* awData structure */
00043   /**
00044    * Database for atomic molecular weights
00045    *
00046    *  Values are taken from the 1989 Standard Atomic Weights, CRC
00047    *
00048    *  awTable[] is a static function with scope limited to this file.
00049    *  It can only be referenced via the static Elements class function,
00050    *  LookupWtElements().
00051    *
00052    *  units = kg / kg-mol (or equivalently gm / gm-mol)
00053    *
00054    * (note: this structure was picked because it's simple, compact,
00055    *          and extensible).
00056    *
00057    */
00058   struct awData {
00059     char name[4];         ///< Null Terminated name, First letter capitalized
00060     double atomicWeight;  ///< atomic weight in kg / kg-mol
00061   };
00062 
00063   /*!
00064    *  @var static struct awData aWTable[]
00065    *  \brief aWTable is a vector containing the atomic weights database.
00066    *
00067    *  The size of the table is given by the initial instantiation.
00068    */
00069   static struct awData aWTable[] = {
00070     {"H",    1.00794},
00071     {"D",    2.0    },
00072     {"Tr",   3.0    },
00073     {"He",   4.002602},
00074     {"Li",   6.941  },
00075     {"Be",   9.012182},
00076     {"B",   10.811  },
00077     {"C",   12.011  },
00078     {"N",   14.00674},
00079     {"O",   15.9994 },
00080     {"F",   18.9984032},
00081     {"Ne",  20.1797 },
00082     {"Na",  22.98977},
00083     {"Mg",  24.3050 },
00084     {"Al",  26.98154},
00085     {"Si",  28.0855 },
00086     {"P",   30.97376},
00087     {"S",   32.066  },
00088     {"Cl",  35.4527 },
00089     {"Ar",  39.948  },
00090     {"K",   39.0983 },
00091     {"Ca",  40.078  },
00092     {"Sc",  44.95591},
00093     {"Ti",  47.88   },
00094     {"V",   50.9415 },
00095     {"Cr",  51.9961 },
00096     {"Mn",  54.9381 },
00097     {"Fe",  55.847  },
00098     {"Co",  58.9332 },
00099     {"Ni",  58.69   },
00100     {"Cu",  63.546  },
00101     {"Zn",  65.39   },
00102     {"Ga",  69.723  },
00103     {"Ge",  72.61   },
00104     {"As",  74.92159},
00105     {"Se",  78.96   },
00106     {"Br",  79.904  },
00107     {"Kr",  83.80   },
00108     {"Rb",  85.4678 },
00109     {"Sr",  87.62   },
00110     {"Y",   88.90585},
00111     {"Zr",  91.224  },
00112     {"Nb",  92.90638},
00113     {"Mo",  95.94   },
00114     {"Tc",  97.9072 },
00115     {"Ru", 101.07   },
00116     {"Rh", 102.9055 },
00117     {"Pd", 106.42   },
00118     {"Ag", 107.8682 },
00119     {"Cd", 112.411  },
00120     {"In", 114.82   },
00121     {"Sn", 118.710  },
00122     {"Sb", 121.75   },
00123     {"Te", 127.6    },
00124     {"I",  126.90447},
00125     {"Xe", 131.29   },
00126     {"Cs", 132.90543},
00127     {"Ba", 137.327  },
00128     {"La", 138.9055 },
00129     {"Ce", 140.115  },
00130     {"Pr", 140.90765},
00131     {"Nd", 144.24   },
00132     {"Pm", 144.9127 },
00133     {"Sm", 150.36   },
00134     {"Eu", 151.965  },
00135     {"Gd", 157.25   },
00136     {"Tb", 158.92534},
00137     {"Dy", 162.50   },
00138     {"Ho", 164.93032},
00139     {"Er", 167.26   },
00140     {"Tm", 168.93421},
00141     {"Yb", 173.04   },
00142     {"Lu", 174.967  },
00143     {"Hf", 178.49   },
00144     {"Ta", 180.9479 },
00145     {"W",  183.85   },
00146     {"Re", 186.207  },
00147     {"Os", 190.2    },
00148     {"Ir", 192.22   },
00149     {"Pt", 195.08   },
00150     {"Au", 196.96654},
00151     {"Hg", 200.59   },
00152     {"Ti", 204.3833 },
00153     {"Pb", 207.2    },
00154     {"Bi", 208.98037},
00155     {"Po", 208.9824 },
00156     {"At", 209.9871 },
00157     {"Rn", 222.0176 },
00158     {"Fr", 223.0197 },
00159     {"Ra", 226.0254 },
00160     {"Ac", 227.0279 },
00161     {"Th", 232.0381 },
00162     {"Pa", 231.03588},
00163     {"U",  238.0508 },
00164     {"Np", 237.0482 },
00165     {"Pu", 244.0482 }
00166   }; 
00167    
00168 
00169   //  Static function to look up an atomic weight 
00170   /*
00171    *   This static function looks up the argument string in the
00172    *   database above and returns the associated molecular weight.
00173    *   The data are from the periodic table.
00174    *
00175    *  Note: The idea behind this function is to provide a unified 
00176    *        source for the element atomic weights. This helps to
00177    *        ensure that mass is conserved.
00178    *
00179    *  @param s  String, Only the first 3 characters are significant
00180    *
00181    *  @return
00182    *    Return value contains the atomic weight of the element
00183    *    If a match for the string is not found, a value of -1.0 is
00184    *    returned.
00185    *
00186    *  @exception CanteraError 
00187    *    If a match is not found, a CanteraError is thrown as well  
00188    */
00189   doublereal Elements::LookupWtElements(const std::string& ename) {
00190     int num = sizeof(aWTable) / sizeof(struct awData);
00191     string s3 = ename.substr(0,3);
00192     for (int i = 0; i < num; i++) {
00193       if (s3 == aWTable[i].name) {
00194         return (aWTable[i].atomicWeight);
00195       }
00196     }
00197     throw CanteraError("LookupWtElements", "element not found");
00198     return -1.0;
00199   }
00200   
00201     
00202   //!  Exception class to indicate a fixed set of elements.
00203   /*!
00204    *   This class is used to warn the user when the number of elements
00205    *   are changed after at least one species is defined.
00206    */ 
00207   class ElementsFrozen : public CanteraError {
00208   public:
00209     //! Constructor for class
00210     /*!
00211      * @param func Function where the error occurred.
00212      */
00213     ElementsFrozen(string func) 
00214       : CanteraError(func,
00215                      "elements cannot be added after species.") {}
00216   };
00217   
00218   /*
00219    *  Elements Class Constructor
00220    *    We initialize all internal variables to zero here.
00221    */
00222   Elements::Elements() :
00223     m_mm(0),
00224     m_elementsFrozen(false),
00225     numSubscribers(0)
00226   {
00227   }
00228 
00229   /*
00230    * Elements Class Destructor
00231    *   If the number of subscribers is not zero, through an error.
00232    *   A logic problem has occurred.
00233    *
00234    *  @exception CanteraError
00235    */
00236   Elements::~Elements() { 
00237     if (numSubscribers != 0) {
00238       throw CanteraError("~Elements", "numSubscribers not zero");
00239     }
00240   }
00241 
00242   Elements::Elements(const Elements &right) :
00243     m_mm(0),
00244     m_elementsFrozen(false),
00245     numSubscribers(0)
00246   {
00247     *this = operator=(right);
00248   }
00249 
00250   Elements& Elements::operator=(const Elements &right) {
00251     if (&right == this) return *this;
00252     
00253     m_mm             = right.m_mm;
00254     m_elementsFrozen = right.m_elementsFrozen;
00255     m_atomicWeights  = right.m_atomicWeights;
00256     m_atomicNumbers  = right.m_atomicNumbers;
00257     m_elementNames   = right.m_elementNames;
00258     m_entropy298     = right.m_entropy298;
00259 
00260     numSubscribers = 0;
00261 
00262     return *this;
00263   }
00264 
00265 
00266  
00267   /*
00268    * freezeElements():
00269    *
00270    *   Set the freeze flag. This is a prerequesite to other 
00271    *   activivities, i.e., this is done before species are defined.
00272    */
00273   void Elements::freezeElements() {
00274     m_elementsFrozen = true;
00275   }
00276 
00277 #ifdef INCL_DEPRECATED_METHODS
00278   /*
00279    *
00280    *   Returns an ElementData struct that contains the parameters 
00281    *   for element index m.
00282    */
00283   ElementData Elements::element(int m) const { 
00284     ElementData e;
00285     e.name = m_elementNames[m];
00286     e.atomicWeight = m_atomicWeights[m];
00287     return e;
00288   }
00289 #endif  
00290   /*
00291    * elementIndex():
00292    *
00293    * Index of element named \c name. The index is an integer
00294    * assigned to each element in the order it was added,
00295    * beginning with 0 for the first element.  If \c name is not
00296    * the name of an element in the set, then the value -1 is
00297    * returned.
00298    *
00299    */
00300 #ifdef USE_DGG_CODE
00301   int Elements::elementIndex(std::string name) const{
00302     map<string, int>::const_iterator it;
00303     it = m_definedElements.find(name);
00304     if (it != m_definedElements.end()) {
00305       return it->second;
00306     }
00307     return -1;
00308   }
00309 #else
00310   int Elements::elementIndex(std::string name) const {
00311     for (int i = 0; i < m_mm; i++) {
00312       if (m_elementNames[i] == name) return i;
00313     }
00314     return -1;
00315   }
00316 #endif
00317 
00318   /*
00319    *
00320    * Name of the element with index \c m.  @param m Element
00321    * index. If m < 0 or m >= nElements() an exception is thrown.
00322    */
00323   string Elements::elementName(int m) const {
00324     if (m < 0 || m >= nElements()) {
00325       throw ElementRangeError("Elements::elementName", m, nElements());
00326     }
00327     return m_elementNames[m];
00328   }
00329 
00330 
00331 
00332   doublereal Elements::entropyElement298(int m) const {
00333     AssertThrowMsg(m_entropy298[m] != ENTROPY298_UNKNOWN,
00334                    "Elements::entropy298",
00335                    "Entropy at 298 K of element is unknown");
00336     AssertTrace(m >= 0 && m < m_mm);
00337     return (m_entropy298[m]);
00338   }
00339   
00340   /*
00341    *
00342    * Add an element to the current set of elements in the current object.
00343    * @param symbol  symbol string
00344    * @param weight  atomic weight in kg/kmol.
00345    *
00346    *  The default weight is a special value, which will cause the
00347    *  routine to look up the actual weight via a string lookup.
00348    *
00349    *  There are two interfaces to this routine. The XML interface
00350    *  looks up the required parameters for the regular interface
00351    *  and then calls the base routine.
00352    */
00353   void Elements::
00354   addElement(const std::string& symbol, doublereal weight) 
00355   {
00356     if (weight == -12345.0) {
00357       weight = LookupWtElements(symbol);
00358       if (weight < 0.0) {
00359         throw ElementsFrozen("addElement");
00360       }
00361     }
00362     if (m_elementsFrozen) {
00363       throw ElementsFrozen("addElement");
00364       return;
00365     }
00366     m_atomicWeights.push_back(weight);
00367     m_elementNames.push_back(symbol);
00368 #ifdef USE_DGG_CODE
00369     m_definedElements[symbol] = nElements() + 1;
00370 #endif
00371     m_mm++;
00372   }
00373 
00374   void Elements::
00375   addElement(const XML_Node& e) {
00376     doublereal weight = atof(e["atomicWt"].c_str());
00377     string symbol = e["name"];
00378     addElement(symbol, weight);
00379   }
00380 
00381   /*
00382    *  addUniqueElement():
00383    *
00384    * Add a unique element to the set. This routine will not allow 
00385    * duplicate elements to be input.
00386    *
00387    * @param symbol  symbol string
00388    * @param weight  atomic weight in kg/kmol.
00389    *
00390    *
00391    *  The default weight is a special value, which will cause the
00392    *  routine to look up the actual weight via a string lookup.
00393    */
00394 #ifdef USE_DGG_CODE
00395   void Elements::
00396   addUniqueElement(const std::string& symbol, doublereal weight, int atomicNumber,
00397                    doublereal entropy298) 
00398   {
00399     if (m_elementsFrozen) 
00400       throw ElementsFrozen("addElement");
00401 
00402     if (weight == -12345.0) {
00403       weight =  LookupWtElements(symbol);
00404     }
00405 
00406     /*
00407      * First decide if this element has been previously added.
00408      * If it unique, add it to the list.
00409      */
00410      
00411     int i = m_definedElements[symbol] - 1;
00412     if (i < 0) {
00413       m_atomicWeights.push_back(weight);
00414       m_elementNames.push_back(symbol);
00415       m_atomicNumbers.push_back(atomicNumber);
00416       m_entropy298.push_back(entropy298);
00417       m_mm++;
00418     } 
00419     else {
00420       if (m_atomicWeights[i] != weight) {
00421         throw CanteraError("AddUniqueElement", 
00422                            "Duplicate Elements (" + symbol +
00423                            ") have different weights");
00424       }
00425     }
00426   }
00427 
00428 #else
00429   void Elements::
00430   addUniqueElement(const std::string& symbol, 
00431                    doublereal weight, int atomicNumber, doublereal entropy298) 
00432   {
00433     if (weight == -12345.0) {
00434       weight =  LookupWtElements(symbol);
00435       if (weight < 0.0) {
00436         throw ElementsFrozen("addElement");
00437       }
00438     }
00439     /*
00440      * First decide if this element has been previously added
00441      * by conducting a string search. If it unique, add it to
00442      * the list.
00443      */
00444     int ifound = 0;
00445     int i = 0;
00446     for (vector<string>::const_iterator it = m_elementNames.begin();
00447          it < m_elementNames.end(); ++it, ++i) {
00448       if (*it == symbol) {
00449         ifound = 1;
00450         break;
00451       }
00452     }
00453     if (!ifound) {
00454       if (m_elementsFrozen) {
00455         throw ElementsFrozen("addElement");
00456         return;
00457       }
00458       m_atomicWeights.push_back(weight);
00459       m_elementNames.push_back(symbol);
00460       m_atomicNumbers.push_back(atomicNumber);
00461       m_entropy298.push_back(entropy298);
00462       m_mm++;
00463     } else {
00464       if (m_atomicWeights[i] != weight) {
00465         throw CanteraError("AddUniqueElement", 
00466                            "Duplicate Elements (" + symbol +
00467                            ") have different weights");
00468       }
00469     }
00470   }
00471 #endif
00472 
00473 
00474   /*
00475    * @todo call addUniqueElement(symbol, weight) instead of
00476    * addElement.
00477    */
00478   void Elements::
00479   addUniqueElement(const XML_Node& e) {
00480     doublereal weight = 0.0;
00481     if (e.hasAttrib("atomicWt")) 
00482       weight = atof(stripws(e["atomicWt"]).c_str());
00483     int anum = 0;
00484     if (e.hasAttrib("atomicNumber")) 
00485       anum = atoi(stripws(e["atomicNumber"]).c_str());
00486     string symbol = e["name"];
00487     doublereal entropy298 = ENTROPY298_UNKNOWN;
00488     if (e.hasChild("entropy298")) {
00489       XML_Node& e298Node = e.child("entropy298");
00490       if (e298Node.hasAttrib("value")) {
00491         entropy298 = atofCheck(stripws(e298Node["value"]).c_str());
00492       }
00493     }
00494     if (weight != 0.0) {
00495       addUniqueElement(symbol, weight, anum, entropy298);
00496     } else {
00497       addUniqueElement(symbol);
00498     }
00499   }
00500  
00501   /*
00502    * clear()
00503    *
00504    *   Remove all elements from the structure.
00505    */
00506   void Elements::clear() {
00507     m_mm = 0;
00508     m_atomicWeights.resize(0);
00509     m_elementNames.resize(0);
00510     m_elementsFrozen = false;
00511   }
00512 
00513   /*
00514    * ready():
00515    *
00516    * True if the elements have been frozen
00517    */
00518   bool Elements::ready() const { 
00519     return (m_elementsFrozen);
00520   }
00521 
00522 
00523   void Elements::addElementsFromXML(const XML_Node& phase) {
00524 
00525     // get the declared element names
00526     if (! phase.hasChild("elementArray")) {
00527       throw CanteraError("Elements::addElementsFromXML",
00528                          "phase xml node doesn't have \"elementArray\" XML Node");
00529     }
00530     XML_Node& elements = phase.child("elementArray");
00531     vector<string> enames;
00532     getStringArray(elements, enames);
00533         
00534     // // element database defaults to elements.xml
00535     string element_database = "elements.xml";
00536     if (elements.hasAttrib("datasrc")) 
00537       element_database = elements["datasrc"];
00538 
00539     XML_Node* doc = get_XML_File(element_database);
00540     XML_Node* dbe = &doc->child("ctml/elementData");
00541 
00542     XML_Node& root = phase.root();
00543     XML_Node* local_db = 0;
00544     if (root.hasChild("ctml")) {
00545       if (root.child("ctml").hasChild("elementData")) {
00546         local_db = &root.child("ctml/elementData");
00547       }
00548     }
00549 
00550     int nel = static_cast<int>(enames.size());
00551     int i;
00552     string enm;
00553     XML_Node* e = 0;
00554     for (i = 0; i < nel; i++) {
00555       e = 0;
00556       if (local_db) {
00557         //writelog("looking in local database.");
00558         e = local_db->findByAttr("name",enames[i]);
00559         //if (!e) writelog(enames[i]+" not found.");
00560       }
00561       if (!e)
00562         e = dbe->findByAttr("name",enames[i]);
00563       if (e) {
00564         addUniqueElement(*e);
00565       }
00566       else {
00567         throw CanteraError("addElementsFromXML","no data for element "
00568                            +enames[i]);
00569       }
00570     }
00571 
00572   }
00573 
00574   /*
00575    *  subscribe(), unsubscribe(), and reportSubscriptions():
00576    *
00577    *   Handles setting and reporting the number of subscriptions to this
00578    *   object.
00579    */
00580   void Elements::subscribe() {
00581     ++numSubscribers;
00582   }
00583   int Elements::unsubscribe() {
00584     --numSubscribers;
00585     return numSubscribers;
00586   }
00587   int Elements::reportSubscriptions() const {
00588     return numSubscribers;
00589   }
00590 
00591   /********************* GLOBAL STATIC SECTION **************************/
00592   /*
00593    *  We keep track of a vector of pointers to element objects.
00594    *  Initially there are no Elements objects. Whenever one is created,
00595    *  the pointer to that object is added onto this list.  
00596    */
00597   vector<Elements *> Elements::Global_Elements_List;
00598   /***********************************************************************/
00599 }
Generated by  doxygen 1.6.3