diff --git a/Cantera/src/thermo/ConstCpPoly.cpp b/Cantera/src/thermo/ConstCpPoly.cpp new file mode 100644 index 000000000..e72f56681 --- /dev/null +++ b/Cantera/src/thermo/ConstCpPoly.cpp @@ -0,0 +1,143 @@ +/** + * @file ConstCpPoly.cpp + * Declarations for the \link Cantera::SpeciesThermoInterpType SpeciesThermoInterpType \endlink object that + * employs a constant heat capacity assumption (see \ref spthermo and + * \link Cantera::ConstCpPoly ConstCpPoly \endlink). + */ +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + + +#include "ConstCpPoly.h" +#include + +namespace Cantera { + + ConstCpPoly::ConstCpPoly() + : m_t0(0.0), + m_cp0_R(0.0), + m_h0_R(0.0), + m_s0_R(0.0), + m_logt0(0.0), + m_lowT(0.0), + m_highT(0.0), + m_Pref(0.0), + m_index(0) { + } + + ConstCpPoly::ConstCpPoly(int n, doublereal tlow, doublereal thigh, + doublereal pref, + const doublereal* coeffs) : + m_lowT (tlow), + m_highT (thigh), + m_Pref (pref), + m_index (n) { + m_t0 = coeffs[0]; + m_h0_R = coeffs[1] / GasConstant; + m_s0_R = coeffs[2] / GasConstant; + m_cp0_R = coeffs[3] / GasConstant; + m_logt0 = log(m_t0); + } + + ConstCpPoly::ConstCpPoly(const ConstCpPoly& b) : + m_t0 (b.m_t0), + m_cp0_R (b.m_cp0_R), + m_h0_R (b.m_h0_R), + m_s0_R (b.m_s0_R), + m_logt0 (b.m_logt0), + m_lowT (b.m_lowT), + m_highT (b.m_highT), + m_Pref (b.m_Pref), + m_index (b.m_index) + { + } + + ConstCpPoly& ConstCpPoly::operator=(const ConstCpPoly& b) { + if (&b != this) { + m_t0 = b.m_t0; + m_cp0_R = b.m_cp0_R; + m_h0_R = b.m_h0_R; + m_s0_R = b.m_s0_R; + m_logt0 = b.m_logt0; + m_lowT = b.m_lowT; + m_highT = b.m_highT; + m_Pref = b.m_Pref; + m_index = b.m_index; + } + return *this; + } + + ConstCpPoly::~ConstCpPoly(){} + + SpeciesThermoInterpType * + ConstCpPoly::duplMyselfAsSpeciesThermoInterpType() const { + ConstCpPoly* newCCP = new ConstCpPoly(*this); + return (SpeciesThermoInterpType*) newCCP; + } + + doublereal ConstCpPoly::minTemp() const { + return m_lowT; + } + doublereal ConstCpPoly::maxTemp() const { + return m_highT; + } + doublereal ConstCpPoly::refPressure() const { + return m_Pref; + } + + void ConstCpPoly::updateProperties(const doublereal* tt, + doublereal* cp_R, + doublereal* h_RT, + doublereal* s_R) const { + double t = *tt; + doublereal logt = log(t); + doublereal rt = 1.0/t; + cp_R[m_index] = m_cp0_R; + h_RT[m_index] = rt*(m_h0_R + (t - m_t0) * m_cp0_R); + s_R[m_index] = m_s0_R + m_cp0_R * (logt - m_logt0); + } + + void ConstCpPoly::updatePropertiesTemp(const doublereal temp, + doublereal* cp_R, + doublereal* h_RT, + doublereal* s_R) const { + doublereal logt = log(temp); + doublereal rt = 1.0/temp; + cp_R[m_index] = m_cp0_R; + h_RT[m_index] = rt*(m_h0_R + (temp - m_t0) * m_cp0_R); + s_R[m_index] = m_s0_R + m_cp0_R * (logt - m_logt0); + } + + void ConstCpPoly::reportParameters(int &n, int &type, + doublereal &tlow, doublereal &thigh, + doublereal &pref, + doublereal* const coeffs) const { + n = m_index; + type = CONSTANT_CP; + tlow = m_lowT; + thigh = m_highT; + pref = m_Pref; + coeffs[0] = m_t0; + coeffs[1] = m_h0_R * GasConstant; + coeffs[2] = m_s0_R * GasConstant; + coeffs[3] = m_cp0_R * GasConstant; + } + + void ConstCpPoly::modifyParameters(doublereal* coeffs) { + m_t0 = coeffs[0]; + m_h0_R = coeffs[1] / GasConstant; + m_s0_R = coeffs[2] / GasConstant; + m_cp0_R = coeffs[3] / GasConstant; + m_logt0 = log(m_t0); + } + + +} + + diff --git a/Cantera/src/thermo/ConstCpPoly.h b/Cantera/src/thermo/ConstCpPoly.h new file mode 100644 index 000000000..e824c53dc --- /dev/null +++ b/Cantera/src/thermo/ConstCpPoly.h @@ -0,0 +1,196 @@ +/** + * @file ConstCpPoly.h + * Headers for the \link Cantera::SpeciesThermoInterpType SpeciesThermoInterpType\endlink + * object that employs a constant heat capacity assumption (see \ref spthermo and + * \link Cantera::ConstCpPoly ConstCpPoly\endlink). + */ +/* + * $Author$ + * $Revision$ + * $Date$ + */ +// Copyright 2001 California Institute of Technology + + +#ifndef CT_CONSTCPPOLY_H +#define CT_CONSTCPPOLY_H + +#include "SpeciesThermoInterpType.h" + +namespace Cantera { + + /** + * A constant-heat capacity species thermodynamic property manager class. + * This makes the + * assumption that the heat capacity is a constant. Then, the following + * relations are used to complete the specification of the thermodynamic + * functions for the species. + * + * \f[ + * \frac{c_p(T)}{R} = Cp0\_R + * \f] + * \f[ + * \frac{h^0(T)}{RT} = \frac{1}{T} * (h0\_R + (T - T_0) * Cp0\_R) + * \f] + * \f[ + * \frac{s^0(T)}{R} = (s0\_R + (log(T) - log(T_0)) * Cp0\_R) + * \f] + * + * This parameterization takes 4 input values. These are: + * - c[0] = \f$ T_0 \f$(Kelvin) + * - c[1] = \f$ H_k^o(T_0, p_{ref}) \f$ (J/kmol) + * - c[2] = \f$ S_k^o(T_0, p_{ref}) \f$ (J/kmol K) + * - c[3] = \f$ {Cp}_k^o(T_0, p_{ref}) \f$ (J(kmol K) + * + * The multispecies SimpleThermo class makes the same assumptions as + * this class does. + * + * @see SimpleThermo + * + * @ingroup spthermo + */ + class ConstCpPoly: public SpeciesThermoInterpType { + + public: + + //! empty constructor + ConstCpPoly(); + + //! Constructor used in templated instantiations + /*! + * @param n Species index + * @param tlow Minimum temperature + * @param thigh Maximum temperature + * @param pref reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state for species n. + * There are 4 coefficients for the %ConstCpPoly parameterization. + * - c[0] = \f$ T_0 \f$(Kelvin) + * - c[1] = \f$ H_k^o(T_0, p_{ref}) \f$ (J/kmol) + * - c[2] = \f$ S_k^o(T_0, p_{ref}) \f$ (J/kmol K) + * - c[3] = \f$ {Cp}_k^o(T_0, p_{ref}) \f$ (J(kmol K) + * + */ + ConstCpPoly(int n, doublereal tlow, doublereal thigh, + doublereal pref, + const doublereal* coeffs); + + //! copy constructor + ConstCpPoly(const ConstCpPoly&); + + //! Assignment operator + ConstCpPoly& operator=(const ConstCpPoly&); + + //! Destructor + virtual ~ConstCpPoly(); + + //! Duplicator + virtual SpeciesThermoInterpType * + duplMyselfAsSpeciesThermoInterpType() const; + //! Returns the minimum temperature that the thermo + //! parameterization is valid + doublereal minTemp() const; + + //! Returns the maximum temperature that the thermo + //! parameterization is valid + doublereal maxTemp() const; + + //! Returns the reference pressure (Pa) + doublereal refPressure() const; + + //! Returns an integer representing the type of parameterization + virtual int reportType() const { return CONSTANT_CP; } + + //! Update the properties for this species, given a temperature polynomial + /*! + * This method is called with a pointer to an array containing the functions of + * temperature needed by this parameterization, and three pointers to arrays where the + * computed property values should be written. This method updates only one value in + * each array. + * + * Form and Length of the temperature polynomial: + * - m_t[0] = tt; + * + * @param tt Vector of temperature polynomials + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + void updateProperties(const doublereal* tt, + doublereal* cp_R, doublereal* h_RT, + doublereal* s_R) const; + + //! Compute the reference-state property of one species + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of one of the species. The species index is used + * to reference into the cp_R, h_RT, and s_R arrays. + * + * @param temp Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + void updatePropertiesTemp(const doublereal temp, + doublereal* cp_R, doublereal* h_RT, + doublereal* s_R) const; + //!This utility function reports back the type of + //! parameterization and all of the parameters for the + //! species, index. + /*! + * All parameters are output variables + * + * @param n Species index + * @param type Integer type of the standard type + * @param tlow output - Minimum temperature + * @param thigh output - Maximum temperature + * @param pref output - reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + void reportParameters(int &n, int &type, + doublereal &tlow, doublereal &thigh, + doublereal &pref, + doublereal* const coeffs) const; + //! Modify parameters for the standard state + /*! + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void modifyParameters(doublereal* coeffs); + + protected: + //! Base temperature + doublereal m_t0; + //! Dimensionless value of the heat capacity + doublereal m_cp0_R; + //! dimensionless value of the enthaply at t0 + doublereal m_h0_R; + //! Dimensionless value of the entropy at t0 + doublereal m_s0_R; + //! log of the t0 value + doublereal m_logt0; + //! Minimum temperature for which the parameterization is valid (Kelvin) + doublereal m_lowT; + //! Maximum temperature for which the parameterization is valid (Kelvin) + doublereal m_highT; + //! Reference pressure (Pa) + doublereal m_Pref; + //! Species Index + int m_index; + + private: + + }; + +} + +#endif diff --git a/Cantera/src/thermo/ConstDensityThermo.cpp b/Cantera/src/thermo/ConstDensityThermo.cpp new file mode 100755 index 000000000..e576c04ea --- /dev/null +++ b/Cantera/src/thermo/ConstDensityThermo.cpp @@ -0,0 +1,151 @@ +/** + * @file ConstDensityThermo.cpp + * Declarations for a Thermo manager for incompressible ThermoPhases + * (see \ref thermoprops and \link Cantera::ConstDensityThermo ConstDensityThermo +\endlink). + */ +/* + * $Id$ + * + * Copyright 2002 California Institute of Technology + */ + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "ct_defs.h" +#include "mix_defs.h" +#include "ConstDensityThermo.h" +#include "SpeciesThermo.h" +#include + +namespace Cantera { + + int ConstDensityThermo:: + eosType() const { return cIncompressible; } + + doublereal ConstDensityThermo::enthalpy_mole() const { + doublereal p0 = m_spthermo->refPressure(); + return GasConstant * temperature() * + mean_X(&enthalpy_RT()[0]) + + (pressure() - p0)/molarDensity(); + } + + doublereal ConstDensityThermo::intEnergy_mole() const { + doublereal p0 = m_spthermo->refPressure(); + return GasConstant * temperature() * + mean_X(&enthalpy_RT()[0]) + - p0/molarDensity(); + } + + doublereal ConstDensityThermo::entropy_mole() const { + return GasConstant * (mean_X(&entropy_R()[0]) - + sum_xlogx()); + } + + doublereal ConstDensityThermo::gibbs_mole() const { + return enthalpy_mole() - temperature() * entropy_mole(); + } + + doublereal ConstDensityThermo::cp_mole() const { + return GasConstant * mean_X(&cp_R()[0]); + } + + doublereal ConstDensityThermo::cv_mole() const { + return cp_mole(); + } + + doublereal ConstDensityThermo::pressure() const { + return m_press; + } + + void ConstDensityThermo::setPressure(doublereal p) { + m_press = p; + } + + void ConstDensityThermo::getActivityConcentrations(doublereal* c) const { + getConcentrations(c); + } + + void ConstDensityThermo::getActivityCoefficients(doublereal* ac) const { + for (int k = 0; k < m_kk; k++) { + ac[k] = 1.0; + } + } + + doublereal ConstDensityThermo::standardConcentration(int k) const { + return molarDensity(); + } + + doublereal ConstDensityThermo::logStandardConc(int k) const { + return log(molarDensity()); + } + + void ConstDensityThermo::getChemPotentials(doublereal* mu) const { + doublereal vdp = (pressure() - m_spthermo->refPressure())/ + molarDensity(); + doublereal xx; + doublereal rt = temperature() * GasConstant; + const array_fp& g_RT = gibbs_RT(); + for (int k = 0; k < m_kk; k++) { + xx = fmaxx(SmallNumber, moleFraction(k)); + mu[k] = rt*(g_RT[k] + log(xx)) + vdp; + } + } + + + void ConstDensityThermo::getStandardChemPotentials(doublereal* mu0) const { + getPureGibbs(mu0); + } + + void ConstDensityThermo::initThermo() { + m_kk = nSpecies(); + m_mm = nElements(); + doublereal tmin = m_spthermo->minTemp(); + doublereal tmax = m_spthermo->maxTemp(); + if (tmin > 0.0) m_tmin = tmin; + if (tmax > 0.0) m_tmax = tmax; + m_p0 = refPressure(); + + int leng = m_kk; + m_h0_RT.resize(leng); + m_g0_RT.resize(leng); + m_expg0_RT.resize(leng); + m_cp0_R.resize(leng); + m_s0_R.resize(leng); + m_pe.resize(leng, 0.0); + m_pp.resize(leng); + } + + + void ConstDensityThermo::setToEquilState(const doublereal* lambda_RT) { + throw CanteraError("setToEquilState","not yet impl."); + } + + void ConstDensityThermo::_updateThermo() const { + doublereal tnow = temperature(); + if (m_tlast != tnow) { + m_spthermo->update(tnow, &m_cp0_R[0], &m_h0_RT[0], + &m_s0_R[0]); + m_tlast = tnow; + int k; + for (k = 0; k < m_kk; k++) { + m_g0_RT[k] = m_h0_RT[k] - m_s0_R[k]; + } + m_tlast = tnow; + } + } + + void ConstDensityThermo::setParametersFromXML(const XML_Node& eosdata) { + eosdata._require("model","Incompressible"); + doublereal rho = getFloat(eosdata, "density", "-"); + setDensity(rho); + } + +} + + + + diff --git a/Cantera/src/thermo/ConstDensityThermo.h b/Cantera/src/thermo/ConstDensityThermo.h new file mode 100755 index 000000000..1e32b84b4 --- /dev/null +++ b/Cantera/src/thermo/ConstDensityThermo.h @@ -0,0 +1,451 @@ +/** + * @file ConstDensityThermo.h + * Header for a Thermo manager for incompressible ThermoPhases + * (see \ref thermoprops and \link Cantera::ConstDensityThermo ConstDensityThermo\endlink). + */ +/* + * $Author$ + * $Date$ + * $Revision$ + * + * Copyright 2002 California Institute of Technology + * + */ + +#ifndef CT_CONSTRHOTHERMO_H +#define CT_CONSTRHOTHERMO_H + +#include "ct_defs.h" +#include "mix_defs.h" +#include "ThermoPhase.h" +#include "SpeciesThermo.h" +#include "utilities.h" + +namespace Cantera { + + //! Overloads the virtual methods of class ThermoPhase to implement the + //! incompressible equation of state. + /** + * + * + * Specification of Species Standard State Properties + * + * + * Specification of Solution Thermodynamic Properties + * + * The density is assumed to be constant, no matter what the concentration of the solution. + * + * + * Application within %Kinetics Managers + * + * + * XML Example + * + * An example of an XML Element named phase setting up a SurfPhase object named diamond_100 + * is given below. + * + * @ingroup thermoprops + */ + class ConstDensityThermo : public ThermoPhase { + + public: + + //! Constructor. + /*! + * + */ + ConstDensityThermo() : m_tlast(0.0) {} + + //! Destructor + virtual ~ConstDensityThermo() {} + + // overloaded methods of class ThermoPhase + + virtual int eosType() const; + + //! Return the Molar Enthalpy. Units: J/kmol. + /*! + * + */ + + /// Molar enthalpy. Units: J/kmol. + virtual doublereal enthalpy_mole() const; + + /// Molar internal energy. Units: J/kmol. + virtual doublereal intEnergy_mole() const; + + + /// Molar entropy. Units: J/kmol/K. + virtual doublereal entropy_mole() const; + + /// Molar Gibbs function. Units: J/kmol. + virtual doublereal gibbs_mole() const; + + /// Molar heat capacity at constant pressure. Units: J/kmol/K. + virtual doublereal cp_mole() const; + + /// Molar heat capacity at constant volume. Units: J/kmol/K. + virtual doublereal cv_mole() const; + + //! Return the thermodynamic pressure (Pa). + /*! + * This method must be overloaded in derived classes. Since the + * mass density, temperature, and mass fractions are stored, + * this method should use these values to implement the + * mechanical equation of state \f$ P(T, \rho, Y_1, \dots, + * Y_K) \f$. + */ + virtual doublereal pressure() const; + + //! Set the internally storred pressure (Pa) at constant + //! temperature and composition + /*! + * This method must be reimplemented in derived classes, where it + * may involve the solution of a nonlinear equation. Within %Cantera, + * the independent variable is the density. Therefore, this function + * solves for the density that will yield the desired input pressure. + * The temperature and composition iare held constant during this process. + * + * This base class function will print an error, if not overwritten. + * + * @param p input Pressure (Pa) + */ + virtual void setPressure(doublereal p); + + //! This method returns an array of generalized concentrations + /*! + * \f$ C^a_k\f$ are defined such that \f$ a_k = C^a_k / + * C^0_k, \f$ where \f$ C^0_k \f$ is a standard concentration + * defined below and \f$ a_k \f$ are activities used in the + * thermodynamic functions. These activity (or generalized) + * concentrations are used + * by kinetics manager classes to compute the forward and + * reverse rates of elementary reactions. Note that they may + * or may not have units of concentration --- they might be + * partial pressures, mole fractions, or surface coverages, + * for example. + * + * @param c Output array of generalized concentrations. The + * units depend upon the implementation of the + * reaction rate expressions within the phase. + */ + virtual void getActivityConcentrations(doublereal* c) const; + + //! Get the array of non-dimensional molar-based activity coefficients at + //! the current solution temperature, pressure, and solution concentration. + /*! + * @param ac Output vector of activity coefficients. Length: m_kk. + */ + virtual void getActivityCoefficients(doublereal* ac) const; + + //! Get the species chemical potentials. Units: J/kmol. + /*! + * This function returns a vector of chemical potentials of the + * species in solution at the current temperature, pressure + * and mole fraction of the solution. + * + * @param mu Output vector of species chemical + * potentials. Length: m_kk. Units: J/kmol + */ + virtual void getChemPotentials(doublereal* mu) const; + + //! Get the array of chemical potentials at unit activity for the species + //! at their standard states at the current T and P of the solution. + /*! + * These are the standard state chemical potentials \f$ \mu^0_k(T,P) + * \f$. The values are evaluated at the current + * temperature and pressure of the solution + * + * @param mu0 Output vector of chemical potentials. + * Length: m_kk. + */ + virtual void getStandardChemPotentials(doublereal* mu0) const; + + //! Return the standard concentration for the kth species + /*! + * The standard concentration \f$ C^0_k \f$ used to normalize + * the activity (i.e., generalized) concentration. In many cases, this quantity + * will be the same for all species in a phase - for example, + * for an ideal gas \f$ C^0_k = P/\hat R T \f$. For this + * reason, this method returns a single value, instead of an + * array. However, for phases in which the standard + * concentration is species-specific (e.g. surface species of + * different sizes), this method may be called with an + * optional parameter indicating the species. + * + * @param k Optional parameter indicating the species. The default + * is to assume this refers to species 0. + * @return + * Returns the standard Concentration in units of m3 kmol-1. + */ + virtual doublereal standardConcentration(int k=0) const; + + //! Natural logarithm of the standard concentration of the kth species. + /*! + * @param k index of the species (defaults to zero) + */ + virtual doublereal logStandardConc(int k=0) const; + + //! Get the Gibbs functions for the standard + //! state of the species at the current T and P of the solution + /*! + * Units are Joules/kmol + * @param gpure Output vector of standard state gibbs free energies + * Length: m_kk. + */ + virtual void getPureGibbs(doublereal* gpure) const { + const array_fp& gibbsrt = gibbs_RT(); + scale(gibbsrt.begin(), gibbsrt.end(), gpure, _RT()); + } + + //! Get the nondimensional Enthalpy functions for the species + //! at their standard states at the current T and P of the solution. + /*! + * @param hrt Output vector of nondimensional standard state enthalpies. + * Length: m_kk. + */ + void getEnthalpy_RT(doublereal* hrt) const { + const array_fp& _h = enthalpy_RT(); + std::copy(_h.begin(), _h.end(), hrt); + } + + //! Get the array of nondimensional Entropy functions for the + //! standard state species at the current T and P of the solution. + /*! + * @param sr Output vector of nondimensional standard state entropies. + * Length: m_kk. + */ + void getEntropy_R(doublereal* sr) const { + const array_fp& _s = entropy_R(); + std::copy(_s.begin(), _s.end(), sr); + } + + //! Get the nondimensional Gibbs functions for the species + //! in their standard states at the current T and P of the solution. + /*! + * @param grt Output vector of nondimensional standard state gibbs free energies + * Length: m_kk. + */ + virtual void getGibbs_RT(doublereal* grt) const { + const array_fp& gibbsrt = gibbs_RT(); + std::copy(gibbsrt.begin(), gibbsrt.end(), grt); + } + + //! Get the nondimensional Heat Capacities at constant + //! pressure for the species standard states + //! at the current T and P of the solution + /*! + * @param cpr Output vector of nondimensional standard state heat capacities + * Length: m_kk. + */ + void getCp_R(doublereal* cpr) const { + const array_fp& _cpr = cp_R(); + std::copy(_cpr.begin(), _cpr.end(), cpr); + } + + + // new methods defined here + + //! Returns a reference to the vector of nondimensional + //! enthalpies of the reference state at the current temperature + //! of the solution and the reference pressure for the species. + const array_fp& enthalpy_RT() const { + _updateThermo(); + return m_h0_RT; + } + + //! Returns a reference to the vector of nondimensional + //! Gibbs Free Energies of the reference state at the current temperature + //! of the solution and the reference pressure for the species. + const array_fp& gibbs_RT() const { + _updateThermo(); + return m_g0_RT; + } + + //! Returns a reference to the vector of exponentials of the nondimensional + //! Gibbs Free Energies of the reference state at the current temperature + //! of the solution and the reference pressure for the species. + const array_fp& expGibbs_RT() const { + _updateThermo(); + int k; + for (k = 0; k != m_kk; k++) m_expg0_RT[k] = std::exp(m_g0_RT[k]); + return m_expg0_RT; + } + + //! Returns a reference to the vector of nondimensional + //! entropies of the reference state at the current temperature + //! of the solution and the reference pressure for each species. + const array_fp& entropy_R() const { + _updateThermo(); + return m_s0_R; + } + + //! Returns a reference to the vector of nondimensional + //! constant pressure heat capacities of the reference state + //! at the current temperature of the solution + //! and reference pressure for each species. + const array_fp& cp_R() const { + _updateThermo(); + return m_cp0_R; + } + + //! Set the potential energy of species k + /*! + * @param k species index + * @param pe Potential energy (J kmol-1). + */ + virtual void setPotentialEnergy(int k, doublereal pe) { + m_pe[k] = pe; + } + + //! Returns the potential energy of species k + /*! + * @param k species index + */ + virtual doublereal potentialEnergy(int k) const { + return m_pe[k]; + } + + //! Initialize the ThermoPhase object after all species have been set up + /*! + * @internal 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 from ThermoPhase::initThermoXML(), + * which is called from importPhase(), + * just prior to returning from function importPhase(). + * + * @see importCTML.cpp + */ + virtual void initThermo(); + + //!This method is used by the ChemEquil equilibrium solver. + /*! + * It sets the state such that the chemical potentials satisfy + * \f[ \frac{\mu_k}{\hat R T} = \sum_m A_{k,m} + * \left(\frac{\lambda_m} {\hat R T}\right) \f] where + * \f$ \lambda_m \f$ is the element potential of element m. The + * temperature is unchanged. Any phase (ideal or not) that + * implements this method can be equilibrated by ChemEquil. + * + * @param lambda_RT Input vector of dimensionless element potentials + * The length is equal to nElements(). + */ + virtual void setToEquilState(const doublereal* lambda_RT); + + + //! Set the equation of state parameters + /*! + * @internal + * The number and meaning of these depends on the subclass. + * + * @param n number of parameters + * @param c array of \a n coefficients + */ + virtual void setParameters(int n, doublereal* c) { + setDensity(c[0]); + } + + //! Get the equation of state parameters in a vector + /*! + * @internal + * The number and meaning of these depends on the subclass. + * + * @param n number of parameters + * @param c array of \a n coefficients + */ + virtual void getParameters(int &n, doublereal * const c) { + double d = density(); + c[0] = d; + n = 1; + } + + //! Set equation of state parameter values from XML entries. + /*! + * + * This method is called by function importPhase() in + * file importCTML.cpp when processing a phase definition in + * an input file. It should be overloaded in subclasses to set + * any parameters that are specific to that particular phase + * model. Note, this method is called before the phase is + * initialzed with elements and/or species. + * + * @param eosdata An XML_Node object corresponding to + * the "thermo" entry for this phase in the input file. + */ + virtual void setParametersFromXML(const XML_Node& eosdata); + + protected: + + //! number of elements + int m_mm; + + + //! Minimum temperature for valid species standard state thermo props + /*! + * This is the minimum temperature at which all species have valid standard + * state thermo props defined. + */ + doublereal m_tmin; + + //! Maximum temperature for valid species standard state thermo props + /*! + * This is the maximum temperature at which all species have valid standard + * state thermo props defined. + */ + doublereal m_tmax; + + //! Reference state pressure + /*! + * Value of the reference state pressure in Pascals. + * All species must have the same reference state pressure. + */ + doublereal m_p0; + + //! last value of the temperature processed by reference state + mutable doublereal m_tlast; + + //! Temporary storage for dimensionless reference state enthalpies + mutable array_fp m_h0_RT; + + //! Temporary storage for dimensionless reference state heat capacities + mutable array_fp m_cp0_R; + + //! Temporary storage for dimensionless reference state gibbs energies + mutable array_fp m_g0_RT; + + //! Temporary storage for dimensionless reference state entropies + mutable array_fp m_s0_R; + + //! currently unsed + /*! + * @deprecated + */ + mutable array_fp m_expg0_RT; + + //! Currently unused + /* + * @deprecated + */ + mutable array_fp m_pe; + + //! Temporary array containing internally calculated partial pressures + mutable array_fp m_pp; + + //! Current pressure (Pa) + doublereal m_press; + + private: + + //! Function to update the reference state thermo functions + void _updateThermo() const; + }; +} + +#endif diff --git a/Cantera/src/thermo/Constituents.cpp b/Cantera/src/thermo/Constituents.cpp new file mode 100755 index 000000000..28301a6db --- /dev/null +++ b/Cantera/src/thermo/Constituents.cpp @@ -0,0 +1,490 @@ +/** + * @file Constituents.cpp + * Header file Class \link Cantera::Constituents Constitutents\endlink which + * manages a set of elements and species (see \ref phases). + */ + +/* $Author$ + * $Date$ + * $Revision$ + * + + */ + +// Copyright 2001 California Institute of Technology + + +#ifdef WIN32 +#pragma warning(disable:4786) +#endif + +#include "Constituents.h" +#include "Elements.h" +using namespace std; + +namespace Cantera { + + /* + * Constructor sets all base variable types to zero. Also, it + * sets the pointer to the Elements object for this object to the + * default value of BaseElements. If the BaseElements Elements + * object doesn't exist, it creates it. + * + * Input + * -------- + * ptr_Elements: If the Constituents object requires a different + * Elements object than the default one, input + * address here. This argument defaults to null, + * in which case the default Elements Object is + * chosen. + */ + + /* + * DGG: I have reversed the role of ptr_Elements. In this version, + * the default is that a new Elements object is created, so this + * Constituents object is independent of any other object. But if + * ptr_Elements is supplied, it will be used. This way, a class + * implementing a multi-phase mixture is responsible for + * maintaining the global elements list for the mixture, and no + * static global element list is required. + */ + Constituents::Constituents(Elements* ptr_Elements) : + m_kk(0), + m_speciesFrozen(false) , + m_Elements(ptr_Elements) { + + if (!m_Elements) m_Elements = new Elements(); + + // Register subscription to Elements object whether or not we + // created it here. + m_Elements->subscribe(); + } + + /** + * Destructor for class Constituents. + * + * Some cleanup of of the Global_Elements_List array is + * effected by unsubscribing to m_Elements. + */ + Constituents::~Constituents() + { + int ileft = m_Elements->unsubscribe(); + /* + * Here we may delete Elements Objects or not. Right now, we + * will delete them. We also delete the global pointer entry + * to keep everything consistent. + */ + if (ileft <= 0) { + vector::iterator it; + for (it = Elements::Global_Elements_List.begin(); + it != Elements::Global_Elements_List.end(); ++it) { + if (*it == m_Elements) { + Elements::Global_Elements_List.erase(it); + break; + } + } + delete m_Elements; + } + } + + int Constituents::nElements() const { return m_Elements->nElements(); } + + + /** + * Return the Atomic weight of element m. + * units = Kg / Kmol + */ + doublereal Constituents::atomicWeight(int m) const { + return m_Elements->atomicWeight(m); + } + + /** + * returns a reference to the vector of atomic weights pertinent + * to this constituents object + * units = kg / Kmol + */ + const vector_fp& Constituents::atomicWeights() const { + return m_Elements->atomicWeights(); + } + + + /** + * Return the atomic number of element m. + */ + int Constituents::atomicNumber(int m) const { + return m_Elements->atomicNumber(m); + } + + + /** + * Add an element to the set. + * @param symbol symbol string + * @param weight atomic weight in kg/mol. + * + * If weight is not given, then a lookup is performed in the + * element object + * + */ + void Constituents:: + addElement(const std::string& symbol, doublereal weight) + { + m_Elements->addElement(symbol, weight); + } + + void Constituents:: + addElement(const XML_Node& e) + { + m_Elements->addElement(e); + } + + /* + * Add a unique element to the set. A check on the symbol is made + * If the symbol is already an element, then a new element is + * not created. + * + * @param symbol symbol string + * @param weight atomic weight in kg/mol. + * + * If weight is not given, then a lookup is performed in the + * element object + * + * -> Passthrough to the Element lvl. + */ + void Constituents:: + addUniqueElement(const std::string& symbol, doublereal weight) + { + m_Elements->addUniqueElement(symbol, weight); + } + + void Constituents:: + addUniqueElement(const XML_Node& e) + { + m_Elements->addUniqueElement(e); + } + + void Constituents::addElementsFromXML(const XML_Node& phase) { + m_Elements->addElementsFromXML(phase); + } + + /* + * -> Passthrough to the Element lvl. + */ + void Constituents::freezeElements() { + m_Elements->freezeElements(); + } + + /* + * -> Passthrough to the Element lvl. + */ + bool Constituents::elementsFrozen() { + return m_Elements->elementsFrozen(); + } + + /* + * Index of element named \a name. The index is an integer + * assigned to each element in the order it was added, + * beginning with 0 for the first element. If \a name is not + * the name of an element in the set, then the value -1 is + * returned. + * + * + * -> Passthrough to the Element class. + */ + int Constituents::elementIndex(std::string name) const { + return (m_Elements->elementIndex(name)); + } + + /* + * Name of the element with index m. + * + * This is a passthrough routine to the Element object. + * @param m @{ Element index. @} + * \exception If m < 0 or m >= nElements(), the + * exception, ElementRangeError, is thrown. + */ + string Constituents::elementName(int m) const { + return (m_Elements->elementName(m)); + } + + /******************************************************************* + * + * elementNames(): + * + * Returns a read-only reference to the vector of element names. + * @code + * Constituents c; + * ... + * const vector& enames = c.elementNames(); + * int n = enames.size(); + * for (int i = 0; i < n; i++) cout << enames[i] << endl; + * @endcode + * + * + * -> Passthrough to the Element lvl. + */ + const vector& Constituents::elementNames() const { + return m_Elements->elementNames(); + } + + /********************************************************************** + * + * molecularWeight() + * + * Returns the molecular weight of a species given the species index + * + * units = kg / kmol. + */ + doublereal Constituents::molecularWeight(int k) const { + if (k < 0 || k >= nSpecies()) { + throw SpeciesRangeError("Constituents::molecularWeight", + k, nSpecies()); + } + return m_weight[k]; + } + + /********************************************************************** + * + * molecularWeights() + * + * Returns a const reference to the vector of molecular weights + * for all of the species defined in the object. + * + * units = kg / kmol. + */ + const array_fp& Constituents::molecularWeights() const { + return m_weight; + } + + /********************************************************************** + * + * charge(): + * + * Electrical charge of one species k molecule, divided by + * \f$ e = 1.602 \times 10^{-19}\f$ Coulombs. + */ + doublereal Constituents::charge(int k) const { + return m_speciesCharge[k]; + } + + /* + * + * addSpecies() + * + * Add a species to a Constituents object. Note, no check is made + * as to whether the species has a unique name. + * + * Input + * --------- + * name = string containing the name + * comp[] + * charge = + * weight = weight of the species. Default = 0.0. + * Note, the weight is a bit redundent and potentially + * harmful. If weight is less than or equal to zero, + * the weight is calculated from the element composition + * and it need not be supplied on the command line. + */ + void Constituents:: + addSpecies(const std::string& name, const doublereal* comp, + doublereal charge, doublereal size) { + m_Elements->freezeElements(); + m_speciesNames.push_back(name); + m_speciesCharge.push_back(charge); + m_speciesSize.push_back(size); + double wt = 0.0; + int m_mm = m_Elements->nElements(); + const vector_fp &aw = m_Elements->atomicWeights(); + for (int m = 0; m < m_mm; m++) { + m_speciesComp.push_back(comp[m]); + wt += comp[m] * aw[m]; + } + m_weight.push_back(wt); + m_kk++; + } + + /* + * + * addUniqueSpecies(): + * + * Add a species to a Constituents object. This routine will + * first check to see if the species is already part of the + * phase. It does this via a string comparison with the + * existing species in the phase. + */ + void Constituents:: + addUniqueSpecies(const std::string& name, const doublereal* comp, + doublereal charge, doublereal size) { + vector::const_iterator it = m_speciesNames.begin(); + for (int k = 0; k < m_kk; k++) { + if (*it == name) { + /* + * We have found a match. At this point we could do some + * compatibility checks. However, let's just return for the + * moment without specifying any error. + */ + int m_mm = m_Elements->nElements(); + for (int i = 0; i < m_mm; i++) { + if (comp[i] != m_speciesComp[m_kk * m_mm + i]) { + throw CanteraError("addUniqueSpecies", + "Duplicate species have different " + "compositions: " + *it); + } + } + if (charge != m_speciesCharge[m_kk]) { + throw CanteraError("addUniqueSpecies", + "Duplicate species have different " + "charges: " + *it); + } + if (size != m_speciesSize[m_kk]) { + throw CanteraError("addUniqueSpecies", + "Duplicate species have different " + "sizes: " + *it); + } + return; + } + ++it; + } + addSpecies(name, comp, charge, size); + } + + /* + * + * freezeSpecies() + * Set the boolean indicating that we are no longer allowing + * species to be added to the Constituents class object. + */ + void Constituents::freezeSpecies() { + m_speciesFrozen = true; + } + + /* + * + * speciesIndex() + * + * Index of species named \c name. The first species added + * will have index 0, and the last one index nSpecies() - 1. + * + * Note, the [] operator shouldn't be used for map's because it + * creates new entries. Here, we use find() to look up entries. + * + * If name isn't in the list, then a -1 is returned. + */ + int Constituents::speciesIndex(std::string name) const { + vector::const_iterator it = m_speciesNames.begin(); + for (int k = 0; k < m_kk; k++) { + if (*it == name) { + /* + * We have found a match. + */ + return k; + } + ++it; + } + return -1; + } + + /* + * + * speciesName() + * + * Name of the species with index k + */ + string Constituents::speciesName(int k) const { + if (k < 0 || k >= nSpecies()) + throw SpeciesRangeError("Constituents::speciesName", + k, nSpecies()); + return m_speciesNames[k]; + } + + /* + * + * speciesNames() + * + * Return a const reference to the vector of species names + */ + const vector& Constituents::speciesNames() const { + return m_speciesNames; + } + + /* + * + * ready(): + * True if both elements and species have been frozen + */ + bool Constituents::ready() const { + return (m_Elements->elementsFrozen() && m_speciesFrozen); + } + + /* + * Returns the number of atoms of element \c m in species \c k. + */ + doublereal Constituents::nAtoms(int k, int m) const + { + const int m_mm = m_Elements->nElements(); + if (m < 0 || m >=m_mm) + throw ElementRangeError("Constituents::nAtoms",m,nElements()); + if (k < 0 || k >= nSpecies()) + throw SpeciesRangeError("Constituents::nAtoms",k,nSpecies()); + return m_speciesComp[m_mm * k + m]; + } + + /* + * + * getAtoms() + * + * Get a vector containing the atomic composition + * of species k + */ + void Constituents::getAtoms(int k, double *atomArray) const + { + const int m_mm = m_Elements->nElements(); + for (int m = 0; m < m_mm; m++) { + atomArray[m] = (double) m_speciesComp[m_mm * k + m]; + } + } + + /** + * This copy constructor just calls the assignment operator + * for this class. + * The assignment operator does a deep copy. + */ + Constituents::Constituents(const Constituents& right) { + *this = right; + } + + /** + * Assignment operator for the Constituents class. + * Right now we pretty much do a straight uncomplicated + * copy of all of the protected data. + */ + Constituents& Constituents::operator=(const Constituents& right) { + /* + * Check for self assignment. + */ + if (this == &right) return *this; + /* + * We do a straight assignment operator on all of the + * data. The vectors are copied. + */ + m_kk = right.m_kk; + m_weight = right.m_weight; + m_speciesFrozen = right.m_speciesFrozen; + if (m_Elements) { + m_Elements->unsubscribe(); + } + m_Elements = right.m_Elements; + if (m_Elements) { + m_Elements->subscribe(); + } + m_speciesNames = right.m_speciesNames; + m_speciesComp = right.m_speciesComp; + m_speciesCharge = right.m_speciesCharge; + m_speciesSize = right.m_speciesSize; + /* + * Return the reference to the current object + */ + return *this; + } + + +} diff --git a/Cantera/src/thermo/Constituents.h b/Cantera/src/thermo/Constituents.h new file mode 100755 index 000000000..dd3286765 --- /dev/null +++ b/Cantera/src/thermo/Constituents.h @@ -0,0 +1,392 @@ +/** + * @file Constituents.h + * Header file Class \link Cantera::Constituents Constitutents\endlink which + * manages a set of elements and species (see \ref phases). + */ + +/* $Author$ + * $Date$ + * $Revision$ + * + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_CONSTIT_H +#define CT_CONSTIT_H + + +#include "ct_defs.h" +//using namespace std; + +#include "SpeciesThermo.h" +#include "ctexceptions.h" +#include "stringUtils.h" +#include "xml.h" + +namespace Cantera { + + class Elements; + + /************** DEFINITIONS OF ERRORS *****************************/ + + //! Specific fatal error indicating that the index of a species is out of range. + /*! + * + * @ingroup errorhandling + */ + class SpeciesRangeError : public CanteraError { + public: + //! Constructor + /*! + * @param func Function where the error occurred. + * @param k current species index value + * @param kmax Maximum permissible species index value. The + * minimum permissible species index value is assumed to be 0 + * + */ + SpeciesRangeError(std::string func, int k, int kmax) : + CanteraError(func, "Species index " + int2str(k) + + " outside valid range of 0 to " + int2str(kmax-1)) {} + }; + + /******************************************************************/ + + + //! Class %Constituents manages a set of elements and species. + /*! + * Class %Constituents is designed to provide information + * about the elements and species in a phase - names, index + * numbers (location in arrays), atomic or molecular weights, + * etc. No computations are performed by the methods of this + * class. The set of elements must include all those that compose + * the species, but may include additional elements. The species + * all must belong to the same phase. + * + * @ingroup phases + */ + class Constituents { + + public: + + //! Constructor. + /*! + * Constructor sets all base variable types to zero. Also, it + * sets the pointer to the Elements object for this object. + * + * @param ptr_Elements + * The default is that a new Elements object is created, so this + * Constituents object is independent of any other object. But if + * ptr_Elements is supplied, it will be used. This way, a class + * implementing a multi-phase mixture is responsible for + * maintaining the global elements list for the mixture, and no + * static global element list is required. + */ + Constituents(Elements* ptr_Elements = 0); + + /// Destructor. + ~Constituents(); + + /// This copy constructor just calls the assignment operator + /// for this class. + /*! + * @param right reference to the object to be copied. + */ + Constituents(const Constituents& right); + + /// Assignment operator + /*! + * @param right Reference to the object to be copied. + */ + Constituents& operator=(const Constituents& right); + + /// @name Element Information + // @{ + + /// Name of the element with index m. + /// This is a passthrough routine to the Element object. + /// \param m Element index. + /// \exception If m < 0 or m >= nElements(), the + /// exception, ElementRangeError, is thrown. + std::string elementName(int m) const; + + + /// Index of element named 'name'. + /// The index is an integer + /// assigned to each element in the order it was added, + /// beginning with 0 for the first element. + /// @param name name of the element + /// + /// If 'name' is not + /// the name of an element in the set, then the value -1 is + /// returned. + int elementIndex(std::string name) const; + + + /// Atomic weight of element m. + /*! + * @param m Element index + */ + doublereal atomicWeight(int m) const; + + /// Atomic number of element m. + /*! + * @param m Element index + */ + int atomicNumber(int m) const; + + /// Return a read-only reference to the vector of element names. + const std::vector& elementNames() const; + + /// Return a read-only reference to the vector of atomic weights. + const vector_fp& atomicWeights() const; + + + /// Number of elements. + int nElements() const; + + // @} + + + + /// @name Adding Elements and Species + /// These methods are used to add new elements or species. + /// These are not usually called by user programs. + /// + /// Since species are checked to insure that they are only + /// composed of declared elements, it is necessary to first + /// add all elements before adding any species. + + //@{ + + //! Add an element. + /*! + * @param symbol Atomic symbol std::string. + * @param weight Atomic mass in amu. + */ + void addElement(const std::string& symbol, doublereal weight); + + //! Add an element from an XML specification. + /*! + * @param e Reference to the XML_Node where the element is described. + */ + void addElement(const XML_Node& e); + + //! Adde an element, checking for uniqueness + /*! + * The uniqueness is checked by comparing the string symbol. If + * not unique, nothing is done. + * + * @param symbol String symbol of the element + * @param weight Atomic weight of the element (kg kmol-1). + */ + void addUniqueElement(const std::string& symbol, doublereal weight); + + //! Adde an element, checking for uniqueness + /*! + * The uniqueness is checked by comparing the string symbol. If + * not unique, nothing is done. + * + * @param e Reference to the XML_Node where the element is described. + */ + void addUniqueElement(const XML_Node& e); + + //! Add all elements referenced in an XML_Node tree + /*! + * @param phase Reference to the top XML_Node of a phase + */ + void addElementsFromXML(const XML_Node& phase); + + /// Prohibit addition of more elements, and prepare to add species. + void freezeElements(); + + /// True if freezeElements has been called. + bool elementsFrozen(); + + //@} + + /// Returns the number of species in the phase + int nSpecies() const { return m_kk; } + + //! Molecular weight of species \c k. + /*! + * @param k index of species \c k + * @return + * Returns the molecular weight of species \c k. + */ + doublereal molecularWeight(int k) const; + + //! Return the Molar mass of species \c k + /*! + * Preferred name for molecular weight. + * + * @param k index for species + * @return + * Return the molar mass of species k kg/kmol. + */ + doublereal molarMass(int k) const { + return molecularWeight(k); + } + + /** + * Return a const reference to the vector of molecular weights + * of the species + */ + const vector_fp& molecularWeights() const; + + /*! + * Electrical charge of one species k molecule, divided by + * the magnitude of the electron charge ( \f$ e = 1.602 + * \times 10^{-19}\f$ Coulombs). Dimensionless. + * + * @param k species index + */ + doublereal charge(int k) const; + + /** + * @name Adding Species + * These methods are used to add new species. + * They are not usually called by user programs. + */ + //@{ + void addSpecies(const std::string& name, const doublereal* comp, + doublereal charge = 0.0, doublereal size = 1.0); + + //! Add a species to the phase, checking for uniqueness of the name + /*! + * This routine checks for uniqueness of the string name. It only + * adds the species if it is unique. + * + * @param name String name of the species + * @param comp Double vector containing the elemental composition of the + * species. + * @param charge Charge of the species. Defaults to zero. + * @param size Size of the species (meters). Defaults to 1 meter. + */ + void addUniqueSpecies(const std::string& name, const doublereal* comp, + doublereal charge = 0.0, + doublereal size = 1.0); + + //! Index of species named 'name' + /*! + * The first species added + * will have index 0, and the last one index nSpecies() - 1. + * + * @param name String name of the species + * @return + * Returns the index of the species. + */ + int speciesIndex(std::string name) const; + + //! Name of the species with index k + /*! + * @param k index of the species + */ + std::string speciesName(int k) const; + + /// Return a const referernce to the vector of species names + const std::vector& speciesNames() const; + + //! This routine returns the size of species k + /*! + * @param k index of the species + * @return + * Returns the size of the species. Units are meters. + */ + doublereal size(int k) const { return m_speciesSize[k]; } + + /** + * Prohibit addition of more species, and prepare for + * calculations with this set of elements and species. + */ + void freezeSpecies(); + + /// True if freezeSpecies has been called. + bool speciesFrozen() { return m_speciesFrozen; } + + /// Remove all elements and species + void clear(); + + //@} + + /// True if both elements and species have been frozen + bool ready() const; + + //! Number of atoms of element \c m in species \c k. + /*! + * @param k species index + * @param m element index + */ + doublereal nAtoms(int k, int m) const; + + + //! Get a vector containing the atomic composition of species k + /*! + * @param k species index + * @param atomArray vector containing the atomic number in the species. + * Length: m_mm + */ + void getAtoms(int k, double *atomArray) const; + + + protected: + + //! Number of species in the phase. + int m_kk; + //! Vector of molecular weights of the species + /*! + * This vector has length m_kk. + * The units of the vector are kg kmol-1. + */ + vector_fp m_weight; + + //! Boolean indicating whether the number of species has been frozen. + /*! + * During the construction of the phase, this is false. After + * construction of the the phase, this is true. + */ + bool m_speciesFrozen; + + /*! + * Pointer to the element object corresponding to this + * phase. Normally, this will be the default Element object + * common to all phases. + */ + Elements * m_Elements; + + //! Vector of the species names + std::vector m_speciesNames; + + //! Atomic composition of the species. + /*! + * the number of atoms of i in species k is equal to + * m_speciesComp[k * m_mm + i] + * The length of this vector is equal to m_kk * m_mm + */ + vector_fp m_speciesComp; + + /** + * m_speciesCharge: Vector of species charges + * length = m_kk + */ + vector_fp m_speciesCharge; + + /** + * m_speciesSize(): Vector of species sizes. + * length m_kk + * This is used in some equations of state + * which employ the constant partial molar + * volume approximation. It's so fundamental + * we've put it at the Constituents class level + */ + vector_fp m_speciesSize; + + private: + + }; + + +} // namespace + +#endif diff --git a/Cantera/src/thermo/Crystal.h b/Cantera/src/thermo/Crystal.h new file mode 100644 index 000000000..dae466d77 --- /dev/null +++ b/Cantera/src/thermo/Crystal.h @@ -0,0 +1,68 @@ +/** + * @file Crystal.h + * + * $Author$ + * $Date$ + * $Revision$ + */ +#ifndef CT_CRYSTAL_H +#define CT_CRYSTAL_H + +#include "MultiPhase.h" + +namespace Cantera { + + /// A class for crystals. Each crystal consists of one or more + /// sublattices, each represented by an object of type + /// LatticePhase. + + class Crystal : public MultiPhase { + + public: + typedef LatticePhase lattice_t; + typedef vector lattice_list; + + /// Constructor. The constructor takes no arguments, since + /// phases are added using method addPhase. + Crystal() : MultiPhase() {} + + /// Destructor. Does nothing. Class MultiPhase does not take + /// "ownership" (i.e. responsibility for destroying) the + /// phase objects. + virtual ~Crystal() {} + + void addLattices(lattice_list& lattices, + const vector_fp& latticeSiteDensity); + + /// Add a phase to the mixture. + /// @param p pointer to the phase object + /// @param moles total number of moles of all species in this phase + void addLattice(lattice_t* lattice, doublereal siteDensity) { + MultiPhase::addPhase(lattice, siteDensity); + } + + /// Return a reference to phase n. The state of phase n is + /// also updated to match the state stored locally in the + /// mixture object. + lattice_t& lattice(index_t n) { + return *(lattice_t*)&phase(n); + } + + protected: + + + }; + + inline std::ostream& operator<<(std::ostream& s, Cantera::Crystal& x) { + size_t ip; + for (ip = 0; ip < x.nPhases(); ip++) { + s << "*************** Lattice " << ip << " *****************" << endl; + s << "SiteDensity: " << x.phaseMoles(ip) << endl; + + s << report(x.phase(ip)) << endl; + } + return s; + } +} + +#endif diff --git a/Cantera/src/thermo/DebyeHuckel.cpp b/Cantera/src/thermo/DebyeHuckel.cpp index 8b058cd0d..5bfe711c7 100644 --- a/Cantera/src/thermo/DebyeHuckel.cpp +++ b/Cantera/src/thermo/DebyeHuckel.cpp @@ -21,7 +21,8 @@ #endif #include "DebyeHuckel.h" -#include "importCTML.h" +//#include "importCTML.h" +#include "ThermoFactory.h" #include "WaterProps.h" #include "WaterPDSS.h" #include diff --git a/Cantera/src/thermo/EdgePhase.h b/Cantera/src/thermo/EdgePhase.h new file mode 100644 index 000000000..0a3cc247d --- /dev/null +++ b/Cantera/src/thermo/EdgePhase.h @@ -0,0 +1,83 @@ +/** + * @file EdgePhase.h + * Declarations for the EdgePhase ThermoPhase object, which models the interface + * between two surfaces (see \ref thermoprops and \link Cantera::EdgePhase EdgePhase\endlink). + */ + +/* $Author$ + * $Date$ + * $Revision$ + * + * Copyright 2002 California Institute of Technology + * + */ + +#ifndef CT_EDGEPHASE_H +#define CT_EDGEPHASE_H + +#include "mix_defs.h" +#include "ThermoPhase.h" +#include "SurfPhase.h" + +namespace Cantera { + + //! A thermodynamic %Phase representing a one dimensional edge between two surfaces + /*! + * This thermodynamic function is largely a wrapper around the SurfPhase + * thermodynamic object. + * + * All of the equations and formulations carry through from SurfPhase to this + * EdgePhase object. + * It should be noted however, that dimensional object with length dimensions, + * have their dimensions reduced by one. + * + * @ingroup thermoprops + */ + class EdgePhase : public SurfPhase { + + public: + + //! Constructor + /*! + * @param n0 Surface site density (kmol m-1). + */ + EdgePhase(doublereal n0 = 0.0); + + //! Destructor + virtual ~EdgePhase() {} + + //! returns the equation of state type + virtual int eosType() const { return cEdge; } + + + //! Set the Equation-of-State parameters by reading an XML Node Input + /*! + * + * The Equation-of-State data consists of one item, the site density. + * + * @param thermoData Reference to an XML_Node named thermo + * containing the equation-of-state data. The + * XML_Node is within the phase XML_Node describing + * the %EdgePhase object. + * + * An example of the contents of the thermoData XML_Node is provided + * below. The units attribute is used to supply the units of the + * site density in any convenient form. Internally it is changed + * into MKS form. + * + * @code + * + * 3e-15 + * + * @endcode + */ + virtual void setParametersFromXML(const XML_Node& thermoData); + }; +} + +#endif + + + + + diff --git a/Cantera/src/thermo/Elements.cpp b/Cantera/src/thermo/Elements.cpp new file mode 100644 index 000000000..2a1e65e54 --- /dev/null +++ b/Cantera/src/thermo/Elements.cpp @@ -0,0 +1,601 @@ +/** + * @file Elements.cpp + * Declaration file for class, Elements, which contains the elements that + * make up species (see \ref phases and \link Cantera::Elements Elements\endlink). + * + * This file contains the definitions for functions in the class Elements. + * It also contains a database of atomic weights. + */ + +/**************************************************************************** + * $RCSfile$ + * $Author$ + * $Date$ + * $Revision$ + * + * + ****************************************************************************/ +// Copyright 2003 California Institute of Technology + + +#ifdef WIN32 +#pragma warning(disable:4786) +#endif + +#include "Elements.h" +#include "xml.h" +#include "ctml.h" +#include "ctexceptions.h" + +using namespace ctml; +using namespace std; + +#ifdef USE_DGG_CODE +#include +#endif + +namespace Cantera { + + + /* awData structure */ + /** + * Database for atomic molecular weights + * + * Values are taken from the 1989 Standard Atomic Weights, CRC + * + * awTable[] is a static function with scope limited to this file. + * It can only be referenced via the static Elements class function, + * LookupWtElements(). + * + * units = kg / kg-mol (or equivalently gm / gm-mol) + * + * (note: this structure was picked because it's simple, compact, + * and extensible). + * + */ + struct awData { + char name[4]; ///< Null Terminated name, First letter capitalized + double atomicWeight; ///< atomic weight in kg / kg-mol + }; + + /*! + * @var static struct awData aWTable[] + * \brief aWTable is a vector containing the atomic weights database. + * + * The size of the table is given by the initial instantiation. + */ + static struct awData aWTable[] = { + {"H", 1.00794}, + {"D", 2.0 }, + {"Tr", 3.0 }, + {"He", 4.002602}, + {"Li", 6.941 }, + {"Be", 9.012182}, + {"B", 10.811 }, + {"C", 12.011 }, + {"N", 14.00674}, + {"O", 15.9994 }, + {"F", 18.9984032}, + {"Ne", 20.1797 }, + {"Na", 22.98977}, + {"Mg", 24.3050 }, + {"Al", 26.98154}, + {"Si", 28.0855 }, + {"P", 30.97376}, + {"S", 32.066 }, + {"Cl", 35.4527 }, + {"Ar", 39.948 }, + {"K", 39.0983 }, + {"Ca", 40.078 }, + {"Sc", 44.95591}, + {"Ti", 47.88 }, + {"V", 50.9415 }, + {"Cr", 51.9961 }, + {"Mn", 54.9381 }, + {"Fe", 55.847 }, + {"Co", 58.9332 }, + {"Ni", 58.69 }, + {"Cu", 63.546 }, + {"Zn", 65.39 }, + {"Ga", 69.723 }, + {"Ge", 72.61 }, + {"As", 74.92159}, + {"Se", 78.96 }, + {"Br", 79.904 }, + {"Kr", 83.80 }, + {"Rb", 85.4678 }, + {"Sr", 87.62 }, + {"Y", 88.90585}, + {"Zr", 91.224 }, + {"Nb", 92.90638}, + {"Mo", 95.94 }, + {"Tc", 97.9072 }, + {"Ru", 101.07 }, + {"Rh", 102.9055 }, + {"Pd", 106.42 }, + {"Ag", 107.8682 }, + {"Cd", 112.411 }, + {"In", 114.82 }, + {"Sn", 118.710 }, + {"Sb", 121.75 }, + {"Te", 127.6 }, + {"I", 126.90447}, + {"Xe", 131.29 }, + {"Cs", 132.90543}, + {"Ba", 137.327 }, + {"La", 138.9055 }, + {"Ce", 140.115 }, + {"Pr", 140.90765}, + {"Nd", 144.24 }, + {"Pm", 144.9127 }, + {"Sm", 150.36 }, + {"Eu", 151.965 }, + {"Gd", 157.25 }, + {"Tb", 158.92534}, + {"Dy", 162.50 }, + {"Ho", 164.93032}, + {"Er", 167.26 }, + {"Tm", 168.93421}, + {"Yb", 173.04 }, + {"Lu", 174.967 }, + {"Hf", 178.49 }, + {"Ta", 180.9479 }, + {"W", 183.85 }, + {"Re", 186.207 }, + {"Os", 190.2 }, + {"Ir", 192.22 }, + {"Pt", 195.08 }, + {"Au", 196.96654}, + {"Hg", 200.59 }, + {"Ti", 204.3833 }, + {"Pb", 207.2 }, + {"Bi", 208.98037}, + {"Po", 208.9824 }, + {"At", 209.9871 }, + {"Rn", 222.0176 }, + {"Fr", 223.0197 }, + {"Ra", 226.0254 }, + {"Ac", 227.0279 }, + {"Th", 232.0381 }, + {"Pa", 231.03588}, + {"U", 238.0508 }, + {"Np", 237.0482 }, + {"Pu", 244.0482 } + }; + + + //! Static function to look up an atomic weight + /*! + * + * This static function looks up the argument string in the + * database above and returns the associated molecular weight. + * The data are from the periodic table. + * + * Note: The idea behind this function is to provide a unified + * source for the element atomic weights. This helps to + * ensure that mass is conserved. + * + * @param + * ElemName String. Only the first 3 characters are significant + * + * @return + * Return value contains the atomic weight of the element + * If a match for the string is not found, a value of -1.0 is + * returned. + * + * @exception CanteraError + * If a match is not found, a CanteraError is thrown as well + */ + double Elements::LookupWtElements(const std::string& s) { + int num = sizeof(aWTable) / sizeof(struct awData); + string s3 = s.substr(0,3); + for (int i = 0; i < num; i++) { + //if (!std::strncmp(s.c_str(), aWTable[i].name, 3)) { + if (s3 == aWTable[i].name) { + return (aWTable[i].atomicWeight); + } + } + throw CanteraError("LookupWtElements", "element not found"); + return -1.0; + } + + + //! Exception class to indicate a fixed set of elements. + /*! + * This class is used to warn the user when the number of elements + * are changed after at least one species is defined. + */ + class ElementsFrozen : public CanteraError { + public: + //! Constructor for class + /*! + * @param func Function where the error occurred. + */ + ElementsFrozen(string func) + : CanteraError(func, + "elements cannot be added after species.") {} + }; + + /* + * Elements Class Constructor + * We initialize all internal variables to zero here. + */ + Elements::Elements() : + m_mm(0), + m_elementsFrozen(false), + numSubscribers(0) + { + } + + /* + * Elements Class Destructor + * If the number of subscribers is not zero, through an error. + * A logic problem has occurred. + * + * @exception CanteraError + */ + Elements::~Elements() + { + if (numSubscribers != 0) { + throw CanteraError("~Elements", "numSubscribers not zero"); + } + } + + /* + * freezeElements(): + * + * Set the freeze flag. This is a prerequesite to other + * activivities, i.e., this is done before species are defined. + */ + void Elements::freezeElements() { + m_elementsFrozen = true; + } + +#ifdef INCL_DEPRECATED_METHODS + /* + * + * Returns an ElementData struct that contains the parameters + * for element index m. + */ + ElementData Elements::element(int m) const { + ElementData e; + e.name = m_elementNames[m]; + e.atomicWeight = m_atomicWeights[m]; + return e; + } +#endif + /* + * elementIndex(): + * + * Index of element named \c name. The index is an integer + * assigned to each element in the order it was added, + * beginning with 0 for the first element. If \c name is not + * the name of an element in the set, then the value -1 is + * returned. + * + */ +#ifdef USE_DGG_CODE + int Elements::elementIndex(std::string name) const{ + map::const_iterator it; + it = m_definedElements.find(name); + if (it != m_definedElements.end()) { + return it->second; + } + return -1; + } +#else + int Elements::elementIndex(std::string name) const { + for (int i = 0; i < m_mm; i++) { + if (m_elementNames[i] == name) return i; + } + return -1; + } +#endif + + /* + * + * Name of the element with index \c m. @param m Element + * index. If m < 0 or m >= nElements() an exception is thrown. + */ + string Elements::elementName(int m) const { + if (m >= 0 && m < nElements()) + return m_elementNames[m]; + else + throw ElementRangeError("Elements::elementName",m,nElements()); + } + + /* + * + * Add an element to the current set of elements in the current object. + * @param symbol symbol string + * @param weight atomic weight in kg/kmol. + * + * The default weight is a special value, which will cause the + * routine to look up the actual weight via a string lookup. + * + * There are two interfaces to this routine. The XML interface + * looks up the required parameters for the regular interface + * and then calls the base routine. + */ + void Elements:: + addElement(const std::string& symbol, doublereal weight) + { + if (weight == -12345.0) { + weight = LookupWtElements(symbol); + if (weight < 0.0) { + throw ElementsFrozen("addElement"); + } + } + if (m_elementsFrozen) { + throw ElementsFrozen("addElement"); + return; + } + m_atomicWeights.push_back(weight); + m_elementNames.push_back(symbol); +#ifdef USE_DGG_CODE + m_definedElements[symbol] = nElements() + 1; +#endif + m_mm++; + } + + void Elements:: + addElement(const XML_Node& e) { + doublereal weight = atof(e["atomicWt"].c_str()); + string symbol = e["name"]; + addElement(symbol, weight); + } + + /* + * addUniqueElement(): + * + * Add a unique element to the set. This routine will not allow + * duplicate elements to be input. + * + * @param symbol symbol string + * @param weight atomic weight in kg/kmol. + * + * + * The default weight is a special value, which will cause the + * routine to look up the actual weight via a string lookup. + */ +#ifdef USE_DGG_CODE + void Elements:: + addUniqueElement(const std::string& symbol, doublereal weight, int atomicNumber) + { + if (m_elementsFrozen) + throw ElementsFrozen("addElement"); + + if (weight == -12345.0) { + weight = LookupWtElements(symbol); + } + + /* + * First decide if this element has been previously added. + * If it unique, add it to the list. + */ + + int i = m_definedElements[symbol] - 1; + if (i < 0) { + m_atomicWeights.push_back(weight); + m_elementNames.push_back(symbol); + m_atomicNumbers.push_back(atomicNumber); + m_mm++; + } + else { + if (m_atomicWeights[i] != weight) { + throw CanteraError("AddUniqueElement", + "Duplicate Elements (" + symbol + + ") have different weights"); + } + } + } + +#else + void Elements:: + addUniqueElement(const std::string& symbol, + doublereal weight, int atomicNumber) + { + if (weight == -12345.0) { + weight = LookupWtElements(symbol); + if (weight < 0.0) { + throw ElementsFrozen("addElement"); + } + } + /* + * First decide if this element has been previously added + * by conducting a string search. If it unique, add it to + * the list. + */ + int ifound = 0; + int i = 0; + for (vector::const_iterator it = m_elementNames.begin(); + it < m_elementNames.end(); ++it, ++i) { + if (*it == symbol) { + ifound = 1; + break; + } + } + if (!ifound) { + if (m_elementsFrozen) { + throw ElementsFrozen("addElement"); + return; + } + m_atomicWeights.push_back(weight); + m_elementNames.push_back(symbol); + m_atomicNumbers.push_back(atomicNumber); + m_mm++; + } else { + if (m_atomicWeights[i] != weight) { + throw CanteraError("AddUniqueElement", + "Duplicate Elements (" + symbol + + ") have different weights"); + } + } + } +#endif + + + /* + * @todo call addUniqueElement(symbol, weight) instead of + * addElement. + */ + void Elements:: + addUniqueElement(const XML_Node& e) { + doublereal weight = 0.0; + if (e.hasAttrib("atomicWt")) + weight = atof(stripws(e["atomicWt"]).c_str()); + int anum = 0; + if (e.hasAttrib("atomicNumber")) + anum = atoi(stripws(e["atomicNumber"]).c_str()); + string symbol = e["name"]; + if (weight != 0.0) + addUniqueElement(symbol, weight, anum); + else + addUniqueElement(symbol); + } + + /* + * clear() + * + * Remove all elements from the structure. + */ + void Elements::clear() { + m_mm = 0; + m_atomicWeights.resize(0); + m_elementNames.resize(0); + m_elementsFrozen = false; + } + + /* + * ready(): + * + * True if the elements have been frozen + */ + bool Elements::ready() const { + return (m_elementsFrozen); + } + + /* + * Elements(const Elements&) - copy constructor: + * + * This copy constructor just calls the assignment operator for this + * class. + */ + Elements::Elements(const Elements& right) + { + *this = right; + /* + * Set the number of subscribers to zero during a copy constructor + */ + numSubscribers = 0; + } + + /* + * Elements& Elements::operator=(const Elements& right): + * + * (assignment operator) + * + * This is the assignment operator for the Elements class. + * Right now we pretty much do a straight uncomplicated + * assignment. However, subscribers are not mucked with, as they + * have to do with the address of the object to be subscribed to + */ + Elements& Elements::operator=(const Elements& right) + { + /* + * Check for self assignment. + */ + if (this == &right) return *this; + /* + * We do a straight assignment operator on all of the + * data. The vectors are copied. + */ + m_mm = right.m_mm; + m_elementsFrozen = right.m_elementsFrozen; + m_atomicWeights = right.m_atomicWeights; + m_elementNames = right.m_elementNames; + /* + * We must not muck with the number of subscribers to this object + * during a straight assignment. This number was set in the + * constructor operation. + */ + /* + * Return the reference to the current object + */ + return *this; + } + + + void Elements::addElementsFromXML(const XML_Node& phase) { + + // get the declared element names + XML_Node& elements = phase.child("elementArray"); + vector enames; + getStringArray(elements, enames); + + // // element database defaults to elements.xml + string element_database = "elements.xml"; + if (elements.hasAttrib("datasrc")) + element_database = elements["datasrc"]; + + XML_Node* doc = get_XML_File(element_database); + XML_Node* dbe = &doc->child("ctml/elementData"); + + XML_Node& root = phase.root(); + XML_Node* local_db = 0; + if (root.hasChild("ctml")) { + if (root.child("ctml").hasChild("elementData")) { + local_db = &root.child("ctml/elementData"); + } + } + + int nel = static_cast(enames.size()); + int i; + string enm; + XML_Node* e = 0; + for (i = 0; i < nel; i++) { + e = 0; + if (local_db) { + //writelog("looking in local database."); + e = local_db->findByAttr("name",enames[i]); + //if (!e) writelog(enames[i]+" not found."); + } + if (!e) + e = dbe->findByAttr("name",enames[i]); + if (e) { + addUniqueElement(*e); + } + else { + throw CanteraError("addElementsFromXML","no data for element " + +enames[i]); + } + } + + } + + /* + * subscribe(), unsubscribe(), and reportSubscriptions(): + * + * Handles setting and reporting the number of subscriptions to this + * object. + */ + void Elements::subscribe() { + ++numSubscribers; + } + int Elements::unsubscribe() { + --numSubscribers; + return numSubscribers; + } + int Elements::reportSubscriptions() const { + return numSubscribers; + } + + /********************* GLOBAL STATIC SECTION **************************/ + /* + * We keep track of a vector of pointers to element objects. + * Initially there are no Elements objects. Whenever one is created, + * the pointer to that object is added onto this list. + */ + vector Elements::Global_Elements_List; + /***********************************************************************/ +} diff --git a/Cantera/src/thermo/Elements.h b/Cantera/src/thermo/Elements.h new file mode 100644 index 000000000..9cf2381c4 --- /dev/null +++ b/Cantera/src/thermo/Elements.h @@ -0,0 +1,266 @@ +/** + * @file Elements.h + * Header file for class, Elements, which contains the elements that + * make up species (see \ref phases and \link Cantera::Elements Elements\endlink). + * + * This file contains the declarations for the elements class. + */ +/*********************************************************************** + * $RCSfile$ + * $Author$ + * $Date$ + * $Revision$ + ***********************************************************************/ +// Copyright 2001 California Institute of Technology + +#ifndef CT_ELEMENTS_H +#define CT_ELEMENTS_H + +#undef USE_DGG_CODE + +#include "ct_defs.h" +//#include "ctexceptions.h" + +namespace Cantera { + + class XML_Node; + class ElementRangeError; + + + //! Object containing the elements that make up species in a phase. + /*! + * Class %Elements manages the elements that are part of a + * chemistry specification. This class may support calculations + * employing Multiple phases. In this case, a single Elements object may + * be shared by more than one Constituents class. Reactions between + * the phases may then be described using stoichiometry base on the + * same Elements class object. + * + * The member functions return information about the elements described + * in a particular instantiation of the class. + * + * @ingroup phases + */ + class Elements { + + public: + + /// Default constructor for the elements class + Elements(); + + //! Default destructor for the elements class + ~Elements(); + + //! Function to lookup the atomic weight of an element + /*! + * @param ename Element symbol name. + */ + static double LookupWtElements(const std::string &ename); + + /// Atomic weight of element m. + /*! + * @param m element index + */ + doublereal atomicWeight(int m) const { return m_atomicWeights[m]; } + + /// Atomic number of element m. + /*! + * @param m element index + */ + int atomicNumber(int m) const { return m_atomicNumbers[m]; } + + /// vector of element atomic weights + const vector_fp& atomicWeights() const { return m_atomicWeights; } + + /** + * Inline function that returns the number of elements in the object. + * + * @return + * \c int: The number of elements in the object. + */ + int nElements() const { return m_mm; } + + //! Function that returns the index of an element. + /*! + * Index of element named \c name. The index is an integer + * assigned to each element in the order it was added, + * beginning with 0 for the first element. If \c name is not + * the name of an element in the set, then the value -1 is + * returned. + * + * @param name String containing the index. + */ + int elementIndex(std::string name) const; + + //! Name of the element with index \c m. + /*! + * @param m Element index. If m < 0 or m >= nElements() an exception is thrown. + */ + std::string elementName(int m) const; + + //! Returns a string vector containing the element names + /*! + * Returns a read-only reference to the vector of element names. + * @return const vector& : The vector contains + * the element names in their indexed order. + */ + const std::vector& elementNames() const { + return m_elementNames; + } + + //! Add an element to the current set of elements in the current object. + /*! + * The default weight is a special value, which will cause the + * routine to look up the actual weight via a string lookup. + * + * There are two interfaces to this routine. The XML interface + * looks up the required parameters for the regular interface + * and then calls the base routine. + * + * @param symbol string symbol for the element. + * @param weight Atomic weight of the element. If no argument + * is provided, a lookup is attempted. + */ + void addElement(const std::string& symbol, + doublereal weight = -12345.0); + + //! Add an element to the current set of elements in the current object. + /*! + * @param e Reference to the XML_Node containing the element information + * The node name is the element symbol and the atomWt attribute + * is used as the atomic weight. + */ + void addElement(const XML_Node& e); + + //! Add an element only if the element hasn't been added before. + /*! + * This is accomplished via a string match on symbol. + * + * @param symbol string symbol for the element. + * @param weight Atomic weight of the element. If no argument + * is provided, a lookup is attempted. + * @param atomicNumber defaults to 0 + */ + void addUniqueElement(const std::string& symbol, + doublereal weight = -12345.0, int atomicNumber = 0); + + //! Add an element to the current set of elements in the current object. + /*! + * @param e Reference to the XML_Node containing the element information + * The node name is the element symbol and the atomWt attribute + * is used as the atomic weight. + */ + void addUniqueElement(const XML_Node& e); + + //! Add multiple elements from a XML_Node phase description + /*! + * @param phase XML_Node reference to a phase + */ + void addElementsFromXML(const XML_Node& phase); + + //! Prohibit addition of more elements, and prepare to add species. + void freezeElements(); + + /// True if freezeElements has been called. + bool elementsFrozen() { return m_elementsFrozen; } + + /// Remove all elements + void clear(); + + /// True if both elements and species have been frozen + bool ready() const; + + //! copy constructor + /*! + * This copy constructor just calls the assignment operator for this + * class. It sets the number of subscribers to zer0. + * + * @param right Reference to the object to be copied. + */ + Elements(const Elements& right); + + //! Assigntment operator + /*! + * This is the assignment operator for the Elements class. + * Right now we pretty much do a straight uncomplicated + * assignment. However, subscribers are not mucked with, as they + * have to do with the address of the object to be subscribed to + * + * @param right Reference to the object to be copied. + */ + Elements& operator=(const Elements& right); + + //! subscribe to this object + /*! + * Increment by one the number of subscriptions to this object. + */ + void subscribe(); + + //! unsubscribe to this object + /*! + * decrement by one the number of subscriptions to this object. + */ + int unsubscribe(); + + //! report the number of subscriptions + int reportSubscriptions() const; + + protected: + + /******************************************************************/ + /* Description of DATA in the Object */ + /******************************************************************/ + + //! Number of elements. + int m_mm; + + /* m_elementsFrozen: */ + /** boolean indicating completion of object + * + * If this is true, then no elements may be added to the + * object. + */ + bool m_elementsFrozen; + + /** + * Vector of element atomic weights: + * + * units = kg / kmol + */ + vector_fp m_atomicWeights; + + /** + * Vector of element atomic numbers: + * + */ + vector_int m_atomicNumbers; + + /** Vector of strings containing the names of the elements + * + * Note, a string search is the primary way to identify elements. + */ + std::vector m_elementNames; + + /** + * Number of Constituents Objects that use this object + * + * Number of Constituents Objects that require this Elements object + * to complete its definition. + * The destructor checks to see that this is equal to zero. + * when the element object is released. + */ + int numSubscribers; + + /********* GLOBAL STATIC SECTION *************/ + + public: + /** Vector of pointers to Elements Objects + * + */ + static std::vector Global_Elements_List; + + }; + +} // namespace + +#endif diff --git a/Cantera/src/thermo/GeneralSpeciesThermo.cpp b/Cantera/src/thermo/GeneralSpeciesThermo.cpp new file mode 100644 index 000000000..1dc4e736a --- /dev/null +++ b/Cantera/src/thermo/GeneralSpeciesThermo.cpp @@ -0,0 +1,243 @@ +/** + * @file GeneralSpeciesThermo.cpp + * Declarations for a completely general species thermodynamic property + * manager for a phase (see \ref spthermo and + * \link Cantera::GeneralSpeciesThermo GeneralSpeciesThermo\endlink). + */ +/* + * $Id$ + */ +// Copyright 2001-2004 California Institute of Technology + +#include "GeneralSpeciesThermo.h" +#include "NasaPoly1.h" +#include "NasaPoly2.h" +#include "ShomatePoly.h" +#include "ConstCpPoly.h" +#include "Mu0Poly.h" +#include "SpeciesThermoFactory.h" +#include +using namespace std; + +namespace Cantera { + + + /* + * Constructors + */ + GeneralSpeciesThermo::GeneralSpeciesThermo() : + SpeciesThermo(), + m_tlow_max(0.0), + m_thigh_min(1.0E30), + m_p0(OneAtm), + m_kk(0) + { + m_tlow_max = 0.0; + m_thigh_min = 1.0E30; + } + + GeneralSpeciesThermo:: + GeneralSpeciesThermo(const GeneralSpeciesThermo &b) : + m_tlow_max(b.m_tlow_max), + m_thigh_min(b.m_thigh_min), + m_kk(b.m_kk) { + m_sp = b.m_sp; + } + + const GeneralSpeciesThermo& + GeneralSpeciesThermo::operator=(const GeneralSpeciesThermo &b) { + if (&b != this) { + m_tlow_max = b.m_tlow_max; + m_thigh_min = b.m_thigh_min; + m_kk = b.m_kk; + m_sp = b.m_sp; + } + return *this; + } + + GeneralSpeciesThermo::~GeneralSpeciesThermo() { + for (int k = 0; k < m_kk; k++) { + SpeciesThermoInterpType *sp = m_sp[k]; + if (sp) { + delete sp; + m_sp[k] = 0; + } + } + } + + + SpeciesThermo * + GeneralSpeciesThermo::duplMyselfAsSpeciesThermo() const { + GeneralSpeciesThermo *gsth = new GeneralSpeciesThermo(*this); + return (SpeciesThermo *) gsth; + } + + + /* + * Install parameterization for a species. + * @param index Species index + * @param type parameterization type + * @param c coefficients. The meaning of these depends on + * the parameterization. + */ + void GeneralSpeciesThermo::install(std::string name, + int index, + int type, + const doublereal* c, + doublereal minTemp, + doublereal maxTemp, + doublereal refPressure) { + /* + * Resize the arrays if necessary, filling the empty + * slots with the zero pointer. + */ + if (index > m_kk - 1) { + m_sp.resize(index+1, 0); + m_kk = index+1; + } + + /* + * Create the necessary object + */ + switch (type) { + case NASA1: + m_sp[index] = new NasaPoly1(index, minTemp, maxTemp, + refPressure, c); + break; + case SHOMATE1: + m_sp[index] = new ShomatePoly(index, minTemp, maxTemp, + refPressure, c); + break; + case CONSTANT_CP: + case SIMPLE: + m_sp[index] = new ConstCpPoly(index, minTemp, maxTemp, + refPressure, c); + break; + case MU0_INTERP: + m_sp[index] = new Mu0Poly(index, minTemp, maxTemp, + refPressure, c); + break; + case SHOMATE2: + m_sp[index] = new ShomatePoly2(index, minTemp, maxTemp, + refPressure, c); + break; + case NASA2: + m_sp[index] = new NasaPoly2(index, minTemp, maxTemp, + refPressure, c); + break; + default: + throw UnknownSpeciesThermoModel( + "GeneralSpeciesThermo::install", + "unknown species type", int2str(type)); + break; + } + m_tlow_max = max(minTemp, m_tlow_max); + m_thigh_min = min(maxTemp, m_thigh_min); + } + + /** + * Update the properties for one species. + */ + void GeneralSpeciesThermo:: + update_one(int k, doublereal t, doublereal* cp_R, + doublereal* h_RT, doublereal* s_R) const { + SpeciesThermoInterpType * sp_ptr = m_sp[k]; + sp_ptr->updatePropertiesTemp(t, cp_R, h_RT, s_R); + } + + + /** + * Update the properties for all species. + */ + void GeneralSpeciesThermo:: + update(doublereal t, doublereal* cp_R, + doublereal* h_RT, doublereal* s_R) const { + vector::const_iterator _begin, _end; + _begin = m_sp.begin(); + _end = m_sp.end(); + SpeciesThermoInterpType * sp_ptr; + for (; _begin != _end; ++_begin) { + sp_ptr = *(_begin); + sp_ptr->updatePropertiesTemp(t, cp_R, h_RT, s_R); + } + } + + /** + * This utility function reports the type of parameterization + * used for the species, index. + */ + int GeneralSpeciesThermo::reportType(int index) const { + SpeciesThermoInterpType *sp = m_sp[index]; + return sp->reportType(); + } + + /** + * This utility function reports back the type of + * parameterization and all of the parameters for the + * species, index. + * For the NASA object, there are 15 coefficients. + */ + void GeneralSpeciesThermo:: + reportParams(int index, int &type, + doublereal * const c, + doublereal &minTemp, + doublereal &maxTemp, + doublereal &refPressure) const { + SpeciesThermoInterpType *sp = m_sp[index]; + int n; + sp->reportParameters(n, type, minTemp, maxTemp, + refPressure, c); + if (n != index) { + throw CanteraError(" ", "confused"); + } + } + + //! Modify parameters for the standard state + /*! + * @param index Species index + * @param c Vector of coefficients used to set the + * parameters for the standard state. + */ + void GeneralSpeciesThermo:: + modifyParams(int index, doublereal *c) { + SpeciesThermoInterpType *sp = m_sp[index]; + sp->modifyParameters(c); + } + + + /** + * Return the lowest temperature at which the thermodynamic + * parameterization is valid. If no argument is supplied, the + * value is the one for which all species parameterizations + * are valid. Otherwise, if an integer argument is given, the + * value applies only to the species with that index. + */ + doublereal GeneralSpeciesThermo::minTemp(int k) const { + if (k < 0) + return m_tlow_max; + else { + SpeciesThermoInterpType *sp = m_sp[k]; + return sp->minTemp(); + } + } + + doublereal GeneralSpeciesThermo::maxTemp(int k) const { + if (k < 0) { + return m_thigh_min; + } else { + SpeciesThermoInterpType *sp = m_sp[k]; + return sp->maxTemp(); + } + } + + doublereal GeneralSpeciesThermo::refPressure(int k) const { + if (k < 0) { + return m_p0; + } else { + SpeciesThermoInterpType *sp = m_sp[k]; + return sp->refPressure(); + } + } + + +} diff --git a/Cantera/src/thermo/GeneralSpeciesThermo.h b/Cantera/src/thermo/GeneralSpeciesThermo.h new file mode 100644 index 000000000..cf8f11d8e --- /dev/null +++ b/Cantera/src/thermo/GeneralSpeciesThermo.h @@ -0,0 +1,230 @@ +/** + * @file GeneralSpeciesThermo.h + * Headers for a completely general species thermodynamic property + * manager for a phase (see \ref spthermo and + * \link Cantera::GeneralSpeciesThermo GeneralSpeciesThermo\endlink). + * + * Because it is general, it is slow. + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +#ifndef CT_GENERALSPECIESTHERMO_H +#define CT_GENERALSPECIESTHERMO_H +#include +#include "ct_defs.h" +#include "SpeciesThermoMgr.h" +#include "NasaPoly1.h" +#include "speciesThermoTypes.h" +//#include "polyfit.h" + +namespace Cantera { + + + //! A species thermodynamic property manager for a phase. + /*! + * This is a general manager that can handle a wide variety + * of species thermodynamic polynomials for individual species. + * It is slow, however, because it recomputes the functions of + * temperature needed for each species. What it does is to create + * a vector of SpeciesThermoInterpType objects. + * + * @ingroup spthermo + */ + class GeneralSpeciesThermo : public SpeciesThermo { + + public: + + //! Constructor + GeneralSpeciesThermo(); + + //! Copy constructor + GeneralSpeciesThermo(const GeneralSpeciesThermo &); + + //! Assignment operator + const GeneralSpeciesThermo & operator=(const GeneralSpeciesThermo &); + + //! destructor + virtual ~GeneralSpeciesThermo(); + + //! Duplicator + virtual SpeciesThermo *duplMyselfAsSpeciesThermo() const ; + + //! Install a new species thermodynamic property + //! parameterization for one species. + /*! + * Install a SpeciesThermoInterpType object for the species, index. + * This routine contains an internal list of SpeciesThermoInterpType + * objects that it knows about. A factory-type lookup is done + * to create the object. + * + * @param name Name of the species + * @param index The 'update' method will update the property + * values for this species + * at position i index in the property arrays. + * @param type int flag specifying the type of parameterization to be + * installed. + * @param c vector of coefficients for the parameterization. + * This vector is simply passed through to the + * parameterization constructor. It's length depends upon + * the parameterization. + * @param minTemp minimum temperature for which this parameterization + * is valid. + * @param maxTemp maximum temperature for which this parameterization + * is valid. + * @param refPressure standard-state pressure for this + * parameterization. + * @see speciesThermoTypes.h + * + * @todo Create a factory method for SpeciesThermoInterpType. + * That's basically what we are doing here. + */ + virtual void install(std::string name, int index, int type, + const doublereal* c, + doublereal minTemp, doublereal maxTemp, + doublereal refPressure); + + //! Like update(), but only updates the single species k. + /*! + * @param k species index + * @param T Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void update_one(int k, doublereal T, doublereal* cp_R, + doublereal* h_RT, + doublereal* s_R) const; + + //! Compute the reference-state properties for all species. + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of each of the standard states. + * + * @param T Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void update(doublereal T, doublereal* cp_R, + doublereal* h_RT, doublereal* s_R) const; + + //! Minimum temperature. + /*! + * If no argument is supplied, this + * method returns the minimum temperature for which \e all + * parameterizations are valid. If an integer index k is + * supplied, then the value returned is the minimum + * temperature for species k in the phase. + * + * @param k Species index + */ + virtual doublereal minTemp(int k=-1) const; + + //! Maximum temperature. + /*! + * If no argument is supplied, this + * method returns the maximum temperature for which \e all + * parameterizations are valid. If an integer index k is + * supplied, then the value returned is the maximum + * temperature for parameterization k. + * + * @param k Species Index + */ + virtual doublereal maxTemp(int k=-1) const; + + //! The reference-state pressure for species k. + /*! + * + * returns the reference state pressure in Pascals for + * species k. If k is left out of the argument list, + * it returns the reference state pressure for the first + * species. + * Note that some SpeciesThermo implementations, such + * as those for ideal gases, require that all species + * in the same phase have the same reference state pressures. + * + * @param k Species Index + */ + virtual doublereal refPressure(int k = -1) const; + + //! This utility function reports the type of parameterization + //! used for the species with index number index. + /*! + * + * @param index Species index + */ + virtual int reportType(int index) const; + + //! This utility function reports back the type of + //! parameterization and all of the parameters for the species, index. + /*! + * @param index Species index + * @param type Integer type of the standard type + * @param c Vector of coefficients used to set the + * parameters for the standard state. + * @param minTemp output - Minimum temperature + * @param maxTemp output - Maximum temperature + * @param refPressure output - reference pressure (Pa). + */ + virtual void reportParams(int index, int &type, + doublereal * const c, + doublereal &minTemp, + doublereal &maxTemp, + doublereal &refPressure) const; + + //! Modify parameters for the standard state + /*! + * @param index Species index + * @param c Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void modifyParams(int index, doublereal *c); + + protected: + + /** + * This is the main unknown in the object. It is + * a list of pointers to type SpeciesThermoInterpType. + * Note, this object owns the objects, so they are deleted + * in the destructor of this object. + */ + std::vector m_sp; + + //! Maximum value of the lowest temperature + doublereal m_tlow_max; + + //! Minimum value of the highest temperature + doublereal m_thigh_min; + + //! reference pressure (Pa) + doublereal m_p0; + + /** + * Internal variable indicating the length of the + * number of species in the phase. + */ + int m_kk; + + private: + + + + }; + +} + +#endif + diff --git a/Cantera/src/thermo/HMWSoln.cpp b/Cantera/src/thermo/HMWSoln.cpp index 1e526354b..864688b67 100644 --- a/Cantera/src/thermo/HMWSoln.cpp +++ b/Cantera/src/thermo/HMWSoln.cpp @@ -17,7 +17,8 @@ #endif #include "HMWSoln.h" -#include "importCTML.h" +//#include "importCTML.h" +#include "ThermoFactory.h" #include "WaterProps.h" #include "WaterPDSS.h" #include diff --git a/Cantera/src/thermo/HMWSoln_input.cpp b/Cantera/src/thermo/HMWSoln_input.cpp index 9d37338ff..e2e9b042d 100644 --- a/Cantera/src/thermo/HMWSoln_input.cpp +++ b/Cantera/src/thermo/HMWSoln_input.cpp @@ -11,7 +11,8 @@ */ #include "HMWSoln.h" -#include "importCTML.h" +//#include "importCTML.h" +#include "ThermoFactory.h" #include "WaterProps.h" #include "WaterPDSS.h" #include diff --git a/Cantera/src/thermo/IdealGasPDSS.cpp b/Cantera/src/thermo/IdealGasPDSS.cpp index 3b49f49bb..7ccd8a26a 100644 --- a/Cantera/src/thermo/IdealGasPDSS.cpp +++ b/Cantera/src/thermo/IdealGasPDSS.cpp @@ -17,8 +17,8 @@ #include "xml.h" #include "ctml.h" #include "IdealGasPDSS.h" -#include "importCTML.h" - +//#include "importCTML.h" +#include "ThermoFactory.h" #include "ThermoPhase.h" diff --git a/Cantera/src/thermo/IdealGasPhase.cpp b/Cantera/src/thermo/IdealGasPhase.cpp new file mode 100644 index 000000000..acb10629f --- /dev/null +++ b/Cantera/src/thermo/IdealGasPhase.cpp @@ -0,0 +1,408 @@ +/** + * + * @file IdealGasPhase.cpp + * ThermoPhase object for the ideal gas equation of + * state - workhorse for %Cantera (see \ref thermoprops + * and class \link Cantera::IdealGasPhase IdealGasPhase\endlink). + * + */ +/* + * $Id$ + */ + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "ct_defs.h" +#include "mix_defs.h" +#include "IdealGasPhase.h" +#include "SpeciesThermo.h" + +using namespace std; + +namespace Cantera { + // Empty Constructor + IdealGasPhase::IdealGasPhase(): + m_mm(0), + m_tmin(0.0), + m_tmax(0.0), + m_p0(-1.0), + m_tlast(0.0), + m_logc0(0.0) + { + } + + // Molar Thermodynamic Properties of the Solution ---------- + // Mechanical Equation of State ---------------------------- + // Chemical Potentials and Activities ---------------------- + + /* + * Returns the standard concentration \f$ C^0_k \f$, which is used to normalize + * the generalized concentration. + */ + doublereal IdealGasPhase::standardConcentration(int k) const { + double p = pressure(); + return p/(GasConstant * temperature()); + } + + /* + * Returns the natural logarithm of the standard + * concentration of the kth species + */ + doublereal IdealGasPhase::logStandardConc(int k) const { + _updateThermo(); + double p = pressure(); + double lc = std::log (p / (GasConstant * temperature())); + return lc; + } + + /* + * Get the array of non-dimensional activity coefficients + */ + void IdealGasPhase::getActivityCoefficients(doublereal *ac) const { + for (int k = 0; k < m_kk; k++) { + ac[k] = 1.0; + } + } + + /* + * Get the array of chemical potentials at unit activity \f$ + * \mu^0_k(T,P) \f$. + */ + void IdealGasPhase::getStandardChemPotentials(doublereal* muStar) const { + const array_fp& gibbsrt = gibbs_RT_ref(); + scale(gibbsrt.begin(), gibbsrt.end(), muStar, _RT()); + double tmp = log (pressure() /m_spthermo->refPressure()); + tmp *= GasConstant * temperature(); + for (int k = 0; k < m_kk; k++) { + muStar[k] += tmp; // add RT*ln(P/P_0) + } + } + + // Partial Molar Properties of the Solution -------------- + + void IdealGasPhase::getChemPotentials(doublereal* mu) const { + getStandardChemPotentials(mu); + //doublereal logp = log(pressure()/m_spthermo->refPressure()); + doublereal xx; + doublereal rt = temperature() * GasConstant; + //const array_fp& g_RT = gibbs_RT_ref(); + for (int k = 0; k < m_kk; k++) { + xx = fmaxx(SmallNumber, moleFraction(k)); + mu[k] += rt*(log(xx)); + } + } + + /* + * Get the array of partial molar enthalpies of the species + * units = J / kmol + */ + void IdealGasPhase::getPartialMolarEnthalpies(doublereal* hbar) const { + const array_fp& _h = enthalpy_RT_ref(); + doublereal rt = GasConstant * temperature(); + scale(_h.begin(), _h.end(), hbar, rt); + } + + /* + * Get the array of partial molar entropies of the species + * units = J / kmol / K + */ + void IdealGasPhase::getPartialMolarEntropies(doublereal* sbar) const { + const array_fp& _s = entropy_R_ref(); + doublereal r = GasConstant; + scale(_s.begin(), _s.end(), sbar, r); + doublereal logp = log(pressure()/m_spthermo->refPressure()); + for (int k = 0; k < m_kk; k++) { + doublereal xx = fmaxx(SmallNumber, moleFraction(k)); + sbar[k] += r * (- logp - log(xx)); + } + } + + /* + * Get the array of partial molar internal energies of the species + * units = J / kmol + */ + void IdealGasPhase::getPartialMolarIntEnergies(doublereal* ubar) const { + const array_fp& _h = enthalpy_RT_ref(); + doublereal rt = GasConstant * temperature(); + for (int k = 0; k < m_kk; k++) { + ubar[k] = rt * (_h[k] - 1.0); + } + } + + /* + * Get the array of partial molar heat capacities + */ + void IdealGasPhase::getPartialMolarCp(doublereal* cpbar) const { + const array_fp& _cp = cp_R_ref(); + scale(_cp.begin(), _cp.end(), cpbar, GasConstant); + } + + /* + * Get the array of partial molar volumes + * units = m^3 / kmol + */ + void IdealGasPhase::getPartialMolarVolumes(doublereal* vbar) const { + double vol = 1.0 / molarDensity(); + for (int k = 0; k < m_kk; k++) { + vbar[k] = vol; + } + } + + // Properties of the Standard State of the Species in the Solution -- + + /* + * Get the nondimensional Enthalpy functions for the species + * at their standard states at the current T and P of the + * solution + */ + void IdealGasPhase::getEnthalpy_RT(doublereal* hrt) const { + const array_fp& _h = enthalpy_RT_ref(); + copy(_h.begin(), _h.end(), hrt); + } + + /* + * Get the array of nondimensional entropy functions for the + * standard state species + * at the current T and P of the solution. + */ + void IdealGasPhase::getEntropy_R(doublereal* sr) const { + const array_fp& _s = entropy_R_ref(); + copy(_s.begin(), _s.end(), sr); + double tmp = log (pressure() /m_spthermo->refPressure()); + for (int k = 0; k < m_kk; k++) { + sr[k] -= tmp; + } + } + + /* + * Get the nondimensional gibbs function for the species + * standard states at the current T and P of the solution. + */ + void IdealGasPhase::getGibbs_RT(doublereal* grt) const { + const array_fp& gibbsrt = gibbs_RT_ref(); + copy(gibbsrt.begin(), gibbsrt.end(), grt); + double tmp = log (pressure() /m_spthermo->refPressure()); + for (int k = 0; k < m_kk; k++) { + grt[k] += tmp; + } + } + + /* + * get the pure Gibbs free energies of each species assuming + * it is in its standard state. This is the same as + * getStandardChemPotentials(). + */ + void IdealGasPhase::getPureGibbs(doublereal* gpure) const { + const array_fp& gibbsrt = gibbs_RT_ref(); + scale(gibbsrt.begin(), gibbsrt.end(), gpure, _RT()); + double tmp = log (pressure() /m_spthermo->refPressure()); + tmp *= _RT(); + for (int k = 0; k < m_kk; k++) { + gpure[k] += tmp; + } + } + + /* + * Returns the vector of nondimensional + * internal Energies of the standard state at the current temperature + * and pressure of the solution for each species. + */ + void IdealGasPhase::getIntEnergy_RT(doublereal *urt) const { + const array_fp& _h = enthalpy_RT_ref(); + for (int k = 0; k < m_kk; k++) { + urt[k] = _h[k] - 1.0; + } + } + + /* + * Get the nondimensional heat capacity at constant pressure + * function for the species + * standard states at the current T and P of the solution. + */ + void IdealGasPhase::getCp_R(doublereal* cpr) const { + const array_fp& _cpr = cp_R_ref(); + copy(_cpr.begin(), _cpr.end(), cpr); + } + + /* + * Get the molar volumes of the species standard states at the current + * T and P of the solution. + * units = m^3 / kmol + * + * @param vol Output vector containing the standard state volumes. + * Length: m_kk. + */ + void IdealGasPhase::getStandardVolumes(doublereal *vol) const { + double tmp = 1.0 / molarDensity(); + for (int k = 0; k < m_kk; k++) { + vol[k] = tmp; + } + } + + // Thermodynamic Values for the Species Reference States --------- + + /* + * Returns the vector of nondimensional + * enthalpies of the reference state at the current temperature + * and reference presssure. + */ + void IdealGasPhase::getEnthalpy_RT_ref(doublereal *hrt) const { + const array_fp& _h = enthalpy_RT_ref(); + copy(_h.begin(), _h.end(), hrt); + } + + /* + * Returns the vector of nondimensional + * enthalpies of the reference state at the current temperature + * and reference pressure. + */ + void IdealGasPhase::getGibbs_RT_ref(doublereal *grt) const { + const array_fp& gibbsrt = gibbs_RT_ref(); + copy(gibbsrt.begin(), gibbsrt.end(), grt); + } + + /* + * Returns the vector of the + * gibbs function of the reference state at the current temperature + * and reference pressure. + * units = J/kmol + */ + void IdealGasPhase::getGibbs_ref(doublereal *g) const { + const array_fp& gibbsrt = gibbs_RT_ref(); + scale(gibbsrt.begin(), gibbsrt.end(), g, _RT()); + } + + /* + * Returns the vector of nondimensional + * entropies of the reference state at the current temperature + * and reference pressure. + */ + void IdealGasPhase::getEntropy_R_ref(doublereal *er) const { + const array_fp& _s = entropy_R_ref(); + copy(_s.begin(), _s.end(), er); + } + + /* + * Returns the vector of nondimensional + * internal Energies of the reference state at the current temperature + * of the solution and the reference pressure for each species. + */ + void IdealGasPhase::getIntEnergy_RT_ref(doublereal *urt) const { + const array_fp& _h = enthalpy_RT_ref(); + for (int k = 0; k < m_kk; k++) { + urt[k] = _h[k] - 1.0; + } + } + + /* + * Returns the vector of nondimensional + * constant pressure heat capacities of the reference state + * at the current temperature and reference pressure. + */ + void IdealGasPhase::getCp_R_ref(doublereal *cprt) const { + const array_fp& _cpr = cp_R_ref(); + copy(_cpr.begin(), _cpr.end(), cprt); + } + + void IdealGasPhase::getStandardVolumes_ref(doublereal *vol) const { + doublereal tmp = _RT() / m_p0; + for (int k = 0; k < m_kk; k++) { + vol[k] = tmp; + } + } + + + // new methods defined here ------------------------------- + + + void IdealGasPhase::initThermo() { + + m_mm = nElements(); + doublereal tmin = m_spthermo->minTemp(); + doublereal tmax = m_spthermo->maxTemp(); + if (tmin > 0.0) m_tmin = tmin; + if (tmax > 0.0) m_tmax = tmax; + m_p0 = refPressure(); + + int leng = m_kk; + m_h0_RT.resize(leng); + m_g0_RT.resize(leng); + m_expg0_RT.resize(leng); + m_cp0_R.resize(leng); + m_s0_R.resize(leng); + m_pe.resize(leng, 0.0); + m_pp.resize(leng); + } + + /* + * Set mixture to an equilibrium state consistent with specified + * chemical potentials and temperature. This method is needed by + * the ChemEquil equillibrium solver. + */ + void IdealGasPhase::setToEquilState(const doublereal* mu_RT) + { + double tmp, tmp2; + const array_fp& grt = gibbs_RT_ref(); + + /* + * Within the method, we protect against inf results if the + * exponent is too high. + * + * If it is too low, we set + * the partial pressure to zero. This capability is needed + * by the elemental potential method. + */ + doublereal pres = 0.0; + for (int k = 0; k < m_kk; k++) { + tmp = -grt[k] + mu_RT[k]; + if (tmp < -600.) { + m_pp[k] = 0.0; + } else if (tmp > 500.0) { + tmp2 = tmp / 500.; + tmp2 *= tmp2; + m_pp[k] = m_p0 * exp(500.) * tmp2; + } else { + m_pp[k] = m_p0 * exp(tmp); + } + pres += m_pp[k]; + } + // set state + setState_PX(pres, &m_pp[0]); + } + + + /// This method is called each time a thermodynamic property is + /// requested, to check whether the internal species properties + /// within the object need to be updated. + /// Currently, this updates the species thermo polynomial values + /// for the current value of the temperature. A check is made + /// to see if the temperature has changed since the last + /// evaluation. This object does not contain any persistent + /// data that depends on the concentration, that needs to be + /// updated. The state object modifies its concentration + /// dependent information at the time the setMoleFractions() + /// (or equivalent) call is made. + void IdealGasPhase::_updateThermo() const { + doublereal tnow = temperature(); + + // If the temperature has changed since the last time these + // properties were computed, recompute them. + if (m_tlast != tnow) { + m_spthermo->update(tnow, &m_cp0_R[0], &m_h0_RT[0], + &m_s0_R[0]); + m_tlast = tnow; + + // update the species Gibbs functions + int k; + for (k = 0; k < m_kk; k++) { + m_g0_RT[k] = m_h0_RT[k] - m_s0_R[k]; + } + m_logc0 = log(m_p0/(GasConstant * tnow)); + m_tlast = tnow; + } + } +} + diff --git a/Cantera/src/thermo/IdealGasPhase.h b/Cantera/src/thermo/IdealGasPhase.h new file mode 100644 index 000000000..d67481bb0 --- /dev/null +++ b/Cantera/src/thermo/IdealGasPhase.h @@ -0,0 +1,930 @@ +/** + * @file IdealGasPhase.h + * ThermoPhase object for the ideal gas equation of + * state - workhorse for %Cantera (see \ref thermoprops + * and class \link Cantera::IdealGasPhase IdealGasPhase\endlink). + * + */ + +/* $Author$ + * $Date$ + * $Revision$ + * + * Copyright 2001 California Institute of Technology + * + */ + + +#ifndef CT_IDEALGASPHASE_H +#define CT_IDEALGASPHASE_H + +//#include "ct_defs.h" +#include "mix_defs.h" +#include "ThermoPhase.h" +#include "SpeciesThermo.h" +#include "utilities.h" + +namespace Cantera { + + + //! Class %IdealGasPhase represents low-density gases that obey the + //! ideal gas equation of state. + /*! + * + * %IdealGasPhase derives from class ThermoPhase, + * and overloads the virtual methods defined there with ones that + * use expressions appropriate for ideal gas mixtures. + * + * The independent unknowns are density, mass fraction, and temperature. + * the #setPressure() function will calculate the density consistent with + * the current mass fraction vector and temperature and the desired pressure, + * and then set the density in the derived State object. + * + *
+ *

Specification of Species Standard %State Properties

+ *
+ * + * It is assumed that the reference state thermodynamics may be + * obtained by a pointer to a populated species thermodynamic property + * manager class in the base class, ThermoPhase::m_spthermo + * (see the base class \link Cantera#SpeciesThermo SpeciesThermo \endlink for a + * description of the specification of reference state species thermodynamics functions). + * The reference state, + * where the pressure is fixed at a single pressure, + * is a key species property calculation for the Ideal Gas Equation + * of state. + * + * This class is optimized for speed of execution. All calls to thermodynamic functions + * first call internal routines (aka #enthalpy_RT_ref()) which return references + * the reference state thermodynamics functions. Within these internal reference + * state functions, the function #_updateThermo() is called, that first checks to see + * whether the temperature has changed. If it has, it updates the internal reference + * state thermo functions by calling the SpeciesThermo object. + * + * Functions for the calculation of standard state properties for species + * at arbitray pressure are provided in %IdealGasPhase. However, they + * are all derived from their reference state conterparts. + * + * The standard state enthalpy is independent of pressure: + * + * \f[ + * h^o_k(T,P) = h^{ref}_k(T) + * \f] + * + * The standard state constant-pressure heat capacity is independent of pressure: + * + * \f[ + * Cp^o_k(T,P) = Cp^{ref}_k(T) + * \f] + * + * The standard state entropy depends in the following fashion on pressure: + * + * \f[ + * S^o_k(T,P) = S^{ref}_k(T) - R \ln(\frac{P}{P_{ref}}) + * \f] + * The standard state gibbs free energy is obtained from the enthalpy and entropy + * functions: + * + * \f[ + * \mu^o_k(T,P) = h^o_k(T,P) - S^o_k(T,P) T + * \f] + * + * \f[ + * \mu^o_k(T,P) = \mu^{ref}_k(T) + R T \ln( \frac{P}{P_{ref}}) + * \f] + * + * where + * \f[ + * \mu^{ref}_k(T) = h^{ref}_k(T) - T S^{ref}_k(T) + * \f] + * + * The standard state internal energy is obtained from the enthalpy function also + * + * \f[ + * u^o_k(T,P) = h^o_k(T) - R T + * \f] + * + * The molar volume of a species is given by the ideal gas law + * + * \f[ + * V^o_k(T,P) = \frac{R T}{P} \mbox{\quad where} + * \f] + * + * R = 8314.47215 Joules kmol-1 K-1, from the 1999 CODATA convention. + * For a complete list of physical constants used within %Cantera, see \ref physConstants . + * + *
+ *

Specification of Solution Thermodynamic Properties

+ *
+ * + * The activity of a species defined in the phase is given by the ideal gas law: + * \f[ + * a_k = X_k + * \f] + * where \f$ X_k \f$ is the mole fraction of species k. + * The chemical potential for species k is equal to + * + * \f[ + * \mu_k(T,P) = \mu^o_k(T, P) + R T \log(X_k) + * \f] + * + * In terms of the reference state, the above can be rewritten + * + * + * \f[ + * \mu_k(T,P) = \mu^{ref}_k(T, P) + R T \log(\frac{P X_k}{P_{ref}}) + * \f] + * + * The partial molar entropy for species k is given by the following relation, + * + * \f[ + * \tilde{s}_k(T,P) = s^o_k(T,P) - R \log(X_k) = s^{ref}_k(T) - R \log(\frac{P X_k}{P_{ref}}) + * \f] + * + * The partial molar enthalpy for species k is + * + * \f[ + * \tilde{h}_k(T,P) = h^o_k(T,P) = h^{ref}_k(T) + * \f] + * + * The partial molar Internal Energy for species k is + * + * \f[ + * \tilde{u}_k(T,P) = u^o_k(T,P) = u^{ref}_k(T) + * \f] + * + * The partial molar Heat Capacity for species k is + * + * \f[ + * \tilde{Cp}_k(T,P) = Cp^o_k(T,P) = Cp^{ref}_k(T) + * \f] + * + * + *
+ *

%Application within %Kinetics Managers

+ *
+ * + * \f$ C^a_k\f$ are defined such that \f$ a_k = C^a_k / + * C^s_k, \f$ where \f$ C^s_k \f$ is a standard concentration + * defined below and \f$ a_k \f$ are activities used in the + * thermodynamic functions. These activity (or generalized) + * concentrations are used + * by kinetics manager classes to compute the forward and + * reverse rates of elementary reactions. + * The activity concentration,\f$ C^a_k \f$,is given by the following expression. + * + * \f[ + * C^a_k = C^s_k X_k = \frac{P}{R T} X_k + * \f] + * + * The standard concentration for species k is independent of k and equal to + * + * \f[ + * C^s_k = C^s = \frac{P}{R T} + * \f] + * + * For example, a bulk-phase binary gas reaction between species j and k, producing + * a new gas species l would have the + * following equation for its rate of progress variable, \f$ R^1 \f$, which has + * units of kmol m-3 s-1. + * + * \f[ + * R^1 = k^1 C_j^a C_k^a = k^1 (C^s a_j) (C^s a_k) + * \f] + * where + * \f[ + * C_j^a = C^s a_j \mbox{\quad and \quad} C_k^a = C^s a_k + * \f] + * + * \f$ C_j^a \f$ is the activity concentration of species j, and + * \f$ C_k^a \f$ is the activity concentration of species k. \f$ C^s \f$ + * is the standard concentration. \f$ a_j \f$ is + * the activity of species j which is equal to the mole fraction of j. + * + * The reverse rate constant can then be obtained from the law of microscopic reversibility + * and the equilibrium expression for the system. + * + * \f[ + * \frac{a_j a_k}{ a_l} = K_a^{o,1} = \exp(\frac{\mu^o_l - \mu^o_j - \mu^o_k}{R T} ) + * \f] + * + * \f$ K_a^{o,1} \f$ is the dimensionless form of the equilibrium constant, associated with + * the pressure dependent standard states \f$ \mu^o_l(T,P) \f$ and their associated activities, + * \f$ a_l \f$, repeated here: + * + * \f[ + * \mu_l(T,P) = \mu^o_l(T, P) + R T \log(a_l) + * \f] + * + * We can switch over to expressing the equilibrium constant in terms of the reference + * state chemical potentials + * + * \f[ + * K_a^{o,1} = \exp(\frac{\mu^{ref}_l - \mu^{ref}_j - \mu^{ref}_k}{R T} ) * \frac{P_{ref}}{P} + * \f] + * + * The concentration equilibrium constant, \f$ K_c \f$, may be obtained by changing over + * to activity concentrations. When this is done: + * + * \f[ + * \frac{C^a_j C^a_k}{ C^a_l} = C^o K_a^{o,1} = K_c^1 = + * \exp(\frac{\mu^{ref}_l - \mu^{ref}_j - \mu^{ref}_k}{R T} ) * \frac{P_{ref}}{RT} + * \f] + * + * %Kinetics managers will calculate the concentration equilibrium constant, \f$ K_c \f$, + * using the second and third part of the above expression as a definition for the concentration + * equilibrium constant. + * + * For completeness, the pressure equilibrium constant may be obtained as well + * + * \f[ + * \frac{P_j P_k}{ P_l P_{ref}} = K_p^1 = \exp(\frac{\mu^{ref}_l - \mu^{ref}_j - \mu^{ref}_k}{R T} ) + * \f] + * + * \f$ K_p \f$ is the simplest form of the equilibrium constant for ideal gases. However, it isn't + * necessarily the simplest form of the equilibrium constant for other types of phases; \f$ K_c \f$ is + * used instead because it is completely general. + * + * The reverse rate of progress may be written down as + * \f[ + * R^{-1} = k^{-1} C_l^a = k^{-1} (C^o a_l) + * \f] + * + * where we can use the concept of microscopic reversibility to write the reverse rate constant in terms of the + * forward reate constant and the concentration equilibrium constant, \f$ K_c \f$. + * + * \f[ + * k^{-1} = k^1 K^1_c + * \f] + * + * \f$k^{-1} \f$ has units of s-1. + * + *
+ *

Instantiation of the Class

+ *
+ * + * + * The constructor for this phase is located in the default ThermoFactory + * for %Cantera. A new %IdealGasPhase may be created by the following code snippet: + * + * @code + * XML_Node *xc = get_XML_File("silane.xml"); + * XML_Node * const xs = xc->findNameID("phase", "silane"); + * ThermoPhase *silane_tp = newPhase(*xs); + * IdealGasPhase *silaneGas = dynamic_cast (silane_tp); + * @endcode + * + * or by the following constructor: + * + * @code + * XML_Node *xc = get_XML_File("silane.xml"); + * XML_Node * const xs = xc->findNameID("phase", "silane"); + * IdealGasPhase *silaneGas = new IdealGasPhase(*xs); + * @endcode + * + *
+ *

XML Example

+ *
+ * An example of an XML Element named phase setting up a IdealGasPhase object named silane + * is given below. + * + * @verbatim + + + Si H He + + H2 H HE SIH4 SI SIH SIH2 SIH3 H3SISIH SI2H6 + H2SISIH2 SI3H8 SI2 SI3 + + + + + + + @endverbatim + * + * The model attribute "IdealGas" of the thermo XML element identifies the phase as + * being of the type handled by the IdealGasPhase object. + * + * @ingroup thermoprops + * + */ + class IdealGasPhase : public ThermoPhase { + + public: + + //! Empty Constructor + IdealGasPhase(); + + //! Destructor + virtual ~IdealGasPhase() {} + + //! Equation of state flag. + /*! + * Returns the value cIdealGas, defined in mix_defs.h. + */ + virtual int eosType() const { return cIdealGas; } + + /** + * @name Molar Thermodynamic Properties of the Solution ------------------------------ + * @{ + */ + + + //! Return the Molar enthalpy. Units: J/kmol. + /*! + * For an ideal gas mixture, + * \f[ + * \hat h(T) = \sum_k X_k \hat h^0_k(T), + * \f] + * and is a function only of temperature. + * The standard-state pure-species enthalpies + * \f$ \hat h^0_k(T) \f$ are computed by the species thermodynamic + * property manager. + * + * \see SpeciesThermo + */ + virtual doublereal enthalpy_mole() const { + return GasConstant * temperature() * + mean_X(&enthalpy_RT_ref()[0]); + } + + /** + * Molar internal energy. J/kmol. For an ideal gas mixture, + * \f[ + * \hat u(T) = \sum_k X_k \hat h^0_k(T) - \hat R T, + * \f] + * and is a function only of temperature. + * The reference-state pure-species enthalpies + * \f$ \hat h^0_k(T) \f$ are computed by the species thermodynamic + * property manager. + * @see SpeciesThermo + */ + virtual doublereal intEnergy_mole() const { + return GasConstant * temperature() + * ( mean_X(&enthalpy_RT_ref()[0]) - 1.0); + } + + /** + * Molar entropy. Units: J/kmol/K. + * For an ideal gas mixture, + * \f[ + * \hat s(T, P) = \sum_k X_k \hat s^0_k(T) - \hat R \log (P/P^0). + * \f] + * The reference-state pure-species entropies + * \f$ \hat s^0_k(T) \f$ are computed by the species thermodynamic + * property manager. + * @see SpeciesThermo + */ + virtual doublereal entropy_mole() const { + return GasConstant * (mean_X(&entropy_R_ref()[0]) - + sum_xlogx() - std::log(pressure()/m_spthermo->refPressure())); + } + + /** + * Molar Gibbs free Energy for an ideal gas. + * Units = J/kmol. + */ + virtual doublereal gibbs_mole() const { + return enthalpy_mole() - temperature() * entropy_mole(); + } + + + /** + * Molar heat capacity at constant pressure. Units: J/kmol/K. + * For an ideal gas mixture, + * \f[ + * \hat c_p(t) = \sum_k \hat c^0_{p,k}(T). + * \f] + * The reference-state pure-species heat capacities + * \f$ \hat c^0_{p,k}(T) \f$ are computed by the species thermodynamic + * property manager. + * @see SpeciesThermo + */ + virtual doublereal cp_mole() const { + return GasConstant * mean_X(&cp_R_ref()[0]); + } + + /** + * Molar heat capacity at constant volume. Units: J/kmol/K. + * For an ideal gas mixture, + * \f[ \hat c_v = \hat c_p - \hat R. \f] + */ + virtual doublereal cv_mole() const { + return cp_mole() - GasConstant; + } + + //@} + + /** + * @name Mechanical Equation of State ------------------------------------------------ + * @{ + */ + + /** + * Pressure. Units: Pa. + * For an ideal gas mixture, + * \f[ P = n \hat R T. \f] + */ + virtual doublereal pressure() const { + return GasConstant * molarDensity() * temperature(); + } + + + //! Set the pressure at constant temperature and composition. + /*! + * Units: Pa. + * This method is implemented by setting the mass density to + * \f[ + * \rho = \frac{P \overline W}{\hat R T }. + * \f] + * + * @param p Pressure (Pa) + */ + virtual void setPressure(doublereal p) { + setDensity(p * meanMolecularWeight() + /(GasConstant * temperature())); + } + + //! Returns the isothermal compressibility. Units: 1/Pa. + /** + * The isothermal compressibility is defined as + * \f[ + * \kappa_T = -\frac{1}{v}\left(\frac{\partial v}{\partial P}\right)_T + * \f] + * For ideal gases it's equal to the negative of the inverse of the pressure + */ + virtual doublereal isothermalCompressibility() const { + return -1.0/pressure(); + } + + //! Return the volumetric thermal expansion coefficient. Units: 1/K. + /*! + * The thermal expansion coefficient is defined as + * \f[ + * \beta = \frac{1}{v}\left(\frac{\partial v}{\partial T}\right)_P + * \f] + * For ideal gases, it's equal to the inverse of the temperature. + */ + virtual doublereal thermalExpansionCoeff() const { + return 1.0/temperature(); + } + + //@} + + /** + * @name Chemical Potentials and Activities ------------------------------------------ + * + * + * The activity \f$a_k\f$ of a species in solution is + * related to the chemical potential by + * \f[ + * \mu_k(T,P,X_k) = \mu_k^0(T,P) + * + \hat R T \log a_k. + * \f] + * The quantity \f$\mu_k^0(T,P)\f$ is + * the standard state chemical potential at unit activity. + * It may depend on the pressure and the temperature. However, + * it may not depend on the mole fractions of the species + * in the solution. + * + * The activities are related to the generalized + * concentrations, \f$\tilde C_k\f$, and standard + * concentrations, \f$C^0_k\f$, by the following formula: + * + * \f[ + * a_k = \frac{\tilde C_k}{C^0_k} + * \f] + * The generalized concentrations are used in the kinetics classes + * to describe the rates of progress of reactions involving the + * species. Their formulation depends upons the specification + * of the rate constants for reaction, especially the units used + * in specifying the rate constants. The bridge between the + * thermodynamic equilibrium expressions that use a_k and the + * kinetics expressions which use the generalized concentrations + * is provided by the multiplicative factor of the + * standard concentrations. + * @{ + */ + + //! This method returns the array of generalized concentrations. + /*! + * For an ideal gas mixture, these are simply the actual concentrations. + * + * @param c Output array of generalized concentrations. The + * units depend upon the implementation of the + * reaction rate expressions within the phase. + */ + virtual void getActivityConcentrations(doublereal* c) const { + getConcentrations(c); + } + + //! Returns the standard concentration \f$ C^0_k \f$, which is used to normalize + //! the generalized concentration. + /*! + * This is defined as the concentration by which the generalized + * concentration is normalized to produce the activity. + * In many cases, this quantity will be the same for all species in a phase. + * Since the activity for an ideal gas mixture is + * simply the mole fraction, for an ideal gas \f$ C^0_k = P/\hat R T \f$. + * + * @param k Optional parameter indicating the species. The default + * is to assume this refers to species 0. + * @return + * Returns the standard Concentration in units of m3 kmol-1. + */ + virtual doublereal standardConcentration(int k=0) const; + + //! Returns the natural logarithm of the standard + //! concentration of the kth species + /*! + * @param k index of the species. (defaults to zero) + */ + virtual doublereal logStandardConc(int k=0) const; + + //! Get the array of non-dimensional activity coefficients at + //! the current solution temperature, pressure, and solution concentration. + /*! + * For ideal gases, the activity coefficients are all equal to one. + * + * @param ac Output vector of activity coefficients. Length: m_kk. + */ + virtual void getActivityCoefficients(doublereal* ac) const; + + + //@} + /// @name Partial Molar Properties of the Solution ---------------------------------- + //@{ + + + //! Get the species chemical potentials. Units: J/kmol. + /*! + * This function returns a vector of chemical potentials of the + * species in solution at the current temperature, pressure + * and mole fraction of the solution. + * + * @param mu Output vector of species chemical + * potentials. Length: m_kk. Units: J/kmol + */ + virtual void getChemPotentials(doublereal* mu) const; + + //! Get the species partial molar enthalpies. Units: J/kmol. + /*! + * @param hbar Output vector of species partial molar enthalpies. + * Length: m_kk. units are J/kmol. + */ + virtual void getPartialMolarEnthalpies(doublereal* hbar) const; + + //! Get the species partial molar entropies. Units: J/kmol/K. + /*! + * @param sbar Output vector of species partial molar entropies. + * Length = m_kk. units are J/kmol/K. + */ + virtual void getPartialMolarEntropies(doublereal* sbar) const; + + //! Get the species partial molar enthalpies. Units: J/kmol. + /*! + * @param ubar Output vector of speciar partial molar internal energies. + * Length = m_kk. units are J/kmol. + */ + virtual void getPartialMolarIntEnergies(doublereal* ubar) const; + + //! Get the partial molar heat capacities Units: J/kmol/K + /*! + * @param cpbar Output vector of species partial molar heat capacities at constant pressure. + * Length = m_kk. units are J/kmol/K. + */ + virtual void getPartialMolarCp(doublereal* cpbar) const; + + //! Get the species partial molar volumes. Units: m^3/kmol. + /*! + * @param vbar Output vector of speciar partial molar volumes. + * Length = m_kk. units are m^3/kmol. + */ + virtual void getPartialMolarVolumes(doublereal* vbar) const; + + //@} + /// @name Properties of the Standard State of the Species in the Solution ---------- + //@{ + + //! Get the array of chemical potentials at unit activity for the + //! species standard states at the current T and P of the solution. + /*! + * These are the standard state chemical potentials \f$ \mu^0_k(T,P) + * \f$. The values are evaluated at the current + * temperature and pressure of the solution + * + * @param mu Output vector of chemical potentials. + * Length: m_kk. + */ + virtual void getStandardChemPotentials(doublereal* mu) const; + + //! Get the nondimensional Enthalpy functions for the species standard states + //! at their standard states at the current T and P of the solution. + /*! + * @param hrt Output vector of nondimensional standard state enthalpies. + * Length: m_kk. + */ + virtual void getEnthalpy_RT(doublereal* hrt) const; + + //! Get the array of nondimensional Entropy functions for the + //! species standard states at the current T and P of the solution. + /*! + * @param sr Output vector of nondimensional standard state entropies. + * Length: m_kk. + */ + virtual void getEntropy_R(doublereal* sr) const; + + //! Get the nondimensional Gibbs functions for the species + //! standard states at the current T and P of the solution. + /*! + * @param grt Output vector of nondimensional standard state gibbs free energies + * Length: m_kk. + */ + virtual void getGibbs_RT(doublereal* grt) const; + + //! Get the Gibbs functions for the standard + //! state of the species at the current T and P of the solution + /*! + * Units are Joules/kmol + * @param gpure Output vector of standard state gibbs free energies + * Length: m_kk. + */ + virtual void getPureGibbs(doublereal* gpure) const; + + //! Returns the vector of nondimensional Internal Energies of the standard + //! state species at the current T and P of the solution + /*! + * @param urt output vector of nondimensional standard state internal energies + * of the species. Length: m_kk. + */ + virtual void getIntEnergy_RT(doublereal *urt) const; + + //! Get the nondimensional Heat Capacities at constant + //! pressure for the species standard states + //! at the current T and P of the solution + /*! + * @param cpr Output vector of nondimensional standard state heat capacities + * Length: m_kk. + */ + virtual void getCp_R(doublereal* cpr) const; + + //! Get the molar volumes of the species standard states at the current + //! T and P of the solution. + /*! + * units = m^3 / kmol + * + * @param vol Output vector containing the standard state volumes. + * Length: m_kk. + */ + virtual void getStandardVolumes(doublereal *vol) const; + + //@} + /// @name Thermodynamic Values for the Species Reference States --------------------- + //@{ + + + //! Returns the vector of nondimensional + //! enthalpies of the reference state at the current temperature + //! of the solution and the reference pressure for the species. + /*! + * @param hrt Output vector containing the nondimensional reference state + * enthalpies. Length: m_kk. + */ + virtual void getEnthalpy_RT_ref(doublereal *hrt) const; + + //! Returns the vector of nondimensional + //! Gibbs Free Energies of the reference state at the current temperature + //! of the solution and the reference pressure for the species. + /*! + * @param grt Output vector containing the nondimensional reference state + * Gibbs Free energies. Length: m_kk. + */ + virtual void getGibbs_RT_ref(doublereal *grt) const; + + //! Returns the vector of the + //! gibbs function of the reference state at the current temperature + //! of the solution and the reference pressure for the species. + /*! + * units = J/kmol + * + * @param g Output vector containing the reference state + * Gibbs Free energies. Length: m_kk. Units: J/kmol. + */ + virtual void getGibbs_ref(doublereal *g) const; + + //! Returns the vector of nondimensional + //! entropies of the reference state at the current temperature + //! of the solution and the reference pressure for each species. + /*! + * @param er Output vector containing the nondimensional reference state + * entropies. Length: m_kk. + */ + virtual void getEntropy_R_ref(doublereal *er) const; + + //! Returns the vector of nondimensional + //! internal Energies of the reference state at the current temperature + //! of the solution and the reference pressure for each species. + /*! + * @param urt Output vector of nondimensional reference state + * internal energies of the species. + * Length: m_kk + */ + virtual void getIntEnergy_RT_ref(doublereal *urt) const; + + //! Returns the vector of nondimensional + //! constant pressure heat capacities of the reference state + //! at the current temperature of the solution + //! and reference pressure for each species. + /*! + * @param cprt Output vector of nondimensional reference state + * heat capacities at constant pressure for the species. + * Length: m_kk + */ + virtual void getCp_R_ref(doublereal *cprt) const; + + //! Get the molar volumes of the species standard states at the current + //! T and P_ref of the solution. + /*! + * units = m^3 / kmol + * + * @param vol Output vector containing the standard state volumes. + * Length: m_kk. + */ + virtual void getStandardVolumes_ref(doublereal *vol) const; + + //@} + /// @name NonVirtual Internal methods to Return References to Reference State Thermo + //@{ + + //! Returns a reference to the dimensionless reference state enthalpy vector. + /*! + * This function is part of the layer that checks/recalculates the reference + * state thermo functions. + */ + const array_fp& enthalpy_RT_ref() const { + _updateThermo(); + return m_h0_RT; + } + + //! Returns a reference to the dimensionless reference state Gibbs free energy vector. + /*! + * This function is part of the layer that checks/recalculates the reference + * state thermo functions. + */ + const array_fp& gibbs_RT_ref() const { + _updateThermo(); + return m_g0_RT; + } + + //! Returns a reference to the exponent of the dimensionless reference state Gibbs Free energy vector. + /*! + * This function is part of the layer that checks/recalculates the reference + * state thermo functions. + */ + const array_fp& expGibbs_RT_ref() const { + _updateThermo(); + int k; + for (k = 0; k != m_kk; k++) m_expg0_RT[k] = std::exp(m_g0_RT[k]); + return m_expg0_RT; + } + + //! Returns a reference to the dimensionless reference state Entropy vector. + /*! + * This function is part of the layer that checks/recalculates the reference + * state thermo functions. + */ + const array_fp& entropy_R_ref() const { + _updateThermo(); + return m_s0_R; + } + + //! Returns a reference to the dimensionless reference state Heat Capacity vector. + /*! + * This function is part of the layer that checks/recalculates the reference + * state thermo functions. + */ + const array_fp& cp_R_ref() const { + _updateThermo(); + return m_cp0_R; + } + + //@} + + //! Initialize the ThermoPhase object after all species have been set up + /*! + * @internal 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 from ThermoPhase::initThermoXML(), + * which is called from importPhase(), + * just prior to returning from function importPhase(). + * + * @see importCTML.cpp + */ + virtual void initThermo(); + + //!This method is used by the ChemEquil equilibrium solver. + /*! + * @internal + * @name Chemical Equilibrium + * @{ + * + * Set mixture to an equilibrium state consistent with specified + * element potentials and temperature. + * It sets the state such that the chemical potentials satisfy + * \f[ \frac{\mu_k}{\hat R T} = \sum_m A_{k,m} + * \left(\frac{\lambda_m} {\hat R T}\right) \f] where + * \f$ \lambda_m \f$ is the element potential of element m. The + * temperature is unchanged. Any phase (ideal or not) that + * implements this method can be equilibrated by ChemEquil. + * + * @param lambda_RT vector of non-dimensional element potentials + * \f[ \lambda_m/RT \f]. + */ + virtual void setToEquilState(const doublereal* lambda_RT); + + //@} + + protected: + + //! Number of Elements in the phase + /*! + * This member is defined here, from a call to the Elements ojbect, for speed. + */ + int m_mm; + + //! Minimum temperature for valid species standard state thermo props + /*! + * This is the minimum temperature at which all species have valid standard + * state thermo props defined. + */ + doublereal m_tmin; + + //! Maximum temperature for valid species standard state thermo props + /*! + * This is the maximum temperature at which all species have valid standard + * state thermo props defined. + */ + doublereal m_tmax; + + //! Reference state pressure + /*! + * Value of the reference state pressure in Pascals. + * All species must have the same reference state pressure. + */ + doublereal m_p0; + + //! last value of the temperature processed by reference state + mutable doublereal m_tlast; + + //! Temporary storage for log of p/rt + mutable doublereal m_logc0; + + //! Temporary storage for dimensionless reference state enthalpies + mutable array_fp m_h0_RT; + + //! Temporary storage for dimensionless reference state heat capacities + mutable array_fp m_cp0_R; + + //! Temporary storage for dimensionless reference state gibbs energies + mutable array_fp m_g0_RT; + + //! Temporary storage for dimensionless reference state entropies + mutable array_fp m_s0_R; + + //! currently unsed + /*! + * @deprecated + */ + mutable array_fp m_expg0_RT; + + //! Currently unused + /* + * @deprecated + */ + mutable array_fp m_pe; + + //! Temporary array containing internally calculated partial pressures + mutable array_fp m_pp; + + private: + + //! Update the species reference state thermodynamic functions + /*! + * The polynomials for the standard state functions are only + * reevalulated if the temperature has changed. + * + */ + void _updateThermo() const; + + }; +} + +#endif diff --git a/Cantera/src/thermo/IdealMolalSoln.cpp b/Cantera/src/thermo/IdealMolalSoln.cpp index 9d7931b55..672da8011 100644 --- a/Cantera/src/thermo/IdealMolalSoln.cpp +++ b/Cantera/src/thermo/IdealMolalSoln.cpp @@ -25,7 +25,8 @@ */ #include "IdealMolalSoln.h" -#include "importCTML.h" +//#include "importCTML.h" +#include "ThermoFactory.h" #include namespace Cantera { diff --git a/Cantera/src/thermo/IdealSolidSolnPhase.h b/Cantera/src/thermo/IdealSolidSolnPhase.h index 9773489b4..01da48ace 100644 --- a/Cantera/src/thermo/IdealSolidSolnPhase.h +++ b/Cantera/src/thermo/IdealSolidSolnPhase.h @@ -25,7 +25,8 @@ #include "mix_defs.h" #include "ThermoPhase.h" -#include "importCTML.h" +//#include "importCTML.h" +#include "ThermoFactory.h" #include "SpeciesThermo.h" diff --git a/Cantera/src/thermo/LatticePhase.cpp b/Cantera/src/thermo/LatticePhase.cpp new file mode 100644 index 000000000..1e2256d3b --- /dev/null +++ b/Cantera/src/thermo/LatticePhase.cpp @@ -0,0 +1,129 @@ +/** + * + * @file LatticePhase.cpp + * + * $Id$ + */ + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "ct_defs.h" +#include "mix_defs.h" +#include "LatticePhase.h" +#include "SpeciesThermo.h" +#include + +namespace Cantera { + + doublereal LatticePhase:: + enthalpy_mole() const { + doublereal p0 = m_spthermo->refPressure(); + return GasConstant * temperature() * + mean_X(&enthalpy_RT()[0]) + + (pressure() - p0)/molarDensity(); + } + + doublereal LatticePhase::intEnergy_mole() const { + doublereal p0 = m_spthermo->refPressure(); + return GasConstant * temperature() * + mean_X(&enthalpy_RT()[0]) + - p0/molarDensity(); + } + + doublereal LatticePhase::entropy_mole() const { + return GasConstant * (mean_X(&entropy_R()[0]) - + sum_xlogx()); + } + + doublereal LatticePhase::gibbs_mole() const { + return enthalpy_mole() - temperature() * entropy_mole(); + } + + doublereal LatticePhase::cp_mole() const { + return GasConstant * mean_X(&cp_R()[0]); + } + + void LatticePhase::getActivityConcentrations(doublereal* c) const { + getMoleFractions(c); + } + + void LatticePhase::getActivityCoefficients(doublereal* ac) const { + for (int k = 0; k < m_kk; k++) { + ac[k] = 1.0; + } + } + + doublereal LatticePhase::standardConcentration(int k) const { + return 1.0; + } + + doublereal LatticePhase::logStandardConc(int k) const { + return 0.0; + } + + void LatticePhase::getChemPotentials(doublereal* mu) const { + doublereal vdp = (pressure() - m_spthermo->refPressure())/ + molarDensity(); + doublereal xx; + doublereal rt = temperature() * GasConstant; + const array_fp& g_RT = gibbs_RT(); + for (int k = 0; k < m_kk; k++) { + xx = fmaxx(SmallNumber, moleFraction(k)); + mu[k] = rt*(g_RT[k] + log(xx)) + vdp; + } + } + + void LatticePhase::getStandardChemPotentials(doublereal* mu0) const { + const array_fp& gibbsrt = gibbs_RT(); + scale(gibbsrt.begin(), gibbsrt.end(), mu0, _RT()); + } + + void LatticePhase::initThermo() { + m_kk = nSpecies(); + m_mm = nElements(); + doublereal tmin = m_spthermo->minTemp(); + doublereal tmax = m_spthermo->maxTemp(); + if (tmin > 0.0) m_tmin = tmin; + if (tmax > 0.0) m_tmax = tmax; + m_p0 = refPressure(); + + int leng = m_kk; + m_h0_RT.resize(leng); + m_g0_RT.resize(leng); + m_cp0_R.resize(leng); + m_s0_R.resize(leng); + setMolarDensity(m_molar_density); + } + + + void LatticePhase::_updateThermo() const { + doublereal tnow = temperature(); + if (fabs(molarDensity() - m_molar_density)/m_molar_density > 0.0001) { + throw CanteraError("_updateThermo","molar density changed from " + +fp2str(m_molar_density)+" to "+fp2str(molarDensity())); + } + if (m_tlast != tnow) { + m_spthermo->update(tnow, &m_cp0_R[0], &m_h0_RT[0], + &m_s0_R[0]); + m_tlast = tnow; + int k; + for (k = 0; k < m_kk; k++) { + m_g0_RT[k] = m_h0_RT[k] - m_s0_R[k]; + } + m_tlast = tnow; + } + } + + void LatticePhase::setParametersFromXML(const XML_Node& eosdata) { + eosdata._require("model","Lattice"); + m_molar_density = getFloat(eosdata, "site_density", "-"); + m_vacancy = getString(eosdata, "vacancy_species"); + } +} + + + + diff --git a/Cantera/src/thermo/LatticePhase.h b/Cantera/src/thermo/LatticePhase.h new file mode 100644 index 000000000..736dd4e39 --- /dev/null +++ b/Cantera/src/thermo/LatticePhase.h @@ -0,0 +1,150 @@ +/** + * + * @file LatticePhase.h + */ + +/* $Author$ + * $Date$ + * $Revision$ + * + * Copyright 2005 California Institute of Technology + * + */ + +#ifndef CT_LATTICE_H +#define CT_LATTICE_H + +#include "ct_defs.h" +#include "mix_defs.h" +#include "ThermoPhase.h" +#include "SpeciesThermo.h" +#include "utilities.h" + +namespace Cantera { + + /** + */ + class LatticePhase : public ThermoPhase { + + public: + + LatticePhase() : m_tlast(0.0) {} + + virtual ~LatticePhase() {} + + virtual int eosType() const { return cLattice; } + + virtual doublereal enthalpy_mole() const; + + virtual doublereal intEnergy_mole() const; + + virtual doublereal entropy_mole() const; + + virtual doublereal gibbs_mole() const; + + virtual doublereal cp_mole() const; + + virtual doublereal cv_mole() const { + return cp_mole(); + } + + virtual doublereal pressure() const { + return m_press; + } + + virtual void setPressure(doublereal p) { + m_press = p; + setMolarDensity(m_molar_density); + } + + virtual void getActivityConcentrations(doublereal* c) const; + + virtual void getActivityCoefficients(doublereal* ac) const; + + virtual void getChemPotentials(doublereal* mu) const; + virtual void getStandardChemPotentials(doublereal* mu0) const; + virtual doublereal standardConcentration(int k=0) const; + virtual doublereal logStandardConc(int k=0) const; + + virtual void getPureGibbs(doublereal* gpure) const { + const array_fp& gibbsrt = gibbs_RT(); + scale(gibbsrt.begin(), gibbsrt.end(), gpure, _RT()); + } + + void getEnthalpy_RT(doublereal* hrt) const { + const array_fp& _h = enthalpy_RT(); + std::copy(_h.begin(), _h.end(), hrt); + } + + void getEntropy_R(doublereal* sr) const { + const array_fp& _s = entropy_R(); + std::copy(_s.begin(), _s.end(), sr); + } + + virtual void getGibbs_RT(doublereal* grt) const { + const array_fp& gibbsrt = gibbs_RT(); + std::copy(gibbsrt.begin(), gibbsrt.end(), grt); + } + + void getCp_R(doublereal* cpr) const { + const array_fp& _cpr = cp_R(); + std::copy(_cpr.begin(), _cpr.end(), cpr); + } + + + // new methods defined here + + const array_fp& enthalpy_RT() const { + _updateThermo(); + return m_h0_RT; + } + + const array_fp& gibbs_RT() const { + _updateThermo(); + return m_g0_RT; + } + + const array_fp& entropy_R() const { + _updateThermo(); + return m_s0_R; + } + + const array_fp& cp_R() const { + _updateThermo(); + return m_cp0_R; + } + + virtual void initThermo(); + + // set the site density of sublattice n + virtual void setParameters(int n, doublereal* c) {} + + virtual void getParameters(int &n, doublereal * const c) { + double d = molarDensity(); + c[0] = d; + n = 1; + } + + virtual void setParametersFromXML(const XML_Node& eosdata); + + + protected: + + int m_mm; + doublereal m_tmin, m_tmax, m_p0; + mutable doublereal m_tlast; + mutable array_fp m_h0_RT; + mutable array_fp m_cp0_R; + mutable array_fp m_g0_RT; + mutable array_fp m_s0_R; + doublereal m_press; + std::string m_vacancy; + doublereal m_molar_density; + + private: + + void _updateThermo() const; + }; +} + +#endif diff --git a/Cantera/src/thermo/LatticeSolidPhase.cpp b/Cantera/src/thermo/LatticeSolidPhase.cpp new file mode 100644 index 000000000..34a59f9d2 --- /dev/null +++ b/Cantera/src/thermo/LatticeSolidPhase.cpp @@ -0,0 +1,235 @@ +/** + * + * @file LatticeSolidPhase.cpp + * + * $Id$ + */ + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "ct_defs.h" +#include "mix_defs.h" +#include "LatticeSolidPhase.h" +#include "LatticePhase.h" +#include "SpeciesThermo.h" +#include "ThermoFactory.h" +//#include "importCTML.h" + +#include +using namespace std; + +namespace Cantera { + + doublereal LatticeSolidPhase:: + enthalpy_mole() const { + _updateThermo(); + doublereal ndens, sum = 0.0; + int n; + for (n = 0; n < m_nlattice; n++) { + ndens = m_lattice[n]->molarDensity(); + sum += ndens * m_lattice[n]->enthalpy_mole(); + } + return sum/molarDensity(); + } + + doublereal LatticeSolidPhase::intEnergy_mole() const { + _updateThermo(); + doublereal ndens, sum = 0.0; + int n; + for (n = 0; n < m_nlattice; n++) { + ndens = m_lattice[n]->molarDensity(); + sum += ndens * m_lattice[n]->intEnergy_mole(); + } + return sum/molarDensity(); + } + + doublereal LatticeSolidPhase::entropy_mole() const { + _updateThermo(); + doublereal ndens, sum = 0.0; + int n; + for (n = 0; n < m_nlattice; n++) { + ndens = m_lattice[n]->molarDensity(); + sum += ndens * m_lattice[n]->entropy_mole(); + } + return sum/molarDensity(); + } + + doublereal LatticeSolidPhase::gibbs_mole() const { + _updateThermo(); + doublereal ndens, sum = 0.0; + int n; + for (n = 0; n < m_nlattice; n++) { + ndens = m_lattice[n]->molarDensity(); + sum += ndens * m_lattice[n]->gibbs_mole(); + } + return sum/molarDensity(); + } + + doublereal LatticeSolidPhase::cp_mole() const { + _updateThermo(); + doublereal ndens, sum = 0.0; + int n; + for (n = 0; n < m_nlattice; n++) { + ndens = m_lattice[n]->molarDensity(); + sum += ndens * m_lattice[n]->cp_mole(); + } + return sum/molarDensity(); + } + + void LatticeSolidPhase::getActivityConcentrations(doublereal* c) const { + _updateThermo(); + int n; + int strt = 0; + for (n = 0; n < m_nlattice; n++) { + m_lattice[n]->getMoleFractions(c+strt); + strt += m_lattice[n]->nSpecies(); + } + } + + void LatticeSolidPhase::getActivityCoefficients(doublereal* ac) const { + for (int k = 0; k < m_kk; k++) { + ac[k] = 1.0; + } + } + + doublereal LatticeSolidPhase::standardConcentration(int k) const { + return 1.0; + } + + doublereal LatticeSolidPhase::logStandardConc(int k) const { + return 0.0; + } + + void LatticeSolidPhase::getChemPotentials(doublereal* mu) const { + _updateThermo(); + int n; + int strt = 0; + double dratio; + for (n = 0; n < m_nlattice; n++) { + dratio = m_lattice[n]->molarDensity()/molarDensity(); + m_lattice[n]->getChemPotentials(mu+strt); + scale(mu + strt, mu + strt + m_lattice[n]->nSpecies(), mu + strt, dratio); + strt += m_lattice[n]->nSpecies(); + } + } + + void LatticeSolidPhase::getStandardChemPotentials(doublereal* mu0) const { + _updateThermo(); + int n; + int strt = 0; + double dratio; + for (n = 0; n < m_nlattice; n++) { + dratio = m_lattice[n]->molarDensity()/molarDensity(); + m_lattice[n]->getStandardChemPotentials(mu0+strt); + scale(mu0 + strt, mu0 + strt + m_lattice[n]->nSpecies(), mu0 + strt, dratio); + strt += m_lattice[n]->nSpecies(); + } + } + + void LatticeSolidPhase::initThermo() { + m_kk = nSpecies(); + m_mm = nElements(); + m_x.resize(m_kk); + int n, nsp, k, loc = 0; + doublereal ndens; + m_molar_density = 0.0; + for (n = 0; n < m_nlattice; n++) { + nsp = m_lattice[n]->nSpecies(); + ndens = m_lattice[n]->molarDensity(); + for (k = 0; k < nsp; k++) { + m_x[loc] = ndens * m_lattice[n]->moleFraction(k); + loc++; + } + m_molar_density += ndens; + } + setMoleFractions(DATA_PTR(m_x)); + +// const vector& spnames = speciesNames(); +// int n, k, kl, namesize; +// int nl = m_sitedens.size(); +// string s; +// m_lattice.resize(m_kk,-1); +// vector_fp conc(m_kk, 0.0); + +// compositionMap xx; +// for (n = 0; n < nl; n++) { +// for (k = 0; k < m_kk; k++) { +// xx[speciesName(k)] = -1.0; +// } +// parseCompString(m_sp[n], xx); +// for (k = 0; k < m_kk; k++) { +// if (xx[speciesName(k)] != -1.0) { +// conc[k] = m_sitedens[n]*xx[speciesName(k)]; +// m_lattice[k] = n; +// } +// } + +// } +// for (k = 0; k < m_kk; k++) { +// if (m_lattice[k] == -1) { +// throw CanteraError("LatticeSolidPhase::" +// "setParametersFromXML","Species "+speciesName(k) +// +" not a member of any lattice."); +// } +// } +// setMoleFractions(DATA_PTR(conc)); + } + + + void LatticeSolidPhase::_updateThermo() const { + doublereal tnow = temperature(); + // if (fabs(molarDensity() - m_molar_density)/m_molar_density > 0.0001) { + // throw CanteraError("_updateThermo","molar density changed from " + // +fp2str(m_molar_density)+" to "+fp2str(molarDensity())); + //} + if (m_tlast != tnow) { + int n; + getMoleFractions(DATA_PTR(m_x)); + int strt = 0; + for (n = 0; n < m_nlattice; n++) { + m_lattice[n]->setTemperature(tnow); + m_lattice[n]->setMoleFractions(DATA_PTR(m_x) + strt); + m_lattice[n]->setPressure(m_press); + strt += m_lattice[n]->nSpecies(); + } + m_tlast = tnow; + } + } + + void LatticeSolidPhase::setLatticeMoleFractions(int nn, + string x) { + m_lattice[nn]->setMoleFractionsByName(x); + int n, k, loc=0, nsp; + doublereal ndens; + for (n = 0; n < m_nlattice; n++) { + nsp = m_lattice[n]->nSpecies(); + ndens = m_lattice[n]->molarDensity(); + for (k = 0; k < nsp; k++) { + m_x[loc] = ndens * m_lattice[n]->moleFraction(k); + loc++; + } + } + setMoleFractions(DATA_PTR(m_x)); + } + + void LatticeSolidPhase::setParametersFromXML(const XML_Node& eosdata) { + eosdata._require("model","LatticeSolid"); + XML_Node& la = eosdata.child("LatticeArray"); + vector lattices; + la.getChildren("phase",lattices); + int n; + int nl = lattices.size(); + m_nlattice = nl; + for (n = 0; n < nl; n++) { + XML_Node& i = *lattices[n]; + m_lattice.push_back((LatticePhase*)newPhase(i)); + } + } +} + + + + diff --git a/Cantera/src/thermo/LatticeSolidPhase.h b/Cantera/src/thermo/LatticeSolidPhase.h new file mode 100644 index 000000000..c06938f15 --- /dev/null +++ b/Cantera/src/thermo/LatticeSolidPhase.h @@ -0,0 +1,92 @@ +/** + * + * @file LatticeSolidPhase.h + */ + +/* $Author$ + * $Date$ + * $Revision$ + * + * Copyright 2005 California Institute of Technology + * + */ + +#ifndef CT_LATTICESOLID_H +#define CT_LATTICESOLID_H + +#include "ct_defs.h" +#include "mix_defs.h" +#include "ThermoPhase.h" +#include "SpeciesThermo.h" +#include "utilities.h" + +namespace Cantera { + + class LatticePhase; + + class LatticeSolidPhase : public ThermoPhase { + + public: + + LatticeSolidPhase() : m_tlast(0.0) {} + + virtual ~LatticeSolidPhase() {} + + virtual int eosType() const { return cLatticeSolid; } + + virtual doublereal enthalpy_mole() const; + + virtual doublereal intEnergy_mole() const; + + virtual doublereal entropy_mole() const; + + virtual doublereal gibbs_mole() const; + + virtual doublereal cp_mole() const; + + virtual doublereal cv_mole() const { + return cp_mole(); + } + + virtual doublereal pressure() const { + return m_press; + } + + virtual void setPressure(doublereal p) { + m_press = p; + setMolarDensity(m_molar_density); + } + + virtual void getActivityConcentrations(doublereal* c) const; + + virtual void getActivityCoefficients(doublereal* ac) const; + + virtual void getChemPotentials(doublereal* mu) const; + virtual void getStandardChemPotentials(doublereal* mu0) const; + virtual doublereal standardConcentration(int k=0) const; + virtual doublereal logStandardConc(int k=0) const; + + virtual void initThermo(); + + virtual void setParametersFromXML(const XML_Node& eosdata); + + void setLatticeMoleFractions(int n, std::string x); + + protected: + + int m_mm; + int m_kk; + mutable doublereal m_tlast; + doublereal m_press; + doublereal m_molar_density; + int m_nlattice; + std::vector m_lattice; + mutable vector_fp m_x; + + private: + + void _updateThermo() const; + }; +} + +#endif diff --git a/Cantera/src/thermo/Makefile.in b/Cantera/src/thermo/Makefile.in index 34c477bf1..b5b3a72ea 100644 --- a/Cantera/src/thermo/Makefile.in +++ b/Cantera/src/thermo/Makefile.in @@ -11,7 +11,7 @@ .SUFFIXES : .SUFFIXES : .cpp .d .o .h -INCDIR = ../../../build/include/cantera/kernel/thermo +INCDIR = ../../../build/include/cantera/kernel INSTALL_TSC = ../../../bin/install_tsc do_ranlib = @DO_RANLIB@ do_electro = @COMPILE_ELECTROLYTES@ @@ -29,6 +29,26 @@ PIC_FLAG=@PIC@ CXX_FLAGS = @CXXFLAGS@ $(LOCAL_DEFS) $(CXX_OPT) $(PIC_FLAG) $(DEBUG_FLAG) +# Basic Cantera Thermodynamics Object Files +THERMO_OBJ = State.o Elements.o Constituents.o Phase.o \ + ThermoPhase.o IdealGasPhase.o ConstDensityThermo.o \ + SpeciesThermoFactory.o ConstCpPoly.o \ + Mu0Poly.o GeneralSpeciesThermo.o SurfPhase.o \ + ThermoFactory.o phasereport.o @phase_object_files@ + +THERMO_H = State.h Elements.h Constituents.h Phase.h mix_defs.h \ + ThermoPhase.h IdealGasPhase.h ConstDensityThermo.h \ + SpeciesThermoFactory.h ThermoFactory.h \ + NasaPoly1.h NasaPoly2.h NasaThermo.h \ + ShomateThermo.h ShomatePoly.h ConstCpPoly.h \ + SimpleThermo.h SpeciesThermoMgr.h \ + SpeciesThermoInterpType.h \ + GeneralSpeciesThermo.h Mu0Poly.h \ + speciesThermoTypes.h SpeciesThermo.h SurfPhase.h \ + EdgePhase.h \ + @phase_header_files@ + + # Extended Cantera Thermodynamics Object Files ifeq ($(do_electro),1) @@ -51,13 +71,13 @@ ISSP_OBJ = IdealSolidSolnPhase.o StoichSubstanceSSTP.o SingleSpeciesTP.o ISSP_H = IdealSolidSolnPhase.h StoichSubstanceSSTP.h SingleSpeciesTP.h endif -CATHERMO_OBJ = $(ELECTRO_OBJ) $(ISSP_OBJ) +CATHERMO_OBJ = $(THERMO_OBJ) $(ELECTRO_OBJ) $(ISSP_OBJ) -CATHERMO_H = $(ELECTRO_H) $(ISSP_H) +CATHERMO_H = $(THERMO_H) $(ELECTRO_H) $(ISSP_H) -CXX_INCLUDES = -I.. @CXX_INCLUDES@ -LIB = @buildlib@/libcaThermo.a +CXX_INCLUDES = -I../base @CXX_INCLUDES@ +LIB = @buildlib@/libthermo.a DEPENDS = $(CATHERMO_OBJ:.o=.d) diff --git a/Cantera/src/thermo/MetalPhase.h b/Cantera/src/thermo/MetalPhase.h new file mode 100644 index 000000000..9e7dd3a43 --- /dev/null +++ b/Cantera/src/thermo/MetalPhase.h @@ -0,0 +1,95 @@ +/** + * + * @file MetalPhase.h + * + */ + +/* $Author$ + * $Date$ + * $Revision$ + * + * Copyright 2003 California Institute of Technology + * + */ + + +#ifndef CT_METALPHASE_H +#define CT_METALPHASE_H + + +#include "mix_defs.h" +#include "ThermoPhase.h" +#include "SpeciesThermo.h" + +namespace Cantera { + + /** + * @ingroup thermoprops + * + * Class MetalPhase represents electrons in a metal. + * + */ + class MetalPhase : public ThermoPhase { + + public: + + MetalPhase() {} + + virtual ~MetalPhase() {} + + // Overloaded methoods of class ThermoPhase + + virtual int eosType() const { return cMetal; } + + virtual doublereal enthalpy_mole() const { return 0.0; } + virtual doublereal intEnergy_mole() const { return 0.0; } + virtual doublereal entropy_mole() const { return 0.0; } + virtual doublereal gibbs_mole() const { return 0.0; } + virtual doublereal cp_mole() const { return 0.0; } + virtual doublereal cv_mole() const { return 0.0; } + + virtual void setPressure(doublereal pres) { m_press = pres; } + virtual doublereal pressure() const { return m_press; } + + virtual void getChemPotentials(doublereal* mu) const { + int n, nsp = nSpecies(); + for (n = 0; n < nsp; n++) mu[n] = 0.0; + } + + virtual void getStandardChemPotentials(doublereal* mu0) const { + int n, nsp = nSpecies(); + for (n = 0; n < nsp; n++) mu0[n] = 0.0; + } + + virtual void getActivityConcentrations(doublereal* c) const { + int n, nsp = nSpecies(); + for (n = 0; n < nsp; n++) c[n] = 1.0; + } + + virtual doublereal standardConcentration(int k=0) const { + return 1.0; + } + + virtual doublereal logStandardConc(int k=0) const { + return 0.0; + } + + virtual void setParametersFromXML(const XML_Node& eosdata) { + eosdata._require("model","Metal"); + doublereal rho = getFloat(eosdata, "density", "-"); + setDensity(rho); + } + + protected: + + private: + doublereal m_press; + }; +} + +#endif + + + + + diff --git a/Cantera/src/thermo/Mu0Poly.cpp b/Cantera/src/thermo/Mu0Poly.cpp new file mode 100644 index 000000000..81cfc3333 --- /dev/null +++ b/Cantera/src/thermo/Mu0Poly.cpp @@ -0,0 +1,414 @@ +/** + * @file Mu0Poly.cpp + * Definitions for a single-species standard state object derived + * from \link Cantera::SpeciesThermoInterpType SpeciesThermoInterpType\endlink based + * on a piecewise constant mu0 interpolation + * (see \ref spthermo and class \link Cantera::Mu0Poly Mu0Poly\endlink). + */ +/* + * $Author$ + * $Revision$ + * $Date$ + */ + + +#include "Mu0Poly.h" +#include "ctexceptions.h" +#include "speciesThermoTypes.h" +#include "SpeciesThermo.h" +#include "xml.h" +#include "ctml.h" + +using namespace std; +using namespace ctml; + +namespace Cantera { + + + Mu0Poly::Mu0Poly() : m_numIntervals(0), + m_H298(0.0), + m_lowT(0.0), + m_highT(0.0), + m_Pref(0.0), + m_index(0) { + } + + /* + * Mu0Poly(): + * + * In the constructor, we calculate and store the + * piecewise linear approximation to the thermodynamic + * functions. + * + * coeffs[0] = number of points (integer) + * 1 = H298(J/kmol) + * 2 = T1 (Kelvin) + * 3 = mu1 (J/kmol) + * 4 = T2 (Kelvin) + * 5 = mu2 (J/kmol) + * 6 = T3 (Kelvin) + * 7 = mu3 (J/kmol) + * ........ + */ + Mu0Poly::Mu0Poly(int n, doublereal tlow, doublereal thigh, + doublereal pref, + const doublereal* coeffs) : + m_numIntervals(0), + m_H298(0.0), + m_lowT (tlow), + m_highT (thigh), + m_Pref (pref), + m_index (n) { + + processCoeffs(coeffs); + } + + + Mu0Poly::Mu0Poly(const Mu0Poly &b) + : m_numIntervals (b.m_numIntervals), + m_H298 (b.m_H298), + m_t0_int (b.m_t0_int), + m_mu0_R_int (b.m_mu0_R_int), + m_h0_R_int (b.m_h0_R_int), + m_s0_R_int (b.m_s0_R_int), + m_cp0_R_int (b.m_cp0_R_int), + m_lowT (b.m_lowT), + m_highT (b.m_highT), + m_Pref (b.m_Pref), + m_index (b.m_index) { + } + + Mu0Poly& Mu0Poly::operator=(const Mu0Poly& b) { + if (&b != this) { + m_numIntervals = b.m_numIntervals; + m_H298 = b.m_H298; + m_t0_int = b.m_t0_int; + m_mu0_R_int = b.m_mu0_R_int; + m_h0_R_int = b.m_h0_R_int; + m_s0_R_int = b.m_s0_R_int; + m_cp0_R_int = b.m_cp0_R_int; + m_lowT = b.m_lowT; + m_highT = b.m_highT; + m_Pref = b.m_Pref; + m_index = b.m_index; + } + return *this; + } + + /** + * Destructor: + */ + Mu0Poly::~Mu0Poly(){ + } + + SpeciesThermoInterpType * + Mu0Poly::duplMyselfAsSpeciesThermoInterpType() const { + Mu0Poly* mp = new Mu0Poly(*this); + return (SpeciesThermoInterpType *) mp; + } + + doublereal Mu0Poly::minTemp() const { return m_lowT;} + doublereal Mu0Poly::maxTemp() const { return m_highT;} + doublereal Mu0Poly::refPressure() const { return m_Pref; } + + /** + * updateProperties is the main workhorse program. + * Given a temperature (*tt), it calculates the thermodynamic + * functions H/RT, S_R, and cp_R, and returns the answer. + * + * Note, it returns an answer by inserting the values into the + * index position, m_index in vectors of H/RT, S_R, and cp_R. + * + * + * Input + * ------- + * *tt = Temperature (Kelvin) + * + */ + void Mu0Poly:: + updateProperties(const doublereal* tt, doublereal* cp_R, + doublereal* h_RT, doublereal* s_R) const { + int j = m_numIntervals; + double T = *tt; + for (int i = 0; i < m_numIntervals; i++) { + double T2 = m_t0_int[i+1]; + if (T <=T2) { + j = i; + break; + } + } + double T1 = m_t0_int[j]; + double cp_Rj = m_cp0_R_int[j]; + + doublereal rt = 1.0/T; + cp_R[m_index] = cp_Rj; + h_RT[m_index] = rt*(m_h0_R_int[j] + (T - T1) * cp_Rj); + s_R[m_index] = m_s0_R_int[j] + cp_Rj * (log(T/T1)); + } + + void Mu0Poly:: + updatePropertiesTemp(const doublereal T, + doublereal* cp_R, + doublereal* h_RT, + doublereal* s_R) const { + updateProperties(&T, cp_R, h_RT, s_R); + } + + /* + * report all of the parameters that make up this + * interpolation. + * + * + */ + void Mu0Poly::reportParameters(int &n, int &type, + doublereal &tlow, doublereal &thigh, + doublereal &pref, + doublereal* const coeffs) const { + n = m_index; + type = MU0_INTERP; + tlow = m_lowT; + thigh = m_highT; + pref = m_Pref; + coeffs[0] = m_numIntervals+1; + coeffs[1] = m_H298 * GasConstant; + int j = 2; + for (int i = 0; i < m_numIntervals+1; i++) { + coeffs[j] = m_t0_int[i]; + coeffs[j+1] = m_mu0_R_int[i] * GasConstant; + j += 2; + } + } + + void Mu0Poly::modifyParameters(doublereal* coeffs) { + processCoeffs(coeffs); + } + + /* + * Install a Mu0 polynomial thermodynamic reference state property + * parameterization for species k into a SpeciesThermo instance, + * getting the information from an XML database. + */ + void installMu0ThermoFromXML(std::string speciesName, + SpeciesThermo& sp, int k, + const XML_Node* Mu0Node_ptr) { + + doublereal tmin, tmax; + bool dimensionlessMu0Values = false; + const XML_Node& Mu0Node = *Mu0Node_ptr; + + tmin = fpValue(Mu0Node["Tmin"]); + tmax = fpValue(Mu0Node["Tmax"]); + doublereal pref = fpValue(Mu0Node["Pref"]); + + doublereal h298 = 0.0; + if (Mu0Node.hasChild("H298")) { + h298 = getFloat(Mu0Node, "H298", "actEnergy"); + } + + int numPoints = 1; + if (Mu0Node.hasChild("numPoints")) { + numPoints = getInteger(Mu0Node, "numPoints"); + } + + vector_fp cValues(numPoints); + const XML_Node *valNode_ptr = + getByTitle(const_cast(Mu0Node), "Mu0Values"); + if (!valNode_ptr) { + throw CanteraError("installMu0ThermoFromXML", + "missing required while processing " + + speciesName); + } + getFloatArray(*valNode_ptr, cValues, true, "actEnergy"); + /* + * Check to see whether the Mu0's were input in a dimensionless + * form. If they were, then the assumed temperature needs to be + * adjusted from the assumed T = 273.15 + */ + string uuu = (*valNode_ptr)["units"]; + if (uuu == "Dimensionless") { + dimensionlessMu0Values = true; + } + int ns = cValues.size(); + if (ns != numPoints) { + throw CanteraError("installMu0ThermoFromXML", + "numPoints inconsistent while processing " + + speciesName); + } + + vector_fp cTemperatures(numPoints); + const XML_Node *tempNode_ptr = + getByTitle(const_cast(Mu0Node), "Mu0Temperatures"); + if (!tempNode_ptr) { + throw CanteraError("installMu0ThermoFromXML", + "missing required while processing + " + + speciesName); + } + getFloatArray(*tempNode_ptr, cTemperatures, false); + ns = cTemperatures.size(); + if (ns != numPoints) { + throw CanteraError("installMu0ThermoFromXML", + "numPoints inconsistent while processing " + + speciesName); + } + + /* + * Fix up dimensionless Mu0 values if input + */ + if (dimensionlessMu0Values) { + for (int i = 0; i < numPoints; i++) { + cValues[i] *= cTemperatures[i] / 273.15; + } + } + + + vector_fp c(2 + 2 * numPoints); + + c[0] = numPoints; + c[1] = h298; + for (int i = 0; i < numPoints; i++) { + c[2+i*2] = cTemperatures[i]; + c[2+i*2+1] = cValues[i]; + } + + sp.install(speciesName, k, MU0_INTERP, &c[0], tmin, tmax, pref); + } + + /* + * Mu0Poly(): + * + * In the constructor, we calculate and store the + * piecewise linear approximation to the thermodynamic + * functions. + * + * coeffs[0] = number of points (integer) + * 1 = H298(J/kmol) + * 2 = T1 (Kelvin) + * 3 = mu1 (J/kmol) + * 4 = T2 (Kelvin) + * 5 = mu2 (J/kmol) + * 6 = T3 (Kelvin) + * 7 = mu3 (J/kmol) + * ........ + */ + void Mu0Poly::processCoeffs(const doublereal* coeffs) { + + int i, iindex; + double T1, T2; + int nPoints = (int) coeffs[0]; + if (nPoints < 2) { + throw CanteraError("Mu0Poly", + "nPoints must be >= 2"); + } + m_numIntervals = nPoints - 1; + m_H298 = coeffs[1] / GasConstant; + int iT298 = 0; + /* + * Resize according to the number of points + */ + m_t0_int.resize(nPoints); + m_h0_R_int.resize(nPoints); + m_s0_R_int.resize(nPoints); + m_cp0_R_int.resize(nPoints); + m_mu0_R_int.resize(nPoints); + /* + * Calculate the T298 interval and make sure that + * the temperatures are strictly monotonic. + * Also distribute the data into the internal arrays. + */ + bool ifound = false; + for (i = 0, iindex = 2; i < nPoints; i++) { + T1 = coeffs[iindex]; + m_t0_int[i] = T1; + m_mu0_R_int[i] = coeffs[iindex+1] / GasConstant; + if (T1 == 298.15) { + iT298 = i; + ifound = true; + } + if (i < nPoints - 1) { + T2 = coeffs[iindex+2]; + if (T2 <= T1) { + throw CanteraError("Mu0Poly", + "Temperatures are not monotonic increasing"); + } + } + iindex += 2; + } + if (!ifound) { + throw CanteraError("Mu0Poly", + "One temperature has to be 298.15"); + } + + /* + * Starting from the interval with T298, we go up + */ + doublereal mu2, s1, s2, h1, h2, cpi, deltaMu, deltaT; + T1 = m_t0_int[iT298]; + doublereal mu1 = m_mu0_R_int[iT298]; + m_h0_R_int[iT298] = m_H298; + m_s0_R_int[iT298] = - (mu1 - m_h0_R_int[iT298]) / T1; + for (i = iT298; i < m_numIntervals; i++) { + T1 = m_t0_int[i]; + s1 = m_s0_R_int[i]; + h1 = m_h0_R_int[i]; + mu1 = m_mu0_R_int[i]; + T2 = m_t0_int[i+1]; + mu2 = m_mu0_R_int[i+1]; + deltaMu = mu2 - mu1; + deltaT = T2 - T1; + cpi = (deltaMu - T1 * s1 + T2 * s1) / (deltaT - T2 * log(T2/T1)); + h2 = h1 + cpi * deltaT; + s2 = s1 + cpi * log(T2/T1); + m_cp0_R_int[i] = cpi; + m_h0_R_int[i+1] = h2; + m_s0_R_int[i+1] = s2; + m_cp0_R_int[i+1] = cpi; + } + + /* + * Starting from the interval with T298, we go down + */ + if (iT298 > 0) { + T2 = m_t0_int[iT298]; + mu2 = m_mu0_R_int[iT298]; + m_h0_R_int[iT298] = m_H298; + m_s0_R_int[iT298] = - (mu2 - m_h0_R_int[iT298]) / T2; + for (i = iT298 - 1; i >= 0; i--) { + T1 = m_t0_int[i]; + mu1 = m_mu0_R_int[i]; + T2 = m_t0_int[i+1]; + mu2 = m_mu0_R_int[i+1]; + s2 = m_s0_R_int[i+1]; + h2 = m_h0_R_int[i+1]; + deltaMu = mu2 - mu1; + deltaT = T2 - T1; + cpi = (deltaMu - T1 * s2 + T2 * s2) / (deltaT - T1 * log(T2/T1)); + h1 = h2 - cpi * deltaT; + s1 = s2 - cpi * log(T2/T1); + m_cp0_R_int[i] = cpi; + m_h0_R_int[i] = h1; + m_s0_R_int[i] = s1; + if (i == (m_numIntervals-1)) { + m_cp0_R_int[i+1] = cpi; + } + } + } +#ifdef DEBUG_HKM_NOT + printf(" Temp mu0(J/kmol) cp0(J/kmol/K) " + " h0(J/kmol) s0(J/kmol/K) \n"); + for (i = 0; i < nPoints; i++) { + printf("%12.3g %12.5g %12.5g %12.5g %12.5g\n", + m_t0_int[i], m_mu0_R_int[i] * GasConstant, + m_cp0_R_int[i]* GasConstant, + m_h0_R_int[i]* GasConstant, + m_s0_R_int[i]* GasConstant); + fflush(stdout); + } +#endif + } + +} + + + + + diff --git a/Cantera/src/thermo/Mu0Poly.h b/Cantera/src/thermo/Mu0Poly.h new file mode 100644 index 000000000..1c6678a50 --- /dev/null +++ b/Cantera/src/thermo/Mu0Poly.h @@ -0,0 +1,307 @@ +/** + * @file Mu0Poly.h + * Header for a single-species standard state object derived + * from \link Cantera::SpeciesThermoInterpType SpeciesThermoInterpType\endlink based + * on a piecewise constant mu0 interpolation + * (see \ref spthermo and class \link Cantera::Mu0Poly Mu0Poly\endlink). + */ + +/* $Author$ + * $Revision$ + * $Date$ + */ + + + +#ifndef CT_MU0POLY_H +#define CT_MU0POLY_H + +#include "SpeciesThermoInterpType.h" + +namespace Cantera { + class SpeciesThermo; + class XML_Node; + + //! The %Mu0Poly class implements an interpolation of the Gibbs free energy based on a + //! piecewise constant heat capacity approximation. + /*! + * The %Mu0Poly class implements a piecewise constant heat capacity approximation. + * of the standard state chemical potential of one + * species at a single reference pressure. + * The chemical potential is input as a series of (\f$T\f$, \f$ \mu^o(T)\f$) + * values. The first temperature is assumed to be equal + * to 298.15 K; however, this may be relaxed in the future. + * This information, and an assumption of a constant + * heat capacity within each interval is enough to + * calculate all thermodynamic functions. + * + * The piece-wise constant heat capacity is calculated from the change in the chemical potential over each interval. + * Once the heat capacity is known, the other thermodynamic functions may be determined. + * The basic equation for going from temperature point 1 to temperature point 2 + * are as follows for \f$ T \f$, \f$ T_1 <= T <= T_2 \f$ + * + * \f[ + * \mu^o(T_1) = h^o(T_1) - T_1 * s^o(T_1) + * \f] + * \f[ + * \mu^o(T_2) - \mu^o(T_1) = Cp^o(T_1)(T_2 - T_1) - Cp^o(T_1)(T_2)ln(\frac{T_2}{T_1}) - s^o(T_1)(T_2 - T_1) + * \f] + * \f[ + * s^o(T_2) = s^o(T_1) + Cp^o(T_1)ln(\frac{T_2}{T_1}) + * \f] + * \f[ + * h^o(T_2) = h^o(T_1) + Cp^o(T_1)(T_2 - T_1) + * \f] + * + * Within each interval the following relations are used. For \f$ T \f$, \f$ T_1 <= T <= T_2 \f$ + * + * \f[ + * \mu^o(T) = \mu^o(T_1) + Cp^o(T_1)(T - T_1) - Cp^o(T_1)(T_2)ln(\frac{T}{T_1}) - s^o(T_1)(T - T_1) + * \f] + * \f[ + * s^o(T) = s^o(T_1) + Cp^o(T_1)ln(\frac{T}{T_1}) + * \f] + * \f[ + * h^o(T) = h^o(T_1) + Cp^o(T_1)(T - T_1) + * \f] + * + * Notes about temperature interpolation for \f$ T < T_1 \f$ and \f$ T > T_{npoints} \f$. + * These are achieved by assuming a constant heat capacity + * equal to the value in the closest temperature interval. + * No error is thrown. + * + * @note In the future, a better assumption about the heat + * capacity may be employed, so that it can be continuous. + * + * @ingroup spthermo + */ + class Mu0Poly: public SpeciesThermoInterpType { + + public: + + //! Constructor + Mu0Poly(); + + //! Constructor used in templated instantiations + /*! + * + * In the constructor, we calculate and store the + * piecewise linear approximation to the thermodynamic + * functions. + * + * @param n Species index + * @param tlow Minimum temperature + * @param thigh Maximum temperature + * @param pref reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state for species n. + * There are \f$ 2+npoints*2 \f$ coefficients, where + * \f$ npoints \f$ are the number of temperature points. + * Their identity is further broken down: + * - coeffs[0] = number of points (integer) + * - coeffs[1] = \f$ h^o(298.15 K) \f$ (J/kmol) + * - coeffs[2] = \f$ T_1 \f$ (Kelvin) + * - coeffs[3] = \f$ \mu^o(T_1) \f$ (J/kmol) + * - coeffs[4] = \f$ T_2 \f$ (Kelvin) + * - coeffs[5] = \f$ \mu^o(T_2) \f$ (J/kmol) + * - coeffs[6] = \f$ T_3 \f$ (Kelvin) + * - coeffs[7] = \f$ \mu^o(T_3) \f$ (J/kmol) + * - ........ + * . + */ + Mu0Poly(int n, doublereal tlow, doublereal thigh, + doublereal pref, const doublereal* coeffs); + + //! Copy constructor + Mu0Poly(const Mu0Poly &); + + //! Assignment operator + Mu0Poly& operator=(const Mu0Poly&); + + //! Destructor + virtual ~Mu0Poly(); + + //! Duplicator + virtual SpeciesThermoInterpType * + duplMyselfAsSpeciesThermoInterpType() const; + + //! Returns the minimum temperature that the thermo + //! parameterization is valid + virtual doublereal minTemp() const; + + //! Returns the maximum temperature that the thermo + //! parameterization is valid + virtual doublereal maxTemp() const; + + //! Returns the reference pressure (Pa) + virtual doublereal refPressure() const; + + //! Returns an integer representing the type of parameterization + virtual int reportType() const { return MU0_INTERP; } + + + //! Update the properties for this species, given a temperature polynomial + /*! + * This method is called with a pointer to an array containing the functions of + * temperature needed by this parameterization, and three pointers to arrays where the + * computed property values should be written. This method updates only one value in + * each array. + * + * Temperature Polynomial: + * + * tPoly[0] = temp (Kelvin) + * + * @param tPoly vector of temperature polynomials. Length = 1 + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void updateProperties(const doublereal* tPoly, + doublereal* cp_R, doublereal* h_RT, + doublereal* s_R) const ; + + //! Compute the reference-state property of one species + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of one of the species. The species index is used + * to reference into the cp_R, h_RT, and s_R arrays. + * + * @param temp Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void updatePropertiesTemp(const doublereal temp, + doublereal* cp_R, + doublereal* h_RT, + doublereal* s_R) const ; + + //!This utility function reports back the type of + //! parameterization and all of the parameters for the + //! species, index. + /*! + * All parameters are output variables + * + * @param n Species index + * @param type Integer type of the standard type + * @param tlow output - Minimum temperature + * @param thigh output - Maximum temperature + * @param pref output - reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void reportParameters(int &n, int &type, + doublereal &tlow, doublereal &thigh, + doublereal &pref, + doublereal* const coeffs) const; + + //! Modify parameters for the standard state + /*! + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void modifyParameters(doublereal* coeffs); + + protected: + + /** + * Number of intervals in the interpolating linear + * approximation. Number of points is one more than the + * number of intervals. + */ + int m_numIntervals; + + /** + * Value of the enthalpy at T = 298.15. + * This value is tied to the Heat of formation of + * the species at 298.15. + */ + doublereal m_H298; + + /** + * Points at which the standard state chemical potential + * are given. + */ + vector_fp m_t0_int; + + /** + * Mu0's are primary input data. They aren't strictly + * needed, but are kept here for convenience. + */ + vector_fp m_mu0_R_int; + + //! Dimensionless Enthalpies at the temperature points + vector_fp m_h0_R_int; + + //! Entropy at the points + vector_fp m_s0_R_int; + + //! Heat capacity at the points + vector_fp m_cp0_R_int; + //! Limiting low temperature + doublereal m_lowT; + //! Limiting high temperature + doublereal m_highT; + + //! Reference pressure + doublereal m_Pref; + + //! Species index + int m_index; + + private: + + //! process the coefficients + /*! + * Mu0Poly(): + * + * In the constructor, we calculate and store the + * piecewise linear approximation to the thermodynamic + * functions. + * + * @param coeffs coefficients. These are defined as follows: + * + * coeffs[0] = number of points (integer) + * 1 = H298(J/kmol) + * 2 = T1 (Kelvin) + * 3 = mu1 (J/kmol) + * 4 = T2 (Kelvin) + * 5 = mu2 (J/kmol) + * 6 = T3 (Kelvin) + * 7 = mu3 (J/kmol) + * ........ + */ + void processCoeffs(const doublereal * coeffs); + + }; + + //! Install a Mu0 polynomial thermodynamic reference state + /*! + * Install a Mu0 polynomial thermodynamic reference state property + * parameterization for species k into a SpeciesThermo instance, + * getting the information from an XML database. + * + * @param speciesName Name of the species + * @param sp Owning SpeciesThermo object + * @param k Species index + * @param Mu0Node_ptr Pointer to the XML element containing the + * Mu0 information. + * + * @ingroup spthermo + */ + void installMu0ThermoFromXML(std::string speciesName, + SpeciesThermo& sp, int k, + const XML_Node* Mu0Node_ptr); +} + +#endif + + diff --git a/Cantera/src/thermo/NasaPoly1.h b/Cantera/src/thermo/NasaPoly1.h new file mode 100755 index 000000000..487616754 --- /dev/null +++ b/Cantera/src/thermo/NasaPoly1.h @@ -0,0 +1,286 @@ + +/** + * @file NasaPoly1.h + * Header for a single-species standard state object derived + * from \link Cantera::SpeciesThermoInterpType SpeciesThermoInterpType\endlink based + * on the NASA temperature polynomial form applied to one temperature region + * (see \ref spthermo and class \link Cantera::NasaPoly1 NasaPoly1\endlink). + * + * This parameterization has one NASA temperature region. + */ + + +#ifndef CT_NASAPOLY1_H +#define CT_NASAPOLY1_H + + +/* $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#include "global.h" +#include "SpeciesThermoInterpType.h" + +namespace Cantera { + + /** + * The NASA polynomial parameterization for one temperature range. + * This parameterization expresses the heat capacity as a + * fourth-order polynomial. Note that this is the form used in the + * 1971 NASA equilibrium program and by the Chemkin software + * package, but differs from the form used in the more recent NASA + * equilibrium program. + * + * Seven coefficients \f$(a_0,\dots,a_6)\f$ are used to represent + * \f$ c_p^0(T)\f$, \f$ h^0(T)\f$, and \f$ s^0(T) \f$ as + * polynomials in \f$ T \f$ : + * \f[ + * \frac{c_p(T)}{R} = a_0 + a_1 T + a_2 T^2 + a_3 T^3 + a_4 T^4 + * \f] + * \f[ + * \frac{h^0(T)}{RT} = a_0 + \frac{a_1}{2} T + \frac{a_2}{3} T^2 + * + \frac{a_3}{4} T^3 + \frac{a_4}{5} T^4 + \frac{a_5}{T}. + * \f] + * \f[ + * \frac{s^0(T)}{R} = a_0\ln T + a_1 T + \frac{a_2}{2} T^2 + + \frac{a_3}{3} T^3 + \frac{a_4}{4} T^4 + a_6. + * \f] + * + * This class is designed specifically for use by class NasaThermo. + * @ingroup spthermo + */ + class NasaPoly1 : public SpeciesThermoInterpType { + + public: + + //! Empty constructor + NasaPoly1() + : m_lowT(0.0), m_highT (0.0), + m_Pref(0.0), m_index (0), m_coeff(array_fp(7)) {} + + + //! constructor used in templated instantiations + /*! + * @param n Species index + * @param tlow Minimum temperature + * @param thigh Maximum temperature + * @param pref reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + NasaPoly1(int n, doublereal tlow, doublereal thigh, doublereal pref, + const doublereal* coeffs) : + m_lowT (tlow), + m_highT (thigh), + m_Pref (pref), + m_index (n), + m_coeff (array_fp(7)) { + std::copy(coeffs, coeffs + 7, m_coeff.begin()); + } + + //! copy constructor + /*! + * @param b object to be copied + */ + NasaPoly1(const NasaPoly1& b) : + m_lowT (b.m_lowT), + m_highT (b.m_highT), + m_Pref (b.m_Pref), + m_index (b.m_index), + m_coeff (array_fp(7)) { + std::copy(b.m_coeff.begin(), + b.m_coeff.begin() + 7, + m_coeff.begin()); + } + + //! assignment operator + /*! + * @param b object to be copied + */ + NasaPoly1& operator=(const NasaPoly1& b) { + if (&b != this) { + m_lowT = b.m_lowT; + m_highT = b.m_highT; + m_Pref = b.m_Pref; + m_index = b.m_index; + std::copy(b.m_coeff.begin(), + b.m_coeff.begin() + 7, + m_coeff.begin()); + } + return *this; + } + + //! Destructor + virtual ~NasaPoly1(){} + + //! duplicator + virtual SpeciesThermoInterpType * + duplMyselfAsSpeciesThermoInterpType() const { + NasaPoly1* np = new NasaPoly1(*this); + return (SpeciesThermoInterpType *) np; + } + + //! Returns the minimum temperature that the thermo + //! parameterization is valid + virtual doublereal minTemp() const { return m_lowT;} + + //! Returns the maximum temperature that the thermo + //! parameterization is valid + virtual doublereal maxTemp() const { return m_highT;} + + //! Returns the reference pressure (Pa) + virtual doublereal refPressure() const { return m_Pref; } + + //! Returns an integer representing the type of parameterization + virtual int reportType() const { return NASA1; } + + + //! Update the properties for this species, given a temperature polynomial + /*! + * This method is called with a pointer to an array containing the functions of + * temperature needed by this parameterization, and three pointers to arrays where the + * computed property values should be written. This method updates only one value in + * each array. + * + * Temperature Polynomial: + * tt[0] = t; + * tt[1] = t*t; + * tt[2] = m_t[1]*t; + * tt[3] = m_t[2]*t; + * tt[4] = 1.0/t; + * tt[5] = std::log(t); + * + * @param tt vector of temperature polynomials + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void updateProperties(const doublereal* tt, + doublereal* cp_R, doublereal* h_RT, doublereal* s_R) const { + + doublereal ct0 = m_coeff[2]; // a0 + doublereal ct1 = m_coeff[3]*tt[0]; // a1 * T + doublereal ct2 = m_coeff[4]*tt[1]; // a2 * T^2 + doublereal ct3 = m_coeff[5]*tt[2]; // a3 * T^3 + doublereal ct4 = m_coeff[6]*tt[3]; // a4 * T^4 + + doublereal cp, h, s; + cp = ct0 + ct1 + ct2 + ct3 + ct4; + h = ct0 + 0.5*ct1 + OneThird*ct2 + 0.25*ct3 + 0.2*ct4 + + m_coeff[0]*tt[4]; // last term is a5/T + s = ct0*tt[5] + ct1 + 0.5*ct2 + OneThird*ct3 + +0.25*ct4 + m_coeff[1]; // last term is a6 + + // return the computed properties in the location in the output + // arrays for this species + cp_R[m_index] = cp; + h_RT[m_index] = h; + s_R[m_index] = s; + //writelog("NASA1: for species "+int2str(m_index)+", h_RT = "+ + // fp2str(h)+"\n"); + } + + + //! Compute the reference-state property of one species + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of one of the species. The species index is used + * to reference into the cp_R, h_RT, and s_R arrays. + * + * @param temp Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void updatePropertiesTemp(const doublereal temp, + doublereal* cp_R, doublereal* h_RT, + doublereal* s_R) const { + double tPoly[6]; + tPoly[0] = temp; + tPoly[1] = temp * temp; + tPoly[2] = tPoly[1] * temp; + tPoly[3] = tPoly[2] * temp; + tPoly[4] = 1.0 / temp; + tPoly[5] = std::log(temp); + updateProperties(tPoly, cp_R, h_RT, s_R); + } + + //!This utility function reports back the type of + //! parameterization and all of the parameters for the + //! species, index. + /*! + * All parameters are output variables + * + * @param n Species index + * @param type Integer type of the standard type + * @param tlow output - Minimum temperature + * @param thigh output - Maximum temperature + * @param pref output - reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void reportParameters(int &n, int &type, + doublereal &tlow, doublereal &thigh, + doublereal &pref, + doublereal* const coeffs) const { + n = m_index; + type = NASA1; + tlow = m_lowT; + thigh = m_highT; + pref = m_Pref; + coeffs[5] = m_coeff[0]; + coeffs[6] = m_coeff[1]; + for (int i = 2; i < 7; i++) { + coeffs[i-2] = m_coeff[i]; + } +#ifdef WARN_ABOUT_CHANGES_FROM_VERSION_1_6 + cout << "************************************************\n" + cout << "Warning: NasaPoly1::reportParameters now returns \n" + << "the coefficient array in the same order as in\n" + << "the input file. See file NasaPoly1.h" << endl; + cout << "************************************************\n" +#endif + } + + //! Modify parameters for the standard state + /*! + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void modifyParameters(doublereal* coeffs) { + m_coeff[0] = coeffs[5]; + m_coeff[1] = coeffs[6]; + for (int i = 0; i < 5; i++) { + m_coeff[i+2] = coeffs[i]; + } + } + + protected: + //! lowest valid temperature + doublereal m_lowT; + //! highest valid temperature + doublereal m_highT; + //! standard-state pressure + doublereal m_Pref; + //! species index + int m_index; + //! array of polynomial coefficients + array_fp m_coeff; + + }; + +} +#endif + diff --git a/Cantera/src/thermo/NasaPoly2.h b/Cantera/src/thermo/NasaPoly2.h new file mode 100644 index 000000000..7c93d656c --- /dev/null +++ b/Cantera/src/thermo/NasaPoly2.h @@ -0,0 +1,288 @@ +/** + * @file NasaPoly2.h + * Header for a single-species standard state object derived + * from \link Cantera::SpeciesThermoInterpType SpeciesThermoInterpType\endlink based + * on the NASA temperature polynomial form applied to two temperature regions + * (see \ref spthermo and class \link Cantera::NasaPoly2 NasaPoly2\endlink). + * + * Two zoned Nasa polynomial parameterization + */ + +/* $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_NASAPOLY2_H +#define CT_NASAPOLY2_H + +#include "SpeciesThermoInterpType.h" + +namespace Cantera { + + /** + * + * + * The NASA polynomial parameterization for two temperature ranges. + * This parameterization expresses the heat capacity as a + * fourth-order polynomial. Note that this is the form used in the + * 1971 NASA equilibrium program and by the Chemkin software + * package, but differs from the form used in the more recent NASA + * equilibrium program. + * + * Seven coefficients \f$(a_0,\dots,a_6)\f$ are used to represent + * \f$ c_p^0(T)\f$, \f$ h^0(T)\f$, and \f$ s^0(T) \f$ as + * polynomials in \f$ T \f$ : + * \f[ + * \frac{c_p(T)}{R} = a_0 + a_1 T + a_2 T^2 + a_3 T^3 + a_4 T^4 + * \f] + * \f[ + * \frac{h^0(T)}{RT} = a_0 + \frac{a_1}{2} T + \frac{a_2}{3} T^2 + * + \frac{a_3}{4} T^3 + \frac{a_4}{5} T^4 + \frac{a_5}{T}. + * \f] + * \f[ + * \frac{s^0(T)}{R} = a_0\ln T + a_1 T + \frac{a_2}{2} T^2 + + \frac{a_3}{3} T^3 + \frac{a_4}{4} T^4 + a_6. + * \f] + * + * This class is designed specifically for use by the class + * GeneralSpeciesThermo. + * + * @ingroup spthermo + */ + class NasaPoly2 : public SpeciesThermoInterpType { + + public: + + //! Empty constructor + NasaPoly2() + : m_lowT(0.0), + m_midT(0.0), + m_highT (0.0), + m_Pref(0.0), + mnp_low(0), + mnp_high(0), + m_index(0), + m_coeff(array_fp(15)) { + } + + //! Full Constructor + /*! + * @param n Species index + * @param tlow output - Minimum temperature + * @param thigh output - Maximum temperature + * @param pref output - reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + NasaPoly2(int n, doublereal tlow, doublereal thigh, doublereal pref, + const doublereal* coeffs) : + m_lowT(tlow), + m_highT(thigh), + m_Pref(pref), + mnp_low(0), + mnp_high(0), + m_index(n), + m_coeff(array_fp(15)) { + + std::copy(coeffs, coeffs + 15, m_coeff.begin()); + m_midT = coeffs[0]; + mnp_low = new NasaPoly1(m_index, m_lowT, m_midT, + m_Pref, &m_coeff[1]); + mnp_high = new NasaPoly1(m_index, m_midT, m_highT, + m_Pref, &m_coeff[8]); + } + + //! Copy Constructor + /*! + * @param b objecto to be copied. + */ + NasaPoly2(const NasaPoly2& b) : + m_lowT(b.m_lowT), + m_midT(b.m_midT), + m_highT(b.m_highT), + m_Pref(b.m_Pref), + mnp_low(0), + mnp_high(0), + m_index(b.m_index), + m_coeff(array_fp(15)) { + + std::copy(b.m_coeff.begin(), + b.m_coeff.begin() + 15, + m_coeff.begin()); + mnp_low = new NasaPoly1(m_index, m_lowT, m_midT, + m_Pref, &m_coeff[1]); + mnp_high = new NasaPoly1(m_index, m_midT, m_highT, + m_Pref, &m_coeff[8]); + } + + //! Assignment operator + /*! + * @param b objecto to be copied. + */ + NasaPoly2& operator=(const NasaPoly2& b) { + if (&b != this) { + m_lowT = b.m_lowT; + m_midT = b.m_midT; + m_highT = b.m_highT; + m_Pref = b.m_Pref; + m_index = b.m_index; + std::copy(b.m_coeff.begin(), + b.m_coeff.begin() + 15, + m_coeff.begin()); + if (mnp_low) delete mnp_low; + if (mnp_high) delete mnp_high; + mnp_low = new NasaPoly1(m_index, m_lowT, m_midT, + m_Pref, &m_coeff[1]); + mnp_high = new NasaPoly1(m_index, m_midT, m_highT, + m_Pref, &m_coeff[8]); + } + return *this; + } + + //! destructor + virtual ~NasaPoly2(){ + delete mnp_low; + delete mnp_high; + } + + //! duplicator + virtual SpeciesThermoInterpType * + duplMyselfAsSpeciesThermoInterpType() const { + NasaPoly2* np = new NasaPoly2(*this); + return (SpeciesThermoInterpType *) np; + } + + //! Returns the minimum temperature that the thermo + //! parameterization is valid + doublereal minTemp() const { return m_lowT;} + + //! Returns the maximum temperature that the thermo + //! parameterization is valid + doublereal maxTemp() const { return m_highT;} + + //! Returns the reference pressure (Pa) + doublereal refPressure() const { return m_Pref; } + + //! Returns an integer representing the type of parameterization + virtual int reportType() const { return NASA2; } + + + //! Update the properties for this species, given a temperature polynomial + /*! + * This method is called with a pointer to an array containing the functions of + * temperature needed by this parameterization, and three pointers to arrays where the + * computed property values should be written. This method updates only one value in + * each array. + * + * Temperature Polynomial: + * tt[0] = t; + * tt[1] = t*t; + * tt[2] = m_t[1]*t; + * tt[3] = m_t[2]*t; + * tt[4] = 1.0/t; + * tt[5] = std::log(t); + * + * @param tt vector of temperature polynomials + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + void updateProperties(const doublereal* tt, + doublereal* cp_R, doublereal* h_RT, doublereal* s_R) const { + + double T = tt[0]; + if (T <= m_midT) { + mnp_low->updateProperties(tt, cp_R, h_RT, s_R); + } else { + mnp_high->updateProperties(tt, cp_R, h_RT, s_R); + } + } + + //! Compute the reference-state property of one species + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of one of the species. The species index is used + * to reference into the cp_R, h_RT, and s_R arrays. + * + * @param temp Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + void updatePropertiesTemp(const doublereal temp, + doublereal* cp_R, + doublereal* h_RT, + doublereal* s_R) const { + if (temp <= m_midT) { + mnp_low->updatePropertiesTemp(temp, cp_R, h_RT, s_R); + } else { + mnp_high->updatePropertiesTemp(temp, cp_R, h_RT, s_R); + } + } + + //!This utility function reports back the type of + //! parameterization and all of the parameters for the + //! species, index. + /*! + * All parameters are output variables + * + * @param n Species index + * @param type Integer type of the standard type + * @param tlow output - Minimum temperature + * @param thigh output - Maximum temperature + * @param pref output - reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + void reportParameters(int &n, int &type, + doublereal &tlow, doublereal &thigh, + doublereal &pref, + doublereal* const coeffs) const { + n = m_index; + type = NASA2; + tlow = m_lowT; + thigh = m_highT; + pref = m_Pref; + for (int i = 0; i < 15; i++) { + coeffs[i] = m_coeff[i]; + } + } + + protected: + //! lowest valid temperature + doublereal m_lowT; + //! Midrange temperature + doublereal m_midT; + //! Highest valid temperatre + doublereal m_highT; + //! Reference state pressure + doublereal m_Pref; + //! pointer to the NasaPoly1 object for the low temperature region. + NasaPoly1 *mnp_low; + //! pointer to the NasaPoly1 object for the high temperature region. + NasaPoly1 *mnp_high; + //! species index + int m_index; + //! array of polynomial coefficients + array_fp m_coeff; + + }; + +} +#endif + + + + diff --git a/Cantera/src/thermo/NasaThermo.h b/Cantera/src/thermo/NasaThermo.h new file mode 100755 index 000000000..1b3b37df8 --- /dev/null +++ b/Cantera/src/thermo/NasaThermo.h @@ -0,0 +1,508 @@ +/** + * @file NasaThermo.h + * Header for the 2 regime 7 coefficient Nasa thermodynamic + * polynomials for multiple species in a phase, derived from the + * \link Cantera::SpeciesThermo SpeciesThermo\endlink base class (see \ref spthermo and + * \link Cantera::NasaThermo NasaThermo\endlink). + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + + +#ifndef CT_NASATHERMO_H +#define CT_NASATHERMO_H +#include + +#include "SpeciesThermoMgr.h" +#include "NasaPoly1.h" +#include "speciesThermoTypes.h" +//#include "polyfit.h" +#include "global.h" + +namespace Cantera { + + /** + * A species thermodynamic property manager for the NASA + * polynomial parameterization with two temperature ranges. + * + * This class is designed to efficiently evaluate the properties + * of a large number of species with the NASA parameterization. + * + * The original NASA polynomial parameterization expressed the + * heat capacity as a fourth-order polynomial in temperature, with + * separate coefficients for each of two temperature ranges. (The + * newer NASA format adds coefficients for 1/T and 1/T^2, and + * allows multiple temperature ranges.) This class is designed for + * use with the original parameterization, which is used, for + * example, by the Chemkin software package. + * + * In many cases, the midpoint temperature is the same for many + * species. To take advantage of this, class NasaThermo groups + * species with a common midpoint temperature, so that checking + * which range the desired temperature is in need be done only + * once for each group. + * + * @note There is a special CTML element for entering the + * coefficients of this parameterization. + * @see importCTML + * + * @ingroup spthermo + */ + class NasaThermo : public SpeciesThermo { + + public: + + //! Initialized to the type of parameterization + /*! + * Note, this value is used in some template functions + */ + const int ID; + + //! constructor + NasaThermo() : + ID(NASA), + m_tlow_max(0.0), + m_thigh_min(1.e30), + m_p0(-1.0), + m_ngroups(0) + { + m_t.resize(6); + } + + //! destructor + virtual ~NasaThermo() {} + + //! install a new species thermodynamic property + //! parameterization for one species. + /*! + * + * @param name Name of the species + * @param index The 'update' method will update the property + * values for this species + * at position i index in the property arrays. + * @param type int flag specifying the type of parameterization to be + * installed. + * @param c vector of coefficients for the parameterization. + * - c[0] midpoint temperature + * - c[1] - c[7] coefficients for low T range + * - c[8] - c[14] coefficients for high T range + * @param minTemp minimum temperature for which this parameterization + * is valid. + * @param maxTemp maximum temperature for which this parameterization + * is valid. + * @param refPressure standard-state pressure for this + * parameterization. + * @see speciesThermoTypes.h + */ + virtual void install(string name, int index, int type, + const doublereal* c, + doublereal minTemp, doublereal maxTemp, + doublereal refPressure) { + + m_name[index] = name; + int imid = int(c[0]); // midpoint temp converted to integer + int igrp = m_index[imid]; // has this value been seen before? + if (igrp == 0) { // if not, prepare new group + vector v; + m_high.push_back(v); + m_low.push_back(v); + m_tmid.push_back(c[0]); + m_index[imid] = igrp = static_cast(m_high.size()); + m_ngroups++; + } + + m_group_map[index] = igrp; + m_posInGroup_map[index] = (int) m_low[igrp-1].size(); + + doublereal tlow = minTemp; + doublereal tmid = c[0]; + doublereal thigh = maxTemp; + const doublereal* clow = c + 1; + + vector_fp chigh(7); + copy(c + 8, c + 15, chigh.begin()); + + m_high[igrp-1].push_back(NasaPoly1(index, tmid, thigh, + refPressure, &chigh[0])); + m_low[igrp-1].push_back(NasaPoly1(index, tlow, tmid, + refPressure, clow)); + + vector_fp clu(7), chu(7); + clu[5] = clow[0]; + clu[6] = clow[1]; + copy(clow+2, clow+7, clu.begin()); + chu[5] = chigh[0]; + chu[6] = chigh[1]; + copy(chigh.begin()+2, chigh.begin()+7, chu.begin()); + + checkContinuity(name, tmid, &clu[0], &chu[0]); + + if (tlow > m_tlow_max) m_tlow_max = tlow; + if (thigh < m_thigh_min) m_thigh_min = thigh; + if ((int) m_tlow.size() < index + 1) { + m_tlow.resize(index + 1, tlow); + m_thigh.resize(index + 1, thigh); + } + m_tlow[index] = tlow; + m_thigh[index] = thigh; + if (m_p0 < 0.0) { + m_p0 = refPressure; + } else if (fabs(m_p0 - refPressure) > 0.1) { + string logmsg = " WARNING NasaThermo: New Species, " + name + ", has a different reference pressure, " + + fp2str(refPressure) + ", than existing reference pressure, " + fp2str(m_p0) + "\n"; + writelog(logmsg); + logmsg = " This may become a fatal error in the future \n"; + writelog(logmsg); + } + m_p0 = refPressure; + } + + //! Like update(), but only updates the single species k. + /*! + * @param k species index + * @param t Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + * + */ + virtual void update_one(int k, doublereal t, doublereal* cp_R, + doublereal* h_RT, doublereal* s_R) const { + + m_t[0] = t; + m_t[1] = t*t; + m_t[2] = m_t[1]*t; + m_t[3] = m_t[2]*t; + m_t[4] = 1.0/t; + m_t[5] = log(t); + + int grp = m_group_map[k]; + int pos = m_posInGroup_map[k]; + const vector &mlg = m_low[grp-1]; + const NasaPoly1 *nlow = &(mlg[pos]); + + doublereal tmid = nlow->maxTemp(); + if (t < tmid) { + nlow->updateProperties(&m_t[0], cp_R, h_RT, s_R); + } else { + const vector &mhg = m_high[grp-1]; + const NasaPoly1 *nhigh = &(mhg[pos]); + nhigh->updateProperties(&m_t[0], cp_R, h_RT, s_R); + } + } + + //! Compute the reference-state properties for all species. + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of each of the standard states. + * + * @param t Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void update(doublereal t, doublereal* cp_R, + doublereal* h_RT, doublereal* s_R) const { + int i; + + // load functions of temperature into m_t vector + m_t[0] = t; + m_t[1] = t*t; + m_t[2] = m_t[1]*t; + m_t[3] = m_t[2]*t; + m_t[4] = 1.0/t; + m_t[5] = log(t); + + // iterate over the groups + vector::const_iterator _begin, _end; + for (i = 0; i != m_ngroups; i++) { + if (t > m_tmid[i]) { + _begin = m_high[i].begin(); + _end = m_high[i].end(); + } + else { + _begin = m_low[i].begin(); + _end = m_low[i].end(); + } + for (; _begin != _end; ++_begin) + _begin->updateProperties(&m_t[0], cp_R, h_RT, s_R); + } + } + + //! Minimum temperature. + /*! + * If no argument is supplied, this + * method returns the minimum temperature for which \e all + * parameterizations are valid. If an integer index k is + * supplied, then the value returned is the minimum + * temperature for species k in the phase. + * + * @param k Species index + */ + virtual doublereal minTemp(int k=-1) const { + if (k < 0) + return m_tlow_max; + else + return m_tlow[k]; + } + + //! Maximum temperature. + /*! + * If no argument is supplied, this + * method returns the maximum temperature for which \e all + * parameterizations are valid. If an integer index k is + * supplied, then the value returned is the maximum + * temperature for parameterization k. + * + * @param k Species index + */ + virtual doublereal maxTemp(int k=-1) const { + if (k < 0) + return m_thigh_min; + else + return m_thigh[k]; + } + + //! The reference-state pressure for species k. + /*! + * + * returns the reference state pressure in Pascals for + * species k. If k is left out of the argument list, + * it returns the reference state pressure for the first + * species. + * Note that some SpeciesThermo implementations, such + * as those for ideal gases, require that all species + * in the same phase have the same reference state pressures. + * + * @param k Species index + */ + virtual doublereal refPressure(int k = -1) const { + return m_p0; + } + + //! This utility function reports the type of parameterization + //! used for the species with index number index. + /*! + * + * @param index Species index + */ + virtual int reportType(int index) const { return NASA; } + + /*! + * This utility function reports back the type of + * parameterization and all of the parameters for the + * species, index. + * + * @param index Species index + * @param type Integer type of the standard type + * @param c Vector of coefficients used to set the + * parameters for the standard state. + * For the NASA object, there are 15 coefficients. + * @param minTemp output - Minimum temperature + * @param maxTemp output - Maximum temperature + * @param refPressure output - reference pressure (Pa). + */ + virtual void reportParams(int index, int &type, + doublereal * const c, + doublereal &minTemp, + doublereal &maxTemp, + doublereal &refPressure) const { + type = reportType(index); + if (type == NASA) { + int grp = m_group_map[index]; + int pos = m_posInGroup_map[index]; + const vector &mlg = m_low[grp-1]; + const vector &mhg = m_high[grp-1]; + const NasaPoly1 *lowPoly = &(mlg[pos]); + const NasaPoly1 *highPoly = &(mhg[pos]); + int itype = NASA; + doublereal tmid = lowPoly->maxTemp(); + c[0] = tmid; + int n; + double ttemp; + lowPoly->reportParameters(n, itype, minTemp, ttemp, refPressure, + c + 1); + if (n != index) { + throw CanteraError(" ", "confused"); + } + if (itype != NASA1) { + throw CanteraError(" ", "confused"); + } + highPoly->reportParameters(n, itype, ttemp, maxTemp, refPressure, + c + 8); + if (n != index) { + throw CanteraError(" ", "confused"); + } + if (itype != NASA1) { + throw CanteraError(" ", "confused"); + } + } else { + throw CanteraError(" ", "confused"); + } + } + + //! Modify parameters for the standard state + /*! + * This utility function modifies the array of coefficients. + * The array is the same as that returned by reportParams, so + * a call can first be made to reportParams to populate the + * array, and then modifyParams can be called to alter + * selected values. For the NASA object, there are 15 + * coefficients. + + * @param index Species index + * @param c Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void modifyParams(int index, doublereal *c) { + int type = reportType(index); + if (type == NASA) { + int grp = m_group_map[index]; + int pos = m_posInGroup_map[index]; + vector &mlg = m_low[grp-1]; + vector &mhg = m_high[grp-1]; + NasaPoly1 *lowPoly = &(mlg[pos]); + NasaPoly1 *highPoly = &(mhg[pos]); + doublereal tmid = lowPoly->maxTemp(); + if (c[0] != tmid) { + throw CanteraError(" ", "Tmid cannot be changed"); + } + lowPoly->modifyParameters(c + 1); + highPoly->modifyParameters(c + 8); + checkContinuity(m_name[index], c[0], c + 1, c + 8); + } else { + throw CanteraError(" ", "confused"); + } + } + + protected: + //! Vector of vector of NasaPoly1's for the high temp region. + /*! + * This is the high temp region representation. + * The first Length is equal to the number of groups. + * The second vector is equal to the number of species + * in that particular group. + */ + vector > m_high; + + //! Vector of vector of NasaPoly1's for the low temp region. + /*! + * This is the low temp region representation. + * The first Length is equal to the number of groups. + * The second vector is equal to the number of species + * in that particular group. + */ + vector > m_low; + + //! Map between the midpoint temperature, as an int, to the group number + /*! + * Length is equal to the number of groups. Only used in the setup. + */ + map m_index; + + //! Vector of log temperature limits + /*! + * Length is equal to the number of groups. + */ + vector_fp m_tmid; + + //! Maximum value of the low temperature limit + doublereal m_tlow_max; + + //! Minimum value of the high temperature limit + doublereal m_thigh_min; + + //! Vector of low temperature limits (species index) + /*! + * Length is equal to number of species + */ + vector_fp m_tlow; + + //! Vector of low temperature limits (species index) + /*! + * Length is equal to number of species + */ + vector_fp m_thigh; + + //! Reference pressure (Pa) + /*! + * all species must have the same reference pressure. + */ + doublereal m_p0; + + //! number of groups + int m_ngroups; + + //! Vector of temperature polynomials + mutable vector_fp m_t; + + /*! + * This map takes as its index, the species index in the phase. + * It returns the group index, where the temperature polynomials + * for that species are stored. group indecises start at 1, + * so a decrement is always performed to access vectors. + */ + mutable map m_group_map; + + /*! + * This map takes as its index, the species index in the phase. + * It returns the position index within the group, where the + * temperature polynomials for that species are storred. + */ + mutable map m_posInGroup_map; + + //! Species name as a function of the species index + mutable map m_name; + + private: + + //! see SpeciesThermoFactory.cpp for the definition + /*! + * @param name string name of species + * @param tmid Mid temperature, between the two temperature regions + * @param clow coefficients for lower temperature region + * @param chigh coefficients for higher temperature region + */ + void checkContinuity(std::string name, double tmid, const doublereal* clow, + doublereal* chigh); + + //! for internal use by checkContinuity + /*! + * @param t temperature + * @param c coefficient array + */ + doublereal enthalpy_RT(double t, const doublereal* c) { + return c[0] + 0.5*c[1]*t + OneThird*c[2]*t*t + + 0.25*c[3]*t*t*t + 0.2*c[4]*t*t*t*t + + c[5]/t; + } + + //! for internal use by checkContinuity + /*! + * @param t temperature + * @param c coefficient array + */ + doublereal entropy_R(double t, const doublereal* c) { + return c[0]*log(t) + c[1]*t + 0.5*c[2]*t*t + + OneThird*c[3]*t*t*t + 0.25*c[4]*t*t*t*t + + c[6]; + } + + }; + +} + +#endif + diff --git a/Cantera/src/thermo/PDSS.cpp b/Cantera/src/thermo/PDSS.cpp index 1ff6dd56e..132a3538e 100644 --- a/Cantera/src/thermo/PDSS.cpp +++ b/Cantera/src/thermo/PDSS.cpp @@ -17,7 +17,8 @@ #include "xml.h" #include "ctml.h" #include "PDSS.h" -#include "importCTML.h" +//#include "importCTML.h" +#include "ThermoFactory.h" #include "SpeciesThermo.h" #include "ThermoPhase.h" diff --git a/Cantera/src/thermo/Phase.cpp b/Cantera/src/thermo/Phase.cpp new file mode 100755 index 000000000..54100a196 --- /dev/null +++ b/Cantera/src/thermo/Phase.cpp @@ -0,0 +1,328 @@ +/** + * @file Phase.cpp + * Definition file for class, Phase, which contains functions for setting the + * state of a phase, and for referencing species by name + * (see \ref phases and class \link Cantera::Phase Phase\endlink). + */ + +// Copyright 2001 California Institute of Technology + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "ct_defs.h" +#include "Phase.h" +#include "vec_functions.h" +#include "ctexceptions.h" + +using namespace std; + +namespace Cantera { + + + /* + * Copy Constructor + * + * This function just does the default initialization, and + * then calls the assignment operator. + */ + Phase::Phase(const Phase &right) : + m_kk(-1), + m_ndim(3), + m_index(-1), + m_xml(new XML_Node("phase")), + m_id(""), + m_name("") + { + /* + * Call the assignment operator. + */ + *this = operator=(right); + } + + /* + * Assignment operator + * + * This operation is sort of complicated. We have to + * call the assignment operator for the Constituents and + * State operators that Phase inherits from. Then, + * we have to copy our own data, making sure to do a + * deep copy on the XML_Node data owned by this object. + */ + const Phase &Phase::operator=(const Phase &right) { + /* + * Check for self assignment. + */ + if (this == &right) return *this; + /* + * Now call the inherited-classes assignment operators. + */ + (void) Constituents::operator=(right); + (void) State::operator=(right); + /* + * Handle its own data + */ + m_kk = right.m_kk; + m_ndim = right.m_ndim; + m_index = right.m_index; + m_data = right.m_data; + /* + * This is a little complicated. -> Because we delete m_xml + * in the destructor, we own m_xml completely, and we need + * to have our own individual copies of the XML data tree + * in each object + */ + m_xml = new XML_Node(*(right.m_xml)); + m_id = right.m_id; + m_name = right.m_name; + + return *this; + } + + + void Phase::saveState(vector_fp& state) const { + state.resize(nSpecies() + 2); + saveState(state.size(),&(state[0])); + } + void Phase::saveState(int lenstate, doublereal* state) const { + state[0] = temperature(); + state[1] = density(); + getMassFractions(state + 2); + } + + void Phase::restoreState(const vector_fp& state) { + restoreState(state.size(),&state[0]); + } + + void Phase::restoreState(int lenstate, const doublereal* state) { + if (int(lenstate) >= nSpecies() + 2) { + setMassFractions_NoNorm(state + 2); + setTemperature(state[0]); + setDensity(state[1]); + } + else { + throw ArraySizeError("Phase::restoreState", + lenstate,nSpecies()+2); + } + } + + void Phase::setMoleFractionsByName(compositionMap& xMap) { + int kk = nSpecies(); + doublereal x; + vector_fp mf(kk, 0.0); + for (int k = 0; k < kk; k++) { + x = xMap[speciesName(k)]; + if (x > 0.0) mf[k] = x; + } + setMoleFractions(&mf[0]); + } + + void Phase::setMoleFractionsByName(const std::string& x) { + compositionMap xx; + int kk = nSpecies(); + for (int k = 0; k < kk; k++) { + xx[speciesName(k)] = -1.0; + } + parseCompString(x, xx); + setMoleFractionsByName(xx); + //int kk = nSpecies(); + //vector_fp mf(kk); + //for (int k = 0; k < kk; k++) { + // mf[k] = xx[speciesName(k)]; + //} + //setMoleFractions(mf.begin()); + } + + void Phase::setMassFractionsByName(compositionMap& yMap) { + int kk = nSpecies(); + doublereal y; + vector_fp mf(kk, 0.0); + for (int k = 0; k < kk; k++) { + y = yMap[speciesName(k)]; + if (y > 0.0) mf[k] = y; + } + setMassFractions(&mf[0]); + } + + void Phase::setMassFractionsByName(const std::string& y) { + compositionMap yy; + int kk = nSpecies(); + for (int k = 0; k < kk; k++) { + yy[speciesName(k)] = -1.0; + } + parseCompString(y, yy); + setMassFractionsByName(yy); + } + + /** Set the temperature (K), density (kg/m^3), and mole fractions. */ + void Phase::setState_TRX(doublereal t, doublereal dens, + const doublereal* x) { + setMoleFractions(x); setTemperature(t); setDensity(dens); + } + + void Phase::setState_TNX(doublereal t, doublereal n, + const doublereal* x) { + setMoleFractions(x); setTemperature(t); setMolarDensity(n); + } + + /** Set the temperature (K), density (kg/m^3), and mole fractions. */ + void Phase::setState_TRX(doublereal t, doublereal dens, + compositionMap& x) { + setMoleFractionsByName(x); setTemperature(t); setDensity(dens); + } + + /** Set the temperature (K), density (kg/m^3), and mass fractions. */ + void Phase::setState_TRY(doublereal t, doublereal dens, + const doublereal* y) { + setMassFractions(y); setTemperature(t); setDensity(dens); + } + + /** Set the temperature (K), density (kg/m^3), and mass fractions. */ + void Phase::setState_TRY(doublereal t, doublereal dens, + compositionMap& y) { + setMassFractionsByName(y); setTemperature(t); setDensity(dens); + } + + /** Set the temperature (K) and density (kg/m^3) */ + void Phase::setState_TR(doublereal t, doublereal rho) { + setTemperature(t); setDensity(rho); + } + + /** Set the temperature (K) and mole fractions. */ + void Phase::setState_TX(doublereal t, doublereal* x) { + setTemperature(t); setMoleFractions(x); + } + + /** Set the temperature (K) and mass fractions. */ + void Phase::setState_TY(doublereal t, doublereal* y) { + setTemperature(t); setMassFractions(y); + } + + /** Set the density (kg/m^3) and mole fractions. */ + void Phase::setState_RX(doublereal rho, doublereal* x) { + setMoleFractions(x); setDensity(rho); + } + + /** Set the density (kg/m^3) and mass fractions. */ + void Phase::setState_RY(doublereal rho, doublereal* y) { + setMassFractions(y); setDensity(rho); + } + + /* + * Copy the vector of molecular weights into vector weights. + */ + void Phase::getMolecularWeights(vector_fp& weights) { + const array_fp& mw = Constituents::molecularWeights(); + if (weights.size() < mw.size()) weights.resize(mw.size()); + copy(mw.begin(), mw.end(), weights.begin()); + } + + /* + * Copy the vector of molecular weights into array weights. + * @deprecated + */ + void Phase::getMolecularWeights(int iwt, doublereal* weights) { + const array_fp& mw = Constituents::molecularWeights(); + copy(mw.begin(), mw.end(), weights); + } + + /* + * Copy the vector of molecular weights into array weights. + */ + void Phase::getMolecularWeights(doublereal* weights) { + const array_fp& mw = Constituents::molecularWeights(); + copy(mw.begin(), mw.end(), weights); + } + + /** + * Return a const reference to the internal vector of + * molecular weights. + */ + const array_fp& Phase::molecularWeights() { + return Constituents::molecularWeights(); + } + + + /** + * Get the mole fractions by name. + */ + void Phase::getMoleFractionsByName(compositionMap& x) { + x.clear(); + int kk = nSpecies(); + for (int k = 0; k < kk; k++) { + x[speciesName(k)] = State::moleFraction(k); + } + } + + doublereal Phase::moleFraction(int k) const { + return State::moleFraction(k); + } + + doublereal Phase::moleFraction(std::string name) const { + int iloc = speciesIndex(name); + if (iloc >= 0) return State::moleFraction(iloc); + else return 0.0; + } + + doublereal Phase::massFraction(int k) const { + return State::massFraction(k); + } + + doublereal Phase::massFraction(std::string name) const { + int iloc = speciesIndex(name); + if (iloc >= 0) return massFractions()[iloc]; + else return 0.0; + } + + doublereal Phase::chargeDensity() const { + int k; + int nsp = nSpecies(); + doublereal cdens = 0.0; + for (k = 0; k < nsp; k++) + cdens += charge(k)*State::moleFraction(k); + cdens *= Faraday; + return cdens; + } + + +// void Phase::update_T(int n) const { +// m_T_updater.update(n); +// } + +// void Phase::update_C(int n) const { +// m_C_updater.update(n); +// } + + /** + * Finished adding species, prepare to use them for calculation + * of mixture properties. + */ + void Phase::freezeSpecies() { + Constituents::freezeSpecies(); + init(Constituents::molecularWeights()); + int kk = nSpecies(); + int nv = kk + 2; + m_data.resize(nv,0.0); + m_data[0] = 300.0; + m_data[1] = 0.001; + m_data[2] = 1.0; + + //setState_TRY(300.0, density(), &m_data[2]); + + m_kk = nSpecies(); + } + + bool Phase::ready() const { + return (m_kk > 0 && Constituents::ready() && State::ready()); + } + +// int Phase::installUpdater_T(Updater* u) { +// return m_T_updater.install(u); +// } + +// int Phase::installUpdater_C(Updater* u) { +// return m_C_updater.install(u); +// } +} diff --git a/Cantera/src/thermo/Phase.h b/Cantera/src/thermo/Phase.h new file mode 100755 index 000000000..94b30fdb2 --- /dev/null +++ b/Cantera/src/thermo/Phase.h @@ -0,0 +1,512 @@ +/** + * @file Phase.h + * Header file for class, Phase, which contains functions for setting the + * state of a phase, and for referencing species by name, and also contains text for the module phases + * (see \ref phases and class \link Cantera::Phase Phase\endlink). + */ +/* + * $Author$ + * $Revision$ + * $Date$ + */ +// Copyright 2001 California Institute of Technology + +#ifndef CT_PHASE_H +#define CT_PHASE_H + +#include "State.h" +#include "Constituents.h" +#include "vec_functions.h" + +#include "ctml.h" +using namespace ctml; + +namespace Cantera { + + + /** + * @defgroup phases Phases of Matter + * + * These classes are used to represent the composition and state of a + * single phase of matter. + * Together these classes form the basis for describing the species and + * element compositions of a phase as well as the stoichiometry + * of each species, and for describing the current state of the + * phase. They do not in themselves contain Thermodynamic equation of + * state information. However, they do comprise all of the necessary + * background functionality to support thermodynamic calculations, and the + * class ThermoPhase inherits from the class Phase (see \ref thermoprops). + * + * Class Elements manages the elements that are part of a + * chemistry specification for a phase. This class may support calculations + * employing Multiple phases. In this case, a single Elements object may + * be shared by more than one Constituents class. Reactions between + * the phases may then be described using stoichiometry base on the + * same Elements class object. + * + * The member functions of class %Elements return information about the elements described + * in a particular instantiation of the class. + * + * Class %Constituents is designed to provide information + * about the elements and species in a phase - names, index + * numbers (location in arrays), atomic or molecular weights, + * etc. No computations are performed by the methods of this + * class. The set of elements must include all those that compose + * the species, but may include additional elements. + * + * %Constituents contains a pointer to the Elements object, and + * it contains wrapper functions for all of the functionality + * of the %Elements object, i.e., atomic weights, number and identity + * of the elements. %Elements may be added to a phase by using + * the function Constituents::addUniqueElement(). The %Elements + * object may be shared amongst different Phases. + * + * %Constituents also contains utilities retrieving the index of + * a species in the phase given its name, Constituents::speciesIndex(). + * + * Class State manages the independent variables of temperature, mass density, + * and species mass/mole fraction that define the thermodynamic + * state. + * + * Class %State stores just enough information about a + * multicomponent solution to specify its intensive thermodynamic + * state. It stores values for the temperature, mass density, and + * an array of species mass fractions. It also stores an array of + * species molecular weights, which are used to convert between + * mole and mass representations of the composition. These are the + * \e only properties of the species that class %State knows about. + * + * Class %State is not usually used directly in application + * programs. Its primary use is as a base class for class + * Phase. Class %State has no virtual methods, and none of its + * methods are meant to be overloaded. However, this is one exception. + * If the phase is incompressible, then the density must be replaced + * by the pressure as the independent variable. In this case, functions + * such as State::setMassFractions() within the class %State must actually now + * calculate the density (at constant T and P) instead of leaving + * it alone as befits an independent variable. Therefore, these types + * of functions are virtual functions and need to be overloaded + * for incompressible phases. Note, for nearly incompressible phases + * (or phases which utilize standard states based on a T and P) this + * change in independent variables may be advantageous as well, + * and these functions in %State need to overload as well so that the + * storred density within State doesn't become out of date. + * + * Class Phase derives from both clases + * Constituents and State. In addition to the methods of those two + * classes, it implements methods that allow referencing a species + * by name. And, it contains a lot of utility functions that will + * set the %State of the phase in its entirety, by first setting + * the composition, then the temperature and then the density. + * An example of this is the function, + * Phase::setState_TRY(doublereal t, doublereal dens, const doublereal* y). + * + * Class Phase contains method for saving and restoring the + * full internal states of each phase. These are called Phase::saveState() + * and Phase::restoreState(). These functions operate on a state + * vector, which is in general of length (2 + nSpecies()). The first + * two entries of the state vector is temperature and density. + * + */ + + + //! Base class for phases of mater + /*! + * Base class for phases of matter. Class Phase derives from both + * Constituents and State. In addition to the methods of those two + * classes, it implements methods that allow referencing a species + * by name. + * + * Class Phase derives from both clases + * Constituents and State. In addition to the methods of those two + * classes, it implements methods that allow referencing a species + * by name. And, it contains a lot of utility functions that will + * set the %State of the phase in its entirety, by first setting + * the composition, then the temperature and then the density. + * An example of this is the function, + * Phase::setState_TRY(doublereal t, doublereal dens, const doublereal* y). + * + * Class Phase contains method for saving and restoring the + * full internal states of each phase. These are called Phase::saveState() + * and Phase::restoreState(). These functions operate on a state + * vector, which is in general of length (2 + nSpecies()). The first + * two entries of the state vector is temperature and density. + * + * + * @todo + * Make the concept of saving state vectors more general, so that + * it can handle other cases where there are additional internal state + * variables, such as the voltage, a potential energy, or a strain field. + * + * @ingroup phases + */ + class Phase : public Constituents, public State { + + public: + + /// Default constructor. + Phase() : m_kk(-1), m_ndim(3), m_index(-1), + m_xml(new XML_Node("phase")), + m_id(""), m_name("") {} + + /// Destructor. + virtual ~Phase(){ + delete m_xml; + m_xml = 0; + } + + /** + * Copy Constructor + * + * @param c Reference to the class to be used in the copy + */ + Phase(const Phase &c); + + /** + * Assignment operator + * + * @param c Reference to the class to be used in the copy + */ + const Phase &operator=(const Phase &c); + + //! Returns a reference to the XML_Node storred for the phase + /*! + * The XML_Node for the phase contains all of the input data used + * to set up the model for the phase, during its initialization. + */ + XML_Node& xml() { return *m_xml; } + + //! Return the string id for the phase + std::string id() const { return m_id; } + + //! Set the string id for the phase + /*! + * @param id String id of the phase + */ + void setID(std::string id) {m_id = id;} + + //! Return the name of the phase + std::string name() const { return m_name; } + + //! Sets the string name for the phase + /*! + * @param nm String name of the phase + */ + void setName(std::string nm) { m_name = nm; } + + //! Returns the index of the phase + int index() const { return m_index; } + + //! Sets the index of the phase + /*! + * @param m Integer index of the phase + */ + void setIndex(int m) { m_index = m; } + + //! Save the current internal state of the phase + /*! + * Write to vector 'state' the current internal state. + * + * @param state output vector. Will be resized to nSpecies() + 2 on return. + */ + void saveState(vector_fp& state) const; + + //! Write to array 'state' the current internal state. + /*! + * @param lenstate length of the state array. Must be >= nSpecies() + 2 + * @param state output vector. Must be of length nSpecies() + 2 or + * greater. + */ + void saveState(int lenstate, doublereal* state) const; + + //!Restore a state saved on a previous call to saveState. + /*! + * @param state State vector containing the previously saved state. + */ + void restoreState(const vector_fp& state); + + //! Restore the state of the phase from a previously saved state vector. + /*! + * @param lenstate Length of the state vector + * @param state Vector of state conditions. + */ + void restoreState(int lenstate, const doublereal* state); + + /** + * Set the species mole fractions by name. + * @param xMap map from species names to mole fraction values. + * Species not listed by name in \c xMap are set to zero. + */ + void setMoleFractionsByName(compositionMap& xMap); + + //! Set the mole fractions of a group of species by name + /*! + * The string x is in the form of a composition map + * Species which are not listed by name in the composition + * map are set to zero. + * + * @param x string x in the form of a composition map + */ + void setMoleFractionsByName(const std::string& x); + + /** + * Set the species mass fractions by name. + * @param yMap map from species names to mass fraction values. + * Species not listed by name in \c yMap are set to zero. + */ + void setMassFractionsByName(compositionMap& yMap); + + + //! Set the species mass fractions by name. + /*! + * Species not listed by name in \c x are set to zero. + * + * @param x String containing a composition map + */ + void setMassFractionsByName(const std::string& x); + + //! Set the internally storred temperature (K), density, and mole fractions. + /*! + * Note, the mole fractions are always set first, before the density + * + * @param t Temperature in kelvin + * @param dens Density (kg/m^3) + * @param x vector of species mole fractions. + * Length is equal to m_kk + */ + void setState_TRX(doublereal t, doublereal dens, const doublereal* x); + + + //! Set the internally storred temperature (K), density, and mole fractions. + /*! + * Note, the mole fractions are always set first, before the density + * + * @param t Temperature in kelvin + * @param dens Density (kg/m^3) + * @param x Composition Map containing the mole fractions. + * Species not included in the map are assumed to have + * a zero mole fraction. + */ + void setState_TRX(doublereal t, doublereal dens, compositionMap& x); + + //! Set the internally storred temperature (K), density, and mass fractions. + /*! + * Note, the mass fractions are always set first, before the density + * + * @param t Temperature in kelvin + * @param dens Density (kg/m^3) + * @param y vector of species mass fractions. + * Length is equal to m_kk + */ + void setState_TRY(doublereal t, doublereal dens, const doublereal* y); + + //! Set the internally storred temperature (K), density, and mass fractions. + /*! + * Note, the mass fractions are always set first, before the density + * + * @param t Temperature in kelvin + * @param dens Density (kg/m^3) + * @param y Composition Map containing the mass fractions. + * Species not included in the map are assumed to have + * a zero mass fraction. + */ + void setState_TRY(doublereal t, doublereal dens, compositionMap& y); + + //! Set the internally storred temperature (K), molar density (kmol/m^3), and mole fractions. + /*! + * Note, the mole fractions are always set first, before the molar density + * + * @param t Temperature in kelvin + * @param n molar density (kmol/m^3) + * @param x vector of species mole fractions. + * Length is equal to m_kk + */ + void setState_TNX(doublereal t, doublereal n, const doublereal* x); + + //! Set the internally storred temperature (K) and density (kg/m^3) + /*! + * @param t Temperature in kelvin + * @param rho Density (kg/m^3) + */ + void setState_TR(doublereal t, doublereal rho); + + //! Set the internally storred temperature (K) and mole fractions. + /*! + * @param t Temperature in kelvin + * @param x vector of species mole fractions. + * Length is equal to m_kk + */ + void setState_TX(doublereal t, doublereal* x); + + //! Set the internally storred temperature (K) and mass fractions. + /*! + * @param t Temperature in kelvin + * @param y vector of species mass fractions. + * Length is equal to m_kk + */ + void setState_TY(doublereal t, doublereal* y); + + //! Set the density (kg/m^3) and mole fractions. + /*! + * @param rho Density (kg/m^3) + * @param x vector of species mole fractions. + * Length is equal to m_kk + */ + void setState_RX(doublereal rho, doublereal* x); + + //! Set the density (kg/m^3) and mass fractions. + /*! + * @param rho Density (kg/m^3) + * @param y vector of species mass fractions. + * Length is equal to m_kk + */ + void setState_RY(doublereal rho, doublereal* y); + + /** + * Copy the vector of molecular weights into vector weights. + * + * @param weights Output vector of molecular weights (kg/kmol) + */ + void getMolecularWeights(vector_fp& weights); + + /** + * Copy the vector of molecular weights into array weights. + * + * @param iwt Unused. + * @param weights Output array of molecular weights (kg/kmol) + * + * @deprecated + */ + void getMolecularWeights(int iwt, doublereal* weights); + + /** + * Copy the vector of molecular weights into array weights. + * + * @param weights Output array of molecular weights (kg/kmol) + */ + void getMolecularWeights(doublereal* weights); + + /** + * Return a const reference to the internal vector of + * molecular weights. + */ + const array_fp& molecularWeights(); + + /** + * Get the mole fractions by name. + * + * @param x Output composition map containing the + * species mole fractions. + */ + void getMoleFractionsByName(compositionMap& x); + + //! Return the mole fraction of a single species + /*! + * @param k String name of the species + * + * @return Mole fraction of the species + */ + doublereal moleFraction(int k) const; + + //! Return the mole fraction of a single species + /*! + * @param name String name of the species + * + * @return Mole fraction of the species + */ + doublereal moleFraction(std::string name) const; + + //! Return the mass fraction of a single species + /*! + * @param k String name of the species + * + * @return Mass Fraction of the species + */ + doublereal massFraction(int k) const; + + //! Return the mass fraction of a single species + /*! + * @param name String name of the species + * + * @return Mass Fraction of the species + */ + doublereal massFraction(std::string name) const; + + /** + * Charge density [C/m^3]. + */ + doublereal chargeDensity() const; + + /// Returns the number of spatial dimensions (1, 2, or 3) + int nDim() {return m_ndim;} + + //! Set the number of spatial dimensions (1, 2, or 3) + /*! + * The number of spatial dimensions is used for vector involving + * directions. + * + * @param ndim Input number of dimensions. + */ + void setNDim(int ndim) {m_ndim = ndim;} + + /** + * Finished adding species, prepare to use them for calculation + * of mixture properties. + */ + virtual void freezeSpecies(); + + virtual bool ready() const; + + + protected: + + /** + * m_kk = Number of species in the phase. @internal m_kk is a + * member of both the State and Constituents classes. + * Therefore, to avoid multiple inheritance problems, we need + * to restate it in here, so that the declarations in the two + * base classes become hidden. + */ + int m_kk; + /** + * m_ndim is the dimensionality of the phase. Volumetric + * phases have dimensionality 3 and surface phases have + * dimensionality 2. + */ + int m_ndim; + /** + * m_index is the index of the phase + * + */ + int m_index; + + private: + + //! This stores the initial state of the system + /*! + * @deprecated + * This doesn't seem to be used much anymore. + */ + vector_fp m_data; + + //! Pointer to the XML node containing the XML info for this phase + XML_Node* m_xml; + + //! ID of the phase. + /*! + * This is the value of the ID attribute of the XML phase node. + */ + std::string m_id; + + //! Name of the phase. + /*! + * Initially, this is the value of the ID attribute of the XML phase node. + */ + std::string m_name; + }; + + //! typedef for the base Phase class + typedef Phase phase_t; +} + +#endif diff --git a/Cantera/src/thermo/PureFluidPhase.cpp b/Cantera/src/thermo/PureFluidPhase.cpp new file mode 100644 index 000000000..17e3897b5 --- /dev/null +++ b/Cantera/src/thermo/PureFluidPhase.cpp @@ -0,0 +1,253 @@ +/** + * @file PureFluidPhase.cpp + * Definitions for a ThermoPhase object for a pure fluid phase consisting of gas, liquid, mixed-gas-liquid + * and supercritical fluid (see \ref thermoprops + * and class \link Cantera::PureFluidPhase PureFluidPhase\endlink). + */ +/* + * $Id$ + */ +#include "xml.h" +#include "PureFluidPhase.h" + +#include "../../../ext/tpx/Sub.h" +#include "../../../ext/tpx/utils.h" + +namespace Cantera { + + PureFluidPhase::~PureFluidPhase() { delete m_sub; } + + void PureFluidPhase:: + initThermo() { + if (m_sub) delete m_sub; + m_sub = tpx::GetSub(m_subflag); + if (m_sub == 0) { + throw CanteraError("PureFluidPhase::initThermo", + "could not create new substance object."); + } + m_mw = m_sub->MolWt(); + m_weight[0] = m_mw; + setMolecularWeight(0,m_mw); + double one = 1.0; + setMoleFractions(&one); + double cp0_R, h0_RT, s0_R, T0, p; + T0 = 298.15; + if (T0 < m_sub->Tcrit()) { + m_sub->Set(tpx::TX, T0, 1.0); + p = 0.01*m_sub->P(); + } + else { + p = 0.001*m_sub->Pcrit(); + } + m_sub->Set(tpx::TP, T0, p); + + m_spthermo->update_one(0, T0, &cp0_R, &h0_RT, &s0_R); + double s_R = s0_R - log(p/refPressure()); + m_sub->setStdState(h0_RT*GasConstant*298.15/m_mw, + s_R*GasConstant/m_mw, T0, p); + if (m_verbose) { + writelog("PureFluidPhase::initThermo: initialized phase " + +id()+"\n"); + } + } + + void PureFluidPhase:: + setParametersFromXML(const XML_Node& eosdata) { + eosdata._require("model","PureFluid"); + m_subflag = atoi(eosdata["fluid_type"].c_str()); + if (m_subflag < 0) + throw CanteraError("PureFluidPhase::setParametersFromXML", + "missing or negative substance flag"); + } + + doublereal PureFluidPhase:: + enthalpy_mole() const { + setTPXState(); + doublereal h = m_sub->h() * m_mw; + check(h); + return h; + } + + doublereal PureFluidPhase:: + intEnergy_mole() const { + setTPXState(); + doublereal u = m_sub->u() * m_mw; + check(u); + return u; + } + + doublereal PureFluidPhase:: + entropy_mole() const { + setTPXState(); + doublereal s = m_sub->s() * m_mw; + check(s); + return s; + } + + doublereal PureFluidPhase:: + gibbs_mole() const { + setTPXState(); + doublereal g = m_sub->g() * m_mw; + check(g); + return g; + } + + doublereal PureFluidPhase:: + cp_mole() const { + setTPXState(); + doublereal cp = m_sub->cp() * m_mw; + check(cp); + return cp; + } + + doublereal PureFluidPhase:: + cv_mole() const { + setTPXState(); + doublereal cv = m_sub->cv() * m_mw; + check(cv); + return cv; + } + + doublereal PureFluidPhase:: + pressure() const { + setTPXState(); + doublereal p = m_sub->P(); + check(p); + return p; + } + + void PureFluidPhase:: + setPressure(doublereal p) { + Set(tpx::TP, temperature(), p); + setDensity(1.0/m_sub->v()); + check(); + } + + void PureFluidPhase::Set(int n, double x, double y) const { + try { + m_sub->Set(n, x, y); + } + catch(tpx::TPX_Error) { + reportTPXError(); + } + } + + void PureFluidPhase::setTPXState() const { + Set(tpx::TV, temperature(), 1.0/density()); + } + + void PureFluidPhase::check(doublereal v) const { + if (m_sub->Error() || v == tpx::Undef) { + throw CanteraError("PureFluidPhase",string(tpx::errorMsg( + m_sub->Error()))); + } + } + + void PureFluidPhase::reportTPXError() const { + string msg = tpx::TPX_Error::ErrorMessage; + string proc = "tpx::"+tpx::TPX_Error::ErrorProcedure; + throw CanteraError(proc,msg); + } + + + doublereal PureFluidPhase::isothermalCompressibility() const { + return m_sub->isothermalCompressibility(); + } + + doublereal PureFluidPhase::thermalExpansionCoeff() const { + return m_sub->thermalExpansionCoeff(); + } + + tpx::Substance& PureFluidPhase::TPX_Substance() { return *m_sub; } + + /// critical temperature + doublereal PureFluidPhase::critTemperature() const { return m_sub->Tcrit(); } + + /// critical pressure + doublereal PureFluidPhase::critPressure() const { return m_sub->Pcrit(); } + + /// critical density + doublereal PureFluidPhase::critDensity() const { return 1.0/m_sub->Vcrit(); } + + + /// saturation temperature + doublereal PureFluidPhase::satTemperature(doublereal p) const { + try { + doublereal ts = m_sub->Tsat(p); + return ts; + } + catch(tpx::TPX_Error) { + reportTPXError(); + return -1.0; + } + } + + void PureFluidPhase::setState_HP(doublereal h, doublereal p, + doublereal tol) { + Set(tpx::HP, h, p); + setState_TR(m_sub->Temp(), 1.0/m_sub->v()); + check(); + } + + void PureFluidPhase::setState_UV(doublereal u, doublereal v, + doublereal tol) { + Set(tpx::UV, u, v); + setState_TR(m_sub->Temp(), 1.0/m_sub->v()); + check(); + } + + void PureFluidPhase::setState_SV(doublereal s, doublereal v, + doublereal tol) { + Set(tpx::SV, s, v); + setState_TR(m_sub->Temp(), 1.0/m_sub->v()); + check(); + } + + void PureFluidPhase::setState_SP(doublereal s, doublereal p, + doublereal tol) { + Set(tpx::SP, s, p); + setState_TR(m_sub->Temp(), 1.0/m_sub->v()); + check(); + } + + /// saturation pressure + doublereal PureFluidPhase::satPressure(doublereal t) const { + doublereal vsv = m_sub->v(); + try { + Set(tpx::TV,t,vsv); + doublereal ps = m_sub->Ps(); + return ps; + } + catch(tpx::TPX_Error) { + reportTPXError(); + return -1.0; + } + } + + doublereal PureFluidPhase::vaporFraction() const { + setTPXState(); + doublereal x = m_sub->x(); + check(x); + return x; + } + + void PureFluidPhase::setState_Tsat(doublereal t, doublereal x) { + setTemperature(t); + setTPXState(); + Set(tpx::TX, t, x); + setDensity(1.0/m_sub->v()); + check(); + } + + void PureFluidPhase::setState_Psat(doublereal p, doublereal x) { + setTPXState(); + Set(tpx::PX, p, x); + setTemperature(m_sub->Temp()); + setDensity(1.0/m_sub->v()); + check(); + } + +} + + + diff --git a/Cantera/src/thermo/PureFluidPhase.h b/Cantera/src/thermo/PureFluidPhase.h new file mode 100644 index 000000000..426e46a6d --- /dev/null +++ b/Cantera/src/thermo/PureFluidPhase.h @@ -0,0 +1,317 @@ +/** + * @file PureFluidPhase.h + * Header for a ThermoPhase object for a pure fluid phase consisting of gas, liquid, mixed-gas-liquid + * and supercrit fluid (see \ref thermoprops + * and class \link Cantera::PureFluidPhase PureFluidPhase\endlink). + * + * + * This object is only available if the WITH_PURE_FLUIDS optional compile + * capability has been turned on in Cantera's makefile system. + * It inherits from ThermoPhase, but is built on top of the tpx package. + */ + +/* $Author$ + * $Date$ + * $Revision$ + * + * Copyright 2003 California Institute of Technology + */ + +#ifndef CT_EOS_TPX_H +#define CT_EOS_TPX_H + +#include "ThermoPhase.h" + +/** + * This object is only available if the WITH_PURE_FLUIDS optional compile + * capability has been turned on in Cantera's makefile system. + */ +#ifdef WITH_PURE_FLUIDS + +#include "mix_defs.h" + +namespace tpx { + class Substance; +} + +namespace Cantera { + + //! This phase object consists of a single component that can be a gas, a liquid, + //! a mixed gas-liquid fluid, or a fluid beyond its critical point + /*! + * The object inherits from ThermoPhase. However, its build on top of the + * tpx package. + * + * + *

Specification of Species Standard State Properties

+ * + * + *

Application within %Kinetics Managers

+ * + * + *

XML Example

+ * + * + *

Instantiation of the Class

+ * + * @ingroup thermoprops + */ + class PureFluidPhase : public ThermoPhase { + + public: + + //! Base Constructor + PureFluidPhase() : ThermoPhase(), m_sub(0), m_subflag(0), + m_mw(-1.0), m_verbose(false) {} + + //! Destructor + virtual ~PureFluidPhase(); + + //! Equation of state type + virtual int eosType() const { return cPureFluid; } + + /// Molar enthalpy. Units: J/kmol. + virtual doublereal enthalpy_mole() const; + + /// Molar internal energy. Units: J/kmol. + virtual doublereal intEnergy_mole() const; + + /// Molar entropy. Units: J/kmol/K. + virtual doublereal entropy_mole() const; + + /// Molar Gibbs function. Units: J/kmol. + virtual doublereal gibbs_mole() const; + + /// Molar heat capacity at constant pressure. Units: J/kmol/K. + virtual doublereal cp_mole() const; + + /// Molar heat capacity at constant volume. Units: J/kmol/K. + virtual doublereal cv_mole() const; + + //! Return the thermodynamic pressure (Pa). + /*! + * This method calculates the current pressure consistent with the + * independent variables, T, rho. + */ + virtual doublereal pressure() const; + + //! sets the thermodynamic pressure (Pa). + /*! + * This method calculates the density that is consistent with the + * desired pressure, given the temperature. + * + * @param p Pressure (Pa) + */ + virtual void setPressure(doublereal p); + + //! Get the species chemical potentials. Units: J/kmol. + /*! + * This function returns a vector of chemical potentials of the + * species in solution at the current temperature, pressure + * and mole fraction of the solution. + * + * @param mu Output vector of species chemical + * potentials. Length: m_kk. Units: J/kmol + */ + virtual void getChemPotentials(doublereal* mu) const { + mu[0] = gibbs_mole(); + } + + //! Returns the isothermal compressibility. Units: 1/Pa. + /*! + * The isothermal compressibility is defined as + * \f[ + * \kappa_T = -\frac{1}{v}\left(\frac{\partial v}{\partial P}\right)_T + * \f] + */ + virtual doublereal isothermalCompressibility() const; + + //! Return the volumetric thermal expansion coefficient. Units: 1/K. + /*! + * The thermal expansion coefficient is defined as + * \f[ + * \beta = \frac{1}{v}\left(\frac{\partial v}{\partial T}\right)_P + * \f] + */ + virtual doublereal thermalExpansionCoeff() const; + + //! Returns a reference to the substance object + tpx::Substance& TPX_Substance(); + + /// critical temperature + virtual doublereal critTemperature() const; + + /// critical pressure + virtual doublereal critPressure() const; + + /// critical density + virtual doublereal critDensity() const; + + /// saturation temperature + /*! + * @param p Pressure (Pa) + */ + virtual doublereal satTemperature(doublereal p) const; + + //! Set the internally storred specific enthalpy (J/kg) and pressure (Pa) of the phase. + /*! + * @param h Specific enthalpy (J/kg) + * @param p Pressure (Pa) + * @param tol Optional parameter setting the tolerance of the + * calculation. + */ + virtual void setState_HP(doublereal h, doublereal p, + doublereal tol = 1.e-8); + + //! Set the specific internal energy (J/kg) and specific volume (m^3/kg). + /*! + * This function fixes the internal state of the phase so that + * the specific internal energy and specific volume have the value of the input parameters. + * + * @param u specific internal energy (J/kg) + * @param v specific volume (m^3/kg). + * @param tol Optional parameter setting the tolerance of the + * calculation. + */ + virtual void setState_UV(doublereal u, doublereal v, + doublereal tol = 1.e-8); + + //! Set the specific entropy (J/kg/K) and specific volume (m^3/kg). + /*! + * This function fixes the internal state of the phase so that + * the specific entropy and specific volume have the value of the input parameters. + * + * @param s specific entropy (J/kg/K) + * @param v specific volume (m^3/kg). + * @param tol Optional parameter setting the tolerance of the + * calculation. + */ + virtual void setState_SV(doublereal s, doublereal v, + doublereal tol = 1.e-8); + + //! Set the specific entropy (J/kg/K) and pressure (Pa). + /*! + * This function fixes the internal state of the phase so that + * the specific entropy and the pressure have the value of the input parameters. + * + * @param s specific entropy (J/kg/K) + * @param p specific pressure (Pa). + * @param tol Optional parameter setting the tolerance of the + * calculation. + */ + virtual void setState_SP(doublereal s, doublereal p, + doublereal tol = 1.e-8); + + + + //! @name Saturation properties. + /*! + * These methods are only implemented by subclasses that + * implement full liquid-vapor equations of state. They may be + * moved out of ThermoPhase at a later date. + */ + //@{ + + //! Return the saturation pressure given the temperatur + /*! + * @param t Temperature (Kelvin) + */ + virtual doublereal satPressure(doublereal t) const; + + //! Return the fraction of vapor at the current conditions + virtual doublereal vaporFraction() const; + + //! Set the state to a saturated system at a particular temperature + /*! + * @param t Temperature (kelvin) + * @param x Fraction of vapor + */ + virtual void setState_Tsat(doublereal t, doublereal x); + + //! Set the state to a saturated system at a particular pressure + /*! + * @param p Pressure (Pa) + * @param x Fraction of vapor + */ + virtual void setState_Psat(doublereal p, doublereal x); + //@} + + //! Initialize the ThermoPhase object after all species have been set up + /*! + * @internal 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 from ThermoPhase::initThermoXML(), + * which is called from importPhase(), + * just prior to returning from function importPhase(). + * + * @see importCTML.cpp + */ + virtual void initThermo(); + + //! Set equation of state parameter values from XML entries. + /*! + * + * This method is called by function importPhase() in + * file importCTML.cpp when processing a phase definition in + * an input file. It should be overloaded in subclasses to set + * any parameters that are specific to that particular phase + * model. Note, this method is called before the phase is + * initialzed with elements and/or species. + * + * @param eosdata An XML_Node object corresponding to + * the "thermo" entry for this phase in the input file. + */ + virtual void setParametersFromXML(const XML_Node& eosdata); + + protected: + + //! Main call to the tpx level to set the state of the system + /*! + * @param n Integer indicating which 2 thermo components are held constant + * @param x Value of the first component + * @param y Value of the second component + */ + void Set(int n, double x, double y) const; + + //! Sets the state using a TPX::TV call + void setTPXState() const; + + //! Carry out a internal check on tpx, it may have thrown an error. + /*! + * @param v Defaults to zero + */ + void check(doublereal v = 0.0) const; + + //! Report errors in the TPX level + void reportTPXError() const; + + private: + + //! Pointer to the underlying tpx object Substance that does the work + mutable tpx::Substance* m_sub; + + //! Int indicating the type of the fluid + /*! + * The tpx package uses an int to indicate what fluid is being sought. + */ + int m_subflag; + + //! Molecular weight of the substance (kg kmol-1) + doublereal m_mw; + + //! flag to turn on some printing. + bool m_verbose; + }; + +} + +#endif +#endif + diff --git a/Cantera/src/thermo/ShomatePoly.h b/Cantera/src/thermo/ShomatePoly.h new file mode 100755 index 000000000..ba89bf578 --- /dev/null +++ b/Cantera/src/thermo/ShomatePoly.h @@ -0,0 +1,594 @@ +/** + * @file ShomatePoly.h + * Header for a single-species standard state object derived + * from \link Cantera::SpeciesThermoInterpType SpeciesThermoInterpType\endlink based + * on the Shomate temperature polynomial form applied to one temperature region + * (see \ref spthermo and class \link Cantera::ShomatePoly ShomatePoly\endlink and + * \link Cantera::ShomatePoly2 ShomatePoly2\endlink). + * Shomate polynomial expressions. + */ +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_SHOMATEPOLY1_H +#define CT_SHOMATEPOLY1_H + +#include "SpeciesThermoInterpType.h" + +namespace Cantera { + + + //! The Shomate polynomial parameterization for one temperature range + //! for one species + /*! + * + * Seven coefficients \f$(A,\dots,G)\f$ are used to represent + * \f$ c_p^0(T)\f$, \f$ h^0(T)\f$, and \f$ s^0(T) \f$ as + * polynomials in the temperature, \f$ T \f$ : + * + * \f[ + * \tilde{c}_p^0(T) = A + B t + C t^2 + D t^3 + \frac{E}{t^2} + * \f] + * \f[ + * \tilde{h}^0(T) = A t + \frac{B t^2}{2} + \frac{C t^3}{3} + + \frac{D t^4}{4} - \frac{E}{t} + F. + * \f] + * \f[ + * \tilde{s}^0(T) = A\ln t + B t + \frac{C t^2}{2} + + \frac{D t^3}{3} - \frac{E}{2t^2} + G. + * \f] + * + * In the above expressions, the thermodynamic polynomials are expressed + * in dimensional units, but the temperature,\f$ t \f$, is divided by 1000. The + * following dimensions are assumed in the above expressions: + * + * - \f$ \tilde{c}_p^0(T)\f$ = Heat Capacity (J/gmol*K) + * - \f$ \tilde{h}^0(T) \f$ = standard Enthalpy (kJ/gmol) + * - \f$ \tilde{s}^0(T) \f$= standard Entropy (J/gmol*K) + * - \f$ t \f$= temperature (K) / 1000. + * + * For more information about Shomate polynomials, see the NIST website, + * http://webbook.nist.gov/ + * + * Before being used within Cantera, the dimensions must be adjusted to those + * used by Cantera (i.e., Joules and kmol). + * + + * @ingroup spthermo + */ + class ShomatePoly : public SpeciesThermoInterpType { + + public: + + //! Empty constructor + ShomatePoly() + : m_lowT(0.0), m_highT (0.0), + m_Pref(0.0), m_index (0) {} + + //! Constructor used in templated instantiations + /*! + * @param n Species index + * @param tlow Minimum temperature + * @param thigh Maximum temperature + * @param pref reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state for species n. + * There are 7 coefficients for the Shomate polynomial: + * - c[0] = \f$ A \f$ + * - c[1] = \f$ B \f$ + * - c[2] = \f$ C \f$ + * - c[3] = \f$ D \f$ + * - c[4] = \f$ E \f$ + * - c[5] = \f$ F \f$ + * - c[6] = \f$ G \f$ + * + * See the class description for the polynomial representation of the + * thermo functions in terms of \f$ A, \dots, G \f$. + */ + ShomatePoly(int n, doublereal tlow, doublereal thigh, doublereal pref, + const doublereal* coeffs) : + m_lowT (tlow), + m_highT (thigh), + m_Pref (pref), + m_index (n) { + m_coeff.resize(7); + std::copy(coeffs, coeffs + 7, m_coeff.begin()); + } + + //! copy constructor + /*! + * @param b object to be copied + */ + ShomatePoly(const ShomatePoly& b) : + m_lowT (b.m_lowT), + m_highT (b.m_highT), + m_Pref (b.m_Pref), + m_coeff (array_fp(7)), + m_index (b.m_index) { + std::copy(b.m_coeff.begin(), + b.m_coeff.begin() + 7, + m_coeff.begin()); + } + + //! Assignment operator + /*! + * @param b + */ + ShomatePoly& operator=(const ShomatePoly& b) { + if (&b != this) { + m_lowT = b.m_lowT; + m_highT = b.m_highT; + m_Pref = b.m_Pref; + m_index = b.m_index; + m_coeff.resize(7); + std::copy(b.m_coeff.begin(), + b.m_coeff.begin() + 7, + m_coeff.begin()); + } + return *this; + } + + //! Destructor + virtual ~ShomatePoly(){} + + //! Duplicator from the base class + virtual SpeciesThermoInterpType * + duplMyselfAsSpeciesThermoInterpType() const { + ShomatePoly* sp = new ShomatePoly(*this); + return (SpeciesThermoInterpType *) sp; + } + + //! Returns the minimum temperature that the thermo + //! parameterization is valid + virtual doublereal minTemp() const { return m_lowT;} + + //! Returns the maximum temperature that the thermo + //! parameterization is valid + virtual doublereal maxTemp() const { return m_highT;} + + //! Returns the reference pressure (Pa) + virtual doublereal refPressure() const { return m_Pref; } + + //! Returns an integer representing the type of parameterization + virtual int reportType() const { return SHOMATE; } + + + //! Update the properties for this species, given a temperature polynomial + /*! + * This method is called with a pointer to an array containing the functions of + * temperature needed by this parameterization, and three pointers to arrays where the + * computed property values should be written. This method updates only one value in + * each array. + * + * tt is T/1000. + * m_t[0] = tt; + * m_t[1] = tt*tt; + * m_t[2] = m_t[1]*tt; + * m_t[3] = 1.0/m_t[1]; + * m_t[4] = log(tt); + * m_t[5] = 1.0/GasConstant; + * m_t[6] = 1.0/(GasConstant * T); + * + * @param tt Vector of temperature polynomials + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void updateProperties(const doublereal* tt, + doublereal* cp_R, doublereal* h_RT, + doublereal* s_R) const { + + doublereal A = m_coeff[0]; + doublereal Bt = m_coeff[1]*tt[0]; + doublereal Ct2 = m_coeff[2]*tt[1]; + doublereal Dt3 = m_coeff[3]*tt[2]; + doublereal Etm2 = m_coeff[4]*tt[3]; + doublereal F = m_coeff[5]; + doublereal G = m_coeff[6]; + + doublereal cp, h, s; + cp = A + Bt + Ct2 + Dt3 + Etm2; + h = tt[0]*(A + 0.5*Bt + OneThird*Ct2 + 0.25*Dt3 - Etm2) + F; + s = A*tt[4] + Bt + 0.5*Ct2 + OneThird*Dt3 - 0.5*Etm2 + G; + + /* + * Shomate polynomials parameterizes assuming units of + * J/(gmol*K) for cp_r and s_R and kJ/(gmol) for h. + * However, Cantera assumes default MKS units of + * J/(kmol*K). This requires us to multiply cp and s + * by 1.e3 and h by 1.e6, before we then nondimensionlize + * the results by dividing by (GasConstant * T), + * where GasConstant has units of J/(kmol * K). + */ + cp_R[m_index] = 1.e3 * cp * tt[5]; + h_RT[m_index] = 1.e6 * h * tt[6]; + s_R[m_index] = 1.e3 * s * tt[5]; + } + + //! Compute the reference-state property of one species + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of one of the species. The species index is used + * to reference into the cp_R, h_RT, and s_R arrays. + * + * @param temp Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void updatePropertiesTemp(const doublereal temp, + doublereal* cp_R, doublereal* h_RT, + doublereal* s_R) const { + double tPoly[7]; + doublereal tt = 1.e-3*temp; + tPoly[0] = tt; + tPoly[1] = tt * tt; + tPoly[2] = tPoly[1] * tt; + tPoly[3] = 1.0/tPoly[1]; + tPoly[4] = std::log(tt); + tPoly[5] = 1.0/GasConstant; + tPoly[6] = 1.0/(GasConstant * temp); + updateProperties(tPoly, cp_R, h_RT, s_R); + } + + //!This utility function reports back the type of + //! parameterization and all of the parameters for the + //! species, index. + /*! + * All parameters are output variables + * + * @param n Species index + * @param type Integer type of the standard type + * @param tlow output - Minimum temperature + * @param thigh output - Maximum temperature + * @param pref output - reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void reportParameters(int &n, int &type, + doublereal &tlow, doublereal &thigh, + doublereal &pref, + doublereal* const coeffs) const { + n = m_index; + type = SHOMATE; + tlow = m_lowT; + thigh = m_highT; + pref = m_Pref; + for (int i = 0; i < 7; i++) { + coeffs[i] = m_coeff[i]; + } + } + + //! Modify parameters for the standard state + /*! + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void modifyParameters(doublereal* coeffs) { + if (m_coeff.size() != 7) { + throw CanteraError("modifyParameters", + "modifying something that hasn't been initialized"); + } + std::copy(coeffs, coeffs + 7, m_coeff.begin()); + } + + protected: + //! Minimum temperature for which the parameterization is valid (Kelvin) + doublereal m_lowT; + //! Maximum temperature for which the parameterization is valid (Kelvin) + doublereal m_highT; + //! Reference pressure (Pa) + doublereal m_Pref; + //! Array of coeffcients + array_fp m_coeff; + //! Species Index + int m_index; + + private: + + }; + + //! The Shomate polynomial parameterization for two temperature ranges + //! for one species + /*! + * + * Seven coefficients \f$(A,\dots,G)\f$ are used to represent + * \f$ c_p^0(T)\f$, \f$ h^0(T)\f$, and \f$ s^0(T) \f$ as + * polynomials in the temperature, \f$ T \f$, in one temperature region: + * + * \f[ + * \tilde{c}_p^0(T) = A + B t + C t^2 + D t^3 + \frac{E}{t^2} + * \f] + * \f[ + * \tilde{h}^0(T) = A t + \frac{B t^2}{2} + \frac{C t^3}{3} + + \frac{D t^4}{4} - \frac{E}{t} + F. + * \f] + * \f[ + * \tilde{s}^0(T) = A\ln t + B t + \frac{C t^2}{2} + + \frac{D t^3}{3} - \frac{E}{2t^2} + G. + * \f] + * + * In the above expressions, the thermodynamic polynomials are expressed + * in dimensional units, but the temperature,\f$ t \f$, is divided by 1000. The + * following dimensions are assumed in the above expressions: + * + * - \f$ \tilde{c}_p^0(T)\f$ = Heat Capacity (J/gmol*K) + * - \f$ \tilde{h}^0(T) \f$ = standard Enthalpy (kJ/gmol) + * - \f$ \tilde{s}^0(T) \f$= standard Entropy (J/gmol*K) + * - \f$ t \f$= temperature (K) / 1000. + * + * For more information about Shomate polynomials, see the NIST website, + * http://webbook.nist.gov/ + * + * Before being used within Cantera, the dimensions must be adjusted to those + * used by Cantera (i.e., Joules and kmol). + * + * This function uses two temperature regions, each with a Shomate polynomial + * representation to represent the thermo functions. There are 15 coefficients, + * therefore, in this representation. The first coefficient is the midrange + * temperature. + * + * + * @ingroup spthermo + */ + class ShomatePoly2 : public SpeciesThermoInterpType { + public: + + //! Empty constructor + ShomatePoly2() + : m_lowT(0.0), + m_midT(0.0), + m_highT (0.0), + m_Pref(0.0), + msp_low(0), + msp_high(0), + m_index(0) { + m_coeff.resize(15); + } + + //! Constructor used in templated instantiations + /*! + * @param n Species index + * @param tlow Minimum temperature + * @param thigh Maximum temperature + * @param pref reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + * There are 15 coefficients for the 2-zone Shomate polynomial. + * The first coefficient is the value of Tmid. The next 7 + * coefficients are the low temperature range Shomate coefficients. + * The last 7 are the high temperature range Shomate coefficients. + */ + ShomatePoly2(int n, doublereal tlow, doublereal thigh, doublereal pref, + const doublereal* coeffs) : + m_lowT (tlow), + m_midT(0.0), + m_highT (thigh), + m_Pref (pref), + msp_low(0), + msp_high(0), + m_index (n) { + m_coeff.resize(15); + std::copy(coeffs, coeffs + 15, m_coeff.begin()); + m_midT = coeffs[0]; + msp_low = new ShomatePoly(n, tlow, m_midT, pref, coeffs+1); + msp_high = new ShomatePoly(n, m_midT, thigh, pref, coeffs+8); + } + + //! Copy constructor + /*! + * @param b object to be copied. + */ + ShomatePoly2(const ShomatePoly2& b) : + m_lowT (b.m_lowT), + m_midT (b.m_midT), + m_highT (b.m_highT), + m_Pref (b.m_Pref), + msp_low(0), + msp_high(0), + m_coeff (array_fp(15)), + m_index (b.m_index) { + std::copy(b.m_coeff.begin(), + b.m_coeff.begin() + 15, + m_coeff.begin()); + msp_low = new ShomatePoly(m_index, m_lowT, m_midT, + m_Pref, &m_coeff[1]); + msp_high = new ShomatePoly(m_index, m_midT, m_highT, + m_Pref, &m_coeff[8]); + } + + //! Assignment operator + /*! + * @param b object to be copied. + */ + ShomatePoly2& operator=(const ShomatePoly2& b) { + if (&b != this) { + m_lowT = b.m_lowT; + m_midT = b.m_midT; + m_highT = b.m_highT; + m_Pref = b.m_Pref; + m_index = b.m_index; + std::copy(b.m_coeff.begin(), + b.m_coeff.begin() + 15, + m_coeff.begin()); + if (msp_low) delete msp_low; + if (msp_high) delete msp_high; + msp_low = new ShomatePoly(m_index, m_lowT, m_midT, + m_Pref, &m_coeff[1]); + msp_high = new ShomatePoly(m_index, m_midT, m_highT, + m_Pref, &m_coeff[8]); + } + return *this; + } + + //! Destructor + virtual ~ShomatePoly2(){ + delete msp_low; + delete msp_high; + } + + + //! duplicator + virtual SpeciesThermoInterpType * + duplMyselfAsSpeciesThermoInterpType() const { + ShomatePoly2* sp = new ShomatePoly2(*this); + return (SpeciesThermoInterpType *) sp; + } + + //! Returns the minimum temperature that the thermo + //! parameterization is valid + virtual doublereal minTemp() const { return m_lowT;} + + //! Returns the maximum temperature that the thermo + //! parameterization is valid + virtual doublereal maxTemp() const { return m_highT;} + + //! Returns the reference pressure (Pa) + virtual doublereal refPressure() const { return m_Pref; } + + //! Returns an integer representing the type of parameterization + virtual int reportType() const { return SHOMATE2; } + + + //! Update the properties for this species, given a temperature polynomial + /*! + * This method is called with a pointer to an array containing the functions of + * temperature needed by this parameterization, and three pointers to arrays where the + * computed property values should be written. This method updates only one value in + * each array. + * + * Temperature Polynomial: + * tt[0] = t; + * tt[1] = t*t; + * tt[2] = m_t[1]*t; + * tt[3] = m_t[2]*t; + * tt[4] = 1.0/t; + * tt[5] = std::log(t); + * + * @param tt vector of temperature polynomials + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void updateProperties(const doublereal* tt, + doublereal* cp_R, doublereal* h_RT, + doublereal* s_R) const { + double T = 1000 * tt[0]; + if (T <= m_midT) { + msp_low->updateProperties(tt, cp_R, h_RT, s_R); + } else { + msp_high->updateProperties(tt, cp_R, h_RT, s_R); + } + + } + + //! Compute the reference-state property of one species + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of one of the species. The species index is used + * to reference into the cp_R, h_RT, and s_R arrays. + * + * @param temp Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void updatePropertiesTemp(const doublereal temp, + doublereal* cp_R, + doublereal* h_RT, + doublereal* s_R) const { + if (temp <= m_midT) { + msp_low->updatePropertiesTemp(temp, cp_R, h_RT, s_R); + } else { + msp_high->updatePropertiesTemp(temp, cp_R, h_RT, s_R); + } + } + + //!This utility function reports back the type of + //! parameterization and all of the parameters for the + //! species, index. + /*! + * All parameters are output variables + * + * @param n Species index + * @param type Integer type of the standard type + * @param tlow output - Minimum temperature + * @param thigh output - Maximum temperature + * @param pref output - reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void reportParameters(int &n, int &type, + doublereal &tlow, doublereal &thigh, + doublereal &pref, + doublereal* const coeffs) const { + n = m_index; + type = SHOMATE2; + tlow = m_lowT; + thigh = m_highT; + pref = m_Pref; + for (int i = 0; i < 15; i++) { + coeffs[i] = m_coeff[i]; + } + } + + //! Modify parameters for the standard state + /*! + * Here, we take the tact that we will just regenerate the + * object. + * + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void modifyParameters(doublereal* coeffs) { + delete msp_low; + delete msp_high; + std::copy(coeffs, coeffs + 15, m_coeff.begin()); + m_midT = coeffs[0]; + msp_low = new ShomatePoly(m_index, m_lowT, m_midT, m_Pref, coeffs+1); + msp_high = new ShomatePoly(m_index, m_midT, m_highT, m_Pref, coeffs+8); + } + + protected: + //! Minimum temperature the representation is valid(kelvin) + doublereal m_lowT; + //! Midrange temperature (kelvin) + doublereal m_midT; + //! Maximum temperature the representation is valid (kelvin) + doublereal m_highT; + //! Reference pressure (Pascal) + doublereal m_Pref; + //! Pointer to the Shomate polynomial for the low temperature region. + ShomatePoly *msp_low; + //! Pointer to the Shomate polynomial for the high temperature region. + ShomatePoly *msp_high; + //! Array of the original coefficients. + array_fp m_coeff; + //! Species index + int m_index; + }; +} + +#endif diff --git a/Cantera/src/thermo/ShomateThermo.h b/Cantera/src/thermo/ShomateThermo.h new file mode 100755 index 000000000..ceaf72f6d --- /dev/null +++ b/Cantera/src/thermo/ShomateThermo.h @@ -0,0 +1,465 @@ +/** + * @file ShomateThermo.h + * Header for the 2 regions Shomate polynomial + * for multiple species in a phase, derived from the + * \link Cantera::SpeciesThermo SpeciesThermo\endlink base class (see \ref spthermo and + * \link Cantera::ShomateThermo ShomateThermo\endlink). + */ +/* + * $Id$ + */ +// Copyright 2001 California Institute of Technology + + +#ifndef CT_SHOMATETHERMO_H +#define CT_SHOMATETHERMO_H + +#include "SpeciesThermoMgr.h" +#include "ShomatePoly.h" +#include "speciesThermoTypes.h" + +namespace Cantera { + + //! A species thermodynamic property manager for the Shomate polynomial parameterization. + /*! + * This is the parameterization used + * in the NIST Chemistry WebBook (http://webbook.nist.gov/chemistry) + * The parameterization assumes there are two temperature regions + * each with its own Shomate polynomial representation, for each + * species in the phase. + * + * \f[ + * \tilde{c}_p^0(T) = A + B t + C t^2 + D t^3 + \frac{E}{t^2} + * \f] + * \f[ + * \tilde{h}^0(T) = A t + \frac{B t^2}{2} + \frac{C t^3}{3} + + \frac{D t^4}{4} - \frac{E}{t} + F. + * \f] + * \f[ + * \tilde{s}^0(T) = A\ln t + B t + \frac{C t^2}{2} + + \frac{D t^3}{3} - \frac{E}{2t^2} + G. + * \f] + * + * In the above expressions, the thermodynamic polynomials are expressed + * in dimensional units, but the temperature,\f$ t \f$, is divided by 1000. The + * following dimensions are assumed in the above expressions: + * + * - \f$ \tilde{c}_p^0(T)\f$ = Heat Capacity (J/gmol*K) + * - \f$ \tilde{h}^0(T) \f$ = standard Enthalpy (kJ/gmol) + * - \f$ \tilde{s}^0(T) \f$= standard Entropy (J/gmol*K) + * - \f$ t \f$= temperature (K) / 1000. + * + * Note, the polynomial data (i.e., A, ... , G) is entered in dimensional + * form. + * + * This is in contrast to the NASA database polynomials which are entered in + * nondimensional form (i.e., NASA parameterizes C_p/R, while Shomate + * parameterizes C_p assuming units of J/gmol*K - and kJ/gmol*K for H). + * Note, also that the H - H_298.15 equation has units of kJ/gmol, because of + * the implicit integration of (t = T 1000), which provides a + * multiplier of 1000 to the Enthalpy equation. + * + * @ingroup spthermo + */ + class ShomateThermo : public SpeciesThermo { + + public: + + //! Initialized to the type of parameterization + /*! + * Note, this value is used in some template functions + */ + const int ID; + + //! constructor + ShomateThermo() : + ID(SHOMATE), + m_tlow_max(0.0), + m_thigh_min(1.e30), + m_p0(-1.0), + m_ngroups(0) + { m_t.resize(7); } + + //! destructor + virtual ~ShomateThermo() {} + + //! Install a new species thermodynamic property + //! parameterization for one species using Shomate polynomials + //! + /*! + * Two temperature regions are assumed. + * + * @param name Name of the species + * @param index Species index + * @param type int flag specifying the type of parameterization to be + * installed. + * @param c Vector of coefficients for the parameterization. + * There are 15 coefficients for the 2-zone Shomate polynomial. + * The first coefficient is the value of Tmid. The next 7 + * coefficients are the low temperature range Shomate coefficients. + * The last 7 are the high temperature range Shomate coefficients. + * + * @param minTemp minimum temperature for which this parameterization + * is valid. + * @param maxTemp maximum temperature for which this parameterization + * is valid. + * @param refPressure standard-state pressure for this + * parameterization. + * + * @see ShomatePoly + * @see ShomatePoly2 + */ + virtual void install(string name, int index, int type, + const doublereal* c, + doublereal minTemp, doublereal maxTemp, + doublereal refPressure) { + int imid = int(c[0]); // midpoint temp converted to integer + int igrp = m_index[imid]; // has this value been seen before? + if (igrp == 0) { // if not, prepare new group + vector v; + m_high.push_back(v); + m_low.push_back(v); + m_tmid.push_back(c[0]); + m_index[imid] = igrp = static_cast(m_high.size()); + m_ngroups++; + } + m_group_map[index] = igrp; + m_posInGroup_map[index] = (int) m_low[igrp-1].size(); + doublereal tlow = minTemp; + doublereal tmid = c[0]; + doublereal thigh = maxTemp; + + const doublereal* clow = c + 1; + const doublereal* chigh = c + 8; + m_high[igrp-1].push_back(ShomatePoly(index, tmid, thigh, + refPressure, chigh)); + m_low[igrp-1].push_back(ShomatePoly(index, tlow, tmid, + refPressure, clow)); + if (tlow > m_tlow_max) m_tlow_max = tlow; + if (thigh < m_thigh_min) m_thigh_min = thigh; + + if ((int) m_tlow.size() < index + 1) { + m_tlow.resize(index + 1, tlow); + m_thigh.resize(index + 1, thigh); + } + m_tlow[index] = tlow; + m_thigh[index] = thigh; + + if (m_p0 < 0.0) { + m_p0 = refPressure; + } else if (fabs(m_p0 - refPressure) > 0.1) { + string logmsg = " WARNING ShomateThermo: New Species, " + name + + ", has a different reference pressure, " + + fp2str(refPressure) + ", than existing reference pressure, " + fp2str(m_p0) + "\n"; + writelog(logmsg); + logmsg = " This may become a fatal error in the future \n"; + writelog(logmsg); + } + m_p0 = refPressure; + + } + + //! Like update(), but only updates the single species k. + /*! + * @param k species index + * @param t Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void update_one(int k, doublereal t, doublereal* cp_R, + doublereal* h_RT, doublereal* s_R) const { + + doublereal tt = 1.e-3*t; + m_t[0] = tt; + m_t[1] = tt*tt; + m_t[2] = m_t[1]*tt; + m_t[3] = 1.0/m_t[1]; + m_t[4] = log(tt); + m_t[5] = 1.0/GasConstant; + m_t[6] = 1.0/(GasConstant * t); + + int grp = m_group_map[k]; + int pos = m_posInGroup_map[k]; + const vector &mlg = m_low[grp-1]; + const ShomatePoly *nlow = &(mlg[pos]); + + doublereal tmid = nlow->maxTemp(); + if (t < tmid) { + nlow->updateProperties(&m_t[0], cp_R, h_RT, s_R); + } else { + const vector &mhg = m_high[grp-1]; + const ShomatePoly *nhigh = &(mhg[pos]); + nhigh->updateProperties(&m_t[0], cp_R, h_RT, s_R); + } + } + + //! Compute the reference-state properties for all species. + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of each of the standard states. + * + * @param t Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void update(doublereal t, doublereal* cp_R, + doublereal* h_RT, doublereal* s_R) const { + int i; + + doublereal tt = 1.e-3*t; + m_t[0] = tt; + m_t[1] = tt*tt; + m_t[2] = m_t[1]*tt; + m_t[3] = 1.0/m_t[1]; + m_t[4] = log(tt); + m_t[5] = 1.0/GasConstant; + m_t[6] = 1.0/(GasConstant * t); + + vector::const_iterator _begin, _end; + for (i = 0; i != m_ngroups; i++) { + if (t > m_tmid[i]) { + _begin = m_high[i].begin(); + _end = m_high[i].end(); + } + else { + _begin = m_low[i].begin(); + _end = m_low[i].end(); + } + for (; _begin != _end; ++_begin) { + _begin->updateProperties(&m_t[0], cp_R, h_RT, s_R); + } + } + } + + //! Minimum temperature. + /*! + * If no argument is supplied, this + * method returns the minimum temperature for which \e all + * parameterizations are valid. If an integer index k is + * supplied, then the value returned is the minimum + * temperature for species k in the phase. + * + * @param k Species index + */ + virtual doublereal minTemp(int k=-1) const { + if (k < 0) + return m_tlow_max; + else + return m_tlow[k]; + } + + //! Maximum temperature. + /*! + * If no argument is supplied, this + * method returns the maximum temperature for which \e all + * parameterizations are valid. If an integer index k is + * supplied, then the value returned is the maximum + * temperature for parameterization k. + * + * @param k species index + */ + virtual doublereal maxTemp(int k=-1) const { + if (k < 0) + return m_thigh_min; + else + return m_thigh[k]; + } + + //! The reference-state pressure for species k. + /*! + * + * returns the reference state pressure in Pascals for + * species k. If k is left out of the argument list, + * it returns the reference state pressure for the first + * species. + * Note that some SpeciesThermo implementations, such + * as those for ideal gases, require that all species + * in the same phase have the same reference state pressures. + * + * @param k species index + */ + virtual doublereal refPressure(int k=-1) const { + return m_p0; + } + + //! This utility function reports the type of parameterization + //! used for the species with index number index. + /*! + * + * @param index Species index + */ + virtual int reportType(int index) const { return SHOMATE; } + + /*! + * This utility function reports back the type of + * parameterization and all of the parameters for the + * species, index. + * + * @param index Species index + * @param type Integer type of the standard type + * @param c Vector of coefficients used to set the + * parameters for the standard state. + * + * @param minTemp output - Minimum temperature + * @param maxTemp output - Maximum temperature + * @param refPressure output - reference pressure (Pa). + */ + virtual void reportParams(int index, int &type, + doublereal * const c, + doublereal &minTemp, + doublereal &maxTemp, + doublereal &refPressure) const { + type = reportType(index); + if (type == SHOMATE) { + int grp = m_group_map[index]; + int pos = m_posInGroup_map[index]; + int itype = SHOMATE; + const vector &mlg = m_low[grp-1]; + const vector &mhg = m_high[grp-1]; + const ShomatePoly *lowPoly = &(mlg[pos]); + const ShomatePoly *highPoly = &(mhg[pos]); + doublereal tmid = lowPoly->maxTemp(); + c[0] = tmid; + int n; + double ttemp; + lowPoly->reportParameters(n, itype, minTemp, ttemp, refPressure, + c + 1); + if (n != index) { + throw CanteraError(" ", "confused"); + } + if (itype != SHOMATE && itype != SHOMATE1) { + throw CanteraError(" ", "confused"); + } + highPoly->reportParameters(n, itype, ttemp, maxTemp, + refPressure, c + 8); + if (n != index) { + throw CanteraError(" ", "confused"); + } + if (itype != SHOMATE && itype != SHOMATE1) { + throw CanteraError(" ", "confused"); + } + } else { + throw CanteraError(" ", "confused"); + } + } + + //! Modify parameters for the standard state + /*! + * @param index Species index + * @param c Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void modifyParams(int index, doublereal *c) { + int type = reportType(index); + if (type == SHOMATE) { + int grp = m_group_map[index]; + int pos = m_posInGroup_map[index]; + vector &mlg = m_low[grp-1]; + vector &mhg = m_high[grp-1]; + ShomatePoly *lowPoly = &(mlg[pos]); + ShomatePoly *highPoly = &(mhg[pos]); + doublereal tmid = lowPoly->maxTemp(); + if (fabs(c[0] - tmid) > 0.001) { + throw CanteraError("modifyParams", "can't change mid temp"); + } + + lowPoly->modifyParameters(c + 1); + + highPoly->modifyParameters(c + 8); + + } else { + throw CanteraError(" ", "confused"); + } + } + + protected: + + //! Vector of vector of NasaPoly1's for the high temp region. + /*! + * This is the high temp region representation. + * The first Length is equal to the number of groups. + * The second vector is equal to the number of species + * in that particular group. + */ + vector > m_high; + + //! Vector of vector of NasaPoly1's for the low temp region. + /*! + * This is the low temp region representation. + * The first Length is equal to the number of groups. + * The second vector is equal to the number of species + * in that particular group. + */ + vector > m_low; + + //! Map between the midpoint temperature, as an int, to the group number + /*! + * Length is equal to the number of groups. Only used in the setup. + */ + map m_index; + + //! Vector of log temperature limits + /*! + * Length is equal to the number of groups. + */ + vector_fp m_tmid; + + //! Maximum value of the low temperature limit + doublereal m_tlow_max; + + //! Minimum value of the high temperature limit + doublereal m_thigh_min; + + //! Vector of low temperature limits (species index) + /*! + * Length is equal to number of species + */ + vector_fp m_tlow; + + //! Vector of low temperature limits (species index) + /*! + * Length is equal to number of species + */ + vector_fp m_thigh; + + //! Reference pressure (Pa) + /*! + * all species must have the same reference pressure. + */ + doublereal m_p0; + + //! number of groups + int m_ngroups; + + //! Vector of temperature polynomials + mutable vector_fp m_t; + + /*! + * This map takes as its index, the species index in the phase. + * It returns the group index, where the temperature polynomials + * for that species are stored. group indecises start at 1, + * so a decrement is always performed to access vectors. + */ + mutable map m_group_map; + + /*! + * This map takes as its index, the species index in the phase. + * It returns the position index within the group, where the + * temperature polynomials for that species are storred. + */ + mutable map m_posInGroup_map; + }; + +} + +#endif diff --git a/Cantera/src/thermo/SimpleThermo.h b/Cantera/src/thermo/SimpleThermo.h new file mode 100644 index 000000000..0ede23ad9 --- /dev/null +++ b/Cantera/src/thermo/SimpleThermo.h @@ -0,0 +1,379 @@ +/** + * @file SimpleThermo.h + * Header for the SimpleThermo (constant heat capacity) species reference-state model + * for multiple species in a phase, derived from the + * \link Cantera::SpeciesThermo SpeciesThermo\endlink base class (see \ref spthermo and + * \link Cantera::SimpleThermo SimpleThermo\endlink). + */ +/* + * $Id$ + */ + +#ifndef CT_SIMPLETHERMO_H +#define CT_SIMPLETHERMO_H + +#include "SpeciesThermoMgr.h" + +namespace Cantera { + + /*! + * A constant-heat capacity species thermodynamic property manager class. + * This makes the + * assumption that the heat capacity is a constant. Then, the following + * relations are used to complete the specification of the thermodynamic + * functions for each species in the phase. + * + * \f[ + * \frac{c_p(T)}{R} = Cp0\_R + * \f] + * \f[ + * \frac{h^0(T)}{RT} = \frac{1}{T} * (h0\_R + (T - T_0) * Cp0\_R) + * \f] + * \f[ + * \frac{s^0(T)}{R} = (s0\_R + (log(T) - log(T_0)) * Cp0\_R) + * \f] + * + * This parameterization takes 4 input values. These are: + * - c[0] = \f$ T_0 \f$(Kelvin) + * - c[1] = \f$ H_k^o(T_0, p_{ref}) \f$ (J/kmol) + * - c[2] = \f$ S_k^o(T_0, p_{ref}) \f$ (J/kmol K) + * - c[3] = \f$ {Cp}_k^o(T_0, p_{ref}) \f$ (J(kmol K) + * + * All species must have the same reference pressure. + * The single-species standard-state property Manager ConstCpPoly has the same + * parameterization as the SimpleThermo class does. + * + * @see ConstCpPoly + * + * @ingroup spthermo + */ + class SimpleThermo : public SpeciesThermo { + + public: + + //! Initialized to the type of parameterization + /*! + * Note, this value is used in some template functions. For this object the + * value is SIMPLE. + */ + const int ID; + + //! Constructor + SimpleThermo() : + ID(SIMPLE), + m_tlow_max(0.0), + m_thigh_min(1.e30), + m_p0(-1.0), + m_nspData(0) {} + + //! Destructor + virtual ~SimpleThermo() {} + + //! Install a new species thermodynamic property + //! parameterization for one species. + /*! + * + * @param name String name of the species + * @param index Species index, k + * @param type int flag specifying the type of parameterization to be + * installed. + * @param c Vector of coefficients for the parameterization. + * There are 4 coefficients. The values (and units) are the following + * - c[0] = \f$ T_0 \f$(Kelvin) + * - c[1] = \f$ H_k^o(T_0, p_{ref}) \f$ (J/kmol) + * - c[2] = \f$ S_k^o(T_0, p_{ref}) \f$ (J/kmol K) + * - c[3] = \f$ {Cp}_k^o(T_0, p_{ref}) \f$ (J(kmol K) + * + * @param minTemp minimum temperature for which this parameterization + * is valid. + * @param maxTemp maximum temperature for which this parameterization + * is valid. + * @param refPressure standard-state pressure for this + * parameterization. + * + * @see ConstCpPoly + */ + virtual void install(string name, int index, int type, + const doublereal* c, + doublereal minTemp, doublereal maxTemp, doublereal refPressure) { + //writelog("installing const_cp for species "+name+"\n"); + m_logt0.push_back(log(c[0])); + m_t0.push_back(c[0]); + m_h0_R.push_back(c[1]/GasConstant); + m_s0_R.push_back(c[2]/GasConstant); + m_cp0_R.push_back(c[3]/GasConstant); + m_index.push_back(index); + m_loc[index] = m_nspData; + m_nspData++; + doublereal tlow = minTemp; + doublereal thigh = maxTemp; + + if (tlow > m_tlow_max) m_tlow_max = tlow; + if (thigh < m_thigh_min) m_thigh_min = thigh; + + if ((int) m_tlow.size() < index + 1) { + m_tlow.resize(index + 1, tlow); + m_thigh.resize(index + 1, thigh); + } + m_tlow[index] = tlow; + m_thigh[index] = thigh; + + if (m_p0 < 0.0) { + m_p0 = refPressure; + } else if (fabs(m_p0 - refPressure) > 0.1) { + string logmsg = " WARNING SimpleThermo: New Species, " + name + + ", has a different reference pressure, " + + fp2str(refPressure) + ", than existing reference pressure, " + fp2str(m_p0) + "\n"; + writelog(logmsg); + logmsg = " This may become a fatal error in the future \n"; + writelog(logmsg); + } + m_p0 = refPressure; + } + + //! Compute the reference-state properties for all species. + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of each of the standard states. + * + * @param t Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void update(doublereal t, doublereal* cp_R, + doublereal* h_RT, doublereal* s_R) const { + int k, ki; + doublereal logt = log(t); + doublereal rt = 1.0/t; + for (k = 0; k < m_nspData; k++) { + ki = m_index[k]; + cp_R[ki] = m_cp0_R[k]; + h_RT[ki] = rt*(m_h0_R[k] + (t - m_t0[k]) * m_cp0_R[k]); + s_R[ki] = m_s0_R[k] + m_cp0_R[k] * (logt - m_logt0[k]); + } + } + + //! Like update(), but only updates the single species k. + /*! + * @param k species index + * @param t Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void update_one(int k, doublereal t, doublereal* cp_R, + doublereal* h_RT, doublereal* s_R) const { + doublereal logt = log(t); + doublereal rt = 1.0/t; + int loc = m_loc[k]; + cp_R[k] = m_cp0_R[loc]; + h_RT[k] = rt*(m_h0_R[loc] + (t - m_t0[loc]) * m_cp0_R[loc]); + s_R[k] = m_s0_R[loc] + m_cp0_R[loc] * (logt - m_logt0[loc]); + } + + //! Minimum temperature. + /*! + * If no argument is supplied, this + * method returns the minimum temperature for which \e all + * parameterizations are valid. If an integer index k is + * supplied, then the value returned is the minimum + * temperature for species k in the phase. + * + * @param k Species index + */ + virtual doublereal minTemp(int k=-1) const { + if (k < 0) + return m_tlow_max; + else + return m_tlow[m_loc[k]]; + } + + //! Maximum temperature. + /*! + * If no argument is supplied, this + * method returns the maximum temperature for which \e all + * parameterizations are valid. If an integer index k is + * supplied, then the value returned is the maximum + * temperature for parameterization k. + * + * @param k Species Index + */ + virtual doublereal maxTemp(int k=-1) const { + if (k < 0) + return m_thigh_min; + else + return m_thigh[m_loc[k]]; + } + + //! The reference-state pressure for species k. + /*! + * + * returns the reference state pressure in Pascals for + * species k. If k is left out of the argument list, + * it returns the reference state pressure for the first + * species. + * Note that some SpeciesThermo implementations, such + * as those for ideal gases, require that all species + * in the same phase have the same reference state pressures. + * + * @param k Species Index + */ + virtual doublereal refPressure(int k=-1) const {return m_p0;} + + //! This utility function reports the type of parameterization + //! used for the species with index number index. + /*! + * + * @param index Species index + */ + virtual int reportType(int index) const { return SIMPLE; } + + /*! + * This utility function reports back the type of + * parameterization and all of the parameters for the + * species, index. + * + * @param index Species index + * @param type Integer type of the standard type + * @param c Vector of coefficients used to set the + * parameters for the standard state. + * For the SimpleThermo object, there are 4 coefficients. + * @param minTemp output - Minimum temperature + * @param maxTemp output - Maximum temperature + * @param refPressure output - reference pressure (Pa). + * + */ + virtual void reportParams(int index, int &type, + doublereal * const c, + doublereal &minTemp, + doublereal &maxTemp, + doublereal &refPressure) const { + type = reportType(index); + int loc = m_loc[index]; + if (type == SIMPLE) { + c[0] = m_t0[loc]; + c[1] = m_h0_R[loc] * GasConstant; + c[2] = m_s0_R[loc] * GasConstant; + c[3] = m_cp0_R[loc] * GasConstant; + minTemp = m_tlow[loc]; + maxTemp = m_thigh[loc]; + refPressure = m_p0; + } + } + + //! Modify parameters for the standard state + /*! + * The thermo parameterization for a single species is overwritten. + * + * @param index Species index + * @param c Vector of coefficients used to set the + * parameters for the standard state. + * Must be length >= 4. + */ + virtual void modifyParams(int index, doublereal *c) { + int loc = m_loc[index]; + if (loc < 0) { + throw CanteraError("SimpleThermo::modifyParams", + "modifying parameters for species which hasn't been set yet"); + } + /* + * Change the data + */ + m_t0[loc] = c[0]; + m_h0_R[loc] = c[1] / GasConstant; + m_s0_R[loc] = c[2] / GasConstant; + m_cp0_R[loc] = c[3] / GasConstant; + } + + protected: + + //! Mapping between the species index and the vector index where the coefficients are kept + /*! + * This object doesn't have a one-to one correspondence between the species index, kspec, + * and the data location index,indexData, m_cp0_R[indexData]. + * This index keeps track of it. + * indexData = m_loc[kspec] + */ + mutable map m_loc; + + //! Map between the vector index where the coefficients are kept and the species index + /*! + * Length is equal to the number of dataPoints. + * kspec = m_index[indexData] + */ + vector_int m_index; + + //! Maximum value of the low temperature limit + doublereal m_tlow_max; + + //! Minimum value of the high temperature limit + doublereal m_thigh_min; + + //! Vector of low temperature limits (species index) + /*! + * Length is equal to number of data points + */ + vector_fp m_tlow; + + //! Vector of low temperature limits (species index) + /*! + * Length is equal to number of data points + */ + vector_fp m_thigh; + + //! Vector of base temperatures (kelvin) + /*! + * Length is equal to the number of species data points + */ + vector_fp m_t0; + + //! Vector of base log temperatures (kelvin) + /*! + * Length is equal to the number of species data points + */ + vector_fp m_logt0; + + //! Vector of base dimensionless Enthalpies + /*! + * Length is equal to the number of species data points + */ + vector_fp m_h0_R; + + //! Vector of base dimensionless Entropies + /*! + * Length is equal to the number of species data points + */ + vector_fp m_s0_R; + + //! Vector of base dimensionless heat capacities + /*! + * Length is equal to the number of species data points + */ + vector_fp m_cp0_R; + + //! Reference pressure (Pa) + /*! + * all species must have the same reference pressure. + */ + doublereal m_p0; + + //! Number of species data points in the object. + /*! + * This is less than or equal to the number of species in the phase. + */ + int m_nspData; + + }; + +} + +#endif diff --git a/Cantera/src/thermo/SpeciesThermo.h b/Cantera/src/thermo/SpeciesThermo.h new file mode 100755 index 000000000..eac95c520 --- /dev/null +++ b/Cantera/src/thermo/SpeciesThermo.h @@ -0,0 +1,298 @@ +/** + * @file SpeciesThermo.h + * Virtual base class for the calculation of multiple-species thermodynamic + * property managers and text for the spthermo module (see \ref spthermo + * and class \link Cantera::SpeciesThermo SpeciesThermo\endlink). + * + * We also describe the doxygen module spthermo (see \ref spthermo ) + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_SPECIESTHERMO_H +#define CT_SPECIESTHERMO_H + +#include "ct_defs.h" + +namespace Cantera { + + /** + * @defgroup spthermo Species Standard-State Thermodynamic Properties + * + * To compute the thermodynamic properties of multicomponent + * solutions, it is necessary to know something about the + * thermodynamic properties of the individual species present in + * the solution. Exactly what sort of species properties are + * required depends on the thermodynamic model for the + * solution. For a gaseous solution (i.e., a gas mixture), the + * species properties required are usually ideal gas properties at + * the mixture temperature and at a reference pressure (often 1 + * atm or 1 bar). For other types of solutions, however, it may + * not be possible to isolate the species in a "pure" state. For + * example, the thermodynamic properties of, say, Na+ and Cl- in + * saltwater are not easily determined from data on the properties + * of solid NaCl, or solid Na metal, or chlorine gas. In this + * case, the solvation in water is fundamental to the identity of + * the species, and some other reference state must be used. One + * common convention for liquid solutions is to use thermodynamic + * data for the solutes for the limit of infinite dilution in the + * pure solvent; another convention is to reference all properties + * to unit molality. + * + * In defining these standard states for species in a phase, we make + * the following definition. A reference state is a standard state + * of a species in a phase limited to one pressure, the reference + * pressure. The reference state specifies the dependence of all + * thermodynamic functions as a function of the temperature, in + * between a minimum temperature and a maximum temperature. The + * reference state also specifies the molar volume of the species + * as a function of temperature. The molar volume is a thermodynamic + * function. + * A full standard state does the same thing as a reference state, + * but specifies the thermodynamics functions at all pressures. + * + * Whatever the conventions used by a particular solution model, + * means need to be provided to compute the species properties in + * the reference state. Class SpeciesThermo is the base class + * for a family of classes that compute properties of all + * species in a phase in their reference states, for a range of temperatures. + * Note, the pressure dependence of the species thermodynamic functions is not + * handled by this particular species thermodynamic model. %SpeciesThermo + * calculates the thermodynamic values of all species in a single + * phase during each call. + * + * + * The following classes inherit from %SpeciesThermo. Each of these classes + * handle multiple species, usually all of the species in a phase. + * + * - NasaThermo in file NasaThermo.h + * - This is a two zone model, with each zone consisting of a 7 + * coefficient Nasa Polynomial format. + * . + * - ShomateThermo in file ShomateThermo.h + * - This is a two zone model, with each zone consisting of a 7 + * coefficient Shomate Polynomial format. + * . + * - SimpleThermo in file SimpleThermo.h + * - This is a one-zone constant heat capacity model. + * . + * - GeneralSpeciesThermo in file GeneralSpeciesThermo.h + * - This is a general model. Each species is handled separately + * via a vector over SpeciesThermoInterpType classes. + * . + * - SpeciesThermo1 in file SpeciesThermoMgr.h + * - SpeciesThermoDuo in file SpeciesThermoMgr.h + * - This is a combination of two SpeciesThermo types. + * . + * . + * + * The class SpeciesThermoInterpType is a pure virtual base class for + * calculation of thermodynamic functions for a single species + * in its reference state. + * The following classes inherit from %SpeciesThermoInterpType + * - NasaPoly1 in file NasaPoly1.h + * - This is a one zone model, consisting of a 7 + * coefficient Nasa Polynomial format. + * . + * - NasaPoly2 in file NasaPoly2.h + * - This is a two zone model, with each zone consisting of a 7 + * coefficient Nasa Polynomial format. + * . + * - ShomatePoly in file ShomatePoly.h + * - This is a one zone model, consisting of a 7 + * coefficient Shomate Polynomial format. + * . + * - ShomatePoly2 in file ShomatePoly.h + * - This is a two zone model, with each zone consisting of a 7 + * coefficient Shomate Polynomial format. + * . + * - ConstCpPoly in file ConstCpPoly.h + * - This is a one-zone constant heat capacity model. + * . + * - Mu0Poly in file Mu0Poly.h + * - This is a multizoned model. The chemical potential is given + * at a set number of temperatures. Between each temperature + * the heat capacity is treated as a constant. + * . + * . + */ + //@{ + + //////////////////////// class SpeciesThermo //////////////////// + + + //! Pure Virtual base class for the species thermo manager classes. + /*! + * This class defines the interface which all subclasses must implement. + * + * Class %SpeciesThermo is the base class + * for a family of classes that compute properties of a set of + * species in their reference state at a range of temperatures. + * Note, the pressure dependence of the reference state is not + * handled by this particular species standard state model. + */ + class SpeciesThermo { + + public: + + //! Constructor + SpeciesThermo() {} + + //! Destructor + virtual ~SpeciesThermo() {} + + + //! Install a new species thermodynamic property + //! parameterization for one species. + /*! + * + * @param name Name of the species + * @param index The 'update' method will update the property + * values for this species + * at position i index in the property arrays. + * @param type int flag specifying the type of parameterization to be + * installed. + * @param c vector of coefficients for the parameterization. + * This vector is simply passed through to the + * parameterization constructor. + * @param minTemp minimum temperature for which this parameterization + * is valid. + * @param maxTemp maximum temperature for which this parameterization + * is valid. + * @param refPressure standard-state pressure for this + * parameterization. + * @see speciesThermoTypes.h + */ + virtual void install(std::string name, int index, int type, + const doublereal* c, + doublereal minTemp, + doublereal maxTemp, + doublereal refPressure)=0; + + + //! Compute the reference-state properties for all species. + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of each of the standard states. + * + * @param T Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void update(doublereal T, + doublereal* cp_R, + doublereal* h_RT, + doublereal* s_R) const=0; + + + //! Like update(), but only updates the single species k. + /*! + * @param k species index + * @param T Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + * + */ + virtual void update_one(int k, doublereal T, + doublereal* cp_R, + doublereal* h_RT, + doublereal* s_R) const { + update(T, cp_R, h_RT, s_R); + } + + //! Minimum temperature. + /*! + * If no argument is supplied, this + * method returns the minimum temperature for which \e all + * parameterizations are valid. If an integer index k is + * supplied, then the value returned is the minimum + * temperature for species k in the phase. + * + * @param k Species index + */ + virtual doublereal minTemp(int k=-1) const =0; + + //! Maximum temperature. + /*! + * If no argument is supplied, this + * method returns the maximum temperature for which \e all + * parameterizations are valid. If an integer index k is + * supplied, then the value returned is the maximum + * temperature for parameterization k. + * + * @param k Species Index + */ + virtual doublereal maxTemp(int k=-1) const =0; + + //! The reference-state pressure for species k. + /*! + * + * returns the reference state pressure in Pascals for + * species k. If k is left out of the argument list, + * it returns the reference state pressure for the first + * species. + * Note that some SpeciesThermo implementations, such + * as those for ideal gases, require that all species + * in the same phase have the same reference state pressures. + * + * @param k Species Index + */ + virtual doublereal refPressure(int k=-1) const =0; + + //! This utility function reports the type of parameterization + //! used for the species with index number index. + /*! + * + * @param index Species index + */ + virtual int reportType(int index = -1) const = 0; + + + //! This utility function reports back the type of + //! parameterization and all of the parameters for the species, index. + /*! + * @param index Species index + * @param type Integer type of the standard type + * @param c Vector of coefficients used to set the + * parameters for the standard state. + * @param minTemp output - Minimum temperature + * @param maxTemp output - Maximum temperature + * @param refPressure output - reference pressure (Pa). + */ + virtual void reportParams(int index, int &type, + doublereal * const c, + doublereal &minTemp, + doublereal &maxTemp, + doublereal &refPressure) const =0; + + //! Modify parameters for the standard state + /*! + * @param index Species index + * @param c Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void modifyParams(int index, doublereal *c) = 0; + + }; + //@} +} + +#endif + diff --git a/Cantera/src/thermo/SpeciesThermoFactory.cpp b/Cantera/src/thermo/SpeciesThermoFactory.cpp new file mode 100755 index 000000000..ccc7eac47 --- /dev/null +++ b/Cantera/src/thermo/SpeciesThermoFactory.cpp @@ -0,0 +1,488 @@ +/** + * @file SpeciesThermoFactory.cpp + * Definitions for factory to build instances of classes that manage the + * standard-state thermodynamic properties of a set of species + * (see \ref spthermo and class \link Cantera::SpeciesThermoFactory SpeciesThermoFactory\endlink); + */ +/* + * $Id$ + */ +// Copyright 2001 California Institute of Technology + +#ifdef WIN32 +#pragma warning(disable:4786) +#endif + + +#include "SpeciesThermoFactory.h" +using namespace std; + +#include "SpeciesThermo.h" +#include "NasaThermo.h" +#include "ShomateThermo.h" +#include "SimpleThermo.h" +#include "GeneralSpeciesThermo.h" +#include "Mu0Poly.h" + +#include "SpeciesThermoMgr.h" +#include "speciesThermoTypes.h" + +#include "xml.h" +#include "ctml.h" + +using namespace ctml; + + +namespace Cantera { + + SpeciesThermoFactory* SpeciesThermoFactory::s_factory = 0; + + /** + * Examine the types of species thermo parameterizations, + * and return a flag indicating the type of parameterization + * needed by the species. + * + * @param spData_node Species Data XML node. This node contains a list + * of species XML nodes underneath it. + * + * @todo Make sure that spDadta_node is species Data XML node by checking its name is speciesData + */ + static void getSpeciesThermoTypes(XML_Node* spData_node, + int& has_nasa, int& has_shomate, int& has_simple, + int &has_other) { + const XML_Node& sparray = *spData_node; + std::vector sp; + + // get all of the species nodes + sparray.getChildren("species",sp); + size_t n, ns = sp.size(); + for (n = 0; n < ns; n++) { + XML_Node* spNode = sp[n]; + if (spNode->hasChild("thermo")) { + const XML_Node& th = sp[n]->child("thermo"); + if (th.hasChild("NASA")) has_nasa = 1; + if (th.hasChild("Shomate")) has_shomate = 1; + if (th.hasChild("const_cp")) has_simple = 1; + if (th.hasChild("poly")) { + if (th.child("poly")["order"] == "1") has_simple = 1; + else throw CanteraError("newSpeciesThermo", + "poly with order > 1 not yet supported"); + } + if (th.hasChild("Mu0")) has_other = 1; + } else { + throw UnknownSpeciesThermoModel("getSpeciesThermoTypes:", + spNode->attrib("name"), "missing"); + } + } + } + + + /** + * Return a species thermo manager to handle the parameterizations + * specified in a CTML phase specification. + */ + SpeciesThermo* SpeciesThermoFactory::newSpeciesThermo(XML_Node* spData_node) { + int inasa = 0, ishomate = 0, isimple = 0, iother = 0; + try { + getSpeciesThermoTypes(spData_node, inasa, ishomate, isimple, iother); + } catch (UnknownSpeciesThermoModel) { + iother = 1; + popError(); + } + if (iother) { + writelog("returning new GeneralSpeciesThermo"); + return new GeneralSpeciesThermo(); + } + return newSpeciesThermo(NASA*inasa + + SHOMATE*ishomate + SIMPLE*isimple); + } + + SpeciesThermo* SpeciesThermoFactory:: + newSpeciesThermo(std::vector spData_nodes) { + int n = static_cast(spData_nodes.size()); + int inasa = 0, ishomate = 0, isimple = 0, iother = 0; + for (int j = 0; j < n; j++) { + try { + getSpeciesThermoTypes(spData_nodes[j], inasa, ishomate, isimple, iother); + } catch (UnknownSpeciesThermoModel) { + iother = 1; + popError(); + } + } + if (iother) { + return new GeneralSpeciesThermo(); + } + return newSpeciesThermo(NASA*inasa + + SHOMATE*ishomate + SIMPLE*isimple); + } + + + /* + * @todo is this used? + */ + SpeciesThermo* SpeciesThermoFactory:: + newSpeciesThermoOpt(std::vector nodes) { + int n = static_cast(nodes.size()); + int inasa = 0, ishomate = 0, isimple = 0, iother = 0; + for (int j = 0; j < n; j++) { + try { + getSpeciesThermoTypes(nodes[j], inasa, ishomate, isimple, iother); + } catch (UnknownSpeciesThermoModel) { + iother = 1; + popError(); + } + } + if (iother) { + return new GeneralSpeciesThermo(); + } + return newSpeciesThermo(NASA*inasa + + SHOMATE*ishomate + SIMPLE*isimple); + } + + + + SpeciesThermo* SpeciesThermoFactory::newSpeciesThermo(int type) { + + switch (type) { + case NASA: + return new NasaThermo; + case SHOMATE: + return new ShomateThermo; + case SIMPLE: + return new SimpleThermo; + case NASA + SHOMATE: + return new SpeciesThermoDuo; + case NASA + SIMPLE: + return new SpeciesThermoDuo; + case SHOMATE + SIMPLE: + return new SpeciesThermoDuo; + default: + throw UnknownSpeciesThermo( + "SpeciesThermoFactory::newSpeciesThermo",type); + return 0; + } + } + + + /* + * Check the continuity of properties at the midpoint + * temperature. + */ + void NasaThermo::checkContinuity(std::string name, double tmid, const doublereal* clow, + doublereal* chigh) { + + // heat capacity + doublereal cplow = poly4(tmid, clow); + doublereal cphigh = poly4(tmid, chigh); + doublereal delta = cplow - cphigh; + if (fabs(delta/cplow) > 0.001) { + writelog("\n\n**** WARNING ****\nFor species "+name+ + ", discontinuity in cp/R detected at Tmid = " + +fp2str(tmid)+"\n"); + writelog("\tValue computed using low-temperature polynomial: " + +fp2str(cplow)+".\n"); + writelog("\tValue computed using high-temperature polynomial: " + +fp2str(cphigh)+".\n"); + } + + // enthalpy + doublereal hrtlow = enthalpy_RT(tmid, clow); + doublereal hrthigh = enthalpy_RT(tmid, chigh); + delta = hrtlow - hrthigh; + if (fabs(delta/hrtlow) > 0.001) { + writelog("\n\n**** WARNING ****\nFor species "+name+ + ", discontinuity in h/RT detected at Tmid = " + +fp2str(tmid)+"\n"); + writelog("\tValue computed using low-temperature polynomial: " + +fp2str(hrtlow)+".\n"); + writelog("\tValue computed using high-temperature polynomial: " + +fp2str(hrthigh)+".\n"); + } + + // entropy + doublereal srlow = entropy_R(tmid, clow); + doublereal srhigh = entropy_R(tmid, chigh); + delta = srlow - srhigh; + if (fabs(delta/srlow) > 0.001) { + writelog("\n\n**** WARNING ****\nFor species "+name+ + ", discontinuity in s/R detected at Tmid = " + +fp2str(tmid)+"\n"); + writelog("\tValue computed using low-temperature polynomial: " + +fp2str(srlow)+".\n"); + writelog("\tValue computed using high-temperature polynomial: " + +fp2str(srhigh)+".\n"); + } + } + + + /** + * Install a NASA polynomial thermodynamic property + * parameterization for species k into a SpeciesThermo instance. + * This is called by method installThermoForSpecies if a NASA + * block is found in the XML input. + */ + static void installNasaThermoFromXML(std::string speciesName, + SpeciesThermo& sp, int k, + const XML_Node* f0ptr, const XML_Node* f1ptr) { + doublereal tmin0, tmax0, tmin1, tmax1, tmin, tmid, tmax; + + const XML_Node& f0 = *f0ptr; + + // default to a single temperature range + bool dualRange = false; + + // but if f1ptr is suppled, then it is a two-range + // parameterization + if (f1ptr) {dualRange = true;} + + tmin0 = fpValue(f0["Tmin"]); + tmax0 = fpValue(f0["Tmax"]); + tmin1 = tmax0; + tmax1 = tmin1 + 0.0001; + if (dualRange) { + tmin1 = fpValue((*f1ptr)["Tmin"]); + tmax1 = fpValue((*f1ptr)["Tmax"]); + } + + vector_fp c0, c1; + if (fabs(tmax0 - tmin1) < 0.01) { + // f0 has the lower T data, and f1 the higher T data + tmin = tmin0; + tmid = tmax0; + tmax = tmax1; + getFloatArray(f0.child("floatArray"), c0, false); + if (dualRange) + getFloatArray(f1ptr->child("floatArray"), c1, false); + else { + // if there is no higher range data, then copy c0 to c1. + c1.resize(7,0.0); + copy(c0.begin(), c0.end(), c1.begin()); + } + } + else if (fabs(tmax1 - tmin0) < 0.01) { + // f1 has the lower T data, and f0 the higher T data + tmin = tmin1; + tmid = tmax1; + tmax = tmax0; + getFloatArray(f1ptr->child("floatArray"), c0, false); + getFloatArray(f0.child("floatArray"), c1, false); + } + else { + throw CanteraError("installNasaThermo", + "non-continuous temperature ranges."); + } + + // The NasaThermo species property manager expects the + // coefficients in a different order, so rearrange them. + array_fp c(15); + c[0] = tmid; + doublereal p0 = OneAtm; + c[1] = c0[5]; + c[2] = c0[6]; + copy(c0.begin(), c0.begin()+5, c.begin() + 3); + c[8] = c1[5]; + c[9] = c1[6]; + copy(c1.begin(), c1.begin()+5, c.begin() + 10); + sp.install(speciesName, k, NASA, &c[0], tmin, tmax, p0); + } + +#ifdef INCL_NASA96 + + /** + * Install a NASA96 polynomial thermodynamic property + * parameterization for species k into a SpeciesThermo instance. + */ + static void installNasa96ThermoFromXML(std::string speciesName, + SpeciesThermo& sp, int k, + const XML_Node* f0ptr, const XML_Node* f1ptr) { + doublereal tmin0, tmax0, tmin1, tmax1, tmin, tmid, tmax; + + const XML_Node& f0 = *f0ptr; + bool dualRange = false; + if (f1ptr) {dualRange = true;} + tmin0 = fpValue(f0["Tmin"]); + tmax0 = fpValue(f0["Tmax"]); + tmin1 = tmax0; + tmax1 = tmin1 + 0.0001; + if (dualRange) { + tmin1 = fpValue((*f1ptr)["Tmin"]); + tmax1 = fpValue((*f1ptr)["Tmax"]); + } + + vector_fp c0, c1; + if (fabs(tmax0 - tmin1) < 0.01) { + tmin = tmin0; + tmid = tmax0; + tmax = tmax1; + getFloatArray(f0.child("floatArray"), c0, false); + if (dualRange) + getFloatArray(f1ptr->child("floatArray"), c1, false); + else { + c1.resize(7,0.0); + copy(c0.begin(), c0.end(), c1.begin()); + } + } + else if (fabs(tmax1 - tmin0) < 0.01) { + tmin = tmin1; + tmid = tmax1; + tmax = tmax0; + getFloatArray(f1ptr->child("floatArray"), c0, false); + getFloatArray(f0.child("floatArray"), c1, false); + } + else { + throw CanteraError("installNasaThermo", + "non-continuous temperature ranges."); + } + array_fp c(15); + c[0] = tmid; + doublereal p0 = OneAtm; + c[1] = c0[5]; + c[2] = c0[6]; + copy(c0.begin(), c0.begin()+5, c.begin() + 3); + c[8] = c1[5]; + c[9] = c1[6]; + copy(c1.begin(), c1.begin()+5, c.begin() + 10); + sp.install(speciesName, k, NASA, &c[0], tmin, tmax, p0); + } + +#endif + + + /** + * Install a Shomate polynomial thermodynamic property + * parameterization for species k. + */ + static void installShomateThermoFromXML(std::string speciesName, + SpeciesThermo& sp, int k, + const XML_Node* f0ptr, const XML_Node* f1ptr) { + doublereal tmin0, tmax0, tmin1, tmax1, tmin, tmid, tmax; + + const XML_Node& f0 = *f0ptr; + bool dualRange = false; + if (f1ptr) {dualRange = true;} + tmin0 = fpValue(f0["Tmin"]); + tmax0 = fpValue(f0["Tmax"]); + tmin1 = tmax0; + tmax1 = tmin1 + 0.0001; + if (dualRange) { + tmin1 = fpValue((*f1ptr)["Tmin"]); + tmax1 = fpValue((*f1ptr)["Tmax"]); + } + + vector_fp c0, c1; + if (fabs(tmax0 - tmin1) < 0.01) { + tmin = tmin0; + tmid = tmax0; + tmax = tmax1; + getFloatArray(f0.child("floatArray"), c0, false); + if (dualRange) + getFloatArray(f1ptr->child("floatArray"), c1, false); + else { + c1.resize(7,0.0); + copy(c0.begin(), c0.begin()+7, c1.begin()); + } + } + else if (fabs(tmax1 - tmin0) < 0.01) { + tmin = tmin1; + tmid = tmax1; + tmax = tmax0; + getFloatArray(f1ptr->child("floatArray"), c0, false); + getFloatArray(f0.child("floatArray"), c1, false); + } + else { + throw CanteraError("installShomateThermo", + "non-continuous temperature ranges."); + } + array_fp c(15); + c[0] = tmid; + doublereal p0 = OneAtm; + copy(c0.begin(), c0.begin()+7, c.begin() + 1); + copy(c1.begin(), c1.begin()+7, c.begin() + 8); + sp.install(speciesName, k, SHOMATE, &c[0], tmin, tmax, p0); + } + + + + /** + * Install a constant-cp thermodynamic property + * parameterization for species k. + */ + static void installSimpleThermoFromXML(std::string speciesName, + SpeciesThermo& sp, int k, + const XML_Node& f) { + doublereal tmin, tmax; + tmin = fpValue(f["Tmin"]); + tmax = fpValue(f["Tmax"]); + if (tmax == 0.0) tmax = 1.0e30; + + vector_fp c(4); + c[0] = getFloat(f, "t0", "-"); + c[1] = getFloat(f, "h0", "-"); + c[2] = getFloat(f, "s0", "-"); + c[3] = getFloat(f, "cp0", "-"); + doublereal p0 = OneAtm; + sp.install(speciesName, k, SIMPLE, &c[0], tmin, tmax, p0); + } + + /** + * Install a species thermodynamic property parameterization + * for one species into a species thermo manager. + * @param k species number + * @param s XML node specifying species + * @param spthermo species thermo manager + */ + void SpeciesThermoFactory:: + installThermoForSpecies(int k, const XML_Node& s, + SpeciesThermo& spthermo) { + /* + * Check to see that the species block has a thermo block + * before processing. Throw an error if not there. + */ + if (!(s.hasChild("thermo"))) { + throw UnknownSpeciesThermoModel("installSpecies", + s["name"], ""); + } + const XML_Node& thermo = s.child("thermo"); + const std::vector& tp = thermo.children(); + int nc = static_cast(tp.size()); + if (nc == 1) { + const XML_Node* f = tp[0]; + if (f->name() == "Shomate") { + installShomateThermoFromXML(s["name"], spthermo, k, f, 0); + } + else if (f->name() == "const_cp") { + installSimpleThermoFromXML(s["name"], spthermo, k, *f); + } + else if (f->name() == "NASA") { + installNasaThermoFromXML(s["name"], spthermo, k, f, 0); + } + else if (f->name() == "Mu0") { + installMu0ThermoFromXML(s["name"], spthermo, k, f); + } + else { + throw UnknownSpeciesThermoModel("installSpecies", + s["name"], f->name()); + } + } + else if (nc == 2) { + const XML_Node* f0 = tp[0]; + const XML_Node* f1 = tp[1]; + if (f0->name() == "NASA" && f1->name() == "NASA") { + installNasaThermoFromXML(s["name"], spthermo, k, f0, f1); + } + else if (f0->name() == "Shomate" && f1->name() == "Shomate") { + installShomateThermoFromXML(s["name"], spthermo, k, f0, f1); + } + else { + throw UnknownSpeciesThermoModel("installSpecies", s["name"], + f0->name() + " and " + + f1->name()); + } + } + else { + throw UnknownSpeciesThermoModel("installSpecies", s["name"], + "multiple"); + } + } + +} diff --git a/Cantera/src/thermo/SpeciesThermoFactory.h b/Cantera/src/thermo/SpeciesThermoFactory.h new file mode 100755 index 000000000..576de2259 --- /dev/null +++ b/Cantera/src/thermo/SpeciesThermoFactory.h @@ -0,0 +1,252 @@ +/** + * @file SpeciesThermoFactory.h + * Header for factory to build instances of classes that manage the + * standard-state thermodynamic properties of a set of species + * (see \ref spthermo and class \link Cantera::SpeciesThermoFactory SpeciesThermoFactory\endlink); + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef SPECIESTHERMO_FACTORY_H +#define SPECIESTHERMO_FACTORY_H + +#include "SpeciesThermo.h" +#include "ctexceptions.h" + +namespace Cantera { + + class XML_Node; + + /** + * Throw a named error for an unknown or missing species thermo model. + * + * @ingroup thermoprops + */ + class UnknownSpeciesThermoModel: public CanteraError { + public: + //! constructor + /*! + * @param proc Function name error occurred. + * @param spName Species Name that caused the error + * @param speciesThermoModel Unrecognized species thermo model name + */ + UnknownSpeciesThermoModel(std::string proc, std::string spName, + std::string speciesThermoModel) : + CanteraError(proc, "species " + spName + + ": Specified speciesThermoPhase model " + + speciesThermoModel + + " does not match any known type.") {} + //! destructor + virtual ~UnknownSpeciesThermoModel() {} + }; + + //! Factory to build instances of classes that manage the + //! standard-state thermodynamic properties of a set of species. + /*! + * This class is implemented as a singleton -- one in which + * only one instance is needed. The recommended way to access + * the factory is to call this static method, which + * instantiates the class if it is the first call, but + * otherwise simply returns the pointer to the existing + * instance. + * + * @ingroup thermoprops + */ + class SpeciesThermoFactory { + + public: + + //! Static method to return an instance of this class + /*! + * This class is implemented as a singleton -- one in which + * only one instance is needed. The recommended way to access + * the factory is to call this static method, which + * instantiates the class if it is the first call, but + * otherwise simply returns the pointer to the existing + * instance. + */ + static SpeciesThermoFactory* factory() { + if (!s_factory) s_factory = new SpeciesThermoFactory; + return s_factory; + } + + //! Delete static instance of this class + /** + * If it is necessary to explicitly delete the factory before + * the process terminates (for example, when checking for + * memory leaks) then this method can be called to delete it. + */ + static void deleteFactory() { + if (s_factory) { + delete s_factory; + s_factory = 0; + } + } + + //! Destructor + /** + * Doesn't do anything. We do not delete statically + * created single instance of this class here, because it would + * create an infinite loop if destructor is called for that + * single instance. + */ + virtual ~SpeciesThermoFactory() { + } + + //! Create a new species property manager. + /*! + * @param type the integer type to be created. + */ + virtual SpeciesThermo* newSpeciesThermo(int type); + + //! Create a new species property manager. + /*! + * This routine will look through species nodes. It will discover what + * each species needs for its species property managers. Then, + * it will malloc and return the proper species property manager to use. + * + * @param spData_node Pointer to a speciesData XML Node. + * Each speciesData node contains a list of XML species elements + * e.g., \ + */ + virtual SpeciesThermo* newSpeciesThermo(XML_Node* spData_node); + + //! Create a new species property manager for a group of species + /*! + * This routine will look through species nodes. It will discover what + * each species needs for its species property managers. Then, + * it will malloc and return the proper species property manager to use. + * + * @param spData_nodes Vector of XML_Nodes, each of which is a speciesData XML Node. + * Each speciesData node contains a list of XML species elements + * e.g., \ + */ + virtual SpeciesThermo* newSpeciesThermo(std::vector spData_nodes); + + //! Create a new species property manager. + /*! + * This routine will look through species nodes. It will discover what + * each species needs for its species property managers. Then, + * it will malloc and return the proper species property manager to use. + * + * + * @param spData_nodes Vector of XML_Nodes, each of which is a speciesData XML Node. + * Each %speciesData node contains a list of XML species elements + * e.g., \ + * + * @todo is this used? + */ + virtual SpeciesThermo* newSpeciesThermoOpt(std::vector spData_nodes); + + + virtual void installThermoForSpecies(int k, const XML_Node& s, + SpeciesThermo& spthermo); + + private: + + //! pointer to the sole instance of this class + static SpeciesThermoFactory* s_factory; + + //! Constructor. This is made private, so that only the static + //! method factory() can instantiate the class. + SpeciesThermoFactory(){} + }; + + + ////////////////////// Convenience functions //////////////////// + // + // These functions allow using a different factory class that + // derives from SpeciesThermoFactory. + // + ////////////////////////////////////////////////////////////////// + + + //! Create a new species thermo manager instance, by specifying + //!the type and (optionally) a pointer to the factory to use to create it. + /*! + * This utility program will look through species nodes. It will discover what + * each species needs for its species property managers. Then, + * it will malloc and return the proper species property manager to use. + * + * These functions allow using a different factory class that + * derives from SpeciesThermoFactory. + * + * @param type Species thermo type. + * @param f Pointer to a SpeciesThermoFactory. optional parameter. + * Defautls to NULL. + */ + inline SpeciesThermo* newSpeciesThermoMgr(int type, + SpeciesThermoFactory* f=0) { + if (f == 0) { + f = SpeciesThermoFactory::factory(); + } + SpeciesThermo* sptherm = f->newSpeciesThermo(type); + return sptherm; + } + + //! Function to return SpeciesThermo manager + /*! + * This utility program will look through species nodes. It will discover what + * each species needs for its species property managers. Then, + * it will malloc and return the proper species property manager to use. + * + * These functions allow using a different factory class that + * derives from SpeciesThermoFactory. + * + * @param spData_node Vector of XML_Nodes, each of which is a speciesData XML Node. + * Each %speciesData node contains a list of XML species elements + * e.g., \ + * @param f Pointer to a SpeciesThermoFactory. optional parameter. + * Defautls to NULL. + */ + inline SpeciesThermo* newSpeciesThermoMgr(XML_Node* spData_node, + SpeciesThermoFactory* f=0) { + if (f == 0) { + f = SpeciesThermoFactory::factory(); + } + SpeciesThermo* sptherm = f->newSpeciesThermo(spData_node); + return sptherm; + } + + //! Function to return SpeciesThermo manager + /*! + * This utility program will look through species nodes. It will discover what + * each species needs for its species property managers. Then, + * it will malloc and return the proper species property manager to use. + * + * These functions allow using a different factory class that + * derives from SpeciesThermoFactory. + * + * @param spData_nodes Vector of XML_Nodes, each of which is a speciesData XML Node. + * Each %speciesData node contains a list of XML species elements + * e.g., \ + * @param f Pointer to a SpeciesThermoFactory. optional parameter. + * Defautls to NULL. + * @param opt Boolean defaults to false. + */ + inline SpeciesThermo* newSpeciesThermoMgr(std::vector spData_nodes, + SpeciesThermoFactory* f=0, bool opt=false) { + if (f == 0) { + f = SpeciesThermoFactory::factory(); + } + SpeciesThermo* sptherm; + if (opt) { + sptherm = f->newSpeciesThermoOpt(spData_nodes); + } else { + sptherm = f->newSpeciesThermo(spData_nodes); + } + return sptherm; + } + +} + +#endif + + diff --git a/Cantera/src/thermo/SpeciesThermoInterpType.h b/Cantera/src/thermo/SpeciesThermoInterpType.h new file mode 100644 index 000000000..1ccf2dcd3 --- /dev/null +++ b/Cantera/src/thermo/SpeciesThermoInterpType.h @@ -0,0 +1,150 @@ +/** + * @file SpeciesThermoInterpType.h + * Pure Virtual Base class for individual species reference state + * themodynamic managers (see \ref spthermo and class \link Cantera::SpeciesThermoInterpType SpeciesThermoInterpType \endlink). + */ + /* + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + +#include "speciesThermoTypes.h" + +#ifndef CT_SPECIESTHERMOINTERPTYPE_H +#define CT_SPECIESTHERMOINTERPTYPE_H + +namespace Cantera { + + //! Pure Virtual Base class for individual species reference state + //! themodynamic managers. + /*! + * This differs from the SpeciesThermo virtual + * base class in the sense that this class is meant to handle only + * one species. The speciesThermo class is meant to handle the + * calculation of all the species (or a large subset) in a phase. + * + * One key feature is that the update routines use the same + * form as the update routines in the speciesThermo class. They update + * into a vector of cp_R, s_R, and H_R that spans all of the species in + * a phase. Therefore, this class must carry along a species index into that + * vector. + * + * These routine may be templated. A key requirement of the template is that + * there is a constructor with the following form: + * + * @code + * SpeciesThermoInterpType(int index, doublereal tlow, doublereal thigh, + * doublereal pref, const doublereal* coeffs) + * @endcode + * + * The constructor is used to instantiate the object. + * + * @ingroup spthermo + */ + class SpeciesThermoInterpType { + + public: + + //! Constructor + SpeciesThermoInterpType() {}; + + //! Destructor + virtual ~SpeciesThermoInterpType() {}; + + //! duplicator + virtual SpeciesThermoInterpType * + duplMyselfAsSpeciesThermoInterpType() const = 0; + + + //! Returns the minimum temperature that the thermo + //! parameterization is valid + virtual doublereal minTemp() const = 0; + + //! Returns the maximum temperature that the thermo + //! parameterization is valid + virtual doublereal maxTemp() const = 0; + + //! Returns the reference pressure (Pa) + virtual doublereal refPressure() const = 0; + + //! Returns an integer representing the type of parameterization + virtual int reportType() const = 0; + + //! Update the properties for this species, given a temperature polynomial + /*! + * This method is called with a pointer to an array containing the functions of + * temperature needed by this parameterization, and three pointers to arrays where the + * computed property values should be written. This method updates only one value in + * each array. + * + * The form and length of the Temperature Polynomial may vary depending on the + * parameterization. + * + * @param tempPoly vector of temperature polynomials + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void updateProperties(const doublereal* tempPoly, + doublereal* cp_R, doublereal* h_RT, + doublereal* s_R) const = 0; + + //! Compute the reference-state property of one species + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of one of the species. The species index is used + * to reference into the cp_R, h_RT, and s_R arrays. + * + * @param temp Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void updatePropertiesTemp(const doublereal temp, + doublereal* cp_R, + doublereal* h_RT, + doublereal* s_R) const = 0; + + //!This utility function reports back the type of + //! parameterization and all of the parameters for the + //! species, index. + /*! + * All parameters are output variables + * + * @param index Species index + * @param type Integer type of the standard type + * @param minTemp output - Minimum temperature + * @param maxTemp output - Maximum temperature + * @param refPressure output - reference pressure (Pa). + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void reportParameters(int &index, int &type, + doublereal &minTemp, doublereal &maxTemp, + doublereal &refPressure, + doublereal* const coeffs) const = 0; + + //! Modify parameters for the standard state + /*! + * @param coeffs Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void modifyParameters(doublereal* coeffs) {} + + }; + +} + +#endif + diff --git a/Cantera/src/thermo/SpeciesThermoMgr.h b/Cantera/src/thermo/SpeciesThermoMgr.h new file mode 100755 index 000000000..c82aef6e1 --- /dev/null +++ b/Cantera/src/thermo/SpeciesThermoMgr.h @@ -0,0 +1,500 @@ +/** + * @file SpeciesThermoMgr.h + * This file contains descriptions of templated subclasses of + * the virtual base class, SpeciesThermo, which + * include SpeciesThermoDuo and SpeciesThermo1 + * (see \ref spthermo and classes + * \link Cantera::SpeciesThermoDuo SpeciesThermoDuo\endlink and + * \link Cantera::SpeciesThermo1 SpeciesThermo1\endlink) + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + +#ifndef CT_SPECIESTHERMO_MGR_H +#define CT_SPECIESTHERMO_MGR_H + +#include "ct_defs.h" +#include "ctexceptions.h" +#include "stringUtils.h" +#include "SpeciesThermo.h" +#include + +namespace Cantera { + + //! Invokes the 'updateProperties' method of all objects in the list. + /*! + * This templated function has one template, InputIter. It should + * point to a class such as one that inherits from the virtual + * base class, SpeciesThermoInterpType, which has + * an updateProperties(T, Cp_R, h_RT, s)R) function + * + * @param begin Beginning iterator + * @param end end iterator + * @param T Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + * + * @ingroup spthermo + */ + template + inline void _updateAll(InputIter begin, + InputIter end, + doublereal T, + vector_fp& cp_R, + vector_fp& h_RT, + vector_fp& s_R) + { + for (; begin != end; ++begin) + begin->updateProperties(T, cp_R, h_RT, s_R); + } + + //! Iterates through a list of objects which implement a method + //! 'minTemp()', and returns the largest 'minTemp' value. + /*! + * This templated function has one template, InputIter. It should + * point to a class such as one that inherits from either + * SpeciesThermoInterpType or SpeciesThermo, which have a minTemp() function + * + * @param begin Beginning iterator + * @param end end iterator + * + * @ingroup spthermo + */ + template + doublereal _minTemp(InputIter begin, InputIter end) { + doublereal _minT = 0.0; + for (; begin != end; ++begin) + _minT = fmaxx(_minT, begin->minTemp()); + return _minT; + } + + //! Iterates through a list of objects which implement a method + //! 'maxTemp()', and returns the smallest 'maxTemp' value. + /*! + * This templated function has one template, InputIter. It should + * point to a class such as one that inherits from either + * SpeciesThermoInterpType or SpeciesThermo which have a minTemp() function + * + * @param begin Beginning iterator + * @param end end iterator + * + * @ingroup spthermo + */ + template + doublereal _maxTemp(_InputIter begin, _InputIter end) { + doublereal _maxT = 1.e10; + for (; begin != end; ++begin) + _maxT = fminn(_maxT, begin->maxTemp()); + return _maxT; + } + + /////////////////////// Exceptions ////////////////////////////// + + //! Exception thrown if species reference pressures don't match. + /*! + * @ingroup spthermo + */ + class RefPressureMismatch : public CanteraError { + public: + //! constructor + /*! + * @param proc name of the procecdure + * @param prnew reference pressure + * @param prold old reference pressure + */ + RefPressureMismatch(std::string proc, doublereal prnew, + doublereal prold) : CanteraError(proc, + "Species reference pressure (" + + fp2str(prnew) + ") does not match previously-defined " + + "reference pressure (" + fp2str(prold) + ")") {} + //! destructor + virtual ~RefPressureMismatch() {} + }; + + //! Unknown species thermo manager string error + /*! + * @ingroup spthermo + */ + class UnknownSpeciesThermo : public CanteraError { + public: + //! constructor + /*! + * @param proc name of the procecdure + * @param type unknown type + */ + UnknownSpeciesThermo(std::string proc, int type) : + CanteraError(proc, "Specified species parameterization type (" + int2str(type) + + ") does not match any known type.") {} + //! destructor + virtual ~UnknownSpeciesThermo() {} + }; + + + + /** + * This species thermo manager requires that all species have one + * of two parameterizations. + * + * Note this seems to be a slow way to do things, and it may be on its way out. + * + * @ingroup spthermo + */ + template + class SpeciesThermoDuo : public SpeciesThermo { + + public: + //! Constructor + SpeciesThermoDuo() {} + //! Destructor + virtual ~SpeciesThermoDuo(){} + + /** + * install a new species thermodynamic property + * parameterization for one species. + * + * @param name Name of the species + * @param sp The 'update' method will update the property + * values for this species + * at position i index in the property arrays. + * @param type int flag specifying the type of parameterization to be + * installed. + * @param c vector of coefficients for the parameterization. + * This vector is simply passed through to the + * parameterization constructor. + * @param minTemp minimum temperature for which this parameterization + * is valid. + * @param maxTemp maximum temperature for which this parameterization + * is valid. + * @param refPressure standard-state pressure for this + * parameterization. + * @see speciesThermoTypes.h + */ + virtual void install(std::string name, int sp, int type, + const doublereal* c, + doublereal minTemp, + doublereal maxTemp, + doublereal refPressure) { + m_p0 = refPressure; + if (type == m_thermo1.ID) { + m_thermo1.install(name, sp, 0, c, minTemp, maxTemp, + refPressure); + speciesToType[sp] = m_thermo1.ID; + } else if (type == m_thermo2.ID) { + m_thermo2.install(name, sp, 0, c, minTemp, maxTemp, + refPressure); + speciesToType[sp] = m_thermo2.ID; + } else { + throw UnknownSpeciesThermo("SpeciesThermoDuo:install",type); + } + } + + //! Compute the reference-state properties for all species. + /*! + * Given temperature T in K, this method updates the values of + * the non-dimensional heat capacity at constant pressure, + * enthalpy, and entropy, at the reference pressure, Pref + * of each of the standard states. + * + * @param t Temperature (Kelvin) + * @param cp_R Vector of Dimensionless heat capacities. + * (length m_kk). + * @param h_RT Vector of Dimensionless enthalpies. + * (length m_kk). + * @param s_R Vector of Dimensionless entropies. + * (length m_kk). + */ + virtual void update(doublereal t, doublereal* cp_R, + doublereal* h_RT, doublereal* s_R) const { + m_thermo1.update(t, cp_R, h_RT, s_R); + m_thermo2.update(t, cp_R, h_RT, s_R); + } + + //! Minimum temperature. + /*! + * If no argument is supplied, this + * method returns the minimum temperature for which \e all + * parameterizations are valid. If an integer index k is + * supplied, then the value returned is the minimum + * temperature for species k in the phase. + * + * @param k Species index + */ + virtual doublereal minTemp(int k = -1) const { + doublereal tm1 = m_thermo1.minTemp(); + doublereal tm2 = m_thermo2.minTemp(); + return (tm1 < tm2 ? tm2 : tm1); + } + + //! Maximum temperature. + /*! + * If no argument is supplied, this + * method returns the maximum temperature for which \e all + * parameterizations are valid. If an integer index k is + * supplied, then the value returned is the maximum + * temperature for parameterization k. + * + * @param k index for parameterization k + */ + virtual doublereal maxTemp(int k = -1) const { + doublereal tm1 = m_thermo1.maxTemp(); + doublereal tm2 = m_thermo2.maxTemp(); + return (tm1 < tm2 ? tm1 : tm2); + } + + /** + * The reference-state pressure for species k. + * + * returns the reference state pressure in Pascals for + * species k. If k is left out of the argument list, + * it returns the reference state pressure for the first + * species. + * Note that some SpeciesThermo implementations, such + * as those for ideal gases, require that all species + * in the same phase have the same reference state pressures. + * + * @param k index for parameterization k + */ + virtual doublereal refPressure(int k = -1) const { + return m_p0; + } + + //! This utility function reports the type of parameterization + //! used for the species with index number index. + /*! + * + * @param k Species index + */ + virtual int reportType(int k) const { + std::map::const_iterator p = speciesToType.find(k); + if (p != speciesToType.end()) { + const int type = p->second; + return type; + } + return -1; + } + + /*! + * This utility function reports back the type of + * parameterization and all of the parameters for the + * species, index. + * + * @param index Species index + * @param type Integer type of the standard type + * @param c Vector of coefficients used to set the + * parameters for the standard state. + * @param minTemp output - Minimum temperature + * @param maxTemp output - Maximum temperature + * @param refPressure output - reference pressure (Pa). + * + */ + virtual void reportParams(int index, int &type, + doublereal * const c, + doublereal &minTemp, + doublereal &maxTemp, + doublereal &refPressure) const { + int ctype = reportType(index); + if (ctype == m_thermo1.ID) { + m_thermo1.reportParams(index, type, c, minTemp, maxTemp, + refPressure); + } else if (ctype == m_thermo2.ID) { + m_thermo2.reportParams(index, type, c, minTemp, maxTemp, + refPressure); + } else { + throw CanteraError(" ", "confused"); + } + } + + //! Modify parameters for the standard state + /*! + * @param index Species index + * @param c Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void modifyParams(int index, doublereal *c) { + int ctype = reportType(index); + if (ctype == m_thermo1.ID) { + m_thermo1.modifyParams(index, c); + } else if (ctype == m_thermo2.ID) { + m_thermo2.modifyParams(index, c); + } else { + throw CanteraError("modifyParams", "confused"); + } + } + + + private: + + //! Thermo Type 1 + T1 m_thermo1; + //! Thermo Type 2 + T2 m_thermo2; + //! Reference pressure + doublereal m_p0; + //! map from species to type + std::map speciesToType; + }; + + //! This species thermo manager requires that all species have the + //! same parameterization. + /*! + * + * This is a templated class. The first template is called SPM. SPM + * is an object that calculates the thermo for one species. This + * class contains a vector of SPM's, one for each + * species. Together, the vector of SPM's is itself a SpeciesThermo + * class. + * + * @todo The form of the template class, SPM, is basically + * unspecified. it needs to be nailed down to a specific + * form. One way to do this is with a virtual base class + * formulation. Note, that the specification could be that it + * inherits from the class SpeciesThermo, itself. + * + * @deprecated Note this is currently unused and it may be on its way out. + * + * @ingroup spthermo + */ + template + class SpeciesThermo1 : public SpeciesThermo { + + public: + //! base constructor + SpeciesThermo1() : m_pref(0.0) {} + //! destructor + virtual ~SpeciesThermo1(){} + + //! Install one species into this Species Thermo Manager + /*! + * @param name Name of the species + * @param sp Species index + * @param type species type in terms of an int + * @param c Parameters for the species thermo + */ + virtual void install(std::string name, int sp, int type, const vector_fp& c) { + m_thermo.push_back(SPM(sp, c)); + if (m_pref) { + if (m_thermo.begin()->refPressure() != m_pref) { + throw RefPressureMismatch("SpeciesThermo1:install", + refPressure(), m_pref); + } + } + else m_pref = m_thermo.begin()->refPressure(); + } + + //! update the object, because the temperature changed + /*! + * @param t temperature(Kelvin) + * @param cp_R vector of dimensionless heat capacity + * @param h_RT vector of dimensionless enthalpy + * @param s_R vector of dimensionless entropy + */ + virtual void update(doublereal t, vector_fp& cp_R, + vector_fp& h_RT, vector_fp& s_R) const { + _updateAll(m_thermo.begin(),m_thermo.end(), + t, cp_R, h_RT, s_R); + } + + //! update the object for one species, because the temperature changed + /*! + * @param k species index + * @param t temperature(Kelvin) + * @param cp_R vector of dimensionless heat capacity + * @param h_RT vector of dimensionless enthalpy + * @param s_R vector of dimensionless entropy + */ + virtual void update_one(int k, doublereal t, vector_fp& cp_R, + vector_fp& h_RT, vector_fp& s_R) const { + m_thermo[k]->update(t, cp_R, h_RT, s_R); + } + + //! returns the minimum temperature + /*! + * @param k species index. Defaults to -1. + */ + virtual doublereal minTemp(int k = -1) const { + if (k < 0) + return _minTemp(m_thermo.begin(), m_thermo.end()); + else + return m_thermo[k].minTemp(); + } + + //! returns the maximum temperature + /*! + * @param k species index. Defaults to -1. + */ + virtual doublereal maxTemp(int k = -1) const { + if (k < 0) + return _maxTemp(m_thermo.begin(), m_thermo.end()); + else + return m_thermo[k].maxTemp(); + } + + //! returns the reference pressure + /*! + * @param k species index. Defaults to -1. + */ + virtual doublereal refPressure(int k = -1) const { + return m_pref; + } + + //! This utility function reports the type of parameterization + //! used for the species with index number index. + /*! + * Note, all parameterizations are the same, by definition, here + * + * @param k Species index + */ + virtual int reportType(int k) const { + return m_thermo[k]->reportType(-1); + } + + /*! + * This utility function reports back the type of + * parameterization and all of the parameters for the + * species, index. + * + * @param index Species index + * @param type Integer type of the standard type + * @param c Vector of coefficients used to set the + * parameters for the standard state. + * @param minTemp output - Minimum temperature + * @param maxTemp output - Maximum temperature + * @param refPressure output - reference pressure (Pa). + */ + virtual void reportParams(int index, int &type, + doublereal * const c, + doublereal &minTemp, + doublereal &maxTemp, + doublereal &refPressure) const { + m_thermo[index]->reportParameters(index, type, c, minTemp, maxTemp, refPressure); + } + + //! Modify parameters for the standard state + /*! + * @param index Species index + * @param c Vector of coefficients used to set the + * parameters for the standard state. + */ + virtual void modifyParams(int index, doublereal *c) { + m_thermo[index]->modifyParameters(index, c); + } + + private: + //! Vector of SPM objects. There are m_kk of them + std::vector m_thermo; + //! Reference pressure (Pa) + doublereal m_pref; + }; + //#endif + +} + +#endif diff --git a/Cantera/src/thermo/State.cpp b/Cantera/src/thermo/State.cpp new file mode 100644 index 000000000..6955c3aa2 --- /dev/null +++ b/Cantera/src/thermo/State.cpp @@ -0,0 +1,227 @@ +/** + * + * @file State.cpp + * Definitions for the class State, that manages the independent variables of temperature, mass density, + * and species mass/mole fraction that define the thermodynamic state (see \ref phases and + * class \link Cantera::State State\endlink). + * + */ + +/* + * $Author$ + * $Date$ + * $Revision$ + * + * Copyright 2003-2004 California Institute of Technology + * See file License.txt for licensing information + * + */ + +#include "utilities.h" +#include "ctexceptions.h" +#include "stringUtils.h" +#include "State.h" + +//#ifdef DARWIN +//#include +//#endif + +using namespace std; + +namespace Cantera { + + State::State() : m_kk(0), m_temp(0.0), m_dens(0.001), m_mmw(0.0) {} + + State::~State() {} + + State::State(const State& right) : + m_kk(0), + m_temp(0.0), + m_dens(0.001), + m_mmw(0.0) { + /* + * Call the assignment operator. + */ + *this = operator=(right); + } + + /* + * Assignment operator for the State Class + */ + State& State::operator=(const State& right) { + /* + * Check for self assignment. + */ + if (this == &right) return *this; + /* + * We do a straight assignment operator on all of the + * data. The vectors are copied. + */ + m_temp = right.m_temp; + m_dens = right.m_dens; + m_mmw = right.m_mmw; + m_y = right.m_y; + m_molwts = right.m_molwts; + m_rmolwts = right.m_rmolwts; + /* + * Return the reference to the current object + */ + return *this; + } + + doublereal State::moleFraction(int k) const { + if (k >= 0 && k < m_kk) { + return m_ym[k] * m_mmw; + } + else { + throw CanteraError("State:moleFraction", + "illegal species index number"); + } + } + + void State::setMoleFractions(const doublereal* x) { + int k; + doublereal sum = 0.0, norm = 0.0; + sum = dot(x, x + m_kk, m_molwts.begin()); + for (k = 0; k != m_kk; ++k) { + m_ym[k] = x[k] / sum; + m_y[k] = m_molwts[k]*m_ym[k]; + norm += x[k]; + } + m_mmw = sum/norm; + } + + void State::setMoleFractions_NoNorm(const doublereal* x) { + int k; + m_mmw = dot(x, x + m_kk, m_molwts.begin()); + doublereal rmmw = 1.0/m_mmw; + for (k = 0; k != m_kk; ++k) { + m_ym[k] = x[k]*rmmw; + m_y[k] = m_ym[k] * m_molwts[k]; + } + } + + doublereal State::massFraction(int k) const { + if (k >= 0 && k < m_kk) { + return m_y[k]; + } + else { + throw CanteraError("State:massFraction", + "illegal species index number"); + } + } + + doublereal State::concentration(int k) const { + if (k >= 0 && k < m_kk) { + return m_y[k] * m_dens * m_rmolwts[k] ; + } + else { + throw CanteraError("State:massFraction", + "illegal species index number"); + } + } + + void State::setMassFractions(const doublereal* y) { + doublereal norm = 0.0, sum = 0.0; + int k; + //cblas_dcopy(m_kk, y, 1, m_y.begin(), 1); + for (k = 0; k != m_kk; ++k) { + norm += y[k]; + m_y[k] = y[k]; + } + //scale(y, y + m_kk, m_y.begin(), 1.0/norm); + scale(m_kk, 1.0/norm, m_y.begin()); + + for (k = 0; k != m_kk; ++k) { + m_ym[k] = m_y[k] * m_rmolwts[k]; + sum += m_ym[k]; + } + m_mmw = 1.0/sum; + } + + void State::setMassFractions_NoNorm(const doublereal* y) { + int k; + doublereal sum = 0.0; + for (k = 0; k != m_kk; ++k) { + m_y[k] = y[k]; + m_ym[k] = m_y[k] * m_rmolwts[k]; + sum += m_ym[k]; + } + m_mmw = 1.0/sum; + } + + doublereal State::sum_xlogx() const { + return m_mmw* Cantera::sum_xlogx(m_ym.begin(), m_ym.end()) + log(m_mmw); + } + + doublereal State::sum_xlogQ(doublereal* Q) const { + return m_mmw * Cantera::sum_xlogQ(m_ym.begin(), m_ym.end(), Q); + } + + void State::setConcentrations(const doublereal* c) { + int k; + doublereal sum = 0.0, norm = 0.0; + for (k = 0; k != m_kk; ++k) { + sum += c[k]*m_molwts[k]; + norm += c[k]; + } + m_mmw = sum/norm; + setDensity(sum); + doublereal rsum = 1.0/sum; + for (k = 0; k != m_kk; ++k) { + m_ym[k] = c[k] * rsum; + m_y[k] = m_ym[k] * m_molwts[k]; + } + } + + void State::getConcentrations(doublereal* c) const { + scale(m_ym.begin(), m_ym.end(), c, m_dens); + } + + doublereal State::mean_Y(const doublereal* Q) const { + return dot(m_y.begin(), m_y.end(), Q); + } + + void State::getMoleFractions(doublereal* x) const { + scale(m_ym.begin(), m_ym.end(), x, m_mmw); + } + + void State::getMassFractions(doublereal* y) const { + copy(m_y.begin(), m_y.end(), y); + } + + void State::init(const array_fp& mw) { + m_kk = mw.size(); + m_molwts.resize(m_kk); + m_rmolwts.resize(m_kk); + m_y.resize(m_kk, 0.0); + m_ym.resize(m_kk, 0.0); + copy(mw.begin(), mw.end(), m_molwts.begin()); + for (int k = 0; k < m_kk; k++) { + if (m_molwts[k] < 0.0) { + throw CanteraError("State::init", + "negative molecular weight for species number "+int2str(k)); + } + /* + * Some surface phases may define species representing + * empty sites that have zero molecular weight. Give them + * a very small molecular weight to avoid dividing by + * zero. + */ + if (m_molwts[k] < Tiny) m_molwts[k] = Tiny; + m_rmolwts[k] = 1.0/m_molwts[k]; + } + + /* + * Now that we have resized the State object, let's fill it with + * a valid mass fraction vector that sums to one. The State object + * should never have a mass fraction vector that doesn't sum to one. + * We will assume that species 0 has a mass fraction of 1.0 and + * mass fraction of all other species is 0.0. + */ + m_y[0] = 1.0; + m_ym[0] = m_y[0] * m_rmolwts[0]; + m_mmw = 1.0 / m_ym[0]; + } + +} diff --git a/Cantera/src/thermo/State.h b/Cantera/src/thermo/State.h new file mode 100755 index 000000000..fb433f99d --- /dev/null +++ b/Cantera/src/thermo/State.h @@ -0,0 +1,415 @@ +/** + * @file State.h + * Header for the class State, that manages the independent variables of temperature, mass density, + * and species mass/mole fraction that define the thermodynamic state (see \ref phases and + * class \link Cantera::State State\endlink). + */ + +/* + * $Author$ + * $Date$ + * $Revision$ + * + * Copyright 2001-2003 California Institute of Technology + * See file License.txt for licensing information + * + */ + +#ifndef CT_STATE2_H +#define CT_STATE2_H + +#include "ct_defs.h" + +namespace Cantera { + + + //! Manages the independent variables of temperature, mass density, + //! and species mass/mole fraction that define the thermodynamic + //! state. + /*! + * Class State stores just enough information about a + * multicomponent solution to specify its intensive thermodynamic + * state. It stores values for the temperature, mass density, and + * an array of species mass fractions. It also stores an array of + * species molecular weights, which are used to convert between + * mole and mass representations of the composition. These are the + * \e only properties of the species that class State knows about. + * For efficiency in mass/mole conversion, the vector of mass + * fractions divided by molecular weight \f$ Y_k/M_k \f$ is also + * stored. + * + * Class State is not usually used directly in application + * programs. Its primary use is as a base class for class + * Phase. Class State has no virtual methods, and none of its + * methods are meant to be overloaded. However, this is one exception. + * If the phase is incompressible, then the density must be replaced + * by the pressure as the independent variable. In this case, functions + * such as setMassFraction within the class %State must actually now + * calculate the density (at constant T and P) instead of leaving + * it alone as befits an independent variable. Threfore, these type + * of functions are virtual functions and need to be overloaded + * for incompressible phases. Note, for almost incompressible phases + * (or phases which utilize standard states based on a T and P) this + * may be advantageous as well, and they need to overload these functions + * too. + * + * @ingroup phases + */ + class State { + + public: + + /** + * Constructor. + */ + State(); + + /** + * Destructor. Since no memory is allocated by methods of this + * class, the destructor does nothing. + */ + virtual ~State(); + + /** + * Copy Constructor for the State Class + * + * @param right Reference to the class to be copied. + */ + State(const State& right); + + /** + * Assignment operator for the state class. + * + * @param right Reference to the class to be copied. + */ + State& operator=(const State& right); + + + /// @name Species Information + /// + /// The only thing class State knows about the species is their + /// molecular weights. + //@{ + + /// Return a read-only reference to the array of molecular + /// weights. + const array_fp& molecularWeights() const { return m_molwts; } + + + //@} + /// @name Composition + //@{ + + + //! Get the species mole fraction vector. + /*! + * @param x On return, x contains the mole fractions. Must have a + * length greater than or equal to the number of species. + */ + void getMoleFractions(doublereal* x) const; + + + //! The mole fraction of species k. + /*! + * If k is ouside the valid + * range, an exception will be thrown. Note that it is + * somewhat more efficent to call getMoleFractions if the + * mole fractions of all species are desired. + * @param k species index + */ + doublereal moleFraction(int k) const; + + /** + * Set the mole fractions to the specified values, and then + * normalize them so that they sum to 1.0. + * @param x Array of unnormalized mole fraction values (input). + * Must have a length greater than or equal to the number of + * species. + * + * @param x Input vector of mole fractions. + * Length is m_kk. + */ + virtual void setMoleFractions(const doublereal* x); + + /** + * Set the mole fractions to the specified values without + * normalizing. This is useful when the normalization + * condition is being handled by some other means, for example + * by a constraint equation as part of a larger set of + * equations. + * + * @param x Input vector of mole fractions. + * Length is m_kk. + */ + virtual void setMoleFractions_NoNorm(const doublereal* x); + + /** + * Get the species mass fractions. + * @param y On return, y + * contains the mass fractions. Array \a y must have a length + * greater than or equal to the number of species. + * + * @param y Output vector of mass fractions. + * Length is m_kk. + */ + void getMassFractions(doublereal* y) const; + + //! Mass fraction of species k. + /*! + * If k is outside the valid + * range, an exception will be thrown. Note that it is + * somewhat more efficent to call getMassFractions if the + * mass fractions of all species are desired. + * + * @param k species index + */ + doublereal massFraction(int k) const; + + /** + * Set the mass fractions to the specified values, and then + * normalize them so that they sum to 1.0. + * @param y Array of unnormalized mass fraction values (input). + * Must have a length greater than or equal to the number of + * species. + * + * @param y Input vector of mass fractions. + * Length is m_kk. + */ + virtual void setMassFractions(const doublereal* y); + + /** + * Set the mass fractions to the specified values without + * normalizing. This is useful when the normalization + * condition is being handled by some other means, for example + * by a constraint equation as part of a larger set of + * equations. + * + * @param y Input vector of mass fractions. + * Length is m_kk. + */ + virtual void setMassFractions_NoNorm(const doublereal* y); + + /** + * Get the species concentrations (kmol/m^3). @param c On + * return, \a c contains the concentrations for all species. + * Array \a c must have a length greater than or equal to the + * number of species. + */ + void getConcentrations(doublereal* c) const; + + /** + * Concentration of species k. If k is outside the valid + * range, an exception will be thrown. + * + * @param k Index of species + */ + doublereal concentration(int k) const; + + /** + * Set the concentrations to the specified values within the + * phase. + * + * @param c The input vector to this routine is in dimensional + * units. For volumetric phases c[k] is the + * concentration of the kth species in kmol/m3. + * For surface phases, c[k] is the concentration + * in kmol/m2. The length of the vector is the number + * of species in the phase. + */ + virtual void setConcentrations(const doublereal* c); + + /** + * Returns a read-only pointer to the start of the + * massFraction array + */ + const doublereal* massFractions() const { return &m_y[0]; } + + /** + * Returns a read-only pointer to the start of the + * moleFraction/MW array. This array is the array of mole + * fractions, each divided by the mean molecular weight. + */ + const doublereal* moleFractdivMMW() const { return &m_ym[0];} + + + //@} + + /// @name Mean Properties + //@{ + /** + * Evaluate the mole-fraction-weighted mean of Q: + * \f[ \sum_k X_k Q_k. \f] + * Array Q should contain pure-species molar property + * values. + * + * @param Q input vector of length m_kk that is to be averaged. + * @return + * mole-freaction-weighted mean of Q + */ + doublereal mean_X(const doublereal* Q) const { + return m_mmw*std::inner_product(m_ym.begin(), m_ym.end(), Q, 0.0); + } + + /** + * Evaluate the mass-fraction-weighted mean of Q: + * \f[ \sum_k Y_k Q_k \f] + * + * @param Q Array Q contains a vector of species property values in mass units. + * @return + * Return value containing the mass-fraction-weighted mean of Q. + */ + doublereal mean_Y(const doublereal* Q) const; + + /** + * The mean molecular weight. Units: (kg/kmol) + */ + doublereal meanMolecularWeight() const { + return m_mmw; + } + + //! Evaluate \f$ \sum_k X_k \log X_k \f$. + /*! + * @return + * returns the indicated sum. units are dimensionless. + */ + doublereal sum_xlogx() const; + + //! Evaluate \f$ \sum_k X_k \log Q_k \f$. + /*! + * @param Q Vector of length m_kk to take the log average of + * @return Returns the indicated sum. + */ + doublereal sum_xlogQ(doublereal* Q) const; + //@} + + /// @name Thermodynamic Properties + /// Class State only stores enough thermodynamic data to + /// specify the state. In addition to composition information, + /// it stores the temperature and + /// mass density. + //@{ + + /// Temperature (K). + doublereal temperature() const { return m_temp; } + + /// Density (kg/m^3). + doublereal density() const { return m_dens; } + + /// Molar density (kmol/m^3). + doublereal molarDensity() const { + return m_dens/meanMolecularWeight(); + } + + //! Set the internally storred density (kg/m^3) of the phase + /*! + * Note the density of a phase is an indepedent variable. + * + * @param density Input density (kg/m^3). + */ + virtual void setDensity(doublereal density) { + m_dens = density; + } + + //! Set the internally storred molar density (kmol/m^3) of the phase. + /*! + * @param molarDensity Input molar density (kmol/m^3). + */ + virtual void setMolarDensity(doublereal molarDensity) { + m_dens = molarDensity*meanMolecularWeight(); + } + + //! Set the temperature (K). + /*! + * This function sets the internally storred temperature of the phase. + * + * @param temp Temperature in kelvin + * + * @todo Make State::setTemperature a virtual function + */ + void setTemperature(doublereal temp) { + m_temp = temp; + } + //@} + + //! True if the number species has been set + bool ready() const { return (m_kk > 0); } + + + protected: + + /** + * @internal + * Initialize. Make a local copy of the vector of + * molecular weights, and resize the composition arrays to + * the appropriate size. The only information an instance of + * State has about the species is their molecular weights. + * + * @param mw Vector of molecular weights of the species. + */ + void init(const array_fp& mw); //, density_is_independent = true); + + /** + * m_kk is the number of species in the phase + */ + int m_kk; + + //! Set the molecular weight of a single species to a given value + /*! + * @param k id of the species + * @param mw Molecular Weight (kg kmol-1) + */ + void setMolecularWeight(int k, double mw) { + m_molwts[k] = mw; + m_rmolwts[k] = 1.0/mw; + } + + private: + + /** + * Temperature. This is an independent variable + * units = Kelvin + */ + doublereal m_temp; + + /** + * Density. This is an independent variable except in + * the incompressible degenerate case. Thus, + * the pressure is determined from this variable + * not the other way round. + * units = kg m-3 + */ + doublereal m_dens; + + /** + * m_mmw is the mean molecular weight of the mixture + * (kg kmol-1) + */ + doublereal m_mmw; + + /** + * m_ym[k] = mole fraction of species k divided by the + * mean molecular weight of mixture. + */ + mutable array_fp m_ym; + + /** + * m_y[k] = mass fraction of species k + */ + mutable array_fp m_y; + + /** + * m_molwts[k] = molecular weight of species k (kg kmol-1) + */ + array_fp m_molwts; + + /** + * m_rmolwts[k] = inverse of the molecular weight of species k + * units = kmol kg-1. + */ + array_fp m_rmolwts; + + }; + +} + +#endif diff --git a/Cantera/src/thermo/StoichSubstance.cpp b/Cantera/src/thermo/StoichSubstance.cpp new file mode 100644 index 000000000..3a8f04fd8 --- /dev/null +++ b/Cantera/src/thermo/StoichSubstance.cpp @@ -0,0 +1,74 @@ +/** + * + * @file StoichSubstance.cpp + * + */ + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "ct_defs.h" +#include "mix_defs.h" +#include "StoichSubstance.h" +#include "SpeciesThermo.h" + +namespace Cantera { + + void StoichSubstance::initThermo() { + m_kk = nSpecies(); + if (m_kk > 1) { + throw CanteraError("initThermo", + "stoichiometric substances may only contain one species."); + } + doublereal tmin = m_spthermo->minTemp(); + doublereal tmax = m_spthermo->maxTemp(); + if (tmin > 0.0) m_tmin = tmin; + if (tmax > 0.0) m_tmax = tmax; + m_p0 = refPressure(); + + int leng = m_kk; + m_h0_RT.resize(leng); + m_cp0_R.resize(leng); + m_s0_R.resize(leng); + } + + + void StoichSubstance::_updateThermo() const { + doublereal tnow = temperature(); + if (m_tlast != tnow) { + m_spthermo->update(tnow, &m_cp0_R[0], &m_h0_RT[0], + &m_s0_R[0]); + m_tlast = tnow; + } + } + + void StoichSubstance:: + getUnitsStandardConc(double *uA, int k, int sizeUA) { + for (int i = 0; i < sizeUA; i++) { + uA[i] = 0.0; + } + } + + void StoichSubstance::setParameters(int n, double * c) { + double rho = c[0]; + setDensity(rho); + } + + void StoichSubstance::getParameters(int &n, double * const c) { + double rho = density(); + c[0] = rho; + } + + void StoichSubstance::setParametersFromXML(const XML_Node& eosdata) { + eosdata._require("model","StoichSubstance"); + doublereal rho = getFloat(eosdata, "density", "-"); + setDensity(rho); + } + +} + + + + diff --git a/Cantera/src/thermo/StoichSubstance.h b/Cantera/src/thermo/StoichSubstance.h new file mode 100644 index 000000000..7b35751af --- /dev/null +++ b/Cantera/src/thermo/StoichSubstance.h @@ -0,0 +1,429 @@ +/** + * + * @file StoichSubstance.h + * + * This file contains the class declarations for the StoichSubstance + * ThermoPhase class. + */ + +/* $Author$ + * $Date$ + * $Revision$ + * + * Copyright 2001 California Institute of Technology + * + */ + + +#ifndef CT_STOICHSUBSTANCE_H +#define CT_STOICHSUBSTANCE_H + +#include "mix_defs.h" +#include "ThermoPhase.h" +#include "SpeciesThermo.h" + +namespace Cantera { + + /** + * @ingroup thermoprops + * + * Class StoichSubstance represents a stoichiometric (fixed composition) + * incompressible substance. + * \nosubgrouping + * + */ + class StoichSubstance : public ThermoPhase { + + public: + + StoichSubstance(): + m_kk(0), + m_tmin(0.0), + m_tmax(0.0), + m_press(OneAtm), + m_p0(OneAtm), + m_tlast(-1.0) {} + + virtual ~StoichSubstance() {} + + /** + * + * @name Utilities + * @{ + */ + + /** + * Equation of state flag. Returns the value cStoichSubstance, + * defined in mix_defs.h. + */ + virtual int eosType() const { return cStoichSubstance; } + + + /** + * @} + * @name Molar Thermodynamic Properties of the Solution --------- + * @{ + */ + + /** + * Molar enthalpy. Units: J/kmol. For an incompressible, + * stoichiometric substance, the internal energy is + * independent of pressure, and therefore the molar enthalpy + * is \f[ \hat h(T, P) = \hat u(T) + P \hat v \f], where the + * molar specific volume is constant. + */ + virtual doublereal enthalpy_mole() const { + double hh = intEnergy_mole() + m_press / molarDensity(); + return hh; + } + + /** + * Molar internal energy. J/kmol. For an incompressible, + * stoichiometric substance, the molar internal energy is + * independent of pressure. Since the thermodynamic properties + * are specified by giving the standard-state enthalpy, the + * term \f$ P_0 \hat v\f$ is subtracted from the specified molar + * enthalpy to compute the molar internal energy. + */ + virtual doublereal intEnergy_mole() const { + _updateThermo(); + return GasConstant * temperature() * m_h0_RT[0] + - m_p0 / molarDensity(); + } + + /** + * Molar entropy. Units: J/kmol/K. For an incompressible, + * stoichiometric substance, the molar entropy depends only on + * the temperature. + */ + virtual doublereal entropy_mole() const { + _updateThermo(); + return GasConstant * m_s0_R[0]; + } + + + /** + * Molar gibbs Function. Units: J/kmol. This is determined + * from the molar enthalpy and entropy functions. + */ + virtual doublereal gibbs_mole() const { + return enthalpy_mole() - temperature() * entropy_mole(); + } + + + /** + * Molar heat capacity at constant pressure. Units: J/kmol/K. + * For an incompressible substance, \f$ \hat c_p = \hat c_v\f$. + */ + virtual doublereal cp_mole() const { + _updateThermo(); + return GasConstant * m_cp0_R[0]; + } + + /** + * Molar heat capacity at constant volume. Units: J/kmol/K. + * For an incompressible substance, \f$ \hat c_p = \hat c_v\f$. + */ + virtual doublereal cv_mole() const { + return cp_mole(); + } + + //@} + + + /** + * @name Mechanical Equation of State + * @{ + */ + + + //! Report the Pressure. Units: Pa. + /*! + * For an incompressible substance, the density is independent + * of pressure. This method simply returns the storred + * pressure value. + */ + virtual doublereal pressure() const { + return m_press; + } + + + //! Set the pressure at constant temperature. Units: Pa. + /*! + * For an incompressible substance, the density is + * independent of pressure. Therefore, this method only + * stores the specified pressure value. It does not + * modify the density. + * + * @param p Pressure (units - Pa) + */ + virtual void setPressure(doublereal p) { + m_press = p; + } + + //@} + + /** + * @name Chemical Potentials and Activities + *@{ + */ + + /** + * This method returns the array of generalized + * concentrations. For a stoichiometric substance, there is + * only one species, and the generalized concentration is 1.0. + */ + virtual void getActivityConcentrations(doublereal* c) const { + c[0] = 1.0; + } + + /** + * The standard concentration. This is defined as the concentration + * by which the generalized concentration is normalized to produce + * the activity. + */ + virtual doublereal standardConcentration(int k=0) const { + return 1.0; + } + + /** + * Returns the natural logarithm of the standard + * concentration of the kth species + */ + virtual doublereal logStandardConc(int k=0) const { + return 0.0; + } + + /** + * Get the array of chemical potentials at unit activity + * \f$ \mu^0_k \f$. + * + * For a stoichiometric substance, there is no activity term in + * the chemical potential expression, and therefore the + * standard chemical potential and the chemical potential + * are both equal to the molar Gibbs function. + */ + virtual void getStandardChemPotentials(doublereal* mu0) const { + mu0[0] = gibbs_mole(); + } + + /** + * Returns the units of the standard and generalized + * concentrations Note they have the same units, as their + * ratio 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. + * + * uA[0] = kmol units - default = 0 + * uA[1] = m units - default = 0 + * 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 + */ + virtual void getUnitsStandardConc(double *uA, int k = 0, + int sizeUA = 6); + + + //@} + /// @name Partial Molar Properties of the Solution ---------------------------------- + //@{ + + + /** + * Get the array of non-dimensional chemical potentials + * \f$ \mu_k / \hat R T \f$. + */ + virtual void getChemPotentials_RT(doublereal* mu) const { + mu[0] = gibbs_mole() / (GasConstant * temperature()); + } + + /** + * For a stoichiometric substance, there is only one species. + * This method returns the molar gibbs function in the + * first element of array \c mu. + */ + virtual void getChemPotentials(doublereal* mu) const { + mu[0] = gibbs_mole(); + } + + /** + * Get the species electrochemical potentials. Units: J/kmol. + * This method adds a term \f$ Fz_k \phi_k \f$ to the + * to each chemical potential. + */ + void getElectrochemPotentials(doublereal* mu) const { + getChemPotentials(mu); + } + + /** + * Returns an array of partial molar enthalpies for the species + * in the mixture. + * Units (J/kmol) + */ + virtual void getPartialMolarEnthalpies(doublereal* hbar) const { + hbar[0] = enthalpy_mole(); + } + + /** + * Returns an array of partial molar entropies of the species in the + * solution. Units: J/kmol/K. + */ + virtual void getPartialMolarEntropies(doublereal* sbar) const { + sbar[0] = entropy_mole(); + } + + /** + * returns an array of partial molar volumes of the species + * in the solution. Units: m^3 kmol-1. + */ + virtual void getPartialMolarVolumes(doublereal* vbar) const { + vbar[0] = 1.0 / molarDensity(); + } + + + //@} + /// @name Properties of the Standard State of the Species in the Solution ------------------------------------- + //@{ + /** + * Get the nondimensional Enthalpy functions for the species + * at their standard states at the current + * T and P of the solution. + */ + virtual void getEnthalpy_RT(doublereal* hrt) const { + hrt[0] = enthalpy_mole() / (GasConstant * temperature()); + } + + + /** + * Get the array of nondimensional Enthalpy functions for the + * standard state species + * at the current T and P of the solution. + */ + virtual void getEntropy_R(doublereal* sr) const { + sr[0] = entropy_mole() / GasConstant; + } + + /** + * Get the nondimensional Gibbs functions for the species + * at their standard states of solution at the current T and P + * of the solution. + */ + virtual void getGibbs_RT(doublereal* grt) const { + grt[0] = gibbs_mole() / (GasConstant * temperature()); + } + + /** + * Get the nondimensional Heat Capacities at constant + * pressure for the standard state of the species + * at the current T and P. + */ + virtual void getCp_R(doublereal* cpr) const { + cpr[0] = cp_mole() / GasConstant; + } + + /** + * Get the standard volumes for the standard state of the species + * at the current T and P + */ + virtual void getStandardVolumes(doublereal*vol) const { + vol[0] = 1.0 / molarDensity(); + } + + //@} + /// @name Thermodynamic Values for the Species Reference States -------------------- + //@{ + + /** + * Returns the vector of nondimensional + * enthalpies of the reference state at the current temperature + * of the solution and the reference pressure for the species. + * + * This function fills in its one entry in hrt[] by calling + * the underlying species thermo function for the + * dimensionless enthalpy. + */ + virtual void getEnthalpy_RT_ref(doublereal *hrt) const { + _updateThermo(); + hrt[0] = m_h0_RT[0]; + } + + /** + * Returns the vector of nondimensional + * enthalpies of the reference state at the current temperature + * of the solution and the reference pressure for the species. + * + * This function fills in its one entry in hrt[] by calling + * the underlying species thermo function for the + * dimensionless gibbs free energy, calculated from the + * dimensionless enthalpy and entropy. + */ + virtual void getGibbs_RT_ref(doublereal *grt) const { + _updateThermo(); + grt[0] = m_h0_RT[0] - m_s0_R[0]; + } + + /** + * Returns the vector of the + * gibbs function of the reference state at the current temperature + * of the solution and the reference pressure for the species. + * units = J/kmol + * + * This function fills in its one entry in g[] by calling + * the underlying species thermo functions for the + * gibbs free energy, calculated from enthalpy and the + * entropy, and the multiplying by RT. + */ + virtual void getGibbs_ref(doublereal *g) const { + getGibbs_RT_ref(g); + g[0] *= GasConstant * temperature(); + } + + /** + * Returns the vector of nondimensional + * entropies of the reference state at the current temperature + * of the solution and the reference pressure for the species. + * + * This function fills in its one entry in hrt[] by calling + * the underlying species thermo function for the + * dimensionless entropy. + */ + virtual void getEntropy_R_ref(doublereal *er) const { + _updateThermo(); + er[0] = m_s0_R[0]; + } + + + virtual void initThermo(); + + virtual void setParameters(int n, double *c); + virtual void getParameters(int &n, double * const c); + + virtual void setParametersFromXML(const XML_Node& eosdata); + + protected: + + int m_kk; + doublereal m_tmin, m_tmax, m_press, m_p0; + + mutable doublereal m_tlast; + mutable array_fp m_h0_RT; + mutable array_fp m_cp0_R; + mutable array_fp m_s0_R; + + private: + + void _updateThermo() const; + }; + +} + +#endif + + + + + diff --git a/Cantera/src/thermo/StoichSubstanceSSTP.cpp b/Cantera/src/thermo/StoichSubstanceSSTP.cpp index fc10e8617..79f207b45 100644 --- a/Cantera/src/thermo/StoichSubstanceSSTP.cpp +++ b/Cantera/src/thermo/StoichSubstanceSSTP.cpp @@ -22,7 +22,8 @@ #include "StoichSubstanceSSTP.h" #include "SpeciesThermo.h" #include -#include "importCTML.h" +//#include "importCTML.h" +#include "ThermoFactory.h" namespace Cantera { diff --git a/Cantera/src/thermo/SurfPhase.cpp b/Cantera/src/thermo/SurfPhase.cpp new file mode 100644 index 000000000..938be29f6 --- /dev/null +++ b/Cantera/src/thermo/SurfPhase.cpp @@ -0,0 +1,303 @@ +/** + * @file SurfPhase.cpp + * Definitions for a simple thermoydnamics model of a surface phase derived from ThermoPhase, + * assuming an ideal solution model + * (see \ref thermoprops and class \link Cantera::SurfPhase SurfPhase\endlink). + */ + +// Copyright 2002 California Institute of Technology + + +// turn off warnings under Windows +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "SurfPhase.h" +#include "EdgePhase.h" +#include "utilities.h" +//#include "importCTML.h" +#include "ThermoFactory.h" + +#include +using namespace std; + + + /////////////////////////////////////////////////////////// + // + // class SurfPhase methods + // + /////////////////////////////////////////////////////////// + +namespace Cantera { + + SurfPhase:: + SurfPhase(doublereal n0): + ThermoPhase(), + m_n0(n0), + m_logn0(0.0), + m_tmin(0.0), + m_tmax(0.0), + m_press(OneAtm), + m_tlast(0.0) + { + if (n0 > 0.0) m_logn0 = log(n0); + setNDim(2); + } + + SurfPhase::SurfPhase(XML_Node& xmlphase) { + const XML_Node& th = xmlphase.child("thermo"); + string model = th["model"]; + if (model != "Surface") { + throw CanteraError("SurfPhase::SurfPhase", + "thermo model attribute must be Surface"); + } + importPhase(xmlphase, this); + } + + + doublereal SurfPhase:: + enthalpy_mole() const { + if (m_n0 <= 0.0) return 0.0; + _updateThermo(); + return mean_X(DATA_PTR(m_h0)); + } + + SurfPhase:: + ~SurfPhase() { } + + /* + * For a surface phase, the pressure is not a relevant + * thermodynamic variable, and so the Enthalpy is equal to the + * internal energy. + */ + doublereal SurfPhase:: + intEnergy_mole() const { return enthalpy_mole(); } + + void SurfPhase:: + getStandardChemPotentials(doublereal* mu0) const { + _updateThermo(); + copy(m_mu0.begin(), m_mu0.end(), mu0); + } + + void SurfPhase:: + getChemPotentials(doublereal* mu) const { + _updateThermo(); + copy(m_mu0.begin(), m_mu0.end(), mu); + int k; + getActivityConcentrations(DATA_PTR(m_work)); + for (k = 0; k < m_kk; k++) { + mu[k] += GasConstant * temperature() * (log(m_work[k]) - logStandardConc(k)); + } + } + + void SurfPhase:: + getActivityConcentrations(doublereal* c) const { + getConcentrations(c); + } + + doublereal SurfPhase:: + standardConcentration(int k) const { + return m_n0/size(k); + } + + doublereal SurfPhase:: + logStandardConc(int k) const { + return m_logn0 - m_logsize[k]; + } + + + /// The only parameter that can be set is the site density. + void SurfPhase:: + setParameters(int n, doublereal* c) { + if (n != 1) { + throw CanteraError("SurfPhase::setParameters", + "Bad value for number of parameter"); + } + m_n0 = c[0]; + if (m_n0 <= 0.0) { + throw CanteraError("SurfPhase::setParameters", + "Bad value for parameter"); + } + m_logn0 = log(m_n0); + } + + void SurfPhase:: + getEnthalpy_RT(doublereal* hrt) const { + _updateThermo(); + double rrt = 1.0/(GasConstant*temperature()); + scale(m_h0.begin(), m_h0.end(), hrt, rrt); + } + + void SurfPhase:: + getEntropy_R(doublereal* sr) const { + _updateThermo(); + double rr = 1.0/GasConstant; + scale(m_s0.begin(), m_s0.end(), sr, rr); + } + + void SurfPhase:: + initThermo() { + m_h0.resize(m_kk); + m_s0.resize(m_kk); + m_cp0.resize(m_kk); + m_mu0.resize(m_kk); + m_work.resize(m_kk); + m_pe.resize(m_kk, 0.0); + vector_fp cov(m_kk, 0.0); + cov[0] = 1.0; + setCoverages(DATA_PTR(cov)); + m_logsize.resize(m_kk); + for (int k = 0; k < m_kk; k++) + m_logsize[k] = log(size(k)); + } + + void SurfPhase:: + setPotentialEnergy(int k, doublereal pe) { + m_pe[k] = pe; + _updateThermo(true); + } + + void SurfPhase:: + setSiteDensity(doublereal n0) { + doublereal x = n0; + setParameters(1, &x); + } + + + //void SurfPhase:: + //setElectricPotential(doublereal V) { + // for (int k = 0; k < m_kk; k++) { + // m_pe[k] = charge(k)*Faraday*V; + // } + // _updateThermo(true); + //} + + + /** + * Set the coverage fractions to a specified + * state. This routine converts to concentrations + * in kmol/m2, using m_n0, the surface site density, + * and size(k), which is defined to be the number of + * surface sites occupied by the kth molecule. + * It then calls State::setConcentrations to set the + * internal concentration in the object. + */ + void SurfPhase:: + setCoverages(const doublereal* theta) { + double sum = 0.0; + int k; + for (k = 0; k < m_kk; k++) sum += theta[k]; + + for (k = 0; k < m_kk; k++) { + m_work[k] = m_n0*theta[k]/(sum*size(k)); + } + /* + * Call the State:: class function + * setConcentrations. + */ + setConcentrations(DATA_PTR(m_work)); + } + + void SurfPhase:: + setCoveragesNoNorm(const doublereal* theta) { + for (int k = 0; k < m_kk; k++) { + m_work[k] = m_n0*theta[k]/(size(k)); + } + /* + * Call the State:: class function + * setConcentrations. + */ + setConcentrations(DATA_PTR(m_work)); + } + + void SurfPhase:: + getCoverages(doublereal* theta) const { + getConcentrations(theta); + for (int k = 0; k < m_kk; k++) { + theta[k] *= size(k)/m_n0; + } + } + + void SurfPhase:: + setCoveragesByName(std::string cov) { + int kk = nSpecies(); + int k; + compositionMap cc; + for (k = 0; k < kk; k++) { + cc[speciesName(k)] = -1.0; + } + parseCompString(cov, cc); + doublereal c; + vector_fp cv(kk, 0.0); + for (k = 0; k < kk; k++) { + c = cc[speciesName(k)]; + if (c > 0.0) cv[k] = c; + } + setCoverages(DATA_PTR(cv)); + } + + + void SurfPhase:: + _updateThermo(bool force) const { + doublereal tnow = temperature(); + if (m_tlast != tnow || force) { + m_spthermo->update(tnow, DATA_PTR(m_cp0), DATA_PTR(m_h0), + DATA_PTR(m_s0)); + m_tlast = tnow; + doublereal rt = GasConstant * tnow; + int k; + for (k = 0; k < m_kk; k++) { + m_h0[k] *= rt; + m_s0[k] *= GasConstant; + m_cp0[k] *= GasConstant; + m_mu0[k] = m_h0[k] - tnow*m_s0[k]; + } + m_tlast = tnow; + } + } + + void SurfPhase:: + setParametersFromXML(const XML_Node& eosdata) { + eosdata._require("model","Surface"); + doublereal n = getFloat(eosdata, "site_density", "-"); + if (n <= 0.0) + throw CanteraError("SurfPhase::setParametersFromXML", + "missing or negative site density"); + m_n0 = n; + m_logn0 = log(m_n0); + } + + + void SurfPhase::setStateFromXML(const XML_Node& state) { + + if (state.hasChild("temperature")) { + double t = getFloat(state, "temperature", "temperature"); + setTemperature(t); + } + + if (state.hasChild("coverages")) { + string comp = getString(state,"coverages"); + setCoveragesByName(comp); + } + } + + + EdgePhase::EdgePhase(doublereal n0) : SurfPhase(n0) { + setNDim(1); + } + + void EdgePhase:: + setParametersFromXML(const XML_Node& eosdata) { + eosdata._require("model","Edge"); + doublereal n = getFloat(eosdata, "site_density", "-"); + if (n <= 0.0) + throw CanteraError("EdgePhase::setParametersFromXML", + "missing or negative site density"); + m_n0 = n; + m_logn0 = log(m_n0); + } + + +} diff --git a/Cantera/src/thermo/SurfPhase.h b/Cantera/src/thermo/SurfPhase.h new file mode 100644 index 000000000..b93a04ab4 --- /dev/null +++ b/Cantera/src/thermo/SurfPhase.h @@ -0,0 +1,559 @@ +/** + * @file SurfPhase.h + * Header for a simple thermoydnamics model of a surface phase derived from ThermoPhase, + * assuming an ideal solution model + * (see \ref thermoprops and class \link Cantera::SurfPhase SurfPhase\endlink). + */ + +/* $Author$ + * $Date$ + * $Revision$ + * + * Copyright 2002 California Institute of Technology + * + */ + + +#ifndef CT_SURFPHASE_H +#define CT_SURFPHASE_H + +#include "mix_defs.h" +#include "ThermoPhase.h" + + +namespace Cantera { + + + + //! A simple thermoydnamics model for a surface phase, assuming an ideal solution model. + /*! + * The surface consists of a grid of equivalent sites. Surface species may be defined to + * occupy one or more sites. The surface species are assumed to be + * independent, and thus the species form an ideal solution. + * + * The density of surface sites is given by the variable \f$ n_0 \f$, which has MKS units + * of kmol m-2. + * + * + * Specification of Species Standard State Properties + * + * It is assumed that the reference state thermodynamics may be + * obtained by a pointer to a populated species thermodynamic property + * manager class (see ThermoPhase::m_spthermo). How to relate pressure + * changes to the reference state thermodynamics is resolved at this level. + * + * Pressure is defined as an independent variable in this phase. However, it has + * no effect on any quantities, as the molar concentration is a constant. + * + * Therefore, The standard state internal energy for species k is + * equal to the enthalpy for species k. + * + * \f[ + * u^o_k = h^o_k + * \f] + * + * Also, the standard state chemical potentials, entropy, and heat capacities + * are independent of pressure. The standard state gibbs free energy is obtained + * from the enthalpy and entropy functions. + * + * Specification of Solution Thermodynamic Properties + * + * The activity of species defined in the phase is given by + * \f[ + * a_k = \theta_k + * \f] + * + * The chemical potential for species k is equal to + * \f[ + * \mu_k(T,P) = \mu^o_k(T) + R T \log(\theta_k) + * \f] + * + * Pressure is defined as an independent variable in this phase. However, it has + * no effect on any quantities, as the molar concentration is a constant. + * + * The internal energy for species k is equal to the enthalpy for species k + * \f[ + * u_k = h_k + * \f] + * + * The entropy for the phase is given by the following relation, which is + * independent of the pressure: + * + * \f[ + * s_k(T,P) = s^o_k(T) - R \log(\theta_k) + * \f] + * + * Application within %Kinetics Managers + * + * The activity concentration,\f$ C^a_k \f$, used by the kinetics manager, is equal to + * the actual concentration, \f$ C^s_k \f$, and is given by the following + * expression. + * \f[ + * C^a_k = C^s_k = \frac{\theta_k n_0}{s_k} + * \f] + * + * The standard concentration for species k is: + * \f[ + * C^0_k = \frac{n_0}{s_k} + * \f] + * + * Instantiation of the Class + * + * The constructor for this phase is located in the default ThermoFactory + * for Cantera. A new SurfPhase may be created by the following code snippet: + * + * @code + * XML_Node *xc = get_XML_File("diamond.xml"); + * XML_Node * const xs = xc->findNameID("phase", "diamond_100"); + * ThermoPhase *diamond100TP_tp = newPhase(*xs); + * SurfPhase *diamond100TP = dynamic_cast (diamond100TP_tp); + * @endcode + * + * or by the following constructor: + * + * @code + * XML_Node *xc = get_XML_File("diamond.xml"); + * XML_Node * const xs = xc->findNameID("phase", "diamond_100"); + * SurfPhase *diamond100TP = new SurfPhase(*xs); + * @endcode + * + * XML Example + * + * An example of an XML Element named phase setting up a SurfPhase object named diamond_100 + * is given below. + * + * @verbatim + * + * H C + * c6HH c6H* c6*H c6** c6HM c6HM* c6*M c6B + * + * + * 1200.0 + * c6H*:0.1, c6HH:0.9 + * + * + * 3e-09 + * + * + * + * + * gas_phase diamond_bulk + * + * + * + * @endverbatim + * + * The model attribute, "Surface", on the thermo element identifies the phase as being + * a SurfPhase object. + * + * @ingroup thermoprops + */ + class SurfPhase : public ThermoPhase { + + public: + + //! Constructor. + /*! + * @param n0 Site Density of the Surface Phase + * Units: kmol m-2. + */ + SurfPhase(doublereal n0 = 0.0); + + //! Constructor. + /*! + * @param xmlphase XML node pointing to a SurfPhase description + */ + SurfPhase(XML_Node& xmlphase); + + + //! Destructor. + virtual ~SurfPhase(); + + //----- reimplimented methods of class ThermoPhase ------ + + //! Equation of state type flag. + /*! + * Redefine this to return cSurf, listed in mix_defs.h. + */ + virtual int eosType() const { return cSurf; } + + //! Return the Molar Enthalpy. Units: J/kmol. + /*! + * For an ideal solution, + * \f[ + * \hat h(T,P) = \sum_k X_k \hat h^0_k(T), + * \f] + * and is a function only of temperature. + * The standard-state pure-species Enthalpies + * \f$ \hat h^0_k(T) \f$ are computed by the species thermodynamic + * property manager. + * + * \see SpeciesThermo + */ + virtual doublereal enthalpy_mole() const; + + //! Return the Molar Internal Energy. Units: J/kmol + /** + * For a surface phase, the pressure is not a relevant + * thermodynamic variable, and so the Enthalpy is equal to the + * Internal Energy. + */ + virtual doublereal intEnergy_mole() const; + + //! Get the array of chemical potentials at unit activity for the + //! standard state species at the current T and P of the solution. + /*! + * These are the standard state chemical potentials \f$ \mu^0_k(T,P) + * \f$. The values are evaluated at the current + * temperature and pressure of the solution + * + * @param mu0 Output vector of chemical potentials. + * Length: m_kk. + */ + virtual void getStandardChemPotentials(doublereal* mu0) const; + + //! Get the species chemical potentials. Units: J/kmol. + /*! + * This function returns a vector of chemical potentials of the + * species in solution at the current temperature, pressure + * and mole fraction of the solution. + * + * @param mu Output vector of species chemical + * potentials. Length: m_kk. Units: J/kmol + */ + virtual void getChemPotentials(doublereal* mu) const; + + //! Return a vector of activity concentrations for each species + /*! + * For this phase the activity concentrations,\f$ C^a_k \f$, are defined to be + * equal to the actual concentrations, \f$ C^s_k \f$. + * Activity concentrations are + * + * \f[ + * C^a_k = C^s_k = \frac{\theta_k n_0}{s_k} + * \f] + * + * where \f$ \theta_k \f$ is the surface site fraction for species k, + * \f$ n_0 \f$ is the surface site density for the phase, and + * \f$ s_k \f$ is the surface size of species k. + * + * \f$ C^a_k\f$ that are defined such that \f$ a_k = C^a_k / + * C^0_k, \f$ where \f$ C^0_k \f$ is a standard concentration + * defined below and \f$ a_k \f$ are activities used in + * the thermodynamic functions. These activity concentrations are used + * by kinetics manager classes to compute the forward and + * reverse rates of elementary reactions. Note that they may + * or may not have units of concentration --- they might be + * partial pressures, mole fractions, or surface coverages, + * + * @param c vector of activity concentration (kmol m-2). + */ + virtual void getActivityConcentrations(doublereal* c) const; + + //! Return the standard concentration for the kth species + /*! + * The standard concentration \f$ C^0_k \f$ used to normalize + * the activity (i.e., generalized) concentration. + * For this phase, the standard concentration is species- + * specific + * + * \f[ + * C^0_k = \frac{n_0}{s_k} + * \f] + * + * This definition implies that the activity is equal to \f$ \theta_k \f$. + * + * @param k Optional parameter indicating the species. The default + * is to assume this refers to species 0. + * @return + * Returns the standard Concentration in units of m3 kmol-1. + */ + virtual doublereal standardConcentration(int k = 0) const; + + //! Return the log of the standard concentration for the kth species + /*! + * @param k species index (default 0) + */ + virtual doublereal logStandardConc(int k=0) const; + + //! Set the equation of state parameters from the argument list + /*! + * @internal + * Set equation of state parameters. + * + * @param n number of parameters. Must be one + * @param c array of \a n coefficients + * c[0] = The site density (kmol m-2) + */ + virtual void setParameters(int n, doublereal* c); + + //! Set the Equation-of-State parameters by reading an XML Node Input + /*! + * + * The Equation-of-State data consists of one item, the site density. + * + * @param thermoData Reference to an XML_Node named thermo + * containing the equation-of-state data. The + * XML_Node is within the phase XML_Node describing + * the %SurfPhase object. + * + * An example of the contents of the thermoData XML_Node is provided + * below. The units attribute is used to supply the units of the + * site density in any convenient form. Internally it is changed + * into MKS form. + * + * @verbatim + * + * 3e-09 + * + * @endverbatim + */ + virtual void setParametersFromXML(const XML_Node& thermoData); + + + //! Initialize the SurfPhase object after all species have been set up + /*! + * @internal 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 from ThermoPhase::initThermoXML(), + * which is called from importPhase(), + * just prior to returning from function importPhase(). + * + * @see importCTML.cpp + */ + virtual void initThermo(); + + + //! Set the initial state of the Surface Phase from an XML_Node + /*! + * State variables that can be set by this routine are + * the temperature and the surface site coverages. + * + * @param state XML_Node containing the state information + * + * An example of the XML code block is given below. + * + * @verbatim + * + * 1200.0 + * c6H*:0.1, c6HH:0.9 + * + * @endverbatim + */ + virtual void setStateFromXML(const XML_Node& state); + + //! Returns the site density + /*! + * Site density kmol m-2 + */ + doublereal siteDensity(){ return m_n0; } + + //! Sets the potential energy of species k. + /*! + * + * @param k Species index + * @param pe Value of the potential energy (J kmol-1) + */ + void setPotentialEnergy(int k, doublereal pe); + + //! Return the potential energy of species k. + /*! + * Returns the potential energy of species, k, + * J kmol-1 + * + * @param k Species index + */ + doublereal potentialEnergy(int k) {return m_pe[k];} + + //! Set the site density of the surface phase (kmol m-2) + /*! + * @param n0 Site density of the surface phase (kmol m-2) + */ + void setSiteDensity(doublereal n0); + + //! Get the nondimensional Enthalpy functions for the species standard states + //! at their standard states at the current T and P of the solution. + /*! + * @param hrt Output vector of nondimensional standard state enthalpies. + * Length: m_kk. + */ + void getEnthalpy_RT(doublereal* hrt) const; + + //! Get the array of nondimensional Entropy functions for the + //! species standard states at the current T and P of the solution. + /*! + * @param sr Output vector of nondimensional standard state entropies. + * Length: m_kk. + */ + void getEntropy_R(doublereal* sr) const; + + //! Return the thermodynamic pressure (Pa). + /*! + * This method must be overloaded in derived classes. Since the + * mass density, temperature, and mass fractions are stored, + * this method should use these values to implement the + * mechanical equation of state \f$ P(T, \rho, Y_1, \dots, + * Y_K) \f$. + */ + virtual doublereal pressure() const { + return m_press; + } + + //! Set the internally storred pressure (Pa) at constant + //! temperature and composition + /*! + * This method must be reimplemented in derived classes, where it + * may involve the solution of a nonlinear equation. Within %Cantera, + * the independent variable is the density. Therefore, this function + * solves for the density that will yield the desired input pressure. + * The temperature and composition iare held constant during this process. + * + * This base class function will print an error, if not overwritten. + * + * @param p input Pressure (Pa) + */ + virtual void setPressure(doublereal p) { + m_press = p; + } + + + //------- new methods defined in this class ---------- + + //! Set the surface site fractions to a specified state. + /*! + * This routine converts to concentrations + * in kmol/m2, using m_n0, the surface site density, + * and size(k), which is defined to be the number of + * surface sites occupied by the kth molecule. + * It then calls State::setConcentrations to set the + * internal concentration in the object. + * + * @param theta This is the surface site fraction + * for the kth species in the surface phase. + * This is a dimensionless quantity. + * + * This routine normalizes the theta's to 1, before application + */ + void setCoverages(const doublereal* theta); + + //! Set the surface site fractions to a specified state. + /*! + * This routine converts to concentrations + * in kmol/m2, using m_n0, the surface site density, + * and size(k), which is defined to be the number of + * surface sites occupied by the kth molecule. + * It then calls State::setConcentrations to set the + * internal concentration in the object. + * + * @param theta This is the surface site fraction + * for the kth species in the surface phase. + * This is a dimensionless quantity. + */ + void setCoveragesNoNorm(const doublereal* theta); + + + //! Set the coverages from a string of colon-separated name:value pairs. + /*! + * @param cov String containing colon-separated name:value pairs + */ + void setCoveragesByName(std::string cov); + + //! Return a vector of surface coverages + /*! + * Get the coverages. + * + * @param theta Array theta must be at least as long as + * the number of species. + */ + void getCoverages(doublereal* theta) const; + + protected: + + //! Surface site density (kmol m-2) + doublereal m_n0; + + //! log of the surface site density + doublereal m_logn0; + + //! Minimum temperature for valid species standard state thermo props + /*! + * This is the minimum temperature at which all species have valid standard + * state thermo props defined. + */ + doublereal m_tmin; + + //! Maximum temperature for valid species standard state thermo props + /*! + * This is the maximum temperature at which all species have valid standard + * state thermo props defined. + */ + doublereal m_tmax; + + //! Current value of the pressure (Pa) + doublereal m_press; + + //! Current value of the temperature (Kelvin) + mutable doublereal m_tlast; + + //! Temporary storage for the reference state enthalpies + mutable array_fp m_h0; + + //! Temporary storage for the reference state entropies + mutable array_fp m_s0; + + //! Temporary storage for the reference state heat capacities + mutable array_fp m_cp0; + + //! Temporary storage for the reference state gibbs energies + mutable array_fp m_mu0; + + //! Temporary work array + mutable array_fp m_work; + + //! Potential energy of each species in the surface phase + /*! + * @todo Fix potential energy + * Note, the potential energy terms seem to be orphaned at the moment. + * They are not connected to the Gibbs free energy calculation in + * this object + * + * @deprecated + */ + mutable array_fp m_pe; + + //! vector storring the log of the size of each species. + /*! + * The size of each species is defined as the number of surface + * sites each species occupies. + */ + mutable array_fp m_logsize; + + private: + + //! Update the species reference state thermodynamic functions + /*! + * The polynomials for the standard state functions are only + * reevalulated if the temperature has changed. + * + * @param force Boolean, which if true, forces a reevalulation + * of the thermo polynomials. + * default = false. + */ + void _updateThermo(bool force=false) const; + + }; +} + +#endif + + + + + diff --git a/Cantera/src/thermo/ThermoFactory.cpp b/Cantera/src/thermo/ThermoFactory.cpp new file mode 100644 index 000000000..ef3f4fe91 --- /dev/null +++ b/Cantera/src/thermo/ThermoFactory.cpp @@ -0,0 +1,578 @@ +/** + * @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" + +#ifdef WITH_PURE_FLUIDS +#include "PureFluidPhase.h" +#endif + +#include "ConstDensityThermo.h" +#include "SurfPhase.h" +#include "EdgePhase.h" + +#ifdef WITH_METAL +#include "MetalPhase.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 + +using namespace std; + +namespace Cantera { + + ThermoFactory* ThermoFactory::s_factory = 0; + + static int ntypes = 9; + static string _types[] = {"IdealGas", "Incompressible", + "Surface", "Edge", "Metal", "StoichSubstance", + "PureFluid", "LatticeSolid", "Lattice"}; + + static int _itypes[] = {cIdealGas, cIncompressible, + cSurf, cEdge, cMetal, cStoichSubstance, + cPureFluid, cLatticeSolid, cLattice}; + + /* + * 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; + // map d; + 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; + +#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 + + 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); + 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 sparrays; + phase.getChildren("speciesArray", sparrays); + int jsp, nspa = static_cast(sparrays.size()); + vector 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 + // + // 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 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 spnames; + getStringArray(species, spnames); + int nsp = static_cast(spnames.size()); + + // if 'all' is specified, then add all species + // defined in this database to the phase + if (nsp == 1 && spnames[0] == "all") { + vector allsp; + db->getChildren("species",allsp); + nsp = static_cast(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 uniquesp; + db->getChildren("species",uniquesp); + nsp = static_cast(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) { + + // get the composition of the species + const XML_Node& a = s.child("atomArray"); + map 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::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. + * Failures of any kind return the null pointer. + */ + const XML_Node *speciesXML_Node(std::string kname, + const XML_Node *phaseSpeciesData) { + /* + * First look at the species database. + * -> Look for the subelement "stoichIsMods" + * in each of the species SS databases. + */ + if (!phaseSpeciesData) return ((const XML_Node *) 0); + string jname; + vector 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); + } + +} diff --git a/Cantera/src/thermo/ThermoFactory.h b/Cantera/src/thermo/ThermoFactory.h new file mode 100644 index 000000000..fa465c02c --- /dev/null +++ b/Cantera/src/thermo/ThermoFactory.h @@ -0,0 +1,229 @@ +/** + * @file ThermoFactory.h + * Headers 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 + + +#ifndef THERMO_FACTORY_H +#define THERMO_FACTORY_H + +#include "ThermoPhase.h" +#include "xml.h" +#include "SpeciesThermoFactory.h" + +namespace Cantera { + + + /*! + * @addtogroup thermoprops + * + * Standard %ThermoPhase objects may be instantiated by calling + * the main %Cantera factory class for %ThermoPhase objects; This class is called ThermoFactory. + */ + //@{ + + //! Specific error to be thrown if the type of Thermo mananger is unrecognized. + /*! + * This particular error class may be caught, if the application may have other + * models that the main Cantera appliation doesn't know about. + */ + class UnknownThermoPhaseModel : public CanteraError { + public: + //! Constructor + /*! + * @param proc Function name where the error occurred. + * @param thermoModel Sting name of ThermoPhase which didn't match + */ + UnknownThermoPhaseModel(std::string proc, std::string thermoModel) : + CanteraError(proc, "Specified ThermoPhase model " + + thermoModel + + " does not match any known type.") {} + //! destructor + virtual ~UnknownThermoPhaseModel() {} + }; + + + //! Factory class for thermodynamic property managers. + /*! + * This class keeps a list of the known ThermoPhase classes, and is used + * to create new instances of these classes. + */ + class ThermoFactory { + + public: + + //! Static function that creates a static instance of the factor. + static ThermoFactory* factory() { + if (!s_factory) s_factory = new ThermoFactory; + return s_factory; + } + + //! delete the static instance of this factory + static void deleteFactory() { + if (s_factory) { + delete s_factory; + s_factory = 0; + } + } + + //! Destructor doesn't do anything. + /*! + * We do not delete statically + * created single instance of this class here, because it would + * create an infinite loop if destructor is called for that + * single instance. + */ + virtual ~ThermoFactory() { } + + //! Create a new thermodynamic property manager. + /*! + * @param model String to look up the model against + * + * @return + * Returns a pointer to a new ThermoPhase instance matching the + * model string. Returns NULL if something went wrong. + * Throws an exception UnknownThermoPhaseModel if the string + * wasn't matched. + */ + virtual ThermoPhase* newThermoPhase(std::string model); + + private: + //! static member of a single instance + static ThermoFactory* s_factory; + + //! Private constructor prevents usage + ThermoFactory(){} + }; + + //! Create a new thermo manager instance. + /*! + * @param model String to look up the model against + * @param f ThermoFactor instance to use in matching the string + * + * @return + * Returns a pointer to a new ThermoPhase instance matching the + * model string. Returns NULL if something went wrong. + * Throws an exception UnknownThermoPhaseModel if the string + * wasn't matched. + */ + inline ThermoPhase* newThermoPhase(std::string model, + ThermoFactory* f=0) { + if (f == 0) { + f = ThermoFactory::factory(); + } + return f->newThermoPhase(model); + } + + + /*! + * 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 using Cantera's internal ThermoPhase Factory routines + * on the model to figure out + * what ThermoPhase derived class should be 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. + * + * @param phase XML_Node reference pointing to the phase XML element. + * + * @return + * Returns a pointer to the completed and initialized ThermoPhase object. + * + * @ingroup inputfiles + */ + ThermoPhase* newPhase(XML_Node& phase); + ThermoPhase* newPhase(std::string infile, std::string id); + + //! Import a phase information into an empty thermophase object + /*! + * Here we read an XML description of the thermodynamic information + * for a phase. At the end of this routine, the phase should + * be ready to be used within applications. This routine contains + * some key routines that are used as pass back routines so that + * the phase (and the contents of the XML file) may contain + * variable paramerizations for the specification of the + * species standard states, the equation of state, and the + * specification of other nonidealities. Below, a description + * is presented of the main algorithm for bringing up a %ThermoPhase + * object, with care to present points where customizations + * occur. + * + * Before invoking this routine, either the ThermoPhase Factory routines + * are called or direct constructor routines are called that + * instantiate an inherited ThermoPhase object. This object is input + * to this routine, and therefore contains inherited routines that + * drive the custimation of the initialization process. + * + * At the start of the routine, we import descriptions of the elements + * that make up the species in a phase. + * + * We call setParametersFromXML(eos) to read parameters about + * the thermo phase before the species are read in. + * + * We call addElementsFromXML() to add elements into the + * description of the phase. + * + * We 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. + * + * We import information about the species, including their + * reference state thermodynamic polynomials. We then freeze + * the state of the species in the element. + * + * Finally, we call initThermoXML(), + * a member function of the ThermoPhase object, to "finish" + * the description. Now that the species are known, + * additional information may be read in about the thermodynamics + * of the phase, (e.g., virial coefficients, which are + * binary or ternary interaction parameters between species). + * + * @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. + * + * @param spfactory species Thermo factory pointer, if + * available. If not available, one will be + * created. + * + * @ingroup thermoprops + */ + bool importPhase(XML_Node& phase, ThermoPhase* th, + SpeciesThermoFactory* spfactory = 0); + + bool installSpecies(int k, const XML_Node& s, thermo_t& p, + SpeciesThermo& spthermo, int rule, + SpeciesThermoFactory* factory = 0); + + const XML_Node *speciesXML_Node(std::string kname, + const XML_Node *phaseSpeciesData); + //@} + +} + +#endif + + diff --git a/Cantera/src/thermo/ThermoPhase.cpp b/Cantera/src/thermo/ThermoPhase.cpp new file mode 100644 index 000000000..06b214756 --- /dev/null +++ b/Cantera/src/thermo/ThermoPhase.cpp @@ -0,0 +1,464 @@ +/** + * @file ThermoPhase.cpp + * Definition file for class ThermoPhase, the base class for phases with + * thermodynamic properties + * (see class \link Cantera::ThermoPhase ThermoPhase\endlink). + */ + +/* + * $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" + +using namespace std; + +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_lambdaRRT = right.m_lambdaRRT; + 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) const { + 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 std::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 std::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 < 500; n++) { + double h0 = enthalpy_mass(); + dt = (h - h0)/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 < 500; 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 < 500; 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 < 500; 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(std::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. + * + * The base %ThermoPhase class assigns thedefault quantities + * of (kmol/m3). + * Inherited classes are responsible for overriding the default + * values if necessary. + * + * 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 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(std::string inputFile, std::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 + * + * This function is called from importPhase() + * after the elements and the + * species are initialized with default ideal solution + * level data. + * + * @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, std::string id) { + /* + * The default implementation just calls initThermo(), which + * inheriting classes may override. + */ + 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); + } + } + + + + /* + * Called by function 'equilibrate' in ChemEquil.h to transfer + * the element potentials to this object after every successful + * equilibration routine. + * The element potentials are storred in their dimensionless + * forms, calculated by dividing by RT. + * @param lambda vector containing the element potentials. + * Length = nElements. Units are Joules/kmol. + */ + void ThermoPhase::setElementPotentials(const vector_fp& lambda) { + doublereal rrt = 1.0/(GasConstant* temperature()); + int mm = nElements(); + if (lambda.size() < (size_t) mm) { + throw CanteraError("setElementPotentials", "lambda too small"); + } + if (!m_hasElementPotentials) { + m_lambdaRRT.resize(mm); + } + for (int m = 0; m < mm; m++) { + m_lambdaRRT[m] = lambda[m] * rrt; + } + m_hasElementPotentials = true; + } + + /* + * Returns the storred element potentials. + * The element potentials are retrieved from their storred + * dimensionless forms by multiplying by RT. + * @param lambda Vector containing the element potentials. + * Length = nElements. Units are Joules/kmol. + */ + bool ThermoPhase::getElementPotentials(doublereal* lambda) const { + doublereal rt = GasConstant* temperature(); + int mm = nElements(); + if (m_hasElementPotentials) { + for (int m = 0; m < mm; m++) { + lambda[m] = m_lambdaRRT[m] * rt; + } + } + return (m_hasElementPotentials); + } + +} diff --git a/Cantera/src/thermo/ThermoPhase.h b/Cantera/src/thermo/ThermoPhase.h new file mode 100755 index 000000000..0e5dd5b99 --- /dev/null +++ b/Cantera/src/thermo/ThermoPhase.h @@ -0,0 +1,1460 @@ +/** + * @file ThermoPhase.h + * Header file for class ThermoPhase, the base class for phases with + * thermodynamic properties, and the text for the Module thermoprops + * (see \ref thermoprops and class \link Cantera::ThermoPhase ThermoPhase\endlink). + */ + +/* + * $Author$ + * $Date$ + * $Revision$ + * + * Copyright 2002 California Institute of Technology + * + */ + +#ifndef CT_THERMOPHASE_H +#define CT_THERMOPHASE_H + +#include "Phase.h" + + +namespace Cantera { + + /*! + * @name CONSTANTS - Specification of the Molality conventention + */ + //@{ + //! Standard state uses the molar convention + const int cAC_CONVENTION_MOLAR = 0; + //! Stanadrd state uses the molality convention + const int cAC_CONVENTION_MOLALITY = 1; + //@} + class XML_Node; + + /** + * @defgroup thermoprops Thermodynamic Properties + * + * + * These classes are used to compute the thermodynamic properties of + * phases of matter. The main base class for describing thermodynamic + * properties of phases within %Cantera is called ThermoPhase. %ThermoPhase + * is a large class that describes the interface within %Cantera to Thermodynamic + * functions for a phase. + * + * + * The calculation of thermodynamic functions within %ThermoPhase is + * broken down roughly into two or more steps. First, the standard state + * properties + * of all of the species are calculated at the current temperature and at + * either + * the current pressure or at a reference pressure. If the calculation is + * carried out at a refereence pressure instead of at the current pressure + * the calculation is called a "reference state properties" calculation, + * just to make the distinction (even though it may be considered to be + * a fixed-pressure standard-state calculation). The next step is to + * adjust the reference state calculation to the current pressure. The + * thermodynamic + * functions then are considered to be at the standard state of each species. + * Lastly the mixing contributions are added to arrive at the thermodynamic + * functions for the solution. + * + * The %ThermoPhase class provides interfaces to thermodynamic properties + * calculated for + * the reference state of each species, the standard state values for + * each species, the thermodynamic functions for solution values, both + * on a per mole of solution basis (i.e., enthalpy_mole()), on a per kg of + * solution basis, and on a + * partial molar basis for each species (i.e., + * getPartialMolarEnthalpies(double *hbar)). + * At each level, functions for the enthalpy, entropy, Gibbs free energy, + * internal energy, and volume are provided. So, 5 levels (reference state, + * standard state, partial molar, per mole of solution, and per mass of + * solution) + * and 5 functions multiplied together makes 25 possible functions. That's + * why %ThermoPhase is such a large class. + * + * + * Mechanical properties + * + * Treatment of the electrochemical potential + * + * Treatment of other potential energy contributions. + * + * Setting the %State of the phase + * + * Instantiation of %ThermoPhase properties occurs via the following path. + * + * Molar Basis vs. Molality Basis + * + * The following Objects inherit from %ThermoPhase. These are known to the + * internal factory methods + * + * - IdealGasPhase in IdealGasPhase.h + * - StoichSubstance in StoichSubstance.h + * - SurfPhase in SurfPhase.h + * - EdgePhase in EdgePhase.h + * - LatticePhase in LatticePhase.h + * - LatticeSolidPhase in LatticeSolidPhase.h + * - ConstDensityThermo in ConstDensityThermo.h + * - PureFluidPhase in PureFluidPhase.h + * . + * + * The following additional objects inherit from %ThermoPhase. Most of these + * are associated with an electrochemistry capability that is under + * construction. + * + * - DebyeHuckel in thermo/DebyeHuckel.h + * - SingleSpeciesTP in thermo/SingleSpeciesTP.h + * - StoichSubstanceSSTP in thermo/StoichSubstanceSSTP.h + * - VPStandardStateTP in thermo/VPStandardStateTP.h + * - IdealMolalSoln in thermo/IdealMolalSoln.h + * - IdealSolidSolnPhase in thermo/IdealSolidSolnPhase.h + * - IdealGasPDSS in thermo/IdealGasPDSS.h + * - MolalityVPSSTP in thermo/MolalityVPSSTP.h + * - HMWSoln in thermo/HMWSoln.h + * - WaterSSTP in thermo/WaterSSTP.h + * . + * + * @see newPhase(std::string file, std::string id) Description for how to + * read ThermoPhases from XML files. + * @see newPhase(XML_Node &phase) How to call the Factory routine to create + * and initialize %ThermoPhase objects. + */ + + + //! Base class for a phase with thermodynamic properties. + /*! + * Class %ThermoPhase is the base class for the family of classes + * that represent phases of matter of any type. It defines a + * common public interface, and implements a few methods. Most of + * the methods, however, are declared virtual and are meant to be + * overloaded in derived classes. The standard way used + * throughout Cantera to compute properties of phases of matter is + * through pointers of type ThermoPhase* that point to objects of + * subclasses of ThermoPhase. + * + * Class %ThermoPhase extends class Phase by adding methods to compute + * thermodynamic + * properties in addition to the ones (temperature, density, + * composition) that class Phase provides. The distinction is that + * the methods declared in ThermoPhase require knowing the + * particular equation of state of the phase of interest, while + * those of class Phase do not, since they only involve data values + * stored within the object. + * + * Instances of subclasses of %ThermoPhase should be created using + * the factory class ThermoFactory, not by calling the constructor + * directly. This allows new classes to be used with the various + * Cantera language interfaces. + * + * To implement a new equation of state, derive a class from + * ThermoPhase and overload the virtual methods in + * ThermoPhase. Methods that are not needed can be left + * unimplimented, which will cause an exception to be thrown if it + * is called. + * + * Relationship with the kinetics operator: + * + * Describe activity coefficients. + * + * Describe K_a, K_p, and K_c, These are three different equilibrium + * constants. + * + * K_a is the calculation of the equilibrium constant from the + * standard state Gibbs free energy values. It is by definition + * dimensionless. + * + * K_p is the calculation of the equilibrium constant from the + * reference state gibbs free energy values. It is by definition + * dimensionless. The pressure dependence is handled entirely + * on the rhs of the equilibrium expression. + * + * K_c is the equilibrium constant calculated from the + * activity concentrations. The dimensions depend on the number + * of products and reactants. + * + * + * The kinetics manager requires the calculation of K_c for the + * calculation of the reverse rate constant + * + * + * @ingroup thermoprops + * @ingroup phases + */ + class ThermoPhase : public Phase { + + public: + + //! Constructor. Note that ThermoPhase is meant to be used as + //! a base class, so this constructor should not be called + //! explicitly. + ThermoPhase() : Phase(), m_spthermo(0), m_speciesData(0), + m_index(-1), m_phi(0.0), m_hasElementPotentials(false) {} + + //! Destructor. Deletes the species thermo manager. + virtual ~ThermoPhase() { + delete m_spthermo; + } + + + //!Copy Constructor for the %ThermoPhase object. + /*! + * Currently, this is not fully implemented. If called it will + * throw an exception. + */ + ThermoPhase(const ThermoPhase &); + + //! Assignment operator + /*! + * This is NOT a virtual function. + * + * @param right Reference to %ThermoPhase object to be copied into the + * current one. + */ + ThermoPhase& operator=(const ThermoPhase &right); + + /** + * 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. + */ + virtual ThermoPhase *duplMyselfAsThermoPhase(); + + /** + * + * @name Information Methods + * @{ + */ + + //! Equation of state type flag. + /*! + * The base class returns + * zero. Subclasses should define this to return a unique + * non-zero value. Constants defined for this purpose are + * listed in mix_defs.h. + */ + virtual int eosType() const { return 0; } + + /** + * Returns the reference pressure in Pa. This function is a wrapper + * that calls the species thermo refPressure function. + */ + doublereal refPressure() const { + return m_spthermo->refPressure(); + } + + + //! Minimum temperature for which the thermodynamic data for the species or phase are valid. + /*! + * If no argument is supplied, the + * value returned will be the lowest temperature at which the + * data for \e all species are valid. Otherwise, the value + * will be only for species \a k. This function is a wrapper + * that calls the species thermo minTemp function. + * + * @param k index of the species. Default is -1, which will return the max of the min value + * over all species. + */ + doublereal minTemp(int k = -1) { + return m_spthermo->minTemp(k); + } + + //! Maximum temperature for which the thermodynamic data for the species + //! are valid. + /*! + * If no argument is supplied, the + * value returned will be the highest temperature at which the + * data for \e all species are valid. Otherwise, the value + * will be only for species \a k. This function is a wrapper + * that calls the species thermo maxTemp function. + * + * @param k index of the species. Default is -1, which will return the min of the max value + * over all species. + */ + doublereal maxTemp(int k = -1) { + return m_spthermo->maxTemp(k); + } + + /** + * @} + * @name Molar Thermodynamic Properties of the Solution + * @{ + */ + + /// Molar enthalpy. Units: J/kmol. + virtual doublereal enthalpy_mole() const { + return err("enthalpy_mole"); + } + + /// Molar internal energy. Units: J/kmol. + virtual doublereal intEnergy_mole() const { + return err("intEnergy_mole"); + } + + /// Molar entropy. Units: J/kmol/K. + virtual doublereal entropy_mole() const { + return err("entropy_mole"); + } + + /// Molar Gibbs function. Units: J/kmol. + virtual doublereal gibbs_mole() const { + return err("gibbs_mole"); + } + + /// Molar heat capacity at constant pressure. Units: J/kmol/K. + virtual doublereal cp_mole() const { + return err("cp_mole"); + } + + /// Molar heat capacity at constant volume. Units: J/kmol/K. + virtual doublereal cv_mole() const { + return err("cv_mole"); + } + + + /** + * @} + * @name Mechanical Properties + * @{ + */ + + //! Return the thermodynamic pressure (Pa). + /*! + * This method must be overloaded in derived classes. Since the + * mass density, temperature, and mass fractions are stored, + * this method should use these values to implement the + * mechanical equation of state \f$ P(T, \rho, Y_1, \dots, + * Y_K) \f$. + */ + virtual doublereal pressure() const { + return err("pressure"); + } + + //! Set the internally storred pressure (Pa) at constant + //! temperature and composition + /*! + * This method must be reimplemented in derived classes, where it + * may involve the solution of a nonlinear equation. Within %Cantera, + * the independent variable is the density. Therefore, this function + * solves for the density that will yield the desired input pressure. + * The temperature and composition iare held constant during this process. + * + * This base class function will print an error, if not overwritten. + * + * @param p input Pressure (Pa) + */ + virtual void setPressure(doublereal p) { + err("setPressure"); + } + + //! Returns the isothermal compressibility. Units: 1/Pa. + /*! + * The isothermal compressibility is defined as + * \f[ + * \kappa_T = -\frac{1}{v}\left(\frac{\partial v}{\partial P}\right)_T + * \f] + */ + virtual doublereal isothermalCompressibility() const { + err("isothermalCompressibility"); return -1.0; + } + + //! Return the volumetric thermal expansion coefficient. Units: 1/K. + /*! + * The thermal expansion coefficient is defined as + * \f[ + * \beta = \frac{1}{v}\left(\frac{\partial v}{\partial T}\right)_P + * \f] + */ + virtual doublereal thermalExpansionCoeff() const { + err("thermalExpansionCoeff()"); return -1.0; + } + + /// @deprecated + virtual void updateDensity() { + deprecatedMethod("ThermoPhase","updateDensity",""); + } + + /** + * @} + * @name Electric Potential + * + * The phase may be at some non-zero electrical + * potential. These methods set or get the value of the + * electric potential. + */ + //@{ + + //! Set the electric potential of this phase (V). + /*! + * This is used by classes InterfaceKinetics and EdgeKinetics to + * compute the rates of charge-transfer reactions, and in computing + * the electrochemical potentials of the species. + * + * Each phase may have its own electric potential. + * + * @param v Input value of the electric potential in Volts + */ + void setElectricPotential(doublereal v) { + m_phi = v; + } + + //! Returns the electric potential of this phase (V). + /*! + * Units are Volts + */ + doublereal electricPotential() const { return m_phi; } + + /** + * @} + * @name Activities, Standard States, and Activity Concentrations + * + * The activity \f$a_k\f$ of a species in solution is related + * to the chemical potential by \f[ \mu_k = \mu_k^0(T,P) + + * \hat R T \log a_k. \f] The quantity \f$\mu_k^0(T,P)\f$ is + * the standard chemical potential at unit activity, + * which depends on temperature and pressure, + * but not on composition. The + * activity is dimensionless. + * @{ + */ + + + //! This method returns the convention used in specification + //! of the activities, of which there are currently two, molar- + //! and molality-based conventions. + /*! + * Currently, there are two activity conventions: + * - Molar-based activities + * %Unit activity of species at either a hypothetical pure + * solution of the species or at a hypothetical + * pure ideal solution at infinite dilution + * cAC_CONVENTION_MOLAR 0 + * - default + * + * - Molality-based acvtivities + * (unit activity of solutes at a hypothetical 1 molal + * solution referenced to infinite dilution at all + * pressures and temperatures). + * cAC_CONVENTION_MOLALITY 1 + */ + virtual int activityConvention() const; + + + //! This method returns an array of generalized concentrations + /*! + * \f$ C^a_k\f$ are defined such that \f$ a_k = C^a_k / + * C^0_k, \f$ where \f$ C^0_k \f$ is a standard concentration + * defined below and \f$ a_k \f$ are activities used in the + * thermodynamic functions. These activity (or generalized) + * concentrations are used + * by kinetics manager classes to compute the forward and + * reverse rates of elementary reactions. Note that they may + * or may not have units of concentration --- they might be + * partial pressures, mole fractions, or surface coverages, + * for example. + * + * @param c Output array of generalized concentrations. The + * units depend upon the implementation of the + * reaction rate expressions within the phase. + */ + virtual void getActivityConcentrations(doublereal* c) const { + err("getActivityConcentrations"); + } + + //! Return the standard concentration for the kth species + /*! + * The standard concentration \f$ C^0_k \f$ used to normalize + * the activity (i.e., generalized) concentration. In many cases, this quantity + * will be the same for all species in a phase - for example, + * for an ideal gas \f$ C^0_k = P/\hat R T \f$. For this + * reason, this method returns a single value, instead of an + * array. However, for phases in which the standard + * concentration is species-specific (e.g. surface species of + * different sizes), this method may be called with an + * optional parameter indicating the species. + * + * @param k Optional parameter indicating the species. The default + * is to assume this refers to species 0. + * @return + * Returns the standard Concentration in units of m3 kmol-1. + */ + virtual doublereal standardConcentration(int k=0) const { + err("standardConcentration"); + return -1.0; + } + + //! Natural logarithm of the standard concentration of the kth species. + /*! + * @param k index of the species (defaults to zero) + */ + virtual doublereal logStandardConc(int k=0) const { + err("logStandardConc"); + return -1.0; + } + + //! Returns the units of the standard and generalized concentrations. + /*! + * Note they have the same units, as their + * ratio 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. + * + * The base %ThermoPhase class assigns the default quantities + * of (kmol/m3) for all species. + * Inherited classes are responsible for overriding the default + * values if necessary. + * + * @param uA Output vector containing the units + * 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 + * @param k species index. Defaults to 0. + * @param sizeUA output int containing the size of the vector. + * Currently, this is equal to 6. + */ + virtual void getUnitsStandardConc(double *uA, int k = 0, + int sizeUA = 6); + + //! Get the array of non-dimensional activities at + //! the current solution temperature, pressure, and solution concentration. + /*! + * + * We resolve this function at this level by calling + * on the activityConcentration function. However, + * derived classes may want to override this default + * implementation. + * + * @param a Output vector of activities. Length: m_kk. + */ + virtual void getActivities(doublereal* a) const; + + //! Get the array of non-dimensional molar-based activity coefficients at + //! the current solution temperature, pressure, and solution concentration. + /*! + * @param ac Output vector of activity coefficients. Length: m_kk. + */ + virtual void getActivityCoefficients(doublereal* ac) const { + if (m_kk == 1) { + ac[0] = 1.0; + } else { + err("getActivityCoefficients"); + } + } + + //@} + /// @name Partial Molar Properties of the Solution + //@{ + + /** + * Get the array of non-dimensional species chemical potentials + * These are partial molar Gibbs free energies. + * \f$ \mu_k / \hat R T \f$. + * Units: unitless + * + * @param mu Output vector of dimensionless chemical potentials. + * Length: m_kk. + */ + virtual void getChemPotentials_RT(doublereal* mu) const { + err("getChemPotentials_RT"); + } + + + //! Get the species chemical potentials. Units: J/kmol. + /*! + * This function returns a vector of chemical potentials of the + * species in solution at the current temperature, pressure + * and mole fraction of the solution. + * + * @param mu Output vector of species chemical + * potentials. Length: m_kk. Units: J/kmol + */ + virtual void getChemPotentials(doublereal* mu) const { + err("getChemPotentials"); + } + + //! Get the species electrochemical potentials. + /*! + * These are partial molar quantities. This method adds a term \f$ Fz_k + * \phi_k \f$ to each chemical potential. + * + * @param mu Output vector of species electrochemical + * potentials. Length: m_kk. Units: J/kmol + */ + void getElectrochemPotentials(doublereal* mu) const { + getChemPotentials(mu); + double ve = Faraday * electricPotential(); + for (int k = 0; k < m_kk; k++) { + mu[k] += ve*charge(k); + } + } + + //! Returns an array of partial molar enthalpies for the species + //! in the mixture. Units (J/kmol) + /*! + * @param hbar Output vector of species partial molar enthalpies. + * Length: m_kk. units are J/kmol. + */ + virtual void getPartialMolarEnthalpies(doublereal* hbar) const { + err("getPartialMolarEnthalpies"); + } + + //! Returns an array of partial molar entropies of the species in the + //! solution. Units: J/kmol/K. + /*! + * @param sbar Output vector of species partial molar entropies. + * Length = m_kk. units are J/kmol/K. + */ + virtual void getPartialMolarEntropies(doublereal* sbar) const { + err("getPartialMolarEntropies"); + } + + //! Return an array of partial molar internal energies for the + //! species in the mixture. Units: J/kmol. + /*! + * @param ubar Output vector of speciar partial molar internal energies. + * Length = m_kk. units are J/kmol. + */ + virtual void getPartialMolarIntEnergies(doublereal* ubar) const { + err("getPartialMolarIntEnergies"); + } + + //! Return an array of partial molar heat capacities for the + //! species in the mixture. Units: J/kmol/K + /*! + * @param cpbar Output vector of species partial molar heat + * capacities at constant pressure. + * Length = m_kk. units are J/kmol/K. + */ + virtual void getPartialMolarCp(doublereal* cpbar) const { + err("getPartialMolarCp"); + } + + //! Return an array of partial molar volumes for the + //! species in the mixture. Units: m^3/kmol. + /*! + * @param vbar Output vector of speciar partial molar volumes. + * Length = m_kk. units are m^3/kmol. + */ + virtual void getPartialMolarVolumes(doublereal* vbar) const { + err("getPartialMolarVolumes"); + } + + //@} + /// @name Properties of the Standard State of the Species in the Solution + //@{ + + //! Get the array of chemical potentials at unit activity for the species + //! at their standard states at the current T and P of the solution. + /*! + * These are the standard state chemical potentials \f$ \mu^0_k(T,P) + * \f$. The values are evaluated at the current + * temperature and pressure of the solution + * + * @param mu Output vector of chemical potentials. + * Length: m_kk. + */ + virtual void getStandardChemPotentials(doublereal* mu) const { + err("getStandardChemPotentials"); + } + + //! Get the nondimensional Enthalpy functions for the species + //! at their standard states at the current T and P of the solution. + /*! + * @param hrt Output vector of nondimensional standard state enthalpies. + * Length: m_kk. + */ + virtual void getEnthalpy_RT(doublereal* hrt) const { + err("getEnthalpy_RT"); + } + + //! Get the array of nondimensional Entropy functions for the + //! standard state species at the current T and P of the solution. + /*! + * @param sr Output vector of nondimensional standard state entropies. + * Length: m_kk. + */ + virtual void getEntropy_R(doublereal* sr) const { + err("getEntropy_R"); + } + + //! Get the nondimensional Gibbs functions for the species + //! in their standard states at the current T and P of the solution. + /*! + * @param grt Output vector of nondimensional standard state gibbs free energies + * Length: m_kk. + */ + virtual void getGibbs_RT(doublereal* grt) const { + err("getGibbs_RT"); + } + + //! Get the Gibbs functions for the standard + //! state of the species at the current T and P of the solution + /*! + * Units are Joules/kmol + * @param gpure Output vector of standard state gibbs free energies + * Length: m_kk. + */ + virtual void getPureGibbs(doublereal* gpure) const { + err("getPureGibbs"); + } + + //! Returns the vector of nondimensional Internal Energies of the standard + //! state species at the current T and P of the solution + /*! + * @param urt output vector of nondimensional standard state internal energies + * of the species. Length: m_kk. + */ + virtual void getIntEnergy_RT(doublereal *urt) const { + err("getIntEnergy_RT"); + } + + //! Get the nondimensional Heat Capacities at constant + //! pressure for the species standard states + //! at the current T and P of the solution + /*! + * @param cpr Output vector of nondimensional standard state heat capacities + * Length: m_kk. + */ + virtual void getCp_R(doublereal* cpr) const { + err("getCp_R"); + } + + //! Get the molar volumes of the species standard states at the current + //! T and P of the solution. + /*! + * units = m^3 / kmol + * + * @param vol Output vector containing the standard state volumes. + * Length: m_kk. + */ + virtual void getStandardVolumes(doublereal *vol) const { + err("getStandardVolumes"); + } + + //@} + /// @name Thermodynamic Values for the Species Reference States + //@{ + + + //! Returns the vector of nondimensional + //! enthalpies of the reference state at the current temperature + //! of the solution and the reference pressure for the species. + /*! + * This base function will throw a CanteraException unless + * it is overwritten in a derived class. + * + * @param hrt Output vector containing the nondimensional reference state + * enthalpies + * Length: m_kk. + */ + virtual void getEnthalpy_RT_ref(doublereal *hrt) const { + err("getEnthalpy_RT_ref"); + } + + //! Returns the vector of nondimensional + //! Gibbs Free Energies of the reference state at the current temperature + //! of the solution and the reference pressure for the species. + /*! + * @param grt Output vector containing the nondimensional reference state + * Gibbs Free energies. Length: m_kk. + */ + virtual void getGibbs_RT_ref(doublereal *grt) const { + err("getGibbs_RT_ref"); + } + + //! Returns the vector of the + //! gibbs function of the reference state at the current temperature + //! of the solution and the reference pressure for the species. + /*! + * units = J/kmol + * + * @param g Output vector containing the reference state + * Gibbs Free energies. Length: m_kk. Units: J/kmol. + */ + virtual void getGibbs_ref(doublereal *g) const { + err("getGibbs_ref"); + } + + //! Returns the vector of nondimensional + //! entropies of the reference state at the current temperature + //! of the solution and the reference pressure for each species. + /*! + * @param er Output vector containing the nondimensional reference state + * entropies. Length: m_kk. + */ + virtual void getEntropy_R_ref(doublereal *er) const { + err("getEntropy_R_ref"); + } + + //! Returns the vector of nondimensional + //! internal Energies of the reference state at the current temperature + //! of the solution and the reference pressure for each species. + /*! + * @param urt Output vector of nondimensional reference state + * internal energies of the species. + * Length: m_kk + */ + virtual void getIntEnergy_RT_ref(doublereal *urt) const { + err("getIntEnergy_RT_ref"); + } + + //! Returns the vector of nondimensional + //! constant pressure heat capacities of the reference state + //! at the current temperature of the solution + //! and reference pressure for each species. + /*! + * @param cprt Output vector of nondimensional reference state + * heat capacities at constant pressure for the species. + * Length: m_kk + */ + virtual void getCp_R_ref(doublereal *cprt) const { + err("getCp_R_ref()"); + } + + //! Get the molar volumes of the species reference states at the current + //! T and P_ref of the solution. + /*! + * units = m^3 / kmol + * + * @param vol Output vector containing the standard state volumes. + * Length: m_kk. + */ + virtual void getStandardVolumes_ref(doublereal *vol) const { + err("getStandardVolumes_ref"); + } + + /////////////////////////////////////////////////////// + // + // The methods below are not virtual, and should not + // be overloaded. + // + ////////////////////////////////////////////////////// + + /** + * @} + * @name Specific Properties + * @{ + */ + + /** + * Specific enthalpy. Units: J/kg. + */ + doublereal enthalpy_mass() const { + return enthalpy_mole()/meanMolecularWeight(); + } + + /** + * Specific internal energy. Units: J/kg. + */ + doublereal intEnergy_mass() const { + return intEnergy_mole()/meanMolecularWeight(); + } + + /** + * Specific entropy. Units: J/kg/K. + */ + doublereal entropy_mass() const { + return entropy_mole()/meanMolecularWeight(); + } + + /** + * Specific Gibbs function. Units: J/kg. + */ + doublereal gibbs_mass() const { + return gibbs_mole()/meanMolecularWeight(); + } + + /** + * Specific heat at constant pressure. Units: J/kg/K. + */ + doublereal cp_mass() const { + return cp_mole()/meanMolecularWeight(); + } + + /** + * Specific heat at constant volume. Units: J/kg/K. + */ + doublereal cv_mass() const { + return cv_mole()/meanMolecularWeight(); + } + //@} + + //! Return the Gas Constant multiplied by the current temperature + /*! + * The units are Joules kmol-1 + */ + doublereal _RT() const { + return temperature() * GasConstant; + } + + /** + * @name Setting the State + * + * These methods set all or part of the thermodynamic + * state. + * @{ + */ + + //! Set the temperature (K), pressure (Pa), and mole fractions. + /*! + * Note, the mole fractions are set first before the pressure is set. + * Setting the pressure may involve the solution of a nonlinear equation. + * + * @param t Temperature (K) + * @param p Pressure (Pa) + * @param x Vector of mole fractions. + * Length is equal to m_kk. + */ + void setState_TPX(doublereal t, doublereal p, const doublereal* x); + + //! Set the temperature (K), pressure (Pa), and mole fractions. + /*! + * Note, the mole fractions are set first before the pressure is set. + * Setting the pressure may involve the solution of a nonlinear equation. + * + * @param t Temperature (K) + * @param p Pressure (Pa) + * @param x Composition map of mole fractions. Species not in + * the composition map are assumed to have zero mole fraction + */ + void setState_TPX(doublereal t, doublereal p, compositionMap& x); + + //! Set the temperature (K), pressure (Pa), and mole fractions. + /*! + * Note, the mole fractions are set first before the pressure is set. + * Setting the pressure may involve the solution of a nonlinear equation. + * + * @param t Temperature (K) + * @param p Pressure (Pa) + * @param x String containing a composition map of the mole fractions. Species not in + * the composition map are assumed to have zero mole fraction + */ + void setState_TPX(doublereal t, doublereal p, const std::string& x); + + //! Set the internally storred temperature (K), pressure (Pa), and mass fractions of the phase. + /*! + * Note, the mass fractions are set first before the pressure is set. + * Setting the pressure may involve the solution of a nonlinear equation. + * + * @param t Temperature (K) + * @param p Pressure (Pa) + * @param y Vector of mass fractions. + * Length is equal to m_kk. + */ + void setState_TPY(doublereal t, doublereal p, const doublereal* y); + + //! Set the internally storred temperature (K), pressure (Pa), and mass fractions of the phase + /*! + * Note, the mass fractions are set first before the pressure is set. + * Setting the pressure may involve the solution of a nonlinear equation. + * + * @param t Temperature (K) + * @param p Pressure (Pa) + * @param y Composition map of mass fractions. Species not in + * the composition map are assumed to have zero mass fraction + */ + void setState_TPY(doublereal t, doublereal p, compositionMap& y); + + //! Set the internally storred temperature (K), pressure (Pa), and mass fractions of the phase + /*! + * Note, the mass fractions are set first before the pressure is set. + * Setting the pressure may involve the solution of a nonlinear equation. + * + * @param t Temperature (K) + * @param p Pressure (Pa) + * @param y String containing a composition map of the mass fractions. Species not in + * the composition map are assumed to have zero mass fraction + */ + void setState_TPY(doublereal t, doublereal p, const std::string& y); + + //! Set the temperature (K) and pressure (Pa) + /*! + * Setting the pressure may involve the solution of a nonlinear equation. + * + * @param t Temperature (K) + * @param p Pressure (Pa) + */ + void setState_TP(doublereal t, doublereal p); + + //! Set the pressure (Pa) and mole fractions. + /*! + * Note, the mole fractions are set first before the pressure is set. + * Setting the pressure may involve the solution of a nonlinear equation. + * + * @param p Pressure (Pa) + * @param x Vector of mole fractions. + * Length is equal to m_kk. + */ + void setState_PX(doublereal p, doublereal* x); + + + //! Set the internally storred pressure (Pa) and mass fractions. + /*! + * Note, the temperature is held constant during this operation. + * Note, the mass fractions are set first before the pressure is set. + * Setting the pressure may involve the solution of a nonlinear equation. + * + * @param p Pressure (Pa) + * @param y Vector of mass fractions. + * Length is equal to m_kk. + */ + void setState_PY(doublereal p, doublereal* y); + + //! Set the internally storred specific enthalpy (J/kg) and pressure (Pa) of the phase. + /*! + * @param h Specific enthalpy (J/kg) + * @param p Pressure (Pa) + * @param tol Optional parameter setting the tolerance of the + * calculation. + */ + virtual void setState_HP(doublereal h, doublereal p, + doublereal tol = 1.e-4); + + //! Set the specific internal energy (J/kg) and specific volume (m^3/kg). + /*! + * This function fixes the internal state of the phase so that + * the specific internal energy and specific volume have the value of the input parameters. + * + * @param u specific internal energy (J/kg) + * @param v specific volume (m^3/kg). + * @param tol Optional parameter setting the tolerance of the + * calculation. + */ + virtual void setState_UV(doublereal u, doublereal v, + doublereal tol = 1.e-4); + + //! Set the specific entropy (J/kg/K) and pressure (Pa). + /*! + * This function fixes the internal state of the phase so that + * the specific entropy and the pressure have the value of the input parameters. + * + * @param s specific entropy (J/kg/K) + * @param p specific pressure (Pa). + * @param tol Optional parameter setting the tolerance of the + * calculation. + */ + virtual void setState_SP(doublereal s, doublereal p, + doublereal tol = 1.e-4); + + //! Set the specific entropy (J/kg/K) and specific volume (m^3/kg). + /*! + * This function fixes the internal state of the phase so that + * the specific entropy and specific volume have the value of the input parameters. + * + * @param s specific entropy (J/kg/K) + * @param v specific volume (m^3/kg). + * @param tol Optional parameter setting the tolerance of the + * calculation. + */ + virtual void setState_SV(doublereal s, doublereal v, doublereal tol = 1.e-4); + + //@} + + /** + * @name Chemical Equilibrium + * Chemical equilibrium. + * @{ + */ + + + //!This method is used by the ChemEquil equilibrium solver. + /*! + * It sets the state such that the chemical potentials satisfy + * \f[ \frac{\mu_k}{\hat R T} = \sum_m A_{k,m} + * \left(\frac{\lambda_m} {\hat R T}\right) \f] where + * \f$ \lambda_m \f$ is the element potential of element m. The + * temperature is unchanged. Any phase (ideal or not) that + * implements this method can be equilibrated by ChemEquil. + * + * @param lambda_RT Input vector of dimensionless element potentials + * The length is equal to nElements(). + */ + virtual void setToEquilState(const doublereal* lambda_RT) { + err("setToEquilState"); + } + + //! Stores the element potentials in the ThermoPhase object + /*! + * Called by function 'equilibrate' in ChemEquil.h to transfer + * the element potentials to this object after every successful + * equilibration routine. + * The element potentials are storred in their dimensionless + * forms, calculated by dividing by RT. + * + * @param lambda Input vector containing the element potentials. + * Length = nElements. Units are Joules/kmol. + */ + void setElementPotentials(const vector_fp& lambda); + + + //! Returns the element potentials storred in the ThermoPhase object + /*! + * Returns the storred element potentials. + * The element potentials are retrieved from their storred + * dimensionless forms by multiplying by RT. + * @param lambda Output vector containing the element potentials. + * Length = nElements. Units are Joules/kmol. + * @return bool indicating whether thare are any valid storred element + * potentials. The calling routine should check this + * bool. In the case that there aren't any, lambda is not + * touched. + */ + bool getElementPotentials(doublereal* lambda) const; + + //@} + + + //--------------------------------------------------------- + /// @name Critical State Properties. + /// These methods are only implemented by some subclasses, and may + /// be moved out of ThermoPhase at a later date. + + //@{ + + /// Critical temperature (K). + virtual doublereal critTemperature() const { + err("critTemperature"); return -1.0; + } + + /// Critical pressure (Pa). + virtual doublereal critPressure() const { + err("critPressure"); return -1.0; + } + + /// Critical density (kg/m3). + virtual doublereal critDensity() const { + err("critDensity"); return -1.0; + } + + //@} + + //! @name Saturation properties. + /*! + * These methods are only implemented by subclasses that + * implement full liquid-vapor equations of state. They may be + * moved out of ThermoPhase at a later date. + */ + //@{ + + //! Return the saturation temperature given the pressure + /*! + * @param p Pressure (Pa) + */ + virtual doublereal satTemperature(doublereal p) const { + err("satTemperature"); return -1.0; + } + + //! Return the saturation pressure given the temperature + /*! + * @param t Temperature (Kelvin) + */ + virtual doublereal satPressure(doublereal t) const { + err("satPressure"); return -1.0; + } + + //! Return the fraction of vapor at the current conditions + virtual doublereal vaporFraction() const { + err("vaprFraction"); return -1.0; + } + + //! Set the state to a saturated system at a particular temperature + /*! + * @param t Temperature (kelvin) + * @param x Fraction of vapor + */ + virtual void setState_Tsat(doublereal t, doublereal x) { + err("setState_sat"); + } + + //! Set the state to a saturated system at a particular pressure + /*! + * @param p Pressure (Pa) + * @param x Fraction of vapor + */ + virtual void setState_Psat(doublereal p, doublereal x) { + err("setState_sat"); + } + + //@} + + + //! @name Initialization Methods - For Internal Use (%ThermoPhase) + /*! + * The following methods are used in the process of constructing + * the phase and setting its parameters from a specification in an + * input file. They are not normally used in application programs. + * To see how they are used, + * see files importCTML.cpp and ThermoFactory.cpp. + */ + //@{ + + //! Store a reference to the XML tree containing the species data for this phase. + /*! + * This is used to access data needed to construct transport manager later. + * @internal + * + * @param data Pointer to the XML_Node data containing + * information about the species in the phase. + */ + void saveSpeciesData(const XML_Node* data) { + m_speciesData = data; + } + + /// Return a pointer to the XML tree containing the species + /// data for this phase. + const XML_Node* speciesData() { + if (!m_speciesData) { + throw CanteraError("ThermoPhase::speciesData", + "m_speciesData is NULL"); + } + return m_speciesData; + } + + + + //! Install a species thermodynamic property manager. + /*! + * The species thermodynamic property manager + * computes properties of the pure species for use in + * constructing solution properties. It is meant for internal + * use, and some classes derived from ThermoPhase may not use + * any species thermodynamic property manager. This method is + * called by function importPhase() in importCTML.cpp. + * + * @param spthermo input pointer to the species thermodynamic property + * manager. + * + * @internal + */ + void setSpeciesThermo(SpeciesThermo* spthermo) + { m_spthermo = spthermo; } + + /** + * @internal Return a reference to the species thermodynamic property + * manager. @todo This method will fail if no species thermo + * manager has been installed. + */ + SpeciesThermo& speciesThermo() { return *m_spthermo; } + + /** + * @internal + * Initialization of a ThermoPhase object using an + * ctml file. + * + * This routine is a precursor to initThermoXML(XML_Node*) + * routine, which does most of the work. + * Here we read extra information about the XML description + * of a phase. Regular information about elements and species + * and their reference state thermodynamic information + * have already been read at this point. + * For example, we do not need to call this function for + * ideal gas equations of state. + * + * @param inputFile 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 encountered will be used. + */ + virtual void initThermoFile(std::string inputFile, std::string id); + + + //!Import and initialize a ThermoPhase object using an XML tree. + /*! + * @internal + * + * Here we read extra information about the XML description + * of a phase. Regular information about elements and species + * and their reference state thermodynamic information + * have already been read at this point. + * For example, we do not need to call this function for + * ideal gas equations of state. This function is called from importPhase() + * after the elements and the species are initialized with + * default ideal solution level data. + * + * The default implementation in ThermoPhase calls the + * virtual function initThermo() and then sets the "state" of the + * phase by looking for an XML element named "state", and then + * interpreting its contents by calling the virtual function + * setStateFromXML(). + * + * @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. + */ + virtual void initThermoXML(XML_Node& phaseNode, std::string id); + + //! Initialize the ThermoPhase object after all species have been set up + /*! + * @internal 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 from ThermoPhase::initThermoXML(), + * which is called from importPhase(), + * just prior to returning from function importPhase(). + * + * @see importCTML.cpp + */ + virtual void initThermo(); + + // The following methods are used by the clib interface + // library, and should not be used by application programs. + + /*! + * @internal + * Index number. This method can be used to identify the + * location of a phase object in a list, and is used by the + * interface library (clib) routines for this purpose. + */ + int index() { return m_index; } + + + /** + * @internal Set the index number. The Cantera interface + * library uses this method to set the index number to the + * location of the pointer to this object in the pointer array + * it maintains. Using this method for any other purpose will + * lead to unpredictable results if used in conjunction with + * the interface library. + * + * @param m Input the index number. + */ + void setIndex(int m) { m_index = m; } + + + //! Set the equation of state parameters + /*! + * @internal + * The number and meaning of these depends on the subclass. + * + * @param n number of parameters + * @param c array of \a n coefficients + */ + virtual void setParameters(int n, doublereal* c) {} + + + //! Get the equation of state parameters in a vector + /*! + * @internal + * The number and meaning of these depends on the subclass. + * + * @param n number of parameters + * @param c array of \a n coefficients + */ + virtual void getParameters(int &n, doublereal * const c) {} + + + //! Set equation of state parameter values from XML entries. + /*! + * + * This method is called by function importPhase() in + * file importCTML.cpp when processing a phase definition in + * an input file. It should be overloaded in subclasses to set + * any parameters that are specific to that particular phase + * model. Note, this method is called before the phase is + * initialzed with elements and/or species. + * + * @param eosdata An XML_Node object corresponding to + * the "thermo" entry for this phase in the input file. + */ + virtual void setParametersFromXML(const XML_Node& eosdata) {} + + + //! Set the initial state of the phase to the conditions + //! specified in the state XML element. + /*! + * + * This method sets the temperature, pressure, and mole + * fraction vector to a set default value. + * + * @param state AN XML_Node object corresponding to + * the "state" entry for this phase in the + * input file. + */ + virtual void setStateFromXML(const XML_Node& state); + + + //@} + + + protected: + + //! Pointer to the species thermodynamic property manager + SpeciesThermo* m_spthermo; + + /// 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. + const XML_Node* m_speciesData; + + //! Index number of the phase + /*! + * The Cantera interface library uses this member to set the index number to the + * location of the pointer to this object in the pointer array of ThermoPhase's + * it maintains. Using this member for any other purpose will + * lead to unpredictable results if used in conjunction with + * the interface library. + */ + int m_index; + + //! Storred value of the electric potential for this phase + /*! + * Units are Volts + */ + doublereal m_phi; + + /// Vector of element potentials. + /// -> length equal to number of elements + vector_fp m_lambdaRRT; + //! Boolean indicating whether there is a valid set of saved element potentials for this phase + bool m_hasElementPotentials; + + private: + + //! Error function that gets called for unhandled cases + /*! + * @param msg String containing the message. + */ + doublereal err(std::string msg) const; + + }; + + //! typedef for the ThermoPhase class + typedef ThermoPhase thermophase_t; + + //! typedef for the ThermoPhase class + typedef ThermoPhase thermo_t; +} + +#endif + diff --git a/Cantera/src/thermo/WaterPDSS.cpp b/Cantera/src/thermo/WaterPDSS.cpp index 7d92787cc..628d408b0 100644 --- a/Cantera/src/thermo/WaterPDSS.cpp +++ b/Cantera/src/thermo/WaterPDSS.cpp @@ -15,7 +15,8 @@ #include "ctml.h" #include "WaterPDSS.h" #include "WaterPropsIAPWS.h" -#include "importCTML.h" +//#include "importCTML.h" +#include "ThermoFactory.h" #include diff --git a/Cantera/src/thermo/WaterSSTP.cpp b/Cantera/src/thermo/WaterSSTP.cpp index c8963fda7..4c1d24ba2 100644 --- a/Cantera/src/thermo/WaterSSTP.cpp +++ b/Cantera/src/thermo/WaterSSTP.cpp @@ -15,7 +15,8 @@ #include "xml.h" #include "WaterSSTP.h" #include "WaterPropsIAPWS.h" -#include "importCTML.h" +//#include "importCTML.h" +#include "ThermoFactory.h" #include namespace Cantera { diff --git a/Cantera/src/thermo/mix_defs.h b/Cantera/src/thermo/mix_defs.h new file mode 100755 index 000000000..cbcb42718 --- /dev/null +++ b/Cantera/src/thermo/mix_defs.h @@ -0,0 +1,62 @@ +#ifndef CT_MIX_DEFS_H +#define CT_MIX_DEFS_H + +namespace Cantera { + + /** + * This generic id is used as the default in virtual base + * classes that employ id's. It is used to indicate the lack + * of an inherited class that would define the id. + */ + const int cNone = 0; + + // species thermo types + const int cNASA = 1; + const int cShomate = 2; + const int cNASA96 = 3; + + /** + * Equation of state types: + * + * These types are used in the member function eosType() of + * the virtual base class ThermoPhase. They are used to + * distinguish different types of equation of states. Also, they + * may be used for upcasting from the ThermoPhase class. Their + * id's should be distinct. + * + * Users who wish to define their own equation of states which + * derive from ThermoPhase should define a unique id which + * doesn't conflict with those listed below. The Cantera Kernel + * however, will not be know about the class and will therefore + * not be able to initialize the class within its "factory" + * routines. + */ + const int cIdealGas = 1; // IdealGasPhase in IdealGasPhase.h + const int cIncompressible = 2; // ConstDensityThermo in ConstDensityThermo.h + /// A surface phase. Used by class SurfPhase. + const int cSurf = 3; + + /// A metal phase. + const int cMetal = 4; // MetalPhase in MetalPhase.h + // const int cSolidCompound = 5; // SolidCompound in SolidCompound.h + const int cStoichSubstance = 5; // StoichSubstance.h + + const int cLatticeSolid = 20; // LatticeSolidPhase.h + const int cLattice = 21; + + // pure fluids with liquid/vapor eqs of state + const int cPureFluid = 10; + + /// An edge between two 2D surfaces + const int cEdge = 6; + + // kinetic manager types + const int cGasKinetics = 2; + const int cGRI30 = 3; + const int cInterfaceKinetics = 4; + const int cLineKinetics = 5; + const int cEdgeKinetics = 6; + const int cSolidKinetics = 7; +} + +#endif diff --git a/Cantera/src/thermo/phasereport.cpp b/Cantera/src/thermo/phasereport.cpp new file mode 100644 index 000000000..147aad76b --- /dev/null +++ b/Cantera/src/thermo/phasereport.cpp @@ -0,0 +1,170 @@ + +// turn off warnings under Windows +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "ThermoPhase.h" +#include "PureFluidPhase.h" +#include +#include "mix_defs.h" + +using namespace std; + +namespace Cantera { + + /** + * Format a summary of the mixture state for output. + */ + string report(const ThermoPhase& th, bool show_thermo) { + + char p[200]; + string s = ""; + try { + if (th.name() != "") { + sprintf(p, " \n %s:\n", th.name().c_str()); + s += p; + } + sprintf(p, " \n temperature %12.6g K\n", th.temperature()); + s += p; + sprintf(p, " pressure %12.6g Pa\n", th.pressure()); + s += p; + sprintf(p, " density %12.6g kg/m^3\n", th.density()); + s += p; + sprintf(p, " mean mol. weight %12.6g amu\n", th.meanMolecularWeight()); + s += p; +#ifdef WITH_PURE_FLUIDS + if (th.eosType() == cPureFluid) { + double xx = ((PureFluidPhase*)(&th))->vaporFraction(); + // if (th.temperature() < th.critTemperature()) { + sprintf(p, " vapor fraction %12.6g \n", + xx); //th.vaporFraction()); + s += p; + //} + } +#endif + doublereal phi = th.electricPotential(); + if (phi != 0.0) { + sprintf(p, " potential %12.6g V\n", phi); + s += p; + } + if (show_thermo) { + sprintf(p, " \n"); + s += p; + sprintf(p, " 1 kg 1 kmol\n"); + s += p; + sprintf(p, " ----------- ------------\n"); + s += p; + sprintf(p, " enthalpy %12.6g %12.4g J\n", + th.enthalpy_mass(), th.enthalpy_mole()); + s += p; + sprintf(p, " internal energy %12.6g %12.4g J\n", + th.intEnergy_mass(), th.intEnergy_mole()); + s += p; + sprintf(p, " entropy %12.6g %12.4g J/K\n", + th.entropy_mass(), th.entropy_mole()); + s += p; + sprintf(p, " Gibbs function %12.6g %12.4g J\n", + th.gibbs_mass(), th.gibbs_mole()); + s += p; + sprintf(p, " heat capacity c_p %12.6g %12.4g J/K\n", + th.cp_mass(), th.cp_mole()); + s += p; + sprintf(p, " heat capacity c_v %12.6g %12.4g J/K\n", + th.cv_mass(), th.cv_mole()); + s += p; + } + + int kk = th.nSpecies(); + array_fp x(kk); + array_fp y(kk); + array_fp mu(kk); + th.getMoleFractions(&x[0]); + th.getMassFractions(&y[0]); + th.getChemPotentials(&mu[0]); + doublereal rt = GasConstant * th.temperature(); + int k; + if (th.nSpecies() > 1) { + + if (show_thermo) { + sprintf(p, " \n X " + " Y Chem. Pot. / RT \n"); + s += p; + sprintf(p, " ------------- " + "------------ ------------\n"); + s += p; + for (k = 0; k < kk; k++) { + if (x[k] > SmallNumber) { + sprintf(p, "%18s %12.6g %12.6g %12.6g\n", + th.speciesName(k).c_str(), x[k], y[k], mu[k]/rt); + } + else { + sprintf(p, "%18s %12.6g %12.6g \n", + th.speciesName(k).c_str(), x[k], y[k]); + } + s += p; + } + } + else { + sprintf(p, " \n X" + "Y\n"); + s += p; + sprintf(p, " -------------" + " ------------\n"); + s += p; + for (k = 0; k < kk; k++) { + sprintf(p, "%18s %12.6g %12.6g\n", + th.speciesName(k).c_str(), x[k], y[k]); + s += p; + } + } + } + } + catch (CanteraError) { + ; + } + return s; + } + + void writephase(const ThermoPhase& th, bool show_thermo) { + string s = report(th, show_thermo); + writelog(s+"\n"); + } + + /** + * Format a composition list for output. + */ + string formatCompList(const Phase& mix, int xyc) { + + const doublereal Threshold = 1.e-20; + + char p[200]; + string s = ""; + int kk = mix.nSpecies(); + array_fp zz(kk); + switch (xyc) { + case 0: mix.getMoleFractions(&zz[0]); break; + case 1: mix.getMassFractions(&zz[0]); break; + case 2: mix.getConcentrations(&zz[0]); break; + default: return "error: xyc must be 0, 1, or 2"; + } + + doublereal z; + int k; + for (k = 0; k < kk; k++) { + z = fabs(zz[k]); + if (z < Threshold) zz[k] = 0.0; + } + + for (k = 0; k < kk; k++) { + sprintf(p, "%18s\t %12.6e\n", mix.speciesName(k).c_str(), + zz[k]); + s += p; + } + return s; + } + +} + + diff --git a/Cantera/src/thermo/speciesThermoTypes.h b/Cantera/src/thermo/speciesThermoTypes.h new file mode 100755 index 000000000..10b383ca2 --- /dev/null +++ b/Cantera/src/thermo/speciesThermoTypes.h @@ -0,0 +1,105 @@ +/** + * @file speciesThermoTypes.h + * Contains const definitions for types of species + * reference-state thermodynamics managers (see \ref spthermo) + */ +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef SPECIES_THERMO_TYPES_H +#define SPECIES_THERMO_TYPES_H + +//! Constant Cp +#define CONSTANT_CP 1 + +//! Polynomial +#define POLYNOMIAL_4 2 + +//! Two regions of 7 coefficient NASA Polynomials +//! This is implemented in the class NasaPoly2 in NasaPoly2.h +#define NASA 4 + +//! Two regions of 7 coefficient NASA Polynomials +//! This is implemented in the class NasaPoly2 in NasaPoly2.h +#define NASA2 4 + +//! Two regions of Shomate Polynomials. +#define SHOMATE 8 + +//! Two regions of Shomate Polynomials. +#define SHOMATE2 8 + +//! Tiger Polynomials. Not implemented here. +#define TIGER 16 + +//! Constant Cp thermo. +//! This is implemented in ConstCpPoly in constCpPoly.h for one species. +//! If the whole phase is constcp, SimpleThermo in SimpleThermo.h +//! implements this for the whole phase. +#define SIMPLE 32 + +//! piecewise interpolation of mu0. +//! This is implemented in Mu0Poly in Mu0Poly.h +#define MU0_INTERP 64 + +//! one region of Shomate Polynomials used in NIST database +//! This is implemented in the NIST database. +//! This is implemented in ShomatePoly in ShomatePoly.h +#define SHOMATE1 128 + +//! 7 coefficient NASA Polynomials +//! This is implemented in the class NasaPoly1 in NasaPoly1.h +#define NASA1 256 + +#include "ct_defs.h" + +#include "stringUtils.h" +#include "global.h" + +namespace Cantera { + + //! Error for unknown thermo parameterization + struct UnknownThermoParam { + //! Constructor + /*! + * @param thermotype Integer specifying the thermo parameterization + * + * @todo Is this used? + */ + UnknownThermoParam(int thermotype) { + writelog(std::string("\n ### ERROR ### \n") + + "Unknown species thermo parameterization (" + + int2str(thermotype) + ")\n\n"); + } + }; + + + //! holds parameterization-dependent index information + /*! + * These are all integers. + * @todo Is this used? + */ + struct ThermoIndexData { + //! param + int param; + //! number of coefficients + int nCoefficients; + //! coefficient for Tmin + int Tmin_coeff; + //! coefficient for Tmax + int Tmax_coeff; + //! reference pressure coefficient + int Pref_coeff; + }; + +} + +#endif + +