DebyeHuckel.cpp

Go to the documentation of this file.
00001 /**
00002  *  @file DebyeHuckel.cpp
00003  *    Declarations for the %DebyeHuckel ThermoPhase object, which models dilute
00004  *    electrolyte solutions
00005  *    (see \ref thermoprops and \link Cantera::DebyeHuckel DebyeHuckel \endlink).
00006  *
00007  * Class %DebyeHuckel represents a dilute liquid electrolyte phase which
00008  * obeys the Debye Huckel formulation for nonideality.
00009  */
00010 /*
00011  * Copywrite (2006) Sandia Corporation. Under the terms of 
00012  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
00013  * U.S. Government retains certain rights in this software.
00014  */
00015 /*
00016  * $Id: DebyeHuckel.cpp 279 2009-12-05 19:08:43Z hkmoffa $
00017  */
00018 //! Max function
00019 #ifndef MAX
00020 #define MAX(x,y)    (( (x) > (y) ) ? (x) : (y))
00021 #endif
00022 
00023 #include "DebyeHuckel.h"
00024 #include "ThermoFactory.h"
00025 #include "WaterProps.h"
00026 #include "PDSS_Water.h"
00027 #include <cstring>
00028 #include <cstdlib>
00029 
00030 using namespace std;
00031 
00032 namespace Cantera {
00033 
00034   /*
00035    * Default constructor
00036    */
00037   DebyeHuckel::DebyeHuckel() :
00038     MolalityVPSSTP(),
00039     m_formDH(DHFORM_DILUTE_LIMIT),
00040     m_formGC(2),
00041     m_IionicMolality(0.0),
00042     m_maxIionicStrength(30.0),
00043     m_useHelgesonFixedForm(false),
00044     m_IionicMolalityStoich(0.0),
00045     m_form_A_Debye(A_DEBYE_CONST),
00046     m_A_Debye(1.172576),   // units = sqrt(kg/gmol)
00047     m_B_Debye(3.28640E9),   // units = sqrt(kg/gmol) / m
00048     m_waterSS(0),
00049     m_densWaterSS(1000.),
00050     m_waterProps(0)
00051   {
00052  
00053     m_npActCoeff.resize(3);
00054     m_npActCoeff[0] = 0.1127;
00055     m_npActCoeff[1] = -0.01049;
00056     m_npActCoeff[2] = 1.545E-3;
00057   }
00058 
00059   /*
00060    * Working constructors
00061    *
00062    *  The two constructors below are the normal way
00063    *  the phase initializes itself. They are shells that call
00064    *  the routine initThermo(), with a reference to the
00065    *  XML database to get the info for the phase.
00066    */
00067   DebyeHuckel::DebyeHuckel(std::string inputFile, std::string id) :
00068     MolalityVPSSTP(),
00069     m_formDH(DHFORM_DILUTE_LIMIT),
00070     m_formGC(2),
00071     m_IionicMolality(0.0),
00072     m_maxIionicStrength(30.0),
00073     m_useHelgesonFixedForm(false),
00074     m_IionicMolalityStoich(0.0),
00075     m_form_A_Debye(A_DEBYE_CONST),
00076     m_A_Debye(1.172576),   // units = sqrt(kg/gmol)
00077     m_B_Debye(3.28640E9),  // units = sqrt(kg/gmol) / m
00078     m_waterSS(0),
00079     m_densWaterSS(1000.),
00080     m_waterProps(0)
00081   {
00082     m_npActCoeff.resize(3);
00083     m_npActCoeff[0] = 0.1127;
00084     m_npActCoeff[1] = -0.01049;
00085     m_npActCoeff[2] = 1.545E-3;
00086     constructPhaseFile(inputFile, id); 
00087   }
00088 
00089   DebyeHuckel::DebyeHuckel(XML_Node& phaseRoot, std::string id) :
00090     MolalityVPSSTP(),
00091     m_formDH(DHFORM_DILUTE_LIMIT),
00092     m_formGC(2),
00093     m_IionicMolality(0.0),
00094     m_maxIionicStrength(3.0),
00095     m_useHelgesonFixedForm(false),
00096     m_IionicMolalityStoich(0.0),
00097     m_form_A_Debye(A_DEBYE_CONST),
00098     m_A_Debye(1.172576),   // units = sqrt(kg/gmol)
00099     m_B_Debye(3.28640E9),   // units = sqrt(kg/gmol) / m
00100     m_waterSS(0),
00101     m_densWaterSS(1000.),
00102     m_waterProps(0)
00103   {
00104     m_npActCoeff.resize(3);
00105     m_npActCoeff[0] = 0.1127;
00106     m_npActCoeff[1] = -0.01049;
00107     m_npActCoeff[2] = 1.545E-3;
00108     constructPhaseXML(phaseRoot, id);   
00109   }
00110  
00111   /*
00112    * Copy Constructor:
00113    *
00114    *  Note this stuff will not work until the underlying phase
00115    *  has a working copy constructor
00116    */
00117   DebyeHuckel::DebyeHuckel(const DebyeHuckel &b) :
00118     MolalityVPSSTP(),
00119     m_formDH(DHFORM_DILUTE_LIMIT),
00120     m_formGC(2),
00121     m_IionicMolality(0.0),
00122     m_maxIionicStrength(30.0),
00123     m_useHelgesonFixedForm(false),
00124     m_IionicMolalityStoich(0.0),
00125     m_form_A_Debye(A_DEBYE_CONST),
00126     m_A_Debye(1.172576),   // units = sqrt(kg/gmol)
00127     m_B_Debye(3.28640E9),   // units = sqrt(kg/gmol) / m
00128     m_waterSS(0),
00129     m_densWaterSS(1000.),
00130     m_waterProps(0)
00131   {
00132     /*
00133      * Use the assignment operator to do the brunt
00134      * of the work for the copy construtor.
00135      */
00136     *this = b;
00137   }
00138 
00139   /*
00140    * operator=()
00141    *
00142    *  Note this stuff will not work until the underlying phase
00143    *  has a working assignment operator
00144    */
00145   DebyeHuckel& DebyeHuckel::
00146   operator=(const DebyeHuckel &b) {
00147     if (&b != this) {
00148       MolalityVPSSTP::operator=(b);
00149       m_formDH              = b.m_formDH;
00150       m_formGC              = b.m_formGC;
00151       m_Aionic              = b.m_Aionic;
00152       m_npActCoeff          = b.m_npActCoeff;
00153       m_IionicMolality      = b.m_IionicMolality;
00154       m_maxIionicStrength   = b.m_maxIionicStrength;
00155       m_useHelgesonFixedForm= b.m_useHelgesonFixedForm;
00156       m_IionicMolalityStoich= b.m_IionicMolalityStoich;
00157       m_form_A_Debye        = b.m_form_A_Debye;
00158       m_A_Debye             = b.m_A_Debye;
00159       m_B_Debye             = b.m_B_Debye;
00160       m_B_Dot               = b.m_B_Dot;
00161       m_npActCoeff          = b.m_npActCoeff;
00162     
00163       // This is an internal shallow copy of the PDSS_Water pointer
00164       m_waterSS = dynamic_cast<PDSS_Water *>(providePDSS(0)) ;
00165       if (!m_waterSS) {
00166         throw CanteraError("DebyHuckel::operator=()", "Dynamic cast to waterPDSS failed");
00167       }
00168    
00169       m_densWaterSS         = b.m_densWaterSS;
00170 
00171       if (m_waterProps) {
00172         delete m_waterProps;
00173         m_waterProps = 0;
00174       }
00175       if (b.m_waterProps) {
00176         m_waterProps = new WaterProps(m_waterSS);
00177       }
00178 
00179       m_expg0_RT            = b.m_expg0_RT;
00180       m_pe                  = b.m_pe;
00181       m_pp                  = b.m_pp;
00182       m_tmpV                = b.m_tmpV;
00183       m_speciesCharge_Stoich= b.m_speciesCharge_Stoich;
00184       m_Beta_ij             = b.m_Beta_ij;
00185       m_lnActCoeffMolal     = b.m_lnActCoeffMolal;
00186       m_d2lnActCoeffMolaldT2= b.m_d2lnActCoeffMolaldT2;
00187     }
00188     return *this;
00189   }
00190 
00191 
00192   /*
00193    * ~DebyeHuckel():   (virtual)
00194    *
00195    *   Destructor for DebyeHuckel. Release objects that
00196    * it owns.
00197    */
00198   DebyeHuckel::~DebyeHuckel() {
00199     if (m_waterProps) {
00200       delete m_waterProps; m_waterProps = 0;
00201     }
00202   }
00203 
00204   /*
00205    *  duplMyselfAsThermoPhase():
00206    *
00207    *  This routine operates at the ThermoPhase level to 
00208    *  duplicate the current object. It uses the copy constructor
00209    *  defined above.
00210    */
00211   ThermoPhase* DebyeHuckel::duplMyselfAsThermoPhase() const {
00212     DebyeHuckel* mtp = new DebyeHuckel(*this);
00213     return (ThermoPhase *) mtp;
00214   }
00215 
00216   /*
00217    * Equation of state type flag. The base class returns
00218    * zero. Subclasses should define this to return a unique
00219    * non-zero value. Constants defined for this purpose are
00220    * listed in mix_defs.h.
00221    */
00222   int DebyeHuckel::eosType() const {
00223     int res;
00224     switch (m_formGC) {
00225     case 0:
00226       res = cDebyeHuckel0;
00227       break;
00228     case 1:
00229       res = cDebyeHuckel1;
00230       break;
00231     case 2:
00232       res = cDebyeHuckel2;
00233       break;
00234     default:
00235       throw CanteraError("eosType", "Unknown type");
00236       break;
00237     }
00238     return res;
00239   }
00240 
00241   //
00242   // -------- Molar Thermodynamic Properties of the Solution --------------- 
00243   //
00244   /*
00245    * Molar enthalpy of the solution. Units: J/kmol.
00246    */
00247   doublereal DebyeHuckel::enthalpy_mole() const {
00248     getPartialMolarEnthalpies(DATA_PTR(m_tmpV));
00249     return mean_X(DATA_PTR(m_tmpV));
00250   }
00251 
00252   /*
00253    * Molar internal energy of the solution. Units: J/kmol.
00254    *
00255    * This is calculated from the soln enthalpy and then
00256    * subtracting pV.
00257    */
00258   doublereal DebyeHuckel::intEnergy_mole() const {
00259     double hh = enthalpy_mole();
00260     double pres = pressure();
00261     double molarV = 1.0/molarDensity();
00262     double uu = hh - pres * molarV;
00263     return uu;
00264   }
00265 
00266   /*
00267    *  Molar soln entropy at constant pressure. Units: J/kmol/K. 
00268    *
00269    *  This is calculated from the partial molar entropies.
00270    */
00271   doublereal DebyeHuckel::entropy_mole() const {
00272     getPartialMolarEntropies(DATA_PTR(m_tmpV));
00273     return mean_X(DATA_PTR(m_tmpV));
00274   }
00275 
00276   // Molar Gibbs function. Units: J/kmol. 
00277   doublereal DebyeHuckel::gibbs_mole() const {
00278     getChemPotentials(DATA_PTR(m_tmpV));
00279     return mean_X(DATA_PTR(m_tmpV));
00280   }
00281 
00282   /*
00283    * Molar heat capacity at constant pressure. Units: J/kmol/K. 
00284    *
00285    * Returns the solution heat capacition at constant pressure.
00286    * This is calculated from the partial molar heat capacities.
00287    */
00288   doublereal DebyeHuckel::cp_mole() const {
00289     getPartialMolarCp(DATA_PTR(m_tmpV));
00290     double val = mean_X(DATA_PTR(m_tmpV));
00291     return val;
00292   }
00293 
00294   /// Molar heat capacity at constant volume. Units: J/kmol/K. 
00295   doublereal DebyeHuckel::cv_mole() const {
00296     //getPartialMolarCv(m_tmpV.begin());
00297     //return mean_X(m_tmpV.begin());
00298     err("not implemented");
00299     return 0.0;
00300   }
00301 
00302   //
00303   // ------- Mechanical Equation of State Properties ------------------------
00304   //
00305 
00306   /*
00307    * Pressure. Units: Pa.
00308    * For this incompressible system, we return the internally storred
00309    * independent value of the pressure.
00310    */
00311   doublereal DebyeHuckel::pressure() const {
00312     return m_Pcurrent;
00313   }
00314 
00315   void DebyeHuckel::setPressure(doublereal p) {
00316     setState_TP(temperature(), p);
00317   }
00318 
00319   void DebyeHuckel::setState_TP(doublereal t, doublereal p) {
00320 
00321     State::setTemperature(t);
00322     /*
00323      * Store the current pressure
00324      */
00325     m_Pcurrent = p;
00326 
00327     /*
00328      * update the standard state thermo
00329      * -> This involves calling the water function and setting the pressure
00330      */
00331     _updateStandardStateThermo();
00332 
00333     /*
00334      * Calculate all of the other standard volumes
00335      * -> note these are constant for now
00336      */
00337     calcDensity();
00338   }
00339 
00340   /*
00341    * Calculate the density of the mixture using the partial
00342    * molar volumes and mole fractions as input
00343    *
00344    * The formula for this is
00345    *
00346    * \f[
00347    * \rho = \frac{\sum_k{X_k W_k}}{\sum_k{X_k V_k}}
00348    * \f]
00349    *
00350    * where \f$X_k\f$ are the mole fractions, \f$W_k\f$ are
00351    * the molecular weights, and \f$V_k\f$ are the pure species
00352    * molar volumes.
00353    *
00354    * Note, the basis behind this formula is that in an ideal
00355    * solution the partial molar volumes are equal to the pure
00356    * species molar volumes. We have additionally specified
00357    * in this class that the pure species molar volumes are
00358    * independent of temperature and pressure.
00359    *
00360    */
00361   void DebyeHuckel::calcDensity() {
00362     if (m_waterSS) {
00363       
00364       /*
00365        * Store the internal density of the water SS.
00366        * Note, we would have to do this for all other
00367        * species if they had pressure dependent properties.
00368        */
00369       m_densWaterSS = m_waterSS->density();
00370     }
00371     double *vbar = &m_pp[0];
00372     getPartialMolarVolumes(vbar);
00373     double *x = &m_tmpV[0];
00374     getMoleFractions(x);
00375     doublereal vtotal = 0.0;
00376     for (int i = 0; i < m_kk; i++) {
00377       vtotal += vbar[i] * x[i];
00378     }
00379     doublereal dd = meanMolecularWeight() / vtotal;
00380     State::setDensity(dd);
00381   }
00382 
00383 
00384   /*
00385    * The isothermal compressibility. Units: 1/Pa.
00386    * The isothermal compressibility is defined as
00387    * \f[
00388    * \kappa_T = -\frac{1}{v}\left(\frac{\partial v}{\partial P}\right)_T
00389    * \f]
00390    *
00391    *  It's equal to zero for this model, since the molar volume
00392    *  doesn't change with pressure or temperature.
00393    */
00394   doublereal DebyeHuckel::isothermalCompressibility() const {
00395     throw CanteraError("DebyeHuckel::isothermalCompressibility",
00396                        "unimplemented");
00397     return 0.0;
00398   }
00399 
00400   /*
00401    * The thermal expansion coefficient. Units: 1/K.
00402    * The thermal expansion coefficient is defined as
00403    *
00404    * \f[
00405    * \beta = \frac{1}{v}\left(\frac{\partial v}{\partial T}\right)_P
00406    * \f]
00407    *
00408    *  It's equal to zero for this model, since the molar volume
00409    *  doesn't change with pressure or temperature.
00410    */
00411   doublereal DebyeHuckel::thermalExpansionCoeff() const {
00412     throw CanteraError("DebyeHuckel::thermalExpansionCoeff",
00413                        "unimplemented");
00414     return 0.0;
00415   }
00416     
00417   /*
00418    * Overwritten setDensity() function is necessary because the
00419    * density is not an indendent variable.
00420    *
00421    * This function will now throw an error condition
00422    *
00423    * @internal May have to adjust the strategy here to make
00424    * the eos for these materials slightly compressible, in order
00425    * to create a condition where the density is a function of
00426    * the pressure.
00427    *
00428    * This function will now throw an error condition.
00429    *
00430    *  NOTE: This is an overwritten function from the State.h
00431    *        class
00432    */
00433   void DebyeHuckel::setDensity(doublereal rho) {
00434     double dens = density();
00435     if (rho != dens) {
00436       throw CanteraError("Idea;MolalSoln::setDensity",
00437                          "Density is not an independent variable");
00438     }
00439   }
00440 
00441   /*
00442    * Overwritten setMolarDensity() function is necessary because the
00443    * density is not an indendent variable.
00444    *
00445    * This function will now throw an error condition.
00446    *
00447    *  NOTE: This is a virtual function, overwritten function from the State.h
00448    *        class
00449    */
00450   void DebyeHuckel::setMolarDensity(const doublereal conc) {
00451   double concI = molarDensity();
00452     if (conc != concI) {
00453       throw CanteraError("Idea;MolalSoln::setMolarDensity",
00454                          "molarDensity/density is not an independent variable");
00455     }
00456   }
00457 
00458   /*
00459    * Overwritten setTemperature(double) from State.h. This
00460    * function sets the temperature, and makes sure that
00461    * the value propagates to underlying objects.
00462    */
00463   void DebyeHuckel::setTemperature(const doublereal temp) {
00464     setState_TP(temp, m_Pcurrent);
00465   }
00466 
00467 
00468   //
00469   // ------- Activities and Activity Concentrations
00470   //
00471 
00472   /*
00473    * This method returns an array of generalized concentrations
00474    * \f$ C_k\f$ that are defined such that 
00475    * \f$ a_k = C_k / C^0_k, \f$ where \f$ C^0_k \f$ 
00476    * is a standard concentration
00477    * defined below.  These generalized concentrations are used
00478    * by kinetics manager classes to compute the forward and
00479    * reverse rates of elementary reactions. 
00480    *
00481    * @param c Array of generalized concentrations. The 
00482    *           units depend upon the implementation of the
00483    *           reaction rate expressions within the phase.
00484    */
00485   void DebyeHuckel::getActivityConcentrations(doublereal* c) const {
00486     double c_solvent = standardConcentration();
00487     getActivities(c);
00488     for (int k = 0; k < m_kk; k++) {
00489       c[k] *= c_solvent;
00490     }
00491   }
00492 
00493   /*
00494    * The standard concentration \f$ C^0_k \f$ used to normalize
00495    * the generalized concentration. In many cases, this quantity
00496    * will be the same for all species in a phase - for example,
00497    * for an ideal gas \f$ C^0_k = P/\hat R T \f$. For this
00498    * reason, this method returns a single value, instead of an
00499    * array.  However, for phases in which the standard
00500    * concentration is species-specific (e.g. surface species of
00501    * different sizes), this method may be called with an
00502    * optional parameter indicating the species.
00503    *
00504    * For the time being we will use the concentration of pure
00505    * solvent for the the standard concentration of all species.
00506    * This has the effect of making reaction rates
00507    * based on the molality of species proportional to the
00508    * molality of the species.
00509    */
00510   doublereal DebyeHuckel::standardConcentration(int k) const {
00511     double mvSolvent = m_speciesSize[m_indexSolvent];
00512     return 1.0 / mvSolvent;
00513   }
00514     
00515   /*
00516    * Returns the natural logarithm of the standard 
00517    * concentration of the kth species
00518    */
00519   doublereal DebyeHuckel::logStandardConc(int k) const {
00520     double c_solvent = standardConcentration(k);
00521     return log(c_solvent);
00522   }
00523     
00524   /*
00525    * Returns the units of the standard and general concentrations
00526    * Note they have the same units, as their divisor is 
00527    * defined to be equal to the activity of the kth species
00528    * in the solution, which is unitless.
00529    *
00530    * This routine is used in print out applications where the
00531    * units are needed. Usually, MKS units are assumed throughout
00532    * the program and in the XML input files. 
00533    *
00534    * On return uA contains the powers of the units (MKS assumed)
00535    * of the standard concentrations and generalized concentrations
00536    * for the kth species.
00537    *
00538    *  uA[0] = kmol units - default  = 1
00539    *  uA[1] = m    units - default  = -nDim(), the number of spatial
00540    *                                dimensions in the Phase class.
00541    *  uA[2] = kg   units - default  = 0;
00542    *  uA[3] = Pa(pressure) units - default = 0;
00543    *  uA[4] = Temperature units - default = 0;
00544    *  uA[5] = time units - default = 0
00545    */
00546   void DebyeHuckel::getUnitsStandardConc(double *uA, int k, int sizeUA) const {
00547     for (int i = 0; i < sizeUA; i++) {
00548       if (i == 0) uA[0] = 1.0;
00549       if (i == 1) uA[1] = -nDim();
00550       if (i == 2) uA[2] = 0.0;
00551       if (i == 3) uA[3] = 0.0;
00552       if (i == 4) uA[4] = 0.0;
00553       if (i == 5) uA[5] = 0.0;
00554     }
00555   }    
00556 
00557 
00558   /*
00559    * Get the array of non-dimensional activities at
00560    * the current solution temperature, pressure, and
00561    * solution concentration.
00562    * (note solvent activity coefficient is on the molar scale).
00563    *
00564    */
00565   void DebyeHuckel::getActivities(doublereal* ac) const {
00566     _updateStandardStateThermo();
00567     /*
00568      * Update the molality array, m_molalities()
00569      *   This requires an update due to mole fractions
00570      */
00571     s_update_lnMolalityActCoeff();
00572     for (int k = 0; k < m_kk; k++) {
00573       if (k != m_indexSolvent) {
00574         ac[k] = m_molalities[k] * exp(m_lnActCoeffMolal[k]);
00575       }
00576     }
00577     double xmolSolvent = moleFraction(m_indexSolvent);
00578     ac[m_indexSolvent] =
00579       exp(m_lnActCoeffMolal[m_indexSolvent]) * xmolSolvent;
00580   }
00581 
00582   /*
00583    * getMolalityActivityCoefficients()             (virtual, const)
00584    *
00585    * Get the array of non-dimensional Molality based
00586    * activity coefficients at
00587    * the current solution temperature, pressure, and
00588    * solution concentration.
00589    * (note solvent activity coefficient is on the molar scale).
00590    *
00591    *  Note, most of the work is done in an internal private routine
00592    */
00593   void DebyeHuckel::
00594   getMolalityActivityCoefficients(doublereal* acMolality) const {
00595     _updateStandardStateThermo();
00596     A_Debye_TP(-1.0, -1.0);
00597     s_update_lnMolalityActCoeff();
00598     copy(m_lnActCoeffMolal.begin(), m_lnActCoeffMolal.end(), acMolality);
00599     for (int k = 0; k < m_kk; k++) {
00600       acMolality[k] = exp(acMolality[k]);
00601     }
00602   }
00603 
00604   //
00605   // ------ Partial Molar Properties of the Solution -----------------
00606   //
00607   /*
00608    * Get the species chemical potentials. Units: J/kmol.
00609    *
00610    * This function returns a vector of chemical potentials of the 
00611    * species in solution.
00612    *
00613    * \f[
00614    *    \mu_k = \mu^{o}_k(T,P) + R T ln(m_k)
00615    * \f]
00616    *
00617    * \f[
00618    *    \mu_solvent = \mu^{o}_solvent(T,P) +
00619    *            R T ((X_solvent - 1.0) / X_solvent)
00620    * \f]
00621    */
00622   void DebyeHuckel::getChemPotentials(doublereal* mu) const{
00623     double xx;
00624     const double xxSmall = 1.0E-150; 
00625     /*
00626      * First get the standard chemical potentials in
00627      * molar form.
00628      *  -> this requires updates of standard state as a function
00629      *     of T and P
00630      */
00631     getStandardChemPotentials(mu);
00632     /*
00633      * Update the activity coefficients
00634      * This also updates the internal molality array.
00635      */
00636     s_update_lnMolalityActCoeff();
00637     /*
00638      *   
00639      */
00640     doublereal RT = GasConstant * temperature();
00641     double xmolSolvent = moleFraction(m_indexSolvent);
00642     for (int k = 0; k < m_kk; k++) {
00643       if (m_indexSolvent != k) {
00644         xx = MAX(m_molalities[k], xxSmall);
00645         mu[k] += RT * (log(xx) + m_lnActCoeffMolal[k]);
00646       }
00647     }
00648     xx = MAX(xmolSolvent, xxSmall);
00649     mu[m_indexSolvent] +=  
00650       RT * (log(xx) + m_lnActCoeffMolal[m_indexSolvent]);
00651   }
00652 
00653 
00654   /*
00655    * Returns an array of partial molar enthalpies for the species
00656    * in the mixture.
00657    * Units (J/kmol)
00658    *
00659    * We calculate this quantity partially from the relation and
00660    * partially by calling the standard state enthalpy function.
00661    *
00662    *     hbar_i = - T**2 * d(chemPot_i/T)/dT 
00663    *
00664    * We calculate 
00665    */
00666   void DebyeHuckel::getPartialMolarEnthalpies(doublereal* hbar) const {
00667     /*
00668      * Get the nondimensional standard state enthalpies
00669      */
00670     getEnthalpy_RT(hbar);
00671     /*
00672      * Dimensionalize it.
00673      */
00674     double T = temperature();
00675     double RT = GasConstant * T;
00676     for (int k = 0; k < m_kk; k++) {
00677       hbar[k] *= RT;
00678     }
00679     /*
00680      * Check to see whether activity coefficients are temperature
00681      * dependent. If they are, then calculate the their temperature
00682      * derivatives and add them into the result.
00683      */
00684     double dAdT = dA_DebyedT_TP();
00685     if (dAdT != 0.0) {
00686       /*
00687        * Update the activity coefficients, This also update the
00688        * internally storred molalities.
00689        */
00690       s_update_lnMolalityActCoeff();
00691       s_update_dlnMolalityActCoeff_dT();
00692       double RTT = GasConstant * T * T;
00693       for (int k = 0; k < m_kk; k++) {
00694         hbar[k] -= RTT * m_dlnActCoeffMolaldT[k];
00695       }
00696     }
00697   }
00698 
00699   /*
00700    *
00701    * getPartialMolarEntropies()        (virtual, const)
00702    *
00703    * Returns an array of partial molar entropies of the species in the
00704    * solution. Units: J/kmol.
00705    *
00706    * Maxwell's equations provide an insight in how to calculate this
00707    * (p.215 Smith and Van Ness)
00708    *
00709    *      d(chemPot_i)/dT = -sbar_i
00710    *   
00711    * For this phase, the partial molar entropies are equal to the
00712    * SS species entropies plus the ideal solution contribution.following
00713    * contribution:
00714    *  \f[
00715    * \bar s_k(T,P) =  \hat s^0_k(T) - R log(M0 * molality[k])
00716    * \f]
00717    * \f[
00718    * \bar s_solvent(T,P) =  \hat s^0_solvent(T) 
00719    *             - R ((xmolSolvent - 1.0) / xmolSolvent)
00720    * \f]
00721    *
00722    * The reference-state pure-species entropies,\f$ \hat s^0_k(T) \f$,
00723    * at the reference pressure, \f$ P_{ref} \f$,  are computed by the
00724    * species thermodynamic
00725    * property manager. They are polynomial functions of temperature.
00726    * @see SpeciesThermo
00727    */
00728   void DebyeHuckel::
00729   getPartialMolarEntropies(doublereal* sbar) const {
00730     int k;
00731     /*
00732      * Get the standard state entropies at the temperature
00733      * and pressure of the solution.
00734      */
00735     getEntropy_R(sbar);
00736     /*
00737      * Dimensionalize the entropies
00738      */
00739     doublereal R = GasConstant;
00740     for (k = 0; k < m_kk; k++) {
00741       sbar[k] *= R;
00742     }
00743     /*
00744      * Update the activity coefficients, This also update the
00745      * internally storred molalities.
00746      */
00747     s_update_lnMolalityActCoeff();
00748     /*
00749      * First we will add in the obvious dependence on the T
00750      * term out front of the log activity term
00751      */
00752     doublereal mm;
00753     for (k = 0; k < m_kk; k++) {
00754       if (k != m_indexSolvent) {
00755         mm = fmaxx(SmallNumber, m_molalities[k]);
00756         sbar[k] -= R * (log(mm) + m_lnActCoeffMolal[k]);
00757       }
00758     }
00759     double xmolSolvent = moleFraction(m_indexSolvent);
00760     mm = fmaxx(SmallNumber, xmolSolvent);
00761     sbar[m_indexSolvent] -= R *(log(mm) + m_lnActCoeffMolal[m_indexSolvent]);
00762     /*
00763      * Check to see whether activity coefficients are temperature
00764      * dependent. If they are, then calculate the their temperature
00765      * derivatives and add them into the result.
00766      */
00767     double dAdT = dA_DebyedT_TP();
00768     if (dAdT != 0.0) {
00769       s_update_dlnMolalityActCoeff_dT();
00770       double RT = R * temperature();
00771       for (k = 0; k < m_kk; k++) {
00772         sbar[k] -= RT * m_dlnActCoeffMolaldT[k];
00773       }
00774     }
00775   }
00776 
00777   /*
00778    * getPartialMolarVolumes()                (virtual, const)
00779    *
00780    * returns an array of partial molar volumes of the species
00781    * in the solution. Units: m^3 kmol-1.
00782    *
00783    * For this solution, the partial molar volumes are normally
00784    *  equal to theconstant species molar volumes, except
00785    * when the activity coefficients depend on pressure.
00786    *
00787    * The general relation is 
00788    *
00789    *       vbar_i = d(chemPot_i)/dP at const T, n
00790    *
00791    *              = V0_i + d(Gex)/dP)_T,M
00792    *
00793    *              = V0_i + RT d(lnActCoeffi)dP _T,M
00794    *
00795    */
00796   void DebyeHuckel::getPartialMolarVolumes(doublereal* vbar) const {
00797     getStandardVolumes(vbar);
00798     /*
00799      * Update the derivatives wrt the activity coefficients.
00800      */
00801     s_update_lnMolalityActCoeff();
00802     s_update_dlnMolalityActCoeff_dP();  
00803     double T = temperature();
00804     double RT = GasConstant * T;
00805     for (int k = 0; k < m_kk; k++) {
00806       vbar[k] += RT * m_dlnActCoeffMolaldP[k];
00807     }
00808   }
00809 
00810   /*
00811    * Partial molar heat capacity of the solution:
00812    *   The kth partial molar heat capacity is  equal to 
00813    *   the temperature derivative of the partial molar
00814    *   enthalpy of the kth species in the solution at constant
00815    *   P and composition (p. 220 Smith and Van Ness).
00816    *
00817    *     Cp = -T d2(chemPot_i)/dT2
00818    */
00819   void DebyeHuckel::getPartialMolarCp(doublereal* cpbar) const {
00820     /*
00821      * Get the nondimensional gibbs standard state of the
00822      * species at the T and P of the solution.
00823      */
00824     getCp_R(cpbar);
00825         
00826     for (int k = 0; k < m_kk; k++) {
00827       cpbar[k] *= GasConstant;
00828     }
00829         
00830     /*
00831      * Check to see whether activity coefficients are temperature
00832      * dependent. If they are, then calculate the their temperature
00833      * derivatives and add them into the result.
00834      */
00835     double dAdT = dA_DebyedT_TP();
00836     if (dAdT != 0.0) {
00837       /*
00838        * Update the activity coefficients, This also update the
00839        * internally storred molalities.
00840        */
00841       s_update_lnMolalityActCoeff();
00842       s_update_dlnMolalityActCoeff_dT();
00843       s_update_d2lnMolalityActCoeff_dT2();
00844       double T = temperature();
00845       double RT = GasConstant * T;
00846       double RTT = RT * T;
00847       for (int k = 0; k < m_kk; k++) {
00848         cpbar[k] -= (2.0 * RT * m_dlnActCoeffMolaldT[k] +
00849                      RTT * m_d2lnActCoeffMolaldT2[k]);
00850       }
00851     }
00852   }
00853 
00854 
00855   
00856 
00857   /*
00858    *  -------------- Utilities -------------------------------
00859    */
00860 
00861   /*
00862    *  Initialization routine for a DebyeHuckel phase.
00863    *
00864    * This is a virtual routine. This routine will call initThermo()
00865    * for the parent class as well.
00866    */
00867   void DebyeHuckel::initThermo() {
00868     MolalityVPSSTP::initThermo();
00869     initLengths();
00870   }
00871 
00872   /*
00873    * constructPhaseFile
00874    *
00875    * Initialization of a Debye-Huckel phase using an
00876    * xml file.
00877    *
00878    * This routine is a precursor to initThermo(XML_Node*)
00879    * routine, which does most of the work.
00880    *
00881    * @param infile XML file containing the description of the
00882    *        phase
00883    *
00884    * @param id  Optional parameter identifying the name of the
00885    *            phase. If none is given, the first XML
00886    *            phase element will be used.
00887    */
00888   void DebyeHuckel::constructPhaseFile(std::string inputFile, std::string id) {
00889 
00890     if (inputFile.size() == 0) {
00891       throw CanteraError("DebyeHuckel::initThermo",
00892                          "input file is null");
00893     }
00894     std::string path = findInputFile(inputFile);
00895     ifstream fin(path.c_str());
00896     if (!fin) {
00897       throw CanteraError("DebyeHuckel::initThermo","could not open "
00898                          +path+" for reading.");
00899     }
00900     /*
00901      * The phase object automatically constructs an XML object.
00902      * Use this object to store information.
00903      */
00904     XML_Node &phaseNode_XML = xml();
00905     XML_Node *fxml = new XML_Node();
00906     fxml->build(fin);
00907     XML_Node *fxml_phase = findXMLPhase(fxml, id);
00908     if (!fxml_phase) {
00909       throw CanteraError("DebyeHuckel::initThermo",
00910                          "ERROR: Can not find phase named " +
00911                          id + " in file named " + inputFile);
00912     }
00913     fxml_phase->copy(&phaseNode_XML);   
00914     constructPhaseXML(*fxml_phase, id);
00915     delete fxml;
00916   }
00917 
00918   /**
00919    * interp_est()                          (static)
00920    *
00921    * utility function to assign an integer value from a string
00922    * for the ElectrolyteSpeciesType field.
00923    */
00924   static int interp_est(std::string estString) {
00925     const char *cc = estString.c_str();
00926     string lc = lowercase(estString);
00927     const char *ccl = lc.c_str();
00928     if (!strcmp(ccl, "solvent")) {
00929       return cEST_solvent;
00930     } else if (!strcmp(ccl, "chargedspecies")) {
00931       return cEST_chargedSpecies;
00932     } else if (!strcmp(ccl, "weakacidassociated")) {
00933       return cEST_weakAcidAssociated;
00934     } else if (!strcmp(ccl, "strongacidassociated")) {
00935       return cEST_strongAcidAssociated;
00936     } else if (!strcmp(ccl, "polarneutral")) {
00937       return cEST_polarNeutral;
00938     } else if (!strcmp(ccl, "nonpolarneutral")) {
00939       return cEST_nonpolarNeutral;
00940     }
00941     int retn, rval;
00942     if ((retn = sscanf(cc, "%d", &rval)) != 1) {
00943       return -1;
00944     }
00945     return rval;
00946   }
00947         
00948   /*
00949    *   Import and initialize a DebyeHuckel phase 
00950    *   specification in an XML tree into the current object.
00951    *   Here we read an XML description of the phase.
00952    *   We import descriptions of the elements that make up the
00953    *   species in a phase.
00954    *   We import information about the species, including their
00955    *   reference state thermodynamic polynomials. We then freeze
00956    *   the state of the species.
00957    *
00958    *   Then, we read the species molar volumes from the xml 
00959    *   tree to finish the initialization.
00960    *
00961    * @param phaseNode This object must be the phase node of a
00962    *             complete XML tree
00963    *             description of the phase, including all of the
00964    *             species data. In other words while "phase" must
00965    *             point to an XML phase object, it must have
00966    *             sibling nodes "speciesData" that describe
00967    *             the species in the phase.
00968    * @param id   ID of the phase. If nonnull, a check is done
00969    *             to see if phaseNode is pointing to the phase
00970    *             with the correct id. 
00971    */
00972   void DebyeHuckel::constructPhaseXML(XML_Node& phaseNode, std::string id) {
00973    
00974     if (id.size() > 0) {
00975       std::string idp = phaseNode.id();
00976       if (idp != id) {
00977         throw CanteraError("DebyeHuckel::constructPhaseXML", 
00978                            "phasenode and Id are incompatible");
00979       }
00980     }
00981 
00982     /*
00983      * Find the Thermo XML node 
00984      */
00985     if (!phaseNode.hasChild("thermo")) {
00986       throw CanteraError("DebyeHuckel::constructPhaseXML",
00987                          "no thermo XML node");
00988     }
00989     XML_Node& thermoNode = phaseNode.child("thermo");
00990 
00991     /*
00992      * Possibly change the form of the standard concentrations
00993      */
00994     if (thermoNode.hasChild("standardConc")) {
00995       XML_Node& scNode = thermoNode.child("standardConc");
00996       m_formGC = 2;
00997       std::string formString = scNode.attrib("model");
00998       if (formString != "") {
00999         if (formString == "unity") {
01000           m_formGC = 0;
01001           printf("exit standardConc = unity not done\n");
01002           exit(EXIT_FAILURE);
01003         } else if (formString == "molar_volume") {
01004           m_formGC = 1;
01005           printf("exit standardConc = molar_volume not done\n");
01006           exit(EXIT_FAILURE);
01007         } else if (formString == "solvent_volume") {
01008           m_formGC = 2;
01009         } else {
01010           throw CanteraError("DebyeHuckel::constructPhaseXML",
01011                              "Unknown standardConc model: " + formString);
01012         }
01013       }
01014     }
01015     /*
01016      * Get the Name of the Solvent:
01017      *      <solvent> solventName </solvent>
01018      */
01019     std::string solventName = "";
01020     if (thermoNode.hasChild("solvent")) {
01021       XML_Node& scNode = thermoNode.child("solvent");
01022       vector<std::string> nameSolventa;
01023       getStringArray(scNode, nameSolventa);
01024       int nsp = static_cast<int>(nameSolventa.size());
01025       if (nsp != 1) {
01026         throw CanteraError("DebyeHuckel::constructPhaseXML",
01027                            "badly formed solvent XML node");
01028       }
01029       solventName = nameSolventa[0];
01030     }
01031 
01032     /*
01033      * Determine the form of the Debye-Huckel model,
01034      * m_formDH.  We will use this information to size arrays below.
01035      */
01036     if (thermoNode.hasChild("activityCoefficients")) {
01037       XML_Node& scNode = thermoNode.child("activityCoefficients");
01038       m_formDH = DHFORM_DILUTE_LIMIT;
01039       std::string formString = scNode.attrib("model");
01040       if (formString != "") {
01041         if        (formString == "Dilute_limit") {
01042           m_formDH = DHFORM_DILUTE_LIMIT;
01043         } else if (formString == "Bdot_with_variable_a") {
01044           m_formDH = DHFORM_BDOT_AK  ;
01045         } else if (formString == "Bdot_with_common_a") {
01046           m_formDH = DHFORM_BDOT_ACOMMON;
01047         } else if (formString == "Beta_ij") {
01048           m_formDH = DHFORM_BETAIJ;
01049         } else if (formString == "Pitzer_with_Beta_ij") {
01050           m_formDH = DHFORM_PITZER_BETAIJ;
01051         } else {
01052           throw CanteraError("DebyeHuckel::constructPhaseXML",
01053                              "Unknown standardConc model: " + formString);
01054         }
01055       }
01056     } else {
01057       /*
01058        * If there is no XML node named "activityCoefficients", assume
01059        * that we are doing the extreme dilute limit assumption
01060        */
01061       m_formDH = DHFORM_DILUTE_LIMIT;
01062     }
01063 
01064     /*
01065      * Call the Cantera importPhase() function. This will import
01066      * all of the species into the phase. This will also handle
01067      * all of the solvent and solute standard states
01068      */
01069     bool m_ok = importPhase(phaseNode, this);
01070     if (!m_ok) {
01071       throw CanteraError("DebyeHuckel::constructPhaseXML",
01072                          "importPhase failed "); 
01073     }
01074   }
01075 
01076   /*
01077    * Process the XML file after species are set up.
01078    *
01079    *  This gets called from importPhase(). It processes the XML file
01080    *  after the species are set up. This is the main routine for
01081    *  reading in activity coefficient parameters.
01082    *
01083    * @param phaseNode This object must be the phase node of a
01084    *             complete XML tree
01085    *             description of the phase, including all of the
01086    *             species data. In other words while "phase" must
01087    *             point to an XML phase object, it must have
01088    *             sibling nodes "speciesData" that describe
01089    *             the species in the phase.
01090    * @param id   ID of the phase. If nonnull, a check is done
01091    *             to see if phaseNode is pointing to the phase
01092    *             with the correct id.
01093    */
01094   void DebyeHuckel::
01095   initThermoXML(XML_Node& phaseNode, std::string id) {
01096     int k;
01097     std::string stemp;
01098     /*
01099      * Find the Thermo XML node 
01100      */
01101     if (!phaseNode.hasChild("thermo")) {
01102       throw CanteraError("HMWSoln::initThermoXML",
01103                          "no thermo XML node");
01104     }
01105     XML_Node& thermoNode = phaseNode.child("thermo");
01106 
01107     /*
01108      * Possibly change the form of the standard concentrations
01109      */
01110     if (thermoNode.hasChild("standardConc")) {
01111       XML_Node& scNode = thermoNode.child("standardConc");
01112       m_formGC = 2;
01113       std::string formString = scNode.attrib("model");
01114       if (formString != "") {
01115         if (formString == "unity") {
01116           m_formGC = 0;
01117           printf("exit standardConc = unity not done\n");
01118           exit(EXIT_FAILURE);
01119         } else if (formString == "molar_volume") {
01120           m_formGC = 1;
01121           printf("exit standardConc = molar_volume not done\n");
01122           exit(EXIT_FAILURE);
01123         } else if (formString == "solvent_volume") {
01124           m_formGC = 2;
01125         } else {
01126           throw CanteraError("DebyeHuckel::constructPhaseXML",
01127                              "Unknown standardConc model: " + formString);
01128         }
01129       }
01130     }
01131 
01132   
01133 
01134     /*
01135      * Reconcile the solvent name and index.
01136      */
01137     /*
01138      * Get the Name of the Solvent:
01139      *      <solvent> solventName </solvent>
01140      */
01141     std::string solventName = "";
01142     if (thermoNode.hasChild("solvent")) {
01143       XML_Node& scNode = thermoNode.child("solvent");
01144       vector<std::string> nameSolventa;
01145       getStringArray(scNode, nameSolventa);
01146       int nsp = static_cast<int>(nameSolventa.size());
01147       if (nsp != 1) {
01148         throw CanteraError("DebyeHuckel::initThermoXML",
01149                            "badly formed solvent XML node");
01150       }
01151       solventName = nameSolventa[0];
01152     }
01153     for (k = 0; k < m_kk; k++) {
01154       std::string sname = speciesName(k);
01155       if (solventName == sname) {
01156         m_indexSolvent = k;
01157         break;
01158       }
01159     }
01160     if (m_indexSolvent == -1) {
01161       cout << "DebyeHuckel::initThermoXML: Solvent Name not found" 
01162            << endl;
01163       throw CanteraError("DebyeHuckel::initThermoXML",
01164                          "Solvent name not found");
01165     }
01166     if (m_indexSolvent != 0) {
01167       throw CanteraError("DebyeHuckel::initThermoXML",
01168                          "Solvent " + solventName +
01169                          " should be first species");
01170     }
01171     
01172     /*
01173      * Determine the form of the Debye-Huckel model,
01174      * m_formDH.  We will use this information to size arrays below.
01175      */
01176     if (thermoNode.hasChild("activityCoefficients")) {
01177       XML_Node& scNode = thermoNode.child("activityCoefficients");
01178       m_formDH = DHFORM_DILUTE_LIMIT;
01179       std::string formString = scNode.attrib("model");
01180       if (formString != "") {
01181         if        (formString == "Dilute_limit") {
01182           m_formDH = DHFORM_DILUTE_LIMIT;
01183         } else if (formString == "Bdot_with_variable_a") {
01184           m_formDH = DHFORM_BDOT_AK  ;
01185         } else if (formString == "Bdot_with_common_a") {
01186           m_formDH = DHFORM_BDOT_ACOMMON;
01187         } else if (formString == "Beta_ij") {
01188           m_formDH = DHFORM_BETAIJ;
01189         } else if (formString == "Pitzer_with_Beta_ij") {
01190           m_formDH = DHFORM_PITZER_BETAIJ;
01191         } else {
01192           throw CanteraError("DebyeHuckel::constructPhaseXML",
01193                              "Unknown standardConc model: " + formString);
01194         }
01195       }
01196     } else {
01197       /*
01198        * If there is no XML node named "activityCoefficients", assume
01199        * that we are doing the extreme dilute limit assumption
01200        */
01201       m_formDH = DHFORM_DILUTE_LIMIT;
01202     }
01203 
01204     /*
01205      * Initialize all of the lengths of arrays in the object
01206      * now that we know what species are in the phase.
01207      */
01208     initThermo();
01209 
01210     /*
01211      * Now go get the specification of the standard states for
01212      * species in the solution. This includes the molar volumes
01213      * data blocks for incompressible species.
01214      */
01215     XML_Node& speciesList = phaseNode.child("speciesArray");
01216     XML_Node* speciesDB =
01217       get_XML_NameID("speciesData", speciesList["datasrc"],
01218                      &phaseNode.root());
01219     const vector<string>&sss = speciesNames();
01220 
01221     for (k = 0; k < m_kk; k++) {
01222       XML_Node* s =  speciesDB->findByAttr("name", sss[k]);
01223       if (!s) {
01224         throw CanteraError("DebyeHuckel::initThermoXML",
01225                          "Species Data Base " + sss[k] + " not found");
01226       }
01227       XML_Node *ss = s->findByName("standardState");
01228       if (!ss) {
01229         throw CanteraError("DebyeHuckel::initThermoXML",
01230                          "Species " + sss[k] + 
01231                            " standardState XML block  not found");
01232       }
01233       std::string modelStringa = ss->attrib("model");
01234       if (modelStringa == "") {
01235         throw CanteraError("DebyeHuckel::initThermoXML",
01236                            "Species " + sss[k] + 
01237                            " standardState XML block model attribute not found");
01238       }
01239       std::string modelString = lowercase(modelStringa);
01240 
01241       if (k == 0) {
01242         if (modelString == "wateriapws" || modelString == "real_water" ||
01243             modelString == "waterpdss") {
01244           /*
01245            * Initialize the water standard state model
01246            */
01247           m_waterSS = dynamic_cast<PDSS_Water *>(providePDSS(0)) ;
01248           if (!m_waterSS) {
01249             throw CanteraError("HMWSoln::installThermoXML", 
01250                                "Dynamic cast to PDSS_Water failed");
01251           }
01252           /*
01253            * Fill in the molar volume of water (m3/kmol)
01254            * at standard conditions to fill in the m_speciesSize entry
01255            * with something reasonable.
01256            */
01257           m_waterSS->setState_TP(300., OneAtm);
01258           double dens = m_waterSS->density();
01259           double mw = m_waterSS->molecularWeight();
01260           m_speciesSize[0] = mw / dens;
01261 #ifdef DEBUG_MODE_NOT
01262           cout << "Solvent species " << sss[k] << " has volume " <<  
01263             m_speciesSize[k] << endl;
01264 #endif
01265         } else if (modelString == "constant_incompressible") {
01266           m_speciesSize[k] = getFloat(*ss, "molarVolume", "toSi");
01267 #ifdef DEBUG_MODE_NOT
01268           cout << "species " << sss[k] << " has volume " <<  
01269             m_speciesSize[k] << endl;
01270 #endif
01271         } else {
01272           throw CanteraError("DebyeHuckel::initThermoXML",
01273                              "Solvent SS Model \"" + modelStringa + 
01274                              "\" is not known");
01275         }
01276       } else {
01277         if (modelString != "constant_incompressible") {
01278           throw CanteraError("DebyeHuckel::initThermoXML",
01279                              "Solute SS Model \"" + modelStringa + 
01280                              "\" is not known");
01281         }
01282         m_speciesSize[k] = getFloat(*ss, "molarVolume", "toSI");
01283 #ifdef DEBUG_MODE_NOT
01284         cout << "species " << sss[k] << " has volume " <<  
01285           m_speciesSize[k] << endl;
01286 #endif
01287       }
01288 
01289     }
01290 
01291     /*
01292      * Go get all of the coefficients and factors in the
01293      * activityCoefficients XML block
01294      */
01295     XML_Node *acNodePtr = 0;
01296     if (thermoNode.hasChild("activityCoefficients")) {
01297       XML_Node& acNode = thermoNode.child("activityCoefficients");
01298       acNodePtr = &acNode;
01299       /*
01300        * Look for parameters for A_Debye
01301        */
01302       if (acNode.hasChild("A_Debye")) {
01303         XML_Node *ss = acNode.findByName("A_Debye");
01304         string modelStringa = ss->attrib("model");
01305         string modelString = lowercase(modelStringa);
01306         if (modelString != "") {
01307           if (modelString == "water") {
01308             m_form_A_Debye = A_DEBYE_WATER;
01309           } else {
01310             throw CanteraError("DebyeHuckel::initThermoXML",
01311                                "A_Debye Model \"" + modelStringa + 
01312                              "\" is not known");
01313           }
01314         } else {
01315           m_A_Debye = getFloat(acNode, "A_Debye");
01316 #ifdef DEBUG_HKM_NOT
01317           cout << "A_Debye = " << m_A_Debye << endl;
01318 #endif
01319         }
01320       }
01321 
01322       /*
01323        * Initialize the water property calculator. It will share
01324        * the internal eos water calculator.
01325        */
01326       if (m_form_A_Debye == A_DEBYE_WATER) {
01327         if (m_waterProps) delete m_waterProps;
01328         m_waterProps = new WaterProps(m_waterSS);
01329       }
01330 
01331       /*
01332        * Look for parameters for B_Debye
01333        */
01334       if (acNode.hasChild("B_Debye")) {
01335         m_B_Debye = getFloat(acNode, "B_Debye");
01336 #ifdef DEBUG_HKM_NOT
01337         cout << "B_Debye = " << m_B_Debye << endl;
01338 #endif
01339       }
01340 
01341       /*
01342        * Look for parameters for B_dot
01343        */
01344       if (acNode.hasChild("B_dot")) {
01345         if (m_formDH == DHFORM_BETAIJ ||
01346             m_formDH == DHFORM_DILUTE_LIMIT ||
01347             m_formDH == DHFORM_PITZER_BETAIJ) {
01348           throw CanteraError("DebyeHuckel:init",
01349                              "B_dot entry in the wrong DH form");
01350         }
01351         double bdot_common = getFloat(acNode, "B_dot");
01352 #ifdef DEBUG_HKM_NOT
01353         cout << "B_dot = " << bdot_common << endl;
01354 #endif
01355         /*
01356          * Set B_dot parameters for charged species
01357          */
01358         for (int k = 0; k < m_kk; k++) {
01359           double z_k = charge(k);
01360           if (fabs (z_k) > 0.0001) {
01361             m_B_Dot[k] = bdot_common;
01362           } else {
01363             m_B_Dot[k] = 0.0;
01364           }
01365         }
01366       }
01367 
01368       /*
01369        * Look for Parameters for the Maximum Ionic Strength
01370        */
01371       if (acNode.hasChild("maxIonicStrength")) {
01372         m_maxIionicStrength = getFloat(acNode, "maxIonicStrength");
01373 #ifdef DEBUG_HKM_NOT
01374         cout << "m_maxIionicStrength = "
01375              <<m_maxIionicStrength << endl;
01376 #endif
01377       }
01378 
01379       /*
01380        * Look for Helgeson Parameters
01381        */
01382       if (acNode.hasChild("UseHelgesonFixedForm")) {
01383         m_useHelgesonFixedForm = true; 
01384       } else {
01385         m_useHelgesonFixedForm = false; 
01386       }
01387 
01388       /*
01389        * Look for parameters for the Ionic radius
01390        */
01391       if (acNode.hasChild("ionicRadius")) {
01392         XML_Node& irNode = acNode.child("ionicRadius");
01393 
01394         std::string Aunits = "";
01395         double Afactor = 1.0;
01396         if (irNode.hasAttrib("units")) {
01397           std::string Aunits = irNode.attrib("units");
01398           Afactor = toSI(Aunits); 
01399         }
01400 
01401         if (irNode.hasAttrib("default")) {
01402           std::string ads = irNode.attrib("default");
01403           double ad = fpValue(ads);
01404           for (int k = 0; k < m_kk; k++) {
01405             m_Aionic[k] = ad * Afactor;
01406           }
01407         }
01408 
01409         /*
01410          * If the Debye-Huckel form is BDOT_AK, we can
01411          * have separate values for the denominator's ionic
01412          * size. -> That's how the activity coefficient is
01413          * parameterized. In this case only do we allow the
01414          * code to read in these parameters.
01415          */
01416         if (m_formDH == DHFORM_BDOT_AK) {
01417           /*
01418            * Define a string-string map, and interpret the
01419            * value of the xml element as binary pairs separated
01420            * by colons, e.g.:
01421            *      Na+:3.0
01422            *      Cl-:4.0
01423            *      H+:9.0
01424            *      OH-:3.5
01425            * Read them into the map.
01426            */
01427           map<string, string> m;
01428           getMap(irNode, m);
01429           /*
01430            * Iterate over the map pairs, interpreting the 
01431            * first string as a species in the current phase.
01432            * If no match is made, silently ignore the
01433            * lack of agreement (HKM -> may be changed in the
01434            * future).
01435            */
01436           map<std::string,std::string>::const_iterator _b = m.begin();
01437           for (; _b != m.end(); ++_b) {
01438             int kk = speciesIndex(_b->first);
01439             if (kk < 0) {
01440               //throw CanteraError(
01441               //   "DebyeHuckel::initThermoXML error",
01442               //   "no species match was found"
01443               //   );
01444             } else {
01445               m_Aionic[kk] = fpValue(_b->second) * Afactor;
01446             }
01447           }
01448         }
01449       }
01450       /*
01451        * Get the matrix of coefficients for the Beta
01452        * binary interaction parameters. We assume here that
01453        * this matrix is symmetric, so that we only have to
01454        * input 1/2 of the values.
01455        */
01456       if (acNode.hasChild("DHBetaMatrix")) {
01457         if (m_formDH == DHFORM_BETAIJ ||
01458             m_formDH == DHFORM_PITZER_BETAIJ) {
01459           XML_Node& irNode = acNode.child("DHBetaMatrix");
01460           const vector<string>& sn = speciesNames();
01461           getMatrixValues(irNode, sn, sn, m_Beta_ij, true, true);
01462         } else {
01463           throw CanteraError("DebyeHuckel::initThermoXML:",
01464                              "DHBetaMatrix found for wrong type");
01465         }
01466       }
01467 
01468       /*
01469        * Fill in parameters for the calculation of the 
01470        * stoichiometric Ionic Strength
01471        *
01472        * The default is that stoich charge is the same as the
01473        * regular charge.
01474        */
01475       m_speciesCharge_Stoich.resize(m_kk, 0.0);
01476       for (k = 0; k < m_kk; k++) {
01477         m_speciesCharge_Stoich[k] = m_speciesCharge[k];
01478       }
01479       /*
01480        * First look at the species database.
01481        *  -> Look for the subelement "stoichIsMods"
01482        *     in each of the species SS databases.
01483        */
01484       std::vector<const XML_Node *> xspecies= speciesData(); 
01485       std::string kname, jname;
01486       int jj = xspecies.size();
01487       for (k = 0; k < m_kk; k++) {
01488         int jmap = -1;
01489         kname = speciesName(k);
01490         for (int j = 0; j < jj; j++) {
01491           const XML_Node& sp = *xspecies[j];
01492           jname = sp["name"];
01493           if (jname == kname) {
01494             jmap = j;
01495             break;
01496           }
01497         }
01498         if (jmap > -1) {
01499           const XML_Node& sp = *xspecies[jmap];
01500           if (sp.hasChild("stoichIsMods")) {
01501             double val = getFloat(sp, "stoichIsMods");
01502             m_speciesCharge_Stoich[k] = val;
01503           }
01504         }
01505       }
01506       
01507       /*
01508        * Now look at the activity coefficient database
01509        */
01510       if (acNodePtr) {
01511         if (acNodePtr->hasChild("stoichIsMods")) {
01512           XML_Node& sIsNode = acNodePtr->child("stoichIsMods");
01513 
01514           map<std::string, std::string> msIs;
01515           getMap(sIsNode, msIs);
01516           map<std::string,std::string>::const_iterator _b = msIs.begin();
01517           for (; _b != msIs.end(); ++_b) {
01518             int kk = speciesIndex(_b->first);
01519             if (kk < 0) {
01520               //throw CanteraError(
01521               //   "DebyeHuckel::initThermoXML error",
01522               //   "no species match was found"
01523               //   );
01524             } else {
01525               double val = fpValue(_b->second);
01526               m_speciesCharge_Stoich[kk] = val;
01527             }
01528           }
01529         }
01530       }
01531     }
01532 
01533     /*
01534      * Fill in the vector specifying the electrolyte species
01535      * type
01536      *
01537      *   First fill in default values. Everthing is either
01538      *   a charge species, a nonpolar neutral, or the solvent.
01539      */
01540     for (k = 0; k < m_kk; k++) {
01541       if (fabs(m_speciesCharge[k]) > 0.0001) {
01542         m_electrolyteSpeciesType[k] = cEST_chargedSpecies;
01543         if (fabs(m_speciesCharge_Stoich[k] - m_speciesCharge[k])
01544             > 0.0001) {
01545           m_electrolyteSpeciesType[k] = cEST_weakAcidAssociated;
01546         }
01547       } else if (fabs(m_speciesCharge_Stoich[k]) > 0.0001) {
01548         m_electrolyteSpeciesType[k] = cEST_weakAcidAssociated;
01549       } else {
01550         m_electrolyteSpeciesType[k] = cEST_nonpolarNeutral;
01551       }
01552     }
01553     m_electrolyteSpeciesType[m_indexSolvent] = cEST_solvent;
01554     /*
01555      * First look at the species database.
01556      *  -> Look for the subelement "stoichIsMods"
01557      *     in each of the species SS databases.
01558      */
01559     std::vector<const XML_Node *> xspecies= speciesData(); 
01560     const XML_Node *spPtr = 0;
01561     std::string kname;
01562     for (k = 0; k < m_kk; k++) {
01563       kname = speciesName(k);
01564       spPtr = xspecies[k];
01565       if (!spPtr) {
01566         if (spPtr->hasChild("electrolyteSpeciesType")) {
01567           std::string est = getChildValue(*spPtr, "electrolyteSpeciesType");
01568           if ((m_electrolyteSpeciesType[k] = interp_est(est)) == -1) {
01569             throw CanteraError("DebyeHuckel:initThermoXML",
01570                                "Bad electrolyte type: " + est);
01571           }
01572         }
01573       }
01574     }
01575     /*
01576      * Then look at the phase thermo specification
01577      */
01578     if (acNodePtr) {
01579       if (acNodePtr->hasChild("electrolyteSpeciesType")) {
01580         XML_Node& ESTNode = acNodePtr->child("electrolyteSpeciesType");
01581         map<std::string, std::string> msEST;
01582         getMap(ESTNode, msEST);
01583         map<std::string,std::string>::const_iterator _b = msEST.begin();
01584         for (; _b != msEST.end(); ++_b) {
01585           int kk = speciesIndex(_b->first);
01586           if (kk < 0) {
01587           } else {
01588             std::string est = _b->second;
01589             if ((m_electrolyteSpeciesType[kk] = interp_est(est))  == -1) {
01590               throw CanteraError("DebyeHuckel:initThermoXML",
01591                                  "Bad electrolyte type: " + est);
01592             }
01593           }
01594         }
01595       }
01596     }
01597 
01598     
01599   
01600     /*
01601      * Lastly set the state
01602      */
01603     if (phaseNode.hasChild("state")) {
01604       XML_Node& stateNode = phaseNode.child("state");
01605       setStateFromXML(stateNode);
01606     }
01607 
01608   }
01609 
01610   /*
01611    * @internal
01612    * Set equation of state parameters. The number and meaning of
01613    * these depends on the subclass. 
01614    * @param n number of parameters
01615    * @param c array of \i n coefficients
01616    * 
01617    */
01618   void DebyeHuckel::setParameters(int n, doublereal* const c) {
01619   }
01620 
01621   void DebyeHuckel::getParameters(int &n, doublereal * const c) const {
01622   }
01623 
01624   /*
01625    * Set equation of state parameter values from XML
01626    * entries. This method is called by function importPhase in
01627    * file importCTML.cpp when processing a phase definition in
01628    * an input file. It should be overloaded in subclasses to set
01629    * any parameters that are specific to that particular phase
01630    * model.
01631    *
01632    * @param eosdata An XML_Node object corresponding to
01633    * the "thermo" entry for this phase in the input file.
01634    *
01635    * HKM -> Right now, the parameters are set elsewhere (initThermoXML)
01636    *        It just didn't seem to fit.
01637    */
01638   void DebyeHuckel::setParametersFromXML(const XML_Node& eosdata) {
01639   }
01640     
01641   /*
01642    * Report the molar volume of species k
01643    *
01644    * units - \f$ m^3 kmol^-1 \f$
01645    */
01646   // double DebyeHuckel::speciesMolarVolume(int k) const {
01647   // return m_speciesSize[k];
01648   //}
01649 
01650 
01651   /*
01652    * A_Debye_TP()                              (virtual)
01653    *
01654    *   Returns the A_Debye parameter as a function of temperature
01655    *  and pressure. 
01656    *
01657    *  The default is to assume that it is constant, given
01658    *  in the initialization process and storred in the
01659    *  member double, m_A_Debye
01660    */
01661   double DebyeHuckel::A_Debye_TP(double tempArg, double presArg) const {
01662     double T = temperature(); 
01663     double A;
01664     if (tempArg != -1.0) {
01665       T = tempArg;
01666     }
01667     double P = pressure();
01668     if (presArg != -1.0) {
01669       P = presArg;
01670     }
01671 
01672     switch (m_form_A_Debye) {
01673     case A_DEBYE_CONST:
01674       A = m_A_Debye;
01675       break;
01676     case A_DEBYE_WATER:
01677       A = m_waterProps->ADebye(T, P, 0);
01678       m_A_Debye = A;
01679       break;
01680     default:
01681       printf("shouldn't be here\n");
01682       exit(EXIT_FAILURE);
01683     }
01684     return A;
01685   }
01686 
01687   /*
01688    * dA_DebyedT_TP()                              (virtual)
01689    *
01690    *  Returns the derivative of the A_Debye parameter with
01691    *  respect to temperature as a function of temperature
01692    *  and pressure. 
01693    *
01694    * units = A_Debye has units of sqrt(gmol kg-1).
01695    *         Temp has units of Kelvin.
01696    */
01697   double DebyeHuckel::dA_DebyedT_TP(double tempArg, double presArg) const {
01698     double T = temperature();
01699     if (tempArg != -1.0) {
01700       T = tempArg;
01701     }
01702     double P = pressure();
01703     if (presArg != -1.0) {
01704       P = presArg;
01705     }
01706     double dAdT;
01707     switch (m_form_A_Debye) {
01708     case A_DEBYE_CONST:
01709       dAdT = 0.0;
01710       break;
01711     case A_DEBYE_WATER:
01712       dAdT = m_waterProps->ADebye(T, P, 1);
01713       break;
01714     default:
01715       printf("shouldn't be here\n");
01716       exit(EXIT_FAILURE);
01717     }
01718     return dAdT;
01719   }
01720 
01721   /*
01722    * d2A_DebyedT2_TP()                              (virtual)
01723    *
01724    *  Returns the 2nd derivative of the A_Debye parameter with
01725    *  respect to temperature as a function of temperature
01726    *  and pressure.
01727    * 
01728    * units = A_Debye has units of sqrt(gmol kg-1).
01729    *         Temp has units of Kelvin.
01730    */
01731   double DebyeHuckel::d2A_DebyedT2_TP(double tempArg, double presArg) const {
01732     double T = temperature();
01733     if (tempArg != -1.0) {
01734       T = tempArg;
01735     }
01736     double P = pressure();
01737     if (presArg != -1.0) {
01738       P = presArg;
01739     }
01740     double d2AdT2;
01741     switch (m_form_A_Debye) {
01742     case A_DEBYE_CONST:
01743       d2AdT2 = 0.0;
01744       break;
01745     case A_DEBYE_WATER:
01746       d2AdT2 = m_waterProps->ADebye(T, P, 2);
01747       break;
01748     default:
01749       printf("shouldn't be here\n");
01750       exit(EXIT_FAILURE);
01751     }
01752     return d2AdT2;
01753   }
01754 
01755   /*
01756    * dA_DebyedP_TP()                              (virtual)
01757    *
01758    *  Returns the derivative of the A_Debye parameter with
01759    *  respect to pressure, as a function of temperature
01760    *  and pressure. 
01761    *
01762    * units = A_Debye has units of sqrt(gmol kg-1).
01763    *         Pressure has units of pascals.
01764    */
01765   double DebyeHuckel::dA_DebyedP_TP(double tempArg, double presArg) const {
01766     double T = temperature();
01767     if (tempArg != -1.0) {
01768       T = tempArg;
01769     }
01770     double P = pressure();
01771     if (presArg != -1.0) {
01772       P = presArg;
01773     }
01774     double dAdP;
01775     switch (m_form_A_Debye) {
01776     case A_DEBYE_CONST:
01777       dAdP = 0.0;
01778       break;
01779     case A_DEBYE_WATER:
01780       dAdP = m_waterProps->ADebye(T, P, 3);
01781       break;
01782     default:
01783       printf("shouldn't be here\n");
01784       exit(EXIT_FAILURE);
01785     }
01786     return dAdP;
01787   }
01788 
01789   /*
01790    * ----------- Critical State Properties --------------------------
01791    */
01792 
01793   /*
01794    * ---------- Other Property Functions
01795    */
01796   double DebyeHuckel::AionicRadius(int k) const {
01797     return m_Aionic[k];
01798   }
01799 
01800   /*
01801    * ------------ Private and Restricted Functions ------------------
01802    */
01803 
01804   /*
01805    * Bail out of functions with an error exit if they are not
01806    * implemented.
01807    */
01808   doublereal DebyeHuckel::err(std::string msg) const {
01809     throw CanteraError("DebyeHuckel",
01810                        "Unfinished func called: " + msg );
01811     return 0.0;
01812   }
01813 
01814 
01815   /*
01816    * initLengths():
01817    *
01818    * This internal function adjusts the lengths of arrays based on
01819    * the number of species
01820    */
01821   void DebyeHuckel::initLengths() {
01822     m_kk = nSpecies();
01823  
01824     /*
01825      * Obtain the limits of the temperature from the species
01826      * thermo handler's limits.
01827      */
01828     int leng = m_kk;
01829     m_electrolyteSpeciesType.resize(m_kk, cEST_polarNeutral);
01830     m_speciesSize.resize(leng);
01831     m_Aionic.resize(leng, 0.0);
01832     m_lnActCoeffMolal.resize(leng, 0.0);
01833     m_dlnActCoeffMolaldT.resize(leng, 0.0);
01834     m_d2lnActCoeffMolaldT2.resize(leng, 0.0);
01835     m_dlnActCoeffMolaldP.resize(leng, 0.0);
01836     m_B_Dot.resize(leng, 0.0);
01837     m_expg0_RT.resize(leng, 0.0);
01838     m_pe.resize(leng, 0.0);
01839     m_pp.resize(leng, 0.0);
01840     m_tmpV.resize(leng, 0.0);
01841     if (m_formDH == DHFORM_BETAIJ ||
01842         m_formDH == DHFORM_PITZER_BETAIJ) {
01843       m_Beta_ij.resize(leng, leng, 0.0);
01844     }
01845   }
01846 
01847   /*
01848    * nonpolarActCoeff()                    (private)
01849    *
01850    *   Static function that implements the non-polar species
01851    *   salt-out modifications.
01852    *   Returns the calculated activity coefficients.
01853    */
01854   double DebyeHuckel::_nonpolarActCoeff(double IionicMolality) const {
01855     double I2 = IionicMolality * IionicMolality;
01856     double l10actCoeff = 
01857       m_npActCoeff[0] * IionicMolality + 
01858       m_npActCoeff[1] * I2 +
01859       m_npActCoeff[2] * I2 * IionicMolality;
01860     return pow(10.0 , l10actCoeff); 
01861   }
01862 
01863 
01864   /**
01865    *  _osmoticCoeffHelgesonFixedForm()          
01866    *
01867    *      Formula for the osmotic coefficient that occurs in
01868    *      the GWB. It is originally from Helgeson for a variable
01869    *      NaCl brine. It's to be used with extreme caution.
01870    */
01871   double DebyeHuckel::
01872   _osmoticCoeffHelgesonFixedForm() const {
01873     const double a0 = 1.454;
01874     const double b0 = 0.02236;
01875     const double c0 = 9.380E-3;
01876     const double d0 = -5.362E-4;
01877     double Is = m_IionicMolalityStoich;
01878     if (Is <= 0.0) {
01879       return 0.0;
01880     }
01881     double Is2 = Is * Is;
01882     double bhat = 1.0 + a0 * sqrt(Is);
01883     double func = bhat - 2.0 * log(bhat) - 1.0/bhat;
01884     double v1 = m_A_Debye / (a0 * a0 * a0 * Is) * func;
01885     double oc = 1.0 - v1 + b0 * Is / 2.0 + 2.0 * c0 * Is2 / 3.0
01886       + 3.0 * d0 * Is2 * Is / 4.0;
01887     return oc;
01888   }
01889 
01890 
01891   /*
01892    *  _activityWaterHelgesonFixedForm()          
01893    *
01894    *      Formula for the log of the activity of the water
01895    *      solvent that occurs in
01896    *      the GWB. It is originally from Helgeson for a variable
01897    *      NaCl brine. It's to be used with extreme caution.
01898    */
01899   double DebyeHuckel::
01900   _lnactivityWaterHelgesonFixedForm() const {
01901     /*
01902      * Update the internally storred vector of molalities
01903      */
01904     calcMolalities();
01905     double oc = _osmoticCoeffHelgesonFixedForm();
01906     double sum = 0.0;
01907     for (int k = 0; k < m_kk; k++) {
01908       if (k != m_indexSolvent) {
01909         sum += MAX(m_molalities[k], 0.0);
01910       }
01911     }
01912     if (sum > 2.0 * m_maxIionicStrength) {
01913       sum = 2.0 *  m_maxIionicStrength;
01914     };
01915     double lac = - m_Mnaught * sum * oc;
01916     return lac;
01917   }
01918 
01919   /*
01920    * s_update_lnMolalityActCoeff():
01921    *
01922    *   Using internally stored values, this function calculates
01923    *   the activity coefficients for all species.
01924    *
01925    *   The ln(activity_solvent) is first calculated for the
01926    *   solvent. Then the molar based activity coefficient
01927    *   is calculated and returned.
01928    *
01929    *   ( Note this is the main routine for implementing the
01930    *     activity coefficient formulation.)
01931    */
01932   void DebyeHuckel::s_update_lnMolalityActCoeff() const {
01933     double z_k, zs_k1, zs_k2;
01934     /*
01935      * Update the internally storred vector of molalities
01936      */
01937     calcMolalities();
01938     /*
01939      * Calculate the apparent (real) ionic strength.
01940      *
01941      * Note this is not the stoichiometric ionic strengh,
01942      * where reactions of ions forming neutral salts
01943      * are ignorred in calculating the ionic strength. 
01944      */
01945     m_IionicMolality = 0.0;
01946     for (int k = 0; k < m_kk; k++) {
01947       z_k = m_speciesCharge[k];
01948       m_IionicMolality += m_molalities[k] * z_k * z_k;
01949     }
01950     m_IionicMolality /= 2.0;
01951 
01952     if (m_IionicMolality > m_maxIionicStrength) {
01953       m_IionicMolality = m_maxIionicStrength;
01954     }
01955 
01956     /*
01957      * Calculate the stoichiometric ionic charge
01958      */
01959     m_IionicMolalityStoich = 0.0;
01960     for (int k = 0; k < m_kk; k++) {
01961       z_k = m_speciesCharge[k];
01962       zs_k1 =  m_speciesCharge_Stoich[k];
01963       if (z_k == zs_k1) {
01964         m_IionicMolalityStoich += m_molalities[k] * z_k * z_k;
01965       } else {
01966         zs_k2 = z_k - zs_k1;
01967         m_IionicMolalityStoich
01968           += m_molalities[k] * (zs_k1 * zs_k1 + zs_k2 * zs_k2);
01969       }
01970     }
01971     m_IionicMolalityStoich /= 2.0;
01972 
01973     if (m_IionicMolalityStoich > m_maxIionicStrength) {
01974       m_IionicMolalityStoich = m_maxIionicStrength;
01975     }
01976 
01977     /*
01978      * Possibly update the storred value of the
01979      * Debye-Huckel parameter A_Debye
01980      * This parameter appears on the top of the activity
01981      * coefficient expression.
01982      * It depends on T (and P), as it depends explicity
01983      * on the temperature. Also, the dielectric constant
01984      * is usually a fairly strong function of T, also.
01985      */
01986     m_A_Debye = A_Debye_TP();
01987 
01988     /*
01989      * Calculate a safe value for the mole fraction
01990      * of the solvent
01991      */
01992     double xmolSolvent = moleFraction(m_indexSolvent);
01993     xmolSolvent = MAX(8.689E-3, xmolSolvent);
01994 
01995     int est;
01996     double ac_nonPolar = 1.0;
01997     double numTmp = m_A_Debye * sqrt(m_IionicMolality);
01998     double denomTmp = m_B_Debye * sqrt(m_IionicMolality);
01999     double coeff;
02000     double lnActivitySolvent = 0.0;
02001     double tmp;
02002     double tmpLn;
02003     double y, yp1, sigma;
02004     switch (m_formDH) {
02005     case DHFORM_DILUTE_LIMIT:
02006       for (int k = 0; k < m_kk; k++) {
02007         z_k = m_speciesCharge[k];
02008         m_lnActCoeffMolal[k] = - z_k * z_k * numTmp;
02009       }
02010       lnActivitySolvent = 
02011         (xmolSolvent - 1.0)/xmolSolvent +
02012         2.0 / 3.0 * m_A_Debye * m_Mnaught *
02013         m_IionicMolality * sqrt(m_IionicMolality);
02014       break;
02015 
02016     case DHFORM_BDOT_AK:
02017       ac_nonPolar = _nonpolarActCoeff(m_IionicMolality);
02018       for (int k = 0; k < m_kk; k++) {
02019         est = m_electrolyteSpeciesType[k];
02020         if (est == cEST_nonpolarNeutral) {
02021           m_lnActCoeffMolal[k] = log(ac_nonPolar);
02022         } else {
02023           z_k = m_speciesCharge[k];
02024           m_lnActCoeffMolal[k] = 
02025             - z_k * z_k * numTmp / (1.0 + denomTmp * m_Aionic[k])
02026             + log(10.0) * m_B_Dot[k] * m_IionicMolality;
02027         }
02028       }
02029 
02030       lnActivitySolvent = (xmolSolvent - 1.0)/xmolSolvent;
02031       coeff = 2.0 / 3.0 * m_A_Debye * m_Mnaught 
02032         * sqrt(m_IionicMolality);
02033       tmp = 0.0;
02034       if (denomTmp > 0.0) {
02035         for (int k = 0; k < m_kk; k++) {
02036           if (k != m_indexSolvent || m_Aionic[k] != 0.0) {
02037             y = denomTmp * m_Aionic[k];
02038             yp1 = y + 1.0;
02039             sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1)); 
02040             z_k = m_speciesCharge[k];
02041             tmp += m_molalities[k] * z_k * z_k * sigma / 2.0;
02042           }
02043         }
02044       }
02045       lnActivitySolvent += coeff * tmp;
02046       tmp = 0.0;
02047       for (int k = 0; k < m_kk; k++) {
02048         z_k = m_speciesCharge[k];
02049         if ((k != m_indexSolvent) && (z_k != 0.0)) {
02050           tmp +=  m_B_Dot[k] * m_molalities[k];
02051         }
02052       }
02053       lnActivitySolvent -=
02054         m_Mnaught * log(10.0) * m_IionicMolality * tmp / 2.0;
02055 
02056       /*
02057        * Special section to implement the Helgeson fixed form 
02058        * for the water brine activity coefficient.
02059        */
02060       if (m_useHelgesonFixedForm) {
02061         lnActivitySolvent = _lnactivityWaterHelgesonFixedForm();
02062       }
02063       break;
02064 
02065     case DHFORM_BDOT_ACOMMON:
02066       denomTmp *= m_Aionic[0];
02067       for (int k = 0; k < m_kk; k++) {
02068         z_k = m_speciesCharge[k];
02069         m_lnActCoeffMolal[k] = 
02070           - z_k * z_k * numTmp / (1.0 + denomTmp)
02071           + log(10.0) * m_B_Dot[k] * m_IionicMolality;
02072       }
02073       if (denomTmp > 0.0) {
02074         y = denomTmp;
02075         yp1 = y + 1.0;
02076         sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
02077       } else {
02078         sigma = 0.0;
02079       } 
02080       lnActivitySolvent =  
02081         (xmolSolvent - 1.0)/xmolSolvent + 
02082         2.0 /3.0 * m_A_Debye * m_Mnaught * 
02083         m_IionicMolality * sqrt(m_IionicMolality) * sigma;
02084       tmp = 0.0;
02085       for (int k = 0; k < m_kk; k++) {
02086         z_k = m_speciesCharge[k];
02087         if ((k != m_indexSolvent) && (z_k != 0.0)) {
02088           tmp +=  m_B_Dot[k] * m_molalities[k];
02089         }
02090       }
02091       lnActivitySolvent -=
02092         m_Mnaught * log(10.0) * m_IionicMolality * tmp / 2.0;
02093 
02094       break;
02095 
02096     case DHFORM_BETAIJ:
02097       denomTmp = m_B_Debye * m_Aionic[0];
02098       denomTmp *= sqrt(m_IionicMolality);
02099       lnActivitySolvent =  
02100         (xmolSolvent - 1.0)/xmolSolvent;
02101          
02102       for (int k = 0; k < m_kk; k++) {
02103         if (k != m_indexSolvent) {
02104           z_k = m_speciesCharge[k];
02105           m_lnActCoeffMolal[k] = 
02106             - z_k * z_k * numTmp / (1.0 + denomTmp);
02107           for (int j = 0; j < m_kk; j++) {
02108             double beta =   m_Beta_ij.value(k, j);
02109 #ifdef DEBUG_HKM_NOT
02110             if (beta != 0.0) {
02111               printf("b: k = %d, j = %d, betakj = %g\n",
02112                      k, j, beta);
02113             }
02114 #endif
02115             m_lnActCoeffMolal[k] += 2.0 * m_molalities[j] * beta;
02116           }
02117         }
02118       }
02119       if (denomTmp > 0.0) {
02120         y = denomTmp;
02121         yp1 = y + 1.0;
02122         sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 -2.0*log(yp1));
02123       } else {
02124         sigma = 0.0;
02125       }
02126       lnActivitySolvent =  
02127         (xmolSolvent - 1.0)/xmolSolvent + 
02128         2.0 /3.0 * m_A_Debye * m_Mnaught * 
02129         m_IionicMolality * sqrt(m_IionicMolality) * sigma;
02130       tmp = 0.0;
02131       for (int k = 0; k < m_kk; k++) {
02132         for (int j = 0; j < m_kk; j++) {
02133           tmp +=
02134             m_Beta_ij.value(k, j) * m_molalities[k] * m_molalities[j];
02135         }
02136       }
02137       lnActivitySolvent -= m_Mnaught * tmp;
02138       break;
02139 
02140     case DHFORM_PITZER_BETAIJ:
02141       denomTmp = m_B_Debye * sqrt(m_IionicMolality);
02142       denomTmp *= m_Aionic[0];
02143       numTmp = m_A_Debye * sqrt(m_IionicMolality);
02144       tmpLn = log(1.0 + denomTmp);
02145       for (int k = 0; k < m_kk; k++) {
02146         if (k != m_indexSolvent) {
02147           z_k = m_speciesCharge[k];
02148           m_lnActCoeffMolal[k] = 
02149             - z_k * z_k * numTmp / 3.0 / (1.0 + denomTmp);
02150           m_lnActCoeffMolal[k] +=
02151             - 2.0 * z_k * z_k * m_A_Debye * tmpLn / 
02152             (3.0 * m_B_Debye * m_Aionic[0]);
02153           for (int j = 0; j < m_kk; j++) {
02154             m_lnActCoeffMolal[k] += 2.0 * m_molalities[j] * 
02155               m_Beta_ij.value(k, j);
02156           }
02157         }
02158       }
02159       sigma = 1.0 / (1.0 + denomTmp); 
02160       lnActivitySolvent =
02161         (xmolSolvent - 1.0)/xmolSolvent + 
02162         2.0 /3.0 * m_A_Debye * m_Mnaught * 
02163         m_IionicMolality * sqrt(m_IionicMolality) * sigma;
02164       tmp = 0.0;
02165       for (int k = 0; k < m_kk; k++) {
02166         for (int j = 0; j < m_kk; j++) {
02167           tmp +=
02168             m_Beta_ij.value(k, j) * m_molalities[k] * m_molalities[j];
02169         }
02170       }
02171       lnActivitySolvent -= m_Mnaught * tmp;
02172       break;
02173 
02174     default:
02175       printf("ERROR\n");
02176       exit(EXIT_FAILURE);
02177     }
02178     /*
02179      * Above, we calculated the ln(activitySolvent). Translate that
02180      * into the molar-based activity coefficient by dividing by
02181      * the solvent mole fraction. Solvents are not on the molality
02182      * scale.
02183      */ 
02184     xmolSolvent = moleFraction(m_indexSolvent);
02185     m_lnActCoeffMolal[m_indexSolvent] = 
02186       lnActivitySolvent - log(xmolSolvent);
02187   }
02188 
02189   /*
02190    * s_update_dMolalityActCoeff_dT()         (private, const )
02191    *
02192    *   Using internally stored values, this function calculates
02193    *   the temperature derivative of the logarithm of the
02194    *   activity coefficient for all species in the mechanism.
02195    *
02196    *   We assume that the activity coefficients are current.
02197    *
02198    *   solvent activity coefficient is on the molality
02199    *   scale. It's derivative is too.
02200    */
02201   void DebyeHuckel::s_update_dlnMolalityActCoeff_dT() const {
02202     double z_k, coeff, tmp, y, yp1, sigma, tmpLn;
02203     int k;
02204     // First we store dAdT explicitly here
02205     double dAdT =  dA_DebyedT_TP();
02206     if (dAdT == 0.0) {
02207       for (k = 0; k < m_kk; k++) {
02208         m_dlnActCoeffMolaldT[k] = 0.0;
02209       }
02210       return;
02211     }
02212     /*
02213      * Calculate a safe value for the mole fraction
02214      * of the solvent
02215      */
02216     double xmolSolvent = moleFraction(m_indexSolvent);
02217     xmolSolvent = MAX(8.689E-3, xmolSolvent);
02218 
02219 
02220     double sqrtI  = sqrt(m_IionicMolality);
02221     double numdAdTTmp = dAdT * sqrtI;
02222     double denomTmp = m_B_Debye * sqrtI;
02223     double d_lnActivitySolvent_dT = 0;
02224 
02225     switch (m_formDH) {
02226     case DHFORM_DILUTE_LIMIT:
02227       for (int k = 1; k < m_kk; k++) {
02228         m_dlnActCoeffMolaldT[k] = 
02229           m_lnActCoeffMolal[k] * dAdT / m_A_Debye;
02230       }
02231       d_lnActivitySolvent_dT =  2.0 / 3.0 * dAdT * m_Mnaught *
02232         m_IionicMolality * sqrt(m_IionicMolality);
02233       m_dlnActCoeffMolaldT[m_indexSolvent] =  d_lnActivitySolvent_dT;
02234       break;
02235 
02236     case DHFORM_BDOT_AK:
02237       for (int k = 0; k < m_kk; k++) {
02238         z_k = m_speciesCharge[k];
02239         m_dlnActCoeffMolaldT[k] =
02240           - z_k * z_k * numdAdTTmp / (1.0 + denomTmp * m_Aionic[k]);
02241       }
02242 
02243       m_dlnActCoeffMolaldT[m_indexSolvent] = 0.0;
02244           
02245       coeff = 2.0 / 3.0 * dAdT * m_Mnaught * sqrtI;
02246       tmp = 0.0;
02247       if (denomTmp > 0.0) {
02248         for (int k = 0; k < m_kk; k++) {
02249           y = denomTmp * m_Aionic[k];
02250           yp1 = y + 1.0;
02251           sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1)); 
02252           z_k = m_speciesCharge[k];
02253           tmp += m_molalities[k] * z_k * z_k * sigma / 2.0;
02254         }
02255       }
02256       m_dlnActCoeffMolaldT[m_indexSolvent] += coeff * tmp;
02257       break;
02258 
02259     case DHFORM_BDOT_ACOMMON:
02260       denomTmp *= m_Aionic[0];
02261       for (int k = 0; k < m_kk; k++) {
02262         z_k = m_speciesCharge[k];
02263         m_dlnActCoeffMolaldT[k] = 
02264           - z_k * z_k * numdAdTTmp / (1.0 + denomTmp);
02265       }
02266       if (denomTmp > 0.0) {
02267         y = denomTmp;
02268         yp1 = y + 1.0;
02269         sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1)); 
02270       } else {
02271         sigma = 0.0;
02272       }
02273       m_dlnActCoeffMolaldT[m_indexSolvent] =
02274         2.0 /3.0 * dAdT * m_Mnaught * 
02275         m_IionicMolality * sqrtI * sigma;
02276       break;
02277 
02278     case DHFORM_BETAIJ:
02279       denomTmp *= m_Aionic[0];
02280       for (int k = 0; k < m_kk; k++) {
02281         if (k != m_indexSolvent) {
02282           z_k = m_speciesCharge[k];
02283           m_dlnActCoeffMolaldT[k] = 
02284             - z_k * z_k * numdAdTTmp / (1.0 + denomTmp);
02285         }
02286       }
02287       if (denomTmp > 0.0) {
02288         y = denomTmp;
02289         yp1 = y + 1.0;
02290         sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1)); 
02291       } else {
02292         sigma = 0.0;
02293       }
02294       m_dlnActCoeffMolaldT[m_indexSolvent] =
02295         2.0 /3.0 * dAdT * m_Mnaught * 
02296         m_IionicMolality * sqrtI * sigma;
02297       break;
02298 
02299     case DHFORM_PITZER_BETAIJ:
02300       denomTmp *= m_Aionic[0];
02301       tmpLn = log(1.0 + denomTmp);
02302       for (int k = 0; k < m_kk; k++) {
02303         if (k != m_indexSolvent) {
02304           z_k = m_speciesCharge[k];
02305           m_dlnActCoeffMolaldT[k] = 
02306             - z_k * z_k * numdAdTTmp / (1.0 + denomTmp)
02307             - 2.0 * z_k * z_k * dAdT * tmpLn 
02308             / (m_B_Debye * m_Aionic[0]);
02309           m_dlnActCoeffMolaldT[k] /= 3.0; 
02310         }
02311       }
02312 
02313       sigma = 1.0 / ( 1.0 + denomTmp);
02314       m_dlnActCoeffMolaldT[m_indexSolvent] =
02315         2.0 /3.0 * dAdT * m_Mnaught * 
02316         m_IionicMolality * sqrtI * sigma;
02317       break;
02318 
02319     default:
02320       printf("ERROR\n");
02321       exit(EXIT_FAILURE);
02322       break;
02323     }
02324 
02325  
02326   }
02327 
02328   /*
02329    * s_update_d2lnMolalityActCoeff_dT2()         (private, const )
02330    *
02331    *   Using internally stored values, this function calculates
02332    *   the temperature 2nd derivative of the logarithm of the
02333    *   activity coefficient
02334    *   for all species in the mechanism.
02335    *
02336    *   We assume that the activity coefficients are current.
02337    *
02338    *   solvent activity coefficient is on the molality
02339    *   scale. It's derivatives are too.
02340    */
02341   void DebyeHuckel::s_update_d2lnMolalityActCoeff_dT2() const {
02342     double z_k, coeff, tmp, y, yp1, sigma, tmpLn;
02343     int k;
02344     double dAdT =  dA_DebyedT_TP();
02345     double d2AdT2 = d2A_DebyedT2_TP();
02346     if (d2AdT2 == 0.0 && dAdT == 0.0) {
02347       for (k = 0; k < m_kk; k++) {
02348         m_d2lnActCoeffMolaldT2[k] = 0.0;
02349       }
02350       return;
02351     }
02352 
02353     /*
02354      * Calculate a safe value for the mole fraction
02355      * of the solvent
02356      */
02357     double xmolSolvent = moleFraction(m_indexSolvent);
02358     xmolSolvent = MAX(8.689E-3, xmolSolvent);
02359 
02360 
02361     double sqrtI  = sqrt(m_IionicMolality);
02362     double numd2AdT2Tmp = d2AdT2 * sqrtI;
02363     double denomTmp = m_B_Debye * sqrtI;
02364 
02365     switch (m_formDH) {
02366     case DHFORM_DILUTE_LIMIT:
02367       for (int k = 0; k < m_kk; k++) {
02368         m_d2lnActCoeffMolaldT2[k] =
02369           m_lnActCoeffMolal[k] * d2AdT2 / m_A_Debye;
02370       }
02371       break;
02372 
02373     case DHFORM_BDOT_AK:
02374       for (int k = 0; k < m_kk; k++) {
02375         z_k = m_speciesCharge[k];
02376         m_d2lnActCoeffMolaldT2[k] =
02377           - z_k * z_k * numd2AdT2Tmp / (1.0 + denomTmp * m_Aionic[k]);
02378       }
02379 
02380       m_d2lnActCoeffMolaldT2[m_indexSolvent] = 0.0;
02381           
02382       coeff = 2.0 / 3.0 * d2AdT2 * m_Mnaught * sqrtI;
02383       tmp = 0.0;
02384       if (denomTmp > 0.0) {
02385         for (int k = 0; k < m_kk; k++) {
02386           y = denomTmp * m_Aionic[k];
02387           yp1 = y + 1.0;
02388           sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1)); 
02389           z_k = m_speciesCharge[k];
02390           tmp += m_molalities[k] * z_k * z_k * sigma / 2.0;
02391         }
02392       }
02393       m_d2lnActCoeffMolaldT2[m_indexSolvent] += coeff * tmp;
02394       break;
02395 
02396     case DHFORM_BDOT_ACOMMON:
02397       denomTmp *= m_Aionic[0];
02398       for (int k = 0; k < m_kk; k++) {
02399         z_k = m_speciesCharge[k];
02400         m_d2lnActCoeffMolaldT2[k] = 
02401           - z_k * z_k * numd2AdT2Tmp / (1.0 + denomTmp);
02402       }
02403       if (denomTmp > 0.0) {
02404         y = denomTmp;
02405         yp1 = y + 1.0;
02406         sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
02407       } else {
02408         sigma = 0.0;
02409       }
02410       m_d2lnActCoeffMolaldT2[m_indexSolvent] =
02411         2.0 /3.0 * d2AdT2 * m_Mnaught *
02412         m_IionicMolality * sqrtI * sigma;
02413       break;
02414 
02415     case DHFORM_BETAIJ:
02416       denomTmp *= m_Aionic[0];
02417       for (int k = 0; k < m_kk; k++) {
02418         if (k != m_indexSolvent) {
02419           z_k = m_speciesCharge[k];
02420           m_d2lnActCoeffMolaldT2[k] = 
02421             - z_k * z_k * numd2AdT2Tmp / (1.0 + denomTmp);
02422         }
02423       }
02424       if (denomTmp > 0.0) {
02425         y = denomTmp;
02426         yp1 = y + 1.0;
02427         sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 -2.0*log(yp1));
02428       } else {
02429         sigma = 0.0;
02430       } 
02431       m_d2lnActCoeffMolaldT2[m_indexSolvent] =
02432         2.0 /3.0 * d2AdT2 * m_Mnaught * 
02433         m_IionicMolality * sqrtI * sigma;
02434       break;
02435 
02436     case DHFORM_PITZER_BETAIJ:
02437       denomTmp *= m_Aionic[0];
02438       tmpLn = log(1.0 + denomTmp);
02439       for (int k = 0; k < m_kk; k++) {
02440         if (k != m_indexSolvent) {
02441           z_k = m_speciesCharge[k];
02442           m_d2lnActCoeffMolaldT2[k] = 
02443             - z_k * z_k * numd2AdT2Tmp / (1.0 + denomTmp)
02444             - 2.0 * z_k * z_k * d2AdT2 * tmpLn 
02445             / (m_B_Debye * m_Aionic[0]);
02446           m_d2lnActCoeffMolaldT2[k] /= 3.0; 
02447         }
02448       }
02449 
02450       sigma = 1.0 / ( 1.0 + denomTmp);
02451       m_d2lnActCoeffMolaldT2[m_indexSolvent] =
02452         2.0 /3.0 * d2AdT2 * m_Mnaught * 
02453         m_IionicMolality * sqrtI * sigma;
02454       break;
02455 
02456     default:
02457       printf("ERROR\n");
02458       exit(EXIT_FAILURE);
02459       break;
02460     }
02461   }
02462 
02463   /*
02464    * s_update_dlnMolalityActCoeff_dP()         (private, const )
02465    *
02466    *   Using internally stored values, this function calculates
02467    *   the pressure derivative of the logarithm of the
02468    *   activity coefficient for all species in the mechanism.
02469    *
02470    *   We assume that the activity coefficients, molalities,
02471    *   and A_Debye are current.
02472    *
02473    *   solvent activity coefficient is on the molality
02474    *   scale. It's derivatives are too.
02475    */
02476   void DebyeHuckel::s_update_dlnMolalityActCoeff_dP() const {
02477     double z_k, coeff, tmp, y, yp1, sigma, tmpLn;
02478     int k, est;
02479     double dAdP =  dA_DebyedP_TP();
02480     if (dAdP == 0.0) {
02481       for (k = 0; k < m_kk; k++) {
02482         m_dlnActCoeffMolaldP[k] = 0.0;
02483       }
02484       return;
02485     }
02486     /*
02487      * Calculate a safe value for the mole fraction
02488      * of the solvent
02489      */
02490     double xmolSolvent = moleFraction(m_indexSolvent);
02491     xmolSolvent = MAX(8.689E-3, xmolSolvent);
02492 
02493 
02494     double sqrtI  = sqrt(m_IionicMolality);
02495     double numdAdPTmp = dAdP * sqrtI;
02496     double denomTmp = m_B_Debye * sqrtI;
02497 
02498     switch (m_formDH) {
02499     case DHFORM_DILUTE_LIMIT:
02500       for (int k = 0; k < m_kk; k++) {
02501         m_dlnActCoeffMolaldP[k] = 
02502           m_lnActCoeffMolal[k] * dAdP / m_A_Debye;
02503       }
02504       break;
02505 
02506     case DHFORM_BDOT_AK:
02507       for (int k = 0; k < m_kk; k++) {
02508         est = m_electrolyteSpeciesType[k];
02509         if (est == cEST_nonpolarNeutral) {
02510           m_lnActCoeffMolal[k] = 0.0;
02511         } else {
02512           z_k = m_speciesCharge[k];
02513           m_dlnActCoeffMolaldP[k] =
02514             - z_k * z_k * numdAdPTmp / (1.0 + denomTmp * m_Aionic[k]);
02515         }
02516       }
02517 
02518       m_dlnActCoeffMolaldP[m_indexSolvent] = 0.0;
02519           
02520       coeff = 2.0 / 3.0 * dAdP * m_Mnaught * sqrtI;
02521       tmp = 0.0;
02522       if (denomTmp > 0.0) {
02523         for (int k = 0; k < m_kk; k++) {
02524           y = denomTmp * m_Aionic[k];
02525           yp1 = y + 1.0;
02526           sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1)); 
02527           z_k = m_speciesCharge[k];
02528           tmp += m_molalities[k] * z_k * z_k * sigma / 2.0;
02529         }
02530       }
02531       m_dlnActCoeffMolaldP[m_indexSolvent] += coeff * tmp;
02532       break;
02533 
02534     case DHFORM_BDOT_ACOMMON:
02535       denomTmp *= m_Aionic[0];
02536       for (int k = 0; k < m_kk; k++) {
02537         z_k = m_speciesCharge[k];
02538         m_dlnActCoeffMolaldP[k] = 
02539           - z_k * z_k * numdAdPTmp / (1.0 + denomTmp);
02540       }
02541       if (denomTmp > 0.0) {
02542         y = denomTmp;
02543         yp1 = y + 1.0;
02544         sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1)); 
02545       } else {
02546         sigma = 0.0;
02547       }
02548       m_dlnActCoeffMolaldP[m_indexSolvent] =
02549         2.0 /3.0 * dAdP * m_Mnaught * 
02550         m_IionicMolality * sqrtI * sigma;
02551       break;
02552 
02553     case DHFORM_BETAIJ:
02554       denomTmp *= m_Aionic[0];
02555       for (int k = 0; k < m_kk; k++) {
02556         if (k != m_indexSolvent) {
02557           z_k = m_speciesCharge[k];
02558           m_dlnActCoeffMolaldP[k] = 
02559             - z_k * z_k * numdAdPTmp / (1.0 + denomTmp);
02560         }
02561       }
02562       if (denomTmp > 0.0) {
02563         y = denomTmp;
02564         yp1 = y + 1.0;
02565         sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1)); 
02566       } else {
02567         sigma = 0.0;
02568       }
02569       m_dlnActCoeffMolaldP[m_indexSolvent] =
02570         2.0 /3.0 * dAdP * m_Mnaught * 
02571         m_IionicMolality * sqrtI * sigma;
02572       break;
02573 
02574     case DHFORM_PITZER_BETAIJ:
02575       denomTmp *= m_Aionic[0];
02576       tmpLn = log(1.0 + denomTmp);
02577       for (int k = 0; k < m_kk; k++) {
02578         if (k != m_indexSolvent) {
02579           z_k = m_speciesCharge[k];
02580           m_dlnActCoeffMolaldP[k] = 
02581             - z_k * z_k * numdAdPTmp / (1.0 + denomTmp)
02582             - 2.0 * z_k * z_k * dAdP * tmpLn 
02583             / (m_B_Debye * m_Aionic[0]);
02584           m_dlnActCoeffMolaldP[k] /= 3.0; 
02585         }
02586       }
02587 
02588       sigma = 1.0 / ( 1.0 + denomTmp);
02589       m_dlnActCoeffMolaldP[m_indexSolvent] =
02590         2.0 /3.0 * dAdP * m_Mnaught * 
02591         m_IionicMolality * sqrtI * sigma;
02592       break;
02593 
02594     default:
02595       printf("ERROR\n");
02596       exit(EXIT_FAILURE);
02597       break;
02598     }
02599   }
02600  
02601   /*
02602    * Updates the standard state thermodynamic functions at the current T and P of the solution.
02603    *
02604    * @internal
02605    *
02606    * This function gets called for every call to functions in this
02607    * class. It checks to see whether the temperature or pressure has changed and
02608    * thus the ss thermodynamics functions for all of the species
02609    * must be recalculated.
02610    */                    
02611   //  void DebyeHuckel::_updateStandardStateThermo() const {
02612   // doublereal tnow = temperature();
02613   // doublereal pnow = m_Pcurrent;
02614   // if (m_waterSS) {
02615   //   m_waterSS->setTempPressure(tnow, pnow);
02616   // }
02617   // m_VPSS_ptr->setState_TP(tnow, pnow);
02618     // VPStandardStateTP::updateStandardStateThermo();
02619 
02620   //}
02621  
02622 }
Generated by  doxygen 1.6.3