MineralEQ3.cpp

Go to the documentation of this file.
00001 /**
00002  *  @file MineralEQ3.cpp
00003  *   Definition file for the MineralEQ3 class, which represents a fixed-composition
00004  * incompressible substance (see \ref thermoprops and 
00005  * class \link Cantera::MineralEQ3 MineralEQ3\endlink)
00006  */
00007 
00008 /*
00009  * Copywrite (2005) Sandia Corporation. Under the terms of
00010  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
00011  * U.S. Government retains certain rights in this software.
00012  *
00013  * Copyright 2001 California Institute of Technology
00014  */
00015 
00016 /*
00017  * $Id: MineralEQ3.cpp 279 2009-12-05 19:08:43Z hkmoffa $ 
00018  */
00019 
00020 #include "ct_defs.h"
00021 #include "mix_defs.h"
00022 #include "MineralEQ3.h"
00023 #include "SpeciesThermo.h"
00024 
00025 #include "ThermoFactory.h"
00026 #include "MineralEQ3.h"
00027 
00028 #include <string>
00029 
00030 using namespace std;
00031 
00032 namespace Cantera {
00033 
00034   /*
00035    * ----  Constructors -------
00036    */
00037 
00038   /*
00039    * Default Constructor for the MineralEQ3 class
00040    */
00041   MineralEQ3::MineralEQ3():
00042     StoichSubstanceSSTP ()
00043   {
00044   }
00045 
00046   // Create and initialize a MineralEQ3 ThermoPhase object 
00047   // from an asci input file
00048   /*
00049    * @param infile name of the input file
00050    * @param id     name of the phase id in the file.
00051    *               If this is blank, the first phase in the file is used.
00052    */
00053   MineralEQ3::MineralEQ3(std::string infile, std::string id) :
00054     StoichSubstanceSSTP()
00055   {
00056     XML_Node* root = get_XML_File(infile);
00057     if (id == "-") id = "";
00058     XML_Node* xphase = get_XML_NameID("phase", std::string("#")+id, root);
00059     if (!xphase) {
00060       throw CanteraError("MineralEQ3::MineralEQ3",
00061                           "Couldn't find phase name in file:" + id);
00062     }
00063     // Check the model name to ensure we have compatibility
00064     const XML_Node& th = xphase->child("thermo");
00065     std::string model = th["model"];
00066     if (model != "StoichSubstance" && model != "MineralEQ3") {
00067       throw CanteraError("MineralEQ3::MineralEQ3",
00068                          "thermo model attribute must be StoichSubstance");
00069     }
00070     importPhase(*xphase, this);
00071   }
00072 
00073   // Full Constructor.
00074   /*
00075    *  @param phaseRef XML node pointing to a MineralEQ3 description
00076    *  @param id       Id of the phase. 
00077    */
00078   MineralEQ3::MineralEQ3(XML_Node& xmlphase, std::string id) :
00079     StoichSubstanceSSTP()
00080   {
00081     if (id != "") {
00082       std::string idxml = xmlphase["id"];
00083       if (id != idxml) {
00084         throw CanteraError("MineralEQ3::MineralEQ3",
00085                            "id's don't match");
00086       }
00087     }
00088     const XML_Node& th = xmlphase.child("thermo");
00089     std::string model = th["model"];
00090     if (model != "StoichSubstance" && model != "MineralEQ3") {
00091       throw CanteraError("MineralEQ3::MineralEQ3",
00092                          "thermo model attribute must be StoichSubstance");
00093     }
00094     importPhase(xmlphase, this);
00095   }
00096 
00097   //! Copy constructor
00098   /*!
00099    * @param right Object to be copied
00100    */
00101   MineralEQ3::MineralEQ3(const MineralEQ3  &right) :
00102     StoichSubstanceSSTP()
00103   {
00104     *this = operator=(right);
00105   }
00106   
00107   //! Assignment operator
00108   /*!
00109    * @param right Object to be copied
00110    */
00111   MineralEQ3 & 
00112   MineralEQ3::operator=(const MineralEQ3 & right) {
00113     if (&right == this) {
00114       return *this;
00115     }
00116     StoichSubstanceSSTP::operator=(right);
00117     m_Mu0_pr_tr = right.m_Mu0_pr_tr;
00118     m_Entrop_pr_tr = right.m_Entrop_pr_tr;
00119     m_deltaG_formation_pr_tr = right.m_deltaG_formation_pr_tr;
00120     m_deltaH_formation_pr_tr = right.m_deltaH_formation_pr_tr;
00121     m_V0_pr_tr               = right.m_V0_pr_tr;
00122     m_a                      = right.m_a;
00123     m_b                      = right.m_b;
00124     m_c                      = right.m_c;
00125 
00126     return *this;
00127   }
00128 
00129   /*
00130    * Destructor for the routine (virtual)
00131    *        
00132    */
00133   MineralEQ3::~MineralEQ3() 
00134   {
00135   }
00136 
00137   // Duplication function
00138   /*
00139    * This virtual function is used to create a duplicate of the
00140    * current phase. It's used to duplicate the phase when given
00141    * a ThermoPhase pointer to the phase.
00142    *
00143    * @return It returns a ThermoPhase pointer.
00144    */
00145   ThermoPhase *MineralEQ3::duplMyselfAsThermoPhase() const {
00146     MineralEQ3 *stp = new MineralEQ3(*this);
00147     return (ThermoPhase *) stp;
00148   }
00149 
00150 
00151   /*
00152    * ---- Utilities -----
00153    */
00154 
00155   /*
00156    * Equation of state flag. Returns the value cStoichSubstance,
00157    * defined in mix_defs.h.
00158    */
00159   int MineralEQ3::eosType() const {
00160     return cStoichSubstance; 
00161   }
00162 
00163   /*
00164    * ---- Molar Thermodynamic properties of the solution ----
00165    */
00166 
00167   /**
00168    * ----- Mechanical Equation of State ------
00169    */
00170 
00171   /*
00172    * Pressure. Units: Pa.
00173    * For an incompressible substance, the density is independent
00174    * of pressure. This method simply returns the stored
00175    * pressure value.
00176    */ 
00177   doublereal MineralEQ3::pressure() const {
00178     return m_press;
00179   }
00180     
00181   /*
00182    * Set the pressure at constant temperature. Units: Pa.
00183    * For an incompressible substance, the density is 
00184    * independent of pressure. Therefore, this method only 
00185    * stores the specified pressure value. It does not 
00186    * modify the density.
00187    */
00188   void MineralEQ3::setPressure(doublereal p) {
00189     m_press = p;
00190   }
00191 
00192   /*
00193    * The isothermal compressibility. Units: 1/Pa.
00194    * The isothermal compressibility is defined as
00195    * \f[
00196    * \kappa_T = -\frac{1}{v}\left(\frac{\partial v}{\partial P}\right)_T
00197    * \f]
00198    *
00199    *  It's equal to zero for this model, since the molar volume
00200    *  doesn't change with pressure or temperature.
00201    */
00202   doublereal MineralEQ3::isothermalCompressibility() const {
00203     return 0.0;
00204   } 
00205     
00206   /*
00207    * The thermal expansion coefficient. Units: 1/K.
00208    * The thermal expansion coefficient is defined as
00209    *
00210    * \f[
00211    * \beta = \frac{1}{v}\left(\frac{\partial v}{\partial T}\right)_P
00212    * \f]
00213    *
00214    *  It's equal to zero for this model, since the molar volume
00215    *  doesn't change with pressure or temperature.
00216    */
00217   doublereal MineralEQ3::thermalExpansionCoeff() const {
00218     return 0.0;
00219   }
00220     
00221   /*
00222    * ---- Chemical Potentials and Activities ----
00223    */
00224 
00225   /*
00226    * This method returns the array of generalized
00227    * concentrations.  For a stoichiomeetric substance, there is
00228    * only one species, and the generalized concentration is 1.0.
00229    */
00230   void MineralEQ3::
00231   getActivityConcentrations(doublereal* c) const {
00232     c[0] = 1.0;
00233   }
00234 
00235   /*
00236    * The standard concentration. This is defined as the concentration 
00237    * by which the generalized concentration is normalized to produce 
00238    * the activity. 
00239    */ 
00240   doublereal MineralEQ3::standardConcentration(int k) const {
00241     return 1.0;
00242   }
00243 
00244   /*
00245    * Returns the natural logarithm of the standard 
00246    * concentration of the kth species
00247    */
00248   doublereal MineralEQ3::logStandardConc(int k) const {
00249     return 0.0;
00250   }
00251 
00252   /*
00253    * Returns the units of the standard and generalized
00254    * concentrations Note they have the same units, as their
00255    * ratio is defined to be equal to the activity of the kth
00256    * species in the solution, which is unitless.
00257    *
00258    * This routine is used in print out applications where the
00259    * units are needed. Usually, MKS units are assumed throughout
00260    * the program and in the XML input files.
00261    *
00262    *  uA[0] = kmol units - default  = 1
00263    *  uA[1] = m    units - default  = -nDim(), the number of spatial
00264    *                                dimensions in the Phase class.
00265    *  uA[2] = kg   units - default  = 0;
00266    *  uA[3] = Pa(pressure) units - default = 0;
00267    *  uA[4] = Temperature units - default = 0;
00268    *  uA[5] = time units - default = 0
00269    */
00270   void MineralEQ3::
00271   getUnitsStandardConc(doublereal *uA, int k, int sizeUA) const {
00272     for (int i = 0; i < 6; i++) {
00273       uA[i] = 0;
00274     }
00275   }
00276 
00277   /*
00278    *  ---- Partial Molar Properties of the Solution ----
00279    */
00280 
00281 
00282 
00283   /*
00284    * ---- Properties of the Standard State of the Species in the Solution
00285    * ----
00286    */
00287 
00288   /*
00289    * Get the array of chemical potentials at unit activity 
00290    * \f$ \mu^0_k \f$.
00291    *
00292    * For a stoichiometric substance, there is no activity term in 
00293    * the chemical potential expression, and therefore the
00294    * standard chemical potential and the chemical potential
00295    * are both equal to the molar Gibbs function.
00296    */
00297   void MineralEQ3::
00298   getStandardChemPotentials(doublereal* mu0) const {
00299     getGibbs_RT(mu0);
00300     mu0[0] *= GasConstant * temperature();
00301   }
00302     
00303   /*
00304    * Get the nondimensional Enthalpy functions for the species
00305    * at their standard states at the current 
00306    * <I>T</I> and <I>P</I> of the solution.
00307    * Molar enthalpy. Units: J/kmol.  For an incompressible,
00308    * stoichiometric substance, the internal energy is
00309    * independent of pressure, and therefore the molar enthalpy
00310    * is \f[ \hat h(T, P) = \hat u(T) + P \hat v \f], where the
00311    * molar specific volume is constant.
00312    */
00313   void MineralEQ3::getEnthalpy_RT(doublereal* hrt) const {
00314     getEnthalpy_RT_ref(hrt);
00315     doublereal RT = GasConstant * temperature();
00316     doublereal presCorrect = (m_press - m_p0) /  molarDensity();
00317     hrt[0] += presCorrect / RT;
00318   }
00319 
00320   /*
00321    * Get the array of nondimensional Entropy functions for the
00322    * standard state species
00323    * at the current <I>T</I> and <I>P</I> of the solution.
00324    */
00325   void MineralEQ3::getEntropy_R(doublereal* sr) const {
00326     getEntropy_R_ref(sr);
00327   }
00328 
00329   /*
00330    * Get the nondimensional Gibbs functions for the species
00331    * at their standard states of solution at the current T and P
00332    * of the solution
00333    */
00334   void MineralEQ3::getGibbs_RT(doublereal* grt) const {
00335     getEnthalpy_RT(grt);
00336     grt[0] -= m_s0_R[0];
00337   }
00338 
00339   /*
00340    * Get the nondimensional Gibbs functions for the standard
00341    * state of the species at the current T and P.
00342    */
00343   void MineralEQ3::getCp_R(doublereal* cpr) const {
00344     _updateThermo();
00345     cpr[0] = m_cp0_R[0];
00346   }
00347    
00348   /*
00349    * Molar internal energy (J/kmol).
00350    * For an incompressible,
00351    * stoichiometric substance, the molar internal energy is
00352    * independent of pressure. Since the thermodynamic properties
00353    * are specified by giving the standard-state enthalpy, the
00354    * term \f$ P_0 \hat v\f$ is subtracted from the specified molar
00355    * enthalpy to compute the molar internal energy.
00356    */
00357   void MineralEQ3::getIntEnergy_RT(doublereal* urt) const {
00358     _updateThermo();
00359     doublereal RT = GasConstant * temperature();
00360     doublereal PV = m_p0 / molarDensity();
00361     urt[0] = m_h0_RT[0] - PV / RT;
00362   }
00363 
00364   /*
00365    * ---- Thermodynamic Values for the Species Reference States ----
00366    */
00367   /*
00368    * Molar internal energy or the reference state at the current
00369    * temperature, T  (J/kmol).  
00370    * For an incompressible,
00371    * stoichiometric substance, the molar internal energy is
00372    * independent of pressure. Since the thermodynamic properties
00373    * are specified by giving the standard-state enthalpy, the
00374    * term \f$ P_0 \hat v\f$ is subtracted from the specified molar
00375    * enthalpy to compute the molar internal energy.
00376    *
00377    * Note, this is equal to the standard state internal energy
00378    * evaluated at the reference pressure.
00379    */
00380   void MineralEQ3::getIntEnergy_RT_ref(doublereal* urt) const {
00381     _updateThermo();
00382     doublereal RT = GasConstant * temperature();
00383     doublereal PV = m_p0 / molarDensity();
00384     urt[0] = m_h0_RT[0] - PV / RT;
00385   }
00386               
00387   /*
00388    * ---- Saturation Properties
00389    */
00390     
00391  
00392 
00393   /*
00394    * ---- Initialization and Internal functions
00395    */
00396 
00397   /**
00398    * @internal Initialize. This method is provided to allow
00399    * subclasses to perform any initialization required after all
00400    * species have been added. For example, it might be used to
00401    * resize internal work arrays that must have an entry for
00402    * each species.  The base class implementation does nothing,
00403    * and subclasses that do not require initialization do not
00404    * need to overload this method.  When importing a CTML phase
00405    * description, this method is called just prior to returning
00406    * from function importPhase.
00407    *
00408    * @see importCTML.cpp
00409    */
00410   void MineralEQ3::initThermo() {
00411   
00412     /*
00413      * Call the base class thermo initializer
00414      */
00415     StoichSubstanceSSTP::initThermo();
00416   }
00417 
00418   /**
00419    * setParameters:
00420    *
00421    *   Generic routine that is used to set the parameters used
00422    *   by this model.
00423    *        C[0] = density of phase [ kg/m3 ]
00424    */
00425   void MineralEQ3::setParameters(int n, doublereal * const c) {
00426     doublereal rho = c[0];
00427     setDensity(rho);
00428   }
00429 
00430   /**
00431    * getParameters:
00432    *
00433    *   Generic routine that is used to get the parameters used
00434    *   by this model.
00435    *        n = 1
00436    *        C[0] = density of phase [ kg/m3 ]
00437    */
00438   void MineralEQ3::getParameters(int &n, doublereal * const c) const {
00439     doublereal rho = density();
00440     n = 1;
00441     c[0] = rho;
00442   }
00443 
00444   // Initialize the phase parameters from an XML file.
00445   /*
00446    * initThermoXML()                 (virtual from ThermoPhase)
00447    *
00448    *  This gets called from importPhase(). It processes the XML file
00449    *  after the species are set up. This is the main routine for
00450    *  reading in activity coefficient parameters.
00451    *
00452    * @param phaseNode This object must be the phase node of a
00453    *             complete XML tree
00454    *             description of the phase, including all of the
00455    *             species data. In other words while "phase" must
00456    *             point to an XML phase object, it must have
00457    *             sibling nodes "speciesData" that describe
00458    *             the species in the phase.
00459    * @param id   ID of the phase. If nonnull, a check is done
00460    *             to see if phaseNode is pointing to the phase
00461    *             with the correct id.
00462    */
00463   void MineralEQ3::initThermoXML(XML_Node& phaseNode, std::string id) {
00464     /*
00465      * Find the Thermo XML node 
00466      */
00467     if (!phaseNode.hasChild("thermo")) {
00468       throw CanteraError("HMWSoln::initThermoXML",
00469                          "no thermo XML node");
00470     }
00471    
00472     std::vector<const XML_Node *> xspecies = speciesData();
00473     const XML_Node *xsp = xspecies[0];
00474 
00475     XML_Node *aStandardState = 0;
00476     if (xsp->hasChild("standardState")) {
00477       aStandardState = &xsp->child("standardState");
00478     } else {
00479       throw CanteraError("MineralEQ3::initThermoXML",
00480                          "no standard state mode");
00481     }
00482     doublereal volVal = 0.0;
00483     string smodel = (*aStandardState)["model"];
00484     if (smodel != "constantVolume") {
00485       throw CanteraError("MineralEQ3::initThermoXML",
00486                          "wrong standard state mode");
00487     }
00488     if (aStandardState->hasChild("V0_Pr_Tr")) {
00489       XML_Node& aV = aStandardState->child("V0_Pr_Tr");
00490       string Aunits = "";
00491       double Afactor = toSI("cm3/gmol");
00492       if (aV.hasAttrib("units")) {
00493         Aunits = aV.attrib("units");
00494         Afactor = toSI(Aunits); 
00495       }
00496       volVal = getFloat(*aStandardState, "V0_Pr_Tr");
00497       m_V0_pr_tr= volVal;
00498       volVal *= Afactor;
00499       m_speciesSize[0] = volVal;
00500     } else {
00501       throw CanteraError("MineralEQ3::initThermoXML",
00502                          "wrong standard state mode");
00503     }
00504     doublereal rho = molecularWeight(0) / volVal;
00505     setDensity(rho);
00506     
00507     const XML_Node &sThermo = xsp->child("thermo");
00508     const XML_Node &MinEQ3node = sThermo.child("MinEQ3");
00509    
00510 
00511     m_deltaG_formation_pr_tr =
00512       getFloatDefaultUnits(MinEQ3node, "DG0_f_Pr_Tr", "cal/gmol", "actEnergy");
00513     m_deltaH_formation_pr_tr =
00514       getFloatDefaultUnits(MinEQ3node, "DH0_f_Pr_Tr", "cal/gmol", "actEnergy");
00515     m_Entrop_pr_tr = getFloatDefaultUnits(MinEQ3node, "S0_Pr_Tr", "cal/gmol/K");
00516     m_a = getFloatDefaultUnits(MinEQ3node, "a", "cal/gmol/K");
00517     m_b = getFloatDefaultUnits(MinEQ3node, "b", "cal/gmol/K2");
00518     m_c = getFloatDefaultUnits(MinEQ3node, "c", "cal-K/gmol");
00519 
00520   
00521     convertDGFormation();
00522   
00523   }
00524 
00525 
00526   void MineralEQ3::setParametersFromXML(const XML_Node& eosdata) {
00527     std::string model = eosdata["model"];
00528     if (model != "MineralEQ3") {
00529       throw CanteraError("MineralEQ3::MineralEQ3",
00530                          "thermo model attribute must be MineralEQ3");
00531     }
00532   }
00533 
00534  doublereal MineralEQ3::LookupGe(const std::string& elemName) {
00535 #ifdef OLDWAY
00536     int num = sizeof(geDataTable) / sizeof(struct GeData);
00537     string s3 = elemName.substr(0,3);
00538     for (int i = 0; i < num; i++) {
00539       //if (!std::strncmp(elemName.c_str(), aWTable[i].name, 3)) {
00540       if (s3 == geDataTable[i].name) {
00541         return (geDataTable[i].GeValue);
00542       }
00543     }
00544     throw CanteraError("LookupGe", "element " + s + " not found");
00545     return -1.0;
00546 #else
00547     int iE = elementIndex(elemName);
00548     if (iE < 0) {
00549       throw CanteraError("PDSS_HKFT::LookupGe", "element " + elemName + " not found");
00550     }
00551     doublereal geValue = entropyElement298(iE);
00552     if (geValue == ENTROPY298_UNKNOWN) {
00553       throw CanteraError("PDSS_HKFT::LookupGe",
00554                          "element " + elemName + " doesn not have a supplied entropy298");
00555     }
00556     geValue *= (-298.15);
00557     return geValue;
00558 #endif
00559   }
00560 
00561  void MineralEQ3::convertDGFormation() {
00562     /*
00563      * Ok let's get the element compositions and conversion factors.
00564      */
00565     int ne = nElements();
00566     doublereal na;
00567     doublereal ge;
00568     string ename;
00569 
00570     doublereal totalSum = 0.0;
00571     for (int m = 0; m < ne; m++) {
00572       na = nAtoms(0, m);
00573       if (na > 0.0) {
00574         ename = elementName(m);
00575         ge = LookupGe(ename);
00576         totalSum += na * ge;
00577       }
00578     }
00579     // Add in the charge
00580     // if (m_charge_j != 0.0) {
00581     // ename = "H";
00582       // ge = LookupGe(ename);
00583     // totalSum -= m_charge_j * ge;
00584     //}
00585     // Ok, now do the calculation. Convert to joules kmol-1
00586     doublereal dg = m_deltaG_formation_pr_tr * 4.184 * 1.0E3;
00587     //! Store the result into an internal variable.
00588     m_Mu0_pr_tr = dg + totalSum;
00589   }
00590 
00591 }
00592 
00593 
Generated by  doxygen 1.6.3