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.
305 lines
7.3 KiB
C++
305 lines
7.3 KiB
C++
/**
|
|
* @file SingleSpeciesTP.cpp
|
|
* Definitions for the SingleSpeciesTP class, which is a filter class for ThermoPhase,
|
|
* that eases the construction of single species phases
|
|
* ( see \ref thermoprops and class \link Cantera::SingleSpeciesTP SingleSpeciesTP\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/SingleSpeciesTP.h"
|
|
#include "cantera/base/stringUtils.h"
|
|
#include "cantera/base/global.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace Cantera
|
|
{
|
|
SingleSpeciesTP::SingleSpeciesTP() :
|
|
m_press(OneAtm),
|
|
m_p0(OneAtm),
|
|
m_h0_RT(1),
|
|
m_cp0_R(1),
|
|
m_s0_R(1)
|
|
{
|
|
}
|
|
|
|
SingleSpeciesTP::SingleSpeciesTP(const SingleSpeciesTP& right):
|
|
m_press(OneAtm),
|
|
m_p0(OneAtm)
|
|
{
|
|
*this = right;
|
|
}
|
|
|
|
SingleSpeciesTP& SingleSpeciesTP::operator=(const SingleSpeciesTP& right)
|
|
{
|
|
if (&right != this) {
|
|
ThermoPhase::operator=(right);
|
|
m_press = right.m_press;
|
|
m_p0 = right.m_p0;
|
|
m_h0_RT = right.m_h0_RT;
|
|
m_cp0_R = right.m_cp0_R;
|
|
m_s0_R = right.m_s0_R;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ThermoPhase* SingleSpeciesTP::duplMyselfAsThermoPhase() const
|
|
{
|
|
return new SingleSpeciesTP(*this);
|
|
}
|
|
|
|
int SingleSpeciesTP::eosType() const
|
|
{
|
|
throw NotImplementedError("SingleSpeciesTP::eosType");
|
|
}
|
|
|
|
// ------------ Molar Thermodynamic Properties --------------------
|
|
|
|
doublereal SingleSpeciesTP::enthalpy_mole() const
|
|
{
|
|
double hbar;
|
|
getPartialMolarEnthalpies(&hbar);
|
|
return hbar;
|
|
}
|
|
|
|
doublereal SingleSpeciesTP::intEnergy_mole() const
|
|
{
|
|
double ubar;
|
|
getPartialMolarIntEnergies(&ubar);
|
|
return ubar;
|
|
}
|
|
|
|
doublereal SingleSpeciesTP::entropy_mole() const
|
|
{
|
|
double sbar;
|
|
getPartialMolarEntropies(&sbar);
|
|
return sbar;
|
|
}
|
|
|
|
doublereal SingleSpeciesTP::gibbs_mole() const
|
|
{
|
|
double gbar;
|
|
|
|
// Get the chemical potential of the first species. This is the same as the
|
|
// partial molar Gibbs free energy.
|
|
getChemPotentials(&gbar);
|
|
return gbar;
|
|
}
|
|
|
|
doublereal SingleSpeciesTP::cp_mole() const
|
|
{
|
|
double cpbar;
|
|
|
|
// Really should have a partial molar heat capacity function in ThermoPhase.
|
|
// However, the standard state heat capacity will do fine here for now.
|
|
getCp_R(&cpbar);
|
|
cpbar *= GasConstant;
|
|
return cpbar;
|
|
}
|
|
|
|
doublereal SingleSpeciesTP::cv_mole() const
|
|
{
|
|
// For single species, we go directory to the general Cp - Cv relation
|
|
//
|
|
// Cp = Cv + alpha**2 * V * T / beta
|
|
//
|
|
// where
|
|
// alpha = volume thermal expansion coefficient
|
|
// beta = isothermal compressibility
|
|
doublereal cvbar = cp_mole();
|
|
doublereal alpha = thermalExpansionCoeff();
|
|
doublereal beta = isothermalCompressibility();
|
|
doublereal V = molecularWeight(0)/density();
|
|
doublereal T = temperature();
|
|
if (beta != 0.0) {
|
|
cvbar -= alpha * alpha * V * T / beta;
|
|
}
|
|
return cvbar;
|
|
}
|
|
|
|
// ----------- Partial Molar Properties of the Solution -----------------
|
|
|
|
void SingleSpeciesTP::getChemPotentials(doublereal* mu) const
|
|
{
|
|
getStandardChemPotentials(mu);
|
|
}
|
|
|
|
void SingleSpeciesTP::getChemPotentials_RT(doublereal* murt) const
|
|
{
|
|
getStandardChemPotentials(murt);
|
|
murt[0] /= RT();
|
|
}
|
|
|
|
void SingleSpeciesTP::getPartialMolarEnthalpies(doublereal* hbar) const
|
|
{
|
|
getEnthalpy_RT(hbar);
|
|
hbar[0] *= RT();
|
|
}
|
|
|
|
void SingleSpeciesTP::getPartialMolarIntEnergies(doublereal* ubar) const
|
|
{
|
|
getIntEnergy_RT(ubar);
|
|
ubar[0] *= RT();
|
|
}
|
|
|
|
void SingleSpeciesTP::getPartialMolarEntropies(doublereal* sbar) const
|
|
{
|
|
getEntropy_R(sbar);
|
|
sbar[0] *= GasConstant;
|
|
}
|
|
|
|
void SingleSpeciesTP::getPartialMolarCp(doublereal* cpbar) const
|
|
{
|
|
getCp_R(cpbar);
|
|
cpbar[0] *= GasConstant;
|
|
}
|
|
|
|
void SingleSpeciesTP::getPartialMolarVolumes(doublereal* vbar) const
|
|
{
|
|
vbar[0] = molecularWeight(0) / density();
|
|
}
|
|
|
|
// Properties of the Standard State of the Species in the Solution
|
|
|
|
void SingleSpeciesTP::getPureGibbs(doublereal* gpure) const
|
|
{
|
|
getGibbs_RT(gpure);
|
|
gpure[0] *= RT();
|
|
}
|
|
|
|
void SingleSpeciesTP::getStandardVolumes(doublereal* vbar) const
|
|
{
|
|
vbar[0] = molecularWeight(0) / density();
|
|
}
|
|
|
|
// ---- Thermodynamic Values for the Species Reference States -------
|
|
|
|
void SingleSpeciesTP::getEnthalpy_RT_ref(doublereal* hrt) const
|
|
{
|
|
_updateThermo();
|
|
hrt[0] = m_h0_RT[0];
|
|
}
|
|
|
|
void SingleSpeciesTP::getGibbs_RT_ref(doublereal* grt) const
|
|
{
|
|
_updateThermo();
|
|
grt[0] = m_h0_RT[0] - m_s0_R[0];
|
|
}
|
|
|
|
void SingleSpeciesTP::getGibbs_ref(doublereal* g) const
|
|
{
|
|
getGibbs_RT_ref(g);
|
|
g[0] *= RT();
|
|
}
|
|
|
|
void SingleSpeciesTP::getEntropy_R_ref(doublereal* er) const
|
|
{
|
|
_updateThermo();
|
|
er[0] = m_s0_R[0];
|
|
}
|
|
|
|
void SingleSpeciesTP::getCp_R_ref(doublereal* cpr) const
|
|
{
|
|
_updateThermo();
|
|
cpr[0] = m_cp0_R[0];
|
|
}
|
|
|
|
// ------------------ Setting the State ------------------------
|
|
|
|
void SingleSpeciesTP::setState_HP(doublereal h, doublereal p,
|
|
doublereal tol)
|
|
{
|
|
doublereal dt;
|
|
setPressure(p);
|
|
for (int n = 0; n < 50; n++) {
|
|
dt = clip((h - enthalpy_mass())/cp_mass(), -100.0, 100.0);
|
|
setState_TP(temperature() + dt, p);
|
|
if (fabs(dt) < tol) {
|
|
return;
|
|
}
|
|
}
|
|
throw CanteraError("setState_HP","no convergence. dt = {}", dt);
|
|
}
|
|
|
|
void SingleSpeciesTP::setState_UV(doublereal u, doublereal v,
|
|
doublereal tol)
|
|
{
|
|
doublereal dt;
|
|
if (v == 0.0) {
|
|
setDensity(1.0E100);
|
|
} else {
|
|
setDensity(1.0/v);
|
|
}
|
|
for (int n = 0; n < 50; n++) {
|
|
dt = clip((u - intEnergy_mass())/cv_mass(), -100.0, 100.0);
|
|
setTemperature(temperature() + dt);
|
|
if (fabs(dt) < tol) {
|
|
return;
|
|
}
|
|
}
|
|
throw CanteraError("setState_UV", "no convergence. dt = {}\n"
|
|
"u = {} v = {}\n", dt, u, v);
|
|
}
|
|
|
|
void SingleSpeciesTP::setState_SP(doublereal s, doublereal p,
|
|
doublereal tol)
|
|
{
|
|
doublereal dt;
|
|
setPressure(p);
|
|
for (int n = 0; n < 50; n++) {
|
|
dt = clip((s - entropy_mass())*temperature()/cp_mass(), -100.0, 100.0);
|
|
setState_TP(temperature() + dt, p);
|
|
if (fabs(dt) < tol) {
|
|
return;
|
|
}
|
|
}
|
|
throw CanteraError("setState_SP","no convergence. dt = {}", dt);
|
|
}
|
|
|
|
void SingleSpeciesTP::setState_SV(doublereal s, doublereal v,
|
|
doublereal tol)
|
|
{
|
|
doublereal dt;
|
|
if (v == 0.0) {
|
|
setDensity(1.0E100);
|
|
} else {
|
|
setDensity(1.0/v);
|
|
}
|
|
for (int n = 0; n < 50; n++) {
|
|
dt = clip((s - entropy_mass())*temperature()/cv_mass(), -100.0, 100.0);
|
|
setTemperature(temperature() + dt);
|
|
if (fabs(dt) < tol) {
|
|
return;
|
|
}
|
|
}
|
|
throw CanteraError("setState_SV","no convergence. dt = {}", dt);
|
|
}
|
|
|
|
bool SingleSpeciesTP::addSpecies(shared_ptr<Species> spec)
|
|
{
|
|
if (m_kk != 0) {
|
|
throw CanteraError("SingleSpeciesTP::addSpecies",
|
|
"Stoichiometric substances may only contain one species.");
|
|
}
|
|
bool added = ThermoPhase::addSpecies(spec);
|
|
if (added) {
|
|
double x = 1.0;
|
|
ThermoPhase::setMoleFractions(&x);
|
|
}
|
|
return added;
|
|
}
|
|
|
|
void SingleSpeciesTP::_updateThermo() const
|
|
{
|
|
doublereal tnow = temperature();
|
|
if (m_tlast != tnow) {
|
|
m_spthermo->update(tnow, m_cp0_R.data(), m_h0_RT.data(), m_s0_R.data());
|
|
m_tlast = tnow;
|
|
}
|
|
}
|
|
|
|
}
|