Cantera 2.6.0
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
14using std::string;
15using namespace Cantera;
16
17namespace {
18// these correspond to ordering withing propertyFlag::type
19std::string propertySymbols[] = {"H", "S", "U", "V", "P", "T"};
20}
21
22namespace tpx
23{
24Substance::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
37void 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
48double Substance::P()
49{
50 return TwoPhase() ? Ps() : Pp();
51}
52
53const double DeltaT = 0.000001;
54
55double 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
93double 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
145double 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
198double 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
246double 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
263int 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
275double 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
293double 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
338static const double TolAbsH = 1.e-4; // J/kg
339static const double TolAbsU = 1.e-4;
340static const double TolAbsS = 1.e-7;
341static const double TolAbsP = 0.0; // Pa, this is supposed to be zero
342static const double TolAbsV = 1.e-8;
343static const double TolAbsT = 1.e-3;
344static const double TolRel = 1.e-8;
345
346void 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
470void 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
479void 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
488void 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
498double 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
508void 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
572double 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
590int 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
633void 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
736double 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
759static const double ErrP = 1.e-7;
760static const double Big = 1.e30;
761
762void 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
781void 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,...
Namespace for the Cantera kernel.
Definition: AnyMap.h:29
const double Undef
Fairly random number to be used to initialize variables against to see if they are subsequently defin...
Definition: ct_defs.h:161
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition: ct_defs.h:113
T clip(const T &value, const T &lower, const T &upper)
Clip value such that lower <= value <= upper.
Definition: global.h:356
Contains declarations for string manipulation functions within Cantera.