diff --git a/docs/api/_images/DropletSeparator.svg b/docs/api/_images/DropletSeparator.svg
index b41a4eceb..6f80e1828 100644
--- a/docs/api/_images/DropletSeparator.svg
+++ b/docs/api/_images/DropletSeparator.svg
@@ -3,7 +3,7 @@
inkscape:export-ydpi="1012"
inkscape:export-xdpi="1012"
sodipodi:docname="DropletSeparator.svg"
- inkscape:version="1.1.2 (1:1.1+202202050950+0a00cf5339)"
+ inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
id="svg8"
version="1.1"
viewBox="0 0 69.223438 95.000002"
@@ -160,7 +160,7 @@
y="83.53054"
style="fill:#000000;fill-opacity:1;stroke-width:0.529166;stroke-linecap:round;stroke-linejoin:round">out1
+ id="tspan9360">2
out2
+ id="tspan7156">1
out1
+ style="font-size:6.42057px;baseline-shift:sub;fill:#ffffff;fill-opacity:1;stroke-width:0.529166"
+ id="tspan9360">2
out2
+ style="font-size:6.42057px;baseline-shift:sub;fill:#ffffff;fill-opacity:1;stroke-width:0.529166"
+ id="tspan7156">1
`__
might be interesting for you. In contrast to the pure fluids, the properties
-cover liquid state only. TESPy supports using pure incompressibles as well as
-the predefined mixtures.
+cover liquid state only.
Fluid mixtures
++++++++++++++
-CoolProp provides a back end for predefined mixtures, which is rather instable
-using HEOS. If you want to use the mixture feature of CoolProp we recommend
-using the REFPROP back end instead.
+TESPy provides support for three types of mixtures:
+
+- ideal: Mixtures for gases only.
+- ideal-cond: Mixture for gases with condensation calculation for water share.
+- incompressible: Mixtures for incompressible fluids.
+
+Furthermore, CoolProp provides a back end for predefined mixtures, which is
+rather instable using HEOS. Using the CoolProp mixture back-end is not tested,
+reach out if you would like to support us in adopting the TESPy implementation.
+In general, to use the mixture feature of CoolProp we recommend using the
+REFPROP back end instead of HEOS.
Using other engines
-------------------
diff --git a/docs/modules/ude.rst b/docs/modules/ude.rst
index ee1488f4f..716c938a0 100644
--- a/docs/modules/ude.rst
+++ b/docs/modules/ude.rst
@@ -118,7 +118,7 @@ derivatives to mass flow are not zero.
... c0 = self.conns[0]
... c1 = self.conns[1]
... if c0.m.is_var:
- ... ude.jacobian[c0.m.J_col] = 1
+ ... self.jacobian[c0.m.J_col] = 1
... if c1.m.is_var:
... self.jacobian[c1.m.J_col] = -2 * self.conns[1].m.val_SI
diff --git a/docs/whats_new.rst b/docs/whats_new.rst
index f03b829f0..7f5718e07 100644
--- a/docs/whats_new.rst
+++ b/docs/whats_new.rst
@@ -3,6 +3,7 @@ What's New
Discover noteable new features and improvements in each release
+.. include:: whats_new/v0-7-2.rst
.. include:: whats_new/v0-7-1.rst
.. include:: whats_new/v0-7-0.rst
.. include:: whats_new/v0-6-3.rst
diff --git a/docs/whats_new/v0-7-2.rst b/docs/whats_new/v0-7-2.rst
new file mode 100644
index 000000000..430690e81
--- /dev/null
+++ b/docs/whats_new/v0-7-2.rst
@@ -0,0 +1,18 @@
+v0.7.1 - Newton's Nature (January, 21, 2024)
+++++++++++++++++++++++++++++++++++++++++++++
+
+Bug Fixes
+#########
+- The `delta` value of the :py:class:`tespy.connections.connection.Ref` class
+ was oriented with the wrong sign. A positive delta lead to a negative value.
+ Fixed in (`PR #459 `__).
+- In initial simulations the temperature value of mixtures is 0 by default.
+ For calculating temperatures of the mixtures during that initial simulation,
+ that value was used as starting value causing CoolProp to raise an error and
+ the calculation to crash. This is now prevented by checking if the starting
+ value is reasonable or not
+ (`PR #477 `__).
+
+Contributors
+############
+- Francesco Witte (`@fwitte `__)
diff --git a/pyproject.toml b/pyproject.toml
index eb184e992..648caec25 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -25,7 +25,7 @@ exclude = ["docs/_build"]
[project]
name = "tespy"
-version = "0.7.1.post1"
+version = "0.7.2"
description = "Thermal Engineering Systems in Python (TESPy)"
readme = "README.rst"
authors = [
@@ -47,7 +47,7 @@ classifiers = [
]
requires-python = ">=3.9"
dependencies = [
- "CoolProp>=6.4,<7",
+ "CoolProp>=6.6,<7",
"jinja2",
"matplotlib>=3.2.1,<4",
"numpy>=1.13.3,<2",
diff --git a/src/tespy/__init__.py b/src/tespy/__init__.py
index fde368697..9a79a2196 100644
--- a/src/tespy/__init__.py
+++ b/src/tespy/__init__.py
@@ -3,7 +3,7 @@
import os
__datapath__ = os.path.join(importlib.resources.files("tespy"), "data")
-__version__ = '0.7.1.post1 - Newton\'s Nature'
+__version__ = '0.7.2 - Newton\'s Nature'
# tespy data and connections import
from . import connections # noqa: F401
diff --git a/src/tespy/components/combustion/engine.py b/src/tespy/components/combustion/engine.py
index 6f47613a3..aa4661371 100644
--- a/src/tespy/components/combustion/engine.py
+++ b/src/tespy/components/combustion/engine.py
@@ -1302,11 +1302,9 @@ def calc_parameters(self):
self.outl[i].h.val_SI - self.inl[i].h.val_SI)
self.get_attr('pr' + str(i + 1)).val = (
self.outl[i].p.val_SI / self.inl[i].p.val_SI)
- self.get_attr('zeta' + str(i + 1)).val = (
- (self.inl[i].p.val_SI - self.outl[i].p.val_SI) * np.pi ** 2 / (
- 4 * self.inl[i].m.val_SI ** 2 *
- (self.inl[i].vol.val_SI + self.outl[i].vol.val_SI)
- ))
+ self.get_attr('zeta' + str(i + 1)).val = self.calc_zeta(
+ self.inl[i], self.outl[i]
+ )
self.P.val = self.calc_P()
self.Qloss.val = self.calc_Qloss()
diff --git a/src/tespy/components/component.py b/src/tespy/components/component.py
index 8c8dec496..3d9e0f24c 100644
--- a/src/tespy/components/component.py
+++ b/src/tespy/components/component.py
@@ -12,8 +12,6 @@
SPDX-License-Identifier: MIT
"""
-from collections import OrderedDict
-
import numpy as np
from tespy.tools import logger
@@ -122,7 +120,7 @@ def __init__(self, label, **kwargs):
self.fkt_group = self.label
# add container for components attributes
- self.parameters = OrderedDict(self.get_parameters().copy())
+ self.parameters = self.get_parameters().copy()
self.__dict__.update(self.parameters)
self.set_attr(**kwargs)
@@ -342,7 +340,7 @@ def preprocess(self, num_nw_vars):
self.num_eq = 0
self.vars = {}
self.num_vars = 0
- self.constraints = OrderedDict(self.get_mandatory_constraints().copy())
+ self.constraints = self.get_mandatory_constraints().copy()
self.prop_specifications = {}
self.var_specifications = {}
self.group_specifications = {}
@@ -415,7 +413,7 @@ def preprocess(self, num_nw_vars):
if data.is_set and data.func is not None:
self.num_eq += data.num_eq
- self.jacobian = OrderedDict()
+ self.jacobian = {}
self.residual = np.zeros(self.num_eq)
sum_eq = 0
@@ -813,18 +811,18 @@ def check_parameter_bounds(self):
if isinstance(data, dc_cp):
if data.val > data.max_val + ERR :
msg = (
- 'Invalid value for ' + p + ': ' + p + ' = ' +
- str(data.val) + ' above maximum value (' +
- str(data.max_val) + ') at component ' + self.label +
- '.')
+ f"Invalid value for {p}: {p} = {data.val} above "
+ f"maximum value ({data.max_val}) at component "
+ f"{self.label}."
+ )
logger.warning(msg)
elif data.val < data.min_val - ERR :
msg = (
- 'Invalid value for ' + p + ': ' + p + ' = ' +
- str(data.val) + ' below minimum value (' +
- str(data.min_val) + ') at component ' + self.label +
- '.')
+ f"Invalid value for {p}: {p} = {data.val} below "
+ f"minimum value ({data.max_val}) at component "
+ f"{self.label}."
+ )
logger.warning(msg)
elif isinstance(data, dc_cc) and data.is_set:
@@ -968,7 +966,8 @@ def enthalpy_equality_func_doc(self, label):
indices = str(indices[0])
latex = (
r'0=h_{\mathrm{in,}i}-h_{\mathrm{out,}i}'
- r'\; \forall i \in [' + indices + r']')
+ r'\; \forall i \in [' + indices + r']'
+ )
return generate_latex_eq(self, latex, label)
def enthalpy_equality_deriv(self, k):
@@ -1021,8 +1020,7 @@ def pr_func(self, pr='', inconn=0, outconn=0):
0 = p_{in} \cdot pr - p_{out}
"""
pr = self.get_attr(pr)
- return (self.inl[inconn].p.val_SI * pr.val -
- self.outl[outconn].p.val_SI)
+ return self.inl[inconn].p.val_SI * pr.val - self.outl[outconn].p.val_SI
def pr_func_doc(self, label, pr='', inconn=0, outconn=0):
r"""
@@ -1083,6 +1081,15 @@ def pr_deriv(self, increment_filter, k, pr='', inconn=0, outconn=0):
if pr.is_var:
self.jacobian[k, self.pr.J_col] = i.p.val_SI
+ def calc_zeta(self, i, o):
+ if abs(i.m.val_SI) <= 1e-4:
+ return 0
+ else:
+ return (
+ (i.p.val_SI - o.p.val_SI) * np.pi ** 2
+ / (4 * i.m.val_SI ** 2 * (i.vol.val_SI + o.vol.val_SI))
+ )
+
def zeta_func(self, zeta='', inconn=0, outconn=0):
r"""
Calculate residual value of :math:`\zeta`-function.
diff --git a/src/tespy/components/heat_exchangers/base.py b/src/tespy/components/heat_exchangers/base.py
index 82830fac8..5ad36b254 100644
--- a/src/tespy/components/heat_exchangers/base.py
+++ b/src/tespy/components/heat_exchangers/base.py
@@ -381,10 +381,10 @@ def calculate_td_log(self):
o2 = self.outl[1]
# temperature value manipulation for convergence stability
- T_i1 = i1.calc_T(T0=i1.T.val_SI)
- T_i2 = i2.calc_T(T0=i2.T.val_SI)
- T_o1 = o1.calc_T(T0=o1.T.val_SI)
- T_o2 = o2.calc_T(T0=o2.T.val_SI)
+ T_i1 = i1.calc_T()
+ T_i2 = i2.calc_T()
+ T_o1 = o1.calc_T()
+ T_o2 = o2.calc_T()
if T_i1 <= T_o2:
T_i1 = T_o2 + 0.01
@@ -581,8 +581,8 @@ def ttd_u_func(self):
"""
i = self.inl[0]
o = self.outl[1]
- T_i1 = i.calc_T(T0=i.T.val_SI)
- T_o2 = o.calc_T(T0=o.T.val_SI)
+ T_i1 = i.calc_T()
+ T_o2 = o.calc_T()
return self.ttd_u.val - T_i1 + T_o2
def ttd_u_func_doc(self, label):
@@ -636,8 +636,8 @@ def ttd_l_func(self):
"""
i = self.inl[1]
o = self.outl[0]
- T_i2 = i.calc_T(T0=i.T.val_SI)
- T_o1 = o.calc_T(T0=o.T.val_SI)
+ T_i2 = i.calc_T()
+ T_o1 = o.calc_T()
return self.ttd_l.val - T_o1 + T_i2
def ttd_l_func_doc(self, label):
@@ -830,11 +830,9 @@ def calc_parameters(self):
for i in range(2):
self.get_attr('pr' + str(i + 1)).val = (
self.outl[i].p.val_SI / self.inl[i].p.val_SI)
- self.get_attr('zeta' + str(i + 1)).val = (
- (self.inl[i].p.val_SI - self.outl[i].p.val_SI) * np.pi ** 2 / (
- 4 * self.inl[i].m.val_SI ** 2 *
- (self.inl[i].vol.val_SI + self.outl[i].vol.val_SI)
- ))
+ self.get_attr('zeta' + str(i + 1)).val = self.calc_zeta(
+ self.inl[i], self.outl[i]
+ )
# kA and logarithmic temperature difference
if self.ttd_u.val < 0 or self.ttd_l.val < 0:
@@ -842,8 +840,10 @@ def calc_parameters(self):
elif self.ttd_l.val == self.ttd_u.val:
self.td_log.val = self.ttd_l.val
else:
- self.td_log.val = ((self.ttd_l.val - self.ttd_u.val) /
- np.log(self.ttd_l.val / self.ttd_u.val))
+ self.td_log.val = (
+ (self.ttd_l.val - self.ttd_u.val)
+ / np.log(self.ttd_l.val / self.ttd_u.val)
+ )
self.kA.val = -self.Q.val / self.td_log.val
def entropy_balance(self):
diff --git a/src/tespy/components/heat_exchangers/condenser.py b/src/tespy/components/heat_exchangers/condenser.py
index 241a9c01b..377660332 100644
--- a/src/tespy/components/heat_exchangers/condenser.py
+++ b/src/tespy/components/heat_exchangers/condenser.py
@@ -19,8 +19,6 @@
from tespy.tools.data_containers import GroupedComponentCharacteristics as dc_gcc
from tespy.tools.data_containers import SimpleDataContainer as dc_simple
from tespy.tools.document_models import generate_latex_eq
-from tespy.tools.fluid_properties import T_mix_ph
-from tespy.tools.fluid_properties import T_sat_p
from tespy.tools.fluid_properties import dh_mix_dpQ
from tespy.tools.fluid_properties import h_mix_pQ
@@ -329,9 +327,9 @@ def calculate_td_log(self):
o2 = self.outl[1]
T_i1 = i1.calc_T_sat()
- T_i2 = i2.calc_T(T0=i2.T.val_SI)
- T_o1 = o1.calc_T(T0=o1.T.val_SI)
- T_o2 = o2.calc_T(T0=o2.T.val_SI)
+ T_i2 = i2.calc_T()
+ T_o1 = o1.calc_T()
+ T_o2 = o2.calc_T()
if T_i1 <= T_o2 and not i1.T.is_set:
T_i1 = T_o2 + 0.5
@@ -454,7 +452,7 @@ def ttd_u_func(self):
i = self.inl[0]
o = self.outl[1]
T_i1 = i.calc_T_sat()
- T_o2 = o.calc_T(T0=self.outl[1].T.val_SI)
+ T_o2 = o.calc_T()
return self.ttd_u.val - T_i1 + T_o2
def ttd_u_func_doc(self, label):
diff --git a/src/tespy/components/heat_exchangers/parabolic_trough.py b/src/tespy/components/heat_exchangers/parabolic_trough.py
index 5a41e98d1..1b948ae7e 100644
--- a/src/tespy/components/heat_exchangers/parabolic_trough.py
+++ b/src/tespy/components/heat_exchangers/parabolic_trough.py
@@ -274,7 +274,7 @@ def energy_group_func(self):
i = self.inl[0]
o = self.outl[0]
- T_m = 0.5 * (i.calc_T(T0=i.T.val_SI) + o.calc_T(T0=o.T.val_SI))
+ T_m = 0.5 * (i.calc_T() + o.calc_T())
iam = (
1 - self.iam_1.val * abs(self.aoi.val)
@@ -361,10 +361,8 @@ def calc_parameters(self):
self.Q.val = i.m.val_SI * (o.h.val_SI - i.h.val_SI)
self.pr.val = o.p.val_SI / i.p.val_SI
- self.zeta.val = (
- (i.p.val_SI - o.p.val_SI) * np.pi ** 2
- / (4 * i.m.val_SI ** 2 * (i.vol.val_SI + o.vol.val_SI))
- )
+ self.zeta.val = self.calc_zeta(i, o)
+
if self.energy_group.is_set:
self.Q_loss.val = - self.E.val * self.A.val + self.Q.val
self.Q_loss.is_result = True
diff --git a/src/tespy/components/heat_exchangers/simple.py b/src/tespy/components/heat_exchangers/simple.py
index 29d141ea9..a9e555079 100644
--- a/src/tespy/components/heat_exchangers/simple.py
+++ b/src/tespy/components/heat_exchangers/simple.py
@@ -535,8 +535,8 @@ def kA_group_func(self):
i = self.inl[0]
o = self.outl[0]
- ttd_1 = i.calc_T(T0=i.T.val_SI) - self.Tamb.val_SI
- ttd_2 = o.calc_T(T0=o.T.val_SI) - self.Tamb.val_SI
+ ttd_1 = i.calc_T() - self.Tamb.val_SI
+ ttd_2 = o.calc_T() - self.Tamb.val_SI
# For numerical stability: If temperature differences have
# different sign use mean difference to avoid negative logarithm.
@@ -651,8 +651,8 @@ def kA_char_group_func(self):
# For numerical stability: If temperature differences have
# different sign use mean difference to avoid negative logarithm.
- ttd_1 = i.calc_T(T0=i.T.val_SI) - self.Tamb.val_SI
- ttd_2 = o.calc_T(T0=o.T.val_SI) - self.Tamb.val_SI
+ ttd_1 = i.calc_T() - self.Tamb.val_SI
+ ttd_2 = o.calc_T() - self.Tamb.val_SI
if (ttd_1 / ttd_2) < 0:
td_log = (ttd_2 + ttd_1) / 2
@@ -884,10 +884,7 @@ def calc_parameters(self):
self.Q.val = i.m.val_SI * (o.h.val_SI - i.h.val_SI)
self.pr.val = o.p.val_SI / i.p.val_SI
- self.zeta.val = (
- (i.p.val_SI - o.p.val_SI) * np.pi ** 2
- / (4 * i.m.val_SI ** 2 * (i.vol.val_SI + o.vol.val_SI))
- )
+ self.zeta.val = self.calc_zeta(i, o)
if self.Tamb.is_set:
ttd_1 = i.T.val_SI - self.Tamb.val_SI
diff --git a/src/tespy/components/heat_exchangers/solar_collector.py b/src/tespy/components/heat_exchangers/solar_collector.py
index bce3198ce..7129bd5c6 100644
--- a/src/tespy/components/heat_exchangers/solar_collector.py
+++ b/src/tespy/components/heat_exchangers/solar_collector.py
@@ -231,7 +231,7 @@ def energy_group_func(self):
i = self.inl[0]
o = self.outl[0]
- T_m = 0.5 * (i.calc_T(T0=i.T.val_SI) + o.calc_T(T0=o.T.val_SI))
+ T_m = 0.5 * (i.calc_T() + o.calc_T())
return (
i.m.val_SI * (o.h.val_SI - i.h.val_SI)
@@ -311,10 +311,8 @@ def calc_parameters(self):
self.Q.val = i.m.val_SI * (o.h.val_SI - i.h.val_SI)
self.pr.val = o.p.val_SI / i.p.val_SI
- self.zeta.val = (
- (i.p.val_SI - o.p.val_SI) * np.pi ** 2
- / (4 * i.m.val_SI ** 2 * (i.vol.val_SI + o.vol.val_SI))
- )
+ self.zeta.val = self.calc_zeta(i, o)
+
if self.energy_group.is_set:
self.Q_loss.val = -(self.E.val * self.A.val - self.Q.val)
self.Q_loss.is_result = True
diff --git a/src/tespy/components/nodes/separator.py b/src/tespy/components/nodes/separator.py
index c826f022c..bf8a7be85 100644
--- a/src/tespy/components/nodes/separator.py
+++ b/src/tespy/components/nodes/separator.py
@@ -299,9 +299,9 @@ def energy_balance_func(self):
\forall j \in \text{outlets}
"""
residual = []
- T_in = self.inl[0].calc_T(T0=300)
+ T_in = self.inl[0].calc_T()
for o in self.outl:
- residual += [T_in - o.calc_T(T0=300)]
+ residual += [T_in - o.calc_T()]
return residual
def energy_balance_func_doc(self, label):
diff --git a/src/tespy/components/piping/valve.py b/src/tespy/components/piping/valve.py
index 3b23aadcb..723e1d829 100644
--- a/src/tespy/components/piping/valve.py
+++ b/src/tespy/components/piping/valve.py
@@ -316,10 +316,7 @@ def calc_parameters(self):
i = self.inl[0]
o = self.outl[0]
self.pr.val = o.p.val_SI / i.p.val_SI
- self.zeta.val = (
- (i.p.val_SI - o.p.val_SI) * np.pi ** 2
- / (4 * i.m.val_SI ** 2 * (i.vol.val_SI + o.vol.val_SI))
- )
+ self.zeta.val = self.calc_zeta(i, o)
def entropy_balance(self):
r"""
diff --git a/src/tespy/components/reactors/fuel_cell.py b/src/tespy/components/reactors/fuel_cell.py
index 088d09757..fe0198bcc 100644
--- a/src/tespy/components/reactors/fuel_cell.py
+++ b/src/tespy/components/reactors/fuel_cell.py
@@ -828,15 +828,13 @@ def initialise_target(self, c, key):
def calc_parameters(self):
r"""Postprocessing parameter calculation."""
- self.Q.val = - self.inl[0].m.val_SI * (
- self.outl[0].h.val_SI - self.inl[0].h.val_SI)
+ self.Q.val = -self.inl[0].m.val_SI * (
+ self.outl[0].h.val_SI - self.inl[0].h.val_SI
+ )
self.pr.val = self.outl[0].p.val_SI / self.inl[0].p.val_SI
self.e.val = self.P.val / self.inl[2].m.val_SI
self.eta.val = self.e.val / self.e0
i = self.inl[0]
o = self.outl[0]
- self.zeta.val = (
- (i.p.val_SI - o.p.val_SI) * np.pi ** 2
- / (4 * i.m.val_SI ** 2 * (i.vol.val_SI + o.vol.val_SI))
- )
+ self.zeta.val = self.calc_zeta(i, o)
diff --git a/src/tespy/components/reactors/water_electrolyzer.py b/src/tespy/components/reactors/water_electrolyzer.py
index 9e8e959d8..e93d87a9a 100644
--- a/src/tespy/components/reactors/water_electrolyzer.py
+++ b/src/tespy/components/reactors/water_electrolyzer.py
@@ -1157,18 +1157,16 @@ def initialise_target(self, c, key):
def calc_parameters(self):
r"""Postprocessing parameter calculation."""
- self.Q.val = - self.inl[0].m.val_SI * (
- self.outl[0].h.val_SI - self.inl[0].h.val_SI)
+ self.Q.val = -self.inl[0].m.val_SI * (
+ self.outl[0].h.val_SI - self.inl[0].h.val_SI
+ )
self.pr.val = self.outl[0].p.val_SI / self.inl[0].p.val_SI
self.e.val = self.P.val / self.outl[2].m.val_SI
self.eta.val = self.e0 / self.e.val
i = self.inl[0]
o = self.outl[0]
- self.zeta.val = (
- (i.p.val_SI - o.p.val_SI) * np.pi ** 2
- / (4 * i.m.val_SI ** 2 * (i.vol.val_SI + o.vol.val_SI))
- )
+ self.zeta.val = self.calc_zeta(i, o)
def exergy_balance(self, T0):
self.E_P = (
diff --git a/src/tespy/components/turbomachinery/compressor.py b/src/tespy/components/turbomachinery/compressor.py
index a28d0d344..5c7668d92 100644
--- a/src/tespy/components/turbomachinery/compressor.py
+++ b/src/tespy/components/turbomachinery/compressor.py
@@ -374,7 +374,7 @@ def char_map_pr_func(self):
i = self.inl[0]
o = self.outl[0]
- x = np.sqrt(i.T.design / i.calc_T(T0=i.T.val_SI))
+ x = np.sqrt(i.T.design / i.calc_T())
y = (i.m.val_SI * i.p.design) / (i.m.design * i.p.val_SI * x)
yarr, zarr = self.char_map_pr.char_func.evaluate_x(x)
@@ -477,7 +477,7 @@ def char_map_eta_s_func(self):
i = self.inl[0]
o = self.outl[0]
- x = np.sqrt(i.T.design / i.calc_T(T0=i.T.val_SI))
+ x = np.sqrt(i.T.design / i.calc_T())
y = (i.m.val_SI * i.p.design) / (i.m.design * i.p.val_SI * x)
yarr, zarr = self.char_map_eta_s.char_func.evaluate_x(x)
diff --git a/src/tespy/connections/connection.py b/src/tespy/connections/connection.py
index ceb64a66f..e37049f05 100644
--- a/src/tespy/connections/connection.py
+++ b/src/tespy/connections/connection.py
@@ -9,8 +9,6 @@
SPDX-License-Identifier: MIT
"""
-from collections import OrderedDict
-
import numpy as np
from tespy.components.component import Component
@@ -38,6 +36,7 @@
from tespy.tools.fluid_properties import viscosity_mix_ph
from tespy.tools.fluid_properties.functions import dT_mix_ph_dfluid
from tespy.tools.fluid_properties.functions import p_sat_T
+from tespy.tools.fluid_properties.helpers import get_mixture_temperature_range
from tespy.tools.fluid_properties.helpers import get_number_of_fluids
from tespy.tools.global_vars import ERR
from tespy.tools.global_vars import fluid_property_data as fpd
@@ -303,7 +302,7 @@ def _check_connector_id(self, component, connector_id, connecter_locations):
if connector_id not in connecter_locations:
msg = (
"Error creating connection. Specified connector for "
- f"{component.label} ({connector_id} is not available. Choose "
+ f"{component.label} ({connector_id}) is not available. Choose "
f"from " + ", ".join(connecter_locations) + "."
)
logger.error(msg)
@@ -523,8 +522,9 @@ def _parameter_specification(self, key, value):
if value is None:
self.get_attr(key).set_attr(is_set=False)
+
if f"{key}_ref" in self.property_data:
- self.get_attr(key).set_attr(is_set=False)
+ self.get_attr(f"{key}_ref").set_attr(is_set=False)
if key in ["m", "p", "h"]:
self.get_attr(key).is_var = True
@@ -626,7 +626,7 @@ def preprocess(self):
container._solved = False
self.residual = np.zeros(self.num_eq)
- self.jacobian = OrderedDict()
+ self.jacobian = {}
def simplify_specifications(self):
systemvar_specs = []
@@ -726,7 +726,7 @@ def primary_ref_func(self, k, **kwargs):
ref = self.get_attr(f"{variable}_ref").ref
self.residual[k] = (
self.get_attr(variable).val_SI
- - ref.obj.get_attr(variable).val_SI * ref.factor + ref.delta_SI
+ - (ref.obj.get_attr(variable).val_SI * ref.factor + ref.delta_SI)
)
def primary_ref_deriv(self, k, **kwargs):
@@ -739,6 +739,8 @@ def primary_ref_deriv(self, k, **kwargs):
self.jacobian[k, ref.obj.get_attr(variable).J_col] = -ref.factor
def calc_T(self, T0=None):
+ if T0 is None:
+ T0 = self.T.val_SI
return T_mix_ph(self.p.val_SI, self.h.val_SI, self.fluid_data, self.mixing_rule, T0=T0)
def T_func(self, k, **kwargs):
@@ -761,7 +763,7 @@ def T_deriv(self, k, **kwargs):
def T_ref_func(self, k, **kwargs):
ref = self.T_ref.ref
self.residual[k] = (
- self.calc_T() - ref.obj.calc_T() * ref.factor + ref.delta_SI
+ self.calc_T() - (ref.obj.calc_T() * ref.factor + ref.delta_SI)
)
def T_ref_deriv(self, k, **kwargs):
@@ -810,7 +812,7 @@ def v_ref_func(self, k, **kwargs):
ref = self.v_ref.ref
self.residual[k] = (
self.calc_vol(T0=self.T.val_SI) * self.m.val_SI
- - ref.obj.calc_vol(T0=ref.obj.T.val_SI) * ref.obj.m.val_SI * ref.factor + ref.delta_SI
+ - (ref.obj.calc_vol(T0=ref.obj.T.val_SI) * ref.obj.m.val_SI * ref.factor + ref.delta_SI)
)
def v_ref_deriv(self, k, **kwargs):
@@ -919,7 +921,15 @@ def calc_results(self):
)
logger.error(msg)
_converged = False
-
+ else:
+ _, Tmax = get_mixture_temperature_range(self.fluid_data)
+ if self.T.val_SI > Tmax:
+ msg = (
+ "The temperature value of the mixture is above the "
+ "upper temperature limit of a mixture component. "
+ "The resulting temperature might not be erroneous."
+ )
+ logger.warning(msg)
else:
try:
if not self.x.is_set:
diff --git a/src/tespy/networks/network.py b/src/tespy/networks/network.py
index 9f699dd08..1fcf09011 100644
--- a/src/tespy/networks/network.py
+++ b/src/tespy/networks/network.py
@@ -15,7 +15,6 @@
"""
import json
import os
-from collections import OrderedDict
from time import time
import numpy as np
@@ -189,7 +188,7 @@ def set_defaults(self):
# user defined function dictionary for fast access
self.user_defined_eq = {}
# bus dictionary
- self.busses = OrderedDict()
+ self.busses = {}
# results and specification dictionary
self.results = {}
self.specifications = {}
@@ -455,6 +454,10 @@ def del_conns(self, *args):
comps = list({cp for c in args for cp in [c.source, c.target]})
for c in args:
self.conns.drop(c.label, inplace=True)
+ if "Connection" in self.results:
+ self.results["Connection"].drop(
+ c.label, inplace=True, errors="ignore"
+ )
msg = ('Deleted connection ' + c.label + ' from network.')
logger.debug(msg)
@@ -555,6 +558,9 @@ def _del_comps(self, comps):
comp not in self.conns["target"].values
):
self.comps.drop(comp.label, inplace=True)
+ self.results[comp.__class__.__name__].drop(
+ comp.label, inplace=True, errors="ignore"
+ )
msg = f"Deleted component {comp.label} from network."
logger.debug(msg)
@@ -742,7 +748,7 @@ def create_fluid_wrapper_branches(self):
set(branch_data["connections"])
& set(ob_data["connections"])
)
- if len(common_connections) > 0:
+ if len(common_connections) > 0 and ob_name in merged:
merged[branch_name]["connections"] = list(
set(branch_data["connections"] + ob_data["connections"])
)
@@ -1262,7 +1268,7 @@ def init_design(self):
c.m.design = np.nan
c.p.design = np.nan
c.h.design = np.nan
- c.fluid.design = OrderedDict()
+ c.fluid.design = {}
c.new_design = True
@@ -1501,9 +1507,9 @@ def init_conn_design_params(self, c, df):
if c.label not in df.index:
# no matches in the connections of the network and the design files
msg = (
- f"Could not find connection {c.label} in design case. Please "
+ f"Could not find connection '{c.label}' in design case. Please "
"make sure no connections have been modified or components "
- "havebeen relabeled for your offdesign calculation."
+ "have been relabeled for your offdesign calculation."
)
logger.exception(msg)
raise hlp.TESPyNetworkError(msg)
diff --git a/src/tespy/tools/data_containers.py b/src/tespy/tools/data_containers.py
index f55447d32..61375b466 100644
--- a/src/tespy/tools/data_containers.py
+++ b/src/tespy/tools/data_containers.py
@@ -12,8 +12,6 @@
SPDX-License-Identifier: MIT
"""
-import collections
-
import numpy as np
from tespy.tools import logger
@@ -354,15 +352,15 @@ def attr():
values.
"""
return {
- 'val': collections.OrderedDict(),
- 'val0': collections.OrderedDict(),
+ 'val': dict(),
+ 'val0': dict(),
'is_set': set(),
- 'design': collections.OrderedDict(),
- 'wrapper': collections.OrderedDict(),
- 'back_end': collections.OrderedDict(),
- 'engine': collections.OrderedDict(),
+ 'design': dict(),
+ 'wrapper': dict(),
+ 'back_end': dict(),
+ 'engine': dict(),
"is_var": set(),
- "J_col": collections.OrderedDict(),
+ "J_col": dict(),
}
def serialize(self):
diff --git a/src/tespy/tools/fluid_properties/helpers.py b/src/tespy/tools/fluid_properties/helpers.py
index 27740208a..c085a4509 100644
--- a/src/tespy/tools/fluid_properties/helpers.py
+++ b/src/tespy/tools/fluid_properties/helpers.py
@@ -67,9 +67,11 @@ def get_molar_fractions(fluid_data):
def inverse_temperature_mixture(p=None, target_value=None, fluid_data=None, T0=None, f=None):
# calculate the fluid properties for fluid mixtures
valmin, valmax = get_mixture_temperature_range(fluid_data)
- if T0 is None:
+ if T0 is None or T0 == 0 or np.isnan(T0):
T0 = (valmin + valmax) / 2.0
+ valmax *= 2
+
function_kwargs = {
"p": p, "fluid_data": fluid_data, "T": T0,
"function": f, "parameter": "T" , "delta": 0.01
diff --git a/src/tespy/tools/fluid_properties/mixtures.py b/src/tespy/tools/fluid_properties/mixtures.py
index b156a133f..645537783 100644
--- a/src/tespy/tools/fluid_properties/mixtures.py
+++ b/src/tespy/tools/fluid_properties/mixtures.py
@@ -253,7 +253,7 @@ def exergy_chemical_ideal_cond(pamb, Tamb, fluid_data, Chem_Ex):
fluid_aliases = fluid_data[fluid]["wrapper"]._aliases
- if molar_liquid > 0:
+ if molar_liquid > 0 and "water" in fluid_aliases:
y = [
Chem_Ex[k][2] for k in fluid_aliases if k in Chem_Ex
]
diff --git a/src/tespy/tools/fluid_properties/wrappers.py b/src/tespy/tools/fluid_properties/wrappers.py
index bc2f84df7..565c98bf4 100644
--- a/src/tespy/tools/fluid_properties/wrappers.py
+++ b/src/tespy/tools/fluid_properties/wrappers.py
@@ -191,15 +191,6 @@ def h_ps(self, p, s):
return self.AS.hmass()
def h_pT(self, p, T):
- if self.back_end == "INCOMP":
- if T == (self._T_max + self._T_min) / 2:
- T += ERR
- self.AS.update(CP.PT_INPUTS, p, T)
- h = self.AS.hmass() * 0.5
- T -= 2 * ERR
- self.AS.update(CP.PT_INPUTS, p, T)
- h += self.AS.hmass() * 0.5
- return h
self.AS.update(CP.PT_INPUTS, p, T)
return self.AS.hmass()
diff --git a/src/tespy/tools/global_vars.py b/src/tespy/tools/global_vars.py
index 37a9273e5..8cd773f1a 100644
--- a/src/tespy/tools/global_vars.py
+++ b/src/tespy/tools/global_vars.py
@@ -8,14 +8,13 @@
SPDX-License-Identifier: MIT
"""
-from collections import OrderedDict
ERR = 1e-6
molar_masses = {}
gas_constants = {}
gas_constants['uni'] = 8.314462618
-fluid_property_data = OrderedDict({
+fluid_property_data = {
'm': {
'text': 'mass flow',
'SI_unit': 'kg / s',
@@ -99,6 +98,6 @@
'latex_eq': r'0 = s_\mathrm{spec} - s\left(p, h \right)',
'documentation': {'float_fmt': '{:,.2f}'}
}
-})
+}
combustion_gases = ['methane', 'ethane', 'propane', 'butane', 'hydrogen', 'nDodecane']
diff --git a/src/tespy/tools/helpers.py b/src/tespy/tools/helpers.py
index e177474ec..efd5b7720 100644
--- a/src/tespy/tools/helpers.py
+++ b/src/tespy/tools/helpers.py
@@ -11,7 +11,6 @@
import json
import os
-from collections import OrderedDict
from collections.abc import Mapping
from copy import deepcopy
@@ -65,27 +64,6 @@ def merge_dicts(dict1, dict2):
return result
-def nested_OrderedDict(dictionary):
- """Create a nested OrderedDict from a nested dict.
-
- Parameters
- ----------
- dictionary : dict
- Nested dict.
-
- Returns
- -------
- dictionary : collections.OrderedDict
- Nested OrderedDict.
- """
- dictionary = OrderedDict(dictionary)
- for key, value in dictionary.items():
- if isinstance(value, dict):
- dictionary[key] = nested_OrderedDict(value)
-
- return dictionary
-
-
class TESPyNetworkError(Exception):
"""Custom message for network related errors."""
@@ -628,7 +606,7 @@ def newton(func, deriv, params, y, **kwargs):
logger.debug(msg)
break
- if tol_mode == 'abs':
+ if tol_mode == 'abs' or y == 0:
expr = abs(res) >= tol_abs
elif tol_mode == 'rel':
expr = abs(res / y) >= tol_rel
@@ -680,7 +658,7 @@ def newton_with_kwargs(
logger.debug(msg)
break
- if tol_mode == 'abs':
+ if tol_mode == 'abs' or target_value == 0:
expr = abs(residual) >= tol_abs
elif tol_mode == 'rel':
expr = abs(residual / target_value) >= tol_rel
diff --git a/src/tespy/tools/optimization.py b/src/tespy/tools/optimization.py
index b7a513878..4154d1a95 100644
--- a/src/tespy/tools/optimization.py
+++ b/src/tespy/tools/optimization.py
@@ -6,7 +6,6 @@
import pandas as pd
from tespy.tools.helpers import merge_dicts
-from tespy.tools.helpers import nested_OrderedDict
class OptimizationProblem:
@@ -70,14 +69,8 @@ def __init__(self, model, variables={}, constraints={}, objective="objective"):
"upper limits": {"Connections": {}, "Components": {}}
}
# merge the passed values into the default dictionary structure
- variables = merge_dicts(variables, default_variables)
- constraints = merge_dicts(constraints, default_constraints)
-
- # pygmo creates a vector for the variables and constraints, which has
- # to be in consistent order. Therefore use OrderedDicts instead of
- # dictionaries
- self.variables = nested_OrderedDict(variables)
- self.constraints = nested_OrderedDict(constraints)
+ self.variables = merge_dicts(variables, default_variables)
+ self.constraints = merge_dicts(constraints, default_constraints)
self.objective = objective
self.variable_list = []
self.constraint_list = []
@@ -307,3 +300,5 @@ def run(self, algo, pop, num_ind, num_gen):
round(pop.champion_x[i], 4)
)
)
+
+ return pop
diff --git a/tests/test_components/test_combustion.py b/tests/test_components/test_combustion.py
index 7912781fb..b8290babd 100644
--- a/tests/test_components/test_combustion.py
+++ b/tests/test_components/test_combustion.py
@@ -9,9 +9,10 @@
SPDX-License-Identifier: MIT
"""
-
import shutil
+import pytest
+
from tespy.components import CombustionChamber
from tespy.components import CombustionEngine
from tespy.components import DiabaticCombustionChamber
@@ -103,6 +104,20 @@ def test_CombustionChamber(self):
str(round(self.c3.fluid.val['O2'], 4)) + '.')
assert 0.0 == round(self.c3.fluid.val['O2'], 4), msg
+ def test_CombustionChamberHighTemperature(self):
+ instance = CombustionChamber('combustion chamber')
+ self.setup_CombustionChamber_network(instance)
+
+ # connection parameter specification
+ air = {'N2': 0.8, 'O2': 0.2}
+ fuel = {'CH4': 1}
+ self.c1.set_attr(fluid=air, p=1, T=30, m=1)
+ self.c2.set_attr(fluid=fuel, T=30)
+ instance.set_attr(lamb=1)
+ self.nw.solve('design')
+ self.nw._convergence_check()
+ assert self.c3.T.val_SI == pytest.approx(2110, abs=0.1)
+
def test_DiabaticCombustionChamber(self):
"""
Test component properties of diabatic combustion chamber.
diff --git a/tests/test_connections.py b/tests/test_connections.py
index acc620a20..6d35bdf80 100644
--- a/tests/test_connections.py
+++ b/tests/test_connections.py
@@ -58,7 +58,7 @@ def test_volumetric_flow_reference(self):
c2.set_attr(v=Ref(c1, 2, 10))
self.nw.solve('design')
- v_expected = round(c1.v.val * 2 - 10, 4)
+ v_expected = round(c1.v.val * 2 + 10, 4)
v_is = round(c2.v.val, 4)
msg = (
'The mass flow of the connection 2 should be equal to '
@@ -87,7 +87,7 @@ def test_temperature_reference(self):
c2.set_attr(T=Ref(c1, 1.5, -75))
self.nw.solve('design')
- T_expected = round(convert_from_SI("T", c1.T.val_SI * 1.5, c1.T.unit) + 75, 4)
+ T_expected = round(convert_from_SI("T", c1.T.val_SI * 1.5, c1.T.unit) - 75, 4)
T_is = round(c2.T.val, 4)
msg = (
'The temperature of the connection 2 should be equal to '
@@ -116,7 +116,7 @@ def test_primary_reference(self):
c2.set_attr(m=Ref(c1, 2, -0.5))
self.nw.solve('design')
- m_expected = round(convert_from_SI("m", c1.m.val_SI * 2, c1.m.unit) + 0.5, 4)
+ m_expected = round(convert_from_SI("m", c1.m.val_SI * 2, c1.m.unit) - 0.5, 4)
m_is = round(c2.m.val, 4)
msg = (
'The mass flow of the connection 2 should be equal to '
diff --git a/tests/test_networks/test_binary_incompressible.py b/tests/test_networks/test_binary_incompressible.py
index c5aa7be94..9683e71cd 100644
--- a/tests/test_networks/test_binary_incompressible.py
+++ b/tests/test_networks/test_binary_incompressible.py
@@ -51,4 +51,4 @@ def setup_method(self):
self.nw.solve("design")
def test_binaries(self):
- self.nw._convergence_check()
\ No newline at end of file
+ self.nw._convergence_check()
diff --git a/tutorial/heat_pump_exergy/NH3_calculations.py b/tutorial/heat_pump_exergy/NH3_calculations.py
index a602ae0dc..af56e171f 100644
--- a/tutorial/heat_pump_exergy/NH3_calculations.py
+++ b/tutorial/heat_pump_exergy/NH3_calculations.py
@@ -19,6 +19,7 @@
import plotly.graph_objects as go
from fluprodia import FluidPropertyDiagram
import pandas as pd
+import matplotlib.pyplot as plt
# %% network
pamb = 1.013 # ambient pressure
@@ -166,16 +167,18 @@
for key, data in result_dict.items():
result_dict[key]['datapoints'] = diagram.calc_individual_isoline(**data)
-diagram.set_limits(x_min=0, x_max=2100, y_min=1e0, y_max=2e2)
diagram.calc_isolines()
-diagram.draw_isolines('logph')
+
+fig, ax = plt.subplots(1, figsize=(16, 10))
+diagram.draw_isolines(fig, ax, 'logph', x_min=0, x_max=2100, y_min=1e0, y_max=2e2)
for key in result_dict.keys():
datapoints = result_dict[key]['datapoints']
- diagram.ax.plot(datapoints['h'], datapoints['p'], color='#ff0000')
- diagram.ax.scatter(datapoints['h'][0], datapoints['p'][0], color='#ff0000')
+ ax.plot(datapoints['h'], datapoints['p'], color='#ff0000')
+ ax.scatter(datapoints['h'][0], datapoints['p'][0], color='#ff0000')
-diagram.save('NH3_logph.svg')
+plt.tight_layout()
+fig.savefig('NH3_logph.svg')
# %% exergy analysis
diff --git a/tutorial/heat_pump_exergy/R410A_calculations.py b/tutorial/heat_pump_exergy/R410A_calculations.py
index 457a10472..5cb3e6a01 100644
--- a/tutorial/heat_pump_exergy/R410A_calculations.py
+++ b/tutorial/heat_pump_exergy/R410A_calculations.py
@@ -19,6 +19,7 @@
import plotly.graph_objects as go
from fluprodia import FluidPropertyDiagram
import pandas as pd
+import matplotlib.pyplot as plt
# %% network
pamb = 1.013 # ambient pressure
@@ -165,16 +166,18 @@
for key, data in result_dict.items():
result_dict[key]['datapoints'] = diagram.calc_individual_isoline(**data)
-diagram.set_limits(x_min=200, x_max=500, y_min=0.8e1, y_max=0.8e2)
diagram.calc_isolines()
-diagram.draw_isolines('logph')
+
+fig, ax = plt.subplots(1, figsize=(16, 10))
+diagram.draw_isolines(fig, ax, 'logph', x_min=200, x_max=500, y_min=0.8e1, y_max=0.8e2)
for key in result_dict.keys():
datapoints = result_dict[key]['datapoints']
- diagram.ax.plot(datapoints['h'], datapoints['p'], color='#ff0000')
- diagram.ax.scatter(datapoints['h'][0], datapoints['p'][0], color='#ff0000')
+ ax.plot(datapoints['h'], datapoints['p'], color='#ff0000')
+ ax.scatter(datapoints['h'][0], datapoints['p'][0], color='#ff0000')
-diagram.save('R410A_logph.svg')
+plt.tight_layout()
+fig.savefig('R410A_logph.svg')
# %% exergy analysis