2# ifndef _CRT_SECURE_NO_WARNINGS
3# define _CRT_SECURE_NO_WARNINGS
27#include "externals/IF97/IF97.h"
30std::size_t
strcmp(
const std::string& s,
const std::string& e) {
33std::size_t
strcmp(
const std::string& s,
const char* e) {
36std::size_t
strcmp(
const char* e,
const std::string& s) {
41void strcpy(std::string& s,
const std::string& e) {
45shared_ptr<CoolProp::HelmholtzEOSBackend>
Water,
Air;
79#if !defined(NO_FMTLIB) && FMT_VERSION >= 90000
80int format_as(
givens given) {
81 return fmt::underlying(given);
85void _HAPropsSI_inputs(
double p,
const std::vector<givens>& input_keys,
const std::vector<double>& input_vals,
double& T,
double& psi_w);
101static double epsilon = 0.621945, R_bar = 8.314472;
102static int FlagUseVirialCorrelations = 0, FlagUseIsothermCompressCorrelation = 0, FlagUseIdealGasEnthalpyCorrelations = 0;
106static inline bool check_bounds(
const givens prop,
const double& value,
double& min_val,
double& max_val) {
121 min_val = -143.15 + 273.15;
122 max_val = 350 + 273.15;
141 bool ret = !((value < min_val) || (value > max_val));
146static double MM_Air(
void) {
150static double MM_Water(
void) {
154static double B_Air(
double T) {
157 Air->update_DmolarT_direct(1e-12, T);
158 Air->unspecify_phase();
161static double dBdT_Air(
double T) {
164 Air->update_DmolarT_direct(1e-12, T);
165 Air->unspecify_phase();
168static double B_Water(
double T) {
171 Water->update_DmolarT_direct(1e-12, T);
172 Water->unspecify_phase();
175static double dBdT_Water(
double T) {
178 Water->update_DmolarT_direct(1e-12, T);
179 Water->unspecify_phase();
182static double C_Air(
double T) {
185 Air->update_DmolarT_direct(1e-12, T);
186 Air->unspecify_phase();
189static double dCdT_Air(
double T) {
192 Air->update_DmolarT_direct(1e-12, T);
193 Air->unspecify_phase();
196static double C_Water(
double T) {
199 Water->update_DmolarT_direct(1e-12, T);
200 Water->unspecify_phase();
203static double dCdT_Water(
double T) {
206 Water->update_DmolarT_direct(1e-12, T);
207 Water->unspecify_phase();
211 if (flag == 0 || flag == 1) {
212 FlagUseVirialCorrelations = flag;
214 printf(
"UseVirialCorrelations takes an integer, either 0 (no) or 1 (yes)\n");
218 if (flag == 0 || flag == 1) {
219 FlagUseIsothermCompressCorrelation = flag;
221 printf(
"UseIsothermCompressCorrelation takes an integer, either 0 (no) or 1 (yes)\n");
225 if (flag == 0 || flag == 1) {
226 FlagUseIdealGasEnthalpyCorrelations = flag;
228 printf(
"UseIdealGasEnthalpyCorrelations takes an integer, either 0 (no) or 1 (yes)\n");
231static double Brent_HAProps_W(
givens OutputKey,
double p,
givens In1Name,
double Input1,
double TargetVal,
double W_min,
double W_max) {
240 double Input1, TargetVal;
241 std::vector<givens> input_keys;
242 std::vector<double> input_vals;
245 BrentSolverResids(
givens OutputKey,
double p,
givens In1Key,
double Input1,
double TargetVal)
246 : OutputKey(OutputKey), p(p), In1Key(In1Key), Input1(Input1), TargetVal(TargetVal) {
247 input_keys.resize(2);
248 input_keys[0] = In1Key;
250 input_vals.resize(2);
251 input_vals[0] = Input1;
254 double call(
double W) {
256 double T = _HUGE, psi_w = _HUGE;
259 std::cout <<
format(
"T: %g K, psi_w %g\n", T, psi_w);
265 BrentSolverResids BSR = BrentSolverResids(OutputKey, p, In1Name, Input1, TargetVal);
269 double r_min = BSR.call(W_min);
271 double r_max = BSR.call(W_max);
273 if (!W_min_valid && !W_max_valid) {
274 throw CoolProp::ValueError(
format(
"Both W_min [%g] and W_max [%g] yield invalid output values in Brent_HAProps_W", W_min, W_max).c_str());
275 }
else if (W_min_valid && !W_max_valid) {
276 while (!W_max_valid) {
278 W_max = 0.95 * W_max + 0.05 * W_min;
279 r_max = BSR.call(W_max);
282 }
else if (!W_min_valid && W_max_valid) {
283 while (!W_min_valid) {
285 W_min = 0.95 * W_min + 0.05 * W_max;
286 r_min = BSR.call(W_min);
291 if (r_min * r_max > 0) {
292 if (std::abs(r_min) < std::abs(r_max)) {
302static double Brent_HAProps_T(
givens OutputKey,
double p,
givens In1Name,
double Input1,
double TargetVal,
double T_min,
double T_max) {
310 double Input1, TargetVal;
311 std::vector<givens> input_keys;
312 std::vector<double> input_vals;
315 BrentSolverResids(
givens OutputKey,
double p,
givens In1Key,
double Input1,
double TargetVal)
316 : OutputKey(OutputKey), p(p), In1Key(In1Key), Input1(Input1), TargetVal(TargetVal) {
317 input_keys.resize(2);
318 input_keys[0] = In1Key;
320 input_vals.resize(2);
321 input_vals[0] = Input1;
324 double call(
double T_drybulb) {
328 return val - TargetVal;
332 BrentSolverResids BSR = BrentSolverResids(OutputKey, p, In1Name, Input1, TargetVal);
336 double r_min = BSR.call(T_min);
338 double r_max = BSR.call(T_max);
340 if (!T_min_valid && !T_max_valid) {
341 throw CoolProp::ValueError(
format(
"Both T_min [%g] and T_max [%g] yield invalid output values in Brent_HAProps_T", T_min, T_max).c_str());
342 }
else if (T_min_valid && !T_max_valid) {
343 while (!T_max_valid) {
345 T_max = 0.95 * T_max + 0.05 * T_min;
346 r_max = BSR.call(T_max);
349 }
else if (!T_min_valid && T_max_valid) {
350 while (!T_min_valid) {
352 T_min = 0.95 * T_min + 0.05 * T_max;
353 r_min = BSR.call(T_min);
358 if (r_min * r_max > 0) {
359 if (std::abs(r_min) < std::abs(r_max)) {
365 double mach_eps = 1e-15, tol = 1e-10;
370static double Secant_Tdb_at_saturated_W(
double psi_w,
double p,
double T_guess) {
375 double pp_water, psi_w, p;
378 BrentSolverResids(
double psi_w,
double p) : psi_w(psi_w), p(p) {
379 pp_water = psi_w * p;
381 ~BrentSolverResids() {};
383 double call(
double T) {
387 p_ws = IF97::psat97(T);
393 double pp_water_calc = f * p_ws;
394 double psi_w_calc = pp_water_calc / p;
395 return (psi_w_calc - psi_w) / psi_w;
399 BrentSolverResids Resids(psi_w, p);
406 }
catch (std::exception& ) {
477static double _B_aw(
double T) {
480 double a[] = {0, 0.665687e2, -0.238834e3, -0.176755e3};
481 double b[] = {0, -0.237, -1.048, -3.183};
482 double rhobarstar = 1000, Tstar = 100;
483 return 1 / rhobarstar * (a[1] * pow(T / Tstar, b[1]) + a[2] * pow(T / Tstar, b[2]) + a[3] * pow(T / Tstar, b[3]))
487static double _dB_aw_dT(
double T) {
490 double a[] = {0, 0.665687e2, -0.238834e3, -0.176755e3};
491 double b[] = {0, -0.237, -1.048, -3.183};
492 double rhobarstar = 1000, Tstar = 100;
493 return 1 / rhobarstar / Tstar
494 * (a[1] * b[1] * pow(T / Tstar, b[1] - 1) + a[2] * b[2] * pow(T / Tstar, b[2] - 1) + a[3] * b[3] * pow(T / Tstar, b[3] - 1))
498static double _C_aaw(
double T) {
501 double c[] = {0, 0.482737e3, 0.105678e6, -0.656394e8, 0.294442e11, -0.319317e13};
502 double rhobarstar = 1000, Tstar = 1, summer = 0;
504 for (i = 1; i <= 5; i++) {
505 summer += c[i] * pow(T / Tstar, 1 - i);
507 return 1.0 / rhobarstar / rhobarstar * summer / 1e6;
510static double _dC_aaw_dT(
double T) {
513 double c[] = {0, 0.482737e3, 0.105678e6, -0.656394e8, 0.294442e11, -0.319317e13};
514 double rhobarstar = 1000, Tstar = 1, summer = 0;
516 for (i = 2; i <= 5; i++) {
517 summer += c[i] * (1 - i) * pow(T / Tstar, -i);
519 return 1.0 / rhobarstar / rhobarstar / Tstar * summer / 1e6;
522static double _C_aww(
double T) {
525 double d[] = {0, -0.1072887e2, 0.347804e4, -0.383383e6, 0.334060e8};
526 double rhobarstar = 1, Tstar = 1, summer = 0;
528 for (i = 1; i <= 4; i++) {
529 summer += d[i] * pow(T / Tstar, 1 - i);
531 return -1.0 / rhobarstar / rhobarstar * exp(summer) / 1e6;
534static double _dC_aww_dT(
double T) {
537 double d[] = {0, -0.1072887e2, 0.347804e4, -0.383383e6, 0.334060e8};
538 double rhobarstar = 1, Tstar = 1, summer1 = 0, summer2 = 0;
540 for (i = 1; i <= 4; i++) {
541 summer1 += d[i] * pow(T / Tstar, 1 - i);
543 for (i = 2; i <= 4; i++) {
544 summer2 += d[i] * (1 - i) * pow(T / Tstar, -i);
546 return -1.0 / rhobarstar / rhobarstar / Tstar * exp(summer1) * summer2
550static double B_m(
double T,
double psi_w) {
552 double B_aa, B_ww, B_aw;
553 if (FlagUseVirialCorrelations == 1) {
554 B_aa = -0.000721183853646 + 1.142682674467e-05 * T - 8.838228412173e-08 * pow(T, 2) + 4.104150642775e-10 * pow(T, 3)
555 - 1.192780880645e-12 * pow(T, 4) + 2.134201312070e-15 * pow(T, 5) - 2.157430412913e-18 * pow(T, 6) + 9.453830907795e-22 * pow(T, 7);
556 B_ww = -10.8963128394 + 2.439761625859e-01 * T - 2.353884845100e-03 * pow(T, 2) + 1.265864734412e-05 * pow(T, 3)
557 - 4.092175700300e-08 * pow(T, 4) + 7.943925411344e-11 * pow(T, 5) - 8.567808759123e-14 * pow(T, 6) + 3.958203548563e-17 * pow(T, 7);
564 return pow(1 - psi_w, 2) * B_aa + 2 * (1 - psi_w) * psi_w * B_aw + psi_w * psi_w * B_ww;
567static double dB_m_dT(
double T,
double psi_w) {
569 double dB_dT_aa, dB_dT_ww, dB_dT_aw;
570 if (FlagUseVirialCorrelations) {
571 dB_dT_aa = 1.65159324353e-05 - 3.026130954749e-07 * T + 2.558323847166e-09 * pow(T, 2) - 1.250695660784e-11 * pow(T, 3)
572 + 3.759401946106e-14 * pow(T, 4) - 6.889086380822e-17 * pow(T, 5) + 7.089457032972e-20 * pow(T, 6)
573 - 3.149942145971e-23 * pow(T, 7);
574 dB_dT_ww = 0.65615868848 - 1.487953162679e-02 * T + 1.450134660689e-04 * pow(T, 2) - 7.863187630094e-07 * pow(T, 3)
575 + 2.559556607010e-09 * pow(T, 4) - 4.997942221914e-12 * pow(T, 5) + 5.417678681513e-15 * pow(T, 6)
576 - 2.513856275241e-18 * pow(T, 7);
578 dB_dT_aa = dBdT_Air(T);
579 dB_dT_ww = dBdT_Water(T);
581 dB_dT_aw = _dB_aw_dT(T);
582 return pow(1 - psi_w, 2) * dB_dT_aa + 2 * (1 - psi_w) * psi_w * dB_dT_aw + psi_w * psi_w * dB_dT_ww;
585static double C_m(
double T,
double psi_w) {
587 double C_aaa, C_www, C_aww, C_aaw;
588 if (FlagUseVirialCorrelations) {
589 C_aaa = 1.29192158975e-08 - 1.776054020409e-10 * T + 1.359641176409e-12 * pow(T, 2) - 6.234878717893e-15 * pow(T, 3)
590 + 1.791668730770e-17 * pow(T, 4) - 3.175283581294e-20 * pow(T, 5) + 3.184306136120e-23 * pow(T, 6) - 1.386043640106e-26 * pow(T, 7);
591 C_www = -0.580595811134 + 1.365952762696e-02 * T - 1.375986293288e-04 * pow(T, 2) + 7.687692259692e-07 * pow(T, 3)
592 - 2.571440816920e-09 * pow(T, 4) + 5.147432221082e-12 * pow(T, 5) - 5.708156494894e-15 * pow(T, 6) + 2.704605721778e-18 * pow(T, 7);
599 return pow(1 - psi_w, 3) * C_aaa + 3 * pow(1 - psi_w, 2) * psi_w * C_aaw + 3 * (1 - psi_w) * psi_w * psi_w * C_aww + pow(psi_w, 3) * C_www;
602static double dC_m_dT(
double T,
double psi_w) {
605 double dC_dT_aaa, dC_dT_www, dC_dT_aww, dC_dT_aaw;
607 if (FlagUseVirialCorrelations) {
608 dC_dT_aaa = -2.46582342273e-10 + 4.425401935447e-12 * T - 3.669987371644e-14 * pow(T, 2) + 1.765891183964e-16 * pow(T, 3)
609 - 5.240097805744e-19 * pow(T, 4) + 9.502177003614e-22 * pow(T, 5) - 9.694252610339e-25 * pow(T, 6)
610 + 4.276261986741e-28 * pow(T, 7);
611 dC_dT_www = 0.0984601196142 - 2.356713397262e-03 * T + 2.409113323685e-05 * pow(T, 2) - 1.363083778715e-07 * pow(T, 3)
612 + 4.609623799524e-10 * pow(T, 4) - 9.316416405390e-13 * pow(T, 5) + 1.041909136255e-15 * pow(T, 6)
613 - 4.973918480607e-19 * pow(T, 7);
615 dC_dT_aaa = dCdT_Air(T);
616 dC_dT_www = dCdT_Water(T);
618 dC_dT_aaw = _dC_aaw_dT(T);
619 dC_dT_aww = _dC_aww_dT(T);
620 return pow(1 - psi_w, 3) * dC_dT_aaa + 3 * pow(1 - psi_w, 2) * psi_w * dC_dT_aaw + 3 * (1 - psi_w) * psi_w * psi_w * dC_dT_aww
621 + pow(psi_w, 3) * dC_dT_www;
624 return psi_w * epsilon / (1 - psi_w);
627static double HenryConstant(
double T) {
629 double p_ws, beta_N2, beta_O2, beta_Ar, beta_a, tau, Tr, Tc = 647.096;
632 p_ws = IF97::psat97(T);
633 beta_N2 = p_ws * exp(-9.67578 / Tr + 4.72162 * pow(tau, 0.355) / Tr + 11.70585 * pow(Tr, -0.41) * exp(tau));
634 beta_O2 = p_ws * exp(-9.44833 / Tr + 4.43822 * pow(tau, 0.355) / Tr + 11.42005 * pow(Tr, -0.41) * exp(tau));
635 beta_Ar = p_ws * exp(-8.40954 / Tr + 4.29587 * pow(tau, 0.355) / Tr + 10.52779 * pow(Tr, -0.41) * exp(tau));
636 beta_a = 1 / (0.7812 / beta_N2 + 0.2095 / beta_O2 + 0.0093 / beta_Ar);
637 return 1 / (1.01325 * beta_a);
643 if (FlagUseIsothermCompressCorrelation) {
644 k_T = 1.6261876614E-22 * pow(T, 6) - 3.3016385196E-19 * pow(T, 5) + 2.7978984577E-16 * pow(T, 4) - 1.2672392901E-13 * pow(T, 3)
645 + 3.2382864853E-11 * pow(T, 2) - 4.4318979503E-09 * T + 2.5455947289E-07;
658 double f = 0, Rbar = 8.314371, eps = 1e-8;
659 double x1 = 0, x2 = 0, x3, y1 = 0, y2, change = _HUGE;
661 double p_ws, B_aa, B_aw, B_ww, C_aaa, C_aaw, C_aww, C_www, line1, line2, line3, line4, line5, line6, line7, line8, k_T, beta_H, LHS, RHS, psi_ws,
670 beta_H = HenryConstant(T);
687 if (FlagUseVirialCorrelations) {
688 B_aa = -0.000721183853646 + 1.142682674467e-05 * T - 8.838228412173e-08 * pow(T, 2) + 4.104150642775e-10 * pow(T, 3)
689 - 1.192780880645e-12 * pow(T, 4) + 2.134201312070e-15 * pow(T, 5) - 2.157430412913e-18 * pow(T, 6) + 9.453830907795e-22 * pow(T, 7);
690 B_ww = -10.8963128394 + 2.439761625859e-01 * T - 2.353884845100e-03 * pow(T, 2) + 1.265864734412e-05 * pow(T, 3)
691 - 4.092175700300e-08 * pow(T, 4) + 7.943925411344e-11 * pow(T, 5) - 8.567808759123e-14 * pow(T, 6) + 3.958203548563e-17 * pow(T, 7);
692 C_aaa = 1.29192158975e-08 - 1.776054020409e-10 * T + 1.359641176409e-12 * pow(T, 2) - 6.234878717893e-15 * pow(T, 3)
693 + 1.791668730770e-17 * pow(T, 4) - 3.175283581294e-20 * pow(T, 5) + 3.184306136120e-23 * pow(T, 6) - 1.386043640106e-26 * pow(T, 7);
694 C_www = -0.580595811134 + 1.365952762696e-02 * T - 1.375986293288e-04 * pow(T, 2) + 7.687692259692e-07 * pow(T, 3)
695 - 2.571440816920e-09 * pow(T, 4) + 5.147432221082e-12 * pow(T, 5) - 5.708156494894e-15 * pow(T, 6) + 2.704605721778e-18 * pow(T, 7);
708 while ((iter <= 3 || change > eps) && iter < 100) {
714 x2 = 1.00 + 0.000001;
724 psi_ws = f * p_ws / p;
727 line1 = ((1 + k_T * p_ws) * (p - p_ws) - k_T * (p * p - p_ws * p_ws) / 2.0) / (Rbar * T) * vbar_ws + log(1 - beta_H * (1 - psi_ws) * p);
728 line2 = pow(1 - psi_ws, 2) * p / (Rbar * T) * B_aa - 2 * pow(1 - psi_ws, 2) * p / (Rbar * T) * B_aw
729 - (p - p_ws - pow(1 - psi_ws, 2) * p) / (Rbar * T) * B_ww;
730 line3 = pow(1 - psi_ws, 3) * p * p / pow(Rbar * T, 2) * C_aaa
731 + (3 * pow(1 - psi_ws, 2) * (1 - 2 * (1 - psi_ws)) * p * p) / (2 * pow(Rbar * T, 2)) * C_aaw;
732 line4 = -3 * pow(1 - psi_ws, 2) * psi_ws * p * p / pow(Rbar * T, 2) * C_aww
733 - ((3 - 2 * psi_ws) * psi_ws * psi_ws * p * p - p_ws * p_ws) / (2 * pow(Rbar * T, 2)) * C_www;
734 line5 = -(pow(1 - psi_ws, 2) * (-2 + 3 * psi_ws) * psi_ws * p * p) / pow(Rbar * T, 2) * B_aa * B_ww;
735 line6 = -(2 * pow(1 - psi_ws, 3) * (-1 + 3 * psi_ws) * p * p) / pow(Rbar * T, 2) * B_aa * B_aw;
736 line7 = (6 * pow(1 - psi_ws, 2) * psi_ws * psi_ws * p * p) / pow(Rbar * T, 2) * B_ww * B_aw
737 - (3 * pow(1 - psi_ws, 4) * p * p) / (2 * pow(Rbar * T, 2)) * B_aa * B_aa;
738 line8 = -(2 * pow(1 - psi_ws, 2) * psi_ws * (-2 + 3 * psi_ws) * p * p) / pow(Rbar * T, 2) * B_aw * B_aw
739 - (p_ws * p_ws - (4 - 3 * psi_ws) * pow(psi_ws, 3) * p * p) / (2 * pow(Rbar * T, 2)) * B_ww * B_ww;
740 RHS = line1 + line2 + line3 + line4 + line5 + line6 + line7 + line8;
747 x3 = x2 - y2 / (y2 - y1) * (x2 - x1);
748 change = std::abs(y2 / (y2 - y1) * (x2 - x1));
761 printf(
"Sorry, Need to update!");
764 if (!
strcmp(Code,
"GIVEN_TDP"))
766 else if (!
strcmp(Code,
"GIVEN_HUMRAT"))
768 else if (!
strcmp(Code,
"GIVEN_TWB"))
770 else if (!
strcmp(Code,
"GIVEN_RH"))
772 else if (!
strcmp(Code,
"GIVEN_ENTHALPY"))
775 fprintf(stderr,
"Code to returnHumAirCode in HumAir.c [%s] not understood", Code);
787 double mu_a, mu_w, Phi_av, Phi_va, Ma, Mw;
796 Phi_av = sqrt(2.0) / 4.0 * pow(1 + Ma / Mw, -0.5) * pow(1 + sqrt(mu_a / mu_w) * pow(Mw / Ma, 0.25), 2);
797 Phi_va = sqrt(2.0) / 4.0 * pow(1 + Mw / Ma, -0.5) * pow(1 + sqrt(mu_w / mu_a) * pow(Ma / Mw, 0.25), 2);
798 return (1 - psi_w) * mu_a / ((1 - psi_w) + psi_w * Phi_av) + psi_w * mu_w / (psi_w + (1 - psi_w) * Phi_va);
808 double mu_a, mu_w, k_a, k_w, Phi_av, Phi_va, Ma, Mw;
820 Phi_av = sqrt(2.0) / 4.0 * pow(1 + Ma / Mw, -0.5) * pow(1 + sqrt(mu_a / mu_w) * pow(Mw / Ma, 0.25), 2);
821 Phi_va = sqrt(2.0) / 4.0 * pow(1 + Mw / Ma, -0.5) * pow(1 + sqrt(mu_w / mu_a) * pow(Ma / Mw, 0.25), 2);
822 return (1 - psi_w) * k_a / ((1 - psi_w) + psi_w * Phi_av) + psi_w * k_w / (psi_w + (1 - psi_w) * Phi_va);
833 double v_bar0, v_bar = 0, R_bar = 8.314472, x1 = 0, x2 = 0, x3, y1 = 0, y2, resid, eps, Bm, Cm;
840 v_bar0 = R_bar * T / p;
849 while ((iter <= 3 || std::abs(resid) > eps) && iter < 100) {
855 x2 = v_bar0 + 0.000001;
863 resid = (p - (R_bar)*T / v_bar * (1 + Bm / v_bar + Cm / (v_bar * v_bar))) / p;
870 x3 = x2 - y2 / (y2 - y1) * (x2 - x1);
879double Pressure(
double T,
double v_bar,
double psi_w) {
880 double R_bar = 8.314472;
881 double Bm = B_m(T, psi_w);
882 double Cm = C_m(T, psi_w);
883 return (R_bar)*T / v_bar * (1 + Bm / v_bar + Cm / (v_bar * v_bar));
886 double hbar_w_0, tau, hbar_w;
888 hbar_w_0 = -0.01102303806;
891 double Tref = 473.15, vmolarref = 0.038837428192186184, href = 51885.582451893446;
895 double hoffset = href - href_EOS;
899 Water->update_DmolarT_direct(p / (R_bar * T), T);
900 Water->unspecify_phase();
909 double sbar_w, tau, R_bar;
913 double Tref = 473.15, pref = 101325, sref = 141.18297895840303;
917 double soffset = sref - sref_EOS;
923 Water->unspecify_phase();
929 double hbar_a_0, tau, hbar_a, R_bar_Lemmon;
931 hbar_a_0 = -7914.149298;
933 R_bar_Lemmon = 8.314510;
935 double Tref = 473.15, vmolarref = 0.038837428192186184, href = 13782.240592933371;
937 double tauref = 132.6312 / Tref;
939 double hoffset = href - href_EOS;
945 Air->update_DmolarT_direct(p / (R_bar * T), T);
946 Air->unspecify_phase();
951 double sbar_0_Lem, tau, sbar_a, R_bar_Lemmon = 8.314510, T0 = 273.15, p0 = 101325, vmolar_a_0;
954 sbar_0_Lem = -196.1375815;
956 vmolar_a_0 = R_bar_Lemmon * T0 / p0;
959 double Tref = 473.15, vmolarref = 0.038837605637863169, sref = 212.22365283759311;
961 double tauref = 132.6312 / Tref;
963 + R_bar_Lemmon * log(vmolarref / vmolar_a_0);
964 double soffset = sref - sref_EOS;
970 Air->update_DmolarT_direct(1 / vmolar_a_0, T);
971 Air->unspecify_phase();
972 sbar_a = sbar_0_Lem + soffset
974 + R_bar_Lemmon * log(vmolar_a / vmolar_a_0);
991 double hbar_0, hbar_a, hbar_w, hbar, R_bar = 8.314472;
999 if (FlagUseIdealGasEnthalpyCorrelations) {
1000 hbar_w = 2.7030251618E-03 * T * T + 3.1994361015E+01 * T + 3.6123174929E+04;
1001 hbar_a = 9.2486716590E-04 * T * T + 2.8557221776E+01 * T - 7.8616129429E+03;
1011 hbar = hbar_0 + (1 - psi_w) * hbar_a + psi_w * hbar_w
1012 + R_bar * T * ((B_m(T, psi_w) - T * dB_m_dT(T, psi_w)) / vmolar + (C_m(T, psi_w) - T / 2.0 * dC_m_dT(T, psi_w)) / (vmolar * vmolar));
1021 double M_ha = MM_Water() * psi_w + (1 - psi_w) * 0.028966;
1022 return h_bar / M_ha;
1028 double M_ha = MM_Water() * psi_w + (1 - psi_w) * 0.028966;
1029 return h_bar * (1 + W) / M_ha;
1034 double M_ha = MM_Water() * psi_w + (1 - psi_w) * 0.028966;
1035 return h_bar / M_ha;
1041 double M_ha = MM_Water() * psi_w + (1 - psi_w) * 0.028966;
1042 return h_bar * (1 + W) / M_ha;
1055 double x1 = 0, x2 = 0, x3 = 0, y1 = 0, y2 = 0, eps = 1e-8, f = 999, R_bar_Lem = 8.314510;
1057 double sbar_0, sbar_a = 0, sbar_w = 0, sbar, R_bar = 8.314472, vbar_a_guess, Baa, Caaa, vbar_a = 0;
1058 double B, dBdT, C, dCdT;
1060 sbar_0 = 0.02366427495;
1066 dBdT = dB_m_dT(T, psi_w);
1069 dCdT = dC_m_dT(T, psi_w);
1071 vbar_a_guess = R_bar_Lem * T / p;
1073 while ((iter <= 3 || std::abs(f) > eps) && iter < 100) {
1079 x2 = vbar_a_guess + 0.001;
1085 f = R_bar_Lem * T / vbar_a * (1 + Baa / vbar_a + Caaa / pow(vbar_a, 2)) - p;
1091 x3 = x2 - y2 / (y2 - y1) * (x2 - x1);
1099 if (FlagUseIdealGasEnthalpyCorrelations) {
1100 std::cout <<
"Not implemented" << std::endl;
1106 sbar = sbar_0 + (1 - psi_w) * sbar_a + psi_w * sbar_w
1107 - R_bar * ((B + T * dBdT) / v_bar + (C + T * dCdT) / (2 * pow(v_bar, 2)) + (1 - psi_w) * log(1 - psi_w) + psi_w * log(psi_w));
1109 sbar = sbar_0 + sbar_a - R_bar * ((B + T * dBdT) / v_bar + (C + T * dCdT) / (2 * pow(v_bar, 2)));
1117 double M_ha = MM_Water() * psi_w + (1 - psi_w) * 0.028966;
1118 return s_bar / M_ha;
1123 double M_ha = MM_Water() * psi_w + (1 - psi_w) * 0.028966;
1125 return s_bar * (1 + W) / M_ha;
1130 double p_w, eps, resid, Tdp = 0, x1 = 0, x2 = 0, x3, y1 = 0, y2, T0;
1131 double p_ws_dp, f_dp;
1134 if ((1 - psi_w) < 1e-16) {
1147 if (p_w > 611.6547241637944) {
1148 T0 = IF97::Tsat97(p) - 1;
1158 while ((iter <= 3 || std::abs(resid) > eps) && iter < 100) {
1171 if (Tdp >= 273.16) {
1173 p_ws_dp = IF97::psat97(Tdp);
1181 resid = p_w - p_ws_dp * f_dp;
1188 x3 = x2 - y2 / (y2 - y1) * (x2 - x1);
1204 WetBulbSolver(
double T,
double p,
double psi_w) : _p(p), _W(epsilon * psi_w / (1 - psi_w)) {
1206 double v_bar_w =
MolarVolume(T, p, psi_w), M_ha = MM_Water() * psi_w + (1 - psi_w) * 0.028966;
1207 LHS =
MolarEnthalpy(T, p, psi_w, v_bar_w) * (1 + _W) / M_ha;
1210 double epsilon = 0.621945;
1211 double f_wb, p_ws_wb, p_s_wb, W_s_wb, h_w, M_ha_wb, psi_wb, v_bar_wb;
1217 p_ws_wb = IF97::psat97(Twb);
1224 p_s_wb = f_wb * p_ws_wb;
1226 W_s_wb = epsilon * p_s_wb / (_p - p_s_wb);
1228 psi_wb = W_s_wb / (epsilon + W_s_wb);
1237 h_w =
h_Ice(Twb, _p);
1241 M_ha_wb = MM_Water() * psi_wb + (1 - psi_wb) * 0.028966;
1243 double RHS = (
MolarEnthalpy(Twb, _p, psi_wb, v_bar_wb) * (1 + W_s_wb) / M_ha_wb + (_W - W_s_wb) * h_w);
1259 double psi_w, T = Ts;
1261 std::vector<givens> inp(2);
1265 std::vector<double> val(2);
1290 double Tsat = IF97::Tsat97(p);
1303 if (return_val > Tmax + 1) {
1315 double Tmin =
Brent(WBTS, 210, Tsat - 1, 1e-12, 1e-12, 50);
1317 return_val =
Brent(WBS, Tmin - 30, Tmax - 1, 1e-12, 1e-12, 50);
1324static givens Name2Type(
const std::string& Name) {
1335 else if (!
strcmp(Name,
"Hha"))
1337 else if (!
strcmp(Name,
"InternalEnergy") || !
strcmp(Name,
"U") || !
strcmp(Name,
"Uda"))
1339 else if (!
strcmp(Name,
"Uha"))
1343 else if (!
strcmp(Name,
"Sha"))
1349 else if (!
strcmp(Name,
"P"))
1353 else if (!
strcmp(Name,
"Vha"))
1361 else if (!
strcmp(Name,
"Cha") || !
strcmp(Name,
"cp_ha"))
1363 else if (!
strcmp(Name,
"CV"))
1365 else if (!
strcmp(Name,
"CVha") || !
strcmp(Name,
"cv_ha"))
1367 else if (!
strcmp(Name,
"P_w"))
1369 else if (!
strcmp(Name,
"isentropic_exponent"))
1371 else if (!
strcmp(Name,
"speed_of_sound"))
1373 else if (!
strcmp(Name,
"Z"))
1377 "Sorry, your input [%s] was not understood to Name2Type. Acceptable values are T,P,R,W,D,B,H,S,M,K and aliases thereof\n", Name.c_str()));
1379int TypeMatch(
int TypeCode,
const std::string& Input1Name,
const std::string& Input2Name,
const std::string& Input3Name) {
1381 if (TypeCode == Name2Type(Input1Name))
return 1;
1382 if (TypeCode == Name2Type(Input2Name))
return 2;
1383 if (TypeCode == Name2Type(Input3Name))
1389 double p_ws, f, W, epsilon = 0.621945, Tdp, p_ws_dp, f_dp, p_w_dp, p_s, RH;
1394 return W / (epsilon + W);
1398 p_ws = IF97::psat97(T);
1409 double psi_ws = p_s / p;
1415 if (Tdp >= 273.16) {
1416 p_ws_dp = IF97::psat97(Tdp);
1425 p_w_dp = f_dp * p_ws_dp;
1434 double p_ws, f, p_s;
1437 p_ws = IF97::psat97(T);
1449 return psi_w * p / p_s;
1453 switch (Name2Type(Name)) {
1487 switch (Name2Type(Name)) {
1520double HAProps(
const std::string& OutputName,
const std::string& Input1Name,
double Input1,
const std::string& Input2Name,
double Input2,
1521 const std::string& Input3Name,
double Input3) {
1526 double out =
HAPropsSI(OutputName, Input1Name, Input1, Input2Name, Input2, Input3Name, Input3);
1533 if (input_keys.size() != 2) {
1537 if (input_keys[0] == key) {
1539 }
else if (input_keys[1] == key) {
1553 const double target;
1556 std::vector<double> input_vals;
1561 : p(p), target(target), output(output), _T(_HUGE), _psi_w(_HUGE) {
1562 input_vals.resize(2, T);
1579 const double target;
1582 std::vector<double> input_vals;
1587 : p(p), target(target), output(output), _T(_HUGE), _psi_w(_HUGE) {
1588 input_vals.resize(2, W);
1602void _HAPropsSI_inputs(
double p,
const std::vector<givens>& input_keys,
const std::vector<double>& input_vals,
double& T,
double& psi_w) {
1604 std::cout <<
format(
"length of input_keys is %d\n", input_keys.size());
1606 if (input_keys.size() != input_vals.size()) {
1607 throw CoolProp::ValueError(
format(
"Length of input_keys (%d) does not equal that of input_vals (%d)", input_keys.size(), input_vals.size()));
1613 long other = 1 - key;
1614 T = input_vals[key];
1616 std::cout <<
format(
"One of the inputs is T: %g K\n", T);
1618 givens othergiven = input_keys[other];
1619 switch (othergiven) {
1624 std::cout <<
format(
"other input value is %g\n", input_vals[other]);
1625 std::cout <<
format(
"other input index is %d\n", othergiven);
1647 double MainInputValue = T;
1649 double SecondaryInputValue = input_vals[other];
1650 givens SecondaryInputKey = input_keys[other];
1651 W = Brent_HAProps_W(SecondaryInputKey, p, MainInputKey, MainInputValue, SecondaryInputValue, W_min, W_max);
1662 std::cout <<
format(
"The main input is not T\n", T);
1673 "Sorry, but currently at least one of the variables as an input to HAPropsSI() must be temperature, relative humidity, humidity ratio, "
1674 "or dewpoint\n Eventually will add a 2-D NR solver to find T and psi_w simultaneously, but not included now");
1677 int number_of_water_content_inputs =
1679 if (number_of_water_content_inputs > 1) {
1681 "Sorry, but cannot provide two inputs that are both water-content (humidity ratio, relative humidity, absolute humidity");
1684 long other = 1 - key;
1687 double MainInputValue = input_vals[key];
1688 givens MainInputKey = input_keys[key];
1690 double SecondaryInputValue = input_vals[other];
1691 givens SecondaryInputKey = input_keys[other];
1694 std::cout <<
format(
"Main input is %g\n", MainInputValue);
1695 std::cout <<
format(
"Secondary input is %g\n", SecondaryInputValue);
1700 check_bounds(
GIVEN_T, 273.15, T_min, T_max);
1703 if (MainInputValue < 1e-10) {
1719 if (MainInputValue < 1e-10) {
1724 double T_dummy = -1,
1727 double pp_water_sat = psi_w_sat * p;
1730 if (pp_water_sat >
Water->p_triple()) {
1731 T_min = IF97::Tsat97(pp_water_sat);
1736 T_min = Secant_Tdb_at_saturated_W(psi_w_sat, p, T_min);
1749 T = Brent_HAProps_T(SecondaryInputKey, p, MainInputKey, MainInputValue, SecondaryInputValue, T_min, T_max);
1750 }
catch (std::exception& e) {
1752 std::cout <<
"ERROR: " << e.what() << std::endl;
1761 std::vector<givens> input_keys(2,
GIVEN_T);
1762 input_keys[1] = MainInputKey;
1763 std::vector<double> input_vals(2, T);
1764 input_vals[1] = MainInputValue;
1770 std::cout <<
format(
"_HAPropsSI_outputs :: T: %g K, psi_w: %g\n", T, psi_w);
1773 double M_ha = (1 - psi_w) * 0.028966 + MM_Water() * psi_w;
1777 switch (OutputType) {
1785 return v_bar * (1 + W) / M_ha;
1789 return v_bar / M_ha;
1838 double v_bar1, v_bar2, h_bar1, h_bar2, cp_ha, dT = 1e-3;
1843 cp_ha = (h_bar2 - h_bar1) / (2 * dT);
1844 return cp_ha / M_ha;
1851 double v_bar, p_1, p_2, u_bar1, u_bar2, cv_bar, dT = 1e-3;
1853 p_1 =
Pressure(T - dT, v_bar, psi_w);
1855 p_2 =
Pressure(T + dT, v_bar, psi_w);
1857 cv_bar = (u_bar2 - u_bar1) / (2 * dT);
1858 return cv_bar / M_ha;
1865 p_1 =
Pressure(T, v_bar - dv, psi_w);
1866 p_2 =
Pressure(T, v_bar + dv, psi_w);
1867 CoolPropDbl dpdv__constT = (p_2 - p_1) / (2 * dv);
1868 return -cp / cv * dpdv__constT * v_bar / p;
1875 p_1 =
Pressure(T, v_bar - dv, psi_w);
1876 p_2 =
Pressure(T, v_bar + dv, psi_w);
1878 CoolPropDbl dpdrho__constT = (p_2 - p_1) / (2 * dv) * dvdrho;
1879 return sqrt(1 / M_ha * cp / cv * dpdrho__constT);
1883 double R_u_molar = 8.314472;
1884 return p * v_bar / (R_u_molar * T);
1890double HAPropsSI(
const std::string& OutputName,
const std::string& Input1Name,
double Input1,
const std::string& Input2Name,
double Input2,
1891 const std::string& Input3Name,
double Input3) {
1899 std::cout <<
format(
"HAPropsSI(%s,%s,%g,%s,%g,%s,%g)\n", OutputName.c_str(), Input1Name.c_str(), Input1, Input2Name.c_str(), Input2,
1900 Input3Name.c_str(), Input3);
1903 std::vector<givens> input_keys(2);
1904 std::vector<double> input_vals(2);
1906 givens In1Type, In2Type, In3Type, OutputType;
1907 double p, T = _HUGE, psi_w = _HUGE;
1910 In1Type = Name2Type(Input1Name.c_str());
1911 In2Type = Name2Type(Input2Name.c_str());
1912 In3Type = Name2Type(Input3Name.c_str());
1915 OutputType = Name2Type(OutputName.c_str());
1918 if (OutputType == In1Type) {
1921 if (OutputType == In2Type) {
1924 if (OutputType == In3Type) {
1931 input_keys[0] = In2Type;
1932 input_keys[1] = In3Type;
1933 input_vals[0] = Input2;
1934 input_vals[1] = Input3;
1935 }
else if (In2Type ==
GIVEN_P) {
1937 input_keys[0] = In1Type;
1938 input_keys[1] = In3Type;
1939 input_vals[0] = Input1;
1940 input_vals[1] = Input3;
1941 }
else if (In3Type ==
GIVEN_P) {
1943 input_keys[0] = In1Type;
1944 input_keys[1] = In2Type;
1945 input_vals[0] = Input1;
1946 input_vals[1] = Input2;
1951 if (input_keys[0] == input_keys[1]) {
1952 throw CoolProp::ValueError(
"Other two inputs to HAPropsSI aside from pressure cannot be the same");
1956 double min_val = _HUGE, max_val = -_HUGE;
1957 for (std::size_t i = 0; i < input_keys.size(); i++) {
1958 if (!check_bounds(input_keys[i], input_vals[i], min_val, max_val)) {
1960 input_keys[i], input_vals[i], min_val, max_val));
1970 std::cout <<
format(
"HAPropsSI input conversion yields T: %g, psi_w: %g\n", T, psi_w);
1974 if (!check_bounds(
GIVEN_P, p, min_val, max_val)) {
1975 throw CoolProp::ValueError(
format(
"The pressure value (%g) is outside the range of validity: (%g) to (%g)", p, min_val, max_val));
1980 if (!check_bounds(
GIVEN_T, T, min_val, max_val)) {
1981 throw CoolProp::ValueError(
format(
"The temperature value (%g) is outside the range of validity: (%g) to (%g)", T, min_val, max_val));
1986 if (!check_bounds(
GIVEN_PSIW, psi_w, min_val, max_val)) {
1988 format(
"The water mole fraction value (%g) is outside the range of validity: (%g) to (%g)", psi_w, min_val, max_val));
1996 if (!check_bounds(OutputType, val, min_val, max_val)) {
1998 format(
"The output for key (%d) with value (%g) is outside the range of validity: (%g) to (%g)", OutputType, val, min_val, max_val));
2006 std::cout <<
format(
"HAPropsSI is about to return invalid number");
2012 std::cout <<
format(
"HAPropsSI is about to return %g\n", val);
2015 }
catch (std::exception& e) {
2023double HAProps_Aux(
const char* Name,
double T,
double p,
double W,
char* units) {
2032 double psi_w, B_aa, C_aaa, B_ww, C_www, B_aw, C_aaw, C_aww, v_bar;
2035 if (!
strcmp(Name,
"Baa")) {
2037 strcpy(units,
"m^3/mol");
2039 }
else if (!
strcmp(Name,
"Caaa")) {
2041 strcpy(units,
"m^6/mol^2");
2043 }
else if (!
strcmp(Name,
"Bww")) {
2045 strcpy(units,
"m^3/mol");
2047 }
else if (!
strcmp(Name,
"Cwww")) {
2049 strcpy(units,
"m^6/mol^2");
2051 }
else if (!
strcmp(Name,
"dBaa")) {
2053 strcpy(units,
"m^3/mol");
2055 }
else if (!
strcmp(Name,
"dCaaa")) {
2056 C_aaa = dCdT_Air(T);
2057 strcpy(units,
"m^6/mol^2");
2059 }
else if (!
strcmp(Name,
"dBww")) {
2060 B_ww = dBdT_Water(T);
2061 strcpy(units,
"m^3/mol");
2063 }
else if (!
strcmp(Name,
"dCwww")) {
2064 C_www = dCdT_Water(T);
2065 strcpy(units,
"m^6/mol^2");
2067 }
else if (!
strcmp(Name,
"Baw")) {
2069 strcpy(units,
"m^3/mol");
2071 }
else if (!
strcmp(Name,
"Caww")) {
2073 strcpy(units,
"m^6/mol^2");
2075 }
else if (!
strcmp(Name,
"Caaw")) {
2077 strcpy(units,
"m^6/mol^2");
2079 }
else if (!
strcmp(Name,
"dBaw")) {
2080 double dB_aw = _dB_aw_dT(T);
2081 strcpy(units,
"m^3/mol");
2083 }
else if (!
strcmp(Name,
"dCaww")) {
2084 double dC_aww = _dC_aww_dT(T);
2085 strcpy(units,
"m^6/mol^2");
2087 }
else if (!
strcmp(Name,
"dCaaw")) {
2088 double dC_aaw = _dC_aaw_dT(T);
2089 strcpy(units,
"m^6/mol^2");
2091 }
else if (!
strcmp(Name,
"beta_H")) {
2093 return HenryConstant(T);
2094 }
else if (!
strcmp(Name,
"kT")) {
2103 }
else if (!
strcmp(Name,
"p_ws")) {
2106 return IF97::psat97(T);
2109 }
else if (!
strcmp(Name,
"vbar_ws")) {
2110 strcpy(units,
"m^3/mol");
2116 return dg_dp_Ice(T, p) * MM_Water() / 1000 / 1000;
2118 }
else if (!
strcmp(Name,
"f")) {
2124 if (!
strcmp(Name,
"Bm")) {
2125 strcpy(units,
"m^3/mol");
2126 return B_m(T, psi_w);
2127 }
else if (!
strcmp(Name,
"Cm")) {
2128 strcpy(units,
"m^6/mol^2");
2129 return C_m(T, psi_w);
2130 }
else if (!
strcmp(Name,
"hvirial")) {
2132 return 8.3145 * T * ((B_m(T, psi_w) - T * dB_m_dT(T, psi_w)) / v_bar + (C_m(T, psi_w) - T / 2.0 * dC_m_dT(T, psi_w)) / (v_bar * v_bar));
2146 else if (!
strcmp(Name,
"hbaro_w")) {
2148 }
else if (!
strcmp(Name,
"hbaro_a")) {
2150 }
else if (!
strcmp(Name,
"h_Ice")) {
2153 }
else if (!
strcmp(Name,
"s_Ice")) {
2156 }
else if (!
strcmp(Name,
"psub_Ice")) {
2159 }
else if (!
strcmp(Name,
"g_Ice")) {
2162 }
else if (!
strcmp(Name,
"rho_Ice")) {
2166 printf(
"Sorry I didn't understand your input [%s] to HAProps_Aux\n", Name);
2179 return 2.14627073E+03 - 3.28917768E+01 * T + 1.89471075E-01 * T * T - 4.86290986E-04 * T * T * T + 4.69540143E-07 * T * T * T * T;
2183 if (!
strcmp(Name,
"s")) {
2184 return s_Ice(T, p * 1000.0);
2185 }
else if (!
strcmp(Name,
"rho")) {
2186 return rho_Ice(T, p * 1000.0);
2187 }
else if (!
strcmp(Name,
"h")) {
2188 return h_Ice(T, p * 1000.0);
2198# include <catch2/catch_all.hpp>
2200TEST_CASE(
"Check HA Virials from Table A.2.1",
"[RP1485]") {
2202 CHECK(std::abs(HumidAir::B_Air(-60 + 273.15) / (-33.065 / 1e6) - 1) < 1e-3);
2203 CHECK(std::abs(HumidAir::B_Air(0 + 273.15) / (-13.562 / 1e6) - 1) < 1e-3);
2204 CHECK(std::abs(HumidAir::B_Air(200 + 273.15) / (11.905 / 1e6) - 1) < 1e-3);
2205 CHECK(std::abs(HumidAir::B_Air(350 + 273.15) / (18.949 / 1e6) - 1) < 1e-3);
2208 CHECK(std::abs(HumidAir::B_Water(-60 + 273.15) / (-11174 / 1e6) - 1) < 1e-3);
2209 CHECK(std::abs(HumidAir::B_Water(0 + 273.15) / (-2025.6 / 1e6) - 1) < 1e-3);
2210 CHECK(std::abs(HumidAir::B_Water(200 + 273.15) / (-200.52 / 1e6) - 1) < 1e-3);
2211 CHECK(std::abs(HumidAir::B_Water(350 + 273.15) / (-89.888 / 1e6) - 1) < 1e-3);
2214 CHECK(std::abs(HumidAir::_B_aw(-60 + 273.15) / (-68.306 / 1e6) - 1) < 1e-3);
2215 CHECK(std::abs(HumidAir::_B_aw(0 + 273.15) / (-38.074 / 1e6) - 1) < 1e-3);
2216 CHECK(std::abs(HumidAir::_B_aw(200 + 273.15) / (-2.0472 / 1e6) - 1) < 1e-3);
2217 CHECK(std::abs(HumidAir::_B_aw(350 + 273.15) / (7.5200 / 1e6) - 1) < 1e-3);
2221 CHECK(std::abs(HumidAir::C_Air(-60 + 273.15) / (2177.9 / 1e12) - 1) < 1e-3);
2222 CHECK(std::abs(HumidAir::C_Air(0 + 273.15) / (1893.1 / 1e12) - 1) < 1e-3);
2223 CHECK(std::abs(HumidAir::C_Air(200 + 273.15) / (1551.2 / 1e12) - 1) < 1e-3);
2224 CHECK(std::abs(HumidAir::C_Air(350 + 273.15) / (1464.7 / 1e12) - 1) < 1e-3);
2227 CHECK(std::abs(HumidAir::C_Water(-60 + 273.15) / (-1.5162999202e-04) - 1) < 1e-3);
2228 CHECK(std::abs(HumidAir::C_Water(0 + 273.15) / (-10981960 / 1e12) - 1) < 1e-3);
2229 CHECK(std::abs(HumidAir::C_Water(200 + 273.15) / (-0.00000003713759442) - 1) < 1e-3);
2230 CHECK(std::abs(HumidAir::C_Water(350 + 273.15) / (-0.000000001198914198) - 1) < 1e-3);
2233 CHECK(std::abs(HumidAir::_C_aaw(-60 + 273.15) / (1027.3 / 1e12) - 1) < 1e-3);
2234 CHECK(std::abs(HumidAir::_C_aaw(0 + 273.15) / (861.02 / 1e12) - 1) < 1e-3);
2235 CHECK(std::abs(HumidAir::_C_aaw(200 + 273.15) / (627.15 / 1e12) - 1) < 1e-3);
2236 CHECK(std::abs(HumidAir::_C_aaw(350 + 273.15) / (583.79 / 1e12) - 1) < 1e-3);
2239 CHECK(std::abs(HumidAir::_C_aww(-60 + 273.15) / (-1821432 / 1e12) - 1) < 1e-3);
2240 CHECK(std::abs(HumidAir::_C_aww(0 + 273.15) / (-224234 / 1e12) - 1) < 1e-3);
2241 CHECK(std::abs(HumidAir::_C_aww(200 + 273.15) / (-8436.5 / 1e12) - 1) < 1e-3);
2242 CHECK(std::abs(HumidAir::_C_aww(350 + 273.15) / (-2486.9 / 1e12) - 1) < 1e-3);
2245TEST_CASE(
"Enhancement factor from Table A.3",
"[RP1485]") {
2251TEST_CASE(
"Isothermal compressibility from Table A.5",
"[RP1485]") {
2258TEST_CASE(
"Henry constant from Table A.6",
"[RP1485]") {
2259 CHECK(std::abs(HumidAir::HenryConstant(0.010001 + 273.15) / (0.22600e-9) - 1) < 1e-3);
2260 CHECK(std::abs(HumidAir::HenryConstant(300 + 273.15) / (0.58389e-9) - 1) < 1e-3);
2267 std::string in1, in2, in3, out;
2268 double v1, v2, v3, expected;
2269 hel(std::string in1,
double v1, std::string in2,
double v2, std::string in3,
double v3, std::string out,
double expected) {
2276 this->expected = expected;
2280std::vector<hel> table_A11 = {hel(
"T", 473.15,
"W", 0.00,
"P", 101325,
"B", 45.07 + 273.15), hel(
"T", 473.15,
"W", 0.00,
"P", 101325,
"V", 1.341),
2281 hel(
"T", 473.15,
"W", 0.00,
"P", 101325,
"H", 202520), hel(
"T", 473.15,
"W", 0.00,
"P", 101325,
"S", 555.8),
2282 hel(
"T", 473.15,
"W", 0.50,
"P", 101325,
"B", 81.12 + 273.15), hel(
"T", 473.15,
"W", 0.50,
"P", 101325,
"V", 2.416),
2283 hel(
"T", 473.15,
"W", 0.50,
"P", 101325,
"H", 1641400), hel(
"T", 473.15,
"W", 0.50,
"P", 101325,
"S", 4829.5),
2284 hel(
"T", 473.15,
"W", 1.00,
"P", 101325,
"B", 88.15 + 273.15), hel(
"T", 473.15,
"W", 1.00,
"P", 101325,
"V", 3.489),
2285 hel(
"T", 473.15,
"W", 1.00,
"P", 101325,
"H", 3079550), hel(
"T", 473.15,
"W", 1.00,
"P", 101325,
"S", 8889.0)};
2287std::vector<hel> table_A12 = {
2288 hel(
"T", 473.15,
"W", 0.00,
"P", 1e6,
"B", 90.47 + 273.15), hel(
"T", 473.15,
"W", 0.00,
"P", 1e6,
"V", 0.136),
2289 hel(
"T", 473.15,
"W", 0.00,
"P", 1e6,
"H", 201940),
2291 hel(
"T", 473.15,
"W", 0.50,
"P", 1e6,
"B", 148.49 + 273.15), hel(
"T", 473.15,
"W", 0.50,
"P", 1e6,
"V", 0.243),
2292 hel(
"T", 473.15,
"W", 0.50,
"P", 1e6,
"H", 1630140), hel(
"T", 473.15,
"W", 0.50,
"P", 1e6,
"S", 3630.2),
2293 hel(
"T", 473.15,
"W", 1.00,
"P", 1e6,
"B", 159.92 + 273.15), hel(
"T", 473.15,
"W", 1.00,
"P", 1e6,
"V", 0.347),
2294 hel(
"T", 473.15,
"W", 1.00,
"P", 1e6,
"H", 3050210), hel(
"T", 473.15,
"W", 1.00,
"P", 1e6,
"S", 7141.3)};
2296std::vector<hel> table_A15 = {
2297 hel(
"T", 473.15,
"W", 0.10,
"P", 1e7,
"B", 188.92 + 273.15), hel(
"T", 473.15,
"W", 0.10,
"P", 1e7,
"V", 0.016),
2298 hel(
"T", 473.15,
"W", 0.10,
"P", 1e7,
"H", 473920), hel(
"T", 473.15,
"W", 0.10,
"P", 1e7,
"S", -90.1),
2299 hel(
"T", 473.15,
"W", 0.10,
"P", 1e7,
"R", 0.734594),
2302class HAPropsConsistencyFixture
2305 std::vector<hel> inputs;
2306 std::string in1, in2, in3, out;
2307 double v1, v2, v3, expected, actual;
2308 void set_values(hel& h) {
2315 this->expected = h.expected;
2319 actual =
HumidAir::HAPropsSI(out.c_str(), in1.c_str(), v1, in2.c_str(), v2, in3.c_str(), v3);
2323TEST_CASE_METHOD(HAPropsConsistencyFixture,
"ASHRAE RP1485 Tables",
"[RP1485]") {
2324 SECTION(
"Table A.15") {
2326 for (std::size_t i = 0; i < inputs.size(); ++i) {
2327 set_values(inputs[i]);
2329 CAPTURE(inputs[i].in1);
2330 CAPTURE(inputs[i].v1);
2331 CAPTURE(inputs[i].in2);
2332 CAPTURE(inputs[i].v2);
2333 CAPTURE(inputs[i].in3);
2334 CAPTURE(inputs[i].v3);
2340 CHECK(std::abs(actual / expected - 1) < 0.01);
2343 SECTION(
"Table A.11") {
2345 for (std::size_t i = 0; i < inputs.size(); ++i) {
2346 set_values(inputs[i]);
2348 CAPTURE(inputs[i].in1);
2349 CAPTURE(inputs[i].v1);
2350 CAPTURE(inputs[i].in2);
2351 CAPTURE(inputs[i].v2);
2352 CAPTURE(inputs[i].in3);
2353 CAPTURE(inputs[i].v3);
2359 CHECK(std::abs(actual / expected - 1) < 0.01);
2362 SECTION(
"Table A.12") {
2364 for (std::size_t i = 0; i < inputs.size(); ++i) {
2365 set_values(inputs[i]);
2367 CAPTURE(inputs[i].in1);
2368 CAPTURE(inputs[i].v1);
2369 CAPTURE(inputs[i].in2);
2370 CAPTURE(inputs[i].v2);
2371 CAPTURE(inputs[i].in3);
2372 CAPTURE(inputs[i].v3);
2378 CHECK(std::abs(actual / expected - 1) < 0.01);
2382TEST_CASE(
"Assorted tests",
"[HAPropsSI]") {
2388bool is_not_a_pair(
const std::set<std::size_t>& item) {
2389 return item.size() != 2;
2392const int number_of_inputs = 6;
2393std::string inputs[number_of_inputs] = {
"W",
"D",
"B",
"R",
"T",
"V"};
2395class ConsistencyTestData
2399 std::vector<Dictionary> data;
2400 std::list<std::set<std::size_t>> inputs_list;
2401 ConsistencyTestData() {
2408 std::vector<std::size_t> indices(number_of_inputs);
2409 for (std::size_t i = 0; i < number_of_inputs; ++i) {
2413 std::set<std::size_t> indices_set(indices.begin(), indices.end());
2414 std::set<std::set<std::size_t>> inputs_powerset =
powerset(indices_set);
2415 inputs_list = std::list<std::set<std::size_t>>(inputs_powerset.begin(), inputs_powerset.end());
2416 inputs_list.remove_if(is_not_a_pair);
2418 const int NT = 10, NW = 5;
2420 for (
double T = 210; T < 350; T += (350 - 210) / (NT - 1)) {
2422 for (
double W = 1e-5; W < Wsat; W += (Wsat - 1e-5) / (NW - 1)) {
2425 for (
int i = 0; i < number_of_inputs; ++i) {
2429 data.push_back(vals);
2430 std::cout <<
format(
"T %g W %g\n", T, W);
2437TEST_CASE(
"HAProps tests",
"[HAProps]") {
2438 Eigen::ArrayXd Tdb = Eigen::ArrayXd::LinSpaced(100, -10, 55) + 273.15;
2439 for (
auto Tdb_ : Tdb) {