19 std::string propertySymbols[] = {
"H",
"S",
"U",
"V",
"P",
"T"};
24 Substance::Substance() :
32 m_entropy_offset(0.0),
37 void Substance::setStdState(
double h0,
double s0,
double t0,
double p0)
39 Set(PropertyPair::TP, t0, p0);
42 double hoff = h0 - hh;
43 double soff = s0 - ss;
44 m_entropy_offset += soff;
45 m_energy_offset += hoff;
50 return TwoPhase() ? Ps() : Pp();
53 const double DeltaT = 0.000001;
55 double Substance::cv()
57 double Tsave = T, dt = 1.e-4*T;
59 double T1 = std::max(Tmin(), Tsave - dt);
60 double T2 = std::min(Tmax(), Tsave + dt);
64 if ((x0 == 1.0 || x0 == 0.0) && x1 != x0) {
74 if ((x0 == 1.0 || x0 == 0.0) && x2 != x0) {
83 return T*(s2 - s1)/(T2-T1);
86 double Substance::cp()
88 double Tsave = T, dt = 1.e-4*T;
89 double T1 = std::max(Tmin(), Tsave - dt);
90 double T2 = std::min(Tmax(), Tsave + dt);
95 return std::numeric_limits<double>::infinity();
98 Set(PropertyPair::TP, T1, p0);
104 Set(PropertyPair::TP, T1, p0);
108 Set(PropertyPair::TP, T2, p0);
114 Set(PropertyPair::TP, T2, p0);
118 Set(PropertyPair::TP, Tsave, p0);
119 return T*(s2 - s1)/(T2-T1);
122 double Substance::thermalExpansionCoeff()
124 double Tsave = T, dt = 1.e-4*T;
125 double T1 = std::max(Tmin(), Tsave - dt);
126 double T2 = std::min(Tmax(), Tsave + dt);
133 return std::numeric_limits<double>::infinity();
136 Set(PropertyPair::TP, T1, p0);
142 Set(PropertyPair::TP, T1, p0);
146 Set(PropertyPair::TP, T2, p0);
152 Set(PropertyPair::TP, T2, p0);
156 Set(PropertyPair::TP, Tsave, p0);
157 return 2.0*(v2 - v1)/((v2 + v1)*(T2-T1));
160 double Substance::isothermalCompressibility()
162 double Psave = P(), dp = 1.e-4*Psave;
167 return std::numeric_limits<double>::infinity();
171 double P1 = Psave - dp;
172 double P2 = Psave + dp;
174 Set(PropertyPair::TP, T, P1);
180 Set(PropertyPair::TP, T, P1);
184 Set(PropertyPair::TP, T, P2);
190 Set(PropertyPair::TP, T, P2);
194 Set(PropertyPair::TP, T, Psave);
195 return -(v2 - v1)/(v0*(P2-P1));
198 double Substance::dPsdT()
203 double dpdt = (Ps() - ps1)/DeltaT;
208 int Substance::TwoPhase()
214 return ((Rho < Rhf) && (Rho > Rhv) ? 1 : 0);
217 double Substance::x()
220 return (1.0/Rho < Vcrit() ? 0.0 : 1.0);
225 }
else if (Rho >= Rhf) {
230 return (1.0/Rho - vl)/(vv - vl);
235 double Substance::Tsat(
double p)
237 if (p <= 0.0 || p > Pcrit()) {
238 throw CanteraError(
"Substance::Tsat",
"illegal pressure value");
241 double tol = 1.e-6*p;
244 T = 0.5*(Tcrit() - Tmin());
247 T = 0.5*(Tcrit() - Tmin());
250 while (fabs(dp) > tol) {
258 double dt = dp/dPsdT();
259 double dta = fabs(dt);
266 if (LoopCount > 100) {
268 throw CanteraError(
"Substance::Tsat",
"No convergence");
277 static const double TolAbsH = 1.e-4;
278 static const double TolAbsU = 1.e-4;
279 static const double TolAbsS = 1.e-7;
280 static const double TolAbsP = 0.0;
281 static const double TolAbsV = 1.e-8;
282 static const double TolAbsT = 1.e-3;
283 static const double TolRel = 1.e-8;
285 void Substance::Set(PropertyPair::type XY,
double x0,
double y0)
292 XY =
static_cast<PropertyPair::type
>(-XY);
296 case PropertyPair::TV:
300 case PropertyPair::HP:
301 if (Lever(Pgiven, y0, x0, propertyFlag::H)) {
304 set_xy(propertyFlag::H, propertyFlag::P,
305 x0, y0, TolAbsH, TolAbsP, TolRel, TolRel);
307 case PropertyPair::SP:
308 if (Lever(Pgiven, y0, x0, propertyFlag::S)) {
311 set_xy(propertyFlag::S, propertyFlag::P,
312 x0, y0, TolAbsS, TolAbsP, TolRel, TolRel);
314 case PropertyPair::PV:
315 if (Lever(Pgiven, x0, y0, propertyFlag::V)) {
318 set_xy(propertyFlag::P, propertyFlag::V,
319 x0, y0, TolAbsP, TolAbsV, TolRel, TolRel);
321 case PropertyPair::TP:
325 Set(PropertyPair::TX, x0, 1.0);
327 Set(PropertyPair::TX, x0, 0.0);
332 set_xy(propertyFlag::T, propertyFlag::P,
333 x0, y0, TolAbsT, TolAbsP, TolRel, TolRel);
335 case PropertyPair::UV:
336 set_xy(propertyFlag::U, propertyFlag::V,
337 x0, y0, TolAbsU, TolAbsV, TolRel, TolRel);
339 case PropertyPair::ST:
340 if (Lever(Tgiven, y0, x0, propertyFlag::S)) {
343 set_xy(propertyFlag::S, propertyFlag::T,
344 x0, y0, TolAbsS, TolAbsT, TolRel, TolRel);
346 case PropertyPair::SV:
347 set_xy(propertyFlag::S, propertyFlag::V,
348 x0, y0, TolAbsS, TolAbsV, TolRel, TolRel);
350 case PropertyPair::UP:
351 if (Lever(Pgiven, y0, x0, propertyFlag::U)) {
354 set_xy(propertyFlag::U, propertyFlag::P,
355 x0, y0, TolAbsU, TolAbsP, TolRel, TolRel);
357 case PropertyPair::VH:
358 set_xy(propertyFlag::V, propertyFlag::H,
359 x0, y0, TolAbsV, TolAbsH, TolRel, TolRel);
361 case PropertyPair::TH:
362 set_xy(propertyFlag::T, propertyFlag::H,
363 x0, y0, TolAbsT, TolAbsH, TolRel, TolRel);
365 case PropertyPair::SH:
366 set_xy(propertyFlag::S, propertyFlag::H,
367 x0, y0, TolAbsS, TolAbsH, TolRel, TolRel);
369 case PropertyPair::PX:
371 if (y0 > 1.0 || y0 < 0.0) {
373 "Invalid vapor fraction, {}", y0);
374 }
else if (temp >= Tcrit()) {
376 "Can't set vapor fraction above the critical point");
380 Rho = 1.0/((1.0 - y0)/Rhf + y0/Rhv);
383 case PropertyPair::TX:
384 if (y0 > 1.0 || y0 < 0.0) {
386 "Invalid vapor fraction, {}", y0);
387 }
else if (x0 >= Tcrit()) {
389 "Can't set vapor fraction above the critical point");
393 Rho = 1.0/((1.0 - y0)/Rhf + y0/Rhv);
403 void Substance::set_Rho(
double r0)
408 throw CanteraError(
"Substance::set_Rho",
"Invalid density: {}", r0);
412 void Substance::set_T(
double t0)
414 if ((t0 >= Tmin()) && (t0 <= Tmax())) {
417 throw CanteraError(
"Substance::set_T",
"illegal temperature: {}", t0);
421 void Substance::set_v(
double v0)
427 "negative specific volume: {}", v0);
431 double Substance::Ps()
433 if (T < Tmin() || T > Tcrit()) {
435 "illegal temperature value {}", T);
441 void Substance::update_sat()
443 if ((T != Tslast) && (T < Tcrit())) {
444 double Rho_save = Rho;
446 double lps = log(pp);
449 for (i = 0; i<20; i++) {
458 double gf = hp() - T*sp();
460 Rho = pp*MolWt()/(GasConstant*T);
467 double gv = hp() - T*sp();
474 if (fabs(dg) < 0.001 && Rhf > Rhv) {
477 double dp = dg/(1.0/Rhv - 1.0/Rhf);
480 lps -= dg/(pp*(1.0/Rhv - 1.0/Rhf));
487 pp = psold + 0.5*(Pcrit() - psold);
489 }
else if (pp < 0.0) {
496 "wrong root found for sat. liquid or vapor at P = {}", pp);
500 throw CanteraError(
"substance::update_sat",
"no convergence");
509 double Substance::vprop(propertyFlag::type ijob)
512 case propertyFlag::H:
514 case propertyFlag::S:
516 case propertyFlag::U:
518 case propertyFlag::V:
520 case propertyFlag::P:
523 throw CanteraError(
"Substance::vprop",
"invalid job index");
527 int Substance::Lever(
int itp,
double sat,
double val, propertyFlag::type ifunc)
531 double Rhosave = Rho;
533 if (sat >= Tcrit()) {
538 }
else if (itp == Pgiven) {
539 if (sat >= Pcrit()) {
554 Set(PropertyPair::TX, T, 1.0);
555 double Valg = vprop(ifunc);
556 Set(PropertyPair::TX, T, 0.0);
557 double Valf = vprop(ifunc);
558 if (val >= Valf && val <= Valg) {
559 double xx = (val - Valf)/(Valg - Valf);
560 double vv = (1.0 - xx)/Rhf + xx/Rhv;
570 void Substance::set_xy(propertyFlag::type ifx, propertyFlag::type ify,
572 double atx,
double aty,
573 double rtx,
double rty)
575 double v_here, t_here;
576 double dvs1 = 2.0*Vcrit();
577 double dvs2 = 0.7*Vcrit();
580 double v_save = 1.0/Rho;
585 Set(PropertyPair::TV,Tcrit()*1.1,Vcrit()*1.1);
588 }
else if (Rho ==
Undef) {
590 Set(PropertyPair::TV,T,Vcrit()*1.1);
601 double x_here = prop(ifx);
602 double y_here = prop(ify);
603 double err_x = fabs(X - x_here);
604 double err_y = fabs(Y - y_here);
606 if ((err_x < atx + rtx*Xa) && (err_y < aty + rty*Ya)) {
611 double dt = 0.001*t_here;
612 if (t_here + dt > Tmax()) {
617 double dv = 0.001*v_here;
618 if (v_here <= Vcrit()) {
623 Set(PropertyPair::TV, t_here + dt, v_here);
624 double dxdt = (prop(ifx) - x_here)/dt;
625 double dydt = (prop(ify) - y_here)/dt;
628 Set(PropertyPair::TV, t_here, v_here + dv);
629 double dxdv = (prop(ifx) - x_here)/dv;
630 double dydv = (prop(ify) - y_here)/dv;
632 double det = dxdt*dydv - dydt*dxdv;
633 dt = ((X - x_here)*dydv - (Y - y_here)*dxdv)/det;
634 dv = ((Y - y_here)*dxdt - (X - x_here)*dydt)/det;
636 double dvm = 0.2*v_here;
643 double dtm = 0.1*t_here;
644 double dva = fabs(dv);
645 double dta = fabs(dt);
654 t_here =
clip(t_here, Tmin(), Tmax());
658 Set(PropertyPair::TV, t_here, v_here);
660 if (LoopCount > 200) {
661 std::string msg = fmt::format(
"No convergence. {} = {}, {} = {}",
662 propertySymbols[ifx], X, propertySymbols[ify], Y);
663 if (t_here == Tmin()) {
664 msg += fmt::format(
"\nAt temperature limit (Tmin = {})", Tmin());
665 }
else if (t_here == Tmax()) {
666 msg += fmt::format(
"\nAt temperature limit (Tmax = {})", Tmax());
673 double Substance::prop(propertyFlag::type ijob)
675 if (ijob == propertyFlag::P) {
678 if (ijob == propertyFlag::T) {
682 if ((xx > 0.0) && (xx < 1.0)) {
683 double Rho_save = Rho;
685 double vp = vprop(ijob);
687 double lp = vprop(ijob);
688 double pp = (1.0 - xx)*lp + xx*vp;
696 static const double ErrP = 1.e-7;
697 static const double Big = 1.e30;
699 void Substance::BracketSlope(
double Pressure)
702 dv = (v_here < Vcrit() ? -0.05*v_here : 0.2*v_here);
710 double dpdv = (Pmax - Pmin)/(Vmax - Vmin);
713 dv = dvbf*(Pressure - P_here)/dpdv;
718 void Substance::set_TPp(
double Temp,
double Pressure)
726 double dvs1 = 2.0*Vcrit();
727 double dvs2 = 0.7*Vcrit();
730 double v_save = 1.0/Rho;
735 while (P_here = Pp(),
736 fabs(Pressure - P_here) >= ErrP* Pressure || LoopCount == 0) {
738 BracketSlope(Pressure);
741 if (v_here <= Vcrit()) {
744 Set(PropertyPair::TV, Temp, v_here+dv);
745 double dpdv = (Pp() - P_here)/dv;
747 BracketSlope(Pressure);
749 if ((P_here > Pressure) && (v_here > Vmin)) {
751 }
else if ((P_here < Pressure) && (v_here < Vmax)) {
754 if (v_here == Vmin) {
757 if (v_here == Vmax) {
761 throw CanteraError(
"Substance::set_TPp",
"Vmin >= Vmax");
762 }
else if ((Vmin > 0.0) && (Vmax < Big)) {
768 BracketSlope(Pressure);
770 dv = (Pressure - P_here)/dpdv;
774 double dvm = 0.2*v_here;
782 double vt = v_here + dv;
783 if ((vt < Vmin) || (vt > Vmax)) {
784 dv = Vmin + (Pressure - Pmin)*(Vmax - Vmin)/(Pmax - Pmin) - v_here;
787 double dva = fabs(dv);
793 throw CanteraError(
"Substance::set_TPp",
"dv = 0 and no convergence");
795 Set(PropertyPair::TV, Temp, v_here);
797 if (LoopCount > 100) {
798 Set(PropertyPair::TV, Temp, v_save);
800 "no convergence for P* = {}, V* = {}",
801 Pressure/Pcrit(), v_save/Vcrit());
804 Set(PropertyPair::TV, Temp,v_here);
T clip(const T &value, const T &lower, const T &upper)
Clip value such that lower <= value <= upper.
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...
Base class for exceptions thrown by Cantera classes.
Contains declarations for string manipulation functions within Cantera.
Namespace for the Cantera kernel.