mirror of
https://github.com/CoolProp/CoolProp.git
synced 2026-01-09 14:08:08 -05:00
436 lines
12 KiB
Python
436 lines
12 KiB
Python
from CoolProp.Plots import Ph
|
|
import CoolProp.CoolProp as CP
|
|
import matplotlib as mpl
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.colors as colors
|
|
import matplotlib.ticker
|
|
import numpy as np
|
|
import random
|
|
import scipy.ndimage
|
|
import scipy.interpolate
|
|
from mpl_toolkits.mplot3d import Axes3D
|
|
import matplotlib._pylab_helpers
|
|
|
|
|
|
def fill_nan(A):
|
|
'''
|
|
interpolate to fill nan values
|
|
'''
|
|
inds = np.arange(A.shape[0])
|
|
good = np.where(np.isfinite(A))
|
|
f = scipy.interpolate.interp1d(inds[good], A[good],bounds_error=False)
|
|
B = np.where(np.isfinite(A),A,f(inds))
|
|
return B
|
|
|
|
|
|
def get_Z(X_in,Y_in,fluid):
|
|
'''
|
|
Just a wrapper to call CoolProp
|
|
'''
|
|
return False
|
|
|
|
|
|
def fill_Z(X,Y):
|
|
'''
|
|
Use 2D arrays X and Y to calculate a Z value
|
|
'''
|
|
Z = np.empty_like(X)
|
|
for i in range(len(X)):
|
|
for j in range(len(X[i])):
|
|
Z[i,j] = get_Z(X[i,j],Y[i,j],fluid)
|
|
Z[i] = fill_nan(Z[i])
|
|
tr = np.transpose(Z)
|
|
for i in range(len(tr)):
|
|
tr[i] = fill_nan(tr[i])
|
|
return np.transpose(tr)
|
|
|
|
|
|
def plotLineAndProjection(ax,X,Y,Z,draw='CXYZ',color='black',xlim=None,ylim=None,zlim=None):
|
|
alpha = 0.5
|
|
|
|
if 'C' in draw:
|
|
ax.plot(X,Y,zs=Z,color=color)
|
|
#else:
|
|
# ax.plot(X,Y,Z,color=color,alpha=0)
|
|
|
|
if xlim == None: xlim = ax.get_xlim()
|
|
if ylim == None: ylim = ax.get_ylim()
|
|
if zlim == None: zlim = ax.get_zlim()
|
|
|
|
if 'X' in draw:
|
|
constArr = np.ones_like(X) * xlim[0]
|
|
ax.plot(constArr,Y,zs=Z,color=color,alpha=alpha)
|
|
ax.set_xlim(xlim)
|
|
if 'Y' in draw:
|
|
constArr = np.ones_like(Y) * ylim[1]
|
|
ax.plot(X,constArr,zs=Z,color=color,alpha=alpha)
|
|
ax.set_ylim(ylim)
|
|
if 'Z' in draw:
|
|
constArr = np.ones_like(Z) * zlim[0]
|
|
ax.plot(X,Y,zs=constArr,color=color,alpha=alpha)
|
|
ax.set_zlim(zlim)
|
|
|
|
|
|
def make3Dlpot(X,Y,Z=None,ax=None,invert='',draw='CXYZ',color='blue',xlim=None,ylim=None,zlim=None):
|
|
'''
|
|
A simple wrapper around the plotting routines
|
|
|
|
invert: which axes to invert could be 'X' or 'YZ' or ...
|
|
draw: which lines to draw could be 'C' or 'YZ' or ...
|
|
C for curve and the rest are projections in the different dimensions
|
|
'''
|
|
|
|
if Z == None: Z = fill_Z(X, Y)
|
|
|
|
## Now we have all data and need to resample it for a smoother plot
|
|
resFactor = 10
|
|
Xr = scipy.ndimage.zoom(X, resFactor, order=1)
|
|
Yr = scipy.ndimage.zoom(Y, resFactor, order=1)
|
|
Zr = scipy.ndimage.zoom(Z, resFactor, order=1)
|
|
|
|
## Make the plot
|
|
if ax==None:
|
|
fig = plt.figure()
|
|
ax = fig.gca(projection='3d')
|
|
else:
|
|
fig = ''
|
|
|
|
if 'X' in invert: ax.invert_xaxis()
|
|
if 'Y' in invert: ax.invert_yaxis()
|
|
if 'Z' in invert: ax.invert_zaxis()
|
|
|
|
## Reduce data again and call the plotting wrapper
|
|
stride=np.round(len(Xr)/3.0)
|
|
|
|
for i in range(len(Xr)):
|
|
if np.mod(i,stride)==0:
|
|
plotLineAndProjection(ax,Xr[i],Yr[i],Zr[i],draw=draw,color=color,xlim=xlim,ylim=ylim,zlim=zlim)
|
|
plotLineAndProjection(ax,Xr[-1],Yr[-1],Zr[-1],draw=draw,color=color,xlim=xlim,ylim=ylim,zlim=zlim)
|
|
|
|
for i in range(len(Xr[0])):
|
|
if np.mod(i,stride)==0:
|
|
Xi = [row[i] for row in Xr]
|
|
Yi = [row[i] for row in Yr]
|
|
Zi = [row[i] for row in Zr]
|
|
plotLineAndProjection(ax,Xi,Yi,Zi,draw=draw,color=color,xlim=xlim,ylim=ylim,zlim=zlim)
|
|
Xi = [row[-1] for row in Xr]
|
|
Yi = [row[-1] for row in Yr]
|
|
Zi = [row[-1] for row in Zr]
|
|
plotLineAndProjection(ax,Xi,Yi,Zi,draw=draw,color=color,xlim=xlim,ylim=ylim,zlim=zlim)
|
|
|
|
#cset = ax.contour(X, Y, Z, cmap=cm.coolwarm)
|
|
#ax.clabel(cset, fontsize=9, inline=1)
|
|
|
|
## In case we need a surface plot
|
|
#from matplotlib import cm
|
|
#from matplotlib.ticker import LinearLocator, FormatStrFormatter
|
|
#
|
|
#surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
|
|
# linewidth=0, antialiased=False)
|
|
#ax.set_zlim(-1.01, 1.01)
|
|
#
|
|
#ax.zaxis.set_major_locator(LinearLocator(10))
|
|
#ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
|
|
#
|
|
#fig.colorbar(surf, shrink=0.5, aspect=5)
|
|
|
|
## Alternative plotting solution
|
|
#cmap = plt.get_cmap('jet')
|
|
#
|
|
#if PRINT:
|
|
# ax.plot_surface(
|
|
# X, Y, Z,
|
|
# rstride=100,
|
|
# cstride=100,
|
|
# alpha=0.5,
|
|
# cmap=cmap,
|
|
# linewidth=0,
|
|
# antialiased=True
|
|
# )
|
|
#else:
|
|
# #ax.plot_surface(
|
|
# ax.plot_wireframe(
|
|
# X, Y, Z,
|
|
# rstride=100,
|
|
# cstride=100,
|
|
# #alpha=0.5,
|
|
# #cmap=cmap,
|
|
# #linewidth=0,
|
|
# #antialiased=False
|
|
# )
|
|
|
|
### Make the plot
|
|
#fig = plt.figure()
|
|
#ax = fig.gca(projection='3d')
|
|
##cset = ax.contour(X, Y, Z, cmap=cm.coolwarm)
|
|
##ax.clabel(cset, fontsize=9, inline=1)
|
|
#
|
|
#ax.plot_wireframe(
|
|
# X, Y, Z,
|
|
# rstride=np.round(resFactor*10),
|
|
# cstride=np.round(resFactor*10),
|
|
# #alpha=0.5,
|
|
# #cmap=cmap,
|
|
# #linewidth=0,
|
|
# #antialiased=False
|
|
# )
|
|
|
|
## Plot the two-phase dome and its projections
|
|
#ax.plot(X_TP,Y_TP,Z_TP,color='black')
|
|
|
|
#xlim = ax.get_xlim()
|
|
#ylim = ax.get_ylim()
|
|
#zlim = ax.get_zlim()
|
|
#
|
|
#constArr = np.ones_like(X_TP) * xlim[0]
|
|
#ax.plot(constArr,Y_TP,Z_TP,color='black')
|
|
#constArr = np.ones_like(Y_TP) * ylim[1]
|
|
#ax.plot(X_TP,constArr,Z_TP,color='black')
|
|
#constArr = np.ones_like(Z_TP) * zlim[0]
|
|
#ax.plot(X_TP,Y_TP,constArr,color='black')
|
|
|
|
## Or do we want contour lines?
|
|
##cset = ax.contour(X, Y, Z, zdir='z', offset=zlim[0], cmap=cmap)
|
|
##cset = ax.contour(X, Y, Z, zdir='x', offset=xlim[0], cmap=cmap)
|
|
##cset = ax.contour(X, Y, Z, zdir='y', offset=ylim[1], cmap=cmap)
|
|
#
|
|
#ax.set_xlim(xlim)
|
|
#ax.set_ylim(ylim)
|
|
#ax.set_zlim(zlim)
|
|
|
|
#majorFormatter = matplotlib.ticker.EngFormatter("J/kg")
|
|
#majorFormatter = matplotlib.ticker.
|
|
majorFormatter = matplotlib.ticker.ScalarFormatter()
|
|
#majorFormatter.set_scientific(True)
|
|
majorFormatter.set_powerlimits((-4,4))
|
|
ax.xaxis.set_major_formatter(majorFormatter)
|
|
ax.yaxis.set_major_formatter(majorFormatter)
|
|
ax.zaxis.set_major_formatter(majorFormatter)
|
|
return fig, ax, Z
|
|
|
|
|
|
def getlim(key,dicts,fac=1):
|
|
min = np.min([dict[key]/fac for dict in dicts])
|
|
max = np.max([dict[key]/fac for dict in dicts])
|
|
return [np.floor(min)*fac, np.ceil(max)*fac]
|
|
|
|
|
|
#########################################################################
|
|
# Here starts the real script
|
|
#########################################################################
|
|
|
|
|
|
fluid = 'water'
|
|
CP.enable_TTSE_LUT(fluid)
|
|
|
|
PRINT = False
|
|
|
|
if PRINT:
|
|
points = 200
|
|
dpi = 300
|
|
toFile = True
|
|
else:
|
|
points = 100
|
|
dpi = 75
|
|
toFile = False
|
|
|
|
|
|
Tmin = CP.PropsSI('Tmin' ,'T',0,'P',0,fluid) + 5.0
|
|
Tcri = CP.PropsSI('Tcrit','T',0,'P',0,fluid)
|
|
Tmax = CP.PropsSI('Tcrit','T',0,'P',0,fluid) * 2.0
|
|
|
|
## Get phase boundary
|
|
T_TP = np.linspace(Tmin, Tcri-0.5, 0.5*points)
|
|
D_TP = CP.PropsSI('D','T',T_TP,'Q',0,fluid)
|
|
P_TP = CP.PropsSI('P','T',T_TP,'Q',0,fluid)
|
|
H_TP = CP.PropsSI('H','T',T_TP,'Q',0,fluid)
|
|
S_TP = CP.PropsSI('S','T',T_TP,'Q',0,fluid)
|
|
|
|
D_TP = np.append(D_TP, CP.PropsSI('D','T',T_TP[::-1],'Q',1,fluid))
|
|
P_TP = np.append(P_TP, CP.PropsSI('P','T',T_TP[::-1],'Q',1,fluid))
|
|
H_TP = np.append(H_TP, CP.PropsSI('H','T',T_TP[::-1],'Q',1,fluid))
|
|
S_TP = np.append(S_TP, CP.PropsSI('S','T',T_TP[::-1],'Q',1,fluid))
|
|
|
|
logV_TP = np.log10(1.0/D_TP)
|
|
logP_TP = np.log10(1.0*P_TP)
|
|
T_TP = np.append(T_TP, T_TP[::-1])
|
|
|
|
|
|
## These are the default TTSE ranges
|
|
D_L = CP.PropsSI('D','T',Tmin,'Q',0,fluid)
|
|
D_V = CP.PropsSI('D','T',Tmin,'Q',1,fluid)
|
|
H_L = CP.PropsSI('H','T',Tmin,'Q',0,fluid)
|
|
H_V = CP.PropsSI('H','T',Tmin,'Q',1,fluid)
|
|
P_L = CP.PropsSI('P','T',Tmin,'Q',0,fluid)
|
|
P_V = CP.PropsSI('P','T',Tmin,'Q',1,fluid)
|
|
|
|
Hmin = H_L
|
|
Hmax = H_L + (H_V-H_L)*2.0
|
|
|
|
#Pmin = CP.PropsSI('ptriple','T',0,'P',0,fluid) + 1
|
|
Pmin = np.min([P_L,P_V])
|
|
Pmax = CP.PropsSI('pcrit','T',0,'P',0,fluid) * 2.0 # should be p_reduce
|
|
|
|
Dmin = CP.PropsSI('D','H',Hmin,'P',Pmax,fluid)
|
|
Dmax = CP.PropsSI('D','H',Hmax,'P',Pmin,fluid)
|
|
|
|
|
|
#########################################################################
|
|
# Start with the first diagram, hps
|
|
#########################################################################
|
|
## Set the ranges for the plot
|
|
X_TP = H_TP
|
|
Y_TP = logP_TP
|
|
Z_TP = S_TP
|
|
|
|
Xmin = Hmin
|
|
Xmax = Hmax
|
|
Ymin = np.log10(Pmin)
|
|
Ymax = np.log10(Pmax)
|
|
|
|
X = np.linspace(Xmin, Xmax, points) # Enthalpy
|
|
Y = np.linspace(Ymin, Ymax, points) # Pressure
|
|
X, Y = np.meshgrid(X, Y)
|
|
|
|
|
|
def get_Z(X_in,Y_in,fluid,out='S'):
|
|
'''
|
|
Just a wrapper to call CoolProp
|
|
'''
|
|
X = X_in
|
|
Y = np.power(10.0,Y_in)
|
|
Z = np.NAN
|
|
try:
|
|
Z = CP.PropsSI(out,'H',X,'P',Y,fluid)
|
|
except(ValueError):
|
|
print("CoolProp failed, returning NAN")
|
|
Z = np.NAN
|
|
return Z
|
|
|
|
|
|
## Now we also need the variables
|
|
Z = fill_Z(X,Y)
|
|
get_Z_old = get_Z
|
|
|
|
|
|
def get_Z(X_in,Y_in,fluid):
|
|
return get_Z_old(X_in,Y_in,fluid,out='D')
|
|
|
|
|
|
logVdata = np.log10(1.0/fill_Z(X,Y))
|
|
|
|
|
|
def get_Z(X_in,Y_in,fluid):
|
|
return get_Z_old(X_in,Y_in,fluid,out='T')
|
|
|
|
|
|
Tdata = fill_Z(X,Y)
|
|
|
|
HPSdict = {'H': X, 'P': Y, 'S': Z, 'V': logVdata, 'T': Tdata, 'H_TP': X_TP, 'P_TP': Y_TP, 'S_TP': Z_TP}
|
|
|
|
|
|
#########################################################################
|
|
# Start with the next diagram, vTp
|
|
#########################################################################
|
|
|
|
## Set the ranges for the plot
|
|
X_TP = logV_TP
|
|
Y_TP = T_TP
|
|
Z_TP = logP_TP
|
|
|
|
#Xmin = np.log10(1.0/Dmax)
|
|
#Xmax = np.log10(1.0/Dmin)
|
|
Xmin = np.min(X_TP)
|
|
Xmax = np.max(X_TP)
|
|
Ymin = np.min(Y_TP)
|
|
Ymax = Tmax
|
|
|
|
X = np.linspace(Xmin, Xmax, points) # Volume
|
|
Y = np.linspace(Ymin, Ymax, points) # Temperature
|
|
X, Y = np.meshgrid(X, Y)
|
|
|
|
|
|
def get_Z(X_in,Y_in,fluid,out='P'):
|
|
'''
|
|
Just a wrapper to call CoolProp
|
|
'''
|
|
X = 1.0/np.power(10.0,X_in)
|
|
Y = Y_in
|
|
Z = np.NAN
|
|
try:
|
|
Z = np.log10(CP.PropsSI(out,'D',X,'T',Y,fluid))
|
|
except(ValueError):
|
|
print("CoolProp failed, returning NAN")
|
|
Z = np.NAN
|
|
return Z
|
|
|
|
|
|
## Now we also need the variables
|
|
Z = fill_Z(X,Y)
|
|
get_Z_old = get_Z
|
|
|
|
|
|
def get_Z(X_in,Y_in,fluid):
|
|
return np.power(10.0,get_Z_old(X_in,Y_in,fluid,out='H'))
|
|
|
|
|
|
Hdata = fill_Z(X,Y)
|
|
|
|
|
|
def get_Z(X_in,Y_in,fluid):
|
|
return np.power(10.0,get_Z_old(X_in,Y_in,fluid,out='S'))
|
|
|
|
|
|
Sdata = fill_Z(X,Y)
|
|
|
|
VTPdict = {'V': X, 'T': Y, 'P': Z, 'H': Hdata, 'S': Sdata, 'V_TP': X_TP, 'T_TP': Y_TP, 'P_TP': Z_TP}
|
|
|
|
|
|
#########################################################################
|
|
# Now we have all the data and can start mixing the
|
|
# different definitions
|
|
#########################################################################
|
|
|
|
dicts = [HPSdict,VTPdict]
|
|
|
|
Hlim = getlim('H',dicts,fac=1e6)
|
|
Plim = getlim('P',dicts)
|
|
Slim = getlim('S',dicts,fac=1e3)
|
|
Vlim = getlim('V',dicts)
|
|
Tlim = getlim('T',dicts,fac=1e2)
|
|
|
|
figHPS, axHPS, Z = make3Dlpot(HPSdict['H'],HPSdict['P'],Z=HPSdict['S'],invert='Z',draw='XYZ',color='blue',xlim=Hlim,ylim=Plim,zlim=Slim[::-1])
|
|
## Plot the two-phase dome and its projections
|
|
plotLineAndProjection(axHPS,HPSdict['H_TP'],HPSdict['P_TP'],HPSdict['S_TP'])
|
|
## .. and add the other plot
|
|
make3Dlpot(VTPdict['H'],VTPdict['P'],Z=VTPdict['S'],ax=axHPS,draw='XYZ',color='red')
|
|
|
|
axHPS.set_xlabel(r'$h$')
|
|
axHPS.set_ylabel(r'log $p$')
|
|
axHPS.set_zlabel(r'$s$')
|
|
|
|
|
|
figVTP, axVTP, Z = make3Dlpot(VTPdict['V'],VTPdict['T'],Z=VTPdict['P'],draw='XYZ',color='red',xlim=Vlim,ylim=Tlim,zlim=Plim)
|
|
## Plot the two-phase dome and its projections
|
|
plotLineAndProjection(axVTP,VTPdict['V_TP'],VTPdict['T_TP'],VTPdict['P_TP'])
|
|
## .. and add the other plot
|
|
make3Dlpot(HPSdict['V'],HPSdict['T'],Z=HPSdict['P'],ax=axVTP,draw='XYZ',color='blue')
|
|
|
|
axVTP.set_xlabel(r'log $v$')
|
|
axVTP.set_ylabel(r'$T$')
|
|
axVTP.set_zlabel(r'log $p$')
|
|
|
|
|
|
if toFile:
|
|
figHPS.savefig('phs-'+fluid+'.png', dpi = dpi, transparent = True)
|
|
figHPS.savefig('phs-'+fluid+'.pdf', transparent = True)
|
|
#figHPS.close()
|
|
figVTP.savefig('pvT-'+fluid+'.png', dpi = dpi, transparent = True)
|
|
figVTP.savefig('pvT-'+fluid+'.pdf', transparent = True)
|
|
#figVTP.close()
|
|
#plt.savefig('TTSE_RANGES.pdf')
|
|
else:
|
|
plt.show()
|
|
#figHPS.show()
|
|
#figVTP.show()
|