Cantera  2.0
WaterPropsIAPWSphi.cpp
Go to the documentation of this file.
1 /**
2  * @file WaterPropsIAPWSphi.cpp
3  * Definitions for Lowest level of the classes which support a real water model
4  * (see class \link Cantera::WaterPropsIAPWS WaterPropsIAPWS\endlink and class #WaterPropsIAPWSphi).
5  */
6 /*
7  * Copyright (2006) Sandia Corporation. Under the terms of
8  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
9  * U.S. Government retains certain rights in this software.
10  */
12 
13 #include <cstdio>
14 #include <cmath>
15 
16 using std::printf;
17 using std::sqrt;
18 using std::log;
19 using std::exp;
20 using std::pow;
21 using std::fabs;
22 
23 /*
24  * Critical Point values in mks units: Note, these aren't used in this
25  * routine, except for internal checks. All calculations here are done
26  * in dimensionless units.
27  */
28 // \cond
29 static const doublereal T_c = 647.096; // Kelvin
30 static const doublereal P_c = 22.064E6; // Pascals
31 static const doublereal Rho_c = 322.; // kg m-3
32 static const doublereal M_water = 18.015268; // kg kmol-1
33 // \endcond
34 
35 /*
36  * The added constants were calculated so that u = s = 0
37  * for liquid at the triple point. These where determined
38  * by the program testPress. I'm not quite satisfied with
39  * the result, but will let it stand for the moment.
40  * H didn't turn out to be .611872 J/kg, but .611782 J/kg.
41  * There may be a slight error here somehow.
42  */
43 // \cond
44 static const doublereal ni0[9] = {
45  0.0,
46  -8.32044648201 - 0.000000001739715,
47  6.6832105268 + 0.000000000793232,
48  3.00632,
49  0.012436,
50  0.97315,
51  1.27950,
52  0.96956,
53  0.24873
54 };
55 
56 static const doublereal gammi0[9] = {
57  0.0,
58  0.0,
59  0.0,
60  0.0,
61  1.28728967,
62  3.53734222,
63  7.74073708,
64  9.24437796,
65  27.5075105
66 };
67 
68 static const int ciR[56] = {
69  0, // 0
70  0, // 1
71  0,
72  0,
73  0,
74  0, // 5
75  0,
76  0,
77  1,
78  1,
79  1, // 10
80  1,
81  1,
82  1,
83  1,
84  1, // 15
85  1,
86  1,
87  1,
88  1,
89  1, // 20
90  1,
91  1,
92  2,
93  2,
94  2, // 25
95  2,
96  2,
97  2,
98  2,
99  2, // 30
100  2,
101  2,
102  2,
103  2,
104  2, // 35
105  2,
106  2,
107  2,
108  2,
109  2, // 40
110  2,
111  2,
112  3,
113  3,
114  3, // 45
115  3,
116  4,
117  6,
118  6,
119  6, // 50
120  6,
121  0,
122  0,
123  0,
124  0 // 55
125 };
126 
127 static const int diR[55] = {
128  0, // 0
129  1, // 1
130  1,
131  1,
132  2,
133  2, // 5
134  3,
135  4,
136  1,
137  1,
138  1, // 10
139  2,
140  2,
141  3,
142  4,
143  4, // 15
144  5,
145  7,
146  9,
147  10,
148  11, // 20
149  13,
150  15,
151  1,
152  2,
153  2, // 25
154  2,
155  3,
156  4,
157  4,
158  4, // 30
159  5,
160  6,
161  6,
162  7,
163  9, // 35
164  9,
165  9,
166  9,
167  9,
168  10, // 40
169  10,
170  12,
171  3,
172  4,
173  4, // 45
174  5,
175  14,
176  3,
177  6,
178  6, // 50
179  6,
180  3,
181  3,
182  3 // 54
183 };
184 
185 static const int tiR[55] = {
186  0, // 0
187  0, // 1
188  0,
189  0,
190  0,
191  0, // 5
192  0,
193  0,
194  4, // 8
195  6,
196  12, // 10
197  1,
198  5,
199  4,
200  2,
201  13, // 15
202  9,
203  3,
204  4,
205  11,
206  4, // 20
207  13,
208  1,
209  7,
210  1,
211  9, // 25
212  10,
213  10,
214  3,
215  7,
216  10, // 30
217  10,
218  6,
219  10,
220  10,
221  1, // 35
222  2,
223  3,
224  4,
225  8,
226  6, // 40
227  9,
228  8,
229  16,
230  22,
231  23, // 45
232  23,
233  10,
234  50,
235  44,
236  46, // 50
237  50,
238  0,
239  1,
240  4 // 54
241 };
242 static const doublereal ni[57] = {
243  +0.0,
244  +0.12533547935523E-1, // 1
245  +0.78957634722828E1, // 2
246  -0.87803203303561E1, // 3
247  +0.31802509345418E0, // 4
248  -0.26145533859358E0, // 5
249  -0.78199751687981E-2, // 6
250  +0.88089493102134E-2, // 7
251  -0.66856572307965E0, // 8
252  +0.20433810950965, // 9
253  -0.66212605039687E-4, // 10
254  -0.19232721156002E0, // 11
255  -0.25709043003438E0, // 12
256  +0.16074868486251E0, // 13
257  -0.40092828925807E-1, // 14
258  +0.39343422603254E-6, // 15
259  -0.75941377088144E-5, // 16
260  +0.56250979351888E-3, // 17
261  -0.15608652257135E-4, // 18
262  +0.11537996422951E-8, // 19
263  +0.36582165144204E-6, // 20
264  -0.13251180074668E-11,// 21
265  -0.62639586912454E-9, // 22
266  -0.10793600908932E0, // 23
267  +0.17611491008752E-1, // 24
268  +0.22132295167546E0, // 25
269  -0.40247669763528E0, // 26
270  +0.58083399985759E0, // 27
271  +0.49969146990806E-2, // 28
272  -0.31358700712549E-1, // 29
273  -0.74315929710341E0, // 30
274  +0.47807329915480E0, // 31
275  +0.20527940895948E-1, // 32
276  -0.13636435110343E0, // 33
277  +0.14180634400617E-1, // 34
278  +0.83326504880713E-2, // 35
279  -0.29052336009585E-1, // 36
280  +0.38615085574206E-1, // 37
281  -0.20393486513704E-1, // 38
282  -0.16554050063734E-2, // 39
283  +0.19955571979541E-2, // 40
284  +0.15870308324157E-3, // 41
285  -0.16388568342530E-4, // 42
286  +0.43613615723811E-1, // 43
287  +0.34994005463765E-1, // 44
288  -0.76788197844621E-1, // 45
289  +0.22446277332006E-1, // 46
290  -0.62689710414685E-4, // 47
291  -0.55711118565645E-9, // 48
292  -0.19905718354408E0, // 49
293  +0.31777497330738E0, // 50
294  -0.11841182425981E0, // 51
295  -0.31306260323435E2, // 52
296  +0.31546140237781E2, // 53
297  -0.25213154341695E4, // 54
298  -0.14874640856724E0, // 55
299  +0.31806110878444E0 // 56
300 };
301 
302 
303 static const doublereal alphai[3] = {
304  +20.,
305  +20.,
306  +20.
307 };
308 
309 static const doublereal betai[3] = {
310  +150.,
311  +150.,
312  +250.
313 };
314 
315 static const doublereal gammai[3] = {
316  +1.21,
317  +1.21,
318  +1.25
319 };
320 
321 static const doublereal epsi[3] = {
322  +1.0,
323  +1.0,
324  +1.0
325 };
326 
327 static const doublereal ai[2] = {
328  +3.5,
329  +3.5
330 };
331 
332 static const doublereal bi[2] = {
333  +0.85,
334  +0.95
335 };
336 
337 static const doublereal Bi[2] = {
338  +0.2,
339  +0.2
340 };
341 
342 static const doublereal Ci[2] = {
343  +28.0,
344  +32.0
345 };
346 
347 static const doublereal Di[2] = {
348  +700.,
349  +800.
350 };
351 
352 static const doublereal Ai[2] = {
353  +0.32,
354  +0.32
355 };
356 
357 static const doublereal Bbetai[2] = {
358  +0.3,
359  +0.3
360 };
361 // \endcond
362 
363 /*
364  * Constructor for the object.
365  */
367  TAUsave(-1.0),
368  TAUsqrt(-1.0),
369  DELTAsave(-1.0)
370 {
371  for (int i = 0; i < 52; i++) {
372  TAUp[i] = 1.0;
373  }
374  for (int i = 0; i < 16; i++) {
375  DELTAp[i] = 1.0;
376  }
377 }
378 
379 /*
380  * intCheck() calculates all of the functions at a one point and
381  * prints out the result. It's used for conducting the internal
382  * check.
383  */
384 void WaterPropsIAPWSphi::intCheck(doublereal tau, doublereal delta)
385 {
386  tdpolycalc(tau, delta);
387  doublereal nau = phi0();
388  doublereal res = phiR();
389  doublereal res_d = phiR_d();
390  doublereal nau_d = phi0_d();
391  doublereal res_dd = phiR_dd();
392  doublereal nau_dd = phi0_dd();
393  doublereal res_t = phiR_t();
394  doublereal nau_t = phi0_t();
395  doublereal res_tt = phiR_tt();
396  doublereal nau_tt = phi0_tt();
397  doublereal res_dt = phiR_dt();
398  doublereal nau_dt = phi0_dt();
399 
400  std::printf("nau = %20.12e\t\tres = %20.12e\n", nau, res);
401  std::printf("nau_d = %20.12e\t\tres_d = %20.12e\n", nau_d, res_d);
402  printf("nau_dd = %20.12e\t\tres_dd = %20.12e\n", nau_dd, res_dd);
403  printf("nau_t = %20.12e\t\tres_t = %20.12e\n", nau_t, res_t);
404  printf("nau_tt = %20.12e\t\tres_tt = %20.12e\n", nau_tt, res_tt);
405  printf("nau_dt = %20.12e\t\tres_dt = %20.12e\n", nau_dt, res_dt);
406 }
407 
409 {
410  doublereal T = 500.;
411  doublereal rho = 838.025;
412  doublereal tau = T_c/T;
413  doublereal delta = rho / Rho_c;
414  printf(" T = 500 K, rho = 838.025 kg m-3\n");
415  intCheck(tau, delta);
416 }
417 
419 {
420  doublereal T = 647;
421  doublereal rho = 358.0;
422  doublereal tau = T_c/T;
423  doublereal delta = rho / Rho_c;
424  printf(" T = 647 K, rho = 358.0 kg m-3\n");
425  intCheck(tau, delta);
426 }
427 
428 /*
429  * Calculate the polynomials in tau and delta, and store them in static
430  * storage.
431  */
432 void WaterPropsIAPWSphi::tdpolycalc(doublereal tau, doublereal delta)
433 {
434  if ((tau != TAUsave) || 1) {
435  TAUsave = tau;
436  TAUsqrt = sqrt(tau);
437  TAUp[0] = 1.0;
438  for (int i = 1; i < 51; i++) {
439  TAUp[i] = TAUp[i-1] * tau;
440  }
441  }
442  if ((delta != DELTAsave) || 1) {
443  DELTAsave = delta;
444  DELTAp[0] = 1.0;
445  for (int i = 1; i <= 15; i++) {
446  DELTAp[i] = DELTAp[i-1] * delta;
447  }
448  }
449 }
450 
451 /*
452  * Calculate Eqn. 6.5 for phi0, the ideal gas part of the
453  * dimensionless Helmholtz free energy.
454  */
455 doublereal WaterPropsIAPWSphi::phi0() const
456 {
457  doublereal tau = TAUsave;
458  doublereal delta = DELTAsave;
459  doublereal retn = log(delta) + ni0[1] + ni0[2]*tau + ni0[3]*log(tau);
460 
461  retn += ni0[4] * log(1.0 - exp(-gammi0[4]*tau));
462  retn += ni0[5] * log(1.0 - exp(-gammi0[5]*tau));
463  retn += ni0[6] * log(1.0 - exp(-gammi0[6]*tau));
464  retn += ni0[7] * log(1.0 - exp(-gammi0[7]*tau));
465  retn += ni0[8] * log(1.0 - exp(-gammi0[8]*tau));
466  return retn;
467 }
468 
469 /*
470  * Calculate Eqn. 6.6 for phiR, the residual part of the
471  * dimensionless Helmholtz free energy.
472  *
473  * tau = dimensionless temperature
474  * delta = dimensionless pressure
475  */
476 doublereal WaterPropsIAPWSphi::phiR() const
477 {
478  doublereal tau = TAUsave;
479  doublereal delta = DELTAsave;
480  int i, j;
481 
482  /*
483  * Write out the first seven polynomials in the expression
484  */
485  doublereal T375 = pow(tau, 0.375);
486  doublereal val = (ni[1] * delta / TAUsqrt +
487  ni[2] * delta * TAUsqrt * T375 +
488  ni[3] * delta * tau +
489  ni[4] * DELTAp[2] * TAUsqrt +
490  ni[5] * DELTAp[2] * T375 * T375 +
491  ni[6] * DELTAp[3] * T375 +
492  ni[7] * DELTAp[4] * tau);
493  /*
494  * Next, do polynomial contributions 8 to 51
495  */
496  for (i = 8; i <= 51; i++) {
497  val += (ni[i] * DELTAp[diR[i]] * TAUp[tiR[i]] * exp(-DELTAp[ciR[i]]));
498  }
499 
500  /*
501  * Next do contributions 52 to 54
502  */
503  for (j = 0; j < 3; j++) {
504  i = 52 + j;
505  doublereal dtmp = delta - epsi[j];
506  doublereal ttmp = tau - gammai[j];
507  val += (ni[i] * DELTAp[diR[i]] * TAUp[tiR[i]] *
508  exp(-alphai[j]*dtmp*dtmp - betai[j]*ttmp*ttmp));
509  }
510 
511  /*
512  * Next do contributions 55 and 56
513  */
514  for (j = 0; j < 2; j++) {
515  i = 55 + j;
516  doublereal deltam1 = delta - 1.0;
517  doublereal dtmp2 = deltam1 * deltam1;
518  doublereal atmp = 0.5 / Bbetai[j];
519  doublereal theta = (1.0 - tau) + Ai[j] * pow(dtmp2, atmp);
520  doublereal triag = theta * theta + Bi[j] * pow(dtmp2, ai[j]);
521  doublereal ttmp = tau - 1.0;
522 
523  doublereal triagtmp = pow(triag, bi[j]);
524 
525  doublereal phi = exp(-Ci[j]*dtmp2 - Di[j]*ttmp*ttmp);
526  val += (ni[i] * triagtmp * delta * phi);
527  }
528 
529  return val;
530 }
531 
532 /*
533  * Calculate the Phi function, which is basically the helmholtz free energy
534  * Eqn. (6.4)
535  */
536 doublereal WaterPropsIAPWSphi::phi(doublereal tau, doublereal delta)
537 {
538  tdpolycalc(tau, delta);
539  doublereal nau = phi0();
540  doublereal res = phiR();
541  doublereal retn = nau + res;
542  return retn;
543 }
544 
545 
546 /*
547  * Calculate d_phiR_d(delta), the first derivative of phiR
548  * wrt delta
549  *
550  * tau = dimensionless temperature
551  * delta = dimensionless pressure
552  */
553 doublereal WaterPropsIAPWSphi::phiR_d() const
554 {
555  doublereal tau = TAUsave;
556  doublereal delta = DELTAsave;
557  int i, j;
558 
559  /*
560  * Write out the first seven polynomials in the expression
561  */
562  doublereal T375 = pow(tau, 0.375);
563  doublereal val = (ni[1] / TAUsqrt +
564  ni[2] * TAUsqrt * T375 +
565  ni[3] * tau +
566  ni[4] * 2.0 * delta * TAUsqrt +
567  ni[5] * 2.0 * delta * T375 * T375 +
568  ni[6] * 3.0 * DELTAp[2] * T375 +
569  ni[7] * 4.0 * DELTAp[3] * tau);
570  /*
571  * Next, do polynomial contributions 8 to 51
572  */
573  for (i = 8; i <= 51; i++) {
574  val += ((ni[i] * exp(-DELTAp[ciR[i]]) * DELTAp[diR[i] - 1] *
575  TAUp[tiR[i]]) * (diR[i] - ciR[i]* DELTAp[ciR[i]]));
576  }
577 
578  /*
579  * Next do contributions 52 to 54
580  */
581  for (j = 0; j < 3; j++) {
582  i = 52 + j;
583  doublereal dtmp = delta - epsi[j];
584  doublereal ttmp = tau - gammai[j];
585  doublereal tmp = (ni[i] * DELTAp[diR[i]] * TAUp[tiR[i]] *
586  exp(-alphai[j]*dtmp*dtmp - betai[j]*ttmp*ttmp));
587  val += tmp * (diR[i]/delta - 2.0 * alphai[j] * dtmp);
588  }
589 
590  /*
591  * Next do contributions 55 and 56
592  */
593  for (j = 0; j < 2; j++) {
594  i = 55 + j;
595  doublereal deltam1 = delta - 1.0;
596  doublereal dtmp2 = deltam1 * deltam1;
597  doublereal atmp = 0.5 / Bbetai[j];
598  doublereal theta = (1.0 - tau) + Ai[j] * pow(dtmp2, atmp);
599  doublereal triag = theta * theta + Bi[j] * pow(dtmp2, ai[j]);
600  doublereal ttmp = tau - 1.0;
601 
602  doublereal triagtmp = pow(triag, bi[j]);
603  doublereal triagtmpm1 = pow(triag, bi[j]-1.0);
604  doublereal atmpM1 = atmp - 1.0;
605  doublereal ptmp = pow(dtmp2,atmpM1);
606  doublereal p2tmp = pow(dtmp2, ai[j]-1.0);
607  doublereal dtriagddelta =
608  deltam1 *(Ai[j] * theta * 2.0 / Bbetai[j] * ptmp +
609  2.0*Bi[j]*ai[j]*p2tmp);
610 
611  doublereal phi = exp(-Ci[j]*dtmp2 - Di[j]*ttmp*ttmp);
612  doublereal dphiddelta = -2.0*Ci[j]*deltam1*phi;
613  doublereal dtriagtmpddelta = bi[j] * triagtmpm1 * dtriagddelta;
614 
615  doublereal tmp = ni[i] * (triagtmp * (phi + delta*dphiddelta) +
616  dtriagtmpddelta * delta * phi);
617  val += tmp;
618  }
619 
620  return val;
621 }
622 
623 /*
624  * Calculate d_phi0_d(delta), the first derivative of phi0
625  * wrt delta
626  *
627  * tau = dimensionless temperature
628  * delta = dimensionless pressure
629  */
630 doublereal WaterPropsIAPWSphi::phi0_d() const
631 {
632  doublereal delta = DELTAsave;
633  return (1.0/delta);
634 }
635 
636 /*
637  * Calculate the dPhidDelta function, which is basically the derivative
638  * of helmholtz free energy wrt delta
639  * Eqn. (6.4)
640  */
641 doublereal WaterPropsIAPWSphi::phi_d(doublereal tau, doublereal delta)
642 {
643  tdpolycalc(tau, delta);
644  doublereal nau = phi0_d();
645  doublereal res = phiR_d();
646  doublereal retn = nau + res;
647  return retn;
648 }
649 
650 /*
651  * Calculate the dimensionless pressure at tau and delta;
652  *
653  * p/(rhoRT) = delta * phi_d()
654  *
655  * note: this is done so much, we have a separate routine.
656  */
657 doublereal WaterPropsIAPWSphi::pressureM_rhoRT(doublereal tau, doublereal delta)
658 {
659  tdpolycalc(tau, delta);
660  doublereal res = phiR_d();
661  doublereal retn = 1.0 + delta * res;
662  return retn;
663 }
664 
665 /*
666  * Calculate d2_phiR_dd(delta), the second derivative of phiR
667  * wrt delta
668  *
669  * tau = dimensionless temperature
670  * delta = dimensionless pressure
671  */
672 doublereal WaterPropsIAPWSphi::phiR_dd() const
673 {
674  doublereal tau = TAUsave;
675  doublereal delta = DELTAsave;
676  int i, j;
677  doublereal atmp;
678 
679  /*
680  * Write out the first seven polynomials in the expression
681  */
682  doublereal T375 = pow(tau, 0.375);
683  doublereal val = (ni[4] * 2.0 * TAUsqrt +
684  ni[5] * 2.0 * T375 * T375 +
685  ni[6] * 6.0 * delta * T375 +
686  ni[7] * 12.0 * DELTAp[2] * tau);
687  /*
688  * Next, do polynomial contributions 8 to 51
689  */
690  for (i = 8; i <= 51; i++) {
691  doublereal dtmp = DELTAp[ciR[i]];
692  doublereal tmp = ni[i] * exp(-dtmp) * TAUp[tiR[i]];
693  if (diR[i] == 1) {
694  atmp = 1.0/delta;
695  } else {
696  atmp = DELTAp[diR[i] - 2];
697  }
698  tmp *= atmp *((diR[i] - ciR[i]*dtmp)*(diR[i]-1.0-ciR[i]*dtmp) -
699  ciR[i]*ciR[i]*dtmp);
700  val += tmp;
701  }
702 
703  /*
704  * Next do contributions 52 to 54
705  */
706  for (j = 0; j < 3; j++) {
707  i = 52 + j;
708  doublereal dtmp = delta - epsi[j];
709  doublereal ttmp = tau - gammai[j];
710  doublereal tmp = (ni[i] * TAUp[tiR[i]] *
711  exp(-alphai[j]*dtmp*dtmp - betai[j]*ttmp*ttmp));
712  doublereal deltmp = DELTAp[diR[i]];
713  doublereal deltmpM1 = deltmp/delta;
714  doublereal deltmpM2 = deltmpM1 / delta;
715  doublereal d2tmp = dtmp * dtmp;
716 
717  val += tmp * (-2.0*alphai[j]*deltmp +
718  4.0 * alphai[j] * alphai[j] * deltmp * d2tmp -
719  4.0 * diR[i] * alphai[j] * deltmpM1 * dtmp +
720  diR[i] * (diR[i] - 1.0) * deltmpM2);
721  }
722 
723  /*
724  * Next do contributions 55 and 56
725  */
726  for (j = 0; j < 2; j++) {
727  i = 55 + j;
728  doublereal deltam1 = delta - 1.0;
729  doublereal dtmp2 = deltam1 * deltam1;
730  atmp = 0.5 / Bbetai[j];
731  doublereal theta = (1.0 - tau) + Ai[j] * pow(dtmp2, atmp);
732  doublereal triag = theta * theta + Bi[j] * pow(dtmp2, ai[j]);
733  doublereal ttmp = tau - 1.0;
734 
735  doublereal triagtmp = pow(triag, bi[j]);
736  doublereal triagtmpm1 = pow(triag, bi[j]-1.0);
737  doublereal atmpM1 = atmp - 1.0;
738  doublereal ptmp = pow(dtmp2,atmpM1);
739  doublereal p2tmp = pow(dtmp2, ai[j]-1.0);
740  doublereal dtriagddelta =
741  deltam1 *(Ai[j] * theta * 2.0 / Bbetai[j] * ptmp +
742  2.0*Bi[j]*ai[j]*p2tmp);
743 
744  doublereal phi = exp(-Ci[j]*dtmp2 - Di[j]*ttmp*ttmp);
745  doublereal dphiddelta = -2.0*Ci[j]*deltam1*phi;
746  doublereal dtriagtmpddelta = bi[j] * triagtmpm1 * dtriagddelta;
747 
748 
749  doublereal d2phiddelta2 = 2.0 * Ci[j] * phi * (2.0*Ci[j]*dtmp2 - 1.0);
750 
751  doublereal pptmp = ptmp / dtmp2;
752  doublereal d2triagddelta2 = dtriagddelta / deltam1;
753  d2triagddelta2 +=
754  dtmp2 *(4.0*Bi[j]*ai[j]*(ai[j]-1.0)*pow(dtmp2,ai[j]-2.0) +
755  2.0*Ai[j]*Ai[j]/(Bbetai[j]*Bbetai[j])*ptmp*ptmp +
756  Ai[j]*theta*4.0/Bbetai[j]*(atmp-1.0)*pptmp);
757 
758  doublereal d2triagtmpd2delta =
759  bi[j] * (triagtmpm1 * d2triagddelta2 +
760  (bi[j]-1.0)*triagtmpm1/triag*dtriagddelta*dtriagddelta);
761 
762  doublereal ctmp = (triagtmp * (2.0*dphiddelta + delta*d2phiddelta2) +
763  2.0*dtriagtmpddelta*(phi + delta * dphiddelta) +
764  d2triagtmpd2delta * delta * phi);
765 
766  val += ni[i] * ctmp;
767  }
768 
769  return val;
770 }
771 
772 /*
773  * Calculate d2_phi0_dd(delta), the second derivative of phi0
774  * wrt delta
775  *
776  * tau = dimensionless temperature
777  * delta = dimensionless pressure
778  */
779 doublereal WaterPropsIAPWSphi::phi0_dd() const
780 {
781  doublereal delta = DELTAsave;
782  return (-1.0/(delta*delta));
783 }
784 
785 /*
786  * Calculate the d2_PhidDelta2 function, which is the second derivative
787  * of helmholtz free energy wrt delta
788  * Eqn. (6.4)
789  */
790 doublereal WaterPropsIAPWSphi::phi_dd(doublereal tau, doublereal delta)
791 {
792  tdpolycalc(tau, delta);
793  doublereal nau = phi0_dd();
794  doublereal res = phiR_dd();
795  doublereal retn = nau + res;
796  return retn;
797 }
798 
799 doublereal WaterPropsIAPWSphi::dimdpdrho(doublereal tau, doublereal delta)
800 {
801  tdpolycalc(tau, delta);
802  doublereal res1 = phiR_d();
803  doublereal res2 = phiR_dd();
804  doublereal retn = 1.0 + delta * (2.0*res1 + delta*res2);
805  return retn;
806 }
807 
808 doublereal WaterPropsIAPWSphi::dimdpdT(doublereal tau, doublereal delta)
809 {
810  tdpolycalc(tau, delta);
811  doublereal res1 = phiR_d();
812  doublereal res2 = phiR_dt();
813  doublereal retn = (1.0 + delta * res1) - tau * delta * (res2);
814  return retn;
815 }
816 
817 /*
818  * Calculate d_phi0/d(tau)
819  */
820 doublereal WaterPropsIAPWSphi::phi0_t() const
821 {
822  doublereal tau = TAUsave;
823  doublereal retn = ni0[2] + ni0[3]/tau;
824  retn += (ni0[4] * gammi0[4] * (1.0/(1.0 - exp(-gammi0[4]*tau)) - 1.0));
825  retn += (ni0[5] * gammi0[5] * (1.0/(1.0 - exp(-gammi0[5]*tau)) - 1.0));
826  retn += (ni0[6] * gammi0[6] * (1.0/(1.0 - exp(-gammi0[6]*tau)) - 1.0));
827  retn += (ni0[7] * gammi0[7] * (1.0/(1.0 - exp(-gammi0[7]*tau)) - 1.0));
828  retn += (ni0[8] * gammi0[8] * (1.0/(1.0 - exp(-gammi0[8]*tau)) - 1.0));
829  return retn;
830 }
831 
832 /*
833  * Calculate Eqn. 6.6 for dphiRdtau, the derivative residual part of the
834  * dimensionless Helmholtz free energy wrt temperature
835  *
836  * tau = dimensionless temperature
837  * delta = dimensionless pressure
838  */
839 doublereal WaterPropsIAPWSphi::phiR_t() const
840 {
841  doublereal tau = TAUsave;
842  doublereal delta = DELTAsave;
843  int i, j;
844  doublereal atmp, tmp;
845 
846  /*
847  * Write out the first seven polynomials in the expression
848  */
849  doublereal T375 = pow(tau, 0.375);
850  doublereal val = ((-0.5) *ni[1] * delta / TAUsqrt / tau +
851  ni[2] * delta * 0.875 / TAUsqrt * T375 +
852  ni[3] * delta +
853  ni[4] * DELTAp[2] * 0.5 / TAUsqrt +
854  ni[5] * DELTAp[2] * 0.75 * T375 * T375 / tau +
855  ni[6] * DELTAp[3] * 0.375 * T375 / tau +
856  ni[7] * DELTAp[4]);
857  /*
858  * Next, do polynomial contributions 8 to 51
859  */
860  for (i = 8; i <= 51; i++) {
861  tmp = (ni[i] * DELTAp[diR[i]] * TAUp[tiR[i]-1] * exp(-DELTAp[ciR[i]]));
862  val += tiR[i] * tmp;
863  }
864 
865  /*
866  * Next do contributions 52 to 54
867  */
868  for (j = 0; j < 3; j++) {
869  i = 52 + j;
870  doublereal dtmp = delta - epsi[j];
871  doublereal ttmp = tau - gammai[j];
872  tmp = (ni[i] * DELTAp[diR[i]] * TAUp[tiR[i]] *
873  exp(-alphai[j]*dtmp*dtmp - betai[j]*ttmp*ttmp));
874  val += tmp *(tiR[i]/tau - 2.0 * betai[j]*ttmp);
875  }
876 
877  /*
878  * Next do contributions 55 and 56
879  */
880  for (j = 0; j < 2; j++) {
881  i = 55 + j;
882  doublereal deltam1 = delta - 1.0;
883  doublereal dtmp2 = deltam1 * deltam1;
884  atmp = 0.5 / Bbetai[j];
885  doublereal theta = (1.0 - tau) + Ai[j] * pow(dtmp2, atmp);
886  doublereal triag = theta * theta + Bi[j] * pow(dtmp2, ai[j]);
887  doublereal ttmp = tau - 1.0;
888 
889  doublereal triagtmp = pow(triag, bi[j]);
890 
891  doublereal phi = exp(-Ci[j]*dtmp2 - Di[j]*ttmp*ttmp);
892 
893 
894  doublereal dtriagtmpdtau = -2.0*theta * bi[j] * triagtmp / triag;
895 
896  doublereal dphidtau = - 2.0 * Di[j] * ttmp * phi;
897 
898  val += ni[i] * delta * (dtriagtmpdtau * phi + triagtmp * dphidtau);
899  }
900 
901  return val;
902 }
903 
904 /*
905  * Calculate the dPhidtau function, which is basically the derivative
906  * of helmholtz free energy wrt tau
907  * Eqn. (6.4)
908  */
909 doublereal WaterPropsIAPWSphi::phi_t(doublereal tau, doublereal delta)
910 {
911  tdpolycalc(tau, delta);
912  doublereal nau = phi0_t();
913  doublereal res = phiR_t();
914  doublereal retn = nau + res;
915  return retn;
916 }
917 
918 /*
919  * Calculate d2_phi0/dtau2
920  */
921 doublereal WaterPropsIAPWSphi::phi0_tt() const
922 {
923  doublereal tau = TAUsave;
924  doublereal tmp, itmp;
925  doublereal retn = - ni0[3]/(tau * tau);
926  for (int i = 4; i <= 8; i++) {
927  tmp = exp(-gammi0[i]*tau);
928  itmp = 1.0 - tmp;
929  retn -= (ni0[i] * gammi0[i] * gammi0[i] * tmp / (itmp * itmp));
930  }
931  return retn;
932 }
933 
934 /*
935  * Calculate Eqn. 6.6 for dphiRdtau, the second derivative residual part of the
936  * dimensionless Helmholtz free energy wrt temperature
937  *
938  * tau = dimensionless temperature
939  * delta = dimensionless pressure
940  */
941 doublereal WaterPropsIAPWSphi::phiR_tt() const
942 {
943  doublereal tau = TAUsave;
944  doublereal delta = DELTAsave;
945  int i, j;
946  doublereal atmp, tmp;
947 
948  /*
949  * Write out the first seven polynomials in the expression
950  */
951  doublereal T375 = pow(tau, 0.375);
952  doublereal val = ((-0.5) * (-1.5) * ni[1] * delta / (TAUsqrt * tau * tau) +
953  ni[2] * delta * 0.875 * (-0.125) * T375 / (TAUsqrt * tau) +
954  ni[4] * DELTAp[2] * 0.5 * (-0.5)/ (TAUsqrt * tau) +
955  ni[5] * DELTAp[2] * 0.75 *(-0.25) * T375 * T375 / (tau * tau) +
956  ni[6] * DELTAp[3] * 0.375 *(-0.625) * T375 / (tau * tau));
957  /*
958  * Next, do polynomial contributions 8 to 51
959  */
960  for (i = 8; i <= 51; i++) {
961  if (tiR[i] > 1) {
962  tmp = (ni[i] * DELTAp[diR[i]] * TAUp[tiR[i]-2] * exp(-DELTAp[ciR[i]]));
963  val += tiR[i] * (tiR[i] - 1.0) * tmp;
964  }
965  }
966 
967  /*
968  * Next do contributions 52 to 54
969  */
970  for (j = 0; j < 3; j++) {
971  i = 52 + j;
972  doublereal dtmp = delta - epsi[j];
973  doublereal ttmp = tau - gammai[j];
974  tmp = (ni[i] * DELTAp[diR[i]] * TAUp[tiR[i]] *
975  exp(-alphai[j]*dtmp*dtmp - betai[j]*ttmp*ttmp));
976  atmp = tiR[i]/tau - 2.0 * betai[j]*ttmp;
977  val += tmp *(atmp * atmp - tiR[i]/(tau*tau) - 2.0*betai[j]);
978  }
979 
980  /*
981  * Next do contributions 55 and 56
982  */
983  for (j = 0; j < 2; j++) {
984  i = 55 + j;
985  doublereal deltam1 = delta - 1.0;
986  doublereal dtmp2 = deltam1 * deltam1;
987  atmp = 0.5 / Bbetai[j];
988  doublereal theta = (1.0 - tau) + Ai[j] * pow(dtmp2, atmp);
989  doublereal triag = theta * theta + Bi[j] * pow(dtmp2, ai[j]);
990  doublereal ttmp = tau - 1.0;
991 
992  doublereal triagtmp = pow(triag, bi[j]);
993  doublereal triagtmpM1 = triagtmp / triag;
994 
995  doublereal phi = exp(-Ci[j]*dtmp2 - Di[j]*ttmp*ttmp);
996 
997 
998  doublereal dtriagtmpdtau = -2.0*theta * bi[j] * triagtmp / triag;
999 
1000  doublereal dphidtau = - 2.0 * Di[j] * ttmp * phi;
1001 
1002  doublereal d2triagtmpdtau2 =
1003  (2 * bi[j] * triagtmpM1 +
1004  4 * theta * theta * bi[j] * (bi[j]-1.0) * triagtmpM1 / triag);
1005 
1006  doublereal d2phidtau2 = 2.0*Di[j]*phi *(2.0*Di[j]*ttmp*ttmp - 1.0);
1007 
1008  tmp = (d2triagtmpdtau2 * phi +
1009  2 * dtriagtmpdtau * dphidtau +
1010  triagtmp * d2phidtau2);
1011  val += ni[i] * delta * tmp;
1012  }
1013 
1014  return val;
1015 }
1016 
1017 /*
1018  * Calculate the d2Phidtau2 function, which is basically the second derivative
1019  * of helmholtz free energy wrt tau
1020  * Eqn. (6.4)
1021  */
1022 doublereal WaterPropsIAPWSphi::phi_tt(doublereal tau, doublereal delta)
1023 {
1024  tdpolycalc(tau, delta);
1025  doublereal nau = phi0_tt();
1026  doublereal res = phiR_tt();
1027  doublereal retn = nau + res;
1028  return retn;
1029 }
1030 
1031 /**
1032  * Calculate d2_phi0/dtauddelta
1033  */
1035 {
1036  return 0.0;
1037 }
1038 
1039 /*
1040  * Calculate d2_phiR_d(delta)d(tau), the mixed derivative of phi
1041  * wrt delta and tau.
1042  *
1043  * tau = dimensionless temperature
1044  * delta = dimensionless pressure
1045  */
1047 {
1048  doublereal tau = TAUsave;
1049  doublereal delta = DELTAsave;
1050  int i, j;
1051  doublereal tmp;
1052  /*
1053  * Write out the first seven polynomials in the expression
1054  */
1055  doublereal T375 = pow(tau, 0.375);
1056  doublereal val = (ni[1] * (-0.5) / (TAUsqrt * tau) +
1057  ni[2] * (0.875) * T375 / TAUsqrt +
1058  ni[3] +
1059  ni[4] * 2.0 * delta * (0.5) / TAUsqrt +
1060  ni[5] * 2.0 * delta * (0.75) * T375 * T375 / tau +
1061  ni[6] * 3.0 * DELTAp[2] * 0.375 * T375 / tau +
1062  ni[7] * 4.0 * DELTAp[3]);
1063  /*
1064  * Next, do polynomial contributions 8 to 51
1065  */
1066  for (i = 8; i <= 51; i++) {
1067  tmp = (ni[i] * tiR[i] * exp(-DELTAp[ciR[i]]) * DELTAp[diR[i] - 1] *
1068  TAUp[tiR[i] - 1]);
1069  val += tmp * (diR[i] - ciR[i] * DELTAp[ciR[i]]);
1070  }
1071 
1072  /*
1073  * Next do contributions 52 to 54
1074  */
1075  for (j = 0; j < 3; j++) {
1076  i = 52 + j;
1077  doublereal dtmp = delta - epsi[j];
1078  doublereal ttmp = tau - gammai[j];
1079  tmp = (ni[i] * DELTAp[diR[i]] * TAUp[tiR[i]] *
1080  exp(-alphai[j]*dtmp*dtmp - betai[j]*ttmp*ttmp));
1081  val += tmp * ((diR[i]/delta - 2.0 * alphai[j] * dtmp) *
1082  (tiR[i]/tau - 2.0 * betai[j] * ttmp));
1083  }
1084 
1085  /*
1086  * Next do contributions 55 and 56
1087  */
1088  for (j = 0; j < 2; j++) {
1089  i = 55 + j;
1090  doublereal deltam1 = delta - 1.0;
1091  doublereal dtmp2 = deltam1 * deltam1;
1092  doublereal atmp = 0.5 / Bbetai[j];
1093  doublereal theta = (1.0 - tau) + Ai[j] * pow(dtmp2, atmp);
1094  doublereal triag = theta * theta + Bi[j] * pow(dtmp2, ai[j]);
1095  doublereal ttmp = tau - 1.0;
1096 
1097  doublereal triagtmp = pow(triag, bi[j]);
1098  doublereal triagtmpm1 = pow(triag, bi[j]-1.0);
1099  doublereal atmpM1 = atmp - 1.0;
1100  doublereal ptmp = pow(dtmp2,atmpM1);
1101  doublereal p2tmp = pow(dtmp2, ai[j]-1.0);
1102  doublereal dtriagddelta =
1103  deltam1 *(Ai[j] * theta * 2.0 / Bbetai[j] * ptmp +
1104  2.0*Bi[j]*ai[j]*p2tmp);
1105 
1106  doublereal phi = exp(-Ci[j]*dtmp2 - Di[j]*ttmp*ttmp);
1107  doublereal dphiddelta = -2.0*Ci[j]*deltam1*phi;
1108  doublereal dtriagtmpddelta = bi[j] * triagtmpm1 * dtriagddelta;
1109 
1110 
1111  doublereal dtriagtmpdtau = -2.0*theta * bi[j] * triagtmp / triag;
1112 
1113  doublereal dphidtau = - 2.0 * Di[j] * ttmp * phi;
1114 
1115  doublereal d2phiddeltadtau = 4.0 * Ci[j] * Di[j] * deltam1 * ttmp * phi;
1116 
1117  doublereal d2triagtmpddeltadtau =
1118  (-Ai[j] * bi[j] * 2.0 / Bbetai[j] * triagtmpm1 * deltam1 * ptmp
1119  -2.0 * theta * bi[j] * (bi[j] - 1.0) * triagtmpm1 / triag * dtriagddelta);
1120 
1121 
1122  doublereal tmp = ni[i] * (triagtmp * (dphidtau + delta*d2phiddeltadtau) +
1123  delta * dtriagtmpddelta * dphidtau +
1124  dtriagtmpdtau * (phi + delta * dphiddelta) +
1125  d2triagtmpddeltadtau * delta * phi);
1126  val += tmp;
1127  }
1128 
1129  return val;
1130 }
1131 
1132 /*
1133  * This program computes the reduced density, given the reduced pressure
1134  * and the reduced temperature, tau. It takes an initial guess, deltaGuess.
1135  * DeltaGuess is important as this is a multivalued function below the
1136  * critical point.
1137  *
1138  */
1139 doublereal WaterPropsIAPWSphi::dfind(doublereal p_red, doublereal tau, doublereal deltaGuess)
1140 {
1141  doublereal dd = deltaGuess;
1142  bool conv = false;
1143  doublereal deldd = dd;
1144  doublereal pcheck = 1.0E-30 + 1.0E-8 * p_red;
1145  for (int n = 0; n < 200; n++) {
1146  /*
1147  * Calculate the internal polynomials, and then calculate the
1148  * phi deriv functions needed by this routine.
1149  */
1150  tdpolycalc(tau, dd);
1151  doublereal q1 = phiR_d();
1152  doublereal q2 = phiR_dd();
1153 
1154  /*
1155  * Calculate the predicted reduced pressure, pred0, based on the
1156  * current tau and dd.
1157  */
1158  doublereal pred0 = dd + dd * dd * q1;
1159  /*
1160  * Calculate the derivative of the predicted reduced pressure
1161  * wrt the reduced density, dd, This is dpddelta
1162  */
1163  doublereal dpddelta = 1.0 + 2.0 * dd * q1 + dd * dd * q2;
1164  /*
1165  * If dpddelta is negative, then we are in the middle of the
1166  * 2 phase region, beyond the stability curve. We need to adjust
1167  * the initial guess outwards and start a new iteration.
1168  */
1169  if (dpddelta <= 0.0) {
1170  if (deltaGuess > 1.0) {
1171  dd = dd * 1.05;
1172  }
1173  if (deltaGuess < 1.0) {
1174  dd = dd * 0.95;
1175  }
1176  continue;
1177  }
1178  /*
1179  * Check for convergence
1180  */
1181  if (fabs(pred0-p_red) < pcheck) {
1182  conv = true;
1183  break;
1184  }
1185 
1186  /*
1187  * Dampen and crop the update
1188  */
1189  doublereal dpdx = dpddelta;
1190  if (n < 10) {
1191  dpdx = dpddelta * 1.1;
1192  }
1193  if (dpdx < 0.001) {
1194  dpdx = 0.001;
1195  }
1196 
1197  /*
1198  * Formulate the update to reduced density using
1199  * Newton's method. Then, crop it to a max value
1200  * of 0.02
1201  */
1202  deldd = - (pred0 - p_red) / dpdx;
1203  if (fabs(deldd) > 0.05) {
1204  deldd = deldd * 0.05 / fabs(deldd);
1205  }
1206  /*
1207  * updated the reduced density value
1208  */
1209  dd = dd + deldd;
1210  if (fabs(deldd/dd) < 1.0E-14) {
1211  conv = true;
1212  break;
1213  }
1214  /*
1215  * Check for negative densities
1216  */
1217  if (dd <= 0.0) {
1218  dd = 1.0E-24;
1219  }
1220  }
1221  /*
1222  * Check for convergence, and return 0.0 if it wasn't achieved.
1223  */
1224  if (! conv) {
1225  dd = 0.0;
1226  }
1227  return dd;
1228 }
1229 
1230 /**
1231  * Calculate the dimensionless gibbs free energy g/RT.
1232  */
1234 {
1235  doublereal delta = DELTAsave;
1236  doublereal rd = phiR_d();
1237  doublereal g = 1.0 + phi0() + phiR() + delta * rd;
1238  return g;
1239 }
1240 
1241 /**
1242  * Calculate the dimensionless enthalpy h/RT.
1243  */
1245 {
1246  doublereal delta = DELTAsave;
1247  doublereal tau = TAUsave;
1248  doublereal rd = phiR_d();
1249  doublereal nt = phi0_t();
1250  doublereal rt = phiR_t();
1251  doublereal hRT = 1.0 + tau * (nt + rt) + delta * rd;
1252  return hRT;
1253 }
1254 
1255 /*
1256  * Calculate the dimensionless entropy s/R.
1257  */
1259 {
1260  doublereal tau = TAUsave;
1261  doublereal nt = phi0_t();
1262  doublereal rt = phiR_t();
1263  doublereal p0 = phi0();
1264  doublereal pR = phiR();
1265  doublereal sR = tau * (nt + rt) - p0 - pR;
1266  return sR;
1267 }
1268 
1269 /*
1270  * Calculate the dimensionless internal energy, u/RT.
1271  */
1273 {
1274  doublereal tau = TAUsave;
1275  doublereal nt = phi0_t();
1276  doublereal rt = phiR_t();
1277  doublereal uR = tau * (nt + rt);
1278  return uR;
1279 }
1280 
1281 /*
1282  * Calculate the dimensionless constant volume Heat Capacity, Cv/R
1283  */
1284 doublereal WaterPropsIAPWSphi::cv_R() const
1285 {
1286  doublereal tau = TAUsave;
1287  doublereal ntt = phi0_tt();
1288  doublereal rtt = phiR_tt();
1289  doublereal cvR = - tau * tau * (ntt + rtt);
1290  return cvR;
1291 }
1292 
1293 /*
1294  * Calculate the dimensionless constant pressure Heat Capacity, Cp/R
1295  */
1296 doublereal WaterPropsIAPWSphi::cp_R() const
1297 {
1298  doublereal tau = TAUsave;
1299  doublereal delta = DELTAsave;
1300  doublereal cvR = cv_R();
1301  //doublereal nd = phi0_d();
1302  doublereal rd = phiR_d();
1303  doublereal rdd = phiR_dd();
1304  doublereal rdt = phiR_dt();
1305  doublereal num = (1.0 + delta * rd - delta * tau * rdt);
1306  doublereal cpR = cvR + (num * num /
1307  (1.0 + 2.0 * delta * rd + delta * delta * rdd));
1308  return cpR;
1309 }
1310