Cantera  2.1.2
Sub.cpp
Go to the documentation of this file.
1 //! @file Sub.cpp
2 /*
3  * The Substance class
4  * D. Goodwin, Caltech Nov. 1996
5  */
6 #include "cantera/tpx/Sub.h"
8 #include "cantera/base/global.h"
9 
10 using std::string;
11 using namespace Cantera;
12 
13 namespace tpx
14 {
15 Substance::Substance() :
16  T(Undef),
17  Rho(Undef),
18  Tslast(Undef),
19  Rhf(Undef),
20  Rhv(Undef),
21  Pst(Undef),
22  m_energy_offset(0.0),
23  m_entropy_offset(0.0),
24  kbr(0)
25 {
26 }
27 
28 double Substance::P()
29 {
30  return TwoPhase() ? Ps() : Pp();
31 }
32 
33 const double DeltaT = 0.000001;
34 
35 double Substance::dPsdT()
36 {
37  double tsave = T;
38  double ps1 = Ps();
39  T = T + DeltaT;
40  double dpdt = (Ps() - ps1)/DeltaT;
41  T = tsave;
42  return dpdt;
43 }
44 
45 int Substance::TwoPhase()
46 {
47  if (T >= Tcrit()) {
48  return 0;
49  }
50  update_sat();
51  return ((Rho < Rhf) && (Rho > Rhv) ? 1 : 0);
52 }
53 
54 double Substance::x()
55 {
56  if (T >= Tcrit()) {
57  return (1.0/Rho < Vcrit() ? 0.0 : 1.0);
58  } else {
59  update_sat();
60  if (Rho <= Rhv) {
61  return 1.0;
62  } else if (Rho >= Rhf) {
63  return 0.0;
64  } else {
65  double vv = 1.0/Rhv;
66  double vl = 1.0/Rhf;
67  return (1.0/Rho - vl)/(vv - vl);
68  }
69  }
70 }
71 
72 double Substance::Tsat(double p)
73 {
74  if (p <= 0.0 || p > Pcrit()) {
75  throw TPX_Error("Substance::Tsat", "illegal pressure value");
76  }
77  int LoopCount = 0;
78  double tol = 1.e-6*p;
79  double Tsave = T;
80  if (T < Tmin()) {
81  T = 0.5*(Tcrit() - Tmin());
82  }
83  if (T >= Tcrit()) {
84  T = 0.5*(Tcrit() - Tmin());
85  }
86  double dp;
87  do {
88  if (T > Tcrit()) {
89  T = Tcrit() - 0.001;
90  }
91  if (T < Tmin()) {
92  T = Tmin() + 0.001;
93  }
94  dp = p - Ps();
95  double dt = dp/dPsdT();
96  double dta = fabs(dt);
97  double dtm = 0.1*T;
98  if (dta > dtm) {
99  dt = dt*dtm/dta;
100  }
101  T = T + dt;
102  LoopCount++;
103  if (LoopCount > 100) {
104  T = Tsave;
105  throw TPX_Error("Substance::Tsat", "No convergence");
106  }
107  } while (fabs(dp) > tol);
108  double tsat = T;
109  T = Tsave;
110  return tsat;
111 }
112 
113 // absolute tolerances
114 static const double TolAbsH = 0.0001; // J/kg
115 static const double TolAbsU = 0.0001;
116 static const double TolAbsS = 1.e-7;
117 static const double TolAbsP = 0.000; // Pa
118 static const double TolAbsV = 1.e-8;
119 static const double TolAbsT = 1.e-3;
120 static const double TolRel = 3.e-8;
121 
122 void Substance::Set(PropertyPair::type XY, double x0, double y0)
123 {
124  double temp;
125 
126  /* if inverted (PT) switch order and change sign of XY (TP = -PT) */
127  if (XY < 0) {
128  std::swap(x0, y0);
129  XY = static_cast<PropertyPair::type>(-XY);
130  }
131 
132  switch (XY) {
133  case PropertyPair::TV:
134  set_T(x0);
135  set_v(y0);
136  break;
137 
138  case PropertyPair::HP:
139  if (Lever(Pgiven, y0, x0, propertyFlag::H)) {
140  return;
141  }
142  set_xy(propertyFlag::H, propertyFlag::P,
143  x0, y0, TolAbsH, TolAbsP, TolRel, TolRel);
144  break;
145 
146  case PropertyPair::SP:
147  if (Lever(Pgiven, y0, x0, propertyFlag::S)) {
148  return;
149  }
150  set_xy(propertyFlag::S, propertyFlag::P,
151  x0, y0, TolAbsS, TolAbsP, TolRel, TolRel);
152  break;
153 
154  case PropertyPair::PV:
155  if (Lever(Pgiven, x0, y0, propertyFlag::V)) {
156  return;
157  }
158  set_xy(propertyFlag::P, propertyFlag::V,
159  x0, y0, TolAbsP, TolAbsV, TolRel, TolRel);
160  break;
161 
162  case PropertyPair::TP:
163  if (x0 < Tcrit()) {
164  set_T(x0);
165  if (y0 < Ps()) {
166  Set(PropertyPair::TX, x0, 1.0);
167  } else {
168  Set(PropertyPair::TX, x0, 0.0);
169  }
170  } else {
171  set_T(x0);
172  }
173  set_xy(propertyFlag::T, propertyFlag::P,
174  x0, y0, TolAbsT, TolAbsP, TolRel, TolRel);
175  break;
176 
177  case PropertyPair::UV:
178  set_xy(propertyFlag::U, propertyFlag::V,
179  x0, y0, TolAbsU, TolAbsV, TolRel, TolRel);
180  break;
181 
182  case PropertyPair::ST:
183  if (Lever(Tgiven, y0, x0, propertyFlag::S)) {
184  return;
185  }
186  set_xy(propertyFlag::S, propertyFlag::T,
187  x0, y0, TolAbsS, TolAbsT, TolRel, TolRel);
188  break;
189 
190  case PropertyPair::SV:
191  set_xy(propertyFlag::S, propertyFlag::V,
192  x0, y0, TolAbsS, TolAbsV, TolRel, TolRel);
193  break;
194 
195  case PropertyPair::UP:
196  if (Lever(Pgiven, y0, x0, propertyFlag::U)) {
197  return;
198  }
199  set_xy(propertyFlag::U, propertyFlag::P,
200  x0, y0, TolAbsU, TolAbsP, TolRel, TolRel);
201  break;
202 
203  case PropertyPair::VH:
204  set_xy(propertyFlag::V, propertyFlag::H,
205  x0, y0, TolAbsV, TolAbsH, TolRel, TolRel);
206  break;
207 
208  case PropertyPair::TH:
209  set_xy(propertyFlag::T, propertyFlag::H,
210  x0, y0, TolAbsT, TolAbsH, TolRel, TolRel);
211  break;
212 
213  case PropertyPair::SH:
214  set_xy(propertyFlag::S, propertyFlag::H,
215  x0, y0, TolAbsS, TolAbsH, TolRel, TolRel);
216  break;
217 
218  case PropertyPair::PX:
219  temp = Tsat(x0);
220  if (y0 > 1.0 || y0 < 0.0) {
221  throw TPX_Error("Substance::Set",
222  "Invalid vapor fraction, " + fp2str(y0));
223  } else if (temp >= Tcrit()) {
224  throw TPX_Error("Substance::Set",
225  "Can't set vapor fraction above the critical point");
226  } else {
227  set_T(temp);
228  update_sat();
229  Rho = 1.0/((1.0 - y0)/Rhf + y0/Rhv);
230  }
231  break;
232 
233  case PropertyPair::TX:
234  if (y0 > 1.0 || y0 < 0.0) {
235  throw TPX_Error("Substance::Set",
236  "Invalid vapor fraction, " + fp2str(y0));
237  } else if (x0 >= Tcrit()) {
238  throw TPX_Error("Substance::Set",
239  "Can't set vapor fraction above the critical point");
240  } else {
241  set_T(x0);
242  update_sat();
243  Rho = 1.0/((1.0 - y0)/Rhf + y0/Rhv);
244  }
245  break;
246 
247  default:
248  throw TPX_Error("Substance::Set", "Invalid input.");
249  }
250 }
251 
252 //------------------ Protected and Private Functions -------------------
253 
254 void Substance::set_Rho(double r0)
255 {
256  if (r0 > 0.0) {
257  Rho = r0;
258  } else {
259  throw TPX_Error("Substance::set_Rho", "Invalid density: " + fp2str(r0));
260  }
261 }
262 
263 void Substance::set_T(double t0)
264 {
265  if ((t0 >= Tmin()) && (t0 <= Tmax())) {
266  T = t0;
267  } else {
268  throw TPX_Error("Substance::set_T", "illegal temperature: " + fp2str(t0));
269  }
270 }
271 
272 void Substance::set_v(double v0)
273 {
274  if (v0 > 0) {
275  Rho = 1.0/v0;
276  } else {
277  throw TPX_Error("Substance::set_v",
278  "negative specific volume: "+fp2str(v0));
279  }
280 }
281 
282 double Substance::Ps()
283 {
284  if (T < Tmin() || T > Tcrit()) {
285  throw TPX_Error("Substance::Ps",
286  "illegal temperature value "+fp2str(T));
287  }
288  update_sat();
289  return Pst;
290 }
291 
292 void Substance::update_sat()
293 {
294  if ((T != Tslast) && (T < Tcrit())) {
295  double Rho_save = Rho;
296 
297  double pp = Psat();
298  double lps = log(pp);
299  // trial value = Psat from correlation
300  int i;
301 
302  for (i = 0; i<20; i++) {
303  if (i==0) {
304  Rho = ldens(); // trial value = liquid density
305  } else {
306  Rho = Rhf;
307  }
308  set_TPp(T,pp);
309  Rhf = Rho; // sat liquid density
310 
311  double gf = hp() - T*sp();
312  if (i==0) {
313  Rho = pp*MolWt()/(8314.0*T); // trial value = ideal gas
314  } else {
315  Rho = Rhv;
316  }
317  set_TPp(T,pp);
318 
319  Rhv = Rho; // sat vapor density
320  double gv = hp() - T*sp();
321  double dg = gv - gf;
322 
323  if (Rhv > Rhf) {
324  std::swap(Rhv, Rhf);
325  dg = - dg;
326  }
327 
328  if (fabs(dg) < 0.001 && Rhf > Rhv) {
329  break;
330  }
331  double dp = dg/(1.0/Rhv - 1.0/Rhf);
332  double psold = pp;
333 
334  if (fabs(dp) > pp) {
335  lps -= dg/(pp*(1.0/Rhv - 1.0/Rhf));
336  pp = exp(lps);
337  } else {
338  pp -= dp;
339  lps = log(pp);
340  }
341  if (pp > Pcrit()) {
342  pp = psold + 0.5*(Pcrit() - psold);
343  lps = log(pp);
344  } else if (pp < 0.0) {
345  pp = psold/2.0;
346  lps = log(pp);
347  }
348  }
349  if (Rhf <= Rhv) {
350  throw TPX_Error("Substance::update_sat",
351  "wrong root found for sat. liquid or vapor at P = "+fp2str(pp));
352  }
353 
354  if (i >= 20) {
355  throw TPX_Error("substance::update_sat","no convergence");
356  } else {
357  Pst = pp;
358  Tslast = T;
359  }
360  Rho = Rho_save;
361  }
362 }
363 
364 double Substance::vprop(propertyFlag::type ijob)
365 {
366  switch (ijob) {
367  case propertyFlag::H:
368  return hp();
369  case propertyFlag::S:
370  return sp();
371  case propertyFlag::U:
372  return up();
373  case propertyFlag::V:
374  return vp();
375  case propertyFlag::P:
376  return Pp();
377  default:
378  throw TPX_Error("Substance::vprop", "invalid job index");
379  }
380 }
381 
382 int Substance::Lever(int itp, double sat, double val, propertyFlag::type ifunc)
383 {
384  double psat;
385  double Tsave = T;
386  double Rhosave = Rho;
387  if (itp == Tgiven) {
388  if (sat >= Tcrit()) {
389  return 0;
390  }
391  T = sat;
392  psat = Ps();
393  } else if (itp == Pgiven) {
394  if (sat >= Pcrit()) {
395  return 0;
396  }
397  psat = sat;
398  try {
399  T = Tsat(psat);
400  } catch (TPX_Error&) {
401  // Failure to converge here is not an error
402  T = Tsave;
403  Rho = Rhosave;
404  return 0;
405  }
406  } else {
407  throw TPX_Error("Substance::Lever","general error");
408  }
409  Set(PropertyPair::TX, T, 1.0);
410  double Valg = vprop(ifunc);
411  Set(PropertyPair::TX, T, 0.0);
412  double Valf = vprop(ifunc);
413  if (val >= Valf && val <= Valg) {
414  double xx = (val - Valf)/(Valg - Valf);
415  double vv = (1.0 - xx)/Rhf + xx/Rhv;
416  set_v(vv);
417  return 1;
418  } else {
419  T = Tsave;
420  Rho = Rhosave;
421  return 0;
422  }
423 }
424 
425 void Substance::set_xy(propertyFlag::type ifx, propertyFlag::type ify,
426  double X, double Y,
427  double atx, double aty,
428  double rtx, double rty)
429 {
430  double v_here, t_here;
431  double dvs1 = 2.0*Vcrit();
432  double dvs2 = 0.7*Vcrit();
433  int LoopCount = 0;
434 
435  double v_save = 1.0/Rho;
436  double t_save = T;
437 
438  if ((T == Undef) && (Rho == Undef)) {
439  // new object, try to pick a "reasonable" starting point
440  Set(PropertyPair::TV,Tcrit()*1.1,Vcrit()*1.1);
441  t_here = T;
442  v_here = 1.0/Rho;
443  } else if (Rho == Undef) {
444  // new object, try to pick a "reasonable" starting point
445  Set(PropertyPair::TV,T,Vcrit()*1.1);
446  t_here = T;
447  v_here = 1.0/Rho;
448  } else {
449  v_here = v_save;
450  t_here = t_save;
451  }
452 
453  double Xa = fabs(X);
454  double Ya = fabs(Y);
455 
456  while (true) {
457  double x_here = prop(ifx);
458  double y_here = prop(ify);
459  double err_x = fabs(X - x_here);
460  double err_y = fabs(Y - y_here);
461 
462  if ((err_x < atx + rtx*Xa) && (err_y < aty + rty*Ya)) {
463  break;
464  }
465 
466  /* perturb t */
467  double dt = 0.001*t_here;
468  if (t_here + dt > Tmax()) {
469  dt *= -1.0;
470  }
471 
472  /* perturb v */
473  double dv = 0.001*v_here;
474  if (v_here <= Vcrit()) {
475  dv *= -1.0;
476  }
477 
478  /* derivatives with respect to T */
479  Set(PropertyPair::TV, t_here + dt, v_here);
480  double dxdt = (prop(ifx) - x_here)/dt;
481  double dydt = (prop(ify) - y_here)/dt;
482 
483  /* derivatives with respect to v */
484  Set(PropertyPair::TV, t_here, v_here + dv);
485  double dxdv = (prop(ifx) - x_here)/dv;
486  double dydv = (prop(ify) - y_here)/dv;
487 
488  double det = dxdt*dydv - dydt*dxdv;
489  dt = ((X - x_here)*dydv - (Y - y_here)*dxdv)/det;
490  dv = ((Y - y_here)*dxdt - (X - x_here)*dydt)/det;
491 
492  double dvm = 0.2*v_here;
493  if (v_here < dvs1) {
494  dvm *= 0.5;
495  }
496  if (v_here < dvs2) {
497  dvm *= 0.5;
498  }
499  double dtm = 0.1*t_here;
500  double dva = fabs(dv);
501  double dta = fabs(dt);
502  if (dva > dvm) {
503  dv *= dvm/dva;
504  }
505  if (dta > dtm) {
506  dt *= dtm/dta;
507  }
508  v_here += dv;
509  t_here += dt;
510  t_here = clip(t_here, Tmin(), Tmax());
511  if (v_here <= 0.0) {
512  v_here = 0.0001;
513  }
514  Set(PropertyPair::TV, t_here, v_here);
515  LoopCount++;
516  if (LoopCount > 200) {
517  throw TPX_Error("Substance::set_xy","no convergence");
518  }
519  }
520 }
521 
522 double Substance::prop(propertyFlag::type ijob)
523 {
524  if (ijob == propertyFlag::P) {
525  return P();
526  }
527  if (ijob == propertyFlag::T) {
528  return T;
529  }
530  double xx = x();
531  if ((xx > 0.0) && (xx < 1.0)) {
532  double Rho_save = Rho;
533  Rho = Rhv;
534  double vp = vprop(ijob);
535  Rho = Rhf;
536  double lp = vprop(ijob);
537  double pp = (1.0 - xx)*lp + xx*vp;
538  Rho = Rho_save;
539  return pp;
540  } else {
541  return vprop(ijob);
542  }
543 }
544 
545 static const double ErrP = 1.e-7;
546 static const double Big = 1.e30;
547 
548 void Substance::BracketSlope(double Pressure)
549 {
550  if (kbr == 0) {
551  dv = (v_here < Vcrit() ? -0.05*v_here : 0.2*v_here);
552  if (Vmin > 0.0) {
553  dv = 0.2*v_here;
554  }
555  if (Vmax < Big) {
556  dv = -0.05*v_here;
557  }
558  } else {
559  double dpdv = (Pmax - Pmin)/(Vmax - Vmin);
560  v_here = Vmax;
561  P_here = Pmax;
562  dv = dvbf*(Pressure - P_here)/dpdv;
563  dvbf = 0.5*dvbf;
564  }
565 }
566 
567 void Substance::set_TPp(double Temp, double Pressure)
568 {
569  kbr = 0;
570  dvbf = 1.0;
571  Vmin = 0.0;
572  Vmax = Big;
573  Pmin = Big;
574  Pmax = 0.0;
575  double dvs1 = 2.0*Vcrit();
576  double dvs2 = 0.7*Vcrit();
577  int LoopCount = 0;
578 
579  double v_save = 1.0/Rho;
580  T = Temp;
581  v_here = vp();
582 
583  // loop
584  while (P_here = Pp(),
585  fabs(Pressure - P_here) >= ErrP* Pressure || LoopCount == 0) {
586  if (P_here < 0.0) {
587  BracketSlope(Pressure);
588  } else {
589  dv = 0.001*v_here;
590  if (v_here <= Vcrit()) {
591  dv *= -1.0;
592  }
593  Set(PropertyPair::TV, Temp, v_here+dv);
594  double dpdv = (Pp() - P_here)/dv;
595  if (dpdv > 0.0) {
596  BracketSlope(Pressure);
597  } else {
598  if ((P_here > Pressure) && (v_here > Vmin)) {
599  Vmin = v_here;
600  } else if ((P_here < Pressure) && (v_here < Vmax)) {
601  Vmax = v_here;
602  }
603  if (v_here == Vmin) {
604  Pmin = P_here;
605  }
606  if (v_here == Vmax) {
607  Pmax = P_here;
608  }
609  if (Vmin >= Vmax) {
610  throw TPX_Error("Substance::set_TPp","Vmin >= Vmax");
611  } else if ((Vmin > 0.0) && (Vmax < Big)) {
612  kbr = 1;
613  }
614  dvbf = 1.0;
615  if (dpdv == 0.0) {
616  dvbf = 0.5;
617  BracketSlope(Pressure);
618  } else {
619  dv = (Pressure - P_here)/dpdv;
620  }
621  }
622  }
623  double dvm = 0.2*v_here;
624  if (v_here < dvs1) {
625  dvm *= 0.5;
626  }
627  if (v_here < dvs2) {
628  dvm *= 0.5;
629  }
630  if (kbr != 0) {
631  double vt = v_here + dv;
632  if ((vt < Vmin) || (vt > Vmax)) {
633  dv = Vmin + (Pressure - Pmin)*(Vmax - Vmin)/(Pmax - Pmin) - v_here;
634  }
635  }
636  double dva = fabs(dv);
637  if (dva > dvm) {
638  dv *= dvm/dva;
639  }
640  v_here += dv;
641  if (dv == 0.0) {
642  throw TPX_Error("Substance::set_TPp","dv = 0 and no convergence");
643  }
644  Set(PropertyPair::TV, Temp, v_here);
645  LoopCount++;
646  if (LoopCount > 100) {
647  Set(PropertyPair::TV, Temp, v_save);
648  throw TPX_Error("Substance::set_TPp",string("no convergence for ")
649  +"P* = "+fp2str(Pressure/Pcrit())+". V* = "
650  +fp2str(v_save/Vcrit()));
651  }
652  }
653  Set(PropertyPair::TV, Temp,v_here);
654 }
655 }
T clip(const T &value, const T &lower, const T &upper)
Clip value such that lower <= value <= upper.
Definition: global.h:324
This file contains definitions for utility functions and text for modules, inputfiles, logs, textlogs, HTML_logs (see Input File Handling, Diagnostic Output, Writing messages to the screen and Writing HTML Logfiles).
const doublereal Undef
Fairly random number to be used to initialize variables against to see if they are subsequently defin...
Definition: ct_defs.h:147
std::string fp2str(const double x, const std::string &fmt)
Convert a double into a c++ string.
Definition: stringUtils.cpp:29
Contains declarations for string manipulation functions within Cantera.