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);
562 SatL->unspecify_phase();
563 SatV->unspecify_phase();
566 negativer[0] = -(deltaV * (1 + deltaV * SatV->dalphar_dDelta()) - deltaL * (1 + deltaL * SatL->dalphar_dDelta()));
567 negativer[1] = -(deltaV * SatV->dalphar_dDelta() + SatV->alphar() + log(deltaV) - deltaL * SatL->dalphar_dDelta() - SatL->alphar() - log(deltaL));
569 case saturation_PHSU_pure_options::IMPOSED_PL:
571 negativer[2] = -(SatL->p() / specified_value - 1);
573 case saturation_PHSU_pure_options::IMPOSED_PV:
575 negativer[2] = -(SatV->p() / specified_value - 1);
577 case saturation_PHSU_pure_options::IMPOSED_HL:
579 negativer[2] = -(SatL->hmolar() - specified_value);
581 case saturation_PHSU_pure_options::IMPOSED_HV:
583 negativer[2] = -(SatV->hmolar() - specified_value);
585 case saturation_PHSU_pure_options::IMPOSED_SL:
587 negativer[2] = -(SatL->smolar() - specified_value);
589 case saturation_PHSU_pure_options::IMPOSED_SV:
591 negativer[2] = -(SatV->smolar() - specified_value);
596 error = sqrt(pow(negativer[0], 2) + pow(negativer[1], 2) + pow(negativer[2], 2));
601 throw SolutionError(
format(
"saturation_PHSU_pure solver was good, but went bad. Current error is %Lg", error));
615 std::vector<CoolPropDbl> r(2, _HUGE), v;
616 std::vector<std::vector<CoolPropDbl>> J(2, std::vector<CoolPropDbl>(2, _HUGE));
620 shared_ptr<HelmholtzEOSMixtureBackend> SatL = HEOS.
SatL, SatV = HEOS.
SatV;
623 CoolPropDbl deltaL = 0, deltaV = 0, tau = 0, error, p_error;
628 if (options.
imposed_rho == saturation_D_pure_options::IMPOSED_RHOL) {
634 }
else if (options.
imposed_rho == saturation_D_pure_options::IMPOSED_RHOV) {
666 CoolPropDbl d2alphar_ddelta_dtauL = SatL->d2alphar_dDelta_dTau();
667 CoolPropDbl d2alphar_ddelta_dtauV = SatV->d2alphar_dDelta_dTau();
670 CoolPropDbl dalphar_ddeltaL = SatL->dalphar_dDelta();
671 CoolPropDbl dalphar_ddeltaV = SatV->dalphar_dDelta();
674 r[0] = -(deltaV * (1 + deltaV * dalphar_ddeltaV) - deltaL * (1 + deltaL * dalphar_ddeltaL));
676 r[1] = -(deltaV * dalphar_ddeltaV + alpharV + log(deltaV) - deltaL * dalphar_ddeltaL - alpharL - log(deltaL));
679 J[0][0] = pow(deltaV, 2) * d2alphar_ddelta_dtauV - pow(deltaL, 2) * d2alphar_ddelta_dtauL;
681 J[1][0] = deltaV * d2alphar_ddelta_dtauV + dalphar_dtauV - deltaL * d2alphar_ddelta_dtauL - dalphar_dtauL;
683 if (options.
imposed_rho == saturation_D_pure_options::IMPOSED_RHOL) {
684 CoolPropDbl d2alphar_ddelta2V = SatV->d2alphar_dDelta2();
686 J[0][1] = deltaV + 2 * pow(deltaV, 2) * dalphar_ddeltaV + pow(deltaV, 3) * d2alphar_ddelta2V;
687 J[1][1] = pow(deltaV, 2) * d2alphar_ddelta2V + 2 * deltaV * dalphar_ddeltaV + 1;
689 J[0][1] = 1 + 2 * deltaV * dalphar_ddeltaV + pow(deltaV, 2) * d2alphar_ddelta2V;
690 J[1][1] = deltaV * d2alphar_ddelta2V + 2 * dalphar_ddeltaV + 1 / deltaV;
692 }
else if (options.
imposed_rho == saturation_D_pure_options::IMPOSED_RHOV) {
693 CoolPropDbl d2alphar_ddelta2L = SatL->d2alphar_dDelta2();
695 J[0][1] = -deltaL - 2 * pow(deltaL, 2) * dalphar_ddeltaL - pow(deltaL, 3) * d2alphar_ddelta2L;
696 J[1][1] = -pow(deltaL, 2) * d2alphar_ddelta2L - 2 * deltaL * dalphar_ddeltaL - 1;
698 J[0][1] = -1 - 2 * deltaL * dalphar_ddeltaL - pow(deltaL, 2) * d2alphar_ddelta2L;
699 J[1][1] = -deltaL * d2alphar_ddelta2L - 2 * dalphar_ddeltaL - 1 / deltaL;
707 double omega = options.
omega;
709 if (options.
imposed_rho == saturation_D_pure_options::IMPOSED_RHOL) {
711 deltaV = exp(log(deltaV)+omega*v[1]);
714 if (deltaV + omega*v[1] <= 0) {omega = 0.5*deltaV/v[1];}
715 if (tau + omega*v[0] <= 0) {omega = 0.5*tau/v[0];}
716 deltaV += omega*v[1];
722 deltaL = exp(log(deltaL)+omega*v[1]);
725 if (deltaL + omega*v[1] <= 0) {omega = 0.5*deltaL/v[1];}
726 if (tau + omega*v[0] <= 0) {omega = 0.5*tau/v[0];}
727 deltaL += omega*v[1];
737 p_error = (pL-pV)/pL;
739 error = sqrt(pow(r[0], 2) + pow(r[1], 2));
747 }
while (error > 1e-9);
749 if (std::abs(p_error) > p_error_limit) {
750 throw SolutionError(
format(
"saturation_D_pure solver abs error on p [%Lg] > limit [%Lg]", p_error, p_error_limit));
756 _options.
omega = 1.0;
766 options.
pL = _options.
pL;
767 options.
pV = _options.
pV;
770 options.
p = _options.
pL;
791 shared_ptr<HelmholtzEOSMixtureBackend> SatL = HEOS.
SatL, SatV = HEOS.
SatV;
793 CoolPropDbl rhoL = _HUGE, rhoV = _HUGE, JL, JV, KL, KV, dJL, dJV, dKL, dKV;
794 CoolPropDbl DELTA, deltaL = 0, deltaV = 0, error, PL, PV, stepL, stepV;
807 rhoL = HEOS.
get_components()[0].ancillaries.rhoL.evaluate(T - 0.1);
808 rhoV = HEOS.
get_components()[0].ancillaries.rhoV.evaluate(T - 0.1);
857 CoolPropDbl dalphar_ddeltaL = SatL->dalphar_dDelta();
858 CoolPropDbl dalphar_ddeltaV = SatV->dalphar_dDelta();
859 CoolPropDbl d2alphar_ddelta2L = SatL->d2alphar_dDelta2();
860 CoolPropDbl d2alphar_ddelta2V = SatV->d2alphar_dDelta2();
862 JL = deltaL * (1 + deltaL * dalphar_ddeltaL);
863 JV = deltaV * (1 + deltaV * dalphar_ddeltaV);
864 KL = deltaL * dalphar_ddeltaL + alpharL + log(deltaL);
865 KV = deltaV * dalphar_ddeltaV + alpharV + log(deltaV);
867 PL = R_u * reduce.
rhomolar * T * JL;
868 PV = R_u * reduce.
rhomolar * T * JV;
871 dJL = 1 + 2 * deltaL * dalphar_ddeltaL + deltaL * deltaL * d2alphar_ddelta2L;
872 dJV = 1 + 2 * deltaV * dalphar_ddeltaV + deltaV * deltaV * d2alphar_ddelta2V;
873 dKL = 2 * dalphar_ddeltaL + deltaL * d2alphar_ddelta2L + 1 / deltaL;
874 dKV = 2 * dalphar_ddeltaV + deltaV * d2alphar_ddelta2V + 1 / deltaV;
876 DELTA = dJV * dKL - dJL * dKV;
878 error = sqrt((KL - KV) * (KL - KV) + (JL - JV) * (JL - JV));
881 stepL = options.
omega / DELTA * ((KV - KL) * dJV - (JV - JL) * dKV);
882 stepV = options.
omega / DELTA * ((KV - KL) * dJL - (JV - JL) * dKL);
887 for (
double omega_local = 1.0; omega_local > 0.1; omega_local /= 1.1) {
888 deltaL = deltaL0 + omega_local * stepL;
889 deltaV = deltaV0 + omega_local * stepV;
891 if (deltaL > 1 && deltaV < 1 && deltaV > 0) {
902 }
while (error > 1e-10 && std::abs(stepL) > 10 *
DBL_EPSILON * std::abs(stepL) && std::abs(stepV) > 10 *
DBL_EPSILON * std::abs(stepV));
906 if (std::abs(p_error) > p_error_limit) {
911 throw SolutionError(
format(
"saturation_T_pure_Akasaka solver abs error on p [%g] > limit [%g]", std::abs(p_error), p_error_limit));
932 shared_ptr<HelmholtzEOSMixtureBackend> SatL = HEOS.
SatL, SatV = HEOS.
SatV;
934 CoolPropDbl rhoL = _HUGE, rhoV = _HUGE, error = 999, DeltavL, DeltavV, pL, pV, p, last_error;
935 int iter = 0, small_step_count = 0,
936 backwards_step_count = 0;
948 rhoL = HEOS.
get_components()[0].ancillaries.rhoL.evaluate(T - 0.1);
949 rhoV = HEOS.
get_components()[0].ancillaries.rhoV.evaluate(T - 0.1);
960 if ((rhoL < crit.rhomolar * 0.8 || rhoL > tripleL.
rhomolar * 1.2 || rhoV > crit.
rhomolar * 1.2 || rhoV < tripleV.
rhomolar * 0.8)
964 std::cout <<
format(
"[Maxwell] ancillaries correction T: %0.16Lg rhoL: %0.16Lg rhoV: %0.16Lg rhoc: %g rhoLtrip: %g rhoVtrip: %g\n", T, rhoL, rhoV, crit.
rhomolar, tripleL.
rhomolar, tripleV.
rhomolar );
970 SatL->update_DmolarT_direct(rhoL, T);
973 rhoV = SatL->p() / (SatL->gas_constant() * T);
975 rhoV = p / (SatL->gas_constant() * T);
978 SatV->update_DmolarT_direct(rhoV, T);
980 SatL->update_DmolarT_direct(rhoL, T);
981 SatV->update_DmolarT_direct(rhoV, T);
984 std::cout <<
format(
"[Maxwell] ancillaries T: %0.16Lg rhoL: %0.16Lg rhoV: %0.16Lg pL: %g pV: %g\n", T, rhoL, rhoV, SatL->p(),
991 if (std::abs((SatL->p() - p) / p) > 0.1) {
992 for (
int iii = 0; iii < 6; ++iii) {
997 CoolPropDbl deltarhoLHalley = -(2 * f * dpdrho) / (2 *
POW2(dpdrho) - f * d2pdrho2);
998 rhoL += deltarhoLHalley;
999 if (std::abs(deltarhoLHalley / rhoL) <
DBL_EPSILON) {
1002 SatL->update_DmolarT_direct(rhoL, T);
1006 SatL->update_DmolarT_direct(rhoL, T);
1007 SatV->update_DmolarT_direct(rhoV, T);
1012 if (std::abs((SatV->p() - p) / p) > 0.1) {
1014 rhoV = SatV->solver_rho_Tp(T, p, rhoV);
1029 SatL->update_DmolarT_direct(rhoL, T);
1030 SatV->update_DmolarT_direct(rhoV, T);
1032 std::cout <<
format(
"[Maxwell] starting T: %0.16Lg rhoL: %Lg rhoV: %Lg pL: %Lg pV: %g\n", T, rhoL, rhoV, SatL->p(), SatV->p());
1037 CoolPropDbl vL = 1 / SatL->rhomolar(), vV = 1 / SatV->rhomolar();
1045 CoolPropDbl helmholtzL = (SatL->calc_alpha0() + SatL->calc_alphar()) * RT;
1046 CoolPropDbl helmholtzV = (SatV->calc_alpha0() + SatV->calc_alphar()) * RT;
1049 CoolPropDbl pM = (helmholtzL - helmholtzV) / (vV - vL);
1053 CoolPropDbl B = alphaL * (pL - pV - alphaV * (vL - vV));
1057 CoolPropDbl sqrt_arg = std::abs(B * B / (4 * A * A) - C / A);
1061 if (std::abs(sqrt_arg) > 1e-10) {
1062 DeltavL = -0.5 * B / A +
sign((alphaL - alphaV) / alphaV) * sqrt(sqrt_arg);
1066 DeltavL = -0.5 * B / A +
sign((alphaL - alphaV) / alphaV) * sqrt(sqrt_arg * powerfactor) / sqrt(powerfactor);
1068 DeltavV = (pL - pV + alphaL * DeltavL) / alphaV;
1071 rhoL = 1 / (vL + DeltavL);
1072 rhoV = 1 / (vV + DeltavV);
1073 if (B * B / (4 * A * A) - C / A < -10 *
DBL_EPSILON) {
1079 SatL->update_DmolarT_direct(rhoL, T);
1080 SatV->update_DmolarT_direct(rhoV, T);
1083 error = std::abs((SatL->p() - SatV->p()) / SatL->p());
1086 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,
1087 DeltavL / vL, DeltavV / vV, pL, pV);
1092 if (std::abs(DeltavL * rhoL) < 1e-13 || std::abs(DeltavV * rhoV) < 1e-13) {
1097 if (std::abs(error) > std::abs(last_error)) {
1098 backwards_step_count++;
1104 throw SolutionError(
format(
"Maxwell solver did not converge after 30 iterations; rhoL: %0.16Lg rhoV: %0.16Lg error: %Lg dvL/vL: %Lg "
1105 "dvV/vV: %Lg pL: %Lg pV: %Lg\n",
1106 rhoL, rhoV, error, DeltavL / vL, DeltavV / vV, pL, pV));
1108 }
while ((SatL->p() < 0) || (error > 1e-10 && small_step_count < 4 && backwards_step_count < 6));
1110 std::cout <<
format(
"[Maxwell] pL: %g pV: %g\n", SatL->p(), SatV->p());
1115 std::vector<CoolPropDbl>& x, std::vector<CoolPropDbl>& y) {
1116 for (
unsigned int i = 0; i < K.size(); i++) {
1117 double denominator = (1 - beta + beta * K[i]);
1118 x[i] = z[i] / denominator;
1119 y[i] = K[i] * z[i] / denominator;
1124 const std::vector<CoolPropDbl>& z, std::vector<CoolPropDbl>& K,
mixture_VLE_IO& options) {
1127 std::size_t N = z.size();
1128 std::vector<CoolPropDbl> ln_phi_liq, ln_phi_vap;
1129 ln_phi_liq.resize(N);
1130 ln_phi_vap.resize(N);
1132 std::vector<CoolPropDbl>&x = HEOS.
SatL->get_mole_fractions_ref(), &y = HEOS.
SatV->get_mole_fractions_ref();
1141 HEOS.
SatL->set_mole_fractions(x);
1142 HEOS.
SatV->set_mole_fractions(y);
1143 HEOS.
SatL->calc_reducing_state();
1144 HEOS.
SatV->calc_reducing_state();
1150 double summer_c = 0, v_SRK = 1 / rhomolar_liq;
1151 const std::vector<CoolPropFluid>& components = HEOS.
get_components();
1152 for (std::size_t i = 0; i < components.size(); ++i) {
1159 summer_c += z[i] * (0.40768 * R * Tc / pc * (0.29441 - pc / (rhomolarc * R * Tc)));
1161 rhomolar_liq = 1 / (v_SRK - summer_c);
1162 HEOS.
SatL->update_TP_guessrho(T, p, rhomolar_liq);
1163 HEOS.
SatV->update_TP_guessrho(T, p, rhomolar_vap);
1166 HEOS.
SatL->update_TP_guessrho(T, p, HEOS.
SatL->rhomolar());
1167 HEOS.
SatV->update_TP_guessrho(T, p, HEOS.
SatV->rhomolar());
1173 for (std::size_t i = 0; i < N; ++i) {
1187 K[i] = exp(ln_phi_liq[i] - ln_phi_vap[i]);
1189 f += z[i] * (K[i] - 1) / (1 - beta + beta * K[i]);
1191 double dfdK = K[i] * z[i] / pow(1 - beta + beta * K[i], (
int)2);
1192 df += dfdK * (deriv_liq - deriv_vap);
1195 if (std::abs(df) <= 1e-14) {
1196 if (std::abs(f) <= 1e-12)
1200 throw ValueError(
format(
"df very small (df = %g) in successive_substitution but f is not converged (f = %g > 1e-12).", df, f));
1210 if (std::abs(change) > 0.05 * p) {
1213 p += omega * change;
1219 HEOS.
SatL->set_mole_fractions(x);
1220 HEOS.
SatV->set_mole_fractions(y);
1224 throw ValueError(
format(
"saturation_p was unable to reach a solution within 50 iterations"));
1226 }
while (std::abs(f) > 1e-12 && iter < options.
Nstep_max);
1228 HEOS.
SatL->update_TP_guessrho(T, p, HEOS.
SatL->rhomolar());
1229 HEOS.
SatV->update_TP_guessrho(T, p, HEOS.
SatV->rhomolar());
1231 options.
p = HEOS.
SatL->p();
1232 options.
T = HEOS.
SatL->T();
1246 J.resize(
N + 1,
N + 1);
1264 std::vector<CoolPropDbl> x0 = x;
1265 Eigen::VectorXd r0 = r;
1266 Eigen::MatrixXd J0 = J;
1272 double dT = 1e-3, T1 = T + dT, T2 = T - dT;
1274 this->rhomolar_liq = rhomolar_liq0;
1275 this->rhomolar_vap = rhomolar_vap0;
1277 Eigen::VectorXd r1 = r;
1279 this->rhomolar_liq = rhomolar_liq0;
1280 this->rhomolar_vap = rhomolar_vap0;
1282 Eigen::VectorXd r2 = r;
1284 Eigen::VectorXd diffn = (r1 - r2) / (2 * dT);
1285 std::cout <<
format(
"For T\n");
1293 this->rhomolar_liq = rhomolar_liq0 + drho;
1294 this->rhomolar_vap = rhomolar_vap0;
1296 Eigen::VectorXd rr1 = r;
1298 this->rhomolar_liq = rhomolar_liq0 - drho;
1299 this->rhomolar_vap = rhomolar_vap0;
1301 Eigen::VectorXd rr2 = r;
1303 Eigen::VectorXd diffn = (rr1 - rr2) / (2 * drho);
1304 std::cout <<
format(
"For rho\n");
1308 for (std::size_t i = 0; i < x.size() - 1; ++i) {
1313 this->x[x.size() - 1] -= dx;
1315 this->rhomolar_liq = rhomolar_liq0;
1316 this->rhomolar_vap = rhomolar_vap0;
1318 Eigen::VectorXd r1 = this->r;
1322 this->x[x.size() - 1] += dx;
1324 this->rhomolar_liq = rhomolar_liq0;
1325 this->rhomolar_vap = rhomolar_vap0;
1327 Eigen::VectorXd r2 = this->r;
1329 Eigen::VectorXd diffn = (r1 - r2) / (2 * dx);
1330 std::cout <<
format(
"For x%d N %d\n", i, N);
1341 std::cout <<
" NRsat::call: p " << IO.
p <<
" T " << IO.
T <<
" dl " << IO.
rhomolar_liq <<
" dv " << IO.
rhomolar_vap << std::endl;
1377 Eigen::VectorXd v = J.colPivHouseholderQr().solve(-r);
1380 for (
unsigned int i = 0; i < N - 1; ++i) {
1381 err_rel[i] = v[i] / y[i];
1384 y[N - 1] = 1 - std::accumulate(y.begin(), y.end() - 1, 0.0);
1386 for (
unsigned int i = 0; i < N - 1; ++i) {
1387 err_rel[i] = v[i] / x[i];
1390 x[N - 1] = 1 - std::accumulate(x.begin(), x.end() - 1, 0.0);
1394 err_rel[N - 1] = v[N - 1] / T;
1397 err_rel[N - 1] = v[N - 1] / p;
1400 err_rel[N - 1] = v[N - 1] / T;
1401 rhomolar_liq += v[N];
1402 err_rel[N] = v[N] / rhomolar_liq;
1404 throw ValueError(
"invalid imposed_variable");
1410 min_rel_change = err_rel.cwiseAbs().minCoeff();
1416 }
while (this->error_rms > 1e-7 && min_rel_change > 1000 *
DBL_EPSILON && iter < IO.
Nstep_max);
1425 const std::vector<CoolPropFluid>& fluidsL = HEOS.
SatL->get_components();
1426 const std::vector<CoolPropFluid>& fluidsV = HEOS.
SatV->get_components();
1427 if (!fluidsL.empty() && !fluidsV.empty()) {
1449 rSatV.set_mole_fractions(y);
1458 rSatV.update_TP_guessrho(T, p, rhomolar_vap);
1459 rhomolar_vap = rSatV.rhomolar();
1461 throw ValueError(
"imposed variable not set for NR VLE");
1467 p = 0.5 * (p_liq + p_vap);
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 r(N) = p_liq - p_vap;
1498 for (std::size_t j = 0; j < N - 1; ++j) {
1508 for (std::size_t i = 0; i < N; ++i) {
1512 r(i) = ln_f_liq - ln_f_vap;
1514 for (std::size_t j = 0; j < N - 1; ++j) {
1528 for (std::size_t i = 0; i < N; ++i) {
1532 r(i) = ln_f_liq - ln_f_vap;
1534 for (std::size_t j = 0; j < N - 1; ++j) {
1548 error_rms = r.norm();
1553 for (std::size_t i = 0; i < N; ++i) {
1561 dTsat_dPsat = -dQ_dPsat / dQ_dTsat;
1562 dPsat_dTsat = -dQ_dTsat / dQ_dPsat;
1588 r.resize(2 * N - 1);
1589 J.resize(2 * N - 1, 2 * N - 1);
1590 err_rel.resize(2 * N - 1);
1606 Eigen::VectorXd v = J.colPivHouseholderQr().solve(-r);
1608 for (
unsigned int i = 0; i < N - 1; ++i) {
1609 err_rel[i] = v[i] / x[i];
1611 err_rel[i + (N - 1)] = v[i + (N - 1)] / y[i];
1612 y[i] += v[i + (N - 1)];
1614 x[N - 1] = 1 - std::accumulate(x.begin(), x.end() - 1, 0.0);
1615 y[N - 1] = 1 - std::accumulate(y.begin(), y.end() - 1, 0.0);
1619 err_rel[2 * N - 2] = v[2 * N - 2] / T;
1622 err_rel[2 * N - 2] = v[2 * N - 2] / p;
1624 throw ValueError(
"invalid imposed_variable");
1628 min_rel_change = err_rel.cwiseAbs().minCoeff();
1634 }
while (this->error_rms > 1e-9 && min_rel_change > 1000 *
DBL_EPSILON && iter < IO.
Nstep_max);
1657 rSatV.set_mole_fractions(y);
1664 rSatV.update_TP_guessrho(T, p, rhomolar_vap);
1665 rhomolar_vap = rSatV.rhomolar();
1670 p = 0.5 * (p_liq + p_vap);
1679 for (std::size_t i = 0; i < N; ++i) {
1683 r[i] = ln_f_liq - ln_f_vap;
1687 r[i + N] = (z[i] - x[i]) / (y[i] - x[i]) - beta;
1692 for (std::size_t i = 0; i < N; ++i) {
1693 for (std::size_t j = 0; j < N - 1; ++j) {
1710 for (std::size_t i = 0; i < N - 1; ++i) {
1711 std::size_t k = i + N;
1712 J(k, i) = (z[i] - y[i]) / pow(y[i] - x[i], 2);
1713 J(k, i + (N - 1)) = -(z[i] - x[i]) / pow(y[i] - x[i], 2);
1716 error_rms = r.norm();
1722 const std::vector<double>&z, &lnK;
1738 lnK.resize(z.size());
1740 double g0 = 0, g1 = 0, beta = -1;
1742 for (
int i = 0; i < static_cast<int>(z.size()); ++i) {
1744 if (m_T < 0 && m_p < 0) {
1746 lnK[i] = SaturationSolvers::Wilson_lnK_factor(HEOS, HEOS.T(), HEOS.p(), i);
1749 lnK[i] = SaturationSolvers::Wilson_lnK_factor(HEOS, m_T, m_p, i);
1752 g0 += z[i] * (K[i] - 1);
1753 g1 += z[i] * (1 - 1 / K[i]);
1762 }
else if (g1 > 0) {
1774 std::cout <<
format(
"1) T: %g p: %g beta: %g\n", HEOS.T(), HEOS.p(), beta);
1782 HEOS.SatL->set_mole_fractions(x);
1783 HEOS.SatL->calc_reducing_state();
1784 HEOS.SatV->set_mole_fractions(y);
1785 HEOS.SatV->calc_reducing_state();
1788 std::cout <<
format(
"2) SS1: i beta K x y rho' rho''\n");
1790 for (
int step_count = 0; step_count < num_steps; ++step_count) {
1792 HEOS.SatL->set_mole_fractions(x);
1793 HEOS.SatV->set_mole_fractions(y);
1794 HEOS.SatL->calc_reducing_state();
1795 HEOS.SatV->calc_reducing_state();
1797 this->rho_TP_global();
1800 double g0 = 0, g1 = 0;
1801 for (std::size_t i = 0; i < z.size(); ++i) {
1802 lnK[i] = log(HEOS.SatL->fugacity_coefficient(i) / HEOS.SatV->fugacity_coefficient(i));
1804 g0 += z[i] * (K[i] - 1);
1805 g1 += z[i] * (1 - 1 / K[i]);
1810 }
else if (g1 > 0) {
1823 vec_to_string(y,
"%0.6f").c_str(), rhomolar_liq, rhomolar_vap);
1828 std::vector<double> tpdL, tpdH;
1831 double the_T = (m_T > 0 && m_p > 0) ? m_T : HEOS.T();
1832 double the_p = (m_T > 0 && m_p > 0) ? m_p : HEOS.p();
1838 HEOS.SatL->set_mole_fractions(z);
1839 HEOS.SatV->set_mole_fractions(z);
1840 HEOS.SatL->calc_reducing_state();
1841 HEOS.SatV->calc_reducing_state();
1844 double rhoL = HEOS.SatL->solver_rho_Tp_global(the_T, the_p, 0.9 / HEOS.SatL->SRK_covolume());
1845 double rhoV = HEOS.SatV->solver_rho_Tp_global(the_T, the_p, 0.9 / HEOS.SatV->SRK_covolume());
1846 HEOS.SatL->update_DmolarT_direct(rhoL, the_T);
1847 HEOS.SatV->update_DmolarT_direct(rhoV, the_T);
1851 this->tpd_liq = HEOS.SatL->tangent_plane_distance(the_T, the_p, x, rhomolar_liq);
1852 this->tpd_vap = HEOS.SatV->tangent_plane_distance(the_T, the_p, y, rhomolar_vap);
1854 this->DELTAG_nRT = (1 - beta) * tpd_liq + beta * (tpd_vap);
1856 std::cout <<
format(
"3) tpd': %g tpd'': %g DELTAG/nRT: %g\n", tpd_liq, tpd_vap, DELTAG_nRT);
1860 if (this->tpd_liq < -DBL_EPSILON || this->tpd_vap < -DBL_EPSILON || this->DELTAG_nRT < -
DBL_EPSILON) {
1862 std::cout <<
format(
"3) PHASE SPLIT beta in (eps,1-eps) \n");
1872 CoolPropDbl rho_bulk = HEOS.solver_rho_Tp_global(the_T, the_p, 0.9 / HEOS.SRK_covolume());
1873 HEOS.update_DmolarT_direct(rho_bulk, the_T);
1876 std::vector<double> fugacity_coefficient0(z.size()), fugacity0(z.size());
1877 for (std::size_t i = 0; i < z.size(); ++i) {
1878 fugacity_coefficient0[i] = HEOS.fugacity_coefficient(i);
1879 fugacity0[i] = HEOS.fugacity(i);
1883 xL.resize(z.size());
1884 xH.resize(z.size());
1885 for (std::size_t i = 0; i < z.size(); ++i) {
1886 xL[i] = z[i] * K0[i];
1887 xH[i] = z[i] / K0[i];
1894 std::cout <<
format(
"3) SS2: i x' x'' rho' rho'' tpd' tpd''\n");
1900 double diffbulkL = 0, diffbulkH = 0;
1901 for (
int step_count = 0; step_count < 100; ++step_count) {
1904 HEOS.SatL->set_mole_fractions(xH);
1905 HEOS.SatV->set_mole_fractions(xL);
1906 HEOS.SatL->calc_reducing_state();
1907 HEOS.SatV->calc_reducing_state();
1912 double tpd_L = 0, tpd_H = 0;
1913 for (std::size_t i = 0; i < xL.size(); ++i) {
1917 tpdL.push_back(tpd_L);
1918 tpdH.push_back(tpd_H);
1921 diffbulkL = 0, diffbulkH = 0;
1922 for (std::size_t i = 0; i < z.size(); ++i) {
1923 xL[i] = z[i] * fugacity_coefficient0[i] / HEOS.SatV->fugacity_coefficient(i);
1924 diffbulkL += std::abs(xL[i] - z[i]);
1925 xH[i] = z[i] * fugacity_coefficient0[i] / HEOS.SatL->fugacity_coefficient(i);
1926 diffbulkH += std::abs(xH[i] - z[i]);
1932 rhomolar_liq, rhomolar_vap, tpd_L, tpd_H);
1936 if (diffbulkL < 1e-2 || diffbulkH < 1e-2) {
1942 if (tpd_L < -1e-12 || tpd_H < -1e-12) {
1947 if (diffbulkH > 0.25 || diffbulkL > 0.25) {
1956 double the_T = (m_T > 0 && m_p > 0) ? m_T : HEOS.T();
1957 double the_p = (m_T > 0 && m_p > 0) ? m_p : HEOS.p();
1960 double rhoL = HEOS.SatL->solver_rho_Tp_global(the_T, the_p, 0.9 / HEOS.SatL->SRK_covolume());
1961 double rhoV = HEOS.SatV->solver_rho_Tp_global(the_T, the_p, 0.9 / HEOS.SatV->SRK_covolume());
1962 HEOS.SatL->update_DmolarT_direct(rhoL, the_T);
1963 HEOS.SatV->update_DmolarT_direct(rhoV, the_T);
1965 rhomolar_liq = HEOS.SatL->rhomolar();
1966 rhomolar_vap = HEOS.SatV->rhomolar();
1972 if (m_T > 0 && m_p > 0) {
1973 HEOS.SatL->update_TP_guessrho(m_T, m_p, rhomolar_liq);
1974 HEOS.SatV->update_TP_guessrho(m_T, m_p, rhomolar_vap);
1976 HEOS.SatL->update_TP_guessrho(HEOS.T(), HEOS.p(), rhomolar_liq);
1977 HEOS.SatV->update_TP_guessrho(HEOS.T(), HEOS.p(), rhomolar_vap);
1979 rhomolar_liq = HEOS.SatL->rhomolar();
1980 rhomolar_vap = HEOS.SatV->rhomolar();
1986 if (m_T > 0 && m_p > 0) {
1987 rhomolar_liq = HEOS.SatL->solver_rho_Tp_SRK(m_T, m_p,
iphase_liquid);
1988 rhomolar_vap = HEOS.SatV->solver_rho_Tp_SRK(m_T, m_p,
iphase_gas);
1990 rhomolar_liq = HEOS.SatL->solver_rho_Tp_SRK(HEOS.T(), HEOS.p(),
iphase_liquid);
1991 rhomolar_vap = HEOS.SatV->solver_rho_Tp_SRK(HEOS.T(), HEOS.p(),
iphase_gas);
1995 if (HEOS.backend_name().find(
"Helmholtz") == 0) {
1998 double summer_c = 0, v_SRK = 1 / rhomolar_liq;
1999 for (std::size_t i = 0; i < z.size(); ++i) {
2004 summer_c += z[i] * (0.40768 * R * Tc / pc * (0.29441 - pc / (rhomolarc * R * Tc)));
2006 rhomolar_liq = 1 / (v_SRK - summer_c);
2011 const std::size_t N = IO.x.size();
2013 double min_rel_change;
2025 Eigen::VectorXd v = J.colPivHouseholderQr().solve(-r);
2027 for (
unsigned int i = 0; i < N - 1; ++i) {
2028 err_rel[i] = v[i] / IO.x[i];
2030 err_rel[i + (N - 1)] = v[i + (N - 1)] / IO.y[i];
2031 IO.y[i] += v[i + (N - 1)];
2033 IO.x[N - 1] = 1 - std::accumulate(IO.x.begin(), IO.x.end() - 1, 0.0);
2034 IO.y[N - 1] = 1 - std::accumulate(IO.y.begin(), IO.y.end() - 1, 0.0);
2038 min_rel_change = err_rel.cwiseAbs().minCoeff();
2041 if (iter == IO.Nstep_max) {
2042 throw ValueError(
format(
"PTflash_twophase::call reached max number of iterations [%d]", IO.Nstep_max));
2044 }
while (this->error_rms > 1e-9 && min_rel_change > 1000 *
DBL_EPSILON && iter < IO.Nstep_max);
2047 const std::size_t N = IO.x.size();
2049 r.resize(2 * N - 2);
2050 J.resize(2 * N - 2, 2 * N - 2);
2051 err_rel.resize(2 * N - 2);
2053 HEOS.SatL->set_mole_fractions(IO.x);
2054 HEOS.SatL->update_TP_guessrho(IO.T, IO.p, IO.rhomolar_liq);
2056 HEOS.SatV->set_mole_fractions(IO.y);
2057 HEOS.SatV->update_TP_guessrho(IO.T, IO.p, IO.rhomolar_vap);
2063 for (std::size_t k = 0; k < N; ++k) {
2064 r(k) = log(HEOS.SatL->fugacity(k) / HEOS.SatV->fugacity(k));
2065 for (std::size_t j = 0; j < N - 1; ++j) {
2076 for (std::size_t i = 0; i < N - 2; ++i) {
2077 std::size_t k = i + N;
2078 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]);
2079 for (std::size_t j = 0; j < N - 2; ++j) {
2080 J(k, j) = (IO.z[j] - IO.x[j]) /
POW2(IO.y[j] - IO.x[j]);
2081 J(k, j + N - 1) = -(IO.z[j] - IO.x[j]) /
POW2(IO.y[j] - IO.x[j]);
2083 std::size_t j = N - 2;
2084 J(k, j) = -(IO.z[j] - IO.x[j]) /
POW2(IO.y[j] - IO.x[j]);
2085 J(k, j + N - 1) = +(IO.z[j] - IO.x[j]) /
POW2(IO.y[j] - IO.x[j]);
2087 this->error_rms = r.norm();
2091 #if defined(ENABLE_CATCH)
2092 # include <catch2/catch_all.hpp>
2094 TEST_CASE(
"Check the PT flash calculation for two-phase inputs",
"[PTflash_twophase]") {
2096 AS->set_mole_fractions(std::vector<double>(2, 0.5));
2102 o.
x = AS->mole_fractions_liquid();
2103 o.
y = AS->mole_fractions_vapor();
2104 o.
z = AS->get_mole_fractions();
2111 solver.build_arrays();
2112 double err = solver.r.norm();
2113 REQUIRE(std::abs(err) < 1e-10);
2116 std::vector<double> x0 = o.
x;
2118 o.
x[1] = 1 - o.
x[0];
2121 double diffx0 = o.
x[0] - x0[0];
2122 REQUIRE(std::abs(diffx0) < 1e-10);