Cantera  2.0
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  * Copyright (2006) Sandia Corporation. Under the terms of
9  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
10  * U.S. Government retains certain rights in this software.
11  */
15 #include <cmath>
16 #include <cstdio>
17 #include <cstdlib>
18 
19 namespace Cantera
20 {
21 /*
22  * Critical Point values of water in mks units
23  */
24 //! Critical Temperature value (kelvin)
25 const doublereal T_c = 647.096;
26 //! Critical Pressure (Pascals)
27 static const doublereal P_c = 22.064E6;
28 //! Value of the Density at the critical point (kg m-3)
29 const doublereal Rho_c = 322.;
30 //! Molecular Weight of water that is consistent with the paper (kg kmol-1)
31 static const doublereal M_water = 18.015268;
32 
33 //! Gas constant that is quoted in the paper
34 /*
35  * Note, this is the Rgas value quoted in the paper. For consistency
36  * we have to use that value and not the updated value
37  *
38  * The Ratio of R/M = 0.46151805 kJ kg-1 K-1 , which is Eqn. (6.3) in the paper.
39  */
40 static const doublereal Rgas = 8.314371E3; // Joules kmol-1 K-1
41 
42 // Base constructor
44  m_phi(0),
45  tau(-1.0),
46  delta(-1.0),
47  iState(-30000)
48 {
49  m_phi = new WaterPropsIAPWSphi();
50 }
51 
52 // Copy constructor
53 /*
54  * @param b Object to be copied
55  */
57  m_phi(0),
58  tau(b.tau),
59  delta(b.delta),
60  iState(b.iState)
61 {
62  m_phi = new WaterPropsIAPWSphi();
64 }
65 
66 // assignment constructor
67 /*
68  * @param right Object to be copied
69  */
71 {
72  if (this == &b) {
73  return *this;
74  }
75  tau = b.tau;
76  delta = b.delta;
77  iState = b.iState;
79  return *this;
80 }
81 
82 // destructor
84 {
85  delete(m_phi);
86  m_phi = 0;
87 }
88 
89 /*
90  * Calculate the dimensionless temp and rho and store internally.
91  *
92  * @param temperature input temperature (kelvin)
93  * @param rho density in kg m-3
94  *
95  * this is a private function
96  */
97 void WaterPropsIAPWS::calcDim(doublereal temperature, doublereal rho)
98 {
99  tau = T_c / temperature;
100  delta = rho / Rho_c;
101  /*
102  * Determine the internal state
103  */
104  if (temperature > T_c) {
105  iState = WATER_SUPERCRIT;
106  } else {
107  if (delta < 1.0) {
108  iState = WATER_GAS;
109  } else {
110  iState = WATER_LIQUID;
111  }
112  }
113 }
114 
115 // Calculate the Helmholtz free energy in mks units of J kmol-1 K-1,
116 // using the last temperature and density
118 {
119  doublereal retn = m_phi->phi(tau, delta);
120  doublereal temperature = T_c/tau;
121  doublereal RT = Rgas * temperature;
122  return (retn * RT);
123 }
124 
125 /*
126  * Calculate the pressure (Pascals), using the
127  * current internally stored temperature and density
128  * Temperature: kelvin
129  * rho: density in kg m-3
130  */
131 doublereal WaterPropsIAPWS::pressure() const
132 {
133  doublereal retn = m_phi->pressureM_rhoRT(tau, delta);
134  doublereal rho = delta * Rho_c;
135  doublereal temperature = T_c / tau;
136  return (retn * rho * Rgas * temperature/M_water);
137 }
138 
139 /*
140  * Calculates the density given the temperature and the pressure,
141  * and a guess at the density. Note, below T_c, this is a
142  * multivalued function.
143  *
144  * parameters:
145  * temperature: Kelvin
146  * pressure : Pressure in Pascals (Newton/m**2)
147  * phase : guessed phase of water
148  * : -1: no guessed phase
149  * rhoguess : guessed density of the water
150  * : -1.0 no guessed density
151  *
152  * If a problem is encountered, a negative 1 is returned.
153  */
154 doublereal WaterPropsIAPWS::density(doublereal temperature, doublereal pressure,
155  int phase, doublereal rhoguess)
156 {
157 
158  doublereal deltaGuess = 0.0;
159  if (rhoguess == -1.0) {
160  if (phase != -1) {
161  if (temperature > T_c) {
162  rhoguess = pressure * M_water / (Rgas * temperature);
163  } else {
164  if (phase == WATER_GAS || phase == WATER_SUPERCRIT) {
165  rhoguess = pressure * M_water / (Rgas * temperature);
166  } else if (phase == WATER_LIQUID) {
167  /*
168  * Provide a guess about the liquid density that is
169  * relatively high -> convergence from above seems robust.
170  */
171  rhoguess = 1000.;
172  } else if (phase == WATER_UNSTABLELIQUID || phase == WATER_UNSTABLEGAS) {
173  throw Cantera::CanteraError("WaterPropsIAPWS::density",
174  "Unstable Branch finder is untested");
175  } else {
176  throw Cantera::CanteraError("WaterPropsIAPWS::density",
177  "unknown state: " + Cantera::int2str(phase));
178  }
179  }
180  } else {
181  /*
182  * Assume the Gas phase initial guess, if nothing is
183  * specified to the routine
184  */
185  rhoguess = pressure * M_water / (Rgas * temperature);
186  }
187 
188  }
189  doublereal p_red = pressure * M_water / (Rgas * temperature * Rho_c);
190  deltaGuess = rhoguess / Rho_c;
191  setState_TR(temperature, rhoguess);
192  doublereal delta_retn = m_phi->dfind(p_red, tau, deltaGuess);
193  doublereal density_retn;
194  if (delta_retn >0.0) {
195  delta = delta_retn;
196 
197  /*
198  * Dimensionalize the density before returning
199  */
200  density_retn = delta_retn * Rho_c;
201  /*
202  * Set the internal state -> this may be
203  * a duplication. However, let's just be sure.
204  */
205  setState_TR(temperature, density_retn);
206 
207 
208  } else {
209  density_retn = -1.0;
210  }
211  return density_retn;
212 }
213 
214 // Calculates the density given the temperature and the pressure,
215 // and a guess at the density, while not changing the internal state
216 /*
217  * Note, below T_c, this is a multivalued function.
218  *
219  * The #density() function calculates the density that is consistent with
220  * a particular value of the temperature and pressure. It may therefore be
221  * multivalued or potentially there may be no answer from this function. It therefore
222  * takes a phase guess and a density guess as optional parameters. If no guesses are
223  *
224  * supplied to density(), a gas phase guess is assumed. This may or may not be what
225  * is wanted. Therefore, density() should usually at least be supplied with a phase
226  * guess so that it may manufacture an appropriate density guess.
227  * #density() manufactures the initial density guess, nondimensionalizes everything,
228  * and then calls #WaterPropsIAPWSphi::dfind(), which does the iterative calculation
229  * to find the density condition that matches the desired input pressure.
230  *
231  * @param pressure : Pressure in Pascals (Newton/m**2)
232  * @param phase : guessed phase of water
233  * : -1: no guessed phase
234  * @param rhoguess : guessed density of the water
235  * : -1.0 no guessed density
236  * @return
237  * Returns the density. If an error is encountered in the calculation
238  * the value of -1.0 is returned.
239  */
240 doublereal WaterPropsIAPWS::density_const(doublereal pressure,
241  int phase, doublereal rhoguess) const
242 {
243  doublereal temperature = T_c / tau;
244  doublereal deltaGuess = 0.0;
245  doublereal deltaSave = delta;
246  if (rhoguess == -1.0) {
247  if (phase != -1) {
248  if (temperature > T_c) {
249  rhoguess = pressure * M_water / (Rgas * temperature);
250  } else {
251  if (phase == WATER_GAS || phase == WATER_SUPERCRIT) {
252  rhoguess = pressure * M_water / (Rgas * temperature);
253  } else if (phase == WATER_LIQUID) {
254  /*
255  * Provide a guess about the liquid density that is
256  * relatively high -> convergence from above seems robust.
257  */
258  rhoguess = 1000.;
259  } else if (phase == WATER_UNSTABLELIQUID || phase == WATER_UNSTABLEGAS) {
260  throw Cantera::CanteraError("WaterPropsIAPWS::density",
261  "Unstable Branch finder is untested");
262  } else {
263  throw Cantera::CanteraError("WaterPropsIAPWS::density",
264  "unknown state: " + Cantera::int2str(phase));
265  }
266  }
267  } else {
268  /*
269  * Assume the Gas phase initial guess, if nothing is
270  * specified to the routine
271  */
272  rhoguess = pressure * M_water / (Rgas * temperature);
273  }
274 
275  }
276  doublereal p_red = pressure * M_water / (Rgas * temperature * Rho_c);
277  deltaGuess = rhoguess / Rho_c;
278 
279  delta = deltaGuess;
281  // setState_TR(temperature, rhoguess);
282 
283  doublereal delta_retn = m_phi->dfind(p_red, tau, deltaGuess);
284  doublereal density_retn;
285  if (delta_retn > 0.0) {
286  delta = delta_retn;
287 
288  /*
289  * Dimensionalize the density before returning
290  */
291  density_retn = delta_retn * Rho_c;
292 
293  } else {
294  density_retn = -1.0;
295  }
296 
297  delta = deltaSave;
299  return density_retn;
300 }
301 
302 // Returns the density (kg m-3)
303 /*
304  * The density is an independent variable in the underlying equation of state
305  *
306  * @return Returns the density (kg m-3)
307  */
308 doublereal WaterPropsIAPWS::density() const
309 {
310  return (delta * Rho_c);
311 }
312 
313 // Returns the temperature (Kelvin)
314 /*
315  * @return Returns the internally stored temperature
316  */
318 {
319  return (T_c / tau);
320 }
321 
322 /*
323  * psat_est provides a rough estimate of the saturation
324  * pressure given the temperature. This is used as an initial
325  * guess for refining the pressure.
326  *
327  * Input
328  * temperature (kelvin)
329  *
330  * return:
331  * psat (Pascals)
332  */
333 doublereal WaterPropsIAPWS::psat_est(doublereal temperature) const
334 {
335 
336  static const doublereal A[8] = {
337  -7.8889166E0,
338  2.5514255E0,
339  -6.716169E0,
340  33.2239495E0,
341  -105.38479E0,
342  174.35319E0,
343  -148.39348E0,
344  48.631602E0
345  };
346  doublereal ps;
347  if (temperature < 314.) {
348  doublereal pl = 6.3573118E0 - 8858.843E0 / temperature
349  + 607.56335E0 * pow(temperature, -0.6);
350  ps = 0.1 * exp(pl);
351  } else {
352  doublereal v = temperature / 647.25;
353  doublereal w = fabs(1.0-v);
354  doublereal b = 0.0;
355  for (int i = 0; i < 8; i++) {
356  doublereal z = i + 1;
357  b += A[i] * pow(w, ((z+1.0)/2.0));
358  }
359  doublereal q = b / v;
360  ps = 22.093*exp(q);
361  }
362  /*
363  * Original correlation was in cgs. Convert to mks
364  */
365  ps *= 1.0E6;
366  return ps;
367 }
368 
369 /*
370  * Returns the coefficient of isothermal compressibility
371  * of temperature and pressure.
372  * kappa = - d (ln V) / dP at constant T.
373  */
375 {
376  doublereal dpdrho_val = dpdrho();
377  doublereal dens = delta * Rho_c;
378  return (1.0 / (dens * dpdrho_val));
379 }
380 
381 // Returns the value of dp / drho at constant T at the current
382 // state of the object
383 /*
384  * units - Joules / kg
385  *
386  * @return returns dpdrho
387  */
388 doublereal WaterPropsIAPWS::dpdrho() const
389 {
390  doublereal retn = m_phi->dimdpdrho(tau, delta);
391  doublereal temperature = T_c/tau;
392  doublereal val = retn * Rgas * temperature / M_water;
393  return val;
394 }
395 
396 // Returns the isochoric pressure derivative wrt temperature
397 /*
398  * beta = M / (rho * Rgas) (d (pressure) / dT) at constant rho
399  *
400  * Note for ideal gases this is equal to one.
401  *
402  * beta = delta (phi0_d() + phiR_d())
403  * - tau delta (phi0_dt() + phiR_dt())
404  */
406 {
407  doublereal retn = m_phi->dimdpdT(tau, delta);
408  return (retn);
409 }
410 
411 // Returns the coefficient of thermal expansion.
412 /*
413  * alpha = d (ln V) / dT at constant P.
414  *
415  * @return Returns the coefficient of thermal expansion
416  */
418 {
419  doublereal kappa = isothermalCompressibility();
420  doublereal beta = coeffPresExp();
421  doublereal dens = delta * Rho_c;
422  return (kappa * dens * Rgas * beta / M_water);
423 }
424 
425 // Calculate the Gibbs free energy in mks units of J kmol-1 K-1.
426 // using the last temperature and density
427 doublereal WaterPropsIAPWS::Gibbs() const
428 {
429  doublereal gRT = m_phi->gibbs_RT();
430  doublereal temperature = T_c/tau;
431  return (gRT * Rgas * temperature);
432 }
433 
434 
435 // Utility routine in the calculation of the saturation pressure
436 /*
437  * Private routine
438  *
439  * Calculate the Gibbs free energy in mks units of
440  * J kmol-1 K-1.
441  *
442  * @param temperature temperature (kelvin)
443  * @param pressure pressure (Pascal)
444  * @param densLiq Output density of liquid
445  * @param densGas output Density of gas
446  * @param delGRT output delGRT
447  */
449 corr(doublereal temperature, doublereal pressure, doublereal& densLiq,
450  doublereal& densGas, doublereal& delGRT)
451 {
452 
453  densLiq = density(temperature, pressure, WATER_LIQUID, densLiq);
454  if (densLiq <= 0.0) {
455  throw Cantera::CanteraError("WaterPropsIAPWS::corr",
456  "Error occurred trying to find liquid density at (T,P) = "
457  + Cantera::fp2str(temperature) + " " + Cantera::fp2str(pressure));
458  }
459  setState_TR(temperature, densLiq);
460  doublereal gibbsLiqRT = m_phi->gibbs_RT();
461 
462  densGas = density(temperature, pressure, WATER_GAS, densGas);
463  if (densGas <= 0.0) {
464  throw Cantera::CanteraError("WaterPropsIAPWS::corr",
465  "Error occurred trying to find gas density at (T,P) = "
466  + Cantera::fp2str(temperature) + " " + Cantera::fp2str(pressure));
467  }
468  setState_TR(temperature, densGas);
469  doublereal gibbsGasRT = m_phi->gibbs_RT();
470 
471  delGRT = gibbsLiqRT - gibbsGasRT;
472 }
473 
474 // Utility routine in the calculation of the saturation pressure
475 /*
476  * Private routine
477  *
478  * @param temperature temperature (kelvin)
479  * @param pressure pressure (Pascal)
480  * @param densLiq Output density of liquid
481  * @param densGas output Density of gas
482  * @param pcorr output corrected pressure
483  */
485 corr1(doublereal temperature, doublereal pressure, doublereal& densLiq,
486  doublereal& densGas, doublereal& pcorr)
487 {
488 
489  densLiq = density(temperature, pressure, WATER_LIQUID, densLiq);
490  if (densLiq <= 0.0) {
491  throw Cantera::CanteraError("WaterPropsIAPWS::corr1",
492  "Error occurred trying to find liquid density at (T,P) = "
493  + Cantera::fp2str(temperature) + " " + Cantera::fp2str(pressure));
494  }
495  setState_TR(temperature, densLiq);
496  doublereal prL = m_phi->phiR();
497 
498  densGas = density(temperature, pressure, WATER_GAS, densGas);
499  if (densGas <= 0.0) {
500  throw Cantera::CanteraError("WaterPropsIAPWS::corr1",
501  "Error occurred trying to find gas density at (T,P) = "
502  + Cantera::fp2str(temperature) + " " + Cantera::fp2str(pressure));
503  }
504  setState_TR(temperature, densGas);
505  doublereal prG = m_phi->phiR();
506 
507  doublereal rhs = (prL - prG) + log(densLiq/densGas);
508  rhs /= (1.0/densGas - 1.0/densLiq);
509 
510  pcorr = rhs * Rgas * temperature / M_water;
511 }
512 
513 
514 // This function returns the saturation pressure given the
515 // temperature as an input parameter, and sets the internal state to the saturated
516 // conditions.
517 /*
518  * Note this function will return the saturation pressure, given the temperature.
519  * It will then set the state of the system to the saturation condition. The input
520  * parameter waterState is used to either specify the liquid state or the
521  * gas state at the desired temperature and saturated pressure.
522  *
523  * If the input temperature, T, is above T_c, this routine will set the internal
524  * state to T and the pressure to P_c. Then, return P_c.
525  *
526  * @param temperature input temperature (kelvin)
527  * @param waterState integer specifying the water state
528  *
529  * @return Returns the saturation pressure
530  * units = Pascal
531  */
532 doublereal WaterPropsIAPWS::psat(doublereal temperature, int waterState)
533 {
534  static int method = 1;
535  doublereal densLiq = -1.0, densGas = -1.0, delGRT = 0.0;
536  doublereal dp, pcorr;
537  if (temperature >= T_c) {
538  densGas = density(temperature, P_c, WATER_SUPERCRIT);
539  setState_TR(temperature, densGas);
540  return P_c;
541  }
542  doublereal p = psat_est(temperature);
543  for (int i = 0; i < 30; i++) {
544  if (method == 1) {
545  corr(temperature, p, densLiq, densGas, delGRT);
546  doublereal delV = M_water * (1.0/densLiq - 1.0/densGas);
547  dp = - delGRT * Rgas * temperature / delV;
548  } else {
549  corr1(temperature, p, densLiq, densGas, pcorr);
550  dp = pcorr - p;
551  }
552  p += dp;
553 
554  if ((method == 1) && delGRT < 1.0E-8) {
555  break;
556  } else {
557  if (fabs(dp/p) < 1.0E-9) {
558  break;
559  }
560  }
561  }
562  // Put the fluid in the desired end condition
563  if (waterState == WATER_LIQUID) {
564  setState_TR(temperature, densLiq);
565  } else if (waterState == WATER_GAS) {
566  setState_TR(temperature, densGas);
567  } else {
568  throw Cantera::CanteraError("WaterPropsIAPWS::psat",
569  "unknown water state input: " + Cantera::int2str(waterState));
570  }
571  return p;
572 }
573 
574 // Returns the Phase State flag for the current state of the object
575 /*
576  * @param checkState If true, this function does a complete check to see where
577  * in parameter space we are
578  *
579  * There are three values:
580  * WATER_GAS below the critical temperature but below the critical density
581  * WATER_LIQUID below the critical temperature but above the critical density
582  * WATER_SUPERCRIT above the critical temperature
583  */
584 int WaterPropsIAPWS::phaseState(bool checkState) const
585 {
586  if (checkState) {
587  if (tau <= 1.0) {
588  iState = WATER_SUPERCRIT;
589  } else {
590  doublereal T = T_c / tau;
591  doublereal rho = delta * Rho_c;
592  //doublereal psatTable = psat_est(T);
593  doublereal rhoMidAtm = 0.5 * (OneAtm * M_water / (Rgas * 373.15) + 1.0E3);
594  doublereal rhoMid = Rho_c + (T - T_c) * (Rho_c - rhoMidAtm) / (T_c - 373.15);
595  int iStateGuess = WATER_LIQUID;
596  if (rho < rhoMid) {
597  iStateGuess = WATER_GAS;
598  }
599  doublereal kappa = isothermalCompressibility();
600  if (kappa >= 0.0) {
601  iState = iStateGuess;
602  } else {
603  // When we are here we are between the spinodal curves
604  doublereal rhoDel = rho * 1.000001;
605 
606  //setState_TR(T, rhoDel);
607  doublereal deltaSave = delta;
608  doublereal deltaDel = rhoDel / Rho_c;
609  delta = deltaDel;
610  m_phi->tdpolycalc(tau, deltaDel);
611 
612  doublereal kappaDel = isothermalCompressibility();
613  doublereal d2rhodp2 = (rhoDel * kappaDel - rho * kappa) / (rhoDel - rho);
614  if (d2rhodp2 > 0.0) {
615  iState = WATER_UNSTABLELIQUID;
616  } else {
617  iState = WATER_UNSTABLEGAS;
618  }
619  //setState_TR(T, rho);
620  delta = deltaSave;
621 
623  }
624  }
625  }
626  return iState;
627 }
628 
629 // Return the value of the density at the water spinodal point (on the liquid side)
630 // for the current temperature.
631 /*
632  * @return returns the density with units of kg m-3
633  */
635 {
636  doublereal temperature = T_c/tau;
637  doublereal delta_save = delta;
638  // return the critical density if we are above or even just a little below
639  // the critical temperature. We just don't want to worry about the critical
640  // point at this juncture.
641  if (temperature >= T_c - 0.001) {
642  return Rho_c;
643  }
644  doublereal p = psat_est(temperature);
645  doublereal rho_low = 0.0;
646  doublereal rho_high = 1000;
647 
648  doublereal densSatLiq = density_const(p, WATER_LIQUID);
649  doublereal dens_old = densSatLiq;
650  delta = dens_old / Rho_c;
652  doublereal dpdrho_old = dpdrho();
653  if (dpdrho_old > 0.0) {
654  rho_high = std::min(dens_old, rho_high);
655  } else {
656  rho_low = std::max(rho_low, dens_old);
657  }
658  doublereal dens_new = densSatLiq* (1.0001);
659  delta = dens_new / Rho_c;
661  doublereal dpdrho_new = dpdrho();
662  if (dpdrho_new > 0.0) {
663  rho_high = std::min(dens_new, rho_high);
664  } else {
665  rho_low = std::max(rho_low, dens_new);
666  }
667  bool conv = false;
668 
669  for (int it = 0; it < 50; it++) {
670  doublereal slope = (dpdrho_new - dpdrho_old)/(dens_new - dens_old);
671  if (slope >= 0.0) {
672  slope = std::max(slope, dpdrho_new *5.0/ dens_new);
673  } else {
674  slope = -dpdrho_new;
675  //slope = MIN(slope, dpdrho_new *5.0 / dens_new);
676  // shouldn't be here for liquid spinodal
677  }
678  doublereal delta_rho = - dpdrho_new / slope;
679  if (delta_rho > 0.0) {
680  delta_rho = std::min(delta_rho, dens_new * 0.1);
681  } else {
682  delta_rho = std::max(delta_rho, - dens_new * 0.1);
683  }
684  doublereal dens_est = dens_new + delta_rho;
685  if (dens_est < rho_low) {
686  dens_est = 0.5 * (rho_low + dens_new);
687  }
688  if (dens_est > rho_high) {
689  dens_est = 0.5 * (rho_high + dens_new);
690  }
691 
692 
693  dens_old = dens_new;
694  dpdrho_old = dpdrho_new;
695  dens_new = dens_est;
696 
697  delta = dens_new / Rho_c;
699  dpdrho_new = dpdrho();
700  if (dpdrho_new > 0.0) {
701  rho_high = std::min(dens_new, rho_high);
702  } else if (dpdrho_new < 0.0) {
703  rho_low = std::max(rho_low, dens_new);
704  } else {
705  conv = true;
706  break;
707  }
708 
709  if (fabs(dpdrho_new) < 1.0E-5) {
710  conv = true;
711  break;
712  }
713  }
714 
715  if (!conv) {
716  throw Cantera::CanteraError(" WaterPropsIAPWS::densSpinodalWater()",
717  " convergence failure");
718  }
719  // Restore the original delta
720  delta = delta_save;
722 
723  return dens_new;
724 }
725 
726 // Return the value of the density at the water spinodal point (on the gas side)
727 // for the current temperature.
728 /*
729  * @return returns the density with units of kg m-3
730  */
732 {
733  doublereal temperature = T_c/tau;
734  doublereal delta_save = delta;
735  // return the critical density if we are above or even just a little below
736  // the critical temperature. We just don't want to worry about the critical
737  // point at this juncture.
738  if (temperature >= T_c - 0.001) {
739  return Rho_c;
740  }
741  doublereal p = psat_est(temperature);
742  doublereal rho_low = 0.0;
743  doublereal rho_high = 1000;
744 
745  doublereal densSatGas = density_const(p, WATER_GAS);
746  doublereal dens_old = densSatGas;
747  delta = dens_old / Rho_c;
749  doublereal dpdrho_old = dpdrho();
750  if (dpdrho_old < 0.0) {
751  rho_high = std::min(dens_old, rho_high);
752  } else {
753  rho_low = std::max(rho_low, dens_old);
754  }
755  doublereal dens_new = densSatGas * (0.99);
756  delta = dens_new / Rho_c;
758  doublereal dpdrho_new = dpdrho();
759  if (dpdrho_new < 0.0) {
760  rho_high = std::min(dens_new, rho_high);
761  } else {
762  rho_low = std::max(rho_low, dens_new);
763  }
764  bool conv = false;
765 
766  for (int it = 0; it < 50; it++) {
767  doublereal slope = (dpdrho_new - dpdrho_old)/(dens_new - dens_old);
768  if (slope >= 0.0) {
769  slope = dpdrho_new;
770  //slope = MAX(slope, dpdrho_new *5.0/ dens_new);
771  // shouldn't be here for gas spinodal
772  } else {
773  //slope = -dpdrho_new;
774  slope = std::min(slope, dpdrho_new *5.0 / dens_new);
775 
776  }
777  doublereal delta_rho = - dpdrho_new / slope;
778  if (delta_rho > 0.0) {
779  delta_rho = std::min(delta_rho, dens_new * 0.1);
780  } else {
781  delta_rho = std::max(delta_rho, - dens_new * 0.1);
782  }
783  doublereal dens_est = dens_new + delta_rho;
784  if (dens_est < rho_low) {
785  dens_est = 0.5 * (rho_low + dens_new);
786  }
787  if (dens_est > rho_high) {
788  dens_est = 0.5 * (rho_high + dens_new);
789  }
790 
791 
792  dens_old = dens_new;
793  dpdrho_old = dpdrho_new;
794  dens_new = dens_est;
795 
796  delta = dens_new / Rho_c;
798  dpdrho_new = dpdrho();
799  if (dpdrho_new < 0.0) {
800  rho_high = std::min(dens_new, rho_high);
801  } else if (dpdrho_new > 0.0) {
802  rho_low = std::max(rho_low, dens_new);
803  } else {
804  conv = true;
805  break;
806  }
807 
808  if (fabs(dpdrho_new) < 1.0E-5) {
809  conv = true;
810  break;
811  }
812  }
813 
814  if (!conv) {
815  throw Cantera::CanteraError(" WaterPropsIAPWS::densSpinodalSteam()",
816  " convergence failure");
817  }
818  // Restore the original delta
819  delta = delta_save;
821 
822  return dens_new;
823 }
824 
825 /*
826  * Sets the internal state of the object to the
827  * specified temperature and density.
828  */
829 void WaterPropsIAPWS::setState_TR(doublereal temperature, doublereal rho)
830 {
831  calcDim(temperature, rho);
833 }
834 
835 /*
836  * Calculate the enthalpy in mks units of
837  * J kmol-1 K-1.
838  */
839 doublereal WaterPropsIAPWS::enthalpy() const
840 {
841  doublereal temperature = T_c/tau;
842  doublereal hRT = m_phi->enthalpy_RT();
843  return (hRT * Rgas * temperature);
844 }
845 
846 /*
847  * Calculate the internal Energy in mks units of
848  * J kmol-1 K-1.
849  */
850 doublereal WaterPropsIAPWS::intEnergy() const
851 {
852  doublereal temperature = T_c / tau;
853  doublereal uRT = m_phi->intEnergy_RT();
854  return (uRT * Rgas * temperature);
855 }
856 
857 /*
858  * Calculate the enthalpy in mks units of356
859  * J kmol-1 K-1.
860  */
861 doublereal WaterPropsIAPWS::entropy() const
862 {
863  doublereal sR = m_phi->entropy_R();
864  return (sR * Rgas);
865 }
866 
867 /*
868  * Calculate heat capacity at constant volume
869  * J kmol-1 K-1.
870  */
871 doublereal WaterPropsIAPWS::cv() const
872 {
873  doublereal cvR = m_phi->cv_R();
874  return (cvR * Rgas);
875 }
876 
877 // Calculate the constant pressure heat capacity in mks units of J kmol-1 K-1
878 // at the last temperature and density
879 doublereal WaterPropsIAPWS::cp() const
880 {
881  doublereal cpR = m_phi->cp_R();
882  return (cpR * Rgas);
883 }
884 
885 // Calculate the molar volume (kmol m-3)
886 // at the last temperature and density
888 {
889  doublereal rho = delta * Rho_c;
890  return (M_water / rho);
891 }
892 
893 }