Files
CoolProp/dev/scripts/fit_ancillary_ODRPACK.py
Matthis Thorade 19a4875879 More autopep8 (#1621)
* autopep8 rule-groups E101,W1,W2,W3

* autopep8 with rule group E3 (blank lines)

autopep8 --in-place --recursive --max-line-length=200 --exclude="externals" --select="E101,E3,W1,W2,W3" .

* tabs and space W191

* autopep8 aggressive
2017-12-13 14:43:41 +01:00

366 lines
11 KiB
Python

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)