12# define _CRTDBG_MAP_ALLOC
13# ifndef _CRT_SECURE_NO_WARNINGS
14# define _CRT_SECURE_NO_WARNINGS
32#include "boost/math/tools/toms748_solve.hpp"
47static std::atomic<int> deriv_counter{0};
55 if (fluid_names.size() == 1) {
77 std::vector<CoolPropFluid>
components(component_names.size());
78 for (
unsigned int i = 0; i <
components.size(); ++i) {
115 this->
N = components.size();
120 std::vector<std::vector<double>> ones(1, std::vector<double>(1, 1));
121 Reducing = std::make_shared<GERG2008ReducingFunction>(
components, ones, ones, ones, ones);
141 if (generate_SatL_and_SatV) {
153 if (mf.size() !=
N) {
154 throw ValueError(
format(
"size of mole fraction vector [%d] does not equal that of component vector [%d]", mf.size(),
N));
168 state->sync_linked_states(source);
176 ptr->sync_linked_states(
this);
180 if (mass_fractions.size() !=
N) {
181 throw ValueError(
format(
"size of mass fraction vector [%d] does not equal that of component vector [%d]", mass_fractions.size(),
N));
183 std::vector<CoolPropDbl> moles;
186 for (
unsigned int i = 0; i <
components.size(); ++i) {
187 tmp = mass_fractions[i] /
components[i].molar_mass();
188 moles.push_back(tmp);
193 for (
const auto& m : moles) {
249 if (!ParamName.compare(
"name")) {
251 }
else if (!ParamName.compare(
"aliases")) {
253 }
else if (!ParamName.compare(
"aliases_bar")) {
255 }
else if (!ParamName.compare(
"CAS") || !ParamName.compare(
"CAS_number")) {
257 }
else if (!ParamName.compare(
"formula")) {
259 }
else if (!ParamName.compare(
"ASHRAE34")) {
261 }
else if (!ParamName.compare(
"REFPROPName") || !ParamName.compare(
"REFPROP_name") || !ParamName.compare(
"REFPROPname")) {
263 }
else if (ParamName.find(
"BibTeX") == 0)
265 std::vector<std::string> parts =
strsplit(ParamName,
'-');
266 if (parts.size() != 2) {
267 throw ValueError(
format(
"Unable to parse BibTeX string %s", ParamName.c_str()));
269 const std::string& key = parts[1];
270 if (!key.compare(
"EOS")) {
272 }
else if (!key.compare(
"CP0")) {
274 }
else if (!key.compare(
"VISCOSITY")) {
276 }
else if (!key.compare(
"CONDUCTIVITY")) {
278 }
else if (!key.compare(
"ECS_LENNARD_JONES")) {
280 }
else if (!key.compare(
"ECS_VISCOSITY_FITS")) {
282 }
else if (!key.compare(
"ECS_CONDUCTIVITY_FITS")) {
284 }
else if (!key.compare(
"SURFACE_TENSION")) {
286 }
else if (!key.compare(
"MELTING_LINE")) {
291 }
else if (ParamName.find(
"pure") == 0) {
297 }
else if (ParamName ==
"INCHI" || ParamName ==
"InChI" || ParamName ==
"INCHI_STRING") {
298 return cpfluid.
InChI;
299 }
else if (ParamName ==
"INCHI_Key" || ParamName ==
"InChIKey" || ParamName ==
"INCHIKEY") {
301 }
else if (ParamName ==
"2DPNG_URL") {
303 }
else if (ParamName ==
"SMILES" || ParamName ==
"smiles") {
305 }
else if (ParamName ==
"CHEMSPIDER_ID") {
307 }
else if (ParamName ==
"JSON") {
310 throw ValueError(
format(
"fluid parameter [%s] is invalid", ParamName.c_str()));
347 superanc_ptr->ensure_HSU_under_lock(caller_a1, caller_a2, h_callable, s_callable, u_callable);
352 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N - 1));
355 if (parameter.find(
"SUPERANC::") == 0) {
357 auto& superanc = *superanc_ptr;
358 std::string key = parameter.substr(10);
360 return superanc.get_pmax();
361 }
else if (key ==
"pmin") {
362 return superanc.get_pmin();
363 }
else if (key ==
"Tmin") {
364 return superanc.get_Tmin();
365 }
else if (key ==
"Tcrit_num") {
366 return superanc.get_Tcrit_num();
367 }
else if (key ==
"caloric_build_count") {
371 return static_cast<double>(superanc.get_caloric_build_count());
373 throw ValueError(
format(
"Superancillary parameter [%s] is invalid", key.c_str()));
379 throw ValueError(
format(
"fluid parameter [%s] is invalid", parameter.c_str()));
387 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j,
N - 1));
389 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N - 1));
392 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j,
N - 1));
394 if (model ==
"linear") {
396 double gammaT = 0.5 * (Tc1 + Tc2) / sqrt(Tc1 * Tc2);
398 double gammaV = 4.0 * (1 / rhoc1 + 1 / rhoc2) / pow(pow(rhoc1, -1.0 / 3.0) + pow(rhoc2, -1.0 / 3.0), 3);
403 }
else if (model ==
"Lorentz-Berthelot") {
409 throw ValueError(
format(
"mixing rule [%s] is not understood", model.c_str()));
414 const double value) {
418 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j,
N - 1));
420 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N - 1));
423 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j,
N - 1));
425 if (parameter ==
"Fij") {
429 Reducing->set_binary_interaction_double(i, j, parameter, value);
433 state->set_binary_interaction_double(i, j, parameter, value);
441 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j,
N - 1));
443 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N - 1));
446 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j,
N - 1));
448 if (parameter ==
"Fij") {
451 return Reducing->get_binary_interaction_double(i, j, parameter);
460 const std::string& value) {
464 throw ValueError(
format(
"Both indices i [%d] and j [%d] are out of bounds. Must be between 0 and %d.", i, j,
N - 1));
466 throw ValueError(
format(
"Index i [%d] is out of bounds. Must be between 0 and %d.", i,
N - 1));
469 throw ValueError(
format(
"Index j [%d] is out of bounds. Must be between 0 and %d.", j,
N - 1));
471 if (parameter ==
"function") {
475 throw ValueError(
format(
"Cannot process this string parameter [%s] in set_binary_interaction_string", parameter.c_str()));
479 state->set_binary_interaction_string(i, j, parameter, value);
489 if (EOS_name ==
"SRK" || EOS_name ==
"Peng-Robinson") {
501 shared_ptr<AbstractCubic> ac;
502 if (EOS_name ==
"SRK") {
503 ac = std::make_shared<SRK>(Tc, pc, acentric, R);
505 ac = std::make_shared<PengRobinson>(Tc, pc, acentric, R);
508 ac->set_rhor(rhomolarc);
510 }
else if (EOS_name ==
"XiangDeiters") {
524 throw ValueError(
format(
"EOS name [%s] is invalid; valid options are SRK, Peng-Robinson, XiangDeiters", EOS_name.c_str()));
530 if (this->
SatL)
SatL->change_EOS(i, EOS_name);
531 if (this->
SatV)
SatV->change_EOS(i, EOS_name);
567 if (!state.compare(
"hs_anchor")) {
569 }
else if (!state.compare(
"max_sat_T")) {
571 }
else if (!state.compare(
"max_sat_p")) {
573 }
else if (!state.compare(
"reducing")) {
575 }
else if (!state.compare(
"critical")) {
577 }
else if (!state.compare(
"triple_liquid")) {
579 }
else if (!state.compare(
"triple_vapor")) {
582 throw ValueError(
format(
"This state [%s] is invalid to calc_state", state.c_str()));
585 if (!state.compare(
"critical")) {
596 throw ValueError(
"acentric factor cannot be calculated for mixtures");
608 for (
unsigned int i = 0; i <
components.size(); ++i) {
617 for (
unsigned int i = 0; i <
components.size(); ++i) {
630 throw ValueError(
"phase composition vectors do not match component count");
634 for (std::size_t i = 0; i <
components.size(); ++i) {
635 const double mm_i =
components[i].molar_mass();
636 MM_l +=
static_cast<double>(x[i]) * mm_i;
637 MM_v +=
static_cast<double>(y[i]) * mm_i;
643 if (param ==
iP && given ==
iT) {
647 return components[0].ancillaries.pL.evaluate(value);
649 return components[0].ancillaries.pV.evaluate(value);
653 }
else if (param ==
iT && given ==
iP) {
657 return components[0].ancillaries.pL.invert(value);
659 return components[0].ancillaries.pV.invert(value);
663 }
else if (param ==
iDmolar && given ==
iT) {
667 return components[0].ancillaries.rhoL.evaluate(value);
669 return components[0].ancillaries.rhoV.evaluate(value);
673 }
else if (param ==
iT && given ==
iDmolar) {
677 return components[0].ancillaries.rhoL.invert(value);
679 return components[0].ancillaries.rhoV.invert(value);
684 return components[0].ancillaries.surface_tension.evaluate(value);
698 return components[0].ancillaries.melting_line.evaluate(param, given, value);
706 return components[0].ancillaries.surface_tension.evaluate(
T());
708 throw ValueError(
format(
"surface tension is only defined within the two-phase region; Try PQ or QT inputs"));
717 switch (
components[0].transport.viscosity_dilute.type) {
744 format(
"dilute viscosity type [%d] is invalid for fluid %s",
components[0].transport.viscosity_dilute.type,
name().c_str()));
757 switch (
components[0].transport.viscosity_initial.type) {
761 initial_density = eta_dilute * B_eta_initial * rho;
774 switch (
components[0].transport.viscosity_higher_order.type) {
804 format(
"higher order viscosity type [%d] is invalid for fluid %s",
components[0].transport.viscosity_dilute.type,
name().c_str()));
807 return initial_density + residual;
812 CoolPropDbl dilute = 0, initial_density = 0, residual = 0, critical = 0;
814 return dilute + initial_density + residual + critical;
819 shared_ptr<HelmholtzEOSBackend> HEOS = std::make_shared<HelmholtzEOSBackend>(
components[i]);
839 throw ValueError(
format(
"Viscosity model is not available for this fluid"));
850 std::vector<std::string> names(1, fluid_name);
919 throw ValueError(
"calc_viscosity_contributions invalid for mixtures");
935 throw ValueError(
format(
"Thermal conductivity model is not available for this fluid"));
946 std::vector<std::string>
name(1, fluid_name);
974 throw ValueError(
format(
"hardcoded conductivity type [%d] is invalid for fluid %s",
1006 format(
"dilute conductivity type [%d] is invalid for fluid %s",
components[0].transport.conductivity_dilute.type,
name().c_str()));
1031 format(
"critical conductivity type [%d] is invalid for fluid %s",
components[0].transport.viscosity_dilute.type,
name().c_str()));
1034 throw ValueError(
"calc_conductivity_contributions invalid for mixtures");
1041 switch (
components[0].transport.conductivity_residual.type) {
1050 format(
"residual conductivity type [%d] is invalid for fluid %s",
components[0].transport.conductivity_residual.type,
name().c_str()));
1052 return lambda_residual;
1056 CoolPropDbl dilute = 0, initial_density = 0, residual = 0, critical = 0;
1058 return dilute + initial_density + residual + critical;
1063 shared_ptr<HelmholtzEOSBackend> HEOS = std::make_shared<HelmholtzEOSBackend>(
components[i]);
1074 shared_ptr<CoolProp::HelmholtzEOSMixtureBackend> REF = std::make_shared<CoolProp::HelmholtzEOSBackend>(reference_fluid);
1097 for (
unsigned int i = 0; i <
components.size(); ++i) {
1104 for (
unsigned int i = 0; i <
components.size(); ++i) {
1119 if (!
SatL)
throw ValueError(
"The saturated liquid state has not been set.");
1120 return SatL->keyed_output(key);
1124 if (!
SatV)
throw ValueError(
"The saturated vapor state has not been set.");
1125 return SatV->keyed_output(key);
1129 if (type ==
"Joule-Thomson") {
1132 }
else if (type ==
"Joule-Inversion") {
1135 }
else if (type ==
"Ideal") {
1138 }
else if (type ==
"Boyle") {
1146 std::vector<std::string> out;
1149 out.push_back(component.name);
1155 throw ValueError(
format(
"For now, calc_ODP is only valid for pure and pseudo-pure fluids, %d components",
components.size()));
1166 throw ValueError(
format(
"For now, calc_GWP20 is only valid for pure and pseudo-pure fluids, %d components",
components.size()));
1177 throw ValueError(
format(
"For now, calc_GWP100 is only valid for pure and pseudo-pure fluids, %d components",
components.size()));
1188 throw ValueError(
format(
"For now, calc_GWP500 is only valid for pure and pseudo-pure fluids, %d components",
components.size()));
1200 if (critpts.size() == 1) {
1202 return critpts[0].T;
1204 throw ValueError(
format(
"critical point finding routine found %d critical points",
static_cast<int>(critpts.size())));
1210 return superanc_ptr->get_Tcrit_num();
1219 if (critpts.size() == 1) {
1221 return critpts[0].p;
1223 throw ValueError(
format(
"critical point finding routine found %d critical points",
static_cast<int>(critpts.size())));
1229 return superanc_ptr->get_pmax();
1238 if (critpts.size() == 1) {
1240 return critpts[0].rhomolar;
1242 throw ValueError(
format(
"critical point finding routine found %d critical points",
static_cast<int>(critpts.size())));
1248 return superanc_ptr->get_rhocrit_num();
1262 throw ValueError(
"calc_pmax_sat not yet defined for mixtures");
1268 double Tmax_sat =
components[0].EOS().max_sat_T.T;
1278 throw ValueError(
"calc_Tmax_sat not yet defined for mixtures");
1284 Tmin_satL =
components[0].EOS().sat_min_liquid.T;
1285 Tmin_satV =
components[0].EOS().sat_min_vapor.T;
1288 throw ValueError(
"calc_Tmin_sat not yet defined for mixtures");
1294 pmin_satL =
components[0].EOS().sat_min_liquid.p;
1295 pmin_satV =
components[0].EOS().sat_min_vapor.p;
1298 throw ValueError(
"calc_pmin_sat not yet defined for mixtures");
1307 for (
unsigned int i = 0; i <
components.size(); ++i) {
1314 for (
unsigned int i = 0; i <
components.size(); ++i) {
1321 for (
unsigned int i = 0; i <
components.size(); ++i) {
1338 if (!superanc_ptr) {
1342 auto& superanc = *superanc_ptr;
1344 if (
T > Tcrit_num) {
1345 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));
1347 auto rhoL = superanc.eval_sat(
T,
'D', 0);
1348 auto rhoV = superanc.eval_sat(
T,
'D', 1);
1349 auto p = superanc.eval_sat(
T,
'P', 1);
1350 SatL->update_TDmolarP_unchecked(
T, rhoL,
p);
1351 SatV->update_TDmolarP_unchecked(
T, rhoV,
p);
1368 bool optional_checks =
false;
1378 throw ValueError(
format(
"The molar density of %f mol/m3 is below the minimum of %f mol/m3",
rhomolar, rhomolar_min));
1382 throw ValueError(
format(
"The temperature of %f K is below the minimum of %f K",
T, T_min));
1394 bool optional_checks =
false;
1413 this->
_T = HEOS.
T();
1415 this->
_p = HEOS.
p();
1417 this->
_Q = HEOS.
Q();
1441 throw ValueError(
"Mole fractions must be set");
1465 std::cout <<
format(
"%s (%d): update called with (%d: (%s), %g, %g)", __FILE__, __LINE__, input_pair,
1470 CoolPropDbl ld_value1 = value1, ld_value2 = value2;
1471 pre_update(input_pair, ld_value1, ld_value2);
1475 switch (input_pair) {
1591 return mass_fractions;
1597 std::cout <<
format(
"%s (%d): update called with (%d: (%s), %g, %g)", __FILE__, __LINE__, input_pair,
1602 CoolPropDbl ld_value1 = value1, ld_value2 = value2;
1603 pre_update(input_pair, ld_value1, ld_value2);
1607 switch (input_pair) {
1658 throw ValueError(
"rhomolar is less than zero");
1661 throw ValueError(
"rhomolar is not a valid number");
1664 if (optional_checks) {
1703 saturation_called =
false;
1712 auto smolar_critical = [
this, &T_crit_, &rhomolar_crit_]() {
return this->
calc_smolar_nocache(T_crit_, rhomolar_crit_); };
1713 auto hmolar_critical = [
this, &T_crit_, &rhomolar_crit_]() {
return this->
calc_hmolar_nocache(T_crit_, rhomolar_crit_); };
1714 auto umolar_critical = [
this, &T_crit_, &rhomolar_crit_]() {
return this->
calc_umolar_nocache(T_crit_, rhomolar_crit_); };
1717 if (
_p > psat_max) {
1728 if (
_T < Tm - 0.001) {
1729 throw ValueError(
format(
"For now, we don't support T [%g K] below Tmelt(p) [%g K]",
_T, Tm));
1777 throw ValueError(
"supercritical pressure but other invalid for now");
1782 else if (
_p >=
components[0].EOS().ptriple * 0.9999 &&
_p <= psat_max) {
1789 auto& superanc = *superanc_ptr;
1791 if (
_p > pmax_num) {
1793 format(
"Pressure to PQ_flash [%0.8Lg Pa] may not be above the numerical critical point of %0.15Lg Pa",
_p, pmax_num));
1795 auto Tsat = superanc.get_T_from_p(
_p);
1796 auto rhoL = superanc.eval_sat(Tsat,
'D', 0);
1797 auto rhoV = superanc.eval_sat(Tsat,
'D', 1);
1804 if (
_T < Tm - 0.001) {
1805 throw ValueError(
format(
"For now, we don't support T [%g K] below Tmelt(p) [%g K]",
_T, Tm));
1819 SatL->update_TDmolarP_unchecked(Tsat, rhoL, psat);
1820 SatV->update_TDmolarP_unchecked(Tsat, rhoV, psat);
1824 Q = (1 / value - 1 /
SatL->rhomolar()) / (1 /
SatV->rhomolar() - 1 /
SatL->rhomolar());
1827 Q = (value -
SatL->smolar()) / (
SatV->smolar() -
SatL->smolar());
1830 Q = (value -
SatL->hmolar()) / (
SatV->hmolar() -
SatL->hmolar());
1833 Q = (value -
SatL->umolar()) / (
SatV->umolar() -
SatL->umolar());
1845 }
else if (
Q > 1 + 1e-9) {
1866 bool definitely_two_phase =
false;
1877 if (
_T < Tm - 0.001) {
1878 throw ValueError(
format(
"For now, we don't support T [%g K] below Tmelt(p) [%g K]",
_T, Tm));
1885 if (
_T <
Tmin() - 0.001) {
1886 throw ValueError(
format(
"For now, we don't support T [%g K] below Tmin(saturation) [%g K]",
_T,
Tmin()));
1894 if (value > T_vap) {
1898 }
else if (value < T_liq) {
1922 if (value > h_vap + h_vap_error_band) {
1926 }
else if (value < h_liq - h_liq_error_band) {
1930 }
else if (value > h_liq + h_liq_error_band && value < h_vap - h_vap_error_band) {
1931 definitely_two_phase =
true;
1947 if (value > s_vap + s_vap_error_band) {
1951 }
else if (value < s_liq - s_liq_error_band) {
1955 }
else if (value > s_liq + s_liq_error_band && value < s_vap - s_vap_error_band) {
1956 definitely_two_phase =
true;
1975 CoolPropDbl u_liq_error_band = 1.5 * h_liq_error_band;
1976 CoolPropDbl u_vap_error_band = 1.5 * h_vap_error_band;
1979 if (value > u_vap + u_vap_error_band) {
1983 }
else if (value < u_liq - u_liq_error_band) {
1987 }
else if (value > u_liq + u_liq_error_band && value < u_vap - u_vap_error_band) {
1988 definitely_two_phase =
true;
1998 if (!definitely_two_phase) {
2005 if (value < rho_vap) {
2008 }
else if (value > rho_liq) {
2020 throw ValueError(
"possibly two-phase inputs not supported for mixtures for now");
2037 saturation_called =
true;
2056 Q = (1 / value - 1 / HEOS.
SatL->rhomolar()) / (1 / HEOS.
SatV->rhomolar() - 1 / HEOS.
SatL->rhomolar());
2059 Q = (value - HEOS.
SatL->smolar()) / (HEOS.
SatV->smolar() - HEOS.
SatL->smolar());
2062 Q = (value - HEOS.
SatL->hmolar()) / (HEOS.
SatV->hmolar() - HEOS.
SatL->hmolar());
2065 Q = (value - HEOS.
SatL->umolar()) / (HEOS.
SatV->umolar() - HEOS.
SatL->umolar());
2083 }
else if (Q > 1 + 1e-9) {
2097 }
else if (
_p <
components[0].EOS().ptriple * 0.9999) {
2105 throw NotImplementedError(
format(
"For now, we don't support p [%g Pa] below ptriple [%g Pa] when T [%g] is less than Tmin [%g]",
2113 throw ValueError(
format(
"The pressure [%g Pa] cannot be used in p_phase_determination",
_p));
2122 double call(
double T)
override {
2125 double dTdp_along_sat =
2126 HEOS->
T() * (1 / HEOS->
SatV->rhomolar() - 1 / HEOS->
SatL->rhomolar()) / (HEOS->
SatV->hmolar() - HEOS->
SatL->hmolar());
2132 shared_ptr<CoolProp::HelmholtzEOSMixtureBackend> HEOS_copy = std::make_shared<CoolProp::HelmholtzEOSMixtureBackend>(
get_components());
2133 Residual resid(*HEOS_copy);
2136 double v2 = resid.call(tripleV.
T);
2157 double call(
double T)
override {
2160 double dTdp_along_sat =
2161 HEOS->
T() * (1 / HEOS->
SatV->rhomolar() - 1 / HEOS->
SatL->rhomolar()) / (HEOS->
SatV->hmolar() - HEOS->
SatL->hmolar());
2167 shared_ptr<CoolProp::HelmholtzEOSMixtureBackend> HEOS_copy = std::make_shared<CoolProp::HelmholtzEOSMixtureBackend>(
get_components());
2168 Residualhmax residhmax(*HEOS_copy);
2179 throw ValueError(
format(
"value to T_phase_determination_pure_or_pseudopure is invalid"));
2183 auto smolar_critical = [
this, &T_crit_, &rhomolar_crit_]() {
return this->
calc_smolar_nocache(T_crit_, rhomolar_crit_); };
2184 auto hmolar_critical = [
this, &T_crit_, &rhomolar_crit_]() {
return this->
calc_hmolar_nocache(T_crit_, rhomolar_crit_); };
2185 auto umolar_critical = [
this, &T_crit_, &rhomolar_crit_]() {
return this->
calc_umolar_nocache(T_crit_, rhomolar_crit_); };
2191 if (
_T < Tm - 0.001) {
2192 throw ValueError(
format(
"For now, we don't support T [%g K] below Tmelt(p) [%g K]",
_T, Tm));
2197 if (_T < T_crit_ && _p > p_crit_) {
2207 }
else if (
_rhomolar > rhomolar_crit_) {
2218 }
else if (
_p > p_crit_) {
2227 throw ValueError(
format(
"T=Tcrit; invalid input for other to T_phase_determination_pure_or_pseudopure"));
2229 }
else if (
_T < T_crit_)
2235 auto& superanc = *superanc_ptr;
2236 auto rhoL = superanc.eval_sat(
_T,
'D', 0);
2237 auto rhoV = superanc.eval_sat(
_T,
'D', 1);
2238 auto psat = superanc.eval_sat(
_T,
'P', 1);
2245 if (std::abs(psat / value - 1) < 1e-6) {
2247 format(
"Saturation pressure [%g Pa] corresponding to T [%g K] is within 1e-4 %% of given p [%Lg Pa]", psat,
_T, value));
2248 }
else if (value < psat) {
2251 }
else if (value > psat) {
2260 Q = (1 / value - 1 / rhoL) / (1 / rhoV - 1 / rhoL);
2264 }
else if (
Q >= 1) {
2278 SatL->update_TDmolarP_unchecked(
_T, rhoL, psat);
2279 SatV->update_TDmolarP_unchecked(
_T, rhoV, psat);
2283 Q = (1 / value - 1 /
SatL->rhomolar()) / (1 /
SatV->rhomolar() - 1 /
SatL->rhomolar());
2286 Q = (value -
SatL->smolar()) / (
SatV->smolar() -
SatL->smolar());
2289 Q = (value -
SatL->hmolar()) / (
SatV->hmolar() -
SatL->hmolar());
2292 Q = (value -
SatL->umolar()) / (
SatV->umolar() -
SatL->umolar());
2329 if (value < p_vap) {
2333 }
else if (value > p_liq) {
2350 throw ValueError(
"Two-phase inputs not supported for pseudo-pure for now");
2363 if (value < rho_vap) {
2366 }
else if (value > rho_liq) {
2371 double Qanc = (1 / value - 1 /
static_cast<double>(
_rhoLanc))
2374 if (value > 0.95 * rho_liq || value < 1.05 * rho_vap) {
2385 }
else if (Qanc > 1.01) {
2397 throw ValueError(
format(
"The saturation properties are needed in T_phase_determination_pure_or_pseudopure"));
2406 if (value >
SatV->calc_smolar()) {
2417 if (value >
SatV->calc_hmolar()) {
2427 if (value >
SatV->calc_umolar()) {
2437 throw ValueError(
format(
"invalid input for other to T_phase_determination_pure_or_pseudopure"));
2454 if (value > HEOS.
SatL->p() * (1e-6 + 1)) {
2458 }
else if (value < HEOS.SatV->
p() * (1 - 1e-6)) {
2464 format(
"Saturation pressure [%g Pa] corresponding to T [%g K] is within 1e-4 %% of given p [%Lg Pa]", HEOS.
SatL->p(),
_T, value));
2470 Q = (1 / value - 1 / HEOS.
SatL->rhomolar()) / (1 / HEOS.
SatV->rhomolar() - 1 / HEOS.
SatL->rhomolar());
2473 Q = (value - HEOS.
SatL->smolar()) / (HEOS.
SatV->smolar() - HEOS.
SatL->smolar());
2476 Q = (value - HEOS.
SatL->hmolar()) / (HEOS.
SatV->hmolar() - HEOS.
SatL->hmolar());
2479 Q = (value - HEOS.
SatL->umolar()) / (HEOS.
SatV->umolar() - HEOS.
SatL->umolar());
2558 throw ValueError(
"supercritical temp but other invalid for now");
2567 dT_dtau = -pow(
T, 2) / Tr, R = HEOS->
gas_constant(), delta = rho / rhor, tau = Tr /
T;
2650 int nTau = 0, nDelta = 1;
2674 double call(
double rhomolar)
override {
2680 double deriv(
double rhomolar)
override {
2684 double second_deriv(
double rhomolar)
override {
2686 return R_u *
T /
POW2(rhor)
2690 dpdrho_resid resid(
this,
T,
p);
2694 light =
Halley(resid, 1e-6, 1e-8, 100);
2695 double d2pdrho2__constT = resid.deriv(light);
2696 if (d2pdrho2__constT > 0) {
2700 }
catch (std::exception& e) {
2702 std::cout << e.what() <<
'\n';
2711 for (std::size_t counter = 0; counter <= 100; counter++) {
2713 double curvature = resid.deriv(rho);
2714 if (curvature > 0) {
2729 for (
double omega = 0.7; omega > 0; omega -= 0.2) {
2731 resid.options.add_number(
"omega", omega);
2732 heavy =
Halley(resid, rhomax, 1e-8, 100);
2733 double d2pdrho2__constT = resid.deriv(heavy);
2734 if (d2pdrho2__constT < 0) {
2739 }
catch (std::exception& e) {
2741 std::cout << e.what() <<
'\n';
2750 double rho = rhomax;
2751 for (std::size_t counter = 0; counter <= 100; counter++) {
2753 double curvature = resid.deriv(rho);
2754 if (curvature < 0 || this->
p() < 0) {
2767 if (light > 0 && heavy > 0) {
2774 else if (light < 0 && heavy < 0) {
2775 double dpdrho_min = resid.call(1e-10);
2776 double dpdrho_max = resid.call(rhomax);
2777 if (dpdrho_max * dpdrho_min > 0) {
2798 rhor(
HEOS->get_reducing_state().rhomolar),
2801 double call(
double rhomolar)
override {
2805 return (peos -
p) /
p;
2807 double deriv(
double rhomolar)
override {
2831 b +=
mole_fractions[i] * 0.08664034999649577215890158147700 * R * Tc / pc;
2854 double rho_liq = -1, rho_vap = -1;
2855 if (
p > p_at_rhomax_stationary) {
2857 for (; counter <= 10; counter++) {
2860 if (p_at_rhomax <
p) {
2861 rhomolar_max *= 1.05;
2870 if (
p < p_at_rhomin_stationary) {
2875 if (rho_vap > 0 && rho_liq > 0) {
2877 if (std::abs(rho_vap - rho_liq) < 1e-10) {
2884 if (gibbsmolar_liq < gibbsmolar_vap) {
2890 }
else if (rho_vap < 0 && rho_liq > 0) {
2893 }
else if (rho_vap > 0 && rho_liq < 0) {
2919 if (rhomolar_guess < 0)
2926 if (rhomolar_guess < 0 || !
ValidNumber(rhomolar_guess))
2940 throw ValueError(
"Liquid density is invalid");
2942 }
catch (std::exception&) {
2986 const double pTarget =
p;
2991 double rhoMolarFallback = NAN;
2997 double rhoLo = rhoHi;
2998 bool bracketed =
false;
2999 for (
int i = 0; i < 400; ++i) {
3001 if (rhoLo <= 1e-10) {
3011 double fa = f(rhoLo), fb = f(rhoHi);
3013 boost::math::uintmax_t max_iter = 100;
3015 boost::math::tools::toms748_solve(f, rhoLo, rhoHi, fa, fb, boost::math::tools::eps_tolerance<double>(48), max_iter);
3016 const double rho = 0.5 * (bracket.first + bracket.second);
3018 rhoMolarFallback = rho;
3024 if (
ValidNumber(rhoMolarFallback) && rhoMolarFallback > 0) {
3029 return rhoMolarFallback;
3031 throw ValueError(
format(
"solver_rho_Tp could not find a supercritical_liquid density for T=%Lg, p=%Lg",
T, pTarget));
3045 if (dpdrho < 0 || d2pdrho2 < 0) {
3053 if (dpdrho < 0 || d2pdrho2 > 0) {
3060 }
catch (std::exception& e) {
3074 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,
3075 rhomolar_guess, e.what()));
3081 for (std::size_t i = 0; i <
components.size(); ++i) {
3083 CoolPropDbl m_i = 0.480 + 1.574 * acentric_i - 0.176 * pow(acentric_i, 2);
3084 CoolPropDbl b_i = 0.08664034999649577215890158147700 * R_u * Tci / pci;
3087 CoolPropDbl a_i = 0.42748023335403414043900347952220 * pow(R_u * Tci, 2) / pci * pow(1 + m_i * (1 - sqrt(
T / Tci)), 2);
3089 for (std::size_t j = 0; j <
components.size(); ++j) {
3091 CoolPropDbl m_j = 0.480 + 1.574 * acentric_j - 0.176 * pow(acentric_j, 2);
3093 CoolPropDbl a_j = 0.42748023335403414043900347952220 * pow(R_u * Tcj, 2) / pcj * pow(1 + m_j * (1 - sqrt(
T / Tcj)), 2);
3111 double Z0 = NAN, Z1 = NAN, Z2 = NAN;
3113 solve_cubic(1, -1, A - B - B * B, -A * B, Nsolns, Z0, Z1, Z2);
3124 if (rhomolar0 > 0 && rhomolar1 <= 0 && rhomolar2 <= 0) {
3127 if (rhomolar0 <= 0 && rhomolar1 > 0 && rhomolar2 <= 0) {
3130 if (rhomolar0 <= 0 && rhomolar1 <= 0 && rhomolar2 > 0) {
3145 throw ValueError(
"Bad phase to solver_rho_Tp_SRK");
3183 return R_u *
T * (1 +
tau * (da0_dTau + dar_dTau) +
delta * dar_dDelta);
3187 std::cout <<
format(
"HelmholtzEOSMixtureBackend::calc_hmolar: 2phase: %d T: %g rhomomolar: %g",
isTwoPhase(),
_T,
_rhomolar) <<
'\n';
3189 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
3231 return R_u * (
tau * (da0_dTau + dar_dTau) - a0 - ar);
3235 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
3260 _smolar = R_u * (
_tau.
pt() * (da0_dTau + dar_dTau) - a0 - ar);
3278 return R_u *
T *
tau * (da0_dTau + dar_dTau);
3282 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
3322 return static_cast<double>(
_cvmolar);
3339 * (-pow(
_tau.
pt(), 2) * (d2ar_dTau2 + d2a0_dTau2)
3341 / (1 + 2 *
_delta.
pt() * dar_dDelta + pow(
_delta.
pt(), 2) * d2ar_dDelta2));
3343 return static_cast<double>(
_cpmolar);
3355 return R_u * (1 + (-pow(
_tau.
pt(), 2)) * d2a0_dTau2);
3360 return SatL->speed_sound();
3362 return SatV->speed_sound();
3364 throw ValueError(
format(
"Speed of sound is not defined for two-phase states because it depends on the distribution of phases."));
3384 - pow(1 +
_delta.
pt() * dar_dDelta -
_delta.
pt() *
_tau.
pt() * d2ar_dDelta_dTau, 2) / (pow(
_tau.
pt(), 2) * (d2ar_dTau2 + d2a0_dTau2))));
3407 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
3433 for (std::size_t i = 0; i <
components.size(); ++i) {
3449 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
3476 throw ValueError(
format(
"fugacity_coefficient is not well-defined in the two-phase region; evaluate on SatL or SatV instead"));
3487 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
3489 localFug =
SatL->fugacity(i);
3491 localFug =
SatV->fugacity(i);
3493 localFug =
_Q *
SatV->fugacity(i) + (1 -
_Q) *
SatL->fugacity(i);
3508 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for the two-phase properties"));
3510 return SatL->chemical_potential(i);
3512 return SatV->chemical_potential(i);
3514 return _Q *
SatV->chemical_potential(i) + (1 -
_Q) *
SatL->chemical_potential(i);
3520 double dna0_dni__const_T_V_nj =
3522 return gas_constant() *
T() * (dna0_dni__const_T_V_nj + dnar_dni__const_T_V_nj);
3546 throw ValueError(
"Mole fractions must be set before calling calc_reducing_state");
3554 deriv_counter.fetch_add(1, std::memory_order_relaxed);
3555 bool cache_values =
true;
3576 bool cache_values =
false;
3578 return derivs.
get(nTau, nDelta);
3585 throw ValueError(
"No alpha0 derivatives are available");
3596 double taustar = Tc / Tr *
tau, deltastar = rhor / rhomolarc *
delta;
3597 if (nTau == 0 && nDelta == 0) {
3598 val = E.
base0(taustar, deltastar);
3599 }
else if (nTau == 0 && nDelta == 1) {
3601 }
else if (nTau == 1 && nDelta == 0) {
3603 }
else if (nTau == 0 && nDelta == 2) {
3605 }
else if (nTau == 1 && nDelta == 1) {
3607 }
else if (nTau == 2 && nDelta == 0) {
3609 }
else if (nTau == 0 && nDelta == 3) {
3611 }
else if (nTau == 1 && nDelta == 2) {
3613 }
else if (nTau == 2 && nDelta == 1) {
3615 }
else if (nTau == 3 && nDelta == 0) {
3620 val *= pow(rhor / rhomolarc, nDelta);
3621 val /= pow(Tr / Tc, nTau);
3624 throw ValueError(
format(
"calc_alpha0_deriv_nocache returned invalid number with inputs nTau: %d, nDelta: %d, tau: %Lg, delta: %Lg", nTau,
3633 CoolPropDbl tau_i = NAN, delta_i = NAN, rho_ci = NAN, T_ci = NAN;
3635 for (
unsigned int i = 0; i <
N; ++i) {
3640 tau_i = T_ci *
tau / Tr;
3641 delta_i =
delta * rhor / rho_ci;
3647 if (nTau == 0 && nDelta == 0) {
3650 }
else if (nTau == 0 && nDelta == 1) {
3652 }
else if (nTau == 1 && nDelta == 0) {
3654 }
else if (nTau == 0 && nDelta == 2) {
3655 summer +=
mole_fractions[i] * Rratio * pow(rhor / rho_ci, 2) *
components[i].EOS().d2alpha0_dDelta2(tau_i, delta_i);
3656 }
else if (nTau == 1 && nDelta == 1) {
3657 summer +=
mole_fractions[i] * Rratio * rhor / rho_ci * T_ci / Tr *
components[i].EOS().d2alpha0_dDelta_dTau(tau_i, delta_i);
3658 }
else if (nTau == 2 && nDelta == 0) {
3672 throw ValueError(
"No alpha0 derivatives are available");
3683 double taustar = Tc / Tr *
tau, deltastar = rhor / rhomolarc *
delta;
3684 return E.
alpha0.
all(taustar, deltastar,
false);
3690 CoolPropDbl summer_00 = 0, summer_01 = 0, summer_10 = 0, summer_02 = 0, summer_11 = 0, summer_20 = 0;
3691 CoolPropDbl tau_i = NAN, delta_i = NAN, rho_ci = NAN, T_ci = NAN;
3693 for (
unsigned int i = 0; i <
N; ++i) {
3698 tau_i = T_ci *
tau / Tr;
3699 delta_i =
delta * rhor / rho_ci;
3706 auto pure =
components[i].EOS().alpha0.all(tau_i, delta_i);
3711 summer_01 +=
mole_fractions[i] * Rratio * rhor / rho_ci * pure.dalphar_ddelta;
3712 summer_10 +=
mole_fractions[i] * Rratio * T_ci / Tr * pure.dalphar_dtau;
3713 summer_02 +=
mole_fractions[i] * Rratio * pow(rhor / rho_ci, 2) * pure.d2alphar_ddelta2;
3714 summer_11 +=
mole_fractions[i] * Rratio * rhor / rho_ci * T_ci / Tr * pure.d2alphar_ddelta_dtau;
3715 summer_20 +=
mole_fractions[i] * Rratio * pow(T_ci / Tr, 2) * pure.d2alphar_dtau2;
3719 ders.alphar = summer_00;
3720 ders.dalphar_ddelta = summer_01;
3721 ders.dalphar_dtau = summer_10;
3722 ders.d2alphar_ddelta2 = summer_02;
3723 ders.d2alphar_ddelta_dtau = summer_11;
3724 ders.d2alphar_dtau2 = summer_20;
3793 const int nTau = 0, nDelta = 0;
3797 const int nTau = 0, nDelta = 1;
3801 const int nTau = 1, nDelta = 0;
3805 const int nTau = 0, nDelta = 2;
3809 const int nTau = 1, nDelta = 1;
3813 const int nTau = 2, nDelta = 0;
3817 const int nTau = 0, nDelta = 3;
3821 const int nTau = 1, nDelta = 2;
3825 const int nTau = 2, nDelta = 1;
3829 const int nTau = 3, nDelta = 0;
3837 dTdP_sat =
T() * (1 /
SatV.rhomolar() - 1 /
SatL.rhomolar()) / (
SatV.hmolar() -
SatL.hmolar());
3842 std::vector<CoolPropDbl> x =
SatL.get_mole_fractions();
3843 std::vector<CoolPropDbl> y =
SatV.get_mole_fractions();
3847 for (std::size_t i = 0; i <
N; ++i) {
3855 }
else if (
_Q == 1) {
3856 for (std::size_t i = 0; i <
N; ++i) {
3865 throw ValueError(
format(
"calc_first_saturation_deriv requires Q==0 (bubble) or Q==1 (dew) for mixtures; got Q=%g",
_Q));
3867 dTdP_sat = -dQ_dPsat / dQ_dTsat;
3871 if (Of1 ==
iT && Wrt1 ==
iP) {
3873 }
else if (Of1 ==
iP && Wrt1 ==
iT) {
3874 return 1 / dTdP_sat;
3877 else if (Wrt1 ==
iT) {
3881 else if (Wrt1 ==
iP) {
3889 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_first_saturation_deriv"));
3893 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_second_saturation_deriv"));
3894 if (Wrt1 ==
iP && Wrt2 ==
iP) {
3909 CoolPropDbl ddT_dTdp_along_sat_constp = (DELTAh * (
_T * dDELTAv_dT_constp + DELTAv) -
_T * DELTAv * dDELTAh_dT_constp) /
POW2(DELTAh);
3910 CoolPropDbl ddp_dTdp_along_sat_constT = (DELTAh * (
_T * dDELTAv_dp_constT) -
_T * DELTAv * dDELTAh_dp_constT) /
POW2(DELTAh);
3912 double ddp_dydpsigma = d2ydp2_constT + dydT_constp * ddp_dTdp_along_sat_constT + d2ydTdp * dTdp_along_sat;
3913 double ddT_dydpsigma = d2ydTdp + dydT_constp * ddT_dTdp_along_sat_constp + d2ydT2_constp * dTdp_along_sat;
3914 return ddp_dydpsigma + ddT_dydpsigma * dTdp_along_sat;
3916 throw ValueError(
format(
"Currently, only possible to take second saturation derivative w.r.t. P (both times)"));
3922 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_second_two_phase_deriv"));
3937 CoolPropDbl numerator = 1 /
SatV->keyed_output(rho_key) - 1 /
SatL->keyed_output(rho_key);
3939 CoolPropDbl dnumerator = -1 /
POW2(
SatV->keyed_output(rho_key)) * drhoV_dp_sat + 1 /
POW2(
SatL->keyed_output(rho_key)) * drhoL_dp_sat;
3940 CoolPropDbl ddenominator = dhV_dp_sat - dhL_dp_sat;
3941 CoolPropDbl d_dvdh_dp__consth = (denominator * dnumerator - numerator * ddenominator) /
POW2(denominator);
3942 return -
POW2(
rhomolar()) * d_dvdh_dp__consth + dv_dh_constp * (-2 *
rhomolar()) * drhomolar_dp__consth;
3944 && ((Wrt1 ==
iHmass && Constant1 ==
iP && Wrt2 ==
iP && Constant2 ==
iHmass)
3945 || (Wrt2 ==
iHmass && Constant2 ==
iP && Wrt1 ==
iP && Constant1 ==
iHmass))) {
3957 CoolPropDbl numerator = 1 /
SatV->keyed_output(rho_key) - 1 /
SatL->keyed_output(rho_key);
3959 CoolPropDbl dnumerator = -1 /
POW2(
SatV->keyed_output(rho_key)) * drhoV_dp_sat + 1 /
POW2(
SatL->keyed_output(rho_key)) * drhoL_dp_sat;
3960 CoolPropDbl ddenominator = dhV_dp_sat - dhL_dp_sat;
3961 CoolPropDbl d_dvdh_dp__consth = (denominator * dnumerator - numerator * ddenominator) /
POW2(denominator);
3962 return -
POW2(rho) * d_dvdh_dp__consth + dv_dh_constp * (-2 * rho) * drho_dp__consth;
3968 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_first_two_phase_deriv"));
3982 CoolPropDbl dvdp_h = dvL_dp + dxdp_h * (1 /
SatV->rhomolar() - 1 /
SatL->rhomolar()) +
Q() * (dvV_dp - dvL_dp);
3993 CoolPropDbl dvdp_h = dvL_dp + dxdp_h * (1 /
SatV->rhomass() - 1 /
SatL->rhomass()) +
Q() * (dvV_dp - dvL_dp);
3996 throw ValueError(
"These inputs are not supported to calc_first_two_phase_deriv");
4003 bool drho_dh__p =
false;
4004 bool drho_dp__h =
false;
4005 bool rho_spline =
false;
4025 throw ValueError(
"These inputs are not supported to calc_first_two_phase_deriv");
4028 if (!this->
SatL || !this->
SatV)
throw ValueError(
format(
"The saturation properties are needed for calc_first_two_phase_deriv_splined"));
4036 shared_ptr<HelmholtzEOSMixtureBackend> Liq = std::make_shared<HelmholtzEOSMixtureBackend>(this->
get_components());
4037 shared_ptr<HelmholtzEOSMixtureBackend> End = std::make_shared<HelmholtzEOSMixtureBackend>(this->
get_components());
4041 Liq->update_DmolarT_direct(
SatL->rhomolar(),
SatL->T());
4056 CoolPropDbl Abracket = (2 * rho_liq - 2 * rho_end + Delta_end * (drho_dh_liq__constp + drho_dh_end));
4058 CoolPropDbl b = 3 /
POW2(Delta_end) * (-rho_liq + rho_end) - 1 / Delta_end * (drho_dh_end + 2 * drho_dh_liq__constp);
4092 CoolPropDbl d_Delta_end_dp__consth = x_end * (dhV_dp_sat - dhL_dp_sat);
4096 CoolPropDbl d_Abracket_dp_consth = (2 * drhoL_dp_sat - 2 * drho_dp_end + Delta_end * (d2rhodhdp_liq + d2rhodhdp_end)
4097 + d_Delta_end_dp__consth * (drho_dh_liq__constp + drho_dh_end));
4098 CoolPropDbl da_dp = 1 /
POW3(Delta_end) * d_Abracket_dp_consth + Abracket * (-3 /
POW4(Delta_end) * d_Delta_end_dp__consth);
4099 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)
4100 + (1 /
POW2(Delta_end) * d_Delta_end_dp__consth) * (drho_dh_end + 2 * drho_dh_liq__constp)
4101 - (1 / Delta_end) * (d2rhodhdp_end + 2 * d2rhodhdp_liq);
4106 (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;
4109 throw ValueError(
"Something went wrong in HelmholtzEOSMixtureBackend::calc_first_two_phase_deriv_splined");
4119 Eigen::MatrixXd Lstar, Mstar;
4121 std::vector<double> call(
const std::vector<double>& tau_delta)
override {
4127 std::vector<double> o(2);
4128 o[0] = Lstar.determinant();
4129 o[1] = Mstar.determinant();
4132 std::vector<std::vector<double>> Jacobian(
const std::vector<double>& x)
override {
4133 std::size_t
N = x.size();
4134 std::vector<std::vector<double>> J(
N, std::vector<double>(
N, 0));
4140 J[0][0] = (adjL * dLdTau).trace();
4141 J[0][1] = (adjL * dLdDelta).trace();
4142 J[1][0] = (adjM * dMdTau).trace();
4143 J[1][1] = (adjM * dMdDelta).trace();
4147 std::vector<std::vector<double>> numerical_Jacobian(
const std::vector<double>& x) {
4148 std::size_t
N = x.size();
4149 std::vector<double> rplus, rminus, xp;
4150 std::vector<std::vector<double>> J(
N, std::vector<double>(
N, 0));
4152 double epsilon = 0.0001;
4155 for (std::size_t i = 0; i <
N; ++i) {
4159 xp[i] -= 2 * epsilon;
4162 for (std::size_t j = 0; j <
N; ++j) {
4163 J[j][i] = (rplus[j] - rminus[j]) / (2 * epsilon);
4166 std::cout << J[0][0] <<
" " << J[0][1] <<
'\n';
4167 std::cout << J[1][0] <<
" " << J[1][1] <<
'\n';
4172 std::vector<double> x, tau_delta(2);
4218 _deriv = (adjL * dLdTau).trace();
4225 dadjLstardTau = adjugate_derivative(Lstar, dLstardTau);
4226 _second_deriv = (dLstardTau * dadjLstardTau + adjL * d2LstardTau2).trace();
4274 tau_new =
tau +
R_tau * cos(theta);
4281 double call(
double theta)
override {
4282 double tau_new = NAN, delta_new = NAN;
4290 double L1 =
Lstar.determinant();
4299 return -
R_tau * sin(theta) * dL1_dtau +
R_delta * cos(theta) * dL1_ddelta;
4305 for (
int i = 0; i < 300; ++i) {
4347 double p_MPa =
HEOS.
p() / 1e6;
4350 double tau_new = NAN, delta_new = NAN;
4373 this->tau = tau_new;
4374 this->delta = delta_new;
4378 this->spinodal_values.
tau.push_back(tau_new);
4379 this->spinodal_values.
delta.push_back(delta_new);
4380 this->spinodal_values.
M1.push_back(M1);
4388 L1star = Lstar.determinant();
4389 M1star = Mstar.determinant();
4404 double delta0 = _HUGE, tau0 = _HUGE;
4405 critical_state->get_critical_point_starting_values(delta0, tau0);
4412 resid_L0.
call(tau0);
4414 while (resid_L0.
deriv(tau0) > 0 && bump_count < 3) {
4418 double tau_L0 =
Halley(resid_L0, tau0, 1e-10, 100);
4425 double R_delta = 0, R_tau = 0;
4437 const double rhomolar_guess) {
4440 if (w.size() != z.size()) {
4441 throw ValueError(
format(
"Trial composition vector size [%d] is not the same as bulk composition [%d]", w.size(), z.size()));
4450 for (std::size_t i = 0; i < w.size(); ++i) {
4459 bool find_critical_points =
false;
4466 if (!reference_state.compare(
"IIR")) {
4467 if (HEOS.
Ttriple() > 273.15) {
4468 throw ValueError(
format(
"Cannot use IIR reference state; Ttriple [%Lg] is greater than 273.15 K", HEOS.
Ttriple()));
4473 double deltah = HEOS.
hmass() - 200000;
4474 double deltas = HEOS.
smass() - 1000;
4480 std::cout <<
format(
"set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
4482 }
else if (!reference_state.compare(
"ASHRAE")) {
4483 if (HEOS.
Ttriple() > 233.15) {
4484 throw ValueError(
format(
"Cannot use ASHRAE reference state; Ttriple [%Lg] is greater than than 233.15 K", HEOS.
Ttriple()));
4489 double deltah = HEOS.
hmass() - 0;
4490 double deltas = HEOS.
smass() - 0;
4496 std::cout <<
format(
"set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
4498 }
else if (!reference_state.compare(
"NBP")) {
4500 throw ValueError(
format(
"Cannot use NBP reference state; p_triple [%Lg Pa] is greater than than 101325 Pa", HEOS.
p_triple()));
4505 double deltah = HEOS.
hmass() - 0;
4506 double deltas = HEOS.
smass() - 0;
4512 std::cout <<
format(
"set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
4514 }
else if (!reference_state.compare(
"DEF")) {
4516 }
else if (!reference_state.compare(
"RESET")) {
4519 throw ValueError(
format(
"reference state string is invalid: [%s]", reference_state.c_str()));
4539 double deltah =
hmolar - hmolar0;
4540 double deltas =
smolar - smolar0;
4548 const std::string& ref) {
4558 shared_ptr<CoolProp::HelmholtzEOSBackend> HEOS = std::make_shared<CoolProp::HelmholtzEOSBackend>(component);
4565 double f = (HEOS->name() ==
"Water" || HEOS->name() ==
"CarbonDioxide") ? 1.00001 : 1.0;
4587 if (!HEOS->is_pure()) {