407 lines
12 KiB
C++
407 lines
12 KiB
C++
/**
|
|
*
|
|
* @file ThermoPhase.cpp
|
|
*/
|
|
|
|
/*
|
|
* $Author$
|
|
* $Date$
|
|
* $Revision$
|
|
*
|
|
* Copyright 2002 California Institute of Technology
|
|
*
|
|
*/
|
|
|
|
// turn off warnings under Windows
|
|
#ifdef WIN32
|
|
#pragma warning(disable:4786)
|
|
#pragma warning(disable:4503)
|
|
#endif
|
|
|
|
#include "ThermoPhase.h"
|
|
|
|
|
|
namespace Cantera {
|
|
/**
|
|
* Copy Constructor for the ThermoPhase object.
|
|
*
|
|
* Currently, this is implemented, but not tested. If called it will
|
|
* throw an exception until fully tested.
|
|
*/
|
|
ThermoPhase::ThermoPhase(const ThermoPhase &right) :
|
|
Phase(),
|
|
m_spthermo(0),
|
|
m_speciesData(0),
|
|
m_index(-1),
|
|
m_phi(0.0),
|
|
m_hasElementPotentials(false)
|
|
{
|
|
/*
|
|
* Call the assignment operator
|
|
*/
|
|
*this = operator=(right);
|
|
}
|
|
|
|
/*
|
|
* operator=()
|
|
*
|
|
* Note this stuff will not work until the underlying phase
|
|
* has a working assignment operator
|
|
*/
|
|
ThermoPhase& ThermoPhase::
|
|
operator=(const ThermoPhase &right) {
|
|
/*
|
|
* Check for self assignment.
|
|
*/
|
|
if (this == &right) return *this;
|
|
|
|
(void)Phase::operator=(right);
|
|
|
|
/*
|
|
* Pointer to the species thermodynamic property manager
|
|
* We own this, so we need to do a deep copy
|
|
*/
|
|
if (m_spthermo) {
|
|
delete m_spthermo;
|
|
}
|
|
//m_spthermo = (right.m_spthermo)->duplMyselfAsSpeciesThermo();
|
|
throw CanteraError("ThermoPhase assignment", "not implemented");
|
|
|
|
/// Pointer to the XML tree containing the species
|
|
/// data for this phase. This is used to access data needed to
|
|
/// construct the transport manager and other properties
|
|
/// later in the initialization process.
|
|
m_speciesData = right.m_speciesData;
|
|
|
|
|
|
m_index = right.m_index;
|
|
m_phi = right.m_phi;
|
|
m_lambda = right.m_lambda;
|
|
m_hasElementPotentials = right.m_hasElementPotentials;
|
|
|
|
return *this;
|
|
}
|
|
|
|
/*
|
|
* Duplication routine for objects which inherit from
|
|
* ThermoPhase.
|
|
*
|
|
* This virtual routine can be used to duplicate thermophase objects
|
|
* inherited from ThermoPhase even if the application only has
|
|
* a pointer to ThermoPhase to work with.
|
|
*
|
|
* Currently, this is not fully implemented. If called, an
|
|
* exception will be called by the ThermoPhase copy constructor.
|
|
*/
|
|
ThermoPhase *ThermoPhase::duplMyselfAsThermoPhase() {
|
|
ThermoPhase* tp = new ThermoPhase(*this);
|
|
return tp;
|
|
}
|
|
|
|
int ThermoPhase::activityConvention() const {
|
|
return cAC_CONVENTION_MOLAR;
|
|
}
|
|
|
|
void ThermoPhase::getActivities(doublereal* a) {
|
|
getActivityConcentrations(a);
|
|
int nsp = nSpecies();
|
|
int k;
|
|
for (k = 0; k < nsp; k++) a[k] /= standardConcentration(k);
|
|
}
|
|
|
|
void ThermoPhase::setState_TPX(doublereal t, doublereal p,
|
|
const doublereal* x) {
|
|
setMoleFractions(x); setTemperature(t); setPressure(p);
|
|
}
|
|
|
|
void ThermoPhase::setState_TPX(doublereal t, doublereal p,
|
|
compositionMap& x) {
|
|
setMoleFractionsByName(x); setTemperature(t); setPressure(p);
|
|
}
|
|
|
|
void ThermoPhase::setState_TPX(doublereal t, doublereal p,
|
|
const string& x) {
|
|
compositionMap xx;
|
|
int kk = nSpecies();
|
|
for (int k = 0; k < kk; k++) xx[speciesName(k)] = -1.0;
|
|
try {
|
|
parseCompString(x, xx);
|
|
}
|
|
catch (CanteraError) {
|
|
throw CanteraError("setState_TPX",
|
|
"Unknown species in composition map: "+ x);
|
|
}
|
|
setMoleFractionsByName(xx); setTemperature(t); setPressure(p);
|
|
}
|
|
|
|
void ThermoPhase::setState_TPY(doublereal t, doublereal p,
|
|
const doublereal* y) {
|
|
setMassFractions(y); setTemperature(t); setPressure(p);
|
|
}
|
|
|
|
void ThermoPhase::setState_TPY(doublereal t, doublereal p,
|
|
compositionMap& y) {
|
|
setMassFractionsByName(y); setTemperature(t); setPressure(p);
|
|
}
|
|
|
|
void ThermoPhase::setState_TPY(doublereal t, doublereal p,
|
|
const string& y) {
|
|
compositionMap yy;
|
|
int kk = nSpecies();
|
|
for (int k = 0; k < kk; k++) yy[speciesName(k)] = -1.0;
|
|
try {
|
|
parseCompString(y, yy);
|
|
}
|
|
catch (CanteraError) {
|
|
throw CanteraError("setState_TPY",
|
|
"Unknown species in composition map: "+ y);
|
|
}
|
|
setMassFractionsByName(yy); setTemperature(t); setPressure(p);
|
|
}
|
|
|
|
void ThermoPhase::setState_TP(doublereal t, doublereal p) {
|
|
setTemperature(t); setPressure(p);
|
|
}
|
|
|
|
void ThermoPhase::setState_PX(doublereal p, doublereal* x) {
|
|
setMoleFractions(x); setPressure(p);
|
|
}
|
|
|
|
void ThermoPhase::setState_PY(doublereal p, doublereal* y) {
|
|
setMassFractions(y); setPressure(p);
|
|
}
|
|
|
|
void ThermoPhase::setState_HP(doublereal h, doublereal p,
|
|
doublereal tol) {
|
|
doublereal dt;
|
|
setPressure(p);
|
|
|
|
// Newton iteration
|
|
for (int n = 0; n < 50; n++) {
|
|
dt = (h - enthalpy_mass())/cp_mass();
|
|
// limit step size to 100 K
|
|
if (dt > 100.0) dt = 100.0;
|
|
else if (dt < -100.0) dt = -100.0;
|
|
setState_TP(temperature() + dt, p);
|
|
if (fabs(dt) < tol) {
|
|
return;
|
|
}
|
|
}
|
|
throw CanteraError("setState_HP","No convergence. dt = " + fp2str(dt));
|
|
}
|
|
|
|
void ThermoPhase::setState_UV(doublereal u, doublereal v,
|
|
doublereal tol) {
|
|
doublereal dt;
|
|
setDensity(1.0/v);
|
|
for (int n = 0; n < 50; n++) {
|
|
dt = (u - intEnergy_mass())/cv_mass();
|
|
if (dt > 100.0) dt = 100.0;
|
|
else if (dt < -100.0) dt = -100.0;
|
|
if (fabs(dt) < tol) {
|
|
setTemperature(temperature() + dt);
|
|
return;
|
|
}
|
|
setTemperature(temperature() + 0.5*dt);
|
|
}
|
|
throw CanteraError("setState_UV",
|
|
"no convergence. dt = " + fp2str(dt)+"\n"
|
|
+"tol = "+fp2str(tol)+"\n"
|
|
+"u = "+fp2str(u)+" v = "+fp2str(v)+"\n");
|
|
}
|
|
|
|
void ThermoPhase::setState_SP(doublereal s, doublereal p,
|
|
doublereal tol) {
|
|
doublereal dt;
|
|
setPressure(p);
|
|
for (int n = 0; n < 50; n++) {
|
|
dt = (s - entropy_mass())*temperature()/cp_mass();
|
|
if (dt > 100.0) dt = 100.0;
|
|
else if (dt < -100.0) dt = -100.0;
|
|
if (fabs(dt) < tol) {
|
|
setState_TP(temperature() + dt, p);
|
|
return;
|
|
}
|
|
setState_TP(temperature() + 0.5*dt, p);
|
|
}
|
|
throw CanteraError("setState_SP","no convergence. dt = " + fp2str(dt));
|
|
}
|
|
|
|
void ThermoPhase::setState_SV(doublereal s, doublereal v,
|
|
doublereal tol) {
|
|
doublereal dt;
|
|
setDensity(1.0/v);
|
|
for (int n = 0; n < 50; n++) {
|
|
dt = (s - entropy_mass())*temperature()/cv_mass();
|
|
if (dt > 100.0) dt = 100.0;
|
|
else if (dt < -100.0) dt = -100.0;
|
|
if (fabs(dt) < tol) {
|
|
setTemperature(temperature() + dt);
|
|
return;
|
|
}
|
|
setTemperature(temperature() + 0.5*dt);
|
|
}
|
|
throw CanteraError("setState_SV","no convergence. dt = " + fp2str(dt));
|
|
}
|
|
|
|
doublereal ThermoPhase::err(string msg) const {
|
|
throw CanteraError("ThermoPhase","Base class method "
|
|
+msg+" called. Equation of state type: "+int2str(eosType()));
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the units of the standard and general concentrations
|
|
* Note they have the same units, as their divisor is
|
|
* defined to be equal to the activity of the kth species
|
|
* in the solution, which is unitless.
|
|
*
|
|
* This routine is used in print out applications where the
|
|
* units are needed. Usually, MKS units are assumed throughout
|
|
* the program and in the XML input files.
|
|
*
|
|
* On return uA contains the powers of the units (MKS assumed)
|
|
* of the standard concentrations and generalized concentrations
|
|
* for the kth species.
|
|
*
|
|
* uA[0] = kmol units - default = 1
|
|
* uA[1] = m units - default = -nDim(), the number of spatial
|
|
* dimensions in the Phase class.
|
|
* uA[2] = kg units - default = 0;
|
|
* uA[3] = Pa(pressure) units - default = 0;
|
|
* uA[4] = Temperature units - default = 0;
|
|
* uA[5] = time units - default = 0
|
|
*/
|
|
void ThermoPhase::getUnitsStandardConc(double *uA, int k, int sizeUA) {
|
|
for (int i = 0; i < sizeUA; i++) {
|
|
if (i == 0) uA[0] = 1.0;
|
|
if (i == 1) uA[1] = -nDim();
|
|
if (i == 2) uA[2] = 0.0;
|
|
if (i == 3) uA[3] = 0.0;
|
|
if (i == 4) uA[4] = 0.0;
|
|
if (i == 5) uA[5] = 0.0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* initThermoFile():
|
|
*
|
|
* Initialization of a Debye-Huckel phase using an
|
|
* xml file.
|
|
*
|
|
* This routine is a precursor to initThermoXML(XML_Node*)
|
|
* routine, which does most of the work.
|
|
*
|
|
* @param infile XML file containing the description of the
|
|
* phase
|
|
*
|
|
* @param id Optional parameter identifying the name of the
|
|
* phase. If none is given, the first XML
|
|
* phase element will be used.
|
|
*/
|
|
void ThermoPhase::initThermoFile(string inputFile, string id) {
|
|
|
|
if (inputFile.size() == 0) {
|
|
throw CanteraError("ThermoPhase::initThermoFile",
|
|
"input file is null");
|
|
}
|
|
string path = findInputFile(inputFile);
|
|
ifstream fin(path.c_str());
|
|
if (!fin) {
|
|
throw CanteraError("initThermoFile","could not open "
|
|
+path+" for reading.");
|
|
}
|
|
/*
|
|
* The phase object automatically constructs an XML object.
|
|
* Use this object to store information.
|
|
*/
|
|
XML_Node &phaseNode_XML = xml();
|
|
XML_Node *fxml = new XML_Node();
|
|
fxml->build(fin);
|
|
XML_Node *fxml_phase = findXMLPhase(fxml, id);
|
|
if (!fxml_phase) {
|
|
throw CanteraError("ThermoPhase::initThermo",
|
|
"ERROR: Can not find phase named " +
|
|
id + " in file named " + inputFile);
|
|
}
|
|
fxml_phase->copy(&phaseNode_XML);
|
|
initThermoXML(*fxml_phase, id);
|
|
delete fxml;
|
|
}
|
|
|
|
/*
|
|
* Import and initialize a ThermoPhase
|
|
* object
|
|
*
|
|
* @param phaseNode 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 id ID of the phase. If nonnull, a check is done
|
|
* to see if phaseNode is pointing to the phase
|
|
* with the correct id.
|
|
*/
|
|
void ThermoPhase::initThermoXML(XML_Node& phaseNode, string id) {
|
|
/*
|
|
* The default implementation just calls initThermo();
|
|
*/
|
|
initThermo();
|
|
/*
|
|
* and sets the state
|
|
*/
|
|
if (phaseNode.hasChild("state")) {
|
|
XML_Node& stateNode = phaseNode.child("state");
|
|
setStateFromXML(stateNode);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize.
|
|
*
|
|
* This method is provided to allow
|
|
* subclasses to perform any initialization required after all
|
|
* species have been added. For example, it might be used to
|
|
* resize internal work arrays that must have an entry for
|
|
* each species. The base class implementation does nothing,
|
|
* and subclasses that do not require initialization do not
|
|
* need to overload this method. When importing a CTML phase
|
|
* description, this method is called just prior to returning
|
|
* from function importPhase.
|
|
*
|
|
* @see importCTML.cpp
|
|
*/
|
|
void ThermoPhase::initThermo() {
|
|
|
|
}
|
|
|
|
/**
|
|
* Set the thermodynamic state.
|
|
*/
|
|
void ThermoPhase::setStateFromXML(const XML_Node& state) {
|
|
|
|
string comp = getString(state,"moleFractions");
|
|
if (comp != "")
|
|
setMoleFractionsByName(comp);
|
|
else {
|
|
comp = getString(state,"massFractions");
|
|
if (comp != "")
|
|
setMassFractionsByName(comp);
|
|
}
|
|
if (state.hasChild("temperature")) {
|
|
double t = getFloat(state, "temperature", "temperature");
|
|
setTemperature(t);
|
|
}
|
|
if (state.hasChild("pressure")) {
|
|
double p = getFloat(state, "pressure", "pressure");
|
|
setPressure(p);
|
|
}
|
|
if (state.hasChild("density")) {
|
|
double rho = getFloat(state, "density", "density");
|
|
setDensity(rho);
|
|
}
|
|
}
|
|
|
|
}
|