Skip to content

Commit a1880e7

Browse files
committed
Merge branch 'dev'
2 parents fc7dd13 + c5ca7bc commit a1880e7

File tree

11 files changed

+275
-90
lines changed

11 files changed

+275
-90
lines changed

README.rst

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
Thermal Engineering Systems in Python
22
=====================================
33
TESPy stands for "Thermal Engineering Systems in Python" and provides a
4-
powerful simulation toolkit for thermal engineering plants such as power
5-
plants, district heating systems or heat pumps. It is an external extension
6-
module within the Open Energy Modelling Framework `oemof <https://oemof.org/>`_
7-
and can be used as a standalone package.
4+
powerful simulation toolkit for thermal engineering plants such as various
5+
types of power plants (including organic rankine cycles), heat pumps or
6+
refrigeration machines. Due to its flexibility it is actually possible to
7+
model any kind of thermal energy conversion process, this also includes energy
8+
balancing of industrial processes, district heating or HVAC systems. It is
9+
part of the Open Energy Modelling Framework `oemof <https://oemof.org/>`_ and
10+
can be used as a standalone package.
811

912
.. figure:: https://raw.githubusercontent.com/oemof/tespy/9915f013c40fe418947a6e4c1fd0cd0eba45893c/docs/api/_images/logo_tespy_big.svg
1013
:align: center
@@ -28,10 +31,9 @@ Key Features
2831
============
2932
* **Open** Source
3033
* **Generic** thermal engineering applications
31-
* **Automatic** model documentation in LaTeX for high transparency and
32-
reproducibility
33-
* **Extendable** framework for the implementation of custom components and
34-
component groups
34+
* **Extendable** framework for the implementation of custom components, fluid
35+
property formulations and equations
36+
* **Integration** of optimization capabilities through an API to pygmo
3537
* **Postprocessing** features like exergy analysis and fluid property plotting
3638

3739
.. start-badges

docs/introduction.rst

+12-10
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ Thermal Engineering Systems in Python
33
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44

55
TESPy stands for "Thermal Engineering Systems in Python" and provides a
6-
powerful simulation toolkit for thermal engineering plants such as power
7-
plants, district heating systems or heat pumps. It is an external extension
8-
module within the Open Energy Modeling Framework `oemof <https://oemof.org/>`_
9-
and can be used as a standalone package.
6+
powerful simulation toolkit for thermal engineering plants such as various
7+
types of power plants (including organic rankine cycles), heat pumps or
8+
refrigeration machines. Due to its flexibility it is actually possible to
9+
model any kind of thermal energy conversion process, this also includes energy
10+
balancing of industrial processes, district heating or HVAC systems. It is
11+
part of the Open Energy Modelling Framework `oemof <https://oemof.org/>`_ and
12+
can be used as a standalone package.
1013

1114
.. image:: /_static/images/logo_tespy_big.svg
1215
:align: center
@@ -28,18 +31,17 @@ mixers and splitters as well as some advanced components
2831
Everybody is welcome to use and/or develop TESPy. Contribution is already
2932
possible on a low level by simply fixing typos in TESPy's documentation or
3033
rephrasing sections which are unclear. If you want to support us that way
31-
please fork the TESPy repository to your own GitHub account and make
32-
changes as described in the GitHub guidelines:
34+
please fork the `TESPy repository <https://github.com/oemof/tespy>`_ to your
35+
own GitHub account and make changes as described in the GitHub guidelines:
3336
https://guides.github.com/activities/hello-world/
3437

3538
Key Features
3639
============
3740
* **Open** Source
3841
* **Generic** thermal engineering applications
39-
* **Automatic** model documentation in LaTeX for high transparency and
40-
reproducibility
41-
* **Extendable** framework for the implementation of custom components and
42-
component groups
42+
* **Extendable** framework for the implementation of custom components, fluid
43+
property formulations and equations
44+
* **Integration** of optimization capabilities through an API to pygmo
4345
* **Postprocessing** features like exergy analysis and fluid property plotting
4446

4547
Quick installation

docs/whats_new.rst

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ What's New
33

44
Discover notable new features and improvements in each release
55

6+
.. include:: whats_new/v0-7-8.rst
67
.. include:: whats_new/v0-7-7.rst
78
.. include:: whats_new/v0-7-6-001.rst
89
.. include:: whats_new/v0-7-6.rst

docs/whats_new/v0-7-7.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
Under development
2-
+++++++++++++++++
1+
v0.7.7 - Newton's Nature (October, 27, 2024)
2+
++++++++++++++++++++++++++++++++++++++++++++
33

44
Bug Fixes
55
#########

docs/whats_new/v0-7-8.rst

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
v0.7.8 - Newton's Nature (December, 04, 2024)
2+
+++++++++++++++++++++++++++++++++++++++++++++
3+
4+
New Features
5+
############
6+
- The `HeatExchanger` class now has three new attributes, :code:`dp1`,
7+
:code:`dp2` (hot side and cold side pressure drop in network pressure unit)
8+
as well as :code:`ttd_min` for the minimal value of the terminal temperature
9+
diference values
10+
(`PR #581 <https://github.com/oemof/tespy/pull/581>`__).
11+
12+
Contributors
13+
############
14+
- Francesco Witte (`@fwitte <https://github.com/fwitte>`__)

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ exclude = ["docs/_build"]
2424

2525
[project]
2626
name = "tespy"
27-
version = "0.7.7"
27+
version = "0.7.8"
2828
description = "Thermal Engineering Systems in Python (TESPy)"
2929
readme = "README.rst"
3030
authors = [

src/tespy/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import os
44

55
__datapath__ = os.path.join(importlib.resources.files("tespy"), "data")
6-
__version__ = '0.7.7 - Newton\'s Nature'
6+
__version__ = '0.7.8 - Newton\'s Nature'
77

88
# tespy data and connections import
99
from . import connections # noqa: F401

src/tespy/components/component.py

+58
Original file line numberDiff line numberDiff line change
@@ -1230,3 +1230,61 @@ def zeta_deriv(self, increment_filter, k, zeta='', inconn=0, outconn=0):
12301230
# custom variable zeta
12311231
if data.is_var:
12321232
self.jacobian[k, data.J_col] = self.numeric_deriv(f, zeta, None, **kwargs)
1233+
1234+
def dp_func(self, dp=None, inconn=None, outconn=None):
1235+
"""Calculate residual value of pressure difference function.
1236+
1237+
Parameters
1238+
----------
1239+
dp : str
1240+
Component parameter to evaluate the dp_func on, e.g.
1241+
:code:`dp1`.
1242+
1243+
inconn : int
1244+
Connection index of inlet.
1245+
1246+
outconn : int
1247+
Connection index of outlet.
1248+
1249+
Returns
1250+
-------
1251+
residual : float
1252+
Residual value of function.
1253+
1254+
.. math::
1255+
1256+
0 = p_{in} - p_{out} - dp
1257+
"""
1258+
inlet_conn = self.inl[inconn]
1259+
outlet_conn = self.outl[outconn]
1260+
dp_value = self.get_attr(dp).val_SI
1261+
return inlet_conn.p.val_SI - outlet_conn.p.val_SI - dp_value
1262+
1263+
def dp_deriv(self, increment_filter, k, dp=None, inconn=None, outconn=None):
1264+
r"""
1265+
Calculate residual value of pressure difference function.
1266+
1267+
Parameters
1268+
----------
1269+
increment_filter : ndarray
1270+
Matrix for filtering non-changing variables.
1271+
1272+
k : int
1273+
Position of equation in Jacobian matrix.
1274+
1275+
dp : str
1276+
Component parameter to evaluate the dp_func on, e.g.
1277+
:code:`dp1`.
1278+
1279+
inconn : int
1280+
Connection index of inlet.
1281+
1282+
outconn : int
1283+
Connection index of outlet.
1284+
"""
1285+
inlet_conn = self.inl[inconn]
1286+
outlet_conn = self.outl[outconn]
1287+
if inlet_conn.p.is_var:
1288+
self.jacobian[k, inlet_conn.p.J_col] = 1
1289+
if outlet_conn.p.is_var:
1290+
self.jacobian[k, outlet_conn.p.J_col] = -1

src/tespy/components/heat_exchangers/base.py

+94-4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
from tespy.tools.document_models import generate_latex_eq
2424
from tespy.tools.fluid_properties import h_mix_pT
2525
from tespy.tools.fluid_properties import s_mix_ph
26+
from tespy.tools.helpers import convert_from_SI
27+
from tespy.tools.helpers import convert_to_SI
2628

2729

2830
@component_registry
@@ -46,13 +48,16 @@ class HeatExchanger(Component):
4648
- :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.kA_char_func`
4749
- :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.ttd_u_func`
4850
- :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.ttd_l_func`
51+
- :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.ttd_min_func`
4952
- :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.eff_cold_func`
5053
- :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.eff_hot_func`
5154
- :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.eff_max_func`
5255
- hot side :py:meth:`tespy.components.component.Component.pr_func`
5356
- cold side :py:meth:`tespy.components.component.Component.pr_func`
5457
- hot side :py:meth:`tespy.components.component.Component.zeta_func`
5558
- cold side :py:meth:`tespy.components.component.Component.zeta_func`
59+
- hot side :py:meth:`tespy.components.component.Component.dp_func`
60+
- cold side :py:meth:`tespy.components.component.Component.dp_func`
5661
5762
Inlets/Outlets
5863
@@ -106,6 +111,12 @@ class HeatExchanger(Component):
106111
pr2 : float, dict, :code:`"var"`
107112
Outlet to inlet pressure ratio at cold side, :math:`pr/1`.
108113
114+
dp1 : float, dict, :code:`"var"`
115+
Inlet to outlet pressure delta at hot side, :math:`dp/\text{Pa}`.
116+
117+
dp2 : float, dict, :code:`"var"`
118+
Inlet to outlet pressure delta at cold side, :math:`dp/\text{Pa}`.
119+
109120
zeta1 : float, dict, :code:`"var"`
110121
Geometry independent friction coefficient at hot side,
111122
:math:`\frac{\zeta}{D^4}/\frac{1}{\text{m}^4}`.
@@ -120,6 +131,9 @@ class HeatExchanger(Component):
120131
ttd_u : float, dict
121132
Upper terminal temperature difference :math:`ttd_\mathrm{u}/\text{K}`.
122133
134+
ttd_min : float, dict
135+
Minumum terminal temperature difference :math:`ttd_\mathrm{min}/\text{K}`.
136+
123137
eff_cold : float, dict
124138
Cold side heat exchanger effectiveness :math:`eff_\text{cold}/\text{1}`.
125139
@@ -229,6 +243,9 @@ def get_parameters(self):
229243
'ttd_l': dc_cp(
230244
min_val=0, num_eq=1, func=self.ttd_l_func,
231245
deriv=self.ttd_l_deriv, latex=self.ttd_l_func_doc),
246+
'ttd_min': dc_cp(
247+
min_val=0, num_eq=1, func=self.ttd_min_func,
248+
deriv=self.ttd_min_deriv),
232249
'pr1': dc_cp(
233250
min_val=1e-4, max_val=1, num_eq=1, deriv=self.pr_deriv,
234251
latex=self.pr_func_doc,
@@ -237,6 +254,14 @@ def get_parameters(self):
237254
min_val=1e-4, max_val=1, num_eq=1, latex=self.pr_func_doc,
238255
deriv=self.pr_deriv, func=self.pr_func,
239256
func_params={'pr': 'pr2', 'inconn': 1, 'outconn': 1}),
257+
'dp1': dc_cp(
258+
min_val=0, max_val=10000, num_eq=1,
259+
deriv=self.dp_deriv, func=self.dp_func,
260+
func_params={'dp': 'dp1', 'inconn': 0, 'outconn': 0}),
261+
'dp2': dc_cp(
262+
min_val=0, max_val=10000, num_eq=1,
263+
deriv=self.dp_deriv, func=self.dp_func,
264+
func_params={'dp': 'dp2', 'inconn': 1, 'outconn': 1}),
240265
'zeta1': dc_cp(
241266
min_val=0, max_val=1e15, num_eq=1, latex=self.zeta_func_doc,
242267
deriv=self.zeta_deriv, func=self.zeta_func,
@@ -285,6 +310,15 @@ def inlets():
285310
def outlets():
286311
return ['out1', 'out2']
287312

313+
def preprocess(self, num_nw_vars):
314+
super().preprocess(num_nw_vars)
315+
316+
if self.dp1.is_set:
317+
self.dp1.val_SI = convert_to_SI('p', self.dp1.val, self.inl[0].p.unit)
318+
319+
if self.dp2.is_set:
320+
self.dp2.val_SI = convert_to_SI('p', self.dp2.val, self.inl[1].p.unit)
321+
288322
def energy_balance_func(self):
289323
r"""
290324
Equation for heat exchanger energy balance.
@@ -706,6 +740,56 @@ def ttd_l_deriv(self, increment_filter, k):
706740
if self.is_variable(c.h, increment_filter):
707741
self.jacobian[k, c.h.J_col] = self.numeric_deriv(f, 'h', c)
708742

743+
def ttd_min_func(self):
744+
r"""
745+
Equation for upper terminal temperature difference.
746+
747+
Returns
748+
-------
749+
residual : float
750+
Residual value of equation.
751+
752+
.. math::
753+
754+
ttd_{l} = T_{out,1} - T_{in,2}
755+
ttd_{u} = T_{in,1} - T_{out,2}
756+
0 = \text{min}\left(ttd_{u}, ttd_{l}\right)
757+
758+
"""
759+
i = self.inl[1]
760+
o = self.outl[0]
761+
T_i2 = i.calc_T()
762+
T_o1 = o.calc_T()
763+
764+
i = self.inl[0]
765+
o = self.outl[1]
766+
T_i1 = i.calc_T()
767+
T_o2 = o.calc_T()
768+
769+
ttd_l = T_o1 - T_i2
770+
ttd_u = T_i1 - T_o2
771+
772+
return self.ttd_min.val - min(ttd_l, ttd_u)
773+
774+
def ttd_min_deriv(self, increment_filter, k):
775+
"""
776+
Calculate partial derivates of minimum terminal temperature function.
777+
778+
Parameters
779+
----------
780+
increment_filter : ndarray
781+
Matrix for filtering non-changing variables.
782+
783+
k : int
784+
Position of derivatives in Jacobian matrix (k-th equation).
785+
"""
786+
f = self.ttd_min_func
787+
for c in self.inl + self.outl:
788+
if self.is_variable(c.p, increment_filter):
789+
self.jacobian[k, c.p.J_col] = self.numeric_deriv(f, 'p', c)
790+
if self.is_variable(c.h, increment_filter):
791+
self.jacobian[k, c.h.J_col] = self.numeric_deriv(f, 'h', c)
792+
709793
def calc_dh_max_cold(self):
710794
r"""Calculate the theoretical maximum enthalpy increase on the cold side
711795
@@ -1030,20 +1114,26 @@ def initialise_target(self, c, key):
10301114

10311115
def calc_parameters(self):
10321116
r"""Postprocessing parameter calculation."""
1033-
# component parameters
10341117
self.Q.val = self.inl[0].m.val_SI * (
10351118
self.outl[0].h.val_SI - self.inl[0].h.val_SI
10361119
)
10371120
self.ttd_u.val = self.inl[0].T.val_SI - self.outl[1].T.val_SI
10381121
self.ttd_l.val = self.outl[0].T.val_SI - self.inl[1].T.val_SI
1122+
self.ttd_min.val = min(self.ttd_u.val, self.ttd_min.val)
10391123

10401124
# pr and zeta
10411125
for i in range(2):
1042-
self.get_attr('pr' + str(i + 1)).val = (
1043-
self.outl[i].p.val_SI / self.inl[i].p.val_SI)
1044-
self.get_attr('zeta' + str(i + 1)).val = self.calc_zeta(
1126+
self.get_attr(f'pr{i + 1}').val = (
1127+
self.outl[i].p.val_SI / self.inl[i].p.val_SI
1128+
)
1129+
self.get_attr(f'zeta{i + 1}').val = self.calc_zeta(
10451130
self.inl[i], self.outl[i]
10461131
)
1132+
self.get_attr(f'dp{i + 1}').val_SI = (
1133+
self.inl[i].p.val_SI - self.outl[i].p.val_SI)
1134+
self.get_attr(f'dp{i + 1}').val = convert_from_SI(
1135+
'p', self.get_attr(f'dp{i + 1}').val_SI, self.inl[i].p.unit
1136+
)
10471137

10481138
# kA and logarithmic temperature difference
10491139
if self.ttd_u.val < 0 or self.ttd_l.val < 0:

0 commit comments

Comments
 (0)