1392 lines
49 KiB
C++
1392 lines
49 KiB
C++
/**
|
|
* @file MixtureFugacityTP.cpp
|
|
* Methods file for a derived class of ThermoPhase that handles
|
|
* non-ideal mixtures based on the fugacity models (see \ref thermoprops and
|
|
* class \link Cantera::MixtureFugacityTP MixtureFugacityTP\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/MixtureFugacityTP.h"
|
|
#include "cantera/thermo/VPSSMgr.h"
|
|
#include "cantera/thermo/PDSS.h"
|
|
#include "cantera/base/stringUtils.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace Cantera
|
|
{
|
|
//====================================================================================================================
|
|
/*
|
|
* Default constructor
|
|
*/
|
|
MixtureFugacityTP::MixtureFugacityTP() :
|
|
ThermoPhase(),
|
|
m_Pcurrent(-1.0),
|
|
moleFractions_(0),
|
|
iState_(FLUID_GAS),
|
|
forcedState_(FLUID_UNDEFINED),
|
|
m_Tlast_ref(-1.0),
|
|
m_logc0(0.0),
|
|
m_h0_RT(0),
|
|
m_cp0_R(0),
|
|
m_g0_RT(0),
|
|
m_s0_R(0)
|
|
{
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Copy Constructor:
|
|
*
|
|
* Note this stuff will not work until the underlying phase
|
|
* has a working copy constructor.
|
|
*
|
|
* The copy constructor just calls the assignment operator
|
|
* to do the heavy lifting.
|
|
*/
|
|
MixtureFugacityTP::MixtureFugacityTP(const MixtureFugacityTP& b) :
|
|
ThermoPhase(),
|
|
m_Pcurrent(-1.0),
|
|
moleFractions_(0),
|
|
iState_(FLUID_GAS),
|
|
forcedState_(FLUID_UNDEFINED),
|
|
m_Tlast_ref(-1.0),
|
|
m_logc0(0.0),
|
|
m_h0_RT(0),
|
|
m_cp0_R(0),
|
|
m_g0_RT(0),
|
|
m_s0_R(0)
|
|
{
|
|
MixtureFugacityTP::operator=(b);
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* operator=()
|
|
*
|
|
* Note this stuff will not work until the underlying phase
|
|
* has a working assignment operator
|
|
*/
|
|
MixtureFugacityTP&
|
|
MixtureFugacityTP::operator=(const MixtureFugacityTP& b)
|
|
{
|
|
if (&b != this) {
|
|
/*
|
|
* Mostly, this is a passthrough to the underlying
|
|
* assignment operator for the ThermoPhase parent object.
|
|
*/
|
|
ThermoPhase::operator=(b);
|
|
/*
|
|
* However, we have to handle data that we own.
|
|
*/
|
|
m_Pcurrent = b.m_Pcurrent;
|
|
moleFractions_ = b.moleFractions_;
|
|
iState_ = b.iState_;
|
|
forcedState_ = b.forcedState_;
|
|
m_Tlast_ref = b.m_Tlast_ref;
|
|
m_logc0 = b.m_logc0;
|
|
m_h0_RT = b.m_h0_RT;
|
|
m_cp0_R = b.m_cp0_R;
|
|
m_g0_RT = b.m_g0_RT;
|
|
m_s0_R = b.m_s0_R;
|
|
/*
|
|
* The VPSSMgr object contains shallow pointers. Whenever you have shallow
|
|
* pointers, they have to be fixed up to point to the correct objects refering
|
|
* back to this ThermoPhase's properties.
|
|
*/
|
|
//m_VPSS_ptr->initAllPtrs(this, m_spthermo);
|
|
/*
|
|
* The PDSS objects contains shallow pointers. Whenever you have shallow
|
|
* pointers, they have to be fixed up to point to the correct objects refering
|
|
* back to this ThermoPhase's properties. This function also sets m_VPSS_ptr
|
|
* so it occurs after m_VPSS_ptr is set.
|
|
*/
|
|
|
|
/*
|
|
* Ok, the VPSSMgr object is ready for business.
|
|
* We need to resync the temperature and the pressure of the new standard states
|
|
* with what is stored in this object.
|
|
*/
|
|
// m_VPSS_ptr->setState_TP(m_Tlast_ss, m_Plast_ss);
|
|
}
|
|
return *this;
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* ~MixtureFugacityTP(): (virtual)
|
|
*
|
|
*/
|
|
MixtureFugacityTP::~MixtureFugacityTP()
|
|
{
|
|
|
|
}
|
|
|
|
/*
|
|
* Duplication function.
|
|
* This calls the copy constructor for this object.
|
|
*/
|
|
ThermoPhase* MixtureFugacityTP::duplMyselfAsThermoPhase() const
|
|
{
|
|
MixtureFugacityTP* vptp = new MixtureFugacityTP(*this);
|
|
return (ThermoPhase*) vptp;
|
|
}
|
|
//====================================================================================================================
|
|
// This method returns the convention used in specification
|
|
// of the standard state, of which there are currently two,
|
|
// temperature based, and variable pressure based.
|
|
/*
|
|
* Currently, there are two standard state conventions:
|
|
* - Temperature-based activities
|
|
* cSS_CONVENTION_TEMPERATURE 0
|
|
* - default
|
|
*
|
|
* - Variable Pressure and Temperature -based activities
|
|
* cSS_CONVENTION_VPSS 1
|
|
*/
|
|
int MixtureFugacityTP::standardStateConvention() const
|
|
{
|
|
return cSS_CONVENTION_TEMPERATURE;
|
|
}
|
|
//====================================================================================================================
|
|
// Set the solution branch to force the ThermoPhase to exist on one branch or another
|
|
/*
|
|
* @param solnBranch Branch that the solution is restricted to.
|
|
* the value -1 means gas. The value -2 means unrestricted.
|
|
* Values of zero or greater refer to species dominated condensed phases.
|
|
*/
|
|
void MixtureFugacityTP::setForcedSolutionBranch(int solnBranch)
|
|
{
|
|
forcedState_ = solnBranch;
|
|
}
|
|
//====================================================================================================================
|
|
// Report the solution branch which the solution is restricted to
|
|
/*
|
|
* @return Branch that the solution is restricted to.
|
|
* the value -1 means gas. The value -2 means unrestricted.
|
|
* Values of zero or greater refer to species dominated condensed phases.
|
|
*/
|
|
int MixtureFugacityTP::forcedSolutionBranch() const
|
|
{
|
|
return forcedState_;
|
|
}
|
|
//====================================================================================================================
|
|
// Report the solution branch which the solution is actually on
|
|
/*
|
|
* @return Branch that the solution is restricted to.
|
|
* the value -1 means gas. The value -2 means superfluid..
|
|
* Values of zero or greater refer to species dominated condensed phases.
|
|
*/
|
|
int MixtureFugacityTP::reportSolnBranchActual() const
|
|
{
|
|
return iState_;
|
|
}
|
|
//====================================================================================================================
|
|
|
|
/*
|
|
* ------------Molar Thermodynamic Properties -------------------------
|
|
*/
|
|
//====================================================================================================================
|
|
|
|
doublereal MixtureFugacityTP::err(std::string msg) const
|
|
{
|
|
throw CanteraError("MixtureFugacityTP","Base class method "
|
|
+msg+" called. Equation of state type: "+int2str(eosType()));
|
|
return 0;
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* ---- Partial Molar Properties of the Solution -----------------
|
|
*/
|
|
//====================================================================================================================
|
|
/*
|
|
* Get the array of non-dimensional species chemical potentials
|
|
* These are partial molar Gibbs free energies.
|
|
* \f$ \mu_k / \hat R T \f$.
|
|
* Units: unitless
|
|
*
|
|
* We close the loop on this function, here, calling
|
|
* getChemPotentials() and then dividing by RT.
|
|
*/
|
|
void MixtureFugacityTP::getChemPotentials_RT(doublereal* muRT) const
|
|
{
|
|
getChemPotentials(muRT);
|
|
doublereal invRT = 1.0 / _RT();
|
|
for (size_t k = 0; k < m_kk; k++) {
|
|
muRT[k] *= invRT;
|
|
}
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* ----- Thermodynamic Values for the Species Standard States States ----
|
|
*/
|
|
void MixtureFugacityTP::getStandardChemPotentials(doublereal* g) const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
copy(m_g0_RT.begin(), m_g0_RT.end(), g);
|
|
doublereal RT = _RT();
|
|
double tmp = log(pressure() /m_spthermo->refPressure());
|
|
for (size_t k = 0; k < m_kk; k++) {
|
|
g[k] = RT * (g[k] + tmp);
|
|
}
|
|
}
|
|
//====================================================================================================================
|
|
void MixtureFugacityTP::getEnthalpy_RT(doublereal* hrt) const
|
|
{
|
|
getEnthalpy_RT_ref(hrt);
|
|
}
|
|
//================================================================================================
|
|
#ifdef H298MODIFY_CAPABILITY
|
|
// Modify the value of the 298 K Heat of Formation of one species in the phase (J kmol-1)
|
|
/*
|
|
* The 298K heat of formation is defined as the enthalpy change to create the standard state
|
|
* of the species from its constituent elements in their standard states at 298 K and 1 bar.
|
|
*
|
|
* @param k Species k
|
|
* @param Hf298New Specify the new value of the Heat of Formation at 298K and 1 bar
|
|
*/
|
|
void MixtureFugacityTP::modifyOneHf298SS(const int k, const doublereal Hf298New)
|
|
{
|
|
m_spthermo->modifyOneHf298(k, Hf298New);
|
|
m_Tlast_ref += 0.0001234;
|
|
}
|
|
#endif
|
|
//====================================================================================================================
|
|
/*
|
|
* Get the array of nondimensional entropy functions for the
|
|
* standard state species
|
|
* at the current <I>T</I> and <I>P</I> of the solution.
|
|
*/
|
|
void MixtureFugacityTP::getEntropy_R(doublereal* sr) const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
copy(m_s0_R.begin(), m_s0_R.end(), sr);
|
|
double tmp = log(pressure() /m_spthermo->refPressure());
|
|
for (size_t k = 0; k < m_kk; k++) {
|
|
sr[k] -= tmp;
|
|
}
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Get the nondimensional gibbs function for the species
|
|
* standard states at the current T and P of the solution.
|
|
*/
|
|
void MixtureFugacityTP::getGibbs_RT(doublereal* grt) const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
copy(m_g0_RT.begin(), m_g0_RT.end(), grt);
|
|
double tmp = log(pressure() /m_spthermo->refPressure());
|
|
for (size_t k = 0; k < m_kk; k++) {
|
|
grt[k] += tmp;
|
|
}
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* get the pure Gibbs free energies of each species assuming
|
|
* it is in its standard state. This is the same as
|
|
* getStandardChemPotentials().
|
|
*/
|
|
void MixtureFugacityTP::getPureGibbs(doublereal* g) const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
scale(m_g0_RT.begin(), m_g0_RT.end(), g, _RT());
|
|
double tmp = log(pressure() /m_spthermo->refPressure());
|
|
tmp *= _RT();
|
|
for (size_t k = 0; k < m_kk; k++) {
|
|
g[k] += tmp;
|
|
}
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Returns the vector of nondimensional
|
|
* internal Energies of the standard state at the current temperature
|
|
* and pressure of the solution for each species.
|
|
*/
|
|
void MixtureFugacityTP::getIntEnergy_RT(doublereal* urt) const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
copy(m_h0_RT.begin(), m_h0_RT.end(), urt);
|
|
doublereal p = pressure();
|
|
doublereal tmp = p / _RT();
|
|
doublereal v0 = _RT() / p;
|
|
for (size_t i = 0; i < m_kk; i++) {
|
|
urt[i] -= tmp * v0;
|
|
}
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Get the nondimensional heat capacity at constant pressure
|
|
* function for the species
|
|
* standard states at the current T and P of the solution.
|
|
*/
|
|
void MixtureFugacityTP::getCp_R(doublereal* cpr) const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
copy(m_cp0_R.begin(), m_cp0_R.end(), cpr);
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Get the molar volumes of the species standard states at the current
|
|
* <I>T</I> and <I>P</I> of the solution.
|
|
* units = m^3 / kmol
|
|
*
|
|
* @param vol Output vector containing the standard state volumes.
|
|
* Length: m_kk.
|
|
*/
|
|
void MixtureFugacityTP::getStandardVolumes(doublereal* vol) const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
doublereal v0 = _RT() / pressure();
|
|
for (size_t i = 0; i < m_kk; i++) {
|
|
vol[i]= v0;
|
|
}
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* ----- Thermodynamic Values for the Species Reference States ----
|
|
*/
|
|
|
|
/*
|
|
* Returns the vector of nondimensional enthalpies of the
|
|
* reference state at the current temperature of the solution and
|
|
* the reference pressure for the species.
|
|
*/
|
|
void MixtureFugacityTP::getEnthalpy_RT_ref(doublereal* hrt) const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
copy(m_h0_RT.begin(), m_h0_RT.end(), hrt);
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Returns the vector of nondimensional
|
|
* enthalpies of the reference state at the current temperature
|
|
* of the solution and the reference pressure for the species.
|
|
*/
|
|
void MixtureFugacityTP::getGibbs_RT_ref(doublereal* grt) const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
copy(m_g0_RT.begin(), m_g0_RT.end(), grt);
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Returns the vector of the
|
|
* gibbs function of the reference state at the current temperature
|
|
* of the solution and the reference pressure for the species.
|
|
* units = J/kmol
|
|
*
|
|
* This is filled in here so that derived classes don't have to
|
|
* take care of it.
|
|
*/
|
|
void MixtureFugacityTP::getGibbs_ref(doublereal* g) const
|
|
{
|
|
const vector_fp& gibbsrt = gibbs_RT_ref();
|
|
scale(gibbsrt.begin(), gibbsrt.end(), g, _RT());
|
|
}
|
|
//====================================================================================================================
|
|
const vector_fp& MixtureFugacityTP::gibbs_RT_ref() const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
return m_g0_RT;
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Returns the vector of nondimensional
|
|
* entropies of the reference state at the current temperature
|
|
* of the solution and the reference pressure for the species.
|
|
*/
|
|
void MixtureFugacityTP::getEntropy_R_ref(doublereal* er) const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
copy(m_s0_R.begin(), m_s0_R.end(), er);
|
|
return;
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Returns the vector of nondimensional
|
|
* constant pressure heat capacities of the reference state
|
|
* at the current temperature of the solution
|
|
* and reference pressure for the species.
|
|
*/
|
|
void MixtureFugacityTP::getCp_R_ref(doublereal* cpr) const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
copy(m_cp0_R.begin(), m_cp0_R.end(), cpr);
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Get the molar volumes of the species reference states at the current
|
|
* <I>T</I> and reference pressure of the solution.
|
|
*
|
|
* units = m^3 / kmol
|
|
*/
|
|
void MixtureFugacityTP::getStandardVolumes_ref(doublereal* vol) const
|
|
{
|
|
_updateReferenceStateThermo();
|
|
double pp = refPressure();
|
|
doublereal v0 = _RT() / pp;
|
|
for (size_t i = 0; i < m_kk; i++) {
|
|
vol[i]= v0;
|
|
}
|
|
}
|
|
//====================================================================================================================
|
|
// Set the initial state of the phase to the conditions specified in the state XML element.
|
|
/*
|
|
*
|
|
* This method sets the temperature, pressure, and mole fraction vector to a set default value.
|
|
* We modify the default behavior here so that TP is evaluated at the same time.
|
|
*
|
|
* @param state AN XML_Node object corresponding to
|
|
* the "state" entry for this phase in the
|
|
* input file.
|
|
*/
|
|
void MixtureFugacityTP::setStateFromXML(const XML_Node& state)
|
|
{
|
|
int doTP = 0;
|
|
string comp = ctml::getChildValue(state,"moleFractions");
|
|
if (comp != "") {
|
|
// not overloaded in current object -> phase state is not calculated.
|
|
setMoleFractionsByName(comp);
|
|
doTP = 1;
|
|
} else {
|
|
comp = ctml::getChildValue(state,"massFractions");
|
|
if (comp != "") {
|
|
// not overloaded in current object -> phase state is not calculated.
|
|
setMassFractionsByName(comp);
|
|
doTP = 1;
|
|
}
|
|
}
|
|
double t = temperature();
|
|
if (state.hasChild("temperature")) {
|
|
t = ctml::getFloat(state, "temperature", "temperature");
|
|
doTP = 1;
|
|
}
|
|
if (state.hasChild("pressure")) {
|
|
double p = ctml::getFloat(state, "pressure", "pressure");
|
|
setState_TP(t, p);
|
|
} else if (state.hasChild("density")) {
|
|
double rho = ctml::getFloat(state, "density", "density");
|
|
setState_TR(t, rho);
|
|
} else if (doTP) {
|
|
double rho = Phase::density();
|
|
setState_TR(t, rho);
|
|
}
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Perform initializations after all species have been
|
|
* added.
|
|
*/
|
|
void MixtureFugacityTP::initThermo()
|
|
{
|
|
initLengths();
|
|
ThermoPhase::initThermo();
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Initialize the internal lengths.
|
|
* (this is not a virtual function)
|
|
*/
|
|
void MixtureFugacityTP::initLengths()
|
|
{
|
|
m_kk = nSpecies();
|
|
moleFractions_.resize(m_kk, 0.0);
|
|
moleFractions_[0] = 1.0;
|
|
m_h0_RT.resize(m_kk, 0.0);
|
|
m_cp0_R.resize(m_kk, 0.0);
|
|
m_g0_RT.resize(m_kk, 0.0);
|
|
m_s0_R.resize(m_kk, 0.0);
|
|
}
|
|
//====================================================================================================================
|
|
void MixtureFugacityTP::setTemperature(const doublereal temp)
|
|
{
|
|
_updateReferenceStateThermo();
|
|
setState_TR(temperature(), density());
|
|
}
|
|
//====================================================================================================================
|
|
void MixtureFugacityTP::setPressure(doublereal p)
|
|
{
|
|
setState_TP(temperature(), p);
|
|
// double chemPot[5], mf[5];
|
|
// getMoleFractions(mf);
|
|
// getChemPotentials(chemPot);
|
|
// for (int i = 0; i < m_kk; i++) {
|
|
// printf(" MixFug:setPres: mu(%d = %g) = %18.8g\n", i, mf[i], chemPot[i]);
|
|
// }
|
|
}
|
|
//====================================================================================================================
|
|
void MixtureFugacityTP::setMassFractions(const doublereal* const y)
|
|
{
|
|
Phase::setMassFractions(y);
|
|
getMoleFractions(DATA_PTR(moleFractions_));
|
|
}
|
|
//====================================================================================================================
|
|
void MixtureFugacityTP::setMassFractions_NoNorm(const doublereal* const y)
|
|
{
|
|
Phase::setMassFractions_NoNorm(y);
|
|
getMoleFractions(DATA_PTR(moleFractions_));
|
|
}
|
|
//====================================================================================================================
|
|
void MixtureFugacityTP::setMoleFractions(const doublereal* const x)
|
|
{
|
|
Phase::setMoleFractions(x);
|
|
getMoleFractions(DATA_PTR(moleFractions_));
|
|
}
|
|
//====================================================================================================================
|
|
void MixtureFugacityTP::setMoleFractions_NoNorm(const doublereal* const x)
|
|
{
|
|
Phase::setMoleFractions_NoNorm(x);
|
|
getMoleFractions(DATA_PTR(moleFractions_));
|
|
}
|
|
//====================================================================================================================
|
|
void MixtureFugacityTP::setConcentrations(const doublereal* const c)
|
|
{
|
|
Phase::setConcentrations(c);
|
|
getMoleFractions(DATA_PTR(moleFractions_));
|
|
}
|
|
//====================================================================================================================
|
|
void MixtureFugacityTP::setMoleFractions_NoState(const doublereal* const x)
|
|
{
|
|
Phase::setMoleFractions(x);
|
|
getMoleFractions(DATA_PTR(moleFractions_));
|
|
updateMixingExpressions();
|
|
}
|
|
//====================================================================================================================
|
|
void MixtureFugacityTP::calcDensity()
|
|
{
|
|
err("MixtureFugacityTP::calcDensity() called, but EOS for phase is not known");
|
|
}
|
|
//====================================================================================================================
|
|
|
|
void MixtureFugacityTP::setState_TP(doublereal t, doublereal pres)
|
|
{
|
|
/*
|
|
* A pretty tricky algorithm is needed here, due to problems involving
|
|
* standard states of real fluids. For those cases you need
|
|
* to combine the T and P specification for the standard state, or else
|
|
* you may venture into the forbidden zone, especially when nearing the
|
|
* triple point.
|
|
* Therefore, we need to do the standard state thermo calc with the
|
|
* (t, pres) combo.
|
|
*/
|
|
getMoleFractions(DATA_PTR(moleFractions_));
|
|
|
|
|
|
Phase::setTemperature(t);
|
|
_updateReferenceStateThermo();
|
|
// Depends on the mole fractions and the temperature
|
|
updateMixingExpressions();
|
|
// setPressure(pres);
|
|
m_Pcurrent = pres;
|
|
// double mmw = meanMolecularWeight();
|
|
|
|
if (forcedState_ == FLUID_UNDEFINED) {
|
|
double rhoNow = Phase::density();
|
|
double rho = densityCalc(t, pres, iState_, rhoNow);
|
|
if (rho > 0.0) {
|
|
Phase::setDensity(rho);
|
|
m_Pcurrent = pres;
|
|
iState_ = phaseState(true);
|
|
} else {
|
|
if (rho < -1.5) {
|
|
rho = densityCalc(t, pres, FLUID_UNDEFINED , rhoNow);
|
|
if (rho > 0.0) {
|
|
Phase::setDensity(rho);
|
|
m_Pcurrent = pres;
|
|
iState_ = phaseState(true);
|
|
} else {
|
|
throw CanteraError("MixtureFugacityTP::setState_TP()", "neg rho");
|
|
}
|
|
} else {
|
|
throw CanteraError("MixtureFugacityTP::setState_TP()", "neg rho");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
} else if (forcedState_ == FLUID_GAS) {
|
|
// Normal density calculation
|
|
if (iState_ < FLUID_LIQUID_0) {
|
|
double rhoNow = Phase::density();
|
|
double rho = densityCalc(t, pres, iState_, rhoNow);
|
|
if (rho > 0.0) {
|
|
Phase::setDensity(rho);
|
|
m_Pcurrent = pres;
|
|
iState_ = phaseState(true);
|
|
if (iState_ >= FLUID_LIQUID_0) {
|
|
throw CanteraError("MixtureFugacityTP::setState_TP()", "wrong state");
|
|
}
|
|
} else {
|
|
throw CanteraError("MixtureFugacityTP::setState_TP()", "neg rho");
|
|
}
|
|
|
|
}
|
|
|
|
|
|
} else if (forcedState_ > FLUID_LIQUID_0) {
|
|
if (iState_ >= FLUID_LIQUID_0) {
|
|
double rhoNow = Phase::density();
|
|
double rho = densityCalc(t, pres, iState_, rhoNow);
|
|
if (rho > 0.0) {
|
|
Phase::setDensity(rho);
|
|
m_Pcurrent = pres;
|
|
iState_ = phaseState(true);
|
|
if (iState_ == FLUID_GAS) {
|
|
throw CanteraError("MixtureFugacityTP::setState_TP()", "wrong state");
|
|
}
|
|
} else {
|
|
throw CanteraError("MixtureFugacityTP::setState_TP()", "neg rho");
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//setTemperature(t);
|
|
//setPressure(pres);
|
|
//calcDensity();
|
|
}
|
|
//====================================================================================================================
|
|
// Set the internally stored temperature (K) and density (kg/m^3)
|
|
/*
|
|
* This overrides the default behavior. In addition to just storing the state in the object, we need to do
|
|
* an equation of state calculation and figure out what phase state we are in.
|
|
*
|
|
* @param t Temperature in kelvin
|
|
* @param rho Density (kg/m^3)
|
|
*/
|
|
void MixtureFugacityTP::setState_TR(doublereal T, doublereal rho)
|
|
{
|
|
getMoleFractions(DATA_PTR(moleFractions_));
|
|
Phase::setTemperature(T);
|
|
_updateReferenceStateThermo();
|
|
Phase::setDensity(rho);
|
|
doublereal mv = molarVolume();
|
|
// depends on mole fraction and temperature
|
|
updateMixingExpressions();
|
|
|
|
m_Pcurrent = pressureCalc(T, mv);
|
|
iState_ = phaseState(true);
|
|
|
|
// printf("setState_TR: state at T = %g, rho = %g, mv = %g, P = %20.13g, iState = %d\n", T, rho, mv, m_Pcurrent, iState_);
|
|
}
|
|
|
|
//====================================================================================================================
|
|
// Set the temperature (K), pressure (Pa), and mole fractions.
|
|
/*
|
|
* Note, the mole fractions are set first before the pressure is set.
|
|
* Setting the pressure may involve the solution of a nonlinear equation.
|
|
*
|
|
* @param t Temperature (K)
|
|
* @param p Pressure (Pa)
|
|
* @param x Vector of mole fractions.
|
|
* Length is equal to m_kk.
|
|
*/
|
|
void MixtureFugacityTP::setState_TPX(doublereal t, doublereal p, const doublereal* x)
|
|
{
|
|
setMoleFractions_NoState(x);
|
|
setState_TP(t,p);
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Import and initialize a ThermoPhase object
|
|
*
|
|
* param phaseNode This object must be the phase node of a
|
|
* complete XML tree
|
|
* description of the phase, including all of the
|
|
* species data. In other words while "phase" must
|
|
* point to an XML phase object, it must have
|
|
* sibling nodes "speciesData" that describe
|
|
* the species in the phase.
|
|
* param id ID of the phase. If nonnull, a check is done
|
|
* to see if phaseNode is pointing to the phase
|
|
* with the correct id.
|
|
*
|
|
* This routine initializes the lengths in the current object and
|
|
* then calls the parent routine.
|
|
*/
|
|
void MixtureFugacityTP::initThermoXML(XML_Node& phaseNode, std::string id)
|
|
{
|
|
MixtureFugacityTP::initLengths();
|
|
|
|
//m_VPSS_ptr->initThermo();
|
|
|
|
// m_VPSS_ptr->initThermoXML(phaseNode, id);
|
|
ThermoPhase::initThermoXML(phaseNode, id);
|
|
}
|
|
//====================================================================================================================
|
|
doublereal MixtureFugacityTP::z() const
|
|
{
|
|
doublereal p = pressure();
|
|
doublereal rho = density();
|
|
doublereal mmw = meanMolecularWeight();
|
|
doublereal molarV = mmw / rho;
|
|
doublereal rt = _RT();
|
|
doublereal zz = p * molarV / rt;
|
|
return zz;
|
|
}
|
|
//====================================================================================================================
|
|
doublereal MixtureFugacityTP::sresid() const
|
|
{
|
|
throw CanteraError("MixtureFugacityTP::sresid()", "Base Class: not implemented");
|
|
return 0.0;
|
|
}
|
|
//====================================================================================================================
|
|
doublereal MixtureFugacityTP::hresid() const
|
|
{
|
|
throw CanteraError("MixtureFugacityTP::hresid()", "Base Class: not implemented");
|
|
return 0.0;
|
|
}
|
|
//====================================================================================================================
|
|
doublereal MixtureFugacityTP::psatEst(doublereal TKelvin) const
|
|
{
|
|
doublereal tcrit = critTemperature();
|
|
doublereal pcrit = critPressure();
|
|
doublereal tt = tcrit/TKelvin;
|
|
if (tt < 1.0) {
|
|
return pcrit;
|
|
}
|
|
doublereal lpr = -0.8734*tt*tt - 3.4522*tt + 4.2918;
|
|
return pcrit*exp(lpr);
|
|
}
|
|
//====================================================================================================================
|
|
doublereal MixtureFugacityTP::liquidVolEst(doublereal TKelvin, doublereal& pres) const
|
|
{
|
|
throw CanteraError("MixtureFugacityTP::liquidVolEst()", "unimplemented");
|
|
return 0.0;
|
|
}
|
|
//====================================================================================================================
|
|
/*
|
|
* Calculates the density given the temperature and the pressure,
|
|
* and a guess at the density. Note, below T_c, this is a
|
|
* multivalued function. This function assumes that the phase is on one side of the vapor dome
|
|
* or the other. It does not allow for crosses of the vapor dome.
|
|
*
|
|
* parameters:
|
|
* temperature: Kelvin
|
|
* pressure : Pressure in Pascals (Newton/m**2)
|
|
* phase : guessed phase of water
|
|
* : -1: no guessed phase
|
|
* rhoguess : guessed density of the water
|
|
*
|
|
* -1.0 no guessed density
|
|
*
|
|
* If a problem is encountered, a negative 1 is returned.
|
|
*
|
|
* @TODO make this a const function
|
|
*/
|
|
doublereal MixtureFugacityTP::densityCalc(doublereal TKelvin, doublereal presPa,
|
|
int phase, doublereal rhoguess)
|
|
{
|
|
double tcrit = critTemperature();
|
|
doublereal mmw = meanMolecularWeight();
|
|
// double pcrit = critPressure();
|
|
// doublereal deltaGuess = 0.0;
|
|
if (rhoguess == -1.0) {
|
|
if (phase != -1) {
|
|
if (TKelvin > tcrit) {
|
|
rhoguess = presPa * mmw / (GasConstant * TKelvin);
|
|
} else {
|
|
if (phase == FLUID_GAS || phase == FLUID_SUPERCRIT) {
|
|
rhoguess = presPa * mmw / (GasConstant * TKelvin);
|
|
} else if (phase >= FLUID_LIQUID_0) {
|
|
double lqvol = liquidVolEst(TKelvin, presPa);
|
|
rhoguess = mmw / lqvol;
|
|
}
|
|
}
|
|
} else {
|
|
/*
|
|
* Assume the Gas phase initial guess, if nothing is
|
|
* specified to the routine
|
|
*/
|
|
rhoguess = presPa * mmw / (GasConstant * TKelvin);
|
|
}
|
|
|
|
}
|
|
|
|
double molarVolBase = mmw / rhoguess;
|
|
double molarVolLast = molarVolBase;
|
|
double vc = mmw / critDensity();
|
|
/*
|
|
* molar volume of the spinodal at the current temperature and mole fractions. this will
|
|
* be updated as we go.
|
|
*/
|
|
double molarVolSpinodal = vc;
|
|
doublereal pcheck = 1.0E-30 + 1.0E-8 * presPa;
|
|
doublereal presBase, dpdVBase, delMV;
|
|
bool conv = false;
|
|
/*
|
|
* We start on one side of the vc and stick with that side
|
|
*/
|
|
bool gasSide = molarVolBase > vc;
|
|
if (gasSide) {
|
|
molarVolLast = (GasConstant * TKelvin)/presPa;
|
|
} else {
|
|
molarVolLast = liquidVolEst(TKelvin, presPa);
|
|
}
|
|
|
|
/*
|
|
* OK, now we do a small solve to calculate the molar volume given the T,P value.
|
|
* The algorithm is taken from dfind()
|
|
*/
|
|
for (int n = 0; n < 200; n++) {
|
|
|
|
/*
|
|
* Calculate the predicted reduced pressure, pred0, based on the
|
|
* current tau and dd.
|
|
*/
|
|
|
|
/*
|
|
* Calculate the derivative of the predicted pressure
|
|
* wrt the molar volume.
|
|
* This routine also returns the pressure, presBase
|
|
*/
|
|
dpdVBase = dpdVCalc(TKelvin, molarVolBase, presBase);
|
|
|
|
/*
|
|
* If dpdV is positve, then we are in the middle of the
|
|
* 2 phase region and beyond the spinodal stability curve. We need to adjust
|
|
* the initial guess outwards and start a new iteration.
|
|
*/
|
|
if (dpdVBase >= 0.0) {
|
|
if (TKelvin > tcrit) {
|
|
throw CanteraError("", "confused");
|
|
}
|
|
/*
|
|
* TODO Spawn a calculation for the value of the spinodal point that is
|
|
* very accurate. Answer the question as to wethera solution is
|
|
* possible on the current side of the vapor dome.
|
|
*/
|
|
if (gasSide) {
|
|
if (molarVolBase >= vc) {
|
|
molarVolSpinodal = molarVolBase;
|
|
molarVolBase = 0.5 * (molarVolLast + molarVolSpinodal);
|
|
} else {
|
|
molarVolBase = 0.5 * (molarVolLast + molarVolSpinodal);
|
|
}
|
|
} else {
|
|
if (molarVolBase <= vc) {
|
|
molarVolSpinodal = molarVolBase;
|
|
molarVolBase = 0.5 * (molarVolLast + molarVolSpinodal);
|
|
} else {
|
|
molarVolBase = 0.5 * (molarVolLast + molarVolSpinodal);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Check for convergence
|
|
*/
|
|
if (fabs(presBase-presPa) < pcheck) {
|
|
conv = true;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Dampen and crop the update
|
|
*/
|
|
doublereal dpdV = dpdVBase;
|
|
if (n < 10) {
|
|
dpdV = dpdVBase * 1.5;
|
|
}
|
|
// if (dpdV > -0.001) dpdV = -0.001;
|
|
|
|
/*
|
|
* Formulate the update to the molar volume by
|
|
* Newton's method. Then, crop it to a max value
|
|
* of 0.1 times the current volume
|
|
*/
|
|
delMV = - (presBase - presPa) / dpdV;
|
|
if (!gasSide || delMV < 0.0) {
|
|
if (fabs(delMV) > 0.2 * molarVolBase) {
|
|
delMV = delMV / fabs(delMV) * 0.2 * molarVolBase;
|
|
}
|
|
}
|
|
/*
|
|
* Only go 1/10 the way towards the spinodal at any one time.
|
|
*/
|
|
if (TKelvin < tcrit) {
|
|
if (gasSide) {
|
|
if (delMV < 0.0) {
|
|
if (-delMV > 0.5 * (molarVolBase - molarVolSpinodal)) {
|
|
delMV = - 0.5 * (molarVolBase - molarVolSpinodal);
|
|
}
|
|
}
|
|
} else {
|
|
if (delMV > 0.0) {
|
|
if (delMV > 0.5 * (molarVolSpinodal - molarVolBase)) {
|
|
delMV = 0.5 * (molarVolSpinodal - molarVolBase);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* updated the molar volume value
|
|
*/
|
|
molarVolLast = molarVolBase;
|
|
molarVolBase += delMV;
|
|
|
|
|
|
if (fabs(delMV/molarVolBase) < 1.0E-14) {
|
|
conv = true;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Check for negative molar volumes
|
|
*/
|
|
if (molarVolBase <= 0.0) {
|
|
molarVolBase = std::min(1.0E-30, fabs(delMV*1.0E-4));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Check for convergence, and return 0.0 if it wasn't achieved.
|
|
*/
|
|
double densBase = 0.0;
|
|
if (! conv) {
|
|
molarVolBase = 0.0;
|
|
throw CanteraError("MixtureFugacityTP::densityCalc()", "Process didnot converge");
|
|
} else {
|
|
densBase = mmw / molarVolBase;
|
|
}
|
|
return densBase;
|
|
}
|
|
//====================================================================================================================
|
|
void MixtureFugacityTP::updateMixingExpressions()
|
|
{
|
|
|
|
}
|
|
//====================================================================================================================
|
|
MixtureFugacityTP::spinodalFunc::spinodalFunc(MixtureFugacityTP* tp) :
|
|
ResidEval(),
|
|
m_tp(tp)
|
|
{
|
|
}
|
|
//====================================================================================================================
|
|
int MixtureFugacityTP::spinodalFunc::evalSS(const doublereal t, const doublereal* const y,
|
|
doublereal* const r)
|
|
{
|
|
int status = 0;
|
|
doublereal molarVol = y[0];
|
|
doublereal tt = m_tp->temperature();
|
|
doublereal pp;
|
|
doublereal val = m_tp->dpdVCalc(tt, molarVol, pp);
|
|
r[0] = val;
|
|
return status;
|
|
}
|
|
//====================================================================================================================
|
|
// Utility routine in the calculation of the saturation pressure
|
|
/*
|
|
* Private routine
|
|
*
|
|
* @param TKelvin temperature (kelvin)
|
|
* @param pres pressure (Pascal)
|
|
* @param densLiq Output density of liquid
|
|
* @param densGas output density of gas
|
|
* @param delGRT output delGRT
|
|
*
|
|
* @return Returns zero if both the gas and the liquid states are found for a given pressure.
|
|
|
|
*/
|
|
int MixtureFugacityTP::corr0(doublereal TKelvin, doublereal pres, doublereal& densLiqGuess,
|
|
doublereal& densGasGuess, doublereal& liqGRT, doublereal& gasGRT)
|
|
{
|
|
|
|
int retn = 0;
|
|
doublereal densLiq = densityCalc(TKelvin, pres, FLUID_LIQUID_0, densLiqGuess);
|
|
if (densLiq <= 0.0) {
|
|
// throw Cantera::CanteraError("MixtureFugacityTP::corr0",
|
|
// "Error occurred trying to find liquid density at (T,P) = "
|
|
// + Cantera::fp2str(TKelvin) + " " + Cantera::fp2str(pres));
|
|
retn = -1;
|
|
} else {
|
|
densLiqGuess = densLiq;
|
|
setState_TR(TKelvin, densLiq);
|
|
liqGRT = gibbs_mole() / _RT();
|
|
}
|
|
|
|
doublereal densGas = densityCalc(TKelvin, pres, FLUID_GAS, densGasGuess);
|
|
if (densGas <= 0.0) {
|
|
//throw Cantera::CanteraError("MixtureFugacityTP::corr0",
|
|
// "Error occurred trying to find gas density at (T,P) = "
|
|
// + Cantera::fp2str(TKelvin) + " " + Cantera::fp2str(pres));
|
|
if (retn == -1) {
|
|
throw Cantera::CanteraError("MixtureFugacityTP::corr0",
|
|
"Error occurred trying to find gas density at (T,P) = "
|
|
+ Cantera::fp2str(TKelvin) + " " + Cantera::fp2str(pres));
|
|
}
|
|
retn = -2;
|
|
} else {
|
|
densGasGuess = densGas;
|
|
setState_TR(TKelvin, densGas);
|
|
gasGRT = gibbs_mole() / _RT();
|
|
}
|
|
// delGRT = gibbsLiqRT - gibbsGasRT;
|
|
return retn;
|
|
}
|
|
//====================================================================================================================
|
|
// Returns the Phase State flag for the current state of the object
|
|
/*
|
|
* @param checkState If true, this function does a complete check to see where
|
|
* in paramters space we are
|
|
*
|
|
* There are three values:
|
|
* WATER_GAS below the critical temperature but below the critical density
|
|
* WATER_LIQUID below the critical temperature but above the critical density
|
|
* WATER_SUPERCRIT above the critical temperature
|
|
*/
|
|
int MixtureFugacityTP::phaseState(bool checkState) const
|
|
{
|
|
int state = iState_;
|
|
if (checkState) {
|
|
double t = temperature();
|
|
double tcrit = critTemperature();
|
|
double rhocrit = critDensity();
|
|
if (t >= tcrit) {
|
|
state = FLUID_SUPERCRIT;
|
|
return state;
|
|
}
|
|
double tmid = tcrit - 100.;
|
|
if (tmid < 0.0) {
|
|
tmid = tcrit / 2.0;
|
|
}
|
|
double pp = psatEst(tmid);
|
|
double mmw = meanMolecularWeight();
|
|
double molVolLiqTmid = liquidVolEst(tmid, pp);
|
|
double molVolGasTmid = GasConstant * tmid / (pp);
|
|
double densLiqTmid = mmw / molVolLiqTmid;
|
|
double densGasTmid = mmw / molVolGasTmid;
|
|
double densMidTmid = 0.5 * (densLiqTmid + densGasTmid);
|
|
doublereal rhoMid = rhocrit + (t - tcrit) * (rhocrit - densMidTmid) / (tcrit - tmid);
|
|
|
|
double rho = density();
|
|
int iStateGuess = FLUID_LIQUID_0;
|
|
if (rho < rhoMid) {
|
|
iStateGuess = FLUID_GAS;
|
|
}
|
|
double molarVol = mmw / rho;
|
|
double presCalc;
|
|
|
|
double dpdv = dpdVCalc(t, molarVol, presCalc);
|
|
if (dpdv < 0.0) {
|
|
state = iStateGuess;
|
|
} else {
|
|
state = FLUID_UNSTABLE;
|
|
}
|
|
|
|
}
|
|
return state;
|
|
}
|
|
//====================================================================================================================
|
|
// Return the value of the density at the liquid spinodal point (on the liquid side)
|
|
// for the current temperature.
|
|
/*
|
|
* @return returns the density with units of kg m-3
|
|
*/
|
|
doublereal MixtureFugacityTP::densSpinodalLiquid() const
|
|
{
|
|
throw CanteraError("", "unimplmented");
|
|
return 0.0;
|
|
}
|
|
//====================================================================================================================
|
|
// Return the value of the density at the gas spinodal point (on the gas side)
|
|
// for the current temperature.
|
|
/*
|
|
* @return returns the density with units of kg m-3
|
|
*/
|
|
doublereal MixtureFugacityTP::densSpinodalGas() const
|
|
{
|
|
throw CanteraError("", "unimplmented");
|
|
return 0.0;
|
|
}
|
|
//====================================================================================================================
|
|
// Calculate the saturation pressure at the current mixture content for the given temperature
|
|
/*
|
|
* This is a non-const routine that is public.
|
|
*
|
|
* The algorithm for this routine has undergone quite a bit of work. It probably needs more work.
|
|
* However, it seems now to be fairly robust.
|
|
* The key requirement is to find an initial pressure where both the liquid and the gas exist. This
|
|
* is not as easy as it sounds, and it gets exceedingly hard as the critical temperature is approached
|
|
* from below.
|
|
* Once we have this initial state, then we seek to equilibrate the gibbs free energies of the
|
|
* gas and liquid and use the formula
|
|
*
|
|
* dp = VdG
|
|
*
|
|
* to create an update condition for deltaP using
|
|
*
|
|
* - (Gliq - Ggas) = (Vliq - Vgas) (deltaP)
|
|
*
|
|
*
|
|
*
|
|
* @param TKelvin (input) Temperature (Kelvin)
|
|
* @param molarVolGas (return) Molar volume of the gas
|
|
* @param molarVolLiquid (return) Molar volume of the liquid
|
|
*
|
|
* @return Returns the saturation pressure at the given temperature
|
|
*
|
|
* @TODO Suggestions for the future would be to switch it to an algorithm that uses the gas molar volume
|
|
* and the liquid molar volumes as the fundamental unknowns.
|
|
*
|
|
*/
|
|
doublereal MixtureFugacityTP::calculatePsat(doublereal TKelvin, doublereal& molarVolGas,
|
|
doublereal& molarVolLiquid)
|
|
{
|
|
// we need this because this is a non-const routine that is public
|
|
setTemperature(TKelvin);
|
|
double tcrit = critTemperature();
|
|
double RhoLiquid, RhoGas;
|
|
double RhoLiquidGood, RhoGasGood;
|
|
double densSave = density();
|
|
double tempSave = temperature();
|
|
double pres;
|
|
doublereal mw = meanMolecularWeight();
|
|
if (TKelvin < tcrit) {
|
|
|
|
pres = psatEst(TKelvin);
|
|
// trial value = Psat from correlation
|
|
int i;
|
|
doublereal volLiquid = liquidVolEst(TKelvin, pres);
|
|
RhoLiquidGood = mw / volLiquid;
|
|
RhoGasGood = pres * mw / (GasConstant * TKelvin);
|
|
doublereal delGRT = 1.0E6;
|
|
doublereal liqGRT, gasGRT;
|
|
int stab;
|
|
doublereal presLast = pres;
|
|
|
|
/*
|
|
* First part of the calculation involves finding a pressure at which the
|
|
* gas and the liquid state coexists.
|
|
*/
|
|
doublereal presLiquid;
|
|
doublereal presGas;
|
|
doublereal presBase = pres;
|
|
bool foundLiquid = false;
|
|
bool foundGas = false;
|
|
|
|
doublereal densLiquid = densityCalc(TKelvin, presBase, FLUID_LIQUID_0, RhoLiquidGood);
|
|
if (densLiquid > 0.0) {
|
|
foundLiquid = true;
|
|
presLiquid = pres;
|
|
RhoLiquidGood = densLiquid;
|
|
}
|
|
if (!foundLiquid) {
|
|
for (int i = 0; i < 50; i++) {
|
|
pres = 1.1 * pres;
|
|
densLiquid = densityCalc(TKelvin, pres, FLUID_LIQUID_0, RhoLiquidGood);
|
|
if (densLiquid > 0.0) {
|
|
foundLiquid = true;
|
|
presLiquid = pres;
|
|
RhoLiquidGood = densLiquid;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pres = presBase;
|
|
doublereal densGas = densityCalc(TKelvin, pres, FLUID_GAS, RhoGasGood);
|
|
if (densGas <= 0.0) {
|
|
foundGas = false;
|
|
} else {
|
|
foundGas = true;
|
|
presGas = pres;
|
|
RhoGasGood = densGas;
|
|
}
|
|
if (!foundGas) {
|
|
for (int i = 0; i < 50; i++) {
|
|
pres = 0.9 * pres;
|
|
densGas = densityCalc(TKelvin, pres, FLUID_GAS, RhoGasGood);
|
|
if (densGas > 0.0) {
|
|
foundGas = true;
|
|
presGas = pres;
|
|
RhoGasGood = densGas;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (foundGas && foundLiquid) {
|
|
if (presGas == presLiquid) {
|
|
pres = presGas;
|
|
goto startIteration;
|
|
}
|
|
pres = 0.5 * (presLiquid + presGas);
|
|
bool goodLiq;
|
|
bool goodGas;
|
|
for (int i = 0; i < 50; i++) {
|
|
|
|
doublereal densLiquid = densityCalc(TKelvin, pres, FLUID_LIQUID_0, RhoLiquidGood);
|
|
if (densLiquid <= 0.0) {
|
|
goodLiq = false;
|
|
} else {
|
|
goodLiq = true;
|
|
RhoLiquidGood = densLiquid;
|
|
presLiquid = pres;
|
|
}
|
|
doublereal densGas = densityCalc(TKelvin, pres, FLUID_GAS, RhoGasGood);
|
|
if (densGas <= 0.0) {
|
|
goodGas = false;
|
|
} else {
|
|
goodGas = true;
|
|
RhoGasGood = densGas;
|
|
presGas = pres;
|
|
}
|
|
if (goodGas && goodLiq) {
|
|
break;
|
|
}
|
|
if (!goodLiq && !goodGas) {
|
|
pres = 0.5 * (pres + presLiquid);
|
|
}
|
|
if (goodLiq || goodGas) {
|
|
pres = 0.5 * (presLiquid + presGas);
|
|
}
|
|
|
|
}
|
|
}
|
|
if (!foundGas || !foundLiquid) {
|
|
printf("error coundn't find a starting pressure\n");
|
|
return (0.0);
|
|
}
|
|
if (presGas != presLiquid) {
|
|
printf("error coundn't find a starting pressure\n");
|
|
return (0.0);
|
|
}
|
|
|
|
startIteration:
|
|
pres = presGas;
|
|
presLast = pres;
|
|
RhoGas = RhoGasGood;
|
|
RhoLiquid = RhoLiquidGood;
|
|
|
|
|
|
/*
|
|
* Now that we have found a good pressure we can proceed with the algorithm.
|
|
*/
|
|
|
|
for (i = 0; i < 20; i++) {
|
|
|
|
stab = corr0(TKelvin, pres, RhoLiquid, RhoGas, liqGRT, gasGRT);
|
|
if (stab == 0) {
|
|
presLast = pres;
|
|
delGRT = liqGRT - gasGRT;
|
|
doublereal delV = mw * (1.0/RhoLiquid - 1.0/RhoGas);
|
|
doublereal dp = - delGRT * GasConstant * TKelvin / delV;
|
|
|
|
if (fabs(dp) > 0.1 * pres) {
|
|
if (dp > 0.0) {
|
|
dp = 0.1 * pres;
|
|
} else {
|
|
dp = -0.1 * pres;
|
|
}
|
|
}
|
|
pres += dp;
|
|
|
|
} else if (stab == -1) {
|
|
delGRT = 1.0E6;
|
|
if (presLast > pres) {
|
|
pres = 0.5 * (presLast + pres);
|
|
} else {
|
|
// we are stuck here - try this
|
|
pres = 1.1 * pres;
|
|
}
|
|
} else if (stab == -2) {
|
|
if (presLast < pres) {
|
|
pres = 0.5 * (presLast + pres);
|
|
} else {
|
|
// we are stuck here - try this
|
|
pres = 0.9 * pres;
|
|
}
|
|
}
|
|
molarVolGas = mw / RhoGas;
|
|
molarVolLiquid = mw / RhoLiquid;
|
|
|
|
|
|
if (fabs(delGRT) < 1.0E-8) {
|
|
// converged
|
|
break;
|
|
}
|
|
}
|
|
|
|
molarVolGas = mw / RhoGas;
|
|
molarVolLiquid = mw / RhoLiquid;
|
|
// Put the fluid in the desired end condition
|
|
setState_TR(tempSave, densSave);
|
|
|
|
return pres;
|
|
|
|
|
|
} else {
|
|
pres = critPressure();
|
|
setState_TP(TKelvin, pres);
|
|
RhoGas = density();
|
|
molarVolGas = mw / RhoGas;
|
|
molarVolLiquid = molarVolGas;
|
|
setState_TR(tempSave, densSave);
|
|
}
|
|
return pres;
|
|
}
|
|
|
|
//====================================================================================================================
|
|
// Calculate the pressure given the temperature and the molar volume
|
|
doublereal MixtureFugacityTP::pressureCalc(doublereal TKelvin, doublereal molarVol) const
|
|
{
|
|
throw CanteraError("MixtureFugacityTP::pressureCalc", "unimplemented");
|
|
return 0.0;
|
|
}
|
|
//====================================================================================================================
|
|
// Calculate the pressure given the temperature and the molar volume
|
|
doublereal MixtureFugacityTP::dpdVCalc(doublereal TKelvin, doublereal molarVol, doublereal& presCalc) const
|
|
{
|
|
throw CanteraError("MixtureFugacityTP::dpdVCalc", "unimplemented");
|
|
return 0.0;
|
|
}
|
|
//====================================================================================================================
|
|
|
|
/*
|
|
* void _updateStandardStateThermo() (protected, virtual, const)
|
|
*
|
|
* If m_useTmpStandardStateStorage is true,
|
|
* This function must be called for every call to functions in this
|
|
* class that need standard state properties.
|
|
* Child classes may require that it be called even if m_useTmpStandardStateStorage
|
|
* is not true.
|
|
* It checks to see whether the temperature has changed and
|
|
* thus the ss thermodynamics functions for all of the species
|
|
* must be recalculated.
|
|
*
|
|
* This
|
|
*/
|
|
void MixtureFugacityTP::_updateReferenceStateThermo() const
|
|
{
|
|
double Tnow = temperature();
|
|
|
|
// If the temperature has changed since the last time these
|
|
// properties were computed, recompute them.
|
|
if (m_Tlast_ref != Tnow) {
|
|
m_spthermo->update(Tnow, &m_cp0_R[0], &m_h0_RT[0], &m_s0_R[0]);
|
|
m_Tlast_ref = Tnow;
|
|
|
|
// update the species Gibbs functions
|
|
for (size_t k = 0; k < m_kk; k++) {
|
|
m_g0_RT[k] = m_h0_RT[k] - m_s0_R[k];
|
|
}
|
|
doublereal pref = refPressure();
|
|
if (pref <= 0.0) {
|
|
throw CanteraError("MixtureFugacityTP::_updateReferenceStateThermo()", "neg ref pressure");
|
|
}
|
|
m_logc0 = log(pref/(GasConstant * Tnow));
|
|
}
|
|
}
|
|
//====================================================================================================================
|
|
|
|
|
|
}
|
|
|
|
|