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);
263 double Tmin = HEOS.
Tmin() + 0.1;
266 const double eps = 1e-12;
294 if (std::abs(HEOS.
Q() - 1) > 1e-10) {
295 throw ValueError(
format(
"non-unity quality not currently allowed for HQ_flash"));
316 }
else if (std::abs(HEOS.
Q()) < 1e-10) {
327 }
else if (std::abs(HEOS.
Q() - 1) < 1e-10) {
339 throw ValueError(
format(
"non-zero or 1 quality not currently allowed for QS_flash"));
353 auto& superanc = optsuperanc.value();
357 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));
359 auto rhoL = superanc.eval_sat(T,
'D', 0);
360 auto rhoV = superanc.eval_sat(T,
'D', 1);
361 auto p = superanc.eval_sat(T,
'P', 1);
362 HEOS.
SatL->update_TDmolarP_unchecked(T, rhoL, p);
363 HEOS.
SatV->update_TDmolarP_unchecked(T, rhoV, p);
365 HEOS.
_rhomolar = 1 / (Q / rhoV + (1 - Q) / rhoL);
379 Tmin_sat = std::max(Tmin_satL, Tmin_satV) - 1e-13;
384 if ((
get_config_bool(CRITICAL_WITHIN_1UK) && std::abs(T - Tmax_sat) < 1e-6) || std::abs(T - Tmax_sat) < 1e-12) {
389 HEOS.
_p = 0.5 * HEOS.
SatV->p() + 0.5 * HEOS.
SatL->p();
391 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));
393 double rhoL = _HUGE, rhoV = _HUGE;
398 HEOS.
_p = 0.5 * HEOS.
SatV->p() + 0.5 * HEOS.
SatL->p();
400 }
else if (!(HEOS.
components[0].EOS().pseudo_pure)) {
407 HEOS.
_p = 0.5 * HEOS.
SatV->p() + 0.5 * HEOS.
SatL->p();
411 CoolPropDbl rhoLanc = _HUGE, rhoVanc = _HUGE, rhoLsat = _HUGE, rhoVsat = _HUGE;
414 rhoLanc = HEOS.
components[0].ancillaries.rhoL.evaluate(HEOS.
_T);
415 HEOS.
SatL->update_TP_guessrho(HEOS.
_T, HEOS.
_p, rhoLanc);
419 rhoVanc = HEOS.
components[0].ancillaries.rhoV.evaluate(HEOS.
_T);
420 HEOS.
SatV->update_TP_guessrho(HEOS.
_T, HEOS.
_p, rhoVanc);
423 throw CoolProp::ValueError(
format(
"For pseudo-pure fluid, quality must be equal to 0 or 1. Two-phase quality is not defined"));
457 SaturationSolvers::newton_raphson_saturation NR;
458 SaturationSolvers::newton_raphson_saturation_options IO;
460 IO.bubble_point = (HEOS.
_Q < 0.5);
470 IO.imposed_variable = SaturationSolvers::newton_raphson_saturation_options::T_IMPOSED;
472 if (IO.bubble_point) {
474 NR.call(HEOS, IO.x, IO.y, IO);
477 NR.call(HEOS, IO.y, IO.x, IO);
481 HEOS.
_rhomolar = 1 / (HEOS.
_Q / IO.rhomolar_vap + (1 - HEOS.
_Q) / IO.rhomolar_liq);
491void get_Henrys_coeffs_FP(
const std::string& CAS,
double& A,
double& B,
double& C,
double& Tmin,
double& Tmax) {
493 if (CAS ==
"7440-59-7")
500 }
else if (CAS ==
"7440-01-9")
507 }
else if (CAS ==
"7440-37-1")
514 }
else if (CAS ==
"7439-90-9")
521 }
else if (CAS ==
"7440-63-3")
528 }
else if (CAS ==
"1333-74-0")
535 }
else if (CAS ==
"7727-37-9")
542 }
else if (CAS ==
"7782-44-7")
549 }
else if (CAS ==
"630-08-0")
556 }
else if (CAS ==
"124-38-9")
563 }
else if (CAS ==
"7783-06-4")
570 }
else if (CAS ==
"74-82-8")
577 }
else if (CAS ==
"74-84-0")
584 }
else if (CAS ==
"2551-62-4")
592 throw ValueError(
"Bad component in Henry's law constants");
601 auto& superanc = optsuperanc.value();
603 if (HEOS.
_p > pmax_num){
604 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));
606 auto T = superanc.get_T_from_p(HEOS.
_p);
607 auto rhoL = superanc.eval_sat(T,
'D', 0);
608 auto rhoV = superanc.eval_sat(T,
'D', 1);
610 HEOS.
SatL->update_TDmolarP_unchecked(T, rhoL, p);
611 HEOS.
SatV->update_TDmolarP_unchecked(T, rhoV, p);
629 HEOS.
SatL->update_TP_guessrho(HEOS.
_TLanc, HEOS.
_p, rhoL);
630 HEOS.
SatV->update_TP_guessrho(HEOS.
_TVanc, HEOS.
_p, rhoV);
634 HEOS.
_p = HEOS.
_Q * HEOS.
SatV->p() + (1 - HEOS.
_Q) * HEOS.
SatL->p();
635 HEOS.
_T = HEOS.
_Q * HEOS.
SatV->T() + (1 - HEOS.
_Q) * HEOS.
SatL->T();
644 pmin_sat = std::max(pmin_satL, pmin_satV);
659 throw ValueError(
format(
"Pressure to PQ_flash [%6g Pa] must be in range [%8Lg Pa, %8Lg Pa]", HEOS.
_p, pmin_sat, pmax_sat));
673 double increment = 0.4;
676 for (
double omega = 1.0; omega > 0; omega -= increment) {
678 options.
omega = omega;
686 if (omega < 1.1 * increment) {
699 HEOS.
_p = HEOS.
_Q * HEOS.
SatV->p() + (1 - HEOS.
_Q) * HEOS.
SatL->p();
719 std::vector<CoolPropDbl> K = HEOS.
K;
721 if (
get_config_bool(HENRYS_LAW_TO_GENERATE_VLE_GUESSES) && std::abs(HEOS.
_Q - 1) < 1e-10) {
722 const std::vector<CoolPropFluid>& components = HEOS.
get_components();
723 std::size_t iWater = 0;
724 double p1star =
PropsSI(
"P",
"T", Tguess,
"Q", 1,
"Water");
726 std::vector<CoolPropDbl> x(y.size());
727 for (std::size_t i = 0; i < components.size(); ++i) {
728 if (components[i].CAS ==
"7732-18-5") {
732 double A, B, C, Tmin, Tmax;
734 double T_R = Tguess / 647.096, tau = 1 - T_R;
735 double k_H = p1star * exp(A / T_R + B * pow(tau, 0.355) / T_R + C * pow(T_R, -0.41) * exp(tau));
736 x[i] = y[i] * HEOS.
_p / k_H;
743 for (std::size_t i = 0; i < y.size(); ++i) {
749 K[iWater] = y[iWater] / x[iWater];
759 SaturationSolvers::newton_raphson_saturation NR;
760 SaturationSolvers::newton_raphson_saturation_options IO;
762 IO.bubble_point = (HEOS.
_Q < 0.5);
770 IO.imposed_variable = SaturationSolvers::newton_raphson_saturation_options::P_IMPOSED;
772 if (IO.bubble_point) {
774 NR.call(HEOS, IO.x, IO.y, IO);
777 NR.call(HEOS, IO.y, IO.x, IO);
790 SaturationSolvers::newton_raphson_saturation NR;
791 SaturationSolvers::newton_raphson_saturation_options IO;
794 IO.x = std::vector<CoolPropDbl>(guess.
x.begin(), guess.
x.end());
795 IO.y = std::vector<CoolPropDbl>(guess.
y.begin(), guess.
y.end());
798 IO.bubble_point =
false;
799 IO.imposed_variable = SaturationSolvers::newton_raphson_saturation_options::P_IMPOSED;
801 if (std::abs(HEOS.
Q()) < 1e-10) {
802 IO.bubble_point =
true;
803 NR.call(HEOS, IO.x, IO.y, IO);
804 }
else if (std::abs(HEOS.
Q() - 1) < 1e-10) {
805 IO.bubble_point =
false;
806 NR.call(HEOS, IO.y, IO.x, IO);
813 HEOS.
_rhomolar = 1 / (HEOS.
_Q / IO.rhomolar_vap + (1 - HEOS.
_Q) / IO.rhomolar_liq);
817 SaturationSolvers::newton_raphson_saturation NR;
818 SaturationSolvers::newton_raphson_saturation_options IO;
821 IO.x = std::vector<CoolPropDbl>(guess.
x.begin(), guess.
x.end());
822 IO.y = std::vector<CoolPropDbl>(guess.
y.begin(), guess.
y.end());
825 IO.bubble_point =
false;
826 IO.imposed_variable = SaturationSolvers::newton_raphson_saturation_options::T_IMPOSED;
829 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,
833 if (std::abs(HEOS.
Q()) < 1e-10) {
834 IO.bubble_point =
true;
835 NR.call(HEOS, IO.x, IO.y, IO);
836 }
else if (std::abs(HEOS.
Q() - 1) < 1e-10) {
837 IO.bubble_point =
false;
838 NR.call(HEOS, IO.y, IO.x, IO);
846 HEOS.
_rhomolar = 1 / (HEOS.
_Q / IO.rhomolar_vap + (1 - HEOS.
_Q) / IO.rhomolar_liq);
877 std::vector<std::pair<std::size_t, std::size_t>> intersections =
888 quality_options quality;
890 quality = SATURATED_LIQUID;
892 quality = SATURATED_VAPOR;
893 }
else if (HEOS.
_Q > 0 && HEOS.
_Q < 1) {
896 throw ValueError(
"Quality is not within 0 and 1");
899 if (quality == SATURATED_LIQUID || quality == SATURATED_VAPOR) {
904 std::vector<std::size_t> solutions;
905 for (std::vector<std::pair<std::size_t, std::size_t>>::const_iterator it = intersections.begin(); it != intersections.end(); ++it) {
907 solutions.push_back(it->first);
911 if (solutions.size() == 1) {
913 std::size_t& imax = solutions[0];
916 if (imax + 2 >= env.T.size()) {
918 }
else if (imax == 0) {
926 SaturationSolvers::newton_raphson_saturation NR;
927 SaturationSolvers::newton_raphson_saturation_options IO;
931 IO.imposed_variable = SaturationSolvers::newton_raphson_saturation_options::P_IMPOSED;
933 IO.rhomolar_vap =
CubicInterp(env.p, env.rhomolar_vap, imax - 1, imax, imax + 1, imax + 2,
static_cast<CoolPropDbl>(IO.p));
934 IO.T =
CubicInterp(env.rhomolar_vap, env.T, imax - 1, imax, imax + 1, imax + 2, IO.rhomolar_vap);
935 }
else if (other ==
iT) {
937 IO.imposed_variable = SaturationSolvers::newton_raphson_saturation_options::T_IMPOSED;
939 IO.rhomolar_vap =
CubicInterp(env.T, env.rhomolar_vap, imax - 1, imax, imax + 1, imax + 2,
static_cast<CoolPropDbl>(IO.T));
940 IO.p =
CubicInterp(env.rhomolar_vap, env.p, imax - 1, imax, imax + 1, imax + 2, IO.rhomolar_vap);
944 IO.rhomolar_liq =
CubicInterp(env.rhomolar_vap, env.rhomolar_liq, imax - 1, imax, imax + 1, imax + 2, IO.rhomolar_vap);
946 if (quality == SATURATED_VAPOR) {
947 IO.bubble_point =
false;
949 IO.x.resize(IO.y.size());
950 for (std::size_t i = 0; i < IO.x.size() - 1; ++i)
952 IO.x[i] =
CubicInterp(env.rhomolar_vap, env.x[i], imax - 1, imax, imax + 1, imax + 2, IO.rhomolar_vap);
954 IO.x[IO.x.size() - 1] = 1 - std::accumulate(IO.x.begin(), IO.x.end() - 1, 0.0);
955 NR.call(HEOS, IO.y, IO.x, IO);
957 IO.bubble_point =
true;
959 IO.y.resize(IO.x.size());
961 std::swap(IO.rhomolar_liq, IO.rhomolar_vap);
962 for (std::size_t i = 0; i < IO.y.size() - 1; ++i)
966 IO.y[i] =
CubicInterp(env.rhomolar_vap, env.x[i], imax - 1, imax, imax + 1, imax + 2, IO.rhomolar_liq);
968 IO.y[IO.y.size() - 1] = 1 - std::accumulate(IO.y.begin(), IO.y.end() - 1, 0.0);
969 NR.call(HEOS, IO.x, IO.y, IO);
971 }
else if (solutions.size() == 0) {
972 throw ValueError(
"No solution was found in PQ_flash");
974 throw ValueError(
"More than 1 solution was found in PQ_flash");
982 std::vector<std::size_t> liquid_solutions, vapor_solutions;
983 for (std::vector<std::pair<std::size_t, std::size_t>>::const_iterator it = intersections.begin(); it != intersections.end(); ++it) {
984 if (std::abs(env.Q[it->first] - 0) < 10 *
DBL_EPSILON && std::abs(env.Q[it->second] - 0) < 10 *
DBL_EPSILON) {
985 liquid_solutions.push_back(it->first);
987 if (std::abs(env.Q[it->first] - 1) < 10 *
DBL_EPSILON && std::abs(env.Q[it->second] - 1) < 10 *
DBL_EPSILON) {
988 vapor_solutions.push_back(it->first);
992 if (liquid_solutions.size() != 1 || vapor_solutions.size() != 1) {
993 throw ValueError(
format(
"Number liquid solutions [%d] or vapor solutions [%d] != 1", liquid_solutions.size(), vapor_solutions.size()));
995 std::size_t iliq = liquid_solutions[0], ivap = vapor_solutions[0];
997 SaturationSolvers::newton_raphson_twophase NR;
998 SaturationSolvers::newton_raphson_twophase_options IO;
1001 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,
1008 IO.imposed_variable = SaturationSolvers::newton_raphson_twophase_options::P_IMPOSED;
1011 rhomolar_vap_sat_vap =
CubicInterp(env.p, env.rhomolar_vap, ivap - 1, ivap, ivap + 1, ivap + 2,
static_cast<CoolPropDbl>(IO.p));
1012 T_sat_vap =
CubicInterp(env.rhomolar_vap, env.T, ivap - 1, ivap, ivap + 1, ivap + 2, rhomolar_vap_sat_vap);
1013 rhomolar_liq_sat_vap =
CubicInterp(env.rhomolar_vap, env.rhomolar_liq, ivap - 1, ivap, ivap + 1, ivap + 2, rhomolar_vap_sat_vap);
1016 rhomolar_liq_sat_liq =
CubicInterp(env.p, env.rhomolar_vap, iliq - 1, iliq, iliq + 1, iliq + 2,
static_cast<CoolPropDbl>(IO.p));
1017 T_sat_liq =
CubicInterp(env.rhomolar_vap, env.T, iliq - 1, iliq, iliq + 1, iliq + 2, rhomolar_liq_sat_liq);
1018 rhomolar_vap_sat_liq =
CubicInterp(env.rhomolar_vap, env.rhomolar_liq, iliq - 1, iliq, iliq + 1, iliq + 2, rhomolar_liq_sat_liq);
1019 }
else if (other ==
iT) {
1023 IO.imposed_variable = SaturationSolvers::newton_raphson_twophase_options::T_IMPOSED;
1026 rhomolar_vap_sat_vap =
CubicInterp(env.T, env.rhomolar_vap, ivap - 1, ivap, ivap + 1, ivap + 2,
static_cast<CoolPropDbl>(IO.T));
1027 p_sat_vap =
CubicInterp(env.rhomolar_vap, env.p, ivap - 1, ivap, ivap + 1, ivap + 2, rhomolar_vap_sat_vap);
1028 rhomolar_liq_sat_vap =
CubicInterp(env.rhomolar_vap, env.rhomolar_liq, ivap - 1, ivap, ivap + 1, ivap + 2, rhomolar_vap_sat_vap);
1031 rhomolar_liq_sat_liq =
CubicInterp(env.T, env.rhomolar_vap, iliq - 1, iliq, iliq + 1, iliq + 2,
static_cast<CoolPropDbl>(IO.T));
1032 p_sat_liq =
CubicInterp(env.rhomolar_vap, env.p, iliq - 1, iliq, iliq + 1, iliq + 2, rhomolar_liq_sat_liq);
1033 rhomolar_vap_sat_liq =
CubicInterp(env.rhomolar_vap, env.rhomolar_liq, iliq - 1, iliq, iliq + 1, iliq + 2, rhomolar_liq_sat_liq);
1039 IO.rhomolar_vap = IO.beta * rhomolar_vap_sat_vap + (1 - IO.beta) * rhomolar_vap_sat_liq;
1040 IO.rhomolar_liq = IO.beta * rhomolar_liq_sat_vap + (1 - IO.beta) * rhomolar_liq_sat_liq;
1041 IO.T = IO.beta * T_sat_vap + (1 - IO.beta) * T_sat_liq;
1042 IO.p = IO.beta * p_sat_vap + (1 - IO.beta) * p_sat_liq;
1045 IO.x.resize(IO.z.size());
1046 IO.y.resize(IO.z.size());
1048 for (std::size_t i = 0; i < IO.x.size() - 1; ++i)
1050 CoolPropDbl x_sat_vap =
CubicInterp(env.rhomolar_vap, env.x[i], ivap - 1, ivap, ivap + 1, ivap + 2, rhomolar_vap_sat_vap);
1051 CoolPropDbl y_sat_vap =
CubicInterp(env.rhomolar_vap, env.y[i], ivap - 1, ivap, ivap + 1, ivap + 2, rhomolar_vap_sat_vap);
1053 CoolPropDbl x_sat_liq =
CubicInterp(env.rhomolar_vap, env.y[i], iliq - 1, iliq, iliq + 1, iliq + 2, rhomolar_liq_sat_liq);
1054 CoolPropDbl y_sat_liq =
CubicInterp(env.rhomolar_vap, env.x[i], iliq - 1, iliq, iliq + 1, iliq + 2, rhomolar_liq_sat_liq);
1056 IO.x[i] = IO.beta * x_sat_vap + (1 - IO.beta) * x_sat_liq;
1057 IO.y[i] = IO.beta * y_sat_vap + (1 - IO.beta) * y_sat_liq;
1059 IO.x[IO.x.size() - 1] = 1 - std::accumulate(IO.x.begin(), IO.x.end() - 1, 0.0);
1060 IO.y[IO.y.size() - 1] = 1 - std::accumulate(IO.y.begin(), IO.y.end() - 1, 0.0);
1075 : HEOS(HEOS), rhomolar_spec(rhomolar_spec), other(other), value(value) {
1078 double call(
double T) {
1082 Qd = (1 / rhomolar_spec - 1 / SatL.
rhomolar()) / (1 / SatV.rhomolar() - 1 / SatL.
rhomolar());
1088 } resid(HEOS, rhomolar_spec, other, value);
1096 Tmin_sat = std::max(Tmin_satL, Tmin_satV) - 1e-13;
1114 : HEOS(HEOS), rhomolar(rhomolar), value(value), other(other), Tmin(Tmin), Tmax(Tmax) {
1118 double call(
double T) {
1123 return (eos - value) / value;
1129 double deriv(
double T) {
1135 double second_deriv(
double T) {
1141 bool input_not_in_range(
double T) {
1142 return (T < Tmin || T > Tmax);
1149 shared_ptr<HelmholtzEOSMixtureBackend> Sat;
1183 y_solid = (yV - yL) / (1 / rhoVtriple - 1 / rhoLtriple) * (1 / HEOS.
_rhomolar - 1 / rhoLtriple) + yL;
1185 if (value < y_solid) {
1194 for (
int i_try = 0; i_try < 7; i_try++)
1216 optionsD.
omega /= 2;
1218 if (i_try >= 6){
throw;}
1223 if (value > Sat->keyed_output(other)) {
1224 solver_resid resid(&HEOS, HEOS.
_rhomolar, value, other, Sat->keyed_output(
iT), HEOS.
Tmax() * 1.5);
1226 HEOS.
_T =
Halley(resid, 0.5 * (Sat->keyed_output(
iT) + HEOS.
Tmax() * 1.5), 1e-10, 100);
1252 HEOS.
_Q = (1 / HEOS.
_rhomolar - 1 / HEOS.
SatL->rhomolar()) / (1 / HEOS.
SatV->rhomolar() - 1 / HEOS.
SatL->rhomolar());
1253 HEOS.
_T = HEOS.
SatL->T();
1289 solver_resid resid(&HEOS, HEOS.
_rhomolar, value, other, TVtriple, HEOS.
Tmax() * 1.5);
1330 solver_resid resid(&HEOS, HEOS.
_rhomolar, value, other, TLtriple, HEOS.
Tmax() * 1.5);
1352 double A[2][2], B[2][2];
1368 throw ValueError(
"other is invalid in HSU_P_flash_singlephase_Newton");
1373 bool failed =
false;
1374 CoolPropDbl omega = 1.0, f2, df2_dtau, df2_ddelta;
1376 while (worst_error > 1e-6 && failed ==
false) {
1392 CoolPropDbl f1 = delta / tau * (1 + delta * dar_ddelta) - p / (rhoc * R * Tc);
1393 CoolPropDbl df1_dtau = (1 + delta * dar_ddelta) * (-delta / tau / tau) + delta / tau * (delta * d2ar_ddelta_dtau);
1394 CoolPropDbl df1_ddelta = (1.0 / tau) * (1 + 2.0 * delta * dar_ddelta + delta * delta * d2ar_ddelta2);
1397 f2 = (1 + delta * dar_ddelta) + tau * (da0_dtau + dar_dtau) - tau * y / (R * Tc);
1398 df2_dtau = delta * d2ar_ddelta_dtau + da0_dtau + dar_dtau + tau * (d2a0_dtau2 + d2ar_dtau2) - y / (R * Tc);
1399 df2_ddelta = (dar_ddelta + delta * d2ar_ddelta2) + tau * (d2a0_ddelta_dtau + d2ar_ddelta_dtau);
1403 f2 = tau * (da0_dtau + dar_dtau) - ar - a0 - y / R;
1404 df2_dtau = tau * (d2a0_dtau2 + d2ar_dtau2) + (da0_dtau + dar_dtau) - dar_dtau - da0_dtau;
1405 df2_ddelta = tau * (d2a0_ddelta_dtau + d2ar_ddelta_dtau) - dar_ddelta - da0_ddelta;
1409 throw ValueError(
"other variable in HSU_P_flash_singlephase_Newton is invalid");
1414 A[0][1] = df1_ddelta;
1416 A[1][1] = df2_ddelta;
1421 tau -= omega * (B[0][0] * f1 + B[0][1] * f2);
1422 delta -= omega * (B[1][0] * f1 + B[1][1] * f2);
1424 if (std::abs(f1) > std::abs(f2))
1425 worst_error = std::abs(f1);
1427 worst_error = std::abs(f2);
1430 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()));
1435 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()));
1444 throw ValueError(
"value for p in HSU_P_flash_singlephase_Brent is invalid");
1447 throw ValueError(
"value for other in HSU_P_flash_singlephase_Brent is invalid");
1456 CoolPropDbl eos0, eos1, rhomolar, rhomolar0, rhomolar1;
1482 double call(
double T) {
1484 if (iter < 2 || std::abs(rhomolar1 / rhomolar0 - 1) > 0.05) {
1504 rhomolar0 = rhomolar;
1505 }
else if (iter == 1) {
1507 rhomolar1 = rhomolar;
1511 rhomolar0 = rhomolar1;
1512 rhomolar1 = rhomolar;
1518 double deriv(
double T) {
1521 double second_deriv(
double T) {
1524 bool input_not_in_range(
double x) {
1525 return (x < Tmin || x > Tmax);
1528 solver_resid resid(&HEOS, HEOS.
_p, value, other, Tmin, Tmax);
1532 Halley(resid, Tmin, 1e-12, 100);
1534 throw ValueError(
"Halley's method was unable to find a solution in HSU_P_flash_singlephase_Brent");
1555 if (eos1 > eos0 && value > eos1) {
1557 format(
"HSU_P_flash_singlephase_Brent could not find a solution because %s [%Lg %s] is above the maximum value of %0.12Lg %s",
1558 name.c_str(), value, units.c_str(), eos1, units.c_str()));
1560 if (eos1 > eos0 && value < eos0) {
1562 format(
"HSU_P_flash_singlephase_Brent could not find a solution because %s [%Lg %s] is below the minimum value of %0.12Lg %s",
1563 name.c_str(), value, units.c_str(), eos0, units.c_str()));
1572 bool saturation_called =
false;
1600 Tmax = 1.5 * HEOS.
Tmax();
1608 auto& superanc = optsuperanc.value();
1610 if (HEOS.
_p > pmax_num){
1611 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));
1613 Tmin = superanc.get_T_from_p(HEOS.
_p)-1e-12;
1617 if (saturation_called) {
1618 Tmin = HEOS.
SatV->T();
1626 if (saturation_called) {
1627 Tmax = HEOS.
SatL->T();
1636 Tmin = HEOS.
Tmin() - 1e-3;
1643 Tmax = 1.5 * HEOS.
Tmax();
1648 Tmin = HEOS.
Tmin() - 1e-3;
1658 }
catch (std::exception& e) {
1659 throw ValueError(
format(
"unable to solve 1phase PY flash with Tmin=%Lg, Tmax=%Lg due to error: %s", Tmin, Tmax, e.what()));
1669 std::size_t iclosest;
1678 throw ValueError(
"two-phase solution for Y");
1682 throw ValueError(
"phase envelope must be built to carry out HSU_P_flash for mixture");
1696 : HEOS(HEOS), T(T), value(value), other(other) {}
1697 double call(
double rhomolar) {
1702 double deriv(
double rhomolar) {
1705 double second_deriv(
double rhomolar) {
1709 solver_resid resid(&HEOS, T, value, other);
1741 Brent(resid, rhoc, rhomin, LDBL_EPSILON, 1e-9, 100);
1742 }
else if (y < yc) {
1760 if (step_count > 30) {
1761 throw ValueError(
format(
"Even by increasing rhoc, not able to bound input; input %Lg is not in range %Lg,%Lg", y, yc, ymin));
1765 Brent(resid, rhomin, rhoc, LDBL_EPSILON, 1e-9, 100);
1767 throw ValueError(
format(
"input %Lg is not in range %Lg,%Lg,%Lg", y, yc, ymin));
1805 CoolPropDbl rhomolar_guess = (rhomelt - rhoL) / (ymelt - yL) * (y - yL) + rhoL;
1808 Halley(resid, rhomolar_guess, 1e-8, 100);
1810 Secant(resid, rhomolar_guess, 0.0001 * rhomolar_guess, 1e-12, 100);
1819 Halley(resid, 0.5 * (rhomin + rhoV), 1e-8, 100);
1822 Brent(resid, rhomin, rhoV, LDBL_EPSILON, 1e-12, 100);
1828 throw ValueError(
format(
"phase to solver_for_rho_given_T_oneof_HSU is invalid"));
1867 Q = (1 / HEOS.
rhomolar() - 1 / HEOS1.
SatL->rhomolar()) / (1 / HEOS1.
SatV->rhomolar() - 1 / HEOS1.
SatL->rhomolar());
1870 Q = (HEOS.
smolar() - HEOS1.
SatL->smolar()) / (HEOS1.
SatV->smolar() - HEOS1.
SatL->smolar());
1873 Q = (HEOS.
hmolar() - HEOS1.
SatL->hmolar()) / (HEOS1.
SatV->hmolar() - HEOS1.
SatL->hmolar());
1876 Q = (HEOS.
umolar() - HEOS1.
SatL->umolar()) / (HEOS1.
SatV->umolar() - HEOS1.
SatL->umolar());
1888 HEOS.
_p = HEOS.
_Q * HEOS1.
SatV->p() + (1 - HEOS.
_Q) * HEOS1.
SatL->p();
1894 throw ValueError(
format(
"Temperature specified is not the imposed phase region."));
1959 : HEOS(HEOS), hmolar(hmolar_spec), smolar(smolar_spec), Qs(_HUGE){};
1960 double call(
double T) {
1964 Qs = (smolar - SatL.
smolar()) / (SatV.smolar() - SatL.
smolar());
1970 } resid(HEOS, hmolar_spec, smolar_spec);
1978 Tmin_sat = std::max(Tmin_satL, Tmin_satV) - 1e-13;
1987 double resid = 9e30, resid_old = 9e30;
1993 r(0) = HEOS.
hmolar() - hmolar_spec;
1994 r(1) = HEOS.
smolar() - smolar_spec;
2000 Eigen::Vector2d v = J.colPivHouseholderQr().solve(-r);
2001 bool good_solution =
false;
2002 double tau0 = HEOS.
tau(), delta0 = HEOS.
delta();
2005 for (
double frac = 1.0; frac > 0.001; frac /= 2) {
2008 double tau_new = tau0 + options.
omega * frac * v(0);
2009 double delta_new = delta0 + options.
omega * frac * v(1);
2010 double T_new = reducing.
T / tau_new;
2011 double rhomolar_new = delta_new * reducing.
rhomolar;
2015 if (resid > resid_old) {
2016 throw ValueError(
format(
"residual not decreasing; frac: %g, resid: %g, resid_old: %g", frac, resid, resid_old));
2018 good_solution =
true;
2025 if (!good_solution) {
2030 throw ValueError(
format(
"HS_flash_singlephase took too many iterations; residual is %g; prior was %g", resid, resid_old));
2032 }
while (std::abs(resid) > 1e-9);
2036 double logp = ((double)rand() / (double)RAND_MAX) * (log(HEOS.
pmax()) - log(HEOS.
p_triple())) + log(HEOS.
p_triple());
2037 T = ((double)rand() / (double)RAND_MAX) * (HEOS.
Tmax() - HEOS.
Ttriple()) + HEOS.
Ttriple();
2050 : HEOS(HEOS), hmolar(hmolar_spec), smolar(smolar_spec){};
2051 double call(
double T) {
2053 double r = HEOS.
hmolar() - hmolar;
2056 } resid(HEOS, hmolar, smolar);
2059 bool good_Tmin =
false;
2064 rmin = resid.call(Tmin);
2069 if (Tmin > HEOS.
Tmax()) {
2072 }
while (!good_Tmin);
2075 bool good_Tmax =
false;
2076 double Tmax = HEOS.
Tmax() * 1.01;
2080 rmax = resid.call(Tmax);
2088 }
while (!good_Tmax);
2089 if (rmin * rmax > 0 && std::abs(rmax) < std::abs(rmin)) {
2095#if defined(ENABLE_CATCH)
2097TEST_CASE(
"PD with T very large should yield error",
"[PDflash]") {
2099 double Tc = HEOS->T_critical();
2101 CHECK_THROWS(HEOS->update(
DmassP_INPUTS, 2, 5 * HEOS->p()));
2104TEST_CASE(
"Stability testing",
"[stability]") {
2105 shared_ptr<HelmholtzEOSMixtureBackend> HEOS(
new HelmholtzEOSMixtureBackend(
strsplit(
"n-Propane&n-Butane&n-Pentane&n-Hexane",
'&')));
2106 std::vector<double> z(4);
2111 HEOS->set_mole_fractions(z);
2114 double TL = HEOS->T();
2117 double TV = HEOS->T();
2119 SECTION(
"Liquid (feed is stable)") {
2120 StabilityRoutines::StabilityEvaluationClass stability_tester(*HEOS);
2121 for (
double T = TL - 1; T >= 100; T -= 1) {
2122 stability_tester.set_TP(T, 101325);
2124 CHECK_NOTHROW(stability_tester.is_stable());
2127 SECTION(
"Vapor (feed is stable)") {
2128 StabilityRoutines::StabilityEvaluationClass stability_tester(*HEOS);
2129 for (
double T = TV + 1; T <= 500; T += 1) {
2130 stability_tester.set_TP(T, 101325);
2132 CHECK_NOTHROW(stability_tester.is_stable());
2135 SECTION(
"Two-phase (feed is unstable)") {
2136 StabilityRoutines::StabilityEvaluationClass stability_tester(*HEOS);
2137 stability_tester.set_TP((TV + TL) / 2.0, 101325);
2138 CHECK(stability_tester.is_stable() ==
false);
2142TEST_CASE(
"Test critical points for methane + H2S",
"[critical_points]") {
2143 shared_ptr<HelmholtzEOSMixtureBackend> HEOS(
new HelmholtzEOSMixtureBackend(
strsplit(
"Methane&H2S",
'&')));
2145 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};
2146 int Npts[] = {2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 1, 1, 1, 1};
2147 int imax =
sizeof(zz) /
sizeof(
double);
2149 for (
int i = 0; i < imax; ++i) {
2151 std::vector<double> z(2);
2154 HEOS->set_mole_fractions(z);
2156 std::vector<CriticalState> pts = HEOS->all_critical_points();
2157 CHECK(pts.size() == Npts[i]);
2161TEST_CASE(
"Test critical points for nitrogen + ethane with HEOS",
"[critical_points]") {
2162 shared_ptr<HelmholtzEOSMixtureBackend> HEOS(
new HelmholtzEOSMixtureBackend(
strsplit(
"Nitrogen&Ethane",
'&')));
2163 std::vector<double> zz =
linspace(0.001, 0.999, 21);
2164 int failure_count = 0;
2165 for (
int i = 0; i < static_cast<std::size_t>(zz.size()); ++i) {
2167 std::vector<double> z(2);
2170 HEOS->set_mole_fractions(z);
2172 std::vector<CriticalState> pts;
2174 pts = HEOS->all_critical_points();
2176 catch(std::exception& e){
2182 CHECK(failure_count < 10);
2185TEST_CASE(
"Test critical points for nitrogen + ethane with PR",
"[critical_points]") {
2186 shared_ptr<PengRobinsonBackend> HEOS(
new PengRobinsonBackend(
strsplit(
"Nitrogen&Ethane",
'&')));
2187 HEOS->set_binary_interaction_double(0, 1,
"kij", 0.0407);
2188 std::vector<double> zz =
linspace(0.001, 0.999, 21);
2189 for (
int i = 0; i < static_cast<std::size_t>(zz.size()); ++i) {
2191 std::vector<double> z(2);
2194 HEOS->set_mole_fractions(z);
2196 std::vector<CriticalState> pts;
2197 CHECK_NOTHROW(pts = HEOS->all_critical_points());