CoolProp  4.2.5
An open-source fluid property and humid air property database
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
HumidAirProp.cpp
Go to the documentation of this file.
1 #if defined(_MSC_VER)
2 #define _CRT_SECURE_NO_WARNINGS
3 #endif
4 
5 
6 
7 #include <stdlib.h>
8 #include "math.h"
9 #include "time.h"
10 #include "stdio.h"
11 #include <string.h>
12 #include <iostream>
13 
14 #include "CoolProp.h"
15 #include "Ice.h"
16 #include "HumidAirProp.h"
17 #include "Solvers.h"
18 #include "CoolPropTools.h"
19 
20 #include "purefluids/Water.h"
21 #include "pseudopurefluids/Air.h"
22 
23 static WaterClass Water = WaterClass();
24 static AirClass Air = AirClass();
25 
27 
28 static const char ITc='B';
29 static double epsilon=0.621945,R_bar=8.314472;
30 static int FlagUseVirialCorrelations=0,FlagUseIsothermCompressCorrelation=0,FlagUseIdealGasEnthalpyCorrelations=0;
31 double f_factor(double T, double p);
32 
33 // A couple of convenience functions that are needed quite a lot
34 static double MM_Air(void)
35 {
36  return Air.params.molemass;
37 }
38 static double MM_Water(void)
39 {
40  return Water.params.molemass;
41 }
42 static double B_Air(double T)
43 {
44  return DerivTerms((char *)"B",T,1e-12,(char *)"Air");
45 }
46 static double dBdT_Air(double T)
47 {
48  return DerivTerms((char *)"dBdT",T,1e-12,(char *)"Air");
49 }
50 static double B_Water(double T)
51 {
52  return DerivTerms((char *)"B",T,1e-12,(char *)"Water");
53 }
54 static double dBdT_Water(double T)
55 {
56  return DerivTerms((char *)"dBdT",T,1e-12,(char *)"Water");
57 }
58 static double C_Air(double T)
59 {
60  return DerivTerms((char *)"C",T,1e-12,(char *)"Air");
61 }
62 static double dCdT_Air(double T)
63 {
64  return DerivTerms((char *)"dCdT",T,1e-12,(char *)"Air");
65 }
66 static double C_Water(double T)
67 {
68  return DerivTerms((char *)"C",T,1e-12,(char *)"Water");
69 }
70 static double dCdT_Water(double T)
71 {
72  return DerivTerms((char *)"dCdT",T,1e-12,(char *)"Water");
73 }
74 void UseVirialCorrelations(int flag)
75 {
76  if (flag==0 || flag==1)
77  {
78  FlagUseVirialCorrelations=flag;
79  }
80  else
81  {
82  printf("UseVirialCorrelations takes an integer, either 0 (no) or 1 (yes)\n");
83  }
84 
85 }
87 {
88  if (flag==0 || flag==1)
89  {
90  FlagUseIsothermCompressCorrelation=flag;
91  }
92  else
93  {
94  printf("UseIsothermCompressCorrelation takes an integer, either 0 (no) or 1 (yes)\n");
95  }
96 }
98 {
99  if (flag==0 || flag==1)
100  {
101  FlagUseIdealGasEnthalpyCorrelations=flag;
102  }
103  else
104  {
105  printf("UseIdealGasEnthalpyCorrelations takes an integer, either 0 (no) or 1 (yes)\n");
106  }
107 }
108 static double Brent_HAProps_T(char *OutputName, char *Input1Name, double Input1, char *Input2Name, double Input2, double TargetVal, double T_min, double T_max)
109 {
110  double T;
111  class BrentSolverResids : public FuncWrapper1D
112  {
113  private:
114  double Input1,Input2,TargetVal;
115  char *OutputName,*Input1Name,*Input2Name;
116  public:
117  double rhoL, rhoV;
118  BrentSolverResids(char *OutputName, char *Input1Name, double Input1, char *Input2Name, double Input2, double TargetVal)
119  {
120  this->OutputName = OutputName;
121  this->Input1Name = Input1Name;
122  this->Input2Name = Input2Name;
123  this->Input1 = Input1;
124  this->Input2 = Input2;
125  this->TargetVal = TargetVal;
126  };
127  ~BrentSolverResids(){};
128 
129  double call(double T){
130  return HAProps(OutputName,(char *)"T",T,Input1Name,Input1,Input2Name,Input2)-TargetVal;
131  }
132  };
133 
134  BrentSolverResids *BSR = new BrentSolverResids(OutputName, Input1Name, Input1, Input2Name, Input2, TargetVal);
135 
136  std::string errstr;
137  T = Brent(BSR,T_min,T_max,1e-7,1e-4,50,&errstr);
138 
139  delete BSR;
140  return T;
141 }
142 
143 static double Secant_HAProps_T(char *OutputName, char *Input1Name, double Input1, char *Input2Name, double Input2, double TargetVal, double T_guess)
144 {
145  // Use a secant solve in order to yield a target output value for HAProps by altering T
146  double x1=0,x2=0,x3=0,y1=0,y2=0,eps=5e-7,f=999,T=300,change;
147  int iter=1;
148 
149  while ((iter<=3 || (fabs(f)>eps && fabs(change)>1e-10)) && iter<100)
150  {
151  if (iter==1){x1=T_guess; T=x1;}
152  if (iter==2){x2=T_guess+0.001; T=x2;}
153  if (iter>2) {T=x2;}
154  f=HAProps(OutputName,(char *)"T",T,Input1Name,Input1,Input2Name,Input2)-TargetVal;
155  if (iter==1){y1=f;}
156  if (iter>1)
157  {
158  y2=f;
159  x3=x2-y2/(y2-y1)*(x2-x1);
160  change = y2/(y2-y1)*(x2-x1);
161  y1=y2; x1=x2; x2=x3;
162  }
163  iter=iter+1;
164  }
165  return T;
166 }
167 
168 static double Secant_HAProps_W(const char *OutputName, const char *Input1Name, double Input1, const char *Input2Name, double Input2, double TargetVal, double W_guess)
169 {
170  // Use a secant solve in order to yield a target output value for HAProps by altering humidity ratio
171  double x1=0,x2=0,x3=0,y1=0,y2=0,eps=1e-8,f=999,W=0.0001;
172  int iter=1;
173 
174  while ((iter<=3 || fabs(f)>eps) && iter<100)
175  {
176  if (iter == 1){x1 = W_guess; W = x1;}
177  if (iter == 2){x2 = W_guess+0.001; W = x2;}
178  if (iter > 2) {W = x2;}
179  f = HAProps(OutputName,(char *)"W",W,Input1Name,Input1,Input2Name,Input2)-TargetVal;
180  if (iter == 1){y1 = f;}
181  if (iter > 1)
182  {
183  y2=f;
184  x3=x2-y2/(y2-y1)*(x2-x1);
185  y1=y2; x1=x2; x2=x3;
186  }
187  iter=iter+1;
188  }
189  return W;
190 }
191 
192 // Mixed virial components
193 static double _B_aw(double T)
194 {
195  // Function return has units of dm^3/mol, to convert to m^3/mol, divide by 1000
196  double a[]={0,0.665687e2,-0.238834e3,-0.176755e3};
197  double b[]={0,-0.237,-1.048,-3.183};
198  double rhobarstar=1000,Tstar=100;
199  return 1/rhobarstar*(a[1]*pow(T/Tstar,b[1])+a[2]*pow(T/Tstar,b[2])+a[3]*pow(T/Tstar,b[3]));
200 }
201 
202 static double _dB_aw_dT(double T)
203 {
204  // Function return has units of dm^3/mol/K, to convert to m^3/mol/K, divide by 1000
205  double a[]={0,0.665687e2,-0.238834e3,-0.176755e3};
206  double b[]={0,-0.237,-1.048,-3.183};
207  double rhobarstar=1000,Tstar=100;
208  return 1/rhobarstar/Tstar*(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));
209 }
210 
211 static double _C_aaw(double T)
212 {
213  // Function return has units of dm^6/mol^2
214  double c[]={0,0.482737e3,0.105678e6,-0.656394e8,0.294442e11,-0.319317e13};
215  double rhobarstar=1000,Tstar=1,summer=0; int i;
216  for (i=1;i<=5;i++)
217  {
218  summer+=c[i]*pow(T/Tstar,1-i);
219  }
220  return 1.0/rhobarstar/rhobarstar*summer;
221 }
222 
223 static double _dC_aaw_dT(double T)
224 {
225  // Function return has units of dm^6/mol^2/K
226  double c[]={0,0.482737e3,0.105678e6,-0.656394e8,0.294442e11,-0.319317e13};
227  double rhobarstar=1000,Tstar=1,summer=0; int i;
228  for (i=2;i<=5;i++)
229  {
230  summer+=c[i]*(1-i)*pow(T/Tstar,-i);
231  }
232  return 1.0/rhobarstar/rhobarstar/Tstar*summer;
233 }
234 
235 static double _C_aww(double T)
236 {
237  // Function return has units of dm^6/mol^2
238  double d[]={0,-0.1072887e2,0.347804e4,-0.383383e6,0.334060e8};
239  double rhobarstar=1,Tstar=1,summer=0; int i;
240  for (i=1;i<=4;i++)
241  {
242  summer+=d[i]*pow(T/Tstar,1-i);
243  }
244  return -1.0/rhobarstar/rhobarstar*exp(summer);
245 }
246 
247 static double _dC_aww_dT(double T)
248 {
249  // Function return has units of dm^6/mol^2/K
250  double d[]={0,-0.1072887e2,0.347804e4,-0.383383e6,0.334060e8};
251  double rhobarstar=1,Tstar=1,summer1=0,summer2=0; int i;
252  for (i=1;i<=4;i++)
253  {
254  summer1+=d[i]*pow(T/Tstar,1-i);
255  }
256  for (i=2;i<=4;i++)
257  {
258  summer2+=d[i]*(1-i)*pow(T/Tstar,-i);
259  }
260  return -1.0/rhobarstar/rhobarstar/Tstar*exp(summer1)*summer2;
261 }
262 
263 
264 static double B_m(double T, double psi_w)
265 {
266  // Bm has units of m^3/mol
267  double Tj,tau_Air,tau_Water,B_aa,B_ww,B_aw;
268  // NDG for fluid EOS for virial terms
269  Tj=132.6312;
270  tau_Air=Tj/T;
271  tau_Water=Water.reduce.T/T;
272  if (FlagUseVirialCorrelations==1)
273  {
274  B_aa=-0.000721183853646 +1.142682674467e-05*T -8.838228412173e-08*pow(T,2)
275  +4.104150642775e-10*pow(T,3) -1.192780880645e-12*pow(T,4) +2.134201312070e-15*pow(T,5)
276  -2.157430412913e-18*pow(T,6) +9.453830907795e-22*pow(T,7);
277  B_ww=-10.8963128394 +2.439761625859e-01*T -2.353884845100e-03*pow(T,2)
278  +1.265864734412e-05*pow(T,3) -4.092175700300e-08*pow(T,4) +7.943925411344e-11*pow(T,5)
279  -8.567808759123e-14*pow(T,6) +3.958203548563e-17*pow(T,7);
280  }
281  else
282  {
283  B_aa=B_Air(T)*MM_Air()/1e3; //[m^3/kg] to [m^3/mol]
284  B_ww=B_Water(T)*MM_Water()/1e3; //[m^3/kg] to [m^3/mol]
285  }
286 
287  B_aw=_B_aw(T)/1e3; //[dm^3/mol] to [m^3/mol]
288  return pow(1-psi_w,2)*B_aa+2*(1-psi_w)*psi_w*B_aw+psi_w*psi_w*B_ww;
289 }
290 
291 static double dB_m_dT(double T, double psi_w)
292 {
293  //dBm_dT has units of m^3/mol/K
294  double Tj,tau_Air,tau_Water,dB_dT_aa,dB_dT_ww,dB_dT_aw;
295  // NDG for fluid EOS for virial terms
296  Tj=132.6312;
297  tau_Air=Tj/T;
298  tau_Water=Water.reduce.T/T;
299  if (FlagUseVirialCorrelations)
300  {
301  dB_dT_aa=1.65159324353e-05 -3.026130954749e-07*T +2.558323847166e-09*pow(T,2) -1.250695660784e-11*pow(T,3) +3.759401946106e-14*pow(T,4) -6.889086380822e-17*pow(T,5) +7.089457032972e-20*pow(T,6) -3.149942145971e-23*pow(T,7);
302  dB_dT_ww=0.65615868848 -1.487953162679e-02*T +1.450134660689e-04*pow(T,2) -7.863187630094e-07*pow(T,3) +2.559556607010e-09*pow(T,4) -4.997942221914e-12*pow(T,5) +5.417678681513e-15*pow(T,6) -2.513856275241e-18*pow(T,7);
303  }
304  else
305  {
306  dB_dT_aa=dBdT_Air(T)*MM_Air()/1e3; //[m^3/kg] to [m^3/mol]
307  dB_dT_ww=dBdT_Water(T)*MM_Water()/1e3; //[m^3/kg] to [m^3/mol]
308  }
309  dB_dT_aw=_dB_aw_dT(T)/1e3; //[dm^3/mol] to [m^3/mol]
310  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;
311 }
312 
313 static double C_m(double T, double psi_w)
314 {
315  // Cm has units of m^6/mol^2
316  double Tj,tau_Air,tau_Water,C_aaa,C_www,C_aww,C_aaw;
317  // NDG for fluid EOS for virial terms
318  Tj=132.6312;
319  tau_Air=Tj/T;
320  tau_Water=Water.reduce.T/T;
321  if (FlagUseVirialCorrelations)
322  {
323  C_aaa=1.29192158975e-08 -1.776054020409e-10*T +1.359641176409e-12*pow(T,2)
324  -6.234878717893e-15*pow(T,3) +1.791668730770e-17*pow(T,4) -3.175283581294e-20*pow(T,5)
325  +3.184306136120e-23*pow(T,6) -1.386043640106e-26*pow(T,7);
326  C_www=-0.580595811134 +1.365952762696e-02*T -1.375986293288e-04*pow(T,2)
327  +7.687692259692e-07*pow(T,3) -2.571440816920e-09*pow(T,4) +5.147432221082e-12*pow(T,5)
328  -5.708156494894e-15*pow(T,6) +2.704605721778e-18*pow(T,7);
329  }
330  else
331  {
332  C_aaa=C_Air(T)*MM_Air()*MM_Air()/1e6; //[m^6/kg^2] to [m^6/mol^2]
333  C_www=C_Water(T)*MM_Water()*MM_Water()/1e6; //[m^6/kg^2] to [m^6/mol^2]
334  }
335  C_aaw=_C_aaw(T)/1e6; //[dm^6/mol] to [m^6/mol^2]
336  C_aww=_C_aww(T)/1e6; //[dm^6/mol] to [m^6/mol^2]
337  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;
338 }
339 
340 static double dC_m_dT(double T, double psi_w)
341 {
342  // dCm_dT has units of m^6/mol^2/K
343 
344  double Tj,tau_Air,tau_Water,dC_dT_aaa,dC_dT_www,dC_dT_aww,dC_dT_aaw;
345  // NDG for fluid EOS for virial terms
346  Tj=132.6312;
347  tau_Air=Tj/T;
348  tau_Water=Water.reduce.T/T;
349  if (FlagUseVirialCorrelations)
350  {
351  dC_dT_aaa=-2.46582342273e-10 +4.425401935447e-12*T -3.669987371644e-14*pow(T,2) +1.765891183964e-16*pow(T,3) -5.240097805744e-19*pow(T,4) +9.502177003614e-22*pow(T,5) -9.694252610339e-25*pow(T,6) +4.276261986741e-28*pow(T,7);
352  dC_dT_www=0.0984601196142 -2.356713397262e-03*T +2.409113323685e-05*pow(T,2) -1.363083778715e-07*pow(T,3) +4.609623799524e-10*pow(T,4) -9.316416405390e-13*pow(T,5) +1.041909136255e-15*pow(T,6) -4.973918480607e-19*pow(T,7);
353  }
354  else
355  {
356  dC_dT_aaa=dCdT_Air(T)*MM_Air()*MM_Air()/1e6; //[m^6/kg^2] to [m^6/mol^2]
357  dC_dT_www=dCdT_Water(T)*MM_Water()*MM_Water()/1e6; //[m^6/kg^2] to [m^6/mol^2]
358  }
359  dC_dT_aaw=_dC_aaw_dT(T)/1e6; //[dm^6/mol] to [m^6/mol^2]
360  dC_dT_aww=_dC_aww_dT(T)/1e6; //[dm^6/mol] to [m^6/mol^2]
361  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+pow(psi_w,3)*dC_dT_www;
362 }
363 double HumidityRatio(double psi_w)
364 {
365  return psi_w*epsilon/(1-psi_w);
366 }
367 
368 static double HenryConstant(double T)
369 {
370  // Result has units of 1/Pa
371  double p_ws,beta_N2,beta_O2,beta_Ar,beta_a,tau,Tr,Tc=647.096;
372  Tr=T/Tc;
373  tau=1-Tr;
374  p_ws=Props("P","T",T,"Q",1.0,"Water"); //[kPa]
375  beta_N2=p_ws*exp(-9.67578/Tr+4.72162*pow(tau,0.355)/Tr+11.70585*pow(Tr,-0.41)*exp(tau));
376  beta_O2=p_ws*exp(-9.44833/Tr+4.43822*pow(tau,0.355)/Tr+11.42005*pow(Tr,-0.41)*exp(tau));
377  beta_Ar=p_ws*exp(-8.40954/Tr+4.29587*pow(tau,0.355)/Tr+10.52779*pow(Tr,-0.41)*exp(tau));
378  beta_a=1/(0.7812/beta_N2+0.2095/beta_O2+0.0093/beta_Ar);
379  return 1/(1.01325*beta_a)/1000.0;
380 }
381 
382 double f_factor(double T, double p)
383 {
384  double f=0,Rbar=8.314371,eps=1e-8,Tj;
385  double x1=0,x2=0,x3,y1=0,y2,change=_HUGE;
386  int iter=1;
387  double p_ws,tau_Air,tau_Water,B_aa,B_aw,B_ww,C_aaa,C_aaw,C_aww,C_www,
388  line1,line2,line3,line4,line5,line6,line7,line8,k_T,beta_H,LHS,RHS,psi_ws,
389  vbar_ws;
390 
391  // Get total pressure in Pa from kPa
392  p*=1000;
393 
394  // Saturation pressure [Pa]
395  if (T>273.16)
396  {
397  // It is liquid water
398  p_ws=PropsSI("P","T",T,"Q",0,"Water");
399  if (FlagUseIsothermCompressCorrelation)
400  {
401  k_T = 1.6261876614E-22*pow(T,6) - 3.3016385196E-19*pow(T,5) + 2.7978984577E-16*pow(T,4)
402  - 1.2672392901E-13*pow(T,3) + 3.2382864853E-11*pow(T,2) - 4.4318979503E-09*T + 2.5455947289E-07;
403  }
404  else
405  {
406  double rho = PropsSI("D","T",T,"P",p,"Water");
407  k_T=DerivTerms((char *)"IsothermalCompressibility",T,rho,(char *)"Water")/1000; //[1/Pa]
408  }
409  beta_H=HenryConstant(T); //[1/Pa]
410  vbar_ws=1.0/Props("D","T",T,"Q",0,"Water")*MM_Water()/1000; //[m^3/mol]
411  }
412  else
413  {
414  // It is ice
415  p_ws=psub_Ice(T)*1000; // [Pa]
416  k_T=IsothermCompress_Ice(T,p/1000); //[1/Pa]
417  beta_H=0;
418  vbar_ws=dg_dp_Ice(T,p/1000)*MM_Water()/1000/1000; //[m^3/mol]
419  }
420 
421  // Hermann: In the iteration process of the enhancement factor in Eq. (3.25), k_T is set to zero for pw,s (T) > p.
422  if (p_ws>p)
423  {
424  k_T=0;
425  beta_H=0;
426  }
427 
428  // NDG for fluid EOS for virial terms
429  Tj=132.6312;
430  tau_Air=Tj/T;
431  tau_Water=Water.reduce.T/T;
432  if (FlagUseVirialCorrelations)
433  {
434  B_aa=-0.000721183853646 +1.142682674467e-05*T -8.838228412173e-08*pow(T,2)
435  +4.104150642775e-10*pow(T,3) -1.192780880645e-12*pow(T,4) +2.134201312070e-15*pow(T,5)
436  -2.157430412913e-18*pow(T,6) +9.453830907795e-22*pow(T,7);
437  B_ww=-10.8963128394 +2.439761625859e-01*T -2.353884845100e-03*pow(T,2)
438  +1.265864734412e-05*pow(T,3) -4.092175700300e-08*pow(T,4) +7.943925411344e-11*pow(T,5)
439  -8.567808759123e-14*pow(T,6) +3.958203548563e-17*pow(T,7);
440  C_aaa=1.29192158975e-08 -1.776054020409e-10*T +1.359641176409e-12*pow(T,2)
441  -6.234878717893e-15*pow(T,3) +1.791668730770e-17*pow(T,4) -3.175283581294e-20*pow(T,5)
442  +3.184306136120e-23*pow(T,6) -1.386043640106e-26*pow(T,7);
443  C_www=-0.580595811134 +1.365952762696e-02*T -1.375986293288e-04*pow(T,2)
444  +7.687692259692e-07*pow(T,3) -2.571440816920e-09*pow(T,4) +5.147432221082e-12*pow(T,5)
445  -5.708156494894e-15*pow(T,6) +2.704605721778e-18*pow(T,7);
446  }
447  else
448  {
449  B_aa=B_Air(T)*MM_Air()/1e3; //[m^3/kg] to [m^3/mol]
450  C_aaa=C_Air(T)*MM_Air()*MM_Air()/1e6; //[m^6/kg^2] to [m^6/mol^2]
451  B_ww=B_Water(T)*MM_Water()/1e3; //[m^3/kg] to [m^3/mol]
452  C_www=C_Water(T)*MM_Water()*MM_Water()/1e6; //[m^6/kg^2] to [m^6/mol^2]
453  }
454  B_aw=_B_aw(T)/1e3; //[dm^3/mol] to [m^3/mol]
455  C_aaw=_C_aaw(T)/1e6; //[dm^6/mol] to [m^6/mol^2]
456  C_aww=_C_aww(T)/1e6; //[dm^6/mol] to [m^6/mol^2]
457 
458  // Use a little secant loop to find f iteratively
459  // Start out with a guess value of 1 for f
460  while ((iter<=3 || change>eps) && iter<100)
461  {
462  if (iter==1){x1=1.00; f=x1;}
463  if (iter==2){x2=1.00+0.000001; f=x2;}
464  if (iter>2) {f=x2;}
465 
466  // Left-hand-side of Equation 3.25
467  LHS=log(f);
468  // Eqn 3.24
469  psi_ws=f*p_ws/p;
470 
471  // All the terms forming the RHS of Eqn 3.25
472  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);
473  line2=pow(1-psi_ws,2)*p/(Rbar*T)*B_aa-2*pow(1-psi_ws,2)*p/(Rbar*T)*B_aw-(p-p_ws-pow(1-psi_ws,2)*p)/(Rbar*T)*B_ww;
474  line3=pow(1-psi_ws,3)*p*p/pow(Rbar*T,2)*C_aaa+(3*pow(1-psi_ws,2)*(1-2*(1-psi_ws))*p*p)/(2*pow(Rbar*T,2))*C_aaw;
475  line4=-3*pow(1-psi_ws,2)*psi_ws*p*p/pow(Rbar*T,2)*C_aww-((3-2*psi_ws)*psi_ws*psi_ws*p*p-p_ws*p_ws)/(2*pow(Rbar*T,2))*C_www;
476  line5=-(pow(1-psi_ws,2)*(-2+3*psi_ws)*psi_ws*p*p)/pow(Rbar*T,2)*B_aa*B_ww;
477  line6=-(2*pow(1-psi_ws,3)*(-1+3*psi_ws)*p*p)/pow(Rbar*T,2)*B_aa*B_aw;
478  line7=(6*pow(1-psi_ws,2)*psi_ws*psi_ws*p*p)/pow(Rbar*T,2)*B_ww*B_aw-(3*pow(1-psi_ws,4)*p*p)/(2*pow(Rbar*T,2))*B_aa*B_aa;
479  line8=-(2*pow(1-psi_ws,2)*psi_ws*(-2+3*psi_ws)*p*p)/pow(Rbar*T,2)*B_aw*B_aw-(p_ws*p_ws-(4-3*psi_ws)*pow(psi_ws,3)*p*p)/(2*pow(Rbar*T,2))*B_ww*B_ww;
480  RHS=line1+line2+line3+line4+line5+line6+line7+line8;
481 
482  if (iter==1){y1=LHS-RHS;}
483  if (iter>1)
484  {
485  y2=LHS-RHS;
486  x3=x2-y2/(y2-y1)*(x2-x1);
487  change=fabs(y2/(y2-y1)*(x2-x1));
488  y1=y2; x1=x2; x2=x3;
489  }
490  iter=iter+1;
491  }
492  if (f>=1.0)
493  return f;
494  else
495  return 1.0;
496 }
497 void HAHelp(void)
498 {
499  printf("Sorry, Need to update!");
500 }
501 int returnHumAirCode(const char * Code)
502 {
503  if (!strcmp(Code,"GIVEN_TDP"))
504  return GIVEN_TDP;
505  else if (!strcmp(Code,"GIVEN_HUMRAT"))
506  return GIVEN_HUMRAT;
507  else if (!strcmp(Code,"GIVEN_TWB"))
508  return GIVEN_TWB;
509  else if (!strcmp(Code,"GIVEN_RH"))
510  return GIVEN_RH;
511  else if (!strcmp(Code,"GIVEN_ENTHALPY"))
512  return GIVEN_ENTHALPY;
513  else
514  {
515  fprintf(stderr,"Code to returnHumAirCode in HumAir.c [%s] not understood",Code);
516  return -1;
517  }
518 }
519 double Viscosity(double T, double p, double psi_w)
520 {
521  /*
522  Using the method of:
523 
524  P.T. Tsilingiris, 2009, Thermophysical and transport properties of humid air at temperature range between 0 and 100 oC, Energy Conversion and Management, 49, 1098-1010
525 
526  but using the detailed measurements for pure fluid from IAPWS formulations
527  */
528  double mu_a,mu_w,Phi_av,Phi_va,Ma,Mw;
529  Mw=MM_Water();
530  Ma=MM_Air();
531  // Viscosity of dry air at dry-bulb temp and total pressure
532  mu_a=Props("V","T",T,"P",p,"Air");
533  // Viscosity of pure saturated water at dry-bulb temperature
534  mu_w=Props("V","P",p,"Q",1,"Water");
535  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); //[-]
536  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); //[-]
537  return (1-psi_w)*mu_a/((1-psi_w)+psi_w*Phi_av)+psi_w*mu_w/(psi_w+(1-psi_w)*Phi_va);
538 }
539 double Conductivity(double T, double p, double psi_w)
540 {
541  /*
542  Using the method of:
543 
544  P.T. Tsilingiris, 2009, Thermophysical and transport properties of humid air at temperature range between 0 and 100 oC, Energy Conversion and Management, 49, 1098-1010
545 
546  but using the detailed measurements for pure fluid from IAPWS formulations
547  */
548  double mu_a,mu_w,k_a,k_w,Phi_av,Phi_va,Ma,Mw;
549  Mw=MM_Water();
550  Ma=MM_Air();
551  // Viscosity of dry air at dry-bulb temp and total pressure
552  k_a=Props("L","T",T,"P",p,"Air");
553  mu_a=Props("V","T",T,"P",p,"Air");
554  // Viscosity of pure saturated water vapor at dry-bulb temperature
555  k_w=Props("L","P",p,"Q",1,"Water");
556  mu_w=Props("V","P",p,"Q",1,"Water");
557  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); //[-]
558  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); //[-]
559  return (1-psi_w)*k_a/((1-psi_w)+psi_w*Phi_av)+psi_w*k_w/(psi_w+(1-psi_w)*Phi_va);
560 }
561 double MolarVolume(double T, double p, double psi_w)
562 {
563  // Output in m^3/mol
564  int iter;
565  double v_bar0, v_bar=0, R_bar=8.314472,x1=0,x2=0,x3,y1=0,y2,resid,eps,Bm,Cm;
566 
567  // -----------------------------
568  // Iteratively find molar volume
569  // -----------------------------
570 
571  // Start by assuming it is an ideal gas to get initial guess
572  v_bar0=R_bar*T/p/1000;
573 
574  //Bring outside the loop since not a function of v_bar
575  Bm=B_m(T,psi_w);
576  Cm=C_m(T,psi_w);
577 
578  iter=1; eps=1e-8; resid=999;
579  while ((iter<=3 || fabs(resid)>eps) && iter<100)
580  {
581  if (iter==1){x1=v_bar0; v_bar=x1;}
582  if (iter==2){x2=v_bar0+0.000001; v_bar=x2;}
583  if (iter>2) {v_bar=x2;}
584 
585  // factor of 1000 is to deal with kmol and mol conversion
586  // want v_bar in m^3/mol and R_bar in kJ/mol-K
587  resid=p-(R_bar/1000)*T/v_bar*(1+Bm/v_bar+Cm/(v_bar*v_bar));
588 
589  if (iter==1){y1=resid;}
590  if (iter>1)
591  {
592  y2=resid;
593  x3=x2-y2/(y2-y1)*(x2-x1);
594  y1=y2; x1=x2; x2=x3;
595  }
596  iter=iter+1;
597  }
598  return v_bar;
599 }
600 double IdealGasMolarEnthalpy_Water(double T, double v_bar)
601 {
602  double hbar_w_0,tau,rhobar,hbar_w,rho;
603  // Ideal-Gas contribution to enthalpy of water
604  hbar_w_0=-0.01102303806;//[kJ/kmol]
605  tau = Water.crit.T/T;
606  rhobar = 1/v_bar; //[kmol/m^3]
607  rho = rhobar * Water.params.molemass;
608  hbar_w = hbar_w_0+R_bar*T*(1+tau*Water.dphi0_dTau(tau,rho/Water.crit.rho));
609  return hbar_w;
610 }
611 double IdealGasMolarEntropy_Water(double T, double p)
612 {
613  double sbar_w,tau,R_bar,rho;
614  R_bar = 8.314371; //[kJ/kmol/K]
615  tau = Water.crit.T/T;
616  rho = p/(R_bar/MM_Water()*T); //[kg/m^3]
617  sbar_w = R_bar*(tau*Water.dphi0_dTau(tau,rho/Water.crit.rho)-Water.phi0(tau,rho/Water.crit.rho)); //[kJ/kmol/K]
618  return sbar_w;
619 }
620 double IdealGasMolarEnthalpy_Air(double T, double v_bar)
621 {
622  double hbar_a_0,tau,rhobar,hbar_a,R_bar_Lemmon, rho;
623  // Ideal-Gas contribution to enthalpy of air
624  hbar_a_0=-7914.149298; //[kJ/kmol]
625  //Tj and rhoj are given by 132.6312 and 302.5507652 respectively
626  tau=132.6312/T;
627  rhobar=1/v_bar; //[kmol/m^3]
628  rho = rhobar * Props1("Air","molemass");
629  R_bar_Lemmon=8.314510; //[kJ/kmol/K]
630  hbar_a=hbar_a_0+R_bar_Lemmon*T*(1+tau*DerivTerms((char *)"dphi0_dTau",T,rho,(char *)"Air")); //[kJ/kmol]
631  return hbar_a;
632 }
633 double IdealGasMolarEntropy_Air(double T, double v_bar_a)
634 {
635  double sbar_0_Lem,tau,sbar_a,R_bar_Lemmon,T0=273.15,p0=101.325,v_0,v_bar_0, rho_a,rho_bar_a, rho_bar_0,rho_0;
636  R_bar_Lemmon=8.314510; //[kJ/kmol/K]
637  // Ideal-Gas contribution to entropy of air
638  sbar_0_Lem=-196.1375815; //[kJ/kmol/K]
639  //Tj and rhoj are given by 132.6312 and 302.5507652 respectively
640  tau=132.6312/T; //[no units]
641  v_0 = R_bar_Lemmon/MM_Air()*T0/p0; //[m^3/kg]
642  rho_bar_a = 1/v_bar_a;
643  rho_a = rho_bar_a * Props1("Air","molemass");
644  v_bar_0 = R_bar_Lemmon*T0/p0; //[m^3/kmol]
645  rho_bar_0 = 1/v_bar_0;
646  rho_0 = rho_bar_0 * Props1("Air","molemass");
647  sbar_a=sbar_0_Lem+R_bar_Lemmon*(tau*DerivTerms((char *)"dphi0_dTau",T,rho_0,(char *)"Air")-DerivTerms((char *)"phi0",T,rho_0,(char *)"Air"))+R_bar_Lemmon*log(v_bar_a/v_bar_0); //[kJ/kmol/K]
648  return sbar_a; //[kJ/kmol/K]
649 }
650 
651 double MolarEnthalpy(double T, double p, double psi_w, double v_bar)
652 {
653  // In units of kJ/kmol
654 
655  // vbar (molar volume) in m^3/kg
656 
657  double hbar_0,hbar_a,hbar_w,hbar,R_bar=8.314472;
658  // ----------------------------------------
659  // Enthalpy
660  // ----------------------------------------
661  // Constant for enthalpy
662  // Not clear why getting rid of this term yields the correct values in the table, but enthalpies are equal to an additive constant, so not a big deal
663  hbar_0=0.0;//2.924425468; //[kJ/kmol]
664 
665  if (FlagUseIdealGasEnthalpyCorrelations)
666  {
667  hbar_w=2.7030251618E-03*T*T + 3.1994361015E+01*T + 3.6123174929E+04;
668  hbar_a=9.2486716590E-04*T*T + 2.8557221776E+01*T - 7.8616129429E+03;
669  }
670  else
671  {
672  hbar_w=IdealGasMolarEnthalpy_Water(T,v_bar);
673  hbar_a=IdealGasMolarEnthalpy_Air(T,v_bar);
674  }
675 
676  hbar=hbar_0+(1-psi_w)*hbar_a+psi_w*hbar_w+R_bar*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));
677  return hbar; //[kJ/kmol]
678 }
679 double MassEnthalpy(double T, double p, double psi_w)
680 {
681  double v_bar = MolarVolume(T, p, psi_w); //[m^3/mol_ha]
682  double h_bar = MolarEnthalpy(T, p, psi_w, v_bar); //[kJ/kmol_ha]
683  double W = HumidityRatio(psi_w); //[kg_w/kg_da]
684  double M_ha = MM_Water()*psi_w+(1-psi_w)*28.966;
685  return h_bar*(1+W)/M_ha; //[kJ/kg_da]
686 }
687 
688 double MolarEntropy(double T, double p, double psi_w, double v_bar)
689 {
690  // In units of kJ/kmol/K
691 
692  // Serious typo in RP-1485 - should use total pressure rather than
693  // reference pressure in density calculation for water vapor molar entropy
694 
695  // vbar (molar volume) in m^3/kmol
696  double x1=0,x2=0,x3=0,y1=0,y2=0,eps=1e-8,f=999,R_bar_Lem=8.314510;
697  int iter=1;
698  double sbar_0,sbar_a=0,sbar_w=0,sbar,R_bar=8.314472,vbar_a_guess, Baa, Caaa,vbar_a=0;
699  double B,dBdT,C,dCdT;
700  // Constant for entropy
701  sbar_0=0.02366427495; //[kJ/kmol/K]
702 
703  //Calculate vbar_a, the molar volume of dry air
704  // B_m, C_m, etc. functions take care of the units
705  Baa = B_m(T,0);
706  B = B_m(T,psi_w);
707  dBdT = dB_m_dT(T,psi_w);
708  Caaa = C_m(T,0);
709  C = C_m(T,psi_w);
710  dCdT = dC_m_dT(T,psi_w);
711 
712  vbar_a_guess = R_bar_Lem*T/p; //[m^3/mol] since p in [kPa]
713 
714  while ((iter<=3 || fabs(f)>eps) && iter<100)
715  {
716  if (iter==1){x1=vbar_a_guess; vbar_a=x1;}
717  if (iter==2){x2=vbar_a_guess+0.001; vbar_a=x2;}
718  if (iter>2) {vbar_a=x2;}
719  f=R_bar_Lem*T/vbar_a*(1+Baa/vbar_a+Caaa/pow(vbar_a,2))-p;
720  if (iter==1){y1=f;}
721  if (iter>1)
722  {
723  y2=f;
724  x3=x2-y2/(y2-y1)*(x2-x1);
725  y1=y2; x1=x2; x2=x3;
726  }
727  iter=iter+1;
728  if (iter>100){ return _HUGE; }
729  }
730 
731  if (FlagUseIdealGasEnthalpyCorrelations)
732  {
733  std::cout << "Not implemented" << std::endl;
734  }
735  else
736  {
737  sbar_w=IdealGasMolarEntropy_Water(T,p);
738  sbar_a=IdealGasMolarEntropy_Air(T,vbar_a);
739  }
740  if (psi_w!=0)
741  {
742  sbar=sbar_0+(1-psi_w)*sbar_a+psi_w*sbar_w-R_bar*(
743  (B+T*dBdT)/v_bar+(C+T*dCdT)/(2*pow(v_bar,2))+
744  (1-psi_w)*log(1-psi_w)+psi_w*log(psi_w));
745  }
746  else{
747  sbar=sbar_0+sbar_a;
748  }
749  return sbar; //[kJ/kmol/K]
750 }
751 
752 double DewpointTemperature(double T, double p, double psi_w)
753 {
754  int iter;
755  double p_w,eps,resid,Tdp=0,x1=0,x2=0,x3,y1=0,y2,T0;
756  double p_ws_dp,f_dp;
757 
758  // Make sure it isn't dry air, return an impossible temperature otherwise
759  if ((1-psi_w)<1e-16)
760  {
761  return -1;
762  }
763  // ------------------------------------------
764  // Iteratively find the dewpoint temperature
765  // ------------------------------------------
766 
767  // The highest dewpoint temperature possible is the dry-bulb temperature.
768  // When they are equal, the air is saturated (R=1)
769 
770  p_w = psi_w*p;
771 
772  // 0.61165... is the triple point pressure of water in kPa
773  if (p_w > 0.6116547241637944){
774  T0 = Props("T","P",p_w,"Q",1.0,"Water");
775  }
776  else{
777  T0 = 268;
778  }
779  // A good guess for Tdp is that enhancement factor is unity, which yields
780  // p_w_s = p_w, and get guess for T from saturation temperature
781 
782  iter=1; eps=1e-8; resid=999;
783  while ((iter<=3 || fabs(resid)>eps) && iter<100)
784  {
785  if (iter==1){x1 = T0; Tdp=x1;}
786  if (iter==2){x2 = x1 + 0.1; Tdp=x2;}
787  if (iter>2) {Tdp=x2;}
788 
789  if (Tdp >= 273.16)
790  {
791  // Saturation pressure at dewpoint [kPa]
792  p_ws_dp=Props("P","T",Tdp,"Q",0,"Water");
793  }
794  else
795  {
796  // Sublimation pressure at icepoint [kPa]
797  p_ws_dp=psub_Ice(Tdp);
798  }
799  // Enhancement Factor at dewpoint temperature [-]
800  f_dp=f_factor(Tdp,p);
801  // Error between target and actual pressure [kPa]
802  resid=p_w-p_ws_dp*f_dp;
803 
804  if (iter==1){y1=resid;}
805  if (iter>1)
806  {
807  y2=resid;
808  x3=x2-y2/(y2-y1)*(x2-x1);
809  y1=y2; x1=x2; x2=x3;
810  }
811  iter=iter+1;
812  }
813  return Tdp;
814 }
815 
817 {
818 private:
819  double _T,_p,_W,LHS,v_bar_w,M_ha;
820 public:
821  WetBulbSolver(double T, double p, double psi_w){
822  _T = T;
823  _p = p;
824  _W = epsilon*psi_w/(1-psi_w);
825 
826  //These things are all not a function of Twb
827  v_bar_w = MolarVolume(T,p,psi_w);
828  M_ha = MM_Water()*psi_w+(1-psi_w)*28.966;
829  LHS = MolarEnthalpy(T,p,psi_w,v_bar_w)*(1+_W)/M_ha;
830  };
832  double call(double Twb)
833  {
834  double epsilon=0.621945;
835  double f_wb,p_ws_wb,p_s_wb,W_s_wb,h_w,M_ha_wb,psi_wb,v_bar_wb;
836 
837  // Enhancement Factor at wetbulb temperature [-]
838  f_wb=f_factor(Twb,_p);
839  if (Twb > 273.16)
840  {
841  // Saturation pressure at wetbulb temperature [kPa]
842  p_ws_wb=Props("P","T",Twb,"Q",0,"Water");
843  }
844  else
845  {
846  // Sublimation pressure at wetbulb temperature [kPa]
847  p_ws_wb=psub_Ice(Twb);
848  }
849 
850  // Vapor pressure
851  p_s_wb = f_wb*p_ws_wb;
852  // wetbulb humidity ratio
853  W_s_wb = epsilon*p_s_wb/(_p-p_s_wb);
854  // wetbulb water mole fraction
855  psi_wb = W_s_wb/(epsilon+W_s_wb);
856  if (Twb > 273.16)
857  {
858  // Enthalpy of water [kJ/kg_water]
859  h_w=Props("H","T",Twb,"P",_p,"Water");
860  }
861  else
862  {
863  // Enthalpy of ice [kJ/kg_water]
864  h_w=h_Ice(Twb,_p)/1000;
865  }
866  // Mole masses of wetbulb and humid air
867 
868  M_ha_wb=MM_Water()*psi_wb+(1-psi_wb)*28.966;
869  v_bar_wb=MolarVolume(Twb,_p,psi_wb);
870  double RHS = (MolarEnthalpy(Twb,_p,psi_wb,v_bar_wb)*(1+W_s_wb)/M_ha_wb+(_W-W_s_wb)*h_w);
871  if (!ValidNumber(LHS-RHS)){throw ValueError();}
872  return LHS - RHS;
873  }
874 };
875 
877 {
878 public:
879  double p,hair_dry,r, RHS;
880  WetBulbTminSolver(double p, double hair_dry){
881  this->p = p;
882  this->hair_dry = hair_dry;
883  };
885  double call(double Ts)
886  {
887  RHS = HAProps("H","T",Ts,"P",p,"R",1);
888  if (!ValidNumber(RHS)){throw ValueError();}
889  r = RHS - this->hair_dry;
890  return r;
891  }
892 };
893 
894 double WetbulbTemperature(double T, double p, double psi_w)
895 {
896  // ------------------------------------------
897  // Iteratively find the wetbulb temperature
898  // ------------------------------------------
899  //
900  // If the temperature is less than the saturation temperature of water
901  // for the given atmospheric pressure, the highest wetbulb temperature that is possible is the dry bulb
902  // temperature
903  //
904  // If the temperature is above the saturation temperature corresponding to the atmospheric pressure,
905  // then the maximum value for the wetbulb temperature is the saturation temperature
906  double Tmax = T;
907  double Tsat = Props("T","P",p,"Q",1.0,"Water");
908  if (T >= Tsat)
909  {
910  Tmax = Tsat;
911  }
912 
913  // Instantiate the solver container class
914  WetBulbSolver WBS(T,p,psi_w);
915 
916  std::string errstr;
917 
918  double return_val;
919  try{
920  return_val = Secant(&WBS,Tmax,0.0001,1e-8,50,&errstr);
921 
922  // Solution obtained is out of range (T>Tmax)
923  if (return_val > Tmax) {throw ValueError();}
924  }
925  catch(std::exception &)
926  {
927  // The lowest wetbulb temperature that is possible for a given dry bulb temperature
928  // is the saturated air temperature which yields the enthalpy of dry air at dry bulb temperature
929 
930  try{
931  double hair_dry = MassEnthalpy(T,p,0);
932 
933  // Directly solve for the saturated temperature that yields the enthalpy desired
934  WetBulbTminSolver WBTS(p,hair_dry);
935  double Tmin = Brent(&WBTS,210,Tsat-1,1e-12,1e-12,50,&errstr);
936 
937  return_val = Brent(&WBS,Tmin-30,Tmax-1,1e-12,1e-12,50,&errstr);
938  }
939  catch(std::exception)
940  {
941  return_val = _HUGE;
942  }
943  }
944  return return_val;
945 }
946 static int Name2Type(const char *Name)
947 {
948  if (!strcmp(Name,"Omega") || !strcmp(Name,"HumRat") || !strcmp(Name,"W"))
949  return GIVEN_HUMRAT;
950  else if (!strcmp(Name,"Tdp") || !strcmp(Name,"T_dp") || !strcmp(Name,"DewPoint") || !strcmp(Name,"D"))
951  return GIVEN_TDP;
952  else if (!strcmp(Name,"Twb") || !strcmp(Name,"T_wb") || !strcmp(Name,"WetBulb") || !strcmp(Name,"B"))
953  return GIVEN_TWB;
954  else if (!strcmp(Name,"Enthalpy") || !strcmp(Name,"H"))
955  return GIVEN_ENTHALPY;
956  else if (!strcmp(Name,"RH") || !strcmp(Name,"RelHum") || !strcmp(Name,"R"))
957  return GIVEN_RH;
958  else if (!strcmp(Name,"Tdb") || !strcmp(Name,"T_db") || !strcmp(Name,"T"))
959  return GIVEN_T;
960  else if (!strcmp(Name,"P"))
961  return GIVEN_P;
962  else if (!strcmp(Name,"V") || !strcmp(Name,"Vda"))
963  return GIVEN_V;
964  else if (!strcmp(Name,"mu") || !strcmp(Name,"Visc") || !strcmp(Name,"M"))
965  return GIVEN_VISC;
966  else if (!strcmp(Name,"k") || !strcmp(Name,"Conductivity") || !strcmp(Name,"K"))
967  return GIVEN_COND;
968  else
969  printf("Sorry, your input [%s] was not understood to Name2Type in HumAir.c. Acceptable values are T,P,R,W,D,B,H,M,K and aliases thereof\n",Name);
970  return -1;
971 }
972 int TypeMatch(int TypeCode, const char *Input1Name, const char *Input2Name, const char *Input3Name)
973 {
974  // Return the index of the input variable that matches the input, otherwise return -1 for failure
975  if (TypeCode==Name2Type(Input1Name))
976  return 1;
977  if (TypeCode==Name2Type(Input2Name))
978  return 2;
979  if (TypeCode==Name2Type(Input3Name))
980  return 3;
981  else
982  return -1;
983 }
984 double MoleFractionWater(double T, double p, int HumInput, double InVal)
985 {
986  double p_ws,f,W,epsilon=0.621945,Tdp,p_ws_dp,f_dp,p_w_dp,p_s,RH;
987 
988  if (HumInput==GIVEN_HUMRAT) //(2)
989  {
990  W=InVal;
991  return W/(epsilon+W);
992  }
993  else if (HumInput==GIVEN_RH)
994  {
995  if (T>=273.16)
996  {
997  // Saturation pressure [kPa]
998  p_ws=Props("P","T",T,"Q",0,"Water");
999  }
1000  else
1001  {
1002  // Sublimation pressure [kPa]
1003  p_ws=psub_Ice(T);
1004 
1005  }
1006  // Enhancement Factor [-]
1007  f=f_factor(T,p);
1008 
1009  // Saturation pressure [kPa]
1010  p_s=f*p_ws;
1011  RH=InVal;
1012  W=epsilon*RH*p_s/(p-RH*p_s);
1013  return W/(epsilon+W);
1014  }
1015  else if (HumInput==GIVEN_TDP)
1016  {
1017  Tdp=InVal;
1018  // Saturation pressure at dewpoint [kPa]
1019  if (Tdp>=273.16)
1020  {
1021  p_ws_dp=Props("P","T",Tdp,"Q",0,"Water");
1022  }
1023  else{
1024  // Sublimation pressure [kPa]
1025  p_ws_dp=psub_Ice(Tdp);
1026  }
1027 
1028  // Enhancement Factor at dewpoint temperature [-]
1029  f_dp=f_factor(Tdp,p);
1030  // Water vapor pressure at dewpoint [kPa]
1031  p_w_dp=f_dp*p_ws_dp;
1032  // Water mole fraction [-]
1033  return p_w_dp/p;
1034  }
1035  else
1036  {
1037  return -1000000;
1038  }
1039 }
1040 
1041 double RelativeHumidity(double T, double p, double psi_w)
1042 {
1043  double p_ws,f,p_s,W;
1044  if (T>=273.16)
1045  {
1046  // Saturation pressure [kPa]
1047  p_ws=Props("P","T",T,"Q",0,"Water");
1048  }
1049  else
1050  {
1051  // sublimation pressure [kPa]
1052  p_ws=psub_Ice(T);
1053 
1054  }
1055  // Enhancement Factor [-]
1056  f=f_factor(T,p);
1057 
1058  // Saturation pressure [kPa]
1059  p_s=f*p_ws;
1060  // Find humidity ratio
1061  W=HumidityRatio(psi_w);
1062  // Find relative humidity using W/e=phi*p_s/(p-phi*p_s)
1063  return W/epsilon*p/(p_s*(1+W/epsilon));
1064 }
1065 EXPORT_CODE double CONVENTION HAProps(const char *OutputName, const char *Input1Name, double Input1, const char *Input2Name, double Input2, const char *Input3Name, double Input3)
1066 {
1067  try
1068  {
1069  int In1Type, In2Type, In3Type,iT,iW,iTdp,iRH,ip,Type1,Type2;
1070  double vals[3],p,T,RH,W,Tdp,psi_w,M_ha,v_bar,h_bar,s_bar,MainInputValue,SecondaryInputValue,T_guess;
1071  double Value1,Value2,W_guess;
1072  char MainInputName[100], SecondaryInputName[100],Name1[100],Name2[100];
1073 
1074  vals[0]=Input1;
1075  vals[1]=Input2;
1076  vals[2]=Input3;
1077 
1078  // First figure out what kind of inputs you have, convert names to Macro expansions
1079  In1Type=Name2Type(Input1Name);
1080  In2Type=Name2Type(Input2Name);
1081  In3Type=Name2Type(Input3Name);
1082 
1083  // Pressure must be included
1084  ip=TypeMatch(GIVEN_P,Input1Name,Input2Name,Input3Name);
1085  if (ip>0)
1086  p=vals[ip-1];
1087  else
1088  return -1000;
1089 
1090  // -----------------------------------------------------------------------------------------------------
1091  // Check whether the remaining values give explicit solution for mole fraction of water - nice and fast
1092  // -----------------------------------------------------------------------------------------------------
1093 
1094  // Find the codes if they are there
1095  iT= TypeMatch(GIVEN_T,Input1Name,Input2Name,Input3Name);
1096  iRH= TypeMatch(GIVEN_RH,Input1Name,Input2Name,Input3Name);
1097  iW= TypeMatch(GIVEN_HUMRAT,Input1Name,Input2Name,Input3Name);
1098  iTdp= TypeMatch(GIVEN_TDP,Input1Name,Input2Name,Input3Name);
1099 
1100  if (iT>0) // Found T (or alias) as an input
1101  {
1102  T=vals[iT-1];
1103  if (iRH>0) //Relative Humidity is provided
1104  {
1105  RH=vals[iRH-1];
1106  psi_w=MoleFractionWater(T,p,GIVEN_RH,RH);
1107  }
1108  else if (iW>0)
1109  {
1110  W=vals[iW-1];
1111  psi_w=MoleFractionWater(T,p,GIVEN_HUMRAT,W);
1112  }
1113  else if (iTdp>0)
1114  {
1115  Tdp=vals[iTdp-1];
1116  psi_w=MoleFractionWater(T,p,GIVEN_TDP,Tdp);
1117  }
1118  else
1119  {
1120  // Temperature and pressure are known, figure out which variable holds the other value
1121  if (In1Type!=GIVEN_T && In1Type!=GIVEN_P)
1122  {
1123  strcpy(SecondaryInputName,Input1Name);
1124  SecondaryInputValue=Input1;
1125  }
1126  else if (In2Type!=GIVEN_T && In2Type!=GIVEN_P)
1127  {
1128  strcpy(SecondaryInputName,Input2Name);
1129  SecondaryInputValue=Input2;
1130  }
1131  else if (In3Type!=GIVEN_T && In3Type!=GIVEN_P)
1132  {
1133  strcpy(SecondaryInputName,Input3Name);
1134  SecondaryInputValue=Input3;
1135  }
1136  else{
1137  return _HUGE;
1138  }
1139  // Find the value for W
1140  W_guess=0.0001;
1141  W=Secant_HAProps_W(SecondaryInputName,"P",p,"T",T,SecondaryInputValue,W_guess);
1142  // Mole fraction of water
1143  psi_w=MoleFractionWater(T,p,GIVEN_HUMRAT,W);
1144  // And on to output...
1145  }
1146  }
1147  else
1148  {
1149  // Need to iterate to find dry bulb temperature since temperature is not provided
1150 
1151  // Pick one input, and alter T to match the other input
1152 
1153  // Get the variables and their values that are NOT pressure for simplicity
1154  // because you know you need pressure as an input and you already have
1155  // its value in variable p
1156  if (ip==1) // Pressure is in slot 1
1157  {
1158  strcpy(Name1,Input2Name);
1159  Value1=Input2;
1160  strcpy(Name2,Input3Name);
1161  Value2=Input3;
1162  }
1163  else if (ip==2) // Pressure is in slot 2
1164  {
1165  strcpy(Name1,Input1Name);
1166  Value1=Input1;
1167  strcpy(Name2,Input3Name);
1168  Value2=Input3;
1169  }
1170  else if (ip==3) // Pressure is in slot 3
1171  {
1172  strcpy(Name1,Input1Name);
1173  Value1=Input1;
1174  strcpy(Name2,Input2Name);
1175  Value2=Input2;
1176  }
1177  else{
1178  return _HUGE;
1179  }
1180 
1181  // Get the integer type codes
1182  Type1=Name2Type(Name1);
1183  Type2=Name2Type(Name2);
1184 
1185  // First, if one of the inputs is something that can potentially yield
1186  // an explicit solution at a given iteration of the solver, use it
1187  if (Type1==GIVEN_RH || Type1==GIVEN_HUMRAT || Type1==GIVEN_TDP)
1188  {
1189  // First input variable is a "nice" one
1190 
1191  // MainInput is the one that you are using in the call to HAProps
1192  MainInputValue=Value1;
1193  strcpy(MainInputName,Name1);
1194  // SecondaryInput is the one that you are trying to match
1195  SecondaryInputValue=Value2;
1196  strcpy(SecondaryInputName,Name2);
1197  }
1198  else if (Type2==GIVEN_RH || Type2==GIVEN_HUMRAT || Type2==GIVEN_TDP)
1199  {
1200  // Second input variable is a "nice" one
1201 
1202  // MainInput is the one that you are using in the call to HAProps
1203  MainInputValue=Value2;
1204  strcpy(MainInputName,Name2);
1205  // SecondaryInput is the one that you are trying to match
1206  SecondaryInputValue=Value1;
1207  strcpy(SecondaryInputName,Name1);
1208  }
1209  else
1210  {
1211  printf("Sorry, but currently at least one of the variables as an input to HAProps() must be temperature, relative humidity, humidity ratio, or dewpoint\n Eventually will add a 2-D NR solver to find T and psi_w simultaneously, but not included now\n");
1212  return -1000;
1213  }
1214 
1215  double T_min = 210;
1216  double T_max = 450;
1217 
1218  T = -1;
1219 
1220  // First try to use the secant solver to find T at a few different temperatures
1221  for (T_guess = 210; T_guess < 450; T_guess += 60)
1222  {
1223  try{
1224  T = Secant_HAProps_T(SecondaryInputName,(char *)"P",p,MainInputName,MainInputValue,SecondaryInputValue,T_guess);
1225  double val = HAProps(SecondaryInputName,(char *)"T",T,(char *)"P",p,MainInputName,MainInputValue);
1226  if (!ValidNumber(T) || !ValidNumber(val) || !(T_min < T && T < T_max) || fabs(val-SecondaryInputValue)>1e-6)
1227  {
1228  throw ValueError();
1229  }
1230  else
1231  {
1232  break;
1233  }
1234  }
1235  catch (std::exception &){};
1236  }
1237 
1238  if (T < 0) // No solution found using secant
1239  {
1240  // Use the Brent's method solver to find T
1241  T = Brent_HAProps_T(SecondaryInputName,(char *)"P",p,MainInputName,MainInputValue,SecondaryInputValue,T_min,T_max);
1242  }
1243 
1244  // If you want the temperature, return it
1245  if (Name2Type(OutputName)==GIVEN_T)
1246  return T;
1247  else
1248  {
1249  // Otherwise, find psi_w for further calculations in the following section
1250  W=HAProps((char *)"W",(char *)"T",T,(char *)"P",p,MainInputName,MainInputValue);
1251  psi_w=MoleFractionWater(T,p,GIVEN_HUMRAT,W);
1252  }
1253  }
1254  M_ha=(1-psi_w)*28.966+MM_Water()*psi_w; //[kg_ha/kmol_ha]
1255 
1256  // -----------------------------------------------------------------
1257  // Calculate and return the desired value for known set of T,p,psi_w
1258  // -----------------------------------------------------------------
1259  if (!strcmp(OutputName,"Vda") || !strcmp(OutputName,"V"))
1260  {
1261  v_bar=MolarVolume(T,p,psi_w); //[m^3/mol_ha]
1262  W=HumidityRatio(psi_w);
1263  return v_bar*(1+W)/M_ha*1000; //[m^3/kg_da]
1264  }
1265  else if (!strcmp(OutputName,"Vha"))
1266  {
1267  v_bar=MolarVolume(T,p,psi_w); //[m^3/mol_ha]
1268  return v_bar/M_ha*1000; //[m^3/kg_ha]
1269  }
1270  else if (!strcmp(OutputName,"Y"))
1271  {
1272  return psi_w; //[mol_w/mol]
1273  }
1274  else if (!strcmp(OutputName,"Hda") || !strcmp(OutputName,"H"))
1275  {
1276  return MassEnthalpy(T,p,psi_w);
1277  }
1278  else if (!strcmp(OutputName,"Hha"))
1279  {
1280  v_bar=MolarVolume(T,p,psi_w); //[m^3/mol_ha]
1281  h_bar=MolarEnthalpy(T,p,psi_w,v_bar); //[kJ/kmol_ha]
1282  return h_bar/M_ha; //[kJ/kg_ha]
1283  }
1284  else if (!strcmp(OutputName,"S") || !strcmp(OutputName,"Entropy"))
1285  {
1286  v_bar=MolarVolume(T,p,psi_w); //[m^3/mol_ha]
1287  s_bar=MolarEntropy(T,p,psi_w,v_bar); //[kJ/kmol_ha]
1288  W=HumidityRatio(psi_w); //[kg_w/kg_da]
1289  return s_bar*(1+W)/M_ha; //[kJ/kg_da]
1290  }
1291  else if (!strcmp(OutputName,"C") || !strcmp(OutputName,"cp"))
1292  {
1293  double v_bar1,v_bar2,h_bar1,h_bar2, cp_bar, dT = 1e-3;
1294  v_bar1=MolarVolume(T-dT,p,psi_w); //[m^3/mol_ha]
1295  h_bar1=MolarEnthalpy(T-dT,p,psi_w,v_bar1); //[kJ/kmol_ha]
1296  v_bar2=MolarVolume(T+dT,p,psi_w); //[m^3/mol_ha]
1297  h_bar2=MolarEnthalpy(T+dT,p,psi_w,v_bar2); //[kJ/kmol_ha]
1298  W=HumidityRatio(psi_w); //[kg_w/kg_da]
1299  cp_bar = (h_bar2-h_bar1)/(2*dT);
1300  return cp_bar*(1+W)/M_ha; //[kJ/kg_da]
1301  }
1302  else if (!strcmp(OutputName,"Cha") || !strcmp(OutputName,"cp_ha"))
1303  {
1304  double v_bar1,v_bar2,h_bar1,h_bar2, cp_bar, dT = 1e-3;
1305  v_bar1=MolarVolume(T-dT,p,psi_w); //[m^3/mol_ha]
1306  h_bar1=MolarEnthalpy(T-dT,p,psi_w,v_bar1); //[kJ/kmol_ha]
1307  v_bar2=MolarVolume(T+dT,p,psi_w); //[m^3/mol_ha]
1308  h_bar2=MolarEnthalpy(T+dT,p,psi_w,v_bar2); //[kJ/kmol_ha]
1309  W=HumidityRatio(psi_w); //[kg_w/kg_da]
1310  cp_bar = (h_bar2-h_bar1)/(2*dT);
1311  return cp_bar/M_ha; //[kJ/kg_da]
1312  }
1313  else if (!strcmp(OutputName,"Tdp") || !strcmp(OutputName,"D"))
1314  {
1315  return DewpointTemperature(T,p,psi_w); //[K]
1316  }
1317  else if (!strcmp(OutputName,"Twb") || !strcmp(OutputName,"T_wb") || !strcmp(OutputName,"WetBulb") || !strcmp(OutputName,"B"))
1318  {
1319  return WetbulbTemperature(T,p,psi_w); //[K]
1320  }
1321  else if (!strcmp(OutputName,"Omega") || !strcmp(OutputName,"HumRat") || !strcmp(OutputName,"W"))
1322  {
1323  return HumidityRatio(psi_w);
1324  }
1325  else if (!strcmp(OutputName,"RH") || !strcmp(OutputName,"RelHum") || !strcmp(OutputName,"R"))
1326  {
1327  return RelativeHumidity(T,p,psi_w);
1328  }
1329  else if (!strcmp(OutputName,"mu") || !strcmp(OutputName,"Visc") || !strcmp(OutputName,"M"))
1330  {
1331  return Viscosity(T,p,psi_w);
1332  }
1333  else if (!strcmp(OutputName,"k") || !strcmp(OutputName,"Conductivity") || !strcmp(OutputName,"K"))
1334  {
1335  return Conductivity(T,p,psi_w);
1336  }
1337  else
1338  {
1339  return -1000;
1340  }
1341  }
1342  catch (std::exception &e)
1343  {
1344  set_err_string(e.what());
1345  return _HUGE;
1346  }
1347  catch (...)
1348  {
1349  return _HUGE;
1350  }
1351 }
1352 
1353 EXPORT_CODE double CONVENTION HAProps_Aux(const char* Name,double T, double p, double W, char *units)
1354 {
1355  // This function provides some things that are not usually needed, but could be interesting for debug purposes.
1356 
1357  // Requires W since it is nice and fast and always defined. Put a dummy value if you want something that doesn't use humidity
1358 
1359  // Takes temperature, pressure, and humidity ratio W as inputs;
1360  double psi_w,Tj,tau_Water,tau_Air,B_aa,C_aaa,B_ww,C_www,B_aw,C_aaw,C_aww,v_bar,delta, tau;
1361 
1362  Tj=132.6312;
1363  tau_Air=Tj/T;
1364  tau_Water=Water.reduce.T/T;
1365 
1366  try{
1367  if (!strcmp(Name,"Baa"))
1368  {
1369  B_aa=B_Air(T)*MM_Air()/1e3; //[m^3/kg] to [m^3/mol]
1370  strcpy(units,"m^3/mol");
1371  return B_aa;
1372  }
1373  else if (!strcmp(Name,"Caaa"))
1374  {
1375  C_aaa=C_Air(T)*MM_Air()*MM_Air()/1e6; //[m^6/kg^2] to [m^6/mol^2]
1376  strcpy(units,"m^6/mol^2");
1377  return C_aaa;
1378  }
1379  else if (!strcmp(Name,"Bww"))
1380  {
1381  B_ww=B_Water(T)*MM_Water()/1e3; //[m^3/kg] to [m^3/mol]
1382  strcpy(units,"m^3/mol");
1383  return B_ww;
1384  }
1385  else if (!strcmp(Name,"Cwww"))
1386  {
1387  C_www=C_Water(T)*MM_Water()*MM_Water()/1e6; //[m^6/kg^2] to [m^6/mol^2]
1388  strcpy(units,"m^6/mol^2");
1389  return C_www;
1390  }
1391  else if (!strcmp(Name,"dBaa"))
1392  {
1393  B_aa=dBdT_Air(T)*MM_Air()/1e3; //[m^3/kg] to [m^3/mol]
1394  strcpy(units,"m^3/mol");
1395  return B_aa;
1396  }
1397  else if (!strcmp(Name,"dCaaa"))
1398  {
1399  C_aaa=dCdT_Air(T)*MM_Air()*MM_Air()/1e6; //[m^6/kg^2] to [m^6/mol^2]
1400  strcpy(units,"m^6/mol^2");
1401  return C_aaa;
1402  }
1403  else if (!strcmp(Name,"dBww"))
1404  {
1405  B_ww=dBdT_Water(T)*MM_Water()/1e3; //[m^3/kg] to [m^3/mol]
1406  strcpy(units,"m^3/mol");
1407  return B_ww;
1408  }
1409  else if (!strcmp(Name,"dCwww"))
1410  {
1411  C_www=dCdT_Water(T)*MM_Water()*MM_Water()/1e6; //[m^6/kg^2] to [m^6/mol^2]
1412  strcpy(units,"m^6/mol^2");
1413  return C_www;
1414  }
1415  else if (!strcmp(Name,"Baw"))
1416  {
1417  B_aw=_B_aw(T)/1e3; //[dm^3/mol] to [m^3/mol]
1418  strcpy(units,"m^3/mol");
1419  return B_aw;
1420  }
1421  else if (!strcmp(Name,"Caww"))
1422  {
1423  C_aww=_C_aww(T)/1e6; //[dm^6/mol] to [m^6/mol^2]
1424  strcpy(units,"m^6/mol^2");
1425  return C_aww;
1426  }
1427  else if (!strcmp(Name,"Caaw"))
1428  {
1429  C_aaw=_C_aaw(T)/1e6; //[dm^6/mol] to [m^6/mol^2]
1430  strcpy(units,"m^6/mol^2");
1431  return C_aaw;
1432  }
1433  else if (!strcmp(Name,"dBaw"))
1434  {
1435  double dB_aw=_dB_aw_dT(T)/1e3; //[dm^3/mol] to [m^3/mol]
1436  strcpy(units,"m^3/mol");
1437  return dB_aw;
1438  }
1439  else if (!strcmp(Name,"dCaww"))
1440  {
1441  double dC_aww=_dC_aww_dT(T)/1e6; //[dm^6/mol] to [m^6/mol^2]
1442  strcpy(units,"m^6/mol^2");
1443  return dC_aww;
1444  }
1445  else if (!strcmp(Name,"dCaaw"))
1446  {
1447  double dC_aaw=_dC_aaw_dT(T)/1e6; //[dm^6/mol] to [m^6/mol^2]
1448  strcpy(units,"m^6/mol^2");
1449  return dC_aaw;
1450  }
1451  else if (!strcmp(Name,"beta_H"))
1452  {
1453  strcpy(units,"1/Pa");
1454  return HenryConstant(T);
1455  }
1456  else if (!strcmp(Name,"kT"))
1457  {
1458  strcpy(units,"1/Pa");
1459  if (T>273.16)
1460  {
1461  double rho = Props("D","T",T,"P",p,"Water");
1462  return DerivTerms((char *)"IsothermalCompressibility",T,rho,(char *)"Water")/1000; //[1/Pa]
1463  }
1464  else
1465  return IsothermCompress_Ice(T,p); //[1/Pa]
1466  }
1467  else if (!strcmp(Name,"p_ws"))
1468  {
1469  strcpy(units,"kPa");
1470  if (T>273.16)
1471  return Props("P","T",T,"Q",0,"Water");
1472  else
1473  return psub_Ice(T);
1474  }
1475  else if (!strcmp(Name,"vbar_ws"))
1476  {
1477  strcpy(units,"m^3/mol");
1478  if (T>273.16)
1479  {
1480  // It is liquid water
1481  return 1.0/Props("D","T",T,"Q",0,"Water")*MM_Water()/1000; //[m^3/mol]
1482  }
1483  else
1484  {
1485  // It is ice
1486  return dg_dp_Ice(T,p/1000)*MM_Water()/1000/1000; //[m^3/mol]
1487  }
1488  }
1489  else if (!strcmp(Name,"f"))
1490  {
1491  strcpy(units,"-");
1492  return f_factor(T,p);
1493  }
1494  // Get psi_w since everything else wants it
1495  psi_w=MoleFractionWater(T,p,GIVEN_HUMRAT,W);
1496  if (!strcmp(Name,"Bm"))
1497  {
1498  strcpy(units,"m^3/mol");
1499  return B_m(T,psi_w);
1500  }
1501  else if (!strcmp(Name,"Cm"))
1502  {
1503  strcpy(units,"m^6/mol^2");
1504  return C_m(T,psi_w);
1505  }
1506  else if (!strcmp(Name,"hvirial"))
1507  {
1508  v_bar=MolarVolume(T,p,psi_w);
1509  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));
1510  }
1511  else if (!strcmp(Name,"ha"))
1512  {
1513  delta=1.1/322; tau=132/T;
1514  return 1+tau*DerivTerms((char *)"dphi0_dTau",tau,delta,(char *)"Water");
1515  }
1516  else if (!strcmp(Name,"hw"))
1517  {
1518  //~ return Props('D','T',T,'P',p,"Water")/322; tau=647/T;
1519  delta=1000/322; tau=647/T;
1520  //~ delta=rho_Water(T,p,TYPE_TP);tau=647/T;
1521  return 1+tau*DerivTerms((char *)"dphi0_dTau",tau,delta,(char *)"Water");
1522  }
1523  else if (!strcmp(Name,"hbaro_w"))
1524  {
1525  v_bar=MolarVolume(T,p,psi_w);
1526  return IdealGasMolarEnthalpy_Water(T,v_bar);
1527  }
1528  else if (!strcmp(Name,"hbaro_a"))
1529  {
1530  v_bar=MolarVolume(T,p,psi_w);
1531  return IdealGasMolarEnthalpy_Air(T,v_bar);
1532  }
1533  else
1534  {
1535  printf("Sorry I didn't understand your input [%s] to HAProps_Aux\n",Name);
1536  return -1;
1537  }
1538  }
1539  catch(std::exception &)
1540  {
1541  return _HUGE;
1542  }
1543  return _HUGE;
1544 }
1545 double cair_sat(double T)
1546 {
1547  // Air saturation specific heat
1548  // Based on a correlation from EES, good from 250K to 300K.
1549  // No error bound checking is carried out
1550  // T: [K]
1551  // cair_s: [kJ/kg-K]
1552  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;
1553 }
1554 
1555 double IceProps(const char* Name, double T, double p)
1556 {
1557  if (!strcmp(Name,"s"))
1558  {
1559  return s_Ice(T,p*1000.0);
1560  }
1561  else if (!strcmp(Name,"rho"))
1562  {
1563  return rho_Ice(T,p*1000.0);
1564  }
1565  else if (!strcmp(Name,"h"))
1566  {
1567  return h_Ice(T,p*1000.0);
1568  }
1569  else
1570  {
1571  return 1e99;
1572  }
1573 }
1574 
1575 
1576 #ifndef DISABLE_CATCH
1577 #include <math.h>
1578 #include "Catch/catch.hpp"
1579 TEST_CASE((char*)"Tests from ASHRAE RP-1485",(char*)"[RP1485]")
1580 {
1581  SECTION((char*)"Table A.8.1")
1582  {
1583  double p1 = 101.325, T= 473.15;
1584  // W B V H S
1585  std::string rows1[] ={ "0.00 45.07 1.341 202.52 0.5558",
1586  "0.05 55.38 1.448 346.49 1.0299",
1587  "0.10 61.85 1.556 490.43 1.4736",
1588  "0.20 69.95 1.771 778.24 2.3336",
1589  "0.30 75.00 1.986 1066.00 3.1751",
1590  "0.40 78.51 2.201 1353.71 4.0059",
1591  "0.50 81.12 2.416 1641.40 4.8295",
1592  "0.60 83.14 2.630 1929.06 5.6479",
1593  "0.70 84.76 2.845 2216.70 6.4623",
1594  "0.80 86.09 3.060 2504.32 7.2736",
1595  "0.90 87.20 3.274 2791.94 8.0824",
1596  "1.00 88.15 3.489 3079.55 8.8890"};
1597  for (int i = 0; i < 12; i++)
1598  {
1599  std::vector<std::string> elements = strsplit(rows1[i],' ');
1600  double W = strtod(elements[0].c_str(),NULL);
1601  SECTION((char*)"B")
1602  {
1603  double B = strtod(elements[1].c_str(),NULL);
1604  double BCP = HAProps("B","T",T,"W",W,"P",p1) - 273.15;
1605  CAPTURE(B);
1606  CAPTURE(BCP);
1607  CHECK(fabs(BCP-B) < 0.01);
1608  }
1609  SECTION((char*)"V")
1610  {
1611  double V = strtod(elements[2].c_str(),NULL);
1612  double VCP = HAProps("V","T",T,"W",W,"P",p1);
1613  CAPTURE(V);
1614  CAPTURE(VCP);
1615  CHECK(fabs(VCP-V) < 0.01);
1616  }
1617  SECTION((char*)"H")
1618  {
1619  double H = strtod(elements[3].c_str(),NULL);
1620  double HCP = HAProps("H","T",T,"W",W,"P",p1);
1621  CAPTURE(H);
1622  CAPTURE(HCP);
1623  CHECK(fabs(HCP-H) < 0.01);
1624  }
1625  SECTION((char*)"S")
1626  {
1627  double S = strtod(elements[4].c_str(),NULL);
1628  double SCP = HAProps("S","T",T,"W",W,"P",p1);
1629  CAPTURE(S);
1630  CAPTURE(SCP);
1631  CHECK(fabs(SCP-S) < 0.01);
1632  }
1633  }
1634  }
1635 }
1636 #endif
double MolarEntropy(double T, double p, double psi_w, double v_bar)
double f_factor(double T, double p)
double h_Ice(double T, double p)
Definition: Ice.cpp:114
void UseVirialCorrelations(int flag)
double call(double Twb)
double WetbulbTemperature(double T, double p, double psi_w)
EXPORT_CODE double CONVENTION HAProps(const char *OutputName, const char *Input1Name, double Input1, const char *Input2Name, double Input2, const char *Input3Name, double Input3)
double IdealGasMolarEnthalpy_Air(double T, double v_bar)
int TypeMatch(int TypeCode, const char *Input1Name, const char *Input2Name, const char *Input3Name)
int returnHumAirCode(const char *Code)
void UseIdealGasEnthalpyCorrelations(int flag)
virtual double phi0(double tau, double delta)
Definition: FluidClass.cpp:381
double rho_Ice(double T, double p)
Definition: Ice.cpp:124
double Props(std::string Output, std::string Name1, double Prop1, std::string Name2, double Prop2, std::string Ref)
Definition: CoolProp.cpp:902
double Props1(std::string FluidName, std::string Output)
Definition: CoolProp.cpp:586
double molemass
Definition: FluidClass.h:43
WetBulbSolver(double T, double p, double psi_w)
std::vector< std::string > strsplit(std::string s, char del)
virtual double dphi0_dTau(double tau, double delta)
Definition: FluidClass.cpp:403
struct CriticalStruct reduce
A pointer to the point that is used to reduce the T and rho for EOS.
Definition: FluidClass.h:222
double IdealGasMolarEntropy_Air(double T, double v_bar_a)
#define EXPORT_CODE
Definition: CoolPropTools.h:31
double RelativeHumidity(double T, double p, double psi_w)
double IdealGasMolarEnthalpy_Water(double T, double v_bar)
#define CAPTURE(msg)
Definition: catch.hpp:8074
void UseIsothermCompressCorrelation(int flag)
double PropsSI(std::string Output, std::string Name1, double Prop1, std::string Name2, double Prop2, std::string Ref)
Definition: CoolProp.cpp:886
virtual double call(double)=0
double IdealGasMolarEntropy_Water(double T, double p)
WetBulbTminSolver(double p, double hair_dry)
double MoleFractionWater(double T, double p, int HumInput, double InVal)
double cair_sat(double T)
TEST_CASE((char *)"Tests from ASHRAE RP-1485",(char *)"[RP1485]")
struct OtherParameters params
Definition: FluidClass.h:220
Definition: Air.h:4
double call(double Ts)
double MolarEnthalpy(double T, double p, double psi_w, double v_bar)
double IceProps(const char *Name, double T, double p)
double Brent(FuncWrapper1D *f, double a, double b, double macheps, double t, int maxiter, std::string *errstr)
Definition: Solvers.cpp:235
void HAHelp(void)
struct CriticalStruct crit
Definition: FluidClass.h:218
EXPORT_CODE double CONVENTION HAProps_Aux(const char *Name, double T, double p, double W, char *units)
double HumidityRatio(double psi_w)
double Viscosity(double T, double p, double psi_w)
double Conductivity(double T, double p, double psi_w)
double DerivTerms(long iTerm, double T, double rho, Fluid *pFluid)
Definition: CoolProp.cpp:1001
double IsothermCompress_Ice(double T, double p)
Definition: Ice.cpp:27
double psub_Ice(double T)
Definition: Ice.cpp:36
double Secant(FuncWrapper1D *f, double x0, double dx, double tol, int maxiter, std::string *errstring)
Definition: Solvers.cpp:123
double dg_dp_Ice(double T, double p)
Definition: Ice.cpp:70
givens
void set_err_string(std::string error_string)
Definition: CoolProp.cpp:191
#define SECTION(name, description)
Definition: catch.hpp:8088
double MolarVolume(double T, double p, double psi_w)
double DewpointTemperature(double T, double p, double psi_w)
#define CHECK(expr)
Definition: catch.hpp:8058
double MassEnthalpy(double T, double p, double psi_w)
double s_Ice(double T, double p)
Definition: Ice.cpp:134
bool ValidNumber(double x)
#define CONVENTION
Definition: CoolPropTools.h:34