mirror of
https://github.com/CoolProp/CoolProp.git
synced 2026-04-23 03:00:17 -04:00
Just a little more debugging in the base classes...
This commit is contained in:
@@ -230,7 +230,8 @@ class IsoLine(Base2DObject):
|
||||
CoolProp.iHmass: { Base2DObject.TS:False, Base2DObject.PH:None , Base2DObject.HS:None , Base2DObject.PS:True , Base2DObject.PD:True , Base2DObject.TD:False, Base2DObject.PT:False},
|
||||
CoolProp.iP : { Base2DObject.TS:False, Base2DObject.PH:None , Base2DObject.HS:False, Base2DObject.PS:None , Base2DObject.PD:None , Base2DObject.TD:False, Base2DObject.PT:None },
|
||||
CoolProp.iSmass: { Base2DObject.TS:None , Base2DObject.PH:True , Base2DObject.HS:None , Base2DObject.PS:None , Base2DObject.PD:True , Base2DObject.TD:False, Base2DObject.PT:True },
|
||||
CoolProp.iT : { Base2DObject.TS:None , Base2DObject.PH:True , Base2DObject.HS:False, Base2DObject.PS:False, Base2DObject.PD:False, Base2DObject.TD:None , Base2DObject.PT:None }
|
||||
CoolProp.iT : { Base2DObject.TS:None , Base2DObject.PH:True , Base2DObject.HS:False, Base2DObject.PS:False, Base2DObject.PD:False, Base2DObject.TD:None , Base2DObject.PT:None },
|
||||
CoolProp.iQ : { Base2DObject.TS:True , Base2DObject.PH:True , Base2DObject.HS:True , Base2DObject.PS:True , Base2DObject.PD:True , Base2DObject.TD:True , Base2DObject.PT:False}
|
||||
}
|
||||
|
||||
def __init__(self, i_index, x_index, y_index, value=0.0, state=None):
|
||||
@@ -267,6 +268,7 @@ class IsoLine(Base2DObject):
|
||||
"""
|
||||
# Figure out if x or y-dimension should be used
|
||||
switch = self.XY_SWITCH[self.i_index][self.y_index*10+self.x_index]
|
||||
|
||||
if switch is None:
|
||||
raise ValueError("This isoline cannot be calculated!")
|
||||
elif switch is False:
|
||||
@@ -300,48 +302,47 @@ class IsoLine(Base2DObject):
|
||||
vals = [v for (_,v) in sorted(zip(order,[np.array(self.value), xvals , yvals ]))]
|
||||
|
||||
if vals[0] is None or vals[1] is None:
|
||||
raise ValueError("One required input is missing, make sure to supply the correct xvals or yvals: {0:s} - {1:s}".format(str(xvals),str(yvals)))
|
||||
raise ValueError("One required input is missing, make sure to supply the correct xvals ({0:s}) or yvals ({1:s}).".format(str(xvals),str(yvals)))
|
||||
|
||||
if vals[0].size > vals[1].size:
|
||||
vals[1] = np.resize(vals[1],vals[0].shape)
|
||||
elif vals[0].size < vals[1].size:
|
||||
vals[0] = np.resize(vals[0],vals[1].shape)
|
||||
|
||||
it = np.nditer([vals[0], vals[1], None])
|
||||
for x, y, z in it:
|
||||
self.state.update(pair, x, y)
|
||||
z[...] = self.state.keyed_output(idxs[2])
|
||||
|
||||
|
||||
vals[2] = np.empty_like(vals[0])
|
||||
err = False
|
||||
for index, _ in np.ndenumerate(vals[0]):
|
||||
try:
|
||||
self.state.update(pair, vals[0][index], vals[1][index])
|
||||
vals[2][index] = self.state.keyed_output(idxs[2])
|
||||
except Exception as e:
|
||||
warnings.warn(
|
||||
"An error occurred for inputs {0:f}, {1:f} with index {2:s}: {3:s}".format(vals[0][index],vals[1][index],str(index),str(e)),
|
||||
UserWarning)
|
||||
vals[2][index] = np.NaN
|
||||
err = True
|
||||
|
||||
for i,v in enumerate(idxs):
|
||||
if v == self.x_index: self.x = it.operands[i]
|
||||
if v == self.y_index: self.y = it.operands[i]
|
||||
|
||||
if v == self.x_index: self.x = vals[i]
|
||||
if v == self.y_index: self.y = vals[i]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class BasePlot(Base2DObject):
|
||||
"""The base class for all plots. It can be instantiated itself, but provides many
|
||||
general facilities to be used in the different plots. """
|
||||
#__metaclass__ = ABCMeta
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
# Define the iteration keys
|
||||
PROPERTIES = {
|
||||
'D': 'density',
|
||||
'H': 'specific enthalpy',
|
||||
'P': 'pressure',
|
||||
'S': 'specific entropy',
|
||||
'T': 'temperature',
|
||||
'U': 'specific internal energy'
|
||||
CoolProp.iDmass: 'density',
|
||||
CoolProp.iHmass: 'specific enthalpy',
|
||||
CoolProp.iP : 'pressure',
|
||||
CoolProp.iSmass: 'specific entropy',
|
||||
CoolProp.iT : 'temperature',
|
||||
CoolProp.iUmass: 'specific internal energy'
|
||||
}
|
||||
|
||||
# Define the unit systems
|
||||
@@ -352,12 +353,12 @@ class BasePlot(Base2DObject):
|
||||
}
|
||||
|
||||
LINE_COLORS = {
|
||||
'T': 'Darkred',
|
||||
'P': 'DarkCyan',
|
||||
'H': 'DarkGreen',
|
||||
'D': 'DarkBlue',
|
||||
'S': 'DarkOrange',
|
||||
'Q': 'black'
|
||||
CoolProp.iT : 'Darkred',
|
||||
CoolProp.iP : 'DarkCyan',
|
||||
CoolProp.iHmass: 'DarkGreen',
|
||||
CoolProp.iDmass: 'DarkBlue',
|
||||
CoolProp.iSmass: 'DarkOrange',
|
||||
CoolProp.iQ : 'black'
|
||||
}
|
||||
|
||||
def __init__(self, fluid_ref, graph_type, unit_system = 'KSI', **kwargs):
|
||||
@@ -396,16 +397,15 @@ class BasePlot(Base2DObject):
|
||||
else:
|
||||
raise ValueError("Invalid unit_system input, expected a string from {0:s}".format(str(self.UNIT_SYSTEMS.keys())))
|
||||
|
||||
self._axis = kwargs.get('axis', plt.gca())
|
||||
self.small = kwargs.get('small', 1e-5)
|
||||
self.axis = kwargs.get('axis', plt.gca())
|
||||
self.small = kwargs.get('small', 1e-5)
|
||||
self.colors = kwargs.get('colors', None)
|
||||
|
||||
self._colors = self.LINE_COLORS.copy()
|
||||
colors = kwargs.get('colors', None)
|
||||
if colors is not None:
|
||||
self._colors.update(colors)
|
||||
@property
|
||||
def axis(self): return self._axis
|
||||
@axis.setter
|
||||
def axis(self, value): self._axis = value
|
||||
|
||||
self._graph_drawn = False
|
||||
|
||||
@property
|
||||
def small(self): return self._small
|
||||
@small.setter
|
||||
@@ -413,6 +413,21 @@ class BasePlot(Base2DObject):
|
||||
self._T_small = self._state.trivial_keyed_output(CoolProp.iT_critical)*value
|
||||
self._P_small = self._state.trivial_keyed_output(CoolProp.iP_critical)*value
|
||||
self._small = value
|
||||
|
||||
@property
|
||||
def colors(self): return self._colors
|
||||
@colors.setter
|
||||
def colors(self, value):
|
||||
self._colors = self.LINE_COLORS.copy()
|
||||
if value is not None:
|
||||
self._colors.update(value)
|
||||
|
||||
def __sat_bounds(self, kind, smin=None, smax=None):
|
||||
warnings.warn(
|
||||
"You called the deprecated function \"__sat_bounds\", \
|
||||
consider replacing it with \"_get_sat_bounds\".",
|
||||
DeprecationWarning)
|
||||
return self._get_sat_bounds(kind, smin, smax)
|
||||
|
||||
def _get_sat_bounds(self, kind, smin=None, smax=None):
|
||||
"""Generates limits for the saturation line in either T or p determined
|
||||
@@ -424,11 +439,11 @@ class BasePlot(Base2DObject):
|
||||
T_triple = self._state.trivial_keyed_output(CoolProp.iT_triple)
|
||||
T_min = self._state.trivial_keyed_output(CoolProp.iT_min)
|
||||
self._state.update(CoolProp.QT_INPUTS, 0, max([T_triple,T_min])+self._T_small)
|
||||
kind = kind.upper()
|
||||
if kind == 'P':
|
||||
kind = self._get_index(kind)
|
||||
if kind == CoolProp.iP:
|
||||
fluid_min = self._state.keyed_output(CoolProp.iP)
|
||||
fluid_max = self._state.trivial_keyed_output(CoolProp.iP_critical)-self._P_small
|
||||
elif kind == 'T':
|
||||
elif kind == CoolProp.iT:
|
||||
fluid_min = self._state.keyed_output(CoolProp.iT)
|
||||
fluid_max = self._state.trivial_keyed_output(CoolProp.iT_critical)-self._T_small
|
||||
else:
|
||||
@@ -465,111 +480,49 @@ class BasePlot(Base2DObject):
|
||||
return str(r"$"+dim.symbol+"="+str(dim.from_SI(isoline.value))+ "$ "+dim.unit if unit else "$").strip()
|
||||
return str(isoline.value).strip()
|
||||
|
||||
|
||||
|
||||
|
||||
def _get_sat_lines(self, kind='T', smin=None,
|
||||
smax=None, num=500, x=[0., 1.]):
|
||||
"""
|
||||
Calculates bubble and dew line in the quantities for your plot.
|
||||
You can specify if you need evenly spaced entries in either
|
||||
pressure or temperature by supplying kind='p' and kind='T'
|
||||
(default), respectively.
|
||||
Limits can be set with kmin (default: triple point or EOS minimum) and
|
||||
kmax (default: critical value).
|
||||
Returns lines[] - a 2D array of dicts containing 'x' and 'y'
|
||||
coordinates for bubble and dew line. Additionally, the dict holds
|
||||
the keys 'kmax', 'label' and 'opts', those can be used for plotting
|
||||
as well.
|
||||
"""
|
||||
if not kind.upper() in ['T', 'P']:
|
||||
raise ValueError(''.join(["Invalid input for determining the ",
|
||||
"saturation lines... Expected either ",
|
||||
"'T' or 'P'"]))
|
||||
#def _get_phase_envelope(self):
|
||||
#
|
||||
#HEOS = CoolProp.AbstractState("HEOS", fluid)
|
||||
#HEOS.build_phase_envelope("")
|
||||
#PED = HEOS.get_phase_envelope_data()
|
||||
#plt.plot(PED.T, np.log(PED.p))
|
||||
#plt.show()
|
||||
|
||||
smin, smax = self.__sat_bounds(kind, smin=smin, smax=smax)
|
||||
sat_range = np.linspace(smin, smax, num)
|
||||
sat_mesh = np.array([sat_range for i in x])
|
||||
|
||||
x_vals = sat_mesh
|
||||
y_vals = sat_mesh
|
||||
if self.graph_type[1] != kind:
|
||||
_, x_vals = self._get_fluid_data(self.graph_type[1],
|
||||
'Q', x,
|
||||
kind, sat_mesh)
|
||||
|
||||
if self.graph_type[0] != kind:
|
||||
_, y_vals = self._get_fluid_data(self.graph_type[0],
|
||||
'Q', x,
|
||||
kind, sat_mesh)
|
||||
|
||||
if self.unit_system == 'KSI':
|
||||
x_vals *= self.KSI_SCALE_FACTOR[self.graph_type[1]]
|
||||
y_vals *= self.KSI_SCALE_FACTOR[self.graph_type[0]]
|
||||
|
||||
# Merge the two lines, capital Y holds important information.
|
||||
# We merge on X values
|
||||
# Every entry, eg. Xy, contains two arrays of values.
|
||||
sat_lines = []
|
||||
for i in range(len(x_vals)): # two dimensions: i = {0,1}
|
||||
line = {'x': x_vals[i],
|
||||
'y': y_vals[i],
|
||||
'smax': smax}
|
||||
|
||||
line['label'] = self.SYMBOL_MAP_KSI['Q'][0] + str(x[i])
|
||||
line['type'] = 'Q'
|
||||
line['value'] = x[i]
|
||||
line['unit'] = self.SYMBOL_MAP_KSI['Q'][1]
|
||||
line['opts'] = {'color': self.COLOR_MAP['Q'],
|
||||
'lw': 1.0}
|
||||
|
||||
if x[i] == 0.:
|
||||
line['label'] = 'bubble line'
|
||||
elif x[i] == 1.:
|
||||
line['label'] = 'dew line'
|
||||
else:
|
||||
line['opts']['lw'] = 0.75
|
||||
line['opts']['alpha'] = 0.5
|
||||
|
||||
sat_lines.append(line)
|
||||
|
||||
return sat_lines
|
||||
|
||||
def _plot_default_annotations(self):
|
||||
def filter_fluid_ref(fluid_ref):
|
||||
fluid_ref_string = fluid_ref
|
||||
if fluid_ref.startswith('REFPROP-MIX'):
|
||||
end = 0
|
||||
fluid_ref_string = ''
|
||||
while fluid_ref.find('[', end + 1) != -1:
|
||||
start = fluid_ref.find('&', end + 1)
|
||||
if end == 0:
|
||||
start = fluid_ref.find(':', end + 1)
|
||||
end = fluid_ref.find('[', end + 1)
|
||||
fluid_ref_string = ' '.join([fluid_ref_string,
|
||||
fluid_ref[start+1:end], '+'])
|
||||
fluid_ref_string = fluid_ref_string[0:len(fluid_ref_string)-2]
|
||||
return fluid_ref_string
|
||||
|
||||
if len(self.graph_type) == 2:
|
||||
y_axis_id = self.graph_type[0]
|
||||
x_axis_id = self.graph_type[1]
|
||||
else:
|
||||
y_axis_id = self.graph_type[0]
|
||||
x_axis_id = self.graph_type[1:len(self.graph_type)]
|
||||
|
||||
tl_str = "%s - %s Graph for %s"
|
||||
if not self.axis.get_title():
|
||||
self.axis.set_title(tl_str % (self.AXIS_LABELS[self.unit_system][y_axis_id][0],
|
||||
self.AXIS_LABELS[self.unit_system][x_axis_id][0],
|
||||
filter_fluid_ref(self.fluid_ref)))
|
||||
# def filter_fluid_ref(fluid_ref):
|
||||
# fluid_ref_string = fluid_ref
|
||||
# if fluid_ref.startswith('REFPROP-MIX'):
|
||||
# end = 0
|
||||
# fluid_ref_string = ''
|
||||
# while fluid_ref.find('[', end + 1) != -1:
|
||||
# start = fluid_ref.find('&', end + 1)
|
||||
# if end == 0:
|
||||
# start = fluid_ref.find(':', end + 1)
|
||||
# end = fluid_ref.find('[', end + 1)
|
||||
# fluid_ref_string = ' '.join([fluid_ref_string,
|
||||
# fluid_ref[start+1:end], '+'])
|
||||
# fluid_ref_string = fluid_ref_string[0:len(fluid_ref_string)-2]
|
||||
# return fluid_ref_string
|
||||
#
|
||||
# if len(self.graph_type) == 2:
|
||||
# y_axis_id = self.graph_type[0]
|
||||
# x_axis_id = self.graph_type[1]
|
||||
# else:
|
||||
# y_axis_id = self.graph_type[0]
|
||||
# x_axis_id = self.graph_type[1:len(self.graph_type)]
|
||||
#
|
||||
# tl_str = "%s - %s Graph for %s"
|
||||
# if not self.axis.get_title():
|
||||
# self.axis.set_title(tl_str % (self.AXIS_LABELS[self.unit_system][y_axis_id][0],
|
||||
# self.AXIS_LABELS[self.unit_system][x_axis_id][0],
|
||||
# filter_fluid_ref(self.fluid_ref)))
|
||||
if not self.axis.get_xlabel():
|
||||
self.axis.set_xlabel(' '.join(self.AXIS_LABELS[self.unit_system][x_axis_id]))
|
||||
dim = self._system.dimensions[self._x_index]
|
||||
self.xlabel(str(dim.label+" $"+dim.symbol+"$ / "+dim.unit).strip())
|
||||
if not self.axis.get_ylabel():
|
||||
self.axis.set_ylabel(' '.join(self.AXIS_LABELS[self.unit_system][y_axis_id]))
|
||||
|
||||
def _draw_graph(self):
|
||||
return
|
||||
dim = self._system.dimensions[self._y_index]
|
||||
self.ylabel(str(dim.label+" $"+dim.symbol+"$ / "+dim.unit).strip())
|
||||
|
||||
def title(self, title):
|
||||
self.axis.set_title(title)
|
||||
@@ -588,10 +541,73 @@ class BasePlot(Base2DObject):
|
||||
self.axis.grid(b)
|
||||
else:
|
||||
self.axis.grid(kwargs)
|
||||
|
||||
|
||||
|
||||
def set_axis_limits(self, limits):
|
||||
"""Set the limits of the internal axis object based on the active units,
|
||||
takes [xmin, xmax, ymin, ymax]"""
|
||||
self.axis.set_xlim([limits[0], limits[1]])
|
||||
self.axis.set_ylim([limits[2], limits[3]])
|
||||
|
||||
def _set_axis_limits(self, limits):
|
||||
"""Set the limits of the internal axis object based on SI units,
|
||||
takes [xmin, xmax, ymin, ymax]"""
|
||||
dim = self._system.dimensions[self._x_index]
|
||||
self.axis.set_xlim([dim.from_SI(limits[0]), dim.from_SI(limits[1])])
|
||||
dim = self._system.dimensions[self._y_index]
|
||||
self.axis.set_ylim([dim.from_SI(limits[2]), dim.from_SI(limits[3])])
|
||||
|
||||
def get_axis_limits(self,x_index=None,y_index=None):
|
||||
"""Returns the previously set limits or generates them and
|
||||
converts the default values to the selected unit system.
|
||||
Returns a list containing [xmin, xmax, ymin, ymax]"""
|
||||
if x_index is None: x_index = self._x_index
|
||||
if y_index is None: y_index = self._y_index
|
||||
if x_index != self.x_index or y_index != self.y_index or \
|
||||
self.axis.get_autoscalex_on() or self.axis.get_autoscaley_on():
|
||||
# One of them is not set.
|
||||
T_lo,T_hi = self._get_sat_bounds(CoolProp.iT)
|
||||
P_lo,P_hi = self._get_sat_bounds(CoolProp.iP)
|
||||
X=[0.0]*4; Y=[0.0]*4
|
||||
i = -1
|
||||
for T in [1.1*T_lo, min([1.75*T_hi,self._state.trivial_keyed_output(CoolProp.iT_max)])]:
|
||||
for P in [1.1*P_lo, 1.75*P_hi]:
|
||||
i+=1
|
||||
self._state.update(CoolProp.PT_INPUTS, P, T)
|
||||
# TODO: include a check for P and T?
|
||||
X[i] = self._state.keyed_output(x_index)
|
||||
Y[i] = self._state.keyed_output(y_index)
|
||||
|
||||
if self.axis.get_autoscalex_on() and x_index == self._x_index:
|
||||
dim = self._system.dimensions[self._x_index]
|
||||
self.axis.set_xlim([dim.from_SI(min(X)),dim.from_SI(max(X))])
|
||||
|
||||
if self.axis.get_autoscaley_on() and y_index == self._y_index:
|
||||
dim = self._system.dimensions[self._y_index]
|
||||
self.axis.set_ylim([dim.from_SI(min(Y)),dim.from_SI(max(Y))])
|
||||
|
||||
return [dim.from_SI(min(X)),dim.from_SI(max(X)),dim.from_SI(min(Y)),dim.from_SI(max(Y))]
|
||||
|
||||
def _get_axis_limits(self,x_index=None,y_index=None):
|
||||
"""Get the limits of the internal axis object in SI units
|
||||
Returns a list containing [xmin, xmax, ymin, ymax]"""
|
||||
if x_index is None: x_index = self._x_index
|
||||
if y_index is None: y_index = self._y_index
|
||||
limits = self.get_axis_limits(x_index,y_index)
|
||||
dim = self._system.dimensions[x_index]
|
||||
limits[0] = dim.to_SI(limits[0])
|
||||
limits[1] = dim.to_SI(limits[1])
|
||||
dim = self._system.dimensions[y_index]
|
||||
limits[2] = dim.to_SI(limits[2])
|
||||
limits[3] = dim.to_SI(limits[3])
|
||||
return limits
|
||||
|
||||
|
||||
def generate_ranges(self, itype, imin, imax, num):
|
||||
"""Generate a range for a certain property"""
|
||||
if itype in [CoolProp.iP, CoolProp.iDmass]:
|
||||
return np.logspace(np.log2(imin),np.log2(imax),num=num,base=2.)
|
||||
return np.linspace(imin, imax, num=num)
|
||||
|
||||
def show(self):
|
||||
self._draw_graph()
|
||||
@@ -610,21 +626,28 @@ if __name__ == "__main__":
|
||||
print(sys.P.label)
|
||||
print(sys.P.to_SI(20))
|
||||
|
||||
#i_index, x_index, y_index, value=None, state=None)
|
||||
iso = IsoLine('T','H','P')
|
||||
print(iso._get_update_pair())
|
||||
|
||||
state = AbstractState("HEOS","water")
|
||||
iso = IsoLine('T','H','P', 300.0, state)
|
||||
hr = PropsSI("H","T",[290,310],"P",[1e5,1e5],"water")
|
||||
iso.calc_range(hr,np.array([0.9e5,1.1e5]))
|
||||
print(iso.x,iso.y)
|
||||
|
||||
|
||||
#bp = BasePlot(fluid_ref, graph_type, unit_system = 'KSI', **kwargs):
|
||||
bp = BasePlot('n-Pentane', 'PH')
|
||||
print(bp._get_sat_bounds('P'))
|
||||
print(bp._get_iso_label(iso))
|
||||
#i_index, x_index, y_index, value=None, state=None)
|
||||
iso = IsoLine('T','H','P')
|
||||
print(iso._get_update_pair())
|
||||
|
||||
state = AbstractState("HEOS","water")
|
||||
iso = IsoLine('T','H','P', 300.0, state)
|
||||
hr = PropsSI("H","T",[290,310],"P",[1e5,1e5],"water")
|
||||
pr = np.linspace(0.9e5,1.1e5,3)
|
||||
iso.calc_range(hr,pr)
|
||||
print(iso.x,iso.y)
|
||||
|
||||
iso = IsoLine('Q','H','P', 0.0, state)
|
||||
iso.calc_range(hr,pr); print(iso.x,iso.y)
|
||||
iso = IsoLine('Q','H','P', 1.0, state)
|
||||
iso.calc_range(hr,pr); print(iso.x,iso.y)
|
||||
|
||||
|
||||
#bp = BasePlot(fluid_ref, graph_type, unit_system = 'KSI', **kwargs):
|
||||
bp = BasePlot('n-Pentane', 'PH', unit_system='EUR')
|
||||
print(bp._get_sat_bounds('P'))
|
||||
print(bp._get_iso_label(iso))
|
||||
print(bp.get_axis_limits())
|
||||
|
||||
|
||||
# get_update_pair(CoolProp.iP,CoolProp.iSmass,CoolProp.iT) -> (0,1,2,CoolProp.PSmass_INPUTS)
|
||||
|
||||
@@ -7,9 +7,11 @@ from scipy.interpolate import interp1d
|
||||
|
||||
import CoolProp.CoolProp as CP
|
||||
|
||||
from .Common import BasePlot
|
||||
from scipy import interpolate
|
||||
from scipy.spatial.kdtree import KDTree
|
||||
import warnings
|
||||
from CoolProp.Plots.Common import IsoLine,BasePlot
|
||||
import CoolProp
|
||||
|
||||
|
||||
|
||||
@@ -144,6 +146,7 @@ def drawLines(Ref,lines,axis,plt_kwargs=None):
|
||||
Returns an array of line objects that can be used to change
|
||||
the colour or style afterwards.
|
||||
"""
|
||||
warnings.warn("You called the deprecated function \"drawLines\".",DeprecationWarning)
|
||||
if not plt_kwargs is None:
|
||||
for line in lines:
|
||||
line['opts'] = plt_kwargs
|
||||
@@ -172,6 +175,90 @@ def drawLines(Ref,lines,axis,plt_kwargs=None):
|
||||
return plottedLines
|
||||
|
||||
|
||||
|
||||
class PropertyPlot(BasePlot):
|
||||
|
||||
def __init__(self, fluid_name, graph_type, units = 'KSI', **kwargs):
|
||||
super(PropertyPlot, self).__init__(fluid_name, graph_type, unit_system=units, **kwargs)
|
||||
self._isolines = {}
|
||||
|
||||
@property
|
||||
def isolines(self): return self._isolines
|
||||
|
||||
def _plotRound(self, values):
|
||||
"""
|
||||
A function round an array-like object while maintaining the
|
||||
amount of entries. This is needed for the isolines since we
|
||||
want the labels to look pretty (=rounding), but we do not
|
||||
know the spacing of the lines. A fixed number of digits after
|
||||
rounding might lead to reduced array size.
|
||||
"""
|
||||
inVal = numpy.unique(numpy.sort(numpy.array(values)))
|
||||
output = inVal[1:] * 0.0
|
||||
digits = -1
|
||||
limit = 10
|
||||
lim = inVal * 0.0 + 10
|
||||
# remove less from the numbers until same length,
|
||||
# more than 10 significant digits does not really
|
||||
# make sense, does it?
|
||||
while len(inVal) > len(output) and digits < limit:
|
||||
digits += 1
|
||||
val = ( numpy.around(numpy.log10(numpy.abs(inVal))) * -1) + digits + 1
|
||||
val = numpy.where(val < lim, val, lim)
|
||||
val = numpy.where(val >-lim, val, -lim)
|
||||
output = numpy.zeros(inVal.shape)
|
||||
for i in range(len(inVal)):
|
||||
output[i] = numpy.around(inVal[i],decimals=int(val[i]))
|
||||
output = numpy.unique(output)
|
||||
return output
|
||||
|
||||
def calc_isolines(self, iso_type, iso_range, num=10, rounding=False, points=200):
|
||||
"""Calculate lines with constant values of type 'iso_type' in terms of x and y as
|
||||
defined by the plot object. 'iso_range' either is a collection of values or
|
||||
simply the minimum and maximum value between which 'num' lines get calculated.
|
||||
The 'rounding' parameter can be used to generate prettier labels if needed.
|
||||
"""
|
||||
|
||||
if iso_range is None or (len(iso_range) == 1 and num != 1):
|
||||
raise ValueError('Automatic interval detection for isoline \
|
||||
boundaries is not supported yet, use the \
|
||||
iso_range=[min, max] parameter.')
|
||||
|
||||
if len(iso_range) == 2 and num is None:
|
||||
raise ValueError('Please specify the number of isoline you want \
|
||||
e.g. num=10')
|
||||
|
||||
if iso_type == 'all':
|
||||
for i_type in IsoLine.XY_SWITCH:
|
||||
if IsoLine.XY_SWITCH[i_type].get(self.y_index*10+self.x_index,None) is not None:
|
||||
# TODO implement the automatic interval detection.
|
||||
limits = self._get_axis_limits(i_type, CoolProp.iT)
|
||||
self.calc_isolines(i_type, [limits[0],limits[1]], num, rounding, points)
|
||||
|
||||
iso_range = numpy.sort(numpy.unique(iso_range))
|
||||
# Generate iso ranges
|
||||
if len(iso_range) == 2:
|
||||
iso_range = self.generate_ranges(iso_type, iso_range[0], iso_range[1], num)
|
||||
if rounding:
|
||||
iso_range = self._plotRound(iso_range)
|
||||
|
||||
limits = self._get_axis_limits()
|
||||
|
||||
ixrange = self.generate_ranges(self._x_index,limits[0],limits[1],points)
|
||||
iyrange = self.generate_ranges(self._y_index,limits[2],limits[3],points)
|
||||
|
||||
dim = self._system.dimensions[iso_type]
|
||||
|
||||
lines = []
|
||||
for i in range(num):
|
||||
lines.append(IsoLine(iso_type,self._x_index,self._y_index, value=dim.to_SI(iso_range[i]), state=self._state))
|
||||
lines[-1].calc_range(ixrange,iyrange)
|
||||
|
||||
self._isolines[iso_type] = lines
|
||||
|
||||
|
||||
|
||||
|
||||
class IsoLines(BasePlot):
|
||||
def __init__(self, fluid_ref, graph_type, iso_type, unit_system='SI', **kwargs):
|
||||
BasePlot.__init__(self, fluid_ref, graph_type, unit_system=unit_system,**kwargs)
|
||||
@@ -663,3 +750,10 @@ def drawIsoLines(Ref, plot, which, iValues=[], num=0, show=False, axis=None):
|
||||
if show:
|
||||
isolines.show()
|
||||
return lines
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
plot = PropertyPlot('n-Pentane', 'PH', units='EUR')
|
||||
plot.calc_isolines(CoolProp.iT, [20,80], num=2, rounding=False, points=5)
|
||||
for i in plot.isolines:
|
||||
print(plot.isolines[i][0].x,plot.isolines[i][0].y)
|
||||
|
||||
@@ -2,7 +2,7 @@ import unittest
|
||||
from CoolProp.CoolProp import PropsSI
|
||||
import CoolProp
|
||||
import numpy as np
|
||||
|
||||
|
||||
def test_input_types():
|
||||
for Fluid in ['Water']:
|
||||
for Tvals in [0.5*PropsSI(Fluid,'Tmin')+0.5*PropsSI(Fluid,'Tcrit'),
|
||||
|
||||
Reference in New Issue
Block a user