Cantera  3.2.0
Loading...
Searching...
No Matches
WaterPropsIAPWS.cpp
Go to the documentation of this file.
1/**
2 * @file WaterPropsIAPWS.cpp
3 * Definitions for a class for calculating the equation of state of water
4 * from the IAPWS 1995 Formulation based on the steam tables thermodynamic
5 * basis (See class @link Cantera::WaterPropsIAPWS WaterPropsIAPWS@endlink).
6 */
7
8// This file is part of Cantera. See License.txt in the top-level directory or
9// at https://cantera.org/license.txt for license and copyright information.
10
14#include "cantera/base/global.h"
15
16namespace Cantera
17{
18// Critical Point values of water in mks units
19
20//! Critical Temperature value (kelvin)
21const double T_c = 647.096;
22//! Critical Pressure (Pascals)
23static const double P_c = 22.064E6;
24//! Value of the Density at the critical point (kg m-3)
25const double Rho_c = 322.;
26
27static const double R_water = 461.51805; // J/kg/K (Eq. 6.3)
28
29void WaterPropsIAPWS::calcDim(double temperature, double rho)
30{
32 delta = rho / Rho_c;
33
34 // Determine the internal state
35 if (temperature > T_c) {
36 iState = WATER_SUPERCRIT;
37 } else {
38 if (delta < 1.0) {
39 iState = WATER_GAS;
40 } else {
41 iState = WATER_LIQUID;
42 }
43 }
44}
45
47{
48 double retn = m_phi.pressureM_rhoRT(tau, delta);
49 double rho = delta * Rho_c;
50 double temperature = T_c / tau;
51 return retn * rho * R_water * temperature;
52}
53
54double WaterPropsIAPWS::density(double temperature, double pressure,
55 int phase, double rhoguess)
56{
57 if (fabs(pressure - P_c) / P_c < 1.e-8 &&
58 fabs(temperature - T_c) / T_c < 1.e-8) {
59 // Catch critical point, as no solution is found otherwise
61 return Rho_c;
62 }
63 double deltaGuess = 0.0;
64 if (rhoguess == -1.0) {
65 if (phase != -1) {
66 if (temperature > T_c) {
67 rhoguess = pressure / (R_water * temperature);
68 } else {
69 if (phase == WATER_GAS || phase == WATER_SUPERCRIT) {
70 rhoguess = pressure / (R_water * temperature);
71 } else if (phase == WATER_LIQUID) {
72 // Provide a guess about the liquid density that is
73 // relatively high -> convergence from above seems robust.
74 rhoguess = 1000.;
75 } else if (phase == WATER_UNSTABLELIQUID || phase == WATER_UNSTABLEGAS) {
76 throw CanteraError("WaterPropsIAPWS::density",
77 "Unstable Branch finder is untested");
78 } else {
79 throw CanteraError("WaterPropsIAPWS::density",
80 "unknown state: {}", phase);
81 }
82 }
83 } else {
84 // Assume the Gas phase initial guess, if nothing is specified to
85 // the routine
86 rhoguess = pressure / (R_water * temperature);
87 }
88 }
89 double p_red = pressure / (R_water * temperature * Rho_c);
90 deltaGuess = rhoguess / Rho_c;
91 setState_TD(temperature, rhoguess);
92 double delta_retn = m_phi.dfind(p_red, tau, deltaGuess);
93 if (delta_retn <= 0) {
94 // No solution found for first initial guess; perturb initial guess once
95 // to avoid spurious failures (band-aid fix)
96 delta_retn = m_phi.dfind(p_red, tau, 0.9 * deltaGuess);
97 }
98 double density_retn;
99 if (delta_retn > 0.0) {
100 delta = delta_retn;
101
102 // Dimensionalize the density before returning
103 density_retn = delta_retn * Rho_c;
104
105 // Set the internal state -> this may be a duplication. However, let's
106 // just be sure.
107 setState_TD(temperature, density_retn);
108 } else {
109 density_retn = -1.0;
110 }
111 return density_retn;
112}
113
114double WaterPropsIAPWS::density_const(double pressure, int phase, double rhoguess) const
115{
116 warn_deprecated("WaterPropsIAPWS::density_const",
117 "To be removed after Cantera 3.1.");
118 double temperature = T_c / tau;
119 double deltaGuess = 0.0;
120 double deltaSave = delta;
121 if (rhoguess == -1.0) {
122 if (phase != -1) {
123 if (temperature > T_c) {
124 rhoguess = pressure / (R_water * temperature);
125 } else {
126 if (phase == WATER_GAS || phase == WATER_SUPERCRIT) {
127 rhoguess = pressure / (R_water * temperature);
128 } else if (phase == WATER_LIQUID) {
129 // Provide a guess about the liquid density that is
130 // relatively high -> convergence from above seems robust.
131 rhoguess = 1000.;
132 } else if (phase == WATER_UNSTABLELIQUID || phase == WATER_UNSTABLEGAS) {
133 throw CanteraError("WaterPropsIAPWS::density_const",
134 "Unstable Branch finder is untested");
135 } else {
136 throw CanteraError("WaterPropsIAPWS::density_const",
137 "unknown state: {}", phase);
138 }
139 }
140 } else {
141 // Assume the Gas phase initial guess, if nothing is specified to
142 // the routine
143 rhoguess = pressure / (R_water * temperature);
144 }
145 }
146 double p_red = pressure / (R_water * temperature * Rho_c);
147 deltaGuess = rhoguess / Rho_c;
148
149 delta = deltaGuess;
151
152 double delta_retn = m_phi.dfind(p_red, tau, deltaGuess);
153 double density_retn;
154 if (delta_retn > 0.0) {
155 delta = delta_retn;
156
157 // Dimensionalize the density before returning
158 density_retn = delta_retn * Rho_c;
159
160 } else {
161 density_retn = -1.0;
162 }
163
164 delta = deltaSave;
166 return density_retn;
167}
168
170{
171 return delta * Rho_c;
172}
173
175{
176 return T_c / tau;
177}
178
179double WaterPropsIAPWS::psat_est(double temperature) const
180{
181 // Formula and constants from: "NBS/NRC Steam Tables: Thermodynamic and
182 // Transport Properties and Computer Programs for Vapor and Liquid States of
183 // Water in SI Units". L. Haar, J. S. Gallagher, G. S. Kell. Hemisphere
184 // Publishing. 1984.
185 static const double A[8] = {
186 -7.8889166E0,
187 2.5514255E0,
188 -6.716169E0,
189 33.2239495E0,
190 -105.38479E0,
191 174.35319E0,
192 -148.39348E0,
193 48.631602E0
194 };
195 double ps;
196 if (temperature < 314.) {
197 double pl = 6.3573118E0 - 8858.843E0 / temperature
198 + 607.56335E0 * pow(temperature, -0.6);
199 ps = 0.1 * exp(pl);
200 } else {
201 double v = temperature / 647.25;
202 double w = fabs(1.0-v);
203 double b = 0.0;
204 for (int i = 0; i < 8; i++) {
205 double z = i + 1;
206 b += A[i] * pow(w, ((z+1.0)/2.0));
207 }
208 double q = b / v;
209 ps = 22.093*exp(q);
210 }
211
212 // Original correlation was in cgs. Convert to mks
213 ps *= 1.0E6;
214 return ps;
215}
216
218{
219 double dpdrho_val = dpdrho();
220 double dens = delta * Rho_c;
221 return 1.0 / (dens * dpdrho_val);
222}
223
225{
226 double retn = m_phi.dimdpdrho(tau, delta);
227 double temperature = T_c/tau;
228 return retn * R_water * temperature;
229}
230
232{
233 return m_phi.dimdpdT(tau, delta);
234}
235
237{
238 double kappa = isothermalCompressibility();
239 double beta = coeffPresExp();
240 double dens = delta * Rho_c;
241 return kappa * dens * R_water * beta;
242}
243
244void WaterPropsIAPWS::corr(double temperature, double pressure,
245 double& densLiq, double& densGas, double& delGRT)
246{
247 densLiq = density(temperature, pressure, WATER_LIQUID, densLiq);
248 if (densLiq <= 0.0) {
249 throw CanteraError("WaterPropsIAPWS::corr",
250 "Error occurred trying to find liquid density at (T,P) = {} {}",
252 }
253 setState_TD(temperature, densLiq);
254 double gibbsLiqRT = m_phi.gibbs_RT();
255
256 densGas = density(temperature, pressure, WATER_GAS, densGas);
257 if (densGas <= 0.0) {
258 throw CanteraError("WaterPropsIAPWS::corr",
259 "Error occurred trying to find gas density at (T,P) = {} {}",
261 }
262 setState_TD(temperature, densGas);
263 double gibbsGasRT = m_phi.gibbs_RT();
264
265 delGRT = gibbsLiqRT - gibbsGasRT;
266}
267
268double WaterPropsIAPWS::psat(double temperature, int waterState)
269{
270 double densLiq = -1.0, densGas = -1.0, delGRT = 0.0;
271 if (temperature >= T_c) {
272 densGas = density(temperature, P_c, WATER_SUPERCRIT);
273 setState_TD(temperature, densGas);
274 return P_c;
275 }
276 double p = psat_est(temperature);
277 for (int i = 0; i < 30; i++) {
278 corr(temperature, p, densLiq, densGas, delGRT);
279 double delV = 1.0/densLiq - 1.0/densGas;
280 p -= delGRT * R_water * temperature / delV;
281 if (delGRT < 1.0E-8) {
282 break;
283 }
284 }
285 // Put the fluid in the desired end condition
286 if (waterState == WATER_LIQUID) {
287 setState_TD(temperature, densLiq);
288 } else if (waterState == WATER_GAS) {
289 setState_TD(temperature, densGas);
290 } else {
291 throw CanteraError("WaterPropsIAPWS::psat",
292 "unknown water state input: {}", waterState);
293 }
294 return p;
295}
296
297int WaterPropsIAPWS::phaseState(bool checkState) const
298{
299 if (checkState) {
300 if (tau <= 1.0) {
301 iState = WATER_SUPERCRIT;
302 } else {
303 double T = T_c / tau;
304 double rho = delta * Rho_c;
305 double rhoMidAtm = 0.5 * (OneAtm / (R_water * 373.15) + 1.0E3);
306 double rhoMid = Rho_c + (T - T_c) * (Rho_c - rhoMidAtm) / (T_c - 373.15);
307 int iStateGuess = WATER_LIQUID;
308 if (rho < rhoMid) {
309 iStateGuess = WATER_GAS;
310 }
311 double kappa = isothermalCompressibility();
312 if (kappa >= 0.0) {
313 iState = iStateGuess;
314 } else {
315 // When we are here we are between the spinodal curves
316 double rhoDel = rho * 1.000001;
317 double deltaSave = delta;
318 double deltaDel = rhoDel / Rho_c;
319 delta = deltaDel;
320 m_phi.tdpolycalc(tau, deltaDel);
321
322 double kappaDel = isothermalCompressibility();
323 double d2rhodp2 = (rhoDel * kappaDel - rho * kappa) / (rhoDel - rho);
324 if (d2rhodp2 > 0.0) {
325 iState = WATER_UNSTABLELIQUID;
326 } else {
327 iState = WATER_UNSTABLEGAS;
328 }
329 delta = deltaSave;
331 }
332 }
333 }
334 return iState;
335}
336
338{
339 warn_deprecated("WaterPropsIAPWS::densSpinodalWater",
340 "To be removed after Cantera 3.1.");
341 double temperature = T_c/tau;
342 double delta_save = delta;
343 // return the critical density if we are above or even just a little below
344 // the critical temperature. We just don't want to worry about the critical
345 // point at this juncture.
346 if (temperature >= T_c - 0.001) {
347 return Rho_c;
348 }
349 double p = psat_est(temperature);
350 double rho_low = 0.0;
351 double rho_high = 1000;
352 double densSatLiq = density_const(p, WATER_LIQUID);
353 double dens_old = densSatLiq;
354 delta = dens_old / Rho_c;
356 double dpdrho_old = dpdrho();
357 if (dpdrho_old > 0.0) {
358 rho_high = std::min(dens_old, rho_high);
359 } else {
360 rho_low = std::max(rho_low, dens_old);
361 }
362 double dens_new = densSatLiq* (1.0001);
363 delta = dens_new / Rho_c;
365 double dpdrho_new = dpdrho();
366 if (dpdrho_new > 0.0) {
367 rho_high = std::min(dens_new, rho_high);
368 } else {
369 rho_low = std::max(rho_low, dens_new);
370 }
371 bool conv = false;
372
373 for (int it = 0; it < 50; it++) {
374 double slope = (dpdrho_new - dpdrho_old)/(dens_new - dens_old);
375 if (slope >= 0.0) {
376 slope = std::max(slope, dpdrho_new *5.0/ dens_new);
377 } else {
378 slope = -dpdrho_new;
379 // shouldn't be here for liquid spinodal
380 }
381 double delta_rho = - dpdrho_new / slope;
382 if (delta_rho > 0.0) {
383 delta_rho = std::min(delta_rho, dens_new * 0.1);
384 } else {
385 delta_rho = std::max(delta_rho, - dens_new * 0.1);
386 }
387 double dens_est = dens_new + delta_rho;
388 if (dens_est < rho_low) {
389 dens_est = 0.5 * (rho_low + dens_new);
390 }
391 if (dens_est > rho_high) {
392 dens_est = 0.5 * (rho_high + dens_new);
393 }
394
395 dens_old = dens_new;
396 dpdrho_old = dpdrho_new;
397 dens_new = dens_est;
398
399 delta = dens_new / Rho_c;
401 dpdrho_new = dpdrho();
402 if (dpdrho_new > 0.0) {
403 rho_high = std::min(dens_new, rho_high);
404 } else if (dpdrho_new < 0.0) {
405 rho_low = std::max(rho_low, dens_new);
406 } else {
407 conv = true;
408 break;
409 }
410
411 if (fabs(dpdrho_new) < 1.0E-5) {
412 conv = true;
413 break;
414 }
415 }
416
417 if (!conv) {
418 throw CanteraError("WaterPropsIAPWS::densSpinodalWater",
419 "convergence failure");
420 }
421 // Restore the original delta
422 delta = delta_save;
424 return dens_new;
425}
426
428{
429 warn_deprecated("WaterPropsIAPWS::densSpinodalSteam",
430 "To be removed after Cantera 3.1.");
431 double temperature = T_c/tau;
432 double delta_save = delta;
433 // return the critical density if we are above or even just a little below
434 // the critical temperature. We just don't want to worry about the critical
435 // point at this juncture.
436 if (temperature >= T_c - 0.001) {
437 return Rho_c;
438 }
439 double p = psat_est(temperature);
440 double rho_low = 0.0;
441 double rho_high = 1000;
442 double densSatGas = density_const(p, WATER_GAS);
443 double dens_old = densSatGas;
444 delta = dens_old / Rho_c;
446 double dpdrho_old = dpdrho();
447 if (dpdrho_old < 0.0) {
448 rho_high = std::min(dens_old, rho_high);
449 } else {
450 rho_low = std::max(rho_low, dens_old);
451 }
452 double dens_new = densSatGas * (0.99);
453 delta = dens_new / Rho_c;
455 double dpdrho_new = dpdrho();
456 if (dpdrho_new < 0.0) {
457 rho_high = std::min(dens_new, rho_high);
458 } else {
459 rho_low = std::max(rho_low, dens_new);
460 }
461 bool conv = false;
462 for (int it = 0; it < 50; it++) {
463 double slope = (dpdrho_new - dpdrho_old)/(dens_new - dens_old);
464 if (slope >= 0.0) {
465 slope = dpdrho_new;
466 // shouldn't be here for gas spinodal
467 } else {
468 slope = std::min(slope, dpdrho_new *5.0 / dens_new);
469
470 }
471 double delta_rho = - dpdrho_new / slope;
472 if (delta_rho > 0.0) {
473 delta_rho = std::min(delta_rho, dens_new * 0.1);
474 } else {
475 delta_rho = std::max(delta_rho, - dens_new * 0.1);
476 }
477 double dens_est = dens_new + delta_rho;
478 if (dens_est < rho_low) {
479 dens_est = 0.5 * (rho_low + dens_new);
480 }
481 if (dens_est > rho_high) {
482 dens_est = 0.5 * (rho_high + dens_new);
483 }
484
485 dens_old = dens_new;
486 dpdrho_old = dpdrho_new;
487 dens_new = dens_est;
488 delta = dens_new / Rho_c;
490 dpdrho_new = dpdrho();
491 if (dpdrho_new < 0.0) {
492 rho_high = std::min(dens_new, rho_high);
493 } else if (dpdrho_new > 0.0) {
494 rho_low = std::max(rho_low, dens_new);
495 } else {
496 conv = true;
497 break;
498 }
499
500 if (fabs(dpdrho_new) < 1.0E-5) {
501 conv = true;
502 break;
503 }
504 }
505
506 if (!conv) {
507 throw CanteraError("WaterPropsIAPWS::densSpinodalSteam",
508 "convergence failure");
509 }
510 // Restore the original delta
511 delta = delta_save;
513 return dens_new;
514}
515
516void WaterPropsIAPWS::setState_TD(double temperature, double rho)
517{
518 calcDim(temperature, rho);
520}
521
523{
524 return m_phi.gibbs_RT() * R_water * T_c / tau;
525}
526
528{
529 return m_phi.enthalpy_RT() * R_water * T_c / tau;
530}
531
533{
534 return m_phi.intEnergy_RT() * R_water * T_c / tau;
535}
536
538{
539 return m_phi.entropy_R() * R_water;
540}
541
543{
544 return m_phi.cv_R() * R_water;
545}
546
548{
549 return m_phi.cp_R() * R_water;
550}
551
552}
Headers for a class for calculating the equation of state of water from the IAPWS 1995 Formulation ba...
Base class for exceptions thrown by Cantera classes.
double coeffThermExp() const
Returns the coefficient of thermal expansion.
double densSpinodalSteam() const
Return the value of the density at the water spinodal point (on the gas side) for the current tempera...
double density() const
Returns the density (kg m-3)
void corr(double temperature, double pressure, double &densLiq, double &densGas, double &delGRT)
Utility routine in the calculation of the saturation pressure.
double pressure() const
Calculates the pressure (Pascals), given the current value of the temperature and density.
double gibbs_mass() const
Get the Gibbs free energy (J/kg) at the current temperature and density.
double isothermalCompressibility() const
Returns the coefficient of isothermal compressibility for the state of the object.
double psat(double temperature, int waterState=WATER_LIQUID)
This function returns the saturation pressure given the temperature as an input parameter,...
double temperature() const
Returns the temperature (Kelvin)
double cv_mass() const
Get the constant volume heat capacity (J/kg/K) at the current temperature and density.
double entropy_mass() const
Get the entropy (J/kg/K) at the current temperature and density.
double densSpinodalWater() const
Return the value of the density at the water spinodal point (on the liquid side) for the current temp...
double delta
Dimensionless density, delta = rho / rho_c.
double cp_mass() const
Get the constant pressure heat capacity (J/kg/K) at the current temperature and density.
double intEnergy_mass() const
Get the internal energy (J/kg) at the current temperature and density.
double coeffPresExp() const
Returns the isochoric pressure derivative wrt temperature.
int iState
Current state of the system.
void setState_TD(double temperature, double rho)
Set the internal state of the object wrt temperature and density.
double tau
Dimensionless temperature, tau = T_C / T.
WaterPropsIAPWSphi m_phi
pointer to the underlying object that does the calculations.
double density_const(double pressure, int phase=-1, double rhoguess=-1.0) const
Calculates the density given the temperature and the pressure, and a guess at the density,...
void calcDim(double temperature, double rho)
Calculate the dimensionless temp and rho and store internally.
int phaseState(bool checkState=false) const
Returns the Phase State flag for the current state of the object.
double dpdrho() const
Returns the value of dp / drho at constant T for the state of the object.
double psat_est(double temperature) const
This function returns an estimated value for the saturation pressure.
double enthalpy_mass() const
Get the enthalpy (J/kg) at the current temperature and density.
double gibbs_RT() const
Calculate the dimensionless Gibbs free energy.
double cv_R() const
Calculate the dimensionless constant volume heat capacity, Cv/R.
double intEnergy_RT() const
Calculate the dimensionless internal energy, u/RT.
double enthalpy_RT() const
Calculate the dimensionless enthalpy, h/RT.
double entropy_R() const
Calculate the dimensionless entropy, s/R.
double pressureM_rhoRT(double tau, double delta)
Calculate the dimensionless pressure at tau and delta;.
double dimdpdrho(double tau, double delta)
Dimensionless derivative of p wrt rho at constant T.
double dfind(double p_red, double tau, double deltaGuess)
This function computes the reduced density, given the reduced pressure and the reduced temperature,...
double dimdpdT(double tau, double delta)
Dimensionless derivative of p wrt T at constant rho.
double cp_R() const
Calculate the dimensionless constant pressure heat capacity, Cv/R.
void tdpolycalc(double tau, double delta)
Calculates internal polynomials in tau and delta.
Definitions for the classes that are thrown when Cantera experiences an error condition (also contain...
This file contains definitions for utility functions and text for modules, inputfiles and logging,...
const double OneAtm
One atmosphere [Pa].
Definition ct_defs.h:96
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const double Rho_c
Value of the Density at the critical point (kg m-3)
const double T_c
Critical Temperature value (kelvin)
static const double P_c
Critical Pressure (Pascals)
void warn_deprecated(const string &source, const AnyBase &node, const string &message)
A deprecation warning for syntax in an input file.
Definition AnyMap.cpp:1997
Contains declarations for string manipulation functions within Cantera.