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