11 using namespace Cantera;
15 std::string propertySymbols[] = {
"H",
"S",
"U",
"V",
"P",
"T"};
20 Substance::Substance() :
28 m_entropy_offset(0.0),
35 return TwoPhase() ? Ps() : Pp();
38 const double DeltaT = 0.000001;
40 double Substance::dPsdT()
45 double dpdt = (Ps() - ps1)/DeltaT;
50 int Substance::TwoPhase()
56 return ((Rho < Rhf) && (Rho > Rhv) ? 1 : 0);
62 return (1.0/Rho < Vcrit() ? 0.0 : 1.0);
67 }
else if (Rho >= Rhf) {
72 return (1.0/Rho - vl)/(vv - vl);
77 double Substance::Tsat(
double p)
79 if (p <= 0.0 || p > Pcrit()) {
80 throw TPX_Error(
"Substance::Tsat",
"illegal pressure value");
86 T = 0.5*(Tcrit() - Tmin());
89 T = 0.5*(Tcrit() - Tmin());
100 double dt = dp/dPsdT();
101 double dta = fabs(dt);
108 if (LoopCount > 100) {
110 throw TPX_Error(
"Substance::Tsat",
"No convergence");
112 }
while (fabs(dp) > tol);
119 static const double TolAbsH = 0.0001;
120 static const double TolAbsU = 0.0001;
121 static const double TolAbsS = 1.e-7;
122 static const double TolAbsP = 0.000;
123 static const double TolAbsV = 1.e-8;
124 static const double TolAbsT = 1.e-3;
125 static const double TolRel = 3.e-8;
127 void Substance::Set(PropertyPair::type XY,
double x0,
double y0)
134 XY =
static_cast<PropertyPair::type
>(-XY);
138 case PropertyPair::TV:
143 case PropertyPair::HP:
144 if (Lever(Pgiven, y0, x0, propertyFlag::H)) {
147 set_xy(propertyFlag::H, propertyFlag::P,
148 x0, y0, TolAbsH, TolAbsP, TolRel, TolRel);
151 case PropertyPair::SP:
152 if (Lever(Pgiven, y0, x0, propertyFlag::S)) {
155 set_xy(propertyFlag::S, propertyFlag::P,
156 x0, y0, TolAbsS, TolAbsP, TolRel, TolRel);
159 case PropertyPair::PV:
160 if (Lever(Pgiven, x0, y0, propertyFlag::V)) {
163 set_xy(propertyFlag::P, propertyFlag::V,
164 x0, y0, TolAbsP, TolAbsV, TolRel, TolRel);
167 case PropertyPair::TP:
171 Set(PropertyPair::TX, x0, 1.0);
173 Set(PropertyPair::TX, x0, 0.0);
178 set_xy(propertyFlag::T, propertyFlag::P,
179 x0, y0, TolAbsT, TolAbsP, TolRel, TolRel);
182 case PropertyPair::UV:
183 set_xy(propertyFlag::U, propertyFlag::V,
184 x0, y0, TolAbsU, TolAbsV, TolRel, TolRel);
187 case PropertyPair::ST:
188 if (Lever(Tgiven, y0, x0, propertyFlag::S)) {
191 set_xy(propertyFlag::S, propertyFlag::T,
192 x0, y0, TolAbsS, TolAbsT, TolRel, TolRel);
195 case PropertyPair::SV:
196 set_xy(propertyFlag::S, propertyFlag::V,
197 x0, y0, TolAbsS, TolAbsV, TolRel, TolRel);
200 case PropertyPair::UP:
201 if (Lever(Pgiven, y0, x0, propertyFlag::U)) {
204 set_xy(propertyFlag::U, propertyFlag::P,
205 x0, y0, TolAbsU, TolAbsP, TolRel, TolRel);
208 case PropertyPair::VH:
209 set_xy(propertyFlag::V, propertyFlag::H,
210 x0, y0, TolAbsV, TolAbsH, TolRel, TolRel);
213 case PropertyPair::TH:
214 set_xy(propertyFlag::T, propertyFlag::H,
215 x0, y0, TolAbsT, TolAbsH, TolRel, TolRel);
218 case PropertyPair::SH:
219 set_xy(propertyFlag::S, propertyFlag::H,
220 x0, y0, TolAbsS, TolAbsH, TolRel, TolRel);
223 case PropertyPair::PX:
225 if (y0 > 1.0 || y0 < 0.0) {
226 throw TPX_Error(
"Substance::Set",
227 "Invalid vapor fraction, " +
fp2str(y0));
228 }
else if (temp >= Tcrit()) {
229 throw TPX_Error(
"Substance::Set",
230 "Can't set vapor fraction above the critical point");
234 Rho = 1.0/((1.0 - y0)/Rhf + y0/Rhv);
238 case PropertyPair::TX:
239 if (y0 > 1.0 || y0 < 0.0) {
240 throw TPX_Error(
"Substance::Set",
241 "Invalid vapor fraction, " +
fp2str(y0));
242 }
else if (x0 >= Tcrit()) {
243 throw TPX_Error(
"Substance::Set",
244 "Can't set vapor fraction above the critical point");
248 Rho = 1.0/((1.0 - y0)/Rhf + y0/Rhv);
253 throw TPX_Error(
"Substance::Set",
"Invalid input.");
259 void Substance::set_Rho(
double r0)
264 throw TPX_Error(
"Substance::set_Rho",
"Invalid density: " +
fp2str(r0));
268 void Substance::set_T(
double t0)
270 if ((t0 >= Tmin()) && (t0 <= Tmax())) {
273 throw TPX_Error(
"Substance::set_T",
"illegal temperature: " +
fp2str(t0));
277 void Substance::set_v(
double v0)
282 throw TPX_Error(
"Substance::set_v",
283 "negative specific volume: "+
fp2str(v0));
287 double Substance::Ps()
289 if (T < Tmin() || T > Tcrit()) {
290 throw TPX_Error(
"Substance::Ps",
291 "illegal temperature value "+
fp2str(T));
297 void Substance::update_sat()
299 if ((T != Tslast) && (T < Tcrit())) {
300 double Rho_save = Rho;
303 double lps = log(pp);
307 for (i = 0; i<20; i++) {
316 double gf = hp() - T*sp();
318 Rho = pp*MolWt()/(8314.0*T);
325 double gv = hp() - T*sp();
333 if (fabs(dg) < 0.001 && Rhf > Rhv) {
336 double dp = dg/(1.0/Rhv - 1.0/Rhf);
340 lps -= dg/(pp*(1.0/Rhv - 1.0/Rhf));
347 pp = psold + 0.5*(Pcrit() - psold);
349 }
else if (pp < 0.0) {
355 throw TPX_Error(
"Substance::update_sat",
356 "wrong root found for sat. liquid or vapor at P = "+
fp2str(pp));
360 throw TPX_Error(
"substance::update_sat",
"no convergence");
369 double Substance::vprop(propertyFlag::type ijob)
372 case propertyFlag::H:
374 case propertyFlag::S:
376 case propertyFlag::U:
378 case propertyFlag::V:
380 case propertyFlag::P:
383 throw TPX_Error(
"Substance::vprop",
"invalid job index");
387 int Substance::Lever(
int itp,
double sat,
double val, propertyFlag::type ifunc)
391 double Rhosave = Rho;
393 if (sat >= Tcrit()) {
398 }
else if (itp == Pgiven) {
399 if (sat >= Pcrit()) {
405 }
catch (TPX_Error&) {
412 throw TPX_Error(
"Substance::Lever",
"general error");
414 Set(PropertyPair::TX, T, 1.0);
415 double Valg = vprop(ifunc);
416 Set(PropertyPair::TX, T, 0.0);
417 double Valf = vprop(ifunc);
418 if (val >= Valf && val <= Valg) {
419 double xx = (val - Valf)/(Valg - Valf);
420 double vv = (1.0 - xx)/Rhf + xx/Rhv;
430 void Substance::set_xy(propertyFlag::type ifx, propertyFlag::type ify,
432 double atx,
double aty,
433 double rtx,
double rty)
435 double v_here, t_here;
436 double dvs1 = 2.0*Vcrit();
437 double dvs2 = 0.7*Vcrit();
440 double v_save = 1.0/Rho;
445 Set(PropertyPair::TV,Tcrit()*1.1,Vcrit()*1.1);
448 }
else if (Rho ==
Undef) {
450 Set(PropertyPair::TV,T,Vcrit()*1.1);
462 double x_here = prop(ifx);
463 double y_here = prop(ify);
464 double err_x = fabs(X - x_here);
465 double err_y = fabs(Y - y_here);
467 if ((err_x < atx + rtx*Xa) && (err_y < aty + rty*Ya)) {
472 double dt = 0.001*t_here;
473 if (t_here + dt > Tmax()) {
478 double dv = 0.001*v_here;
479 if (v_here <= Vcrit()) {
484 Set(PropertyPair::TV, t_here + dt, v_here);
485 double dxdt = (prop(ifx) - x_here)/dt;
486 double dydt = (prop(ify) - y_here)/dt;
489 Set(PropertyPair::TV, t_here, v_here + dv);
490 double dxdv = (prop(ifx) - x_here)/dv;
491 double dydv = (prop(ify) - y_here)/dv;
493 double det = dxdt*dydv - dydt*dxdv;
494 dt = ((X - x_here)*dydv - (Y - y_here)*dxdv)/det;
495 dv = ((Y - y_here)*dxdt - (X - x_here)*dydt)/det;
497 double dvm = 0.2*v_here;
504 double dtm = 0.1*t_here;
505 double dva = fabs(dv);
506 double dta = fabs(dt);
515 t_here =
clip(t_here, Tmin(), Tmax());
519 Set(PropertyPair::TV, t_here, v_here);
521 if (LoopCount > 200) {
522 std::string msg =
"No convergence. " +
523 propertySymbols[ifx] +
" = " +
fp2str(X) +
", " +
524 propertySymbols[ify] +
" = " +
fp2str(Y);
525 if (t_here == Tmin()) {
526 msg +=
"\nAt temperature limit (Tmin = " +
fp2str(Tmin()) +
")";
527 }
else if (t_here == Tmax()) {
528 msg +=
"\nAt temperature limit (Tmax = " +
fp2str(Tmax()) +
")";
530 throw TPX_Error(
"Substance::set_xy", msg);
535 double Substance::prop(propertyFlag::type ijob)
537 if (ijob == propertyFlag::P) {
540 if (ijob == propertyFlag::T) {
544 if ((xx > 0.0) && (xx < 1.0)) {
545 double Rho_save = Rho;
547 double vp = vprop(ijob);
549 double lp = vprop(ijob);
550 double pp = (1.0 - xx)*lp + xx*vp;
558 static const double ErrP = 1.e-7;
559 static const double Big = 1.e30;
561 void Substance::BracketSlope(
double Pressure)
564 dv = (v_here < Vcrit() ? -0.05*v_here : 0.2*v_here);
572 double dpdv = (Pmax - Pmin)/(Vmax - Vmin);
575 dv = dvbf*(Pressure - P_here)/dpdv;
580 void Substance::set_TPp(
double Temp,
double Pressure)
588 double dvs1 = 2.0*Vcrit();
589 double dvs2 = 0.7*Vcrit();
592 double v_save = 1.0/Rho;
597 while (P_here = Pp(),
598 fabs(Pressure - P_here) >= ErrP* Pressure || LoopCount == 0) {
600 BracketSlope(Pressure);
603 if (v_here <= Vcrit()) {
606 Set(PropertyPair::TV, Temp, v_here+dv);
607 double dpdv = (Pp() - P_here)/dv;
609 BracketSlope(Pressure);
611 if ((P_here > Pressure) && (v_here > Vmin)) {
613 }
else if ((P_here < Pressure) && (v_here < Vmax)) {
616 if (v_here == Vmin) {
619 if (v_here == Vmax) {
623 throw TPX_Error(
"Substance::set_TPp",
"Vmin >= Vmax");
624 }
else if ((Vmin > 0.0) && (Vmax < Big)) {
630 BracketSlope(Pressure);
632 dv = (Pressure - P_here)/dpdv;
636 double dvm = 0.2*v_here;
644 double vt = v_here + dv;
645 if ((vt < Vmin) || (vt > Vmax)) {
646 dv = Vmin + (Pressure - Pmin)*(Vmax - Vmin)/(Pmax - Pmin) - v_here;
649 double dva = fabs(dv);
655 throw TPX_Error(
"Substance::set_TPp",
"dv = 0 and no convergence");
657 Set(PropertyPair::TV, Temp, v_here);
659 if (LoopCount > 100) {
660 Set(PropertyPair::TV, Temp, v_save);
661 throw TPX_Error(
"Substance::set_TPp",
string(
"no convergence for ")
662 +
"P* = "+
fp2str(Pressure/Pcrit())+
". V* = "
666 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...
std::string fp2str(const double x, const std::string &fmt)
Convert a double into a c++ string.
Contains declarations for string manipulation functions within Cantera.