Cantera  2.0
ThermoFactory.cpp
Go to the documentation of this file.
1 /**
2  * @file ThermoFactory.cpp
3  * Definitions for the factory class that can create known %ThermoPhase objects
4  * (see \ref thermoprops and class \link Cantera::ThermoFactory ThermoFactory\endlink).
5  *
6  */
7 // Copyright 2001 California Institute of Technology
8 
10 
14 #include "cantera/thermo/VPSSMgr.h"
15 #include "VPSSMgrFactory.h"
16 
22 
25 
29 
32 
33 #undef USE_SSTP
34 #ifdef USE_SSTP
36 #else
38 #endif
39 
43 
46 
47 #include "cantera/thermo/HMWSoln.h"
51 #include "cantera/thermo/MixedSolventElectrolyte.h"
52 
54 
55 #include <cstdlib>
56 
57 using namespace std;
58 using namespace ctml;
59 
60 namespace Cantera
61 {
62 
63 ThermoFactory* ThermoFactory::s_factory = 0;
64 mutex_t ThermoFactory::thermo_mutex;
65 
66 //! Define the number of %ThermoPhase types for use in this factory routine
67 /*!
68  * @deprecated This entire structure could be replaced with a std::map
69  */
70 static int ntypes = 23;
71 
72 //! Define the string name of the %ThermoPhase types that are handled by this factory routine
73 static string _types[] = {"IdealGas", "Incompressible",
74  "Surface", "Edge", "Metal", "StoichSubstance",
75  "PureFluid", "LatticeSolid", "Lattice",
76  "HMW", "IdealSolidSolution", "DebyeHuckel",
77  "IdealMolalSolution", "IdealGasVPSS",
78  "MineralEQ3", "MetalSHEelectrons", "Margules", "PhaseCombo_Interaction",
79  "IonsFromNeutralMolecule", "FixedChemPot", "MolarityIonicVPSSTP",
80  "MixedSolventElectrolyte", "Redlich-Kister"
81  };
82 
83 //! Define the integer id of the %ThermoPhase types that are handled by this factory routine
84 static int _itypes[] = {cIdealGas, cIncompressible,
85  cSurf, cEdge, cMetal, cStoichSubstance,
86  cPureFluid, cLatticeSolid, cLattice,
89  cMineralEQ3, cMetalSHEelectrons,
90  cMargulesVPSSTP, cPhaseCombo_Interaction, cIonsFromNeutral, cFixedChemPot,
91  cMolarityIonicVPSSTP, cMixedSolventElectrolyte, cRedlichKisterVPSSTP
92  };
93 
94 /*
95  * This method returns a new instance of a subclass of ThermoPhase
96  */
98 {
99 
100  int ieos=-1;
101 
102  for (int n = 0; n < ntypes; n++) {
103  if (model == _types[n]) {
104  ieos = _itypes[n];
105  }
106  }
107 
108  ThermoPhase* th=0;
109  switch (ieos) {
110 
111  case cIdealGas:
112  th = new IdealGasPhase;
113  break;
114 
115  case cIncompressible:
116  th = new ConstDensityThermo;
117  break;
118 
119  case cSurf:
120  th = new SurfPhase;
121  break;
122 
123  case cEdge:
124  th = new EdgePhase;
125  break;
126 
128  th = new IdealSolidSolnPhase();
129  break;
130 
131  case cMargulesVPSSTP:
132  th = new MargulesVPSSTP();
133  break;
134 
135  case cRedlichKisterVPSSTP:
136  th = new RedlichKisterVPSSTP();
137  break;
138 
139  case cMolarityIonicVPSSTP:
140  th = new MolarityIonicVPSSTP();
141  break;
142 
143  case cPhaseCombo_Interaction:
144  th = new PhaseCombo_Interaction();
145  break;
146 
147  case cIonsFromNeutral:
148  th = new IonsFromNeutralVPSSTP();
149  break;
150 
151  case cMetal:
152  th = new MetalPhase;
153  break;
154 
155  case cStoichSubstance:
156 #ifdef USE_SSTP
157  th = new StoichSubstanceSSTP;
158 #else
159  th = new StoichSubstance;
160 #endif
161  break;
162 
163  case cFixedChemPot:
164  th = new FixedChemPotSSTP;
165  break;
166 
167  case cMineralEQ3:
168  th = new MineralEQ3();
169  break;
170 
171  case cMetalSHEelectrons:
172  th = new MetalSHEelectrons();
173  break;
174 
175  case cLatticeSolid:
176  th = new LatticeSolidPhase;
177  break;
178 
179  case cLattice:
180  th = new LatticePhase;
181  break;
182 
183  case cPureFluid:
184  th = new PureFluidPhase;
185  break;
186 
187  case cRedlichKwongMFTP:
188  th = new RedlichKwongMFTP;
189  break;
190 
191  case cHMW:
192  th = new HMWSoln;
193  break;
194 
195  case cDebyeHuckel:
196  th = new DebyeHuckel;
197  break;
198 
199  case cIdealMolalSoln:
200  th = new IdealMolalSoln;
201  break;
202 
203  case cVPSS_IdealGas:
204  th = new IdealSolnGasVPSS;
205  break;
206 
207  default:
208  throw UnknownThermoPhaseModel("ThermoFactory::newThermoPhase",
209  model);
210  }
211  return th;
212 }
213 
214 // Translate the eosType id into a string
215 /*
216  * Returns a string representation of the eosType id for a phase.
217  * @param ieos eosType id of the phase. This is unique for the phase
218  * @param length maximum length of the return string. Defaults to 100
219  *
220  * @return returns a string representation.
221  */
222 std::string eosTypeString(int ieos, int length)
223 {
224  std::string ss = "UnknownPhaseType";
225  // bool found = false;
226  for (int n = 0; n < ntypes; n++) {
227  if (_itypes[n] == ieos) {
228  ss = _types[n];
229  //found = true;
230  }
231  }
232  return ss;
233 }
234 
235 
236 /*
237  * Create a new ThermoPhase object and initializes it according to
238  * the XML tree database. This routine first looks up the
239  * identity of the model for the solution thermodynamics in the
240  * model attribute of the thermo child of the xml phase
241  * node. Then, it does a string lookup on the model to figure out
242  * what ThermoPhase derived class is assigned. It creates a new
243  * instance of that class, and then calls importPhase() to
244  * populate that class with the correct parameters from the XML
245  * tree.
246  */
248 {
249  const XML_Node& th = xmlphase.child("thermo");
250  string model = th["model"];
251  ThermoPhase* t = newThermoPhase(model);
252  if (model == "singing cows") {
253  throw CanteraError(" newPhase", "Cows don't sing");
254  }
255  else if (model == "HMW") {
256  HMWSoln* p = dynamic_cast<HMWSoln*>(t);
257  p->constructPhaseXML(xmlphase,"");
258  }
259  else if (model == "IonsFromNeutralMolecule") {
260  IonsFromNeutralVPSSTP* p = dynamic_cast<IonsFromNeutralVPSSTP*>(t);
261  p->constructPhaseXML(xmlphase,"");
262  }
263  else {
264  importPhase(xmlphase, t);
265  }
266  return t;
267 }
268 
269 ThermoPhase* newPhase(std::string infile, std::string id)
270 {
271  XML_Node* root = get_XML_File(infile);
272  if (id == "-") {
273  id = "";
274  }
275  XML_Node* xphase = get_XML_NameID("phase", std::string("#")+id, root);
276  if (!xphase) {
277  throw CanteraError("newPhase",
278  "Couldn't find phase named \"" + id + "\" in file, " + infile);
279  }
280  if (xphase) {
281  return newPhase(*xphase);
282  } else {
283  return (ThermoPhase*) 0;
284  }
285 }
286 
287 //====================================================================================================================
288 //! Gather a vector of pointers to XML_Nodes for a phase
289 /*!
290  *
291  * @param spDataNodeList Output vector of pointer to XML_Nodes which contain the species XML_Nodes for the
292  * species in the current phase.
293  * @param spNamesList Output Vector of strings, which contain the names of the species in the phase
294  * @param spRuleList Output Vector of ints, which contain the value of sprule for each species in the phase
295  * @param spArray_names Vector of pointers to the XML_Nodes which contains the names of the
296  * species in the phase
297  *
298  * @param spArray_dbases Input vector of pointers to species data bases.
299  * We search each data base for the required species names
300  * @param sprule Input vector of sprule values
301  */
302 static void formSpeciesXMLNodeList(std::vector<XML_Node*> &spDataNodeList,
303  std::vector<std::string> &spNamesList,
304  std::vector<int> &spRuleList,
305  const std::vector<XML_Node*> spArray_names,
306  const std::vector<XML_Node*> spArray_dbases,
307  const vector_int sprule)
308 {
309 
310  // used to check that each species is declared only once
311  std::map<std::string, bool> declared;
312 
313  size_t nSpecies = 0;
314  bool skip;
315 
316  for (size_t jsp = 0; jsp < spArray_dbases.size(); jsp++) {
317  const XML_Node& speciesArray = *spArray_names[jsp];
318 
319  // Get the top XML for the database
320  const XML_Node* db = spArray_dbases[jsp];
321 
322  // Get the array of species name strings and then count them
323  std::vector<std::string> spnames;
324  getStringArray(speciesArray, spnames);
325  size_t nsp = spnames.size();
326 
327  // if 'all' is specified as the one and only species in the
328  // spArray_names field, then add all species
329  // defined in the corresponding database to the phase
330  if (nsp == 1 && spnames[0] == "all") {
331  std::vector<XML_Node*> allsp;
332  db->getChildren("species", allsp);
333  nsp = allsp.size();
334  spnames.resize(nsp);
335  for (size_t nn = 0; nn < nsp; nn++) {
336  string stemp = (*allsp[nn])["name"];
337  bool skip = false;
338  if (declared[stemp]) {
339  if (sprule[jsp] >= 10) {
340  skip = true;
341  } else {
342  throw CanteraError("ThermoFactory::formSpeciesXMLNodeList()",
343  "duplicate species: \"" + stemp + "\"");
344  }
345  }
346  if (!skip) {
347  declared[stemp] = true;
348  nSpecies++;
349  spNamesList.resize(nSpecies);
350  spDataNodeList.resize(nSpecies, 0);
351  spRuleList.resize(nSpecies, 0);
352  spNamesList[nSpecies-1] = stemp;
353  spDataNodeList[nSpecies-1] = allsp[nn];
354  spRuleList[nSpecies-1] = sprule[jsp];
355  }
356  }
357  } else if (nsp == 1 && spnames[0] == "unique") {
358  std::vector<XML_Node*> allsp;
359  db->getChildren("species", allsp);
360  nsp = allsp.size();
361  spnames.resize(nsp);
362  for (size_t nn = 0; nn < nsp; nn++) {
363  string stemp = (*allsp[nn])["name"];
364  bool skip = false;
365  if (declared[stemp]) {
366  skip = true;
367  }
368  if (!skip) {
369  declared[stemp] = true;
370  nSpecies++;
371  spNamesList.resize(nSpecies);
372  spDataNodeList.resize(nSpecies, 0);
373  spRuleList.resize(nSpecies, 0);
374  spNamesList[nSpecies-1] = stemp;
375  spDataNodeList[nSpecies-1] = allsp[nn];
376  spRuleList[nSpecies-1] = sprule[jsp];
377  }
378  }
379  } else {
380  for (size_t k = 0; k < nsp; k++) {
381  string stemp = spnames[k];
382  skip = false;
383  if (declared[stemp]) {
384  if (sprule[jsp] >= 10) {
385  skip = true;
386  } else {
387  throw CanteraError("ThermoFactory::formSpeciesXMLNodeList()",
388  "duplicate species: \"" + stemp + "\"");
389  }
390  }
391  if (!skip) {
392  declared[stemp] = true;
393  // Find the species in the database by name.
394  XML_Node* s = db->findByAttr("name", stemp);
395  if (!s) {
396  throw CanteraError("importPhase","no data for species, \""
397  + stemp + "\"");
398  }
399  nSpecies++;
400  spNamesList.resize(nSpecies);
401  spDataNodeList.resize(nSpecies, 0);
402  spRuleList.resize(nSpecies, 0);
403  spNamesList[nSpecies-1] = stemp;
404  spDataNodeList[nSpecies-1] = s;
405  spRuleList[nSpecies-1] = sprule[jsp];
406  }
407  }
408  }
409  }
410 }
411 //====================================================================================================================
412 /*
413  * Import a phase specification.
414  * Here we read an XML description of the phase.
415  * We import descriptions of the elements that make up the
416  * species in a phase.
417  * We import information about the species, including their
418  * reference state thermodynamic polynomials. We then freeze
419  * the state of the species, and finally call initThermoXML(phase, id)
420  * a member function of the ThermoPhase object to "finish"
421  * the description.
422  *
423  *
424  * @param phase This object must be the phase node of a
425  * complete XML tree
426  * description of the phase, including all of the
427  * species data. In other words while "phase" must
428  * point to an XML phase object, it must have
429  * sibling nodes "speciesData" that describe
430  * the species in the phase.
431  * @param th Pointer to the ThermoPhase object which will
432  * handle the thermodynamics for this phase.
433  * We initialize part of the Thermophase object
434  * here, especially for those objects which are
435  * part of the Cantera Kernel.
436  */
438  SpeciesThermoFactory* spfactory)
439 {
440 
441  // Check the the supplied XML node in fact represents a
442  // phase.
443  if (phase.name() != "phase") {
444  throw CanteraError("importPhase",
445  "Current const XML_Node named, " + phase.name() +
446  ", is not a phase element.");
447  }
448 
449  /*
450  * In this section of code, we get the reference to the
451  * phase xml tree within the ThermoPhase object. Then,
452  * we clear it and fill it with the current information that
453  * we are about to use to construct the object. We will then
454  * be able to resurrect the information later by calling xml().
455  */
456  XML_Node& phaseNode_XML = th->xml();
457  phaseNode_XML.clear();
458  phase.copy(&phaseNode_XML);
459 
460  // set the id attribute of the phase to the 'id' attribute
461  // in the XML tree.
462  th->setID(phase.id());
463  th->setName(phase.id());
464 
465  // Number of spatial dimensions. Defaults to 3 (bulk phase)
466  if (phase.hasAttrib("dim")) {
467  int idim = intValue(phase["dim"]);
468  if (idim < 1 || idim > 3)
469  throw CanteraError("importPhase",
470  "phase, " + th->id() +
471  ", has unphysical number of dimensions: " + phase["dim"]);
472  th->setNDim(idim);
473  } else {
474  th->setNDim(3); // default
475  }
476 
477  // Set equation of state parameters. The parameters are
478  // specific to each subclass of ThermoPhase, so this is done
479  // by method setParametersFromXML in each subclass.
480  if (phase.hasChild("thermo")) {
481  const XML_Node& eos = phase.child("thermo");
482  th->setParametersFromXML(eos);
483  } else {
484  throw CanteraError("importPhase",
485  " phase, " + th->id() +
486  ", XML_Node does not have a \"thermo\" XML_Node");
487  }
488 
489  VPStandardStateTP* vpss_ptr = 0;
490  int ssConvention = th->standardStateConvention();
491  if (ssConvention == cSS_CONVENTION_VPSS) {
492  vpss_ptr = dynamic_cast <VPStandardStateTP*>(th);
493  if (vpss_ptr == 0) {
494  throw CanteraError("importPhase",
495  "phase, " + th->id() + ", was VPSS, but dynamic cast failed");
496  }
497  }
498 
499  // if no species thermo factory was supplied,
500  // use the default one.
501  if (!spfactory) {
502  spfactory = SpeciesThermoFactory::factory();
503  }
504 
505  /***************************************************************
506  * Add the elements.
507  ***************************************************************/
508  if (ssConvention != cSS_CONVENTION_SLAVE) {
509  th->addElementsFromXML(phase);
510  }
511 
512  /***************************************************************
513  * Add the species.
514  *
515  * Species definitions may be imported from multiple
516  * sources. For each one, a speciesArray element must be
517  * present.
518  ***************************************************************/
519  XML_Node* db = 0;
520  vector<XML_Node*> sparrays;
521  phase.getChildren("speciesArray", sparrays);
522  int jsp, nspa = static_cast<int>(sparrays.size());
523  if (ssConvention != cSS_CONVENTION_SLAVE) {
524  if (nspa == 0) {
525  throw CanteraError("importPhase",
526  "phase, " + th->id() + ", has zero \"speciesArray\" XML nodes.\n"
527  + " There must be at least one speciesArray nodes "
528  "with one or more species");
529  }
530  }
531  vector<XML_Node*> dbases;
532  vector_int sprule(nspa,0);
533 
534  // loop over the speciesArray elements
535  for (jsp = 0; jsp < nspa; jsp++) {
536 
537  const XML_Node& speciesArray = *sparrays[jsp];
538 
539  // If the speciesArray element has a child element
540  //
541  // <skip element="undeclared">
542  //
543  // then set sprule[jsp] to 1, so
544  // that any species with an undeclared element will be
545  // quietly skipped when importing species.
546  // Additionally, if the skip node has the following attribute:
547  //
548  // <skip species="duplicate">
549  //
550  // then duplicate species names will not cause Cantera to
551  // throw an exception. Instead, the duplicate entry will
552  // be discarded.
553  if (speciesArray.hasChild("skip")) {
554  const XML_Node& sk = speciesArray.child("skip");
555  string eskip = sk["element"];
556  if (eskip == "undeclared") {
557  sprule[jsp] = 1;
558  }
559  string dskip = sk["species"];
560  if (dskip == "duplicate") {
561  sprule[jsp] += 10;
562  }
563  }
564 
565  string fname, idstr;
566 
567  // Get a pointer to the node containing the species
568  // definitions for the species declared in this
569  // speciesArray element. This may be in the local file
570  // containing the phase element, or may be in another
571  // file.
572  db = get_XML_Node(speciesArray["datasrc"], &phase.root());
573  if (db == 0) {
574  throw CanteraError("importPhase()",
575  " Can not find XML node for species database: "
576  + speciesArray["datasrc"]);
577  }
578 
579  // add this node to the list of species database nodes.
580  dbases.push_back(db);
581  }
582 
583  // Now, collect all the species names and all the XML_Node * pointers
584  // for those species in a single vector. This is where we decide what
585  // species are to be included in the phase.
586  // The logic is complicated enough that we put it in a separate routine.
587  std::vector<XML_Node*> spDataNodeList;
588  std::vector<std::string> spNamesList;
589  std::vector<int> spRuleList;
590  formSpeciesXMLNodeList(spDataNodeList, spNamesList, spRuleList,
591  sparrays, dbases, sprule);
592 
593  // If the phase has a species thermo manager already installed,
594  // delete it since we are adding new species.
595  //delete &th->speciesThermo();
596 
597  // Decide whether the the phase has a variable pressure ss or not
598  SpeciesThermo* spth = 0;
599  VPSSMgr* vp_spth = 0;
600  if (ssConvention == cSS_CONVENTION_TEMPERATURE) {
601  // Create a new species thermo manager. Function
602  // 'newSpeciesThermoMgr' looks at the species in the database
603  // to see what thermodynamic property parameterizations are
604  // used, and selects a class that can handle the
605  // parameterizations found.
606  spth = newSpeciesThermoMgr(spDataNodeList);
607 
608  // install it in the phase object
609  th->setSpeciesThermo(spth);
610  } else if (ssConvention == cSS_CONVENTION_SLAVE) {
611  /*
612  * No species thermo manager for this type
613  */
614  } else if (ssConvention == cSS_CONVENTION_VPSS) {
615  vp_spth = newVPSSMgr(vpss_ptr, &phase, spDataNodeList);
616  vpss_ptr->setVPSSMgr(vp_spth);
617  spth = vp_spth->SpeciesThermoMgr();
618  th->setSpeciesThermo(spth);
619  } else {
620  throw CanteraError("importPhase()", "unknown convention");
621  }
622 
623 
624  size_t k = 0;
625 
626  size_t nsp = spDataNodeList.size();
627  if (ssConvention == cSS_CONVENTION_SLAVE) {
628  if (nsp > 0) {
629  throw CanteraError("importPhase()", "For Slave standard states, number of species must be zero: "
630  + int2str(nsp));
631  }
632  }
633  for (size_t i = 0; i < nsp; i++) {
634  XML_Node* s = spDataNodeList[i];
635  AssertTrace(s != 0);
636  bool ok = installSpecies(k, *s, *th, spth, spRuleList[i],
637  &phase, vp_spth, spfactory);
638  if (ok) {
639  th->saveSpeciesData(k, s);
640  ++k;
641  }
642  }
643 
644  if (ssConvention == cSS_CONVENTION_SLAVE) {
645  th->installSlavePhases(&phase);
646  }
647 
648  // done adding species.
649  th->freezeSpecies();
650 
651  // Perform any required subclass-specific initialization.
652  th->initThermo();
653 
654  // Perform any required subclass-specific initialization
655  // that requires the XML phase object
656  std::string id = "";
657  th->initThermoXML(phase, id);
658 
659  return true;
660 }
661 
662 /*
663  * Install a species into a ThermoPhase object, which defines
664  * the phase thermodynamics and speciation.
665  *
666  * This routine first gathers the information from the Species XML
667  * tree and calls addUniqueSpecies() to add it to the
668  * ThermoPhase object, p.
669  * This information consists of:
670  * ecomp[] = element composition of species.
671  * chgr = electric charge of species
672  * name = string name of species
673  * sz = size of the species
674  * (option double used a lot in thermo)
675  *
676  * Then, the routine processes the "thermo" XML element and
677  * calls underlying utility routines to read the XML elements
678  * containing the thermodynamic information for the reference
679  * state of the species. Failures or lack of information trigger
680  * an "UnknownSpeciesThermoModel" exception being thrown.
681  * *
682  * @param k Species Index in the phase
683  * @param s XML_Node containing the species data for this species.
684  * @param p Reference to the ThermoPhase object.
685  * @param spthermo Reference to the SpeciesThermo object, where
686  * the standard state thermo properties for this
687  * species will be installed.
688  * @param rule Parameter that handles what to do with species
689  * who have elements that aren't declared.
690  * Check that all elements in the species
691  * exist in 'p'. If rule != 0, quietly skip
692  * this species if some elements are undeclared;
693  * otherwise, throw an exception
694  * @param phaseNode_ptr Pointer to the XML_Node for this phase
695  * (defaults to 0)
696  * @param factory Pointer to the SpeciesThermoFactory .
697  * (defaults to 0)
698  *
699  * @return
700  * Returns true if everything is ok, false otherwise.
701  */
702 bool installSpecies(size_t k, const XML_Node& s, thermo_t& th,
703  SpeciesThermo* spthermo_ptr, int rule,
704  XML_Node* phaseNode_ptr,
705  VPSSMgr* vpss_ptr,
706  SpeciesThermoFactory* factory)
707 {
708 
709  std::string xname = s.name();
710  if (xname != "species") {
711  throw CanteraError("installSpecies",
712  "Unexpected XML name of species XML_Node: " + xname);
713  }
714  // get the composition of the species
715  const XML_Node& a = s.child("atomArray");
716  map<string,string> comp;
717  getMap(a, comp);
718 
719  // check that all elements in the species
720  // exist in 'p'. If rule != 0, quietly skip
721  // this species if some elements are undeclared;
722  // otherwise, throw an exception
723  map<string,string>::const_iterator _b = comp.begin();
724  for (; _b != comp.end(); ++_b) {
725  if (th.elementIndex(_b->first) == npos) {
726  if (rule == 0) {
727  throw CanteraError("installSpecies",
728  "Species " + s["name"] +
729  " contains undeclared element " + _b->first);
730  } else {
731  return false;
732  }
733  }
734  }
735 
736  // construct a vector of atom numbers for each
737  // element in phase th. Elements not declared in the
738  // species (i.e., not in map comp) will have zero
739  // entries in the vector.
740  size_t nel = th.nElements();
741  vector_fp ecomp(nel, 0.0);
742  for (size_t m = 0; m < nel; m++) {
743  const char* es = comp[th.elementName(m)].c_str();
744  if (strlen(es) > 0) {
745  ecomp[m] = atofCheck(es);
746  }
747  }
748 
749 
750  // get the species charge, if any. Note that the charge need
751  // not be explicitly specified if special element 'E'
752  // (electron) is one of the elements.
753  doublereal chrg = 0.0;
754  if (s.hasChild("charge")) {
755  chrg = getFloat(s, "charge");
756  }
757 
758  // get the species size, if any. (This is used by surface
759  // phases to represent how many sites a species occupies.)
760  doublereal sz = 1.0;
761  if (s.hasChild("size")) {
762  sz = getFloat(s, "size");
763  }
764 
765  // add the species to phase th
766  th.addUniqueSpecies(s["name"], &ecomp[0], chrg, sz);
767 
768  if (vpss_ptr) {
769  VPStandardStateTP* vp_ptr = dynamic_cast<VPStandardStateTP*>(&th);
770  factory->installVPThermoForSpecies(k, s, vp_ptr, vpss_ptr, spthermo_ptr,
771  phaseNode_ptr);
772  } else {
773  // install the thermo parameterization for this species into
774  // the species thermo manager for phase th
775  factory->installThermoForSpecies(k, s, &th, *spthermo_ptr, phaseNode_ptr);
776  }
777 
778  return true;
779 }
780 
781 
782 // Search an XML tree for species data.
783 /*
784  * This utility routine will search the XML tree for the species
785  * named by the string, kname. It will return the XML_Node
786  * pointer to the species data for that species.
787  * Failures of any kind return the null pointer.
788  *
789  * @param kname String containing the name of the species.
790  * @param phaseSpeciesData Pointer to the XML speciesData element
791  * containing the species data for that phase.
792  *
793  */
794 const XML_Node* speciesXML_Node(std::string kname,
795  const XML_Node* phaseSpeciesData)
796 {
797  if (!phaseSpeciesData) {
798  return ((const XML_Node*) 0);
799  }
800  string jname = phaseSpeciesData->name();
801  if (jname != "speciesData") {
802  throw CanteraError("speciesXML_Node()",
803  "Unexpected phaseSpeciesData name: " + jname);
804  }
805  vector<XML_Node*> xspecies;
806  phaseSpeciesData->getChildren("species", xspecies);
807  for (size_t j = 0; j < xspecies.size(); j++) {
808  const XML_Node& sp = *xspecies[j];
809  jname = sp["name"];
810  if (jname == kname) {
811  return &sp;
812  }
813  }
814  return ((const XML_Node*) 0);
815 }
816 
817 }