8#if defined(ENABLE_CATCH)
9# include <catch2/catch_all.hpp>
22 if (!twophase && HEOS.
_T > closest_state.
T) {
60 CoolProp::SaturationSolvers::PTflash_twophase_options o;
61 stability_tester.
get_liq(o.x, o.rhomolar_liq);
62 stability_tester.
get_vap(o.y, o.rhomolar_vap);
67 CoolProp::SaturationSolvers::PTflash_twophase solver(HEOS, o);
70 HEOS.
_Q = (o.z[0] - o.x[0]) / (o.y[0] - o.x[0]);
97 bool saturation_called =
false;
105 throw ValueError(
"twophase not implemented yet");
162 double omega, R, kappa, a, b, A, B, C, Tc, pc, V = 1 / rhomolar;
168 kappa = 0.37464 + 1.54226 * omega - 0.26992 * omega * omega;
169 a = 0.457235 * R * R * Tc * Tc / pc;
170 b = 0.077796 * R * Tc / pc;
171 double den = V * V + 2 * b * V - b * b;
174 A = R * Tc / (V - b) - a * kappa * kappa / (den);
175 B = +2 * a * kappa * (1 + kappa) / (den);
176 C = -a * (1 + 2 * kappa + kappa * kappa) / (den)-p;
180 double sqrt_Tr1 = (-B + sqrt(B * B - 4 * A * C)) / (2 * A);
182 return sqrt_Tr1 * sqrt_Tr1 * Tc;
193 bool saturation_called =
false;
200 if (saturation_called) {
217 Halley(resid, T0, 1e-10, 100);
260 double Tmin = HEOS.
Tmin() + 0.1;
263 const double eps = 1e-12;
291 if (std::abs(HEOS.
Q() - 1) > 1e-10) {
292 throw ValueError(
format(
"non-unity quality not currently allowed for HQ_flash"));
313 }
else if (std::abs(HEOS.
Q()) < 1e-10) {
324 }
else if (std::abs(HEOS.
Q() - 1) < 1e-10) {
336 throw ValueError(
format(
"non-zero or 1 quality not currently allowed for QS_flash"));
350 auto& superanc = optsuperanc.value();
354 throw ValueError(
format(
"Temperature to QT_flash [%0.8Lg K] may not be above the numerical critical point of %0.15Lg K", T, Tcrit_num));
356 auto rhoL = superanc.eval_sat(T,
'D', 0);
357 auto rhoV = superanc.eval_sat(T,
'D', 1);
358 auto p = superanc.eval_sat(T,
'P', 1);
359 HEOS.
SatL->update_TDmolarP_unchecked(T, rhoL, p);
360 HEOS.
SatV->update_TDmolarP_unchecked(T, rhoV, p);
362 HEOS.
_rhomolar = 1 / (Q / rhoV + (1 - Q) / rhoL);
376 Tmin_sat = std::max(Tmin_satL, Tmin_satV) - 1e-13;
381 if ((
get_config_bool(CRITICAL_WITHIN_1UK) && std::abs(T - Tmax_sat) < 1e-6) || std::abs(T - Tmax_sat) < 1e-12) {
386 HEOS.
_p = 0.5 * HEOS.
SatV->p() + 0.5 * HEOS.
SatL->p();
388 throw ValueError(
format(
"Temperature to QT_flash [%0.8Lg K] must be in range [%0.8Lg K, %0.8Lg K]", T, Tmin_sat - 0.1, Tmax_sat));
390 double rhoL = _HUGE, rhoV = _HUGE;
395 HEOS.
_p = 0.5 * HEOS.
SatV->p() + 0.5 * HEOS.
SatL->p();
397 }
else if (!(HEOS.
components[0].EOS().pseudo_pure)) {
404 HEOS.
_p = 0.5 * HEOS.
SatV->p() + 0.5 * HEOS.
SatL->p();
408 CoolPropDbl rhoLanc = _HUGE, rhoVanc = _HUGE, rhoLsat = _HUGE, rhoVsat = _HUGE;
411 rhoLanc = HEOS.
components[0].ancillaries.rhoL.evaluate(HEOS.
_T);
412 HEOS.
SatL->update_TP_guessrho(HEOS.
_T, HEOS.
_p, rhoLanc);
416 rhoVanc = HEOS.
components[0].ancillaries.rhoV.evaluate(HEOS.
_T);
417 HEOS.
SatV->update_TP_guessrho(HEOS.
_T, HEOS.
_p, rhoVanc);
420 throw CoolProp::ValueError(
format(
"For pseudo-pure fluid, quality must be equal to 0 or 1. Two-phase quality is not defined"));
454 SaturationSolvers::newton_raphson_saturation NR;
455 SaturationSolvers::newton_raphson_saturation_options IO;
457 IO.bubble_point = (HEOS.
_Q < 0.5);
467 IO.imposed_variable = SaturationSolvers::newton_raphson_saturation_options::T_IMPOSED;
469 if (IO.bubble_point) {
471 NR.call(HEOS, IO.x, IO.y, IO);
474 NR.call(HEOS, IO.y, IO.x, IO);
478 HEOS.
_rhomolar = 1 / (HEOS.
_Q / IO.rhomolar_vap + (1 - HEOS.
_Q) / IO.rhomolar_liq);
488void get_Henrys_coeffs_FP(
const std::string& CAS,
double& A,
double& B,
double& C,
double& Tmin,
double& Tmax) {
490 if (CAS ==
"7440-59-7")
497 }
else if (CAS ==
"7440-01-9")
504 }
else if (CAS ==
"7440-37-1")
511 }
else if (CAS ==
"7439-90-9")
518 }
else if (CAS ==
"7440-63-3")
525 }
else if (CAS ==
"1333-74-0")
532 }
else if (CAS ==
"7727-37-9")
539 }
else if (CAS ==
"7782-44-7")
546 }
else if (CAS ==
"630-08-0")
553 }
else if (CAS ==
"124-38-9")
560 }
else if (CAS ==
"7783-06-4")
567 }
else if (CAS ==
"74-82-8")
574 }
else if (CAS ==
"74-84-0")
581 }
else if (CAS ==
"2551-62-4")
589 throw ValueError(
"Bad component in Henry's law constants");
598 auto& superanc = optsuperanc.value();
600 if (HEOS.
_p > pmax_num){
601 throw ValueError(
format(
"Pressure to PQ_flash [%0.8Lg Pa] may not be above the numerical critical point of %0.15Lg Pa", HEOS.
_p, pmax_num));
603 auto T = superanc.get_T_from_p(HEOS.
_p);
604 auto rhoL = superanc.eval_sat(T,
'D', 0);
605 auto rhoV = superanc.eval_sat(T,
'D', 1);
607 HEOS.
SatL->update_TDmolarP_unchecked(T, rhoL, p);
608 HEOS.
SatV->update_TDmolarP_unchecked(T, rhoV, p);
626 HEOS.
SatL->update_TP_guessrho(HEOS.
_TLanc, HEOS.
_p, rhoL);
627 HEOS.
SatV->update_TP_guessrho(HEOS.
_TVanc, HEOS.
_p, rhoV);
631 HEOS.
_p = HEOS.
_Q * HEOS.
SatV->p() + (1 - HEOS.
_Q) * HEOS.
SatL->p();
632 HEOS.
_T = HEOS.
_Q * HEOS.
SatV->T() + (1 - HEOS.
_Q) * HEOS.
SatL->T();
641 pmin_sat = std::max(pmin_satL, pmin_satV);
656 throw ValueError(
format(
"Pressure to PQ_flash [%6g Pa] must be in range [%8Lg Pa, %8Lg Pa]", HEOS.
_p, pmin_sat, pmax_sat));
670 double increment = 0.4;
673 for (
double omega = 1.0; omega > 0; omega -= increment) {
675 options.
omega = omega;
683 if (omega < 1.1 * increment) {
696 HEOS.
_p = HEOS.
_Q * HEOS.
SatV->p() + (1 - HEOS.
_Q) * HEOS.
SatL->p();
716 std::vector<CoolPropDbl> K = HEOS.
K;
718 if (
get_config_bool(HENRYS_LAW_TO_GENERATE_VLE_GUESSES) && std::abs(HEOS.
_Q - 1) < 1e-10) {
719 const std::vector<CoolPropFluid>& components = HEOS.
get_components();
720 std::size_t iWater = 0;
721 double p1star =
PropsSI(
"P",
"T", Tguess,
"Q", 1,
"Water");
723 std::vector<CoolPropDbl> x(y.size());
724 for (std::size_t i = 0; i < components.size(); ++i) {
725 if (components[i].CAS ==
"7732-18-5") {
729 double A, B, C, Tmin, Tmax;
731 double T_R = Tguess / 647.096, tau = 1 - T_R;
732 double k_H = p1star * exp(A / T_R + B * pow(tau, 0.355) / T_R + C * pow(T_R, -0.41) * exp(tau));
733 x[i] = y[i] * HEOS.
_p / k_H;
740 for (std::size_t i = 0; i < y.size(); ++i) {
746 K[iWater] = y[iWater] / x[iWater];
756 SaturationSolvers::newton_raphson_saturation NR;
757 SaturationSolvers::newton_raphson_saturation_options IO;
759 IO.bubble_point = (HEOS.
_Q < 0.5);
767 IO.imposed_variable = SaturationSolvers::newton_raphson_saturation_options::P_IMPOSED;
769 if (IO.bubble_point) {
771 NR.call(HEOS, IO.x, IO.y, IO);
774 NR.call(HEOS, IO.y, IO.x, IO);
787 SaturationSolvers::newton_raphson_saturation NR;
788 SaturationSolvers::newton_raphson_saturation_options IO;
791 IO.x = std::vector<CoolPropDbl>(guess.
x.begin(), guess.
x.end());
792 IO.y = std::vector<CoolPropDbl>(guess.
y.begin(), guess.
y.end());
795 IO.bubble_point =
false;
796 IO.imposed_variable = SaturationSolvers::newton_raphson_saturation_options::P_IMPOSED;
798 if (std::abs(HEOS.
Q()) < 1e-10) {
799 IO.bubble_point =
true;
800 NR.call(HEOS, IO.x, IO.y, IO);
801 }
else if (std::abs(HEOS.
Q() - 1) < 1e-10) {
802 IO.bubble_point =
false;
803 NR.call(HEOS, IO.y, IO.x, IO);
810 HEOS.
_rhomolar = 1 / (HEOS.
_Q / IO.rhomolar_vap + (1 - HEOS.
_Q) / IO.rhomolar_liq);
814 SaturationSolvers::newton_raphson_saturation NR;
815 SaturationSolvers::newton_raphson_saturation_options IO;
818 IO.x = std::vector<CoolPropDbl>(guess.
x.begin(), guess.
x.end());
819 IO.y = std::vector<CoolPropDbl>(guess.
y.begin(), guess.
y.end());
822 IO.bubble_point =
false;
823 IO.imposed_variable = SaturationSolvers::newton_raphson_saturation_options::T_IMPOSED;
826 std::cout <<
format(
" QT w/ guess p %g T %g dl %g dv %g x %s y %s\n", IO.p, IO.T, IO.rhomolar_liq, IO.rhomolar_vap,
830 if (std::abs(HEOS.
Q()) < 1e-10) {
831 IO.bubble_point =
true;
832 NR.call(HEOS, IO.x, IO.y, IO);
833 }
else if (std::abs(HEOS.
Q() - 1) < 1e-10) {
834 IO.bubble_point =
false;
835 NR.call(HEOS, IO.y, IO.x, IO);
843 HEOS.
_rhomolar = 1 / (HEOS.
_Q / IO.rhomolar_vap + (1 - HEOS.
_Q) / IO.rhomolar_liq);
874 std::vector<std::pair<std::size_t, std::size_t>> intersections =
885 quality_options quality;
887 quality = SATURATED_LIQUID;
889 quality = SATURATED_VAPOR;
890 }
else if (HEOS.
_Q > 0 && HEOS.
_Q < 1) {
893 throw ValueError(
"Quality is not within 0 and 1");
896 if (quality == SATURATED_LIQUID || quality == SATURATED_VAPOR) {
901 std::vector<std::size_t> solutions;
902 for (std::vector<std::pair<std::size_t, std::size_t>>::const_iterator it = intersections.begin(); it != intersections.end(); ++it) {
904 solutions.push_back(it->first);
908 if (solutions.size() == 1) {
910 std::size_t& imax = solutions[0];
913 if (imax + 2 >= env.T.size()) {
915 }
else if (imax == 0) {
923 SaturationSolvers::newton_raphson_saturation NR;
924 SaturationSolvers::newton_raphson_saturation_options IO;
928 IO.imposed_variable = SaturationSolvers::newton_raphson_saturation_options::P_IMPOSED;
930 IO.rhomolar_vap =
CubicInterp(env.p, env.rhomolar_vap, imax - 1, imax, imax + 1, imax + 2,
static_cast<CoolPropDbl>(IO.p));
931 IO.T =
CubicInterp(env.rhomolar_vap, env.T, imax - 1, imax, imax + 1, imax + 2, IO.rhomolar_vap);
932 }
else if (other ==
iT) {
934 IO.imposed_variable = SaturationSolvers::newton_raphson_saturation_options::T_IMPOSED;
936 IO.rhomolar_vap =
CubicInterp(env.T, env.rhomolar_vap, imax - 1, imax, imax + 1, imax + 2,
static_cast<CoolPropDbl>(IO.T));
937 IO.p =
CubicInterp(env.rhomolar_vap, env.p, imax - 1, imax, imax + 1, imax + 2, IO.rhomolar_vap);
941 IO.rhomolar_liq =
CubicInterp(env.rhomolar_vap, env.rhomolar_liq, imax - 1, imax, imax + 1, imax + 2, IO.rhomolar_vap);
943 if (quality == SATURATED_VAPOR) {
944 IO.bubble_point =
false;
946 IO.x.resize(IO.y.size());
947 for (std::size_t i = 0; i < IO.x.size() - 1; ++i)
949 IO.x[i] =
CubicInterp(env.rhomolar_vap, env.x[i], imax - 1, imax, imax + 1, imax + 2, IO.rhomolar_vap);
951 IO.x[IO.x.size() - 1] = 1 - std::accumulate(IO.x.begin(), IO.x.end() - 1, 0.0);
952 NR.call(HEOS, IO.y, IO.x, IO);
954 IO.bubble_point =
true;
956 IO.y.resize(IO.x.size());
958 std::swap(IO.rhomolar_liq, IO.rhomolar_vap);
959 for (std::size_t i = 0; i < IO.y.size() - 1; ++i)
963 IO.y[i] =
CubicInterp(env.rhomolar_vap, env.x[i], imax - 1, imax, imax + 1, imax + 2, IO.rhomolar_liq);
965 IO.y[IO.y.size() - 1] = 1 - std::accumulate(IO.y.begin(), IO.y.end() - 1, 0.0);
966 NR.call(HEOS, IO.x, IO.y, IO);
968 }
else if (solutions.size() == 0) {
969 throw ValueError(
"No solution was found in PQ_flash");
971 throw ValueError(
"More than 1 solution was found in PQ_flash");
979 std::vector<std::size_t> liquid_solutions, vapor_solutions;
980 for (std::vector<std::pair<std::size_t, std::size_t>>::const_iterator it = intersections.begin(); it != intersections.end(); ++it) {
981 if (std::abs(env.Q[it->first] - 0) < 10 *
DBL_EPSILON && std::abs(env.Q[it->second] - 0) < 10 *
DBL_EPSILON) {
982 liquid_solutions.push_back(it->first);
984 if (std::abs(env.Q[it->first] - 1) < 10 *
DBL_EPSILON && std::abs(env.Q[it->second] - 1) < 10 *
DBL_EPSILON) {
985 vapor_solutions.push_back(it->first);
989 if (liquid_solutions.size() != 1 || vapor_solutions.size() != 1) {
990 throw ValueError(
format(
"Number liquid solutions [%d] or vapor solutions [%d] != 1", liquid_solutions.size(), vapor_solutions.size()));
992 std::size_t iliq = liquid_solutions[0], ivap = vapor_solutions[0];
994 SaturationSolvers::newton_raphson_twophase NR;
995 SaturationSolvers::newton_raphson_twophase_options IO;
998 CoolPropDbl rhomolar_vap_sat_vap, T_sat_vap, rhomolar_liq_sat_vap, rhomolar_liq_sat_liq, T_sat_liq, rhomolar_vap_sat_liq, p_sat_liq,
1005 IO.imposed_variable = SaturationSolvers::newton_raphson_twophase_options::P_IMPOSED;
1008 rhomolar_vap_sat_vap =
CubicInterp(env.p, env.rhomolar_vap, ivap - 1, ivap, ivap + 1, ivap + 2,
static_cast<CoolPropDbl>(IO.p));
1009 T_sat_vap =
CubicInterp(env.rhomolar_vap, env.T, ivap - 1, ivap, ivap + 1, ivap + 2, rhomolar_vap_sat_vap);
1010 rhomolar_liq_sat_vap =
CubicInterp(env.rhomolar_vap, env.rhomolar_liq, ivap - 1, ivap, ivap + 1, ivap + 2, rhomolar_vap_sat_vap);
1013 rhomolar_liq_sat_liq =
CubicInterp(env.p, env.rhomolar_vap, iliq - 1, iliq, iliq + 1, iliq + 2,
static_cast<CoolPropDbl>(IO.p));
1014 T_sat_liq =
CubicInterp(env.rhomolar_vap, env.T, iliq - 1, iliq, iliq + 1, iliq + 2, rhomolar_liq_sat_liq);
1015 rhomolar_vap_sat_liq =
CubicInterp(env.rhomolar_vap, env.rhomolar_liq, iliq - 1, iliq, iliq + 1, iliq + 2, rhomolar_liq_sat_liq);
1016 }
else if (other ==
iT) {
1020 IO.imposed_variable = SaturationSolvers::newton_raphson_twophase_options::T_IMPOSED;
1023 rhomolar_vap_sat_vap =
CubicInterp(env.T, env.rhomolar_vap, ivap - 1, ivap, ivap + 1, ivap + 2,
static_cast<CoolPropDbl>(IO.T));
1024 p_sat_vap =
CubicInterp(env.rhomolar_vap, env.p, ivap - 1, ivap, ivap + 1, ivap + 2, rhomolar_vap_sat_vap);
1025 rhomolar_liq_sat_vap =
CubicInterp(env.rhomolar_vap, env.rhomolar_liq, ivap - 1, ivap, ivap + 1, ivap + 2, rhomolar_vap_sat_vap);
1028 rhomolar_liq_sat_liq =
CubicInterp(env.T, env.rhomolar_vap, iliq - 1, iliq, iliq + 1, iliq + 2,
static_cast<CoolPropDbl>(IO.T));
1029 p_sat_liq =
CubicInterp(env.rhomolar_vap, env.p, iliq - 1, iliq, iliq + 1, iliq + 2, rhomolar_liq_sat_liq);
1030 rhomolar_vap_sat_liq =
CubicInterp(env.rhomolar_vap, env.rhomolar_liq, iliq - 1, iliq, iliq + 1, iliq + 2, rhomolar_liq_sat_liq);
1036 IO.rhomolar_vap = IO.beta * rhomolar_vap_sat_vap + (1 - IO.beta) * rhomolar_vap_sat_liq;
1037 IO.rhomolar_liq = IO.beta * rhomolar_liq_sat_vap + (1 - IO.beta) * rhomolar_liq_sat_liq;
1038 IO.T = IO.beta * T_sat_vap + (1 - IO.beta) * T_sat_liq;
1039 IO.p = IO.beta * p_sat_vap + (1 - IO.beta) * p_sat_liq;
1042 IO.x.resize(IO.z.size());
1043 IO.y.resize(IO.z.size());
1045 for (std::size_t i = 0; i < IO.x.size() - 1; ++i)
1047 CoolPropDbl x_sat_vap =
CubicInterp(env.rhomolar_vap, env.x[i], ivap - 1, ivap, ivap + 1, ivap + 2, rhomolar_vap_sat_vap);
1048 CoolPropDbl y_sat_vap =
CubicInterp(env.rhomolar_vap, env.y[i], ivap - 1, ivap, ivap + 1, ivap + 2, rhomolar_vap_sat_vap);
1050 CoolPropDbl x_sat_liq =
CubicInterp(env.rhomolar_vap, env.y[i], iliq - 1, iliq, iliq + 1, iliq + 2, rhomolar_liq_sat_liq);
1051 CoolPropDbl y_sat_liq =
CubicInterp(env.rhomolar_vap, env.x[i], iliq - 1, iliq, iliq + 1, iliq + 2, rhomolar_liq_sat_liq);
1053 IO.x[i] = IO.beta * x_sat_vap + (1 - IO.beta) * x_sat_liq;
1054 IO.y[i] = IO.beta * y_sat_vap + (1 - IO.beta) * y_sat_liq;
1056 IO.x[IO.x.size() - 1] = 1 - std::accumulate(IO.x.begin(), IO.x.end() - 1, 0.0);
1057 IO.y[IO.y.size() - 1] = 1 - std::accumulate(IO.y.begin(), IO.y.end() - 1, 0.0);
1072 : HEOS(HEOS), rhomolar_spec(rhomolar_spec), other(other), value(value) {
1075 double call(
double T) {
1079 Qd = (1 / rhomolar_spec - 1 / SatL.
rhomolar()) / (1 / SatV.rhomolar() - 1 / SatL.
rhomolar());
1085 } resid(HEOS, rhomolar_spec, other, value);
1093 Tmin_sat = std::max(Tmin_satL, Tmin_satV) - 1e-13;
1111 : HEOS(HEOS), rhomolar(rhomolar), value(value), other(other), Tmin(Tmin), Tmax(Tmax) {
1115 double call(
double T) {
1120 return (eos - value) / value;
1126 double deriv(
double T) {
1132 double second_deriv(
double T) {
1138 bool input_not_in_range(
double T) {
1139 return (T < Tmin || T > Tmax);
1146 shared_ptr<HelmholtzEOSMixtureBackend> Sat;
1180 y_solid = (yV - yL) / (1 / rhoVtriple - 1 / rhoLtriple) * (1 / HEOS.
_rhomolar - 1 / rhoLtriple) + yL;
1182 if (value < y_solid) {
1191 for (
int i_try = 0; i_try < 7; i_try++)
1213 optionsD.
omega /= 2;
1215 if (i_try >= 6){
throw;}
1220 if (value > Sat->keyed_output(other)) {
1221 solver_resid resid(&HEOS, HEOS.
_rhomolar, value, other, Sat->keyed_output(
iT), HEOS.
Tmax() * 1.5);
1223 HEOS.
_T =
Halley(resid, 0.5 * (Sat->keyed_output(
iT) + HEOS.
Tmax() * 1.5), 1e-10, 100);
1249 HEOS.
_Q = (1 / HEOS.
_rhomolar - 1 / HEOS.
SatL->rhomolar()) / (1 / HEOS.
SatV->rhomolar() - 1 / HEOS.
SatL->rhomolar());
1250 HEOS.
_T = HEOS.
SatL->T();
1286 solver_resid resid(&HEOS, HEOS.
_rhomolar, value, other, TVtriple, HEOS.
Tmax() * 1.5);
1327 solver_resid resid(&HEOS, HEOS.
_rhomolar, value, other, TLtriple, HEOS.
Tmax() * 1.5);
1349 double A[2][2], B[2][2];
1365 throw ValueError(
"other is invalid in HSU_P_flash_singlephase_Newton");
1370 bool failed =
false;
1371 CoolPropDbl omega = 1.0, f2, df2_dtau, df2_ddelta;
1373 while (worst_error > 1e-6 && failed ==
false) {
1389 CoolPropDbl f1 = delta / tau * (1 + delta * dar_ddelta) - p / (rhoc * R * Tc);
1390 CoolPropDbl df1_dtau = (1 + delta * dar_ddelta) * (-delta / tau / tau) + delta / tau * (delta * d2ar_ddelta_dtau);
1391 CoolPropDbl df1_ddelta = (1.0 / tau) * (1 + 2.0 * delta * dar_ddelta + delta * delta * d2ar_ddelta2);
1394 f2 = (1 + delta * dar_ddelta) + tau * (da0_dtau + dar_dtau) - tau * y / (R * Tc);
1395 df2_dtau = delta * d2ar_ddelta_dtau + da0_dtau + dar_dtau + tau * (d2a0_dtau2 + d2ar_dtau2) - y / (R * Tc);
1396 df2_ddelta = (dar_ddelta + delta * d2ar_ddelta2) + tau * (d2a0_ddelta_dtau + d2ar_ddelta_dtau);
1400 f2 = tau * (da0_dtau + dar_dtau) - ar - a0 - y / R;
1401 df2_dtau = tau * (d2a0_dtau2 + d2ar_dtau2) + (da0_dtau + dar_dtau) - dar_dtau - da0_dtau;
1402 df2_ddelta = tau * (d2a0_ddelta_dtau + d2ar_ddelta_dtau) - dar_ddelta - da0_ddelta;
1406 throw ValueError(
"other variable in HSU_P_flash_singlephase_Newton is invalid");
1411 A[0][1] = df1_ddelta;
1413 A[1][1] = df2_ddelta;
1418 tau -= omega * (B[0][0] * f1 + B[0][1] * f2);
1419 delta -= omega * (B[1][0] * f1 + B[1][1] * f2);
1421 if (std::abs(f1) > std::abs(f2))
1422 worst_error = std::abs(f1);
1424 worst_error = std::abs(f2);
1427 throw SolutionError(
format(
"Invalid values for inputs p=%g y=%g for fluid %s in HSU_P_flash_singlephase", p, y, _HEOS.
name().c_str()));
1432 throw SolutionError(
format(
"HSU_P_flash_singlephase did not converge with inputs p=%g h=%g for fluid %s", p, y, _HEOS.
name().c_str()));
1441 throw ValueError(
"value for p in HSU_P_flash_singlephase_Brent is invalid");
1444 throw ValueError(
"value for other in HSU_P_flash_singlephase_Brent is invalid");
1453 CoolPropDbl eos0, eos1, rhomolar, rhomolar0, rhomolar1;
1479 double call(
double T) {
1481 if (iter < 2 || std::abs(rhomolar1 / rhomolar0 - 1) > 0.05) {
1501 rhomolar0 = rhomolar;
1502 }
else if (iter == 1) {
1504 rhomolar1 = rhomolar;
1508 rhomolar0 = rhomolar1;
1509 rhomolar1 = rhomolar;
1515 double deriv(
double T) {
1518 double second_deriv(
double T) {
1521 bool input_not_in_range(
double x) {
1522 return (x < Tmin || x > Tmax);
1525 solver_resid resid(&HEOS, HEOS.
_p, value, other, Tmin, Tmax);
1529 Halley(resid, Tmin, 1e-12, 100);
1531 throw ValueError(
"Halley's method was unable to find a solution in HSU_P_flash_singlephase_Brent");
1552 if (eos1 > eos0 && value > eos1) {
1554 format(
"HSU_P_flash_singlephase_Brent could not find a solution because %s [%Lg %s] is above the maximum value of %0.12Lg %s",
1555 name.c_str(), value, units.c_str(), eos1, units.c_str()));
1557 if (eos1 > eos0 && value < eos0) {
1559 format(
"HSU_P_flash_singlephase_Brent could not find a solution because %s [%Lg %s] is below the minimum value of %0.12Lg %s",
1560 name.c_str(), value, units.c_str(), eos0, units.c_str()));
1569 bool saturation_called =
false;
1597 Tmax = 1.5 * HEOS.
Tmax();
1605 auto& superanc = optsuperanc.value();
1607 if (HEOS.
_p > pmax_num){
1608 throw ValueError(
format(
"Pressure to PQ_flash [%0.8Lg Pa] may not be above the numerical critical point of %0.15Lg Pa", HEOS.
_p, pmax_num));
1610 Tmin = superanc.get_T_from_p(HEOS.
_p)-1e-12;
1614 if (saturation_called) {
1615 Tmin = HEOS.
SatV->T();
1623 if (saturation_called) {
1624 Tmax = HEOS.
SatL->T();
1633 Tmin = HEOS.
Tmin() - 1e-3;
1640 Tmax = 1.5 * HEOS.
Tmax();
1645 Tmin = HEOS.
Tmin() - 1e-3;
1655 }
catch (std::exception& e) {
1656 throw ValueError(
format(
"unable to solve 1phase PY flash with Tmin=%Lg, Tmax=%Lg due to error: %s", Tmin, Tmax, e.what()));
1666 std::size_t iclosest;
1675 throw ValueError(
"two-phase solution for Y");
1679 throw ValueError(
"phase envelope must be built to carry out HSU_P_flash for mixture");
1693 : HEOS(HEOS), T(T), value(value), other(other) {}
1694 double call(
double rhomolar) {
1699 double deriv(
double rhomolar) {
1702 double second_deriv(
double rhomolar) {
1706 solver_resid resid(&HEOS, T, value, other);
1738 Brent(resid, rhoc, rhomin, LDBL_EPSILON, 1e-9, 100);
1739 }
else if (y < yc) {
1757 if (step_count > 30) {
1758 throw ValueError(
format(
"Even by increasing rhoc, not able to bound input; input %Lg is not in range %Lg,%Lg", y, yc, ymin));
1762 Brent(resid, rhomin, rhoc, LDBL_EPSILON, 1e-9, 100);
1764 throw ValueError(
format(
"input %Lg is not in range %Lg,%Lg,%Lg", y, yc, ymin));
1802 CoolPropDbl rhomolar_guess = (rhomelt - rhoL) / (ymelt - yL) * (y - yL) + rhoL;
1805 Halley(resid, rhomolar_guess, 1e-8, 100);
1807 Secant(resid, rhomolar_guess, 0.0001 * rhomolar_guess, 1e-12, 100);
1816 Halley(resid, 0.5 * (rhomin + rhoV), 1e-8, 100);
1819 Brent(resid, rhomin, rhoV, LDBL_EPSILON, 1e-12, 100);
1825 throw ValueError(
format(
"phase to solver_for_rho_given_T_oneof_HSU is invalid"));
1864 Q = (1 / HEOS.
rhomolar() - 1 / HEOS1.
SatL->rhomolar()) / (1 / HEOS1.
SatV->rhomolar() - 1 / HEOS1.
SatL->rhomolar());
1867 Q = (HEOS.
smolar() - HEOS1.
SatL->smolar()) / (HEOS1.
SatV->smolar() - HEOS1.
SatL->smolar());
1870 Q = (HEOS.
hmolar() - HEOS1.
SatL->hmolar()) / (HEOS1.
SatV->hmolar() - HEOS1.
SatL->hmolar());
1873 Q = (HEOS.
umolar() - HEOS1.
SatL->umolar()) / (HEOS1.
SatV->umolar() - HEOS1.
SatL->umolar());
1885 HEOS.
_p = HEOS.
_Q * HEOS1.
SatV->p() + (1 - HEOS.
_Q) * HEOS1.
SatL->p();
1891 throw ValueError(
format(
"Temperature specified is not the imposed phase region."));
1956 : HEOS(HEOS), hmolar(hmolar_spec), smolar(smolar_spec), Qs(_HUGE){};
1957 double call(
double T) {
1961 Qs = (smolar - SatL.
smolar()) / (SatV.smolar() - SatL.
smolar());
1967 } resid(HEOS, hmolar_spec, smolar_spec);
1975 Tmin_sat = std::max(Tmin_satL, Tmin_satV) - 1e-13;
1984 double resid = 9e30, resid_old = 9e30;
1990 r(0) = HEOS.
hmolar() - hmolar_spec;
1991 r(1) = HEOS.
smolar() - smolar_spec;
1997 Eigen::Vector2d v = J.colPivHouseholderQr().solve(-r);
1998 bool good_solution =
false;
1999 double tau0 = HEOS.
tau(), delta0 = HEOS.
delta();
2002 for (
double frac = 1.0; frac > 0.001; frac /= 2) {
2005 double tau_new = tau0 + options.
omega * frac * v(0);
2006 double delta_new = delta0 + options.
omega * frac * v(1);
2007 double T_new = reducing.
T / tau_new;
2008 double rhomolar_new = delta_new * reducing.
rhomolar;
2012 if (resid > resid_old) {
2013 throw ValueError(
format(
"residual not decreasing; frac: %g, resid: %g, resid_old: %g", frac, resid, resid_old));
2015 good_solution =
true;
2022 if (!good_solution) {
2027 throw ValueError(
format(
"HS_flash_singlephase took too many iterations; residual is %g; prior was %g", resid, resid_old));
2029 }
while (std::abs(resid) > 1e-9);
2033 double logp = ((double)rand() / (double)RAND_MAX) * (log(HEOS.
pmax()) - log(HEOS.
p_triple())) + log(HEOS.
p_triple());
2034 T = ((double)rand() / (double)RAND_MAX) * (HEOS.
Tmax() - HEOS.
Ttriple()) + HEOS.
Ttriple();
2047 : HEOS(HEOS), hmolar(hmolar_spec), smolar(smolar_spec){};
2048 double call(
double T) {
2050 double r = HEOS.
hmolar() - hmolar;
2053 } resid(HEOS, hmolar, smolar);
2056 bool good_Tmin =
false;
2061 rmin = resid.call(Tmin);
2066 if (Tmin > HEOS.
Tmax()) {
2069 }
while (!good_Tmin);
2072 bool good_Tmax =
false;
2073 double Tmax = HEOS.
Tmax() * 1.01;
2077 rmax = resid.call(Tmax);
2085 }
while (!good_Tmax);
2086 if (rmin * rmax > 0 && std::abs(rmax) < std::abs(rmin)) {
2092#if defined(ENABLE_CATCH)
2094TEST_CASE(
"PD with T very large should yield error",
"[PDflash]") {
2096 double Tc = HEOS->T_critical();
2098 CHECK_THROWS(HEOS->update(
DmassP_INPUTS, 2, 5 * HEOS->p()));
2101TEST_CASE(
"Stability testing",
"[stability]") {
2102 shared_ptr<HelmholtzEOSMixtureBackend> HEOS(
new HelmholtzEOSMixtureBackend(
strsplit(
"n-Propane&n-Butane&n-Pentane&n-Hexane",
'&')));
2103 std::vector<double> z(4);
2108 HEOS->set_mole_fractions(z);
2111 double TL = HEOS->T();
2114 double TV = HEOS->T();
2116 SECTION(
"Liquid (feed is stable)") {
2117 StabilityRoutines::StabilityEvaluationClass stability_tester(*HEOS);
2118 for (
double T = TL - 1; T >= 100; T -= 1) {
2119 stability_tester.set_TP(T, 101325);
2121 CHECK_NOTHROW(stability_tester.is_stable());
2124 SECTION(
"Vapor (feed is stable)") {
2125 StabilityRoutines::StabilityEvaluationClass stability_tester(*HEOS);
2126 for (
double T = TV + 1; T <= 500; T += 1) {
2127 stability_tester.set_TP(T, 101325);
2129 CHECK_NOTHROW(stability_tester.is_stable());
2132 SECTION(
"Two-phase (feed is unstable)") {
2133 StabilityRoutines::StabilityEvaluationClass stability_tester(*HEOS);
2134 stability_tester.set_TP((TV + TL) / 2.0, 101325);
2135 CHECK(stability_tester.is_stable() ==
false);
2139TEST_CASE(
"Test critical points for methane + H2S",
"[critical_points]") {
2140 shared_ptr<HelmholtzEOSMixtureBackend> HEOS(
new HelmholtzEOSMixtureBackend(
strsplit(
"Methane&H2S",
'&')));
2142 double zz[] = {0.998, 0.97, 0.9475, 0.94, 0.93, 0.86, 0.85, 0.84, 0.75, 0.53, 0.52, 0.51, 0.49, 0.36, 0.24, 0.229, 0.09};
2143 int Npts[] = {2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 1, 1, 1, 1};
2144 int imax =
sizeof(zz) /
sizeof(
double);
2146 for (
int i = 0; i < imax; ++i) {
2148 std::vector<double> z(2);
2151 HEOS->set_mole_fractions(z);
2153 std::vector<CriticalState> pts = HEOS->all_critical_points();
2154 CHECK(pts.size() == Npts[i]);
2158TEST_CASE(
"Test critical points for nitrogen + ethane with HEOS",
"[critical_points]") {
2159 shared_ptr<HelmholtzEOSMixtureBackend> HEOS(
new HelmholtzEOSMixtureBackend(
strsplit(
"Nitrogen&Ethane",
'&')));
2160 std::vector<double> zz =
linspace(0.001, 0.999, 21);
2161 for (
int i = 0; i < static_cast<std::size_t>(zz.size()); ++i) {
2163 std::vector<double> z(2);
2166 HEOS->set_mole_fractions(z);
2168 std::vector<CriticalState> pts;
2169 CHECK_NOTHROW(pts = HEOS->all_critical_points());
2173TEST_CASE(
"Test critical points for nitrogen + ethane with PR",
"[critical_points]") {
2174 shared_ptr<PengRobinsonBackend> HEOS(
new PengRobinsonBackend(
strsplit(
"Nitrogen&Ethane",
'&')));
2175 HEOS->set_binary_interaction_double(0, 1,
"kij", 0.0407);
2176 std::vector<double> zz =
linspace(0.001, 0.999, 21);
2177 for (
int i = 0; i < static_cast<std::size_t>(zz.size()); ++i) {
2179 std::vector<double> z(2);
2182 HEOS->set_mole_fractions(z);
2184 std::vector<CriticalState> pts;
2185 CHECK_NOTHROW(pts = HEOS->all_critical_points());