11# define _CRTDBG_MAP_ALLOC
12# ifndef _CRT_SECURE_NO_WARNINGS
13# define _CRT_SECURE_NO_WARNINGS
40static int deriv_counter = 0;
48 if (fluid_names.size() == 1) {
67 std::vector<CoolPropFluid>
components(component_names.size());
68 for (
unsigned int i = 0; i <
components.size(); ++i) {
96 this->
N = components.size();
101 std::vector<std::vector<double>> ones(1, std::vector<double>(1, 1));
114 if (generate_SatL_and_SatV) {
126 if (mf.size() !=
N) {
127 throw ValueError(
format(
"size of mole fraction vector [%d] does not equal that of component vector [%d]", mf.size(),
N));
140 for (std::vector<shared_ptr<HelmholtzEOSMixtureBackend>>::iterator it =
linked_states.begin(); it !=
linked_states.end(); ++it) {
141 it->get()->sync_linked_states(source);
153 if (mass_fractions.size() !=
N) {
154 throw ValueError(
format(
"size of mass fraction vector [%d] does not equal that of component vector [%d]", mass_fractions.size(),
N));
156 std::vector<CoolPropDbl> moles;
159 for (
unsigned int i = 0; i <
components.size(); ++i) {
160 tmp = mass_fractions[i] /
components[i].molar_mass();
161 moles.push_back(tmp);
165 for (std::vector<CoolPropDbl>::iterator it = moles.begin(); it != moles.end(); ++it) {
174 for (std::vector<shared_ptr<HelmholtzEOSMixtureBackend>>::iterator it =
linked_states.begin(); it !=
linked_states.end(); ++it) {
176 it->get()->resize(
N);
201 if (!ParamName.compare(
"name")) {
203 }
else if (!ParamName.compare(
"aliases")) {
205 }
else if (!ParamName.compare(
"CAS") || !ParamName.compare(
"CAS_number")) {
207 }
else if (!ParamName.compare(
"formula")) {
209 }
else if (!ParamName.compare(
"ASHRAE34")) {
211 }
else if (!ParamName.compare(
"REFPROPName") || !ParamName.compare(
"REFPROP_name") || !ParamName.compare(
"REFPROPname")) {
213 }
else if (ParamName.find(
"BibTeX") == 0)
215 std::vector<std::string> parts =
strsplit(ParamName,
'-');
216 if (parts.size() != 2) {
217 throw ValueError(
format(
"Unable to parse BibTeX string %s", ParamName.c_str()));
219 std::string key = parts[1];
220 if (!key.compare(
"EOS")) {
222 }
else if (!key.compare(
"CP0")) {
224 }
else if (!key.compare(
"VISCOSITY")) {
226 }
else if (!key.compare(
"CONDUCTIVITY")) {
228 }
else if (!key.compare(
"ECS_LENNARD_JONES")) {
230 }
else if (!key.compare(
"ECS_VISCOSITY_FITS")) {
232 }
else if (!key.compare(
"ECS_CONDUCTIVITY_FITS")) {
234 }
else if (!key.compare(
"SURFACE_TENSION")) {
236 }
else if (!key.compare(
"MELTING_LINE")) {
241 }
else if (ParamName.find(
"pure") == 0) {
247 }
else if (ParamName ==
"INCHI" || ParamName ==
"InChI" || ParamName ==
"INCHI_STRING") {
248 return cpfluid.
InChI;
249 }
else if (ParamName ==
"INCHI_Key" || ParamName ==
"InChIKey" || ParamName ==
"INCHIKEY") {
251 }
else if (ParamName ==
"2DPNG_URL") {
253 }
else if (ParamName ==
"SMILES" || ParamName ==
"smiles") {
255 }
else if (ParamName ==
"CHEMSPIDER_ID") {
257 }
else if (ParamName ==
"JSON") {
260 throw ValueError(
format(
"fluid parameter [%s] is invalid", ParamName.c_str()));
268 return components[0].EOS().get_superanc_optional();
273 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N-1));
276 if (parameter.find(
"SUPERANC::") == 0){
277 auto& superanc = optsuperanc.value();
279 std::string key = parameter.substr(10);
281 return superanc.get_pmax();
283 else if (key ==
"pmin"){
284 return superanc.get_pmin();
286 else if (key ==
"Tmin"){
287 return superanc.get_Tmin();
289 else if (key ==
"Tcrit_num"){
290 return superanc.get_Tcrit_num();
293 throw ValueError(
format(
"Superancillary parameter [%s] is invalid", key.c_str()));
300 throw ValueError(
format(
"fluid parameter [%s] is invalid", parameter.c_str()));
307 if (i < 0 || i >=
N) {
308 if (j < 0 || j >=
N) {
309 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j,
N-1));
311 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N-1));
313 }
else if (j < 0 || j >=
N) {
314 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j,
N-1));
316 if (model ==
"linear") {
318 double gammaT = 0.5 * (Tc1 + Tc2) / sqrt(Tc1 * Tc2);
320 double gammaV = 4.0 * (1 / rhoc1 + 1 / rhoc2) / pow(pow(rhoc1, -1.0 / 3.0) + pow(rhoc2, -1.0 / 3.0), 3);
325 }
else if (model ==
"Lorentz-Berthelot") {
331 throw ValueError(
format(
"mixing rule [%s] is not understood", model.c_str()));
336 const double value) {
338 if (i < 0 || i >=
N) {
339 if (j < 0 || j >=
N) {
340 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j,
N-1));
342 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N-1));
344 }
else if (j < 0 || j >=
N) {
345 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j,
N-1));
347 if (parameter ==
"Fij") {
351 Reducing->set_binary_interaction_double(i, j, parameter, value);
354 for (std::vector<shared_ptr<HelmholtzEOSMixtureBackend>>::iterator it =
linked_states.begin(); it !=
linked_states.end(); ++it) {
355 it->get()->set_binary_interaction_double(i, j, parameter, value);
361 if (i < 0 || i >=
N) {
362 if (j < 0 || j >=
N) {
363 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j,
N-1));
365 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N-1));
367 }
else if (j < 0 || j >=
N) {
368 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j,
N-1));
370 if (parameter ==
"Fij") {
373 return Reducing->get_binary_interaction_double(i, j, parameter);
382 const std::string& value) {
384 if (i < 0 || i >=
N) {
385 if (j < 0 || j >=
N) {
386 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j,
N-1));
388 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N-1));
390 }
else if (j < 0 || j >=
N) {
391 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j,
N-1));
393 if (parameter ==
"function") {
397 throw ValueError(
format(
"Cannot process this string parameter [%s] in set_binary_interaction_string", parameter.c_str()));
400 for (std::vector<shared_ptr<HelmholtzEOSMixtureBackend>>::iterator it =
linked_states.begin(); it !=
linked_states.end(); ++it) {
401 it->get()->set_binary_interaction_string(i, j, parameter, value);
411 if (EOS_name ==
"SRK" || EOS_name ==
"Peng-Robinson") {
423 shared_ptr<AbstractCubic> ac;
424 if (EOS_name ==
"SRK") {
425 ac.reset(
new SRK(Tc, pc, acentric, R));
430 ac->set_rhor(rhomolarc);
432 }
else if (EOS_name ==
"XiangDeiters") {
450 if (this->
SatL)
SatL->change_EOS(i, EOS_name);
451 if (this->
SatV)
SatV->change_EOS(i, EOS_name);
487 if (!state.compare(
"hs_anchor")) {
489 }
else if (!state.compare(
"max_sat_T")) {
491 }
else if (!state.compare(
"max_sat_p")) {
493 }
else if (!state.compare(
"reducing")) {
495 }
else if (!state.compare(
"critical")) {
497 }
else if (!state.compare(
"triple_liquid")) {
499 }
else if (!state.compare(
"triple_vapor")) {
502 throw ValueError(
format(
"This state [%s] is invalid to calc_state", state.c_str()));
505 if (!state.compare(
"critical")) {
516 throw ValueError(
"acentric factor cannot be calculated for mixtures");
528 for (
unsigned int i = 0; i <
components.size(); ++i) {
537 for (
unsigned int i = 0; i <
components.size(); ++i) {
544 if (param ==
iP && given ==
iT) {
548 return components[0].ancillaries.pL.evaluate(value);
550 return components[0].ancillaries.pV.evaluate(value);
552 }
else if (param ==
iT && given ==
iP) {
556 return components[0].ancillaries.pL.invert(value);
558 return components[0].ancillaries.pV.invert(value);
560 }
else if (param ==
iDmolar && given ==
iT) {
564 return components[0].ancillaries.rhoL.evaluate(value);
566 return components[0].ancillaries.rhoV.evaluate(value);
568 }
else if (param ==
iT && given ==
iDmolar) {
572 return components[0].ancillaries.rhoL.invert(value);
574 return components[0].ancillaries.rhoV.invert(value);
577 return components[0].ancillaries.surface_tension.evaluate(value);
591 return components[0].ancillaries.melting_line.evaluate(param, given, value);
599 return components[0].ancillaries.surface_tension.evaluate(
T());
601 throw ValueError(
format(
"surface tension is only defined within the two-phase region; Try PQ or QT inputs"));
610 switch (
components[0].transport.viscosity_dilute.type) {
637 format(
"dilute viscosity type [%d] is invalid for fluid %s",
components[0].transport.viscosity_dilute.type,
name().c_str()));
650 switch (
components[0].transport.viscosity_initial.type) {
654 initial_density = eta_dilute * B_eta_initial * rho;
667 switch (
components[0].transport.viscosity_higher_order.type) {
672 residual = TransportRoutines::viscosity_higher_order_friction_theory(*
this);
697 format(
"higher order viscosity type [%d] is invalid for fluid %s",
components[0].transport.viscosity_dilute.type,
name().c_str()));
700 return initial_density + residual;
705 CoolPropDbl dilute = 0, initial_density = 0, residual = 0, critical = 0;
707 return dilute + initial_density + residual + critical;
732 throw ValueError(
format(
"Viscosity model is not available for this fluid"));
739 std::vector<std::string> names(1, fluid_name);
743 critical = TransportRoutines::viscosity_ECS(*
this, *ref_fluid);
750 critical = TransportRoutines::viscosity_Chung(*
this);
757 critical = TransportRoutines::viscosity_rhosr(*
this);
808 throw ValueError(
"calc_viscosity_contributions invalid for mixtures");
824 throw ValueError(
format(
"Thermal conductivity model is not available for this fluid"));
831 std::vector<std::string>
name(1, fluid_name);
835 initial_density = TransportRoutines::conductivity_ECS(*
this, *ref_fluid);
844 initial_density = TransportRoutines::conductivity_hardcoded_water(*
this);
847 initial_density = TransportRoutines::conductivity_hardcoded_heavywater(*
this);
850 initial_density = TransportRoutines::conductivity_hardcoded_R23(*
this);
853 initial_density = TransportRoutines::conductivity_hardcoded_helium(*
this);
856 initial_density = TransportRoutines::conductivity_hardcoded_methane(*
this);
859 throw ValueError(
format(
"hardcoded conductivity type [%d] is invalid for fluid %s",
872 dilute = TransportRoutines::conductivity_dilute_ratio_polynomials(*
this);
875 dilute = TransportRoutines::conductivity_dilute_eta0_and_poly(*
this);
878 dilute = TransportRoutines::conductivity_dilute_hardcoded_CO2(*
this);
881 dilute = TransportRoutines::conductivity_dilute_hardcoded_CO2_HuberJPCRD2016(*
this);
884 dilute = TransportRoutines::conductivity_dilute_hardcoded_ethane(*
this);
891 format(
"dilute conductivity type [%d] is invalid for fluid %s",
components[0].transport.conductivity_dilute.type,
name().c_str()));
900 critical = TransportRoutines::conductivity_critical_simplified_Olchowy_Sengers(*
this);
903 critical = TransportRoutines::conductivity_critical_hardcoded_R123(*
this);
906 critical = TransportRoutines::conductivity_critical_hardcoded_ammonia(*
this);
912 critical = TransportRoutines::conductivity_critical_hardcoded_CO2_ScalabrinJPCRD2006(*
this);
916 format(
"critical conductivity type [%d] is invalid for fluid %s",
components[0].transport.viscosity_dilute.type,
name().c_str()));
919 throw ValueError(
"calc_conductivity_contributions invalid for mixtures");
926 switch (
components[0].transport.conductivity_residual.type) {
928 lambda_residual = TransportRoutines::conductivity_residual_polynomial(*
this);
931 lambda_residual = TransportRoutines::conductivity_residual_polynomial_and_exponential(*
this);
935 format(
"residual conductivity type [%d] is invalid for fluid %s",
components[0].transport.conductivity_residual.type,
name().c_str()));
937 return lambda_residual;
941 CoolPropDbl dilute = 0, initial_density = 0, residual = 0, critical = 0;
943 return dilute + initial_density + residual + critical;
975 TransportRoutines::conformal_state_solver(*
this, *REF,
T,
rhomolar);
979 for (
unsigned int i = 0; i <
components.size(); ++i) {
986 for (
unsigned int i = 0; i <
components.size(); ++i) {
1001 if (!
SatL)
throw ValueError(
"The saturated liquid state has not been set.");
1002 return SatL->keyed_output(key);
1006 if (!
SatV)
throw ValueError(
"The saturated vapor state has not been set.");
1007 return SatV->keyed_output(key);
1011 if (type ==
"Joule-Thomson") {
1014 }
else if (type ==
"Joule-Inversion") {
1017 }
else if (type ==
"Ideal") {
1020 }
else if (type ==
"Boyle") {
1028 std::vector<std::string> out;
1029 for (std::size_t i = 0; i <
components.size(); ++i) {
1036 throw ValueError(
format(
"For now, calc_ODP is only valid for pure and pseudo-pure fluids, %d components",
components.size()));
1047 throw ValueError(
format(
"For now, calc_GWP20 is only valid for pure and pseudo-pure fluids, %d components",
components.size()));
1058 throw ValueError(
format(
"For now, calc_GWP100 is only valid for pure and pseudo-pure fluids, %d components",
components.size()));
1069 throw ValueError(
format(
"For now, calc_GWP500 is only valid for pure and pseudo-pure fluids, %d components",
components.size()));
1081 if (critpts.size() == 1) {
1083 return critpts[0].T;
1085 throw ValueError(
format(
"critical point finding routine found %d critical points", critpts.size()));
1091 return optsuperanc.value().get_Tcrit_num();
1100 if (critpts.size() == 1) {
1102 return critpts[0].p;
1104 throw ValueError(
format(
"critical point finding routine found %d critical points", critpts.size()));
1110 return optsuperanc.value().get_pmax();
1119 if (critpts.size() == 1) {
1121 return critpts[0].rhomolar;
1123 throw ValueError(
format(
"critical point finding routine found %d critical points", critpts.size()));
1129 return optsuperanc.value().get_rhocrit_num();
1143 throw ValueError(
"calc_pmax_sat not yet defined for mixtures");
1149 double Tmax_sat =
components[0].EOS().max_sat_T.T;
1159 throw ValueError(
"calc_Tmax_sat not yet defined for mixtures");
1165 Tmin_satL =
components[0].EOS().sat_min_liquid.T;
1166 Tmin_satV =
components[0].EOS().sat_min_vapor.T;
1169 throw ValueError(
"calc_Tmin_sat not yet defined for mixtures");
1175 pmin_satL =
components[0].EOS().sat_min_liquid.p;
1176 pmin_satV =
components[0].EOS().sat_min_vapor.p;
1179 throw ValueError(
"calc_pmin_sat not yet defined for mixtures");
1188 for (
unsigned int i = 0; i <
components.size(); ++i) {
1195 for (
unsigned int i = 0; i <
components.size(); ++i) {
1202 for (
unsigned int i = 0; i <
components.size(); ++i) {
1223 auto& superanc = optsuperanc.value();
1226 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));
1228 auto rhoL = superanc.eval_sat(
T,
'D', 0);
1229 auto rhoV = superanc.eval_sat(
T,
'D', 1);
1230 auto p = superanc.eval_sat(
T,
'P', 1);
1231 SatL->update_TDmolarP_unchecked(
T, rhoL,
p);
1232 SatV->update_TDmolarP_unchecked(
T, rhoV,
p);
1250 bool optional_checks =
false;
1262 throw ValueError(
format(
"The molar density of %f mol/m3 is below the minimum of %f mol/m3",
rhomolar, rhomolar_min));
1266 throw ValueError(
format(
"The temperature of %f K is below the minimum of %f K",
T, T_min));
1278 bool optional_checks =
false;
1297 this->
_T = HEOS.
T();
1299 this->
_p = HEOS.
p();
1301 this->
_Q = HEOS.
Q();
1325 throw ValueError(
"Mole fractions must be set");
1341 std::cout <<
format(
"%s (%d): update called with (%d: (%s), %g, %g)", __FILE__, __LINE__, input_pair,
1346 CoolPropDbl ld_value1 = value1, ld_value2 = value2;
1347 pre_update(input_pair, ld_value1, ld_value2);
1351 switch (input_pair) {
1456 return mass_fractions;
1462 std::cout <<
format(
"%s (%d): update called with (%d: (%s), %g, %g)", __FILE__, __LINE__, input_pair,
1467 CoolPropDbl ld_value1 = value1, ld_value2 = value2;
1468 pre_update(input_pair, ld_value1, ld_value2);
1472 switch (input_pair) {
1505 throw ValueError(
"rhomolar is less than zero");
1508 throw ValueError(
"rhomolar is not a valid number");
1511 if (optional_checks) {
1550 saturation_called =
false;
1559 if (
_p > psat_max) {
1608 throw ValueError(
"supercritical pressure but other invalid for now");
1613 else if (
_p >=
components[0].EOS().ptriple * 0.9999 &&
_p <= psat_max) {
1620 bool definitely_two_phase =
false;
1631 if (
_T < Tm - 0.001) {
1632 throw ValueError(
format(
"For now, we don't support T [%g K] below Tmelt(p) [%g K]",
_T, Tm));
1639 if (
_T <
Tmin() - 0.001) {
1640 throw ValueError(
format(
"For now, we don't support T [%g K] below Tmin(saturation) [%g K]",
_T,
Tmin()));
1648 if (value > T_vap) {
1652 }
else if (value < T_liq) {
1676 if (value > h_vap + h_vap_error_band) {
1680 }
else if (value < h_liq - h_liq_error_band) {
1684 }
else if (value > h_liq + h_liq_error_band && value < h_vap - h_vap_error_band) {
1685 definitely_two_phase =
true;
1701 if (value > s_vap + s_vap_error_band) {
1705 }
else if (value < s_liq - s_liq_error_band) {
1709 }
else if (value > s_liq + s_liq_error_band && value < s_vap - s_vap_error_band) {
1710 definitely_two_phase =
true;
1729 CoolPropDbl u_liq_error_band = 1.5 * h_liq_error_band;
1730 CoolPropDbl u_vap_error_band = 1.5 * h_vap_error_band;
1733 if (value > u_vap + u_vap_error_band) {
1737 }
else if (value < u_liq - u_liq_error_band) {
1741 }
else if (value > u_liq + u_liq_error_band && value < u_vap - u_vap_error_band) {
1742 definitely_two_phase =
true;
1752 if (!definitely_two_phase) {
1759 if (value < rho_vap) {
1762 }
else if (value > rho_liq) {
1772 throw ValueError(
"possibly two-phase inputs not supported for mixtures for now");
1786 saturation_called =
true;
1805 Q = (1 / value - 1 / HEOS.
SatL->rhomolar()) / (1 / HEOS.
SatV->rhomolar() - 1 / HEOS.
SatL->rhomolar());
1808 Q = (value - HEOS.
SatL->smolar()) / (HEOS.
SatV->smolar() - HEOS.
SatL->smolar());
1811 Q = (value - HEOS.
SatL->hmolar()) / (HEOS.
SatV->hmolar() - HEOS.
SatL->hmolar());
1814 Q = (value - HEOS.
SatL->umolar()) / (HEOS.
SatV->umolar() - HEOS.
SatL->umolar());
1832 }
else if (Q > 1 + 1e-9) {
1845 }
else if (
_p <
components[0].EOS().ptriple * 0.9999) {
1853 throw NotImplementedError(
format(
"For now, we don't support p [%g Pa] below ptriple [%g Pa] when T [%g] is less than Tmin [%g]",
1861 throw ValueError(
format(
"The pressure [%g Pa] cannot be used in p_phase_determination",
_p));
1870 double call(
double T) {
1873 double dTdp_along_sat =
1874 HEOS->
T() * (1 / HEOS->
SatV->rhomolar() - 1 / HEOS->
SatL->rhomolar()) / (HEOS->
SatV->hmolar() - HEOS->
SatL->hmolar());
1881 Residual resid(*HEOS_copy);
1884 double v2 = resid.call(tripleV.
T);
1905 double call(
double T) {
1908 double dTdp_along_sat =
1909 HEOS->
T() * (1 / HEOS->
SatV->rhomolar() - 1 / HEOS->
SatL->rhomolar()) / (HEOS->
SatV->hmolar() - HEOS->
SatL->hmolar());
1916 Residualhmax residhmax(*HEOS_copy);
1927 throw ValueError(
format(
"value to T_phase_determination_pure_or_pseudopure is invalid"));
1931 if (_T < _crit.T && _p >
_crit.
p) {
1961 throw ValueError(
format(
"T=Tcrit; invalid input for other to T_phase_determination_pure_or_pseudopure"));
1975 if (value < p_vap) {
1979 }
else if (value > p_liq) {
1996 throw ValueError(
"Two-phase inputs not supported for pseudo-pure for now");
2009 if (value < rho_vap) {
2012 }
else if (value > rho_liq) {
2017 double Qanc = (1 / value - 1 /
static_cast<double>(
_rhoLanc))
2020 if (value > 0.95 * rho_liq || value < 1.05 * rho_vap) {
2031 }
else if (Qanc > 1.01) {
2043 throw ValueError(
format(
"The saturation properties are needed in T_phase_determination_pure_or_pseudopure"));
2052 if (value >
SatV->calc_smolar()) {
2063 if (value >
SatV->calc_hmolar()) {
2073 if (value >
SatV->calc_umolar()) {
2083 throw ValueError(
format(
"invalid input for other to T_phase_determination_pure_or_pseudopure"));
2100 if (value > HEOS.
SatL->p() * (1e-6 + 1)) {
2104 }
else if (value < HEOS.SatV->
p() * (1 - 1e-6)) {
2110 format(
"Saturation pressure [%g Pa] corresponding to T [%g K] is within 1e-4 %% of given p [%Lg Pa]", HEOS.
SatL->p(),
_T, value));
2116 Q = (1 / value - 1 / HEOS.
SatL->rhomolar()) / (1 / HEOS.
SatV->rhomolar() - 1 / HEOS.
SatL->rhomolar());
2119 Q = (value - HEOS.
SatL->smolar()) / (HEOS.
SatV->smolar() - HEOS.
SatL->smolar());
2122 Q = (value - HEOS.
SatL->hmolar()) / (HEOS.
SatV->hmolar() - HEOS.
SatL->hmolar());
2125 Q = (value - HEOS.
SatL->umolar()) / (HEOS.
SatV->umolar() - HEOS.
SatL->umolar());
2204 throw ValueError(
"supercritical temp but other invalid for now");
2213 dT_dtau = -pow(T, 2) / Tr, R = HEOS->
gas_constant(), delta = rho / rhor, tau = Tr / T;
2296 int nTau = 0, nDelta = 1;
2330 double second_deriv(
double rhomolar) {
2332 return R_u *
T /
POW2(rhor)
2336 dpdrho_resid resid(
this,
T,
p);
2340 light =
Halley(resid, 1e-6, 1e-8, 100);
2341 double d2pdrho2__constT = resid.deriv(light);
2342 if (d2pdrho2__constT > 0) {
2346 }
catch (std::exception& e) {
2348 std::cout << e.what() << std::endl;
2357 for (std::size_t counter = 0; counter <= 100; counter++) {
2359 double curvature = resid.deriv(rho);
2360 if (curvature > 0) {
2371 for (
double omega = 0.7; omega > 0; omega -= 0.2) {
2373 resid.options.add_number(
"omega", omega);
2374 heavy =
Halley(resid, rhomax, 1e-8, 100);
2375 double d2pdrho2__constT = resid.deriv(heavy);
2376 if (d2pdrho2__constT < 0) {
2381 }
catch (std::exception& e) {
2383 std::cout << e.what() << std::endl;
2392 double rho = rhomax;
2393 for (std::size_t counter = 0; counter <= 100; counter++) {
2395 double curvature = resid.deriv(rho);
2396 if (curvature < 0 || this->
p() < 0) {
2406 if (light > 0 && heavy > 0) {
2413 else if (light < 0 && heavy < 0) {
2414 double dpdrho_min = resid.call(1e-10);
2415 double dpdrho_max = resid.call(rhomax);
2416 if (dpdrho_max * dpdrho_min > 0) {
2437 rhor(
HEOS->get_reducing_state().rhomolar),
2444 return (peos -
p) /
p;
2489 double rho_liq = -1, rho_vap = -1;
2490 if (
p > p_at_rhomax_stationary) {
2492 for (; counter <= 10; counter++) {
2495 if (p_at_rhomax <
p) {
2496 rhomolar_max *= 1.05;
2505 if (
p < p_at_rhomin_stationary) {
2510 if (rho_vap > 0 && rho_liq > 0) {
2512 if (std::abs(rho_vap - rho_liq) < 1e-10) {
2519 if (gibbsmolar_liq < gibbsmolar_vap) {
2525 }
else if (rho_vap < 0 && rho_liq > 0) {
2528 }
else if (rho_vap > 0 && rho_liq < 0) {
2553 if (rhomolar_guess < 0)
2560 if (rhomolar_guess < 0 || !
ValidNumber(rhomolar_guess))
2574 throw ValueError(
"Liquid density is invalid");
2576 }
catch (std::exception&) {
2607 if (dpdrho < 0 || d2pdrho2 < 0) {
2615 if (dpdrho < 0 || d2pdrho2 > 0) {
2622 }
catch (std::exception& e) {
2636 throw ValueError(
format(
"solver_rho_Tp was unable to find a solution for T=%10Lg, p=%10Lg, with guess value %10Lg with error: %s",
T,
p,
2637 rhomolar_guess, e.what()));
2643 for (std::size_t i = 0; i <
components.size(); ++i) {
2645 CoolPropDbl m_i = 0.480 + 1.574 * acentric_i - 0.176 * pow(acentric_i, 2);
2649 CoolPropDbl a_i = 0.42747 * pow(R_u * Tci, 2) / pci * pow(1 + m_i * (1 - sqrt(
T / Tci)), 2);
2651 for (std::size_t j = 0; j <
components.size(); ++j) {
2653 CoolPropDbl m_j = 0.480 + 1.574 * acentric_j - 0.176 * pow(acentric_j, 2);
2655 CoolPropDbl a_j = 0.42747 * pow(R_u * Tcj, 2) / pcj * pow(1 + m_j * (1 - sqrt(
T / Tcj)), 2);
2675 solve_cubic(1, -1, A - B - B * B, -A * B, Nsolns, Z0, Z1, Z2);
2686 if (rhomolar0 > 0 && rhomolar1 <= 0 && rhomolar2 <= 0) {
2689 if (rhomolar0 <= 0 && rhomolar1 > 0 && rhomolar2 <= 0) {
2692 if (rhomolar0 <= 0 && rhomolar1 <= 0 && rhomolar2 > 0) {
2707 throw ValueError(
"Bad phase to solver_rho_Tp_SRK");
2745 return R_u *
T * (1 +
tau * (da0_dTau + dar_dTau) +
delta * dar_dDelta);
2749 std::cout <<
format(
"HelmholtzEOSMixtureBackend::calc_hmolar: 2phase: %d T: %g rhomomolar: %g",
isTwoPhase(),
_T,
_rhomolar) << std::endl;
2751 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
2793 return R_u * (
tau * (da0_dTau + dar_dTau) - a0 - ar);
2797 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
2819 _smolar = R_u * (
_tau.
pt() * (da0_dTau + dar_dTau) - a0 - ar);
2837 return R_u *
T *
tau * (da0_dTau + dar_dTau);
2841 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
2881 return static_cast<double>(
_cvmolar);
2898 * (-pow(
_tau.
pt(), 2) * (d2ar_dTau2 + d2a0_dTau2)
2900 / (1 + 2 *
_delta.
pt() * dar_dDelta + pow(
_delta.
pt(), 2) * d2ar_dDelta2));
2902 return static_cast<double>(
_cpmolar);
2914 return R_u * (1 + (-pow(
_tau.
pt(), 2)) * d2a0_dTau2);
2919 return SatL->speed_sound();
2921 return SatV->speed_sound();
2923 throw ValueError(
format(
"Speed of sound is not defined for two-phase states because it depends on the distribution of phases."));
2943 - pow(1 +
_delta.
pt() * dar_dDelta -
_delta.
pt() *
_tau.
pt() * d2ar_dDelta_dTau, 2) / (pow(
_tau.
pt(), 2) * (d2ar_dTau2 + d2a0_dTau2))));
2966 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
2992 for (std::size_t i = 0; i <
components.size(); ++i) {
3008 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
3042 double dna0_dni__const_T_V_nj =
3044 return gas_constant() *
T() * (dna0_dni__const_T_V_nj + dnar_dni__const_T_V_nj);
3065 throw ValueError(
"Mole fractions must be set before calling calc_reducing_state");
3074 bool cache_values =
true;
3095 bool cache_values =
false;
3097 return derivs.
get(nTau, nDelta);
3104 throw ValueError(
"No alpha0 derivatives are available");
3115 double taustar = Tc / Tr *
tau, deltastar = rhor / rhomolarc *
delta;
3116 if (nTau == 0 && nDelta == 0) {
3117 val = E.
base0(taustar, deltastar);
3118 }
else if (nTau == 0 && nDelta == 1) {
3120 }
else if (nTau == 1 && nDelta == 0) {
3122 }
else if (nTau == 0 && nDelta == 2) {
3124 }
else if (nTau == 1 && nDelta == 1) {
3126 }
else if (nTau == 2 && nDelta == 0) {
3128 }
else if (nTau == 0 && nDelta == 3) {
3130 }
else if (nTau == 1 && nDelta == 2) {
3132 }
else if (nTau == 2 && nDelta == 1) {
3134 }
else if (nTau == 3 && nDelta == 0) {
3139 val *= pow(rhor / rhomolarc, nDelta);
3140 val /= pow(Tr / Tc, nTau);
3143 throw ValueError(
format(
"calc_alpha0_deriv_nocache returned invalid number with inputs nTau: %d, nDelta: %d, tau: %Lg, delta: %Lg", nTau,
3154 for (
unsigned int i = 0; i <
N; ++i) {
3159 tau_i = T_ci *
tau / Tr;
3160 delta_i =
delta * rhor / rho_ci;
3166 if (nTau == 0 && nDelta == 0) {
3169 }
else if (nTau == 0 && nDelta == 1) {
3171 }
else if (nTau == 1 && nDelta == 0) {
3173 }
else if (nTau == 0 && nDelta == 2) {
3174 summer +=
mole_fractions[i] * Rratio * pow(rhor / rho_ci, 2) *
components[i].EOS().d2alpha0_dDelta2(tau_i, delta_i);
3175 }
else if (nTau == 1 && nDelta == 1) {
3176 summer +=
mole_fractions[i] * Rratio * rhor / rho_ci * T_ci / Tr *
components[i].EOS().d2alpha0_dDelta_dTau(tau_i, delta_i);
3177 }
else if (nTau == 2 && nDelta == 0) {
3249 const int nTau = 0, nDelta = 0;
3253 const int nTau = 0, nDelta = 1;
3257 const int nTau = 1, nDelta = 0;
3261 const int nTau = 0, nDelta = 2;
3265 const int nTau = 1, nDelta = 1;
3269 const int nTau = 2, nDelta = 0;
3273 const int nTau = 0, nDelta = 3;
3277 const int nTau = 1, nDelta = 2;
3281 const int nTau = 2, nDelta = 1;
3285 const int nTau = 3, nDelta = 0;
3294 if (Of1 ==
iT && Wrt1 ==
iP) {
3296 }
else if (Of1 ==
iP && Wrt1 ==
iT) {
3297 return 1 / dTdP_sat;
3300 else if (Wrt1 ==
iT) {
3304 else if (Wrt1 ==
iP) {
3312 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_first_saturation_deriv"));
3318 if (Of1 ==
iT && Wrt1 ==
iP) {
3320 }
else if (Of1 ==
iP && Wrt1 ==
iT) {
3321 return 1 / dTdP_sat;
3324 else if (Wrt1 ==
iT) {
3328 else if (Wrt1 ==
iP) {
3336 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_second_saturation_deriv"));
3337 if (Wrt1 ==
iP && Wrt2 ==
iP) {
3352 CoolPropDbl ddT_dTdp_along_sat_constp = (DELTAh * (
_T * dDELTAv_dT_constp + DELTAv) -
_T * DELTAv * dDELTAh_dT_constp) /
POW2(DELTAh);
3353 CoolPropDbl ddp_dTdp_along_sat_constT = (DELTAh * (
_T * dDELTAv_dp_constT) -
_T * DELTAv * dDELTAh_dp_constT) /
POW2(DELTAh);
3355 double ddp_dydpsigma = d2ydp2_constT + dydT_constp * ddp_dTdp_along_sat_constT + d2ydTdp * dTdp_along_sat;
3356 double ddT_dydpsigma = d2ydTdp + dydT_constp * ddT_dTdp_along_sat_constp + d2ydT2_constp * dTdp_along_sat;
3357 return ddp_dydpsigma + ddT_dydpsigma * dTdp_along_sat;
3359 throw ValueError(
format(
"Currently, only possible to take second saturation derivative w.r.t. P (both times)"));
3365 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_second_two_phase_deriv"));
3380 CoolPropDbl numerator = 1 /
SatV->keyed_output(rho_key) - 1 /
SatL->keyed_output(rho_key);
3382 CoolPropDbl dnumerator = -1 /
POW2(
SatV->keyed_output(rho_key)) * drhoV_dp_sat + 1 /
POW2(
SatL->keyed_output(rho_key)) * drhoL_dp_sat;
3383 CoolPropDbl ddenominator = dhV_dp_sat - dhL_dp_sat;
3384 CoolPropDbl d_dvdh_dp__consth = (denominator * dnumerator - numerator * ddenominator) /
POW2(denominator);
3385 return -
POW2(
rhomolar()) * d_dvdh_dp__consth + dv_dh_constp * (-2 *
rhomolar()) * drhomolar_dp__consth;
3387 && ((Wrt1 ==
iHmass && Constant1 ==
iP && Wrt2 ==
iP && Constant2 ==
iHmass)
3388 || (Wrt2 ==
iHmass && Constant2 ==
iP && Wrt1 ==
iP && Constant1 ==
iHmass))) {
3400 CoolPropDbl numerator = 1 /
SatV->keyed_output(rho_key) - 1 /
SatL->keyed_output(rho_key);
3402 CoolPropDbl dnumerator = -1 /
POW2(
SatV->keyed_output(rho_key)) * drhoV_dp_sat + 1 /
POW2(
SatL->keyed_output(rho_key)) * drhoL_dp_sat;
3403 CoolPropDbl ddenominator = dhV_dp_sat - dhL_dp_sat;
3404 CoolPropDbl d_dvdh_dp__consth = (denominator * dnumerator - numerator * ddenominator) /
POW2(denominator);
3405 return -
POW2(rho) * d_dvdh_dp__consth + dv_dh_constp * (-2 * rho) * drho_dp__consth;
3411 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_first_two_phase_deriv"));
3425 CoolPropDbl dvdp_h = dvL_dp + dxdp_h * (1 /
SatV->rhomolar() - 1 /
SatL->rhomolar()) +
Q() * (dvV_dp - dvL_dp);
3436 CoolPropDbl dvdp_h = dvL_dp + dxdp_h * (1 /
SatV->rhomass() - 1 /
SatL->rhomass()) +
Q() * (dvV_dp - dvL_dp);
3439 throw ValueError(
"These inputs are not supported to calc_first_two_phase_deriv");
3446 bool drho_dh__p =
false;
3447 bool drho_dp__h =
false;
3448 bool rho_spline =
false;
3468 throw ValueError(
"These inputs are not supported to calc_first_two_phase_deriv");
3471 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_first_two_phase_deriv_splined"));
3484 Liq->update_DmolarT_direct(
SatL->rhomolar(),
SatL->T());
3499 CoolPropDbl Abracket = (2 * rho_liq - 2 * rho_end + Delta_end * (drho_dh_liq__constp + drho_dh_end));
3501 CoolPropDbl b = 3 /
POW2(Delta_end) * (-rho_liq + rho_end) - 1 / Delta_end * (drho_dh_end + 2 * drho_dh_liq__constp);
3535 CoolPropDbl d_Delta_end_dp__consth = x_end * (dhV_dp_sat - dhL_dp_sat);
3539 CoolPropDbl d_Abracket_dp_consth = (2 * drhoL_dp_sat - 2 * drho_dp_end + Delta_end * (d2rhodhdp_liq + d2rhodhdp_end)
3540 + d_Delta_end_dp__consth * (drho_dh_liq__constp + drho_dh_end));
3541 CoolPropDbl da_dp = 1 /
POW3(Delta_end) * d_Abracket_dp_consth + Abracket * (-3 /
POW4(Delta_end) * d_Delta_end_dp__consth);
3542 CoolPropDbl db_dp = -6 /
POW3(Delta_end) * d_Delta_end_dp__consth * (rho_end - rho_liq) + 3 /
POW2(Delta_end) * (drho_dp_end - drhoL_dp_sat)
3543 + (1 /
POW2(Delta_end) * d_Delta_end_dp__consth) * (drho_dh_end + 2 * drho_dh_liq__constp)
3544 - (1 / Delta_end) * (d2rhodhdp_end + 2 * d2rhodhdp_liq);
3549 (3 * a *
POW2(Delta) + 2 * b * Delta + c) * d_Delta_dp__consth +
POW3(Delta) * da_dp +
POW2(Delta) * db_dp + Delta * dc_dp + dd_dp;
3552 throw ValueError(
"Something went wrong in HelmholtzEOSMixtureBackend::calc_first_two_phase_deriv_splined");
3562 Eigen::MatrixXd Lstar, Mstar;
3564 std::vector<double> call(
const std::vector<double>& tau_delta) {
3570 std::vector<double> o(2);
3571 o[0] = Lstar.determinant();
3572 o[1] = Mstar.determinant();
3575 std::vector<std::vector<double>> Jacobian(
const std::vector<double>& x) {
3576 std::size_t
N = x.size();
3577 std::vector<std::vector<double>> J(
N, std::vector<double>(
N, 0));
3583 J[0][0] = (adjL * dLdTau).trace();
3584 J[0][1] = (adjL * dLdDelta).trace();
3585 J[1][0] = (adjM * dMdTau).trace();
3586 J[1][1] = (adjM * dMdDelta).trace();
3590 std::vector<std::vector<double>> numerical_Jacobian(
const std::vector<double>& x) {
3591 std::size_t
N = x.size();
3592 std::vector<double> rplus, rminus, xp;
3593 std::vector<std::vector<double>> J(
N, std::vector<double>(
N, 0));
3595 double epsilon = 0.0001;
3598 for (std::size_t i = 0; i <
N; ++i) {
3602 xp[i] -= 2 * epsilon;
3605 for (std::size_t j = 0; j <
N; ++j) {
3606 J[j][i] = (rplus[j] - rminus[j]) / (2 * epsilon);
3609 std::cout << J[0][0] <<
" " << J[0][1] << std::endl;
3610 std::cout << J[1][0] <<
" " << J[1][1] << std::endl;
3615 std::vector<double> x, tau_delta(2);
3660 _deriv = (adjL * dLdTau).trace();
3667 dadjLstardTau = adjugate_derivative(Lstar, dLstardTau);
3668 _second_deriv = (dLstardTau * dadjLstardTau + adjL * d2LstardTau2).trace();
3708 tau_new =
tau +
R_tau * cos(theta);
3716 double tau_new, delta_new;
3724 double L1 =
Lstar.determinant();
3733 return -
R_tau * sin(theta) * dL1_dtau +
R_delta * cos(theta) * dL1_ddelta;
3739 for (
int i = 0; i < 300; ++i) {
3781 double p_MPa =
HEOS.
p() / 1e6;
3784 double tau_new, delta_new;
3807 this->tau = tau_new;
3808 this->delta = delta_new;
3812 this->spinodal_values.
tau.push_back(tau_new);
3813 this->spinodal_values.
delta.push_back(delta_new);
3814 this->spinodal_values.
M1.push_back(M1);
3822 L1star = Lstar.determinant();
3823 M1star = Mstar.determinant();
3834 rapidjson::Document doc;
3836 rapidjson::Value& val = doc;
3837 std::vector<std::vector<DepartureFunctionPointer>>&
mat =
critical_state->residual_helmholtz->Excess.DepartureFunctionMatrix;
3838 if (
mat.size() > 0) {
3839 mat[0][1]->phi.to_json(val, doc);
3848 double delta0 = _HUGE, tau0 = _HUGE;
3849 critical_state->get_critical_point_starting_values(delta0, tau0);
3856 resid_L0.
call(tau0);
3858 while (resid_L0.
deriv(tau0) > 0 && bump_count < 3) {
3862 double tau_L0 =
Halley(resid_L0, tau0, 1e-10, 100);
3869 double R_delta = 0, R_tau = 0;
3881 const double rhomolar_guess) {
3884 if (w.size() != z.size()) {
3885 throw ValueError(
format(
"Trial composition vector size [%d] is not the same as bulk composition [%d]", w.size(), z.size()));
3894 for (std::size_t i = 0; i < w.size(); ++i) {
3903 bool find_critical_points =
false;
3908 for (std::size_t i = 0; i <
components.size(); ++i) {
3910 if (!reference_state.compare(
"IIR")) {
3911 if (HEOS.
Ttriple() > 273.15) {
3912 throw ValueError(
format(
"Cannot use IIR reference state; Ttriple [%Lg] is greater than 273.15 K", HEOS.
Ttriple()));
3917 double deltah = HEOS.
hmass() - 200000;
3918 double deltas = HEOS.
smass() - 1000;
3924 std::cout <<
format(
"set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
3926 }
else if (!reference_state.compare(
"ASHRAE")) {
3927 if (HEOS.
Ttriple() > 233.15) {
3928 throw ValueError(
format(
"Cannot use ASHRAE reference state; Ttriple [%Lg] is greater than than 233.15 K", HEOS.
Ttriple()));
3933 double deltah = HEOS.
hmass() - 0;
3934 double deltas = HEOS.
smass() - 0;
3940 std::cout <<
format(
"set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
3942 }
else if (!reference_state.compare(
"NBP")) {
3944 throw ValueError(
format(
"Cannot use NBP reference state; p_triple [%Lg Pa] is greater than than 101325 Pa", HEOS.
p_triple()));
3949 double deltah = HEOS.
hmass() - 0;
3950 double deltas = HEOS.
smass() - 0;
3956 std::cout <<
format(
"set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
3958 }
else if (!reference_state.compare(
"DEF")) {
3960 }
else if (!reference_state.compare(
"RESET")) {
3963 throw ValueError(
format(
"reference state string is invalid: [%s]", reference_state.c_str()));
3974 for (std::size_t i = 0; i <
components.size(); ++i) {
3980 double deltah = HEOS.
hmolar() - hmolar0;
3981 double deltas = HEOS.
smolar() - smolar0;
3989 const std::string& ref) {
3999 double f = (HEOS->name() ==
"Water" || HEOS->name() ==
"CarbonDioxide") ? 1.00001 : 1.0;
4021 if (!HEOS->is_pure()) {