893 lines
27 KiB
C++
893 lines
27 KiB
C++
/**
|
|
* @file WaterPropsIAPWS.cpp
|
|
* Definitions for a class for calculating the equation of state of water
|
|
* from the IAPWS 1995 Formulation based on the steam tables thermodynamic
|
|
* basis (See class \link Cantera::WaterPropsIAPWS WaterPropsIAPWS\endlink).
|
|
*/
|
|
/*
|
|
* Copyright (2006) 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/WaterPropsIAPWS.h"
|
|
#include "cantera/base/ctexceptions.h"
|
|
#include "cantera/base/stringUtils.h"
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
|
|
namespace Cantera
|
|
{
|
|
/*
|
|
* Critical Point values of water in mks units
|
|
*/
|
|
//! Critical Temperature value (kelvin)
|
|
const doublereal T_c = 647.096;
|
|
//! Critical Pressure (Pascals)
|
|
static const doublereal P_c = 22.064E6;
|
|
//! Value of the Density at the critical point (kg m-3)
|
|
const doublereal Rho_c = 322.;
|
|
//! Molecular Weight of water that is consistent with the paper (kg kmol-1)
|
|
static const doublereal M_water = 18.015268;
|
|
|
|
//! Gas constant that is quoted in the paper
|
|
/*
|
|
* Note, this is the Rgas value quoted in the paper. For consistency
|
|
* we have to use that value and not the updated value
|
|
*
|
|
* The Ratio of R/M = 0.46151805 kJ kg-1 K-1 , which is Eqn. (6.3) in the paper.
|
|
*/
|
|
static const doublereal Rgas = 8.314371E3; // Joules kmol-1 K-1
|
|
|
|
// Base constructor
|
|
WaterPropsIAPWS:: WaterPropsIAPWS() :
|
|
m_phi(0),
|
|
tau(-1.0),
|
|
delta(-1.0),
|
|
iState(-30000)
|
|
{
|
|
m_phi = new WaterPropsIAPWSphi();
|
|
}
|
|
|
|
// Copy constructor
|
|
/*
|
|
* @param b Object to be copied
|
|
*/
|
|
WaterPropsIAPWS::WaterPropsIAPWS(const WaterPropsIAPWS& b) :
|
|
m_phi(0),
|
|
tau(b.tau),
|
|
delta(b.delta),
|
|
iState(b.iState)
|
|
{
|
|
m_phi = new WaterPropsIAPWSphi();
|
|
m_phi->tdpolycalc(tau, delta);
|
|
}
|
|
|
|
// assignment constructor
|
|
/*
|
|
* @param right Object to be copied
|
|
*/
|
|
WaterPropsIAPWS& WaterPropsIAPWS::operator=(const WaterPropsIAPWS& b)
|
|
{
|
|
if (this == &b) {
|
|
return *this;
|
|
}
|
|
tau = b.tau;
|
|
delta = b.delta;
|
|
iState = b.iState;
|
|
m_phi->tdpolycalc(tau, delta);
|
|
return *this;
|
|
}
|
|
|
|
// destructor
|
|
WaterPropsIAPWS::~WaterPropsIAPWS()
|
|
{
|
|
delete(m_phi);
|
|
m_phi = 0;
|
|
}
|
|
|
|
/*
|
|
* Calculate the dimensionless temp and rho and store internally.
|
|
*
|
|
* @param temperature input temperature (kelvin)
|
|
* @param rho density in kg m-3
|
|
*
|
|
* this is a private function
|
|
*/
|
|
void WaterPropsIAPWS::calcDim(doublereal temperature, doublereal rho)
|
|
{
|
|
tau = T_c / temperature;
|
|
delta = rho / Rho_c;
|
|
/*
|
|
* Determine the internal state
|
|
*/
|
|
if (temperature > T_c) {
|
|
iState = WATER_SUPERCRIT;
|
|
} else {
|
|
if (delta < 1.0) {
|
|
iState = WATER_GAS;
|
|
} else {
|
|
iState = WATER_LIQUID;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate the Helmholtz free energy in mks units of J kmol-1 K-1,
|
|
// using the last temperature and density
|
|
doublereal WaterPropsIAPWS::helmholtzFE() const
|
|
{
|
|
doublereal retn = m_phi->phi(tau, delta);
|
|
doublereal temperature = T_c/tau;
|
|
doublereal RT = Rgas * temperature;
|
|
return (retn * RT);
|
|
}
|
|
|
|
/*
|
|
* Calculate the pressure (Pascals), using the
|
|
* current internally stored temperature and density
|
|
* Temperature: kelvin
|
|
* rho: density in kg m-3
|
|
*/
|
|
doublereal WaterPropsIAPWS::pressure() const
|
|
{
|
|
doublereal retn = m_phi->pressureM_rhoRT(tau, delta);
|
|
doublereal rho = delta * Rho_c;
|
|
doublereal temperature = T_c / tau;
|
|
return (retn * rho * Rgas * temperature/M_water);
|
|
}
|
|
|
|
/*
|
|
* Calculates the density given the temperature and the pressure,
|
|
* and a guess at the density. Note, below T_c, this is a
|
|
* multivalued function.
|
|
*
|
|
* 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.
|
|
*/
|
|
doublereal WaterPropsIAPWS::density(doublereal temperature, doublereal pressure,
|
|
int phase, doublereal rhoguess)
|
|
{
|
|
|
|
doublereal deltaGuess = 0.0;
|
|
if (rhoguess == -1.0) {
|
|
if (phase != -1) {
|
|
if (temperature > T_c) {
|
|
rhoguess = pressure * M_water / (Rgas * temperature);
|
|
} else {
|
|
if (phase == WATER_GAS || phase == WATER_SUPERCRIT) {
|
|
rhoguess = pressure * M_water / (Rgas * temperature);
|
|
} else if (phase == WATER_LIQUID) {
|
|
/*
|
|
* Provide a guess about the liquid density that is
|
|
* relatively high -> convergnce from above seems robust.
|
|
*/
|
|
rhoguess = 1000.;
|
|
} else if (phase == WATER_UNSTABLELIQUID || phase == WATER_UNSTABLEGAS) {
|
|
throw Cantera::CanteraError("WaterPropsIAPWS::density",
|
|
"Unstable Branch finder is untested");
|
|
} else {
|
|
throw Cantera::CanteraError("WaterPropsIAPWS::density",
|
|
"unknown state: " + Cantera::int2str(phase));
|
|
}
|
|
}
|
|
} else {
|
|
/*
|
|
* Assume the Gas phase initial guess, if nothing is
|
|
* specified to the routine
|
|
*/
|
|
rhoguess = pressure * M_water / (Rgas * temperature);
|
|
}
|
|
|
|
}
|
|
doublereal p_red = pressure * M_water / (Rgas * temperature * Rho_c);
|
|
deltaGuess = rhoguess / Rho_c;
|
|
setState_TR(temperature, rhoguess);
|
|
doublereal delta_retn = m_phi->dfind(p_red, tau, deltaGuess);
|
|
doublereal density_retn;
|
|
if (delta_retn >0.0) {
|
|
delta = delta_retn;
|
|
|
|
/*
|
|
* Dimensionalize the density before returning
|
|
*/
|
|
density_retn = delta_retn * Rho_c;
|
|
/*
|
|
* Set the internal state -> this may be
|
|
* a duplication. However, let's just be sure.
|
|
*/
|
|
setState_TR(temperature, density_retn);
|
|
|
|
|
|
} else {
|
|
density_retn = -1.0;
|
|
}
|
|
return density_retn;
|
|
}
|
|
|
|
// Calculates the density given the temperature and the pressure,
|
|
// and a guess at the density, while not changing the internal state
|
|
/*
|
|
* Note, below T_c, this is a multivalued function.
|
|
*
|
|
* The #density() function calculates the density that is consistent with
|
|
* a particular value of the temperature and pressure. It may therefore be
|
|
* multivalued or potentially there may be no answer from this function. It therefore
|
|
* takes a phase guess and a density guess as optional parameters. If no guesses are
|
|
*
|
|
* supplied to density(), a gas phase guess is assumed. This may or may not be what
|
|
* is wanted. Therefore, density() should usually at least be supplied with a phase
|
|
* guess so that it may manufacture an appropriate density guess.
|
|
* #density() manufactures the initial density guess, nondimensionalizes everything,
|
|
* and then calls #WaterPropsIAPWSphi::dfind(), which does the iterative calculation
|
|
* to find the density condition that matches the desired input pressure.
|
|
*
|
|
* @param pressure : Pressure in Pascals (Newton/m**2)
|
|
* @param phase : guessed phase of water
|
|
* : -1: no guessed phase
|
|
* @param rhoguess : guessed density of the water
|
|
* : -1.0 no guessed density
|
|
* @return
|
|
* Returns the density. If an error is encountered in the calculation
|
|
* the value of -1.0 is returned.
|
|
*/
|
|
doublereal WaterPropsIAPWS::density_const(doublereal pressure,
|
|
int phase, doublereal rhoguess) const
|
|
{
|
|
doublereal temperature = T_c / tau;
|
|
doublereal deltaGuess = 0.0;
|
|
doublereal deltaSave = delta;
|
|
if (rhoguess == -1.0) {
|
|
if (phase != -1) {
|
|
if (temperature > T_c) {
|
|
rhoguess = pressure * M_water / (Rgas * temperature);
|
|
} else {
|
|
if (phase == WATER_GAS || phase == WATER_SUPERCRIT) {
|
|
rhoguess = pressure * M_water / (Rgas * temperature);
|
|
} else if (phase == WATER_LIQUID) {
|
|
/*
|
|
* Provide a guess about the liquid density that is
|
|
* relatively high -> convergnce from above seems robust.
|
|
*/
|
|
rhoguess = 1000.;
|
|
} else if (phase == WATER_UNSTABLELIQUID || phase == WATER_UNSTABLEGAS) {
|
|
throw Cantera::CanteraError("WaterPropsIAPWS::density",
|
|
"Unstable Branch finder is untested");
|
|
} else {
|
|
throw Cantera::CanteraError("WaterPropsIAPWS::density",
|
|
"unknown state: " + Cantera::int2str(phase));
|
|
}
|
|
}
|
|
} else {
|
|
/*
|
|
* Assume the Gas phase initial guess, if nothing is
|
|
* specified to the routine
|
|
*/
|
|
rhoguess = pressure * M_water / (Rgas * temperature);
|
|
}
|
|
|
|
}
|
|
doublereal p_red = pressure * M_water / (Rgas * temperature * Rho_c);
|
|
deltaGuess = rhoguess / Rho_c;
|
|
|
|
delta = deltaGuess;
|
|
m_phi->tdpolycalc(tau, delta);
|
|
// setState_TR(temperature, rhoguess);
|
|
|
|
doublereal delta_retn = m_phi->dfind(p_red, tau, deltaGuess);
|
|
doublereal density_retn;
|
|
if (delta_retn > 0.0) {
|
|
delta = delta_retn;
|
|
|
|
/*
|
|
* Dimensionalize the density before returning
|
|
*/
|
|
density_retn = delta_retn * Rho_c;
|
|
|
|
} else {
|
|
density_retn = -1.0;
|
|
}
|
|
|
|
delta = deltaSave;
|
|
m_phi->tdpolycalc(tau, delta);
|
|
return density_retn;
|
|
}
|
|
|
|
// Returns the density (kg m-3)
|
|
/*
|
|
* The density is an independent variable in the underlying equation of state
|
|
*
|
|
* @return Returns the density (kg m-3)
|
|
*/
|
|
doublereal WaterPropsIAPWS::density() const
|
|
{
|
|
return (delta * Rho_c);
|
|
}
|
|
|
|
// Returns the temperature (Kelvin)
|
|
/*
|
|
* @return Returns the internally stored temperature
|
|
*/
|
|
doublereal WaterPropsIAPWS::temperature() const
|
|
{
|
|
return (T_c / tau);
|
|
}
|
|
|
|
/*
|
|
* psat_est provides a rough estimate of the saturation
|
|
* pressure given the temperature. This is used as an initial
|
|
* guess for refining the pressure.
|
|
*
|
|
* Input
|
|
* temperature (kelvin)
|
|
*
|
|
* return:
|
|
* psat (Pascals)
|
|
*/
|
|
doublereal WaterPropsIAPWS::psat_est(doublereal temperature) const
|
|
{
|
|
|
|
static const doublereal A[8] = {
|
|
-7.8889166E0,
|
|
2.5514255E0,
|
|
-6.716169E0,
|
|
33.2239495E0,
|
|
-105.38479E0,
|
|
174.35319E0,
|
|
-148.39348E0,
|
|
48.631602E0
|
|
};
|
|
doublereal ps;
|
|
if (temperature < 314.) {
|
|
doublereal pl = 6.3573118E0 - 8858.843E0 / temperature
|
|
+ 607.56335E0 * pow(temperature, -0.6);
|
|
ps = 0.1 * exp(pl);
|
|
} else {
|
|
doublereal v = temperature / 647.25;
|
|
doublereal w = fabs(1.0-v);
|
|
doublereal b = 0.0;
|
|
for (int i = 0; i < 8; i++) {
|
|
doublereal z = i + 1;
|
|
b += A[i] * pow(w, ((z+1.0)/2.0));
|
|
}
|
|
doublereal q = b / v;
|
|
ps = 22.093*exp(q);
|
|
}
|
|
/*
|
|
* Original correlation was in cgs. Convert to mks
|
|
*/
|
|
ps *= 1.0E6;
|
|
return ps;
|
|
}
|
|
|
|
/*
|
|
* Returns the coefficient of isothermal compressibility
|
|
* of temperature and pressure.
|
|
* kappa = - d (ln V) / dP at constant T.
|
|
*/
|
|
doublereal WaterPropsIAPWS::isothermalCompressibility() const
|
|
{
|
|
doublereal dpdrho_val = dpdrho();
|
|
doublereal dens = delta * Rho_c;
|
|
return (1.0 / (dens * dpdrho_val));
|
|
}
|
|
|
|
// Returns the value of dp / drho at constant T at the current
|
|
// state of the object
|
|
/*
|
|
* units - Joules / kg
|
|
*
|
|
* @return returns dpdrho
|
|
*/
|
|
doublereal WaterPropsIAPWS::dpdrho() const
|
|
{
|
|
doublereal retn = m_phi->dimdpdrho(tau, delta);
|
|
doublereal temperature = T_c/tau;
|
|
doublereal val = retn * Rgas * temperature / M_water;
|
|
return val;
|
|
}
|
|
|
|
// Returns the isochoric pressure derivative wrt temperature
|
|
/*
|
|
* beta = M / (rho * Rgas) (d (pressure) / dT) at constant rho
|
|
*
|
|
* Note for ideal gases this is equal to one.
|
|
*
|
|
* beta = delta (phi0_d() + phiR_d())
|
|
* - tau delta (phi0_dt() + phiR_dt())
|
|
*/
|
|
doublereal WaterPropsIAPWS:: coeffPresExp() const
|
|
{
|
|
doublereal retn = m_phi->dimdpdT(tau, delta);
|
|
return (retn);
|
|
}
|
|
|
|
// Returns the coefficient of thermal expansion.
|
|
/*
|
|
* alpha = d (ln V) / dT at constant P.
|
|
*
|
|
* @return Returns the coefficient of thermal expansion
|
|
*/
|
|
doublereal WaterPropsIAPWS:: coeffThermExp() const
|
|
{
|
|
doublereal kappa = isothermalCompressibility();
|
|
doublereal beta = coeffPresExp();
|
|
doublereal dens = delta * Rho_c;
|
|
return (kappa * dens * Rgas * beta / M_water);
|
|
}
|
|
|
|
// Calculate the Gibbs free energy in mks units of J kmol-1 K-1.
|
|
// using the last temperature and density
|
|
doublereal WaterPropsIAPWS::Gibbs() const
|
|
{
|
|
doublereal gRT = m_phi->gibbs_RT();
|
|
doublereal temperature = T_c/tau;
|
|
return (gRT * Rgas * temperature);
|
|
}
|
|
|
|
|
|
// Utility routine in the calculation of the saturation pressure
|
|
/*
|
|
* Private routine
|
|
*
|
|
* Calculate the Gibbs free energy in mks units of
|
|
* J kmol-1 K-1.
|
|
*
|
|
* @param temperature temperature (kelvin)
|
|
* @param pressure pressure (Pascal)
|
|
* @param densLiq Output density of liquid
|
|
* @param densGas output Density of gas
|
|
* @param delGRT output delGRT
|
|
*/
|
|
void WaterPropsIAPWS::
|
|
corr(doublereal temperature, doublereal pressure, doublereal& densLiq,
|
|
doublereal& densGas, doublereal& delGRT)
|
|
{
|
|
|
|
densLiq = density(temperature, pressure, WATER_LIQUID, densLiq);
|
|
if (densLiq <= 0.0) {
|
|
throw Cantera::CanteraError("WaterPropsIAPWS::corr",
|
|
"Error occurred trying to find liquid density at (T,P) = "
|
|
+ Cantera::fp2str(temperature) + " " + Cantera::fp2str(pressure));
|
|
}
|
|
setState_TR(temperature, densLiq);
|
|
doublereal gibbsLiqRT = m_phi->gibbs_RT();
|
|
|
|
densGas = density(temperature, pressure, WATER_GAS, densGas);
|
|
if (densGas <= 0.0) {
|
|
throw Cantera::CanteraError("WaterPropsIAPWS::corr",
|
|
"Error occurred trying to find gas density at (T,P) = "
|
|
+ Cantera::fp2str(temperature) + " " + Cantera::fp2str(pressure));
|
|
}
|
|
setState_TR(temperature, densGas);
|
|
doublereal gibbsGasRT = m_phi->gibbs_RT();
|
|
|
|
delGRT = gibbsLiqRT - gibbsGasRT;
|
|
}
|
|
|
|
// Utility routine in the calculation of the saturation pressure
|
|
/*
|
|
* Private routine
|
|
*
|
|
* @param temperature temperature (kelvin)
|
|
* @param pressure pressure (Pascal)
|
|
* @param densLiq Output density of liquid
|
|
* @param densGas output Density of gas
|
|
* @param pcorr output corrected pressure
|
|
*/
|
|
void WaterPropsIAPWS::
|
|
corr1(doublereal temperature, doublereal pressure, doublereal& densLiq,
|
|
doublereal& densGas, doublereal& pcorr)
|
|
{
|
|
|
|
densLiq = density(temperature, pressure, WATER_LIQUID, densLiq);
|
|
if (densLiq <= 0.0) {
|
|
throw Cantera::CanteraError("WaterPropsIAPWS::corr1",
|
|
"Error occurred trying to find liquid density at (T,P) = "
|
|
+ Cantera::fp2str(temperature) + " " + Cantera::fp2str(pressure));
|
|
}
|
|
setState_TR(temperature, densLiq);
|
|
doublereal prL = m_phi->phiR();
|
|
|
|
densGas = density(temperature, pressure, WATER_GAS, densGas);
|
|
if (densGas <= 0.0) {
|
|
throw Cantera::CanteraError("WaterPropsIAPWS::corr1",
|
|
"Error occurred trying to find gas density at (T,P) = "
|
|
+ Cantera::fp2str(temperature) + " " + Cantera::fp2str(pressure));
|
|
}
|
|
setState_TR(temperature, densGas);
|
|
doublereal prG = m_phi->phiR();
|
|
|
|
doublereal rhs = (prL - prG) + log(densLiq/densGas);
|
|
rhs /= (1.0/densGas - 1.0/densLiq);
|
|
|
|
pcorr = rhs * Rgas * temperature / M_water;
|
|
}
|
|
|
|
|
|
// This function returns the saturation pressure given the
|
|
// temperature as an input parameter, and sets the internal state to the saturated
|
|
// conditions.
|
|
/*
|
|
* Note this function will return the saturation pressure, given the temperature.
|
|
* It will then set the state of the system to the saturation condition. The input
|
|
* parameter waterState is used to either specify the liquid state or the
|
|
* gas state at the desired temperature and saturated pressure.
|
|
*
|
|
* If the input temperature, T, is above T_c, this routine will set the internal
|
|
* state to T and the pressure to P_c. Then, return P_c.
|
|
*
|
|
* @param temperature input temperature (kelvin)
|
|
* @param waterState integer specifying the water state
|
|
*
|
|
* @return Returns the saturation pressure
|
|
* units = Pascal
|
|
*/
|
|
doublereal WaterPropsIAPWS::psat(doublereal temperature, int waterState)
|
|
{
|
|
static int method = 1;
|
|
doublereal densLiq = -1.0, densGas = -1.0, delGRT = 0.0;
|
|
doublereal dp, pcorr;
|
|
if (temperature >= T_c) {
|
|
densGas = density(temperature, P_c, WATER_SUPERCRIT);
|
|
setState_TR(temperature, densGas);
|
|
return P_c;
|
|
}
|
|
doublereal p = psat_est(temperature);
|
|
for (int i = 0; i < 30; i++) {
|
|
if (method == 1) {
|
|
corr(temperature, p, densLiq, densGas, delGRT);
|
|
doublereal delV = M_water * (1.0/densLiq - 1.0/densGas);
|
|
dp = - delGRT * Rgas * temperature / delV;
|
|
} else {
|
|
corr1(temperature, p, densLiq, densGas, pcorr);
|
|
dp = pcorr - p;
|
|
}
|
|
p += dp;
|
|
|
|
if ((method == 1) && delGRT < 1.0E-8) {
|
|
break;
|
|
} else {
|
|
if (fabs(dp/p) < 1.0E-9) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Put the fluid in the desired end condition
|
|
if (waterState == WATER_LIQUID) {
|
|
setState_TR(temperature, densLiq);
|
|
} else if (waterState == WATER_GAS) {
|
|
setState_TR(temperature, densGas);
|
|
} else {
|
|
throw Cantera::CanteraError("WaterPropsIAPWS::psat",
|
|
"unknown water state input: " + Cantera::int2str(waterState));
|
|
}
|
|
return p;
|
|
}
|
|
|
|
// 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 parameter 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 WaterPropsIAPWS::phaseState(bool checkState) const
|
|
{
|
|
if (checkState) {
|
|
if (tau <= 1.0) {
|
|
iState = WATER_SUPERCRIT;
|
|
} else {
|
|
doublereal T = T_c / tau;
|
|
doublereal rho = delta * Rho_c;
|
|
//doublereal psatTable = psat_est(T);
|
|
doublereal rhoMidAtm = 0.5 * (1.01E5 * M_water / (8314.472 * 373.15) + 1.0E3);
|
|
doublereal rhoMid = Rho_c + (T - T_c) * (Rho_c - rhoMidAtm) / (T_c - 373.15);
|
|
int iStateGuess = WATER_LIQUID;
|
|
if (rho < rhoMid) {
|
|
iStateGuess = WATER_GAS;
|
|
}
|
|
doublereal kappa = isothermalCompressibility();
|
|
if (kappa >= 0.0) {
|
|
iState = iStateGuess;
|
|
} else {
|
|
// When we are here we are between the spinodal curves
|
|
doublereal rhoDel = rho * 1.000001;
|
|
|
|
//setState_TR(T, rhoDel);
|
|
doublereal deltaSave = delta;
|
|
doublereal deltaDel = rhoDel / Rho_c;
|
|
delta = deltaDel;
|
|
m_phi->tdpolycalc(tau, deltaDel);
|
|
|
|
doublereal kappaDel = isothermalCompressibility();
|
|
doublereal d2rhodp2 = (rhoDel * kappaDel - rho * kappa) / (rhoDel - rho);
|
|
if (d2rhodp2 > 0.0) {
|
|
iState = WATER_UNSTABLELIQUID;
|
|
} else {
|
|
iState = WATER_UNSTABLEGAS;
|
|
}
|
|
//setState_TR(T, rho);
|
|
delta = deltaSave;
|
|
|
|
m_phi->tdpolycalc(tau, delta);
|
|
}
|
|
}
|
|
}
|
|
return iState;
|
|
}
|
|
|
|
// Return the value of the density at the water spinodal point (on the liquid side)
|
|
// for the current temperature.
|
|
/*
|
|
* @return returns the density with units of kg m-3
|
|
*/
|
|
doublereal WaterPropsIAPWS::densSpinodalWater() const
|
|
{
|
|
doublereal temperature = T_c/tau;
|
|
doublereal delta_save = delta;
|
|
// return the critical density if we are above or even just a little below
|
|
// the critical temperature. We just don't want to worry about the critical
|
|
// point at this juncture.
|
|
if (temperature >= T_c - 0.001) {
|
|
return Rho_c;
|
|
}
|
|
doublereal p = psat_est(temperature);
|
|
doublereal rho_low = 0.0;
|
|
doublereal rho_high = 1000;
|
|
|
|
doublereal densSatLiq = density_const(p, WATER_LIQUID);
|
|
doublereal dens_old = densSatLiq;
|
|
delta = dens_old / Rho_c;
|
|
m_phi->tdpolycalc(tau, delta);
|
|
doublereal dpdrho_old = dpdrho();
|
|
if (dpdrho_old > 0.0) {
|
|
rho_high = std::min(dens_old, rho_high);
|
|
} else {
|
|
rho_low = std::max(rho_low, dens_old);
|
|
}
|
|
doublereal dens_new = densSatLiq* (1.0001);
|
|
delta = dens_new / Rho_c;
|
|
m_phi->tdpolycalc(tau, delta);
|
|
doublereal dpdrho_new = dpdrho();
|
|
if (dpdrho_new > 0.0) {
|
|
rho_high = std::min(dens_new, rho_high);
|
|
} else {
|
|
rho_low = std::max(rho_low, dens_new);
|
|
}
|
|
bool conv = false;
|
|
|
|
for (int it = 0; it < 50; it++) {
|
|
doublereal slope = (dpdrho_new - dpdrho_old)/(dens_new - dens_old);
|
|
if (slope >= 0.0) {
|
|
slope = std::max(slope, dpdrho_new *5.0/ dens_new);
|
|
} else {
|
|
slope = -dpdrho_new;
|
|
//slope = MIN(slope, dpdrho_new *5.0 / dens_new);
|
|
// shouldn't be here for liquid spinodal
|
|
}
|
|
doublereal delta_rho = - dpdrho_new / slope;
|
|
if (delta_rho > 0.0) {
|
|
delta_rho = std::min(delta_rho, dens_new * 0.1);
|
|
} else {
|
|
delta_rho = std::max(delta_rho, - dens_new * 0.1);
|
|
}
|
|
doublereal dens_est = dens_new + delta_rho;
|
|
if (dens_est < rho_low) {
|
|
dens_est = 0.5 * (rho_low + dens_new);
|
|
}
|
|
if (dens_est > rho_high) {
|
|
dens_est = 0.5 * (rho_high + dens_new);
|
|
}
|
|
|
|
|
|
dens_old = dens_new;
|
|
dpdrho_old = dpdrho_new;
|
|
dens_new = dens_est;
|
|
|
|
delta = dens_new / Rho_c;
|
|
m_phi->tdpolycalc(tau, delta);
|
|
dpdrho_new = dpdrho();
|
|
if (dpdrho_new > 0.0) {
|
|
rho_high = std::min(dens_new, rho_high);
|
|
} else if (dpdrho_new < 0.0) {
|
|
rho_low = std::max(rho_low, dens_new);
|
|
} else {
|
|
conv = true;
|
|
break;
|
|
}
|
|
|
|
if (fabs(dpdrho_new) < 1.0E-5) {
|
|
conv = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!conv) {
|
|
throw Cantera::CanteraError(" WaterPropsIAPWS::densSpinodalWater()",
|
|
" convergence failure");
|
|
}
|
|
// Restore the original delta
|
|
delta = delta_save;
|
|
m_phi->tdpolycalc(tau, delta);
|
|
|
|
return dens_new;
|
|
}
|
|
|
|
// Return the value of the density at the water spinodal point (on the gas side)
|
|
// for the current temperature.
|
|
/*
|
|
* @return returns the density with units of kg m-3
|
|
*/
|
|
doublereal WaterPropsIAPWS::densSpinodalSteam() const
|
|
{
|
|
doublereal temperature = T_c/tau;
|
|
doublereal delta_save = delta;
|
|
// return the critical density if we are above or even just a little below
|
|
// the critical temperature. We just don't want to worry about the critical
|
|
// point at this juncture.
|
|
if (temperature >= T_c - 0.001) {
|
|
return Rho_c;
|
|
}
|
|
doublereal p = psat_est(temperature);
|
|
doublereal rho_low = 0.0;
|
|
doublereal rho_high = 1000;
|
|
|
|
doublereal densSatGas = density_const(p, WATER_GAS);
|
|
doublereal dens_old = densSatGas;
|
|
delta = dens_old / Rho_c;
|
|
m_phi->tdpolycalc(tau, delta);
|
|
doublereal dpdrho_old = dpdrho();
|
|
if (dpdrho_old < 0.0) {
|
|
rho_high = std::min(dens_old, rho_high);
|
|
} else {
|
|
rho_low = std::max(rho_low, dens_old);
|
|
}
|
|
doublereal dens_new = densSatGas * (0.99);
|
|
delta = dens_new / Rho_c;
|
|
m_phi->tdpolycalc(tau, delta);
|
|
doublereal dpdrho_new = dpdrho();
|
|
if (dpdrho_new < 0.0) {
|
|
rho_high = std::min(dens_new, rho_high);
|
|
} else {
|
|
rho_low = std::max(rho_low, dens_new);
|
|
}
|
|
bool conv = false;
|
|
|
|
for (int it = 0; it < 50; it++) {
|
|
doublereal slope = (dpdrho_new - dpdrho_old)/(dens_new - dens_old);
|
|
if (slope >= 0.0) {
|
|
slope = dpdrho_new;
|
|
//slope = MAX(slope, dpdrho_new *5.0/ dens_new);
|
|
// shouldn't be here for gas spinodal
|
|
} else {
|
|
//slope = -dpdrho_new;
|
|
slope = std::min(slope, dpdrho_new *5.0 / dens_new);
|
|
|
|
}
|
|
doublereal delta_rho = - dpdrho_new / slope;
|
|
if (delta_rho > 0.0) {
|
|
delta_rho = std::min(delta_rho, dens_new * 0.1);
|
|
} else {
|
|
delta_rho = std::max(delta_rho, - dens_new * 0.1);
|
|
}
|
|
doublereal dens_est = dens_new + delta_rho;
|
|
if (dens_est < rho_low) {
|
|
dens_est = 0.5 * (rho_low + dens_new);
|
|
}
|
|
if (dens_est > rho_high) {
|
|
dens_est = 0.5 * (rho_high + dens_new);
|
|
}
|
|
|
|
|
|
dens_old = dens_new;
|
|
dpdrho_old = dpdrho_new;
|
|
dens_new = dens_est;
|
|
|
|
delta = dens_new / Rho_c;
|
|
m_phi->tdpolycalc(tau, delta);
|
|
dpdrho_new = dpdrho();
|
|
if (dpdrho_new < 0.0) {
|
|
rho_high = std::min(dens_new, rho_high);
|
|
} else if (dpdrho_new > 0.0) {
|
|
rho_low = std::max(rho_low, dens_new);
|
|
} else {
|
|
conv = true;
|
|
break;
|
|
}
|
|
|
|
if (fabs(dpdrho_new) < 1.0E-5) {
|
|
conv = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!conv) {
|
|
throw Cantera::CanteraError(" WaterPropsIAPWS::densSpinodalSteam()",
|
|
" convergence failure");
|
|
}
|
|
// Restore the original delta
|
|
delta = delta_save;
|
|
m_phi->tdpolycalc(tau, delta);
|
|
|
|
return dens_new;
|
|
}
|
|
|
|
/*
|
|
* Sets the internal state of the object to the
|
|
* specified temperature and density.
|
|
*/
|
|
void WaterPropsIAPWS::setState_TR(doublereal temperature, doublereal rho)
|
|
{
|
|
calcDim(temperature, rho);
|
|
m_phi->tdpolycalc(tau, delta);
|
|
}
|
|
|
|
/*
|
|
* Calculate the enthalpy in mks units of
|
|
* J kmol-1 K-1.
|
|
*/
|
|
doublereal WaterPropsIAPWS::enthalpy() const
|
|
{
|
|
doublereal temperature = T_c/tau;
|
|
doublereal hRT = m_phi->enthalpy_RT();
|
|
return (hRT * Rgas * temperature);
|
|
}
|
|
|
|
/*
|
|
* Calculate the internal Energy in mks units of
|
|
* J kmol-1 K-1.
|
|
*/
|
|
doublereal WaterPropsIAPWS::intEnergy() const
|
|
{
|
|
doublereal temperature = T_c / tau;
|
|
doublereal uRT = m_phi->intEnergy_RT();
|
|
return (uRT * Rgas * temperature);
|
|
}
|
|
|
|
/*
|
|
* Calculate the enthalpy in mks units of356
|
|
* J kmol-1 K-1.
|
|
*/
|
|
doublereal WaterPropsIAPWS::entropy() const
|
|
{
|
|
doublereal sR = m_phi->entropy_R();
|
|
return (sR * Rgas);
|
|
}
|
|
|
|
/*
|
|
* Calculate heat capacity at constant volume
|
|
* J kmol-1 K-1.
|
|
*/
|
|
doublereal WaterPropsIAPWS::cv() const
|
|
{
|
|
doublereal cvR = m_phi->cv_R();
|
|
return (cvR * Rgas);
|
|
}
|
|
|
|
// Calculate the constant pressure heat capacity in mks units of J kmol-1 K-1
|
|
// at the last temperature and density
|
|
doublereal WaterPropsIAPWS::cp() const
|
|
{
|
|
doublereal cpR = m_phi->cp_R();
|
|
return (cpR * Rgas);
|
|
}
|
|
|
|
// Calculate the molar volume (kmol m-3)
|
|
// at the last temperature and density
|
|
doublereal WaterPropsIAPWS::molarVolume() const
|
|
{
|
|
doublereal rho = delta * Rho_c;
|
|
return (M_water / rho);
|
|
}
|
|
|
|
}
|