cantera/src/thermo/LatticeSolidPhase.cpp

517 lines
14 KiB
C++

/**
* @file LatticeSolidPhase.cpp
* Definitions for a simple thermodynamics model of a bulk solid phase
* derived from ThermoPhase,
* assuming an ideal solution model based on a lattice of solid atoms
* (see \ref thermoprops and class \link Cantera::LatticeSolidPhase LatticeSolidPhase\endlink).
*/
#include "cantera/thermo/LatticeSolidPhase.h"
#include "cantera/thermo/ThermoFactory.h"
#include "cantera/thermo/SpeciesThermoFactory.h"
#include "cantera/thermo/GeneralSpeciesThermo.h"
#include "cantera/base/ctml.h"
#include "cantera/base/utilities.h"
using namespace std;
namespace Cantera
{
LatticeSolidPhase::LatticeSolidPhase() :
m_press(-1.0),
m_molar_density(0.0),
m_nlattice(0),
m_lattice(0),
m_x(0),
theta_(0),
tmpV_(0)
{
}
LatticeSolidPhase::LatticeSolidPhase(const LatticeSolidPhase& right) :
m_press(-1.0),
m_molar_density(0.0),
m_nlattice(0),
m_lattice(0),
m_x(0),
theta_(0),
tmpV_(0)
{
*this = right;
}
LatticeSolidPhase&
LatticeSolidPhase::operator=(const LatticeSolidPhase& right)
{
if (&right != this) {
ThermoPhase::operator=(right);
m_tlast = right.m_tlast;
m_press = right.m_press;
m_molar_density = right.m_molar_density;
m_nlattice = right.m_nlattice;
deepStdVectorPointerCopy<LatticePhase>(right.m_lattice, m_lattice);
m_x = right.m_x;
theta_ = right.theta_;
tmpV_ = right.tmpV_;
}
return *this;
}
LatticeSolidPhase::~LatticeSolidPhase()
{
// We own the sublattices. So we have to delete the sublattices
for (size_t n = 0; n < m_nlattice; n++) {
delete m_lattice[n];
m_lattice[n] = 0;
}
}
ThermoPhase* LatticeSolidPhase::duplMyselfAsThermoPhase() const
{
return new LatticeSolidPhase(*this);
}
doublereal LatticeSolidPhase::minTemp(size_t k) const
{
if (k != npos) {
for (size_t n = 0; n < m_nlattice; n++) {
if (lkstart_[n+1] < k) {
return (m_lattice[n])->minTemp(k-lkstart_[n]);
}
}
}
doublereal mm = 1.0E300;
for (size_t n = 0; n < m_nlattice; n++) {
double ml = (m_lattice[n])->minTemp();
mm = std::min(mm, ml);
}
return mm;
}
doublereal LatticeSolidPhase::maxTemp(size_t k) const
{
if (k != npos) {
for (size_t n = 0; n < m_nlattice; n++) {
if (lkstart_[n+1] < k) {
return (m_lattice[n])->maxTemp(k - lkstart_[n]);
}
}
}
doublereal mm = -1.0E300;
for (size_t n = 0; n < m_nlattice; n++) {
double ml = (m_lattice[n])->maxTemp();
mm = std::max(mm, ml);
}
return mm;
}
doublereal LatticeSolidPhase::refPressure() const
{
return m_lattice[0]->refPressure();
}
doublereal LatticeSolidPhase::enthalpy_mole() const
{
_updateThermo();
doublereal sum = 0.0;
for (size_t n = 0; n < m_nlattice; n++) {
sum += theta_[n] * m_lattice[n]->enthalpy_mole();
}
return sum;
}
doublereal LatticeSolidPhase::intEnergy_mole() const
{
_updateThermo();
doublereal sum = 0.0;
for (size_t n = 0; n < m_nlattice; n++) {
sum += theta_[n] * m_lattice[n]->intEnergy_mole();
}
return sum;
}
doublereal LatticeSolidPhase::entropy_mole() const
{
_updateThermo();
doublereal sum = 0.0;
for (size_t n = 0; n < m_nlattice; n++) {
sum += theta_[n] * m_lattice[n]->entropy_mole();
}
return sum;
}
doublereal LatticeSolidPhase::gibbs_mole() const
{
_updateThermo();
doublereal sum = 0.0;
for (size_t n = 0; n < m_nlattice; n++) {
sum += theta_[n] * m_lattice[n]->gibbs_mole();
}
return sum;
}
doublereal LatticeSolidPhase::cp_mole() const
{
_updateThermo();
doublereal sum = 0.0;
for (size_t n = 0; n < m_nlattice; n++) {
sum += theta_[n] * m_lattice[n]->cp_mole();
}
return sum;
}
void LatticeSolidPhase::getActivityConcentrations(doublereal* c) const
{
_updateThermo();
size_t strt = 0;
for (size_t n = 0; n < m_nlattice; n++) {
m_lattice[n]->getMoleFractions(c+strt);
strt += m_lattice[n]->nSpecies();
}
}
void LatticeSolidPhase::getActivityCoefficients(doublereal* ac) const
{
for (size_t k = 0; k < m_kk; k++) {
ac[k] = 1.0;
}
}
doublereal LatticeSolidPhase::standardConcentration(size_t k) const
{
return 1.0;
}
doublereal LatticeSolidPhase::logStandardConc(size_t k) const
{
return 0.0;
}
void LatticeSolidPhase::setPressure(doublereal p)
{
m_press = p;
for (size_t n = 0; n < m_nlattice; n++) {
m_lattice[n]->setPressure(m_press);
}
calcDensity();
}
doublereal LatticeSolidPhase::calcDensity()
{
double sum = 0.0;
for (size_t n = 0; n < m_nlattice; n++) {
sum += theta_[n] * m_lattice[n]->density();
}
Phase::setDensity(sum);
return sum;
}
void LatticeSolidPhase::setMoleFractions(const doublereal* const x)
{
size_t nsp, strt = 0;
for (size_t n = 0; n < m_nlattice; n++) {
nsp = m_lattice[n]->nSpecies();
m_lattice[n]->setMoleFractions(x + strt);
strt += nsp;
}
for (size_t k = 0; k < strt; k++) {
m_x[k] = x[k] / m_nlattice;
}
Phase::setMoleFractions(DATA_PTR(m_x));
calcDensity();
}
void LatticeSolidPhase::getMoleFractions(doublereal* const x) const
{
size_t nsp, strt = 0;
// the ifdef block should be the way we calculate this.!!!!!
Phase::getMoleFractions(x);
doublereal sum;
for (size_t n = 0; n < m_nlattice; n++) {
nsp = m_lattice[n]->nSpecies();
sum = 0.0;
for (size_t k = 0; k < nsp; k++) {
sum += (x + strt)[k];
}
for (size_t k = 0; k < nsp; k++) {
(x + strt)[k] /= sum;
}
/*
* At this point we can check against the mole fraction vector of the underlying LatticePhase objects and
* get the same answer.
*/
if (DEBUG_MODE_ENABLED) {
m_lattice[n]->getMoleFractions(&(m_x[strt]));
for (size_t k = 0; k < nsp; k++) {
if (fabs((x + strt)[k] - m_x[strt+k]) > 1.0E-14) {
throw CanteraError("LatticeSolidPhase::getMoleFractions()",
"internal error");
}
}
}
strt += nsp;
}
}
void LatticeSolidPhase::getChemPotentials(doublereal* mu) const
{
_updateThermo();
size_t strt = 0;
for (size_t n = 0; n < m_nlattice; n++) {
size_t nlsp = m_lattice[n]->nSpecies();
m_lattice[n]->getChemPotentials(mu+strt);
strt += nlsp;
}
}
void LatticeSolidPhase::getPartialMolarEnthalpies(doublereal* hbar) const
{
_updateThermo();
size_t strt = 0;
for (size_t n = 0; n < m_nlattice; n++) {
size_t nlsp = m_lattice[n]->nSpecies();
m_lattice[n]->getPartialMolarEnthalpies(hbar + strt);
strt += nlsp;
}
}
void LatticeSolidPhase::getPartialMolarEntropies(doublereal* sbar) const
{
_updateThermo();
size_t strt = 0;
for (size_t n = 0; n < m_nlattice; n++) {
size_t nlsp = m_lattice[n]->nSpecies();
m_lattice[n]->getPartialMolarEntropies(sbar + strt);
strt += nlsp;
}
}
void LatticeSolidPhase::getPartialMolarCp(doublereal* cpbar) const
{
_updateThermo();
size_t strt = 0;
for (size_t n = 0; n < m_nlattice; n++) {
size_t nlsp = m_lattice[n]->nSpecies();
m_lattice[n]->getPartialMolarCp(cpbar + strt);
strt += nlsp;
}
}
void LatticeSolidPhase::getPartialMolarVolumes(doublereal* vbar) const
{
_updateThermo();
size_t strt = 0;
for (size_t n = 0; n < m_nlattice; n++) {
size_t nlsp = m_lattice[n]->nSpecies();
m_lattice[n]->getPartialMolarVolumes(vbar + strt);
strt += nlsp;
}
}
void LatticeSolidPhase::getStandardChemPotentials(doublereal* mu0) const
{
_updateThermo();
size_t strt = 0;
for (size_t n = 0; n < m_nlattice; n++) {
m_lattice[n]->getStandardChemPotentials(mu0+strt);
strt += m_lattice[n]->nSpecies();
}
}
void LatticeSolidPhase::getGibbs_RT_ref(doublereal* grt) const
{
_updateThermo();
for (size_t n = 0; n < m_nlattice; n++) {
m_lattice[n]->getGibbs_RT_ref(grt + lkstart_[n]);
}
}
void LatticeSolidPhase::getGibbs_ref(doublereal* g) const
{
getGibbs_RT_ref(g);
for (size_t k = 0; k < m_kk; k++) {
g[k] *= GasConstant * temperature();
}
}
void LatticeSolidPhase::installSlavePhases(Cantera::XML_Node* phaseNode)
{
size_t kk = 0;
size_t kstart = 0;
m_speciesData.clear();
XML_Node& eosdata = phaseNode->child("thermo");
XML_Node& la = eosdata.child("LatticeArray");
std::vector<XML_Node*> lattices = la.getChildren("phase");
for (size_t n = 0; n < m_nlattice; n++) {
LatticePhase* lp = m_lattice[n];
size_t nsp = lp->nSpecies();
vector<doublereal> constArr(lp->nElements());
const vector_fp& aws = lp->atomicWeights();
for (size_t es = 0; es < lp->nElements(); es++) {
string esName = lp->elementName(es);
double wt = aws[es];
int an = lp->atomicNumber(es);
int e298 = lp->entropyElement298(es); //! @todo Why is this an int instead of a double?
int et = lp->elementType(es);
addElement(esName, wt, an, e298, et);
}
const std::vector<const XML_Node*> & spNode = lp->speciesData();
kstart = kk;
for (size_t k = 0; k < nsp; k++) {
std::string sname = lp->speciesName(k);
std::map<std::string, double> comp;
lp->getAtoms(k, DATA_PTR(constArr));
size_t nel = nElements();
vector_fp ecomp(nel, 0.0);
for (size_t m = 0; m < lp->nElements(); m++) {
if (constArr[m] != 0.0) {
std::string oldEname = lp->elementName(m);
size_t newIndex = elementIndex(oldEname);
if (newIndex == npos) {
throw CanteraError("LatticeSolidPhase::installSlavePhases",
"element not found");
}
ecomp[newIndex] = constArr[m];
}
}
double chrg = lp->charge(k);
double sz = lp->size(k);
addUniqueSpecies(sname, &ecomp[0], chrg, sz);
SpeciesThermoInterpType* stit = newSpeciesThermoInterpType(*spNode[k]);
stit->setIndex(kk);
stit->validate(spNode[k]->attrib("name"));
m_spthermo->install_STIT(stit);
m_speciesData.push_back(new XML_Node(*(spNode[k])));
kk++;
}
/*
* Add in the lattice stoichiometry constraint
*/
if (n > 0) {
string econ = "LC_";
econ += int2str(n);
econ += "_" + id();
size_t m = addElement(econ, 0.0, 0, 0.0, CT_ELEM_TYPE_LATTICERATIO);
size_t mm = nElements();
LatticePhase* lp0 = m_lattice[0];
size_t nsp0 = lp0->nSpecies();
for (size_t k = 0; k < nsp0; k++) {
m_speciesComp[k * mm + m] = -theta_[0];
}
for (size_t k = 0; k < nsp; k++) {
size_t ks = kstart + k;
m_speciesComp[ks * mm + m] = theta_[n];
}
}
}
}
void LatticeSolidPhase::initThermo()
{
initLengths();
size_t nsp, loc = 0;
for (size_t n = 0; n < m_nlattice; n++) {
nsp = m_lattice[n]->nSpecies();
lkstart_[n] = loc;
for (size_t k = 0; k < nsp; k++) {
m_x[loc] =m_lattice[n]->moleFraction(k) / (double) m_nlattice;
loc++;
}
lkstart_[n+1] = loc;
}
setMoleFractions(DATA_PTR(m_x));
ThermoPhase::initThermo();
}
void LatticeSolidPhase::initLengths()
{
theta_.resize(m_nlattice,0);
lkstart_.resize(m_nlattice+1);
m_x.resize(m_kk, 0.0);
tmpV_.resize(m_kk, 0.0);
}
void LatticeSolidPhase::_updateThermo() const
{
doublereal tnow = temperature();
if (m_tlast != tnow) {
getMoleFractions(DATA_PTR(m_x));
size_t strt = 0;
for (size_t n = 0; n < m_nlattice; n++) {
m_lattice[n]->setTemperature(tnow);
m_lattice[n]->setMoleFractions(DATA_PTR(m_x) + strt);
m_lattice[n]->setPressure(m_press);
strt += m_lattice[n]->nSpecies();
}
m_tlast = tnow;
}
}
void LatticeSolidPhase::setLatticeMoleFractionsByName(int nn, const std::string& x)
{
m_lattice[nn]->setMoleFractionsByName(x);
size_t loc = 0;
doublereal ndens;
for (size_t n = 0; n < m_nlattice; n++) {
size_t nsp = m_lattice[n]->nSpecies();
ndens = m_lattice[n]->molarDensity();
for (size_t k = 0; k < nsp; k++) {
m_x[loc] = ndens * m_lattice[n]->moleFraction(k);
loc++;
}
}
setMoleFractions(DATA_PTR(m_x));
}
void LatticeSolidPhase::setParametersFromXML(const XML_Node& eosdata)
{
eosdata._require("model","LatticeSolid");
XML_Node& la = eosdata.child("LatticeArray");
std::vector<XML_Node*> lattices = la.getChildren("phase");
size_t nl = lattices.size();
m_nlattice = nl;
for (size_t n = 0; n < nl; n++) {
XML_Node& i = *lattices[n];
m_lattice.push_back((LatticePhase*)newPhase(i));
}
std::vector<string> pnam;
std::vector<string> pval;
XML_Node& ls = eosdata.child("LatticeStoichiometry");
int np = ctml::getPairs(ls, pnam, pval);
theta_.resize(nl);
for (int i = 0; i < np; i++) {
double val = fpValueCheck(pval[i]);
bool found = false;
for (size_t j = 0; j < nl; j++) {
ThermoPhase& tp = *(m_lattice[j]);
string idj = tp.id();
if (idj == pnam[i]) {
theta_[j] = val;
found = true;
break;
}
}
if (!found) {
throw CanteraError("", "not found");
}
}
}
void LatticeSolidPhase::modifyOneHf298SS(const size_t k, const doublereal Hf298New)
{
for (size_t n = 0; n < m_nlattice; n++) {
if (lkstart_[n+1] < k) {
size_t kk = k-lkstart_[n];
SpeciesThermo& l_spthermo = m_lattice[n]->speciesThermo();
l_spthermo.modifyOneHf298(kk, Hf298New);
}
}
m_tlast += 0.0001234;
_updateThermo();
}
} // End namespace Cantera