cantera/src/thermo/MolalityVPSSTP.cpp
Ray Speth 9c4a0baa55 [Thermo] Simplify adding species for most phase types
Where possible, extend arrays as species are added rather than requiring a
later call to initThermo(). For phases that do not require any data except
that which is included in the Species objects themselves (notably, this
includes IdealGasPhase), species can now be added dynamically without
affecting the phase state.
2016-04-15 20:56:24 -04:00

558 lines
16 KiB
C++

/**
* @file MolalityVPSSTP.cpp
* Definitions for intermediate ThermoPhase object for phases which
* employ molality based activity coefficient formulations
* (see \ref thermoprops
* and class \link Cantera::MolalityVPSSTP MolalityVPSSTP\endlink).
*/
/*
* Copyright (2005) 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/MolalityVPSSTP.h"
#include "cantera/base/stringUtils.h"
#include "cantera/base/ctml.h"
#include "cantera/base/utilities.h"
#include <fstream>
using namespace std;
namespace Cantera
{
MolalityVPSSTP::MolalityVPSSTP() :
m_indexSolvent(0),
m_pHScalingType(PHSCALE_PITZER),
m_indexCLM(npos),
m_weightSolvent(18.01528),
m_xmolSolventMIN(0.01),
m_Mnaught(18.01528E-3)
{
// Change the default to be that charge neutrality in the phase is necessary
// condition for the proper specification of thermodynamic functions within
// the phase
m_chargeNeutralityNecessary = true;
}
MolalityVPSSTP::MolalityVPSSTP(const MolalityVPSSTP& b) :
m_indexSolvent(b.m_indexSolvent),
m_pHScalingType(b.m_pHScalingType),
m_indexCLM(b.m_indexCLM),
m_xmolSolventMIN(b.m_xmolSolventMIN),
m_Mnaught(b.m_Mnaught),
m_molalities(b.m_molalities)
{
*this = b;
}
MolalityVPSSTP& MolalityVPSSTP::operator=(const MolalityVPSSTP& b)
{
if (&b != this) {
VPStandardStateTP::operator=(b);
m_indexSolvent = b.m_indexSolvent;
m_pHScalingType = b.m_pHScalingType;
m_indexCLM = b.m_indexCLM;
m_weightSolvent = b.m_weightSolvent;
m_xmolSolventMIN = b.m_xmolSolventMIN;
m_Mnaught = b.m_Mnaught;
m_molalities = b.m_molalities;
}
return *this;
}
ThermoPhase* MolalityVPSSTP::duplMyselfAsThermoPhase() const
{
return new MolalityVPSSTP(*this);
}
// -------------- Utilities -------------------------------
void MolalityVPSSTP::setpHScale(const int pHscaleType)
{
m_pHScalingType = pHscaleType;
if (pHscaleType != PHSCALE_PITZER && pHscaleType != PHSCALE_NBS) {
throw CanteraError("MolalityVPSSTP::setpHScale",
"Unknown scale type: {}", pHscaleType);
}
}
int MolalityVPSSTP::pHScale() const
{
return m_pHScalingType;
}
void MolalityVPSSTP::setSolvent(size_t k)
{
if (k >= m_kk) {
throw CanteraError("MolalityVPSSTP::setSolute ",
"bad value");
}
m_indexSolvent = k;
AssertThrowMsg(m_indexSolvent==0, "MolalityVPSSTP::setSolvent",
"Molality-based methods limit solvent id to being 0");
m_weightSolvent = molecularWeight(k);
m_Mnaught = m_weightSolvent / 1000.;
}
size_t MolalityVPSSTP::solventIndex() const
{
return m_indexSolvent;
}
void MolalityVPSSTP::setMoleFSolventMin(doublereal xmolSolventMIN)
{
if (xmolSolventMIN <= 0.0) {
throw CanteraError("MolalityVPSSTP::setSolute ", "trouble");
} else if (xmolSolventMIN > 0.9) {
throw CanteraError("MolalityVPSSTP::setSolute ", "trouble");
}
m_xmolSolventMIN = xmolSolventMIN;
}
doublereal MolalityVPSSTP::moleFSolventMin() const
{
return m_xmolSolventMIN;
}
void MolalityVPSSTP::calcMolalities() const
{
getMoleFractions(m_molalities.data());
double xmolSolvent = std::max(m_molalities[m_indexSolvent], m_xmolSolventMIN);
double denomInv = 1.0/ (m_Mnaught * xmolSolvent);
for (size_t k = 0; k < m_kk; k++) {
m_molalities[k] *= denomInv;
}
}
void MolalityVPSSTP::getMolalities(doublereal* const molal) const
{
calcMolalities();
for (size_t k = 0; k < m_kk; k++) {
molal[k] = m_molalities[k];
}
}
void MolalityVPSSTP::setMolalities(const doublereal* const molal)
{
double Lsum = 1.0 / m_Mnaught;
for (size_t k = 1; k < m_kk; k++) {
m_molalities[k] = molal[k];
Lsum += molal[k];
}
double tmp = 1.0 / Lsum;
m_molalities[m_indexSolvent] = tmp / m_Mnaught;
double sum = m_molalities[m_indexSolvent];
for (size_t k = 1; k < m_kk; k++) {
m_molalities[k] = tmp * molal[k];
sum += m_molalities[k];
}
if (sum != 1.0) {
tmp = 1.0 / sum;
for (size_t k = 0; k < m_kk; k++) {
m_molalities[k] *= tmp;
}
}
setMoleFractions(m_molalities.data());
// Essentially we don't trust the input: We calculate the molalities from
// the mole fractions that we just obtained.
calcMolalities();
}
void MolalityVPSSTP::setMolalitiesByName(const compositionMap& mMap)
{
// HKM -> Might need to be more complicated here, setting neutrals so that
// the existing mole fractions are preserved.
// Get a vector of mole fractions
vector_fp mf(m_kk, 0.0);
getMoleFractions(mf.data());
double xmolSmin = std::max(mf[m_indexSolvent], m_xmolSolventMIN);
for (size_t k = 0; k < m_kk; k++) {
double mol_k = getValue(mMap, speciesName(k), 0.0);
if (mol_k > 0) {
mf[k] = mol_k * m_Mnaught * xmolSmin;
}
}
// check charge neutrality
size_t largePos = npos;
double cPos = 0.0;
size_t largeNeg = npos;
double cNeg = 0.0;
double sum = 0.0;
for (size_t k = 0; k < m_kk; k++) {
double ch = charge(k);
if (mf[k] > 0.0) {
if (ch > 0.0 && ch * mf[k] > cPos) {
largePos = k;
cPos = ch * mf[k];
}
if (ch < 0.0 && fabs(ch) * mf[k] > cNeg) {
largeNeg = k;
cNeg = fabs(ch) * mf[k];
}
}
sum += mf[k] * ch;
}
if (sum != 0.0) {
if (sum > 0.0) {
if (cPos > sum) {
mf[largePos] -= sum / charge(largePos);
} else {
throw CanteraError("MolalityVPSSTP:setMolalitiesbyName",
"unbalanced charges");
}
} else {
if (cNeg > (-sum)) {
mf[largeNeg] -= (-sum) / fabs(charge(largeNeg));
} else {
throw CanteraError("MolalityVPSSTP:setMolalitiesbyName",
"unbalanced charges");
}
}
}
sum = 0.0;
for (size_t k = 0; k < m_kk; k++) {
sum += mf[k];
}
sum = 1.0/sum;
for (size_t k = 0; k < m_kk; k++) {
mf[k] *= sum;
}
setMoleFractions(mf.data());
// After we formally set the mole fractions, we calculate the molalities
// again and store it in this object.
calcMolalities();
}
void MolalityVPSSTP::setMolalitiesByName(const std::string& x)
{
compositionMap xx = parseCompString(x, speciesNames());
setMolalitiesByName(xx);
}
// - Activities, Standard States, Activity Concentrations -----------
int MolalityVPSSTP::activityConvention() const
{
return cAC_CONVENTION_MOLALITY;
}
void MolalityVPSSTP::getActivityConcentrations(doublereal* c) const
{
throw NotImplementedError("MolalityVPSSTP::getActivityConcentrations");
}
doublereal MolalityVPSSTP::standardConcentration(size_t k) const
{
throw NotImplementedError("MolalityVPSSTP::standardConcentration");
}
void MolalityVPSSTP::getActivities(doublereal* ac) const
{
throw NotImplementedError("MolalityVPSSTP::getActivities");
}
void MolalityVPSSTP::getActivityCoefficients(doublereal* ac) const
{
getMolalityActivityCoefficients(ac);
AssertThrow(m_indexSolvent==0, "MolalityVPSSTP::getActivityCoefficients");
double xmolSolvent = std::max(moleFraction(m_indexSolvent), m_xmolSolventMIN);
for (size_t k = 1; k < m_kk; k++) {
ac[k] /= xmolSolvent;
}
}
void MolalityVPSSTP::getMolalityActivityCoefficients(doublereal* acMolality) const
{
getUnscaledMolalityActivityCoefficients(acMolality);
applyphScale(acMolality);
}
doublereal MolalityVPSSTP::osmoticCoefficient() const
{
// First, we calculate the activities all over again
vector_fp act(m_kk);
getActivities(act.data());
// Then, we calculate the sum of the solvent molalities
double sum = 0;
for (size_t k = 1; k < m_kk; k++) {
sum += std::max(m_molalities[k], 0.0);
}
double oc = 1.0;
if (sum > 1.0E-200) {
oc = - log(act[m_indexSolvent]) / (m_Mnaught * sum);
}
return oc;
}
void MolalityVPSSTP::setStateFromXML(const XML_Node& state)
{
VPStandardStateTP::setStateFromXML(state);
string comp = getChildValue(state,"soluteMolalities");
if (comp != "") {
setMolalitiesByName(comp);
}
if (state.hasChild("pressure")) {
double p = getFloat(state, "pressure", "pressure");
setPressure(p);
}
}
void MolalityVPSSTP::setState_TPM(doublereal t, doublereal p,
const doublereal* const molalities)
{
setMolalities(molalities);
setState_TP(t, p);
}
void MolalityVPSSTP::setState_TPM(doublereal t, doublereal p, const compositionMap& m)
{
setMolalitiesByName(m);
setState_TP(t, p);
}
void MolalityVPSSTP::setState_TPM(doublereal t, doublereal p, const std::string& m)
{
setMolalitiesByName(m);
setState_TP(t, p);
}
void MolalityVPSSTP::initThermo()
{
VPStandardStateTP::initThermo();
// Find the Cl- species
m_indexCLM = findCLMIndex();
}
void MolalityVPSSTP::getUnscaledMolalityActivityCoefficients(doublereal* acMolality) const
{
throw NotImplementedError("MolalityVPSSTP::getUnscaledMolalityActivityCoefficients");
}
void MolalityVPSSTP::applyphScale(doublereal* acMolality) const
{
throw NotImplementedError("MolalityVPSSTP::applyphScale");
}
size_t MolalityVPSSTP::findCLMIndex() const
{
size_t indexCLM = npos;
size_t eCl = npos;
size_t eE = npos;
size_t ne = nElements();
for (size_t e = 0; e < ne; e++) {
string sn = elementName(e);
if (sn == "Cl" || sn == "CL") {
eCl = e;
break;
}
}
// We have failed if we can't find the Cl element index
if (eCl == npos) {
return npos;
}
for (size_t e = 0; e < ne; e++) {
string sn = elementName(e);
if (sn == "E" || sn == "e") {
eE = e;
break;
}
}
// We have failed if we can't find the E element index
if (eE == npos) {
return npos;
}
for (size_t k = 1; k < m_kk; k++) {
doublereal nCl = nAtoms(k, eCl);
if (nCl != 1.0) {
continue;
}
doublereal nE = nAtoms(k, eE);
if (nE != 1.0) {
continue;
}
for (size_t e = 0; e < ne; e++) {
if (e != eE && e != eCl) {
doublereal nA = nAtoms(k, e);
if (nA != 0.0) {
continue;
}
}
}
string sn = speciesName(k);
if (sn != "Cl-" && sn != "CL-") {
continue;
}
indexCLM = k;
break;
}
return indexCLM;
}
bool MolalityVPSSTP::addSpecies(shared_ptr<Species> spec)
{
bool added = VPStandardStateTP::addSpecies(spec);
if (added) {
if (m_kk == 1) {
// The solvent defaults to species 0
setSolvent(0);
}
m_molalities.push_back(0.0);
}
return added;
}
std::string MolalityVPSSTP::report(bool show_thermo, doublereal threshold) const
{
fmt::MemoryWriter b;
try {
if (name() != "") {
b.write("\n {}:\n", name());
}
b.write("\n");
b.write(" temperature {:12.6g} K\n", temperature());
b.write(" pressure {:12.6g} Pa\n", pressure());
b.write(" density {:12.6g} kg/m^3\n", density());
b.write(" mean mol. weight {:12.6g} amu\n", meanMolecularWeight());
doublereal phi = electricPotential();
b.write(" potential {:12.6g} V\n", phi);
vector_fp x(m_kk);
vector_fp molal(m_kk);
vector_fp mu(m_kk);
vector_fp muss(m_kk);
vector_fp acMolal(m_kk);
vector_fp actMolal(m_kk);
getMoleFractions(&x[0]);
getMolalities(&molal[0]);
getChemPotentials(&mu[0]);
getStandardChemPotentials(&muss[0]);
getMolalityActivityCoefficients(&acMolal[0]);
getActivities(&actMolal[0]);
size_t iHp = speciesIndex("H+");
if (iHp != npos) {
double pH = -log(actMolal[iHp]) / log(10.0);
b.write(" pH {:12.4g}\n", pH);
}
if (show_thermo) {
b.write("\n");
b.write(" 1 kg 1 kmol\n");
b.write(" ----------- ------------\n");
b.write(" enthalpy {:12.6g} {:12.4g} J\n",
enthalpy_mass(), enthalpy_mole());
b.write(" internal energy {:12.6g} {:12.4g} J\n",
intEnergy_mass(), intEnergy_mole());
b.write(" entropy {:12.6g} {:12.4g} J/K\n",
entropy_mass(), entropy_mole());
b.write(" Gibbs function {:12.6g} {:12.4g} J\n",
gibbs_mass(), gibbs_mole());
b.write(" heat capacity c_p {:12.6g} {:12.4g} J/K\n",
cp_mass(), cp_mole());
try {
b.write(" heat capacity c_v {:12.6g} {:12.4g} J/K\n",
cv_mass(), cv_mole());
} catch (NotImplementedError& e) {
b.write(" heat capacity c_v <not implemented>\n");
}
}
b.write("\n");
int nMinor = 0;
doublereal xMinor = 0.0;
if (show_thermo) {
b.write(" X "
" Molalities Chem.Pot. ChemPotSS ActCoeffMolal\n");
b.write(" "
" (J/kmol) (J/kmol)\n");
b.write(" ------------- "
" ------------ ------------ ------------ ------------\n");
for (size_t k = 0; k < m_kk; k++) {
if (x[k] > threshold) {
if (x[k] > SmallNumber) {
b.write("{:>18s} {:12.6g} {:12.6g} {:12.6g} {:12.6g} {:12.6g}\n",
speciesName(k), x[k], molal[k], mu[k], muss[k], acMolal[k]);
} else {
b.write("{:>18s} {:12.6g} {:12.6g} N/A {:12.6g} {:12.6g}\n",
speciesName(k), x[k], molal[k], muss[k], acMolal[k]);
}
} else {
nMinor++;
xMinor += x[k];
}
}
} else {
b.write(" X"
"Molalities\n");
b.write(" -------------"
" ------------\n");
for (size_t k = 0; k < m_kk; k++) {
if (x[k] > threshold) {
b.write("{:>18s} {:12.6g} {:12.6g}\n",
speciesName(k), x[k], molal[k]);
} else {
nMinor++;
xMinor += x[k];
}
}
}
if (nMinor) {
b.write(" [{:+5d} minor] {:12.6g}\n", nMinor, xMinor);
}
} catch (CanteraError& err) {
return b.str() + err.what();
}
return b.str();
}
void MolalityVPSSTP::getCsvReportData(std::vector<std::string>& names,
std::vector<vector_fp>& data) const
{
names.clear();
data.assign(10, vector_fp(nSpecies()));
names.push_back("X");
getMoleFractions(&data[0][0]);
names.push_back("Molal");
getMolalities(&data[1][0]);
names.push_back("Chem. Pot. (J/kmol)");
getChemPotentials(&data[2][0]);
names.push_back("Chem. Pot. SS (J/kmol)");
getStandardChemPotentials(&data[3][0]);
names.push_back("Molal Act. Coeff.");
getMolalityActivityCoefficients(&data[4][0]);
names.push_back("Molal Activity");
getActivities(&data[5][0]);
names.push_back("Part. Mol Enthalpy (J/kmol)");
getPartialMolarEnthalpies(&data[5][0]);
names.push_back("Part. Mol. Entropy (J/K/kmol)");
getPartialMolarEntropies(&data[6][0]);
names.push_back("Part. Mol. Energy (J/kmol)");
getPartialMolarIntEnergies(&data[7][0]);
names.push_back("Part. Mol. Cp (J/K/kmol");
getPartialMolarCp(&data[8][0]);
names.push_back("Part. Mol. Cv (J/K/kmol)");
getPartialMolarVolumes(&data[9][0]);
}
}