Cantera  2.4.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 
51 WaterProps::~WaterProps()
52 {
53  if (m_own_sub) {
54  delete m_waterIAPWS;
55  }
56 }
57 
58 doublereal WaterProps::density_T(doublereal T, doublereal P, int ifunc)
59 {
60  static const doublereal Tc = T - 273.15;
61  static const doublereal U1 = 288.9414;
62  static const doublereal U2 = 508929.2;
63  static const doublereal U3 = 68.12963;
64  static const doublereal U4 = -3.9863;
65 
66  doublereal tmp1 = Tc + U1;
67  doublereal tmp4 = Tc + U4;
68  doublereal t4t4 = tmp4 * tmp4;
69  doublereal tmp3 = Tc + U3;
70  doublereal rho = 1000. * (1.0 - tmp1*t4t4/(U2 * tmp3));
71 
72  // Impose an ideal gas lower bound on rho. We need this to ensure positivity
73  // of rho, even though it is grossly unrepresentative.
74  doublereal rhomin = P / (GasConstant * T);
75  if (rho < rhomin) {
76  rho = rhomin;
77  if (ifunc == 1) {
78  return - rhomin / T;
79  } else if (ifunc == 3) {
80  return rhomin / P;
81  } else if (ifunc == 2) {
82  return 2.0 * rhomin / (T * T);
83  }
84  }
85 
86  if (ifunc == 1) {
87  doublereal drhodT = 1000./U2 * (
88  - tmp4 * tmp4 / (tmp3)
89  - tmp1 * 2 * tmp4 / (tmp3)
90  + tmp1 * t4t4 / (tmp3*tmp3)
91  );
92  return drhodT;
93  } else if (ifunc == 3) {
94  return 0.0;
95  } else if (ifunc == 2) {
96  doublereal t3t3 = tmp3 * tmp3;
97  doublereal d2rhodT2 = 1000./U2 *
98  ((-4.0*tmp4-2.0*tmp1)/tmp3 +
99  (2.0*t4t4 + 4.0*tmp1*tmp4)/t3t3
100  - 2.0*tmp1 * t4t4/(t3t3*tmp3));
101  return d2rhodT2;
102  }
103  return rho;
104 }
105 
106 doublereal WaterProps::relEpsilon(doublereal T, doublereal P_pascal,
107  int ifunc)
108 {
109  static const doublereal U1 = 3.4279E2;
110  static const doublereal U2 = -5.0866E-3;
111  static const doublereal U3 = 9.4690E-7;
112  static const doublereal U4 = -2.0525;
113  static const doublereal U5 = 3.1159E3;
114  static const doublereal U6 = -1.8289E2;
115  static const doublereal U7 = -8.0325E3;
116  static const doublereal U8 = 4.2142E6;
117  static const doublereal U9 = 2.1417;
118  doublereal T2 = T * T;
119 
120  doublereal eps1000 = U1 * exp(U2 * T + U3 * T2);
121  doublereal C = U4 + U5/(U6 + T);
122  doublereal B = U7 + U8/T + U9 * T;
123  doublereal Pbar = P_pascal * 1.0E-5;
124  doublereal tmpBpar = B + Pbar;
125  doublereal tmpB1000 = B + 1000.0;
126  doublereal ltmp = log(tmpBpar/tmpB1000);
127  doublereal epsRel = eps1000 + C * ltmp;
128 
129  if (ifunc == 1 || ifunc == 2) {
130  doublereal tmpC = U6 + T;
131  doublereal dCdT = - U5/(tmpC * tmpC);
132  doublereal dBdT = - U8/(T * T) + U9;
133  doublereal deps1000dT = eps1000 * (U2 + 2.0 * U3 * T);
134  doublereal dltmpdT = (dBdT/tmpBpar - dBdT/tmpB1000);
135  if (ifunc == 1) {
136  return deps1000dT + dCdT * ltmp + C * dltmpdT;
137  }
138  doublereal T3 = T2 * T;
139  doublereal d2CdT2 = - 2.0 * dCdT / tmpC;
140  doublereal d2BdT2 = 2.0 * U8 / (T3);
141  doublereal d2ltmpdT2 = (d2BdT2*(1.0/tmpBpar - 1.0/tmpB1000) +
142  dBdT*dBdT*(1.0/(tmpB1000*tmpB1000) - 1.0/(tmpBpar*tmpBpar)));
143  doublereal d2eps1000dT2 = (deps1000dT * (U2 + 2.0 * U3 * T) + eps1000 * (2.0 * U3));
144 
145  if (ifunc == 2) {
146  doublereal d2epsReldT2 = (d2eps1000dT2 + d2CdT2 * ltmp + 2.0 * dCdT * dltmpdT
147  + C * d2ltmpdT2);
148  return d2epsReldT2;
149  }
150  }
151  if (ifunc == 3) {
152  doublereal dltmpdP = 1.0E-5 / tmpBpar;
153  return C * dltmpdP;
154  }
155  return epsRel;
156 }
157 
158 doublereal WaterProps::ADebye(doublereal T, doublereal P_input, int ifunc)
159 {
160  doublereal psat = satPressure(T);
161  doublereal P;
162  if (psat > P_input) {
163  P = psat;
164  } else {
165  P = P_input;
166  }
167  doublereal epsRelWater = relEpsilon(T, P, 0);
168  doublereal epsilon = epsilon_0 * epsRelWater;
169  doublereal dw = density_IAPWS(T, P);
170  doublereal tmp = sqrt(2.0 * Avogadro * dw / 1000.);
171  doublereal tmp2 = ElectronCharge * ElectronCharge * Avogadro /
172  (epsilon * GasConstant * T);
173  doublereal tmp3 = tmp2 * sqrt(tmp2);
174  doublereal A_Debye = tmp * tmp3 / (8.0 * Pi);
175 
176  // dAdT = - 3/2 Ad/T + 1/2 Ad/dw d(dw)/dT - 3/2 Ad/eps d(eps)/dT
177  // dAdT = - 3/2 Ad/T - 1/2 Ad/Vw d(Vw)/dT - 3/2 Ad/eps d(eps)/dT
178  if (ifunc == 1 || ifunc == 2) {
179  doublereal dAdT = - 1.5 * A_Debye / T;
180 
181  doublereal depsRelWaterdT = relEpsilon(T, P, 1);
182  dAdT -= A_Debye * (1.5 * depsRelWaterdT / epsRelWater);
183 
184  // calculate d(lnV)/dT _constantP, i.e., the cte
185  doublereal cte = coeffThermalExp_IAPWS(T, P);
186  doublereal contrib2 = - A_Debye * (0.5 * cte);
187  dAdT += contrib2;
188 
189  if (ifunc == 1) {
190  return dAdT;
191  }
192 
193  if (ifunc == 2) {
194  // Get the second derivative of the dielectric constant wrt T
195  // -> we will take each of the terms in dAdT and differentiate
196  // it again.
197  doublereal d2AdT2 = 1.5 / T * (A_Debye/T - dAdT);
198  doublereal d2epsRelWaterdT2 = relEpsilon(T, P, 2);
199  d2AdT2 += 1.5 * (- dAdT * depsRelWaterdT / epsRelWater
200  - A_Debye / epsRelWater *
201  (d2epsRelWaterdT2 - depsRelWaterdT * depsRelWaterdT / epsRelWater));
202  doublereal deltaT = -0.1;
203  doublereal Tdel = T + deltaT;
204  doublereal cte_del = coeffThermalExp_IAPWS(Tdel, P);
205  doublereal dctedT = (cte_del - cte) / Tdel;
206  doublereal contrib3 = 0.5 * (-(dAdT * cte) -(A_Debye * dctedT));
207  d2AdT2 += contrib3;
208  return d2AdT2;
209  }
210  }
211 
212  // A_Debye = (1/(8 Pi)) sqrt(2 Na dw / 1000)
213  // (e e/(epsilon R T))^3/2
214  //
215  // dAdP = + 1/2 Ad/dw d(dw)/dP - 3/2 Ad/eps d(eps)/dP
216  // dAdP = - 1/2 Ad/Vw d(Vw)/dP - 3/2 Ad/eps d(eps)/dP
217  // dAdP = + 1/2 Ad * kappa - 3/2 Ad/eps d(eps)/dP
218  //
219  // where kappa = - 1/Vw d(Vw)/dP_T (isothermal compressibility)
220  if (ifunc == 3) {
221  doublereal dAdP = 0.0;
222  doublereal depsRelWaterdP = relEpsilon(T, P, 3);
223  dAdP -= A_Debye * (1.5 * depsRelWaterdP / epsRelWater);
224  doublereal kappa = isothermalCompressibility_IAPWS(T,P);
225  dAdP += A_Debye * (0.5 * kappa);
226  return dAdP;
227  }
228  return A_Debye;
229 }
230 
231 doublereal WaterProps::satPressure(doublereal T)
232 {
233  return m_waterIAPWS->psat(T);
234 }
235 
236 doublereal WaterProps::density_IAPWS(doublereal temp, doublereal press)
237 {
238  return m_waterIAPWS->density(temp, press, WATER_LIQUID);
239 }
240 
241 doublereal WaterProps::density_IAPWS() const
242 {
243  return m_waterIAPWS->density();
244 }
245 
246 doublereal WaterProps::coeffThermalExp_IAPWS(doublereal temp, doublereal press)
247 {
248  doublereal dens = m_waterIAPWS->density(temp, press, WATER_LIQUID);
249  if (dens < 0.0) {
250  throw CanteraError("WaterProps::coeffThermalExp_IAPWS",
251  "Unable to solve for density at T = {} and P = {}", temp, press);
252  }
253  return m_waterIAPWS->coeffThermExp();
254 }
255 
256 doublereal WaterProps::isothermalCompressibility_IAPWS(doublereal temp, doublereal press)
257 {
258  doublereal dens = m_waterIAPWS->density(temp, press, WATER_LIQUID);
259  if (dens < 0.0) {
260  throw CanteraError("WaterProps::isothermalCompressibility_IAPWS",
261  "Unable to solve for density at T = {} and P = {}", temp, press);
262  }
264 }
265 
266 static const doublereal H[4] = {1., 0.978197, 0.579829, -0.202354};
267 
268 static const doublereal Hij[6][7] = {
269  { 0.5132047, 0.2151778, -0.2818107, 0.1778064, -0.04176610, 0., 0.},
270  { 0.3205656, 0.7317883, -1.070786 , 0.4605040, 0., -0.01578386, 0.},
271  { 0., 1.241044 , -1.263184 , 0.2340379, 0., 0., 0.},
272  { 0., 1.476783 , 0., -0.4924179, 0.1600435, 0., -0.003629481},
273  {-0.7782567, 0.0 , 0., 0. , 0., 0., 0.},
274  { 0.1885447, 0.0 , 0., 0. , 0., 0., 0.},
275 };
276 
277 static const doublereal rhoStar = 317.763; // kg / m3
278 static const doublereal presStar = 22.115E6; // Pa
279 
280 doublereal WaterProps::viscosityWater() const
281 {
282  static const doublereal TStar = 647.27; // Kelvin
283  static const doublereal muStar = 55.071E-6; //Pa s
284  doublereal temp = m_waterIAPWS->temperature();
285  doublereal dens = m_waterIAPWS->density();
286 
287  doublereal rhobar = dens/rhoStar;
288  doublereal tbar = temp / TStar;
289  doublereal tbar2 = tbar * tbar;
290  doublereal tbar3 = tbar2 * tbar;
291  doublereal mu0bar = std::sqrt(tbar) / (H[0] + H[1]/tbar + H[2]/tbar2 + H[3]/tbar3);
292 
293  doublereal tfac1 = 1.0 / tbar - 1.0;
294  doublereal tfac2 = tfac1 * tfac1;
295  doublereal tfac3 = tfac2 * tfac1;
296  doublereal tfac4 = tfac3 * tfac1;
297  doublereal tfac5 = tfac4 * tfac1;
298 
299  doublereal rfac1 = rhobar - 1.0;
300  doublereal rfac2 = rfac1 * rfac1;
301  doublereal rfac3 = rfac2 * rfac1;
302  doublereal rfac4 = rfac3 * rfac1;
303  doublereal rfac5 = rfac4 * rfac1;
304  doublereal rfac6 = rfac5 * rfac1;
305 
306  doublereal sum = (Hij[0][0] + Hij[1][0]*tfac1 + Hij[4][0]*tfac4 + Hij[5][0]*tfac5 +
307  Hij[0][1]*rfac1 + Hij[1][1]*tfac1*rfac1 + Hij[2][1]*tfac2*rfac1 + Hij[3][1]*tfac3*rfac1 +
308  Hij[0][2]*rfac2 + Hij[1][2]*tfac1*rfac2 + Hij[2][2]*tfac2*rfac2 +
309  Hij[0][3]*rfac3 + Hij[1][3]*tfac1*rfac3 + Hij[2][3]*tfac2*rfac3 + Hij[3][3]*tfac3*rfac3 +
310  Hij[0][4]*rfac4 + Hij[3][4]*tfac3*rfac4 +
311  Hij[1][5]*tfac1*rfac5 + Hij[3][6]*tfac3*rfac6
312  );
313  doublereal mu1bar = std::exp(rhobar * sum);
314 
315  // Apply the near-critical point corrections if necessary
316  doublereal mu2bar = 1.0;
317  if (tbar >= 0.9970 && tbar <= 1.0082 && rhobar >= 0.755 && rhobar <= 1.290) {
318  doublereal drhodp = 1.0 / m_waterIAPWS->dpdrho();
319  drhodp *= presStar / rhoStar;
320  doublereal xsi = rhobar * drhodp;
321  if (xsi >= 21.93) {
322  mu2bar = 0.922 * std::pow(xsi, 0.0263);
323  }
324  }
325 
326  doublereal mubar = mu0bar * mu1bar * mu2bar;
327  return mubar * muStar;
328 }
329 
331 {
332  static const doublereal Tstar = 647.27;
333  static const doublereal rhostar = 317.763;
334  static const doublereal lambdastar = 0.4945;
335  static const doublereal presstar = 22.115E6;
336  static const doublereal L[4] = {
337  1.0000,
338  6.978267,
339  2.599096,
340  -0.998254
341  };
342  static const doublereal Lji[6][5] = {
343  { 1.3293046, 1.7018363, 5.2246158, 8.7127675, -1.8525999},
344  {-0.40452437, -2.2156845, -10.124111, -9.5000611, 0.93404690},
345  { 0.24409490, 1.6511057, 4.9874687, 4.3786606, 0.0},
346  { 0.018660751, -0.76736002, -0.27297694, -0.91783782, 0.0},
347  {-0.12961068, 0.37283344, -0.43083393, 0.0, 0.0},
348  { 0.044809953, -0.11203160, 0.13333849, 0.0, 0.0},
349  };
350 
351  doublereal temp = m_waterIAPWS->temperature();
352  doublereal dens = m_waterIAPWS->density();
353 
354  doublereal rhobar = dens/rhostar;
355  doublereal tbar = temp / Tstar;
356  doublereal tbar2 = tbar * tbar;
357  doublereal tbar3 = tbar2 * tbar;
358  doublereal lambda0bar = sqrt(tbar) / (L[0] + L[1]/tbar + L[2]/tbar2 + L[3]/tbar3);
359 
360  doublereal tfac1 = 1.0 / tbar - 1.0;
361  doublereal tfac2 = tfac1 * tfac1;
362  doublereal tfac3 = tfac2 * tfac1;
363  doublereal tfac4 = tfac3 * tfac1;
364 
365  doublereal rfac1 = rhobar - 1.0;
366  doublereal rfac2 = rfac1 * rfac1;
367  doublereal rfac3 = rfac2 * rfac1;
368  doublereal rfac4 = rfac3 * rfac1;
369  doublereal rfac5 = rfac4 * rfac1;
370 
371  doublereal sum = (Lji[0][0] + Lji[0][1]*tfac1 + Lji[0][2]*tfac2 + Lji[0][3]*tfac3 + Lji[0][4]*tfac4 +
372  Lji[1][0]*rfac1 + Lji[1][1]*tfac1*rfac1 + Lji[1][2]*tfac2*rfac1 + Lji[1][3]*tfac3*rfac1 + Lji[1][4]*tfac4*rfac1 +
373  Lji[2][0]*rfac2 + Lji[2][1]*tfac1*rfac2 + Lji[2][2]*tfac2*rfac2 + Lji[2][3]*tfac3*rfac2 +
374  Lji[3][0]*rfac3 + Lji[3][1]*tfac1*rfac3 + Lji[3][2]*tfac2*rfac3 + Lji[3][3]*tfac3*rfac3 +
375  Lji[4][0]*rfac4 + Lji[4][1]*tfac1*rfac4 + Lji[4][2]*tfac2*rfac4 +
376  Lji[5][0]*rfac5 + Lji[5][1]*tfac1*rfac5 + Lji[5][2]*tfac2*rfac5
377  );
378  doublereal lambda1bar = exp(rhobar * sum);
379  doublereal mu0bar = std::sqrt(tbar) / (H[0] + H[1]/tbar + H[2]/tbar2 + H[3]/tbar3);
380  doublereal tfac5 = tfac4 * tfac1;
381  doublereal rfac6 = rfac5 * rfac1;
382 
383  sum = (Hij[0][0] + Hij[1][0]*tfac1 + Hij[4][0]*tfac4 + Hij[5][0]*tfac5 +
384  Hij[0][1]*rfac1 + Hij[1][1]*tfac1*rfac1 + Hij[2][1]*tfac2*rfac1 + Hij[3][1]*tfac3*rfac1 +
385  Hij[0][2]*rfac2 + Hij[1][2]*tfac1*rfac2 + Hij[2][2]*tfac2*rfac2 +
386  Hij[0][3]*rfac3 + Hij[1][3]*tfac1*rfac3 + Hij[2][3]*tfac2*rfac3 + Hij[3][3]*tfac3*rfac3 +
387  Hij[0][4]*rfac4 + Hij[3][4]*tfac3*rfac4 +
388  Hij[1][5]*tfac1*rfac5 + Hij[3][6]*tfac3*rfac6
389  );
390  doublereal mu1bar = std::exp(rhobar * sum);
391  doublereal t2r2 = tbar * tbar / (rhobar * rhobar);
392  doublereal drhodp = 1.0 / m_waterIAPWS->dpdrho();
393  drhodp *= presStar / rhoStar;
394  doublereal xsi = rhobar * drhodp;
395  doublereal xsipow = std::pow(xsi, 0.4678);
396  doublereal rho1 = rhobar - 1.;
397  doublereal rho2 = rho1 * rho1;
398  doublereal rho4 = rho2 * rho2;
399  doublereal temp2 = (tbar - 1.0) * (tbar - 1.0);
400 
401  // beta = M / (rho * Rgas) (d (pressure) / dT) at constant rho
402  //
403  // Note for ideal gases this is equal to one.
404  //
405  // beta = delta (phi0_d() + phiR_d())
406  // - tau delta (phi0_dt() + phiR_dt())
407  doublereal beta = m_waterIAPWS->coeffPresExp();
408  doublereal dpdT_const_rho = beta * GasConstant * dens / 18.015268;
409  dpdT_const_rho *= Tstar / presstar;
410  doublereal lambda2bar = 0.0013848 / (mu0bar * mu1bar) * t2r2 * dpdT_const_rho * dpdT_const_rho *
411  xsipow * sqrt(rhobar) * exp(-18.66*temp2 - rho4);
412  return (lambda0bar * lambda1bar + lambda2bar) * lambdastar;
413 }
414 
415 }
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:231
doublereal isothermalCompressibility_IAPWS(doublereal T, doublereal P)
Returns the isothermal compressibility of water.
Definition: WaterProps.cpp:256
doublereal isothermalCompressibility() const
Returns the coefficient of isothermal compressibility for the state of the object.
WaterPropsIAPWS * getWater()
Get a pointer to a changeable WaterPropsIAPWS object.
Definition: PDSS_Water.h:142
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:275
doublereal density_IAPWS() const
Returns the density of water.
Definition: WaterProps.cpp:241
doublereal viscosityWater() const
Returns the viscosity of water at the current conditions (kg/m/s)
Definition: WaterProps.cpp:280
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:330
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:158
doublereal coeffThermalExp_IAPWS(doublereal T, doublereal P)
returns the coefficient of thermal expansion
Definition: WaterProps.cpp:246
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:106
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:8
static doublereal density_T(doublereal T, doublereal P, int ifunc)
Simple calculation of water density at atmospheric pressure.
Definition: WaterProps.cpp:58
bool m_own_sub
true if we own the WaterPropsIAPWS object
Definition: WaterProps.h:278
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.