11 using namespace Cantera;
15 Substance::Substance() :
23 m_entropy_offset(0.0),
30 return TwoPhase() ? Ps() : Pp();
33 const double DeltaT = 0.000001;
35 double Substance::dPsdT()
40 double dpdt = (Ps() - ps1)/DeltaT;
45 int Substance::TwoPhase()
51 return ((Rho < Rhf) && (Rho > Rhv) ? 1 : 0);
57 return (1.0/Rho < Vcrit() ? 0.0 : 1.0);
62 }
else if (Rho >= Rhf) {
67 return (1.0/Rho - vl)/(vv - vl);
72 double Substance::Tsat(
double p)
74 if (p <= 0.0 || p > Pcrit()) {
75 throw TPX_Error(
"Substance::Tsat",
"illegal pressure value");
81 T = 0.5*(Tcrit() - Tmin());
84 T = 0.5*(Tcrit() - Tmin());
95 double dt = dp/dPsdT();
96 double dta = fabs(dt);
103 if (LoopCount > 100) {
105 throw TPX_Error(
"Substance::Tsat",
"No convergence");
107 }
while (fabs(dp) > tol);
114 static const double TolAbsH = 0.0001;
115 static const double TolAbsU = 0.0001;
116 static const double TolAbsS = 1.e-7;
117 static const double TolAbsP = 0.000;
118 static const double TolAbsV = 1.e-8;
119 static const double TolAbsT = 1.e-3;
120 static const double TolRel = 3.e-8;
122 void Substance::Set(PropertyPair::type XY,
double x0,
double y0)
129 XY =
static_cast<PropertyPair::type
>(-XY);
133 case PropertyPair::TV:
138 case PropertyPair::HP:
139 if (Lever(Pgiven, y0, x0, propertyFlag::H)) {
142 set_xy(propertyFlag::H, propertyFlag::P,
143 x0, y0, TolAbsH, TolAbsP, TolRel, TolRel);
146 case PropertyPair::SP:
147 if (Lever(Pgiven, y0, x0, propertyFlag::S)) {
150 set_xy(propertyFlag::S, propertyFlag::P,
151 x0, y0, TolAbsS, TolAbsP, TolRel, TolRel);
154 case PropertyPair::PV:
155 if (Lever(Pgiven, x0, y0, propertyFlag::V)) {
158 set_xy(propertyFlag::P, propertyFlag::V,
159 x0, y0, TolAbsP, TolAbsV, TolRel, TolRel);
162 case PropertyPair::TP:
166 Set(PropertyPair::TX, x0, 1.0);
168 Set(PropertyPair::TX, x0, 0.0);
173 set_xy(propertyFlag::T, propertyFlag::P,
174 x0, y0, TolAbsT, TolAbsP, TolRel, TolRel);
177 case PropertyPair::UV:
178 set_xy(propertyFlag::U, propertyFlag::V,
179 x0, y0, TolAbsU, TolAbsV, TolRel, TolRel);
182 case PropertyPair::ST:
183 if (Lever(Tgiven, y0, x0, propertyFlag::S)) {
186 set_xy(propertyFlag::S, propertyFlag::T,
187 x0, y0, TolAbsS, TolAbsT, TolRel, TolRel);
190 case PropertyPair::SV:
191 set_xy(propertyFlag::S, propertyFlag::V,
192 x0, y0, TolAbsS, TolAbsV, TolRel, TolRel);
195 case PropertyPair::UP:
196 if (Lever(Pgiven, y0, x0, propertyFlag::U)) {
199 set_xy(propertyFlag::U, propertyFlag::P,
200 x0, y0, TolAbsU, TolAbsP, TolRel, TolRel);
203 case PropertyPair::VH:
204 set_xy(propertyFlag::V, propertyFlag::H,
205 x0, y0, TolAbsV, TolAbsH, TolRel, TolRel);
208 case PropertyPair::TH:
209 set_xy(propertyFlag::T, propertyFlag::H,
210 x0, y0, TolAbsT, TolAbsH, TolRel, TolRel);
213 case PropertyPair::SH:
214 set_xy(propertyFlag::S, propertyFlag::H,
215 x0, y0, TolAbsS, TolAbsH, TolRel, TolRel);
218 case PropertyPair::PX:
220 if (y0 > 1.0 || y0 < 0.0) {
221 throw TPX_Error(
"Substance::Set",
222 "Invalid vapor fraction, " +
fp2str(y0));
223 }
else if (temp >= Tcrit()) {
224 throw TPX_Error(
"Substance::Set",
225 "Can't set vapor fraction above the critical point");
229 Rho = 1.0/((1.0 - y0)/Rhf + y0/Rhv);
233 case PropertyPair::TX:
234 if (y0 > 1.0 || y0 < 0.0) {
235 throw TPX_Error(
"Substance::Set",
236 "Invalid vapor fraction, " +
fp2str(y0));
237 }
else if (x0 >= Tcrit()) {
238 throw TPX_Error(
"Substance::Set",
239 "Can't set vapor fraction above the critical point");
243 Rho = 1.0/((1.0 - y0)/Rhf + y0/Rhv);
248 throw TPX_Error(
"Substance::Set",
"Invalid input.");
254 void Substance::set_Rho(
double r0)
259 throw TPX_Error(
"Substance::set_Rho",
"Invalid density: " +
fp2str(r0));
263 void Substance::set_T(
double t0)
265 if ((t0 >= Tmin()) && (t0 <= Tmax())) {
268 throw TPX_Error(
"Substance::set_T",
"illegal temperature: " +
fp2str(t0));
272 void Substance::set_v(
double v0)
277 throw TPX_Error(
"Substance::set_v",
278 "negative specific volume: "+
fp2str(v0));
282 double Substance::Ps()
284 if (T < Tmin() || T > Tcrit()) {
285 throw TPX_Error(
"Substance::Ps",
286 "illegal temperature value "+
fp2str(T));
292 void Substance::update_sat()
294 if ((T != Tslast) && (T < Tcrit())) {
295 double Rho_save = Rho;
298 double lps = log(pp);
302 for (i = 0; i<20; i++) {
311 double gf = hp() - T*sp();
313 Rho = pp*MolWt()/(8314.0*T);
320 double gv = hp() - T*sp();
328 if (fabs(dg) < 0.001 && Rhf > Rhv) {
331 double dp = dg/(1.0/Rhv - 1.0/Rhf);
335 lps -= dg/(pp*(1.0/Rhv - 1.0/Rhf));
342 pp = psold + 0.5*(Pcrit() - psold);
344 }
else if (pp < 0.0) {
350 throw TPX_Error(
"Substance::update_sat",
351 "wrong root found for sat. liquid or vapor at P = "+
fp2str(pp));
355 throw TPX_Error(
"substance::update_sat",
"no convergence");
364 double Substance::vprop(propertyFlag::type ijob)
367 case propertyFlag::H:
369 case propertyFlag::S:
371 case propertyFlag::U:
373 case propertyFlag::V:
375 case propertyFlag::P:
378 throw TPX_Error(
"Substance::vprop",
"invalid job index");
382 int Substance::Lever(
int itp,
double sat,
double val, propertyFlag::type ifunc)
386 double Rhosave = Rho;
388 if (sat >= Tcrit()) {
393 }
else if (itp == Pgiven) {
394 if (sat >= Pcrit()) {
400 }
catch (TPX_Error&) {
407 throw TPX_Error(
"Substance::Lever",
"general error");
409 Set(PropertyPair::TX, T, 1.0);
410 double Valg = vprop(ifunc);
411 Set(PropertyPair::TX, T, 0.0);
412 double Valf = vprop(ifunc);
413 if (val >= Valf && val <= Valg) {
414 double xx = (val - Valf)/(Valg - Valf);
415 double vv = (1.0 - xx)/Rhf + xx/Rhv;
425 void Substance::set_xy(propertyFlag::type ifx, propertyFlag::type ify,
427 double atx,
double aty,
428 double rtx,
double rty)
430 double v_here, t_here;
431 double dvs1 = 2.0*Vcrit();
432 double dvs2 = 0.7*Vcrit();
435 double v_save = 1.0/Rho;
440 Set(PropertyPair::TV,Tcrit()*1.1,Vcrit()*1.1);
443 }
else if (Rho ==
Undef) {
445 Set(PropertyPair::TV,T,Vcrit()*1.1);
457 double x_here = prop(ifx);
458 double y_here = prop(ify);
459 double err_x = fabs(X - x_here);
460 double err_y = fabs(Y - y_here);
462 if ((err_x < atx + rtx*Xa) && (err_y < aty + rty*Ya)) {
467 double dt = 0.001*t_here;
468 if (t_here + dt > Tmax()) {
473 double dv = 0.001*v_here;
474 if (v_here <= Vcrit()) {
479 Set(PropertyPair::TV, t_here + dt, v_here);
480 double dxdt = (prop(ifx) - x_here)/dt;
481 double dydt = (prop(ify) - y_here)/dt;
484 Set(PropertyPair::TV, t_here, v_here + dv);
485 double dxdv = (prop(ifx) - x_here)/dv;
486 double dydv = (prop(ify) - y_here)/dv;
488 double det = dxdt*dydv - dydt*dxdv;
489 dt = ((X - x_here)*dydv - (Y - y_here)*dxdv)/det;
490 dv = ((Y - y_here)*dxdt - (X - x_here)*dydt)/det;
492 double dvm = 0.2*v_here;
499 double dtm = 0.1*t_here;
500 double dva = fabs(dv);
501 double dta = fabs(dt);
510 t_here =
clip(t_here, Tmin(), Tmax());
514 Set(PropertyPair::TV, t_here, v_here);
516 if (LoopCount > 200) {
517 throw TPX_Error(
"Substance::set_xy",
"no convergence");
522 double Substance::prop(propertyFlag::type ijob)
524 if (ijob == propertyFlag::P) {
527 if (ijob == propertyFlag::T) {
531 if ((xx > 0.0) && (xx < 1.0)) {
532 double Rho_save = Rho;
534 double vp = vprop(ijob);
536 double lp = vprop(ijob);
537 double pp = (1.0 - xx)*lp + xx*vp;
545 static const double ErrP = 1.e-7;
546 static const double Big = 1.e30;
548 void Substance::BracketSlope(
double Pressure)
551 dv = (v_here < Vcrit() ? -0.05*v_here : 0.2*v_here);
559 double dpdv = (Pmax - Pmin)/(Vmax - Vmin);
562 dv = dvbf*(Pressure - P_here)/dpdv;
567 void Substance::set_TPp(
double Temp,
double Pressure)
575 double dvs1 = 2.0*Vcrit();
576 double dvs2 = 0.7*Vcrit();
579 double v_save = 1.0/Rho;
584 while (P_here = Pp(),
585 fabs(Pressure - P_here) >= ErrP* Pressure || LoopCount == 0) {
587 BracketSlope(Pressure);
590 if (v_here <= Vcrit()) {
593 Set(PropertyPair::TV, Temp, v_here+dv);
594 double dpdv = (Pp() - P_here)/dv;
596 BracketSlope(Pressure);
598 if ((P_here > Pressure) && (v_here > Vmin)) {
600 }
else if ((P_here < Pressure) && (v_here < Vmax)) {
603 if (v_here == Vmin) {
606 if (v_here == Vmax) {
610 throw TPX_Error(
"Substance::set_TPp",
"Vmin >= Vmax");
611 }
else if ((Vmin > 0.0) && (Vmax < Big)) {
617 BracketSlope(Pressure);
619 dv = (Pressure - P_here)/dpdv;
623 double dvm = 0.2*v_here;
631 double vt = v_here + dv;
632 if ((vt < Vmin) || (vt > Vmax)) {
633 dv = Vmin + (Pressure - Pmin)*(Vmax - Vmin)/(Pmax - Pmin) - v_here;
636 double dva = fabs(dv);
642 throw TPX_Error(
"Substance::set_TPp",
"dv = 0 and no convergence");
644 Set(PropertyPair::TV, Temp, v_here);
646 if (LoopCount > 100) {
647 Set(PropertyPair::TV, Temp, v_save);
648 throw TPX_Error(
"Substance::set_TPp",
string(
"no convergence for ")
649 +
"P* = "+
fp2str(Pressure/Pcrit())+
". V* = "
653 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, HTML_logs (see Input File Handling, Diagnostic Output, Writing messages to the screen and Writing HTML Logfiles).
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.