Files
CoolProp/dev/scripts/fit_saturation_critical_splines.py
2019-01-12 20:45:25 -07:00

410 lines
14 KiB
Python

from __future__ import print_function
import CoolProp as CoolProp
import matplotlib.pyplot as plt
import numpy as np, os, json
# Turn off the critical splines using json
jj = json.loads(CoolProp.CoolProp.get_config_as_json_string()) # Get the json values that are set already
jj['CRITICAL_SPLINES_ENABLED'] = False
CoolProp.CoolProp.set_config_as_json_string(json.dumps(jj)) # Set the values using json
# Double check that it was set properly
jj = json.loads(CoolProp.CoolProp.get_config_as_json_string()); print(jj)
class LinearFitter(object):
def __init__(self):
self.Nconstraints = 0
self.A = np.zeros((2, 2))
self.B = np.zeros((2, 1))
def build(self):
if (self.Nconstraints == 2):
self.a = np.linalg.solve(self.A, self.B).squeeze();
else:
raise ValueError("Number of constraints[%d] is not equal to 2" % Nconstraints);
self.c = list(np.r_[0, 0, self.a])
def add_value_constraint(self, x, y):
i = self.Nconstraints;
if (i == 2):
return false;
self.A[i, 0] = x;
self.A[i, 1] = 1;
self.B[i] = y;
self.Nconstraints += 1;
def add_derivative_constraint(self, x, dydx):
i = self.Nconstraints;
if (i == 2):
return false;
self.A[i, 0] = 1;
self.A[i, 1] = 0;
self.B[i] = dydx;
self.Nconstraints += 1;
def evaluate(self, x):
return np.polyval(self.a, x)
class QuadraticFitter(object):
def __init__(self):
self.Nconstraints = 0
self.A = np.zeros((3, 3))
self.B = np.zeros((3, 1))
def build(self):
if (self.Nconstraints == 3):
self.a = np.linalg.solve(self.A, self.B).squeeze();
else:
raise ValueError("Number of constraints[%d] is not equal to 3" % Nconstraints);
self.c = list(np.r_[0, self.a])
def add_value_constraint(self, x, y):
i = self.Nconstraints;
if (i == 4):
return false;
self.A[i, 0] = x * x;
self.A[i, 1] = x;
self.A[i, 2] = 1;
self.B[i] = y;
self.Nconstraints += 1;
def add_derivative_constraint(self, x, dydx):
i = self.Nconstraints;
if (i == 4):
return false;
self.A[i, 0] = 2 * x;
self.A[i, 1] = 1;
self.A[i, 2] = 0;
self.B[i] = dydx;
self.Nconstraints += 1;
def add_second_derivative_constraint(self, x, d2ydx2):
i = self.Nconstraints;
if (i == 3):
return false;
self.A[i, 0] = 2;
self.A[i, 1] = 0;
self.A[i, 2] = 0;
self.B[i] = d2ydx2;
self.Nconstraints += 1;
def evaluate(self, x):
return np.polyval(self.a, x)
class CubicFitter(object):
def __init__(self):
self.Nconstraints = 0
self.A = np.zeros((4, 4))
self.B = np.zeros((4, 1))
def build(self):
if (self.Nconstraints == 4):
self.a = np.linalg.solve(self.A, self.B).squeeze();
else:
raise ValueError("Number of constraints[%d] is not equal to 4" % Nconstraints);
self.c = list(self.a)
def add_value_constraint(self, x, y):
i = self.Nconstraints;
if (i == 4):
return false;
self.A[i, 0] = x * x * x;
self.A[i, 1] = x * x;
self.A[i, 2] = x;
self.A[i, 3] = 1;
self.B[i] = y;
self.Nconstraints += 1;
def add_derivative_constraint(self, x, dydx):
i = self.Nconstraints;
if (i == 4):
return false;
self.A[i, 0] = 3 * x * x;
self.A[i, 1] = 2 * x;
self.A[i, 2] = 1;
self.A[i, 3] = 0;
self.B[i] = dydx;
self.Nconstraints += 1;
def add_second_derivative_constraint(self, x, d2ydx2):
i = self.Nconstraints;
if (i == 4):
return false;
self.A[i, 0] = 6 * x;
self.A[i, 1] = 2;
self.A[i, 2] = 0;
self.A[i, 3] = 0;
self.B[i] = d2ydx2;
self.Nconstraints += 1;
def evaluate(self, x):
return np.polyval(self.a, x)
class QuarticFitter(object):
def __init__(self):
self.Nconstraints = 0
self.A = np.zeros((5, 5))
self.B = np.zeros((5, 1))
def build(self):
if (self.Nconstraints == 5):
self.a = np.linalg.solve(self.A, self.B);
else:
raise ValueError("Number of constraints[%d] is not equal to 4" % Nconstraints);
def add_value_constraint(self, x, y):
i = self.Nconstraints;
if (i == 5):
return false;
self.A[i, 0] = x * x * x * x;
self.A[i, 1] = x * x * x;
self.A[i, 2] = x * x;
self.A[i, 3] = x;
self.A[i, 4] = 1;
self.B[i] = y;
self.Nconstraints += 1;
def add_derivative_constraint(self, x, dydx):
i = self.Nconstraints;
if (i == 5):
return false;
self.A[i, 0] = 4 * x * x * x;
self.A[i, 1] = 3 * x * x;
self.A[i, 2] = 2 * x;
self.A[i, 3] = 1;
self.A[i, 4] = 0;
self.B[i] = dydx;
self.Nconstraints += 1;
def add_second_derivative_constraint(self, x, d2ydx2):
i = self.Nconstraints;
if (i == 5):
return false;
self.A[i, 0] = 12 * x * x;
self.A[i, 1] = 6 * x;
self.A[i, 2] = x;
self.A[i, 3] = 0;
self.A[i, 4] = 0;
self.B[i] = d2ydx2;
self.Nconstraints += 1;
def evaluate(self, x):
return np.polyval(self.a, x)
CoolProp.CoolProp.set_debug_level(0)
if not os.path.exists('sat_spline_json.json'):
from matplotlib.backends.backend_pdf import PdfPages
sat_crit_spline = PdfPages('sat_crit_spline.pdf')
V = ''
e = {}
e['Tc'] = []
e['Tt'] = []
e['dT'] = []
JSON = {}
for fluid in CoolProp.__fluids__:
perfect = False
cL = None,
cV = None,
if fluid in ['R410A', 'R404A', 'R507A', 'R407C', 'SES36', 'Air', 'R407F']: continue
dT = 3
plt.close('all')
fig, (ax1, ax2) = plt.subplots(1, 2)
pc = CoolProp.CoolProp.PropsSI('pcrit', fluid)
Tc = CoolProp.CoolProp.PropsSI('Tcrit', fluid)
Tt = CoolProp.CoolProp.PropsSI('Ttriple', fluid)
print(fluid, Tc, Tt)
try:
good_T = Tc - dT
rhomolar_crit = CoolProp.CoolProp.PropsSI('rhomolar_critical', fluid)
ok = False # Start assuming not ok
for i in np.linspace(np.log10(2), 6, 5000):
dT = 10**(-i)
bad_T = Tc - dT
p = CoolProp.CoolProp.PropsSI('P', 'T', Tc - dT, 'Q', 0, fluid)
# If this run worked, good_T and good_dT are set to the working values
good_dT = 10**(-(i - 1))
good_T = Tc - good_dT
# Set the flag saying this temperature was OK
ok = True
good_dT = 10**(-17)
good_T = Tc - good_dT
except ValueError as V:
pass
if ok:
rhomolar_endL = CoolProp.CoolProp.PropsSI('Dmolar', 'T', good_T, 'Q', 0, fluid)
rhomolar_endV = CoolProp.CoolProp.PropsSI('Dmolar', 'T', good_T, 'Q', 1, fluid)
if good_dT > 1e-3:
# Cubic or quadratic fit
DELTAT = 1e-5
dT_drhoL = DELTAT / (rhomolar_endL - CoolProp.CoolProp.PropsSI('Dmolar', 'T', good_T - DELTAT, 'Q', 0, fluid))
dT_drhoV = DELTAT / (rhomolar_endV - CoolProp.CoolProp.PropsSI('Dmolar', 'T', good_T - DELTAT, 'Q', 1, fluid))
CFL = CubicFitter()
CFL.add_value_constraint(rhomolar_crit, Tc)
CFL.add_derivative_constraint(rhomolar_crit, 0)
CFL.add_value_constraint(rhomolar_endL, good_T)
CFL.add_derivative_constraint(rhomolar_endL, dT_drhoL)
CFL.build()
zeros = np.roots(np.polyder(CFL.a))
monotonic_liq = not np.any(np.logical_and(zeros > 1.000000001 * rhomolar_crit, zeros < 0.999999999 * rhomolar_endL))
print('monotonic_liq', monotonic_liq)
if not monotonic_liq:
CFL = QuadraticFitter()
CFL.add_value_constraint(rhomolar_crit, Tc)
CFL.add_derivative_constraint(rhomolar_crit, 0)
CFL.add_value_constraint(rhomolar_endL, good_T)
##CFL.add_second_derivative_constraint(rhomolar_endL, dT_drhoL)
CFL.build()
CFV = CubicFitter()
CFV.add_value_constraint(rhomolar_crit, Tc)
CFV.add_derivative_constraint(rhomolar_crit, 0)
CFV.add_value_constraint(rhomolar_endV, good_T)
CFV.add_derivative_constraint(rhomolar_endV, dT_drhoV)
CFV.build()
zeros = np.roots(np.polyder(CFV.a))
monotonic_vap = not np.any(np.logical_and(zeros < 0.999999999 * rhomolar_crit, zeros > 1.000000001 * rhomolar_endV))
print('monotonic_vap (cubic)', monotonic_vap)
if not monotonic_vap:
CFV = QuadraticFitter()
CFV.add_value_constraint(rhomolar_crit, Tc)
CFV.add_derivative_constraint(rhomolar_crit, 0)
CFV.add_value_constraint(rhomolar_endV, good_T)
##CFV.add_second_derivative_constraint(rhomolar_endV, dT_drhoV)
CFV.build()
zeros = np.roots(np.polyder(CFV.a))
monotonic_vap = not np.any(np.logical_and(zeros < 0.999999999 * rhomolar_crit, zeros > 1.000000001 * rhomolar_endV))
print('monotonic_vap', monotonic_vap)
# Plot the cubic part on small axis
ax1.plot(np.linspace(rhomolar_endL, rhomolar_crit), CFL.evaluate(np.linspace(rhomolar_endL, rhomolar_crit)), 'r')
ax1.plot(np.linspace(rhomolar_endV, rhomolar_crit), CFV.evaluate(np.linspace(rhomolar_endV, rhomolar_crit)), 'r')
# Plot the cubic part on big ais
ax2.plot(np.linspace(rhomolar_endL, rhomolar_crit), CFL.evaluate(np.linspace(rhomolar_endL, rhomolar_crit)), 'r')
ax2.plot(np.linspace(rhomolar_endV, rhomolar_crit), CFV.evaluate(np.linspace(rhomolar_endV, rhomolar_crit)), 'r')
# Evaluated from EOS
TsL = np.linspace(good_T, good_T - good_dT, 100)
ax1.plot(CoolProp.CoolProp.PropsSI('Dmolar', 'T', TsL, 'Q', [0] * len(TsL), fluid), TsL, 'g')
ax1.plot(CoolProp.CoolProp.PropsSI('Dmolar', 'T', TsL, 'Q', [1] * len(TsL), fluid), TsL, 'g')
ax1.plot(rhomolar_endL, good_T, 'o')
ax1.plot(rhomolar_endV, good_T, 'o')
ax1.text(rhomolar_crit, good_T, '{T:0.2f} K'.format(T=good_T), va='top', ha='center')
print(fluid, ':::', dT, good_T, bad_T, CoolProp.CoolProp.PropsSI('P', 'T', good_T, 'Q', 0, fluid))
cL = CFL.c
cV = CFV.c
elif dT < 1e-3 and dT > 1e-6:
# Linear fit
LFV = LinearFitter()
LFV.add_value_constraint(rhomolar_crit, Tc)
LFV.add_value_constraint(rhomolar_endV, good_T)
LFV.build()
LFL = LinearFitter()
LFL.add_value_constraint(rhomolar_crit, Tc)
LFL.add_value_constraint(rhomolar_endL, good_T)
LFL.build()
ax1.plot(np.linspace(rhomolar_endL, rhomolar_crit), LFL.evaluate(np.linspace(rhomolar_endL, rhomolar_crit)), 'r')
ax1.plot(np.linspace(rhomolar_endV, rhomolar_crit), LFV.evaluate(np.linspace(rhomolar_endV, rhomolar_crit)), 'r')
e['Tc'].append(Tc)
e['Tt'].append(Tt)
e['dT'].append(dT)
cL = LFL.c
cV = LFV.c
else:
# Evaluated from EOS
TsL = np.linspace(0.999 * Tc, Tc - 1e-15, 100)
ax1.plot(CoolProp.CoolProp.PropsSI('Dmolar', 'T', TsL, 'Q', [0] * len(TsL), fluid), TsL, 'g')
ax1.plot(CoolProp.CoolProp.PropsSI('Dmolar', 'T', TsL, 'Q', [1] * len(TsL), fluid), TsL, 'g')
ax1.text(rhomolar_crit, Tc, 'PERFECT', ha='center', va='bottom')
perfect = True
ax1.plot(rhomolar_crit, Tc, 'o')
ax2.plot(rhomolar_crit, Tc, 'o', ms=2)
ax2.axhline(Tt + 1, lw=2)
TsL = np.linspace(Tt + 1, good_T, 10000)
ax2.plot(CoolProp.CoolProp.PropsSI('Dmolar', 'T', TsL, 'Q', [0] * len(TsL), fluid), TsL, 'g')
ax2.plot(CoolProp.CoolProp.PropsSI('Dmolar', 'T', TsL, 'Q', [1] * len(TsL), fluid), TsL, 'g')
plt.title(fluid)
plt.xlabel(r'$\rho$ [mol/m$^3$]')
plt.ylabel(r'T [K]')
sat_crit_spline.savefig()
plt.close()
if not perfect:
JSON[fluid] = dict(cL=cL,
cV=cV,
_note="Coefficients for the critical cubic spline. T = c[0]*rho^3 + c[1]*rho^2 + c[2]*rho + c[3] with rho in mol/m^3 and T in K",
T_min=good_T,
T_max=Tc,
rhomolar_min=rhomolar_endV,
rhomolar_max=rhomolar_endL
)
print('\tgood_dT', good_dT)
else:
print('\tperfect')
else:
print(fluid, 'FAIL', V)
sat_crit_spline.close()
plt.semilogy(np.array(e['Tc']) / np.array(e['Tt']), e['dT'], 'o', mfc='none')
plt.close()
fp = open('sat_spline_json.json', 'w')
json.dump(JSON, fp)
fp.close()
else:
# Load the generated JSON file
jj = json.load(open('sat_spline_json.json', 'r'))
# Iterate over the list of fluids and inject into the fluid JSON files
for fluid, v in jj.iteritems():
fp = open(os.path.join('..', 'fluids', fluid + '.json'), 'r')
fluid_json = json.load(fp)
if 'critical_region_spline' in fluid_json['EOS'][0]:
del fluid_json['EOS'][0]['critical_region_spline']
fluid_json['EOS'][0]['critical_region_splines'] = v
fp.close()
fp = open(os.path.join('..', 'fluids', fluid + '.json'), 'w')
json.dump(fluid_json, fp)
fp.close()