IdealMolalSoln.cpp

Go to the documentation of this file.
00001 /**
00002  *  @file IdealMolalSoln.cpp
00003  *   ThermoPhase object for the ideal molal equation of
00004  * state (see \ref thermoprops 
00005  * and class \link Cantera::IdealMolalSoln IdealMolalSoln\endlink).
00006  *
00007  * Definition file for a derived class of ThermoPhase that handles
00008  * variable pressure standard state methods for calculating
00009  * thermodynamic properties that are further based upon 
00010  * activities on the molality scale. The Ideal molal
00011  * solution assumes that all molality-based activity
00012  * coefficients are equal to one. This turns out, actually, to be
00013  * highly nonlinear when the solvent densities get low.
00014  */
00015 /*
00016  * Copywrite (2006) Sandia Corporation. Under the terms of 
00017  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
00018  * U.S. Government retains certain rights in this software.
00019  */
00020 /*
00021  *
00022  *  $Date: 2009-12-05 14:08:43 -0500 (Sat, 05 Dec 2009) $
00023  *  $Revision: 279 $
00024  */
00025 
00026 #include "IdealMolalSoln.h"
00027 #include "ThermoFactory.h"
00028 #include <cmath>
00029 
00030 //@{
00031 #ifndef MAX
00032 #define MAX(x,y)    (( (x) > (y) ) ? (x) : (y))
00033 #endif
00034 //@}
00035 
00036 namespace Cantera {
00037 
00038 
00039   static double xxSmall = 1.0E-150;
00040   /**
00041    * Default constructor
00042    */
00043   IdealMolalSoln::IdealMolalSoln() :
00044     MolalityVPSSTP(),
00045     m_formGC(2),
00046     IMS_typeCutoff_(0),
00047     IMS_X_o_cutoff_(0.20),
00048     IMS_gamma_o_min_(0.00001),
00049     IMS_gamma_k_min_(10.0),
00050     IMS_cCut_(.05),
00051     IMS_slopefCut_(0.6),
00052     IMS_dfCut_(0.0),
00053     IMS_efCut_(0.0),
00054     IMS_afCut_(0.0),
00055     IMS_bfCut_(0.0),
00056     IMS_slopegCut_(0.0),
00057     IMS_dgCut_(0.0),
00058     IMS_egCut_(0.0),
00059     IMS_agCut_(0.0),
00060     IMS_bgCut_(0.0)
00061   {
00062   }
00063 
00064   /*
00065    * Copy Constructor:
00066    *
00067    *  Note this stuff will not work until the underlying phase
00068    *  has a working copy constructor
00069    */
00070   IdealMolalSoln::IdealMolalSoln(const IdealMolalSoln &b) :
00071     MolalityVPSSTP(b)
00072   {
00073     /*
00074      * Use the assignment operator to do the brunt
00075      * of the work for the copy construtor.
00076      */
00077     *this = b;
00078   }
00079 
00080   /*
00081    * operator=()
00082    *
00083    *  Note this stuff will not work until the underlying phase
00084    *  has a working assignment operator
00085    */
00086   IdealMolalSoln& IdealMolalSoln::
00087   operator=(const IdealMolalSoln &b) {
00088     if (&b != this) {
00089       MolalityVPSSTP::operator=(b);
00090       m_speciesMolarVolume  = b.m_speciesMolarVolume;
00091       m_formGC              = b.m_formGC;
00092       IMS_typeCutoff_       = b.IMS_typeCutoff_;
00093       IMS_X_o_cutoff_       = b.IMS_X_o_cutoff_;
00094       IMS_gamma_o_min_      = b.IMS_gamma_o_min_;
00095       IMS_gamma_k_min_      = b.IMS_gamma_k_min_;
00096       IMS_cCut_             = b.IMS_cCut_;
00097       IMS_slopefCut_        = b.IMS_slopefCut_;
00098       IMS_dfCut_            = b.IMS_dfCut_;
00099       IMS_efCut_            = b.IMS_efCut_;
00100       IMS_afCut_            = b.IMS_afCut_;
00101       IMS_bfCut_            = b.IMS_bfCut_;
00102       IMS_slopegCut_        = b.IMS_slopegCut_;
00103       IMS_dgCut_            = b.IMS_dgCut_;
00104       IMS_egCut_            = b.IMS_egCut_;
00105       IMS_agCut_            = b.IMS_agCut_;
00106       IMS_bgCut_            = b.IMS_bgCut_;
00107       m_expg0_RT            = b.m_expg0_RT;
00108       m_pe                  = b.m_pe;
00109       m_pp                  = b.m_pp;
00110       m_tmpV                = b.m_tmpV;
00111       IMS_lnActCoeffMolal_  = b.IMS_lnActCoeffMolal_;
00112     }
00113     return *this;
00114   }
00115 
00116   IdealMolalSoln::IdealMolalSoln(std::string inputFile, std::string id) :
00117     MolalityVPSSTP(),
00118     m_formGC(2),
00119     IMS_typeCutoff_(0),
00120     IMS_X_o_cutoff_(0.2),
00121     IMS_gamma_o_min_(0.00001),
00122     IMS_gamma_k_min_(10.0),
00123     IMS_cCut_(.05),
00124     IMS_slopefCut_(0.6),
00125     IMS_dfCut_(0.0),
00126     IMS_efCut_(0.0),
00127     IMS_afCut_(0.0),
00128     IMS_bfCut_(0.0),
00129     IMS_slopegCut_(0.0),
00130     IMS_dgCut_(0.0),
00131     IMS_egCut_(0.0),
00132     IMS_agCut_(0.0),
00133     IMS_bgCut_(0.0)
00134   {
00135     constructPhaseFile(inputFile, id);
00136   }
00137 
00138   IdealMolalSoln::IdealMolalSoln(XML_Node& root, std::string id) :
00139     MolalityVPSSTP(),
00140     m_formGC(2),
00141     IMS_typeCutoff_(0),
00142     IMS_X_o_cutoff_(0.2),
00143     IMS_gamma_o_min_(0.00001),
00144     IMS_gamma_k_min_(10.0),
00145     IMS_cCut_(.05),
00146     IMS_slopefCut_(0.6),
00147     IMS_dfCut_(0.0),
00148     IMS_efCut_(0.0),
00149     IMS_afCut_(0.0),
00150     IMS_bfCut_(0.0),
00151     IMS_slopegCut_(0.0),
00152     IMS_dgCut_(0.0),
00153     IMS_egCut_(0.0),
00154     IMS_agCut_(0.0),
00155     IMS_bgCut_(0.0)
00156   {
00157     constructPhaseXML(root, id);
00158   }
00159 
00160   /*
00161    *
00162    * ~IdealMolalSoln():   (virtual)
00163    *
00164    * Destructor: does nothing:
00165    *
00166    */
00167   IdealMolalSoln::~IdealMolalSoln() {
00168   }
00169 
00170   /**
00171    *
00172    */
00173   ThermoPhase* IdealMolalSoln::duplMyselfAsThermoPhase() const {
00174     IdealMolalSoln* mtp = new IdealMolalSoln(*this);
00175     return (ThermoPhase *) mtp;
00176   }
00177 
00178   //
00179   // -------- Molar Thermodynamic Properties of the Solution --------------- 
00180   //
00181   /*
00182    * Molar enthalpy of the solution: Units: J/kmol.
00183    *
00184    * Returns the amount of enthalpy per mole of solution.
00185    * For an ideal molal solution,
00186    * \f[
00187    * \bar{h}(T, P, X_k) = \sum_k X_k \bar{h}_k(T)  
00188    * \f]
00189    * The formula is written in terms of the partial molar enthalpies.
00190    * \f$ \bar{h}_k(T, p, m_k) \f$.
00191    * See the partial molar enthalpy function, getPartialMolarEnthalpies(),
00192    * for details.
00193    *
00194    * Units: J/kmol
00195    */
00196   doublereal IdealMolalSoln::enthalpy_mole() const {
00197     getPartialMolarEnthalpies(DATA_PTR(m_tmpV));
00198     getMoleFractions(DATA_PTR(m_pp));
00199     double val = mean_X(DATA_PTR(m_tmpV));
00200     return val;
00201   }
00202 
00203   /*
00204    * Molar internal energy of the solution: Units: J/kmol.
00205    *
00206    * Returns the amount of internal energy per mole of solution.
00207    * For an ideal molal solution,
00208    * \f[
00209    * \bar{u}(T, P, X_k) = \sum_k X_k \bar{u}_k(T)  
00210    * \f]
00211    * The formula is written in terms of the partial molar internal energy.
00212    * \f$ \bar{u}_k(T, p, m_k) \f$.
00213    */
00214   doublereal IdealMolalSoln::intEnergy_mole() const {
00215     getPartialMolarEnthalpies(DATA_PTR(m_tmpV));
00216     return mean_X(DATA_PTR(m_tmpV));
00217   }
00218 
00219   /*
00220    * Molar entropy of the solution: Units J/kmol/K.
00221    *
00222    * Returns the amount of entropy per mole of solution.
00223    * For an ideal molal solution,
00224    * \f[
00225    * \bar{s}(T, P, X_k) = \sum_k X_k \bar{s}_k(T)  
00226    * \f]
00227    * The formula is written in terms of the partial molar entropies.
00228    * \f$ \bar{s}_k(T, p, m_k) \f$.
00229    * See the partial molar entropies function, getPartialMolarEntropies(),
00230    * for details.
00231    *
00232    * Units: J/kmol/K.
00233    */
00234   doublereal IdealMolalSoln::entropy_mole() const {
00235     getPartialMolarEntropies(DATA_PTR(m_tmpV));
00236     return mean_X(DATA_PTR(m_tmpV));
00237   }
00238 
00239   /*
00240    * Molar Gibbs function for the solution: Units J/kmol. 
00241    *
00242    * Returns the gibbs free energy of the solution per mole
00243    * of the solution.
00244    *
00245    * \f[
00246    * \bar{g}(T, P, X_k) = \sum_k X_k \mu_k(T)  
00247    * \f]
00248    *
00249    * Units: J/kmol
00250    */
00251   doublereal IdealMolalSoln::gibbs_mole() const {
00252     getChemPotentials(DATA_PTR(m_tmpV));
00253     return mean_X(DATA_PTR(m_tmpV));
00254   }
00255 
00256   /*
00257    * Molar heat capacity at constant pressure: Units: J/kmol/K. 
00258    *  * \f[
00259    * \bar{c}_p(T, P, X_k) = \sum_k X_k \bar{c}_{p,k}(T)  
00260    * \f]
00261    *
00262    * Units: J/kmol/K
00263    */
00264   doublereal IdealMolalSoln::cp_mole() const {
00265     getPartialMolarCp(DATA_PTR(m_tmpV));
00266     double val = mean_X(DATA_PTR(m_tmpV));
00267     return val;
00268   }
00269 
00270   /*
00271    * Molar heat capacity at constant volume: Units: J/kmol/K. 
00272    * NOT IMPLEMENTED.
00273    * Units: J/kmol/K
00274    */
00275   doublereal IdealMolalSoln::cv_mole() const {
00276     return err("not implemented");
00277   }
00278 
00279   //
00280   // ------- Mechanical Equation of State Properties ------------------------
00281   //
00282 
00283 
00284 
00285   /*
00286    * Set the pressure at constant temperature. Units: Pa.
00287    * This method sets a constant within the object.
00288    * The mass density is not a function of pressure.
00289    */
00290   void IdealMolalSoln::setPressure(doublereal p) {
00291     setState_TP(temperature(), p);
00292   }
00293 
00294   void IdealMolalSoln::calcDensity() {
00295     double *vbar = &m_pp[0];
00296     getPartialMolarVolumes(vbar);
00297     double *x = &m_tmpV[0];
00298     getMoleFractions(x);
00299     doublereal vtotal = 0.0;
00300     for (int i = 0; i < m_kk; i++) {
00301       vtotal += vbar[i] * x[i];
00302     }
00303     doublereal dd = meanMolecularWeight() / vtotal;
00304     State::setDensity(dd);
00305   }
00306 
00307   /*
00308    * The isothermal compressibility. Units: 1/Pa.
00309    * The isothermal compressibility is defined as
00310    * \f[
00311    * \kappa_T = -\frac{1}{v}\left(\frac{\partial v}{\partial P}\right)_T
00312    * \f]
00313    *
00314    *  It's equal to zero for this model, since the molar volume
00315    *  doesn't change with pressure or temperature.
00316    */
00317   doublereal IdealMolalSoln::isothermalCompressibility() const {
00318     return 0.0;
00319   }
00320 
00321   /*
00322    * The thermal expansion coefficient. Units: 1/K.
00323    * The thermal expansion coefficient is defined as
00324    *
00325    * \f[
00326    * \beta = \frac{1}{v}\left(\frac{\partial v}{\partial T}\right)_P
00327    * \f]
00328    *
00329    *  It's equal to zero for this model, since the molar volume
00330    *  doesn't change with pressure or temperature.
00331    */
00332   doublereal IdealMolalSoln::thermalExpansionCoeff() const {
00333     return 0.0;
00334   }
00335     
00336   /*
00337    * Overwritten setDensity() function is necessary because the
00338    * density is not an indendent variable.
00339    *
00340    * This function will now throw an error condition
00341    *
00342    * @internal May have to adjust the strategy here to make
00343    * the eos for these materials slightly compressible, in order
00344    * to create a condition where the density is a function of
00345    * the pressure.
00346    *
00347    * This function will now throw an error condition.
00348    *
00349    *  NOTE: This is an overwritten function from the State.h
00350    *        class
00351    */
00352   void IdealMolalSoln::setDensity(const doublereal rho) {
00353     double dens = density();
00354     if (rho != dens) {
00355       throw CanteraError("Idea;MolalSoln::setDensity",
00356                          "Density is not an independent variable");
00357     }
00358   }
00359 
00360   /*
00361    * Overwritten setMolarDensity() function is necessary because the
00362    * density is not an indendent variable.
00363    *
00364    * This function will now throw an error condition.
00365    *
00366    *  NOTE: This is a virtual function, overwritten function from the State.h
00367    *        class
00368    */
00369   void IdealMolalSoln::setMolarDensity(const doublereal conc) {
00370     double concI = State::molarDensity();
00371     if (conc != concI) {
00372       throw CanteraError("IdealMolalSoln::setMolarDensity",
00373                          "molarDensity/denisty is not an independent variable");
00374     }
00375   }
00376 
00377   void IdealMolalSoln::setState_TP(doublereal temp, doublereal pres) {
00378     State::setTemperature(temp);
00379     m_Pcurrent = pres;
00380     updateStandardStateThermo();
00381     //m_densWaterSS = m_waterSS->density();
00382     calcDensity();
00383   }
00384 
00385   //
00386   // ------- Activities and Activity Concentrations
00387   //
00388 
00389   /*
00390    * This method returns an array of activity concentrations \f$ C^a_k\f$.
00391    * \f$ C^a_k\f$ are defined such that 
00392    * \f$ a_k = C^a_k / C^s_k, \f$ where \f$ C^s_k \f$ 
00393    * is a standard concentration
00394    * defined below.  These activity concentrations are used
00395    * by kinetics manager classes to compute the forward and
00396    * reverse rates of elementary reactions. 
00397    *
00398    * @param c Array of activity concentrations. The 
00399    *           units depend upon the implementation of the
00400    *           reaction rate expressions within the phase.
00401    */
00402   void IdealMolalSoln::getActivityConcentrations(doublereal* c) const {
00403     if (m_formGC != 1) {
00404       double c_solvent = standardConcentration();
00405       getActivities(c);
00406       for (int k = 0; k < m_kk; k++) {
00407         c[k] *= c_solvent;
00408       }
00409     } else {
00410       getActivities(c);
00411       for (int k = 0; k < m_kk; k++) {
00412         double c0 = standardConcentration(k);
00413         c[k] *= c0;
00414       }
00415     }
00416   }
00417 
00418   /*
00419    * The standard concentration \f$ C^s_k \f$ used to normalize
00420    * the activity concentration. In many cases, this quantity
00421    * will be the same for all species in a phase - for example,
00422    * for an ideal gas \f$ C^s_k = P/\hat R T \f$. For this
00423    * reason, this method returns a single value, instead of an
00424    * array.  However, for phases in which the standard
00425    * concentration is species-specific (e.g. surface species of
00426    * different sizes), this method may be called with an
00427    * optional parameter indicating the species.
00428    *
00429    */
00430   doublereal IdealMolalSoln::standardConcentration(int k) const {
00431     double c0 = 1.0, mvSolvent;
00432     switch (m_formGC) {
00433     case 0:
00434       break;
00435     case 1:
00436       c0 = 1.0 /m_speciesMolarVolume[m_indexSolvent];
00437       break;
00438     case 2:
00439       mvSolvent = m_speciesMolarVolume[m_indexSolvent];
00440       c0 = 1.0 / mvSolvent;
00441       break;
00442     }
00443     return c0;
00444   }
00445     
00446   /*
00447    * Returns the natural logarithm of the standard 
00448    * concentration of the kth species
00449    */
00450   doublereal IdealMolalSoln::logStandardConc(int k) const {
00451     double c0 = standardConcentration(k);
00452     return log(c0);
00453   }
00454     
00455   /*
00456    * Returns the units of the standard and general concentrations
00457    * Note they have the same units, as their divisor is 
00458    * defined to be equal to the activity of the kth species
00459    * in the solution, which is unitless.
00460    *
00461    * This routine is used in print out applications where the
00462    * units are needed. Usually, MKS units are assumed throughout
00463    * the program and in the XML input files. 
00464    *
00465    * On return uA contains the powers of the units (MKS assumed)
00466    * of the standard concentrations and generalized concentrations
00467    * for the kth species.
00468    *
00469    *  uA[0] = kmol units - default  = 1
00470    *  uA[1] = m    units - default  = -nDim(), the number of spatial
00471    *                                dimensions in the Phase class.
00472    *  uA[2] = kg   units - default  = 0;
00473    *  uA[3] = Pa(pressure) units - default = 0;
00474    *  uA[4] = Temperature units - default = 0;
00475    *  uA[5] = time units - default = 0
00476    */
00477   void IdealMolalSoln::getUnitsStandardConc(double *uA, int k, int sizeUA) const {
00478     int eos = eosType();
00479     if (eos == 0) {
00480       for (int i = 0; i < sizeUA; i++) {
00481         uA[i] = 0.0;
00482       }
00483     } else {
00484       for (int i = 0; i < sizeUA; i++) {
00485         if (i == 0) uA[0] = 1.0;
00486         if (i == 1) uA[1] = -nDim();
00487         if (i == 2) uA[2] = 0.0;
00488         if (i == 3) uA[3] = 0.0;
00489         if (i == 4) uA[4] = 0.0;
00490         if (i == 5) uA[5] = 0.0;
00491       }
00492     }
00493   }
00494 
00495   /*
00496    * Get the array of non-dimensional molality-based
00497    * activities at the current solution temperature, 
00498    * pressure, and solution concentration.
00499    *
00500    *  The max against xmolSolventMIN is to limit the activity
00501    *  coefficient to be finite as the solvent mf goes to zero.
00502    */
00503   void IdealMolalSoln::getActivities(doublereal* ac) const {  
00504     _updateStandardStateThermo();
00505     /*
00506      * Update the molality array, m_molalities()
00507      *   This requires an update due to mole fractions
00508      */
00509     if (IMS_typeCutoff_ == 0) {
00510       calcMolalities();
00511       for (int k = 0; k < m_kk; k++) {
00512         ac[k] = m_molalities[k];
00513       }
00514       double xmolSolvent = moleFraction(m_indexSolvent);
00515       xmolSolvent = fmaxx(m_xmolSolventMIN, xmolSolvent);
00516       ac[m_indexSolvent] = 
00517         exp((xmolSolvent - 1.0)/xmolSolvent);
00518     } else {
00519 
00520       s_updateIMS_lnMolalityActCoeff();
00521       /*
00522        * Now calculate the array of activities.
00523        */
00524       for (int k = 1; k < m_kk; k++) {
00525           ac[k] = m_molalities[k] * exp(IMS_lnActCoeffMolal_[k]);
00526       }
00527       double xmolSolvent = moleFraction(m_indexSolvent);
00528       ac[m_indexSolvent] =
00529       exp(IMS_lnActCoeffMolal_[m_indexSolvent]) * xmolSolvent;
00530      
00531     }
00532   }
00533 
00534   /*
00535    * Get the array of non-dimensional Molality based
00536    * activity coefficients at
00537    * the current solution temperature, pressure, and
00538    * solution concentration.
00539    * See Denbigh
00540    * (note solvent activity coefficient is on the molar scale).
00541    *
00542    *  The max against xmolSolventMIN is to limit the activity
00543    *  coefficient to be finite as the solvent mf goes to zero.
00544    */
00545   void IdealMolalSoln::
00546   getMolalityActivityCoefficients(doublereal* acMolality) const {
00547     if (IMS_typeCutoff_ == 0) {
00548       for (int k = 0; k < m_kk; k++) {
00549         acMolality[k] = 1.0;
00550       }
00551       double xmolSolvent = moleFraction(m_indexSolvent);
00552       xmolSolvent = fmaxx(m_xmolSolventMIN, xmolSolvent);
00553       acMolality[m_indexSolvent] = 
00554         exp((xmolSolvent - 1.0)/xmolSolvent) / xmolSolvent;
00555     } else {
00556       s_updateIMS_lnMolalityActCoeff();
00557       std::copy(IMS_lnActCoeffMolal_.begin(), IMS_lnActCoeffMolal_.end(), acMolality);
00558       for (int k = 0; k < m_kk; k++) {
00559         acMolality[k] = exp(acMolality[k]);
00560       }
00561     }
00562   }
00563 
00564   //
00565   // ------ Partial Molar Properties of the Solution -----------------
00566   //
00567 
00568   /*
00569    * Get the species chemical potentials: Units: J/kmol.
00570    *
00571    * This function returns a vector of chemical potentials of the 
00572    * species in solution.
00573    *
00574    * \f[
00575    *    \mu_k = \mu^{o}_k(T,P) + R T \ln(\frac{m_k}{m^\Delta})
00576    * \f]
00577    * \f[
00578    *    \mu_w = \mu^{o}_w(T,P) +
00579    *            R T ((X_w - 1.0) / X_w)
00580    * \f]
00581    *
00582    * \f$ w \f$ refers to the solvent species. 
00583    * \f$ X_w \f$ is the mole fraction of the solvent.
00584    * \f$ m_k \f$ is the molality of the kth solute.
00585    * \f$ m^\Delta is 1 gmol solute per kg solvent. \f$
00586    *
00587    * Units: J/kmol.
00588    */
00589   void IdealMolalSoln::getChemPotentials(doublereal* mu) const{
00590     double xx;
00591     //const double xxSmall = 1.0E-150; 
00592 
00593     // Assertion is made for speed
00594     AssertThrow(m_indexSolvent == 0, "solvent not the first species");
00595   
00596     /*
00597      * First get the standard chemical potentials
00598      *  -> this requires updates of standard state as a function
00599      *     of T and P
00600      * These are defined at unit molality.
00601      */
00602     getStandardChemPotentials(mu);
00603     /*
00604      * Update the molality array, m_molalities()
00605      *   This requires an update due to mole fractions
00606      */
00607     calcMolalities();
00608     /*
00609      * get the solvent mole fraction
00610      */
00611     double xmolSolvent = moleFraction(m_indexSolvent);
00612     /*
00613      *   
00614      */
00615     doublereal RT = GasConstant * temperature();
00616 
00617     if (IMS_typeCutoff_ == 0 || xmolSolvent > 3.* IMS_X_o_cutoff_/2.0) {
00618   
00619       for (int k = 1; k < m_kk; k++) {
00620         xx = fmaxx(m_molalities[k], xxSmall);
00621         mu[k] += RT * log(xx);
00622       }
00623       /*
00624        * Do the solvent 
00625        *  -> see my notes
00626        */
00627    
00628       xx = fmaxx(xmolSolvent, xxSmall);
00629       mu[m_indexSolvent] += 
00630         (RT * (xmolSolvent - 1.0) / xx);
00631     } else {
00632       /*
00633        * Update the activity coefficients
00634        * This also updates the internal molality array.
00635        */
00636       s_updateIMS_lnMolalityActCoeff();
00637 
00638 
00639       for (int k = 1; k < m_kk; k++) {
00640         xx = MAX(m_molalities[k], xxSmall);
00641         mu[k] += RT * (log(xx) + IMS_lnActCoeffMolal_[k]);
00642       }
00643       xx = MAX(xmolSolvent, xxSmall);
00644       mu[m_indexSolvent] +=
00645         RT * (log(xx) + IMS_lnActCoeffMolal_[m_indexSolvent]);
00646     }
00647 
00648   }
00649 
00650   /*
00651    * Returns an array of partial molar enthalpies for the species
00652    * in the mixture: Units (J/kmol).
00653    *
00654    */
00655   void IdealMolalSoln::getPartialMolarEnthalpies(doublereal* hbar) const {
00656     getEnthalpy_RT(hbar);
00657     doublereal RT = _RT();
00658     for (int k = 0; k < m_kk; k++) {
00659       hbar[k] *= RT;
00660     }
00661   }
00662 
00663   /*
00664    * Returns an array of partial molar entropies of the species in the
00665    * solution: Units: J/kmol.
00666    *
00667    * Maxwell's equations provide an insight in how to calculate this
00668    * (p.215 Smith and Van Ness)
00669    * \f[
00670    *      \frac{d(\mu_k)}{dT} = -\bar{s}_i
00671    * \f]
00672    * For this phase, the partial molar entropies are equal to the
00673    * standard state species entropies plus the ideal molal solution contribution.
00674    * 
00675    * \f[
00676    *   \bar{s}_k(T,P) =  s^0_k(T) - R log( m_k )
00677    * \f]
00678    * \f[
00679    *   \bar{s}_w(T,P) =  s^0_w(T) - R ((X_w - 1.0) / X_w)
00680    * \f]
00681    *
00682    * The subscript, w, refers to the solvent species. \f$ X_w \f$ is
00683    * the mole fraction of solvent.
00684    * The reference-state pure-species entropies,\f$ s^0_k(T) \f$,
00685    * at the reference pressure, \f$ P_{ref} \f$, are computed by the
00686    * species thermodynamic
00687    * property manager. They are polynomial functions of temperature.
00688    * @see SpeciesThermo
00689    */
00690   void IdealMolalSoln::getPartialMolarEntropies(doublereal* sbar) const {
00691     getEntropy_R(sbar);
00692     doublereal R = GasConstant;
00693     doublereal mm;
00694     calcMolalities();
00695     if (IMS_typeCutoff_ == 0) {
00696       for (int k = 0; k < m_kk; k++) {
00697         if (k != m_indexSolvent) {
00698           mm = fmaxx(SmallNumber, m_molalities[k]);
00699           sbar[k] -= R * log(mm);
00700         }
00701       }
00702       double xmolSolvent = moleFraction(m_indexSolvent);
00703       sbar[m_indexSolvent] -= (R * (xmolSolvent - 1.0) / xmolSolvent);
00704     } else {
00705       /*
00706        * Update the activity coefficients, This also update the
00707        * internally stored molalities.
00708        */
00709       s_updateIMS_lnMolalityActCoeff();
00710       /*
00711        * First we will add in the obvious dependence on the T
00712        * term out front of the log activity term
00713        */
00714       doublereal mm;
00715       for (int k = 0; k < m_kk; k++) {
00716         if (k != m_indexSolvent) {
00717           mm = fmaxx(SmallNumber, m_molalities[k]);
00718           sbar[k] -= R * (log(mm) + IMS_lnActCoeffMolal_[k]);
00719         }
00720       }
00721       double xmolSolvent = moleFraction(m_indexSolvent);
00722       mm = fmaxx(SmallNumber, xmolSolvent);
00723       sbar[m_indexSolvent] -= R *(log(mm) + IMS_lnActCoeffMolal_[m_indexSolvent]);
00724 
00725     }
00726   }
00727 
00728   /*
00729    * Returns an array of partial molar volumes of the species
00730    * in the solution: Units: m^3 kmol-1.
00731    *
00732    * For this solution, the partial molar volumes are equal to the
00733    * constant species molar volumes.
00734    *
00735    * Units: m^3 kmol-1.
00736    */
00737   void IdealMolalSoln::getPartialMolarVolumes(doublereal* vbar) const {
00738     getStandardVolumes(vbar);
00739   }
00740 
00741   /*
00742    * Partial molar heat capacity of the solution: Units: J/kmol/K.
00743    *
00744    *   The kth partial molar heat capacity is equal to 
00745    *   the temperature derivative of the partial molar
00746    *   enthalpy of the kth species in the solution at constant
00747    *   P and composition (p. 220 Smith and Van Ness).
00748    *  \f[
00749    *  \bar{Cp}_k(T,P) =  {Cp}^0_k(T) 
00750    * \f]
00751    *
00752    *   For this solution, this is equal to the reference state
00753    *   heat capacities.
00754    *
00755    *  Units: J/kmol/K
00756    */
00757   void IdealMolalSoln::getPartialMolarCp(doublereal* cpbar) const {
00758     /*
00759      * Get the nondimensional gibbs standard state of the
00760      * species at the T and P of the solution.
00761      */
00762     getCp_R(cpbar);
00763         
00764     for (int k = 0; k < m_kk; k++) {
00765       cpbar[k] *= GasConstant;
00766     }
00767   }
00768         
00769   /*
00770    * -------- Properties of the Standard State of the Species
00771    *           in the Solution ------------------
00772    */
00773 
00774 
00775 
00776   /*
00777    * ------ Thermodynamic Values for the Species Reference States ---
00778    */
00779 
00780   // -> This is handled by VPStandardStatesTP
00781 
00782   /*
00783    *  -------------- Utilities -------------------------------
00784    */
00785 
00786   /*
00787    *  Initialization routine for an IdealMolalSoln phase.
00788    *
00789    * This is a virtual routine. This routine will call initThermo()
00790    * for the parent class as well.
00791    */
00792   void IdealMolalSoln::initThermo() {
00793     initLengths();
00794     MolalityVPSSTP::initThermo();
00795   }
00796 
00797   /*
00798    * Initialization of an IdealMolalSoln phase using an
00799    * xml file
00800    *
00801    * This routine is a precursor to constructPhaseFile(XML_Node*)
00802    * routine, which does most of the work.
00803    *
00804    * @param inputFile XML file containing the description of the
00805    *        phase
00806    *
00807    * @param id  Optional parameter identifying the name of the
00808    *            phase. If none is given, the first XML
00809    *            phase element will be used.
00810    */
00811   void IdealMolalSoln::constructPhaseFile(std::string inputFile, 
00812                                           std::string id) {
00813 
00814     if (inputFile.size() == 0) {
00815       throw CanteraError("IdealMolalSoln::constructPhaseFile",
00816                          "input file is null");
00817     }
00818     std::string path = findInputFile(inputFile);
00819     std::ifstream fin(path.c_str());
00820     if (!fin) {
00821       throw CanteraError("IdealMolalSoln::constructPhaseFile",
00822                          "could not open "
00823                          +path+" for reading.");
00824     }
00825     /*
00826      * The phase object automatically constructs an XML object.
00827      * Use this object to store information.
00828      */
00829     XML_Node &phaseNode_XML = xml();
00830     XML_Node *fxml = new XML_Node();
00831     fxml->build(fin);
00832     XML_Node *fxml_phase = findXMLPhase(fxml, id);
00833     if (!fxml_phase) {
00834       throw CanteraError("IdealMolalSoln::constructPhaseFile",
00835                          "ERROR: Can not find phase named " +
00836                          id + " in file named " + inputFile);
00837     }
00838     fxml_phase->copy(&phaseNode_XML);
00839     constructPhaseXML(*fxml_phase, id);
00840     delete fxml;
00841   }
00842         
00843   /*
00844    *   Import and initialize an IdealMolalSoln phase 
00845    *   specification in an XML tree into the current object.
00846    *   Here we read an XML description of the phase.
00847    *   We import descriptions of the elements that make up the
00848    *   species in a phase.
00849    *   We import information about the species, including their
00850    *   reference state thermodynamic polynomials. We then freeze
00851    *   the state of the species.
00852    *
00853    *   Then, we read the species molar volumes from the xml 
00854    *   tree to finish the initialization.
00855    *
00856    * @param phaseNode This object must be the phase node of a
00857    *             complete XML tree
00858    *             description of the phase, including all of the
00859    *             species data. In other words while "phase" must
00860    *             point to an XML phase object, it must have
00861    *             sibling nodes "speciesData" that describe
00862    *             the species in the phase.
00863    * @param id   ID of the phase. If nonnull, a check is done
00864    *             to see if phaseNode is pointing to the phase
00865    *             with the correct id. 
00866    */
00867   void IdealMolalSoln::constructPhaseXML(XML_Node& phaseNode, 
00868                                          std::string id) {
00869     if (id.size() > 0) {
00870       std::string idp = phaseNode.id();
00871       if (idp != id) {
00872         throw CanteraError("IdealMolalSoln::constructPhaseXML", 
00873                            "phasenode and Id are incompatible");
00874       }
00875     }
00876 
00877     /*
00878      * Find the Thermo XML node 
00879      */
00880     if (!phaseNode.hasChild("thermo")) {
00881       throw CanteraError("IdealMolalSoln::constructPhaseXML",
00882                          "no thermo XML node");
00883     }
00884  
00885     /*
00886      * Call the Cantera importPhase() function. This will import
00887      * all of the species into the phase. Then, it will call
00888      * initThermoXML() below.
00889      */
00890     bool m_ok = importPhase(phaseNode, this);
00891     if (!m_ok) {
00892       throw CanteraError("IdealMolalSoln::constructPhaseXML","importPhase failed "); 
00893     }
00894   }
00895 
00896   /*
00897    *   Import and initialize an IdealMolalSoln phase 
00898    *   specification in an XML tree into the current object.
00899    *
00900    *   This routine is called from importPhase() to finish
00901    *   up the initialization of the thermo object. It reads in the
00902    *   species molar volumes.
00903    *
00904    * @param phaseNode This object must be the phase node of a
00905    *             complete XML tree
00906    *             description of the phase, including all of the
00907    *             species data. In other words while "phase" must
00908    *             point to an XML phase object, it must have
00909    *             sibling nodes "speciesData" that describe
00910    *             the species in the phase.
00911    * @param id   ID of the phase. If nonnull, a check is done
00912    *             to see if phaseNode is pointing to the phase
00913    *             with the correct id.
00914    */
00915   void IdealMolalSoln::initThermoXML(XML_Node& phaseNode, std::string id) {
00916 
00917     /*
00918      * Initialize the whole thermo object, using a virtual function.
00919      */
00920     initThermo();
00921 
00922     if (id.size() > 0) {
00923       std::string idp = phaseNode.id();
00924       if (idp != id) {
00925         throw CanteraError("IdealMolalSoln::initThermo", 
00926                            "phasenode and Id are incompatible");
00927       }
00928     }
00929 
00930     /*
00931      * Find the Thermo XML node 
00932      */
00933     if (!phaseNode.hasChild("thermo")) {
00934       throw CanteraError("IdealMolalSoln::initThermo",
00935                          "no thermo XML node");
00936     }
00937     XML_Node& thermoNode = phaseNode.child("thermo");
00938 
00939     /*
00940      * Possible change the form of the standard concentrations
00941      */
00942     if (thermoNode.hasChild("standardConc")) {
00943       XML_Node& scNode = thermoNode.child("standardConc");
00944       m_formGC = 2;
00945       std::string formString = scNode.attrib("model");
00946       if (formString != "") {
00947         if (formString == "unity") {
00948           m_formGC = 0;
00949         } else if (formString == "molar_volume") {
00950           m_formGC = 1;
00951         } else if (formString == "solvent_volume") {
00952           m_formGC = 2;
00953         } else {
00954           throw CanteraError("IdealMolalSoln::initThermo",
00955                              "Unknown standardConc model: " + formString);
00956         }
00957       }
00958     }
00959 
00960     /*
00961      * Get the Name of the Solvent:
00962      *      <solvent> solventName </solvent>
00963      */
00964     std::string solventName = "";
00965     if (thermoNode.hasChild("solvent")) {
00966       XML_Node& scNode = thermoNode.child("solvent");
00967       std::vector<std::string> nameSolventa;
00968       getStringArray(scNode, nameSolventa);
00969       int nsp = static_cast<int>(nameSolventa.size());
00970       if (nsp != 1) {
00971         throw CanteraError("IdealMolalSoln::initThermoXML",
00972                            "badly formed solvent XML node");
00973       }
00974       solventName = nameSolventa[0];
00975     }
00976 
00977     if (thermoNode.hasChild("activityCoefficients")) {
00978       XML_Node& acNode = thermoNode.child("activityCoefficients");
00979       std::string modelString = acNode.attrib("model");
00980       IMS_typeCutoff_ = 0;
00981       if (modelString != "IdealMolalSoln") {
00982         throw CanteraError("IdealMolalSoln::initThermoXML",
00983                            "unknown ActivityCoefficient model: " + modelString);
00984       }
00985       if (acNode.hasChild("idealMolalSolnCutoff")) {
00986         XML_Node& ccNode = acNode.child("idealMolalSolnCutoff");
00987         modelString = ccNode.attrib("model");
00988         if (modelString != "") {
00989           if (modelString == "polyExp") {
00990             IMS_typeCutoff_ = 2;
00991           } else if (modelString == "poly") {
00992             IMS_typeCutoff_ = 1;
00993           } else {
00994             throw CanteraError("IdealMolalSoln::initThermoXML",
00995                                "Unknown  idealMolalSolnCutoff form: " + modelString);
00996           }
00997 
00998           if (ccNode.hasChild("gamma_o_limit")) {
00999             IMS_gamma_o_min_ = getFloat(ccNode, "gamma_o_limit");
01000           }       
01001           if (ccNode.hasChild("gamma_k_limit")) {
01002             IMS_gamma_k_min_ = getFloat(ccNode, "gamma_k_limit");
01003           }
01004           if (ccNode.hasChild("X_o_cutoff")) {
01005             IMS_X_o_cutoff_ = getFloat(ccNode, "X_o_cutoff");
01006           }
01007           if (ccNode.hasChild("c_0_param")) {
01008             IMS_cCut_ = getFloat(ccNode, "c_0_param");
01009           }
01010           if (ccNode.hasChild("slope_f_limit")) {
01011             IMS_slopefCut_ = getFloat(ccNode, "slope_f_limit");
01012           }
01013           if (ccNode.hasChild("slope_g_limit")) {
01014             IMS_slopegCut_ = getFloat(ccNode, "slope_g_limit");
01015           }
01016 
01017         }
01018       }
01019     }
01020 
01021 
01022     /*
01023      * Reconcile the solvent name and index.
01024      */
01025     for (int k = 0; k < m_kk; k++) {
01026       std::string sname = speciesName(k);
01027       if (solventName == sname) {
01028         m_indexSolvent = k;
01029         break;
01030       }
01031     }
01032     if (m_indexSolvent == -1) {
01033       std::cout << "IdealMolalSoln::initThermo: Solvent Name not found" 
01034            << std::endl;
01035       throw CanteraError("IdealMolalSoln::initThermo",
01036                          "Solvent name not found");
01037     }
01038     if (m_indexSolvent != 0) {
01039       throw CanteraError("IdealMolalSoln::initThermo",
01040                          "Solvent " + solventName +
01041                          " should be first species");
01042     }
01043 
01044 
01045     /*
01046      * Now go get the molar volumes
01047      */
01048     XML_Node& speciesList = phaseNode.child("speciesArray");
01049     XML_Node* speciesDB =
01050       get_XML_NameID("speciesData", speciesList["datasrc"],
01051                      &phaseNode.root());
01052     const std::vector<std::string> &sss = speciesNames();
01053 
01054     for (int k = 0; k < m_kk; k++) {
01055       XML_Node* s =  speciesDB->findByAttr("name", sss[k]);
01056       XML_Node *ss = s->findByName("standardState");
01057       m_speciesMolarVolume[k] = getFloat(*ss, "molarVolume", "toSI");
01058     }
01059 
01060     IMS_typeCutoff_ = 2;
01061     if (IMS_typeCutoff_ == 2) {
01062       calcIMSCutoffParams_();
01063     }
01064 
01065     MolalityVPSSTP::initThermoXML(phaseNode, id);
01066 
01067 
01068     setMoleFSolventMin(1.0E-5);
01069     /*
01070      * Set the state
01071      */
01072     if (phaseNode.hasChild("state")) {
01073       XML_Node& stateNode = phaseNode.child("state");
01074       setStateFromXML(stateNode);
01075     }
01076     
01077   }
01078 
01079   /*
01080    * @internal
01081    * Set equation of state parameters. The number and meaning of
01082    * these depends on the subclass. 
01083    * @param n number of parameters
01084    * @param c array of \i n coefficients
01085    * 
01086    */
01087   void IdealMolalSoln::setParameters(int n, doublereal* const c) {
01088   }
01089 
01090   void IdealMolalSoln::getParameters(int &n, doublereal * const c) const {
01091   }
01092 
01093   /*
01094    * Set equation of state parameter values from XML
01095    * entries. This method is called by function importPhase in
01096    * file importCTML.cpp when processing a phase definition in
01097    * an input file. It should be overloaded in subclasses to set
01098    * any parameters that are specific to that particular phase
01099    * model.
01100    *
01101    * @param eosdata An XML_Node object corresponding to
01102    * the "thermo" entry for this phase in the input file.
01103    *
01104    * HKM -> Right now, the parameters are set elsewhere (initThermo)
01105    *        It just didn't seem to fit.
01106    */
01107   void IdealMolalSoln::setParametersFromXML(const XML_Node& eosdata) {
01108   }
01109 
01110   /*
01111    * ----------- Critical State Properties --------------------------
01112    */
01113 
01114   /*
01115    * ------------ Private and Restricted Functions ------------------
01116    */
01117 
01118   /*
01119    * Bail out of functions with an error exit if they are not
01120    * implemented.
01121    */
01122   doublereal IdealMolalSoln::err(std::string msg) const {
01123     throw CanteraError("IdealMolalSoln",
01124                        "Unfinished func called: " + msg );
01125     return 0.0;
01126   }
01127 
01128   
01129 
01130   // This function will be called to update the internally storred
01131   // natural logarithm of the molality activity coefficients
01132   /*
01133    * Normally they are all one. However, sometimes they are not,
01134    * due to stability schemes
01135    *
01136    *    gamma_k_molar =  gamma_k_molal / Xmol_solvent
01137    *
01138    *    gamma_o_molar = gamma_o_molal
01139    */
01140   void  IdealMolalSoln::s_updateIMS_lnMolalityActCoeff() const {
01141     int k;
01142     double tmp;
01143     /*
01144      * Calculate the molalities. Currently, the molalities
01145      * may not be current with respect to the contents of the
01146      * State objects' data.
01147      */
01148     calcMolalities();
01149 
01150     double xmolSolvent = moleFraction(m_indexSolvent);
01151     double xx = MAX(m_xmolSolventMIN, xmolSolvent);
01152 
01153     if (IMS_typeCutoff_ == 0) {
01154       for (k = 1; k < m_kk; k++) {
01155         IMS_lnActCoeffMolal_[k]= 0.0;
01156       }
01157       IMS_lnActCoeffMolal_[m_indexSolvent] = - log(xx) + (xx - 1.0)/xx;
01158       return;
01159     } else if (IMS_typeCutoff_ == 1) {
01160       if (xmolSolvent > 3.0 * IMS_X_o_cutoff_/2.0 ) {
01161         for (k = 1; k < m_kk; k++) {
01162           IMS_lnActCoeffMolal_[k]= 0.0;
01163         }
01164         IMS_lnActCoeffMolal_[m_indexSolvent] = - log(xx) + (xx - 1.0)/xx;
01165         return;
01166       } else if  (xmolSolvent < IMS_X_o_cutoff_/2.0) {  
01167         tmp = log(xx * IMS_gamma_k_min_);
01168         for (k = 1; k < m_kk; k++) {
01169           IMS_lnActCoeffMolal_[k]= tmp;
01170         }
01171         IMS_lnActCoeffMolal_[m_indexSolvent] = log(IMS_gamma_o_min_);
01172         return;
01173       } else {
01174       
01175         /*
01176          * If we are in the middle region, calculate the connecting polynomials
01177          */
01178         double xminus  = xmolSolvent - IMS_X_o_cutoff_/2.0;
01179         double xminus2 = xminus * xminus;
01180         double xminus3 = xminus2 * xminus;
01181         double x_o_cut2 = IMS_X_o_cutoff_ * IMS_X_o_cutoff_;
01182         double x_o_cut3 =  x_o_cut2 * IMS_X_o_cutoff_;
01183     
01184         double h2 = 3.5 * xminus2 /  IMS_X_o_cutoff_ - 2.0 * xminus3 / x_o_cut2;    
01185         double h2_prime = 7.0 * xminus /  IMS_X_o_cutoff_ - 6.0 * xminus2 /  x_o_cut2;
01186 
01187         double h1 =   (1.0 - 3.0 * xminus2 /  x_o_cut2 + 2.0 *  xminus3/ x_o_cut3);
01188         double h1_prime = (- 6.0 * xminus /  x_o_cut2 + 6.0 *  xminus2/ x_o_cut3);
01189 
01190         double h1_g = h1 / IMS_gamma_o_min_;
01191         double h1_g_prime  = h1_prime / IMS_gamma_o_min_;
01192 
01193         double alpha = 1.0 / ( exp(1.0) * IMS_gamma_k_min_);
01194         double h1_f = h1 * alpha;
01195         double h1_f_prime  = h1_prime * alpha;
01196 
01197         double f = h2 + h1_f;
01198         double f_prime = h2_prime + h1_f_prime;
01199 
01200         double g = h2 + h1_g;
01201         double g_prime = h2_prime + h1_g_prime;
01202 
01203         tmp = (xmolSolvent/ g * g_prime + (1.0-xmolSolvent) / f * f_prime);
01204         double lngammak = -1.0 - log(f) + tmp * xmolSolvent;
01205         double lngammao =-log(g) - tmp * (1.0-xmolSolvent);
01206       
01207         tmp = log(xmolSolvent) + lngammak;
01208         for (k = 1; k < m_kk; k++) {
01209           IMS_lnActCoeffMolal_[k]= tmp;
01210         }
01211         IMS_lnActCoeffMolal_[m_indexSolvent] = lngammao;
01212       }
01213     } 
01214 
01215     // Exponentials - trial 2
01216     else if (IMS_typeCutoff_ == 2) {
01217       if (xmolSolvent > IMS_X_o_cutoff_) {
01218         for (k = 1; k < m_kk; k++) {
01219           IMS_lnActCoeffMolal_[k]= 0.0;
01220         }
01221         IMS_lnActCoeffMolal_[m_indexSolvent] = - log(xx) + (xx - 1.0)/xx;
01222         return;
01223       } else {
01224      
01225         double xoverc = xmolSolvent/IMS_cCut_;
01226         double eterm = std::exp(-xoverc);
01227        
01228         double fptmp = IMS_bfCut_  - IMS_afCut_ / IMS_cCut_ - IMS_bfCut_*xoverc
01229           + 2.0*IMS_dfCut_*xmolSolvent - IMS_dfCut_*xmolSolvent*xoverc;
01230         double f_prime = 1.0 + eterm*fptmp;     
01231         double f = xmolSolvent + IMS_efCut_ + eterm * (IMS_afCut_ + xmolSolvent * (IMS_bfCut_ + IMS_dfCut_*xmolSolvent));
01232 
01233         double gptmp = IMS_bgCut_  - IMS_agCut_ / IMS_cCut_ - IMS_bgCut_*xoverc
01234           + 2.0*IMS_dgCut_*xmolSolvent - IMS_dgCut_*xmolSolvent*xoverc;
01235         double g_prime = 1.0 + eterm*gptmp;
01236         double g = xmolSolvent + IMS_egCut_ + eterm * (IMS_agCut_ + xmolSolvent * (IMS_bgCut_ + IMS_dgCut_*xmolSolvent));
01237 
01238         tmp = (xmolSolvent / g * g_prime + (1.0 - xmolSolvent) / f * f_prime);
01239         double lngammak = -1.0 - log(f) + tmp * xmolSolvent;
01240         double lngammao =-log(g) - tmp * (1.0-xmolSolvent);
01241       
01242         tmp = log(xx) + lngammak;
01243         for (k = 1; k < m_kk; k++) {
01244           IMS_lnActCoeffMolal_[k]= tmp;
01245         }
01246         IMS_lnActCoeffMolal_[m_indexSolvent] = lngammao;
01247       }
01248     }
01249     return;
01250   }
01251 
01252   /*
01253    * This internal function adjusts the lengths of arrays.
01254    *
01255    * This function is not virtual nor is it inherited
01256    */
01257   void IdealMolalSoln::initLengths() {
01258     m_kk = nSpecies();
01259     /*
01260      * Obtain the limits of the temperature from the species
01261      * thermo handler's limits.
01262      */
01263     int leng = m_kk;
01264     m_expg0_RT.resize(leng);
01265     m_pe.resize(leng, 0.0);
01266     m_pp.resize(leng);
01267     m_speciesMolarVolume.resize(leng);
01268     m_tmpV.resize(leng);
01269     IMS_lnActCoeffMolal_.resize(leng);
01270   }
01271 
01272 
01273   void  IdealMolalSoln::calcIMSCutoffParams_() {
01274     IMS_afCut_ = 1.0 / (std::exp(1.0) *  IMS_gamma_k_min_);
01275     IMS_efCut_ = 0.0;
01276     bool converged = false;
01277     double oldV = 0.0;
01278     int its;
01279     for (its = 0; its < 100 && !converged; its++) {
01280       oldV = IMS_efCut_;
01281       IMS_afCut_ = 1.0 / (std::exp(1.0) * IMS_gamma_k_min_)  - IMS_efCut_;
01282       IMS_bfCut_ = IMS_afCut_ / IMS_cCut_ + IMS_slopefCut_ - 1.0;
01283       IMS_dfCut_ = ((- IMS_afCut_/IMS_cCut_ + IMS_bfCut_ - IMS_bfCut_*IMS_X_o_cutoff_/IMS_cCut_) 
01284                     /
01285                 (IMS_X_o_cutoff_*IMS_X_o_cutoff_/IMS_cCut_ - 2.0 * IMS_X_o_cutoff_));
01286       double tmp = IMS_afCut_ + IMS_X_o_cutoff_*( IMS_bfCut_ + IMS_dfCut_ * IMS_X_o_cutoff_);
01287       double eterm = std::exp(-IMS_X_o_cutoff_/IMS_cCut_);
01288       IMS_efCut_ = - eterm * (tmp);
01289       if (fabs(IMS_efCut_ - oldV) < 1.0E-14) {
01290         converged = true;
01291       } 
01292     }
01293     if (!converged) {
01294       throw CanteraError(" IdealMolalSoln::calcCutoffParams_()",
01295                          " failed to converge on the f polynomial");
01296     }
01297     converged = false;
01298     double f_0 = IMS_afCut_ + IMS_efCut_;
01299     double f_prime_0 = 1.0 - IMS_afCut_ / IMS_cCut_ + IMS_bfCut_;
01300     IMS_egCut_ = 0.0;
01301     for (its = 0; its < 100 && !converged; its++) {
01302       oldV = IMS_egCut_;
01303       double lng_0 = -log(IMS_gamma_o_min_) -  f_prime_0 / f_0;
01304       IMS_agCut_ = exp(lng_0) - IMS_egCut_;
01305       IMS_bgCut_ = IMS_agCut_ / IMS_cCut_ + IMS_slopegCut_ - 1.0;
01306       IMS_dgCut_ = ((- IMS_agCut_/IMS_cCut_ + IMS_bgCut_ - IMS_bgCut_*IMS_X_o_cutoff_/IMS_cCut_) 
01307                     / 
01308                     (IMS_X_o_cutoff_*IMS_X_o_cutoff_/IMS_cCut_ - 2.0 * IMS_X_o_cutoff_));
01309       double tmp = IMS_agCut_ + IMS_X_o_cutoff_*( IMS_bgCut_ + IMS_dgCut_ *IMS_X_o_cutoff_);
01310       double eterm = std::exp(-IMS_X_o_cutoff_/IMS_cCut_);
01311       IMS_egCut_ = - eterm * (tmp);
01312       if (fabs(IMS_egCut_ - oldV) < 1.0E-14) {
01313         converged = true;
01314       } 
01315     }
01316     if (!converged) {
01317       throw CanteraError(" IdealMolalSoln::calcCutoffParams_()",
01318                          " failed to converge on the g polynomial");
01319     }
01320   }
01321  
01322 }
01323 
Generated by  doxygen 1.6.3