11 N =
cubic->get_Tc().size();
28 Reducing = std::make_shared<ConstantReducingFunction>(
cubic->get_Tr(),
cubic->get_rhor());
38 if (generate_SatL_and_SatV) {
39 bool SatLSatV =
false;
51 if (components.empty()) {
55 for (std::size_t i = 0; i < N; ++i) {
56 const std::string& alpha_type = components[i].alpha_type;
57 if (alpha_type !=
"default") {
58 const std::vector<double>& c = components[i].alpha_coeffs;
59 shared_ptr<AbstractCubicAlphaFunction> acaf;
60 if (alpha_type ==
"Twu") {
61 acaf = std::make_shared<TwuAlphaFunction>(get_cubic()->a0_ii(i), c[0], c[1], c[2], get_cubic()->get_Tr() / get_cubic()->get_Tc()[i]);
62 }
else if (alpha_type ==
"MathiasCopeman" || alpha_type ==
"Mathias-Copeman") {
66 throw ValueError(
"alpha function is not understood");
68 cubic->set_alpha_function(i, acaf);
76 if (components.size() == 0) {
83 for (std::size_t i = 0; i < N; ++i) {
87 _components.push_back(fld);
93 if (!ParamName.compare(
"name")) {
95 }
else if (!ParamName.compare(
"aliases")) {
97 }
else if (!ParamName.compare(
"CAS") || !ParamName.compare(
"CAS_number")) {
99 }
else if (!ParamName.compare(
"formula")) {
101 }
else if (!ParamName.compare(
"ASHRAE34")) {
103 }
else if (!ParamName.compare(
"REFPROPName") || !ParamName.compare(
"REFPROP_name") || !ParamName.compare(
"REFPROPname")) {
105 }
else if (ParamName.find(
"BibTeX") == 0)
108 }
else if (ParamName.find(
"pure") == 0) {
114 }
else if (ParamName ==
"INCHI" || ParamName ==
"InChI" || ParamName ==
"INCHI_STRING") {
116 }
else if (ParamName ==
"INCHI_Key" || ParamName ==
"InChIKey" || ParamName ==
"INCHIKEY") {
118 }
else if (ParamName ==
"2DPNG_URL") {
120 }
else if (ParamName ==
"SMILES" || ParamName ==
"smiles") {
122 }
else if (ParamName ==
"CHEMSPIDER_ID") {
123 throw NotImplementedError(
"Parameter \"CHEMSPIDER_ID\" not available for cubic backends.");
124 }
else if (ParamName ==
"JSON") {
127 throw ValueError(
format(
"fluid parameter [%s] is invalid", ParamName.c_str()));
132 std::vector<std::string> out;
133 out.reserve(components.size());
134 for (
auto& component : components) {
135 out.push_back(component.name);
145 const std::vector<double> Tc = cubic->get_Tc(), pc = cubic->get_pc();
146 for (std::size_t i = 0; i < mole_fractions.size(); ++i) {
147 T_r += mole_fractions[i] * Tc[i];
149 double v_c_Lmol = 2.14107171795 * (Tc[i] / pc[i] * 1000) + 0.00773144012514;
150 v_r += mole_fractions[i] * v_c_Lmol / 1000.0;
152 rhomolar_r = 1 / v_r;
160 double Tr_GERGlike = NAN, rhor_GERGlike = NAN;
161 get_linear_reducing_parameters(rhor_GERGlike, Tr_GERGlike);
162 R_delta *= rhor_GERGlike / rhomolar_reducing() * 5;
163 R_tau *= T_reducing() / Tr_GERGlike * 5;
169 double b = get_cubic()->bm_term(mole_fractions);
170 double v = 1 / (delta * rhomolar_reducing());
171 bool covolume_check = v < 1.1 * b;
173 return covolume_check;
192 double Tr_GERGlike = NAN, rhor_GERGlike = NAN;
193 get_linear_reducing_parameters(rhor_GERGlike, Tr_GERGlike);
194 delta0 *= rhor_GERGlike / rhomolar_reducing();
195 tau0 *= T_reducing() / Tr_GERGlike;
199 double tau = cubic->
get_Tr() /
T;
200 double delta = rhomolar / cubic->
get_rhor();
201 return _rhomolar * gas_constant() * _T * (1 + delta * cubic->
alphar(tau, delta, this->get_mole_fractions_doubleref(), 0, 1));
205 _p = calc_pressure_nocache(_T, _rhomolar);
207 _phase = imposed_phase_index;
219 const double Tmax = calc_superanc_Tmax();
221 _p = calc_pressure_nocache(_T, _rhomolar);
223 recalculate_singlephase_phase();
226 const double rhoL_sat = calc_saturation_ancillary(
iDmolar, 0,
iT, _T);
227 const double rhoV_sat = calc_saturation_ancillary(
iDmolar, 1,
iT, _T);
228 if (_rhomolar >= rhoL_sat) {
229 _p = calc_pressure_nocache(_T, _rhomolar);
232 }
else if (_rhomolar <= rhoV_sat) {
233 _p = calc_pressure_nocache(_T, _rhomolar);
239 const double rho = _rhomolar;
240 _p = calc_saturation_ancillary(
iP, 0,
iT, _T);
241 _Q = (rhoV_sat * (rhoL_sat - rho)) / (rho * (rhoL_sat - rhoV_sat));
243 SatL->update_TDmolarP_unchecked(_T, rhoL_sat, _p);
244 SatV->update_TDmolarP_unchecked(_T, rhoV_sat, _p);
249 const std::vector<CoolPropDbl>& mole_fractions,
const CoolPropDbl& tau,
251 bool cache_values =
true;
252 HelmholtzDerivatives derivs = residual_helmholtz->all(*
this, mole_fractions, tau, delta, cache_values);
257 return derivs.alphar;
259 return derivs.dalphar_ddelta;
261 return derivs.d2alphar_ddelta2;
263 return derivs.d3alphar_ddelta3;
265 return derivs.d4alphar_ddelta4;
274 return derivs.dalphar_dtau;
276 return derivs.d2alphar_ddelta_dtau;
278 return derivs.d3alphar_ddelta2_dtau;
280 return derivs.d4alphar_ddelta3_dtau;
289 return derivs.d2alphar_dtau2;
291 return derivs.d3alphar_ddelta_dtau2;
293 return derivs.d4alphar_ddelta2_dtau2;
301 return derivs.d3alphar_dtau3;
303 return derivs.d4alphar_ddelta_dtau3;
311 return derivs.d4alphar_dtau4;
323 std::cout <<
format(
"%s (%d): update called with (%d: (%s), %g, %g)", __FILE__, __LINE__, input_pair,
333 update_Qmass_pair(input_pair, value1, value2);
337 CoolPropDbl ld_value1 = value1, ld_value2 = value2;
338 pre_update(input_pair, ld_value1, ld_value2);
342 switch (input_pair) {
347 _rhomolar = solver_rho_Tp(value2 , value1 );
355 saturation(input_pair);
360 saturation(input_pair);
393 double am = cubic->
am_term(cubic->
get_Tr() /
T, mole_fractions, 0);
394 double bm = cubic->
bm_term(mole_fractions);
404 double crho1 = R *
T - p * (d1 + d2 + d3);
405 double crho2 = R *
T * (d2 + d3) - p * (d1 * (d2 + d3) + d2 * d3) - am;
406 double crho3 = R *
T * d2 * d3 - p * d1 * d2 * d3 - d1 * am;
409 solve_cubic(crho3, crho2, crho1, crho0, Nsolns, rho0, rho1, rho2);
410 sort3(rho0, rho1, rho2);
419 double imposed_variable = _HUGE;
420 double deltaL = _HUGE, deltaV = _HUGE;
424 : ACB(ACB), inputs(inputs), imposed_variable(imposed_variable) {};
426 double call(
double value)
override {
428 double rho0 = -1, rho1 = -1, rho2 = -1,
T = NAN, p = NAN;
432 p = imposed_variable;
435 T = imposed_variable;
448 double tau = T_r /
T;
450 deltaL = rho2 / rho_r;
451 deltaV = rho0 / rho_r;
453 double DELTAgibbs = log(deltaV) - log(deltaL);
467 double tau = cubic->
get_Tr() / _T;
468 std::vector<double> x(1, 1);
469 double a = cubic->
am_term(tau, x, 0);
475 double crho4 = -
powInt(Delta_1 * Delta_2, 2) * R * _T *
powInt(b, 4) + a *
powInt(b, 3) * (Delta_1 + Delta_2);
477 -2 * ((Delta_1 * Delta_1 * Delta_2 + Delta_1 * Delta_2 * Delta_2) * R * _T *
powInt(b, 3) + a *
powInt(b, 2) * (Delta_1 + Delta_2 - 1));
478 double crho2 = ((-Delta_1 * Delta_1 - Delta_2 * Delta_2 - 4 * Delta_1 * Delta_2) * R * _T *
powInt(b, 2) + a * b * (Delta_1 + Delta_2 - 4));
479 double crho1 = -2 * (Delta_1 + Delta_2) * R * _T * b + 2 * a;
480 double crho0 = -R * _T;
481 double rho0 = NAN, rho1 = NAN, rho2 = NAN, rho3 = NAN;
483 solve_quartic(crho4, crho3, crho2, crho1, crho0, Nsoln, rho0, rho1, rho2, rho3);
484 std::vector<double> roots;
485 if (rho0 > 0 && 1 / rho0 > b) {
486 roots.push_back(rho0);
488 if (rho1 > 0 && 1 / rho1 > b) {
489 roots.push_back(rho1);
491 if (rho2 > 0 && 1 / rho2 > b) {
492 roots.push_back(rho2);
494 if (rho3 > 0 && 1 / rho3 > b) {
495 roots.push_back(rho3);
503 double rhoL = -1, rhoV = -1;
505 if (is_pure_or_pseudopure) {
507 double theta = -log10(_p / pc) * (1 / 0.7 - 1) / (acentric + 1);
508 double Ts_est = Tc / (theta + 1);
510 static std::string errstr;
522 if (is_pure_or_pseudopure) {
524 static std::string errstr;
527 std::vector<double> roots;
530 double neg_log10_pr = (acentric + 1) / (1 / 0.7 - 1) * (Tc / _T - 1);
531 double ps_est = pc * pow(10.0, -neg_log10_pr);
534 if (roots.size() == 2) {
535 double p0 = calc_pressure_nocache(_T, roots[0]);
536 double p1 = calc_pressure_nocache(_T, roots[1]);
541 if (p0 > 0 && p1 < pc) {
560 _rhomolar = 1 / (_Q / rhoV + (1 - _Q) / rhoL);
565 double rho0 = 0, rho1 = 0, rho2 = 0;
566 rho_Tp_cubic(
T, p, Nsoln, rho0, rho1, rho2);
571 }
else if (Nsoln == 3) {
574 return solver_rho_Tp(
T, p);
579 double rho_vap = (rho0 > 0) ? rho0 : rho1;
580 double rho_liq = rho2;
584 bool vap_ok = rho_vap > 0;
585 bool liq_ok = rho_liq > 0;
586 if (vap_ok && liq_ok) {
587 double g_vap = calc_gibbsmolar_nocache(
T, rho_vap);
588 double g_liq = calc_gibbsmolar_nocache(
T, rho_liq);
591 }
else if (vap_ok || liq_ok) {
592 rho = vap_ok ? rho_vap : rho_liq;
594 throw ValueError(
"solver_rho_Tp_global: no positive density root among the three cubic roots");
597 throw ValueError(
"Obtained neither 1 nor three roots");
601 update_DmolarT_direct(rho,
T);
604 _phase = imposed_phase_index;
612 double rho0 = 0, rho1 = 0, rho2 = 0, rho = -1;
613 rho_Tp_cubic(
T, p, Nsoln, rho0, rho1, rho2);
616 }
else if (Nsoln == 3) {
620 }
else if (rho1 > 0) {
622 }
else if (rho2 > 0) {
630 throw ValueError(
"Specified phase is invalid");
633 throw ValueError(
"Obtained neither 1 nor three roots");
642 return solver_rho_Tp(
T, p, imposed_phase_index);
645 double rho0 = 0, rho1 = 0, rho2 = 0, rho = -1;
646 rho_Tp_cubic(
T, p, Nsoln, rho0, rho1, rho2);
649 }
else if (Nsoln == 3) {
650 if (p < p_critical()) {
651 add_transient_pure_state();
652 transient_pure_state->set_mole_fractions(this->mole_fractions);
653 transient_pure_state->update(
PQ_INPUTS, p, 0);
654 if (
T > transient_pure_state->T()) {
655 double rhoV = transient_pure_state->saturated_vapor_keyed_output(
iDmolar);
657 if (rho0 > 0 && rho0 < rhoV) {
659 }
else if (rho1 > 0 && rho1 < rhoV) {
669 throw ValueError(
"Cubic has three roots, but phase not imposed and guess density not provided");
672 throw ValueError(
"Obtained neither 1 nor three roots");
674 if (is_pure_or_pseudopure) {
675 this->recalculate_singlephase_phase();
677 if (rho < rhomolar_reducing()) {
689 for (
unsigned int i = 0; i < N; ++i) {
690 summer += mole_fractions[i] * components[i].molemass;
696 const double value) {
700 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j, N - 1));
702 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i, N - 1));
705 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j, N - 1));
707 if (parameter ==
"kij" || parameter ==
"k_ij") {
708 get_cubic()->set_kij(i, j, value);
710 throw ValueError(
format(
"I don't know what to do with parameter [%s]", parameter.c_str()));
712 for (
auto& state : linked_states) {
713 state->set_binary_interaction_double(i, j, parameter, value);
720 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j, N - 1));
722 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i, N - 1));
725 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j, N - 1));
727 if (parameter ==
"kij" || parameter ==
"k_ij") {
728 return get_cubic()->get_kij(i, j);
730 throw ValueError(
format(
"I don't know what to do with parameter [%s]", parameter.c_str()));
735 get_cubic()->set_all_alpha_functions(donor->
get_cubic()->get_all_alpha_functions());
736 for (
auto& state : linked_states) {
743 get_cubic()->set_kmat(donor->
get_cubic()->get_kmat());
744 for (
auto& state : linked_states) {
751 this->copy_k(&donor);
754 this->set_alpha_from_components();
755 this->set_alpha0_from_components();
756 for (
auto& state : linked_states) {
759 ACB->set_alpha_from_components();
760 ACB->set_alpha0_from_components();
768 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i, N - 1));
770 if (parameter ==
"MC" || parameter ==
"mc" || parameter ==
"Mathias-Copeman") {
771 get_cubic()->set_C_MC(i, c1, c2, c3);
772 if (!components.empty() && i < components.size()) {
773 components[i].alpha_type =
"MathiasCopeman";
774 components[i].alpha_coeffs = {c1, c2, c3};
776 }
else if (parameter ==
"TWU" || parameter ==
"Twu" || parameter ==
"twu") {
777 get_cubic()->set_C_Twu(i, c1, c2, c3);
778 if (!components.empty() && i < components.size()) {
779 components[i].alpha_type =
"Twu";
780 components[i].alpha_coeffs = {c1, c2, c3};
783 throw ValueError(
format(
"I don't know what to do with parameter [%s]", parameter.c_str()));
785 for (
auto& state : linked_states) {
794 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i, N - 1));
797 if (parameter ==
"c" || parameter ==
"cm" || parameter ==
"c_m") {
798 get_cubic()->set_cm(value);
799 for (
auto& state : linked_states) {
803 }
else if (parameter ==
"Q" || parameter ==
"Qk" || parameter ==
"Q_k") {
804 get_cubic()->set_Q_k(i, value);
805 for (
auto& state : linked_states) {
810 throw ValueError(
format(
"I don't know what to do with parameter [%s]", parameter.c_str()));
816 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i, N - 1));
819 if (parameter ==
"c" || parameter ==
"cm" || parameter ==
"c_m") {
820 return get_cubic()->get_cm();
821 }
else if (parameter ==
"Q" || parameter ==
"Qk" || parameter ==
"Q_k") {
822 return get_cubic()->get_Q_k(i);
824 throw ValueError(
format(
"I don't know what to do with parameter [%s]", parameter.c_str()));
829 if (!is_pure_or_pseudopure) {
830 throw ValueError(
"calc_superanc_Tmax: fluid must be pure");
832 int eos_code = get_superanc_eos_code();
834 throw NotImplementedError(
"calc_superanc_Tmax: no superancillary available for this cubic EOS");
836 double Ttilde_max = CubicSuperAncillary::get_Ttilde_max(eos_code);
837 std::vector<double> x = {1.0};
839 double tau_at_Tc = cubic->get_Tr() / cubic->get_Tc()[0];
840 double a0 = cubic->am_term(tau_at_Tc, x, 0);
841 double bm = cubic->bm_term(x);
842 return Ttilde_max * a0 / (cubic->get_R_u() * bm);
846 if (!is_pure_or_pseudopure) {
847 throw NotImplementedError(
"calc_saturation_ancillary is not implemented for mixtures in the cubic backend");
849 int eos_code = get_superanc_eos_code();
851 throw NotImplementedError(
"calc_saturation_ancillary: no superancillary available for this cubic EOS");
858 double Tmax = calc_superanc_Tmax();
860 throw ValueError(
format(
"calc_saturation_ancillary: T (%g K) exceeds superancillary Tmax (%g K)",
T, Tmax));
862 std::vector<double> x = {1.0};
863 double tau = cubic->get_Tr() /
T;
864 double am = cubic->am_term(tau, x, 0);
865 double bm = cubic->bm_term(x);
866 double Ttilde = cubic->get_R_u() *
T * bm / am;
868 using namespace CubicSuperAncillary;
870 double p_tilde = supercubic(eos_code,
P_CODE, Ttilde);
871 return p_tilde * am / (bm * bm);
874 return supercubic(eos_code,
RHOL_CODE, Ttilde) / bm;
876 return supercubic(eos_code,
RHOV_CODE, Ttilde) / bm;
885 if (!is_pure_or_pseudopure) {
886 throw ValueError(
"update_QT_pure_superanc: fluid must be pure");
888 int eos_code = get_superanc_eos_code();
890 throw NotImplementedError(
"update_QT_pure_superanc: no superancillary available for this cubic EOS");
892 double Tmax = calc_superanc_Tmax();
894 throw ValueError(
format(
"update_QT_pure_superanc: T (%g K) exceeds superancillary Tmax (%g K)",
T, Tmax));
897 std::vector<double> x = {1.0};
898 double tau = cubic->get_Tr() /
T;
899 double am = cubic->am_term(tau, x, 0);
900 double bm = cubic->bm_term(x);
901 double Ttilde = cubic->get_R_u() *
T * bm / am;
903 using namespace CubicSuperAncillary;
912 _rhomolar = 1.0 / (Q / rhoV + (1.0 - Q) / rhoL);
915 SatL->update_TDmolarP_unchecked(
T, rhoL, p);
916 SatV->update_TDmolarP_unchecked(
T, rhoV, p);