20 double call(
double rhomolar_liq) {
23 std::cout <<
format(
"inner p: %0.16Lg; res: %0.16Lg", calc_p, calc_p - desired_p) << std::endl;
24 return calc_p - desired_p;
40 double call(
double rhomolar_vap) {
48 std::cout <<
format(
"outer p: %0.16Lg", p) << std::endl;
49 inner_resid inner(HEOS, T, p);
50 rhomolar_liq =
Brent(inner, rhomolar_crit * 1.5, rhomolar_crit * (1 + 1e-8), LDBL_EPSILON, 1e-10, 100);
54 throw ValueError(
"Wrong input for outer_resid");
69 outer_resid resid(HEOS,
iT, y);
73 Brent(&resid, rhomolar_crit * (1 - 1e-8), rhomolar_crit * 0.5,
DBL_EPSILON, 1e-9, 20);
86 : HEOS(&HEOS), T(T), rhomolar_liq(rhomolar_liq_guess), rhomolar_vap(rhomolar_vap_guess){};
87 double call(
double p) {
89 HEOS->
SatL->update_TP_guessrho(T, p, rhomolar_liq);
90 HEOS->
SatV->update_TP_guessrho(T, p, rhomolar_vap);
100 solver_resid resid(HEOS, T, options.
rhoL, options.
rhoV);
103 throw ValueError(
format(
"options.p is not valid in saturation_T_pure_1D_P for T = %Lg", T));
106 throw ValueError(
format(
"options.rhoL is not valid in saturation_T_pure_1D_P for T = %Lg", T));
109 throw ValueError(
format(
"options.rhoV is not valid in saturation_T_pure_1D_P for T = %Lg", T));
113 Secant(resid, options.
p, options.
p * 1.1, 1e-10, 100);
117 Brent(resid, pmin, pmax, LDBL_EPSILON, 1e-8, 100);
131 : HEOS(&HEOS), p(p), rhomolar_liq(rhomolar_liq_guess), rhomolar_vap(rhomolar_vap_guess){};
132 double call(
double T) {
134 HEOS->
SatL->update_TP_guessrho(T, p, rhomolar_liq);
135 HEOS->
SatV->update_TP_guessrho(T, p, rhomolar_vap);
145 solver_resid resid(HEOS, p, options.
rhoL, options.
rhoV);
148 throw ValueError(
"options.T is not valid in saturation_P_pure_1D_T");
151 throw ValueError(
"options.rhoL is not valid in saturation_P_pure_1D_T");
154 throw ValueError(
"options.rhoV is not valid in saturation_P_pure_1D_T");
159 Brent(resid, Tmin, Tmax, LDBL_EPSILON, 1e-11, 100);
172 std::vector<CoolPropDbl> negativer(3, _HUGE), v;
173 std::vector<std::vector<CoolPropDbl>> J(3, std::vector<CoolPropDbl>(3, _HUGE));
178 shared_ptr<HelmholtzEOSMixtureBackend> SatL = HEOS.
SatL, SatV = HEOS.
SatV;
181 CoolPropDbl deltaL = 0, deltaV = 0, tau = 0, error;
182 int iter = 0, specified_parameter;
191 T = HEOS.
get_components()[0].ancillaries.pL.invert(specified_value);
193 throw ValueError(
"Unable to invert ancillary equation");
201 throw ValueError(
"Unable to invert ancillary equation for hL");
210 this->component = &component;
213 double call(
double T) {
223 double Tmin = Tmin_satL;
230 double hTmin = HEOS_copy->hmolar();
232 double hTmax = HEOS_copy->hmolar();
233 T = (Tmax - Tmin) / (hTmax - hTmin) * (HEOS.
hmolar() - hTmin) + Tmin;
241 T = std::max(0.99 * crit.
T, crit.
T - 0.1);
249 T = anc.
invert(specified_value - hs_anchor.
smolar, Tmin, Tmax);
252 T = anc.
invert(specified_value - hs_anchor.
smolar, Tmin - 3, Tmax + 3);
256 if (std::abs(specified_value - hs_anchor.
smolar) < std::abs(vmax)) {
259 throw ValueError(
format(
"Unable to invert ancillary equation for sL for value %Lg with Tminval %g and Tmaxval %g ",
260 specified_value - hs_anchor.
smolar, vmin, vmax));
274 this->component = &component;
277 double call(
double T) {
284 Residual resid(component, HEOS.
smolar());
289 double Tmin = Tmin_satL;
296 if (std::abs(specified_value - hs_anchor.
smolar) < std::abs(vmax)) {
297 T = std::max(0.99 * crit.
T, crit.
T - 0.1);
301 double sTmin = HEOS_copy->smolar();
303 double sTmax = HEOS_copy->smolar();
304 T = (Tmax - Tmin) / (sTmax - sTmin) * (HEOS.
smolar() - sTmin) + Tmin;
324 double rhoL_updated = rhoL - (SatL->p() - SatV->p()) / SatL->first_partial_deriv(
iP,
iDmolar,
iT);
327 if (rhoL_updated > rhoV) {
359 CoolPropDbl d2alphar_ddelta_dtauL = SatL->d2alphar_dDelta_dTau();
360 CoolPropDbl d2alphar_ddelta_dtauV = SatV->d2alphar_dDelta_dTau();
361 CoolPropDbl dalphar_ddeltaL = SatL->dalphar_dDelta();
362 CoolPropDbl dalphar_ddeltaV = SatV->dalphar_dDelta();
363 CoolPropDbl d2alphar_ddelta2L = SatL->d2alphar_dDelta2();
364 CoolPropDbl d2alphar_ddelta2V = SatV->d2alphar_dDelta2();
367 negativer[0] = -(deltaV * (1 + deltaV * dalphar_ddeltaV) - deltaL * (1 + deltaL * dalphar_ddeltaL));
369 negativer[1] = -(deltaV * dalphar_ddeltaV + alpharV + log(deltaV) - deltaL * dalphar_ddeltaL - alpharL - log(deltaL));
371 case saturation_PHSU_pure_options::IMPOSED_PL:
373 negativer[2] = -(pL / specified_value - 1);
375 case saturation_PHSU_pure_options::IMPOSED_PV:
377 negativer[2] = -(pV / specified_value - 1);
379 case saturation_PHSU_pure_options::IMPOSED_HL:
381 negativer[2] = -(hL - specified_value);
383 case saturation_PHSU_pure_options::IMPOSED_HV:
385 negativer[2] = -(hV - specified_value);
387 case saturation_PHSU_pure_options::IMPOSED_SL:
389 negativer[2] = -(sL - specified_value);
391 case saturation_PHSU_pure_options::IMPOSED_SV:
393 negativer[2] = -(sV - specified_value);
400 J[0][0] = pow(deltaV, 2) * d2alphar_ddelta_dtauV - pow(deltaL, 2) * d2alphar_ddelta_dtauL;
402 J[1][0] = deltaV * d2alphar_ddelta_dtauV + dalphar_dtauV - deltaL * d2alphar_ddelta_dtauL - dalphar_dtauL;
406 J[0][1] = -deltaL - 2 * pow(deltaL, 2) * dalphar_ddeltaL - pow(deltaL, 3) * d2alphar_ddelta2L;
408 J[1][1] = -pow(deltaL, 2) * d2alphar_ddelta2L - 2 * deltaL * dalphar_ddeltaL - 1;
411 J[0][1] = -1 - 2 * deltaL * dalphar_ddeltaL - pow(deltaL, 2) * d2alphar_ddelta2L;
413 J[1][1] = -1 / deltaL - 2 * dalphar_ddeltaL - deltaL * d2alphar_ddelta2L;
418 J[0][2] = deltaV + 2 * pow(deltaV, 2) * dalphar_ddeltaV + pow(deltaV, 3) * d2alphar_ddelta2V;
420 J[1][2] = 1 + 2 * deltaV * dalphar_ddeltaV + 1 + pow(deltaV, 2) * d2alphar_ddelta2V;
423 J[0][2] = 1 + 2 * deltaV * dalphar_ddeltaV + pow(deltaV, 2) * d2alphar_ddelta2V;
425 J[1][2] = deltaV * d2alphar_ddelta2V + 2 * dalphar_ddeltaV + 1 / deltaV;
430 case saturation_PHSU_pure_options::IMPOSED_PL:
432 J[2][0] = SatL->first_partial_deriv(
iP,
iTau,
iDelta) / specified_value;
435 J[2][1] = deltaL * SatL->first_partial_deriv(
iP,
iDelta,
iTau) / specified_value;
438 J[2][1] = SatL->first_partial_deriv(
iP,
iDelta,
iTau) / specified_value;
444 case saturation_PHSU_pure_options::IMPOSED_PV:
446 J[2][0] = SatV->first_partial_deriv(
iP,
iTau,
iDelta) / specified_value;
451 J[2][2] = deltaV * SatV->first_partial_deriv(
iP,
iDelta,
iTau) / specified_value;
454 J[2][2] = SatV->first_partial_deriv(
iP,
iDelta,
iTau) / specified_value;
458 case saturation_PHSU_pure_options::IMPOSED_HL:
470 case saturation_PHSU_pure_options::IMPOSED_HV:
482 case saturation_PHSU_pure_options::IMPOSED_SL:
494 case saturation_PHSU_pure_options::IMPOSED_SV:
515 double tau0 = tau, deltaL0 = deltaL, deltaV0 = deltaV;
516 for (
double omega_local = 1.0; omega_local > 0.1; omega_local /= 1.1) {
517 tau = tau0 + omega_local * options.
omega * v[0];
519 deltaL = exp(log(deltaL0) + omega_local * options.
omega * v[1]);
520 deltaV = exp(log(deltaV0) + omega_local * options.
omega * v[2]);
522 deltaL = deltaL0 + omega_local * options.
omega * v[1];
523 deltaV = deltaV0 + omega_local * options.
omega * v[2];
525 if (tau > 1 && deltaL > deltaV) {
537 error = sqrt(pow(negativer[0], 2) + pow(negativer[1], 2) + pow(negativer[2], 2));
553 throw SolutionError(
format(
"saturation_PHSU_pure solver did not converge after 50 iterations for %s=%Lg current error is %Lg",
554 info.c_str(), specified_value, error));
556 }
while (error > 1e-9);
569 std::vector<CoolPropDbl> r(2, _HUGE), v;
570 std::vector<std::vector<CoolPropDbl>> J(2, std::vector<CoolPropDbl>(2, _HUGE));
574 shared_ptr<HelmholtzEOSMixtureBackend> SatL = HEOS.
SatL, SatV = HEOS.
SatV;
577 CoolPropDbl deltaL = 0, deltaV = 0, tau = 0, error, p_error;
582 if (options.
imposed_rho == saturation_D_pure_options::IMPOSED_RHOL) {
588 }
else if (options.
imposed_rho == saturation_D_pure_options::IMPOSED_RHOV) {
620 CoolPropDbl d2alphar_ddelta_dtauL = SatL->d2alphar_dDelta_dTau();
621 CoolPropDbl d2alphar_ddelta_dtauV = SatV->d2alphar_dDelta_dTau();
624 CoolPropDbl dalphar_ddeltaL = SatL->dalphar_dDelta();
625 CoolPropDbl dalphar_ddeltaV = SatV->dalphar_dDelta();
628 r[0] = -(deltaV * (1 + deltaV * dalphar_ddeltaV) - deltaL * (1 + deltaL * dalphar_ddeltaL));
630 r[1] = -(deltaV * dalphar_ddeltaV + alpharV + log(deltaV) - deltaL * dalphar_ddeltaL - alpharL - log(deltaL));
633 J[0][0] = pow(deltaV, 2) * d2alphar_ddelta_dtauV - pow(deltaL, 2) * d2alphar_ddelta_dtauL;
635 J[1][0] = deltaV * d2alphar_ddelta_dtauV + dalphar_dtauV - deltaL * d2alphar_ddelta_dtauL - dalphar_dtauL;
637 if (options.
imposed_rho == saturation_D_pure_options::IMPOSED_RHOL) {
638 CoolPropDbl d2alphar_ddelta2V = SatV->d2alphar_dDelta2();
640 J[0][1] = deltaV + 2 * pow(deltaV, 2) * dalphar_ddeltaV + pow(deltaV, 3) * d2alphar_ddelta2V;
641 J[1][1] = pow(deltaV, 2) * d2alphar_ddelta2V + 2 * deltaV * dalphar_ddeltaV + 1;
643 J[0][1] = 1 + 2 * deltaV * dalphar_ddeltaV + pow(deltaV, 2) * d2alphar_ddelta2V;
644 J[1][1] = deltaV * d2alphar_ddelta2V + 2 * dalphar_ddeltaV + 1 / deltaV;
646 }
else if (options.
imposed_rho == saturation_D_pure_options::IMPOSED_RHOV) {
647 CoolPropDbl d2alphar_ddelta2L = SatL->d2alphar_dDelta2();
649 J[0][1] = -deltaL - 2 * pow(deltaL, 2) * dalphar_ddeltaL - pow(deltaL, 3) * d2alphar_ddelta2L;
650 J[1][1] = -pow(deltaL, 2) * d2alphar_ddelta2L - 2 * deltaL * dalphar_ddeltaL - 1;
652 J[0][1] = -1 - 2 * deltaL * dalphar_ddeltaL - pow(deltaL, 2) * d2alphar_ddelta2L;
653 J[1][1] = -deltaL * d2alphar_ddelta2L - 2 * dalphar_ddeltaL - 1 / deltaL;
661 double omega = options.
omega;
663 if (options.
imposed_rho == saturation_D_pure_options::IMPOSED_RHOL) {
665 deltaV = exp(log(deltaV)+omega*v[1]);
668 if (deltaV + omega*v[1] <= 0) {omega = 0.5*deltaV/v[1];}
669 if (tau + omega*v[0] <= 0) {omega = 0.5*tau/v[0];}
670 deltaV += omega*v[1];
676 deltaL = exp(log(deltaL)+omega*v[1]);
679 if (deltaL + omega*v[1] <= 0) {omega = 0.5*deltaL/v[1];}
680 if (tau + omega*v[0] <= 0) {omega = 0.5*tau/v[0];}
681 deltaL += omega*v[1];
691 p_error = (pL-pV)/pL;
693 error = sqrt(pow(r[0], 2) + pow(r[1], 2));
701 }
while (error > 1e-9);
703 if (std::abs(p_error) > p_error_limit) {
704 throw SolutionError(
format(
"saturation_D_pure solver abs error on p [%Lg] > limit [%Lg]", p_error, p_error_limit));
710 _options.
omega = 1.0;
720 options.
pL = _options.
pL;
721 options.
pV = _options.
pV;
724 options.
p = _options.
pL;
745 shared_ptr<HelmholtzEOSMixtureBackend> SatL = HEOS.
SatL, SatV = HEOS.
SatV;
747 CoolPropDbl rhoL = _HUGE, rhoV = _HUGE, JL, JV, KL, KV, dJL, dJV, dKL, dKV;
748 CoolPropDbl DELTA, deltaL = 0, deltaV = 0, error, PL, PV, stepL, stepV;
761 rhoL = HEOS.
get_components()[0].ancillaries.rhoL.evaluate(T - 0.1);
762 rhoV = HEOS.
get_components()[0].ancillaries.rhoV.evaluate(T - 0.1);
811 CoolPropDbl dalphar_ddeltaL = SatL->dalphar_dDelta();
812 CoolPropDbl dalphar_ddeltaV = SatV->dalphar_dDelta();
813 CoolPropDbl d2alphar_ddelta2L = SatL->d2alphar_dDelta2();
814 CoolPropDbl d2alphar_ddelta2V = SatV->d2alphar_dDelta2();
816 JL = deltaL * (1 + deltaL * dalphar_ddeltaL);
817 JV = deltaV * (1 + deltaV * dalphar_ddeltaV);
818 KL = deltaL * dalphar_ddeltaL + alpharL + log(deltaL);
819 KV = deltaV * dalphar_ddeltaV + alpharV + log(deltaV);
821 PL = R_u * reduce.
rhomolar * T * JL;
822 PV = R_u * reduce.
rhomolar * T * JV;
825 dJL = 1 + 2 * deltaL * dalphar_ddeltaL + deltaL * deltaL * d2alphar_ddelta2L;
826 dJV = 1 + 2 * deltaV * dalphar_ddeltaV + deltaV * deltaV * d2alphar_ddelta2V;
827 dKL = 2 * dalphar_ddeltaL + deltaL * d2alphar_ddelta2L + 1 / deltaL;
828 dKV = 2 * dalphar_ddeltaV + deltaV * d2alphar_ddelta2V + 1 / deltaV;
830 DELTA = dJV * dKL - dJL * dKV;
832 error = sqrt((KL - KV) * (KL - KV) + (JL - JV) * (JL - JV));
835 stepL = options.
omega / DELTA * ((KV - KL) * dJV - (JV - JL) * dKV);
836 stepV = options.
omega / DELTA * ((KV - KL) * dJL - (JV - JL) * dKL);
841 for (
double omega_local = 1.0; omega_local > 0.1; omega_local /= 1.1) {
842 deltaL = deltaL0 + omega_local * stepL;
843 deltaV = deltaV0 + omega_local * stepV;
845 if (deltaL > 1 && deltaV < 1 && deltaV > 0) {
856 }
while (error > 1e-10 && std::abs(stepL) > 10 *
DBL_EPSILON * std::abs(stepL) && std::abs(stepV) > 10 *
DBL_EPSILON * std::abs(stepV));
860 if (std::abs(p_error) > p_error_limit) {
865 throw SolutionError(
format(
"saturation_T_pure_Akasaka solver abs error on p [%g] > limit [%g]", std::abs(p_error), p_error_limit));
886 shared_ptr<HelmholtzEOSMixtureBackend> SatL = HEOS.
SatL, SatV = HEOS.
SatV;
888 CoolPropDbl rhoL = _HUGE, rhoV = _HUGE, error = 999, DeltavL, DeltavV, pL, pV, p, last_error;
889 int iter = 0, small_step_count = 0,
890 backwards_step_count = 0;
902 rhoL = HEOS.
get_components()[0].ancillaries.rhoL.evaluate(T - 0.1);
903 rhoV = HEOS.
get_components()[0].ancillaries.rhoV.evaluate(T - 0.1);
914 if ((rhoL < crit.rhomolar * 0.8 || rhoL > tripleL.
rhomolar * 1.2 || rhoV > crit.
rhomolar * 1.2 || rhoV < tripleV.
rhomolar * 0.8)
919 SatL->update_DmolarT_direct(rhoL, T);
922 rhoV = SatL->p() / (SatL->gas_constant() * T);
924 rhoV = p / (SatL->gas_constant() * T);
927 SatV->update_DmolarT_direct(rhoV, T);
929 SatL->update_DmolarT_direct(rhoL, T);
930 SatV->update_DmolarT_direct(rhoV, T);
933 std::cout <<
format(
"[Maxwell] ancillaries T: %0.16Lg rhoL: %0.16Lg rhoV: %0.16Lg pL: %g pV: %g\n", T, rhoL, rhoV, SatL->p(),
940 if (std::abs((SatL->p() - p) / p) > 0.1) {
941 for (
int iii = 0; iii < 6; ++iii) {
946 CoolPropDbl deltarhoLHalley = -(2 * f * dpdrho) / (2 *
POW2(dpdrho) - f * d2pdrho2);
947 rhoL += deltarhoLHalley;
948 if (std::abs(deltarhoLHalley / rhoL) <
DBL_EPSILON) {
951 SatL->update_DmolarT_direct(rhoL, T);
955 SatL->update_DmolarT_direct(rhoL, T);
956 SatV->update_DmolarT_direct(rhoV, T);
961 if (std::abs((SatV->p() - p) / p) > 0.1) {
963 rhoV = SatV->solver_rho_Tp(T, p, rhoV);
978 SatL->update_DmolarT_direct(rhoL, T);
979 SatV->update_DmolarT_direct(rhoV, T);
981 std::cout <<
format(
"[Maxwell] starting T: %0.16Lg rhoL: %Lg rhoV: %Lg pL: %Lg pV: %g\n", T, rhoL, rhoV, SatL->p(), SatV->p());
986 CoolPropDbl vL = 1 / SatL->rhomolar(), vV = 1 / SatV->rhomolar();
994 CoolPropDbl helmholtzL = (SatL->calc_alpha0() + SatL->calc_alphar()) * RT;
995 CoolPropDbl helmholtzV = (SatV->calc_alpha0() + SatV->calc_alphar()) * RT;
998 CoolPropDbl pM = (helmholtzL - helmholtzV) / (vV - vL);
1002 CoolPropDbl B = alphaL * (pL - pV - alphaV * (vL - vV));
1006 CoolPropDbl sqrt_arg = std::abs(B * B / (4 * A * A) - C / A);
1010 if (std::abs(sqrt_arg) > 1e-10) {
1011 DeltavL = -0.5 * B / A +
sign((alphaL - alphaV) / alphaV) * sqrt(sqrt_arg);
1015 DeltavL = -0.5 * B / A +
sign((alphaL - alphaV) / alphaV) * sqrt(sqrt_arg * powerfactor) / sqrt(powerfactor);
1017 DeltavV = (pL - pV + alphaL * DeltavL) / alphaV;
1020 rhoL = 1 / (vL + DeltavL);
1021 rhoV = 1 / (vV + DeltavV);
1022 if (B * B / (4 * A * A) - C / A < -10 *
DBL_EPSILON) {
1028 SatL->update_DmolarT_direct(rhoL, T);
1029 SatV->update_DmolarT_direct(rhoV, T);
1032 error = std::abs((SatL->p() - SatV->p()) / SatL->p());
1035 std::cout <<
format(
"[Maxwell] rhoL: %0.16Lg rhoV: %0.16Lg error: %Lg dvL/vL: %Lg dvV/vV: %Lg pL: %Lg pV: %Lg\n", rhoL, rhoV, error,
1036 DeltavL / vL, DeltavV / vV, pL, pV);
1041 if (std::abs(DeltavL * rhoL) < 1e-13 || std::abs(DeltavV * rhoV) < 1e-13) {
1046 if (std::abs(error) > std::abs(last_error)) {
1047 backwards_step_count++;
1053 throw SolutionError(
format(
"Maxwell solver did not converge after 30 iterations; rhoL: %0.16Lg rhoV: %0.16Lg error: %Lg dvL/vL: %Lg "
1054 "dvV/vV: %Lg pL: %Lg pV: %Lg\n",
1055 rhoL, rhoV, error, DeltavL / vL, DeltavV / vV, pL, pV));
1057 }
while ((SatL->p() < 0) || (error > 1e-10 && small_step_count < 4 && backwards_step_count < 6));
1059 std::cout <<
format(
"[Maxwell] pL: %g pV: %g\n", SatL->p(), SatV->p());
1064 std::vector<CoolPropDbl>& x, std::vector<CoolPropDbl>& y) {
1065 for (
unsigned int i = 0; i < K.size(); i++) {
1066 double denominator = (1 - beta + beta * K[i]);
1067 x[i] = z[i] / denominator;
1068 y[i] = K[i] * z[i] / denominator;
1073 const std::vector<CoolPropDbl>& z, std::vector<CoolPropDbl>& K,
mixture_VLE_IO& options) {
1076 std::size_t N = z.size();
1077 std::vector<CoolPropDbl> ln_phi_liq, ln_phi_vap;
1078 ln_phi_liq.resize(N);
1079 ln_phi_vap.resize(N);
1081 std::vector<CoolPropDbl>&x = HEOS.
SatL->get_mole_fractions_ref(), &y = HEOS.
SatV->get_mole_fractions_ref();
1090 HEOS.
SatL->set_mole_fractions(x);
1091 HEOS.
SatV->set_mole_fractions(y);
1092 HEOS.
SatL->calc_reducing_state();
1093 HEOS.
SatV->calc_reducing_state();
1099 double summer_c = 0, v_SRK = 1 / rhomolar_liq;
1100 const std::vector<CoolPropFluid>& components = HEOS.
get_components();
1101 for (std::size_t i = 0; i < components.size(); ++i) {
1108 summer_c += z[i] * (0.40768 * R * Tc / pc * (0.29441 - pc / (rhomolarc * R * Tc)));
1110 rhomolar_liq = 1 / (v_SRK - summer_c);
1111 HEOS.
SatL->update_TP_guessrho(T, p, rhomolar_liq);
1112 HEOS.
SatV->update_TP_guessrho(T, p, rhomolar_vap);
1115 HEOS.
SatL->update_TP_guessrho(T, p, HEOS.
SatL->rhomolar());
1116 HEOS.
SatV->update_TP_guessrho(T, p, HEOS.
SatV->rhomolar());
1122 for (std::size_t i = 0; i < N; ++i) {
1136 K[i] = exp(ln_phi_liq[i] - ln_phi_vap[i]);
1138 f += z[i] * (K[i] - 1) / (1 - beta + beta * K[i]);
1140 double dfdK = K[i] * z[i] / pow(1 - beta + beta * K[i], (
int)2);
1141 df += dfdK * (deriv_liq - deriv_vap);
1144 if (std::abs(df) <= 1e-14) {
1145 if (std::abs(f) <= 1e-12)
1149 throw ValueError(
format(
"df very small (df = %g) in successive_substitution but f is not converged (f = %g > 1e-12).", df, f));
1159 if (std::abs(change) > 0.05 * p) {
1162 p += omega * change;
1168 HEOS.
SatL->set_mole_fractions(x);
1169 HEOS.
SatV->set_mole_fractions(y);
1173 throw ValueError(
format(
"saturation_p was unable to reach a solution within 50 iterations"));
1175 }
while (std::abs(f) > 1e-12 && iter < options.
Nstep_max);
1177 HEOS.
SatL->update_TP_guessrho(T, p, HEOS.
SatL->rhomolar());
1178 HEOS.
SatV->update_TP_guessrho(T, p, HEOS.
SatV->rhomolar());
1180 options.
p = HEOS.
SatL->p();
1181 options.
T = HEOS.
SatL->T();
1195 J.resize(
N + 1,
N + 1);
1213 std::vector<CoolPropDbl> x0 = x;
1214 Eigen::VectorXd r0 = r;
1215 Eigen::MatrixXd J0 = J;
1221 double dT = 1e-3, T1 = T + dT, T2 = T - dT;
1223 this->rhomolar_liq = rhomolar_liq0;
1224 this->rhomolar_vap = rhomolar_vap0;
1226 Eigen::VectorXd r1 = r;
1228 this->rhomolar_liq = rhomolar_liq0;
1229 this->rhomolar_vap = rhomolar_vap0;
1231 Eigen::VectorXd r2 = r;
1233 Eigen::VectorXd diffn = (r1 - r2) / (2 * dT);
1234 std::cout <<
format(
"For T\n");
1242 this->rhomolar_liq = rhomolar_liq0 + drho;
1243 this->rhomolar_vap = rhomolar_vap0;
1245 Eigen::VectorXd rr1 = r;
1247 this->rhomolar_liq = rhomolar_liq0 - drho;
1248 this->rhomolar_vap = rhomolar_vap0;
1250 Eigen::VectorXd rr2 = r;
1252 Eigen::VectorXd diffn = (rr1 - rr2) / (2 * drho);
1253 std::cout <<
format(
"For rho\n");
1257 for (std::size_t i = 0; i < x.size() - 1; ++i) {
1262 this->x[x.size() - 1] -= dx;
1264 this->rhomolar_liq = rhomolar_liq0;
1265 this->rhomolar_vap = rhomolar_vap0;
1267 Eigen::VectorXd r1 = this->r;
1271 this->x[x.size() - 1] += dx;
1273 this->rhomolar_liq = rhomolar_liq0;
1274 this->rhomolar_vap = rhomolar_vap0;
1276 Eigen::VectorXd r2 = this->r;
1278 Eigen::VectorXd diffn = (r1 - r2) / (2 * dx);
1279 std::cout <<
format(
"For x%d N %d\n", i, N);
1290 std::cout <<
" NRsat::call: p " << IO.
p <<
" T " << IO.
T <<
" dl " << IO.
rhomolar_liq <<
" dv " << IO.
rhomolar_vap << std::endl;
1326 Eigen::VectorXd v = J.colPivHouseholderQr().solve(-r);
1329 for (
unsigned int i = 0; i < N - 1; ++i) {
1330 err_rel[i] = v[i] / y[i];
1333 y[N - 1] = 1 - std::accumulate(y.begin(), y.end() - 1, 0.0);
1335 for (
unsigned int i = 0; i < N - 1; ++i) {
1336 err_rel[i] = v[i] / x[i];
1339 x[N - 1] = 1 - std::accumulate(x.begin(), x.end() - 1, 0.0);
1343 err_rel[N - 1] = v[N - 1] / T;
1346 err_rel[N - 1] = v[N - 1] / p;
1349 err_rel[N - 1] = v[N - 1] / T;
1350 rhomolar_liq += v[N];
1351 err_rel[N] = v[N] / rhomolar_liq;
1353 throw ValueError(
"invalid imposed_variable");
1359 min_rel_change = err_rel.cwiseAbs().minCoeff();
1365 }
while (this->error_rms > 1e-7 && min_rel_change > 1000 *
DBL_EPSILON && iter < IO.
Nstep_max);
1374 const std::vector<CoolPropFluid>& fluidsL = HEOS.
SatL->get_components();
1375 const std::vector<CoolPropFluid>& fluidsV = HEOS.
SatV->get_components();
1376 if (!fluidsL.empty() && !fluidsV.empty()) {
1398 rSatV.set_mole_fractions(y);
1407 rSatV.update_TP_guessrho(T, p, rhomolar_vap);
1408 rhomolar_vap = rSatV.rhomolar();
1410 throw ValueError(
"imposed variable not set for NR VLE");
1416 p = 0.5 * (p_liq + p_vap);
1426 for (std::size_t i = 0; i < N; ++i) {
1430 r(i) = ln_f_liq - ln_f_vap;
1432 for (std::size_t j = 0; j < N - 1; ++j) {
1446 r(N) = p_liq - p_vap;
1447 for (std::size_t j = 0; j < N - 1; ++j) {
1457 for (std::size_t i = 0; i < N; ++i) {
1461 r(i) = ln_f_liq - ln_f_vap;
1463 for (std::size_t j = 0; j < N - 1; ++j) {
1477 for (std::size_t i = 0; i < N; ++i) {
1481 r(i) = ln_f_liq - ln_f_vap;
1483 for (std::size_t j = 0; j < N - 1; ++j) {
1497 error_rms = r.norm();
1502 for (std::size_t i = 0; i < N; ++i) {
1510 dTsat_dPsat = -dQ_dPsat / dQ_dTsat;
1511 dPsat_dTsat = -dQ_dTsat / dQ_dPsat;
1537 r.resize(2 * N - 1);
1538 J.resize(2 * N - 1, 2 * N - 1);
1539 err_rel.resize(2 * N - 1);
1555 Eigen::VectorXd v = J.colPivHouseholderQr().solve(-r);
1557 for (
unsigned int i = 0; i < N - 1; ++i) {
1558 err_rel[i] = v[i] / x[i];
1560 err_rel[i + (N - 1)] = v[i + (N - 1)] / y[i];
1561 y[i] += v[i + (N - 1)];
1563 x[N - 1] = 1 - std::accumulate(x.begin(), x.end() - 1, 0.0);
1564 y[N - 1] = 1 - std::accumulate(y.begin(), y.end() - 1, 0.0);
1568 err_rel[2 * N - 2] = v[2 * N - 2] / T;
1571 err_rel[2 * N - 2] = v[2 * N - 2] / p;
1573 throw ValueError(
"invalid imposed_variable");
1577 min_rel_change = err_rel.cwiseAbs().minCoeff();
1583 }
while (this->error_rms > 1e-9 && min_rel_change > 1000 *
DBL_EPSILON && iter < IO.
Nstep_max);
1606 rSatV.set_mole_fractions(y);
1613 rSatV.update_TP_guessrho(T, p, rhomolar_vap);
1614 rhomolar_vap = rSatV.rhomolar();
1619 p = 0.5 * (p_liq + p_vap);
1628 for (std::size_t i = 0; i < N; ++i) {
1632 r[i] = ln_f_liq - ln_f_vap;
1636 r[i + N] = (z[i] - x[i]) / (y[i] - x[i]) - beta;
1641 for (std::size_t i = 0; i < N; ++i) {
1642 for (std::size_t j = 0; j < N - 1; ++j) {
1659 for (std::size_t i = 0; i < N - 1; ++i) {
1660 std::size_t k = i + N;
1661 J(k, i) = (z[i] - y[i]) / pow(y[i] - x[i], 2);
1662 J(k, i + (N - 1)) = -(z[i] - x[i]) / pow(y[i] - x[i], 2);
1665 error_rms = r.norm();
1671 const std::vector<double>&z, &lnK;
1687 lnK.resize(z.size());
1689 double g0 = 0, g1 = 0, beta = -1;
1691 for (
int i = 0; i < static_cast<int>(z.size()); ++i) {
1693 if (m_T < 0 && m_p < 0) {
1695 lnK[i] = SaturationSolvers::Wilson_lnK_factor(HEOS, HEOS.T(), HEOS.p(), i);
1698 lnK[i] = SaturationSolvers::Wilson_lnK_factor(HEOS, m_T, m_p, i);
1701 g0 += z[i] * (K[i] - 1);
1702 g1 += z[i] * (1 - 1 / K[i]);
1711 }
else if (g1 > 0) {
1723 std::cout <<
format(
"1) T: %g p: %g beta: %g\n", HEOS.T(), HEOS.p(), beta);
1731 HEOS.SatL->set_mole_fractions(x);
1732 HEOS.SatL->calc_reducing_state();
1733 HEOS.SatV->set_mole_fractions(y);
1734 HEOS.SatV->calc_reducing_state();
1737 std::cout <<
format(
"2) SS1: i beta K x y rho' rho''\n");
1739 for (
int step_count = 0; step_count < num_steps; ++step_count) {
1741 HEOS.SatL->set_mole_fractions(x);
1742 HEOS.SatV->set_mole_fractions(y);
1743 HEOS.SatL->calc_reducing_state();
1744 HEOS.SatV->calc_reducing_state();
1746 this->rho_TP_global();
1749 double g0 = 0, g1 = 0;
1750 for (std::size_t i = 0; i < z.size(); ++i) {
1751 lnK[i] = log(HEOS.SatL->fugacity_coefficient(i) / HEOS.SatV->fugacity_coefficient(i));
1753 g0 += z[i] * (K[i] - 1);
1754 g1 += z[i] * (1 - 1 / K[i]);
1759 }
else if (g1 > 0) {
1772 vec_to_string(y,
"%0.6f").c_str(), rhomolar_liq, rhomolar_vap);
1777 std::vector<double> tpdL, tpdH;
1780 double the_T = (m_T > 0 && m_p > 0) ? m_T : HEOS.T();
1781 double the_p = (m_T > 0 && m_p > 0) ? m_p : HEOS.p();
1787 HEOS.SatL->set_mole_fractions(z);
1788 HEOS.SatV->set_mole_fractions(z);
1789 HEOS.SatL->calc_reducing_state();
1790 HEOS.SatV->calc_reducing_state();
1793 double rhoL = HEOS.SatL->solver_rho_Tp_global(the_T, the_p, 0.9 / HEOS.SatL->SRK_covolume());
1794 double rhoV = HEOS.SatV->solver_rho_Tp_global(the_T, the_p, 0.9 / HEOS.SatV->SRK_covolume());
1795 HEOS.SatL->update_DmolarT_direct(rhoL, the_T);
1796 HEOS.SatV->update_DmolarT_direct(rhoV, the_T);
1800 this->tpd_liq = HEOS.SatL->tangent_plane_distance(the_T, the_p, x, rhomolar_liq);
1801 this->tpd_vap = HEOS.SatV->tangent_plane_distance(the_T, the_p, y, rhomolar_vap);
1803 this->DELTAG_nRT = (1 - beta) * tpd_liq + beta * (tpd_vap);
1805 std::cout <<
format(
"3) tpd': %g tpd'': %g DELTAG/nRT: %g\n", tpd_liq, tpd_vap, DELTAG_nRT);
1809 if (this->tpd_liq < -DBL_EPSILON || this->tpd_vap < -DBL_EPSILON || this->DELTAG_nRT < -
DBL_EPSILON) {
1811 std::cout <<
format(
"3) PHASE SPLIT beta in (eps,1-eps) \n");
1821 CoolPropDbl rho_bulk = HEOS.solver_rho_Tp_global(the_T, the_p, 0.9 / HEOS.SRK_covolume());
1822 HEOS.update_DmolarT_direct(rho_bulk, the_T);
1825 std::vector<double> fugacity_coefficient0(z.size()), fugacity0(z.size());
1826 for (std::size_t i = 0; i < z.size(); ++i) {
1827 fugacity_coefficient0[i] = HEOS.fugacity_coefficient(i);
1828 fugacity0[i] = HEOS.fugacity(i);
1832 xL.resize(z.size());
1833 xH.resize(z.size());
1834 for (std::size_t i = 0; i < z.size(); ++i) {
1835 xL[i] = z[i] * K0[i];
1836 xH[i] = z[i] / K0[i];
1843 std::cout <<
format(
"3) SS2: i x' x'' rho' rho'' tpd' tpd''\n");
1849 double diffbulkL = 0, diffbulkH = 0;
1850 for (
int step_count = 0; step_count < 100; ++step_count) {
1853 HEOS.SatL->set_mole_fractions(xH);
1854 HEOS.SatV->set_mole_fractions(xL);
1855 HEOS.SatL->calc_reducing_state();
1856 HEOS.SatV->calc_reducing_state();
1861 double tpd_L = 0, tpd_H = 0;
1862 for (std::size_t i = 0; i < xL.size(); ++i) {
1866 tpdL.push_back(tpd_L);
1867 tpdH.push_back(tpd_H);
1870 diffbulkL = 0, diffbulkH = 0;
1871 for (std::size_t i = 0; i < z.size(); ++i) {
1872 xL[i] = z[i] * fugacity_coefficient0[i] / HEOS.SatV->fugacity_coefficient(i);
1873 diffbulkL += std::abs(xL[i] - z[i]);
1874 xH[i] = z[i] * fugacity_coefficient0[i] / HEOS.SatL->fugacity_coefficient(i);
1875 diffbulkH += std::abs(xH[i] - z[i]);
1881 rhomolar_liq, rhomolar_vap, tpd_L, tpd_H);
1885 if (diffbulkL < 1e-2 || diffbulkH < 1e-2) {
1891 if (tpd_L < -1e-12 || tpd_H < -1e-12) {
1896 if (diffbulkH > 0.25 || diffbulkL > 0.25) {
1905 double the_T = (m_T > 0 && m_p > 0) ? m_T : HEOS.T();
1906 double the_p = (m_T > 0 && m_p > 0) ? m_p : HEOS.p();
1909 double rhoL = HEOS.SatL->solver_rho_Tp_global(the_T, the_p, 0.9 / HEOS.SatL->SRK_covolume());
1910 double rhoV = HEOS.SatV->solver_rho_Tp_global(the_T, the_p, 0.9 / HEOS.SatV->SRK_covolume());
1911 HEOS.SatL->update_DmolarT_direct(rhoL, the_T);
1912 HEOS.SatV->update_DmolarT_direct(rhoV, the_T);
1914 rhomolar_liq = HEOS.SatL->rhomolar();
1915 rhomolar_vap = HEOS.SatV->rhomolar();
1921 if (m_T > 0 && m_p > 0) {
1922 HEOS.SatL->update_TP_guessrho(m_T, m_p, rhomolar_liq);
1923 HEOS.SatV->update_TP_guessrho(m_T, m_p, rhomolar_vap);
1925 HEOS.SatL->update_TP_guessrho(HEOS.T(), HEOS.p(), rhomolar_liq);
1926 HEOS.SatV->update_TP_guessrho(HEOS.T(), HEOS.p(), rhomolar_vap);
1928 rhomolar_liq = HEOS.SatL->rhomolar();
1929 rhomolar_vap = HEOS.SatV->rhomolar();
1935 if (m_T > 0 && m_p > 0) {
1936 rhomolar_liq = HEOS.SatL->solver_rho_Tp_SRK(m_T, m_p,
iphase_liquid);
1937 rhomolar_vap = HEOS.SatV->solver_rho_Tp_SRK(m_T, m_p,
iphase_gas);
1939 rhomolar_liq = HEOS.SatL->solver_rho_Tp_SRK(HEOS.T(), HEOS.p(),
iphase_liquid);
1940 rhomolar_vap = HEOS.SatV->solver_rho_Tp_SRK(HEOS.T(), HEOS.p(),
iphase_gas);
1944 if (HEOS.backend_name().find(
"Helmholtz") == 0) {
1947 double summer_c = 0, v_SRK = 1 / rhomolar_liq;
1948 for (std::size_t i = 0; i < z.size(); ++i) {
1953 summer_c += z[i] * (0.40768 * R * Tc / pc * (0.29441 - pc / (rhomolarc * R * Tc)));
1955 rhomolar_liq = 1 / (v_SRK - summer_c);
1960 const std::size_t N = IO.x.size();
1962 double min_rel_change;
1974 Eigen::VectorXd v = J.colPivHouseholderQr().solve(-r);
1976 for (
unsigned int i = 0; i < N - 1; ++i) {
1977 err_rel[i] = v[i] / IO.x[i];
1979 err_rel[i + (N - 1)] = v[i + (N - 1)] / IO.y[i];
1980 IO.y[i] += v[i + (N - 1)];
1982 IO.x[N - 1] = 1 - std::accumulate(IO.x.begin(), IO.x.end() - 1, 0.0);
1983 IO.y[N - 1] = 1 - std::accumulate(IO.y.begin(), IO.y.end() - 1, 0.0);
1987 min_rel_change = err_rel.cwiseAbs().minCoeff();
1990 if (iter == IO.Nstep_max) {
1991 throw ValueError(
format(
"PTflash_twophase::call reached max number of iterations [%d]", IO.Nstep_max));
1993 }
while (this->error_rms > 1e-9 && min_rel_change > 1000 *
DBL_EPSILON && iter < IO.Nstep_max);
1996 const std::size_t N = IO.x.size();
1998 r.resize(2 * N - 2);
1999 J.resize(2 * N - 2, 2 * N - 2);
2000 err_rel.resize(2 * N - 2);
2002 HEOS.SatL->set_mole_fractions(IO.x);
2003 HEOS.SatL->update_TP_guessrho(IO.T, IO.p, IO.rhomolar_liq);
2005 HEOS.SatV->set_mole_fractions(IO.y);
2006 HEOS.SatV->update_TP_guessrho(IO.T, IO.p, IO.rhomolar_vap);
2012 for (std::size_t k = 0; k < N; ++k) {
2013 r(k) = log(HEOS.SatL->fugacity(k) / HEOS.SatV->fugacity(k));
2014 for (std::size_t j = 0; j < N - 1; ++j) {
2025 for (std::size_t i = 0; i < N - 2; ++i) {
2026 std::size_t k = i + N;
2027 r(k) = (IO.z[i] - IO.x[i]) / (IO.y[i] - IO.x[i]) - (IO.z[N - 1] - IO.x[N - 1]) / (IO.y[N - 1] - IO.x[N - 1]);
2028 for (std::size_t j = 0; j < N - 2; ++j) {
2029 J(k, j) = (IO.z[j] - IO.x[j]) /
POW2(IO.y[j] - IO.x[j]);
2030 J(k, j + N - 1) = -(IO.z[j] - IO.x[j]) /
POW2(IO.y[j] - IO.x[j]);
2032 std::size_t j = N - 2;
2033 J(k, j) = -(IO.z[j] - IO.x[j]) /
POW2(IO.y[j] - IO.x[j]);
2034 J(k, j + N - 1) = +(IO.z[j] - IO.x[j]) /
POW2(IO.y[j] - IO.x[j]);
2036 this->error_rms = r.norm();
2040 #if defined(ENABLE_CATCH)
2041 # include <catch2/catch_all.hpp>
2043 TEST_CASE(
"Check the PT flash calculation for two-phase inputs",
"[PTflash_twophase]") {
2045 AS->set_mole_fractions(std::vector<double>(2, 0.5));
2051 o.
x = AS->mole_fractions_liquid();
2052 o.
y = AS->mole_fractions_vapor();
2053 o.
z = AS->get_mole_fractions();
2060 solver.build_arrays();
2061 double err = solver.r.norm();
2062 REQUIRE(std::abs(err) < 1e-10);
2065 std::vector<double> x0 = o.
x;
2067 o.
x[1] = 1 - o.
x[0];
2070 double diffx0 = o.
x[0] - x0[0];
2071 REQUIRE(std::abs(diffx0) < 1e-10);