260 lines
8.3 KiB
C++
260 lines
8.3 KiB
C++
/**
|
|
* @file MaskellSolidSolnPhase.cpp Implementation file for an ideal solid
|
|
* solution model with incompressible thermodynamics (see \ref
|
|
* thermoprops and \link Cantera::MaskellSolidSolnPhase
|
|
* MaskellSolidSolnPhase\endlink).
|
|
*/
|
|
|
|
// 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/MaskellSolidSolnPhase.h"
|
|
#include "cantera/base/stringUtils.h"
|
|
#include "cantera/base/xml.h"
|
|
|
|
#include <cassert>
|
|
|
|
namespace Cantera
|
|
{
|
|
|
|
MaskellSolidSolnPhase::MaskellSolidSolnPhase() :
|
|
m_Pcurrent(OneAtm),
|
|
h_mixing(0.0),
|
|
product_species_index(-1),
|
|
reactant_species_index(-1)
|
|
{
|
|
}
|
|
|
|
void MaskellSolidSolnPhase::getActivityConcentrations(doublereal* c) const
|
|
{
|
|
getActivityCoefficients(c);
|
|
for (size_t sp = 0; sp < m_kk; ++sp) {
|
|
c[sp] *= moleFraction(sp);
|
|
}
|
|
}
|
|
|
|
// Molar Thermodynamic Properties of the Solution
|
|
|
|
doublereal MaskellSolidSolnPhase::enthalpy_mole() const
|
|
{
|
|
const doublereal h0 = RT() * mean_X(m_h0_RT);
|
|
const doublereal r = moleFraction(product_species_index);
|
|
const doublereal fmval = fm(r);
|
|
return h0 + r * fmval * h_mixing;
|
|
}
|
|
|
|
doublereal xlogx(doublereal x)
|
|
{
|
|
return x * std::log(x);
|
|
}
|
|
|
|
doublereal MaskellSolidSolnPhase::entropy_mole() const
|
|
{
|
|
const doublereal s0 = GasConstant * mean_X(m_s0_R);
|
|
const doublereal r = moleFraction(product_species_index);
|
|
const doublereal fmval = fm(r);
|
|
const doublereal rfm = r * fmval;
|
|
return s0 + GasConstant * (xlogx(1-rfm) - xlogx(rfm) - xlogx(1-r-rfm) - xlogx((1-fmval)*r) - xlogx(1-r) - xlogx(r));
|
|
}
|
|
|
|
// Mechanical Equation of State
|
|
|
|
void MaskellSolidSolnPhase::setDensity(const doublereal rho)
|
|
{
|
|
// Unless the input density is exactly equal to the density calculated and
|
|
// stored in the State object, we throw an exception. This is because the
|
|
// density is NOT an independent variable.
|
|
double dens = density();
|
|
if (rho != dens) {
|
|
throw CanteraError("MaskellSolidSolnPhase::setDensity",
|
|
"Density is not an independent variable");
|
|
}
|
|
}
|
|
|
|
void MaskellSolidSolnPhase::calcDensity()
|
|
{
|
|
const vector_fp& vbar = getStandardVolumes();
|
|
|
|
vector_fp moleFracs(m_kk);
|
|
Phase::getMoleFractions(&moleFracs[0]);
|
|
doublereal vtotal = 0.0;
|
|
for (size_t i = 0; i < m_kk; i++) {
|
|
vtotal += vbar[i] * moleFracs[i];
|
|
}
|
|
Phase::setDensity(meanMolecularWeight() / vtotal);
|
|
}
|
|
|
|
void MaskellSolidSolnPhase::setPressure(doublereal p)
|
|
{
|
|
m_Pcurrent = p;
|
|
}
|
|
|
|
void MaskellSolidSolnPhase::setMolarDensity(const doublereal n)
|
|
{
|
|
throw CanteraError("MaskellSolidSolnPhase::setMolarDensity",
|
|
"Density is not an independent variable");
|
|
}
|
|
|
|
// Chemical Potentials and Activities
|
|
|
|
void MaskellSolidSolnPhase::getActivityCoefficients(doublereal* ac) const
|
|
{
|
|
static const int cacheId = m_cache.getId();
|
|
CachedArray cached = m_cache.getArray(cacheId);
|
|
if (!cached.validate(temperature(), pressure(), stateMFNumber())) {
|
|
cached.value.resize(2);
|
|
|
|
const doublereal r = moleFraction(product_species_index);
|
|
const doublereal pval = p(r);
|
|
const doublereal rfm = r * fm(r);
|
|
const doublereal A = (std::pow(1 - rfm, pval) * std::pow(rfm, pval) * std::pow(r - rfm, 1 - pval)) /
|
|
(std::pow(1 - r - rfm, 1 + pval) * (1 - r));
|
|
const doublereal B = pval * h_mixing / RT();
|
|
cached.value[product_species_index] = A * std::exp(B);
|
|
cached.value[reactant_species_index] = 1 / (A * r * (1-r) ) * std::exp(-B);
|
|
}
|
|
std::copy(cached.value.begin(), cached.value.end(), ac);
|
|
}
|
|
|
|
void MaskellSolidSolnPhase::getChemPotentials(doublereal* mu) const
|
|
{
|
|
const doublereal r = moleFraction(product_species_index);
|
|
const doublereal pval = p(r);
|
|
const doublereal rfm = r * fm(r);
|
|
const doublereal DgbarDr = pval * h_mixing +
|
|
RT() *
|
|
std::log( (std::pow(1 - rfm, pval) * std::pow(rfm, pval) * std::pow(r - rfm, 1 - pval) * r) /
|
|
(std::pow(1 - r - rfm, 1 + pval) * (1 - r)) );
|
|
mu[product_species_index] = RT() * m_g0_RT[product_species_index] + DgbarDr;
|
|
mu[reactant_species_index] = RT() * m_g0_RT[reactant_species_index] - DgbarDr;
|
|
}
|
|
|
|
void MaskellSolidSolnPhase::getChemPotentials_RT(doublereal* mu) const
|
|
{
|
|
getChemPotentials(mu);
|
|
for (size_t sp=0; sp < m_kk; ++sp) {
|
|
mu[sp] *= 1.0 / RT();
|
|
}
|
|
}
|
|
|
|
// Partial Molar Properties
|
|
|
|
void MaskellSolidSolnPhase::getPartialMolarEnthalpies(doublereal* hbar) const
|
|
{
|
|
throw CanteraError("MaskellSolidSolnPhase::getPartialMolarEnthalpies()", "Not yet implemented.");
|
|
}
|
|
|
|
void MaskellSolidSolnPhase::getPartialMolarEntropies(doublereal* sbar) const
|
|
{
|
|
throw CanteraError("MaskellSolidSolnPhase::getPartialMolarEntropies()", "Not yet implemented.");
|
|
}
|
|
|
|
void MaskellSolidSolnPhase::getPartialMolarCp(doublereal* cpbar) const
|
|
{
|
|
throw CanteraError("MaskellSolidSolnPhase::getPartialMolarCp()", "Not yet implemented.");
|
|
}
|
|
|
|
void MaskellSolidSolnPhase::getPartialMolarVolumes(doublereal* vbar) const
|
|
{
|
|
getStandardVolumes(vbar);
|
|
}
|
|
|
|
void MaskellSolidSolnPhase::getPureGibbs(doublereal* gpure) const
|
|
{
|
|
for (size_t sp=0; sp < m_kk; ++sp) {
|
|
gpure[sp] = RT() * m_g0_RT[sp];
|
|
}
|
|
}
|
|
|
|
void MaskellSolidSolnPhase::getStandardChemPotentials(doublereal* mu) const
|
|
{
|
|
// What is the difference between this and getPureGibbs? IdealSolidSolnPhase
|
|
// gives the same for both
|
|
getPureGibbs(mu);
|
|
}
|
|
|
|
// Utility Functions
|
|
|
|
void MaskellSolidSolnPhase::initThermo()
|
|
{
|
|
if (m_input.hasKey("excess-enthalpy")) {
|
|
set_h_mix(m_input.convert("excess-enthalpy", "J/kmol"));
|
|
}
|
|
if (m_input.hasKey("product-species")) {
|
|
setProductSpecies(m_input["product-species"].asString());
|
|
}
|
|
VPStandardStateTP::initThermo();
|
|
}
|
|
|
|
|
|
void MaskellSolidSolnPhase::initThermoXML(XML_Node& phaseNode, const std::string& id_)
|
|
{
|
|
if (id_.size() > 0 && phaseNode.id() != id_) {
|
|
throw CanteraError("MaskellSolidSolnPhase::initThermoXML",
|
|
"phasenode and Id are incompatible");
|
|
}
|
|
|
|
// Check on the thermo field. Must have:
|
|
// <thermo model="MaskellSolidSolution" />
|
|
if (phaseNode.hasChild("thermo")) {
|
|
XML_Node& thNode = phaseNode.child("thermo");
|
|
if (!caseInsensitiveEquals(thNode["model"], "maskellsolidsolnphase")) {
|
|
throw CanteraError("MaskellSolidSolnPhase::initThermoXML",
|
|
"Unknown thermo model: " + thNode["model"]);
|
|
}
|
|
|
|
// Parse the enthalpy of mixing constant
|
|
if (thNode.hasChild("h_mix")) {
|
|
set_h_mix(fpValue(thNode.child("h_mix").value()));
|
|
} else {
|
|
throw CanteraError("MaskellSolidSolnPhase::initThermoXML",
|
|
"Mixing enthalpy parameter not specified.");
|
|
}
|
|
|
|
if (thNode.hasChild("product_species")) {
|
|
setProductSpecies(thNode.child("product_species").value());
|
|
} else {
|
|
setProductSpecies(speciesName(0)); // default
|
|
}
|
|
} else {
|
|
throw CanteraError("MaskellSolidSolnPhase::initThermoXML",
|
|
"Unspecified thermo model");
|
|
}
|
|
|
|
// Confirm that the phase only contains 2 species
|
|
if (m_kk != 2) {
|
|
throw CanteraError("MaskellSolidSolnPhase::initThermoXML",
|
|
"MaskellSolidSolution model requires exactly 2 species.");
|
|
}
|
|
|
|
// Call the base initThermo, which handles setting the initial state.
|
|
VPStandardStateTP::initThermoXML(phaseNode, id_);
|
|
}
|
|
|
|
void MaskellSolidSolnPhase::setProductSpecies(const std::string& name)
|
|
{
|
|
product_species_index = static_cast<int>(speciesIndex(name));
|
|
if (product_species_index == -1) {
|
|
throw CanteraError("MaskellSolidSolnPhase::setProductSpecies",
|
|
"Species '{}' not found", name);
|
|
}
|
|
reactant_species_index = (product_species_index == 0) ? 1 : 0;
|
|
}
|
|
|
|
doublereal MaskellSolidSolnPhase::s() const
|
|
{
|
|
return 1 + std::exp(h_mixing / RT());
|
|
}
|
|
|
|
doublereal MaskellSolidSolnPhase::fm(const doublereal r) const
|
|
{
|
|
return (1 - std::sqrt(1 - 4*r*(1-r)/s())) / (2*r);
|
|
}
|
|
|
|
doublereal MaskellSolidSolnPhase::p(const doublereal r) const
|
|
{
|
|
const doublereal sval = s();
|
|
return (1 - 2*r) / std::sqrt(sval*sval - 4 * sval * r + 4 * sval * r * r);
|
|
}
|
|
|
|
} // end namespace Cantera
|