[Thermo] Move implementation of HMWSoln into a single file
This commit is contained in:
parent
3790115b99
commit
e9f08fc58e
2 changed files with 736 additions and 764 deletions
|
|
@ -20,6 +20,9 @@
|
|||
#include "cantera/thermo/PDSS_Water.h"
|
||||
#include "cantera/thermo/electrolytes.h"
|
||||
#include "cantera/base/stringUtils.h"
|
||||
#include "cantera/base/ctml.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Cantera
|
||||
{
|
||||
|
|
@ -426,6 +429,425 @@ doublereal HMWSoln::satPressure(doublereal t) {
|
|||
return pres;
|
||||
}
|
||||
|
||||
static void check_nParams(const std::string& method, size_t nParams,
|
||||
size_t m_formPitzerTemp)
|
||||
{
|
||||
if (m_formPitzerTemp == PITZER_TEMP_CONSTANT && nParams != 1) {
|
||||
throw CanteraError(method, "'constant' temperature model requires one"
|
||||
" coefficient for each of parameter, but {} were given", nParams);
|
||||
} else if (m_formPitzerTemp == PITZER_TEMP_LINEAR && nParams != 2) {
|
||||
throw CanteraError(method, "'linear' temperature model requires two"
|
||||
" coefficients for each parameter, but {} were given", nParams);
|
||||
}
|
||||
if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1 && nParams != 5) {
|
||||
throw CanteraError(method, "'complex' temperature model requires five"
|
||||
" coefficients for each parameter, but {} were given", nParams);
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::setBinarySalt(const std::string& sp1, const std::string& sp2,
|
||||
size_t nParams, double* beta0, double* beta1, double* beta2,
|
||||
double* Cphi, double alpha1, double alpha2)
|
||||
{
|
||||
size_t k1 = speciesIndex(sp1);
|
||||
size_t k2 = speciesIndex(sp2);
|
||||
if (k1 == npos) {
|
||||
throw CanteraError("HMWSoln::setBinarySalt", "Species '{}' not found", sp1);
|
||||
} else if (k2 == npos) {
|
||||
throw CanteraError("HMWSoln::setBinarySalt", "Species '{}' not found", sp2);
|
||||
}
|
||||
if (charge(k1) < 0 && charge(k2) > 0) {
|
||||
std::swap(k1, k2);
|
||||
} else if (charge(k1) * charge(k2) >= 0) {
|
||||
throw CanteraError("HMWSoln::setBinarySalt", "Species '{}' and '{}' "
|
||||
"do not have opposite charges ({}, {})", sp1, sp2,
|
||||
charge(k1), charge(k2));
|
||||
}
|
||||
check_nParams("HMWSoln::setBinarySalt", nParams, m_formPitzerTemp);
|
||||
|
||||
size_t c = m_CounterIJ[k1 * m_kk + k2];
|
||||
m_Beta0MX_ij[c] = beta0[0];
|
||||
m_Beta1MX_ij[c] = beta1[0];
|
||||
m_Beta2MX_ij[c] = beta2[0];
|
||||
m_CphiMX_ij[c] = Cphi[0];
|
||||
for (size_t n = 0; n < nParams; n++) {
|
||||
m_Beta0MX_ij_coeff(n, c) = beta0[n];
|
||||
m_Beta1MX_ij_coeff(n, c) = beta1[n];
|
||||
m_Beta2MX_ij_coeff(n, c) = beta2[n];
|
||||
m_CphiMX_ij_coeff(n, c) = Cphi[n];
|
||||
}
|
||||
m_Alpha1MX_ij[c] = alpha1;
|
||||
m_Alpha2MX_ij[c] = alpha2;
|
||||
}
|
||||
|
||||
void HMWSoln::setTheta(const std::string& sp1, const std::string& sp2,
|
||||
size_t nParams, double* theta)
|
||||
{
|
||||
size_t k1 = speciesIndex(sp1);
|
||||
size_t k2 = speciesIndex(sp2);
|
||||
if (k1 == npos) {
|
||||
throw CanteraError("HMWSoln::setTheta", "Species '{}' not found", sp1);
|
||||
} else if (k2 == npos) {
|
||||
throw CanteraError("HMWSoln::setTheta", "Species '{}' not found", sp2);
|
||||
}
|
||||
if (charge(k1) * charge(k2) <= 0) {
|
||||
throw CanteraError("HMWSoln::setTheta", "Species '{}' and '{}' "
|
||||
"should both have the same (non-zero) charge ({}, {})", sp1, sp2,
|
||||
charge(k1), charge(k2));
|
||||
}
|
||||
check_nParams("HMWSoln::setTheta", nParams, m_formPitzerTemp);
|
||||
size_t c = m_CounterIJ[k1 * m_kk + k2];
|
||||
m_Theta_ij[c] = theta[0];
|
||||
for (size_t n = 0; n < nParams; n++) {
|
||||
m_Theta_ij_coeff(n, c) = theta[n];
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::setPsi(const std::string& sp1, const std::string& sp2,
|
||||
const std::string& sp3, size_t nParams, double* psi)
|
||||
{
|
||||
size_t k1 = speciesIndex(sp1);
|
||||
size_t k2 = speciesIndex(sp2);
|
||||
size_t k3 = speciesIndex(sp3);
|
||||
if (k1 == npos) {
|
||||
throw CanteraError("HMWSoln::setPsi", "Species '{}' not found", sp1);
|
||||
} else if (k2 == npos) {
|
||||
throw CanteraError("HMWSoln::setPsi", "Species '{}' not found", sp2);
|
||||
} else if (k3 == npos) {
|
||||
throw CanteraError("HMWSoln::setPsi", "Species '{}' not found", sp3);
|
||||
}
|
||||
|
||||
if (!charge(k1) || !charge(k2) || !charge(k3) ||
|
||||
std::abs(sign(charge(k1) + sign(charge(k2)) + sign(charge(k3)))) != 1) {
|
||||
throw CanteraError("HMWSoln::setPsi", "All species must be ions and"
|
||||
" must include at least one cation and one anion, but given species"
|
||||
" (charges) were: {} ({}), {} ({}), and {} ({}).",
|
||||
sp1, charge(k1), sp2, charge(k2), sp3, charge(k3));
|
||||
}
|
||||
check_nParams("HMWSoln::setPsi", nParams, m_formPitzerTemp);
|
||||
auto cc = {k1*m_kk*m_kk + k2*m_kk + k3,
|
||||
k1*m_kk*m_kk + k3*m_kk + k2,
|
||||
k2*m_kk*m_kk + k1*m_kk + k3,
|
||||
k2*m_kk*m_kk + k3*m_kk + k1,
|
||||
k3*m_kk*m_kk + k2*m_kk + k1,
|
||||
k3*m_kk*m_kk + k1*m_kk + k2};
|
||||
for (auto c : cc) {
|
||||
for (size_t n = 0; n < nParams; n++) {
|
||||
m_Psi_ijk_coeff(n, c) = psi[n];
|
||||
}
|
||||
m_Psi_ijk[c] = psi[0];
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::setLambda(const std::string& sp1, const std::string& sp2,
|
||||
size_t nParams, double* lambda)
|
||||
{
|
||||
size_t k1 = speciesIndex(sp1);
|
||||
size_t k2 = speciesIndex(sp2);
|
||||
if (k1 == npos) {
|
||||
throw CanteraError("HMWSoln::setLambda", "Species '{}' not found", sp1);
|
||||
} else if (k2 == npos) {
|
||||
throw CanteraError("HMWSoln::setLambda", "Species '{}' not found", sp2);
|
||||
}
|
||||
|
||||
if (charge(k1) != 0 && charge(k2) != 0) {
|
||||
throw CanteraError("HMWSoln::setLambda", "Expected at least one neutral"
|
||||
" species, but given species (charges) were: {} ({}) and {} ({}).",
|
||||
sp1, charge(k1), sp2, charge(k2));
|
||||
}
|
||||
if (charge(k1) != 0) {
|
||||
std::swap(k1, k2);
|
||||
}
|
||||
check_nParams("HMWSoln::setLambda", nParams, m_formPitzerTemp);
|
||||
size_t c = k1*m_kk + k2;
|
||||
for (size_t n = 0; n < nParams; n++) {
|
||||
m_Lambda_nj_coeff(n, c) = lambda[n];
|
||||
}
|
||||
m_Lambda_nj(k1, k2) = lambda[0];
|
||||
}
|
||||
|
||||
void HMWSoln::setMunnn(const std::string& sp, size_t nParams, double* munnn)
|
||||
{
|
||||
size_t k = speciesIndex(sp);
|
||||
if (k == npos) {
|
||||
throw CanteraError("HMWSoln::setMunnn", "Species '{}' not found", sp);
|
||||
}
|
||||
|
||||
if (charge(k) != 0) {
|
||||
throw CanteraError("HMWSoln::setMunnn", "Expected a neutral species,"
|
||||
" got {} ({}).", sp, charge(k));
|
||||
}
|
||||
check_nParams("HMWSoln::setMunnn", nParams, m_formPitzerTemp);
|
||||
for (size_t n = 0; n < nParams; n++) {
|
||||
m_Mu_nnn_coeff(n, k) = munnn[n];
|
||||
}
|
||||
m_Mu_nnn[k] = munnn[0];
|
||||
}
|
||||
|
||||
void HMWSoln::setZeta(const std::string& sp1, const std::string& sp2,
|
||||
const std::string& sp3, size_t nParams, double* psi)
|
||||
{
|
||||
size_t k1 = speciesIndex(sp1);
|
||||
size_t k2 = speciesIndex(sp2);
|
||||
size_t k3 = speciesIndex(sp3);
|
||||
if (k1 == npos) {
|
||||
throw CanteraError("HMWSoln::setZeta", "Species '{}' not found", sp1);
|
||||
} else if (k2 == npos) {
|
||||
throw CanteraError("HMWSoln::setZeta", "Species '{}' not found", sp2);
|
||||
} else if (k3 == npos) {
|
||||
throw CanteraError("HMWSoln::setZeta", "Species '{}' not found", sp3);
|
||||
}
|
||||
|
||||
if (charge(k1)*charge(k2)*charge(k3) != 0 ||
|
||||
sign(charge(k1)) + sign(charge(k2)) + sign(charge(k3)) != 0) {
|
||||
throw CanteraError("HMWSoln::setZeta", "Requires one neutral species, "
|
||||
"one cation, and one anion, but given species (charges) were: "
|
||||
"{} ({}), {} ({}), and {} ({}).",
|
||||
sp1, charge(k1), sp2, charge(k2), sp3, charge(k3));
|
||||
}
|
||||
|
||||
//! Make k1 the neutral species
|
||||
if (charge(k2) == 0) {
|
||||
std::swap(k1, k2);
|
||||
} else if (charge(k3) == 0) {
|
||||
std::swap(k1, k3);
|
||||
}
|
||||
|
||||
// Make k2 the cation
|
||||
if (charge(k3) > 0) {
|
||||
std::swap(k2, k3);
|
||||
}
|
||||
|
||||
check_nParams("HMWSoln::setZeta", nParams, m_formPitzerTemp);
|
||||
// In contrast to setPsi, there are no duplicate entries
|
||||
size_t c = k1 * m_kk *m_kk + k2 * m_kk + k3;
|
||||
for (size_t n = 0; n < nParams; n++) {
|
||||
m_Psi_ijk_coeff(n, c) = psi[n];
|
||||
}
|
||||
m_Psi_ijk[c] = psi[0];
|
||||
}
|
||||
|
||||
void HMWSoln::setPitzerTempModel(const std::string& model)
|
||||
{
|
||||
if (ba::iequals(model, "constant") || ba::iequals(model, "default")) {
|
||||
m_formPitzerTemp = PITZER_TEMP_CONSTANT;
|
||||
} else if (ba::iequals(model, "linear")) {
|
||||
m_formPitzerTemp = PITZER_TEMP_LINEAR;
|
||||
} else if (ba::iequals(model, "complex") || ba::iequals(model, "complex1")) {
|
||||
m_formPitzerTemp = PITZER_TEMP_COMPLEX1;
|
||||
} else {
|
||||
throw CanteraError("HMWSoln::setPitzerTempModel",
|
||||
"Unknown Pitzer ActivityCoeff Temp model: {}", model);
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::setA_Debye(double A)
|
||||
{
|
||||
if (A < 0) {
|
||||
m_form_A_Debye = A_DEBYE_WATER;
|
||||
} else {
|
||||
m_form_A_Debye = A_DEBYE_CONST;
|
||||
m_A_Debye = A;
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::setCroppingCoefficients(double ln_gamma_k_min,
|
||||
double ln_gamma_k_max, double ln_gamma_o_min, double ln_gamma_o_max)
|
||||
{
|
||||
CROP_ln_gamma_k_min = ln_gamma_k_min;
|
||||
CROP_ln_gamma_k_max = ln_gamma_k_max;
|
||||
CROP_ln_gamma_o_min = ln_gamma_o_min;
|
||||
CROP_ln_gamma_o_max = ln_gamma_o_max;
|
||||
}
|
||||
|
||||
void HMWSoln::initThermo()
|
||||
{
|
||||
MolalityVPSSTP::initThermo();
|
||||
initLengths();
|
||||
|
||||
for (int i = 0; i < 17; i++) {
|
||||
elambda[i] = 0.0;
|
||||
elambda1[i] = 0.0;
|
||||
}
|
||||
for (size_t k = 0; k < nSpecies(); k++) {
|
||||
m_speciesSize[k] = providePDSS(k)->molarVolume();
|
||||
}
|
||||
|
||||
// Store a local pointer to the water standard state model.
|
||||
m_waterSS = providePDSS(0);
|
||||
|
||||
// Initialize the water property calculator. It will share the internal eos
|
||||
// water calculator.
|
||||
m_waterProps.reset(new WaterProps(dynamic_cast<PDSS_Water*>(m_waterSS)));
|
||||
|
||||
// Lastly calculate the charge balance and then add stuff until the charges
|
||||
// compensate
|
||||
vector_fp mf(m_kk, 0.0);
|
||||
getMoleFractions(mf.data());
|
||||
bool notDone = true;
|
||||
|
||||
while (notDone) {
|
||||
double sum = 0.0;
|
||||
size_t kMaxC = npos;
|
||||
double MaxC = 0.0;
|
||||
for (size_t k = 0; k < m_kk; k++) {
|
||||
sum += mf[k] * charge(k);
|
||||
if (fabs(mf[k] * charge(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 && kMaxC != npos) {
|
||||
if (mf[kMaxC] > (1.1 * sum / charge(kMaxC))) {
|
||||
mf[kMaxC] -= sum / charge(kMaxC);
|
||||
mf[0] += sum / charge(kMaxC);
|
||||
} else {
|
||||
mf[kMaxC] *= 0.5;
|
||||
mf[0] += mf[kMaxC];
|
||||
notDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
setMoleFractions(mf.data());
|
||||
} else {
|
||||
notDone = false;
|
||||
}
|
||||
}
|
||||
|
||||
calcIMSCutoffParams_();
|
||||
calcMCCutoffParams_();
|
||||
setMoleFSolventMin(1.0E-5);
|
||||
}
|
||||
|
||||
void HMWSoln::initThermoXML(XML_Node& phaseNode, const std::string& id_)
|
||||
{
|
||||
if (id_.size() > 0) {
|
||||
string idp = phaseNode.id();
|
||||
if (idp != id_) {
|
||||
throw CanteraError("HMWSoln::initThermoXML",
|
||||
"phasenode and Id are incompatible");
|
||||
}
|
||||
}
|
||||
|
||||
// Find the Thermo XML node
|
||||
if (!phaseNode.hasChild("thermo")) {
|
||||
throw CanteraError("HMWSoln::initThermoXML",
|
||||
"no thermo XML node");
|
||||
}
|
||||
XML_Node& thermoNode = phaseNode.child("thermo");
|
||||
|
||||
// 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");
|
||||
|
||||
// Determine the form of the temperature dependence of the Pitzer
|
||||
// activity coefficient model.
|
||||
string formString = scNode.attrib("TempModel");
|
||||
if (formString != "") {
|
||||
setPitzerTempModel(formString);
|
||||
}
|
||||
|
||||
// Determine the reference temperature of the Pitzer activity
|
||||
// coefficient model's temperature dependence formulation: defaults to
|
||||
// 25C
|
||||
formString = scNode.attrib("TempReference");
|
||||
if (formString != "") {
|
||||
setPitzerRefTemperature(fpValueCheck(formString));
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize all of the lengths of arrays in the object
|
||||
// now that we know what species are in the phase.
|
||||
initLengths();
|
||||
|
||||
// Go get all of the coefficients and factors in the activityCoefficients
|
||||
// XML block
|
||||
if (thermoNode.hasChild("activityCoefficients")) {
|
||||
XML_Node& acNode = thermoNode.child("activityCoefficients");
|
||||
|
||||
// Look for parameters for A_Debye
|
||||
if (acNode.hasChild("A_Debye")) {
|
||||
XML_Node& ADebye = acNode.child("A_Debye");
|
||||
if (ba::iequals(ADebye["model"], "water")) {
|
||||
setA_Debye(-1);
|
||||
} else {
|
||||
setA_Debye(getFloat(acNode, "A_Debye"));
|
||||
}
|
||||
}
|
||||
|
||||
// Look for Parameters for the Maximum Ionic Strength
|
||||
if (acNode.hasChild("maxIonicStrength")) {
|
||||
setMaxIonicStrength(getFloat(acNode, "maxIonicStrength"));
|
||||
}
|
||||
|
||||
for (const auto& xmlACChild : acNode.children()) {
|
||||
string nodeName = xmlACChild->name();
|
||||
|
||||
// Process any of the XML fields that make up the Pitzer Database.
|
||||
// Entries will be ignored if any of the species in the entry aren't
|
||||
// in the solution.
|
||||
if (ba::iequals(nodeName, "binarysaltparameters")) {
|
||||
readXMLBinarySalt(*xmlACChild);
|
||||
} else if (ba::iequals(nodeName, "thetaanion")) {
|
||||
readXMLTheta(*xmlACChild);
|
||||
} else if (ba::iequals(nodeName, "thetacation")) {
|
||||
readXMLTheta(*xmlACChild);
|
||||
} else if (ba::iequals(nodeName, "psicommonanion")) {
|
||||
readXMLPsi(*xmlACChild);
|
||||
} else if (ba::iequals(nodeName, "psicommoncation")) {
|
||||
readXMLPsi(*xmlACChild);
|
||||
} else if (ba::iequals(nodeName, "lambdaneutral")) {
|
||||
readXMLLambdaNeutral(*xmlACChild);
|
||||
} else if (ba::iequals(nodeName, "zetacation")) {
|
||||
readXMLZetaCation(*xmlACChild);
|
||||
}
|
||||
}
|
||||
|
||||
// Go look up the optional Cropping parameters
|
||||
if (acNode.hasChild("croppingCoefficients")) {
|
||||
XML_Node& cropNode = acNode.child("croppingCoefficients");
|
||||
setCroppingCoefficients(
|
||||
getFloat(cropNode.child("ln_gamma_k_min"), "pureSolventValue"),
|
||||
getFloat(cropNode.child("ln_gamma_k_max"), "pureSolventValue"),
|
||||
getFloat(cropNode.child("ln_gamma_o_min"), "pureSolventValue"),
|
||||
getFloat(cropNode.child("ln_gamma_o_max"), "pureSolventValue"));
|
||||
}
|
||||
}
|
||||
|
||||
MolalityVPSSTP::initThermoXML(phaseNode, id_);
|
||||
}
|
||||
|
||||
double HMWSoln::A_Debye_TP(double tempArg, double presArg) const
|
||||
{
|
||||
double T = temperature();
|
||||
|
|
@ -918,6 +1340,320 @@ void HMWSoln::counterIJ_setup() const
|
|||
}
|
||||
}
|
||||
|
||||
void HMWSoln::readXMLBinarySalt(XML_Node& BinSalt)
|
||||
{
|
||||
if (BinSalt.name() != "binarySaltParameters") {
|
||||
throw CanteraError("HMWSoln::readXMLBinarySalt",
|
||||
"Incorrect name for processing this routine: " + BinSalt.name());
|
||||
}
|
||||
|
||||
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
|
||||
if (speciesIndex(iName) == npos || speciesIndex(jName) == npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_fp beta0, beta1, beta2, Cphi;
|
||||
getFloatArray(BinSalt, beta0, false, "", "beta0");
|
||||
getFloatArray(BinSalt, beta1, false, "", "beta1");
|
||||
getFloatArray(BinSalt, beta2, false, "", "beta2");
|
||||
getFloatArray(BinSalt, Cphi, false, "", "Cphi");
|
||||
if (beta0.size() != beta1.size() || beta0.size() != beta2.size() ||
|
||||
beta0.size() != Cphi.size()) {
|
||||
throw CanteraError("HMWSoln::readXMLBinarySalt", "Inconsistent"
|
||||
" array sizes ({}, {}, {}, {})", beta0.size(), beta1.size(),
|
||||
beta2.size(), Cphi.size());
|
||||
}
|
||||
if (beta0.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
|
||||
beta0.resize(5, 0.0);
|
||||
beta1.resize(5, 0.0);
|
||||
beta2.resize(5, 0.0);
|
||||
Cphi.resize(5, 0.0);
|
||||
}
|
||||
double alpha1 = getFloat(BinSalt, "Alpha1");
|
||||
double alpha2 = 0.0;
|
||||
getOptionalFloat(BinSalt, "Alpha2", alpha2);
|
||||
setBinarySalt(iName, jName, beta0.size(), beta0.data(), beta1.data(),
|
||||
beta2.data(), Cphi.data(), alpha1, alpha2);
|
||||
}
|
||||
|
||||
void HMWSoln::readXMLTheta(XML_Node& node)
|
||||
{
|
||||
string ispName, jspName;
|
||||
if (node.name() == "thetaAnion") {
|
||||
ispName = node.attrib("anion1");
|
||||
if (ispName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLTheta", "no anion1 attrib");
|
||||
}
|
||||
jspName = node.attrib("anion2");
|
||||
if (jspName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLTheta", "no anion2 attrib");
|
||||
}
|
||||
} else if (node.name() == "thetaCation") {
|
||||
ispName = node.attrib("cation1");
|
||||
if (ispName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLTheta", "no cation1 attrib");
|
||||
}
|
||||
jspName = node.attrib("cation2");
|
||||
if (jspName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLTheta", "no cation2 attrib");
|
||||
}
|
||||
} else {
|
||||
throw CanteraError("HMWSoln::readXMLTheta",
|
||||
"Incorrect name for processing this routine: " + node.name());
|
||||
}
|
||||
|
||||
// Find the index of the species in the current phase. It's not an error to
|
||||
// not find the species
|
||||
if (speciesIndex(ispName) == npos || speciesIndex(jspName) == npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_fp theta;
|
||||
getFloatArray(node, theta, false, "", "theta");
|
||||
if (theta.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
|
||||
theta.resize(5, 0.0);
|
||||
}
|
||||
setTheta(ispName, jspName, theta.size(), theta.data());
|
||||
}
|
||||
|
||||
void HMWSoln::readXMLPsi(XML_Node& node)
|
||||
{
|
||||
string iName, jName, kName;
|
||||
if (node.name() == "psiCommonCation") {
|
||||
kName = node.attrib("cation");
|
||||
if (kName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLPsi", "no cation attrib");
|
||||
}
|
||||
iName = node.attrib("anion1");
|
||||
if (iName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLPsi", "no anion1 attrib");
|
||||
}
|
||||
jName = node.attrib("anion2");
|
||||
if (jName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLPsi", "no anion2 attrib");
|
||||
}
|
||||
} else if (node.name() == "psiCommonAnion") {
|
||||
kName = node.attrib("anion");
|
||||
if (kName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLPsi", "no anion attrib");
|
||||
}
|
||||
iName = node.attrib("cation1");
|
||||
if (iName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLPsi", "no cation1 attrib");
|
||||
}
|
||||
jName = node.attrib("cation2");
|
||||
if (jName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLPsi", "no cation2 attrib");
|
||||
}
|
||||
} else {
|
||||
throw CanteraError("HMWSoln::readXMLPsi",
|
||||
"Incorrect name for processing this routine: " + node.name());
|
||||
}
|
||||
|
||||
// Find the index of the species in the current phase. It's not an error to
|
||||
// not find the species
|
||||
if (speciesIndex(iName) == npos || speciesIndex(jName) == npos ||
|
||||
speciesIndex(kName) == npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_fp psi;
|
||||
getFloatArray(node, psi, false, "", "psi");
|
||||
if (psi.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
|
||||
psi.resize(5, 0.0);
|
||||
}
|
||||
setPsi(iName, jName, kName, psi.size(), psi.data());
|
||||
}
|
||||
|
||||
void HMWSoln::readXMLLambdaNeutral(XML_Node& node)
|
||||
{
|
||||
vector_fp vParams;
|
||||
if (node.name() != "lambdaNeutral") {
|
||||
throw CanteraError("HMWSoln::readXMLLambdaNeutral",
|
||||
"Incorrect name for processing this routine: " + node.name());
|
||||
}
|
||||
string iName = node.attrib("species1");
|
||||
if (iName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLLambdaNeutral", "no species1 attrib");
|
||||
}
|
||||
string jName = node.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
|
||||
if (speciesIndex(iName) == npos || speciesIndex(jName) == npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_fp lambda;
|
||||
getFloatArray(node, lambda, false, "", "lambda");
|
||||
if (lambda.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
|
||||
lambda.resize(5, 0.0);
|
||||
}
|
||||
setLambda(iName, jName, lambda.size(), lambda.data());
|
||||
}
|
||||
|
||||
void HMWSoln::readXMLMunnnNeutral(XML_Node& node)
|
||||
{
|
||||
if (node.name() != "MunnnNeutral") {
|
||||
throw CanteraError("HMWSoln::readXMLMunnnNeutral",
|
||||
"Incorrect name for processing this routine: " + node.name());
|
||||
}
|
||||
string iName = node.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
|
||||
if (speciesIndex(iName) == npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_fp munnn;
|
||||
getFloatArray(node, munnn, false, "", "munnn");
|
||||
if (munnn.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
|
||||
munnn.resize(5, 0.0);
|
||||
}
|
||||
setMunnn(iName, munnn.size(), munnn.data());
|
||||
}
|
||||
|
||||
void HMWSoln::readXMLZetaCation(const XML_Node& node)
|
||||
{
|
||||
if (node.name() != "zetaCation") {
|
||||
throw CanteraError("HMWSoln::readXMLZetaCation",
|
||||
"Incorrect name for processing this routine: " + node.name());
|
||||
}
|
||||
|
||||
string iName = node.attrib("neutral");
|
||||
if (iName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLZetaCation", "no neutral attrib");
|
||||
}
|
||||
string jName = node.attrib("cation1");
|
||||
if (jName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLZetaCation", "no cation1 attrib");
|
||||
}
|
||||
string kName = node.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
|
||||
if (speciesIndex(iName) == npos || speciesIndex(jName) == npos ||
|
||||
speciesIndex(kName) == npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_fp zeta;
|
||||
getFloatArray(node, zeta, false, "", "zeta");
|
||||
if (zeta.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
|
||||
zeta.resize(5, 0.0);
|
||||
}
|
||||
setZeta(iName, jName, kName, zeta.size(), zeta.data());
|
||||
}
|
||||
|
||||
void HMWSoln::calcIMSCutoffParams_()
|
||||
{
|
||||
double IMS_gamma_o_min_ = 1.0E-5; // value at the zero solvent point
|
||||
double IMS_gamma_k_min_ = 10.0; // minimum at the zero solvent point
|
||||
double IMS_slopefCut_ = 0.6; // slope of the f function at the zero solvent point
|
||||
|
||||
IMS_afCut_ = 1.0 / (std::exp(1.0) * IMS_gamma_k_min_);
|
||||
IMS_efCut_ = 0.0;
|
||||
bool converged = false;
|
||||
double oldV = 0.0;
|
||||
for (int 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 (int 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");
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::calcMCCutoffParams_()
|
||||
{
|
||||
double MC_X_o_min_ = 0.35; // value at the zero solvent point
|
||||
MC_X_o_cutoff_ = 0.6;
|
||||
double MC_slopepCut_ = 0.02; // slope of the p function at the zero solvent point
|
||||
MC_cpCut_ = 0.25;
|
||||
|
||||
// Initial starting values
|
||||
MC_apCut_ = MC_X_o_min_;
|
||||
MC_epCut_ = 0.0;
|
||||
bool converged = false;
|
||||
double oldV = 0.0;
|
||||
double damp = 0.5;
|
||||
for (int 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");
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::s_updatePitzer_CoeffWRTemp(int doDerivs) const
|
||||
{
|
||||
double T = temperature();
|
||||
|
|
|
|||
|
|
@ -1,764 +0,0 @@
|
|||
/**
|
||||
* @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.
|
||||
*/
|
||||
|
||||
// This file is part of Cantera. See License.txt in the top-level directory or
|
||||
// at http://www.cantera.org/license.txt for license and copyright information.
|
||||
|
||||
#include "cantera/thermo/HMWSoln.h"
|
||||
#include "cantera/thermo/ThermoFactory.h"
|
||||
#include "cantera/thermo/PDSS_Water.h"
|
||||
#include "cantera/thermo/electrolytes.h"
|
||||
#include "cantera/base/stringUtils.h"
|
||||
#include "cantera/base/ctml.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Cantera
|
||||
{
|
||||
|
||||
static void check_nParams(const std::string& method, size_t nParams,
|
||||
size_t m_formPitzerTemp)
|
||||
{
|
||||
if (m_formPitzerTemp == PITZER_TEMP_CONSTANT && nParams != 1) {
|
||||
throw CanteraError(method, "'constant' temperature model requires one"
|
||||
" coefficient for each of parameter, but {} were given", nParams);
|
||||
} else if (m_formPitzerTemp == PITZER_TEMP_LINEAR && nParams != 2) {
|
||||
throw CanteraError(method, "'linear' temperature model requires two"
|
||||
" coefficients for each parameter, but {} were given", nParams);
|
||||
}
|
||||
if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1 && nParams != 5) {
|
||||
throw CanteraError(method, "'complex' temperature model requires five"
|
||||
" coefficients for each parameter, but {} were given", nParams);
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::readXMLBinarySalt(XML_Node& BinSalt)
|
||||
{
|
||||
if (BinSalt.name() != "binarySaltParameters") {
|
||||
throw CanteraError("HMWSoln::readXMLBinarySalt",
|
||||
"Incorrect name for processing this routine: " + BinSalt.name());
|
||||
}
|
||||
|
||||
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
|
||||
if (speciesIndex(iName) == npos || speciesIndex(jName) == npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_fp beta0, beta1, beta2, Cphi;
|
||||
getFloatArray(BinSalt, beta0, false, "", "beta0");
|
||||
getFloatArray(BinSalt, beta1, false, "", "beta1");
|
||||
getFloatArray(BinSalt, beta2, false, "", "beta2");
|
||||
getFloatArray(BinSalt, Cphi, false, "", "Cphi");
|
||||
if (beta0.size() != beta1.size() || beta0.size() != beta2.size() ||
|
||||
beta0.size() != Cphi.size()) {
|
||||
throw CanteraError("HMWSoln::readXMLBinarySalt", "Inconsistent"
|
||||
" array sizes ({}, {}, {}, {})", beta0.size(), beta1.size(),
|
||||
beta2.size(), Cphi.size());
|
||||
}
|
||||
if (beta0.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
|
||||
beta0.resize(5, 0.0);
|
||||
beta1.resize(5, 0.0);
|
||||
beta2.resize(5, 0.0);
|
||||
Cphi.resize(5, 0.0);
|
||||
}
|
||||
double alpha1 = getFloat(BinSalt, "Alpha1");
|
||||
double alpha2 = 0.0;
|
||||
getOptionalFloat(BinSalt, "Alpha2", alpha2);
|
||||
setBinarySalt(iName, jName, beta0.size(), beta0.data(), beta1.data(),
|
||||
beta2.data(), Cphi.data(), alpha1, alpha2);
|
||||
}
|
||||
|
||||
void HMWSoln::setBinarySalt(const std::string& sp1, const std::string& sp2,
|
||||
size_t nParams, double* beta0, double* beta1, double* beta2,
|
||||
double* Cphi, double alpha1, double alpha2)
|
||||
{
|
||||
size_t k1 = speciesIndex(sp1);
|
||||
size_t k2 = speciesIndex(sp2);
|
||||
if (k1 == npos) {
|
||||
throw CanteraError("HMWSoln::setBinarySalt", "Species '{}' not found", sp1);
|
||||
} else if (k2 == npos) {
|
||||
throw CanteraError("HMWSoln::setBinarySalt", "Species '{}' not found", sp2);
|
||||
}
|
||||
if (charge(k1) < 0 && charge(k2) > 0) {
|
||||
std::swap(k1, k2);
|
||||
} else if (charge(k1) * charge(k2) >= 0) {
|
||||
throw CanteraError("HMWSoln::setBinarySalt", "Species '{}' and '{}' "
|
||||
"do not have opposite charges ({}, {})", sp1, sp2,
|
||||
charge(k1), charge(k2));
|
||||
}
|
||||
check_nParams("HMWSoln::setBinarySalt", nParams, m_formPitzerTemp);
|
||||
|
||||
size_t c = m_CounterIJ[k1 * m_kk + k2];
|
||||
m_Beta0MX_ij[c] = beta0[0];
|
||||
m_Beta1MX_ij[c] = beta1[0];
|
||||
m_Beta2MX_ij[c] = beta2[0];
|
||||
m_CphiMX_ij[c] = Cphi[0];
|
||||
for (size_t n = 0; n < nParams; n++) {
|
||||
m_Beta0MX_ij_coeff(n, c) = beta0[n];
|
||||
m_Beta1MX_ij_coeff(n, c) = beta1[n];
|
||||
m_Beta2MX_ij_coeff(n, c) = beta2[n];
|
||||
m_CphiMX_ij_coeff(n, c) = Cphi[n];
|
||||
}
|
||||
m_Alpha1MX_ij[c] = alpha1;
|
||||
m_Alpha2MX_ij[c] = alpha2;
|
||||
}
|
||||
|
||||
|
||||
void HMWSoln::readXMLTheta(XML_Node& node)
|
||||
{
|
||||
string ispName, jspName;
|
||||
if (node.name() == "thetaAnion") {
|
||||
ispName = node.attrib("anion1");
|
||||
if (ispName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLTheta", "no anion1 attrib");
|
||||
}
|
||||
jspName = node.attrib("anion2");
|
||||
if (jspName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLTheta", "no anion2 attrib");
|
||||
}
|
||||
} else if (node.name() == "thetaCation") {
|
||||
ispName = node.attrib("cation1");
|
||||
if (ispName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLTheta", "no cation1 attrib");
|
||||
}
|
||||
jspName = node.attrib("cation2");
|
||||
if (jspName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLTheta", "no cation2 attrib");
|
||||
}
|
||||
} else {
|
||||
throw CanteraError("HMWSoln::readXMLTheta",
|
||||
"Incorrect name for processing this routine: " + node.name());
|
||||
}
|
||||
|
||||
// Find the index of the species in the current phase. It's not an error to
|
||||
// not find the species
|
||||
if (speciesIndex(ispName) == npos || speciesIndex(jspName) == npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_fp theta;
|
||||
getFloatArray(node, theta, false, "", "theta");
|
||||
if (theta.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
|
||||
theta.resize(5, 0.0);
|
||||
}
|
||||
setTheta(ispName, jspName, theta.size(), theta.data());
|
||||
}
|
||||
|
||||
void HMWSoln::setTheta(const std::string& sp1, const std::string& sp2,
|
||||
size_t nParams, double* theta)
|
||||
{
|
||||
size_t k1 = speciesIndex(sp1);
|
||||
size_t k2 = speciesIndex(sp2);
|
||||
if (k1 == npos) {
|
||||
throw CanteraError("HMWSoln::setTheta", "Species '{}' not found", sp1);
|
||||
} else if (k2 == npos) {
|
||||
throw CanteraError("HMWSoln::setTheta", "Species '{}' not found", sp2);
|
||||
}
|
||||
if (charge(k1) * charge(k2) <= 0) {
|
||||
throw CanteraError("HMWSoln::setTheta", "Species '{}' and '{}' "
|
||||
"should both have the same (non-zero) charge ({}, {})", sp1, sp2,
|
||||
charge(k1), charge(k2));
|
||||
}
|
||||
check_nParams("HMWSoln::setTheta", nParams, m_formPitzerTemp);
|
||||
size_t c = m_CounterIJ[k1 * m_kk + k2];
|
||||
m_Theta_ij[c] = theta[0];
|
||||
for (size_t n = 0; n < nParams; n++) {
|
||||
m_Theta_ij_coeff(n, c) = theta[n];
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::readXMLPsi(XML_Node& node)
|
||||
{
|
||||
string iName, jName, kName;
|
||||
if (node.name() == "psiCommonCation") {
|
||||
kName = node.attrib("cation");
|
||||
if (kName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLPsi", "no cation attrib");
|
||||
}
|
||||
iName = node.attrib("anion1");
|
||||
if (iName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLPsi", "no anion1 attrib");
|
||||
}
|
||||
jName = node.attrib("anion2");
|
||||
if (jName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLPsi", "no anion2 attrib");
|
||||
}
|
||||
} else if (node.name() == "psiCommonAnion") {
|
||||
kName = node.attrib("anion");
|
||||
if (kName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLPsi", "no anion attrib");
|
||||
}
|
||||
iName = node.attrib("cation1");
|
||||
if (iName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLPsi", "no cation1 attrib");
|
||||
}
|
||||
jName = node.attrib("cation2");
|
||||
if (jName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLPsi", "no cation2 attrib");
|
||||
}
|
||||
} else {
|
||||
throw CanteraError("HMWSoln::readXMLPsi",
|
||||
"Incorrect name for processing this routine: " + node.name());
|
||||
}
|
||||
|
||||
// Find the index of the species in the current phase. It's not an error to
|
||||
// not find the species
|
||||
if (speciesIndex(iName) == npos || speciesIndex(jName) == npos ||
|
||||
speciesIndex(kName) == npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_fp psi;
|
||||
getFloatArray(node, psi, false, "", "psi");
|
||||
if (psi.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
|
||||
psi.resize(5, 0.0);
|
||||
}
|
||||
setPsi(iName, jName, kName, psi.size(), psi.data());
|
||||
}
|
||||
|
||||
void HMWSoln::setPsi(const std::string& sp1, const std::string& sp2,
|
||||
const std::string& sp3, size_t nParams, double* psi)
|
||||
{
|
||||
size_t k1 = speciesIndex(sp1);
|
||||
size_t k2 = speciesIndex(sp2);
|
||||
size_t k3 = speciesIndex(sp3);
|
||||
if (k1 == npos) {
|
||||
throw CanteraError("HMWSoln::setPsi", "Species '{}' not found", sp1);
|
||||
} else if (k2 == npos) {
|
||||
throw CanteraError("HMWSoln::setPsi", "Species '{}' not found", sp2);
|
||||
} else if (k3 == npos) {
|
||||
throw CanteraError("HMWSoln::setPsi", "Species '{}' not found", sp3);
|
||||
}
|
||||
|
||||
if (!charge(k1) || !charge(k2) || !charge(k3) ||
|
||||
std::abs(sign(charge(k1) + sign(charge(k2)) + sign(charge(k3)))) != 1) {
|
||||
throw CanteraError("HMWSoln::setPsi", "All species must be ions and"
|
||||
" must include at least one cation and one anion, but given species"
|
||||
" (charges) were: {} ({}), {} ({}), and {} ({}).",
|
||||
sp1, charge(k1), sp2, charge(k2), sp3, charge(k3));
|
||||
}
|
||||
check_nParams("HMWSoln::setPsi", nParams, m_formPitzerTemp);
|
||||
auto cc = {k1*m_kk*m_kk + k2*m_kk + k3,
|
||||
k1*m_kk*m_kk + k3*m_kk + k2,
|
||||
k2*m_kk*m_kk + k1*m_kk + k3,
|
||||
k2*m_kk*m_kk + k3*m_kk + k1,
|
||||
k3*m_kk*m_kk + k2*m_kk + k1,
|
||||
k3*m_kk*m_kk + k1*m_kk + k2};
|
||||
for (auto c : cc) {
|
||||
for (size_t n = 0; n < nParams; n++) {
|
||||
m_Psi_ijk_coeff(n, c) = psi[n];
|
||||
}
|
||||
m_Psi_ijk[c] = psi[0];
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::readXMLLambdaNeutral(XML_Node& node)
|
||||
{
|
||||
vector_fp vParams;
|
||||
if (node.name() != "lambdaNeutral") {
|
||||
throw CanteraError("HMWSoln::readXMLLambdaNeutral",
|
||||
"Incorrect name for processing this routine: " + node.name());
|
||||
}
|
||||
string iName = node.attrib("species1");
|
||||
if (iName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLLambdaNeutral", "no species1 attrib");
|
||||
}
|
||||
string jName = node.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
|
||||
if (speciesIndex(iName) == npos || speciesIndex(jName) == npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_fp lambda;
|
||||
getFloatArray(node, lambda, false, "", "lambda");
|
||||
if (lambda.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
|
||||
lambda.resize(5, 0.0);
|
||||
}
|
||||
setLambda(iName, jName, lambda.size(), lambda.data());
|
||||
}
|
||||
|
||||
void HMWSoln::setLambda(const std::string& sp1, const std::string& sp2,
|
||||
size_t nParams, double* lambda)
|
||||
{
|
||||
size_t k1 = speciesIndex(sp1);
|
||||
size_t k2 = speciesIndex(sp2);
|
||||
if (k1 == npos) {
|
||||
throw CanteraError("HMWSoln::setLambda", "Species '{}' not found", sp1);
|
||||
} else if (k2 == npos) {
|
||||
throw CanteraError("HMWSoln::setLambda", "Species '{}' not found", sp2);
|
||||
}
|
||||
|
||||
if (charge(k1) != 0 && charge(k2) != 0) {
|
||||
throw CanteraError("HMWSoln::setLambda", "Expected at least one neutral"
|
||||
" species, but given species (charges) were: {} ({}) and {} ({}).",
|
||||
sp1, charge(k1), sp2, charge(k2));
|
||||
}
|
||||
if (charge(k1) != 0) {
|
||||
std::swap(k1, k2);
|
||||
}
|
||||
check_nParams("HMWSoln::setLambda", nParams, m_formPitzerTemp);
|
||||
size_t c = k1*m_kk + k2;
|
||||
for (size_t n = 0; n < nParams; n++) {
|
||||
m_Lambda_nj_coeff(n, c) = lambda[n];
|
||||
}
|
||||
m_Lambda_nj(k1, k2) = lambda[0];
|
||||
}
|
||||
|
||||
void HMWSoln::readXMLMunnnNeutral(XML_Node& node)
|
||||
{
|
||||
if (node.name() != "MunnnNeutral") {
|
||||
throw CanteraError("HMWSoln::readXMLMunnnNeutral",
|
||||
"Incorrect name for processing this routine: " + node.name());
|
||||
}
|
||||
string iName = node.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
|
||||
if (speciesIndex(iName) == npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_fp munnn;
|
||||
getFloatArray(node, munnn, false, "", "munnn");
|
||||
if (munnn.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
|
||||
munnn.resize(5, 0.0);
|
||||
}
|
||||
setMunnn(iName, munnn.size(), munnn.data());
|
||||
}
|
||||
|
||||
void HMWSoln::setMunnn(const std::string& sp, size_t nParams, double* munnn)
|
||||
{
|
||||
size_t k = speciesIndex(sp);
|
||||
if (k == npos) {
|
||||
throw CanteraError("HMWSoln::setMunnn", "Species '{}' not found", sp);
|
||||
}
|
||||
|
||||
if (charge(k) != 0) {
|
||||
throw CanteraError("HMWSoln::setMunnn", "Expected a neutral species,"
|
||||
" got {} ({}).", sp, charge(k));
|
||||
}
|
||||
check_nParams("HMWSoln::setMunnn", nParams, m_formPitzerTemp);
|
||||
for (size_t n = 0; n < nParams; n++) {
|
||||
m_Mu_nnn_coeff(n, k) = munnn[n];
|
||||
}
|
||||
m_Mu_nnn[k] = munnn[0];
|
||||
|
||||
}
|
||||
|
||||
void HMWSoln::readXMLZetaCation(const XML_Node& node)
|
||||
{
|
||||
if (node.name() != "zetaCation") {
|
||||
throw CanteraError("HMWSoln::readXMLZetaCation",
|
||||
"Incorrect name for processing this routine: " + node.name());
|
||||
}
|
||||
|
||||
string iName = node.attrib("neutral");
|
||||
if (iName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLZetaCation", "no neutral attrib");
|
||||
}
|
||||
string jName = node.attrib("cation1");
|
||||
if (jName == "") {
|
||||
throw CanteraError("HMWSoln::readXMLZetaCation", "no cation1 attrib");
|
||||
}
|
||||
string kName = node.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
|
||||
if (speciesIndex(iName) == npos || speciesIndex(jName) == npos ||
|
||||
speciesIndex(kName) == npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_fp zeta;
|
||||
getFloatArray(node, zeta, false, "", "zeta");
|
||||
if (zeta.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
|
||||
zeta.resize(5, 0.0);
|
||||
}
|
||||
setZeta(iName, jName, kName, zeta.size(), zeta.data());
|
||||
}
|
||||
|
||||
void HMWSoln::setZeta(const std::string& sp1, const std::string& sp2,
|
||||
const std::string& sp3, size_t nParams, double* psi)
|
||||
{
|
||||
size_t k1 = speciesIndex(sp1);
|
||||
size_t k2 = speciesIndex(sp2);
|
||||
size_t k3 = speciesIndex(sp3);
|
||||
if (k1 == npos) {
|
||||
throw CanteraError("HMWSoln::setZeta", "Species '{}' not found", sp1);
|
||||
} else if (k2 == npos) {
|
||||
throw CanteraError("HMWSoln::setZeta", "Species '{}' not found", sp2);
|
||||
} else if (k3 == npos) {
|
||||
throw CanteraError("HMWSoln::setZeta", "Species '{}' not found", sp3);
|
||||
}
|
||||
|
||||
if (charge(k1)*charge(k2)*charge(k3) != 0 ||
|
||||
sign(charge(k1)) + sign(charge(k2)) + sign(charge(k3)) != 0) {
|
||||
throw CanteraError("HMWSoln::setZeta", "Requires one neutral species, "
|
||||
"one cation, and one anion, but given species (charges) were: "
|
||||
"{} ({}), {} ({}), and {} ({}).",
|
||||
sp1, charge(k1), sp2, charge(k2), sp3, charge(k3));
|
||||
}
|
||||
|
||||
//! Make k1 the neutral species
|
||||
if (charge(k2) == 0) {
|
||||
std::swap(k1, k2);
|
||||
} else if (charge(k3) == 0) {
|
||||
std::swap(k1, k3);
|
||||
}
|
||||
|
||||
// Make k2 the cation
|
||||
if (charge(k3) > 0) {
|
||||
std::swap(k2, k3);
|
||||
}
|
||||
|
||||
check_nParams("HMWSoln::setZeta", nParams, m_formPitzerTemp);
|
||||
// In contrast to setPsi, there are no duplicate entries
|
||||
size_t c = k1 * m_kk *m_kk + k2 * m_kk + k3;
|
||||
for (size_t n = 0; n < nParams; n++) {
|
||||
m_Psi_ijk_coeff(n, c) = psi[n];
|
||||
}
|
||||
m_Psi_ijk[c] = psi[0];
|
||||
}
|
||||
|
||||
void HMWSoln::setPitzerTempModel(const std::string& model)
|
||||
{
|
||||
if (ba::iequals(model, "constant") || ba::iequals(model, "default")) {
|
||||
m_formPitzerTemp = PITZER_TEMP_CONSTANT;
|
||||
} else if (ba::iequals(model, "linear")) {
|
||||
m_formPitzerTemp = PITZER_TEMP_LINEAR;
|
||||
} else if (ba::iequals(model, "complex") || ba::iequals(model, "complex1")) {
|
||||
m_formPitzerTemp = PITZER_TEMP_COMPLEX1;
|
||||
} else {
|
||||
throw CanteraError("HMWSoln::setPitzerTempModel",
|
||||
"Unknown Pitzer ActivityCoeff Temp model: {}", model);
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::setA_Debye(double A)
|
||||
{
|
||||
if (A < 0) {
|
||||
m_form_A_Debye = A_DEBYE_WATER;
|
||||
} else {
|
||||
m_form_A_Debye = A_DEBYE_CONST;
|
||||
m_A_Debye = A;
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::setCroppingCoefficients(double ln_gamma_k_min,
|
||||
double ln_gamma_k_max, double ln_gamma_o_min, double ln_gamma_o_max)
|
||||
{
|
||||
CROP_ln_gamma_k_min = ln_gamma_k_min;
|
||||
CROP_ln_gamma_k_max = ln_gamma_k_max;
|
||||
CROP_ln_gamma_o_min = ln_gamma_o_min;
|
||||
CROP_ln_gamma_o_max = ln_gamma_o_max;
|
||||
}
|
||||
|
||||
void HMWSoln::initThermo()
|
||||
{
|
||||
MolalityVPSSTP::initThermo();
|
||||
initLengths();
|
||||
|
||||
for (int i = 0; i < 17; i++) {
|
||||
elambda[i] = 0.0;
|
||||
elambda1[i] = 0.0;
|
||||
}
|
||||
for (size_t k = 0; k < nSpecies(); k++) {
|
||||
m_speciesSize[k] = providePDSS(k)->molarVolume();
|
||||
}
|
||||
|
||||
// Store a local pointer to the water standard state model.
|
||||
m_waterSS = providePDSS(0);
|
||||
|
||||
// Initialize the water property calculator. It will share the internal eos
|
||||
// water calculator.
|
||||
m_waterProps.reset(new WaterProps(dynamic_cast<PDSS_Water*>(m_waterSS)));
|
||||
|
||||
// Lastly calculate the charge balance and then add stuff until the charges
|
||||
// compensate
|
||||
vector_fp mf(m_kk, 0.0);
|
||||
getMoleFractions(mf.data());
|
||||
bool notDone = true;
|
||||
|
||||
while (notDone) {
|
||||
double sum = 0.0;
|
||||
size_t kMaxC = npos;
|
||||
double MaxC = 0.0;
|
||||
for (size_t k = 0; k < m_kk; k++) {
|
||||
sum += mf[k] * charge(k);
|
||||
if (fabs(mf[k] * charge(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 && kMaxC != npos) {
|
||||
if (mf[kMaxC] > (1.1 * sum / charge(kMaxC))) {
|
||||
mf[kMaxC] -= sum / charge(kMaxC);
|
||||
mf[0] += sum / charge(kMaxC);
|
||||
} else {
|
||||
mf[kMaxC] *= 0.5;
|
||||
mf[0] += mf[kMaxC];
|
||||
notDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
setMoleFractions(mf.data());
|
||||
} else {
|
||||
notDone = false;
|
||||
}
|
||||
}
|
||||
|
||||
calcIMSCutoffParams_();
|
||||
calcMCCutoffParams_();
|
||||
setMoleFSolventMin(1.0E-5);
|
||||
}
|
||||
|
||||
void HMWSoln::initThermoXML(XML_Node& phaseNode, const std::string& id_)
|
||||
{
|
||||
if (id_.size() > 0) {
|
||||
string idp = phaseNode.id();
|
||||
if (idp != id_) {
|
||||
throw CanteraError("HMWSoln::initThermoXML",
|
||||
"phasenode and Id are incompatible");
|
||||
}
|
||||
}
|
||||
|
||||
// Find the Thermo XML node
|
||||
if (!phaseNode.hasChild("thermo")) {
|
||||
throw CanteraError("HMWSoln::initThermoXML",
|
||||
"no thermo XML node");
|
||||
}
|
||||
XML_Node& thermoNode = phaseNode.child("thermo");
|
||||
|
||||
// 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");
|
||||
|
||||
// Determine the form of the temperature dependence of the Pitzer
|
||||
// activity coefficient model.
|
||||
string formString = scNode.attrib("TempModel");
|
||||
if (formString != "") {
|
||||
setPitzerTempModel(formString);
|
||||
}
|
||||
|
||||
// Determine the reference temperature of the Pitzer activity
|
||||
// coefficient model's temperature dependence formulation: defaults to
|
||||
// 25C
|
||||
formString = scNode.attrib("TempReference");
|
||||
if (formString != "") {
|
||||
setPitzerRefTemperature(fpValueCheck(formString));
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize all of the lengths of arrays in the object
|
||||
// now that we know what species are in the phase.
|
||||
initLengths();
|
||||
|
||||
// Go get all of the coefficients and factors in the activityCoefficients
|
||||
// XML block
|
||||
if (thermoNode.hasChild("activityCoefficients")) {
|
||||
XML_Node& acNode = thermoNode.child("activityCoefficients");
|
||||
|
||||
// Look for parameters for A_Debye
|
||||
if (acNode.hasChild("A_Debye")) {
|
||||
XML_Node& ADebye = acNode.child("A_Debye");
|
||||
if (ba::iequals(ADebye["model"], "water")) {
|
||||
setA_Debye(-1);
|
||||
} else {
|
||||
setA_Debye(getFloat(acNode, "A_Debye"));
|
||||
}
|
||||
}
|
||||
|
||||
// Look for Parameters for the Maximum Ionic Strength
|
||||
if (acNode.hasChild("maxIonicStrength")) {
|
||||
setMaxIonicStrength(getFloat(acNode, "maxIonicStrength"));
|
||||
}
|
||||
|
||||
for (const auto& xmlACChild : acNode.children()) {
|
||||
string nodeName = xmlACChild->name();
|
||||
|
||||
// Process any of the XML fields that make up the Pitzer Database.
|
||||
// Entries will be ignored if any of the species in the entry aren't
|
||||
// in the solution.
|
||||
if (ba::iequals(nodeName, "binarysaltparameters")) {
|
||||
readXMLBinarySalt(*xmlACChild);
|
||||
} else if (ba::iequals(nodeName, "thetaanion")) {
|
||||
readXMLTheta(*xmlACChild);
|
||||
} else if (ba::iequals(nodeName, "thetacation")) {
|
||||
readXMLTheta(*xmlACChild);
|
||||
} else if (ba::iequals(nodeName, "psicommonanion")) {
|
||||
readXMLPsi(*xmlACChild);
|
||||
} else if (ba::iequals(nodeName, "psicommoncation")) {
|
||||
readXMLPsi(*xmlACChild);
|
||||
} else if (ba::iequals(nodeName, "lambdaneutral")) {
|
||||
readXMLLambdaNeutral(*xmlACChild);
|
||||
} else if (ba::iequals(nodeName, "zetacation")) {
|
||||
readXMLZetaCation(*xmlACChild);
|
||||
}
|
||||
}
|
||||
|
||||
// Go look up the optional Cropping parameters
|
||||
if (acNode.hasChild("croppingCoefficients")) {
|
||||
XML_Node& cropNode = acNode.child("croppingCoefficients");
|
||||
setCroppingCoefficients(
|
||||
getFloat(cropNode.child("ln_gamma_k_min"), "pureSolventValue"),
|
||||
getFloat(cropNode.child("ln_gamma_k_max"), "pureSolventValue"),
|
||||
getFloat(cropNode.child("ln_gamma_o_min"), "pureSolventValue"),
|
||||
getFloat(cropNode.child("ln_gamma_o_max"), "pureSolventValue"));
|
||||
}
|
||||
}
|
||||
|
||||
MolalityVPSSTP::initThermoXML(phaseNode, id_);
|
||||
|
||||
}
|
||||
|
||||
void HMWSoln::calcIMSCutoffParams_()
|
||||
{
|
||||
double IMS_gamma_o_min_ = 1.0E-5; // value at the zero solvent point
|
||||
double IMS_gamma_k_min_ = 10.0; // minimum at the zero solvent point
|
||||
double IMS_slopefCut_ = 0.6; // slope of the f function at the zero solvent point
|
||||
|
||||
IMS_afCut_ = 1.0 / (std::exp(1.0) * IMS_gamma_k_min_);
|
||||
IMS_efCut_ = 0.0;
|
||||
bool converged = false;
|
||||
double oldV = 0.0;
|
||||
for (int 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 (int 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");
|
||||
}
|
||||
}
|
||||
|
||||
void HMWSoln::calcMCCutoffParams_()
|
||||
{
|
||||
double MC_X_o_min_ = 0.35; // value at the zero solvent point
|
||||
MC_X_o_cutoff_ = 0.6;
|
||||
double MC_slopepCut_ = 0.02; // slope of the p function at the zero solvent point
|
||||
MC_cpCut_ = 0.25;
|
||||
|
||||
// Initial starting values
|
||||
MC_apCut_ = MC_X_o_min_;
|
||||
MC_epCut_ = 0.0;
|
||||
bool converged = false;
|
||||
double oldV = 0.0;
|
||||
double damp = 0.5;
|
||||
for (int 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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue