HMWSoln_input.cpp

Go to the documentation of this file.
00001 /**
00002  *  @file HMWSoln_input.cpp
00003  *    Definitions for the %HMWSoln ThermoPhase object, which models concentrated
00004  *    electrolyte solutions
00005  *    (see \ref thermoprops and \link Cantera::HMWSoln HMWSoln \endlink) .
00006  *
00007  * This file contains definitions for reading in the interaction terms
00008  * in the formulation.
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: HMWSoln_input.cpp 279 2009-12-05 19:08:43Z hkmoffa $
00017  */
00018 
00019 #include "HMWSoln.h"
00020 #include "ThermoFactory.h"
00021 #include "WaterProps.h"
00022 #include "PDSS_Water.h"
00023 #include <cstring>
00024 #include <cstdlib>
00025 
00026 using namespace std;
00027 
00028 namespace Cantera {
00029 
00030 
00031   //! utility function to assign an integer value from a string
00032   //! for the ElectrolyteSpeciesType field.
00033   /*!
00034    *  @param estString string name of the electrolyte species type
00035    */
00036   int HMWSoln::interp_est(std::string estString) {
00037     const char *cc = estString.c_str();
00038     string lcs = lowercase(estString);
00039     const char *ccl = lcs.c_str();
00040     if (!strcmp(ccl, "solvent")) {
00041       return cEST_solvent;
00042     } else if (!strcmp(ccl, "chargedspecies")) {
00043       return cEST_chargedSpecies;
00044     } else if (!strcmp(ccl, "weakacidassociated")) {
00045       return cEST_weakAcidAssociated;
00046     } else if (!strcmp(ccl, "strongacidassociated")) {
00047       return cEST_strongAcidAssociated;
00048     } else if (!strcmp(ccl, "polarneutral")) {
00049       return cEST_polarNeutral;
00050     } else if (!strcmp(ccl, "nonpolarneutral")) {
00051       return cEST_nonpolarNeutral;
00052     }
00053     int retn, rval;
00054     if ((retn = sscanf(cc, "%d", &rval)) != 1) {
00055       return -1;
00056     }
00057     return rval;
00058   }
00059 
00060   /*
00061    * Process an XML node called "SimpleSaltParameters. 
00062    * This node contains all of the parameters necessary to describe
00063    * the Pitzer model for that particular binary salt.
00064    * This function reads the XML file and writes the coefficients
00065    * it finds to an internal data structures.
00066    */
00067   void HMWSoln::readXMLBinarySalt(XML_Node &BinSalt) {
00068     string xname = BinSalt.name();
00069     if (xname != "binarySaltParameters") {
00070       throw CanteraError("HMWSoln::readXMLBinarySalt",
00071                          "Incorrect name for processing this routine: " + xname);
00072     }
00073     double *charge = DATA_PTR(m_speciesCharge);
00074     string stemp;
00075     int nParamsFound, i;
00076     vector_fp vParams;
00077     string iName = BinSalt.attrib("cation");
00078     if (iName == "") {
00079       throw CanteraError("HMWSoln::readXMLBinarySalt", "no cation attrib");
00080     }
00081     string jName = BinSalt.attrib("anion");
00082     if (jName == "") {
00083       throw CanteraError("HMWSoln::readXMLBinarySalt", "no anion attrib");
00084     }
00085     /*
00086      * Find the index of the species in the current phase. It's not
00087      * an error to not find the species
00088      */
00089     int iSpecies = speciesIndex(iName);
00090     if (iSpecies < 0) {
00091       return;
00092     }
00093     string ispName = speciesName(iSpecies);
00094     if (charge[iSpecies] <= 0) {
00095       throw CanteraError("HMWSoln::readXMLBinarySalt", "cation charge problem");
00096     }
00097     int jSpecies = speciesIndex(jName);
00098     if (jSpecies < 0) {
00099       return;
00100     }
00101     string jspName = speciesName(jSpecies);
00102     if (charge[jSpecies] >= 0) {
00103       throw CanteraError("HMWSoln::readXMLBinarySalt", "anion charge problem");
00104     }
00105         
00106     int n = iSpecies * m_kk + jSpecies;
00107     int counter = m_CounterIJ[n];
00108     int num = BinSalt.nChildren();
00109     for (int iChild = 0; iChild < num; iChild++) {
00110       XML_Node &xmlChild = BinSalt.child(iChild);
00111       stemp = xmlChild.name();
00112       string nodeName = lowercase(stemp);
00113       /*
00114        * Process the binary salt child elements
00115        */
00116       if (nodeName == "beta0") {
00117         /*
00118          * Get the string containing all of the values
00119          */
00120         getFloatArray(xmlChild, vParams, false, "", "beta0");
00121         nParamsFound = vParams.size();
00122         if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) {
00123           if (nParamsFound != 1) {
00124             throw CanteraError("HMWSoln::readXMLBinarySalt::beta0 for " + ispName 
00125                                + "::" + jspName,
00126                                "wrong number of params found");
00127           }
00128           m_Beta0MX_ij[counter] = vParams[0];
00129           m_Beta0MX_ij_coeff(0,counter) = m_Beta0MX_ij[counter];
00130         } else  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
00131           if (nParamsFound != 2) {
00132             throw CanteraError("HMWSoln::readXMLBinarySalt::beta0 for " + ispName 
00133                                + "::" + jspName,
00134                                "wrong number of params found");
00135           }
00136           m_Beta0MX_ij_coeff(0,counter) = vParams[0];
00137           m_Beta0MX_ij_coeff(1,counter) = vParams[1];
00138           m_Beta0MX_ij[counter] = vParams[0];
00139         } else  if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
00140           if (nParamsFound != 5) {
00141             throw CanteraError("HMWSoln::readXMLBinarySalt::beta0 for " + ispName 
00142                                + "::" + jspName,
00143                                "wrong number of params found");
00144           }
00145           for (i = 0; i < nParamsFound; i++) {
00146             m_Beta0MX_ij_coeff(i, counter) = vParams[i];
00147           }
00148           m_Beta0MX_ij[counter] = vParams[0];
00149         }
00150       }
00151       if (nodeName == "beta1") {
00152 
00153         /*
00154          * Get the string containing all of the values
00155          */
00156         getFloatArray(xmlChild, vParams, false, "", "beta1");
00157         nParamsFound = vParams.size();
00158         if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) {
00159           if (nParamsFound != 1) {
00160             throw CanteraError("HMWSoln::readXMLBinarySalt::beta1 for " + ispName 
00161                                + "::" + jspName,
00162                                "wrong number of params found");
00163           }
00164           m_Beta1MX_ij[counter] = vParams[0];
00165           m_Beta1MX_ij_coeff(0,counter) = m_Beta1MX_ij[counter];
00166         } else  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
00167           if (nParamsFound != 2) {
00168             throw CanteraError("HMWSoln::readXMLBinarySalt::beta1 for " + ispName 
00169                                + "::" + jspName,
00170                                "wrong number of params found");
00171           }
00172           m_Beta1MX_ij_coeff(0,counter) = vParams[0];
00173           m_Beta1MX_ij_coeff(1,counter) = vParams[1];
00174           m_Beta1MX_ij[counter] = vParams[0];
00175         } else  if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
00176           if (nParamsFound != 5) {
00177             throw CanteraError("HMWSoln::readXMLBinarySalt::beta1 for " + ispName 
00178                                + "::" + jspName,
00179                                "wrong number of params found");
00180           }
00181           for (i = 0; i < nParamsFound; i++) {
00182             m_Beta1MX_ij_coeff(i, counter) = vParams[i];
00183           }
00184           m_Beta1MX_ij[counter] = vParams[0];
00185         }
00186       }
00187       if (nodeName == "beta2") {
00188         getFloatArray(xmlChild, vParams, false, "", "beta2");
00189         nParamsFound = vParams.size();
00190         if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) {
00191           if (nParamsFound != 1) {
00192             throw CanteraError("HMWSoln::readXMLBinarySalt::beta2 for " + ispName 
00193                                + "::" + jspName,
00194                                "wrong number of params found");
00195           }
00196           m_Beta2MX_ij[counter] = vParams[0];
00197           m_Beta2MX_ij_coeff(0,counter) = m_Beta2MX_ij[counter];
00198         } else  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
00199           if (nParamsFound != 2) {
00200             throw CanteraError("HMWSoln::readXMLBinarySalt::beta2 for " + ispName 
00201                                + "::" + jspName,
00202                                "wrong number of params found");
00203           }
00204           m_Beta2MX_ij_coeff(0,counter) = vParams[0];
00205           m_Beta2MX_ij_coeff(1,counter) = vParams[1];
00206           m_Beta2MX_ij[counter] = vParams[0];
00207         } else  if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
00208           if (nParamsFound != 5) {
00209             throw CanteraError("HMWSoln::readXMLBinarySalt::beta2 for " + ispName 
00210                                + "::" + jspName,
00211                                "wrong number of params found");
00212           }
00213           for (i = 0; i < nParamsFound; i++) {
00214             m_Beta2MX_ij_coeff(i, counter) = vParams[i];
00215           }
00216           m_Beta2MX_ij[counter] = vParams[0];
00217         }
00218 
00219       }
00220       if (nodeName == "cphi") {
00221         /*
00222          * Get the string containing all of the values
00223          */
00224         getFloatArray(xmlChild, vParams, false, "", "Cphi");
00225         nParamsFound = vParams.size();
00226         if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) {
00227           if (nParamsFound != 1) {
00228             throw CanteraError("HMWSoln::readXMLBinarySalt::Cphi for " + ispName 
00229                                + "::" + jspName,
00230                                "wrong number of params found");
00231           }
00232           m_CphiMX_ij[counter] = vParams[0];
00233           m_CphiMX_ij_coeff(0,counter) = m_CphiMX_ij[counter];
00234         } else  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
00235           if (nParamsFound != 2) {
00236             throw CanteraError("HMWSoln::readXMLBinarySalt::Cphi for " + ispName 
00237                                + "::" + jspName,
00238                                "wrong number of params found");
00239           }
00240           m_CphiMX_ij_coeff(0,counter) = vParams[0];
00241           m_CphiMX_ij_coeff(1,counter) = vParams[1];
00242           m_CphiMX_ij[counter] = vParams[0];
00243         } else  if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
00244           if (nParamsFound != 5) {
00245             throw CanteraError("HMWSoln::readXMLBinarySalt::Cphi for " + ispName 
00246                                + "::" + jspName,
00247                                "wrong number of params found");
00248           }
00249           for (i = 0; i < nParamsFound; i++) {
00250             m_CphiMX_ij_coeff(i, counter) = vParams[i];
00251           }
00252           m_CphiMX_ij[counter] = vParams[0];
00253         }
00254       }
00255 
00256       if (nodeName == "alpha1") {
00257         stemp = xmlChild.value();
00258         m_Alpha1MX_ij[counter] = atofCheck(stemp.c_str());
00259       }
00260 
00261       if (nodeName == "alpha2") {
00262         stemp = xmlChild.value();
00263         m_Alpha2MX_ij[counter] = atofCheck(stemp.c_str());
00264       }
00265     }
00266   }
00267 
00268   /**
00269    * Process an XML node called "thetaAnion". 
00270    * This node contains all of the parameters necessary to describe
00271    * the binary interactions between two anions.
00272    */
00273   void HMWSoln::readXMLThetaAnion(XML_Node &BinSalt) {
00274     string xname = BinSalt.name();
00275     vector_fp vParams;
00276     int nParamsFound = 0;
00277     if (xname != "thetaAnion") {
00278       throw CanteraError("HMWSoln::readXMLThetaAnion",
00279                          "Incorrect name for processing this routine: " + xname);
00280     }
00281     double *charge = DATA_PTR(m_speciesCharge);
00282     string stemp;
00283     string ispName = BinSalt.attrib("anion1");
00284     if (ispName == "") {
00285       throw CanteraError("HMWSoln::readXMLThetaAnion", "no anion1 attrib");
00286     }
00287     string jspName = BinSalt.attrib("anion2");
00288     if (jspName == "") {
00289       throw CanteraError("HMWSoln::readXMLThetaAnion", "no anion2 attrib");
00290     }
00291     /*
00292      * Find the index of the species in the current phase. It's not
00293      * an error to not find the species
00294      */
00295     int iSpecies = speciesIndex(ispName);
00296     if (iSpecies < 0) {
00297       return;
00298     }
00299     if (charge[iSpecies] >= 0) {
00300       throw CanteraError("HMWSoln::readXMLThetaAnion", "anion1 charge problem");
00301     }
00302     int jSpecies = speciesIndex(jspName);
00303     if (jSpecies < 0) {
00304       return;
00305     }
00306     if (charge[jSpecies] >= 0) {
00307       throw CanteraError("HMWSoln::readXMLThetaAnion", "anion2 charge problem");
00308     }
00309         
00310     int n = iSpecies * m_kk + jSpecies;
00311     int counter = m_CounterIJ[n];
00312     int num = BinSalt.nChildren();
00313     for (int i = 0; i < num; i++) {
00314       XML_Node &xmlChild = BinSalt.child(i);
00315       stemp = xmlChild.name();
00316       string nodeName = lowercase(stemp);
00317       if (nodeName == "theta") {
00318         getFloatArray(xmlChild, vParams, false, "", stemp);
00319         nParamsFound = vParams.size();
00320         if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) {
00321           if (nParamsFound != 1) {
00322             throw CanteraError("HMWSoln::readXMLThetaAnion::Theta for " + ispName 
00323                                + "::" + jspName,
00324                                "wrong number of params found");
00325           }
00326           m_Theta_ij_coeff(0,counter) = vParams[0];
00327           m_Theta_ij[counter] = vParams[0];
00328         } else  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
00329           if (nParamsFound != 2) {
00330             throw CanteraError("HMWSoln::readXMLThetaAnion::Theta for " + ispName
00331                                + "::" + jspName,
00332                                "wrong number of params found");
00333           }
00334           m_Theta_ij_coeff(0,counter) = vParams[0];
00335           m_Theta_ij_coeff(1,counter) = vParams[1];
00336           m_Theta_ij[counter] = vParams[0];
00337         } else  if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
00338           if (nParamsFound == 1) {
00339             vParams.resize(5, 0.0);
00340             nParamsFound = 5;
00341           } else if (nParamsFound != 5) {
00342             throw CanteraError("HMWSoln::readXMLThetaAnion::Theta for " + ispName
00343                                + "::" + jspName,
00344                                "wrong number of params found");
00345           }
00346           for (i = 0; i < nParamsFound; i++) {
00347             m_Theta_ij_coeff(i, counter) = vParams[i];
00348           }
00349           m_Theta_ij[counter] = vParams[0];
00350         }
00351       }
00352     }
00353   } 
00354 
00355   /**
00356    * Process an XML node called "thetaCation". 
00357    * This node contains all of the parameters necessary to describe
00358    * the binary interactions between two cation.
00359    */
00360   void HMWSoln::readXMLThetaCation(XML_Node &BinSalt) {
00361     string xname = BinSalt.name();
00362     vector_fp vParams;
00363     int nParamsFound = 0;
00364     if (xname != "thetaCation") {
00365       throw CanteraError("HMWSoln::readXMLThetaCation",
00366                          "Incorrect name for processing this routine: " + xname);
00367     }
00368     double *charge = DATA_PTR(m_speciesCharge);
00369     string stemp;
00370     string ispName = BinSalt.attrib("cation1");
00371     if (ispName == "") {
00372       throw CanteraError("HMWSoln::readXMLThetaCation", "no cation1 attrib");
00373     }
00374     string jspName = BinSalt.attrib("cation2");
00375     if (jspName == "") {
00376       throw CanteraError("HMWSoln::readXMLThetaCation", "no cation2 attrib");
00377     }
00378     /*
00379      * Find the index of the species in the current phase. It's not
00380      * an error to not find the species
00381      */
00382     int iSpecies = speciesIndex(ispName);
00383     if (iSpecies < 0) {
00384       return;
00385     }
00386     if (charge[iSpecies] <= 0) {
00387       throw CanteraError("HMWSoln::readXMLThetaCation", "cation1 charge problem");
00388     }
00389     int jSpecies = speciesIndex(jspName);
00390     if (jSpecies < 0) {
00391       return;
00392     }
00393     if (charge[jSpecies] <= 0) {
00394       throw CanteraError("HMWSoln::readXMLThetaCation", "cation2 charge problem");
00395     }
00396         
00397     int n = iSpecies * m_kk + jSpecies;
00398     int counter = m_CounterIJ[n];
00399     int num = BinSalt.nChildren();
00400     for (int i = 0; i < num; i++) {
00401       XML_Node &xmlChild = BinSalt.child(i);
00402       stemp = xmlChild.name();
00403       string nodeName = lowercase(stemp);
00404       if (nodeName == "theta") {
00405         getFloatArray(xmlChild, vParams, false, "", stemp);
00406         nParamsFound = vParams.size();
00407         if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) {
00408           if (nParamsFound != 1) {
00409             throw CanteraError("HMWSoln::readXMLThetaCation::Theta for " + ispName 
00410                                + "::" + jspName,
00411                                "wrong number of params found");
00412           }
00413           m_Theta_ij_coeff(0,counter) = vParams[0];
00414           m_Theta_ij[counter] = vParams[0];
00415         } else  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
00416           if (nParamsFound != 2) {
00417             throw CanteraError("HMWSoln::readXMLThetaCation::Theta for " + ispName
00418                                + "::" + jspName,
00419                                "wrong number of params found");
00420           }
00421           m_Theta_ij_coeff(0,counter) = vParams[0];
00422           m_Theta_ij_coeff(1,counter) = vParams[1];
00423           m_Theta_ij[counter] = vParams[0];
00424         } else  if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
00425           if (nParamsFound == 1) {
00426             vParams.resize(5, 0.0);
00427             nParamsFound = 5;
00428           } else if (nParamsFound != 5) {
00429             throw CanteraError("HMWSoln::readXMLThetaCation::Theta for " + ispName
00430                                + "::" + jspName,
00431                                "wrong number of params found");
00432           }
00433           for (i = 0; i < nParamsFound; i++) {
00434             m_Theta_ij_coeff(i, counter) = vParams[i];
00435           }
00436           m_Theta_ij[counter] = vParams[0];
00437         }
00438       }
00439     }
00440   }
00441 
00442   /*
00443    * Process an XML node called "readXMLPsiCommonCation". 
00444    * This node contains all of the parameters necessary to describe
00445    * the binary interactions between two anions and one common cation.
00446    */
00447   void HMWSoln::readXMLPsiCommonCation(XML_Node &BinSalt) {
00448     string xname = BinSalt.name();
00449     if (xname != "psiCommonCation") {
00450       throw CanteraError("HMWSoln::readXMLPsiCommonCation",
00451                          "Incorrect name for processing this routine: " + xname);
00452     }
00453     double *charge = DATA_PTR(m_speciesCharge);
00454     string stemp;
00455     vector_fp vParams;
00456     int nParamsFound = 0;
00457     string kName = BinSalt.attrib("cation");
00458     if (kName == "") {
00459       throw CanteraError("HMWSoln::readXMLPsiCommonCation", "no cation attrib");
00460     }
00461     string iName = BinSalt.attrib("anion1");
00462     if (iName == "") {
00463       throw CanteraError("HMWSoln::readXMLPsiCommonCation", "no anion1 attrib");
00464     }
00465     string jName = BinSalt.attrib("anion2");
00466     if (jName == "") {
00467       throw CanteraError("HMWSoln::readXMLPsiCommonCation", "no anion2 attrib");
00468     }
00469     /*
00470      * Find the index of the species in the current phase. It's not
00471      * an error to not find the species
00472      */
00473     int kSpecies = speciesIndex(kName);
00474     if (kSpecies < 0) {
00475       return;
00476     }
00477     if (charge[kSpecies] <= 0) {
00478       throw CanteraError("HMWSoln::readXMLPsiCommonCation", 
00479                          "cation charge problem");
00480     }
00481     int iSpecies = speciesIndex(iName);
00482     if (iSpecies < 0) {
00483       return;
00484     }
00485     if (charge[iSpecies] >= 0) {
00486       throw CanteraError("HMWSoln::readXMLPsiCommonCation", 
00487                          "anion1 charge problem");
00488     }
00489     int jSpecies = speciesIndex(jName);
00490     if (jSpecies < 0) {
00491       return;
00492     }
00493     if (charge[jSpecies] >= 0) {
00494       throw CanteraError("HMWSoln::readXMLPsiCommonCation", 
00495                          "anion2 charge problem");
00496     }
00497     
00498     int n = iSpecies * m_kk + jSpecies;
00499     int counter = m_CounterIJ[n];
00500     int num = BinSalt.nChildren();
00501     for (int i = 0; i < num; i++) {
00502       XML_Node &xmlChild = BinSalt.child(i);
00503       stemp = xmlChild.name();
00504       string nodeName = lowercase(stemp);
00505       if (nodeName == "theta") {
00506         stemp = xmlChild.value();
00507         double old = m_Theta_ij[counter];
00508         m_Theta_ij[counter] = atofCheck(stemp.c_str());
00509         if (old != 0.0) {
00510           if (old != m_Theta_ij[counter]) {
00511             throw CanteraError("HMWSoln::readXMLPsiCommonCation",
00512                                "conflicting values");
00513           }
00514         }
00515       }
00516       if (nodeName == "psi") {
00517         getFloatArray(xmlChild, vParams, false, "", stemp);
00518         nParamsFound = vParams.size();
00519         n = iSpecies * m_kk *m_kk + jSpecies * m_kk + kSpecies ;
00520 
00521         if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) {
00522           if (nParamsFound != 1) {
00523             throw CanteraError("HMWSoln::readXMLPsiCommonCation::Psi for "
00524                                + kName + "::" + iName + "::" + jName,
00525                                "wrong number of params found");
00526           }
00527           m_Psi_ijk_coeff(0,n) = vParams[0];
00528           m_Psi_ijk[n] = vParams[0];
00529         } else  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
00530           if (nParamsFound != 2) {
00531             throw CanteraError("HMWSoln::readXMLPsiCation::Psi for "
00532                                + kName + "::" + iName + "::" + jName,
00533                                "wrong number of params found");
00534           }
00535           m_Psi_ijk_coeff(0,n) = vParams[0];
00536           m_Psi_ijk_coeff(1,n) = vParams[1];
00537           m_Psi_ijk[n]         = vParams[0];
00538         } else  if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
00539           if (nParamsFound == 1) {
00540             vParams.resize(5, 0.0);
00541             nParamsFound = 5;
00542           } else if (nParamsFound != 5) {
00543             throw CanteraError("HMWSoln::readXMLPsiCation::Psi for "
00544                                + kName + "::" + iName + "::" + jName,
00545                                "wrong number of params found");
00546           }
00547           for (i = 0; i < nParamsFound; i++) {
00548             m_Psi_ijk_coeff(i, n) = vParams[i];
00549           }
00550           m_Psi_ijk[n] = vParams[0];
00551         }
00552 
00553 
00554         // fill in the duplicate entries
00555         n = iSpecies * m_kk *m_kk + kSpecies * m_kk + jSpecies ;
00556         for (i = 0; i < nParamsFound; i++) {
00557           m_Psi_ijk_coeff(i, n) = vParams[i];
00558         }
00559         m_Psi_ijk[n] = vParams[0];
00560 
00561         n = jSpecies * m_kk *m_kk + iSpecies * m_kk + kSpecies ;
00562         for (i = 0; i < nParamsFound; i++) {
00563           m_Psi_ijk_coeff(i, n) = vParams[i];
00564         }
00565         m_Psi_ijk[n] = vParams[0];
00566 
00567         n = jSpecies * m_kk *m_kk + kSpecies * m_kk + iSpecies ;
00568         for (i = 0; i < nParamsFound; i++) {
00569           m_Psi_ijk_coeff(i, n) = vParams[i];
00570         }
00571         m_Psi_ijk[n] = vParams[0];
00572 
00573         n = kSpecies * m_kk *m_kk + jSpecies * m_kk + iSpecies ;
00574         for (i = 0; i < nParamsFound; i++) {
00575           m_Psi_ijk_coeff(i, n) = vParams[i];
00576         }
00577         m_Psi_ijk[n] = vParams[0];
00578 
00579         n = kSpecies * m_kk *m_kk + iSpecies * m_kk + jSpecies ;
00580         for (i = 0; i < nParamsFound; i++) {
00581           m_Psi_ijk_coeff(i, n) = vParams[i];
00582         }
00583         m_Psi_ijk[n] = vParams[0];
00584       }
00585     }
00586   }
00587    
00588   /**
00589    * Process an XML node called "PsiCommonAnion". 
00590    * This node contains all of the parameters necessary to describe
00591    * the binary interactions between two cations and one common anion.
00592    */
00593   void HMWSoln::readXMLPsiCommonAnion(XML_Node &BinSalt) {
00594     string xname = BinSalt.name();
00595     if (xname != "psiCommonAnion") {
00596       throw CanteraError("HMWSoln::readXMLPsiCommonAnion",
00597                          "Incorrect name for processing this routine: " + xname);
00598     }
00599     double *charge = DATA_PTR(m_speciesCharge);
00600     string stemp;
00601     vector_fp vParams;
00602     int nParamsFound = 0;
00603     string kName = BinSalt.attrib("anion");
00604     if (kName == "") {
00605       throw CanteraError("HMWSoln::readXMLPsiCommonAnion", "no anion attrib");
00606     }
00607     string iName = BinSalt.attrib("cation1");
00608     if (iName == "") {
00609       throw CanteraError("HMWSoln::readXMLPsiCommonAnion", "no cation1 attrib");
00610     }
00611     string jName = BinSalt.attrib("cation2");
00612     if (jName == "") {
00613       throw CanteraError("HMWSoln::readXMLPsiCommonAnion", "no cation2 attrib");
00614     }
00615     /*
00616      * Find the index of the species in the current phase. It's not
00617      * an error to not find the species
00618      */
00619     int kSpecies = speciesIndex(kName);
00620     if (kSpecies < 0) {
00621       return;
00622     }
00623     if (charge[kSpecies] >= 0) {
00624       throw CanteraError("HMWSoln::readXMLPsiCommonAnion", "anion charge problem");
00625     }
00626     int iSpecies = speciesIndex(iName);
00627     if (iSpecies < 0) {
00628       return;
00629     }
00630     if (charge[iSpecies] <= 0) {
00631       throw CanteraError("HMWSoln::readXMLPsiCommonAnion", 
00632                          "cation1 charge problem");
00633     }
00634     int jSpecies = speciesIndex(jName);
00635     if (jSpecies < 0) {
00636       return;
00637     }
00638     if (charge[jSpecies] <= 0) {
00639       throw CanteraError("HMWSoln::readXMLPsiCommonAnion", 
00640                          "cation2 charge problem");
00641     }
00642         
00643     int n = iSpecies * m_kk + jSpecies;
00644     int counter = m_CounterIJ[n];
00645     int num = BinSalt.nChildren();
00646     for (int i = 0; i < num; i++) {
00647       XML_Node &xmlChild = BinSalt.child(i);
00648       stemp = xmlChild.name();
00649       string nodeName = lowercase(stemp);
00650       if (nodeName == "theta") {
00651         stemp = xmlChild.value();
00652         double old = m_Theta_ij[counter];
00653         m_Theta_ij[counter] = atofCheck(stemp.c_str());
00654         if (old != 0.0) {
00655           if (old != m_Theta_ij[counter]) {
00656             throw CanteraError("HMWSoln::readXMLPsiCommonAnion",
00657                                "conflicting values");
00658           }
00659         }
00660       }
00661       if (nodeName == "psi") {
00662 
00663         getFloatArray(xmlChild, vParams, false, "", stemp);
00664         nParamsFound = vParams.size();
00665         n = iSpecies * m_kk *m_kk + jSpecies * m_kk + kSpecies ;
00666 
00667         if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) {
00668           if (nParamsFound != 1) {
00669             throw CanteraError("HMWSoln::readXMLPsiCommonAnion::Psi for "
00670                                + kName + "::" + iName + "::" + jName,
00671                                "wrong number of params found");
00672           }
00673           m_Psi_ijk_coeff(0,n) = vParams[0];
00674           m_Psi_ijk[n] = vParams[0];
00675         } else  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
00676           if (nParamsFound != 2) {
00677             throw CanteraError("HMWSoln::readXMLPsiAnion::Psi for "
00678                                + kName + "::" + iName + "::" + jName,
00679                                "wrong number of params found");
00680           }
00681           m_Psi_ijk_coeff(0,n) = vParams[0];
00682           m_Psi_ijk_coeff(1,n) = vParams[1];
00683           m_Psi_ijk[n]         = vParams[0];
00684         } else  if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
00685           if (nParamsFound == 1) {
00686             vParams.resize(5, 0.0);
00687             nParamsFound = 5;
00688           } else if (nParamsFound != 5) {
00689             throw CanteraError("HMWSoln::readXMLPsiAnion::Psi for "
00690                                + kName + "::" + iName + "::" + jName,
00691                                "wrong number of params found");
00692           }
00693           for (i = 0; i < nParamsFound; i++) {
00694             m_Psi_ijk_coeff(i, n) = vParams[i];
00695           }
00696           m_Psi_ijk[n] = vParams[0];
00697         }
00698 
00699 
00700         // fill in the duplicate entries
00701         n = iSpecies * m_kk *m_kk + kSpecies * m_kk + jSpecies ;
00702         for (i = 0; i < nParamsFound; i++) {
00703           m_Psi_ijk_coeff(i, n) = vParams[i];
00704         }
00705         m_Psi_ijk[n] = vParams[0];
00706 
00707         n = jSpecies * m_kk *m_kk + iSpecies * m_kk + kSpecies ;
00708         for (i = 0; i < nParamsFound; i++) {
00709           m_Psi_ijk_coeff(i, n) = vParams[i];
00710         }
00711         m_Psi_ijk[n] = vParams[0];
00712 
00713         n = jSpecies * m_kk *m_kk + kSpecies * m_kk + iSpecies ;
00714         for (i = 0; i < nParamsFound; i++) {
00715           m_Psi_ijk_coeff(i, n) = vParams[i];
00716         }
00717         m_Psi_ijk[n] = vParams[0];
00718 
00719         n = kSpecies * m_kk *m_kk + jSpecies * m_kk + iSpecies ;
00720         for (i = 0; i < nParamsFound; i++) {
00721           m_Psi_ijk_coeff(i, n) = vParams[i];
00722         }
00723         m_Psi_ijk[n] = vParams[0];
00724 
00725         n = kSpecies * m_kk *m_kk + iSpecies * m_kk + jSpecies ;
00726         for (i = 0; i < nParamsFound; i++) {
00727           m_Psi_ijk_coeff(i, n) = vParams[i];
00728         }
00729         m_Psi_ijk[n] = vParams[0];
00730 
00731       }
00732     }
00733   }
00734 
00735 
00736    
00737   /**
00738    * Process an XML node called "LambdaNeutral". 
00739    * This node contains all of the parameters necessary to describe
00740    * the binary interactions between one neutral species and
00741    * any other species (neutral or otherwise) in the mechanism.
00742    */
00743   void HMWSoln::readXMLLambdaNeutral(XML_Node &BinSalt) {
00744     string xname = BinSalt.name();
00745     vector_fp vParams;
00746     int nParamsFound;
00747     if (xname != "lambdaNeutral") {
00748       throw CanteraError("HMWSoln::readXMLLanbdaNeutral",
00749                          "Incorrect name for processing this routine: " + xname);
00750     }
00751     double *charge = DATA_PTR(m_speciesCharge);
00752     string stemp;
00753     string iName = BinSalt.attrib("species1");
00754     if (iName == "") {
00755       throw CanteraError("HMWSoln::readXMLLambdaNeutral", "no species1 attrib");
00756     }
00757     string jName = BinSalt.attrib("species2");
00758     if (jName == "") {
00759       throw CanteraError("HMWSoln::readXMLLambdaNeutral", "no species2 attrib");
00760     }
00761     /*
00762      * Find the index of the species in the current phase. It's not
00763      * an error to not find the species
00764      */
00765     int iSpecies = speciesIndex(iName);
00766     if (iSpecies < 0) {
00767       return;
00768     }
00769     if (charge[iSpecies] != 0) {
00770       throw CanteraError("HMWSoln::readXMLLambdaNeutral", 
00771                          "neutral charge problem");
00772     }
00773     int jSpecies = speciesIndex(jName);
00774     if (jSpecies < 0) {
00775       return;
00776     }
00777         
00778     int num = BinSalt.nChildren();
00779     for (int i = 0; i < num; i++) {
00780       XML_Node &xmlChild = BinSalt.child(i);
00781       stemp = xmlChild.name();
00782       string nodeName = lowercase(stemp);
00783       if (nodeName == "lambda") {
00784         int nCount = iSpecies*m_kk + jSpecies;
00785         getFloatArray(xmlChild, vParams, false, "", stemp);
00786         nParamsFound = vParams.size();
00787         if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) {
00788           if (nParamsFound != 1) {
00789             throw CanteraError("HMWSoln::readXMLLambdaNeutral::Lambda for " + iName 
00790                                + "::" + jName,
00791                                "wrong number of params found");
00792           }
00793           m_Lambda_nj_coeff(0,nCount) = vParams[0];
00794           m_Lambda_nj(iSpecies,jSpecies) = vParams[0];
00795 
00796         } else  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
00797           if (nParamsFound != 2) {
00798             throw CanteraError("HMWSoln::readXMLLambdaNeutral::Lambda for " + iName
00799                                + "::" + jName,
00800                                "wrong number of params found");
00801           }
00802           m_Lambda_nj_coeff(0,nCount) = vParams[0];
00803           m_Lambda_nj_coeff(1,nCount) = vParams[1];
00804           m_Lambda_nj(iSpecies, jSpecies) = vParams[0];
00805 
00806         } else  if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
00807           if (nParamsFound == 1) {
00808             vParams.resize(5, 0.0);
00809             nParamsFound = 5;
00810           } else if (nParamsFound != 5) {
00811             throw CanteraError("HMWSoln::readXMLLambdaNeutral::Lambda for " + iName
00812                                + "::" + jName,
00813                                "wrong number of params found");
00814           }
00815           for (i = 0; i < nParamsFound; i++) {
00816             m_Lambda_nj_coeff(i,nCount) = vParams[i];
00817           }
00818           m_Lambda_nj(iSpecies, jSpecies) = vParams[0];
00819         }
00820       }
00821     }
00822   }
00823 
00824   /**
00825    * Process an XML node called "MunnnNeutral". 
00826    * This node contains all of the parameters necessary to describe
00827    * the self-ternary interactions for one neutral species.
00828    */
00829   void HMWSoln::readXMLMunnnNeutral(XML_Node &BinSalt) {
00830     string xname = BinSalt.name();
00831     vector_fp vParams;
00832     int nParamsFound;
00833     if (xname != "MunnnNeutral") {
00834       throw CanteraError("HMWSoln::readXMLMunnnNeutral",
00835                          "Incorrect name for processing this routine: " + xname);
00836     }
00837     double *charge = DATA_PTR(m_speciesCharge);
00838     string stemp;
00839     string iName = BinSalt.attrib("species1");
00840     if (iName == "") {
00841       throw CanteraError("HMWSoln::readXMLMunnnNeutral", "no species1 attrib");
00842     }
00843  
00844     /*
00845      * Find the index of the species in the current phase. It's not
00846      * an error to not find the species
00847      */
00848     int iSpecies = speciesIndex(iName);
00849     if (iSpecies < 0) {
00850       return;
00851     }
00852     if (charge[iSpecies] != 0) {
00853       throw CanteraError("HMWSoln::readXMLMunnnNeutral", 
00854                          "neutral charge problem");
00855     }
00856  
00857         
00858     int num = BinSalt.nChildren();
00859     for (int i = 0; i < num; i++) {
00860       XML_Node &xmlChild = BinSalt.child(i);
00861       stemp = xmlChild.name();
00862       string nodeName = lowercase(stemp);
00863       if (nodeName == "munnn") {
00864         getFloatArray(xmlChild, vParams, false, "", "Munnn");
00865         nParamsFound = vParams.size();
00866         if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) {
00867           if (nParamsFound != 1) {
00868             throw CanteraError("HMWSoln::readXMLMunnnNeutral::Munnn for " + iName,        
00869                                "wrong number of params found");
00870           }
00871           m_Mu_nnn_coeff(0,iSpecies) = vParams[0];
00872           m_Mu_nnn[iSpecies] = vParams[0];
00873 
00874         } else  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
00875           if (nParamsFound != 2) {
00876             throw CanteraError("HMWSoln::readXMLMunnnNeutral::Munnn for " + iName,
00877                                "wrong number of params found");
00878           }
00879           m_Mu_nnn_coeff(0, iSpecies) = vParams[0];
00880           m_Mu_nnn_coeff(1, iSpecies) = vParams[1];
00881           m_Mu_nnn[iSpecies] = vParams[0];
00882 
00883         } else  if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
00884           if (nParamsFound == 1) {
00885             vParams.resize(5, 0.0);
00886             nParamsFound = 5;
00887           } else if (nParamsFound != 5) {
00888             throw CanteraError("HMWSoln::readXMLMunnnNeutral::Munnn for " + iName,
00889                                "wrong number of params found");
00890           }
00891           for (i = 0; i < nParamsFound; i++) {
00892             m_Mu_nnn_coeff(i, iSpecies) = vParams[i];
00893           }
00894           m_Mu_nnn[iSpecies] = vParams[0];
00895         }
00896       }
00897     }
00898   }
00899 
00900   /*
00901    * Process an XML node called "readXMLZetaCation". 
00902    * This node contains all of the parameters necessary to describe
00903    * the ternary interactions between a neutral, a cation and an anion
00904    */
00905   void HMWSoln::readXMLZetaCation(const XML_Node &BinSalt) {
00906     string xname = BinSalt.name();
00907     if (xname != "zetaCation") {
00908       throw CanteraError("HMWSoln::readXMLZetaCation",
00909                          "Incorrect name for processing this routine: " + xname);
00910     }
00911     double *charge = DATA_PTR(m_speciesCharge);
00912     string stemp;
00913     vector_fp vParams;
00914     int nParamsFound = 0;
00915     
00916     string iName = BinSalt.attrib("neutral");
00917     if (iName == "") {
00918       throw CanteraError("HMWSoln::readXMLZetaCation", "no neutral attrib");
00919     }
00920   
00921     string jName = BinSalt.attrib("cation1");
00922     if (jName == "") {
00923       throw CanteraError("HMWSoln::readXMLZetaCation", "no cation1 attrib");
00924     }
00925 
00926     string kName = BinSalt.attrib("anion1");
00927     if (kName == "") {
00928       throw CanteraError("HMWSoln::readXMLZetaCation", "no anion1 attrib");
00929     }
00930     /*
00931      * Find the index of the species in the current phase. It's not
00932      * an error to not find the species
00933      */
00934     int iSpecies = speciesIndex(iName);
00935     if (iSpecies < 0) {
00936       return;
00937     }
00938     if (charge[iSpecies] != 0.0) {
00939       throw CanteraError("HMWSoln::readXMLZetaCation",  "neutral charge problem");
00940     }
00941  
00942     int jSpecies = speciesIndex(jName);
00943     if (jSpecies < 0) {
00944       return;
00945     }
00946     if (charge[jSpecies] <= 0.0) {
00947       throw CanteraError("HMWSoln::readXLZetaCation", "cation1 charge problem");
00948     }
00949 
00950     int kSpecies = speciesIndex(kName);
00951     if (kSpecies < 0) {
00952       return;
00953     }
00954     if (charge[kSpecies] >= 0.0) {
00955       throw CanteraError("HMWSoln::readXMLZetaCation", "anion1 charge problem");
00956     }
00957  
00958     int num = BinSalt.nChildren();
00959     for (int i = 0; i < num; i++) {
00960       XML_Node &xmlChild = BinSalt.child(i);
00961       stemp = xmlChild.name();
00962       string nodeName = lowercase(stemp);
00963       if (nodeName == "zeta") {
00964         getFloatArray(xmlChild, vParams, false, "", "zeta");
00965         nParamsFound = vParams.size();
00966         int n = iSpecies * m_kk *m_kk + jSpecies * m_kk + kSpecies ;
00967 
00968         if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) {
00969           if (nParamsFound != 1) {
00970             throw CanteraError("HMWSoln::readXMLZetaCation::Zeta for "
00971                                + iName + "::" + jName + "::" + kName,
00972                                "wrong number of params found");
00973           }
00974           m_Psi_ijk_coeff(0,n) = vParams[0];
00975           m_Psi_ijk[n] = vParams[0];
00976         } else  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
00977           if (nParamsFound != 2) {
00978             throw CanteraError("HMWSoln::readXMLZetaCation::Zeta for "
00979                                + iName + "::" + jName + "::" + kName,
00980                                "wrong number of params found");
00981           }
00982           m_Psi_ijk_coeff(0,n) = vParams[0];
00983           m_Psi_ijk_coeff(1,n) = vParams[1];
00984           m_Psi_ijk[n]         = vParams[0];
00985         } else  if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
00986           if (nParamsFound == 1) {
00987             vParams.resize(5, 0.0);
00988             nParamsFound = 5;
00989           } else if (nParamsFound != 5) {
00990             throw CanteraError("HMWSoln::readXMLZetaCation::Zeta for "
00991                                + iName + "::" + jName + "::" + kName,
00992                                "wrong number of params found");
00993           }
00994           for (i = 0; i < nParamsFound; i++) {
00995             m_Psi_ijk_coeff(i, n) = vParams[i];
00996           }
00997           m_Psi_ijk[n] = vParams[0];
00998         }
00999 
01000         // There are no duplicate entries
01001       }
01002     }
01003   }
01004    
01005   // Process an XML node called "croppingCoefficients"
01006   // for the cropping coefficients values
01007   /*
01008    * @param acNode Activity Coefficient XML Node
01009    */
01010   void HMWSoln::readXMLCroppingCoefficients(const XML_Node &acNode) {
01011 
01012     if (acNode.hasChild("croppingCoefficients")) {
01013       XML_Node &cropNode = acNode.child("croppingCoefficients");
01014       if (cropNode.hasChild("ln_gamma_k_min")) {
01015         XML_Node &gkminNode = cropNode.child("ln_gamma_k_min");
01016         getOptionalFloat(gkminNode, "pureSolventValue", CROP_ln_gamma_k_min);
01017       }
01018       if (cropNode.hasChild("ln_gamma_k_max")) {
01019         XML_Node &gkmaxNode = cropNode.child("ln_gamma_k_max");
01020         getOptionalFloat(gkmaxNode, "pureSolventValue", CROP_ln_gamma_k_max);
01021       }
01022 
01023       if (cropNode.hasChild("ln_gamma_o_min")) {
01024         XML_Node &gominNode = cropNode.child("ln_gamma_o_min");
01025         getOptionalFloat(gominNode, "pureSolventValue", CROP_ln_gamma_o_min);
01026       }
01027 
01028       if (cropNode.hasChild("ln_gamma_o_max")) {
01029         XML_Node &gomaxNode = cropNode.child("ln_gamma_o_max");
01030         getOptionalFloat(gomaxNode, "pureSolventValue", CROP_ln_gamma_o_max);
01031       } 
01032     }
01033   }
01034 
01035   /*
01036    *  Initialization routine for a HMWSoln phase.
01037    *
01038    * This is a virtual routine. This routine will call initThermo()
01039    * for the parent class as well.
01040    */
01041   void HMWSoln::initThermo() {
01042     MolalityVPSSTP::initThermo();
01043     initLengths();
01044   }
01045 
01046   /*
01047    *   Import, construct, and initialize a HMWSoln phase 
01048    *   specification from an XML tree into the current object.
01049    *
01050    * This routine is a precursor to constructPhaseXML(XML_Node*)
01051    * routine, which does most of the work.
01052    *
01053    * @param infile XML file containing the description of the
01054    *        phase
01055    *
01056    * @param id  Optional parameter identifying the name of the
01057    *            phase. If none is given, the first XML
01058    *            phase element will be used.
01059    */
01060   void HMWSoln::constructPhaseFile(std::string inputFile, std::string id) {
01061 
01062     if (inputFile.size() == 0) {
01063       throw CanteraError("HMWSoln:constructPhaseFile",
01064                          "input file is null");
01065     }
01066     string path = findInputFile(inputFile);
01067     std::ifstream fin(path.c_str());
01068     if (!fin) {
01069       throw CanteraError("HMWSoln:constructPhaseFile","could not open "
01070                          +path+" for reading.");
01071     }
01072     /*
01073      * The phase object automatically constructs an XML object.
01074      * Use this object to store information.
01075      */
01076     XML_Node &phaseNode_XML = xml();
01077     XML_Node *fxml = new XML_Node();
01078     fxml->build(fin);
01079     XML_Node *fxml_phase = findXMLPhase(fxml, id);
01080     if (!fxml_phase) {
01081       throw CanteraError("HMWSoln:constructPhaseFile",
01082                          "ERROR: Can not find phase named " +
01083                          id + " in file named " + inputFile);
01084     }
01085     fxml_phase->copy(&phaseNode_XML);   
01086     constructPhaseXML(*fxml_phase, id);
01087     delete fxml;
01088   }
01089   
01090   /*
01091    *   Import, construct, and initialize a HMWSoln phase 
01092    *   specification from an XML tree into the current object.
01093    *
01094    *   Most of the work is carried out by the cantera base
01095    *   routine, importPhase(). That routine imports all of the
01096    *   species and element data, including the standard states
01097    *   of the species.
01098    *
01099    *   Then, In this routine, we read the information 
01100    *   particular to the specification of the activity 
01101    *   coefficient model for the Pitzer parameterization.
01102    *
01103    *   We also read information about the molar volumes of the
01104    *   standard states if present in the XML file.
01105    *
01106    * @param phaseNode This object must be the phase node of a
01107    *             complete XML tree
01108    *             description of the phase, including all of the
01109    *             species data. In other words while "phase" must
01110    *             point to an XML phase object, it must have
01111    *             sibling nodes "speciesData" that describe
01112    *             the species in the phase.
01113    * @param id   ID of the phase. If nonnull, a check is done
01114    *             to see if phaseNode is pointing to the phase
01115    *             with the correct id. 
01116    */
01117   void HMWSoln::constructPhaseXML(XML_Node& phaseNode, std::string id) {
01118     string stemp;
01119     if (id.size() > 0) {
01120       string idp = phaseNode.id();
01121       if (idp != id) {
01122         throw CanteraError("HMWSoln::constructPhaseXML", 
01123                            "phasenode and Id are incompatible");
01124       }
01125     }
01126 
01127     /*
01128      * Find the Thermo XML node 
01129      */
01130     if (!phaseNode.hasChild("thermo")) {
01131       throw CanteraError("HMWSoln::constructPhaseXML",
01132                          "no thermo XML node");
01133     }
01134     XML_Node& thermoNode = phaseNode.child("thermo");
01135 
01136     /*
01137      * Possibly change the form of the standard concentrations
01138      */
01139     if (thermoNode.hasChild("standardConc")) {
01140       XML_Node& scNode = thermoNode.child("standardConc");
01141       m_formGC = 2;
01142       stemp = scNode.attrib("model");
01143       string formString = lowercase(stemp);
01144       if (formString != "") {
01145         if (formString == "unity") {
01146           m_formGC = 0;
01147           printf("exit standardConc = unity not done\n");
01148           exit(EXIT_FAILURE);
01149         } else if (formString == "molar_volume") {
01150           m_formGC = 1;
01151           printf("exit standardConc = molar_volume not done\n");
01152           exit(EXIT_FAILURE);
01153         } else if (formString == "solvent_volume") {
01154           m_formGC = 2;
01155         } else {
01156           throw CanteraError("HMWSoln::constructPhaseXML",
01157                              "Unknown standardConc model: " + formString);
01158         }
01159       }
01160     }
01161     /*
01162      * Get the Name of the Solvent:
01163      *      <solvent> solventName </solvent>
01164      */
01165     string solventName = "";
01166     if (thermoNode.hasChild("solvent")) {
01167       XML_Node& scNode = thermoNode.child("solvent");
01168       vector<string> nameSolventa;
01169       getStringArray(scNode, nameSolventa);
01170       int nsp = static_cast<int>(nameSolventa.size());
01171       if (nsp != 1) {
01172         throw CanteraError("HMWSoln::constructPhaseXML",
01173                            "badly formed solvent XML node");
01174       }
01175       solventName = nameSolventa[0];
01176     }
01177 
01178     /*
01179      * Determine the form of the Pitzer model,
01180      *   We will use this information to size arrays below.
01181      */
01182     if (thermoNode.hasChild("activityCoefficients")) {
01183       XML_Node& scNode = thermoNode.child("activityCoefficients");
01184       m_formPitzer = m_formPitzer;
01185       stemp = scNode.attrib("model");
01186       string formString = lowercase(stemp);
01187       if (formString != "") {
01188         if        (formString == "pitzer" || formString == "default") {
01189           m_formPitzer = PITZERFORM_BASE;
01190         } else if (formString == "base") {
01191           m_formPitzer = PITZERFORM_BASE;
01192         } else {
01193           throw CanteraError("HMWSoln::constructPhaseXML",
01194                              "Unknown Pitzer ActivityCoeff model: "
01195                              + formString);
01196         }
01197       }
01198 
01199       /*
01200        * Determine the form of the temperature dependence
01201        * of the Pitzer activity coefficient model.
01202        */
01203       stemp = scNode.attrib("TempModel");
01204       formString = lowercase(stemp);
01205       if (formString != "") {
01206         if (formString == "constant" || formString == "default") {
01207           m_formPitzerTemp = PITZER_TEMP_CONSTANT;
01208         } else if (formString == "linear") {
01209           m_formPitzerTemp = PITZER_TEMP_LINEAR;
01210         } else if (formString == "complex" || formString == "complex1") {
01211           m_formPitzerTemp = PITZER_TEMP_COMPLEX1;
01212         } else {
01213           throw CanteraError("HMWSoln::constructPhaseXML",
01214                              "Unknown Pitzer ActivityCoeff Temp model: "
01215                              + formString);
01216         }
01217       }
01218 
01219       /*
01220        * Determine the reference temperature
01221        * of the Pitzer activity coefficient model's temperature
01222        * dependence formulation: defaults to 25C
01223        */
01224       stemp = scNode.attrib("TempReference");
01225       formString = lowercase(stemp);
01226       if (formString != "") {
01227         m_TempPitzerRef = atofCheck(formString.c_str());
01228       } else {
01229         m_TempPitzerRef = 273.15 + 25;
01230       }
01231         
01232     }
01233 
01234     /*
01235      * Call the Cantera importPhase() function. This will import
01236      * all of the species into the phase. This will also handle
01237      * all of the solvent and solute standard states
01238      */
01239     bool m_ok = importPhase(phaseNode, this);
01240     if (!m_ok) {
01241       throw CanteraError("HMWSoln::constructPhaseXML","importPhase failed "); 
01242     }
01243     
01244   }
01245 
01246   /**
01247    * Process the XML file after species are set up.
01248    *
01249    *  This gets called from importPhase(). It processes the XML file
01250    *  after the species are set up. This is the main routine for
01251    *  reading in activity coefficient parameters.
01252    *
01253    * @param phaseNode This object must be the phase node of a
01254    *             complete XML tree
01255    *             description of the phase, including all of the
01256    *             species data. In other words while "phase" must
01257    *             point to an XML phase object, it must have
01258    *             sibling nodes "speciesData" that describe
01259    *             the species in the phase.
01260    * @param id   ID of the phase. If nonnull, a check is done
01261    *             to see if phaseNode is pointing to the phase
01262    *             with the correct id.
01263    */
01264   void HMWSoln::
01265   initThermoXML(XML_Node& phaseNode, std::string id) {
01266     int k;
01267     string stemp;
01268     /*
01269      * Find the Thermo XML node 
01270      */
01271     if (!phaseNode.hasChild("thermo")) {
01272       throw CanteraError("HMWSoln::initThermoXML",
01273                          "no thermo XML node");
01274     }
01275     XML_Node& thermoNode = phaseNode.child("thermo");
01276 
01277     /*
01278      * Get the Name of the Solvent:
01279      *      <solvent> solventName </solvent>
01280      */
01281     string solventName = "";
01282     if (thermoNode.hasChild("solvent")) {
01283       XML_Node& scNode = thermoNode.child("solvent");
01284       vector<string> nameSolventa;
01285       getStringArray(scNode, nameSolventa);
01286       int nsp = static_cast<int>(nameSolventa.size());
01287       if (nsp != 1) {
01288         throw CanteraError("HMWSoln::initThermoXML",
01289                            "badly formed solvent XML node");
01290       }
01291       solventName = nameSolventa[0];
01292     }
01293 
01294     /*
01295      * Initialize all of the lengths of arrays in the object
01296      * now that we know what species are in the phase.
01297      */
01298     initLengths();
01299 
01300     /*
01301      * Reconcile the solvent name and index.
01302      */
01303     for (k = 0; k < m_kk; k++) {
01304       string sname = speciesName(k);
01305       if (solventName == sname) {
01306         setSolvent(k);
01307         if (k != 0) {
01308           throw CanteraError("HMWSoln::initThermoXML",
01309                              "Solvent must be species 0 atm");
01310         }
01311         m_indexSolvent = k;
01312         break;
01313       }
01314     }
01315     if (m_indexSolvent == -1) {
01316       std::cout << "HMWSoln::initThermo: Solvent Name not found" 
01317                 << std::endl;
01318       throw CanteraError("HMWSoln::initThermoXML",
01319                          "Solvent name not found");
01320     }
01321     if (m_indexSolvent != 0) {
01322       throw CanteraError("HMWSoln::initThermoXML",
01323                          "Solvent " + solventName +
01324                          " should be first species");
01325     }
01326 
01327     /*
01328      * Now go get the specification of the standard states for
01329      * species in the solution. This includes the molar volumes
01330      * data blocks for incompressible species.
01331      */
01332     XML_Node& speciesList = phaseNode.child("speciesArray");
01333     XML_Node* speciesDB =
01334       get_XML_NameID("speciesData", speciesList["datasrc"],
01335                      &phaseNode.root());
01336     const vector<string>&sss = speciesNames();
01337 
01338     for (k = 0; k < m_kk; k++) {
01339       XML_Node* s =  speciesDB->findByAttr("name", sss[k]);
01340       if (!s) {
01341         throw CanteraError("HMWSoln::initThermoXML",
01342                          "Species Data Base " + sss[k] + " not found");
01343       }
01344       XML_Node *ss = s->findByName("standardState");
01345       if (!ss) {
01346         throw CanteraError("HMWSoln::initThermoXML",
01347                          "Species " + sss[k] + 
01348                            " standardState XML block  not found");
01349       }
01350       string modelStringa = ss->attrib("model");
01351       if (modelStringa == "") {
01352         throw CanteraError("HMWSoln::initThermoXML",
01353                            "Species " + sss[k] + 
01354                            " standardState XML block model attribute not found");
01355       }
01356       string modelString = lowercase(modelStringa);
01357       if (k == 0) {
01358         if (modelString == "wateriapws" || modelString == "real_water" ||
01359             modelString == "waterpdss") {
01360           /*
01361            * Store a local pointer to the water standard state model.
01362            *   -> We've hardcoded it to a PDSS_Water model, so this is ok.
01363            */
01364           m_waterSS = dynamic_cast<PDSS_Water *>(providePDSS(0)) ;
01365           if (!m_waterSS) {
01366             throw CanteraError("HMWSoln::initThermoXML",
01367                                "Dynamic cast to PDSS_Water failed");
01368           }
01369           /*
01370            * Fill in the molar volume of water (m3/kmol)
01371            * at standard conditions to fill in the m_speciesSize entry
01372            * with something reasonable.
01373            */
01374           m_waterSS->setState_TP(300., OneAtm);
01375           double dens = m_waterSS->density();
01376           double mw = m_waterSS->molecularWeight();
01377           m_speciesSize[0] = mw / dens;
01378 #ifdef DEBUG_HKM_NOT
01379           cout << "Solvent species " << sss[k] << " has volume " <<  
01380             m_speciesSize[k] << endl;
01381 #endif
01382         } else {
01383           //  throw CanteraError("HMWSoln::initThermoXML",
01384           //         "Solvent SS Model \"" + modelStringa + 
01385           //         "\" is not allowed, name = " + sss[0]);
01386           m_waterSS = providePDSS(0);
01387           m_waterSS->setState_TP(300., OneAtm);
01388           double dens = m_waterSS->density();
01389           double mw = m_waterSS->molecularWeight();
01390           m_speciesSize[0] = mw / dens;
01391         }
01392       } else {
01393         if (modelString != "constant_incompressible" && modelString != "hkft") {
01394           throw CanteraError("HMWSoln::initThermoXML",
01395                              "Solute SS Model \"" + modelStringa + 
01396                              "\" is not known");
01397         }
01398         if (modelString ==  "constant_incompressible" ) {
01399           m_speciesSize[k] = getFloat(*ss, "molarVolume", "toSI");
01400 #ifdef DEBUG_HKM_NOT
01401           cout << "species " << sss[k] << " has volume " <<  
01402             m_speciesSize[k] << endl;
01403 #endif
01404         }
01405         // HKM Note, have to fill up m_speciesSize[] for HKFT species
01406       }
01407     }
01408 
01409     /*
01410      * Initialize the water property calculator. It will share
01411      * the internal eos water calculator.
01412      */
01413     m_waterProps = new WaterProps(dynamic_cast<PDSS_Water*>(m_waterSS));
01414 
01415     /*
01416      * Fill in parameters for the calculation of the 
01417      * stoichiometric Ionic Strength
01418      *
01419      * The default is that stoich charge is the same as the
01420      * regular charge.
01421      */
01422     for (k = 0; k < m_kk; k++) {
01423       m_speciesCharge_Stoich[k] = m_speciesCharge[k];
01424     }
01425 
01426     /*
01427      * Go get all of the coefficients and factors in the
01428      * activityCoefficients XML block
01429      */
01430     XML_Node *acNodePtr = 0;
01431     if (thermoNode.hasChild("activityCoefficients")) {
01432       XML_Node& acNode = thermoNode.child("activityCoefficients");
01433       acNodePtr = &acNode;
01434       /*
01435        * Look for parameters for A_Debye
01436        */
01437       if (acNode.hasChild("A_Debye")) {
01438         XML_Node &ADebye = acNode.child("A_Debye");
01439         m_form_A_Debye = A_DEBYE_CONST;
01440         stemp = "model";
01441         if (ADebye.hasAttrib(stemp)) {
01442           string atemp = ADebye.attrib(stemp);
01443           stemp = lowercase(atemp);
01444           if (stemp == "water") {
01445             m_form_A_Debye = A_DEBYE_WATER;
01446           }
01447         }
01448         if (m_form_A_Debye == A_DEBYE_CONST) {
01449           m_A_Debye = getFloat(acNode, "A_Debye");
01450         }
01451 #ifdef DEBUG_HKM_NOT
01452         cout << "A_Debye = " << m_A_Debye << endl;
01453 #endif
01454       }
01455 
01456       /*
01457        * Look for Parameters for the Maximum Ionic Strength
01458        */
01459       if (acNode.hasChild("maxIonicStrength")) {
01460         m_maxIionicStrength = getFloat(acNode, "maxIonicStrength");
01461 #ifdef DEBUG_HKM_NOT
01462         cout << "m_maxIionicStrength = "
01463              <<m_maxIionicStrength << endl;
01464 #endif
01465       }
01466 
01467 
01468       /*
01469        * Look for parameters for the Ionic radius
01470        */
01471       if (acNode.hasChild("ionicRadius")) {
01472         XML_Node& irNode = acNode.child("ionicRadius");
01473 
01474         string Aunits = "";
01475         double Afactor = 1.0;
01476         if (irNode.hasAttrib("units")) {
01477           string Aunits = irNode.attrib("units");
01478           Afactor = toSI(Aunits); 
01479         }
01480 
01481         if (irNode.hasAttrib("default")) {
01482           string ads = irNode.attrib("default");
01483           double ad = fpValue(ads);
01484           for (int k = 0; k < m_kk; k++) {
01485             m_Aionic[k] = ad * Afactor;
01486           }
01487         }
01488 
01489       }
01490 
01491       /*
01492        * First look at the species database.
01493        *  -> Look for the subelement "stoichIsMods"
01494        *     in each of the species SS databases.
01495        */
01496       std::vector<const XML_Node *> xspecies = speciesData();
01497    
01498       string kname, jname;
01499       int jj = xspecies.size();
01500       for (k = 0; k < m_kk; k++) {
01501         int jmap = -1;
01502         kname = speciesName(k);
01503         for (int j = 0; j < jj; j++) {
01504           const XML_Node& sp = *xspecies[j];
01505           jname = sp["name"];
01506           if (jname == kname) {
01507             jmap = j;
01508             break;
01509           }
01510         }
01511         if (jmap > -1) {
01512           const XML_Node& sp = *xspecies[jmap];
01513           getOptionalFloat(sp, "stoichIsMods",  m_speciesCharge_Stoich[k]);
01514           // if (sp.hasChild("stoichIsMods")) {
01515           // double val = getFloat(sp, "stoichIsMods");
01516           //m_speciesCharge_Stoich[k] = val;
01517           //}
01518         }
01519       }
01520    
01521       /*
01522        * Now look at the activity coefficient database
01523        */
01524       if (acNodePtr) {
01525         if (acNodePtr->hasChild("stoichIsMods")) {
01526           XML_Node& sIsNode = acNodePtr->child("stoichIsMods");
01527 
01528           map<string, string> msIs;
01529           getMap(sIsNode, msIs);
01530           map<string,string>::const_iterator _b = msIs.begin();
01531           for (; _b != msIs.end(); ++_b) {
01532             int kk = speciesIndex(_b->first);
01533             if (kk < 0) {
01534               //throw CanteraError(
01535               //   "HMWSoln::initThermo error",
01536               //   "no species match was found"
01537               //   );
01538             } else {
01539               double val = fpValue(_b->second);
01540               m_speciesCharge_Stoich[kk] = val;
01541             }
01542           }
01543         }
01544       }
01545       
01546     
01547       /*
01548        * Loop through the children getting multiple instances of
01549        * parameters
01550        */
01551       if (acNodePtr) {
01552         int n = acNodePtr->nChildren();
01553         for (int i = 0; i < n; i++) {
01554           XML_Node &xmlACChild = acNodePtr->child(i);
01555           stemp = xmlACChild.name();
01556           string nodeName = lowercase(stemp);
01557           /*
01558            * Process a binary salt field, or any of the other XML fields
01559            * that make up the Pitzer Database. Entries will be ignored
01560            * if any of the species in the entry isn't in the solution.
01561            */
01562           if (nodeName == "binarysaltparameters") {
01563             readXMLBinarySalt(xmlACChild);
01564           } else if (nodeName == "thetaanion") {
01565             readXMLThetaAnion(xmlACChild);
01566           } else if (nodeName == "thetacation") {
01567             readXMLThetaCation(xmlACChild);
01568           } else if (nodeName == "psicommonanion") { 
01569             readXMLPsiCommonAnion(xmlACChild);
01570           } else if (nodeName == "psicommoncation") { 
01571             readXMLPsiCommonCation(xmlACChild);
01572           } else if (nodeName == "lambdaneutral") {
01573             readXMLLambdaNeutral(xmlACChild);
01574           } else if (nodeName == "zetacation") {
01575             readXMLZetaCation(xmlACChild);
01576           }
01577         }
01578       }
01579 
01580       // Go look up the optional Cropping parameters
01581       readXMLCroppingCoefficients(acNode);
01582 
01583     }
01584 
01585     /*
01586      * Fill in the vector specifying the electrolyte species
01587      * type
01588      *
01589      *   First fill in default values. Everthing is either
01590      *   a charge species, a nonpolar neutral, or the solvent.
01591      */
01592     for (k = 0; k < m_kk; k++) {
01593       if (fabs(m_speciesCharge[k]) > 0.0001) {
01594         m_electrolyteSpeciesType[k] = cEST_chargedSpecies;
01595         if (fabs(m_speciesCharge_Stoich[k] - m_speciesCharge[k])
01596             > 0.0001) {
01597           m_electrolyteSpeciesType[k] = cEST_weakAcidAssociated;
01598         }
01599       } else if (fabs(m_speciesCharge_Stoich[k]) > 0.0001) {
01600         m_electrolyteSpeciesType[k] = cEST_weakAcidAssociated;
01601       } else {
01602         m_electrolyteSpeciesType[k] = cEST_nonpolarNeutral;
01603       }
01604     }
01605     m_electrolyteSpeciesType[m_indexSolvent] = cEST_solvent;
01606     /*
01607      * First look at the species database.
01608      *  -> Look for the subelement "stoichIsMods"
01609      *     in each of the species SS databases.
01610      */
01611     std::vector<const XML_Node *> xspecies = speciesData();
01612     const XML_Node *spPtr = 0;
01613     string kname;
01614     for (k = 0; k < m_kk; k++) {
01615       kname = speciesName(k);
01616       spPtr = xspecies[k];
01617       if (!spPtr) {
01618         if (spPtr->hasChild("electrolyteSpeciesType")) {
01619           string est = getChildValue(*spPtr, "electrolyteSpeciesType");
01620           if ((m_electrolyteSpeciesType[k] = interp_est(est)) == -1) {
01621             throw CanteraError("HMWSoln::initThermoXML",
01622                                "Bad electrolyte type: " + est);
01623           }
01624         }
01625       } 
01626     }
01627     /*
01628      * Then look at the phase thermo specification
01629      */
01630     if (acNodePtr) {
01631       if (acNodePtr->hasChild("electrolyteSpeciesType")) {
01632         XML_Node& ESTNode = acNodePtr->child("electrolyteSpeciesType");
01633         map<string, string> msEST;
01634         getMap(ESTNode, msEST);
01635         map<string,string>::const_iterator _b = msEST.begin();
01636         for (; _b != msEST.end(); ++_b) {
01637           int kk = speciesIndex(_b->first);
01638           if (kk < 0) {
01639           } else {
01640             string est = _b->second;
01641             if ((m_electrolyteSpeciesType[kk] = interp_est(est))  == -1) {
01642               throw CanteraError("HMWSoln::initThermoXML",
01643                                  "Bad electrolyte type: " + est);
01644             }
01645           }
01646         }
01647       }
01648     }
01649 
01650     IMS_typeCutoff_ = 2;
01651     if (IMS_typeCutoff_ == 2) {
01652       calcIMSCutoffParams_();
01653     }
01654     calcMCCutoffParams_();
01655     setMoleFSolventMin(1.0E-5);
01656 
01657     MolalityVPSSTP::initThermoXML(phaseNode, id);
01658     /*
01659      * Lastly set the state
01660      */
01661     //    if (phaseNode.hasChild("state")) {
01662     // XML_Node& stateNode = phaseNode.child("state");
01663     // setStateFromXML(stateNode);
01664     //}
01665 
01666   }
01667 
01668   // Precalculate the IMS Cutoff parameters for typeCutoff = 2
01669   void  HMWSoln::calcIMSCutoffParams_() {
01670     IMS_afCut_ = 1.0 / (std::exp(1.0) *  IMS_gamma_k_min_);
01671     IMS_efCut_ = 0.0;
01672     bool converged = false;
01673     double oldV = 0.0;
01674     int its;
01675     for (its = 0; its < 100 && !converged; its++) {
01676       oldV = IMS_efCut_;
01677       IMS_afCut_ = 1.0 / (std::exp(1.0) * IMS_gamma_k_min_)  -IMS_efCut_;
01678       IMS_bfCut_ = IMS_afCut_ / IMS_cCut_ + IMS_slopefCut_ - 1.0;
01679       IMS_dfCut_ = ((- IMS_afCut_/IMS_cCut_ + IMS_bfCut_ - IMS_bfCut_*IMS_X_o_cutoff_/IMS_cCut_)
01680                 /
01681                 (IMS_X_o_cutoff_*IMS_X_o_cutoff_/IMS_cCut_ - 2.0 * IMS_X_o_cutoff_));
01682       double tmp = IMS_afCut_ + IMS_X_o_cutoff_*( IMS_bfCut_ + IMS_dfCut_ *IMS_X_o_cutoff_);
01683       double eterm = std::exp(-IMS_X_o_cutoff_/IMS_cCut_);
01684       IMS_efCut_ = - eterm * (tmp);
01685       if (fabs(IMS_efCut_ - oldV) < 1.0E-14) {
01686         converged = true;
01687       }
01688     }
01689     if (!converged) {
01690       throw CanteraError("HMWSoln::calcIMSCutoffParams_()",
01691                          " failed to converge on the f polynomial");
01692     }
01693     converged = false;
01694     double f_0 = IMS_afCut_ + IMS_efCut_;
01695     double f_prime_0 = 1.0 - IMS_afCut_ / IMS_cCut_ + IMS_bfCut_;
01696     IMS_egCut_ = 0.0;
01697     for (its = 0; its < 100 && !converged; its++) {
01698       oldV = IMS_egCut_;
01699       double lng_0 = -log(IMS_gamma_o_min_) -  f_prime_0 / f_0;
01700       IMS_agCut_ = exp(lng_0) - IMS_egCut_;
01701       IMS_bgCut_ = IMS_agCut_ / IMS_cCut_ + IMS_slopegCut_ - 1.0;
01702       IMS_dgCut_ = ((- IMS_agCut_/IMS_cCut_ + IMS_bgCut_ - IMS_bgCut_*IMS_X_o_cutoff_/IMS_cCut_)
01703                 /
01704                 (IMS_X_o_cutoff_*IMS_X_o_cutoff_/IMS_cCut_ - 2.0 * IMS_X_o_cutoff_));
01705       double tmp = IMS_agCut_ + IMS_X_o_cutoff_*( IMS_bgCut_ + IMS_dgCut_ *IMS_X_o_cutoff_);
01706       double eterm = std::exp(-IMS_X_o_cutoff_/IMS_cCut_);
01707       IMS_egCut_ = - eterm * (tmp);
01708       if (fabs(IMS_egCut_ - oldV) < 1.0E-14) {
01709         converged = true;
01710       }
01711     }
01712     if (!converged) {
01713       throw CanteraError("HMWSoln::calcIMSCutoffParams_()",
01714                          " failed to converge on the g polynomial");
01715     }
01716   }
01717 
01718   // Precalculate the MC Cutoff parameters
01719   void  HMWSoln::calcMCCutoffParams_() {
01720     MC_X_o_min_ = 0.35;
01721     MC_X_o_cutoff_ = 0.6;
01722     MC_slopepCut_ = 0.02;
01723     MC_cpCut_ = 0.25;
01724 
01725     // Initial starting values
01726     MC_apCut_ = MC_X_o_min_;
01727     MC_epCut_ = 0.0;
01728     bool converged = false;
01729     double oldV = 0.0;
01730     int its;
01731     double damp = 0.5;
01732     for (its = 0; its < 500 && !converged; its++) {
01733       oldV = MC_epCut_;
01734       MC_apCut_ = damp *(MC_X_o_min_ - MC_epCut_) + (1-damp) * MC_apCut_;
01735       double MC_bpCutNew = MC_apCut_ / MC_cpCut_ + MC_slopepCut_ - 1.0;
01736       MC_bpCut_ = damp * MC_bpCutNew + (1-damp) * MC_bpCut_;
01737       double MC_dpCutNew = ((- MC_apCut_/MC_cpCut_ + MC_bpCut_ - MC_bpCut_ * MC_X_o_cutoff_/MC_cpCut_)
01738                    /
01739                    (MC_X_o_cutoff_ * MC_X_o_cutoff_/MC_cpCut_ - 2.0 * MC_X_o_cutoff_));
01740       MC_dpCut_ = damp * MC_dpCutNew + (1-damp) * MC_dpCut_;
01741       double tmp = MC_apCut_ + MC_X_o_cutoff_*( MC_bpCut_ + MC_dpCut_ * MC_X_o_cutoff_);
01742       double eterm = std::exp(- MC_X_o_cutoff_ / MC_cpCut_);
01743       MC_epCut_ = - eterm * (tmp);
01744       double diff = MC_epCut_ - oldV;
01745       if (fabs(diff) < 1.0E-14) {
01746         converged = true;
01747       }
01748     }
01749     if (!converged) {
01750       throw CanteraError("HMWSoln::calcMCCutoffParams_()",
01751                          " failed to converge on the p polynomial");
01752     }
01753   }
01754 
01755 }
Generated by  doxygen 1.6.3