cantera/Cantera/src/thermo/ThermoFactory.cpp
2007-12-19 17:03:17 +00:00

630 lines
18 KiB
C++

/**
* @file ThermoFactory.cpp
* Definitions for the factory class that can create known %ThermoPhase objects
* (see \ref thermoprops and class \link Cantera::ThermoFactory ThermoFactory\endlink).
*
*/
/*
* $Author$
* $Revision$
* $Date$
*/
// Copyright 2001 California Institute of Technology
#ifdef WIN32
#pragma warning(disable:4786)
#endif
#include "ThermoFactory.h"
#include "speciesThermoTypes.h"
#include "SpeciesThermoFactory.h"
#include "IdealGasPhase.h"
#include "IdealSolidSolnPhase.h"
#ifdef WITH_PURE_FLUIDS
#include "PureFluidPhase.h"
#endif
#include "ConstDensityThermo.h"
#include "SurfPhase.h"
#include "EdgePhase.h"
#ifdef WITH_METAL
#include "MetalPhase.h"
#endif
#ifdef WITH_SEMICONDUCTOR
#include "SemiconductorPhase.h"
#endif
#undef USE_SSTP
#ifdef WITH_STOICH_SUBSTANCE
#ifdef USE_SSTP
#include "StoichSubstanceSSTP.h"
#else
#include "StoichSubstance.h"
#endif
#endif
//#include "importCTML.h"
#ifdef WITH_LATTICE_SOLID
#include "LatticeSolidPhase.h"
#include "LatticePhase.h"
#endif
#ifdef WITH_ELECTROLYTES
#include "HMWSoln.h"
#include "DebyeHuckel.h"
#include "IdealMolalSoln.h"
#endif
using namespace std;
namespace Cantera {
ThermoFactory* ThermoFactory::s_factory = 0;
#if defined(THREAD_SAFE_CANTERA)
boost::mutex ThermoFactory::thermo_mutex;
#endif
static int ntypes = 13;
static string _types[] = {"IdealGas", "Incompressible",
"Surface", "Edge", "Metal", "StoichSubstance",
"PureFluid", "LatticeSolid", "Lattice",
"HMW", "IdealSolidSolution", "DebyeHuckel",
"IdealMolalSolution"
};
static int _itypes[] = {cIdealGas, cIncompressible,
cSurf, cEdge, cMetal, cStoichSubstance,
cPureFluid, cLatticeSolid, cLattice,
cHMW, cIdealSolidSolnPhase, cDebyeHuckel,
cIdealMolalSoln
};
/*
* This method returns a new instance of a subclass of ThermoPhase
*/
ThermoPhase* ThermoFactory::newThermoPhase(std::string model) {
int ieos=-1;
for (int n = 0; n < ntypes; n++) {
if (model == _types[n]) ieos = _itypes[n];
}
ThermoPhase* th=0;
switch (ieos) {
case cIdealGas:
th = new IdealGasPhase;
break;
case cIncompressible:
th = new ConstDensityThermo;
break;
case cSurf:
th = new SurfPhase;
break;
case cEdge:
th = new EdgePhase;
break;
case cIdealSolidSolnPhase:
th = new IdealSolidSolnPhase();
break;
#ifdef WITH_METAL
case cMetal:
th = new MetalPhase;
break;
#endif
#ifdef WITH_STOICH_SUBSTANCE
case cStoichSubstance:
#ifdef USE_SSTP
th = new StoichSubstanceSSTP;
#else
th = new StoichSubstance;
#endif
break;
#endif
#ifdef WITH_LATTICE_SOLID
case cLatticeSolid:
th = new LatticeSolidPhase;
break;
case cLattice:
th = new LatticePhase;
break;
#endif
#ifdef WITH_PURE_FLUIDS
case cPureFluid:
th = new PureFluidPhase;
break;
#endif
#ifdef WITH_ELECTROLYTES
case cHMW:
th = new HMWSoln;
break;
case cDebyeHuckel:
th = new DebyeHuckel;
break;
case cIdealMolalSoln:
th = new IdealMolalSoln;
break;
#endif
default:
throw UnknownThermoPhaseModel("ThermoFactory::newThermoPhase",
model);
}
return th;
}
/*
* Create a new ThermoPhase object and initializes it according to
* the XML tree database. This routine first looks up the
* identity of the model for the solution thermodynamics in the
* model attribute of the thermo child of the xml phase
* node. Then, it does a string lookup on the model to figure out
* what ThermoPhase derived class is assigned. It creates a new
* instance of that class, and then calls importPhase() to
* populate that class with the correct parameters from the XML
* tree.
*/
ThermoPhase* newPhase(XML_Node& xmlphase) {
const XML_Node& th = xmlphase.child("thermo");
string model = th["model"];
ThermoPhase* t = newThermoPhase(model);
#ifdef WITH_ELECTROLYTES
if (model == "HMW") {
HMWSoln* p = (HMWSoln*)t;
p->constructPhaseXML(xmlphase,"");
}
else
#endif
importPhase(xmlphase, t);
return t;
}
ThermoPhase* newPhase(std::string infile, std::string id) {
XML_Node* root = get_XML_File(infile);
if (id == "-") id = "";
XML_Node* x = get_XML_Node(string("#")+id, root);
if (x)
return newPhase(*x);
else
return 0;
}
/*
* Import a phase specification.
* Here we read an XML description of the phase.
* We import descriptions of the elements that make up the
* species in a phase.
* We import information about the species, including their
* reference state thermodynamic polynomials. We then freeze
* the state of the species, and finally call initThermoXML(phase, id)
* a member function of the ThermoPhase object to "finish"
* the description.
*
*
* @param phase This object must be the phase node of a
* complete XML tree
* description of the phase, including all of the
* species data. In other words while "phase" must
* point to an XML phase object, it must have
* sibling nodes "speciesData" that describe
* the species in the phase.
* @param th Pointer to the ThermoPhase object which will
* handle the thermodynamics for this phase.
* We initialize part of the Thermophase object
* here, especially for those objects which are
* part of the Cantera Kernel.
*/
bool importPhase(XML_Node& phase, ThermoPhase* th,
SpeciesThermoFactory* spfactory) {
// Check the the supplied XML node in fact represents a
// phase.
if (phase.name() != "phase")
throw CanteraError("importPhase",
"Current const XML_Node is not a phase element.");
// if no species thermo factory was supplied,
// use the default one.
if (!spfactory)
spfactory = SpeciesThermoFactory::factory();
// set the id attribute of the phase to the 'id' attribute
// in the XML tree.
th->setID(phase.id());
th->setName(phase.id());
// Number of spatial dimensions. Defaults to 3 (bulk phase)
if (phase.hasAttrib("dim")) {
int idim = intValue(phase["dim"]);
if (idim < 1 || idim > 3)
throw CanteraError("importPhase",
"unphysical number of dimensions: "+phase["dim"]);
th->setNDim(idim);
}
else
th->setNDim(3); // default
// Set equation of state parameters. The parameters are
// specific to each subclass of ThermoPhase, so this is done
// by method setParametersFromXML in each subclass.
if (phase.hasChild("thermo")) {
const XML_Node& eos = phase.child("thermo");
th->setParametersFromXML(eos);
}
/***************************************************************
* Add the elements.
***************************************************************/
th->addElementsFromXML(phase);
/***************************************************************
* Add the species.
*
* Species definitions may be imported from multiple
* sources. For each one, a speciesArray element must be
* present.
***************************************************************/
XML_Node* db = 0;
vector<XML_Node*> sparrays;
phase.getChildren("speciesArray", sparrays);
int jsp, nspa = static_cast<int>(sparrays.size());
vector<XML_Node*> dbases;
vector_int sprule(nspa,0);
// loop over the speciesArray elements
for (jsp = 0; jsp < nspa; jsp++) {
const XML_Node& species = *sparrays[jsp];
// If the speciesArray element has a child element
// <skip element="undeclared">
// then set sprule[jsp] to 1, so
// that any species with an undeclared element will be
// quietly skipped when importing species.
if (species.hasChild("skip")) {
const XML_Node& sk = species.child("skip");
string eskip = sk["element"];
if (eskip == "undeclared") {
sprule[jsp] = 1;
}
string dskip = sk["species"];
if (dskip == "duplicate") {
sprule[jsp] += 10;
}
}
string fname, idstr;
// get a pointer to the node containing the species
// definitions for the species declared in this
// speciesArray element. This may be in the local file
// containing the phase element, or may be in another
// file.
db = get_XML_Node(species["datasrc"], &phase.root());
// add this node to the list of species database nodes.
dbases.push_back(db);
}
// if the phase has a species thermo manager already installed,
// delete it since we are adding new species.
delete &th->speciesThermo();
// create a new species thermo manager. Function
// 'newSpeciesThermoMgr' looks at the species in the database
// to see what thermodynamic property parameterizations are
// used, and selects a class that can handle the
// parameterizations found.
SpeciesThermo* spth = newSpeciesThermoMgr(dbases);
// install it in the phase object
th->setSpeciesThermo(spth);
SpeciesThermo& spthermo = th->speciesThermo();
// used to check that each species is declared only once
map<string,bool> declared;
int i, k = 0;
// loop over the species arrays
for (jsp = 0; jsp < nspa; jsp++) {
const XML_Node& species = *sparrays[jsp];
db = dbases[jsp];
// Get the array of species name strings.
vector<string> spnames;
getStringArray(species, spnames);
int nsp = static_cast<int>(spnames.size());
// if 'all' is specified, then add all species
// defined in this database to the phase
if (nsp == 1 && spnames[0] == "all") {
vector<XML_Node*> allsp;
db->getChildren("species",allsp);
nsp = static_cast<int>(allsp.size());
spnames.resize(nsp);
for (int nn = 0; nn < nsp; nn++) {
spnames[nn] = (*allsp[nn])["name"];
}
}
else if (nsp == 1 && spnames[0] == "unique") {
vector<XML_Node*> uniquesp;
db->getChildren("species",uniquesp);
nsp = static_cast<int>(uniquesp.size());
spnames.clear();
spnames.resize(nsp);
string spnm;
for (int nn = 0; nn < nsp; nn++) {
spnm = (*uniquesp[nn])["name"];
if (!declared[spnm]) spnames[nn] = spnm;
}
}
string name;
bool skip;
for (i = 0; i < nsp; i++) {
name = spnames[i];
skip = false;
if (name == "") skip = true;
// Check that every species is only declared once
if (declared[name]) {
if (sprule[jsp] >= 10)
skip = true;
else
throw CanteraError("importPhase",
"duplicate species: "+name);
}
if (!skip) {
declared[name] = true;
// Find the species in the database by name.
XML_Node* s = db->findByAttr("name",spnames[i]);
if (s) {
if (installSpecies(k, *s, *th, spthermo, sprule[jsp],
spfactory))
++k;
}
else {
throw CanteraError("importPhase","no data for species "
+name);
}
}
}
}
// done adding species.
th->freezeSpecies();
th->saveSpeciesData(db);
// Perform any required subclass-specific initialization.
string id = "";
th->initThermoXML(phase, id);
return true;
}
// void setEOSParameters(const XML_Node& xmlphase, ThermoPhase* th) {
// // if no thermo model is specified for the phase, simply
// // return
// if (!phase.hasChild("thermo")) return;
// const XML_Node& eos = phase.child("thermo");
// // set the parameters for the particular equation of state type,
// // and
// if (eos["model"] == "Incompressible") {
// if (th->eosType() == cIncompressible) {
// doublereal rho = getFloat(eos, "density", "-");
// th->setParameters(1, &rho);
// }
// else {
// eoserror = true;
// }
// }
// else if (eos["model"] == "StoichSubstance") {
// if (th->eosType() == cStoichSubstance) {
// doublereal rho = getFloat(eos, "density", "-");
// th->setDensity(rho);
// }
// else {
// eoserror = true;
// }
// }
// else if (eos["model"] == "Surface") {
// if (th->eosType() == cSurf) {
// doublereal n = getFloat(eos, "site_density", "-");
// if (n <= 0.0)
// throw CanteraError("importCTML",
// "missing or negative site density");
// th->setParameters(1, &n);
// }
// else {
// eoserror = true;
// }
// }
// else if (eos["model"] == "Edge") {
// if (th->eosType() == cEdge) {
// doublereal n = getFloat(eos, "site_density", "-");
// if (n <= 0.0)
// throw CanteraError("importCTML",
// "missing or negative site density");
// th->setParameters(1, &n);
// }
// else {
// eoserror = true;
// }
// }
// #ifdef INCL_PURE_FLUIDS
// else if (eos["model"] == "PureFluid") {
// if (th->eosType() == cPureFluid) {
// subflag = atoi(eos["fluid_type"].c_str());
// if (subflag < 0)
// throw CanteraError("importCTML",
// "missing fluid type flag");
// }
// else {
// eoserror = true;
// }
// }
// #endif
// if (eoserror) {
// string msg = "Wrong equation of state type for phase "+phase["id"]+"\n";
// msg += eos["model"]+" is not consistent with eos type "+int2str(th->eosType());
// throw CanteraError("importCTML",msg);
// }
/*
* Install a species into a ThermoPhase object, which defines
* the phase thermodynamics and speciation.
*
* This routine first gathers the information from the Species XML
* tree and calls addUniqueSpecies() to add it to the
* ThermoPhase object, p.
* This information consists of:
* ecomp[] = element composition of species.
* chgr = electric charge of species
* name = string name of species
* sz = size of the species
* (option double used a lot in thermo)
*
* Then, the routine processes the "thermo" XML element and
* calls underlying utility routines to read the XML elements
* containing the thermodynamic information for the reference
* state of the species. Failures or lack of information trigger
* an "UnknownSpeciesThermoModel" exception being thrown.
*/
bool installSpecies(int k, const XML_Node& s, thermo_t& p,
SpeciesThermo& spthermo, int rule,
SpeciesThermoFactory* factory) {
std::string xname = s.name();
if (xname != "species") {
throw CanteraError("installSpecies",
"Unexpected XML name of species XML_Node: " + xname);
}
// get the composition of the species
const XML_Node& a = s.child("atomArray");
map<string,string> comp;
getMap(a, comp);
// check that all elements in the species
// exist in 'p'. If rule != 0, quietly skip
// this species if some elements are undeclared;
// otherwise, throw an exception
map<string,string>::const_iterator _b = comp.begin();
for (; _b != comp.end(); ++_b) {
if (p.elementIndex(_b->first) < 0) {
if (rule == 0) {
throw CanteraError("installSpecies",
"Species " + s["name"] +
" contains undeclared element " + _b->first);
}
else
return false;
}
}
// construct a vector of atom numbers for each
// element in phase p. Elements not declared in the
// species (i.e., not in map comp) will have zero
// entries in the vector.
int m, nel = p.nElements();
vector_fp ecomp(nel, 0.0);
for (m = 0; m < nel; m++) {
ecomp[m] = atoi(comp[p.elementName(m)].c_str());
}
// get the species charge, if any. Note that the charge need
// not be explicitly specified if special element 'E'
// (electron) is one of the elements.
doublereal chrg = 0.0;
if (s.hasChild("charge")) chrg = getFloat(s, "charge");
// get the species size, if any. (This is used by surface
// phases to represent how many sites a species occupies.)
doublereal sz = 1.0;
if (s.hasChild("size")) sz = getFloat(s, "size");
// add the species to phase p.
p.addUniqueSpecies(s["name"], &ecomp[0], chrg, sz);
// install the thermo parameterization for this species into
// the species thermo manager for phase p.
factory->installThermoForSpecies(k, s, spthermo);
return true;
}
// Search an XML tree for species data.
/*
* This utility routine will search the XML tree for the species
* named by the string, kname. It will return the XML_Node
* pointer to the species data for that species.
* Failures of any kind return the null pointer.
*
* @param kname String containing the name of the species.
* @param phaseSpeciesData Pointer to the XML speciesData element
* containing the species data for that phase.
*
*/
const XML_Node *speciesXML_Node(std::string kname,
const XML_Node *phaseSpeciesData) {
if (!phaseSpeciesData) return ((const XML_Node *) 0);
string jname = phaseSpeciesData->name();
if (jname != "speciesData") {
throw CanteraError("speciesXML_Node()",
"Unexpected phaseSpeciesData name: " + jname);
}
vector<XML_Node*> xspecies;
phaseSpeciesData->getChildren("species", xspecies);
int jj = xspecies.size();
for (int j = 0; j < jj; j++) {
const XML_Node& sp = *xspecies[j];
jname = sp["name"];
if (jname == kname) {
return &sp;
}
}
return ((const XML_Node *) 0);
}
}