from __future__ import division import numpy as np from scipy.odr import * import scipy.optimize, random import matplotlib.pyplot as plt import textwrap import random from CoolProp.CoolProp import Props, get_REFPROPname def rsquared(x, y): """ Return R^2 where x and y are array-like. from http://stackoverflow.com/questions/893657/how-do-i-calculate-r-squared-using-python-and-numpy """ import scipy.stats slope, intercept, r_value, p_value, std_err = scipy.stats.linregress(x, y) return r_value**2 def saturation_density(Ref, ClassName, form = 'A', LV = 'L', perc_error_allowed = 0.3, fName = None, add_critical = True): """ Parameters ---------- Ref : string The fluid name for the fluid that will be used to generate the saturation data ClassName : The name of the class that will be used in the C++ code form : string If ``'A'``, use a term of the form """ if fName is None: Tc = Props(Ref,'Tcrit') pc = Props(Ref,'pcrit') rhoc = Props(Ref,'rhocrit') Tmin = Props(Ref,'Tmin') print Ref,Tmin,Props(Ref,'Ttriple') TT = np.linspace(Tmin, Tc-1, 1000) TT = list(TT) # Add temperatures around the critical temperature if add_critical: for dT in [1e-10,1e-9,1e-8,1e-7,1e-6,1e-5,1e-4,1e-3,1e-2,1e-1]: TT.append(Tc-dT) TT = np.array(sorted(TT)) p = Props('P','T',TT,'Q',0,Ref) rhoL = Props('D','T',TT,'Q',0,Ref) rhoV = Props('D','T',TT,'Q',1,Ref) else: Tc = 423.27 pc = 3533 rhoc = 470 Tmin = 273 lines = open(fName,'r').readlines() TT,p,rhoL,rhoV = [],[],[],[] for line in lines: _T,_p,_rhoL,_rhoV = line.split(' ') TT.append(float(_T)) p.append(float(_p)) rhoL.append(float(_rhoL)) rhoV.append(float(_rhoV)) TT = np.array(TT) # Start with a large library of potential powers n = [i/6.0 for i in range(1,200)]#+[0.35+i/200.0 for i in range(1,70)]+[0.05+0.01*i for i in range(1,70)] x = 1.0-TT/Tc if LV == 'L': rho_EOS = rhoL elif LV == 'V': rho_EOS = rhoV else: raise ValueError if form == 'A': y = np.array(rho_EOS)/rhoc-1 elif form == 'B': y = (np.log(rho_EOS)-np.log(rhoc))*TT/Tc else: raise ValueError max_abserror = 0 while len(n) > 3: print max_abserror, len(n) def f_p(B, x): # B is a vector of the parameters. # x is an array of the current x values. return sum([_B*x**(_n) for _B,_n in zip(B,n)]) linear = Model(f_p) mydata = Data(x, y) myodr = ODR(mydata, linear, beta0=[0]*len(n)) myoutput = myodr.run() beta = myoutput.beta sd = myoutput.sd_beta if form == 'A': rho_fit = (f_p(myoutput.beta,x)+1)*rhoc elif form == 'B': rho_fit = np.exp(f_p(myoutput.beta,x)*Tc/TT)*rhoc else: raise ValueError print 'first,last',TT[0],TT[-1],rho_fit[0],rho_fit[-1], rho_EOS[0], rho_EOS[-1] max_abserror = np.max(np.abs(rho_fit/rho_EOS-1))*100 dropped_indices = [i for i in range(len(n)) if abs(sd[i])<1e-15 ] if dropped_indices: for i in reversed(sorted(dropped_indices)): n.pop(i) print 'popping...', len(n), 'terms remaining' continue if max_abserror > perc_error_allowed: break # The last good run will be used else: print max_abserror Ncoeffs = str(list(myoutput.beta)).lstrip('[').rstrip(']') tcoeffs = str(n).lstrip('[').rstrip(']') maxerror = max_abserror if form == 'A': code_template = textwrap.dedent( """ for (int i=1; i<={count:d}; i++) {{ summer += N[i]*pow(theta,t[i]); }} return reduce.rho*(summer+1); """.format(count = len(n)) ) elif form == 'B': code_template = textwrap.dedent( """ for (int i=1; i<={count:d}; i++) {{ summer += N[i]*pow(theta,t[i]); }} return reduce.rho*exp(reduce.T/T*summer); """.format(count = len(n)) ) else: raise ValueError # Find the least significant entry (the one with the largest relative standard error) # and remove it n.pop(np.argmax(np.abs(sd/beta))) #Remove elements that are not template = textwrap.dedent( """ double {name:s}Class::rhosat{LV:s}(double T) {{ // Maximum absolute error is {error:f} % between {Tmin:f} K and {Tmax:f} K const double t[] = {{0, {tcoeffs:s}}}; const double N[] = {{0, {Ncoeffs:s}}}; double summer=0,theta; theta=1-T/reduce.T; \t{code:s} }} """) the_string = template.format(tcoeffs = tcoeffs, Ncoeffs = Ncoeffs, name = ClassName, Tmin = Tmin, Tmax = TT[-1], error = maxerror, code = code_template, LV = LV ) f = open('anc.txt','a') f.write(the_string) f.close() return the_string def saturation_pressure_brute(Ref, ClassName): Tc = Props(Ref,'Tcrit') pc = Props(Ref,'pcrit') rhoc = Props(Ref,'rhocrit') Tmin = Props(Ref,'Tmin') TT = np.linspace(Tmin+1e-6, Tc-0.00001, 300) p = np.array([Props('P','T',T,'Q',0,Ref) for T in TT]) rhoL = np.array([Props('D','T',T,'Q',0,Ref) for T in TT]) rhoV = np.array([Props('D','T',T,'Q',1,Ref) for T in TT]) Np = 10 max_abserror = 99999 bbest = [] x = 1.0-TT/Tc y = (np.log(rhoL)-np.log(rhoc))*TT/Tc def f_p(B, x): # B is a vector of the parameters. # x is an array of the current x values. return sum([_B*x**(_n) for _B,_n in zip(B,b)]) linear = Model(f_p) mydata = Data(x, y) for attempt in range(300): n = [i/6.0 for i in range(1,100)]+[0.35+i/200.0 for i in range(1,70)]+[0.05+0.01*i for i in range(1,70)] b = [] for _ in range(6): i = random.randint(0,len(n)-1) b.append(n.pop(i)) myodr = ODR(mydata, linear, beta0 = [1]*len(b)) myoutput = myodr.run() b = np.array(b) keepers = np.abs(myoutput.sd_beta/myoutput.beta) < 0.1 if any(keepers): b = b[keepers] myodr = ODR(mydata, linear, beta0 = [1]*len(b)) myoutput = myodr.run() rho_fit = np.exp(f_p(myoutput.beta, x)*Tc/TT)*rhoc abserror = np.max(np.abs(rho_fit/rhoL-1))*100 print '.' if abserror < max_abserror: max_abserror = abserror bbest = b betabest = myoutput.beta print abserror, myoutput.sum_square, myoutput.sd_beta/myoutput.beta def saturation_pressure(Ref, ClassName, fName = None, LV = None): if fName is None: Tc = Props(Ref,'Tcrit') pc = Props(Ref,'pcrit') rhoc = Props(Ref,'rhocrit') Tmin = Props(Ref,'Tmin') TT = np.linspace(Tmin+1e-6, Tc-0.00001, 300) pL = Props('P','T',TT,'Q',0,Ref) pV = Props('P','T',TT,'Q',1,Ref) rhoL = Props('D','T',TT,'Q',0,Ref) rhoV = Props('D','T',TT,'Q',1,Ref) else: Tc = 423.27 pc = 3533 rhoc = 470 Tmin = 273 lines = open(fName,'r').readlines() TT,p,rhoL,rhoV = [],[],[],[] for line in lines: _T,_p,_rhoL,_rhoV = line.split(' ') TT.append(float(_T)) p.append(float(_p)) rhoL.append(float(_rhoL)) rhoV.append(float(_rhoV)) TT = np.array(TT) Np = 60 n = range(1,Np) max_abserror = 0 while len(n) > 3: def f_p(B, x): # B is a vector of the parameters. # x is an array of the current x values. return sum([_B*x**(_n/2.0) for _B,_n in zip(B,n)]) x = 1.0-TT/Tc if LV == 'L': y = (np.log(pL)-np.log(pc))*TT/Tc elif LV == 'V' or LV is None: y = (np.log(pV)-np.log(pc))*TT/Tc linear = Model(f_p) mydata = Data(x, y) myodr = ODR(mydata, linear, beta0=[0]*len(n)) myoutput = myodr.run() beta = myoutput.beta sd = myoutput.sd_beta p_fit = np.exp(f_p(myoutput.beta,x)*Tc/TT)*pc if LV == 'L': max_abserror = np.max(np.abs((p_fit/pL)-1)*100) elif LV == 'V' or LV is None: max_abserror = np.max(np.abs((p_fit/pV)-1)*100) print max_abserror psat_error = max_abserror dropped_indices = [i for i in range(len(n)) if abs(sd[i])<1e-15 ] if dropped_indices: #for i in reversed(dropped_indices): # randomly drop one of them n.pop(random.choice(dropped_indices)) continue if max_abserror < 0.5: #Max error is 0.5% Ncoeffs = str(list(myoutput.beta)).lstrip('[').rstrip(']') tcoeffs = str(n).lstrip('[').rstrip(']') maxerror = max_abserror N = len(n) else: break # Find the least significant entry (the one with the largest standard error) # and remove it n.pop(np.argmax(sd)) #Remove elements that are not import textwrap template = textwrap.dedent( """ double {name:s}Class::psat{LV:s}(double T) {{ // Maximum absolute error is {psat_error:f} % between {Tmin:f} K and {Tmax:f} K const double t[]={{0, {tcoeffs:s}}}; const double N[]={{0, {Ncoeffs:s}}}; double summer=0,theta; theta=1-T/reduce.T; for (int i=1;i<={N:d};i++) {{ summer += N[i]*pow(theta,t[i]/2); }} return reduce.p.Pa*exp(reduce.T/T*summer); }} """) the_string = template.format(N = len(n)+1, tcoeffs = tcoeffs, Ncoeffs = Ncoeffs, name = ClassName, Tmin = Tmin, Tmax = TT[-1], psat_error = maxerror, LV = LV if LV in ['L','V'] else '' ) f = open('anc.txt','a') f.write(the_string) f.close() return the_string if __name__ == '__main__': for RPFluid,Fluid in [('REFPROP-MIX:R32[0.47319469]&R125[0.2051091]&R134a[0.32169621]','R407F'), #for RPFluid,Fluid in [('R11','R11'), ]: # saturation_pressure_brute(RPFluid, Fluid # saturation_pressure(RPFluid, Fluid, LV = 'L') # saturation_pressure(RPFluid, Fluid, LV = 'V') saturation_density(RPFluid, Fluid, form='A', LV='L') saturation_density(RPFluid, Fluid, form='B', LV='V', perc_error_allowed = 0.4)