From 88f917175520703fad4fda4da1775601e457e3e1 Mon Sep 17 00:00:00 2001 From: Harry Moffat Date: Mon, 19 Jun 2006 23:14:12 +0000 Subject: [PATCH] Added an IdealMolalSoln object. --- Cantera/src/thermo/IdealMolalSoln.cpp | 1026 +++++++++++++++++++++++++ Cantera/src/thermo/IdealMolalSoln.h | 841 ++++++++++++++++++++ Cantera/src/thermo/Makefile.in | 26 +- Cantera/src/thermo/MolalityVPSSTP.cpp | 963 +++++++++++------------ Cantera/src/thermo/MolalityVPSSTP.h | 781 +++++++++---------- 5 files changed, 2769 insertions(+), 868 deletions(-) create mode 100644 Cantera/src/thermo/IdealMolalSoln.cpp create mode 100644 Cantera/src/thermo/IdealMolalSoln.h diff --git a/Cantera/src/thermo/IdealMolalSoln.cpp b/Cantera/src/thermo/IdealMolalSoln.cpp new file mode 100644 index 000000000..2e28991ec --- /dev/null +++ b/Cantera/src/thermo/IdealMolalSoln.cpp @@ -0,0 +1,1026 @@ +/** + * + * @file IdealMolalSoln.cpp + */ +/* + * Copywrite (2005) Sandia Corporation. Under the terms of + * Contract DE-AC04-94AL85000 with Sandia Corporation, the + * U.S. Government retains certain rights in this software. + */ +/* + * $Author$ + * $Date$ + * $Revision$ + */ + + +#ifndef MAX +#define MAX(x,y) (( (x) > (y) ) ? (x) : (y)) +#endif + +#include "IdealMolalSoln.h" +#include "importCTML.h" + +namespace Cantera { + + /** + * Default constructor + */ + IdealMolalSoln::IdealMolalSoln() : + MolalityVPSSTP(), + m_Pcurrent(OneAtm), + m_formGC(2) + { + } + + + /** + * Copy Constructor: + * + * Note this stuff will not work until the underlying phase + * has a working copy constructor + */ + IdealMolalSoln::IdealMolalSoln(const IdealMolalSoln &b) : + MolalityVPSSTP(b) + { + /* + * Use the assignment operator to do the brunt + * of the work for the copy construtor. + */ + *this = b; + } + + /** + * operator=() + * + * Note this stuff will not work until the underlying phase + * has a working assignment operator + */ + IdealMolalSoln& IdealMolalSoln:: + operator=(const IdealMolalSoln &b) { + if (&b != this) { + MolalityVPSSTP::operator=(b); + m_speciesMolarVolume = b.m_speciesMolarVolume; + m_Pcurrent = b.m_Pcurrent; + m_formGC = b.m_formGC; + m_expg0_RT = b.m_expg0_RT; + m_pe = b.m_pe; + m_pp = b.m_pp; + m_tmpV = b.m_tmpV; + } + return *this; + } + + IdealMolalSoln::IdealMolalSoln(string inputFile, string id) : + MolalityVPSSTP() + { + constructPhaseFile(inputFile, id); + } + + IdealMolalSoln::IdealMolalSoln(XML_Node& root, string id) : + MolalityVPSSTP() + { + constructPhaseXML(root, id); + } + + /** + * + * ~IdealMolalSoln(): (virtual) + * + * Destructor: does nothing: + * + */ + IdealMolalSoln::~IdealMolalSoln() { + } + + /** + * + */ + ThermoPhase* IdealMolalSoln::duplMyselfAsThermoPhase() { + IdealMolalSoln* mtp = new IdealMolalSoln(*this); + return (ThermoPhase *) mtp; + } + + // + // -------- Molar Thermodynamic Properties of the Solution --------------- + // + /** + * Molar enthalpy of the solution. Units: J/kmol. + */ + doublereal IdealMolalSoln::enthalpy_mole() const { + getPartialMolarEnthalpies(DATA_PTR(m_tmpV)); + getMoleFractions(DATA_PTR(m_pp)); + double val = mean_X(DATA_PTR(m_tmpV)); + return val; + } + + /** + * Molar internal energy of the solution. Units: J/kmol. + */ + doublereal IdealMolalSoln::intEnergy_mole() const { + getPartialMolarEnthalpies(DATA_PTR(m_tmpV)); + return mean_X(DATA_PTR(m_tmpV)); + } + + doublereal IdealMolalSoln::entropy_mole() const { + getPartialMolarEntropies(DATA_PTR(m_tmpV)); + return mean_X(DATA_PTR(m_tmpV)); + } + + /// Molar Gibbs function. Units: J/kmol. + doublereal IdealMolalSoln::gibbs_mole() const { + getChemPotentials(DATA_PTR(m_tmpV)); + return mean_X(DATA_PTR(m_tmpV)); + } + + /// Molar heat capacity at constant pressure. Units: J/kmol/K. + doublereal IdealMolalSoln::cp_mole() const { + getPartialMolarCp(DATA_PTR(m_tmpV)); + double val = mean_X(DATA_PTR(m_tmpV)); + return val; + } + + + /// Molar heat capacity at constant volume. Units: J/kmol/K. + doublereal IdealMolalSoln::cv_mole() const { + return err("not implemented"); + } + + // + // ------- Mechanical Equation of State Properties ------------------------ + // + + /** + * Pressure. Units: Pa. + * For this incompressible system, we return the internally storred + * independent value of the pressure. + */ + doublereal IdealMolalSoln::pressure() const { + return m_Pcurrent; + } + + /** + * 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] + * + * It's equal to zero for this model, since the molar volume + * doesn't change with pressure or temperature. + */ + doublereal IdealMolalSoln::isothermalCompressibility() const { + return 0.0; + } + + /** + * The 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] + * + * It's equal to zero for this model, since the molar volume + * doesn't change with pressure or temperature. + */ + doublereal IdealMolalSoln::thermalExpansionCoeff() const { + return 0.0; + } + + /** + * Overwritten setDensity() function is necessary because the + * density is not an indendent variable. + * + * This function will now throw an error condition + * + * @internal May have to adjust the strategy here to make + * the eos for these materials slightly compressible, in order + * to create a condition where the density is a function of + * the pressure. + * + * This function will now throw an error condition. + * + * NOTE: This is an overwritten function from the State.h + * class + */ + void IdealMolalSoln::setDensity(doublereal rho) { + double dens = density(); + if (rho != dens) { + throw CanteraError("Idea;MolalSoln::setDensity", + "Density is not an independent variable"); + } + } + + /** + * Overwritten setMolarDensity() function is necessary because the + * density is not an indendent variable. + * + * This function will now throw an error condition. + * + * NOTE: This is a virtual function, overwritten function from the State.h + * class + */ + void IdealMolalSoln::setMolarDensity(doublereal conc) { + double concI = State::molarDensity(); + if (conc != concI) { + throw CanteraError("IdealMolalSoln::setMolarDensity", + "molarDensity/denisty is not an independent variable"); + } + } + + // + // ------- Activities and Activity Concentrations + // + + /** + * This method returns an array of generalized concentrations + * \f$ C_k\f$ that are defined such that + * \f$ a_k = C_k / C^0_k, \f$ where \f$ C^0_k \f$ + * is a standard concentration + * defined below. These generalized concentrations are used + * by kinetics manager classes to compute the forward and + * reverse rates of elementary reactions. + * + * @param c Array of generalized concentrations. The + * units depend upon the implementation of the + * reaction rate expressions within the phase. + */ + void IdealMolalSoln::getActivityConcentrations(doublereal* c) const { + if (m_formGC != 1) { + double c_solvent = standardConcentration(); + getActivities(c); + for (int k = 0; k < m_kk; k++) { + c[k] *= c_solvent; + } + } else { + getActivities(c); + for (int k = 0; k < m_kk; k++) { + double c0 = standardConcentration(k); + c[k] *= c0; + } + } + } + + /** + * The standard concentration \f$ C^0_k \f$ used to normalize + * the 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. + * + * For the time being we will use the concentration of pure + * solvent for the the standard concentration of all species. + * This has the effect of making reaction rates + * based on the molality of species proportional to the + * molality of the species. + */ + doublereal IdealMolalSoln::standardConcentration(int k) const { + double c0, mvSolvent; + switch (m_formGC) { + case 0: + c0 = 1.0; + break; + case 1: + c0 = 1.0 /m_speciesMolarVolume[m_indexSolvent]; + break; + case 2: + mvSolvent = m_speciesMolarVolume[m_indexSolvent]; + c0 = 1.0 / mvSolvent; + break; + } + return c0; + } + + /** + * Returns the natural logarithm of the standard + * concentration of the kth species + */ + doublereal IdealMolalSoln::logStandardConc(int k) const { + double c0 = standardConcentration(k); + return log(c0); + } + + /** + * Returns the units of the standard and general concentrations + * Note they have the same units, as their divisor is + * defined to be equal to the activity of the kth species + * in the solution, which is unitless. + * + * This routine is used in print out applications where the + * units are needed. Usually, MKS units are assumed throughout + * the program and in the XML input files. + * + * On return uA contains the powers of the units (MKS assumed) + * of the standard concentrations and generalized concentrations + * for the kth species. + * + * uA[0] = kmol units - default = 1 + * uA[1] = m units - default = -nDim(), the number of spatial + * dimensions in the Phase class. + * uA[2] = kg units - default = 0; + * uA[3] = Pa(pressure) units - default = 0; + * uA[4] = Temperature units - default = 0; + * uA[5] = time units - default = 0 + */ + void IdealMolalSoln::getUnitsStandardConc(double *uA, int k, int sizeUA) { + int eos = eosType(); + if (eos == 0) { + for (int i = 0; i < sizeUA; i++) { + uA[i] = 0.0; + } + } else { + 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; + } + } + } + + /** + * Get the array of non-dimensional molality-based + * activities at the current solution temperature, + * pressure, and solution concentration. + * + * The max against 8.689E-3 is to limit the activity + * coefficient to be greater than 1.0E-50. + */ + void IdealMolalSoln::getActivities(doublereal* ac) const { + /* + * Update the molality array, m_molalities() + * This requires an update due to mole fractions + */ + calcMolalities(); + for (int k = 0; k < m_kk; k++) { + ac[k] = m_molalities[k]; + } + double xmolSolvent = moleFraction(m_indexSolvent); + xmolSolvent = MAX(8.689E-3, xmolSolvent); + ac[m_indexSolvent] = + exp((xmolSolvent - 1.0)/xmolSolvent); + } + + /** + * Get the array of non-dimensional Molality based + * activity coefficients at + * the current solution temperature, pressure, and + * solution concentration. + * See Denbigh + * (note solvent activity coefficient is on the molar scale). + * + * The max against 5.0E-3 (1/200) is to limit the activity + * coefficient to be greater than 1.0E-50. + */ + void IdealMolalSoln:: + getMolalityActivityCoefficients(doublereal* acMolality) const { + for (int k = 0; k < m_kk; k++) { + acMolality[k] = 1.0; + } + double xmolSolvent = moleFraction(m_indexSolvent); + xmolSolvent = MAX(8.689E-3, xmolSolvent); + acMolality[m_indexSolvent] = + exp((xmolSolvent - 1.0)/xmolSolvent) / xmolSolvent; + } + + // + // ------ 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. + * + * \f[ + * \mu_k = \mu^{o}_k(T,P) + R T ln(m_k / m_units) + * \f] + * + * where m_units is equal to 1 gmol kg-1 + * + * \f[ + * \mu_solvent = \mu^{o}_solvent(T,P) + + * R T ((X_solvent - 1.0) / X_solvent) + * \f] + */ + void IdealMolalSoln::getChemPotentials(doublereal* mu) const{ + double xx; + const double xxSmall = 1.0E-150; + /* + * First get the standard chemical potentials + * -> this requires updates of standard state as a function + * of T and P + * These are defined at unit molality. + */ + getStandardChemPotentials(mu); + /* + * Update the molality array, m_molalities() + * This requires an update due to mole fractions + */ + calcMolalities(); + /* + * + */ + doublereal RT = GasConstant * temperature(); + for (int k = 0; k < m_kk; k++) { + if (k != m_indexSolvent) { + xx = MAX(m_molalities[k], xxSmall); + mu[k] += RT * log(xx); + } + } + /* + * Do the solvent + * -> see my notes + */ + double xmolSolvent = moleFraction(m_indexSolvent); + xx = MAX(xmolSolvent, xxSmall); + mu[m_indexSolvent] += + (RT * (xmolSolvent - 1.0) / xx); + } + + /** + * Returns an array of partial molar enthalpies for the species + * in the mixture. + * Units (J/kmol) + * For this phase, the partial molar enthalpies are equal to the + * SS species enthalpies + * \f[ + * \bar h_k(T,P) = \hat h^{0}_k(T,P) + * \f] + * + * note hbar = ubar + T d(ubar/dT) + * see note about partial molar entropies. + */ + void IdealMolalSoln::getPartialMolarEnthalpies(doublereal* hbar) const { + getEnthalpy_RT(hbar); + doublereal RT = _RT(); + for (int k = 0; k < m_kk; k++) { + hbar[k] *= RT; + } + } + + /** + * + * getPartialMolarEntropies() (virtual, const) + * + * Returns an array of partial molar entropies of the species in the + * solution. Units: J/kmol. + * + * Maxwell's equations provide an insight in how to calculate this + * (p.215 Smith and Van Ness) + * + * d(chemPot_i)/dT = -sbar_i + * + * + * For this phase, the partial molar entropies are equal to the + * SS species entropies plus the ideal solution contribution. + * + * \f[ + * \bar s_k(T,P) = \hat s^0_k(T) - R log( molality[k] / m_units) + * \f] + * \f[ + * \bar s_solvent(T,P) = \hat s^0_solvent(T) + * - R ((xmolSolvent - 1.0) / xmolSolvent) + * \f] + * + * The reference-state pure-species entropies,\f$ \hat s^0_k(T) \f$, + * at the reference pressure, \f$ P_{ref} \f$, are computed by the + * species thermodynamic + * property manager. They are polynomial functions of temperature. + * @see SpeciesThermo + */ + void IdealMolalSoln:: + getPartialMolarEntropies(doublereal* sbar) const { + getEntropy_R(sbar); + doublereal R = GasConstant; + doublereal mm; + calcMolalities(); + for (int k = 0; k < m_kk; k++) { + if (k != m_indexSolvent) { + mm = fmaxx(SmallNumber, m_molalities[k]); + sbar[k] -= R * log(mm); + } + } + double xmolSolvent = moleFraction(m_indexSolvent); + sbar[m_indexSolvent] -= (R * (xmolSolvent - 1.0) / xmolSolvent); + } + + /** + * returns an array of partial molar volumes of the species + * in the solution. Units: m^3 kmol-1. + * + * For this solution, the partial molar volumes are equal to the + * constant species molar volumes. + */ + void IdealMolalSoln::getPartialMolarVolumes(doublereal* vbar) const { + getStandardVolumes(vbar); + } + + /* + * Partial molar heat capacity of the solution: + * The kth partial molar heat capacity is equal to + * the temperature derivative of the partial molar + * enthalpy of the kth species in the solution at constant + * P and composition (p. 220 Smith and Van Ness). + * + * Cp = -T d2(chemPot_i)/dT2 + */ + void IdealMolalSoln::getPartialMolarCp(doublereal* cpbar) const { + /* + * Get the nondimensional gibbs standard state of the + * species at the T and P of the solution. + */ + getCp_R(cpbar); + + for (int k = 0; k < m_kk; k++) { + cpbar[k] *= GasConstant; + } + } + + /* + * -------- Properties of the Standard State of the Species + * in the Solution ------------------ + */ + + /** + * getStandardChemPotentials() (virtual, const) + * + * + * Get the standard state chemical potentials of the species. + * This is the array of chemical potentials at unit activity + * (Mole fraction scale) + * \f$ \mu^0_k(T,P) \f$. + * We define these here as the chemical potentials of the pure + * species at the temperature and pressure of the solution. + * This function is used in the evaluation of the + * equilibrium constant Kc. Therefore, Kc will also depend + * on T and P. This is the norm for liquid and solid systems. + * + * units = J / kmol + */ + void IdealMolalSoln::getStandardChemPotentials(doublereal* mu) const { + getGibbs_ref(mu); + doublereal pref; + doublereal delta_p; + for (int k = 0; k < m_kk; k++) { + pref = m_spthermo->refPressure(k); + delta_p = m_Pcurrent - pref; + mu[k] += delta_p * m_speciesMolarVolume[k]; + } + } + + /** + * Get the nondimensional gibbs function for the species + * standard states at the current T and P of the solution. + * + * \f[ + * \mu^0_k(T,P) = \mu^{ref}_k(T) + (P - P_{ref}) * V_k + * \f] + * where \f$V_k\f$ is the molar volume of pure species k. + * \f$ \mu^{ref}_k(T)\f$ is the chemical potential of pure + * species k at the reference pressure, \f$P_{ref}\f$. + * + * @param grt Vector of length m_kk, which on return sr[k] + * will contain the nondimensional + * standard state gibbs function for species k. + */ + void IdealMolalSoln::getGibbs_RT(doublereal* grt) const { + getPureGibbs(grt); + doublereal invRT = 1.0 / _RT(); + for (int k = 0; k < m_kk; k++) { + grt[k] *= invRT; + } + } + + /** + * + * getPureGibbs() + * + * Get the Gibbs functions for the pure species + * at the current T and P of the solution. + * We assume an incompressible constant partial molar + * volume here: + * \f[ + * \mu^0_k(T,p) = \mu^{ref}_k(T) + (P - P_{ref}) * V_k + * \f] + * where \f$V_k\f$ is the molar volume of pure species k<\I>. + * \f$ u^{ref}_k(T)\f$ is the chemical potential of pure + * species k<\I> at the reference pressure, \f$P_{ref}\f$. + */ + void IdealMolalSoln::getPureGibbs(doublereal* gpure) const { + getGibbs_ref(gpure); + doublereal pref; + doublereal delta_p; + for (int k = 0; k < m_kk; k++) { + pref = m_spthermo->refPressure(k); + delta_p = m_Pcurrent - pref; + gpure[k] += delta_p * m_speciesMolarVolume[k]; + } + } + + /** + * + * getEnthalpy_RT() (virtual, const) + * + * Get the array of nondimensional Enthalpy functions for the ss + * species at the current T and P of the solution. + * We assume an incompressible constant partial molar + * volume here: + * \f[ + * h^0_k(T,P) = h^{ref}_k(T) + (P - P_{ref}) * V_k + * \f] + * where \f$V_k\f$ is the molar volume of SS species k<\I>. + * \f$ h^{ref}_k(T)\f$ is the enthalpy of the SS + * species k<\I> at the reference pressure, \f$P_{ref}\f$. + */ + void IdealMolalSoln:: + getEnthalpy_RT(doublereal* hrt) const { + getEnthalpy_RT_ref(hrt); + doublereal pref; + doublereal delta_p; + double RT = _RT(); + for (int k = 0; k < m_kk; k++) { + pref = m_spthermo->refPressure(k); + delta_p = m_Pcurrent - pref; + hrt[k] += delta_p/ RT * m_speciesMolarVolume[k]; + } + } + + /** + * getEntropy_R() (virtual, const) + * + * Get the nondimensional Entropies for the species + * standard states at the current T and P of the solution. + * + * Note, this is equal to the reference state entropies + * due to the zero volume expansivity: + * i.e., (dS/dp)_T = (dV/dT)_P = 0.0 + * + * @param sr Vector of length m_kk, which on return sr[k] + * will contain the nondimensional + * standard state entropy of species k. + */ + void IdealMolalSoln:: + getEntropy_R(doublereal* sr) const { + getEntropy_R_ref(sr); + } + + /** + * Get the nondimensional heat capacity at constant pressure + * function for the species + * standard states at the current T and P of the solution. + * \f[ + * Cp^0_k(T,P) = Cp^{ref}_k(T) + * \f] + * where \f$V_k\f$ is the molar volume of pure species k. + * \f$ Cp^{ref}_k(T)\f$ is the constant pressure heat capacity + * of species k at the reference pressure, \f$p_{ref}\f$. + * + * @param cpr Vector of length m_kk, which on return cpr[k] + * will contain the nondimensional + * constant pressure heat capacity for species k. + */ + void IdealMolalSoln::getCp_R(doublereal* cpr) const { + getCp_R_ref(cpr); + } + + /** + * Get the molar volumes of each species in their standard + * states at the current + * T and P of the solution. + * units = m^3 / kmol + */ + void IdealMolalSoln::getStandardVolumes(doublereal *vol) const { + copy(m_speciesMolarVolume.begin(), + m_speciesMolarVolume.end(), vol); + } + + + /* + * ------ Thermodynamic Values for the Species Reference States --- + */ + + // -> This is handled by VPStandardStatesTP + + /* + * -------------- Utilities ------------------------------- + */ + + /** + * Initialization of an IdealSolidSolnPhase phase: + * Note this function is pretty much useless because it doesn't + * get the xml tree passed to it. Suggest a change. + */ + void IdealMolalSoln::initThermo() { + initLengths(); + MolalityVPSSTP::initThermo(); + } + + /** + * Initialization of an IdealSolidSolnPhase phase using an + * xml file + * + * This routine is a precursor to initThermo(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 IdealMolalSoln::constructPhaseFile(string inputFile, string id) { + + if (inputFile.size() == 0) { + throw CanteraError("IdealSolidSolnPhase::constructPhaseFile", + "input file is null"); + } + string path = findInputFile(inputFile); + ifstream fin(path.c_str()); + if (!fin) { + throw CanteraError("IdealMolalSoln::constructPhaseFile", + "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("IdealMolalSoln::constructPhaseFile", + "ERROR: Can not find phase named " + + id + " in file named " + inputFile); + } + fxml_phase->copy(&phaseNode_XML); + constructPhaseXML(*fxml_phase, id); + delete fxml; + } + + /** + * Import and initialize an IdealSolidSolnPhase phase + * specification in an XML tree into the current object. + * 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. + * + * Then, we read the species molar volumes from the xml + * tree to finish the initialization. + * + * @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 IdealMolalSoln::constructPhaseXML(XML_Node& phaseNode, string id) { + if (id.size() > 0) { + string idp = phaseNode.id(); + if (idp != id) { + throw CanteraError("IdealMolalSoln::constructPhaseXML", + "phasenode and Id are incompatible"); + } + } + + /* + * Find the Thermo XML node + */ + if (!phaseNode.hasChild("thermo")) { + throw CanteraError("IdealMolalSoln::constructPhaseXML", + "no thermo XML node"); + } + + /* + * Call the Cantera importPhase() function. This will import + * all of the species into the phase. Then, it will call + * initThermoXML() below. + */ + bool m_ok = importPhase(phaseNode, this); + if (!m_ok) { + throw CanteraError("constructPhaseXML","importPhase failed "); + } + } + + /** + * Import and initialize an IdealSolidSolnPhase phase + * specification in an XML tree into the current object. + * 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. + * + * Then, we read the species molar volumes from the xml + * tree to finish the initialization. + * + * @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 IdealMolalSoln::initThermoXML(XML_Node& phaseNode, string id) { + + /* + * Initialize the whole thermo object, using a virtual function. + */ + initThermo(); + + if (id.size() > 0) { + string idp = phaseNode.id(); + if (idp != id) { + throw CanteraError("initThermo", + "phasenode and Id are incompatible"); + } + } + + /* + * Find the Thermo XML node + */ + if (!phaseNode.hasChild("thermo")) { + throw CanteraError("IdealMolalSoln::initThermo", + "no thermo XML node"); + } + XML_Node& thermoNode = phaseNode.child("thermo"); + + /* + * Possible change the form of the standard concentrations + */ + if (thermoNode.hasChild("standardConc")) { + XML_Node& scNode = thermoNode.child("standardConc"); + m_formGC = 2; + string formString = scNode.attrib("model"); + if (formString != "") { + if (formString == "unity") { + m_formGC = 0; + } else if (formString == "molar_volume") { + m_formGC = 1; + } else if (formString == "solvent_volume") { + m_formGC = 2; + } else { + throw CanteraError("IdealMolalSoln::initThermo", + "Unknown standardConc model: " + formString); + } + } + } + + /* + * Get the Name of the Solvent: + * solventName + */ + string solventName = ""; + if (thermoNode.hasChild("solvent")) { + XML_Node& scNode = thermoNode.child("solvent"); + vector nameSolventa; + getStringArray(scNode, nameSolventa); + int nsp = static_cast(nameSolventa.size()); + if (nsp != 1) { + throw CanteraError("IdealMolalSoln::initThermoXML", + "badly formed solvent XML node"); + } + solventName = nameSolventa[0]; + } + + + /* + * Reconcile the solvent name and index. + */ + for (int k = 0; k < m_kk; k++) { + string sname = speciesName(k); + if (solventName == sname) { + m_indexSolvent = k; + break; + } + } + if (m_indexSolvent == -1) { + cout << "IdealMolalSoln::initThermo: Solvent Name not found" + << endl; + throw CanteraError("IdealMolalSoln::initThermo", + "Solvent name not found"); + } + if (m_indexSolvent != 0) { + throw CanteraError("IdealMolalSoln::initThermo", + "Solvent " + solventName + + " should be first species"); + } + + /* + * Now go get the molar volumes + */ + XML_Node& speciesList = phaseNode.child("speciesArray"); + XML_Node* speciesDB = + get_XML_NameID("speciesData", speciesList["datasrc"], + &phaseNode.root()); + const vector&sss = speciesNames(); + + for (int k = 0; k < m_kk; k++) { + XML_Node* s = speciesDB->findByAttr("name", sss[k]); + XML_Node *ss = s->findByName("standardState"); + m_speciesMolarVolume[k] = getFloat(*ss, "molarVolume", "-"); +#ifdef DEBUG_HKM + cout << "species " << sss[k] << " has volume " << + m_speciesMolarVolume[k] << endl; +#endif + } + + /* + * Set the state + */ + if (phaseNode.hasChild("state")) { + XML_Node& stateNode = phaseNode.child("state"); + setStateFromXML(stateNode); + } + + } + + /** + * @internal + * Set equation of state parameters. The number and meaning of + * these depends on the subclass. + * @param n number of parameters + * @param c array of \i n coefficients + * + */ + void IdealMolalSoln::setParameters(int n, doublereal* c) { + } + void IdealMolalSoln::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. + * + * @param eosdata An XML_Node object corresponding to + * the "thermo" entry for this phase in the input file. + * + * HKM -> Right now, the parameters are set elsewhere (initThermo) + * It just didn't seem to fit. + */ + void IdealMolalSoln::setParametersFromXML(const XML_Node& eosdata) { + } + + /* + * ----------- Critical State Properties -------------------------- + */ + + /* + * ------------ Private and Restricted Functions ------------------ + */ + + /** + * Bail out of functions with an error exit if they are not + * implemented. + */ + doublereal IdealMolalSoln::err(string msg) const { + throw CanteraError("IdealMolalSoln", + "Unfinished func called: " + msg ); + return 0.0; + } + + /** + * This internal function adjusts the lengths of arrays + */ + void IdealMolalSoln::initLengths() { + m_kk = nSpecies(); + /* + * Obtain the limits of the temperature from the species + * thermo handler's limits. + */ + int leng = m_kk; + m_expg0_RT.resize(leng); + m_pe.resize(leng, 0.0); + m_pp.resize(leng); + m_speciesMolarVolume.resize(leng); + m_tmpV.resize(leng); + } + +} + diff --git a/Cantera/src/thermo/IdealMolalSoln.h b/Cantera/src/thermo/IdealMolalSoln.h new file mode 100644 index 000000000..5b6222889 --- /dev/null +++ b/Cantera/src/thermo/IdealMolalSoln.h @@ -0,0 +1,841 @@ +/** + * @file IdealMolalSoln.h + * + * Header file for a derived class of ThermoPhase that handles + * variable pressure standard state methods for calculating + * thermodynamic properties that are further based upon + * activities on the molality scale. The Ideal molal + * solution assumes that all molality-based activity + * coefficients are equal to one. + */ + +/* + * $Author$ + * $Date$ + * $Revision$ + */ + +#ifndef CT_IDEALMOLALSOLN_H +#define CT_IDEALMOLALSOLN_H + +#include "MolalityVPSSTP.h" + +namespace Cantera { + + /** + * @defgroup thermoprops Thermodynamic Properties + * + * These classes are used to compute thermodynamic properties. + */ + + class IdealMolalSoln : public MolalityVPSSTP { + + public: + + /// Constructors + IdealMolalSoln(); + IdealMolalSoln(const IdealMolalSoln &); + IdealMolalSoln& operator=(const IdealMolalSoln&); + + IdealMolalSoln(string inputFile, string id = ""); + IdealMolalSoln(XML_Node& phaseRef, string id = ""); + + /// Destructor. + virtual ~IdealMolalSoln(); + + + ThermoPhase *duplMyselfAsThermoPhase(); + + /** + * + * @name Utilities + * @{ + */ + + /** + * 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; } + + + /** + * @} + * @name Molar Thermodynamic Properties of the Solution --------------- + * @{ + */ + + /// Molar enthalpy. Units: J/kmol. + /** + * Molar enthalpy of the solution. Units: J/kmol. + */ + virtual doublereal enthalpy_mole() const; + + /// Molar internal energy. Units: J/kmol. + /** + * Molar internal energy of the solution. Units: J/kmol. + */ + virtual doublereal intEnergy_mole() const; + + /// Molar entropy. Units: J/kmol/K. + /** + * Molar entropy of the solution. Units: J/kmol/K. + * For an ideal, constant partial molar volume solution mixture with + * pure species phases which exhibit zero volume expansivity: + * \f[ + * \hat s(T, P, X_k) = \sum_k X_k \hat s^0_k(T) + * - \hat R \sum_k X_k log(X_k) + * \f] + * The reference-state pure-species entropies + * \f$ \hat s^0_k(T,p_{ref}) \f$ are computed by the + * species thermodynamic + * property manager. The pure species entropies are independent of + * temperature since the volume expansivities are equal to zero. + * @see SpeciesThermo + */ + 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; + + //@} + /** @name Mechanical Equation of State Properties ------------------------- + //@{ + * + * In this equation of state implementation, the density is a + * function only of the mole fractions. Therefore, it can't be + * an independent variable. Instead, the pressure is used as the + * independent variable. Functions which try to set the thermodynamic + * state by calling setDensity() may cause an exception to be + * thrown. + */ + + + /** + * Pressure. Units: Pa. + * For this incompressible system, we return the internally storred + * independent value of the pressure. + */ + virtual doublereal pressure() const; + + /** + * Set the pressure at constant temperature. Units: Pa. + * This method sets a constant within the object. + * The mass density is not a function of pressure. + */ + virtual void setPressure(doublereal p) { + m_Pcurrent = p; + } + + + /** + * Calculate the density of the mixture using the partial + * molar volumes and mole fractions as input + * + * The formula for this is + * + * \f[ + * \rho = \frac{\sum_k{X_k W_k}}{\sum_k{X_k V_k}} + * \f] + * + * where \f$X_k\f$ are the mole fractions, \f$W_k\f$ are + * the molecular weights, and \f$V_k\f$ are the pure species + * molar volumes. + * + * Note, the basis behind this formula is that in an ideal + * solution the partial molar volumes are equal to the pure + * species molar volumes. We have additionally specified + * in this class that the pure species molar volumes are + * independent of temperature and pressure. + * + * NOTE: This is a non-virtual function, which is not a + * member of the ThermoPhase base class. + */ + void calcDensity(); + + /** + * Overwritten setDensity() function is necessary because the + * density is not an indendent variable. + * + * This function will now throw an error condition + * + * @internal May have to adjust the strategy here to make + * the eos for these materials slightly compressible, in order + * to create a condition where the density is a function of + * the pressure. + * + * This function will now throw an error condition. + * + * NOTE: This is an overwritten function from the State.h + * class + */ + void setDensity(doublereal rho); + + /** + * Overwritten setMolarDensity() function is necessary because the + * density is not an indendent variable. + * + * This function will now throw an error condition. + * + * NOTE: This is an overwritten function from the State.h + * class + */ + void setMolarDensity(doublereal rho); + + /** + * 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; + + /** + * The 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; + + /** + * @} + * @name Potential Energy + * + * Species may have an additional potential energy due to the + * presence of external gravitation or electric fields. These + * methods allow specifying a potential energy for individual + * species. + * @{ + */ + + /** + * Set the potential energy of species k to pe. + * Units: J/kmol. + * This function must be reimplemented in inherited classes + * of ThermoPhase. + */ + virtual void setPotentialEnergy(int k, doublereal pe) { + err("setPotentialEnergy"); + } + + /** + * Get the potential energy of species k. + * Units: J/kmol. + * This function must be reimplemented in inherited classes + * of ThermoPhase. + */ + virtual doublereal potentialEnergy(int k) const { + return err("potentialEnergy"); + } + + /** + * 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. + */ + void setElectricPotential(doublereal v) { + m_phi = v; + } + + /// The electric potential of this phase (V). + doublereal electricPotential() const { return m_phi; } + + + /** + * @} + * @name Activities 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) + * + \hat R T \log a_k. \f] The quantity \f$\mu_k^0(T)\f$ is + * the chemical potential at unit activity, which depends only + * on temperature and the pressure. + * @{ + */ + + /** + * This method returns an array of generalized concentrations + * \f$ C_k\f$ that are defined such that + * \f$ a_k = C_k / C^0_k, \f$ where \f$ C^0_k \f$ + * is a standard concentration + * defined below. These generalized concentrations are used + * by kinetics manager classes to compute the forward and + * reverse rates of elementary reactions. + * + * @param c Array of generalized concentrations. The + * units depend upon the implementation of the + * reaction rate expressions within the phase. + */ + virtual void getActivityConcentrations(doublereal* c) const; + + /** + * The standard concentration \f$ C^0_k \f$ used to normalize + * the 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. + */ + virtual doublereal standardConcentration(int k=0) const; + + /** + * Returns the natural logarithm of the standard + * concentration of the kth species + */ + virtual doublereal logStandardConc(int k=0) const; + + /** + * 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 = 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 + */ + 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. + * + * (note solvent is on molar scale) + */ + virtual void getActivities(doublereal* ac) const; + + /** + * Get the array of non-dimensional molality-based + * activity coefficients at the current solution temperature, + * pressure, and solution concentration. + * + * + * (note solvent is on molar scale. The solvent molar + * based activity coefficient is returned). + */ + virtual void + getMolalityActivityCoefficients(doublereal* acMolality) 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. + * \f[ + * \mu_k = \mu^{ref}_k(T) + V_k * (p - p_o) + R T ln(X_k) + * \f] + * or another way to phrase this is + * \f[ + * \mu_k = \mu^o_k(T,p) + R T ln(X_k) + * \f] + * where \f$ \mu^o_k(T,p) = \mu^{ref}_k(T) + V_k * (p - p_o)\f$ + */ + virtual void getChemPotentials(doublereal* mu) const; + + /** + * Get the species electrochemical potentials. + * These are partial molar quantities. + * This method adds a term \f$ Fz_k \phi_k \f$ to the + * to each chemical potential. + * + * 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) + * For this phase, the partial molar enthalpies are equal to the + * pure species enthalpies + * \f[ + * \bar h_k(T,P) = \hat h^{ref}_k(T) + (P - P_{ref}) \hat V^0_k + * \f] + * The reference-state pure-species enthalpies, \f$ \hat h^{ref}_k(T) \f$, + * at the reference pressure,\f$ P_{ref} \f$, + * are computed by the species thermodynamic + * property manager. They are polynomial functions of temperature. + * @see SpeciesThermo + */ + virtual void getPartialMolarEnthalpies(doublereal* hbar) const; + + /** + * getPartialMolarEntropies() (virtual, const) + * + * Returns an array of partial molar entropies of the species in the + * solution. Units: J/kmol. + * + * Maxwell's equations provide an insight in how to calculate this + * (p.215 Smith and Van Ness) + * + * d(chemPot_i)/dT = -sbar_i + * + * + * For this phase, the partial molar entropies are equal to the + * SS species entropies plus the ideal solution contribution.following + * contribution: + * \f[ + * \bar s_k(T,P) = \hat s^0_k(T) - R log(M0 * molality[k]) + * \f] + * \f[ + * \bar s_solvent(T,P) = \hat s^0_solvent(T) + * - R ((xmolSolvent - 1.0) / xmolSolvent) + * \f] + * + * The reference-state pure-species entropies,\f$ \hat s^0_k(T) \f$, + * at the reference pressure, \f$ P_{ref} \f$, are computed by the + * species thermodynamic + * property manager. They are polynomial functions of temperature. + * @see SpeciesThermo + */ + virtual void getPartialMolarEntropies(doublereal* sbar) const; + + /** + * returns an array of partial molar volumes of the species + * in the solution. Units: m^3 kmol-1. + * + * For this solution, thepartial molar volumes are equal to the + * constant species molar volumes. + */ + virtual void getPartialMolarVolumes(doublereal* vbar) const; + + /* + * Partial molar heat capacity of the solution: + * The kth partial molar heat capacity is equal to + * the temperature derivative of the partial molar + * enthalpy of the kth species in the solution at constant + * P and composition (p. 220 Smith and Van Ness). + * + * Cp = -T d2(chemPot_i)/dT2 + */ + virtual void getPartialMolarCp(doublereal* cpbar) const; + + //@} + /// @name Properties of the Standard State of the Species + // in the Solution -- + //@{ + + /** + * Get the standard state chemical potentials of the species. + * This is the array of chemical potentials at unit activity + * \f$ \mu^0_k(T,P) \f$. + * We define these here as the chemical potentials of the pure + * species at the temperature and pressure of the solution. + * This function is used in the evaluation of the + * equilibrium constant Kc. Therefore, Kc will also depend + * on T and P. This is the norm for liquid and solid systems. + * + * units = J / kmol + */ + virtual void getStandardChemPotentials(doublereal* mu) const; + + /** + * Get the nondimensional gibbs function for the species + * standard states at the current T and P of the solution. + * + * \f[ + * \mu^0_k(T,P) = \mu^{ref}_k(T) + (P - P_{ref}) * V_k + * \f] + * where \f$V_k\f$ is the molar volume of pure species k. + * \f$ \mu^{ref}_k(T)\f$ is the chemical potential of pure + * species k at the reference pressure, \f$P_{ref}\f$. + * + * @param grt Vector of length m_kk, which on return sr[k] + * will contain the nondimensional + * standard state gibbs function for species k. + */ + virtual void getGibbs_RT(doublereal* grt) const; + + /** + * Get the nondimensional Gibbs functions for the standard + * state of the species at the current T and P. + */ + virtual void getPureGibbs(doublereal* gpure) const; + + /** + * + * getEnthalpy_RT() (virtual, const) + * + * Get the array of nondimensional Enthalpy functions for the ss + * species at the current T and P of the solution. + * We assume an incompressible constant partial molar + * volume here: + * \f[ + * h^0_k(T,P) = h^{ref}_k(T) + (P - P_{ref}) * V_k + * \f] + * where \f$V_k\f$ is the molar volume of SS species k<\I>. + * \f$ h^{ref}_k(T)\f$ is the enthalpy of the SS + * species k<\I> at the reference pressure, \f$P_{ref}\f$. + */ + virtual void getEnthalpy_RT(doublereal* hrt) const; + + /** + * Get the nondimensional Entropies for the species + * standard states at the current T and P of the solution. + * + * Note, this is equal to the reference state entropies + * due to the zero volume expansivity: + * i.e., (dS/dp)_T = (dV/dT)_P = 0.0 + * + * @param sr Vector of length m_kk, which on return sr[k] + * will contain the nondimensional + * standard state entropy of species k. + */ + virtual void getEntropy_R(doublereal* sr) const; + + /** + * Get the nondimensional heat capacity at constant pressure + * function for the species + * standard states at the current T and P of the solution. + * \f[ + * Cp^0_k(T,P) = Cp^{ref}_k(T) + * \f] + * where \f$V_k\f$ is the molar volume of pure species k. + * \f$ Cp^{ref}_k(T)\f$ is the constant pressure heat capacity + * of species k at the reference pressure, \f$p_{ref}\f$. + * + * @param cpr Vector of length m_kk, which on return cpr[k] + * will contain the nondimensional + * constant pressure heat capacity for species k. + */ + virtual void getCp_R(doublereal* cpr) const; + + /** + * Get the molar volumes of each species in their standard + * states at the current + * T and P of the solution. + * units = m^3 / kmol + */ + virtual void getStandardVolumes(doublereal *vol) const; + + //@} + /// @name Thermodynamic Values for the Species Reference States --- + //@{ + + + /////////////////////////////////////////////////////// + // + // The methods below are not virtual, and should not + // be overloaded. + // + ////////////////////////////////////////////////////// + + /** + * @name Specific Properties + * @{ + */ + + + /** + * @name Setting the State + * + * These methods set all or part of the thermodynamic + * state. + * @{ + */ + + //@} + + /** + * @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. + */ + virtual void setToEquilState(const doublereal* lambda_RT) { + err("setToEquilState"); + } + + // called by function 'equilibrate' in ChemEquil.h to transfer + // the element potentials to this object + void setElementPotentials(const vector_fp& lambda) { + m_lambda = lambda; + } + + void getElementPotentials(doublereal* lambda) { + copy(m_lambda.begin(), m_lambda.end(), lambda); + } + + //@} + + + /** + * @internal + * Set equation of state parameters. The number and meaning of + * these depends on the subclass. + * @param n number of parameters + * @param c array of \i n coefficients + * + */ + virtual void setParameters(int n, doublereal* c); + 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. + * + * @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); + + //--------------------------------------------------------- + /// @name Critical state properties. + /// These methods are only implemented by some subclasses. + + //@{ + + /// 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. + /// + virtual doublereal satTemperature(doublereal p) const { + err("satTemperature"); return -1.0; + } + + virtual doublereal satPressure(doublereal t) const { + err("satPressure"); return -1.0; + } + + virtual doublereal vaporFraction() const { + err("vaprFraction"); return -1.0; + } + + virtual void setState_Tsat(doublereal t, doublereal x) { + err("setState_sat"); + } + + virtual void setState_Psat(doublereal p, doublereal x) { + err("setState_sat"); + } + + //@} + + + /* + * -------------- Utilities ------------------------------- + */ + + /** + * @internal 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. + */ + void setSpeciesThermo(SpeciesThermo* spthermo) + { m_spthermo = spthermo; } + + /** + * 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; } + + + + + /** + * initThermo() (virtual from ThermoPhase) + * + * This internal routine is responsible for setting up + * the internal storage. + */ + virtual void initThermo(); + + /** + * constructPhaseFile (virtual from here) + * + * Initialization of an IdealSolidSolnPhase phase using an + * xml file identified by its path + * + * This routine is a precursor to constructPhaseXML(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. + */ + virtual void constructPhaseFile(string infile, string id=""); + + /** + * constructPhaseXML (virtual from here) + * + * This is the main routine for constructing the phase. + * It processes the XML file, and then it calls importPhase(). + * Then, initThermoXML() is called after importPhase(). + * + * @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 constructPhaseXML(XML_Node& phaseNode, string id); + + /** + * initThermoXML (virtual from ThermoPhase) + * + * + * This routine is called from importPhase() to finish + * up the initialization of the thermo object. It reads in the + * species molar volumes. + * + * @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, string id=""); + + + + /** + * Report the molar volume of species k + * + * units - \f$ m^3 kmol^-1 \f$ + */ + double speciesMolarVolume(int k) const; + + /** + * Fill in a return vector containing the species molar volumes + * units - \f$ m^3 kmol^-1 \f$ + */ + void getSpeciesMolarVolumes(double *smv) const; + //@} + + + + + protected: + /** + * Species molar volume \f$ m^3 kmol^-1 \f$ + */ + array_fp m_speciesMolarVolume; + /* + * Current pressure in Pascal + */ + double m_Pcurrent; + int m_formGC; + + /** + * Vector containing the species reference exp(-G/RT) functions + * at T = m_tlast + */ + mutable vector_fp m_expg0_RT; + + /** + * Vector of potential energies for the species. + */ + mutable vector_fp m_pe; + + /** + * Temporary array used in equilibrium calculations + */ + mutable vector_fp m_pp; + + /** + * vector of size m_kk, used as a temporary holding area. + */ + mutable vector_fp m_tmpV; + + private: + doublereal err(string msg) const; + + + void initLengths(); + }; + +} + +#endif + + + + + diff --git a/Cantera/src/thermo/Makefile.in b/Cantera/src/thermo/Makefile.in index eb93ebbf4..c986363f9 100644 --- a/Cantera/src/thermo/Makefile.in +++ b/Cantera/src/thermo/Makefile.in @@ -14,16 +14,30 @@ INCDIR = ../../../build/include/cantera/kernel/thermo INSTALL_TSC = ../../../bin/install_tsc do_ranlib = @DO_RANLIB@ +do_electro = @COMPILE_ELECTROLYTES@ +do_issp = @COMPILE_IDEAL_SOLUTIONS@ CXX_FLAGS = @CXXFLAGS@ $(CXX_OPT) # Extended Cantera Thermodynamics Object Files -CATHERMO_OBJ = SingleSpeciesTP.o StoichSubstanceSSTP.o \ - MolalityVPSSTP.o VPStandardStateTP.o \ - IdealSolidSolnPhase.o -CATHERMO_H = SingleSpeciesTP.h StoichSubstanceSSTP.h \ + +ifeq ($(do_electro),1) +ELECTRO_OBJ = SingleSpeciesTP.o StoichSubstanceSSTP.o \ + MolalityVPSSTP.o VPStandardStateTP.o \ + IdealSolidSolnPhase.o IdealMolalSoln.o +ELECTRO_H = SingleSpeciesTP.h StoichSubstanceSSTP.h \ MolalityVPSSTP.h VPStandardStateTP.h \ - IdealSolidSolnPhase.h + IdealSolidSolnPhase.h IdealMolalSoln.h +endif +ifeq ($(do_issp),1) +ISSP_OBJ = IdealSolidSolnPhase.o +ISSP_H = IdealSolidSolnPhase.h +endif + +CATHERMO_OBJ = $(ELECTRO_OBJ) $(ISSP_OBJ) + +CATHERMO_H = $(ELECTRO_H) $(ISSP_H) + CXX_INCLUDES = -I.. @CXX_INCLUDES@ LIB = @buildlib@/libcaThermo.a @@ -68,7 +82,7 @@ depends: @MAKE@ .depends .depends: $(DEPENDS) - cat *.d > .depends + cat $(DEPENDS) > .depends TAGS: etags *.h *.cpp diff --git a/Cantera/src/thermo/MolalityVPSSTP.cpp b/Cantera/src/thermo/MolalityVPSSTP.cpp index 4ccac77bf..5d428dd3e 100644 --- a/Cantera/src/thermo/MolalityVPSSTP.cpp +++ b/Cantera/src/thermo/MolalityVPSSTP.cpp @@ -22,521 +22,536 @@ namespace Cantera { - /* - * Default constructor. - * - * This doesn't do much more than initialize constants with - * default values for water at 25C. Water molecular weight - * comes from the default elements.xml file. It actually - * differs slightly from the IAPWS95 value of 18.015268. However, - * density conservation and therefore element conservation - * is the more important principle to follow. - */ - MolalityVPSSTP::MolalityVPSSTP() : - VPStandardStateTP(), - m_indexSolvent(0), - m_weightSolvent(18.01528), - m_xmolSolventMIN(0.01), - m_Mnaught(18.01528E-3) - { - } + /* + * Default constructor. + * + * This doesn't do much more than initialize constants with + * default values for water at 25C. Water molecular weight + * comes from the default elements.xml file. It actually + * differs slightly from the IAPWS95 value of 18.015268. However, + * density conservation and therefore element conservation + * is the more important principle to follow. + */ + MolalityVPSSTP::MolalityVPSSTP() : + VPStandardStateTP(), + m_indexSolvent(0), + m_weightSolvent(18.01528), + m_xmolSolventMIN(0.01), + m_Mnaught(18.01528E-3) + { + } - /** - * Copy Constructor: - * - * Note this stuff will not work until the underlying phase - * has a working copy constructor - */ - MolalityVPSSTP::MolalityVPSSTP(const MolalityVPSSTP &b) : - VPStandardStateTP(), - m_indexSolvent(b.m_indexSolvent), - m_xmolSolventMIN(b.m_xmolSolventMIN), - m_Mnaught(b.m_Mnaught), - m_molalities(b.m_molalities) - { - throw CanteraError("MolalityVPSSTP::operator=()", - "Not Implemented Fully"); - *this = operator=(b); - } + /** + * Copy Constructor: + * + * Note this stuff will not work until the underlying phase + * has a working copy constructor + */ + MolalityVPSSTP::MolalityVPSSTP(const MolalityVPSSTP &b) : + VPStandardStateTP(), + m_indexSolvent(b.m_indexSolvent), + m_xmolSolventMIN(b.m_xmolSolventMIN), + m_Mnaught(b.m_Mnaught), + m_molalities(b.m_molalities) + { + throw CanteraError("MolalityVPSSTP::operator=()", + "Not Implemented Fully"); + *this = operator=(b); + } - /* - * operator=() - * - * Note this stuff will not work until the underlying phase - * has a working assignment operator - */ - MolalityVPSSTP& MolalityVPSSTP:: - operator=(const MolalityVPSSTP &b) { - if (&b != this) { - VPStandardStateTP::operator=(b); - m_indexSolvent = b.m_indexSolvent; - m_weightSolvent = b.m_weightSolvent; - m_xmolSolventMIN = b.m_xmolSolventMIN; - m_Mnaught = b.m_Mnaught; - m_molalities = b.m_molalities; - } - throw CanteraError("MolalityVPSSTP::operator=()", - "Not Implemented Fully"); - return *this; + /* + * operator=() + * + * Note this stuff will not work until the underlying phase + * has a working assignment operator + */ + MolalityVPSSTP& MolalityVPSSTP:: + operator=(const MolalityVPSSTP &b) { + if (&b != this) { + VPStandardStateTP::operator=(b); + m_indexSolvent = b.m_indexSolvent; + m_weightSolvent = b.m_weightSolvent; + m_xmolSolventMIN = b.m_xmolSolventMIN; + m_Mnaught = b.m_Mnaught; + m_molalities = b.m_molalities; } + throw CanteraError("MolalityVPSSTP::operator=()", + "Not Implemented Fully"); + return *this; + } - /** - * - * ~MolalityVPSSTP(): (virtual) - * - * Destructor: does nothing: - * - */ - MolalityVPSSTP::~MolalityVPSSTP() { - } - - /** - * This routine duplicates the current object and returns - * a pointer to ThermoPhase. - */ - ThermoPhase* - MolalityVPSSTP::duplMyselfAsThermoPhase() { - MolalityVPSSTP* mtp = new MolalityVPSSTP(*this); - return (ThermoPhase *) mtp; - } - - /* - * -------------- Utilities ------------------------------- - */ - - /* - * setSolvent(): - * Utilities for Solvent ID and Molality - * Here we also calculate and store the molecular weight - * of the solvent and the m_Mnaught parameter. - * @param k index of the solvent. - */ - void MolalityVPSSTP::setSolvent(int k) { - if (k < 0 || k >= m_kk) { - throw CanteraError("MolalityVPSSTP::setSolute ", - "bad value"); - } - m_indexSolvent = k; - m_weightSolvent = molecularWeight(k); - m_Mnaught = m_weightSolvent / 1000.; + /** + * + * ~MolalityVPSSTP(): (virtual) + * + * Destructor: does nothing: + * + */ + MolalityVPSSTP::~MolalityVPSSTP() { + } + + /** + * This routine duplicates the current object and returns + * a pointer to ThermoPhase. + */ + ThermoPhase* + MolalityVPSSTP::duplMyselfAsThermoPhase() { + MolalityVPSSTP* mtp = new MolalityVPSSTP(*this); + return (ThermoPhase *) mtp; + } + + /* + * -------------- Utilities ------------------------------- + */ + + /* + * setSolvent(): + * Utilities for Solvent ID and Molality + * Here we also calculate and store the molecular weight + * of the solvent and the m_Mnaught parameter. + * @param k index of the solvent. + */ + void MolalityVPSSTP::setSolvent(int k) { + if (k < 0 || k >= m_kk) { + throw CanteraError("MolalityVPSSTP::setSolute ", + "bad value"); } + m_indexSolvent = k; + m_weightSolvent = molecularWeight(k); + m_Mnaught = m_weightSolvent / 1000.; + } - /* - * return the solvent id index number. - */ - int MolalityVPSSTP::solventIndex() const { - return m_indexSolvent; - } + /* + * return the solvent id index number. + */ + int MolalityVPSSTP::solventIndex() const { + return m_indexSolvent; + } - /** - * Sets the minimum mole fraction in the molality formulation. The - * minimum mole fraction must be in the range 0 to 0.9. - */ - void MolalityVPSSTP:: - setMoleFSolventMin(doublereal xmolSolventMIN) { - if (xmolSolventMIN <= 0.0) { - throw CanteraError("MolalityVPSSTP::setSolute ", "trouble"); - } else if (xmolSolventMIN > 0.9) { - throw CanteraError("MolalityVPSSTP::setSolute ", "trouble"); - } - m_xmolSolventMIN = xmolSolventMIN; + /** + * Sets the minimum mole fraction in the molality formulation. The + * minimum mole fraction must be in the range 0 to 0.9. + */ + void MolalityVPSSTP:: + setMoleFSolventMin(doublereal xmolSolventMIN) { + if (xmolSolventMIN <= 0.0) { + throw CanteraError("MolalityVPSSTP::setSolute ", "trouble"); + } else if (xmolSolventMIN > 0.9) { + throw CanteraError("MolalityVPSSTP::setSolute ", "trouble"); } + m_xmolSolventMIN = xmolSolventMIN; + } - /** - * Returns the minimum mole fraction in the molality formulation. - */ - doublereal MolalityVPSSTP::moleFSolventMin() const { - return m_xmolSolventMIN; + /** + * Returns the minimum mole fraction in the molality formulation. + */ + doublereal MolalityVPSSTP::moleFSolventMin() const { + return m_xmolSolventMIN; + } + + /** + * calcMolalities(): + * We calculate the vector of molalities of the species + * in the phase and store the result internally: + * \f[ + * m_i = (n_i) / (1000 * M_o * n_{o,p}) + * \f] + * where + * - \f$ M_o \f$ is the molecular weight of the solvent + * - \f$ n_o \f$ is the mole fraction of the solvent + * - \f$ n_i \f$ is the mole fraction of the solute. + * - \f$ n_{o,p} = max (n_{o, min}, n_o) \f$ + * - \f$ n_{o,min} \f$ = minimum mole fraction of solvent allowed + * in the denominator. + */ + void MolalityVPSSTP::calcMolalities() const { + getMoleFractions(DATA_PTR(m_molalities)); + double xmolSolvent = m_molalities[m_indexSolvent]; + if (xmolSolvent < m_xmolSolventMIN) { + xmolSolvent = m_xmolSolventMIN; } - - /** - * calcMolalities(): - * We calculate the vector of molalities of the species - * in the phase and store the result internally: - * \f[ - * m_i = (n_i) / (1000 * M_o * n_{o,p}) - * \f] - * where - * - \f$ M_o \f$ is the molecular weight of the solvent - * - \f$ n_o \f$ is the mole fraction of the solvent - * - \f$ n_i \f$ is the mole fraction of the solute. - * - \f$ n_{o,p} = max (n_{o, min}, n_o) \f$ - * - \f$ n_{o,min} \f$ = minimum mole fraction of solvent allowed - * in the denominator. - */ - void MolalityVPSSTP::calcMolalities() const { - getMoleFractions(DATA_PTR(m_molalities)); - double xmolSolvent = m_molalities[m_indexSolvent]; - if (xmolSolvent < m_xmolSolventMIN) { - xmolSolvent = m_xmolSolventMIN; - } - double denomInv = 1.0/ (m_Mnaught * xmolSolvent); - for (int k = 0; k < m_kk; k++) { - m_molalities[k] *= denomInv; - } + double denomInv = 1.0/ (m_Mnaught * xmolSolvent); + for (int k = 0; k < m_kk; k++) { + m_molalities[k] *= denomInv; } + } - /** - * getMolalities(): - * We calculate the vector of molalities of the species - * in the phase - * \f[ - * m_i = (n_i) / (1000 * M_o * n_{o,p}) - * \f] - * where - * - \f$ M_o \f$ is the molecular weight of the solvent - * - \f$ n_o \f$ is the mole fraction of the solvent - * - \f$ n_i \f$ is the mole fraction of the solute. - * - \f$ n_{o,p} = max (n_{o, min}, n_o) \f$ - * - \f$ n_{o,min} \f$ = minimum mole fraction of solvent allowed - * in the denominator. - */ - void MolalityVPSSTP::getMolalities(doublereal * const molal) const { - calcMolalities(); - for (int k = 0; k < m_kk; k++) { - molal[k] = m_molalities[k]; + /** + * getMolalities(): + * We calculate the vector of molalities of the species + * in the phase + * \f[ + * m_i = (n_i) / (1000 * M_o * n_{o,p}) + * \f] + * where + * - \f$ M_o \f$ is the molecular weight of the solvent + * - \f$ n_o \f$ is the mole fraction of the solvent + * - \f$ n_i \f$ is the mole fraction of the solute. + * - \f$ n_{o,p} = max (n_{o, min}, n_o) \f$ + * - \f$ n_{o,min} \f$ = minimum mole fraction of solvent allowed + * in the denominator. + */ + void MolalityVPSSTP::getMolalities(doublereal * const molal) const { + calcMolalities(); + for (int k = 0; k < m_kk; k++) { + molal[k] = m_molalities[k]; + } + } + + /** + * setMolalities(): + * We are supplied with the molalities of all of the + * solute species. We then calculate the mole fractions of all + * species and update the ThermoPhase object. + * + * m_i = (n_i) / (W_o/1000 * n_o_p) + * + * where M_o is the molecular weight of the solvent + * n_o is the mole fraction of the solvent + * n_i is the mole fraction of the solute. + * n_o_p = max (n_o_min, n_o) + * n_o_min = minimum mole fraction of solvent allowed + * in the denominator. + */ + void MolalityVPSSTP::setMolalities(const doublereal * const molal) { + + double Lsum = 1.0 / m_Mnaught; + for (int k = 0; k < m_kk; k++) { + if (k != m_indexSolvent) { + m_molalities[k] = molal[k]; + Lsum += molal[k]; } } - - /** - * setMolalities(): - * We are supplied with the molalities of all of the - * solute species. We then calculate the mole fractions of all - * species and update the ThermoPhase object. - * - * m_i = (n_i) / (W_o/1000 * n_o_p) - * - * where M_o is the molecular weight of the solvent - * n_o is the mole fraction of the solvent - * n_i is the mole fraction of the solute. - * n_o_p = max (n_o_min, n_o) - * n_o_min = minimum mole fraction of solvent allowed - * in the denominator. - */ - void MolalityVPSSTP::setMolalities(const doublereal * const molal) { - - double Lsum = 1.0 / m_Mnaught; - for (int k = 0; k < m_kk; k++) { - if (k != m_indexSolvent) { - m_molalities[k] = molal[k]; - Lsum += molal[k]; - } - } - double tmp = 1.0 / Lsum; - m_molalities[m_indexSolvent] = tmp / m_Mnaught; - double sum = m_molalities[m_indexSolvent]; - for (int k = 0; k < m_kk; k++) { - if (k != m_indexSolvent) { - m_molalities[k] = tmp * molal[k]; - sum += m_molalities[k]; - } - } - if (sum != 1.0) { - tmp = 1.0 / sum; - for (int k = 0; k < m_kk; k++) { - m_molalities[k] *= tmp; - } - } - setMoleFractions(DATA_PTR(m_molalities)); - /* - * Essentially we don't trust the input: We calculate - * the molalities from the mole fractions that we - * just obtained. - */ - calcMolalities(); + double tmp = 1.0 / Lsum; + m_molalities[m_indexSolvent] = tmp / m_Mnaught; + double sum = m_molalities[m_indexSolvent]; + for (int k = 0; k < m_kk; k++) { + if (k != m_indexSolvent) { + m_molalities[k] = tmp * molal[k]; + sum += m_molalities[k]; + } } - + if (sum != 1.0) { + tmp = 1.0 / sum; + for (int k = 0; k < m_kk; k++) { + m_molalities[k] *= tmp; + } + } + setMoleFractions(DATA_PTR(m_molalities)); /* - * setMolalitiesByName() - * - * This routine sets the molalities by name - * HKM -> Might need to be more complicated here, setting - * neutrals so that the existing mole fractions are - * preserved. + * Essentially we don't trust the input: We calculate + * the molalities from the mole fractions that we + * just obtained. */ - void MolalityVPSSTP::setMolalitiesByName(compositionMap& mMap) { - int kk = nSpecies(); - doublereal x; - /* - * Get a vector of mole fractions - */ - vector_fp mf(kk, 0.0); - getMoleFractions(DATA_PTR(mf)); - double xmolS = mf[m_indexSolvent]; - double xmolSmin = max(xmolS, m_xmolSolventMIN); - compositionMap::iterator p; - for (int k = 0; k < kk; k++) { - p = mMap.find(speciesName(k)); - if (p != mMap.end()) { - x = mMap[speciesName(k)]; - if (x > 0.0) { - mf[k] = x * m_Mnaught * xmolSmin; - } + calcMolalities(); + } + + /* + * setMolalitiesByName() + * + * This routine sets the molalities by name + * HKM -> Might need to be more complicated here, setting + * neutrals so that the existing mole fractions are + * preserved. + */ + void MolalityVPSSTP::setMolalitiesByName(compositionMap& mMap) { + int kk = nSpecies(); + doublereal x; + /* + * Get a vector of mole fractions + */ + vector_fp mf(kk, 0.0); + getMoleFractions(DATA_PTR(mf)); + double xmolS = mf[m_indexSolvent]; + double xmolSmin = max(xmolS, m_xmolSolventMIN); + compositionMap::iterator p; + for (int k = 0; k < kk; k++) { + p = mMap.find(speciesName(k)); + if (p != mMap.end()) { + x = mMap[speciesName(k)]; + if (x > 0.0) { + mf[k] = x * m_Mnaught * xmolSmin; + } + } + } + /* + * check charge neutrality + */ + int largePos = -1; + double cPos = 0.0; + int largeNeg = -1; + double cNeg = 0.0; + double sum = 0.0; + for (int k = 0; k < kk; k++) { + double ch = charge(k); + if (mf[k] > 0.0) { + if (ch > 0.0) { + if (ch * mf[k] > cPos) { + largePos = k; + cPos = ch * mf[k]; } } - /* - * check charge neutrality - */ - int largePos = -1; - double cPos = 0.0; - int largeNeg = -1; - double cNeg = 0.0; - double sum = 0.0; - for (int k = 0; k < kk; k++) { - double ch = charge(k); - if (mf[k] > 0.0) { - if (ch > 0.0) { - if (ch * mf[k] > cPos) { - largePos = k; - cPos = ch * mf[k]; - } - } - if (ch < 0.0) { - if (fabs(ch) * mf[k] > cNeg) { - largeNeg = k; - cNeg = fabs(ch) * mf[k]; - } - } - } - sum += mf[k] * ch; + if (ch < 0.0) { + if (fabs(ch) * mf[k] > cNeg) { + largeNeg = k; + cNeg = fabs(ch) * mf[k]; + } } - if (sum != 0.0) { - if (sum > 0.0) { - if (cPos > sum) { - mf[largePos] -= sum / charge(largePos); - } else { - throw CanteraError("MolalityVPSSTP:setMolalitiesbyName", - "unbalanced charges"); - } - } else { - if (cNeg > (-sum)) { - mf[largeNeg] -= (-sum) / fabs(charge(largeNeg)); - } else { - throw CanteraError("MolalityVPSSTP:setMolalitiesbyName", - "unbalanced charges"); - } - } + } + sum += mf[k] * ch; + } + if (sum != 0.0) { + if (sum > 0.0) { + if (cPos > sum) { + mf[largePos] -= sum / charge(largePos); + } else { + throw CanteraError("MolalityVPSSTP:setMolalitiesbyName", + "unbalanced charges"); + } + } else { + if (cNeg > (-sum)) { + mf[largeNeg] -= (-sum) / fabs(charge(largeNeg)); + } else { + throw CanteraError("MolalityVPSSTP:setMolalitiesbyName", + "unbalanced charges"); + } + } - } - sum = 0.0; - for (int k = 0; k < kk; k++) { - sum += mf[k]; - } - sum = 1.0/sum; - for (int k = 0; k < kk; k++) { - mf[k] *= sum; - } - setMoleFractions(DATA_PTR(mf)); - /* - * After we formally set the mole fractions, we - * calculate the molalities again and store it in - * this object. - */ - calcMolalities(); } - + sum = 0.0; + for (int k = 0; k < kk; k++) { + sum += mf[k]; + } + sum = 1.0/sum; + for (int k = 0; k < kk; k++) { + mf[k] *= sum; + } + setMoleFractions(DATA_PTR(mf)); /* - * setMolalitiesByNames() - * - * Set the molalities of the solutes by name + * After we formally set the mole fractions, we + * calculate the molalities again and store it in + * this object. */ - void MolalityVPSSTP::setMolalitiesByName(const string& x) { - compositionMap xx; - int kk = nSpecies(); - for (int k = 0; k < kk; k++) { - xx[speciesName(k)] = -1.0; - } - parseCompString(x, xx); - setMolalitiesByName(xx); + calcMolalities(); + } + + /* + * setMolalitiesByNames() + * + * Set the molalities of the solutes by name + */ + void MolalityVPSSTP::setMolalitiesByName(const string& x) { + compositionMap xx; + int kk = nSpecies(); + for (int k = 0; k < kk; k++) { + xx[speciesName(k)] = -1.0; } + parseCompString(x, xx); + setMolalitiesByName(xx); + } - /* - * ------------ Molar Thermodynamic Properties ---------------------- - */ + /* + * ------------ Molar Thermodynamic Properties ---------------------- + */ - /* - * - Activities, Standard States, Activity Concentrations ----------- - */ + /* + * - Activities, Standard States, Activity Concentrations ----------- + */ - /** - * This method returns the activity convention. - * 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 activities - * (unit activity of solutes at a hypothetical 1 molal - * solution referenced to infinite dilution at all - * pressures and temperatures). - * (solvent is still on molar basis). - * cAC_CONVENTION_MOLALITY 1 - * - * We set the convention to molality here. - */ - int MolalityVPSSTP::activityConvention() const { - return cAC_CONVENTION_MOLALITY; + /** + * This method returns the activity convention. + * 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 activities + * (unit activity of solutes at a hypothetical 1 molal + * solution referenced to infinite dilution at all + * pressures and temperatures). + * (solvent is still on molar basis). + * cAC_CONVENTION_MOLALITY 1 + * + * We set the convention to molality here. + */ + int MolalityVPSSTP::activityConvention() const { + return cAC_CONVENTION_MOLALITY; + } + + /** + * Get the array of non-dimensional activity coefficients at + * the current solution temperature, pressure, and + * solution concentration. + * These are mole fraction based activity coefficients. In this + * object, their calculation is based on translating the values + * of Molality based activity coefficients. + * See Denbigh p. 278 for a thorough discussion. + * + * Note, the solvent is treated differently. getMolalityActivityCoeff() + * returns the molar based solvent activity coefficient already. + * Therefore, we do not have to divide by x_s here. + */ + void MolalityVPSSTP::getActivityCoefficients(doublereal* ac) const { + getMolalityActivityCoefficients(ac); + double xmolSolvent = moleFraction(m_indexSolvent); + if (xmolSolvent < m_xmolSolventMIN) { + xmolSolvent = m_xmolSolventMIN; } - - /** - * Get the array of non-dimensional activity coefficients at - * the current solution temperature, pressure, and - * solution concentration. - * These are mole fraction based activity coefficients. In this - * object, their calculation is based on translating the values - * of Molality based activity coefficients. - * See Denbigh p. 278 for a thorough discussion. - * - * Note, the solvent is treated differently. getMolalityActivityCoeff() - * returns the molar based solvent activity coefficient already. - * Therefore, we do not have to divide by x_s here. - */ - void MolalityVPSSTP::getActivityCoefficients(doublereal* ac) const { - getMolalityActivityCoefficients(ac); - double xmolSolvent = moleFraction(m_indexSolvent); - if (xmolSolvent < m_xmolSolventMIN) { - xmolSolvent = m_xmolSolventMIN; - } - for (int k = 0; k < m_kk; k++) { - if (k != m_indexSolvent) { - ac[k] /= xmolSolvent; - } - } + for (int k = 0; k < m_kk; k++) { + if (k != m_indexSolvent) { + ac[k] /= xmolSolvent; + } } + } - /** - * osmotic coefficient: - * - * Calculate the osmotic coefficient of the solvent. Note there - * are lots of definitions of the osmotic coefficient floating - * around. We use the one defined in the Pitzer's book: - * (Activity Coeff in Electrolyte Solutions, K. S. Pitzer - * CRC Press, Boca Raton, 1991, p. 85, Eqn. 28). - * - * Definition: - * - sum(m_i) * Mnaught * oc = ln(activity_solvent) - */ - doublereal MolalityVPSSTP::osmoticCoefficient() const { - /* - * First, we calculate the activities all over again - */ - vector_fp act(m_kk); - getActivities(DATA_PTR(act)); - /* - * Then, we calculate the sum of the solvent molalities - */ - double sum = 0; - for (int k = 0; k < m_kk; k++) { - if (k != m_indexSolvent) { - sum += MAX(m_molalities[k], 0.0); - } - } - double oc = 1.0; - double lac = log(act[m_indexSolvent]); - if (sum > 1.0E-200) { - oc = - lac / (m_Mnaught * sum); - } - return oc; - } - + /** + * osmotic coefficient: + * + * Calculate the osmotic coefficient of the solvent. Note there + * are lots of definitions of the osmotic coefficient floating + * around. We use the one defined in the Pitzer's book: + * (Activity Coeff in Electrolyte Solutions, K. S. Pitzer + * CRC Press, Boca Raton, 1991, p. 85, Eqn. 28). + * + * Definition: + * - sum(m_i) * Mnaught * oc = ln(activity_solvent) + */ + doublereal MolalityVPSSTP::osmoticCoefficient() const { /* - * ------------ Partial Molar Properties of the Solution ------------ + * First, we calculate the activities all over again */ - - - doublereal MolalityVPSSTP::err(string msg) const { - throw CanteraError("MolalityVPSSTP","Base class method " - +msg+" called. Equation of state type: "+int2str(eosType())); - return 0; - } - - /** - * Returns the units of the standard and general concentrations - * Note they have the same units, as their divisor is - * defined to be equal to the activity of the kth species - * in the solution, which is unitless. - * - * This routine is used in print out applications where the - * units are needed. Usually, MKS units are assumed throughout - * the program and in the XML input files. - * - * On return uA contains the powers of the units (MKS assumed) - * of the standard concentrations and generalized concentrations - * for the kth species. - * - * uA[0] = kmol units - default = 1 - * uA[1] = m units - default = -nDim(), the number of spatial - * dimensions in the Phase class. - * uA[2] = kg units - default = 0; - * uA[3] = Pa(pressure) units - default = 0; - * uA[4] = Temperature units - default = 0; - * uA[5] = time units - default = 0 + vector_fp act(m_kk); + getActivities(DATA_PTR(act)); + /* + * Then, we calculate the sum of the solvent molalities */ - void MolalityVPSSTP::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; - } + double sum = 0; + for (int k = 0; k < m_kk; k++) { + if (k != m_indexSolvent) { + sum += MAX(m_molalities[k], 0.0); + } } + double oc = 1.0; + double lac = log(act[m_indexSolvent]); + if (sum > 1.0E-200) { + oc = - lac / (m_Mnaught * sum); + } + return oc; + } + + /* + * ------------ Partial Molar Properties of the Solution ------------ + */ + + + doublereal MolalityVPSSTP::err(string msg) const { + throw CanteraError("MolalityVPSSTP","Base class method " + +msg+" called. Equation of state type: "+int2str(eosType())); + return 0; + } + + /** + * Returns the units of the standard and general concentrations + * Note they have the same units, as their divisor is + * defined to be equal to the activity of the kth species + * in the solution, which is unitless. + * + * This routine is used in print out applications where the + * units are needed. Usually, MKS units are assumed throughout + * the program and in the XML input files. + * + * On return uA contains the powers of the units (MKS assumed) + * of the standard concentrations and generalized concentrations + * for the kth species. + * + * uA[0] = kmol units - default = 1 + * uA[1] = m units - default = -nDim(), the number of spatial + * dimensions in the Phase class. + * uA[2] = kg units - default = 0; + * uA[3] = Pa(pressure) units - default = 0; + * uA[4] = Temperature units - default = 0; + * uA[5] = time units - default = 0 + */ + void MolalityVPSSTP::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; + } + } + /* + * Set the thermodynamic state. + */ + void MolalityVPSSTP::setStateFromXML(const XML_Node& state) { + VPStandardStateTP::setStateFromXML(state); + string comp = getString(state,"soluteMolalities"); + if (comp != "") { + setMolalitiesByName(comp); + } + if (state.hasChild("pressure")) { + double p = getFloat(state, "pressure", "pressure"); + setPressure(p); + } + } + + /** + * Set the temperature (K), pressure (Pa), and molalities + * (gmol kg-1) of the solutes + */ + void MolalityVPSSTP::setState_TPM(doublereal t, doublereal p, + const doublereal * const molalities) { + setMolalities(molalities); + setTemperature(t); + setPressure(p); + } + + /** Set the temperature (K), pressure (Pa), and molalities. */ + void MolalityVPSSTP::setState_TPM(doublereal t, doublereal p, compositionMap& m) { + setMolalitiesByName(m); + setTemperature(t); + setPressure(p); + } + + /** Set the temperature (K), pressure (Pa), and molality. */ + void MolalityVPSSTP::setState_TPM(doublereal t, doublereal p, const string& m) { + setMolalitiesByName(m); + setTemperature(t); + setPressure(p); + } + + + /** + * @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 just prior to returning + * from function importPhase. + * + * @see importCTML.cpp + */ + void MolalityVPSSTP::initThermo() { + initLengths(); + VPStandardStateTP::initThermo(); + /* - * Set the thermodynamic state. + * The solvent defaults to species 0 */ - void MolalityVPSSTP::setStateFromXML(const XML_Node& state) { - VPStandardStateTP::setStateFromXML(state); - string comp = getString(state,"soluteMolalities"); - if (comp != "") { - setMolalitiesByName(comp); - } - if (state.hasChild("pressure")) { - double p = getFloat(state, "pressure", "pressure"); - setPressure(p); - } - } - - /** - * Set the temperature (K), pressure (Pa), and molalities - * (gmol kg-1) of the solutes - */ - void MolalityVPSSTP::setState_TPM(doublereal t, doublereal p, - const doublereal * const molalities) { - setMolalities(molalities); - setTemperature(t); - setPressure(p); - } - - /** - * @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 just prior to returning - * from function importPhase. - * - * @see importCTML.cpp - */ - void MolalityVPSSTP::initThermo() { - initLengths(); - VPStandardStateTP::initThermo(); - - /* - * The solvent defaults to species 0 - */ - setSolvent(0); - } + setSolvent(0); + } void MolalityVPSSTP::initLengths() { int m_kk = nSpecies(); diff --git a/Cantera/src/thermo/MolalityVPSSTP.h b/Cantera/src/thermo/MolalityVPSSTP.h index dcdc2ad57..20cfa5db0 100644 --- a/Cantera/src/thermo/MolalityVPSSTP.h +++ b/Cantera/src/thermo/MolalityVPSSTP.h @@ -25,457 +25,462 @@ namespace Cantera { - /** - * @ingroup thermoprops - */ + /** + * @ingroup thermoprops + */ - /** - * MolalityVPSSTP is a derived class of ThermoPhase that handles - * variable pressure standard state methods for calculating - * thermodynamic properties that are further based upon activities - * based on the molality scale. These include most of the methods - * for calculating liquid electrolyte thermodynamics. - */ - class MolalityVPSSTP : public VPStandardStateTP { + /** + * MolalityVPSSTP is a derived class of ThermoPhase that handles + * variable pressure standard state methods for calculating + * thermodynamic properties that are further based upon activities + * based on the molality scale. These include most of the methods + * for calculating liquid electrolyte thermodynamics. + */ + class MolalityVPSSTP : public VPStandardStateTP { - public: + public: - /// Constructors - MolalityVPSSTP(); - MolalityVPSSTP(const MolalityVPSSTP &); - /// Assignment operator - MolalityVPSSTP& operator=(const MolalityVPSSTP&); + /// Constructors + MolalityVPSSTP(); + MolalityVPSSTP(const MolalityVPSSTP &); + /// Assignment operator + MolalityVPSSTP& operator=(const MolalityVPSSTP&); - /// Destructor. - virtual ~MolalityVPSSTP(); + /// Destructor. + virtual ~MolalityVPSSTP(); - /** - * 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. - */ - virtual ThermoPhase *duplMyselfAsThermoPhase(); + /** + * 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. + */ + virtual ThermoPhase *duplMyselfAsThermoPhase(); - /** - * - * @name Utilities - * @{ - */ + /** + * + * @name Utilities + * @{ + */ - /** - * Equation of state type flag. The ThermoPhase base class returns - * zero. Subclasses should define this to return a unique - * non-zero value. Known constants defined for this purpose are - * listed in mix_defs.h. The MolalityVPSSTP class also returns - * zero, as it is a non-complete class. - */ - virtual int eosType() const { return 0; } + /** + * Equation of state type flag. The ThermoPhase base class returns + * zero. Subclasses should define this to return a unique + * non-zero value. Known constants defined for this purpose are + * listed in mix_defs.h. The MolalityVPSSTP class also returns + * zero, as it is a non-complete class. + */ + virtual int eosType() const { return 0; } - /** - * @} - * @name Molar Thermodynamic Properties - * @{ - */ + /** + * @} + * @name Molar Thermodynamic Properties + * @{ + */ - /** - * @} - * @name Utilities for Solvent ID and Molality - * @{ - */ + /** + * @} + * @name Utilities for Solvent ID and Molality + * @{ + */ - /** - * This routine sets the index number of the solvent for - * the phase. - * - * Note, having a solvent - * is a precursor to many things having to do with molality. - * - * @param k the solvent index number - */ - void setSolvent(int k); + /** + * This routine sets the index number of the solvent for + * the phase. + * + * Note, having a solvent + * is a precursor to many things having to do with molality. + * + * @param k the solvent index number + */ + void setSolvent(int k); - /** - * Sets the minimum mole fraction in the molality formulation. - * Note the molality formulation is singular in the limit that - * the solvent mole fraction goes to zero. Numerically, how - * this limit is treated and resolved is an ongoing issue within - * Cantera. - */ - void setMoleFSolventMin(doublereal xmolSolventMIN); + /** + * Sets the minimum mole fraction in the molality formulation. + * Note the molality formulation is singular in the limit that + * the solvent mole fraction goes to zero. Numerically, how + * this limit is treated and resolved is an ongoing issue within + * Cantera. + */ + void setMoleFSolventMin(doublereal xmolSolventMIN); - /** - * Returns the solvent index. - */ - int solventIndex() const; + /** + * Returns the solvent index. + */ + int solventIndex() const; - /** - * Returns the minimum mole fraction in the molality - * formulation. - */ - doublereal moleFSolventMin() const; + /** + * Returns the minimum mole fraction in the molality + * formulation. + */ + doublereal moleFSolventMin() const; - /** - * Calculates the molality of all species and - * stores the result internally. - */ - void calcMolalities() const; + /** + * Calculates the molality of all species and + * stores the result internally. + */ + void calcMolalities() const; - /** - * getMolalities() - * This function will return the molalities of the - * species. - * - */ - void getMolalities(doublereal * const molal) const; + /** + * getMolalities() + * This function will return the molalities of the + * species. + * + */ + void getMolalities(doublereal * const molal) const; - void setMolalities(const doublereal * const molal); - void setMolalitiesByName(compositionMap& xMap); - void setMolalitiesByName(const string &); + void setMolalities(const doublereal * const molal); + void setMolalitiesByName(compositionMap& xMap); + void setMolalitiesByName(const string &); - /** - * @} - * @name Mechanical Properties - * @{ - */ + /** + * @} + * @name Mechanical Properties + * @{ + */ - /** - * @} - * @name Potential Energy - * - * Species may have an additional potential energy due to the - * presence of external gravitation or electric fields. These - * methods allow specifying a potential energy for individual - * species. - * @{ - */ + /** + * @} + * @name Potential Energy + * + * Species may have an additional potential energy due to the + * presence of external gravitation or electric fields. These + * methods allow specifying a potential energy for individual + * species. + * @{ + */ - /** - * @} - * @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) - * + \hat R T \log a_k. \f] The quantity \f$\mu_k^0(T,P)\f$ is - * the chemical potential at unit activity, which depends only - * on temperature and pressure. - * @{ - */ + /** + * @} + * @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) + * + \hat R T \log a_k. \f] The quantity \f$\mu_k^0(T,P)\f$ is + * the chemical potential at unit activity, which depends only + * on temperature and pressure. + * @{ + */ - /** - * This method returns the activity convention. - * 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 - * - * We set the convention to molality here. - */ - int activityConvention() const; + /** + * This method returns the activity convention. + * 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 + * + * We set the convention to molality here. + */ + int activityConvention() const; - /** - * This method returns an array of generalized concentrations - * \f$ C_k\f$ that are defined such that - * \f$ a_k = C_k / C^0_k, \f$ where \f$ C^0_k \f$ - * is a standard concentration - * defined below. These generalized concentrations are used - * by kinetics manager classes to compute the forward and - * reverse rates of elementary reactions. - * - * @param c 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"); - } + /** + * This method returns an array of generalized concentrations + * \f$ C_k\f$ that are defined such that + * \f$ a_k = C_k / C^0_k, \f$ where \f$ C^0_k \f$ + * is a standard concentration + * defined below. These generalized concentrations are used + * by kinetics manager classes to compute the forward and + * reverse rates of elementary reactions. + * + * @param c 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"); + } - /** - * The standard concentration \f$ C^0_k \f$ used to normalize - * the 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. - */ - virtual doublereal standardConcentration(int k=0) const { - err("standardConcentration"); - return -1.0; - } + /** + * The standard concentration \f$ C^0_k \f$ used to normalize + * the 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. + */ + virtual doublereal standardConcentration(int k=0) const { + err("standardConcentration"); + return -1.0; + } - /** - * Returns the natural logarithm of the standard - * concentration of the kth species - */ - virtual doublereal logStandardConc(int k=0) const { - err("logStandardConc"); - return -1.0; - } + /** + * Returns the natural logarithm of the standard + * concentration of the kth species + */ + 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. - * - * 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 - */ - virtual void getUnitsStandardConc(double *uA, int k = 0, - int sizeUA = 6); + /** + * 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 = 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 + */ + virtual void getUnitsStandardConc(double *uA, int k = 0, + int sizeUA = 6); - /** - * Get the array of non-dimensional activities (molality - * based for this class and classes that derive from it) at - * the current solution temperature, pressure, and - * solution concentration. - */ - virtual void getActivities(doublereal* ac) const { - err("getActivities"); - } + /** + * Get the array of non-dimensional activities (molality + * based for this class and classes that derive from it) at + * the current solution temperature, pressure, and + * solution concentration. + */ + virtual void getActivities(doublereal* ac) const { + err("getActivities"); + } - /** - * Get the array of non-dimensional activity coefficients at - * the current solution temperature, pressure, and - * solution concentration. - * These are mole fraction based activity coefficients. In this - * object, their calculation is based on translating the values - * of Molality based activity coefficients. - * See Denbigh p. 278 for a thorough discussion - */ - void getActivityCoefficients(doublereal* ac) const; + /** + * Get the array of non-dimensional activity coefficients at + * the current solution temperature, pressure, and + * solution concentration. + * These are mole fraction based activity coefficients. In this + * object, their calculation is based on translating the values + * of Molality based activity coefficients. + * See Denbigh p. 278 for a thorough discussion + */ + void getActivityCoefficients(doublereal* ac) const; - /** - * Get the array of non-dimensional molality based - * activity coefficients at the current solution temperature, - * pressure, and solution concentration. - * See Denbigh p. 278 for a thorough discussion - */ - virtual void getMolalityActivityCoefficients(doublereal *acMolality) - const { - err("getMolalityActivityCoefficients"); - } + /** + * Get the array of non-dimensional molality based + * activity coefficients at the current solution temperature, + * pressure, and solution concentration. + * See Denbigh p. 278 for a thorough discussion + */ + virtual void getMolalityActivityCoefficients(doublereal *acMolality) + const { + err("getMolalityActivityCoefficients"); + } - /** - * Calculate the osmotic coefficient - * units = dimensionless - */ - virtual double osmoticCoefficient() const; + /** + * Calculate the osmotic coefficient + * units = dimensionless + */ + virtual double osmoticCoefficient() const; - //@} - /// @name Partial Molar Properties of the Solution - //@{ + //@} + /// @name Partial Molar Properties of the Solution + //@{ - /** - * Get the species electrochemical potentials. - * These are partial molar quantities. - * This method adds a term \f$ Fz_k \phi_k \f$ to the - * to each chemical potential. - * - * 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); - } - } + /** + * Get the species electrochemical potentials. + * These are partial molar quantities. + * This method adds a term \f$ Fz_k \phi_k \f$ to the + * to each chemical potential. + * + * 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); + } + } - //@} - /// @name Properties of the Standard State of the Species in the Solution - //@{ + //@} + /// @name Properties of the Standard State of the Species in the Solution + //@{ - //@} - /// @name Thermodynamic Values for the Species Reference States - //@{ + //@} + /// @name Thermodynamic Values for the Species Reference States + //@{ - /////////////////////////////////////////////////////// - // - // The methods below are not virtual, and should not - // be overloaded. - // - ////////////////////////////////////////////////////// + /////////////////////////////////////////////////////// + // + // The methods below are not virtual, and should not + // be overloaded. + // + ////////////////////////////////////////////////////// - /** - * @name Specific Properties - * @{ - */ + /** + * @name Specific Properties + * @{ + */ - /** - * @name Setting the State - * - * These methods set all or part of the thermodynamic - * state. - * @{ - */ + /** + * @name Setting the State + * + * These methods set all or part of the thermodynamic + * state. + * @{ + */ - //@} + //@} - /** - * @name Chemical Equilibrium - * Routines that implement the Chemical equilibrium capability - * for a single phase, based on the element-potential method. - * @{ - */ + /** + * @name Chemical Equilibrium + * Routines that implement the Chemical equilibrium capability + * for a single phase, based on the element-potential method. + * @{ + */ - /** - * This method is used by the ChemEquil element-potential - * based equilibrium solver. - * It sets the state such that the chemical potentials of the - * species within the current phase 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. - */ - virtual void setToEquilState(const doublereal* lambda_RT) { - err("setToEquilState"); - } + /** + * This method is used by the ChemEquil element-potential + * based equilibrium solver. + * It sets the state such that the chemical potentials of the + * species within the current phase 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. + */ + virtual void setToEquilState(const doublereal* lambda_RT) { + err("setToEquilState"); + } - // called by function 'equilibrate' in ChemEquil.h to transfer - // the element potentials to this object - void setElementPotentials(const vector_fp& lambda) { - m_lambda = lambda; - } + // called by function 'equilibrate' in ChemEquil.h to transfer + // the element potentials to this object + void setElementPotentials(const vector_fp& lambda) { + m_lambda = lambda; + } - void getElementPotentials(doublereal* lambda) { - copy(m_lambda.begin(), m_lambda.end(), lambda); - } + void getElementPotentials(doublereal* lambda) { + copy(m_lambda.begin(), m_lambda.end(), lambda); + } - //@} + //@} - /** - * 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. - * - * The MolalityVPSSTP object defines a new method for setting - * the concentrations of a phase. The new method is defined by a - * block called "soluteMolalities". If this block - * is found, the concentrations within that phase are - * set to the "name":"molalities pairs found within that - * XML block. The solvent concentration is then set - * to everything else. - * - * @param eosdata An XML_Node object corresponding to - * the "thermo" entry for this phase in the input file. - * - */ - virtual void setStateFromXML(const XML_Node& state); + /** + * 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. + * + * The MolalityVPSSTP object defines a new method for setting + * the concentrations of a phase. The new method is defined by a + * block called "soluteMolalities". If this block + * is found, the concentrations within that phase are + * set to the "name":"molalities pairs found within that + * XML block. The solvent concentration is then set + * to everything else. + * + * @param eosdata An XML_Node object corresponding to + * the "thermo" entry for this phase in the input file. + * + */ + virtual void setStateFromXML(const XML_Node& state); - /// 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. + /// 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. - /** - * @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 just prior to returning - * from function importPhase. - * - * @see importCTML.cpp - */ - virtual void initThermo(); + /** + * @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 just prior to returning + * from function importPhase. + * + * @see importCTML.cpp + */ + virtual void initThermo(); - /** - * Import and initialize a ThermoPhase object - * - * @param phaseNode This object must be the phase node of a - * complete XML tree - * description of the phase, including all of the - * species data. In other words while "phase" must - * point to an XML phase object, it must have - * sibling nodes "speciesData" that describe - * the species in the phase. - * @param id ID of the phase. If nonnull, a check is done - * to see if phaseNode is pointing to the phase - * with the correct id. - */ - void initThermoXML(XML_Node& phaseNode, string id); + /** + * Import and initialize a ThermoPhase object + * + * @param phaseNode This object must be the phase node of a + * complete XML tree + * description of the phase, including all of the + * species data. In other words while "phase" must + * point to an XML phase object, it must have + * sibling nodes "speciesData" that describe + * the species in the phase. + * @param id ID of the phase. If nonnull, a check is done + * to see if phaseNode is pointing to the phase + * with the correct id. + */ + void initThermoXML(XML_Node& phaseNode, string id); - /** - * Set the temperature (K), pressure (Pa), and molalities - * (gmol kg-1) of the solutes - */ - void setState_TPM(doublereal t, doublereal p, - const doublereal * const molalities); + /** + * Set the temperature (K), pressure (Pa), and molalities + * (gmol kg-1) of the solutes + */ + void setState_TPM(doublereal t, doublereal p, + const doublereal * const molalities); + /** Set the temperature (K), pressure (Pa), and molalities. */ + void setState_TPM(doublereal t, doublereal p, compositionMap& m); - private: - void initLengths(); + /** Set the temperature (K), pressure (Pa), and molalities. */ + void setState_TPM(doublereal t, doublereal p, const string& m); + + private: + void initLengths(); - protected: + protected: - int m_indexSolvent; - doublereal m_weightSolvent; - /* - * In any molality implementation, it makes sense to have - * a minimum solvent mole fraction requirement, since the - * implementation becomes singular in the xmolSolvent=0 - * limit. The default is to set it to 0.01. - * We then modify the molality definition to ensure that - * molal_solvent = 0 when xmol_solvent = 0. - */ - doublereal m_xmolSolventMIN; - /* - * This is the multiplication factor that goes inside - * log expressions involving the molalities of species. - * Its equal to Wt_0 / 1000. - * where Wt_0 = weight of solvent (kg/kmol) - */ - doublereal m_Mnaught; + int m_indexSolvent; + doublereal m_weightSolvent; + /* + * In any molality implementation, it makes sense to have + * a minimum solvent mole fraction requirement, since the + * implementation becomes singular in the xmolSolvent=0 + * limit. The default is to set it to 0.01. + * We then modify the molality definition to ensure that + * molal_solvent = 0 when xmol_solvent = 0. + */ + doublereal m_xmolSolventMIN; + /* + * This is the multiplication factor that goes inside + * log expressions involving the molalities of species. + * Its equal to Wt_0 / 1000. + * where Wt_0 = weight of solvent (kg/kmol) + */ + doublereal m_Mnaught; - mutable vector_fp m_molalities; - private: - doublereal err(string msg) const; + mutable vector_fp m_molalities; + private: + doublereal err(string msg) const; - }; + }; }