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