Cantera  2.5.1
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 
7 // This file is part of Cantera. See License.txt in the top-level directory or
8 // at https://cantera.org/license.txt for license and copyright information.
9 
10 #include "cantera/tpx/Sub.h"
12 #include "cantera/base/global.h"
13 
14 using std::string;
15 using namespace Cantera;
16 
17 namespace {
18 // these correspond to ordering withing propertyFlag::type
19 std::string propertySymbols[] = {"H", "S", "U", "V", "P", "T"};
20 }
21 
22 namespace tpx
23 {
24 Substance::Substance() :
25  T(Undef),
26  Rho(Undef),
27  Tslast(Undef),
28  Rhf(Undef),
29  Rhv(Undef),
30  Pst(Undef),
31  m_energy_offset(0.0),
32  m_entropy_offset(0.0),
33  kbr(0)
34 {
35 }
36 
37 void Substance::setStdState(double h0, double s0, double t0, double p0)
38 {
39  Set(PropertyPair::TP, t0, p0);
40  double hh = h();
41  double ss = s();
42  double hoff = h0 - hh;
43  double soff = s0 - ss;
44  m_entropy_offset += soff;
45  m_energy_offset += hoff;
46 }
47 
48 double Substance::P()
49 {
50  return TwoPhase() ? Ps() : Pp();
51 }
52 
53 const double DeltaT = 0.000001;
54 
55 double Substance::cv()
56 {
57  if (TwoPhase(true)) {
58  // While cv can be calculated for the two-phase region (the state can
59  // always be continuously varied along an isochor on a T-v diagram),
60  // this calculation is currently not implemented
61  return std::numeric_limits<double>::quiet_NaN();
62  }
63 
64  double Tsave = T, dt = 1.e-4 * T;
65  double x0 = x();
66  double T1 = std::max(Tmin(), Tsave - dt);
67  double T2 = std::min(Tmax(), Tsave + dt);
68 
69  set_T(T1);
70  double x1 = x();
71  if ((x0 == 1.0 || x0 == 0.0) && x1 != x0) {
72  // If the initial state was pure liquid or pure vapor, and the state at
73  // T-dT is not, just take a one-sided difference
74  T1 = Tsave;
75  set_T(T1);
76  }
77  double s1 = s();
78 
79  set_T(T2);
80  double x2 = x();
81  if ((x0 == 1.0 || x0 == 0.0) && x2 != x0) {
82  // If the initial state was pure liquid or pure vapor, and the state at
83  // T+dT is not, just take a one-sided difference
84  T2 = Tsave;
85  set_T(T2);
86  }
87  double s2 = s();
88 
89  set_T(Tsave);
90  return T*(s2 - s1)/(T2-T1);
91 }
92 
93 double Substance::cp()
94 {
95  if (TwoPhase(true)) {
96  // In the two-phase region, cp is infinite
97  return std::numeric_limits<double>::infinity();
98  }
99 
100  double Tsave = T, dt = 1.e-4 * T;
101  double RhoSave = Rho;
102  double T1, T2, s1, s2;
103  double p0 = P();
104 
105  if (Rho >= Rhf) {
106  // initial state is pure liquid
107  T1 = std::max(Tmin(), Tsave - dt);
108  Set(PropertyPair::TP, T1, p0);
109  s1 = s();
110  try {
111  T2 = std::min(Tsat(p0), Tsave + dt);
112  } catch (CanteraError&) {
113  // Tsat does not exist beyond two-phase region
114  T2 = Tsave + dt;
115  }
116  if (T2 < Tsave + dt) {
117  Set(PropertyPair::TX, T2, 0.);
118  } else {
119  Set(PropertyPair::TP, T2, p0);
120  }
121  s2 = s();
122  } else {
123  // initial state is pure vapor
124  try {
125  T1 = std::max(Tsat(p0), Tsave - dt);
126  } catch (CanteraError&) {
127  // Tsat does not exist beyond two-phase region
128  T1 = Tsave - dt;
129  }
130  if (T1 > Tsave - dt) {
131  Set(PropertyPair::TX, T1, 1.);
132  } else {
133  Set(PropertyPair::TP, T1, p0);
134  }
135  s1 = s();
136  T2 = std::min(Tmax(), Tsave + dt);
137  Set(PropertyPair::TP, T2, p0);
138  s2 = s();
139  }
140 
141  Set(PropertyPair::TV, Tsave, 1.0 / RhoSave);
142  return T * (s2 - s1) / (T2 - T1);
143 }
144 
145 double Substance::thermalExpansionCoeff()
146 {
147  if (TwoPhase(true)) {
148  // In the two-phase region, the thermal expansion coefficient is
149  // infinite
150  return std::numeric_limits<double>::infinity();
151  }
152 
153  double Tsave = T, dt = 1.e-4 * T;
154  double RhoSave = Rho;
155  double T1, T2, v1, v2;
156  double p0 = P();
157 
158  if (Rho >= Rhf) {
159  // initial state is pure liquid
160  T1 = std::max(Tmin(), Tsave - dt);
161  Set(PropertyPair::TP, T1, p0);
162  v1 = v();
163  try {
164  T2 = std::min(Tsat(p0), Tsave + dt);
165  } catch (CanteraError&) {
166  // Tsat does not exist beyond two-phase region
167  T2 = Tsave + dt;
168  }
169  if (T2 < Tsave + dt) {
170  Set(PropertyPair::TX, T2, 0.);
171  } else {
172  Set(PropertyPair::TP, T2, p0);
173  }
174  v2 = v();
175  } else {
176  // initial state is pure vapor
177  try {
178  T1 = std::max(Tsat(p0), Tsave - dt);
179  } catch (CanteraError&) {
180  // Tsat does not exist beyond two-phase region
181  T1 = Tsave - dt;
182  }
183  if (T1 > Tsave - dt) {
184  Set(PropertyPair::TX, T1, 1.);
185  } else {
186  Set(PropertyPair::TP, T1, p0);
187  }
188  v1 = v();
189  T2 = std::min(Tmax(), Tsave + dt);
190  Set(PropertyPair::TP, T2, p0);
191  v2 = v();
192  }
193 
194  Set(PropertyPair::TV, Tsave, 1.0 / RhoSave);
195  return 2.0 * (v2 - v1) / ((v2 + v1) * (T2 - T1));
196 }
197 
198 double Substance::isothermalCompressibility()
199 {
200  if (TwoPhase(true)) {
201  // In the two-phase region, the isothermal compressibility is infinite
202  return std::numeric_limits<double>::infinity();
203  }
204 
205  double Psave = P(), dp = 1.e-4 * Psave;
206  double RhoSave = Rho;
207  double P1, P2, v1, v2;
208  double v0 = v();
209 
210  if (Rho >= Rhf) {
211  // initial state is pure liquid
212  P1 = Psave - dp;
213  if (T > Tmin() && T <= Tcrit()) {
214  P1 = std::max(Ps(), P1);
215  }
216  if (P1 > Psave - dp) {
217  Set(PropertyPair::PX, P1, 0.);
218  } else {
219  Set(PropertyPair::TP, T, P1);
220  }
221  v1 = v();
222  P2 = Psave + dp;
223  Set(PropertyPair::TP, T, P2);
224  v2 = v();
225  } else {
226  // initial state is pure vapor
227  P1 = std::max(1.e-7, Psave - dp);
228  Set(PropertyPair::TP, T, P1);
229  v1 = v();
230  P2 = Psave + dp;
231  if (T > Tmin() && T <= Tcrit()) {
232  P2 = std::min(Ps(), P2);
233  }
234  if (P2 < Psave + dp) {
235  Set(PropertyPair::PX, P2, 1.);
236  } else {
237  Set(PropertyPair::TP, T, P2);
238  }
239  v2 = v();
240  }
241 
242  Set(PropertyPair::TV, T, 1.0 / RhoSave);
243  return -(v2 - v1) / (v0 * (P2 - P1));
244 }
245 
246 double Substance::dPsdT()
247 {
248  double tsave = T;
249  double ps1 = Ps();
250  double dpdt;
251  if (T + DeltaT < Tcrit()) {
252  T += DeltaT;
253  dpdt = (Ps() - ps1)/DeltaT;
254  } else {
255  T -= DeltaT;
256  // Ps() will fail for illegal temperature
257  dpdt = (ps1 - Ps())/DeltaT;
258  }
259  T = tsave;
260  return dpdt;
261 }
262 
263 int Substance::TwoPhase(bool strict)
264 {
265  if (T >= Tcrit()) {
266  return 0;
267  }
268  update_sat();
269  if (strict) {
270  return ((Rho > Rhv) && (Rho < Rhf) ? 1 : 0);
271  }
272  return ((Rho >= Rhv) && (Rho <= Rhf) ? 1 : 0);
273 }
274 
275 double Substance::x()
276 {
277  if (T >= Tcrit()) {
278  return (1.0/Rho < Vcrit() ? 0.0 : 1.0);
279  } else {
280  update_sat();
281  if (Rho <= Rhv) {
282  return 1.0;
283  } else if (Rho >= Rhf) {
284  return 0.0;
285  } else {
286  double vv = 1.0/Rhv;
287  double vl = 1.0/Rhf;
288  return (1.0/Rho - vl)/(vv - vl);
289  }
290  }
291 }
292 
293 double Substance::Tsat(double p)
294 {
295  double Tsave = T;
296  if (p <= 0. || p > Pcrit()) {
297  // @TODO: this check should also fail below the triple point pressure
298  // (calculated as Ps() for Tmin() as it is not available as a numerical
299  // parameter). A bug causing low-temperature TP setters to fail for
300  // Heptane (GitHub issue #605) prevents this at the moment.
301  // Without this check, a calculation attempt fails with the less
302  // meaningful error "No convergence" below.
303  throw CanteraError("Substance::Tsat",
304  "Illegal pressure value: {}", p);
305  }
306  T = Tsave;
307  Tsave = T;
308 
309  int LoopCount = 0;
310  double tol = 1.e-6*p;
311  if (T < Tmin() || T > Tcrit()) {
312  T = 0.5*(Tcrit() - Tmin());
313  }
314  double dp = 10*tol;
315  while (fabs(dp) > tol) {
316  T = std::min(T, Tcrit());
317  T = std::max(T, Tmin());
318  dp = p - Ps();
319  double dt = dp/dPsdT();
320  double dta = fabs(dt);
321  double dtm = 0.1*T;
322  if (dta > dtm) {
323  dt = dt*dtm/dta;
324  }
325  T += dt;
326  LoopCount++;
327  if (LoopCount > 100) {
328  T = Tsave;
329  throw CanteraError("Substance::Tsat", "No convergence: p = {}", p);
330  }
331  }
332  double tsat = T;
333  T = Tsave;
334  return tsat;
335 }
336 
337 // property tolerances
338 static const double TolAbsH = 1.e-4; // J/kg
339 static const double TolAbsU = 1.e-4;
340 static const double TolAbsS = 1.e-7;
341 static const double TolAbsP = 0.0; // Pa, this is supposed to be zero
342 static const double TolAbsV = 1.e-8;
343 static const double TolAbsT = 1.e-3;
344 static const double TolRel = 1.e-8;
345 
346 void Substance::Set(PropertyPair::type XY, double x0, double y0)
347 {
348  double temp;
349 
350  /* if inverted (PT) switch order and change sign of XY (TP = -PT) */
351  if (XY < 0) {
352  std::swap(x0, y0);
353  XY = static_cast<PropertyPair::type>(-XY);
354  }
355 
356  switch (XY) {
357  case PropertyPair::TV:
358  set_T(x0);
359  set_v(y0);
360  break;
361  case PropertyPair::HP:
362  if (Lever(Pgiven, y0, x0, propertyFlag::H)) {
363  return;
364  }
365  set_xy(propertyFlag::H, propertyFlag::P,
366  x0, y0, TolAbsH, TolAbsP, TolRel, TolRel);
367  break;
368  case PropertyPair::SP:
369  if (Lever(Pgiven, y0, x0, propertyFlag::S)) {
370  return;
371  }
372  set_xy(propertyFlag::S, propertyFlag::P,
373  x0, y0, TolAbsS, TolAbsP, TolRel, TolRel);
374  break;
375  case PropertyPair::PV:
376  if (Lever(Pgiven, x0, y0, propertyFlag::V)) {
377  return;
378  }
379  set_xy(propertyFlag::P, propertyFlag::V,
380  x0, y0, TolAbsP, TolAbsV, TolRel, TolRel);
381  break;
382  case PropertyPair::TP:
383  set_T(x0);
384  if (x0 < Tcrit()) {
385  if (fabs(y0 - Ps()) / y0 < TolRel) {
386  throw CanteraError("Substance::Set",
387  "Saturated mixture detected: use vapor "
388  "fraction to specify state instead");
389  } else if (y0 < Ps()) {
390  Set(PropertyPair::TX, x0, 1.0);
391  } else {
392  Set(PropertyPair::TX, x0, 0.0);
393  }
394  }
395  set_xy(propertyFlag::T, propertyFlag::P,
396  x0, y0, TolAbsT, TolAbsP, TolRel, TolRel);
397  break;
398  case PropertyPair::UV:
399  set_xy(propertyFlag::U, propertyFlag::V,
400  x0, y0, TolAbsU, TolAbsV, TolRel, TolRel);
401  break;
402  case PropertyPair::ST:
403  if (Lever(Tgiven, y0, x0, propertyFlag::S)) {
404  return;
405  }
406  set_xy(propertyFlag::S, propertyFlag::T,
407  x0, y0, TolAbsS, TolAbsT, TolRel, TolRel);
408  break;
409  case PropertyPair::SV:
410  set_xy(propertyFlag::S, propertyFlag::V,
411  x0, y0, TolAbsS, TolAbsV, TolRel, TolRel);
412  break;
413  case PropertyPair::UP:
414  if (Lever(Pgiven, y0, x0, propertyFlag::U)) {
415  return;
416  }
417  set_xy(propertyFlag::U, propertyFlag::P,
418  x0, y0, TolAbsU, TolAbsP, TolRel, TolRel);
419  break;
420  case PropertyPair::VH:
421  set_xy(propertyFlag::V, propertyFlag::H,
422  x0, y0, TolAbsV, TolAbsH, TolRel, TolRel);
423  break;
424  case PropertyPair::TH:
425  set_xy(propertyFlag::T, propertyFlag::H,
426  x0, y0, TolAbsT, TolAbsH, TolRel, TolRel);
427  break;
428  case PropertyPair::SH:
429  set_xy(propertyFlag::S, propertyFlag::H,
430  x0, y0, TolAbsS, TolAbsH, TolRel, TolRel);
431  break;
432  case PropertyPair::PX:
433  temp = Tmin();
434  set_T(temp);
435  if (y0 > 1.0 || y0 < 0.0) {
436  throw CanteraError("Substance::Set",
437  "Invalid vapor fraction, {}", y0);
438  }
439  if (x0 < Ps() || x0 > Pcrit()) {
440  throw CanteraError("Substance::Set",
441  "Illegal pressure value: {} (supercritical "
442  "or below triple point)", x0);
443  }
444  temp = Tsat(x0);
445  set_T(temp);
446  update_sat();
447  Rho = 1.0/((1.0 - y0)/Rhf + y0/Rhv);
448  break;
449  case PropertyPair::TX:
450  if (y0 > 1.0 || y0 < 0.0) {
451  throw CanteraError("Substance::Set",
452  "Invalid vapor fraction, {}", y0);
453  }
454  if (x0 < Tmin() || x0 > Tcrit()) {
455  throw CanteraError("Substance::Set",
456  "Illegal temperature value: {} "
457  "(supercritical or below triple point)", x0);
458  }
459  set_T(x0);
460  update_sat();
461  Rho = 1.0/((1.0 - y0)/Rhf + y0/Rhv);
462  break;
463  default:
464  throw CanteraError("Substance::Set", "Invalid input.");
465  }
466 }
467 
468 //------------------ Protected and Private Functions -------------------
469 
470 void Substance::set_Rho(double r0)
471 {
472  if (r0 > 0.0) {
473  Rho = r0;
474  } else {
475  throw CanteraError("Substance::set_Rho", "Invalid density: {}", r0);
476  }
477 }
478 
479 void Substance::set_T(double t0)
480 {
481  if ((t0 >= Tmin()) && (t0 <= Tmax())) {
482  T = t0;
483  } else {
484  throw CanteraError("Substance::set_T", "Illegal temperature: {}", t0);
485  }
486 }
487 
488 void Substance::set_v(double v0)
489 {
490  if (v0 > 0) {
491  Rho = 1.0/v0;
492  } else {
493  throw CanteraError("Substance::set_v",
494  "Negative specific volume: {}", v0);
495  }
496 }
497 
498 double Substance::Ps()
499 {
500  if (T < Tmin() || T > Tcrit()) {
501  throw CanteraError("Substance::Ps",
502  "Illegal temperature value: {}", T);
503  }
504  update_sat();
505  return Pst;
506 }
507 
508 void Substance::update_sat()
509 {
510  if (T != Tslast) {
511  double Rho_save = Rho;
512  double pp = Psat(); // illegal temperatures are caught by Psat()
513  double lps = log(pp);
514  // trial value = Psat from correlation
515  int i;
516  for (i = 0; i<20; i++) {
517  if (i==0) {
518  Rho = ldens(); // trial value = liquid density
519  } else {
520  Rho = Rhf;
521  }
522  set_TPp(T,pp);
523  Rhf = Rho; // sat liquid density
524 
525  double gf = hp() - T*sp();
526  if (i==0) {
527  Rho = pp*MolWt()/(GasConstant*T); // trial value = ideal gas
528  } else {
529  Rho = Rhv;
530  }
531  set_TPp(T,pp);
532 
533  Rhv = Rho; // sat vapor density
534  double gv = hp() - T*sp();
535  double dg = gv - gf;
536  if (Rhv > Rhf) {
537  std::swap(Rhv, Rhf);
538  dg = - dg;
539  }
540 
541  if (fabs(dg) < 0.001) {
542  break;
543  }
544  double dp = dg/(1.0/Rhv - 1.0/Rhf);
545  double psold = pp;
546  if (fabs(dp) > pp) {
547  lps -= dg/(pp*(1.0/Rhv - 1.0/Rhf));
548  pp = exp(lps);
549  } else {
550  pp -= dp;
551  lps = log(pp);
552  }
553  if (pp > Pcrit()) {
554  pp = psold + 0.5*(Pcrit() - psold);
555  lps = log(pp);
556  } else if (pp < 0.0) {
557  pp = psold/2.0;
558  lps = log(pp);
559  }
560  }
561 
562  if (i >= 20) {
563  throw CanteraError("Substance::update_sat", "No convergence");
564  } else {
565  Pst = pp;
566  Tslast = T;
567  }
568  Rho = Rho_save;
569  }
570 }
571 
572 double Substance::vprop(propertyFlag::type ijob)
573 {
574  switch (ijob) {
575  case propertyFlag::H:
576  return hp();
577  case propertyFlag::S:
578  return sp();
579  case propertyFlag::U:
580  return up();
581  case propertyFlag::V:
582  return vp();
583  case propertyFlag::P:
584  return Pp();
585  default:
586  throw CanteraError("Substance::vprop", "Invalid job index");
587  }
588 }
589 
590 int Substance::Lever(int itp, double sat, double val, propertyFlag::type ifunc)
591 {
592  double psat;
593  double Tsave = T;
594  double Rhosave = Rho;
595  if (itp == Tgiven) {
596  if (sat >= Tcrit()) {
597  return 0;
598  }
599  T = sat;
600  psat = Ps();
601  } else if (itp == Pgiven) {
602  if (sat >= Pcrit()) {
603  return 0;
604  }
605  psat = sat;
606  try {
607  T = Tsat(psat);
608  } catch (CanteraError&) {
609  // Failure to converge here is not an error
610  T = Tsave;
611  Rho = Rhosave;
612  return 0;
613  }
614  } else {
615  throw CanteraError("Substance::Lever", "General error");
616  }
617  Set(PropertyPair::TX, T, 1.0);
618  double Valg = vprop(ifunc);
619  Set(PropertyPair::TX, T, 0.0);
620  double Valf = vprop(ifunc);
621  if (val >= Valf && val <= Valg) {
622  double xx = (val - Valf)/(Valg - Valf);
623  double vv = (1.0 - xx)/Rhf + xx/Rhv;
624  set_v(vv);
625  return 1;
626  } else {
627  T = Tsave;
628  Rho = Rhosave;
629  return 0;
630  }
631 }
632 
633 void Substance::set_xy(propertyFlag::type ifx, propertyFlag::type ify,
634  double X, double Y,
635  double atx, double aty,
636  double rtx, double rty)
637 {
638  double v_here, t_here;
639  double dvs1 = 2.0*Vcrit();
640  double dvs2 = 0.7*Vcrit();
641  int LoopCount = 0;
642 
643  double v_save = 1.0/Rho;
644  double t_save = T;
645 
646  if ((T == Undef) && (Rho == Undef)) {
647  // new object, try to pick a "reasonable" starting point
648  Set(PropertyPair::TV,Tcrit()*1.1,Vcrit()*1.1);
649  t_here = T;
650  v_here = 1.0/Rho;
651  } else if (Rho == Undef) {
652  // new object, try to pick a "reasonable" starting point
653  Set(PropertyPair::TV,T,Vcrit()*1.1);
654  t_here = T;
655  v_here = 1.0/Rho;
656  } else {
657  v_here = v_save;
658  t_here = t_save;
659  }
660 
661  double Xa = fabs(X);
662  double Ya = fabs(Y);
663  while (true) {
664  double x_here = prop(ifx);
665  double y_here = prop(ify);
666  double err_x = fabs(X - x_here);
667  double err_y = fabs(Y - y_here);
668 
669  if ((err_x < atx + rtx*Xa) && (err_y < aty + rty*Ya)) {
670  break;
671  }
672 
673  /* perturb t */
674  double dt = 0.001*t_here;
675  if (t_here + dt > Tmax()) {
676  dt *= -1.0;
677  }
678 
679  /* perturb v */
680  double dv = 0.001*v_here;
681  if (v_here <= Vcrit()) {
682  dv *= -1.0;
683  }
684 
685  /* derivatives with respect to T */
686  Set(PropertyPair::TV, t_here + dt, v_here);
687  double dxdt = (prop(ifx) - x_here)/dt;
688  double dydt = (prop(ify) - y_here)/dt;
689 
690  /* derivatives with respect to v */
691  Set(PropertyPair::TV, t_here, v_here + dv);
692  double dxdv = (prop(ifx) - x_here)/dv;
693  double dydv = (prop(ify) - y_here)/dv;
694 
695  double det = dxdt*dydv - dydt*dxdv;
696  dt = ((X - x_here)*dydv - (Y - y_here)*dxdv)/det;
697  dv = ((Y - y_here)*dxdt - (X - x_here)*dydt)/det;
698 
699  double dvm = 0.2*v_here;
700  if (v_here < dvs1) {
701  dvm *= 0.5;
702  }
703  if (v_here < dvs2) {
704  dvm *= 0.5;
705  }
706  double dtm = 0.1*t_here;
707  double dva = fabs(dv);
708  double dta = fabs(dt);
709  if (dva > dvm) {
710  dv *= dvm/dva;
711  }
712  if (dta > dtm) {
713  dt *= dtm/dta;
714  }
715  v_here += dv;
716  t_here += dt;
717  t_here = clip(t_here, Tmin(), Tmax());
718  if (v_here <= 0.0) {
719  v_here = 0.0001;
720  }
721  Set(PropertyPair::TV, t_here, v_here);
722  LoopCount++;
723  if (LoopCount > 200) {
724  std::string msg = fmt::format("No convergence. {} = {}, {} = {}",
725  propertySymbols[ifx], X, propertySymbols[ify], Y);
726  if (t_here == Tmin()) {
727  msg += fmt::format("\nAt temperature limit (Tmin = {})", Tmin());
728  } else if (t_here == Tmax()) {
729  msg += fmt::format("\nAt temperature limit (Tmax = {})", Tmax());
730  }
731  throw CanteraError("Substance::set_xy", msg);
732  }
733  }
734 }
735 
736 double Substance::prop(propertyFlag::type ijob)
737 {
738  if (ijob == propertyFlag::P) {
739  return P();
740  }
741  if (ijob == propertyFlag::T) {
742  return T;
743  }
744  double xx = x();
745  if ((xx > 0.0) && (xx < 1.0)) {
746  double Rho_save = Rho;
747  Rho = Rhv;
748  double vp = vprop(ijob);
749  Rho = Rhf;
750  double lp = vprop(ijob);
751  double pp = (1.0 - xx)*lp + xx*vp;
752  Rho = Rho_save;
753  return pp;
754  } else {
755  return vprop(ijob);
756  }
757 }
758 
759 static const double ErrP = 1.e-7;
760 static const double Big = 1.e30;
761 
762 void Substance::BracketSlope(double Pressure)
763 {
764  if (kbr == 0) {
765  dv = (v_here < Vcrit() ? -0.05*v_here : 0.2*v_here);
766  if (Vmin > 0.0) {
767  dv = 0.2*v_here;
768  }
769  if (Vmax < Big) {
770  dv = -0.05*v_here;
771  }
772  } else {
773  double dpdv = (Pmax - Pmin)/(Vmax - Vmin);
774  v_here = Vmax;
775  P_here = Pmax;
776  dv = dvbf*(Pressure - P_here)/dpdv;
777  dvbf = 0.5*dvbf;
778  }
779 }
780 
781 void Substance::set_TPp(double Temp, double Pressure)
782 {
783  kbr = 0;
784  dvbf = 1.0;
785  Vmin = 0.0;
786  Vmax = Big;
787  Pmin = Big;
788  Pmax = 0.0;
789  double dvs1 = 2.0*Vcrit();
790  double dvs2 = 0.7*Vcrit();
791  int LoopCount = 0;
792 
793  double v_save = 1.0/Rho;
794  T = Temp;
795  v_here = vp();
796 
797  // loop
798  while (P_here = Pp(),
799  fabs(Pressure - P_here) >= ErrP* Pressure || LoopCount == 0) {
800  if (P_here < 0.0) {
801  BracketSlope(Pressure);
802  } else {
803  dv = 0.001*v_here;
804  if (v_here <= Vcrit()) {
805  dv *= -1.0;
806  }
807  Set(PropertyPair::TV, Temp, v_here+dv);
808  double dpdv = (Pp() - P_here)/dv;
809  if (dpdv > 0.0) {
810  BracketSlope(Pressure);
811  } else {
812  if ((P_here > Pressure) && (v_here > Vmin)) {
813  Vmin = v_here;
814  } else if ((P_here < Pressure) && (v_here < Vmax)) {
815  Vmax = v_here;
816  }
817  if (v_here == Vmin) {
818  Pmin = P_here;
819  }
820  if (v_here == Vmax) {
821  Pmax = P_here;
822  }
823  if (Vmin >= Vmax) {
824  throw CanteraError("Substance::set_TPp", "Vmin >= Vmax");
825  } else if ((Vmin > 0.0) && (Vmax < Big)) {
826  kbr = 1;
827  }
828  dvbf = 1.0;
829  if (dpdv == 0.0) {
830  dvbf = 0.5;
831  BracketSlope(Pressure);
832  } else {
833  dv = (Pressure - P_here)/dpdv;
834  }
835  }
836  }
837  double dvm = 0.2*v_here;
838  if (v_here < dvs1) {
839  dvm *= 0.5;
840  }
841  if (v_here < dvs2) {
842  dvm *= 0.5;
843  }
844  if (kbr != 0) {
845  double vt = v_here + dv;
846  if ((vt < Vmin) || (vt > Vmax)) {
847  dv = Vmin + (Pressure - Pmin)*(Vmax - Vmin)/(Pmax - Pmin) - v_here;
848  }
849  }
850  double dva = fabs(dv);
851  if (dva > dvm) {
852  dv *= dvm/dva;
853  }
854  v_here += dv;
855  if (dv == 0.0) {
856  throw CanteraError("Substance::set_TPp", "dv = 0 and no convergence");
857  }
858  Set(PropertyPair::TV, Temp, v_here);
859  LoopCount++;
860  if (LoopCount > 200) {
861  Set(PropertyPair::TV, Temp, v_save);
862  throw CanteraError("Substance::set_TPp",
863  "No convergence for P = {}, T = {}\n"
864  "(P* = {}, V* = {})", Pressure, Temp,
865  Pressure/Pcrit(), v_save/Vcrit());
866  }
867  }
868  Set(PropertyPair::TV, Temp,v_here);
869 }
870 }
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
This file contains definitions for utility functions and text for modules, inputfiles,...
T clip(const T &value, const T &lower, const T &upper)
Clip value such that lower <= value <= upper.
Definition: global.h:300
const double Undef
Fairly random number to be used to initialize variables against to see if they are subsequently defin...
Definition: ct_defs.h:157
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition: ct_defs.h:109
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:264
Contains declarations for string manipulation functions within Cantera.