Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
fwitte committed Jul 31, 2024
2 parents 8f2c5f3 + 1f568d8 commit 6a3fcb3
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 24 deletions.
4 changes: 2 additions & 2 deletions docs/modules/fluid_properties.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,14 @@ to use the `iapws` back end:
>>> c2.set_attr(p=1e4)
>>> nwk.solve("design")
>>> round(c2.x.val, 3)
>>> float(round(c2.x.val, 3))
0.99
>>> tu.set_attr(eta_s=None)
>>> c2.set_attr(x=1)
>>> nwk.solve("design")
>>> round(tu.eta_s.val, 3)
>>> float(round(tu.eta_s.val, 3))
0.841
Expand Down
1 change: 1 addition & 0 deletions docs/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ What's New

Discover notable new features and improvements in each release

.. include:: whats_new/v0-7-6.rst
.. include:: whats_new/v0-7-5.rst
.. include:: whats_new/v0-7-4.rst
.. include:: whats_new/v0-7-3.rst
Expand Down
29 changes: 29 additions & 0 deletions docs/whats_new/v0-7-6.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
v0.7.6 - Newton's Nature (July, 31, 2024)
+++++++++++++++++++++++++++++++++++++++++

New Features
############
- Implement new equations for the heat exchanger, i.e. effectiveness parameter.
The parameter can be specified for the hot side `eff_hot` or the cold side
`eff_cold` of the heat exchanger. Additionally, it is possible to specify the
maximum value of both with `eff_max` if it is unknown, which one of them will
be the larger one (`PR #529 <https://github.com/oemof/tespy/pull/529>`__).

Bug Fixes
#########
- The enthalpy numerical precision critereon for mixtures is relaxed: Mixture
fluid property results (temperature, volume, entropy, ...) are only
invalidated, if both absolute and relative precision is not acquired.
Previously, the enthalpy of the mixture, calculated with the temperature
calculated based on the enthalpy determined by the solver had to be within
:code:`1e-3` of the enthalpy determined by the solver
(`PR #529 <https://github.com/oemof/tespy/pull/529>`__).

Bug Fixes
#########
- Fix the logging text for component paramter value violation in the
postprocessing (`PR #529 <https://github.com/oemof/tespy/pull/529>`__).

Contributors
############
- Francesco Witte (`@fwitte <https://github.com/fwitte>`__)
6 changes: 3 additions & 3 deletions src/tespy/components/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -817,18 +817,18 @@ def check_parameter_bounds(self):
for p in self.parameters.keys():
data = self.get_attr(p)
if isinstance(data, dc_cp):
if data.val > data.max_val + ERR :
if data.val > data.max_val + ERR:
msg = (
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 :
elif data.val < data.min_val - ERR:
msg = (
f"Invalid value for {p}: {p} = {data.val} below "
f"minimum value ({data.max_val}) at component "
f"minimum value ({data.min_val}) at component "
f"{self.label}."
)
logger.warning(msg)
Expand Down
227 changes: 222 additions & 5 deletions src/tespy/components/heat_exchangers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ class HeatExchanger(Component):
**Mandatory Equations**
- :py:meth:`tespy.components.component.Component.fluid_func`
- :py:meth:`tespy.components.component.Component.mass_flow_func`
- :py:meth:`tespy.components.heat_exchangers.base.HeatExchanger.energy_balance_func`
**Optional Equations**
Expand All @@ -47,6 +45,9 @@ 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.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`
Expand Down Expand Up @@ -118,6 +119,16 @@ class HeatExchanger(Component):
ttd_u : float, dict
Upper terminal temperature difference :math:`ttd_\mathrm{u}/\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}}`.
Expand Down Expand Up @@ -239,8 +250,21 @@ def get_parameters(self):
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})
param='m',
char_params={'type': 'rel', 'inconn': 1, 'outconn': 1}
),
'eff_cold': dc_cp(
min_val=0, max_val=1, num_eq=1, func=self.eff_cold_func,
deriv=self.eff_cold_deriv
),
'eff_hot': dc_cp(
min_val=0, max_val=1, num_eq=1, func=self.eff_hot_func,
deriv=self.eff_hot_deriv
),
'eff_max': dc_cp(
min_val=0, max_val=1, num_eq=1, func=self.eff_max_func,
deriv=self.eff_max_deriv
)
}

def get_mandatory_constraints(self):
Expand Down Expand Up @@ -681,6 +705,186 @@ 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 calc_dh_max_cold(self):
r"""Calculate the theoretical maximum enthalpy increase on the cold side
Returns
-------
float
Maxmium cold side enthalpy increase.
.. math::
h\left(p_{out,2}, T_{in,1}\right) - h_{in,2}
"""
o2 = self.outl[1]
T_in_hot = self.inl[0].calc_T()
h_at_T_in_hot = h_mix_pT(
o2.p.val_SI, T_in_hot, o2.fluid_data, o2.mixing_rule
)
return h_at_T_in_hot - self.inl[1].h.val_SI

def eff_cold_func(self):
r"""Equation for cold side heat exchanger effectiveness.
Returns
-------
residual : float
Residual value of equation.
.. math::
0 = \text{eff}_\text{cold} \cdot
\left(h\left(p_{out,2}, T_{in,1} \right) - h_{in,2}\right)
- \left( h_{out,2} - h_{in,2} \right)
"""
return (
self.eff_cold.val * self.calc_dh_max_cold()
- (self.outl[1].h.val_SI - self.inl[1].h.val_SI)
)

def eff_cold_deriv(self, increment_filter, k):
"""
Calculate partial derivates of hot side effectiveness function.
Parameters
----------
increment_filter : ndarray
Matrix for filtering non-changing variables.
k : int
Position of derivatives in Jacobian matrix (k-th equation).
"""
f = self.eff_cold_func

i1 = self.inl[0]
i2 = self.inl[1]
o2 = self.outl[1]

for c in [i1, o2]:
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)

if self.is_variable(i2.h):
self.jacobian[k, i2.h.J_col] = 1 - self.eta_cold.val

def calc_dh_max_hot(self):
r"""Calculate the theoretical maximum enthalpy decrease on the hot side
Returns
-------
float
Maxmium hot side enthalpy decrease.
.. math::
h\left(p_{out,1}, T_{in,2}\right) - h_{in,1}
"""
o1 = self.outl[0]
T_in_cold = self.inl[1].calc_T()
h_at_T_in_cold = h_mix_pT(
o1.p.val_SI, T_in_cold, o1.fluid_data, o1.mixing_rule
)
return h_at_T_in_cold - self.inl[0].h.val_SI

def eff_hot_func(self):
r"""Equation for hot side heat exchanger effectiveness.
Returns
-------
residual : float
Residual value of equation.
.. math::
0 = \text{eff}_\text{hot} \cdot
\left(h\left(p_{out,1}, T_{in,2}\right) - h_{in,1}\right)
- \left( h_{out,1} - h_{in,1}\right)
"""
return (
self.eff_hot.val * self.calc_dh_max_hot()
- (self.outl[0].h.val_SI - self.inl[0].h.val_SI)
)

def eff_hot_deriv(self, increment_filter, k):
"""
Calculate partial derivates of hot side effectiveness function.
Parameters
----------
increment_filter : ndarray
Matrix for filtering non-changing variables.
k : int
Position of derivatives in Jacobian matrix (k-th equation).
"""
f = self.eff_hot_func

i1 = self.inl[0]
o1 = self.outl[0]
i2 = self.inl[1]

if self.is_variable(i1.h):
self.jacobian[k, i1.h.J_col] = 1 - self.eta_hot.val

for c in [o1, i2]:
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 eff_max_func(self):
r"""Equation for maximum heat exchanger effectiveness.
.. note::
This functions works on what is larger: hot side or cold side
effectiveness. It may cause numerical issues, if applied, when one
of both sides' effectiveness is already predetermined, e.g. by
temperature specifications.
Returns
-------
residual : float
Residual value of equation.
.. math::
0 = \text{eff}_\text{max} - \text{max}
\left(\text{eff}_\text{hot},\text{eff}_\text{cold}\right)
"""
eff_hot = (
(self.outl[0].h.val_SI - self.inl[0].h.val_SI)
/ self.calc_dh_max_hot()
)
eff_cold = (
(self.outl[1].h.val_SI - self.inl[1].h.val_SI)
/ self.calc_dh_max_cold()
)
return self.eff_max.val - max(eff_hot, eff_cold)

def eff_max_deriv(self, increment_filter, k):
"""
Calculate partial derivates of max effectiveness function.
Parameters
----------
increment_filter : ndarray
Matrix for filtering non-changing variables.
k : int
Position of derivatives in Jacobian matrix (k-th equation).
"""
f = self.eff_max_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 bus_func(self, bus):
r"""
Calculate the value of the bus function.
Expand Down Expand Up @@ -827,7 +1031,8 @@ 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)
self.outl[0].h.val_SI - self.inl[0].h.val_SI
)
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

Expand All @@ -851,6 +1056,18 @@ def calc_parameters(self):
)
self.kA.val = -self.Q.val / self.td_log.val

# heat exchanger efficiencies
self.eff_hot.val = (
(self.outl[0].h.val_SI - self.inl[0].h.val_SI)
/ self.calc_dh_max_hot()
)

self.eff_cold.val = (
(self.outl[1].h.val_SI - self.inl[1].h.val_SI)
/ self.calc_dh_max_cold()
)
self.eff_max.val = max(self.eff_hot.val, self.eff_cold.val)

def entropy_balance(self):
r"""
Calculate entropy balance of a heat exchanger.
Expand Down
5 changes: 3 additions & 2 deletions src/tespy/components/turbomachinery/compressor.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,13 +576,14 @@ def convergence_check(self):
i, o = self.inl, self.outl

if o[0].p.is_var and o[0].p.val_SI < i[0].p.val_SI:
o[0].p.val_SI = o[0].p.val_SI * 1.1
o[0].p.val_SI = i[0].p.val_SI * 1.1

if o[0].h.is_var and o[0].h.val_SI < i[0].h.val_SI:
o[0].h.val_SI = o[0].h.val_SI * 1.1
o[0].h.val_SI = i[0].h.val_SI * 1.1

if i[0].p.is_var and o[0].p.val_SI < i[0].p.val_SI:
i[0].p.val_SI = o[0].p.val_SI * 0.9

if i[0].h.is_var and o[0].h.val_SI < i[0].h.val_SI:
i[0].h.val_SI = o[0].h.val_SI * 0.9

Expand Down
2 changes: 1 addition & 1 deletion src/tespy/connections/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,7 @@ def calc_results(self):
_converged = True
if number_fluids > 1:
h_from_T = h_mix_pT(self.p.val_SI, self.T.val_SI, self.fluid_data, self.mixing_rule)
if abs(h_from_T - self.h.val_SI) > ERR ** .5:
if abs(h_from_T - self.h.val_SI) > ERR ** .5 and abs((h_from_T - self.h.val_SI) / self.h.val_SI) > ERR ** .5:
self.T.val_SI = np.nan
self.vol.val_SI = np.nan
self.v.val_SI = np.nan
Expand Down
Loading

0 comments on commit 6a3fcb3

Please sign in to comment.