From c238a2884f027fb76b500d71b108fcaeb9ada064 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Sun, 10 Nov 2024 16:37:13 +0100 Subject: [PATCH 1/8] Update the introductory text and the feature list --- README.rst | 18 ++++++++++-------- docs/introduction.rst | 18 ++++++++++-------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/README.rst b/README.rst index ec7a4740f..2f1c4a556 100644 --- a/README.rst +++ b/README.rst @@ -1,10 +1,13 @@ Thermal Engineering Systems in Python ===================================== TESPy stands for "Thermal Engineering Systems in Python" and provides a -powerful simulation toolkit for thermal engineering plants such as power -plants, district heating systems or heat pumps. It is an external extension -module within the Open Energy Modelling Framework `oemof `_ -and can be used as a standalone package. +powerful simulation toolkit for thermal engineering plants such as various +types of power plants (including organic rankine cycles), heat pumps or +refrigeration machines. Due to its flexibility it is actually possible to +model any kind of thermal energy conversion process, this also includes energy +balancing of industrial processes, district heating or HVAC systems. It is +part of the Open Energy Modelling Framework `oemof `_ and +can be used as a standalone package. .. figure:: https://raw.githubusercontent.com/oemof/tespy/9915f013c40fe418947a6e4c1fd0cd0eba45893c/docs/api/_images/logo_tespy_big.svg :align: center @@ -28,10 +31,9 @@ Key Features ============ * **Open** Source * **Generic** thermal engineering applications -* **Automatic** model documentation in LaTeX for high transparency and - reproducibility -* **Extendable** framework for the implementation of custom components and - component groups +* **Extendable** framework for the implementation of custom components, fluid + property formulations and equations +* **Integration** of optimization capabilities through an API to pygmo * **Postprocessing** features like exergy analysis and fluid property plotting .. start-badges diff --git a/docs/introduction.rst b/docs/introduction.rst index e5d67f274..a54cb1712 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -3,10 +3,13 @@ Thermal Engineering Systems in Python ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TESPy stands for "Thermal Engineering Systems in Python" and provides a -powerful simulation toolkit for thermal engineering plants such as power -plants, district heating systems or heat pumps. It is an external extension -module within the Open Energy Modeling Framework `oemof `_ -and can be used as a standalone package. +powerful simulation toolkit for thermal engineering plants such as various +types of power plants (including organic rankine cycles), heat pumps or +refrigeration machines. Due to its flexibility it is actually possible to +model any kind of thermal energy conversion process, this also includes energy +balancing of industrial processes, district heating or HVAC systems. It is +part of the Open Energy Modelling Framework `oemof `_ and +can be used as a standalone package. .. image:: /_static/images/logo_tespy_big.svg :align: center @@ -36,10 +39,9 @@ Key Features ============ * **Open** Source * **Generic** thermal engineering applications -* **Automatic** model documentation in LaTeX for high transparency and - reproducibility -* **Extendable** framework for the implementation of custom components and - component groups +* **Extendable** framework for the implementation of custom components, fluid + property formulations and equations +* **Integration** of optimization capabilities through an API to pygmo * **Postprocessing** features like exergy analysis and fluid property plotting Quick installation From c71004995e59055e6cb8202660c6d90b4a509bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Frei=C3=9Fmann?= Date: Thu, 14 Nov 2024 10:00:56 +0100 Subject: [PATCH 2/8] Include link to github repo in introduction. --- docs/introduction.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/introduction.rst b/docs/introduction.rst index a54cb1712..60bf2edd7 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -31,8 +31,8 @@ mixers and splitters as well as some advanced components Everybody is welcome to use and/or develop TESPy. Contribution is already possible on a low level by simply fixing typos in TESPy's documentation or rephrasing sections which are unclear. If you want to support us that way -please fork the TESPy repository to your own GitHub account and make -changes as described in the GitHub guidelines: +please fork the `TESPy repository `_ to your +own GitHub account and make changes as described in the GitHub guidelines: https://guides.github.com/activities/hello-world/ Key Features From ede1acab7af585139abb73d074cfbd7cf5bc11c4 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 4 Dec 2024 10:56:24 +0100 Subject: [PATCH 3/8] Add the basic functionality for a delta p specification for the heat exchanger component --- src/tespy/components/heat_exchangers/base.py | 43 ++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/tespy/components/heat_exchangers/base.py b/src/tespy/components/heat_exchangers/base.py index 52651e077..a3dfdcad0 100644 --- a/src/tespy/components/heat_exchangers/base.py +++ b/src/tespy/components/heat_exchangers/base.py @@ -23,6 +23,8 @@ from tespy.tools.document_models import generate_latex_eq from tespy.tools.fluid_properties import h_mix_pT from tespy.tools.fluid_properties import s_mix_ph +from tespy.tools.helpers import convert_from_SI +from tespy.tools.helpers import convert_to_SI @component_registry @@ -237,6 +239,14 @@ def get_parameters(self): min_val=1e-4, max_val=1, num_eq=1, latex=self.pr_func_doc, deriv=self.pr_deriv, func=self.pr_func, func_params={'pr': 'pr2', 'inconn': 1, 'outconn': 1}), + 'dp1': dc_cp( + min_val=0, max_val=10000, num_eq=1, + deriv=self.dp_deriv, func=self.dp_func, + func_params={'dp': 'dp1', 'inconn': 0, 'outconn': 0}), + 'dp2': dc_cp( + min_val=0, max_val=10000, num_eq=1, + deriv=self.dp_deriv, func=self.dp_func, + func_params={'dp': 'dp2', 'inconn': 1, 'outconn': 1}), 'zeta1': dc_cp( min_val=0, max_val=1e15, num_eq=1, latex=self.zeta_func_doc, deriv=self.zeta_deriv, func=self.zeta_func, @@ -285,6 +295,34 @@ def inlets(): def outlets(): return ['out1', 'out2'] + def preprocess(self, num_nw_vars): + super().preprocess(num_nw_vars) + + if self.dp1.is_set: + self.dp1.val_SI = convert_to_SI('p', self.dp1.val, self.inl[0].p.unit) + + if self.dp2.is_set: + self.dp2.val_SI = convert_to_SI('p', self.dp2.val, self.inl[1].p.unit) + + def dp_func(self, dp=None, inconn=None, outconn=None): + # dp is a string: "dp1" or "dp2" + inlet_conn = self.inl[inconn] + outlet_conn = self.outl[outconn] + dp_value = self.get_attr(dp).val_SI + # 0 = ... + # dp = pressure inlet - pressure outlet + return inlet_conn.p.val_SI - outlet_conn.p.val_SI - dp_value + + def dp_deriv(self, increment_filter, k, dp=None, inconn=None, outconn=None): + inlet_conn = self.inl[inconn] + outlet_conn = self.outl[outconn] + + if inlet_conn.p.is_var: + self.jacobian[k, inlet_conn.p.J_col] = 1 + + if outlet_conn.p.is_var: + self.jacobian[k, outlet_conn.p.J_col] = -1 + def energy_balance_func(self): r""" Equation for heat exchanger energy balance. @@ -1044,6 +1082,11 @@ def calc_parameters(self): self.get_attr('zeta' + str(i + 1)).val = self.calc_zeta( self.inl[i], self.outl[i] ) + self.get_attr(f'dp{i + 1}').val_SI = ( + self.inl[i].p.val_SI - self.outl[i].p.val_SI) + self.get_attr(f'dp{i + 1}').val = convert_from_SI( + 'p', self.get_attr(f'dp{i + 1}').val_SI, self.inl[i].p.unit + ) # kA and logarithmic temperature difference if self.ttd_u.val < 0 or self.ttd_l.val < 0: From f2da5194bdc6e891a83ee120edf7c56f1ce1a5a8 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 4 Dec 2024 13:06:51 +0100 Subject: [PATCH 4/8] Add documentation and a ttd_min parameter --- src/tespy/components/heat_exchangers/base.py | 112 ++++++++++++++++++- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/src/tespy/components/heat_exchangers/base.py b/src/tespy/components/heat_exchangers/base.py index a3dfdcad0..fc2f9b32b 100644 --- a/src/tespy/components/heat_exchangers/base.py +++ b/src/tespy/components/heat_exchangers/base.py @@ -108,6 +108,12 @@ class HeatExchanger(Component): pr2 : float, dict, :code:`"var"` Outlet to inlet pressure ratio at cold side, :math:`pr/1`. + dp1 : float, dict, :code:`"var"` + Inlet to outlet pressure delta at hot side, :math:`dp/\text{Pa}`. + + dp2 : float, dict, :code:`"var"` + Inlet to outlet pressure delta at cold side, :math:`dp/\text{Pa}`. + zeta1 : float, dict, :code:`"var"` Geometry independent friction coefficient at hot side, :math:`\frac{\zeta}{D^4}/\frac{1}{\text{m}^4}`. @@ -122,6 +128,9 @@ class HeatExchanger(Component): ttd_u : float, dict Upper terminal temperature difference :math:`ttd_\mathrm{u}/\text{K}`. + ttd_min : float, dict + Minumum terminal temperature difference :math:`ttd_\mathrm{min}/\text{K}`. + eff_cold : float, dict Cold side heat exchanger effectiveness :math:`eff_\text{cold}/\text{1}`. @@ -231,6 +240,9 @@ def get_parameters(self): 'ttd_l': dc_cp( min_val=0, num_eq=1, func=self.ttd_l_func, deriv=self.ttd_l_deriv, latex=self.ttd_l_func_doc), + 'ttd_min': dc_cp( + min_val=0, num_eq=1, func=self.ttd_min_func, + deriv=self.ttd_min_deriv), 'pr1': dc_cp( min_val=1e-4, max_val=1, num_eq=1, deriv=self.pr_deriv, latex=self.pr_func_doc, @@ -305,21 +317,60 @@ def preprocess(self, num_nw_vars): self.dp2.val_SI = convert_to_SI('p', self.dp2.val, self.inl[1].p.unit) def dp_func(self, dp=None, inconn=None, outconn=None): - # dp is a string: "dp1" or "dp2" + """Calculate residual value of pressure difference function. + + Parameters + ---------- + dp : str + Component parameter to evaluate the dp_func on, e.g. + :code:`dp1`. + + inconn : int + Connection index of inlet. + + outconn : int + Connection index of outlet. + + Returns + ------- + residual : float + Residual value of function. + + .. math:: + + 0 = p_{in} - p_{out} - dp + """ inlet_conn = self.inl[inconn] outlet_conn = self.outl[outconn] dp_value = self.get_attr(dp).val_SI - # 0 = ... - # dp = pressure inlet - pressure outlet return inlet_conn.p.val_SI - outlet_conn.p.val_SI - dp_value def dp_deriv(self, increment_filter, k, dp=None, inconn=None, outconn=None): + r""" + Calculate residual value of pressure difference function. + + Parameters + ---------- + increment_filter : ndarray + Matrix for filtering non-changing variables. + + k : int + Position of equation in Jacobian matrix. + + dp : str + Component parameter to evaluate the dp_func on, e.g. + :code:`dp1`. + + inconn : int + Connection index of inlet. + + outconn : int + Connection index of outlet. + """ inlet_conn = self.inl[inconn] outlet_conn = self.outl[outconn] - if inlet_conn.p.is_var: self.jacobian[k, inlet_conn.p.J_col] = 1 - if outlet_conn.p.is_var: self.jacobian[k, outlet_conn.p.J_col] = -1 @@ -744,6 +795,56 @@ def ttd_l_deriv(self, increment_filter, k): if self.is_variable(c.h, increment_filter): self.jacobian[k, c.h.J_col] = self.numeric_deriv(f, 'h', c) + def ttd_min_func(self): + r""" + Equation for upper terminal temperature difference. + + Returns + ------- + residual : float + Residual value of equation. + + .. math:: + + ttd_{l} = T_{out,1} - T_{in,2} + ttd_{u} = T_{in,1} - T_{out,2} + 0 = \text{min}\left(ttd_{u}, ttd_{l}\right) + + """ + i = self.inl[1] + o = self.outl[0] + T_i2 = i.calc_T() + T_o1 = o.calc_T() + + i = self.inl[0] + o = self.outl[1] + T_i1 = i.calc_T() + T_o2 = o.calc_T() + + ttd_l = T_o1 - T_i2 + ttd_u = T_i1 - T_o2 + + return self.ttd_min.val - min(ttd_l, ttd_u) + + def ttd_min_deriv(self, increment_filter, k): + """ + Calculate partial derivates of minimum terminal temperature function. + + Parameters + ---------- + increment_filter : ndarray + Matrix for filtering non-changing variables. + + k : int + Position of derivatives in Jacobian matrix (k-th equation). + """ + f = self.ttd_min_func + for c in self.inl + self.outl: + if self.is_variable(c.p, increment_filter): + self.jacobian[k, c.p.J_col] = self.numeric_deriv(f, 'p', c) + if self.is_variable(c.h, increment_filter): + self.jacobian[k, c.h.J_col] = self.numeric_deriv(f, 'h', c) + def calc_dh_max_cold(self): r"""Calculate the theoretical maximum enthalpy increase on the cold side @@ -1074,6 +1175,7 @@ def calc_parameters(self): ) self.ttd_u.val = self.inl[0].T.val_SI - self.outl[1].T.val_SI self.ttd_l.val = self.outl[0].T.val_SI - self.inl[1].T.val_SI + self.ttd_min.val = min(self.ttd_u.val, self.ttd_min.val) # pr and zeta for i in range(2): From c2f4d536a44a9c1ce045cda545694e5d73cfb4af Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 4 Dec 2024 13:09:48 +0100 Subject: [PATCH 5/8] Update changelog --- docs/whats_new/v0-7-7.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/whats_new/v0-7-7.rst b/docs/whats_new/v0-7-7.rst index 336d3b138..3a5d4f6d6 100644 --- a/docs/whats_new/v0-7-7.rst +++ b/docs/whats_new/v0-7-7.rst @@ -1,6 +1,14 @@ Under development +++++++++++++++++ +New Features +############ +- The `HeatExchanger` class now has three new attributes, :code:`dp1`, + :code:`dp2` (hot side and cold side pressure drop in network pressure unit) + as well as :code:`ttd_min` for the minimal value of the terminal temperature + diference values + (`PR #581 `__). + Bug Fixes ######### - Only :code:`.json` format files are loaded by the `load_network` method. From 2a67549e94839fb9fd87148b9db95e5f7faf77d0 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 4 Dec 2024 22:20:28 +0100 Subject: [PATCH 6/8] Move dp functions to base component class because they might be reused in other componetns --- src/tespy/components/component.py | 58 ++++++++++++++++++++ src/tespy/components/heat_exchangers/base.py | 58 -------------------- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/tespy/components/component.py b/src/tespy/components/component.py index 516c75d67..f72daae28 100644 --- a/src/tespy/components/component.py +++ b/src/tespy/components/component.py @@ -1230,3 +1230,61 @@ def zeta_deriv(self, increment_filter, k, zeta='', inconn=0, outconn=0): # custom variable zeta if data.is_var: self.jacobian[k, data.J_col] = self.numeric_deriv(f, zeta, None, **kwargs) + + def dp_func(self, dp=None, inconn=None, outconn=None): + """Calculate residual value of pressure difference function. + + Parameters + ---------- + dp : str + Component parameter to evaluate the dp_func on, e.g. + :code:`dp1`. + + inconn : int + Connection index of inlet. + + outconn : int + Connection index of outlet. + + Returns + ------- + residual : float + Residual value of function. + + .. math:: + + 0 = p_{in} - p_{out} - dp + """ + inlet_conn = self.inl[inconn] + outlet_conn = self.outl[outconn] + dp_value = self.get_attr(dp).val_SI + return inlet_conn.p.val_SI - outlet_conn.p.val_SI - dp_value + + def dp_deriv(self, increment_filter, k, dp=None, inconn=None, outconn=None): + r""" + Calculate residual value of pressure difference function. + + Parameters + ---------- + increment_filter : ndarray + Matrix for filtering non-changing variables. + + k : int + Position of equation in Jacobian matrix. + + dp : str + Component parameter to evaluate the dp_func on, e.g. + :code:`dp1`. + + inconn : int + Connection index of inlet. + + outconn : int + Connection index of outlet. + """ + inlet_conn = self.inl[inconn] + outlet_conn = self.outl[outconn] + if inlet_conn.p.is_var: + self.jacobian[k, inlet_conn.p.J_col] = 1 + if outlet_conn.p.is_var: + self.jacobian[k, outlet_conn.p.J_col] = -1 diff --git a/src/tespy/components/heat_exchangers/base.py b/src/tespy/components/heat_exchangers/base.py index fc2f9b32b..579776a2a 100644 --- a/src/tespy/components/heat_exchangers/base.py +++ b/src/tespy/components/heat_exchangers/base.py @@ -316,64 +316,6 @@ def preprocess(self, num_nw_vars): if self.dp2.is_set: self.dp2.val_SI = convert_to_SI('p', self.dp2.val, self.inl[1].p.unit) - def dp_func(self, dp=None, inconn=None, outconn=None): - """Calculate residual value of pressure difference function. - - Parameters - ---------- - dp : str - Component parameter to evaluate the dp_func on, e.g. - :code:`dp1`. - - inconn : int - Connection index of inlet. - - outconn : int - Connection index of outlet. - - Returns - ------- - residual : float - Residual value of function. - - .. math:: - - 0 = p_{in} - p_{out} - dp - """ - inlet_conn = self.inl[inconn] - outlet_conn = self.outl[outconn] - dp_value = self.get_attr(dp).val_SI - return inlet_conn.p.val_SI - outlet_conn.p.val_SI - dp_value - - def dp_deriv(self, increment_filter, k, dp=None, inconn=None, outconn=None): - r""" - Calculate residual value of pressure difference function. - - Parameters - ---------- - increment_filter : ndarray - Matrix for filtering non-changing variables. - - k : int - Position of equation in Jacobian matrix. - - dp : str - Component parameter to evaluate the dp_func on, e.g. - :code:`dp1`. - - inconn : int - Connection index of inlet. - - outconn : int - Connection index of outlet. - """ - inlet_conn = self.inl[inconn] - outlet_conn = self.outl[outconn] - if inlet_conn.p.is_var: - self.jacobian[k, inlet_conn.p.J_col] = 1 - if outlet_conn.p.is_var: - self.jacobian[k, outlet_conn.p.J_col] = -1 - def energy_balance_func(self): r""" Equation for heat exchanger energy balance. From 6632a1494675aa8046920a9179f5f6f534ef4200 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 4 Dec 2024 22:20:56 +0100 Subject: [PATCH 7/8] Tidy up docs and align the heat exchanger classes --- src/tespy/components/heat_exchangers/base.py | 11 +- .../components/heat_exchangers/condenser.py | 132 ++++++++++-------- .../heat_exchangers/desuperheater.py | 14 +- 3 files changed, 89 insertions(+), 68 deletions(-) diff --git a/src/tespy/components/heat_exchangers/base.py b/src/tespy/components/heat_exchangers/base.py index 579776a2a..2d4a6f75a 100644 --- a/src/tespy/components/heat_exchangers/base.py +++ b/src/tespy/components/heat_exchangers/base.py @@ -48,6 +48,7 @@ class HeatExchanger(Component): - :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.kA_char_func` - :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.ttd_u_func` - :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.ttd_l_func` + - :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.ttd_min_func` - :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.eff_cold_func` - :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.eff_hot_func` - :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.eff_max_func` @@ -55,6 +56,8 @@ class HeatExchanger(Component): - cold side :py:meth:`tespy.components.component.Component.pr_func` - hot side :py:meth:`tespy.components.component.Component.zeta_func` - cold side :py:meth:`tespy.components.component.Component.zeta_func` + - hot side :py:meth:`tespy.components.component.Component.dp_func` + - cold side :py:meth:`tespy.components.component.Component.dp_func` Inlets/Outlets @@ -1111,7 +1114,6 @@ def initialise_target(self, c, key): def calc_parameters(self): r"""Postprocessing parameter calculation.""" - # component parameters self.Q.val = self.inl[0].m.val_SI * ( self.outl[0].h.val_SI - self.inl[0].h.val_SI ) @@ -1121,9 +1123,10 @@ def calc_parameters(self): # pr and zeta 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.calc_zeta( + self.get_attr(f'pr{i + 1}').val = ( + self.outl[i].p.val_SI / self.inl[i].p.val_SI + ) + self.get_attr(f'zeta{i + 1}').val = self.calc_zeta( self.inl[i], self.outl[i] ) self.get_attr(f'dp{i + 1}').val_SI = ( diff --git a/src/tespy/components/heat_exchangers/condenser.py b/src/tespy/components/heat_exchangers/condenser.py index 2dc2b2700..1d820eb32 100644 --- a/src/tespy/components/heat_exchangers/condenser.py +++ b/src/tespy/components/heat_exchangers/condenser.py @@ -17,13 +17,11 @@ from tespy.components.component import component_registry from tespy.components.heat_exchangers.base import HeatExchanger -from tespy.tools.data_containers import ComponentCharacteristics as dc_cc -from tespy.tools.data_containers import ComponentProperties as dc_cp -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 dh_mix_dpQ from tespy.tools.fluid_properties import h_mix_pQ +from tespy.tools.helpers import convert_from_SI @component_registry @@ -50,10 +48,16 @@ class Condenser(HeatExchanger): - :py:meth:`tespy.components.heat_exchangers.condenser.Condenser.kA_char_func` - :py:meth:`tespy.components.heat_exchangers.condenser.Condenser.ttd_u_func` - :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.ttd_l_func` + - :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.ttd_min_func` + - :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.eff_cold_func` + - :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.eff_hot_func` + - :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.eff_max_func` - hot side :py:meth:`tespy.components.component.Component.pr_func` - cold side :py:meth:`tespy.components.component.Component.pr_func` - hot side :py:meth:`tespy.components.component.Component.zeta_func` - cold side :py:meth:`tespy.components.component.Component.zeta_func` + - hot side :py:meth:`tespy.components.component.Component.dp_func` + - cold side :py:meth:`tespy.components.component.Component.dp_func` Inlets/Outlets @@ -107,6 +111,12 @@ class Condenser(HeatExchanger): pr2 : float, dict, :code:`"var"` Outlet to inlet pressure ratio at cold side, :math:`pr/1`. + dp1 : float, dict, :code:`"var"` + Inlet to outlet pressure delta at hot side, :math:`dp/\text{Pa}`. + + dp2 : float, dict, :code:`"var"` + Inlet to outlet pressure delta at cold side, :math:`dp/\text{Pa}`. + zeta1 : float, dict, :code:`"var"` Geometry independent friction coefficient at hot side, :math:`\frac{\zeta}{D^4}/\frac{1}{\text{m}^4}`. @@ -122,6 +132,19 @@ class Condenser(HeatExchanger): Upper terminal temperature difference (referring to saturation temprature of condensing fluid) :math:`ttd_\mathrm{u}/\text{K}`. + ttd_min : float, dict + Minumum terminal temperature difference :math:`ttd_\mathrm{min}/\text{K}`. + + eff_cold : float, dict + Cold side heat exchanger effectiveness :math:`eff_\text{cold}/\text{1}`. + + eff_hot : float, dict + Hot side heat exchanger effectiveness :math:`eff_\text{hot}/\text{1}`. + + eff_max : float, dict + Max value of hot and cold side heat exchanger effectiveness values + :math:`eff_\text{max}/\text{1}`. + kA : float, dict Area independent heat transfer coefficient, :math:`kA/\frac{\text{W}}{\text{K}}`. @@ -218,49 +241,13 @@ def component(): return 'condenser' def get_parameters(self): - return { - 'Q': dc_cp( - max_val=0, func=self.energy_balance_hot_func, num_eq=1, - deriv=self.energy_balance_hot_deriv, - latex=self.energy_balance_hot_func_doc), - 'kA': dc_cp( - min_val=0, num_eq=1, func=self.kA_func, latex=self.kA_func_doc, - deriv=self.kA_deriv), - 'td_log': dc_cp(min_val=0, is_result=True), - 'ttd_u': dc_cp( - min_val=0, num_eq=1, func=self.ttd_u_func, - deriv=self.ttd_u_deriv, latex=self.ttd_u_func_doc), - 'ttd_l': dc_cp( - min_val=0, num_eq=1, func=self.ttd_l_func, - deriv=self.ttd_l_deriv, latex=self.ttd_l_func_doc), - 'pr1': dc_cp( - min_val=1e-4, max_val=1, num_eq=1, deriv=self.pr_deriv, - latex=self.pr_func_doc, - func=self.pr_func, func_params={'pr': 'pr1'}), - 'pr2': dc_cp( - min_val=1e-4, max_val=1, num_eq=1, latex=self.pr_func_doc, - deriv=self.pr_deriv, func=self.pr_func, - func_params={'pr': 'pr2', 'inconn': 1, 'outconn': 1}), - 'zeta1': dc_cp( - min_val=0, max_val=1e15, num_eq=1, latex=self.zeta_func_doc, - deriv=self.zeta_deriv, func=self.zeta_func, - func_params={'zeta': 'zeta1'}), - 'zeta2': dc_cp( - min_val=0, max_val=1e15, num_eq=1, latex=self.zeta_func_doc, - deriv=self.zeta_deriv, func=self.zeta_func, - func_params={'zeta': 'zeta2', 'inconn': 1, 'outconn': 1}), - 'kA_char': dc_gcc( - elements=['kA_char1', 'kA_char2'], - num_eq=1, latex=self.kA_char_func_doc, func=self.kA_char_func, - deriv=self.kA_char_deriv), - 'kA_char1': dc_cc(param='m'), - 'kA_char2': dc_cc( - param='m', char_params={ - 'type': 'rel', 'inconn': 1, 'outconn': 1}), + params = super().get_parameters() + params.update({ 'subcooling': dc_simple( val=False, num_eq=1, latex=self.subcooling_func_doc, deriv=self.subcooling_deriv, func=self.subcooling_func) - } + }) + return params def preprocess(self, num_nw_vars): @@ -480,21 +467,25 @@ def ttd_u_func_doc(self, label): def calc_parameters(self): r"""Postprocessing parameter calculation.""" - # component parameters - i1 = self.inl[0] - i2 = self.inl[1] - o1 = self.outl[0] - o2 = self.outl[1] - self.Q.val = i1.m.val_SI * (o1.h.val_SI - i1.h.val_SI) - self.ttd_u.val = i1.calc_T_sat() - o2.T.val_SI - self.ttd_l.val = o1.T.val_SI - i2.T.val_SI + self.Q.val = self.inl[0].m.val_SI * ( + self.outl[0].h.val_SI - self.inl[0].h.val_SI + ) + self.ttd_u.val = self.inl[0].calc_T_sat() - self.outl[1].T.val_SI + self.ttd_l.val = self.outl[0].T.val_SI - self.inl[1].T.val_SI + self.ttd_min.val = min(self.ttd_u.val, self.ttd_min.val) # pr and zeta - for num, (i, o) in enumerate(zip(self.inl, self.outl)): - self.get_attr(f"pr{num + 1}").val = o.p.val_SI / i.p.val_SI - self.get_attr(f"zeta{num + 1}").val = ( - (i.p.val_SI - o.p.val_SI) * math.pi ** 2 - / (4 * i.m.val_SI ** 2 * (i.vol.val_SI + o.vol.val_SI)) + for i in range(2): + self.get_attr(f'pr{i + 1}').val = ( + self.outl[i].p.val_SI / self.inl[i].p.val_SI + ) + self.get_attr(f'zeta{i + 1}').val = self.calc_zeta( + self.inl[i], self.outl[i] + ) + self.get_attr(f'dp{i + 1}').val_SI = ( + self.inl[i].p.val_SI - self.outl[i].p.val_SI) + self.get_attr(f'dp{i + 1}').val = convert_from_SI( + 'p', self.get_attr(f'dp{i + 1}').val_SI, self.inl[i].p.unit ) # kA and logarithmic temperature difference @@ -508,3 +499,32 @@ def calc_parameters(self): / math.log(self.ttd_l.val / self.ttd_u.val) ) self.kA.val = -self.Q.val / self.td_log.val + + # heat exchanger efficiencies + try: + self.eff_hot.val = ( + (self.outl[0].h.val_SI - self.inl[0].h.val_SI) + / self.calc_dh_max_hot() + ) + except ValueError: + self.eff_hot.val = np.nan + msg = ( + "Cannot calculate heat exchanger hot side effectiveness " + "because cold side inlet temperature is out of bounds for hot " + "side fluid." + ) + logger.warning(msg) + try: + self.eff_cold.val = ( + (self.outl[1].h.val_SI - self.inl[1].h.val_SI) + / self.calc_dh_max_cold() + ) + except ValueError: + self.eff_cold.val = np.nan + msg = ( + "Cannot calculate heat exchanger cold side effectiveness " + "because hot side inlet temperature is out of bounds for cold " + "side fluid." + ) + logger.warning(msg) + self.eff_max.val = max(self.eff_hot.val, self.eff_cold.val) diff --git a/src/tespy/components/heat_exchangers/desuperheater.py b/src/tespy/components/heat_exchangers/desuperheater.py index 172adf67d..9f28658ce 100644 --- a/src/tespy/components/heat_exchangers/desuperheater.py +++ b/src/tespy/components/heat_exchangers/desuperheater.py @@ -181,18 +181,16 @@ def component(): return 'desuperheater' def get_mandatory_constraints(self): - return { - 'energy_balance_constraints': { - 'func': self.energy_balance_func, - 'deriv': self.energy_balance_deriv, - 'constant_deriv': False, 'latex': self.energy_balance_func_doc, - 'num_eq': 1}, + constraints = super().get_mandatory_constraints() + constraints.update({ 'saturated_gas_constraints': { 'func': self.saturated_gas_func, 'deriv': self.saturated_gas_deriv, 'constant_deriv': False, 'latex': self.saturated_gas_func_doc, - 'num_eq': 1} - } + 'num_eq': 1 + } + }) + return constraints def saturated_gas_func(self): r""" From c5ca7bc505250e77a1c5b78f04efb52a3d65eb84 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 4 Dec 2024 22:33:17 +0100 Subject: [PATCH 8/8] Fix changelog entries for previous release (0.7.7) and move latest changes to current release --- docs/whats_new.rst | 1 + docs/whats_new/v0-7-7.rst | 12 ++---------- docs/whats_new/v0-7-8.rst | 14 ++++++++++++++ pyproject.toml | 2 +- src/tespy/__init__.py | 2 +- 5 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 docs/whats_new/v0-7-8.rst diff --git a/docs/whats_new.rst b/docs/whats_new.rst index 4c1e6b6aa..ccbad6bc2 100644 --- a/docs/whats_new.rst +++ b/docs/whats_new.rst @@ -3,6 +3,7 @@ What's New Discover notable new features and improvements in each release +.. include:: whats_new/v0-7-8.rst .. include:: whats_new/v0-7-7.rst .. include:: whats_new/v0-7-6-001.rst .. include:: whats_new/v0-7-6.rst diff --git a/docs/whats_new/v0-7-7.rst b/docs/whats_new/v0-7-7.rst index 3a5d4f6d6..37b61e21b 100644 --- a/docs/whats_new/v0-7-7.rst +++ b/docs/whats_new/v0-7-7.rst @@ -1,13 +1,5 @@ -Under development -+++++++++++++++++ - -New Features -############ -- The `HeatExchanger` class now has three new attributes, :code:`dp1`, - :code:`dp2` (hot side and cold side pressure drop in network pressure unit) - as well as :code:`ttd_min` for the minimal value of the terminal temperature - diference values - (`PR #581 `__). +v0.7.7 - Newton's Nature (October, 27, 2024) +++++++++++++++++++++++++++++++++++++++++++++ Bug Fixes ######### diff --git a/docs/whats_new/v0-7-8.rst b/docs/whats_new/v0-7-8.rst new file mode 100644 index 000000000..68dfa0016 --- /dev/null +++ b/docs/whats_new/v0-7-8.rst @@ -0,0 +1,14 @@ +v0.7.8 - Newton's Nature (December, 04, 2024) ++++++++++++++++++++++++++++++++++++++++++++++ + +New Features +############ +- The `HeatExchanger` class now has three new attributes, :code:`dp1`, + :code:`dp2` (hot side and cold side pressure drop in network pressure unit) + as well as :code:`ttd_min` for the minimal value of the terminal temperature + diference values + (`PR #581 `__). + +Contributors +############ +- Francesco Witte (`@fwitte `__) diff --git a/pyproject.toml b/pyproject.toml index a569b394f..3e6448223 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ exclude = ["docs/_build"] [project] name = "tespy" -version = "0.7.7" +version = "0.7.8" description = "Thermal Engineering Systems in Python (TESPy)" readme = "README.rst" authors = [ diff --git a/src/tespy/__init__.py b/src/tespy/__init__.py index bdbb3cdf0..1dc7a6a3c 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.7 - Newton\'s Nature' +__version__ = '0.7.8 - Newton\'s Nature' # tespy data and connections import from . import connections # noqa: F401