Skip to content

Commit

Permalink
Fix API for fluid back end selection and adjust corresponding tests
Browse files Browse the repository at this point in the history
  • Loading branch information
fwitte committed Sep 10, 2023
1 parent d76088b commit c38e969
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 63 deletions.
10 changes: 5 additions & 5 deletions src/tespy/components/heat_exchangers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,7 @@ class HeatExchanger(Component):
>>> from tespy.networks import Network
>>> from tespy.tools import document_model
>>> import shutil
>>> nw = Network(fluids=['water', 'air'], T_unit='C', p_unit='bar',
... h_unit='kJ / kg', iterinfo=False)
>>> nw = Network(T_unit='C', p_unit='bar', h_unit='kJ / kg', iterinfo=False)
>>> exhaust_hot = Source('Exhaust air outlet')
>>> exhaust_cold = Sink('Exhaust air inlet')
>>> cw_cold = Source('cooling water inlet')
Expand All @@ -169,9 +168,9 @@ class HeatExchanger(Component):
>>> he.set_attr(pr1=0.98, pr2=0.98, ttd_u=5,
... design=['pr1', 'pr2', 'ttd_u'], offdesign=['zeta1', 'zeta2', 'kA_char'])
>>> cw_he.set_attr(fluid={'air': 0, 'water': 1}, T=10, p=3,
>>> cw_he.set_attr(fluid={'water': 1}, T=10, p=3,
... offdesign=['m'])
>>> ex_he.set_attr(fluid={'air': 1, 'water': 0}, v=0.1, T=35)
>>> ex_he.set_attr(fluid={'air': 1}, v=0.1, T=35)
>>> he_ex.set_attr(T=17.5, p=1, design=['T'])
>>> nw.solve('design')
>>> nw.save('tmp')
Expand Down Expand Up @@ -700,7 +699,8 @@ def bus_func(self, bus):
h_{out,1} - h_{in,1} \right)
"""
return self.inl[0].m.val_SI * (
self.outl[0].h.val_SI - self.inl[0].h.val_SI)
self.outl[0].h.val_SI - self.inl[0].h.val_SI
)

def bus_func_doc(self, bus):
r"""
Expand Down
2 changes: 1 addition & 1 deletion src/tespy/components/heat_exchangers/parabolic_trough.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class ParabolicTrough(SimpleHeatExchanger):
>>> pt.set_attr(pr=1, aoi=aoi, doc=1,
... Tamb=20, A=1, eta_opt=0.816, c_1=0.0622, c_2=0.00023, E=E,
... iam_1=-1.59e-3, iam_2=9.77e-5)
>>> inc.set_attr(fluid={'S800': 1}, T=220, p=2)
>>> inc.set_attr(fluid={'S800': 1}, fluid_back_ends={'S800': 'INCOMP'}, T=220, p=2)
>>> outg.set_attr(T=260)
>>> nw.solve('design')
>>> round(pt.Q.val, 0)
Expand Down
2 changes: 1 addition & 1 deletion src/tespy/components/heat_exchangers/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ def hazen_williams_func(self):

return (
(i.p.val_SI - o.p.val_SI) * np.sign(i.m.val_SI) -
(10.67 * abs(i[0]) ** 1.852 * self.L.val /
(10.67 * abs(i.m.val_SI) ** 1.852 * self.L.val /
(self.ks.val ** 1.852 * self.D.val ** 4.871)) *
(9.81 * ((v_i + v_o) / 2) ** 0.852))

Expand Down
26 changes: 17 additions & 9 deletions src/tespy/connections/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,17 +398,17 @@ def set_attr(self, **kwargs):
msg = 'Label can only be specified on instance creation.'
logger.error(msg)
raise TESPyConnectionError(msg)
elif 'fluid' in key:
self._fluid_specification(key, kwargs[key])
elif key in self.property_data or key in self.property_data0:
# fluid specification
try:
float(kwargs[key])
is_numeric = True
except (TypeError, ValueError):
is_numeric = False
if 'fluid' in key:
self._fluid_specification(key, kwargs[key])

elif key == 'state':
if key == 'state':
if kwargs[key] in ['l', 'g']:
self.state.set_attr(val=kwargs[key], is_set=True)
elif kwargs[key] is None:
Expand Down Expand Up @@ -528,7 +528,7 @@ def _fluid_specification(self, key, value):
if key == "fluid":
for fluid, fraction in value.items():
if fraction is not None:
self.fluid.val["fluid"] = fraction
self.fluid.val[fluid] = fraction
self.fluid.is_set.add(fluid)
if fluid in self.fluid.is_var:
self.fluid.is_var.remove(fluid)
Expand All @@ -554,7 +554,7 @@ def _fluid_specification(self, key, value):
logger.error(msg)
raise KeyError(msg)

def _check_fluid_datatypes(key, value):
def _check_fluid_datatypes(self, key, value):
if key == "fluid_balance":
if not isinstance(value, bool):
msg = "Datatype for 'fluid_balance' must be boolean."
Expand Down Expand Up @@ -590,6 +590,8 @@ def get_attr(self, key):

def _create_fluid_wrapper(self):
for fluid in self.fluid.val:
if fluid in self.fluid.wrapper:
continue
if fluid in self.fluid.engine and fluid in self.fluid.back_end:
pass
elif fluid in self.fluid.engine:
Expand Down Expand Up @@ -814,10 +816,16 @@ def calc_results(self):
self.s.val_SI = self.calc_s()

if number_fluids == 1:
if not self.x.is_set:
self.x.val_SI = self.calc_x()
if not self.Td_bp.is_set:
self.Td_bp.val_SI = self.calc_Td_bp()
try:
if not self.x.is_set:
self.x.val_SI = self.calc_x()
except ValueError:
self.x.val_SI = np.nan
try:
if not self.Td_bp.is_set:
self.Td_bp.val_SI = self.calc_Td_bp()
except ValueError:
self.x.val_SI = np.nan

for prop in fpd.keys():
self.get_attr(prop).val = convert_from_SI(
Expand Down
40 changes: 26 additions & 14 deletions src/tespy/networks/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,7 @@ def init_components(self):
self.results[comp_type] = pd.DataFrame(
columns=cols, dtype='float64')
if comp_type not in self.specifications:

cols, groups, chars = [], [], []
for col, data in comp.parameters.items():
if isinstance(data, dc_cp):
Expand Down Expand Up @@ -907,7 +908,13 @@ def propagate_fluid_wrappers(self):
if f not in c.fluid.wrapper and f in fluid_set_wrappers:
c.fluid.wrapper[f] = fluid_set_wrappers[f]
elif f not in c.fluid.wrapper:
c._create_fluid_wrapper(f, fp.CoolPropWrapper, "HEOS")
msg = (
f"The fluid {f} seems to be a potential fluid for "
"connection, however, there has is no fluid "
"wrapper available for this fluid on the same "
"branch. Creating a default wrapper."
)
c._create_fluid_wrapper()

def presolve_massflow_topology(self):

Expand Down Expand Up @@ -1056,6 +1063,21 @@ def presolve_fluid_topology(self):
}
self.num_conn_vars += 1

def _reset_topology_reduction_specifications(self):
for c in self.conns["object"]:
if hasattr(c, "_m_tmp"):
value = c.m.val_SI
unit = c.m.unit
c.m = c._m_tmp
c.m.val_SI = value
c.m.unit = unit
del c._m_tmp
if hasattr(c, "_fluid_tmp"):
val = c.fluid.val
c.fluid = c._fluid_tmp
c.fluid.val = val
del c._fluid_tmp

def init_set_properties(self):
"""Specification of SI values for user set values."""
self.all_fluids = []
Expand Down Expand Up @@ -1887,6 +1909,7 @@ def solve(self, mode, init_path=None, design_path=None,
self.initialise()

if init_only:
self._reset_topology_reduction_specifications()
return

msg = 'Starting solver.'
Expand Down Expand Up @@ -2396,20 +2419,9 @@ def postprocessing(self):

def process_connections(self):
"""Process the Connection results."""
for c in self.conns['object']:
if hasattr(c, "_m_tmp"):
value = c.m.val_SI
unit = c.m.unit
c.m = c._m_tmp
c.m.val_SI = value
c.m.unit = unit
del c._m_tmp
if hasattr(c, "_fluid_tmp"):
val = c.fluid.val
c.fluid = c._fluid_tmp
c.fluid.val = val
del c._fluid_tmp
self._reset_topology_reduction_specifications()

for c in self.conns['object']:
c.good_starting_values = True
c.calc_results()

Expand Down
5 changes: 4 additions & 1 deletion src/tespy/tools/fluid_properties/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ def __init__(self, fluid, back_end=None) -> None:
def _set_constants(self):
self._T_min = self.AS.trivial_keyed_output(CP.iT_min)
self._T_max = self.AS.trivial_keyed_output(CP.iT_max)
self._aliases = CP.CoolProp.get_aliases(self.fluid)
try:
self._aliases = CP.CoolProp.get_aliases(self.fluid)
except RuntimeError:
self._aliases = [self.fluid]

if self.back_end == "INCOMP":
self._p_min = 1e2
Expand Down
30 changes: 15 additions & 15 deletions tests/test_components/test_heat_exchangers.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def test_SimpleHeatExchanger(self):
"""Test component properties of simple heat exchanger."""
instance = SimpleHeatExchanger('heat exchanger')
self.setup_SimpleHeatExchanger_network(instance)
fl = {'Ar': 0, 'H2O': 1, 'S800': 0}
fl = {'H2O': 1}
self.c1.set_attr(fluid=fl, m=1, p=10, T=100)
# trigger heat exchanger parameter groups
instance.set_attr(hydro_group='HW', L=100, ks=100, pr=0.99, Tamb=20)
Expand Down Expand Up @@ -156,8 +156,9 @@ def test_ParabolicTrough(self):
"""Test component properties of parabolic trough."""
instance = ParabolicTrough('parabolic trough')
self.setup_SimpleHeatExchanger_network(instance)
fl = {'Ar': 0, 'H2O': 0, 'S800': 1}
self.c1.set_attr(fluid=fl, p=2, T=200)
fl = {'S800': 1}
fbe = {"S800": "INCOMP"}
self.c1.set_attr(fluid=fl, fluid_back_ends=fbe, p=2, T=200)
self.c2.set_attr(T=350)

# test grouped parameter settings with missing parameters
Expand Down Expand Up @@ -261,7 +262,7 @@ def test_SolarCollector(self):
"""Test component properties of solar collector."""
instance = SolarCollector('solar collector')
self.setup_SimpleHeatExchanger_network(instance)
fl = {'Ar': 0, 'H2O': 1, 'S800': 0}
fl = {'H2O': 1}
self.c1.set_attr(fluid=fl, p=10, T=30)
self.c2.set_attr(T=70)

Expand Down Expand Up @@ -341,9 +342,9 @@ def test_HeatExchanger(self):
instance.set_attr(pr1=0.98, pr2=0.98, ttd_u=5,
design=['pr1', 'pr2', 'ttd_u'],
offdesign=['zeta1', 'zeta2', 'kA_char'])
self.c1.set_attr(T=120, p=3, fluid={'Ar': 0, 'H2O': 1, 'S800': 0})
self.c1.set_attr(T=120, p=3, fluid={'H2O': 1})
self.c2.set_attr(T=70)
self.c3.set_attr(T=40, p=5, fluid={'Ar': 1, 'H2O': 0, 'S800': 0})
self.c3.set_attr(T=40, p=5, fluid={'Ar': 1})
b = Bus('heat transfer', P=-80e3)
b.add_comps({'comp': instance})
self.nw.add_busses(b)
Expand Down Expand Up @@ -447,10 +448,11 @@ def test_Condenser(self):
self.setup_HeatExchanger_network(instance)

# design specification
instance.set_attr(pr1=0.98, pr2=0.98, ttd_u=5,
offdesign=['zeta2', 'kA_char'])
self.c1.set_attr(T=100, p0=0.5, fluid={'Ar': 0, 'H2O': 1, 'S800': 0})
self.c3.set_attr(T=30, p=5, fluid={'Ar': 0, 'H2O': 1, 'S800': 0})
instance.set_attr(
pr1=0.98, pr2=0.98, ttd_u=5, offdesign=['zeta2', 'kA_char']
)
self.c1.set_attr(T=100, p0=0.5, fluid={'H2O': 1})
self.c3.set_attr(T=30, p=5, fluid={'H2O': 1})
self.c4.set_attr(T=40)
instance.set_attr(Q=-80e3)
self.nw.solve('design')
Expand Down Expand Up @@ -483,7 +485,7 @@ def test_Condenser(self):

# test upper terminal temperature difference. For the component
# condenser the temperature of the condensing fluid is relevant.
ttd_u = round(T_sat_p(self.c1.get_flow()) - self.c4.T.val_SI, 1)
ttd_u = round(self.c1.calc_T_sat() - self.c4.T.val_SI, 1)
p = round(self.c1.p.val_SI, 5)
msg = ('Value of terminal temperature difference must be ' +
str(round(instance.ttd_u.val, 1)) + ', is ' +
Expand Down Expand Up @@ -517,10 +519,8 @@ def test_CondenserWithEvaporation(self):

# design specification
instance.set_attr(pr1=1, pr2=1, offdesign=["kA"])
self.c1.set_attr(x=1, p=1, fluid={'Ar': 0, 'H2O': 1, 'S800': 0}, m=1)
self.c3.set_attr(
x=0, p=0.7, fluid={'Ar': 0, 'H2O': 1, 'S800': 0}, m=2, design=["m"]
)
self.c1.set_attr(x=1, p=1, fluid={'H2O': 1}, m=1)
self.c3.set_attr(x=0, p=0.7, fluid={'H2O': 1}, m=2, design=["m"])
self.nw.solve('design')
self.nw._convergence_check()
ttd_l = round(instance.ttd_l.val, 3)
Expand Down
26 changes: 13 additions & 13 deletions tests/test_components/test_turbomachinery.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_Compressor(self):
self.setup_network(instance)

# compress NH3, other fluids in network are for turbine, pump, ...
fl = {'N2': 1, 'O2': 0, 'Ar': 0, 'DowQ': 0, 'NH3': 0}
fl = {'N2': 1}
self.c1.set_attr(fluid=fl, v=1, p=1, T=5)
self.c2.set_attr(p=6)
instance.set_attr(eta_s=0.8)
Expand All @@ -54,7 +54,7 @@ def test_Compressor(self):

# test isentropic efficiency value
eta_s_d = (
(isentropic(self.c1.get_flow(), self.c2.get_flow()) -
(isentropic(self.c1.p.val_SI, self.c1.h.val_SI, self.c2.p.val_SI, self.c1.fluid_data, self.c1.mixing_rule) -
self.c1.h.val_SI) / (self.c2.h.val_SI - self.c1.h.val_SI))
msg = ('Value of isentropic efficiency must be ' + str(eta_s_d) +
', is ' + str(instance.eta_s.val) + '.')
Expand All @@ -67,7 +67,7 @@ def test_Compressor(self):

# test calculated value
eta_s = (
(isentropic(self.c1.get_flow(), self.c2.get_flow()) -
(isentropic(self.c1.p.val_SI, self.c1.h.val_SI, self.c2.p.val_SI, self.c1.fluid_data, self.c1.mixing_rule) -
self.c1.h.val_SI) / (self.c2.h.val_SI - self.c1.h.val_SI))
msg = ('Value of isentropic efficiency must be ' + str(eta_s) +
', is ' + str(instance.eta_s.val) + '.')
Expand Down Expand Up @@ -160,25 +160,26 @@ def test_Pump(self):
"""Test component properties of pumps."""
instance = Pump('pump')
self.setup_network(instance)
fl = {'N2': 0, 'O2': 0, 'Ar': 0, 'DowQ': 1, 'NH3': 0}
self.c1.set_attr(fluid=fl, v=1, p=5, T=50)
fl = {'DowQ': 1}
fbe = {'DowQ': 'INCOMP'}
self.c1.set_attr(fluid=fl, fluid_back_ends=fbe, v=1, p=5, T=50)
self.c2.set_attr(p=7)
instance.set_attr(eta_s=1)
self.nw.solve('design')
self.nw._convergence_check()

# test calculated value for efficiency
eta_s = (
(isentropic(self.c1.get_flow(), self.c2.get_flow()) -
(isentropic(self.c1.p.val_SI, self.c1.h.val_SI, self.c2.p.val_SI, self.c1.fluid_data, self.c1.mixing_rule) -
self.c1.h.val_SI) / (self.c2.h.val_SI - self.c1.h.val_SI))
msg = ('Value of isentropic efficiency must be ' + str(eta_s) +
', is ' + str(instance.eta_s.val) + '.')
assert eta_s == instance.eta_s.val, msg

# isentropic efficiency of 1 means inlet and outlet entropy are
# identical
s1 = round(s_mix_ph(self.c1.get_flow()), 4)
s2 = round(s_mix_ph(self.c2.get_flow()), 4)
s1 = round(self.c1.calc_s(), 4)
s2 = round(self.c2.calc_s(), 4)
msg = ('Value of entropy must be identical for inlet (' + str(s1) +
') and outlet (' + str(s2) +
') at 100 % isentropic efficiency.')
Expand Down Expand Up @@ -251,8 +252,7 @@ def test_Turbine(self):
"""Test component properties of turbines."""
instance = Turbine('turbine')
self.setup_network(instance)
fl = {'N2': 0.7556, 'O2': 0.2315, 'Ar': 0.0129, 'DowQ': 0,
'NH3': 0}
fl = {'N2': 0.7556, 'O2': 0.2315, 'Ar': 0.0129}
self.c1.set_attr(fluid=fl, m=15, p=10)
self.c2.set_attr(p=1, T=25)
instance.set_attr(eta_s=0.85)
Expand All @@ -263,7 +263,7 @@ def test_Turbine(self):
# design value of isentropic efficiency
eta_s_d = (
(self.c2.h.val_SI - self.c1.h.val_SI) / (
isentropic(self.c1.get_flow(), self.c2.get_flow()) -
isentropic(self.c1.p.val_SI, self.c1.h.val_SI, self.c2.p.val_SI, self.c1.fluid_data, self.c1.mixing_rule) -
self.c1.h.val_SI))
msg = ('Value of isentropic efficiency must be ' +
str(round(eta_s_d, 3)) + ', is ' + str(instance.eta_s.val) +
Expand All @@ -276,7 +276,7 @@ def test_Turbine(self):
self.nw._convergence_check()
eta_s = (
(self.c2.h.val_SI - self.c1.h.val_SI) / (
isentropic(self.c1.get_flow(), self.c2.get_flow()) -
isentropic(self.c1.p.val_SI, self.c1.h.val_SI, self.c2.p.val_SI, self.c1.fluid_data, self.c1.mixing_rule) -
self.c1.h.val_SI))
msg = ('Value of isentropic efficiency must be ' + str(eta_s) +
', is ' + str(instance.eta_s.val) + '.')
Expand Down Expand Up @@ -342,7 +342,7 @@ def test_Turbomachine(self):
instance.component() + '.')
assert 'turbomachine' == instance.component(), msg
self.setup_network(instance)
fl = {'N2': 0.7556, 'O2': 0.2315, 'Ar': 0.0129, 'DowQ': 0, 'NH3': 0}
fl = {'N2': 0.7556, 'O2': 0.2315, 'Ar': 0.0129}
self.c1.set_attr(fluid=fl, m=10, p=1, h=1e5)
self.c2.set_attr(p=3, h=2e5)

Expand Down
1 change: 1 addition & 0 deletions tests/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,7 @@ def test_missing_CharMap_files():
def test_h_mix_pQ_on_mixtures():
c = Connection(Source("test"), "out1", Sink("test2"), "in1")
c.set_attr(fluid={"O2": 0.24, "N2": 0.76})
c._create_fluid_wrapper()
c.build_fluid_data()
with raises(ValueError):
h_mix_pQ(1e5, 0.5, c.fluid_data, c.mixing_rule)
Loading

0 comments on commit c38e969

Please sign in to comment.