/** * @file HMWSoln_input.cpp * Definitions for the %HMWSoln ThermoPhase object, which models concentrated * electrolyte solutions * (see \ref thermoprops and \link Cantera::HMWSoln HMWSoln \endlink) . * * This file contains definitions for reading in the interaction terms * in the formulation. */ /* * Copyright (2006) Sandia Corporation. Under the terms of * Contract DE-AC04-94AL85000 with Sandia Corporation, the * U.S. Government retains certain rights in this software. */ #include "cantera/thermo/HMWSoln.h" #include "cantera/thermo/ThermoFactory.h" #include "cantera/thermo/WaterProps.h" #include "cantera/thermo/PDSS_Water.h" #include "cantera/base/stringUtils.h" #include #include #include #include using namespace std; using namespace ctml; namespace Cantera { //! utility function to assign an integer value from a string //! for the ElectrolyteSpeciesType field. /*! * @param estString string name of the electrolyte species type */ int HMWSoln::interp_est(std::string estString) { const char* cc = estString.c_str(); string lcs = lowercase(estString); const char* ccl = lcs.c_str(); if (!strcmp(ccl, "solvent")) { return cEST_solvent; } else if (!strcmp(ccl, "chargedspecies")) { return cEST_chargedSpecies; } else if (!strcmp(ccl, "weakacidassociated")) { return cEST_weakAcidAssociated; } else if (!strcmp(ccl, "strongacidassociated")) { return cEST_strongAcidAssociated; } else if (!strcmp(ccl, "polarneutral")) { return cEST_polarNeutral; } else if (!strcmp(ccl, "nonpolarneutral")) { return cEST_nonpolarNeutral; } int retn, rval; if ((retn = sscanf(cc, "%d", &rval)) != 1) { return -1; } return rval; } /* * Process an XML node called "SimpleSaltParameters. * This node contains all of the parameters necessary to describe * the Pitzer model for that particular binary salt. * This function reads the XML file and writes the coefficients * it finds to an internal data structures. */ void HMWSoln::readXMLBinarySalt(XML_Node& BinSalt) { string xname = BinSalt.name(); if (xname != "binarySaltParameters") { throw CanteraError("HMWSoln::readXMLBinarySalt", "Incorrect name for processing this routine: " + xname); } double* charge = DATA_PTR(m_speciesCharge); string stemp; size_t nParamsFound, i; vector_fp vParams; string iName = BinSalt.attrib("cation"); if (iName == "") { throw CanteraError("HMWSoln::readXMLBinarySalt", "no cation attrib"); } string jName = BinSalt.attrib("anion"); if (jName == "") { throw CanteraError("HMWSoln::readXMLBinarySalt", "no anion attrib"); } /* * Find the index of the species in the current phase. It's not * an error to not find the species */ size_t iSpecies = speciesIndex(iName); if (iSpecies == npos) { return; } string ispName = speciesName(iSpecies); if (charge[iSpecies] <= 0) { throw CanteraError("HMWSoln::readXMLBinarySalt", "cation charge problem"); } size_t jSpecies = speciesIndex(jName); if (jSpecies == npos) { return; } string jspName = speciesName(jSpecies); if (charge[jSpecies] >= 0) { throw CanteraError("HMWSoln::readXMLBinarySalt", "anion charge problem"); } size_t n = iSpecies * m_kk + jSpecies; int counter = m_CounterIJ[n]; for (size_t iChild = 0; iChild < BinSalt.nChildren(); iChild++) { XML_Node& xmlChild = BinSalt.child(iChild); stemp = xmlChild.name(); string nodeName = lowercase(stemp); /* * Process the binary salt child elements */ if (nodeName == "beta0") { /* * Get the string containing all of the values */ getFloatArray(xmlChild, vParams, false, "", "beta0"); nParamsFound = vParams.size(); if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) { if (nParamsFound != 1) { throw CanteraError("HMWSoln::readXMLBinarySalt::beta0 for " + ispName + "::" + jspName, "wrong number of params found"); } m_Beta0MX_ij[counter] = vParams[0]; m_Beta0MX_ij_coeff(0,counter) = m_Beta0MX_ij[counter]; } else if (m_formPitzerTemp == PITZER_TEMP_LINEAR) { if (nParamsFound != 2) { throw CanteraError("HMWSoln::readXMLBinarySalt::beta0 for " + ispName + "::" + jspName, "wrong number of params found"); } m_Beta0MX_ij_coeff(0,counter) = vParams[0]; m_Beta0MX_ij_coeff(1,counter) = vParams[1]; m_Beta0MX_ij[counter] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) { if (nParamsFound != 5) { throw CanteraError("HMWSoln::readXMLBinarySalt::beta0 for " + ispName + "::" + jspName, "wrong number of params found"); } for (i = 0; i < nParamsFound; i++) { m_Beta0MX_ij_coeff(i, counter) = vParams[i]; } m_Beta0MX_ij[counter] = vParams[0]; } } if (nodeName == "beta1") { /* * Get the string containing all of the values */ getFloatArray(xmlChild, vParams, false, "", "beta1"); nParamsFound = vParams.size(); if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) { if (nParamsFound != 1) { throw CanteraError("HMWSoln::readXMLBinarySalt::beta1 for " + ispName + "::" + jspName, "wrong number of params found"); } m_Beta1MX_ij[counter] = vParams[0]; m_Beta1MX_ij_coeff(0,counter) = m_Beta1MX_ij[counter]; } else if (m_formPitzerTemp == PITZER_TEMP_LINEAR) { if (nParamsFound != 2) { throw CanteraError("HMWSoln::readXMLBinarySalt::beta1 for " + ispName + "::" + jspName, "wrong number of params found"); } m_Beta1MX_ij_coeff(0,counter) = vParams[0]; m_Beta1MX_ij_coeff(1,counter) = vParams[1]; m_Beta1MX_ij[counter] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) { if (nParamsFound != 5) { throw CanteraError("HMWSoln::readXMLBinarySalt::beta1 for " + ispName + "::" + jspName, "wrong number of params found"); } for (i = 0; i < nParamsFound; i++) { m_Beta1MX_ij_coeff(i, counter) = vParams[i]; } m_Beta1MX_ij[counter] = vParams[0]; } } if (nodeName == "beta2") { getFloatArray(xmlChild, vParams, false, "", "beta2"); nParamsFound = vParams.size(); if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) { if (nParamsFound != 1) { throw CanteraError("HMWSoln::readXMLBinarySalt::beta2 for " + ispName + "::" + jspName, "wrong number of params found"); } m_Beta2MX_ij[counter] = vParams[0]; m_Beta2MX_ij_coeff(0,counter) = m_Beta2MX_ij[counter]; } else if (m_formPitzerTemp == PITZER_TEMP_LINEAR) { if (nParamsFound != 2) { throw CanteraError("HMWSoln::readXMLBinarySalt::beta2 for " + ispName + "::" + jspName, "wrong number of params found"); } m_Beta2MX_ij_coeff(0,counter) = vParams[0]; m_Beta2MX_ij_coeff(1,counter) = vParams[1]; m_Beta2MX_ij[counter] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) { if (nParamsFound != 5) { throw CanteraError("HMWSoln::readXMLBinarySalt::beta2 for " + ispName + "::" + jspName, "wrong number of params found"); } for (i = 0; i < nParamsFound; i++) { m_Beta2MX_ij_coeff(i, counter) = vParams[i]; } m_Beta2MX_ij[counter] = vParams[0]; } } if (nodeName == "cphi") { /* * Get the string containing all of the values */ getFloatArray(xmlChild, vParams, false, "", "Cphi"); nParamsFound = vParams.size(); if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) { if (nParamsFound != 1) { throw CanteraError("HMWSoln::readXMLBinarySalt::Cphi for " + ispName + "::" + jspName, "wrong number of params found"); } m_CphiMX_ij[counter] = vParams[0]; m_CphiMX_ij_coeff(0,counter) = m_CphiMX_ij[counter]; } else if (m_formPitzerTemp == PITZER_TEMP_LINEAR) { if (nParamsFound != 2) { throw CanteraError("HMWSoln::readXMLBinarySalt::Cphi for " + ispName + "::" + jspName, "wrong number of params found"); } m_CphiMX_ij_coeff(0,counter) = vParams[0]; m_CphiMX_ij_coeff(1,counter) = vParams[1]; m_CphiMX_ij[counter] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) { if (nParamsFound != 5) { throw CanteraError("HMWSoln::readXMLBinarySalt::Cphi for " + ispName + "::" + jspName, "wrong number of params found"); } for (i = 0; i < nParamsFound; i++) { m_CphiMX_ij_coeff(i, counter) = vParams[i]; } m_CphiMX_ij[counter] = vParams[0]; } } if (nodeName == "alpha1") { stemp = xmlChild.value(); m_Alpha1MX_ij[counter] = atofCheck(stemp.c_str()); } if (nodeName == "alpha2") { stemp = xmlChild.value(); m_Alpha2MX_ij[counter] = atofCheck(stemp.c_str()); } } } /** * Process an XML node called "thetaAnion". * This node contains all of the parameters necessary to describe * the binary interactions between two anions. */ void HMWSoln::readXMLThetaAnion(XML_Node& BinSalt) { string xname = BinSalt.name(); vector_fp vParams; size_t nParamsFound = 0; if (xname != "thetaAnion") { throw CanteraError("HMWSoln::readXMLThetaAnion", "Incorrect name for processing this routine: " + xname); } double* charge = DATA_PTR(m_speciesCharge); string stemp; string ispName = BinSalt.attrib("anion1"); if (ispName == "") { throw CanteraError("HMWSoln::readXMLThetaAnion", "no anion1 attrib"); } string jspName = BinSalt.attrib("anion2"); if (jspName == "") { throw CanteraError("HMWSoln::readXMLThetaAnion", "no anion2 attrib"); } /* * Find the index of the species in the current phase. It's not * an error to not find the species */ size_t iSpecies = speciesIndex(ispName); if (iSpecies == npos) { return; } if (charge[iSpecies] >= 0) { throw CanteraError("HMWSoln::readXMLThetaAnion", "anion1 charge problem"); } size_t jSpecies = speciesIndex(jspName); if (jSpecies == npos) { return; } if (charge[jSpecies] >= 0) { throw CanteraError("HMWSoln::readXMLThetaAnion", "anion2 charge problem"); } size_t n = iSpecies * m_kk + jSpecies; int counter = m_CounterIJ[n]; for (size_t i = 0; i < BinSalt.nChildren(); i++) { XML_Node& xmlChild = BinSalt.child(i); stemp = xmlChild.name(); string nodeName = lowercase(stemp); if (nodeName == "theta") { getFloatArray(xmlChild, vParams, false, "", stemp); nParamsFound = vParams.size(); if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) { if (nParamsFound != 1) { throw CanteraError("HMWSoln::readXMLThetaAnion::Theta for " + ispName + "::" + jspName, "wrong number of params found"); } m_Theta_ij_coeff(0,counter) = vParams[0]; m_Theta_ij[counter] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_LINEAR) { if (nParamsFound != 2) { throw CanteraError("HMWSoln::readXMLThetaAnion::Theta for " + ispName + "::" + jspName, "wrong number of params found"); } m_Theta_ij_coeff(0,counter) = vParams[0]; m_Theta_ij_coeff(1,counter) = vParams[1]; m_Theta_ij[counter] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) { if (nParamsFound == 1) { vParams.resize(5, 0.0); nParamsFound = 5; } else if (nParamsFound != 5) { throw CanteraError("HMWSoln::readXMLThetaAnion::Theta for " + ispName + "::" + jspName, "wrong number of params found"); } for (size_t j = 0; j < nParamsFound; j++) { m_Theta_ij_coeff(j, counter) = vParams[j]; } m_Theta_ij[counter] = vParams[0]; } } } } /** * Process an XML node called "thetaCation". * This node contains all of the parameters necessary to describe * the binary interactions between two cation. */ void HMWSoln::readXMLThetaCation(XML_Node& BinSalt) { string xname = BinSalt.name(); vector_fp vParams; size_t nParamsFound = 0; if (xname != "thetaCation") { throw CanteraError("HMWSoln::readXMLThetaCation", "Incorrect name for processing this routine: " + xname); } double* charge = DATA_PTR(m_speciesCharge); string stemp; string ispName = BinSalt.attrib("cation1"); if (ispName == "") { throw CanteraError("HMWSoln::readXMLThetaCation", "no cation1 attrib"); } string jspName = BinSalt.attrib("cation2"); if (jspName == "") { throw CanteraError("HMWSoln::readXMLThetaCation", "no cation2 attrib"); } /* * Find the index of the species in the current phase. It's not * an error to not find the species */ size_t iSpecies = speciesIndex(ispName); if (iSpecies == npos) { return; } if (charge[iSpecies] <= 0) { throw CanteraError("HMWSoln::readXMLThetaCation", "cation1 charge problem"); } size_t jSpecies = speciesIndex(jspName); if (jSpecies == npos) { return; } if (charge[jSpecies] <= 0) { throw CanteraError("HMWSoln::readXMLThetaCation", "cation2 charge problem"); } size_t n = iSpecies * m_kk + jSpecies; int counter = m_CounterIJ[n]; for (size_t i = 0; i < BinSalt.nChildren(); i++) { XML_Node& xmlChild = BinSalt.child(i); stemp = xmlChild.name(); string nodeName = lowercase(stemp); if (nodeName == "theta") { getFloatArray(xmlChild, vParams, false, "", stemp); nParamsFound = vParams.size(); if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) { if (nParamsFound != 1) { throw CanteraError("HMWSoln::readXMLThetaCation::Theta for " + ispName + "::" + jspName, "wrong number of params found"); } m_Theta_ij_coeff(0,counter) = vParams[0]; m_Theta_ij[counter] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_LINEAR) { if (nParamsFound != 2) { throw CanteraError("HMWSoln::readXMLThetaCation::Theta for " + ispName + "::" + jspName, "wrong number of params found"); } m_Theta_ij_coeff(0,counter) = vParams[0]; m_Theta_ij_coeff(1,counter) = vParams[1]; m_Theta_ij[counter] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) { if (nParamsFound == 1) { vParams.resize(5, 0.0); nParamsFound = 5; } else if (nParamsFound != 5) { throw CanteraError("HMWSoln::readXMLThetaCation::Theta for " + ispName + "::" + jspName, "wrong number of params found"); } for (size_t j = 0; j < nParamsFound; j++) { m_Theta_ij_coeff(j, counter) = vParams[j]; } m_Theta_ij[counter] = vParams[0]; } } } } /* * Process an XML node called "readXMLPsiCommonCation". * This node contains all of the parameters necessary to describe * the binary interactions between two anions and one common cation. */ void HMWSoln::readXMLPsiCommonCation(XML_Node& BinSalt) { string xname = BinSalt.name(); if (xname != "psiCommonCation") { throw CanteraError("HMWSoln::readXMLPsiCommonCation", "Incorrect name for processing this routine: " + xname); } double* charge = DATA_PTR(m_speciesCharge); string stemp; vector_fp vParams; size_t nParamsFound = 0; string kName = BinSalt.attrib("cation"); if (kName == "") { throw CanteraError("HMWSoln::readXMLPsiCommonCation", "no cation attrib"); } string iName = BinSalt.attrib("anion1"); if (iName == "") { throw CanteraError("HMWSoln::readXMLPsiCommonCation", "no anion1 attrib"); } string jName = BinSalt.attrib("anion2"); if (jName == "") { throw CanteraError("HMWSoln::readXMLPsiCommonCation", "no anion2 attrib"); } /* * Find the index of the species in the current phase. It's not * an error to not find the species */ size_t kSpecies = speciesIndex(kName); if (kSpecies == npos) { return; } if (charge[kSpecies] <= 0) { throw CanteraError("HMWSoln::readXMLPsiCommonCation", "cation charge problem"); } size_t iSpecies = speciesIndex(iName); if (iSpecies == npos) { return; } if (charge[iSpecies] >= 0) { throw CanteraError("HMWSoln::readXMLPsiCommonCation", "anion1 charge problem"); } size_t jSpecies = speciesIndex(jName); if (jSpecies == npos) { return; } if (charge[jSpecies] >= 0) { throw CanteraError("HMWSoln::readXMLPsiCommonCation", "anion2 charge problem"); } size_t n = iSpecies * m_kk + jSpecies; int counter = m_CounterIJ[n]; for (size_t i = 0; i < BinSalt.nChildren(); i++) { XML_Node& xmlChild = BinSalt.child(i); stemp = xmlChild.name(); string nodeName = lowercase(stemp); if (nodeName == "theta") { stemp = xmlChild.value(); double old = m_Theta_ij[counter]; m_Theta_ij[counter] = atofCheck(stemp.c_str()); if (old != 0.0) { if (old != m_Theta_ij[counter]) { throw CanteraError("HMWSoln::readXMLPsiCommonCation", "conflicting values"); } } } if (nodeName == "psi") { getFloatArray(xmlChild, vParams, false, "", stemp); nParamsFound = vParams.size(); n = iSpecies * m_kk *m_kk + jSpecies * m_kk + kSpecies ; if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) { if (nParamsFound != 1) { throw CanteraError("HMWSoln::readXMLPsiCommonCation::Psi for " + kName + "::" + iName + "::" + jName, "wrong number of params found"); } m_Psi_ijk_coeff(0,n) = vParams[0]; m_Psi_ijk[n] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_LINEAR) { if (nParamsFound != 2) { throw CanteraError("HMWSoln::readXMLPsiCation::Psi for " + kName + "::" + iName + "::" + jName, "wrong number of params found"); } m_Psi_ijk_coeff(0,n) = vParams[0]; m_Psi_ijk_coeff(1,n) = vParams[1]; m_Psi_ijk[n] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) { if (nParamsFound == 1) { vParams.resize(5, 0.0); nParamsFound = 5; } else if (nParamsFound != 5) { throw CanteraError("HMWSoln::readXMLPsiCation::Psi for " + kName + "::" + iName + "::" + jName, "wrong number of params found"); } for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; } // fill in the duplicate entries n = iSpecies * m_kk *m_kk + kSpecies * m_kk + jSpecies ; for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; n = jSpecies * m_kk *m_kk + iSpecies * m_kk + kSpecies ; for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; n = jSpecies * m_kk *m_kk + kSpecies * m_kk + iSpecies ; for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; n = kSpecies * m_kk *m_kk + jSpecies * m_kk + iSpecies ; for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; n = kSpecies * m_kk *m_kk + iSpecies * m_kk + jSpecies ; for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; } } } /** * Process an XML node called "PsiCommonAnion". * This node contains all of the parameters necessary to describe * the binary interactions between two cations and one common anion. */ void HMWSoln::readXMLPsiCommonAnion(XML_Node& BinSalt) { string xname = BinSalt.name(); if (xname != "psiCommonAnion") { throw CanteraError("HMWSoln::readXMLPsiCommonAnion", "Incorrect name for processing this routine: " + xname); } double* charge = DATA_PTR(m_speciesCharge); string stemp; vector_fp vParams; size_t nParamsFound = 0; string kName = BinSalt.attrib("anion"); if (kName == "") { throw CanteraError("HMWSoln::readXMLPsiCommonAnion", "no anion attrib"); } string iName = BinSalt.attrib("cation1"); if (iName == "") { throw CanteraError("HMWSoln::readXMLPsiCommonAnion", "no cation1 attrib"); } string jName = BinSalt.attrib("cation2"); if (jName == "") { throw CanteraError("HMWSoln::readXMLPsiCommonAnion", "no cation2 attrib"); } /* * Find the index of the species in the current phase. It's not * an error to not find the species */ size_t kSpecies = speciesIndex(kName); if (kSpecies == npos) { return; } if (charge[kSpecies] >= 0) { throw CanteraError("HMWSoln::readXMLPsiCommonAnion", "anion charge problem"); } size_t iSpecies = speciesIndex(iName); if (iSpecies == npos) { return; } if (charge[iSpecies] <= 0) { throw CanteraError("HMWSoln::readXMLPsiCommonAnion", "cation1 charge problem"); } size_t jSpecies = speciesIndex(jName); if (jSpecies == npos) { return; } if (charge[jSpecies] <= 0) { throw CanteraError("HMWSoln::readXMLPsiCommonAnion", "cation2 charge problem"); } size_t n = iSpecies * m_kk + jSpecies; int counter = m_CounterIJ[n]; for (size_t i = 0; i < BinSalt.nChildren(); i++) { XML_Node& xmlChild = BinSalt.child(i); stemp = xmlChild.name(); string nodeName = lowercase(stemp); if (nodeName == "theta") { stemp = xmlChild.value(); double old = m_Theta_ij[counter]; m_Theta_ij[counter] = atofCheck(stemp.c_str()); if (old != 0.0) { if (old != m_Theta_ij[counter]) { throw CanteraError("HMWSoln::readXMLPsiCommonAnion", "conflicting values"); } } } if (nodeName == "psi") { getFloatArray(xmlChild, vParams, false, "", stemp); nParamsFound = vParams.size(); n = iSpecies * m_kk *m_kk + jSpecies * m_kk + kSpecies ; if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) { if (nParamsFound != 1) { throw CanteraError("HMWSoln::readXMLPsiCommonAnion::Psi for " + kName + "::" + iName + "::" + jName, "wrong number of params found"); } m_Psi_ijk_coeff(0,n) = vParams[0]; m_Psi_ijk[n] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_LINEAR) { if (nParamsFound != 2) { throw CanteraError("HMWSoln::readXMLPsiAnion::Psi for " + kName + "::" + iName + "::" + jName, "wrong number of params found"); } m_Psi_ijk_coeff(0,n) = vParams[0]; m_Psi_ijk_coeff(1,n) = vParams[1]; m_Psi_ijk[n] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) { if (nParamsFound == 1) { vParams.resize(5, 0.0); nParamsFound = 5; } else if (nParamsFound != 5) { throw CanteraError("HMWSoln::readXMLPsiAnion::Psi for " + kName + "::" + iName + "::" + jName, "wrong number of params found"); } for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; } // fill in the duplicate entries n = iSpecies * m_kk *m_kk + kSpecies * m_kk + jSpecies ; for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; n = jSpecies * m_kk *m_kk + iSpecies * m_kk + kSpecies ; for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; n = jSpecies * m_kk *m_kk + kSpecies * m_kk + iSpecies ; for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; n = kSpecies * m_kk *m_kk + jSpecies * m_kk + iSpecies ; for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; n = kSpecies * m_kk *m_kk + iSpecies * m_kk + jSpecies ; for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; } } } /** * Process an XML node called "LambdaNeutral". * This node contains all of the parameters necessary to describe * the binary interactions between one neutral species and * any other species (neutral or otherwise) in the mechanism. */ void HMWSoln::readXMLLambdaNeutral(XML_Node& BinSalt) { string xname = BinSalt.name(); vector_fp vParams; size_t nParamsFound; if (xname != "lambdaNeutral") { throw CanteraError("HMWSoln::readXMLLanbdaNeutral", "Incorrect name for processing this routine: " + xname); } double* charge = DATA_PTR(m_speciesCharge); string stemp; string iName = BinSalt.attrib("species1"); if (iName == "") { throw CanteraError("HMWSoln::readXMLLambdaNeutral", "no species1 attrib"); } string jName = BinSalt.attrib("species2"); if (jName == "") { throw CanteraError("HMWSoln::readXMLLambdaNeutral", "no species2 attrib"); } /* * Find the index of the species in the current phase. It's not * an error to not find the species */ size_t iSpecies = speciesIndex(iName); if (iSpecies == npos) { return; } if (charge[iSpecies] != 0) { throw CanteraError("HMWSoln::readXMLLambdaNeutral", "neutral charge problem"); } size_t jSpecies = speciesIndex(jName); if (jSpecies == npos) { return; } for (size_t i = 0; i < BinSalt.nChildren(); i++) { XML_Node& xmlChild = BinSalt.child(i); stemp = xmlChild.name(); string nodeName = lowercase(stemp); if (nodeName == "lambda") { size_t nCount = iSpecies*m_kk + jSpecies; getFloatArray(xmlChild, vParams, false, "", stemp); nParamsFound = vParams.size(); if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) { if (nParamsFound != 1) { throw CanteraError("HMWSoln::readXMLLambdaNeutral::Lambda for " + iName + "::" + jName, "wrong number of params found"); } m_Lambda_nj_coeff(0,nCount) = vParams[0]; m_Lambda_nj(iSpecies,jSpecies) = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_LINEAR) { if (nParamsFound != 2) { throw CanteraError("HMWSoln::readXMLLambdaNeutral::Lambda for " + iName + "::" + jName, "wrong number of params found"); } m_Lambda_nj_coeff(0,nCount) = vParams[0]; m_Lambda_nj_coeff(1,nCount) = vParams[1]; m_Lambda_nj(iSpecies, jSpecies) = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) { if (nParamsFound == 1) { vParams.resize(5, 0.0); nParamsFound = 5; } else if (nParamsFound != 5) { throw CanteraError("HMWSoln::readXMLLambdaNeutral::Lambda for " + iName + "::" + jName, "wrong number of params found"); } for (size_t j = 0; j < nParamsFound; j++) { m_Lambda_nj_coeff(j,nCount) = vParams[j]; } m_Lambda_nj(iSpecies, jSpecies) = vParams[0]; } } } } /** * Process an XML node called "MunnnNeutral". * This node contains all of the parameters necessary to describe * the self-ternary interactions for one neutral species. */ void HMWSoln::readXMLMunnnNeutral(XML_Node& BinSalt) { string xname = BinSalt.name(); vector_fp vParams; size_t nParamsFound; if (xname != "MunnnNeutral") { throw CanteraError("HMWSoln::readXMLMunnnNeutral", "Incorrect name for processing this routine: " + xname); } double* charge = DATA_PTR(m_speciesCharge); string stemp; string iName = BinSalt.attrib("species1"); if (iName == "") { throw CanteraError("HMWSoln::readXMLMunnnNeutral", "no species1 attrib"); } /* * Find the index of the species in the current phase. It's not * an error to not find the species */ size_t iSpecies = speciesIndex(iName); if (iSpecies == npos) { return; } if (charge[iSpecies] != 0) { throw CanteraError("HMWSoln::readXMLMunnnNeutral", "neutral charge problem"); } for (size_t i = 0; i < BinSalt.nChildren(); i++) { XML_Node& xmlChild = BinSalt.child(i); stemp = xmlChild.name(); string nodeName = lowercase(stemp); if (nodeName == "munnn") { getFloatArray(xmlChild, vParams, false, "", "Munnn"); nParamsFound = vParams.size(); if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) { if (nParamsFound != 1) { throw CanteraError("HMWSoln::readXMLMunnnNeutral::Munnn for " + iName, "wrong number of params found"); } m_Mu_nnn_coeff(0,iSpecies) = vParams[0]; m_Mu_nnn[iSpecies] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_LINEAR) { if (nParamsFound != 2) { throw CanteraError("HMWSoln::readXMLMunnnNeutral::Munnn for " + iName, "wrong number of params found"); } m_Mu_nnn_coeff(0, iSpecies) = vParams[0]; m_Mu_nnn_coeff(1, iSpecies) = vParams[1]; m_Mu_nnn[iSpecies] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) { if (nParamsFound == 1) { vParams.resize(5, 0.0); nParamsFound = 5; } else if (nParamsFound != 5) { throw CanteraError("HMWSoln::readXMLMunnnNeutral::Munnn for " + iName, "wrong number of params found"); } for (size_t j = 0; j < nParamsFound; j++) { m_Mu_nnn_coeff(j, iSpecies) = vParams[j]; } m_Mu_nnn[iSpecies] = vParams[0]; } } } } /* * Process an XML node called "readXMLZetaCation". * This node contains all of the parameters necessary to describe * the ternary interactions between a neutral, a cation and an anion */ void HMWSoln::readXMLZetaCation(const XML_Node& BinSalt) { string xname = BinSalt.name(); if (xname != "zetaCation") { throw CanteraError("HMWSoln::readXMLZetaCation", "Incorrect name for processing this routine: " + xname); } double* charge = DATA_PTR(m_speciesCharge); string stemp; vector_fp vParams; size_t nParamsFound = 0; string iName = BinSalt.attrib("neutral"); if (iName == "") { throw CanteraError("HMWSoln::readXMLZetaCation", "no neutral attrib"); } string jName = BinSalt.attrib("cation1"); if (jName == "") { throw CanteraError("HMWSoln::readXMLZetaCation", "no cation1 attrib"); } string kName = BinSalt.attrib("anion1"); if (kName == "") { throw CanteraError("HMWSoln::readXMLZetaCation", "no anion1 attrib"); } /* * Find the index of the species in the current phase. It's not * an error to not find the species */ size_t iSpecies = speciesIndex(iName); if (iSpecies == npos) { return; } if (charge[iSpecies] != 0.0) { throw CanteraError("HMWSoln::readXMLZetaCation", "neutral charge problem"); } size_t jSpecies = speciesIndex(jName); if (jSpecies == npos) { return; } if (charge[jSpecies] <= 0.0) { throw CanteraError("HMWSoln::readXLZetaCation", "cation1 charge problem"); } size_t kSpecies = speciesIndex(kName); if (kSpecies == npos) { return; } if (charge[kSpecies] >= 0.0) { throw CanteraError("HMWSoln::readXMLZetaCation", "anion1 charge problem"); } for (size_t i = 0; i < BinSalt.nChildren(); i++) { XML_Node& xmlChild = BinSalt.child(i); stemp = xmlChild.name(); string nodeName = lowercase(stemp); if (nodeName == "zeta") { getFloatArray(xmlChild, vParams, false, "", "zeta"); nParamsFound = vParams.size(); size_t n = iSpecies * m_kk *m_kk + jSpecies * m_kk + kSpecies ; if (m_formPitzerTemp == PITZER_TEMP_CONSTANT) { if (nParamsFound != 1) { throw CanteraError("HMWSoln::readXMLZetaCation::Zeta for " + iName + "::" + jName + "::" + kName, "wrong number of params found"); } m_Psi_ijk_coeff(0,n) = vParams[0]; m_Psi_ijk[n] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_LINEAR) { if (nParamsFound != 2) { throw CanteraError("HMWSoln::readXMLZetaCation::Zeta for " + iName + "::" + jName + "::" + kName, "wrong number of params found"); } m_Psi_ijk_coeff(0,n) = vParams[0]; m_Psi_ijk_coeff(1,n) = vParams[1]; m_Psi_ijk[n] = vParams[0]; } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) { if (nParamsFound == 1) { vParams.resize(5, 0.0); nParamsFound = 5; } else if (nParamsFound != 5) { throw CanteraError("HMWSoln::readXMLZetaCation::Zeta for " + iName + "::" + jName + "::" + kName, "wrong number of params found"); } for (size_t j = 0; j < nParamsFound; j++) { m_Psi_ijk_coeff(j, n) = vParams[j]; } m_Psi_ijk[n] = vParams[0]; } // There are no duplicate entries } } } // Process an XML node called "croppingCoefficients" // for the cropping coefficients values /* * @param acNode Activity Coefficient XML Node */ void HMWSoln::readXMLCroppingCoefficients(const XML_Node& acNode) { if (acNode.hasChild("croppingCoefficients")) { XML_Node& cropNode = acNode.child("croppingCoefficients"); if (cropNode.hasChild("ln_gamma_k_min")) { XML_Node& gkminNode = cropNode.child("ln_gamma_k_min"); getOptionalFloat(gkminNode, "pureSolventValue", CROP_ln_gamma_k_min); } if (cropNode.hasChild("ln_gamma_k_max")) { XML_Node& gkmaxNode = cropNode.child("ln_gamma_k_max"); getOptionalFloat(gkmaxNode, "pureSolventValue", CROP_ln_gamma_k_max); } if (cropNode.hasChild("ln_gamma_o_min")) { XML_Node& gominNode = cropNode.child("ln_gamma_o_min"); getOptionalFloat(gominNode, "pureSolventValue", CROP_ln_gamma_o_min); } if (cropNode.hasChild("ln_gamma_o_max")) { XML_Node& gomaxNode = cropNode.child("ln_gamma_o_max"); getOptionalFloat(gomaxNode, "pureSolventValue", CROP_ln_gamma_o_max); } } } /* * Initialization routine for a HMWSoln phase. * * This is a virtual routine. This routine will call initThermo() * for the parent class as well. */ void HMWSoln::initThermo() { MolalityVPSSTP::initThermo(); initLengths(); } /* * Import, construct, and initialize a HMWSoln phase * specification from an XML tree into the current object. * * This routine is a precursor to constructPhaseXML(XML_Node*) * routine, which does most of the work. * * @param infile XML file containing the description of the * phase * * @param id Optional parameter identifying the name of the * phase. If none is given, the first XML * phase element will be used. */ void HMWSoln::constructPhaseFile(std::string inputFile, std::string id) { if (inputFile.size() == 0) { throw CanteraError("HMWSoln:constructPhaseFile", "input file is null"); } string path = findInputFile(inputFile); std::ifstream fin(path.c_str()); if (!fin) { throw CanteraError("HMWSoln:constructPhaseFile","could not open " +path+" for reading."); } /* * The phase object automatically constructs an XML object. * Use this object to store information. */ XML_Node& phaseNode_XML = xml(); XML_Node* fxml = new XML_Node(); fxml->build(fin); XML_Node* fxml_phase = findXMLPhase(fxml, id); if (!fxml_phase) { throw CanteraError("HMWSoln:constructPhaseFile", "ERROR: Can not find phase named " + id + " in file named " + inputFile); } fxml_phase->copy(&phaseNode_XML); constructPhaseXML(*fxml_phase, id); delete fxml; } /* * Import, construct, and initialize a HMWSoln phase * specification from an XML tree into the current object. * * Most of the work is carried out by the cantera base * routine, importPhase(). That routine imports all of the * species and element data, including the standard states * of the species. * * Then, In this routine, we read the information * particular to the specification of the activity * coefficient model for the Pitzer parameterization. * * We also read information about the molar volumes of the * standard states if present in the XML file. * * @param phaseNode This object must be the phase node of a * complete XML tree * description of the phase, including all of the * species data. In other words while "phase" must * point to an XML phase object, it must have * sibling nodes "speciesData" that describe * the species in the phase. * @param id ID of the phase. If nonnull, a check is done * to see if phaseNode is pointing to the phase * with the correct id. */ void HMWSoln::constructPhaseXML(XML_Node& phaseNode, std::string id) { string stemp; if (id.size() > 0) { string idp = phaseNode.id(); if (idp != id) { throw CanteraError("HMWSoln::constructPhaseXML", "phasenode and Id are incompatible"); } } /* * Find the Thermo XML node */ if (!phaseNode.hasChild("thermo")) { throw CanteraError("HMWSoln::constructPhaseXML", "no thermo XML node"); } XML_Node& thermoNode = phaseNode.child("thermo"); /* * Possibly change the form of the standard concentrations */ if (thermoNode.hasChild("standardConc")) { XML_Node& scNode = thermoNode.child("standardConc"); m_formGC = 2; stemp = scNode.attrib("model"); string formString = lowercase(stemp); if (formString != "") { if (formString == "unity") { m_formGC = 0; printf("exit standardConc = unity not done\n"); exit(EXIT_FAILURE); } else if (formString == "molar_volume") { m_formGC = 1; printf("exit standardConc = molar_volume not done\n"); exit(EXIT_FAILURE); } else if (formString == "solvent_volume") { m_formGC = 2; } else { throw CanteraError("HMWSoln::constructPhaseXML", "Unknown standardConc model: " + formString); } } } /* * Get the Name of the Solvent: * solventName */ string solventName = ""; if (thermoNode.hasChild("solvent")) { XML_Node& scNode = thermoNode.child("solvent"); vector nameSolventa; getStringArray(scNode, nameSolventa); int nsp = static_cast(nameSolventa.size()); if (nsp != 1) { throw CanteraError("HMWSoln::constructPhaseXML", "badly formed solvent XML node"); } solventName = nameSolventa[0]; } /* * Determine the form of the Pitzer model, * We will use this information to size arrays below. */ if (thermoNode.hasChild("activityCoefficients")) { XML_Node& scNode = thermoNode.child("activityCoefficients"); stemp = scNode.attrib("model"); string formString = lowercase(stemp); if (formString != "") { if (formString == "pitzer" || formString == "default") { m_formPitzer = PITZERFORM_BASE; } else if (formString == "base") { m_formPitzer = PITZERFORM_BASE; } else { throw CanteraError("HMWSoln::constructPhaseXML", "Unknown Pitzer ActivityCoeff model: " + formString); } } /* * Determine the form of the temperature dependence * of the Pitzer activity coefficient model. */ stemp = scNode.attrib("TempModel"); formString = lowercase(stemp); if (formString != "") { if (formString == "constant" || formString == "default") { m_formPitzerTemp = PITZER_TEMP_CONSTANT; } else if (formString == "linear") { m_formPitzerTemp = PITZER_TEMP_LINEAR; } else if (formString == "complex" || formString == "complex1") { m_formPitzerTemp = PITZER_TEMP_COMPLEX1; } else { throw CanteraError("HMWSoln::constructPhaseXML", "Unknown Pitzer ActivityCoeff Temp model: " + formString); } } /* * Determine the reference temperature * of the Pitzer activity coefficient model's temperature * dependence formulation: defaults to 25C */ stemp = scNode.attrib("TempReference"); formString = lowercase(stemp); if (formString != "") { m_TempPitzerRef = atofCheck(formString.c_str()); } else { m_TempPitzerRef = 273.15 + 25; } } /* * Call the Cantera importPhase() function. This will import * all of the species into the phase. This will also handle * all of the solvent and solute standard states */ bool m_ok = importPhase(phaseNode, this); if (!m_ok) { throw CanteraError("HMWSoln::constructPhaseXML","importPhase failed "); } } /** * Process the XML file after species are set up. * * This gets called from importPhase(). It processes the XML file * after the species are set up. This is the main routine for * reading in activity coefficient parameters. * * @param phaseNode This object must be the phase node of a * complete XML tree * description of the phase, including all of the * species data. In other words while "phase" must * point to an XML phase object, it must have * sibling nodes "speciesData" that describe * the species in the phase. * @param id ID of the phase. If nonnull, a check is done * to see if phaseNode is pointing to the phase * with the correct id. */ void HMWSoln:: initThermoXML(XML_Node& phaseNode, std::string id) { string stemp; /* * Find the Thermo XML node */ if (!phaseNode.hasChild("thermo")) { throw CanteraError("HMWSoln::initThermoXML", "no thermo XML node"); } XML_Node& thermoNode = phaseNode.child("thermo"); /* * Get the Name of the Solvent: * solventName */ string solventName = ""; if (thermoNode.hasChild("solvent")) { XML_Node& scNode = thermoNode.child("solvent"); vector nameSolventa; getStringArray(scNode, nameSolventa); int nsp = static_cast(nameSolventa.size()); if (nsp != 1) { throw CanteraError("HMWSoln::initThermoXML", "badly formed solvent XML node"); } solventName = nameSolventa[0]; } /* * Initialize all of the lengths of arrays in the object * now that we know what species are in the phase. */ initLengths(); /* * Reconcile the solvent name and index. */ for (size_t k = 0; k < m_kk; k++) { string sname = speciesName(k); if (solventName == sname) { setSolvent(k); if (k != 0) { throw CanteraError("HMWSoln::initThermoXML", "Solvent must be species 0 atm"); } m_indexSolvent = k; break; } } if (m_indexSolvent == npos) { std::cout << "HMWSoln::initThermo: Solvent Name not found" << std::endl; throw CanteraError("HMWSoln::initThermoXML", "Solvent name not found"); } if (m_indexSolvent != 0) { throw CanteraError("HMWSoln::initThermoXML", "Solvent " + solventName + " should be first species"); } /* * Now go get the specification of the standard states for * species in the solution. This includes the molar volumes * data blocks for incompressible species. */ XML_Node& speciesList = phaseNode.child("speciesArray"); XML_Node* speciesDB = get_XML_NameID("speciesData", speciesList["datasrc"], &phaseNode.root()); const vector&sss = speciesNames(); for (size_t k = 0; k < m_kk; k++) { XML_Node* s = speciesDB->findByAttr("name", sss[k]); if (!s) { throw CanteraError("HMWSoln::initThermoXML", "Species Data Base " + sss[k] + " not found"); } XML_Node* ss = s->findByName("standardState"); if (!ss) { throw CanteraError("HMWSoln::initThermoXML", "Species " + sss[k] + " standardState XML block not found"); } string modelStringa = ss->attrib("model"); if (modelStringa == "") { throw CanteraError("HMWSoln::initThermoXML", "Species " + sss[k] + " standardState XML block model attribute not found"); } string modelString = lowercase(modelStringa); if (k == 0) { if (modelString == "wateriapws" || modelString == "real_water" || modelString == "waterpdss") { /* * Store a local pointer to the water standard state model. * -> We've hardcoded it to a PDSS_Water model, so this is ok. */ m_waterSS = dynamic_cast(providePDSS(0)) ; if (!m_waterSS) { throw CanteraError("HMWSoln::initThermoXML", "Dynamic cast to PDSS_Water failed"); } /* * Fill in the molar volume of water (m3/kmol) * at standard conditions to fill in the m_speciesSize entry * with something reasonable. */ m_waterSS->setState_TP(300., OneAtm); double dens = m_waterSS->density(); double mw = m_waterSS->molecularWeight(); m_speciesSize[0] = mw / dens; #ifdef DEBUG_HKM_NOT cout << "Solvent species " << sss[k] << " has volume " << m_speciesSize[k] << endl; #endif } else { // throw CanteraError("HMWSoln::initThermoXML", // "Solvent SS Model \"" + modelStringa + // "\" is not allowed, name = " + sss[0]); m_waterSS = providePDSS(0); m_waterSS->setState_TP(300., OneAtm); double dens = m_waterSS->density(); double mw = m_waterSS->molecularWeight(); m_speciesSize[0] = mw / dens; } } else { if (modelString != "constant_incompressible" && modelString != "hkft") { throw CanteraError("HMWSoln::initThermoXML", "Solute SS Model \"" + modelStringa + "\" is not known"); } if (modelString == "constant_incompressible") { m_speciesSize[k] = getFloat(*ss, "molarVolume", "toSI"); #ifdef DEBUG_HKM_NOT cout << "species " << sss[k] << " has volume " << m_speciesSize[k] << endl; #endif } // HKM Note, have to fill up m_speciesSize[] for HKFT species } } /* * Initialize the water property calculator. It will share * the internal eos water calculator. */ m_waterProps = new WaterProps(dynamic_cast(m_waterSS)); /* * Fill in parameters for the calculation of the * stoichiometric Ionic Strength * * The default is that stoich charge is the same as the * regular charge. */ for (size_t k = 0; k < m_kk; k++) { m_speciesCharge_Stoich[k] = m_speciesCharge[k]; } /* * Go get all of the coefficients and factors in the * activityCoefficients XML block */ XML_Node* acNodePtr = 0; if (thermoNode.hasChild("activityCoefficients")) { XML_Node& acNode = thermoNode.child("activityCoefficients"); acNodePtr = &acNode; /* * Look for parameters for A_Debye */ if (acNode.hasChild("A_Debye")) { XML_Node& ADebye = acNode.child("A_Debye"); m_form_A_Debye = A_DEBYE_CONST; stemp = "model"; if (ADebye.hasAttrib(stemp)) { string atemp = ADebye.attrib(stemp); stemp = lowercase(atemp); if (stemp == "water") { m_form_A_Debye = A_DEBYE_WATER; } } if (m_form_A_Debye == A_DEBYE_CONST) { m_A_Debye = getFloat(acNode, "A_Debye"); } #ifdef DEBUG_HKM_NOT cout << "A_Debye = " << m_A_Debye << endl; #endif } /* * Look for Parameters for the Maximum Ionic Strength */ if (acNode.hasChild("maxIonicStrength")) { m_maxIionicStrength = getFloat(acNode, "maxIonicStrength"); #ifdef DEBUG_HKM_NOT cout << "m_maxIionicStrength = " < Look for the subelement "stoichIsMods" * in each of the species SS databases. */ std::vector xspecies = speciesData(); string kname, jname; size_t jj = xspecies.size(); for (size_t k = 0; k < m_kk; k++) { size_t jmap = npos; kname = speciesName(k); for (size_t j = 0; j < jj; j++) { const XML_Node& sp = *xspecies[j]; jname = sp["name"]; if (jname == kname) { jmap = j; break; } } if (jmap != npos) { const XML_Node& sp = *xspecies[jmap]; getOptionalFloat(sp, "stoichIsMods", m_speciesCharge_Stoich[k]); // if (sp.hasChild("stoichIsMods")) { // double val = getFloat(sp, "stoichIsMods"); //m_speciesCharge_Stoich[k] = val; //} } } /* * Now look at the activity coefficient database */ if (acNodePtr) { if (acNodePtr->hasChild("stoichIsMods")) { XML_Node& sIsNode = acNodePtr->child("stoichIsMods"); map msIs; getMap(sIsNode, msIs); map::const_iterator _b = msIs.begin(); for (; _b != msIs.end(); ++_b) { size_t kk = speciesIndex(_b->first); if (kk != npos) { double val = fpValue(_b->second); m_speciesCharge_Stoich[kk] = val; } } } } /* * Loop through the children getting multiple instances of * parameters */ if (acNodePtr) { for (size_t i = 0; i < acNodePtr->nChildren(); i++) { XML_Node& xmlACChild = acNodePtr->child(i); stemp = xmlACChild.name(); string nodeName = lowercase(stemp); /* * Process a binary salt field, or any of the other XML fields * that make up the Pitzer Database. Entries will be ignored * if any of the species in the entry isn't in the solution. */ if (nodeName == "binarysaltparameters") { readXMLBinarySalt(xmlACChild); } else if (nodeName == "thetaanion") { readXMLThetaAnion(xmlACChild); } else if (nodeName == "thetacation") { readXMLThetaCation(xmlACChild); } else if (nodeName == "psicommonanion") { readXMLPsiCommonAnion(xmlACChild); } else if (nodeName == "psicommoncation") { readXMLPsiCommonCation(xmlACChild); } else if (nodeName == "lambdaneutral") { readXMLLambdaNeutral(xmlACChild); } else if (nodeName == "zetacation") { readXMLZetaCation(xmlACChild); } } } // Go look up the optional Cropping parameters readXMLCroppingCoefficients(acNode); } /* * Fill in the vector specifying the electrolyte species * type * * First fill in default values. Everthing is either * a charge species, a nonpolar neutral, or the solvent. */ for (size_t k = 0; k < m_kk; k++) { if (fabs(m_speciesCharge[k]) > 0.0001) { m_electrolyteSpeciesType[k] = cEST_chargedSpecies; if (fabs(m_speciesCharge_Stoich[k] - m_speciesCharge[k]) > 0.0001) { m_electrolyteSpeciesType[k] = cEST_weakAcidAssociated; } } else if (fabs(m_speciesCharge_Stoich[k]) > 0.0001) { m_electrolyteSpeciesType[k] = cEST_weakAcidAssociated; } else { m_electrolyteSpeciesType[k] = cEST_nonpolarNeutral; } } m_electrolyteSpeciesType[m_indexSolvent] = cEST_solvent; /* * First look at the species database. * -> Look for the subelement "stoichIsMods" * in each of the species SS databases. */ std::vector xspecies = speciesData(); const XML_Node* spPtr = 0; string kname; for (size_t k = 0; k < m_kk; k++) { kname = speciesName(k); spPtr = xspecies[k]; if (!spPtr) { if (spPtr->hasChild("electrolyteSpeciesType")) { string est = getChildValue(*spPtr, "electrolyteSpeciesType"); if ((m_electrolyteSpeciesType[k] = interp_est(est)) == -1) { throw CanteraError("HMWSoln::initThermoXML", "Bad electrolyte type: " + est); } } } } /* * Then look at the phase thermo specification */ if (acNodePtr) { if (acNodePtr->hasChild("electrolyteSpeciesType")) { XML_Node& ESTNode = acNodePtr->child("electrolyteSpeciesType"); map msEST; getMap(ESTNode, msEST); map::const_iterator _b = msEST.begin(); for (; _b != msEST.end(); ++_b) { size_t kk = speciesIndex(_b->first); if (kk != npos) { string est = _b->second; if ((m_electrolyteSpeciesType[kk] = interp_est(est)) == -1) { throw CanteraError("HMWSoln::initThermoXML", "Bad electrolyte type: " + est); } } } } } IMS_typeCutoff_ = 2; if (IMS_typeCutoff_ == 2) { calcIMSCutoffParams_(); } calcMCCutoffParams_(); setMoleFSolventMin(1.0E-5); MolalityVPSSTP::initThermoXML(phaseNode, id); /* * Lastly calculate the charge balance and then add stuff until the charges compensate */ vector_fp mf(m_kk, 0.0); getMoleFractions(DATA_PTR(mf)); bool notDone = true; do { double sum = 0.0; size_t kMaxC = npos; double MaxC = 0.0; for (size_t k = 0; k < m_kk; k++) { sum += mf[k] * m_speciesCharge[k]; if (fabs(mf[k] * m_speciesCharge[k]) > MaxC) { kMaxC = k; } } size_t kHp = speciesIndex("H+"); size_t kOHm = speciesIndex("OH-"); if (fabs(sum) > 1.0E-30) { if (kHp != npos) { if (mf[kHp] > sum * 1.1) { mf[kHp] -= sum; mf[0] += sum; notDone = false; } else { if (sum > 0.0) { mf[kHp] *= 0.5; mf[0] += mf[kHp]; sum -= mf[kHp]; } } } if (notDone) { if (kOHm != npos) { if (mf[kOHm] > -sum * 1.1) { mf[kOHm] += sum; mf[0] -= sum; notDone = false; } else { if (sum < 0.0) { mf[kOHm] *= 0.5; mf[0] += mf[kOHm]; sum += mf[kOHm]; } } } if (notDone) { if (kMaxC != npos) { if (mf[kMaxC] > (1.1 * sum / m_speciesCharge[kMaxC])) { mf[kMaxC] -= sum / m_speciesCharge[kMaxC]; mf[0] += sum / m_speciesCharge[kMaxC]; } else { mf[kMaxC] *= 0.5; mf[0] += mf[kMaxC]; notDone = true; } } } } setMoleFractions(DATA_PTR(mf)); } else { notDone = false; } } while (notDone); // if (phaseNode.hasChild("state")) { // XML_Node& stateNode = phaseNode.child("state"); // setStateFromXML(stateNode); //} } //==================================================================================================================== // Precalculate the IMS Cutoff parameters for typeCutoff = 2 void HMWSoln::calcIMSCutoffParams_() { IMS_afCut_ = 1.0 / (std::exp(1.0) * IMS_gamma_k_min_); IMS_efCut_ = 0.0; bool converged = false; double oldV = 0.0; int its; for (its = 0; its < 100 && !converged; its++) { oldV = IMS_efCut_; IMS_afCut_ = 1.0 / (std::exp(1.0) * IMS_gamma_k_min_) -IMS_efCut_; IMS_bfCut_ = IMS_afCut_ / IMS_cCut_ + IMS_slopefCut_ - 1.0; IMS_dfCut_ = ((- IMS_afCut_/IMS_cCut_ + IMS_bfCut_ - IMS_bfCut_*IMS_X_o_cutoff_/IMS_cCut_) / (IMS_X_o_cutoff_*IMS_X_o_cutoff_/IMS_cCut_ - 2.0 * IMS_X_o_cutoff_)); double tmp = IMS_afCut_ + IMS_X_o_cutoff_*(IMS_bfCut_ + IMS_dfCut_ *IMS_X_o_cutoff_); double eterm = std::exp(-IMS_X_o_cutoff_/IMS_cCut_); IMS_efCut_ = - eterm * (tmp); if (fabs(IMS_efCut_ - oldV) < 1.0E-14) { converged = true; } } if (!converged) { throw CanteraError("HMWSoln::calcIMSCutoffParams_()", " failed to converge on the f polynomial"); } converged = false; double f_0 = IMS_afCut_ + IMS_efCut_; double f_prime_0 = 1.0 - IMS_afCut_ / IMS_cCut_ + IMS_bfCut_; IMS_egCut_ = 0.0; for (its = 0; its < 100 && !converged; its++) { oldV = IMS_egCut_; double lng_0 = -log(IMS_gamma_o_min_) - f_prime_0 / f_0; IMS_agCut_ = exp(lng_0) - IMS_egCut_; IMS_bgCut_ = IMS_agCut_ / IMS_cCut_ + IMS_slopegCut_ - 1.0; IMS_dgCut_ = ((- IMS_agCut_/IMS_cCut_ + IMS_bgCut_ - IMS_bgCut_*IMS_X_o_cutoff_/IMS_cCut_) / (IMS_X_o_cutoff_*IMS_X_o_cutoff_/IMS_cCut_ - 2.0 * IMS_X_o_cutoff_)); double tmp = IMS_agCut_ + IMS_X_o_cutoff_*(IMS_bgCut_ + IMS_dgCut_ *IMS_X_o_cutoff_); double eterm = std::exp(-IMS_X_o_cutoff_/IMS_cCut_); IMS_egCut_ = - eterm * (tmp); if (fabs(IMS_egCut_ - oldV) < 1.0E-14) { converged = true; } } if (!converged) { throw CanteraError("HMWSoln::calcIMSCutoffParams_()", " failed to converge on the g polynomial"); } } // Precalculate the MC Cutoff parameters void HMWSoln::calcMCCutoffParams_() { MC_X_o_min_ = 0.35; MC_X_o_cutoff_ = 0.6; MC_slopepCut_ = 0.02; MC_cpCut_ = 0.25; // Initial starting values MC_apCut_ = MC_X_o_min_; MC_epCut_ = 0.0; bool converged = false; double oldV = 0.0; int its; double damp = 0.5; for (its = 0; its < 500 && !converged; its++) { oldV = MC_epCut_; MC_apCut_ = damp *(MC_X_o_min_ - MC_epCut_) + (1-damp) * MC_apCut_; double MC_bpCutNew = MC_apCut_ / MC_cpCut_ + MC_slopepCut_ - 1.0; MC_bpCut_ = damp * MC_bpCutNew + (1-damp) * MC_bpCut_; double MC_dpCutNew = ((- MC_apCut_/MC_cpCut_ + MC_bpCut_ - MC_bpCut_ * MC_X_o_cutoff_/MC_cpCut_) / (MC_X_o_cutoff_ * MC_X_o_cutoff_/MC_cpCut_ - 2.0 * MC_X_o_cutoff_)); MC_dpCut_ = damp * MC_dpCutNew + (1-damp) * MC_dpCut_; double tmp = MC_apCut_ + MC_X_o_cutoff_*(MC_bpCut_ + MC_dpCut_ * MC_X_o_cutoff_); double eterm = std::exp(- MC_X_o_cutoff_ / MC_cpCut_); MC_epCut_ = - eterm * (tmp); double diff = MC_epCut_ - oldV; if (fabs(diff) < 1.0E-14) { converged = true; } } if (!converged) { throw CanteraError("HMWSoln::calcMCCutoffParams_()", " failed to converge on the p polynomial"); } } }