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(
"aliases_bar")) {
207 }
else if (!ParamName.compare(
"CAS") || !ParamName.compare(
"CAS_number")) {
209 }
else if (!ParamName.compare(
"formula")) {
211 }
else if (!ParamName.compare(
"ASHRAE34")) {
213 }
else if (!ParamName.compare(
"REFPROPName") || !ParamName.compare(
"REFPROP_name") || !ParamName.compare(
"REFPROPname")) {
215 }
else if (ParamName.find(
"BibTeX") == 0)
217 std::vector<std::string> parts =
strsplit(ParamName,
'-');
218 if (parts.size() != 2) {
219 throw ValueError(
format(
"Unable to parse BibTeX string %s", ParamName.c_str()));
221 std::string key = parts[1];
222 if (!key.compare(
"EOS")) {
224 }
else if (!key.compare(
"CP0")) {
226 }
else if (!key.compare(
"VISCOSITY")) {
228 }
else if (!key.compare(
"CONDUCTIVITY")) {
230 }
else if (!key.compare(
"ECS_LENNARD_JONES")) {
232 }
else if (!key.compare(
"ECS_VISCOSITY_FITS")) {
234 }
else if (!key.compare(
"ECS_CONDUCTIVITY_FITS")) {
236 }
else if (!key.compare(
"SURFACE_TENSION")) {
238 }
else if (!key.compare(
"MELTING_LINE")) {
243 }
else if (ParamName.find(
"pure") == 0) {
249 }
else if (ParamName ==
"INCHI" || ParamName ==
"InChI" || ParamName ==
"INCHI_STRING") {
250 return cpfluid.
InChI;
251 }
else if (ParamName ==
"INCHI_Key" || ParamName ==
"InChIKey" || ParamName ==
"INCHIKEY") {
253 }
else if (ParamName ==
"2DPNG_URL") {
255 }
else if (ParamName ==
"SMILES" || ParamName ==
"smiles") {
257 }
else if (ParamName ==
"CHEMSPIDER_ID") {
259 }
else if (ParamName ==
"JSON") {
262 throw ValueError(
format(
"fluid parameter [%s] is invalid", ParamName.c_str()));
270 return components[0].EOS().get_superanc_optional();
275 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N-1));
278 if (parameter.find(
"SUPERANC::") == 0){
279 auto& superanc = optsuperanc.value();
281 std::string key = parameter.substr(10);
283 return superanc.get_pmax();
285 else if (key ==
"pmin"){
286 return superanc.get_pmin();
288 else if (key ==
"Tmin"){
289 return superanc.get_Tmin();
291 else if (key ==
"Tcrit_num"){
292 return superanc.get_Tcrit_num();
295 throw ValueError(
format(
"Superancillary parameter [%s] is invalid", key.c_str()));
302 throw ValueError(
format(
"fluid parameter [%s] is invalid", parameter.c_str()));
309 if (i < 0 || i >=
N) {
310 if (j < 0 || j >=
N) {
311 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j,
N-1));
313 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N-1));
315 }
else if (j < 0 || j >=
N) {
316 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j,
N-1));
318 if (model ==
"linear") {
320 double gammaT = 0.5 * (Tc1 + Tc2) / sqrt(Tc1 * Tc2);
322 double gammaV = 4.0 * (1 / rhoc1 + 1 / rhoc2) / pow(pow(rhoc1, -1.0 / 3.0) + pow(rhoc2, -1.0 / 3.0), 3);
327 }
else if (model ==
"Lorentz-Berthelot") {
333 throw ValueError(
format(
"mixing rule [%s] is not understood", model.c_str()));
338 const double value) {
340 if (i < 0 || i >=
N) {
341 if (j < 0 || j >=
N) {
342 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j,
N-1));
344 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N-1));
346 }
else if (j < 0 || j >=
N) {
347 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j,
N-1));
349 if (parameter ==
"Fij") {
353 Reducing->set_binary_interaction_double(i, j, parameter, value);
356 for (std::vector<shared_ptr<HelmholtzEOSMixtureBackend>>::iterator it =
linked_states.begin(); it !=
linked_states.end(); ++it) {
357 it->get()->set_binary_interaction_double(i, j, parameter, value);
363 if (i < 0 || i >=
N) {
364 if (j < 0 || j >=
N) {
365 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j,
N-1));
367 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N-1));
369 }
else if (j < 0 || j >=
N) {
370 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j,
N-1));
372 if (parameter ==
"Fij") {
375 return Reducing->get_binary_interaction_double(i, j, parameter);
384 const std::string& value) {
386 if (i < 0 || i >=
N) {
387 if (j < 0 || j >=
N) {
388 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j,
N-1));
390 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N-1));
392 }
else if (j < 0 || j >=
N) {
393 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j,
N-1));
395 if (parameter ==
"function") {
399 throw ValueError(
format(
"Cannot process this string parameter [%s] in set_binary_interaction_string", parameter.c_str()));
402 for (std::vector<shared_ptr<HelmholtzEOSMixtureBackend>>::iterator it =
linked_states.begin(); it !=
linked_states.end(); ++it) {
403 it->get()->set_binary_interaction_string(i, j, parameter, value);
413 if (EOS_name ==
"SRK" || EOS_name ==
"Peng-Robinson") {
425 shared_ptr<AbstractCubic> ac;
426 if (EOS_name ==
"SRK") {
427 ac.reset(
new SRK(Tc, pc, acentric, R));
432 ac->set_rhor(rhomolarc);
434 }
else if (EOS_name ==
"XiangDeiters") {
452 if (this->
SatL)
SatL->change_EOS(i, EOS_name);
453 if (this->
SatV)
SatV->change_EOS(i, EOS_name);
489 if (!state.compare(
"hs_anchor")) {
491 }
else if (!state.compare(
"max_sat_T")) {
493 }
else if (!state.compare(
"max_sat_p")) {
495 }
else if (!state.compare(
"reducing")) {
497 }
else if (!state.compare(
"critical")) {
499 }
else if (!state.compare(
"triple_liquid")) {
501 }
else if (!state.compare(
"triple_vapor")) {
504 throw ValueError(
format(
"This state [%s] is invalid to calc_state", state.c_str()));
507 if (!state.compare(
"critical")) {
518 throw ValueError(
"acentric factor cannot be calculated for mixtures");
530 for (
unsigned int i = 0; i <
components.size(); ++i) {
539 for (
unsigned int i = 0; i <
components.size(); ++i) {
546 if (param ==
iP && given ==
iT) {
550 return components[0].ancillaries.pL.evaluate(value);
552 return components[0].ancillaries.pV.evaluate(value);
554 }
else if (param ==
iT && given ==
iP) {
558 return components[0].ancillaries.pL.invert(value);
560 return components[0].ancillaries.pV.invert(value);
562 }
else if (param ==
iDmolar && given ==
iT) {
566 return components[0].ancillaries.rhoL.evaluate(value);
568 return components[0].ancillaries.rhoV.evaluate(value);
570 }
else if (param ==
iT && given ==
iDmolar) {
574 return components[0].ancillaries.rhoL.invert(value);
576 return components[0].ancillaries.rhoV.invert(value);
579 return components[0].ancillaries.surface_tension.evaluate(value);
593 return components[0].ancillaries.melting_line.evaluate(param, given, value);
601 return components[0].ancillaries.surface_tension.evaluate(
T());
603 throw ValueError(
format(
"surface tension is only defined within the two-phase region; Try PQ or QT inputs"));
612 switch (
components[0].transport.viscosity_dilute.type) {
639 format(
"dilute viscosity type [%d] is invalid for fluid %s",
components[0].transport.viscosity_dilute.type,
name().c_str()));
652 switch (
components[0].transport.viscosity_initial.type) {
656 initial_density = eta_dilute * B_eta_initial * rho;
669 switch (
components[0].transport.viscosity_higher_order.type) {
674 residual = TransportRoutines::viscosity_higher_order_friction_theory(*
this);
699 format(
"higher order viscosity type [%d] is invalid for fluid %s",
components[0].transport.viscosity_dilute.type,
name().c_str()));
702 return initial_density + residual;
707 CoolPropDbl dilute = 0, initial_density = 0, residual = 0, critical = 0;
709 return dilute + initial_density + residual + critical;
734 throw ValueError(
format(
"Viscosity model is not available for this fluid"));
741 std::vector<std::string> names(1, fluid_name);
745 critical = TransportRoutines::viscosity_ECS(*
this, *ref_fluid);
752 critical = TransportRoutines::viscosity_Chung(*
this);
759 critical = TransportRoutines::viscosity_rhosr(*
this);
810 throw ValueError(
"calc_viscosity_contributions invalid for mixtures");
826 throw ValueError(
format(
"Thermal conductivity model is not available for this fluid"));
833 std::vector<std::string>
name(1, fluid_name);
837 initial_density = TransportRoutines::conductivity_ECS(*
this, *ref_fluid);
846 initial_density = TransportRoutines::conductivity_hardcoded_water(*
this);
849 initial_density = TransportRoutines::conductivity_hardcoded_heavywater(*
this);
852 initial_density = TransportRoutines::conductivity_hardcoded_R23(*
this);
855 initial_density = TransportRoutines::conductivity_hardcoded_helium(*
this);
858 initial_density = TransportRoutines::conductivity_hardcoded_methane(*
this);
861 throw ValueError(
format(
"hardcoded conductivity type [%d] is invalid for fluid %s",
874 dilute = TransportRoutines::conductivity_dilute_ratio_polynomials(*
this);
877 dilute = TransportRoutines::conductivity_dilute_eta0_and_poly(*
this);
880 dilute = TransportRoutines::conductivity_dilute_hardcoded_CO2(*
this);
883 dilute = TransportRoutines::conductivity_dilute_hardcoded_CO2_HuberJPCRD2016(*
this);
886 dilute = TransportRoutines::conductivity_dilute_hardcoded_ethane(*
this);
893 format(
"dilute conductivity type [%d] is invalid for fluid %s",
components[0].transport.conductivity_dilute.type,
name().c_str()));
902 critical = TransportRoutines::conductivity_critical_simplified_Olchowy_Sengers(*
this);
905 critical = TransportRoutines::conductivity_critical_hardcoded_R123(*
this);
908 critical = TransportRoutines::conductivity_critical_hardcoded_ammonia(*
this);
914 critical = TransportRoutines::conductivity_critical_hardcoded_CO2_ScalabrinJPCRD2006(*
this);
918 format(
"critical conductivity type [%d] is invalid for fluid %s",
components[0].transport.viscosity_dilute.type,
name().c_str()));
921 throw ValueError(
"calc_conductivity_contributions invalid for mixtures");
928 switch (
components[0].transport.conductivity_residual.type) {
930 lambda_residual = TransportRoutines::conductivity_residual_polynomial(*
this);
933 lambda_residual = TransportRoutines::conductivity_residual_polynomial_and_exponential(*
this);
937 format(
"residual conductivity type [%d] is invalid for fluid %s",
components[0].transport.conductivity_residual.type,
name().c_str()));
939 return lambda_residual;
943 CoolPropDbl dilute = 0, initial_density = 0, residual = 0, critical = 0;
945 return dilute + initial_density + residual + critical;
977 TransportRoutines::conformal_state_solver(*
this, *REF,
T,
rhomolar);
981 for (
unsigned int i = 0; i <
components.size(); ++i) {
988 for (
unsigned int i = 0; i <
components.size(); ++i) {
1003 if (!
SatL)
throw ValueError(
"The saturated liquid state has not been set.");
1004 return SatL->keyed_output(key);
1008 if (!
SatV)
throw ValueError(
"The saturated vapor state has not been set.");
1009 return SatV->keyed_output(key);
1013 if (type ==
"Joule-Thomson") {
1016 }
else if (type ==
"Joule-Inversion") {
1019 }
else if (type ==
"Ideal") {
1022 }
else if (type ==
"Boyle") {
1030 std::vector<std::string> out;
1031 for (std::size_t i = 0; i <
components.size(); ++i) {
1038 throw ValueError(
format(
"For now, calc_ODP is only valid for pure and pseudo-pure fluids, %d components",
components.size()));
1049 throw ValueError(
format(
"For now, calc_GWP20 is only valid for pure and pseudo-pure fluids, %d components",
components.size()));
1060 throw ValueError(
format(
"For now, calc_GWP100 is only valid for pure and pseudo-pure fluids, %d components",
components.size()));
1071 throw ValueError(
format(
"For now, calc_GWP500 is only valid for pure and pseudo-pure fluids, %d components",
components.size()));
1083 if (critpts.size() == 1) {
1085 return critpts[0].T;
1087 throw ValueError(
format(
"critical point finding routine found %d critical points", critpts.size()));
1093 return optsuperanc.value().get_Tcrit_num();
1102 if (critpts.size() == 1) {
1104 return critpts[0].p;
1106 throw ValueError(
format(
"critical point finding routine found %d critical points", critpts.size()));
1112 return optsuperanc.value().get_pmax();
1121 if (critpts.size() == 1) {
1123 return critpts[0].rhomolar;
1125 throw ValueError(
format(
"critical point finding routine found %d critical points", critpts.size()));
1131 return optsuperanc.value().get_rhocrit_num();
1145 throw ValueError(
"calc_pmax_sat not yet defined for mixtures");
1151 double Tmax_sat =
components[0].EOS().max_sat_T.T;
1161 throw ValueError(
"calc_Tmax_sat not yet defined for mixtures");
1167 Tmin_satL =
components[0].EOS().sat_min_liquid.T;
1168 Tmin_satV =
components[0].EOS().sat_min_vapor.T;
1171 throw ValueError(
"calc_Tmin_sat not yet defined for mixtures");
1177 pmin_satL =
components[0].EOS().sat_min_liquid.p;
1178 pmin_satV =
components[0].EOS().sat_min_vapor.p;
1181 throw ValueError(
"calc_pmin_sat not yet defined for mixtures");
1190 for (
unsigned int i = 0; i <
components.size(); ++i) {
1197 for (
unsigned int i = 0; i <
components.size(); ++i) {
1204 for (
unsigned int i = 0; i <
components.size(); ++i) {
1225 auto& superanc = optsuperanc.value();
1228 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));
1230 auto rhoL = superanc.eval_sat(
T,
'D', 0);
1231 auto rhoV = superanc.eval_sat(
T,
'D', 1);
1232 auto p = superanc.eval_sat(
T,
'P', 1);
1233 SatL->update_TDmolarP_unchecked(
T, rhoL,
p);
1234 SatV->update_TDmolarP_unchecked(
T, rhoV,
p);
1252 bool optional_checks =
false;
1264 throw ValueError(
format(
"The molar density of %f mol/m3 is below the minimum of %f mol/m3",
rhomolar, rhomolar_min));
1268 throw ValueError(
format(
"The temperature of %f K is below the minimum of %f K",
T, T_min));
1280 bool optional_checks =
false;
1299 this->
_T = HEOS.
T();
1301 this->
_p = HEOS.
p();
1303 this->
_Q = HEOS.
Q();
1327 throw ValueError(
"Mole fractions must be set");
1343 std::cout <<
format(
"%s (%d): update called with (%d: (%s), %g, %g)", __FILE__, __LINE__, input_pair,
1348 CoolPropDbl ld_value1 = value1, ld_value2 = value2;
1349 pre_update(input_pair, ld_value1, ld_value2);
1353 switch (input_pair) {
1458 return mass_fractions;
1464 std::cout <<
format(
"%s (%d): update called with (%d: (%s), %g, %g)", __FILE__, __LINE__, input_pair,
1469 CoolPropDbl ld_value1 = value1, ld_value2 = value2;
1470 pre_update(input_pair, ld_value1, ld_value2);
1474 switch (input_pair) {
1507 throw ValueError(
"rhomolar is less than zero");
1510 throw ValueError(
"rhomolar is not a valid number");
1513 if (optional_checks) {
1552 saturation_called =
false;
1561 auto smolar_critical = [
this, &T_crit_, &rhomolar_crit_](){
1564 auto hmolar_critical = [
this, &T_crit_, &rhomolar_crit_](){
1567 auto umolar_critical = [
this, &T_crit_, &rhomolar_crit_](){
1572 if (
_p > psat_max) {
1621 throw ValueError(
"supercritical pressure but other invalid for now");
1626 else if (
_p >=
components[0].EOS().ptriple * 0.9999 &&
_p <= psat_max) {
1633 auto& superanc = optsuperanc.value();
1636 throw ValueError(
format(
"Pressure to PQ_flash [%0.8Lg Pa] may not be above the numerical critical point of %0.15Lg Pa",
_p, pmax_num));
1638 auto Tsat = superanc.get_T_from_p(
_p);
1639 auto rhoL = superanc.eval_sat(Tsat,
'D', 0);
1640 auto rhoV = superanc.eval_sat(Tsat,
'D', 1);
1656 SatL->update_TDmolarP_unchecked(Tsat, rhoL, psat);
1657 SatV->update_TDmolarP_unchecked(Tsat, rhoV, psat);
1661 Q = (1 / value - 1 /
SatL->rhomolar()) / (1 /
SatV->rhomolar() - 1 /
SatL->rhomolar());
1664 Q = (value -
SatL->smolar()) / (
SatV->smolar() -
SatL->smolar());
1667 Q = (value -
SatL->hmolar()) / (
SatV->hmolar() -
SatL->hmolar());
1670 Q = (value -
SatL->umolar()) / (
SatV->umolar() -
SatL->umolar());
1682 }
else if (
Q > 1 + 1e-9) {
1704 bool definitely_two_phase =
false;
1715 if (
_T < Tm - 0.001) {
1716 throw ValueError(
format(
"For now, we don't support T [%g K] below Tmelt(p) [%g K]",
_T, Tm));
1723 if (
_T <
Tmin() - 0.001) {
1724 throw ValueError(
format(
"For now, we don't support T [%g K] below Tmin(saturation) [%g K]",
_T,
Tmin()));
1732 if (value > T_vap) {
1736 }
else if (value < T_liq) {
1760 if (value > h_vap + h_vap_error_band) {
1764 }
else if (value < h_liq - h_liq_error_band) {
1768 }
else if (value > h_liq + h_liq_error_band && value < h_vap - h_vap_error_band) {
1769 definitely_two_phase =
true;
1785 if (value > s_vap + s_vap_error_band) {
1789 }
else if (value < s_liq - s_liq_error_band) {
1793 }
else if (value > s_liq + s_liq_error_band && value < s_vap - s_vap_error_band) {
1794 definitely_two_phase =
true;
1813 CoolPropDbl u_liq_error_band = 1.5 * h_liq_error_band;
1814 CoolPropDbl u_vap_error_band = 1.5 * h_vap_error_band;
1817 if (value > u_vap + u_vap_error_band) {
1821 }
else if (value < u_liq - u_liq_error_band) {
1825 }
else if (value > u_liq + u_liq_error_band && value < u_vap - u_vap_error_band) {
1826 definitely_two_phase =
true;
1836 if (!definitely_two_phase) {
1843 if (value < rho_vap) {
1846 }
else if (value > rho_liq) {
1856 throw ValueError(
"possibly two-phase inputs not supported for mixtures for now");
1873 saturation_called =
true;
1892 Q = (1 / value - 1 / HEOS.
SatL->rhomolar()) / (1 / HEOS.
SatV->rhomolar() - 1 / HEOS.
SatL->rhomolar());
1895 Q = (value - HEOS.
SatL->smolar()) / (HEOS.
SatV->smolar() - HEOS.
SatL->smolar());
1898 Q = (value - HEOS.
SatL->hmolar()) / (HEOS.
SatV->hmolar() - HEOS.
SatL->hmolar());
1901 Q = (value - HEOS.
SatL->umolar()) / (HEOS.
SatV->umolar() - HEOS.
SatL->umolar());
1919 }
else if (Q > 1 + 1e-9) {
1933 }
else if (
_p <
components[0].EOS().ptriple * 0.9999) {
1941 throw NotImplementedError(
format(
"For now, we don't support p [%g Pa] below ptriple [%g Pa] when T [%g] is less than Tmin [%g]",
1949 throw ValueError(
format(
"The pressure [%g Pa] cannot be used in p_phase_determination",
_p));
1958 double call(
double T) {
1961 double dTdp_along_sat =
1962 HEOS->
T() * (1 / HEOS->
SatV->rhomolar() - 1 / HEOS->
SatL->rhomolar()) / (HEOS->
SatV->hmolar() - HEOS->
SatL->hmolar());
1969 Residual resid(*HEOS_copy);
1972 double v2 = resid.call(tripleV.
T);
1993 double call(
double T) {
1996 double dTdp_along_sat =
1997 HEOS->
T() * (1 / HEOS->
SatV->rhomolar() - 1 / HEOS->
SatL->rhomolar()) / (HEOS->
SatV->hmolar() - HEOS->
SatL->hmolar());
2004 Residualhmax residhmax(*HEOS_copy);
2015 throw ValueError(
format(
"value to T_phase_determination_pure_or_pseudopure is invalid"));
2019 auto smolar_critical = [
this, &T_crit_, &rhomolar_crit_](){
2022 auto hmolar_critical = [
this, &T_crit_, &rhomolar_crit_](){
2025 auto umolar_critical = [
this, &T_crit_, &rhomolar_crit_](){
2030 if (_T < T_crit_ && _p > p_crit_) {
2040 }
else if (
_rhomolar > rhomolar_crit_) {
2051 }
else if (
_p > p_crit_) {
2060 throw ValueError(
format(
"T=Tcrit; invalid input for other to T_phase_determination_pure_or_pseudopure"));
2062 }
else if (
_T < T_crit_)
2068 auto& superanc = optsuperanc.value();
2069 auto rhoL = superanc.eval_sat(
_T,
'D', 0);
2070 auto rhoV = superanc.eval_sat(
_T,
'D', 1);
2071 auto psat = superanc.eval_sat(
_T,
'P', 1);
2078 if (std::abs(psat/value-1) < 1e-6){
2080 format(
"Saturation pressure [%g Pa] corresponding to T [%g K] is within 1e-4 %% of given p [%Lg Pa]", psat,
_T, value)
2083 else if (value < psat) {
2086 }
else if (value > psat) {
2095 Q = (1 / value - 1 / rhoL) / (1 / rhoV - 1 / rhoL);
2099 }
else if (
Q >= 1) {
2118 Q = (1/value - 1/
SatL->rhomolar()) / (1/
SatV->rhomolar() - 1/
SatL->rhomolar());
2121 Q = (value -
SatL->smolar()) / (
SatV->smolar() -
SatL->smolar());
2124 Q = (value -
SatL->hmolar()) / (
SatV->hmolar() -
SatL->hmolar());
2127 Q = (value -
SatL->umolar()) / (
SatV->umolar() -
SatL->umolar());
2165 if (value < p_vap) {
2169 }
else if (value > p_liq) {
2186 throw ValueError(
"Two-phase inputs not supported for pseudo-pure for now");
2199 if (value < rho_vap) {
2202 }
else if (value > rho_liq) {
2207 double Qanc = (1 / value - 1 /
static_cast<double>(
_rhoLanc))
2210 if (value > 0.95 * rho_liq || value < 1.05 * rho_vap) {
2221 }
else if (Qanc > 1.01) {
2233 throw ValueError(
format(
"The saturation properties are needed in T_phase_determination_pure_or_pseudopure"));
2242 if (value >
SatV->calc_smolar()) {
2253 if (value >
SatV->calc_hmolar()) {
2263 if (value >
SatV->calc_umolar()) {
2273 throw ValueError(
format(
"invalid input for other to T_phase_determination_pure_or_pseudopure"));
2290 if (value > HEOS.
SatL->p() * (1e-6 + 1)) {
2294 }
else if (value < HEOS.SatV->
p() * (1 - 1e-6)) {
2300 format(
"Saturation pressure [%g Pa] corresponding to T [%g K] is within 1e-4 %% of given p [%Lg Pa]", HEOS.
SatL->p(),
_T, value));
2306 Q = (1 / value - 1 / HEOS.
SatL->rhomolar()) / (1 / HEOS.
SatV->rhomolar() - 1 / HEOS.
SatL->rhomolar());
2309 Q = (value - HEOS.
SatL->smolar()) / (HEOS.
SatV->smolar() - HEOS.
SatL->smolar());
2312 Q = (value - HEOS.
SatL->hmolar()) / (HEOS.
SatV->hmolar() - HEOS.
SatL->hmolar());
2315 Q = (value - HEOS.
SatL->umolar()) / (HEOS.
SatV->umolar() - HEOS.
SatL->umolar());
2394 throw ValueError(
"supercritical temp but other invalid for now");
2403 dT_dtau = -pow(T, 2) / Tr, R = HEOS->
gas_constant(), delta = rho / rhor, tau = Tr / T;
2486 int nTau = 0, nDelta = 1;
2520 double second_deriv(
double rhomolar) {
2522 return R_u *
T /
POW2(rhor)
2526 dpdrho_resid resid(
this,
T,
p);
2530 light =
Halley(resid, 1e-6, 1e-8, 100);
2531 double d2pdrho2__constT = resid.deriv(light);
2532 if (d2pdrho2__constT > 0) {
2536 }
catch (std::exception& e) {
2538 std::cout << e.what() << std::endl;
2547 for (std::size_t counter = 0; counter <= 100; counter++) {
2549 double curvature = resid.deriv(rho);
2550 if (curvature > 0) {
2561 for (
double omega = 0.7; omega > 0; omega -= 0.2) {
2563 resid.options.add_number(
"omega", omega);
2564 heavy =
Halley(resid, rhomax, 1e-8, 100);
2565 double d2pdrho2__constT = resid.deriv(heavy);
2566 if (d2pdrho2__constT < 0) {
2571 }
catch (std::exception& e) {
2573 std::cout << e.what() << std::endl;
2582 double rho = rhomax;
2583 for (std::size_t counter = 0; counter <= 100; counter++) {
2585 double curvature = resid.deriv(rho);
2586 if (curvature < 0 || this->
p() < 0) {
2596 if (light > 0 && heavy > 0) {
2603 else if (light < 0 && heavy < 0) {
2604 double dpdrho_min = resid.call(1e-10);
2605 double dpdrho_max = resid.call(rhomax);
2606 if (dpdrho_max * dpdrho_min > 0) {
2627 rhor(
HEOS->get_reducing_state().rhomolar),
2634 return (peos -
p) /
p;
2679 double rho_liq = -1, rho_vap = -1;
2680 if (
p > p_at_rhomax_stationary) {
2682 for (; counter <= 10; counter++) {
2685 if (p_at_rhomax <
p) {
2686 rhomolar_max *= 1.05;
2695 if (
p < p_at_rhomin_stationary) {
2700 if (rho_vap > 0 && rho_liq > 0) {
2702 if (std::abs(rho_vap - rho_liq) < 1e-10) {
2709 if (gibbsmolar_liq < gibbsmolar_vap) {
2715 }
else if (rho_vap < 0 && rho_liq > 0) {
2718 }
else if (rho_vap > 0 && rho_liq < 0) {
2743 if (rhomolar_guess < 0)
2750 if (rhomolar_guess < 0 || !
ValidNumber(rhomolar_guess))
2764 throw ValueError(
"Liquid density is invalid");
2766 }
catch (std::exception&) {
2797 if (dpdrho < 0 || d2pdrho2 < 0) {
2805 if (dpdrho < 0 || d2pdrho2 > 0) {
2812 }
catch (std::exception& e) {
2826 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,
2827 rhomolar_guess, e.what()));
2833 for (std::size_t i = 0; i <
components.size(); ++i) {
2835 CoolPropDbl m_i = 0.480 + 1.574 * acentric_i - 0.176 * pow(acentric_i, 2);
2839 CoolPropDbl a_i = 0.42747 * pow(R_u * Tci, 2) / pci * pow(1 + m_i * (1 - sqrt(
T / Tci)), 2);
2841 for (std::size_t j = 0; j <
components.size(); ++j) {
2843 CoolPropDbl m_j = 0.480 + 1.574 * acentric_j - 0.176 * pow(acentric_j, 2);
2845 CoolPropDbl a_j = 0.42747 * pow(R_u * Tcj, 2) / pcj * pow(1 + m_j * (1 - sqrt(
T / Tcj)), 2);
2865 solve_cubic(1, -1, A - B - B * B, -A * B, Nsolns, Z0, Z1, Z2);
2876 if (rhomolar0 > 0 && rhomolar1 <= 0 && rhomolar2 <= 0) {
2879 if (rhomolar0 <= 0 && rhomolar1 > 0 && rhomolar2 <= 0) {
2882 if (rhomolar0 <= 0 && rhomolar1 <= 0 && rhomolar2 > 0) {
2897 throw ValueError(
"Bad phase to solver_rho_Tp_SRK");
2935 return R_u *
T * (1 +
tau * (da0_dTau + dar_dTau) +
delta * dar_dDelta);
2939 std::cout <<
format(
"HelmholtzEOSMixtureBackend::calc_hmolar: 2phase: %d T: %g rhomomolar: %g",
isTwoPhase(),
_T,
_rhomolar) << std::endl;
2941 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
2983 return R_u * (
tau * (da0_dTau + dar_dTau) - a0 - ar);
2987 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
3009 _smolar = R_u * (
_tau.
pt() * (da0_dTau + dar_dTau) - a0 - ar);
3027 return R_u *
T *
tau * (da0_dTau + dar_dTau);
3031 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
3071 return static_cast<double>(
_cvmolar);
3088 * (-pow(
_tau.
pt(), 2) * (d2ar_dTau2 + d2a0_dTau2)
3090 / (1 + 2 *
_delta.
pt() * dar_dDelta + pow(
_delta.
pt(), 2) * d2ar_dDelta2));
3092 return static_cast<double>(
_cpmolar);
3104 return R_u * (1 + (-pow(
_tau.
pt(), 2)) * d2a0_dTau2);
3109 return SatL->speed_sound();
3111 return SatV->speed_sound();
3113 throw ValueError(
format(
"Speed of sound is not defined for two-phase states because it depends on the distribution of phases."));
3133 - pow(1 +
_delta.
pt() * dar_dDelta -
_delta.
pt() *
_tau.
pt() * d2ar_dDelta_dTau, 2) / (pow(
_tau.
pt(), 2) * (d2ar_dTau2 + d2a0_dTau2))));
3156 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
3182 for (std::size_t i = 0; i <
components.size(); ++i) {
3198 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
3232 double dna0_dni__const_T_V_nj =
3234 return gas_constant() *
T() * (dna0_dni__const_T_V_nj + dnar_dni__const_T_V_nj);
3255 throw ValueError(
"Mole fractions must be set before calling calc_reducing_state");
3264 bool cache_values =
true;
3285 bool cache_values =
false;
3287 return derivs.
get(nTau, nDelta);
3294 throw ValueError(
"No alpha0 derivatives are available");
3305 double taustar = Tc / Tr *
tau, deltastar = rhor / rhomolarc *
delta;
3306 if (nTau == 0 && nDelta == 0) {
3307 val = E.
base0(taustar, deltastar);
3308 }
else if (nTau == 0 && nDelta == 1) {
3310 }
else if (nTau == 1 && nDelta == 0) {
3312 }
else if (nTau == 0 && nDelta == 2) {
3314 }
else if (nTau == 1 && nDelta == 1) {
3316 }
else if (nTau == 2 && nDelta == 0) {
3318 }
else if (nTau == 0 && nDelta == 3) {
3320 }
else if (nTau == 1 && nDelta == 2) {
3322 }
else if (nTau == 2 && nDelta == 1) {
3324 }
else if (nTau == 3 && nDelta == 0) {
3329 val *= pow(rhor / rhomolarc, nDelta);
3330 val /= pow(Tr / Tc, nTau);
3333 throw ValueError(
format(
"calc_alpha0_deriv_nocache returned invalid number with inputs nTau: %d, nDelta: %d, tau: %Lg, delta: %Lg", nTau,
3344 for (
unsigned int i = 0; i <
N; ++i) {
3349 tau_i = T_ci *
tau / Tr;
3350 delta_i =
delta * rhor / rho_ci;
3356 if (nTau == 0 && nDelta == 0) {
3359 }
else if (nTau == 0 && nDelta == 1) {
3361 }
else if (nTau == 1 && nDelta == 0) {
3363 }
else if (nTau == 0 && nDelta == 2) {
3364 summer +=
mole_fractions[i] * Rratio * pow(rhor / rho_ci, 2) *
components[i].EOS().d2alpha0_dDelta2(tau_i, delta_i);
3365 }
else if (nTau == 1 && nDelta == 1) {
3366 summer +=
mole_fractions[i] * Rratio * rhor / rho_ci * T_ci / Tr *
components[i].EOS().d2alpha0_dDelta_dTau(tau_i, delta_i);
3367 }
else if (nTau == 2 && nDelta == 0) {
3439 const int nTau = 0, nDelta = 0;
3443 const int nTau = 0, nDelta = 1;
3447 const int nTau = 1, nDelta = 0;
3451 const int nTau = 0, nDelta = 2;
3455 const int nTau = 1, nDelta = 1;
3459 const int nTau = 2, nDelta = 0;
3463 const int nTau = 0, nDelta = 3;
3467 const int nTau = 1, nDelta = 2;
3471 const int nTau = 2, nDelta = 1;
3475 const int nTau = 3, nDelta = 0;
3484 if (Of1 ==
iT && Wrt1 ==
iP) {
3486 }
else if (Of1 ==
iP && Wrt1 ==
iT) {
3487 return 1 / dTdP_sat;
3490 else if (Wrt1 ==
iT) {
3494 else if (Wrt1 ==
iP) {
3502 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_first_saturation_deriv"));
3508 if (Of1 ==
iT && Wrt1 ==
iP) {
3510 }
else if (Of1 ==
iP && Wrt1 ==
iT) {
3511 return 1 / dTdP_sat;
3514 else if (Wrt1 ==
iT) {
3518 else if (Wrt1 ==
iP) {
3526 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_second_saturation_deriv"));
3527 if (Wrt1 ==
iP && Wrt2 ==
iP) {
3542 CoolPropDbl ddT_dTdp_along_sat_constp = (DELTAh * (
_T * dDELTAv_dT_constp + DELTAv) -
_T * DELTAv * dDELTAh_dT_constp) /
POW2(DELTAh);
3543 CoolPropDbl ddp_dTdp_along_sat_constT = (DELTAh * (
_T * dDELTAv_dp_constT) -
_T * DELTAv * dDELTAh_dp_constT) /
POW2(DELTAh);
3545 double ddp_dydpsigma = d2ydp2_constT + dydT_constp * ddp_dTdp_along_sat_constT + d2ydTdp * dTdp_along_sat;
3546 double ddT_dydpsigma = d2ydTdp + dydT_constp * ddT_dTdp_along_sat_constp + d2ydT2_constp * dTdp_along_sat;
3547 return ddp_dydpsigma + ddT_dydpsigma * dTdp_along_sat;
3549 throw ValueError(
format(
"Currently, only possible to take second saturation derivative w.r.t. P (both times)"));
3555 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_second_two_phase_deriv"));
3570 CoolPropDbl numerator = 1 /
SatV->keyed_output(rho_key) - 1 /
SatL->keyed_output(rho_key);
3572 CoolPropDbl dnumerator = -1 /
POW2(
SatV->keyed_output(rho_key)) * drhoV_dp_sat + 1 /
POW2(
SatL->keyed_output(rho_key)) * drhoL_dp_sat;
3573 CoolPropDbl ddenominator = dhV_dp_sat - dhL_dp_sat;
3574 CoolPropDbl d_dvdh_dp__consth = (denominator * dnumerator - numerator * ddenominator) /
POW2(denominator);
3575 return -
POW2(
rhomolar()) * d_dvdh_dp__consth + dv_dh_constp * (-2 *
rhomolar()) * drhomolar_dp__consth;
3577 && ((Wrt1 ==
iHmass && Constant1 ==
iP && Wrt2 ==
iP && Constant2 ==
iHmass)
3578 || (Wrt2 ==
iHmass && Constant2 ==
iP && Wrt1 ==
iP && Constant1 ==
iHmass))) {
3590 CoolPropDbl numerator = 1 /
SatV->keyed_output(rho_key) - 1 /
SatL->keyed_output(rho_key);
3592 CoolPropDbl dnumerator = -1 /
POW2(
SatV->keyed_output(rho_key)) * drhoV_dp_sat + 1 /
POW2(
SatL->keyed_output(rho_key)) * drhoL_dp_sat;
3593 CoolPropDbl ddenominator = dhV_dp_sat - dhL_dp_sat;
3594 CoolPropDbl d_dvdh_dp__consth = (denominator * dnumerator - numerator * ddenominator) /
POW2(denominator);
3595 return -
POW2(rho) * d_dvdh_dp__consth + dv_dh_constp * (-2 * rho) * drho_dp__consth;
3601 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_first_two_phase_deriv"));
3615 CoolPropDbl dvdp_h = dvL_dp + dxdp_h * (1 /
SatV->rhomolar() - 1 /
SatL->rhomolar()) +
Q() * (dvV_dp - dvL_dp);
3626 CoolPropDbl dvdp_h = dvL_dp + dxdp_h * (1 /
SatV->rhomass() - 1 /
SatL->rhomass()) +
Q() * (dvV_dp - dvL_dp);
3629 throw ValueError(
"These inputs are not supported to calc_first_two_phase_deriv");
3636 bool drho_dh__p =
false;
3637 bool drho_dp__h =
false;
3638 bool rho_spline =
false;
3658 throw ValueError(
"These inputs are not supported to calc_first_two_phase_deriv");
3661 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_first_two_phase_deriv_splined"));
3674 Liq->update_DmolarT_direct(
SatL->rhomolar(),
SatL->T());
3689 CoolPropDbl Abracket = (2 * rho_liq - 2 * rho_end + Delta_end * (drho_dh_liq__constp + drho_dh_end));
3691 CoolPropDbl b = 3 /
POW2(Delta_end) * (-rho_liq + rho_end) - 1 / Delta_end * (drho_dh_end + 2 * drho_dh_liq__constp);
3725 CoolPropDbl d_Delta_end_dp__consth = x_end * (dhV_dp_sat - dhL_dp_sat);
3729 CoolPropDbl d_Abracket_dp_consth = (2 * drhoL_dp_sat - 2 * drho_dp_end + Delta_end * (d2rhodhdp_liq + d2rhodhdp_end)
3730 + d_Delta_end_dp__consth * (drho_dh_liq__constp + drho_dh_end));
3731 CoolPropDbl da_dp = 1 /
POW3(Delta_end) * d_Abracket_dp_consth + Abracket * (-3 /
POW4(Delta_end) * d_Delta_end_dp__consth);
3732 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)
3733 + (1 /
POW2(Delta_end) * d_Delta_end_dp__consth) * (drho_dh_end + 2 * drho_dh_liq__constp)
3734 - (1 / Delta_end) * (d2rhodhdp_end + 2 * d2rhodhdp_liq);
3739 (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;
3742 throw ValueError(
"Something went wrong in HelmholtzEOSMixtureBackend::calc_first_two_phase_deriv_splined");
3752 Eigen::MatrixXd Lstar, Mstar;
3754 std::vector<double> call(
const std::vector<double>& tau_delta) {
3760 std::vector<double> o(2);
3761 o[0] = Lstar.determinant();
3762 o[1] = Mstar.determinant();
3765 std::vector<std::vector<double>> Jacobian(
const std::vector<double>& x) {
3766 std::size_t
N = x.size();
3767 std::vector<std::vector<double>> J(
N, std::vector<double>(
N, 0));
3773 J[0][0] = (adjL * dLdTau).trace();
3774 J[0][1] = (adjL * dLdDelta).trace();
3775 J[1][0] = (adjM * dMdTau).trace();
3776 J[1][1] = (adjM * dMdDelta).trace();
3780 std::vector<std::vector<double>> numerical_Jacobian(
const std::vector<double>& x) {
3781 std::size_t
N = x.size();
3782 std::vector<double> rplus, rminus, xp;
3783 std::vector<std::vector<double>> J(
N, std::vector<double>(
N, 0));
3785 double epsilon = 0.0001;
3788 for (std::size_t i = 0; i <
N; ++i) {
3792 xp[i] -= 2 * epsilon;
3795 for (std::size_t j = 0; j <
N; ++j) {
3796 J[j][i] = (rplus[j] - rminus[j]) / (2 * epsilon);
3799 std::cout << J[0][0] <<
" " << J[0][1] << std::endl;
3800 std::cout << J[1][0] <<
" " << J[1][1] << std::endl;
3805 std::vector<double> x, tau_delta(2);
3850 _deriv = (adjL * dLdTau).trace();
3857 dadjLstardTau = adjugate_derivative(Lstar, dLstardTau);
3858 _second_deriv = (dLstardTau * dadjLstardTau + adjL * d2LstardTau2).trace();
3898 tau_new =
tau +
R_tau * cos(theta);
3906 double tau_new, delta_new;
3914 double L1 =
Lstar.determinant();
3923 return -
R_tau * sin(theta) * dL1_dtau +
R_delta * cos(theta) * dL1_ddelta;
3929 for (
int i = 0; i < 300; ++i) {
3971 double p_MPa =
HEOS.
p() / 1e6;
3974 double tau_new, delta_new;
3997 this->tau = tau_new;
3998 this->delta = delta_new;
4002 this->spinodal_values.
tau.push_back(tau_new);
4003 this->spinodal_values.
delta.push_back(delta_new);
4004 this->spinodal_values.
M1.push_back(M1);
4012 L1star = Lstar.determinant();
4013 M1star = Mstar.determinant();
4024 rapidjson::Document doc;
4026 rapidjson::Value& val = doc;
4027 std::vector<std::vector<DepartureFunctionPointer>>&
mat =
critical_state->residual_helmholtz->Excess.DepartureFunctionMatrix;
4028 if (
mat.size() > 0) {
4029 mat[0][1]->phi.to_json(val, doc);
4038 double delta0 = _HUGE, tau0 = _HUGE;
4039 critical_state->get_critical_point_starting_values(delta0, tau0);
4046 resid_L0.
call(tau0);
4048 while (resid_L0.
deriv(tau0) > 0 && bump_count < 3) {
4052 double tau_L0 =
Halley(resid_L0, tau0, 1e-10, 100);
4059 double R_delta = 0, R_tau = 0;
4071 const double rhomolar_guess) {
4074 if (w.size() != z.size()) {
4075 throw ValueError(
format(
"Trial composition vector size [%d] is not the same as bulk composition [%d]", w.size(), z.size()));
4084 for (std::size_t i = 0; i < w.size(); ++i) {
4093 bool find_critical_points =
false;
4098 for (std::size_t i = 0; i <
components.size(); ++i) {
4100 if (!reference_state.compare(
"IIR")) {
4101 if (HEOS.
Ttriple() > 273.15) {
4102 throw ValueError(
format(
"Cannot use IIR reference state; Ttriple [%Lg] is greater than 273.15 K", HEOS.
Ttriple()));
4107 double deltah = HEOS.
hmass() - 200000;
4108 double deltas = HEOS.
smass() - 1000;
4114 std::cout <<
format(
"set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
4116 }
else if (!reference_state.compare(
"ASHRAE")) {
4117 if (HEOS.
Ttriple() > 233.15) {
4118 throw ValueError(
format(
"Cannot use ASHRAE reference state; Ttriple [%Lg] is greater than than 233.15 K", HEOS.
Ttriple()));
4123 double deltah = HEOS.
hmass() - 0;
4124 double deltas = HEOS.
smass() - 0;
4130 std::cout <<
format(
"set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
4132 }
else if (!reference_state.compare(
"NBP")) {
4134 throw ValueError(
format(
"Cannot use NBP reference state; p_triple [%Lg Pa] is greater than than 101325 Pa", HEOS.
p_triple()));
4139 double deltah = HEOS.
hmass() - 0;
4140 double deltas = HEOS.
smass() - 0;
4146 std::cout <<
format(
"set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
4148 }
else if (!reference_state.compare(
"DEF")) {
4150 }
else if (!reference_state.compare(
"RESET")) {
4153 throw ValueError(
format(
"reference state string is invalid: [%s]", reference_state.c_str()));
4164 for (std::size_t i = 0; i <
components.size(); ++i) {
4170 double deltah = HEOS.
hmolar() - hmolar0;
4171 double deltas = HEOS.
smolar() - smolar0;
4179 const std::string& ref) {
4189 double f = (HEOS->name() ==
"Water" || HEOS->name() ==
"CarbonDioxide") ? 1.00001 : 1.0;
4211 if (!HEOS->is_pure()) {