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