Cantera  2.3.0
WaterProps.cpp
Go to the documentation of this file.
1 /**
2  * @file WaterProps.cpp
3  */
4 
5 // This file is part of Cantera. See License.txt in the top-level directory or
6 // at http://www.cantera.org/license.txt for license and copyright information.
7 
9 #include "cantera/base/ctml.h"
12 
13 namespace Cantera
14 {
16  m_waterIAPWS(0),
17  m_own_sub(false)
18 {
19  // object owns its own water evaluator
21  m_own_sub = true;
22 }
23 
25  m_waterIAPWS(0),
26  m_own_sub(false)
27 {
28  if (wptr) {
29  // object in slave mode; it doesn't own its own water evaluator.
30  m_waterIAPWS = wptr->getWater();
31  m_own_sub = false;
32  } else {
34  m_own_sub = true;
35  }
36 }
37 
39  m_waterIAPWS(0),
40  m_own_sub(false)
41 {
42  if (waterIAPWS) {
43  m_waterIAPWS = waterIAPWS;
44  m_own_sub = false;
45  } else {
47  m_own_sub = true;
48  }
49 }
50 
52  m_waterIAPWS(0),
53  m_own_sub(false)
54 {
55  *this = b;
56 }
57 
58 WaterProps::~WaterProps()
59 {
60  if (m_own_sub) {
61  delete m_waterIAPWS;
62  }
63 }
64 
65 WaterProps& WaterProps::operator=(const WaterProps& b)
66 {
67  if (&b == this) {
68  return *this;
69  }
70 
71  if (m_own_sub) {
72  delete m_waterIAPWS;
73  }
74  if (b.m_own_sub) {
75  m_waterIAPWS = new WaterPropsIAPWS();
76  m_own_sub = true;
77  } else {
78  m_waterIAPWS = b.m_waterIAPWS;
79  m_own_sub = false;
80  }
81 
82  return *this;
83 }
84 
85 doublereal WaterProps::density_T(doublereal T, doublereal P, int ifunc)
86 {
87  static const doublereal Tc = T - 273.15;
88  static const doublereal U1 = 288.9414;
89  static const doublereal U2 = 508929.2;
90  static const doublereal U3 = 68.12963;
91  static const doublereal U4 = -3.9863;
92 
93  doublereal tmp1 = Tc + U1;
94  doublereal tmp4 = Tc + U4;
95  doublereal t4t4 = tmp4 * tmp4;
96  doublereal tmp3 = Tc + U3;
97  doublereal rho = 1000. * (1.0 - tmp1*t4t4/(U2 * tmp3));
98 
99  // Impose an ideal gas lower bound on rho. We need this to ensure positivity
100  // of rho, even though it is grossly unrepresentative.
101  doublereal rhomin = P / (GasConstant * T);
102  if (rho < rhomin) {
103  rho = rhomin;
104  if (ifunc == 1) {
105  return - rhomin / T;
106  } else if (ifunc == 3) {
107  return rhomin / P;
108  } else if (ifunc == 2) {
109  return 2.0 * rhomin / (T * T);
110  }
111  }
112 
113  if (ifunc == 1) {
114  doublereal drhodT = 1000./U2 * (
115  - tmp4 * tmp4 / (tmp3)
116  - tmp1 * 2 * tmp4 / (tmp3)
117  + tmp1 * t4t4 / (tmp3*tmp3)
118  );
119  return drhodT;
120  } else if (ifunc == 3) {
121  return 0.0;
122  } else if (ifunc == 2) {
123  doublereal t3t3 = tmp3 * tmp3;
124  doublereal d2rhodT2 = 1000./U2 *
125  ((-4.0*tmp4-2.0*tmp1)/tmp3 +
126  (2.0*t4t4 + 4.0*tmp1*tmp4)/t3t3
127  - 2.0*tmp1 * t4t4/(t3t3*tmp3));
128  return d2rhodT2;
129  }
130  return rho;
131 }
132 
133 doublereal WaterProps::relEpsilon(doublereal T, doublereal P_pascal,
134  int ifunc)
135 {
136  static const doublereal U1 = 3.4279E2;
137  static const doublereal U2 = -5.0866E-3;
138  static const doublereal U3 = 9.4690E-7;
139  static const doublereal U4 = -2.0525;
140  static const doublereal U5 = 3.1159E3;
141  static const doublereal U6 = -1.8289E2;
142  static const doublereal U7 = -8.0325E3;
143  static const doublereal U8 = 4.2142E6;
144  static const doublereal U9 = 2.1417;
145  doublereal T2 = T * T;
146 
147  doublereal eps1000 = U1 * exp(U2 * T + U3 * T2);
148  doublereal C = U4 + U5/(U6 + T);
149  doublereal B = U7 + U8/T + U9 * T;
150  doublereal Pbar = P_pascal * 1.0E-5;
151  doublereal tmpBpar = B + Pbar;
152  doublereal tmpB1000 = B + 1000.0;
153  doublereal ltmp = log(tmpBpar/tmpB1000);
154  doublereal epsRel = eps1000 + C * ltmp;
155 
156  if (ifunc == 1 || ifunc == 2) {
157  doublereal tmpC = U6 + T;
158  doublereal dCdT = - U5/(tmpC * tmpC);
159  doublereal dBdT = - U8/(T * T) + U9;
160  doublereal deps1000dT = eps1000 * (U2 + 2.0 * U3 * T);
161  doublereal dltmpdT = (dBdT/tmpBpar - dBdT/tmpB1000);
162  if (ifunc == 1) {
163  return deps1000dT + dCdT * ltmp + C * dltmpdT;
164  }
165  doublereal T3 = T2 * T;
166  doublereal d2CdT2 = - 2.0 * dCdT / tmpC;
167  doublereal d2BdT2 = 2.0 * U8 / (T3);
168  doublereal d2ltmpdT2 = (d2BdT2*(1.0/tmpBpar - 1.0/tmpB1000) +
169  dBdT*dBdT*(1.0/(tmpB1000*tmpB1000) - 1.0/(tmpBpar*tmpBpar)));
170  doublereal d2eps1000dT2 = (deps1000dT * (U2 + 2.0 * U3 * T) + eps1000 * (2.0 * U3));
171 
172  if (ifunc == 2) {
173  doublereal d2epsReldT2 = (d2eps1000dT2 + d2CdT2 * ltmp + 2.0 * dCdT * dltmpdT
174  + C * d2ltmpdT2);
175  return d2epsReldT2;
176  }
177  }
178  if (ifunc == 3) {
179  doublereal dltmpdP = 1.0E-5 / tmpBpar;
180  return C * dltmpdP;
181  }
182  return epsRel;
183 }
184 
185 doublereal WaterProps::ADebye(doublereal T, doublereal P_input, int ifunc)
186 {
187  doublereal psat = satPressure(T);
188  doublereal P;
189  if (psat > P_input) {
190  P = psat;
191  } else {
192  P = P_input;
193  }
194  doublereal epsRelWater = relEpsilon(T, P, 0);
195  doublereal epsilon = epsilon_0 * epsRelWater;
196  doublereal dw = density_IAPWS(T, P);
197  doublereal tmp = sqrt(2.0 * Avogadro * dw / 1000.);
198  doublereal tmp2 = ElectronCharge * ElectronCharge * Avogadro /
199  (epsilon * GasConstant * T);
200  doublereal tmp3 = tmp2 * sqrt(tmp2);
201  doublereal A_Debye = tmp * tmp3 / (8.0 * Pi);
202 
203  // dAdT = - 3/2 Ad/T + 1/2 Ad/dw d(dw)/dT - 3/2 Ad/eps d(eps)/dT
204  // dAdT = - 3/2 Ad/T - 1/2 Ad/Vw d(Vw)/dT - 3/2 Ad/eps d(eps)/dT
205  if (ifunc == 1 || ifunc == 2) {
206  doublereal dAdT = - 1.5 * A_Debye / T;
207 
208  doublereal depsRelWaterdT = relEpsilon(T, P, 1);
209  dAdT -= A_Debye * (1.5 * depsRelWaterdT / epsRelWater);
210 
211  // calculate d(lnV)/dT _constantP, i.e., the cte
212  doublereal cte = coeffThermalExp_IAPWS(T, P);
213  doublereal contrib2 = - A_Debye * (0.5 * cte);
214  dAdT += contrib2;
215 
216  if (ifunc == 1) {
217  return dAdT;
218  }
219 
220  if (ifunc == 2) {
221  // Get the second derivative of the dielectric constant wrt T
222  // -> we will take each of the terms in dAdT and differentiate
223  // it again.
224  doublereal d2AdT2 = 1.5 / T * (A_Debye/T - dAdT);
225  doublereal d2epsRelWaterdT2 = relEpsilon(T, P, 2);
226  d2AdT2 += 1.5 * (- dAdT * depsRelWaterdT / epsRelWater
227  - A_Debye / epsRelWater *
228  (d2epsRelWaterdT2 - depsRelWaterdT * depsRelWaterdT / epsRelWater));
229  doublereal deltaT = -0.1;
230  doublereal Tdel = T + deltaT;
231  doublereal cte_del = coeffThermalExp_IAPWS(Tdel, P);
232  doublereal dctedT = (cte_del - cte) / Tdel;
233  doublereal contrib3 = 0.5 * (-(dAdT * cte) -(A_Debye * dctedT));
234  d2AdT2 += contrib3;
235  return d2AdT2;
236  }
237  }
238 
239  // A_Debye = (1/(8 Pi)) sqrt(2 Na dw / 1000)
240  // (e e/(epsilon R T))^3/2
241  //
242  // dAdP = + 1/2 Ad/dw d(dw)/dP - 3/2 Ad/eps d(eps)/dP
243  // dAdP = - 1/2 Ad/Vw d(Vw)/dP - 3/2 Ad/eps d(eps)/dP
244  // dAdP = + 1/2 Ad * kappa - 3/2 Ad/eps d(eps)/dP
245  //
246  // where kappa = - 1/Vw d(Vw)/dP_T (isothermal compressibility)
247  if (ifunc == 3) {
248  doublereal dAdP = 0.0;
249  doublereal depsRelWaterdP = relEpsilon(T, P, 3);
250  dAdP -= A_Debye * (1.5 * depsRelWaterdP / epsRelWater);
251  doublereal kappa = isothermalCompressibility_IAPWS(T,P);
252  dAdP += A_Debye * (0.5 * kappa);
253  return dAdP;
254  }
255  return A_Debye;
256 }
257 
258 doublereal WaterProps::satPressure(doublereal T)
259 {
260  return m_waterIAPWS->psat(T);
261 }
262 
263 doublereal WaterProps::density_IAPWS(doublereal temp, doublereal press)
264 {
265  return m_waterIAPWS->density(temp, press, WATER_LIQUID);
266 }
267 
268 doublereal WaterProps::density_IAPWS() const
269 {
270  return m_waterIAPWS->density();
271 }
272 
273 doublereal WaterProps::coeffThermalExp_IAPWS(doublereal temp, doublereal press)
274 {
275  doublereal dens = m_waterIAPWS->density(temp, press, WATER_LIQUID);
276  if (dens < 0.0) {
277  throw CanteraError("WaterProps::coeffThermalExp_IAPWS",
278  "Unable to solve for density at T = {} and P = {}", temp, press);
279  }
280  return m_waterIAPWS->coeffThermExp();
281 }
282 
283 doublereal WaterProps::isothermalCompressibility_IAPWS(doublereal temp, doublereal press)
284 {
285  doublereal dens = m_waterIAPWS->density(temp, press, WATER_LIQUID);
286  if (dens < 0.0) {
287  throw CanteraError("WaterProps::isothermalCompressibility_IAPWS",
288  "Unable to solve for density at T = {} and P = {}", temp, press);
289  }
291 }
292 
293 static const doublereal H[4] = {1., 0.978197, 0.579829, -0.202354};
294 
295 static const doublereal Hij[6][7] = {
296  { 0.5132047, 0.2151778, -0.2818107, 0.1778064, -0.04176610, 0., 0.},
297  { 0.3205656, 0.7317883, -1.070786 , 0.4605040, 0., -0.01578386, 0.},
298  { 0., 1.241044 , -1.263184 , 0.2340379, 0., 0., 0.},
299  { 0., 1.476783 , 0., -0.4924179, 0.1600435, 0., -0.003629481},
300  {-0.7782567, 0.0 , 0., 0. , 0., 0., 0.},
301  { 0.1885447, 0.0 , 0., 0. , 0., 0., 0.},
302 };
303 
304 static const doublereal rhoStar = 317.763; // kg / m3
305 static const doublereal presStar = 22.115E6; // Pa
306 
307 doublereal WaterProps::viscosityWater() const
308 {
309  static const doublereal TStar = 647.27; // Kelvin
310  static const doublereal muStar = 55.071E-6; //Pa s
311  doublereal temp = m_waterIAPWS->temperature();
312  doublereal dens = m_waterIAPWS->density();
313 
314  doublereal rhobar = dens/rhoStar;
315  doublereal tbar = temp / TStar;
316  doublereal tbar2 = tbar * tbar;
317  doublereal tbar3 = tbar2 * tbar;
318  doublereal mu0bar = std::sqrt(tbar) / (H[0] + H[1]/tbar + H[2]/tbar2 + H[3]/tbar3);
319 
320  doublereal tfac1 = 1.0 / tbar - 1.0;
321  doublereal tfac2 = tfac1 * tfac1;
322  doublereal tfac3 = tfac2 * tfac1;
323  doublereal tfac4 = tfac3 * tfac1;
324  doublereal tfac5 = tfac4 * tfac1;
325 
326  doublereal rfac1 = rhobar - 1.0;
327  doublereal rfac2 = rfac1 * rfac1;
328  doublereal rfac3 = rfac2 * rfac1;
329  doublereal rfac4 = rfac3 * rfac1;
330  doublereal rfac5 = rfac4 * rfac1;
331  doublereal rfac6 = rfac5 * rfac1;
332 
333  doublereal sum = (Hij[0][0] + Hij[1][0]*tfac1 + Hij[4][0]*tfac4 + Hij[5][0]*tfac5 +
334  Hij[0][1]*rfac1 + Hij[1][1]*tfac1*rfac1 + Hij[2][1]*tfac2*rfac1 + Hij[3][1]*tfac3*rfac1 +
335  Hij[0][2]*rfac2 + Hij[1][2]*tfac1*rfac2 + Hij[2][2]*tfac2*rfac2 +
336  Hij[0][3]*rfac3 + Hij[1][3]*tfac1*rfac3 + Hij[2][3]*tfac2*rfac3 + Hij[3][3]*tfac3*rfac3 +
337  Hij[0][4]*rfac4 + Hij[3][4]*tfac3*rfac4 +
338  Hij[1][5]*tfac1*rfac5 + Hij[3][6]*tfac3*rfac6
339  );
340  doublereal mu1bar = std::exp(rhobar * sum);
341 
342  // Apply the near-critical point corrections if necessary
343  doublereal mu2bar = 1.0;
344  if (tbar >= 0.9970 && tbar <= 1.0082 && rhobar >= 0.755 && rhobar <= 1.290) {
345  doublereal drhodp = 1.0 / m_waterIAPWS->dpdrho();
346  drhodp *= presStar / rhoStar;
347  doublereal xsi = rhobar * drhodp;
348  if (xsi >= 21.93) {
349  mu2bar = 0.922 * std::pow(xsi, 0.0263);
350  }
351  }
352 
353  doublereal mubar = mu0bar * mu1bar * mu2bar;
354  return mubar * muStar;
355 }
356 
358 {
359  static const doublereal Tstar = 647.27;
360  static const doublereal rhostar = 317.763;
361  static const doublereal lambdastar = 0.4945;
362  static const doublereal presstar = 22.115E6;
363  static const doublereal L[4] = {
364  1.0000,
365  6.978267,
366  2.599096,
367  -0.998254
368  };
369  static const doublereal Lji[6][5] = {
370  { 1.3293046, 1.7018363, 5.2246158, 8.7127675, -1.8525999},
371  {-0.40452437, -2.2156845, -10.124111, -9.5000611, 0.93404690},
372  { 0.24409490, 1.6511057, 4.9874687, 4.3786606, 0.0},
373  { 0.018660751, -0.76736002, -0.27297694, -0.91783782, 0.0},
374  {-0.12961068, 0.37283344, -0.43083393, 0.0, 0.0},
375  { 0.044809953, -0.11203160, 0.13333849, 0.0, 0.0},
376  };
377 
378  doublereal temp = m_waterIAPWS->temperature();
379  doublereal dens = m_waterIAPWS->density();
380 
381  doublereal rhobar = dens/rhostar;
382  doublereal tbar = temp / Tstar;
383  doublereal tbar2 = tbar * tbar;
384  doublereal tbar3 = tbar2 * tbar;
385  doublereal lambda0bar = sqrt(tbar) / (L[0] + L[1]/tbar + L[2]/tbar2 + L[3]/tbar3);
386 
387  doublereal tfac1 = 1.0 / tbar - 1.0;
388  doublereal tfac2 = tfac1 * tfac1;
389  doublereal tfac3 = tfac2 * tfac1;
390  doublereal tfac4 = tfac3 * tfac1;
391 
392  doublereal rfac1 = rhobar - 1.0;
393  doublereal rfac2 = rfac1 * rfac1;
394  doublereal rfac3 = rfac2 * rfac1;
395  doublereal rfac4 = rfac3 * rfac1;
396  doublereal rfac5 = rfac4 * rfac1;
397 
398  doublereal sum = (Lji[0][0] + Lji[0][1]*tfac1 + Lji[0][2]*tfac2 + Lji[0][3]*tfac3 + Lji[0][4]*tfac4 +
399  Lji[1][0]*rfac1 + Lji[1][1]*tfac1*rfac1 + Lji[1][2]*tfac2*rfac1 + Lji[1][3]*tfac3*rfac1 + Lji[1][4]*tfac4*rfac1 +
400  Lji[2][0]*rfac2 + Lji[2][1]*tfac1*rfac2 + Lji[2][2]*tfac2*rfac2 + Lji[2][3]*tfac3*rfac2 +
401  Lji[3][0]*rfac3 + Lji[3][1]*tfac1*rfac3 + Lji[3][2]*tfac2*rfac3 + Lji[3][3]*tfac3*rfac3 +
402  Lji[4][0]*rfac4 + Lji[4][1]*tfac1*rfac4 + Lji[4][2]*tfac2*rfac4 +
403  Lji[5][0]*rfac5 + Lji[5][1]*tfac1*rfac5 + Lji[5][2]*tfac2*rfac5
404  );
405  doublereal lambda1bar = exp(rhobar * sum);
406  doublereal mu0bar = std::sqrt(tbar) / (H[0] + H[1]/tbar + H[2]/tbar2 + H[3]/tbar3);
407  doublereal tfac5 = tfac4 * tfac1;
408  doublereal rfac6 = rfac5 * rfac1;
409 
410  sum = (Hij[0][0] + Hij[1][0]*tfac1 + Hij[4][0]*tfac4 + Hij[5][0]*tfac5 +
411  Hij[0][1]*rfac1 + Hij[1][1]*tfac1*rfac1 + Hij[2][1]*tfac2*rfac1 + Hij[3][1]*tfac3*rfac1 +
412  Hij[0][2]*rfac2 + Hij[1][2]*tfac1*rfac2 + Hij[2][2]*tfac2*rfac2 +
413  Hij[0][3]*rfac3 + Hij[1][3]*tfac1*rfac3 + Hij[2][3]*tfac2*rfac3 + Hij[3][3]*tfac3*rfac3 +
414  Hij[0][4]*rfac4 + Hij[3][4]*tfac3*rfac4 +
415  Hij[1][5]*tfac1*rfac5 + Hij[3][6]*tfac3*rfac6
416  );
417  doublereal mu1bar = std::exp(rhobar * sum);
418  doublereal t2r2 = tbar * tbar / (rhobar * rhobar);
419  doublereal drhodp = 1.0 / m_waterIAPWS->dpdrho();
420  drhodp *= presStar / rhoStar;
421  doublereal xsi = rhobar * drhodp;
422  doublereal xsipow = std::pow(xsi, 0.4678);
423  doublereal rho1 = rhobar - 1.;
424  doublereal rho2 = rho1 * rho1;
425  doublereal rho4 = rho2 * rho2;
426  doublereal temp2 = (tbar - 1.0) * (tbar - 1.0);
427 
428  // beta = M / (rho * Rgas) (d (pressure) / dT) at constant rho
429  //
430  // Note for ideal gases this is equal to one.
431  //
432  // beta = delta (phi0_d() + phiR_d())
433  // - tau delta (phi0_dt() + phiR_dt())
434  doublereal beta = m_waterIAPWS->coeffPresExp();
435  doublereal dpdT_const_rho = beta * GasConstant * dens / 18.015268;
436  dpdT_const_rho *= Tstar / presstar;
437  doublereal lambda2bar = 0.0013848 / (mu0bar * mu1bar) * t2r2 * dpdT_const_rho * dpdT_const_rho *
438  xsipow * sqrt(rhobar) * exp(-18.66*temp2 - rho4);
439  return (lambda0bar * lambda1bar + lambda2bar) * lambdastar;
440 }
441 
442 }
doublereal dpdrho() const
Returns the value of dp / drho at constant T for the state of the object.
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data...
doublereal density(doublereal temperature, doublereal pressure, int phase=-1, doublereal rhoguess=-1.0)
Calculates the density given the temperature and the pressure, and a guess at the density...
Class for calculating the equation of state of water.
Implementation of a pressure dependent standard state virtual function for a Pure Water Phase (see Sp...
Header for a class used to house several approximation routines for properties of water...
const doublereal Pi
Pi.
Definition: ct_defs.h:51
doublereal temperature() const
Returns the temperature (Kelvin)
doublereal satPressure(doublereal T)
Returns the saturation pressure given the temperature.
Definition: WaterProps.cpp:258
doublereal isothermalCompressibility_IAPWS(doublereal T, doublereal P)
Returns the isothermal compressibility of water.
Definition: WaterProps.cpp:283
doublereal isothermalCompressibility() const
Returns the coefficient of isothermal compressibility for the state of the object.
The WaterProps class is used to house several approximation routines for properties of water...
Definition: WaterProps.h:93
WaterPropsIAPWS * getWater()
Get a pointer to a changeable WaterPropsIAPWS object.
Definition: PDSS_Water.h:193
Class for the liquid water pressure dependent standard state.
Definition: PDSS_Water.h:49
WaterPropsIAPWS * m_waterIAPWS
Pointer to the WaterPropsIAPWS object.
Definition: WaterProps.h:273
doublereal density_IAPWS() const
Returns the density of water.
Definition: WaterProps.cpp:268
doublereal viscosityWater() const
Returns the viscosity of water at the current conditions (kg/m/s)
Definition: WaterProps.cpp:307
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:65
WaterProps()
Default constructor.
Definition: WaterProps.cpp:15
doublereal coeffThermExp() const
Returns the coefficient of thermal expansion.
doublereal coeffPresExp() const
Returns the isochoric pressure derivative wrt temperature.
const doublereal Avogadro
Avogadro&#39;s Number [number/kmol].
Definition: ct_defs.h:61
const doublereal GasConstant
Universal Gas Constant. [J/kmol/K].
Definition: ct_defs.h:64
Contains declarations for string manipulation functions within Cantera.
doublereal thermalConductivityWater() const
Returns the thermal conductivity of water at the current conditions (W/m/K)
Definition: WaterProps.cpp:357
const doublereal epsilon_0
Permittivity of free space in F/m.
Definition: ct_defs.h:106
doublereal ADebye(doublereal T, doublereal P, int ifunc)
ADebye calculates the value of A_Debye as a function of temperature and pressure according to relatio...
Definition: WaterProps.cpp:185
doublereal coeffThermalExp_IAPWS(doublereal T, doublereal P)
returns the coefficient of thermal expansion
Definition: WaterProps.cpp:273
doublereal relEpsilon(doublereal T, doublereal P_pascal, int ifunc=0)
Bradley-Pitzer equation for the dielectric constant of water as a function of temperature and pressur...
Definition: WaterProps.cpp:133
Namespace for the Cantera kernel.
Definition: application.cpp:29
static doublereal density_T(doublereal T, doublereal P, int ifunc)
Simple calculation of water density at atmospheric pressure.
Definition: WaterProps.cpp:85
bool m_own_sub
true if we own the WaterPropsIAPWS object
Definition: WaterProps.h:276
doublereal psat(doublereal temperature, int waterState=WATER_LIQUID)
This function returns the saturation pressure given the temperature as an input parameter, and sets the internal state to the saturated conditions.