From 421df3e61f82294a7f1db38d003ed82935b5d28d Mon Sep 17 00:00:00 2001 From: tlmerbecks Date: Fri, 24 Jan 2025 21:54:30 +0100 Subject: [PATCH 01/18] adding a steam turbine (with wet expansion) component --- .../components/turbomachinery/turbine.py | 222 ++++++++++++++++++ 1 file changed, 222 insertions(+) diff --git a/src/tespy/components/turbomachinery/turbine.py b/src/tespy/components/turbomachinery/turbine.py index ff1b8c494..052dd4e9d 100644 --- a/src/tespy/components/turbomachinery/turbine.py +++ b/src/tespy/components/turbomachinery/turbine.py @@ -11,6 +11,8 @@ """ import numpy as np +from scipy.optimize import brentq + from tespy.components.component import component_registry from tespy.components.turbomachinery.base import Turbomachine @@ -20,6 +22,7 @@ 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 isentropic +from tespy.tools.fluid_properties import h_mix_pQ @component_registry @@ -520,6 +523,8 @@ def calc_parameters(self): ) ) + self.pr.val = self.outl[0].p.val_SI / self.inl[0].p.val_SI + def exergy_balance(self, T0): r""" Calculate exergy balance of a turbine. @@ -576,3 +581,220 @@ def exergy_balance(self, T0): self.E_bus = {"chemical": 0, "physical": 0, "massless": -self.P.val} self.E_D = self.E_F - self.E_P self.epsilon = self._calc_epsilon() + +@component_registry +class SteamTurbine(Turbine): + r""" + Class for steam turbines with wet expansion. + + **Mandatory Equations** + + - :py:meth:`tespy.components.component.Component.fluid_func` + - :py:meth:`tespy.components.component.Component.mass_flow_func` + + **Optional Equations** + + - :py:meth:`tespy.components.component.Component.pr_func` + - :py:meth:`tespy.components.turbomachinery.base.Turbomachine.energy_balance_func` + - :py:meth:`tespy.components.turbomachinery.turbine.Turbine.eta_dry_s_func` + - :py:meth:`tespy.components.turbomachinery.turbine.Turbine.eta_s_func` + - :py:meth:`tespy.components.turbomachinery.turbine.Turbine.eta_s_char_func` + - :py:meth:`tespy.components.turbomachinery.turbine.Turbine.cone_func` + + Inlets/Outlets + + - in1 + - out1 + + Image + + .. image:: /api/_images/Turbine.svg + :alt: flowsheet of the turbine + :align: center + :class: only-light + + .. image:: /api/_images/Turbine_darkmode.svg + :alt: flowsheet of the turbine + :align: center + :class: only-dark + + Parameters + ---------- + label : str + The label of the component. + + design : list + List containing design parameters (stated as String). + + offdesign : list + List containing offdesign parameters (stated as String). + + design_path : str + Path to the components design case. + + local_offdesign : boolean + Treat this component in offdesign mode in a design calculation. + + local_design : boolean + Treat this component in design mode in an offdesign calculation. + + char_warnings : boolean + Ignore warnings on default characteristics usage for this component. + + printout : boolean + Include this component in the network's results printout. + + P : float, dict + Power, :math:`P/\text{W}` + + eta_s : float, dict + Isentropic efficiency, :math:`\eta_s/1` + + pr : float, dict, :code:`"var"` + Outlet to inlet pressure ratio, :math:`pr/1` + + eta_s_char : tespy.tools.characteristics.CharLine, dict + Characteristic curve for isentropic efficiency, provide CharLine as + function :code:`func`. + + cone : dict + Apply Stodola's cone law (works in offdesign only). + + Example + ------- + A steam turbine expands 10 kg/s of superheated steam at 550 °C and 110 bar + to 0,5 bar at the outlet. For example, it is possible to calulate the power + output and vapour content at the outlet for a given isentropic efficiency. + + >>> from tespy.components import Sink, Source, Turbine + >>> from tespy.connections import Connection + >>> from tespy.networks import Network + >>> from tespy.tools import ComponentCharacteristics as dc_cc + >>> import shutil + >>> nw = Network(p_unit='bar', T_unit='C', h_unit='kJ / kg', iterinfo=False) + >>> si = Sink('sink') + >>> so = Source('source') + >>> t = Turbine('turbine') + >>> t.component() + 'turbine' + >>> inc = Connection(so, 'out1', t, 'in1') + >>> outg = Connection(t, 'out1', si, 'in1') + >>> nw.add_conns(inc, outg) + + In design conditions the isentropic efficiency is specified. For offdesign + a characteristic function will be applied, together with Stodola's cone + law coupling the turbine mass flow to inlet pressure. + + >>> t.set_attr(eta_s=0.9, design=['eta_s'], + ... offdesign=['eta_s_char', 'cone']) + >>> inc.set_attr(fluid={'water': 1}, m=10, T=550, p=110, design=['p']) + >>> outg.set_attr(p=0.5) + >>> nw.solve('design') + >>> nw.save('tmp') + >>> round(t.P.val, 0) + -10452574.0 + >>> round(outg.x.val, 3) + 0.914 + >>> inc.set_attr(m=8) + >>> nw.solve('offdesign', design_path='tmp') + >>> round(t.eta_s.val, 3) + 0.898 + >>> round(inc.p.val, 1) + 88.6 + >>> shutil.rmtree('./tmp', ignore_errors=True) + """ + + + @staticmethod + def component(): + return 'steam turbine' + + def get_parameters(self): + + params = super().get_parameters() + + params["alpha"] = dc_cp( + min_val=0.4, max_val=2.5, num_eq=0) + params["eta_dry_s"] = dc_cp( + min_val=0, max_val=1, num_eq=1, + func=self.eta_dry_s_func, + deriv=self.eta_dry_s_deriv + ) + + return params + + def eta_dry_s_func(self): + + inl = self.inl[0] + outl = self.outl[0] + + state = inl.calc_phase() + if state == "tp": # two-phase or saturated vapour + + ym = 1 - (inl.calc_x() + outl.calc_x()) / 2 # average wetness + self.eta_s.val = self.eta_dry.val * (1 - self.alpha.val * ym) + + return self.eta_s_func() + + else: # superheated vapour + dp = inl.p.val_SI - outl.p.val_SI + + # compute the pressure and enthalpy at which the expansion enters the vapour dome + def find_sat(frac): + + psat = inl.p.val_SI - frac * dp + + # calculate enthalpy under dry expansion to psat + hout_isen = isentropic( + inl.p.val_SI, + inl.h.val_SI, + psat, + inl.fluid_data, + inl.mixing_rule, + T0=inl.T.val_SI + ) + hout = inl.h.val_SI - self.eta_dry_s.val * (inl.h.val_SI - hout_isen) + + # calculate enthalpy of saturated vapour at psat + hsat = h_mix_pQ(psat, 1, inl.fluid_data) + + return hout - hsat + frac = brentq(find_sat, 1, 0) + psat = inl.p.val_SI - frac * dp + hsat = h_mix_pQ(psat, 1, inl.fluid_data) + + # calculate the isentropic efficiency for wet expansion + ym = 1 - (1.0 + outl.calc_x()) / 2 # average wetness + eta_s = self.eta_dry_s.val * (1 - self.alpha.val * ym) + + # calculate the final outlet enthalpy + hout_isen = isentropic( + psat, + hsat, + outl.p.val_SI, + inl.fluid_data, + inl.mixing_rule, + T0=inl.T.val_SI + ) + hout = hsat - eta_s * (hsat- hout_isen) + + # calculate the difference in enthalpy + dh_isen = hout - inl.h.val_SI + dh = outl.h.val_SI - inl.h.val_SI + + # return residual + return -dh + dh_isen + + def eta_dry_s_deriv(self, increment_filter, k): + + f = self.eta_dry_s_func + i = self.inl[0] + o = self.outl[0] + if self.is_variable(i.p, increment_filter): + self.jacobian[k, i.p.J_col] = self.numeric_deriv(f, "p", i) + if self.is_variable(o.p, increment_filter): + self.jacobian[k, o.p.J_col] = self.numeric_deriv(f, "p", o) + if self.is_variable(i.h, increment_filter): + self.jacobian[k, i.h.J_col] = self.numeric_deriv(f, "h", i) + if self.is_variable(o.h, increment_filter): + self.jacobian[k, o.h.J_col] = self.numeric_deriv(f, "h", o) From 7646dab2fbe64797b4f1950740b039556bfa61a3 Mon Sep 17 00:00:00 2001 From: tlmerbecks Date: Fri, 24 Jan 2025 21:57:06 +0100 Subject: [PATCH 02/18] forcing CoolProp to return a meaningful vapour quality for subcooled or superheated states --- src/tespy/tools/fluid_properties/wrappers.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/tespy/tools/fluid_properties/wrappers.py b/src/tespy/tools/fluid_properties/wrappers.py index 90e16b411..fd1d7274e 100644 --- a/src/tespy/tools/fluid_properties/wrappers.py +++ b/src/tespy/tools/fluid_properties/wrappers.py @@ -231,16 +231,15 @@ def p_sat(self, T): def Q_ph(self, p, h): p = self._make_p_subcritical(p) self.AS.update(CP.HmassP_INPUTS, h, p) - return self.AS.Q() - - # if self.AS.phase() == CP.iphase_twophase: - # return self.AS.Q() - # elif self.AS.phase() == CP.iphase_liquid: - # return 0 - # elif self.AS.phase() == CP.iphase_gas: - # return 1 - # else: # all other phases - though this should be unreachable as p is sub-critical - # return -1 + + if self.AS.phase() == CP.iphase_twophase: + return self.AS.Q() + elif self.AS.phase() == CP.iphase_liquid: + return 0 + elif self.AS.phase() == CP.iphase_gas: + return 1 + else: # all other phases - though this should be unreachable as p is sub-critical + return -1 def phase_ph(self, p, h): p = self._make_p_subcritical(p) From cf15c321a88b295b6eba61bc21b68d75d807081f Mon Sep 17 00:00:00 2001 From: tlmerbecks Date: Fri, 24 Jan 2025 21:57:45 +0100 Subject: [PATCH 03/18] adding the steam turbine component to the component module init --- src/tespy/components/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tespy/components/__init__.py b/src/tespy/components/__init__.py index d459df9af..3356130a3 100644 --- a/src/tespy/components/__init__.py +++ b/src/tespy/components/__init__.py @@ -26,4 +26,5 @@ from .subsystem import Subsystem # noqa: F401 from .turbomachinery.compressor import Compressor # noqa: F401 from .turbomachinery.pump import Pump # noqa: F401 -from .turbomachinery.turbine import Turbine # noqa: F401 +from .turbomachinery.turbine import Turbine # noqa: F401 +from .turbomachinery.turbine import SteamTurbine From 39f8be2f820994ebb5188da8a858036725358b84 Mon Sep 17 00:00:00 2001 From: tlmerbecks Date: Sun, 26 Jan 2025 11:02:01 +0100 Subject: [PATCH 04/18] adding the steam turbine component to the component module init --- src/tespy/components/__init__.py | 2 +- .../turbomachinery/steam_turbine.py | 237 ++++++++++++++++++ .../components/turbomachinery/turbine.py | 222 +--------------- 3 files changed, 242 insertions(+), 219 deletions(-) create mode 100644 src/tespy/components/turbomachinery/steam_turbine.py diff --git a/src/tespy/components/__init__.py b/src/tespy/components/__init__.py index 3356130a3..2912ccbd4 100644 --- a/src/tespy/components/__init__.py +++ b/src/tespy/components/__init__.py @@ -26,5 +26,5 @@ from .subsystem import Subsystem # noqa: F401 from .turbomachinery.compressor import Compressor # noqa: F401 from .turbomachinery.pump import Pump # noqa: F401 +from .turbomachinery.steam_turbine import SteamTurbine from .turbomachinery.turbine import Turbine # noqa: F401 -from .turbomachinery.turbine import SteamTurbine diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py new file mode 100644 index 000000000..b3ca1f941 --- /dev/null +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -0,0 +1,237 @@ +# -*- coding: utf-8 + +"""Module of class Turbine. + + +This file is part of project TESPy (github.com/oemof/tespy). It's copyrighted +by the contributors recorded in the version control history of the file, +available from its original location tespy/components/turbomachinery/turbine.py + +SPDX-License-Identifier: MIT +""" + +from scipy.optimize import brentq + + +from tespy.components.component import component_registry +from tespy.components.turbomachinery.turbine import Turbine +from tespy.tools.data_containers import ComponentProperties as dc_cp +from tespy.tools.fluid_properties import isentropic +from tespy.tools.fluid_properties import h_mix_pQ + + +@component_registry +class SteamTurbine(Turbine): + r""" + Class for steam turbines with wet expansion. + + **Mandatory Equations** + + - :py:meth:`tespy.components.component.Component.fluid_func` + - :py:meth:`tespy.components.component.Component.mass_flow_func` + + **Optional Equations** + + - :py:meth:`tespy.components.component.Component.pr_func` + - :py:meth:`tespy.components.turbomachinery.base.Turbomachine.energy_balance_func` + - :py:meth:`tespy.components.turbomachinery.turbine.Turbine.eta_dry_s_func` + - :py:meth:`tespy.components.turbomachinery.turbine.Turbine.eta_s_func` + - :py:meth:`tespy.components.turbomachinery.turbine.Turbine.eta_s_char_func` + - :py:meth:`tespy.components.turbomachinery.turbine.Turbine.cone_func` + + Inlets/Outlets + + - in1 + - out1 + + Image + + .. image:: /api/_images/Turbine.svg + :alt: flowsheet of the turbine + :align: center + :class: only-light + + .. image:: /api/_images/Turbine_darkmode.svg + :alt: flowsheet of the turbine + :align: center + :class: only-dark + + Parameters + ---------- + label : str + The label of the component. + + design : list + List containing design parameters (stated as String). + + offdesign : list + List containing offdesign parameters (stated as String). + + design_path : str + Path to the components design case. + + local_offdesign : boolean + Treat this component in offdesign mode in a design calculation. + + local_design : boolean + Treat this component in design mode in an offdesign calculation. + + char_warnings : boolean + Ignore warnings on default characteristics usage for this component. + + printout : boolean + Include this component in the network's results printout. + + P : float, dict + Power, :math:`P/\text{W}` + + eta_s : float, dict + Isentropic efficiency, :math:`\eta_s/1` + + eta_dry_s : float, dict + Dry isentropic efficiency, :math:`\eta_s/1` + + pr : float, dict, :code:`"var"` + Outlet to inlet pressure ratio, :math:`pr/1` + + eta_s_char : tespy.tools.characteristics.CharLine, dict + Characteristic curve for isentropic efficiency, provide CharLine as + function :code:`func`. + + cone : dict + Apply Stodola's cone law (works in offdesign only). + + Example + ------- + A steam turbine expands 10 kg/s of superheated steam at 550 °C and 110 bar + to 0,5 bar at the outlet. For example, it is possible to calulate the power + output and vapour content at the outlet for a given isentropic efficiency. + + >>> from tespy.components import Sink, Source, Turbine + >>> from tespy.connections import Connection + >>> from tespy.networks import Network + >>> from tespy.tools import ComponentCharacteristics as dc_cc + >>> import shutil + >>> nw = Network(p_unit='bar', T_unit='C', h_unit='kJ / kg', iterinfo=False) + >>> si = Sink('sink') + >>> so = Source('source') + >>> st = SteamTurbine('steam turbine') + >>> st.component() + 'turbine' + >>> inc = Connection(so, 'out1', st, 'in1') + >>> outg = Connection(st, 'out1', si, 'in1') + >>> nw.add_conns(inc, outg) + + In design conditions the isentropic efficiency is specified. + >>> st.set_attr(eta_s=0.9) + >>> inc.set_attr(fluid={'water': 1}, m=10, T=250, p=20) + >>> outg.set_attr(p=0.1) + >>> nw.solve('design') + >>> round(st.P.val, 0) + -7471296.0 + >>> round(outg.x.val, 3) + 0.821 + + To capture the effect of liquid drop-out on the isentropic efficiency, the dry turbine efficiency is specified + >>> st.set_attr(eta_s=None) + >>> st.set_attr(eta_dry_s=0.9) + >>> nw.solve('design') + >>> round(st.P.val, 0) + -7009682.0 + >>> round(outg.x.val, 3) + 0.840 + """ + + + @staticmethod + def component(): + return 'steam turbine' + + def get_parameters(self): + + params = super().get_parameters() + + params["alpha"] = dc_cp( + min_val=0.4, max_val=2.5, num_eq=0) + params["eta_dry_s"] = dc_cp( + min_val=0, max_val=1, num_eq=1, + func=self.eta_dry_s_func, + deriv=self.eta_dry_s_deriv + ) + + return params + + def eta_dry_s_func(self): + + inl = self.inl[0] + outl = self.outl[0] + + state = inl.calc_phase() + if state == "tp": # two-phase or saturated vapour + + ym = 1 - (inl.calc_x() + outl.calc_x()) / 2 # average wetness + self.eta_s.val = self.eta_dry.val * (1 - self.alpha.val * ym) + + return self.eta_s_func() + + else: # superheated vapour + dp = inl.p.val_SI - outl.p.val_SI + + # compute the pressure and enthalpy at which the expansion enters the vapour dome + def find_sat(frac): + psat = inl.p.val_SI - frac * dp + + # calculate enthalpy under dry expansion to psat + hout_isen = isentropic( + inl.p.val_SI, + inl.h.val_SI, + psat, + inl.fluid_data, + inl.mixing_rule, + T0=inl.T.val_SI + ) + hout = inl.h.val_SI - self.eta_dry_s.val * (inl.h.val_SI - hout_isen) + + # calculate enthalpy of saturated vapour at psat + hsat = h_mix_pQ(psat, 1, inl.fluid_data) + + return hout - hsat + frac = brentq(find_sat, 1, 0) + psat = inl.p.val_SI - frac * dp + hsat = h_mix_pQ(psat, 1, inl.fluid_data) + + # calculate the isentropic efficiency for wet expansion + ym = 1 - (1.0 + outl.calc_x()) / 2 # average wetness + eta_s = self.eta_dry_s.val * (1 - self.alpha.val * ym) + + # calculate the final outlet enthalpy + hout_isen = isentropic( + psat, + hsat, + outl.p.val_SI, + inl.fluid_data, + inl.mixing_rule, + T0=inl.T.val_SI + ) + hout = hsat - eta_s * (hsat- hout_isen) + + # calculate the difference in enthalpy + dh_isen = hout - inl.h.val_SI + dh = outl.h.val_SI - inl.h.val_SI + + # return residual + return -dh + dh_isen + + def eta_dry_s_deriv(self, increment_filter, k): + + f = self.eta_dry_s_func + i = self.inl[0] + o = self.outl[0] + if self.is_variable(i.p, increment_filter): + self.jacobian[k, i.p.J_col] = self.numeric_deriv(f, "p", i) + if self.is_variable(o.p, increment_filter): + self.jacobian[k, o.p.J_col] = self.numeric_deriv(f, "p", o) + if self.is_variable(i.h, increment_filter): + self.jacobian[k, i.h.J_col] = self.numeric_deriv(f, "h", i) + if self.is_variable(o.h, increment_filter): + self.jacobian[k, o.h.J_col] = self.numeric_deriv(f, "h", o) diff --git a/src/tespy/components/turbomachinery/turbine.py b/src/tespy/components/turbomachinery/turbine.py index 052dd4e9d..4e5f950f9 100644 --- a/src/tespy/components/turbomachinery/turbine.py +++ b/src/tespy/components/turbomachinery/turbine.py @@ -11,7 +11,6 @@ """ import numpy as np -from scipy.optimize import brentq from tespy.components.component import component_registry @@ -22,7 +21,6 @@ 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 isentropic -from tespy.tools.fluid_properties import h_mix_pQ @component_registry @@ -30,6 +28,10 @@ class Turbine(Turbomachine): r""" Class for gas or steam turbines. + The component Turbine is the parent class for the components: + + - :py:class:`tespy.components.turbomachinery.steam_turbine.SteamTurbine` + **Mandatory Equations** - :py:meth:`tespy.components.component.Component.fluid_func` @@ -582,219 +584,3 @@ def exergy_balance(self, T0): self.E_D = self.E_F - self.E_P self.epsilon = self._calc_epsilon() -@component_registry -class SteamTurbine(Turbine): - r""" - Class for steam turbines with wet expansion. - - **Mandatory Equations** - - - :py:meth:`tespy.components.component.Component.fluid_func` - - :py:meth:`tespy.components.component.Component.mass_flow_func` - - **Optional Equations** - - - :py:meth:`tespy.components.component.Component.pr_func` - - :py:meth:`tespy.components.turbomachinery.base.Turbomachine.energy_balance_func` - - :py:meth:`tespy.components.turbomachinery.turbine.Turbine.eta_dry_s_func` - - :py:meth:`tespy.components.turbomachinery.turbine.Turbine.eta_s_func` - - :py:meth:`tespy.components.turbomachinery.turbine.Turbine.eta_s_char_func` - - :py:meth:`tespy.components.turbomachinery.turbine.Turbine.cone_func` - - Inlets/Outlets - - - in1 - - out1 - - Image - - .. image:: /api/_images/Turbine.svg - :alt: flowsheet of the turbine - :align: center - :class: only-light - - .. image:: /api/_images/Turbine_darkmode.svg - :alt: flowsheet of the turbine - :align: center - :class: only-dark - - Parameters - ---------- - label : str - The label of the component. - - design : list - List containing design parameters (stated as String). - - offdesign : list - List containing offdesign parameters (stated as String). - - design_path : str - Path to the components design case. - - local_offdesign : boolean - Treat this component in offdesign mode in a design calculation. - - local_design : boolean - Treat this component in design mode in an offdesign calculation. - - char_warnings : boolean - Ignore warnings on default characteristics usage for this component. - - printout : boolean - Include this component in the network's results printout. - - P : float, dict - Power, :math:`P/\text{W}` - - eta_s : float, dict - Isentropic efficiency, :math:`\eta_s/1` - - pr : float, dict, :code:`"var"` - Outlet to inlet pressure ratio, :math:`pr/1` - - eta_s_char : tespy.tools.characteristics.CharLine, dict - Characteristic curve for isentropic efficiency, provide CharLine as - function :code:`func`. - - cone : dict - Apply Stodola's cone law (works in offdesign only). - - Example - ------- - A steam turbine expands 10 kg/s of superheated steam at 550 °C and 110 bar - to 0,5 bar at the outlet. For example, it is possible to calulate the power - output and vapour content at the outlet for a given isentropic efficiency. - - >>> from tespy.components import Sink, Source, Turbine - >>> from tespy.connections import Connection - >>> from tespy.networks import Network - >>> from tespy.tools import ComponentCharacteristics as dc_cc - >>> import shutil - >>> nw = Network(p_unit='bar', T_unit='C', h_unit='kJ / kg', iterinfo=False) - >>> si = Sink('sink') - >>> so = Source('source') - >>> t = Turbine('turbine') - >>> t.component() - 'turbine' - >>> inc = Connection(so, 'out1', t, 'in1') - >>> outg = Connection(t, 'out1', si, 'in1') - >>> nw.add_conns(inc, outg) - - In design conditions the isentropic efficiency is specified. For offdesign - a characteristic function will be applied, together with Stodola's cone - law coupling the turbine mass flow to inlet pressure. - - >>> t.set_attr(eta_s=0.9, design=['eta_s'], - ... offdesign=['eta_s_char', 'cone']) - >>> inc.set_attr(fluid={'water': 1}, m=10, T=550, p=110, design=['p']) - >>> outg.set_attr(p=0.5) - >>> nw.solve('design') - >>> nw.save('tmp') - >>> round(t.P.val, 0) - -10452574.0 - >>> round(outg.x.val, 3) - 0.914 - >>> inc.set_attr(m=8) - >>> nw.solve('offdesign', design_path='tmp') - >>> round(t.eta_s.val, 3) - 0.898 - >>> round(inc.p.val, 1) - 88.6 - >>> shutil.rmtree('./tmp', ignore_errors=True) - """ - - - @staticmethod - def component(): - return 'steam turbine' - - def get_parameters(self): - - params = super().get_parameters() - - params["alpha"] = dc_cp( - min_val=0.4, max_val=2.5, num_eq=0) - params["eta_dry_s"] = dc_cp( - min_val=0, max_val=1, num_eq=1, - func=self.eta_dry_s_func, - deriv=self.eta_dry_s_deriv - ) - - return params - - def eta_dry_s_func(self): - - inl = self.inl[0] - outl = self.outl[0] - - state = inl.calc_phase() - if state == "tp": # two-phase or saturated vapour - - ym = 1 - (inl.calc_x() + outl.calc_x()) / 2 # average wetness - self.eta_s.val = self.eta_dry.val * (1 - self.alpha.val * ym) - - return self.eta_s_func() - - else: # superheated vapour - dp = inl.p.val_SI - outl.p.val_SI - - # compute the pressure and enthalpy at which the expansion enters the vapour dome - def find_sat(frac): - - psat = inl.p.val_SI - frac * dp - - # calculate enthalpy under dry expansion to psat - hout_isen = isentropic( - inl.p.val_SI, - inl.h.val_SI, - psat, - inl.fluid_data, - inl.mixing_rule, - T0=inl.T.val_SI - ) - hout = inl.h.val_SI - self.eta_dry_s.val * (inl.h.val_SI - hout_isen) - - # calculate enthalpy of saturated vapour at psat - hsat = h_mix_pQ(psat, 1, inl.fluid_data) - - return hout - hsat - frac = brentq(find_sat, 1, 0) - psat = inl.p.val_SI - frac * dp - hsat = h_mix_pQ(psat, 1, inl.fluid_data) - - # calculate the isentropic efficiency for wet expansion - ym = 1 - (1.0 + outl.calc_x()) / 2 # average wetness - eta_s = self.eta_dry_s.val * (1 - self.alpha.val * ym) - - # calculate the final outlet enthalpy - hout_isen = isentropic( - psat, - hsat, - outl.p.val_SI, - inl.fluid_data, - inl.mixing_rule, - T0=inl.T.val_SI - ) - hout = hsat - eta_s * (hsat- hout_isen) - - # calculate the difference in enthalpy - dh_isen = hout - inl.h.val_SI - dh = outl.h.val_SI - inl.h.val_SI - - # return residual - return -dh + dh_isen - - def eta_dry_s_deriv(self, increment_filter, k): - - f = self.eta_dry_s_func - i = self.inl[0] - o = self.outl[0] - if self.is_variable(i.p, increment_filter): - self.jacobian[k, i.p.J_col] = self.numeric_deriv(f, "p", i) - if self.is_variable(o.p, increment_filter): - self.jacobian[k, o.p.J_col] = self.numeric_deriv(f, "p", o) - if self.is_variable(i.h, increment_filter): - self.jacobian[k, i.h.J_col] = self.numeric_deriv(f, "h", i) - if self.is_variable(o.h, increment_filter): - self.jacobian[k, o.h.J_col] = self.numeric_deriv(f, "h", o) From 9c479a7c4795e29bf8826ffd577cefec2ac5ac40 Mon Sep 17 00:00:00 2001 From: tlmerbecks Date: Sun, 26 Jan 2025 13:48:02 +0100 Subject: [PATCH 05/18] adding the steam turbine component to the component module init --- src/tespy/components/__init__.py | 2 +- .../turbomachinery/steam_turbine.py | 37 ++++++++++++++++--- .../components/turbomachinery/turbine.py | 1 - 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/tespy/components/__init__.py b/src/tespy/components/__init__.py index 2912ccbd4..1c7b5dc00 100644 --- a/src/tespy/components/__init__.py +++ b/src/tespy/components/__init__.py @@ -26,5 +26,5 @@ from .subsystem import Subsystem # noqa: F401 from .turbomachinery.compressor import Compressor # noqa: F401 from .turbomachinery.pump import Pump # noqa: F401 +from .turbomachinery.turbine import Turbine # noqa: F401 from .turbomachinery.steam_turbine import SteamTurbine -from .turbomachinery.turbine import Turbine # noqa: F401 diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py index b3ca1f941..fafa117b6 100644 --- a/src/tespy/components/turbomachinery/steam_turbine.py +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -16,6 +16,7 @@ from tespy.components.component import component_registry from tespy.components.turbomachinery.turbine import Turbine from tespy.tools.data_containers import ComponentProperties as dc_cp +from tespy.tools.document_models import generate_latex_eq from tespy.tools.fluid_properties import isentropic from tespy.tools.fluid_properties import h_mix_pQ @@ -132,7 +133,8 @@ class SteamTurbine(Turbine): >>> round(outg.x.val, 3) 0.821 - To capture the effect of liquid drop-out on the isentropic efficiency, the dry turbine efficiency is specified + To capture the effect of liquid drop-out on the isentropic + efficiency, the dry turbine efficiency is specified >>> st.set_attr(eta_s=None) >>> st.set_attr(eta_dry_s=0.9) >>> nw.solve('design') @@ -142,7 +144,6 @@ class SteamTurbine(Turbine): 0.840 """ - @staticmethod def component(): return 'steam turbine' @@ -156,7 +157,8 @@ def get_parameters(self): params["eta_dry_s"] = dc_cp( min_val=0, max_val=1, num_eq=1, func=self.eta_dry_s_func, - deriv=self.eta_dry_s_deriv + deriv=self.eta_dry_s_deriv, + latex=self.eta_dry_s_func_doc ) return params @@ -177,7 +179,8 @@ def eta_dry_s_func(self): else: # superheated vapour dp = inl.p.val_SI - outl.p.val_SI - # compute the pressure and enthalpy at which the expansion enters the vapour dome + # compute the pressure and enthalpy at which the expansion + # enters the vapour dome def find_sat(frac): psat = inl.p.val_SI - frac * dp @@ -213,7 +216,7 @@ def find_sat(frac): inl.mixing_rule, T0=inl.T.val_SI ) - hout = hsat - eta_s * (hsat- hout_isen) + hout = hsat - eta_s * (hsat - hout_isen) # calculate the difference in enthalpy dh_isen = hout - inl.h.val_SI @@ -235,3 +238,27 @@ def eta_dry_s_deriv(self, increment_filter, k): self.jacobian[k, i.h.J_col] = self.numeric_deriv(f, "h", i) if self.is_variable(o.h, increment_filter): self.jacobian[k, o.h.J_col] = self.numeric_deriv(f, "h", o) + + def eta_dry_s_func_doc(self, label): + r""" + Equation for given dry isentropic efficiency of a turbine. + + Parameters + ---------- + label : str + Label for equation. + + Returns + ------- + latex : str + LaTeX code of equations applied. + """ + latex = ( + r'\begin{split}' + '\n' + r'0 &=-\left(h_\mathrm{out}-h_\mathrm{in}\right)+\left(' + r'h_\mathrm{out,s}-h_\mathrm{in}\right)\cdot\eta_\mathrm{s}\\' + '\n' + r'\eta_\mathrm{s} &=\eta_\mathrm{s}^\mathrm{dry}\cdot(1 - \alpha*y_\mathrm{m})\\' + '\n' + r'y_\mathrm{m} &=\frac{(1-x_\mathrm{in})+(1-x_\mathrm{out})}{2}\\' + '\n' + r'\end{split}' + ) + return generate_latex_eq(self, latex, label) diff --git a/src/tespy/components/turbomachinery/turbine.py b/src/tespy/components/turbomachinery/turbine.py index 4e5f950f9..a73dc8ad8 100644 --- a/src/tespy/components/turbomachinery/turbine.py +++ b/src/tespy/components/turbomachinery/turbine.py @@ -583,4 +583,3 @@ def exergy_balance(self, T0): self.E_bus = {"chemical": 0, "physical": 0, "massless": -self.P.val} self.E_D = self.E_F - self.E_P self.epsilon = self._calc_epsilon() - From 5603db961454bf8162f99fb8980eb50c5aca2314 Mon Sep 17 00:00:00 2001 From: tlmerbecks Date: Sun, 26 Jan 2025 14:11:06 +0100 Subject: [PATCH 06/18] adding doc strings --- .../turbomachinery/steam_turbine.py | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py index fafa117b6..4d769f446 100644 --- a/src/tespy/components/turbomachinery/steam_turbine.py +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -151,7 +151,6 @@ def component(): def get_parameters(self): params = super().get_parameters() - params["alpha"] = dc_cp( min_val=0.4, max_val=2.5, num_eq=0) params["eta_dry_s"] = dc_cp( @@ -164,6 +163,23 @@ def get_parameters(self): return params def eta_dry_s_func(self): + r""" + Equation for given dry isentropic efficiency of a turbine. + + Returns + ------- + residual : float + Residual value of equation. + + .. math:: + + 0 = -\left( h_{out} - h_{in} \right) + + \left( h_{out,s} - h_{in} \right) \cdot \eta_{s,e} + + \eta_{s,e} = \eta_{s,e}^{dry} \cdot \left( 1 - \alpha \cdot y_m \right) + + y_m = \frac{\left( 1-x_{in}\right)+ \left( 1-x_{out} \right)}{2} + """ inl = self.inl[0] outl = self.outl[0] @@ -227,6 +243,18 @@ def find_sat(frac): def eta_dry_s_deriv(self, increment_filter, k): + r""" + Partial derivatives for dry isentropic efficiency function. + + Parameters + ---------- + increment_filter : ndarray + Matrix for filtering non-changing variables. + + k : int + Position of derivatives in Jacobian matrix (k-th equation). + """ + f = self.eta_dry_s_func i = self.inl[0] o = self.outl[0] @@ -257,8 +285,8 @@ def eta_dry_s_func_doc(self, label): r'\begin{split}' + '\n' r'0 &=-\left(h_\mathrm{out}-h_\mathrm{in}\right)+\left(' r'h_\mathrm{out,s}-h_\mathrm{in}\right)\cdot\eta_\mathrm{s}\\' + '\n' - r'\eta_\mathrm{s} &=\eta_\mathrm{s}^\mathrm{dry}\cdot(1 - \alpha*y_\mathrm{m})\\' + '\n' - r'y_\mathrm{m} &=\frac{(1-x_\mathrm{in})+(1-x_\mathrm{out})}{2}\\' + '\n' + r'\eta_\mathrm{s} &=\eta_\mathrm{s}^\mathrm{dry}\cdot \left( 1 - \alpha \cdot y_\mathrm{m} \right)\\' + '\n' + r'y_\mathrm{m} &=\frac{\left( 1-x_\mathrm{in} \right)+\left( 1-x_\mathrm{out} \right)}{2}\\' + '\n' r'\end{split}' ) return generate_latex_eq(self, latex, label) From 3edfebae7c3ebcbe1b54a4494a0076848c4f1c05 Mon Sep 17 00:00:00 2001 From: tlmerbecks Date: Sun, 26 Jan 2025 14:47:24 +0100 Subject: [PATCH 07/18] refactoring of attribute definition & renaming variables --- .../turbomachinery/steam_turbine.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py index 4d769f446..ff8afd68a 100644 --- a/src/tespy/components/turbomachinery/steam_turbine.py +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -16,6 +16,7 @@ from tespy.components.component import component_registry from tespy.components.turbomachinery.turbine import Turbine from tespy.tools.data_containers import ComponentProperties as dc_cp +from tespy.tools.data_containers import GroupedComponentProperties as dc_gcp from tespy.tools.document_models import generate_latex_eq from tespy.tools.fluid_properties import isentropic from tespy.tools.fluid_properties import h_mix_pQ @@ -136,7 +137,7 @@ class SteamTurbine(Turbine): To capture the effect of liquid drop-out on the isentropic efficiency, the dry turbine efficiency is specified >>> st.set_attr(eta_s=None) - >>> st.set_attr(eta_dry_s=0.9) + >>> st.set_attr(eta_dry_s=0.9, alpha=1.0) >>> nw.solve('design') >>> round(st.P.val, 0) -7009682.0 @@ -151,20 +152,19 @@ def component(): def get_parameters(self): params = super().get_parameters() - params["alpha"] = dc_cp( - min_val=0.4, max_val=2.5, num_eq=0) - params["eta_dry_s"] = dc_cp( - min_val=0, max_val=1, num_eq=1, - func=self.eta_dry_s_func, - deriv=self.eta_dry_s_deriv, - latex=self.eta_dry_s_func_doc - ) + params["alpha"] = dc_cp(min_val=0.4, max_val=2.5) + params["eta_s_dry"] = dc_cp(min_val=0.0, max_val=1.0) + params["eta_s_dry_group"] = dc_gcp( + num_eq=1, elements=["alpha", "eta_s_dry"], + func=self.eta_s_wet_func, + deriv=self.eta_s_wet_deriv, + latex=self.eta_s_wet_func_doc) return params - def eta_dry_s_func(self): + def eta_s_wet_func(self): r""" - Equation for given dry isentropic efficiency of a turbine. + Equation for given dry isentropic efficiency of a turbine under wet expansion. Returns ------- @@ -209,7 +209,7 @@ def find_sat(frac): inl.mixing_rule, T0=inl.T.val_SI ) - hout = inl.h.val_SI - self.eta_dry_s.val * (inl.h.val_SI - hout_isen) + hout = inl.h.val_SI - self.eta_s_dry.val * (inl.h.val_SI - hout_isen) # calculate enthalpy of saturated vapour at psat hsat = h_mix_pQ(psat, 1, inl.fluid_data) @@ -221,7 +221,7 @@ def find_sat(frac): # calculate the isentropic efficiency for wet expansion ym = 1 - (1.0 + outl.calc_x()) / 2 # average wetness - eta_s = self.eta_dry_s.val * (1 - self.alpha.val * ym) + eta_s = self.eta_s_dry.val * (1 - self.alpha.val * ym) # calculate the final outlet enthalpy hout_isen = isentropic( @@ -241,7 +241,7 @@ def find_sat(frac): # return residual return -dh + dh_isen - def eta_dry_s_deriv(self, increment_filter, k): + def eta_s_wet_deriv(self, increment_filter, k): r""" Partial derivatives for dry isentropic efficiency function. @@ -255,7 +255,7 @@ def eta_dry_s_deriv(self, increment_filter, k): Position of derivatives in Jacobian matrix (k-th equation). """ - f = self.eta_dry_s_func + f = self.eta_s_wet_func i = self.inl[0] o = self.outl[0] if self.is_variable(i.p, increment_filter): @@ -267,7 +267,7 @@ def eta_dry_s_deriv(self, increment_filter, k): if self.is_variable(o.h, increment_filter): self.jacobian[k, o.h.J_col] = self.numeric_deriv(f, "h", o) - def eta_dry_s_func_doc(self, label): + def eta_s_wet_func_doc(self, label): r""" Equation for given dry isentropic efficiency of a turbine. From e4cf5319654f39cc152fdc29b289cc54eeff7eb5 Mon Sep 17 00:00:00 2001 From: tlmerbecks Date: Sun, 26 Jan 2025 14:59:27 +0100 Subject: [PATCH 08/18] formatting additions --- src/tespy/components/__init__.py | 2 +- src/tespy/components/turbomachinery/steam_turbine.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/tespy/components/__init__.py b/src/tespy/components/__init__.py index 1c7b5dc00..bbbee44b0 100644 --- a/src/tespy/components/__init__.py +++ b/src/tespy/components/__init__.py @@ -26,5 +26,5 @@ from .subsystem import Subsystem # noqa: F401 from .turbomachinery.compressor import Compressor # noqa: F401 from .turbomachinery.pump import Pump # noqa: F401 -from .turbomachinery.turbine import Turbine # noqa: F401 from .turbomachinery.steam_turbine import SteamTurbine +from .turbomachinery.turbine import Turbine # noqa: F401 diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py index ff8afd68a..b6a09ff52 100644 --- a/src/tespy/components/turbomachinery/steam_turbine.py +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -109,11 +109,9 @@ class SteamTurbine(Turbine): to 0,5 bar at the outlet. For example, it is possible to calulate the power output and vapour content at the outlet for a given isentropic efficiency. - >>> from tespy.components import Sink, Source, Turbine + >>> from tespy.components import Sink, Source, SteamTurbine >>> from tespy.connections import Connection >>> from tespy.networks import Network - >>> from tespy.tools import ComponentCharacteristics as dc_cc - >>> import shutil >>> nw = Network(p_unit='bar', T_unit='C', h_unit='kJ / kg', iterinfo=False) >>> si = Sink('sink') >>> so = Source('source') From 9c0ef754afdd378b176558e68d819e6533a775e7 Mon Sep 17 00:00:00 2001 From: tlmerbecks Date: Sun, 26 Jan 2025 15:32:20 +0100 Subject: [PATCH 09/18] Correcting testing failures --- src/tespy/components/turbomachinery/steam_turbine.py | 6 +++--- tests/test_components/test_turbomachinery.py | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py index b6a09ff52..22586ff95 100644 --- a/src/tespy/components/turbomachinery/steam_turbine.py +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -117,7 +117,7 @@ class SteamTurbine(Turbine): >>> so = Source('source') >>> st = SteamTurbine('steam turbine') >>> st.component() - 'turbine' + 'steam turbine' >>> inc = Connection(so, 'out1', st, 'in1') >>> outg = Connection(st, 'out1', si, 'in1') >>> nw.add_conns(inc, outg) @@ -135,12 +135,12 @@ class SteamTurbine(Turbine): To capture the effect of liquid drop-out on the isentropic efficiency, the dry turbine efficiency is specified >>> st.set_attr(eta_s=None) - >>> st.set_attr(eta_dry_s=0.9, alpha=1.0) + >>> st.set_attr(eta_s_dry=0.9, alpha=1.0) >>> nw.solve('design') >>> round(st.P.val, 0) -7009682.0 >>> round(outg.x.val, 3) - 0.840 + 0.84 """ @staticmethod diff --git a/tests/test_components/test_turbomachinery.py b/tests/test_components/test_turbomachinery.py index cb727ac84..0ed70d7ac 100644 --- a/tests/test_components/test_turbomachinery.py +++ b/tests/test_components/test_turbomachinery.py @@ -16,6 +16,7 @@ from tespy.components import Pump from tespy.components import Sink from tespy.components import Source +from tespy.components import SteamTurbine from tespy.components import Turbine from tespy.components.turbomachinery.base import Turbomachine from tespy.connections import Connection @@ -329,6 +330,9 @@ def test_Turbine(self, tmp_path): ') must be (' + str(eta_s) + ').') assert eta_s == round(instance.eta_s.val, 3), msg + def test_SteamTurbine(self, temp_path): + pass + def test_Turbomachine(self): """Test component properties of turbomachines.""" instance = Turbomachine('turbomachine') From 3a930e150b9b4bb09d00a0b95e9d2df35181c82a Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Sun, 26 Jan 2025 15:50:43 +0100 Subject: [PATCH 10/18] Fix a typo and add the calc_eta_s method --- .../turbomachinery/steam_turbine.py | 5 ++- .../components/turbomachinery/turbine.py | 34 ++++++++++--------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py index 22586ff95..580e6f8d4 100644 --- a/src/tespy/components/turbomachinery/steam_turbine.py +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -186,9 +186,7 @@ def eta_s_wet_func(self): if state == "tp": # two-phase or saturated vapour ym = 1 - (inl.calc_x() + outl.calc_x()) / 2 # average wetness - self.eta_s.val = self.eta_dry.val * (1 - self.alpha.val * ym) - - return self.eta_s_func() + return self.calc_eta_s() - self.eta_s_dry.val * (1 - self.alpha.val * ym) else: # superheated vapour dp = inl.p.val_SI - outl.p.val_SI @@ -213,6 +211,7 @@ def find_sat(frac): hsat = h_mix_pQ(psat, 1, inl.fluid_data) return hout - hsat + frac = brentq(find_sat, 1, 0) psat = inl.p.val_SI - frac * dp hsat = h_mix_pQ(psat, 1, inl.fluid_data) diff --git a/src/tespy/components/turbomachinery/turbine.py b/src/tespy/components/turbomachinery/turbine.py index a73dc8ad8..d7e4a4ac7 100644 --- a/src/tespy/components/turbomachinery/turbine.py +++ b/src/tespy/components/turbomachinery/turbine.py @@ -251,6 +251,22 @@ def eta_s_deriv(self, increment_filter, k): if o.h.is_var and self.it == 0: self.jacobian[k, o.h.J_col] = -1 + def calc_eta_s(self): + inl = self.inl[0] + outl = self.outl[0] + return ( + (outl.h.val_SI - inl.h.val_SI) + / (isentropic( + inl.p.val_SI, + inl.h.val_SI, + outl.p.val_SI, + inl.fluid_data, + inl.mixing_rule, + T0=inl.T.val_SI + ) - inl.h.val_SI + ) + ) + def cone_func(self): r""" Equation for stodolas cone law. @@ -510,22 +526,8 @@ def calc_parameters(self): inl = self.inl[0] outl = self.outl[0] - self.eta_s.val = ( - (outl.h.val_SI - inl.h.val_SI) - / ( - isentropic( - inl.p.val_SI, - inl.h.val_SI, - outl.p.val_SI, - inl.fluid_data, - inl.mixing_rule, - T0=inl.T.val_SI - ) - - inl.h.val_SI - ) - ) - - self.pr.val = self.outl[0].p.val_SI / self.inl[0].p.val_SI + self.eta_s.val = self.calc_eta_s() + self.pr.val = outl.p.val_SI / inl.p.val_SI def exergy_balance(self, T0): r""" From cc05a7eaa87d857a6461c66981e8a0a9911779ea Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Sun, 26 Jan 2025 15:51:04 +0100 Subject: [PATCH 11/18] Rename to dh_bisectioned --- .../components/turbomachinery/steam_turbine.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py index 580e6f8d4..73f78a459 100644 --- a/src/tespy/components/turbomachinery/steam_turbine.py +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -222,21 +222,21 @@ def find_sat(frac): # calculate the final outlet enthalpy hout_isen = isentropic( - psat, - hsat, - outl.p.val_SI, - inl.fluid_data, - inl.mixing_rule, - T0=inl.T.val_SI - ) + psat, + hsat, + outl.p.val_SI, + inl.fluid_data, + inl.mixing_rule, + T0=inl.T.val_SI + ) hout = hsat - eta_s * (hsat - hout_isen) # calculate the difference in enthalpy - dh_isen = hout - inl.h.val_SI + dh_bisectioned = hout - inl.h.val_SI dh = outl.h.val_SI - inl.h.val_SI # return residual - return -dh + dh_isen + return dh - dh_bisectioned def eta_s_wet_deriv(self, increment_filter, k): From 88c249bf9b2d7b6749d5f2a954cc0e0c5f22a087 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Sun, 26 Jan 2025 16:16:56 +0100 Subject: [PATCH 12/18] Make some style/layout changes --- src/tespy/components/__init__.py | 2 +- .../turbomachinery/steam_turbine.py | 77 +++++++++++-------- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/tespy/components/__init__.py b/src/tespy/components/__init__.py index bbbee44b0..6962f51bd 100644 --- a/src/tespy/components/__init__.py +++ b/src/tespy/components/__init__.py @@ -26,5 +26,5 @@ from .subsystem import Subsystem # noqa: F401 from .turbomachinery.compressor import Compressor # noqa: F401 from .turbomachinery.pump import Pump # noqa: F401 -from .turbomachinery.steam_turbine import SteamTurbine +from .turbomachinery.steam_turbine import SteamTurbine # noqa: F401 from .turbomachinery.turbine import Turbine # noqa: F401 diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py index 73f78a459..4d7b7eb56 100644 --- a/src/tespy/components/turbomachinery/steam_turbine.py +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -162,23 +162,25 @@ def get_parameters(self): def eta_s_wet_func(self): r""" - Equation for given dry isentropic efficiency of a turbine under wet expansion. + Equation for given dry isentropic efficiency of a turbine under wet + expansion. - Returns - ------- - residual : float - Residual value of equation. - - .. math:: + Returns + ------- + residual : float + Residual value of equation. - 0 = -\left( h_{out} - h_{in} \right) + - \left( h_{out,s} - h_{in} \right) \cdot \eta_{s,e} + .. math:: - \eta_{s,e} = \eta_{s,e}^{dry} \cdot \left( 1 - \alpha \cdot y_m \right) + 0 = -\left( h_{out} - h_{in} \right) + + \left( h_{out,s} - h_{in} \right) \cdot \eta_{s,e} - y_m = \frac{\left( 1-x_{in}\right)+ \left( 1-x_{out} \right)}{2} - """ + \eta_{s,e} = \eta_{s,e}^{dry} \cdot \left( 1 - \alpha + \cdot y_m \right) + y_m = \frac{\left( 1-x_{in}\right)+ \left( 1-x_{out} + \right)}{2} + """ inl = self.inl[0] outl = self.outl[0] @@ -186,7 +188,10 @@ def eta_s_wet_func(self): if state == "tp": # two-phase or saturated vapour ym = 1 - (inl.calc_x() + outl.calc_x()) / 2 # average wetness - return self.calc_eta_s() - self.eta_s_dry.val * (1 - self.alpha.val * ym) + return ( + self.calc_eta_s() + - self.eta_s_dry.val * (1 - self.alpha.val * ym) + ) else: # superheated vapour dp = inl.p.val_SI - outl.p.val_SI @@ -198,14 +203,17 @@ def find_sat(frac): # calculate enthalpy under dry expansion to psat hout_isen = isentropic( - inl.p.val_SI, - inl.h.val_SI, - psat, - inl.fluid_data, - inl.mixing_rule, - T0=inl.T.val_SI - ) - hout = inl.h.val_SI - self.eta_s_dry.val * (inl.h.val_SI - hout_isen) + inl.p.val_SI, + inl.h.val_SI, + psat, + inl.fluid_data, + inl.mixing_rule, + T0=inl.T.val_SI + ) + hout = ( + inl.h.val_SI - self.eta_s_dry.val + * (inl.h.val_SI - hout_isen) + ) # calculate enthalpy of saturated vapour at psat hsat = h_mix_pQ(psat, 1, inl.fluid_data) @@ -239,19 +247,17 @@ def find_sat(frac): return dh - dh_bisectioned def eta_s_wet_deriv(self, increment_filter, k): - r""" - Partial derivatives for dry isentropic efficiency function. - - Parameters - ---------- - increment_filter : ndarray - Matrix for filtering non-changing variables. + Partial derivatives for dry isentropic efficiency function. - k : int - Position of derivatives in Jacobian matrix (k-th equation). - """ + Parameters + ---------- + increment_filter : ndarray + Matrix for filtering non-changing variables. + k : int + Position of derivatives in Jacobian matrix (k-th equation). + """ f = self.eta_s_wet_func i = self.inl[0] o = self.outl[0] @@ -281,9 +287,12 @@ def eta_s_wet_func_doc(self, label): latex = ( r'\begin{split}' + '\n' r'0 &=-\left(h_\mathrm{out}-h_\mathrm{in}\right)+\left(' - r'h_\mathrm{out,s}-h_\mathrm{in}\right)\cdot\eta_\mathrm{s}\\' + '\n' - r'\eta_\mathrm{s} &=\eta_\mathrm{s}^\mathrm{dry}\cdot \left( 1 - \alpha \cdot y_\mathrm{m} \right)\\' + '\n' - r'y_\mathrm{m} &=\frac{\left( 1-x_\mathrm{in} \right)+\left( 1-x_\mathrm{out} \right)}{2}\\' + '\n' + r'h_\mathrm{out,s}-h_\mathrm{in}\right)\cdot\eta_\mathrm{s}\\' + + '\n' + r'\eta_\mathrm{s} &=\eta_\mathrm{s}^\mathrm{dry}\cdot \left( 1 - ' + r'\alpha \cdot y_\mathrm{m} \right)\\' + '\n' + r'y_\mathrm{m} &=\frac{\left( 1-x_\mathrm{in} \right)+\left( ' + r'1-x_\mathrm{out} \right)}{2}\\' + '\n' r'\end{split}' ) return generate_latex_eq(self, latex, label) From 164eb124b65d400858a638c62d2c3bb32a140d31 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Sun, 26 Jan 2025 16:18:57 +0100 Subject: [PATCH 13/18] Simplify residual to make it even more understandable --- src/tespy/components/turbomachinery/steam_turbine.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py index 4d7b7eb56..827110bee 100644 --- a/src/tespy/components/turbomachinery/steam_turbine.py +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -239,12 +239,8 @@ def find_sat(frac): ) hout = hsat - eta_s * (hsat - hout_isen) - # calculate the difference in enthalpy - dh_bisectioned = hout - inl.h.val_SI - dh = outl.h.val_SI - inl.h.val_SI - - # return residual - return dh - dh_bisectioned + # return residual: outlet enthalpy = calculated outlet enthalpy + return outl.h.val_SI - hout def eta_s_wet_deriv(self, increment_filter, k): r""" From 6f8a5df422fd0d3233b9e1eb0cce5c78909f729f Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Sat, 8 Feb 2025 16:08:57 +0100 Subject: [PATCH 14/18] Add scipy dependency and a test for the SteamTurbine --- pyproject.toml | 1 + tests/test_components/test_turbomachinery.py | 92 +++++++++++++------- 2 files changed, 63 insertions(+), 30 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 75a578348..16adfda92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ dependencies = [ "matplotlib>=3.2.1", "numpy>=1.13.3", "pandas>=1.3.0", + "scipy", "tabulate>=0.8.2", ] license = {text = "MIT"} diff --git a/tests/test_components/test_turbomachinery.py b/tests/test_components/test_turbomachinery.py index 0ed70d7ac..209500dbf 100644 --- a/tests/test_components/test_turbomachinery.py +++ b/tests/test_components/test_turbomachinery.py @@ -258,12 +258,22 @@ def test_Turbine(self, tmp_path): # design value of isentropic efficiency eta_s_d = ( - (self.c2.h.val_SI - self.c1.h.val_SI) / ( - isentropic(self.c1.p.val_SI, self.c1.h.val_SI, self.c2.p.val_SI, self.c1.fluid_data, self.c1.mixing_rule) - - self.c1.h.val_SI)) - msg = ('Value of isentropic efficiency must be ' + - str(round(eta_s_d, 3)) + ', is ' + str(instance.eta_s.val) + - '.') + (self.c2.h.val_SI - self.c1.h.val_SI) + / ( + isentropic( + self.c1.p.val_SI, + self.c1.h.val_SI, + self.c2.p.val_SI, + self.c1.fluid_data, + self.c1.mixing_rule + ) + - self.c1.h.val_SI + ) + ) + msg = ( + f'Value of isentropic efficiency must be {round(eta_s_d, 3)}, is ' + f'{instance.eta_s.val}.' + ) assert round(eta_s_d, 3) == round(instance.eta_s.val, 3), msg # trigger invalid value for isentropic efficiency @@ -271,11 +281,22 @@ def test_Turbine(self, tmp_path): self.nw.solve('design') self.nw._convergence_check() eta_s = ( - (self.c2.h.val_SI - self.c1.h.val_SI) / ( - isentropic(self.c1.p.val_SI, self.c1.h.val_SI, self.c2.p.val_SI, self.c1.fluid_data, self.c1.mixing_rule) - - self.c1.h.val_SI)) - msg = ('Value of isentropic efficiency must be ' + str(eta_s) + - ', is ' + str(instance.eta_s.val) + '.') + (self.c2.h.val_SI - self.c1.h.val_SI) + / ( + isentropic( + self.c1.p.val_SI, + self.c1.h.val_SI, + self.c2.p.val_SI, + self.c1.fluid_data, + self.c1.mixing_rule + ) + - self.c1.h.val_SI + ) + ) + msg = ( + f'Value of isentropic efficiency must be {round(eta_s, 3)}, is ' + f'{instance.eta_s.val}.' + ) assert round(eta_s, 3) == round(instance.eta_s.val, 3), msg # unset isentropic efficiency and inlet pressure, @@ -288,21 +309,23 @@ def test_Turbine(self, tmp_path): self.nw.solve('offdesign', design_path=tmp_path) self.nw._convergence_check() # check efficiency - msg = ('Value of isentropic efficiency (' + str(instance.eta_s.val) + - ') must be identical to design case (' + str(eta_s_d) + ').') + msg = ( + f'Value of isentropic efficiency ({instance.eta_s.val}) must be ' + f'identical to design case ({eta_s_d}).' + ) assert round(eta_s_d, 2) == round(instance.eta_s.val, 2), msg # check pressure - msg = ('Value of inlet pressure (' + str(round(self.c1.p.val_SI)) + - ') must be identical to design case (' + - str(round(self.c1.p.design)) + ').') + msg = ( + f'Value of inlet pressure ({round(self.c1.p.val_SI)}) must be ' + f'identical to design case ({round(self.c1.p.design)}).' + ) assert round(self.c1.p.design) == round(self.c1.p.val_SI), msg # lowering mass flow, inlet pressure must sink according to cone law self.c1.set_attr(m=self.c1.m.val * 0.8) self.nw.solve('offdesign', design_path=tmp_path) self.nw._convergence_check() - msg = ('Value of pressure ratio (' + str(instance.pr.val) + - ') must be at (' + str(0.128) + ').') + msg = f'Value of pressure ratio ({instance.pr.val}) must be at 0.128.' assert 0.128 == round(instance.pr.val, 3), msg # testing more parameters for eta_s_char @@ -313,31 +336,40 @@ def test_Turbine(self, tmp_path): self.nw._convergence_check() expr = self.c1.v.val_SI / self.c1.v.design eta_s = round(eta_s_d * instance.eta_s_char.char_func.evaluate(expr), 3) - msg = ('Value of isentropic efficiency (' + - str(round(instance.eta_s.val, 3)) + - ') must be (' + str(eta_s) + ').') + msg = ( + f'Value of isentropic efficiency ({round(instance.eta_s.val, 3)}) ' + f'must be {eta_s}.' + ) assert eta_s == round(instance.eta_s.val, 3), msg # test parameter specification pr instance.eta_s_char.param = 'pr' self.nw.solve('offdesign', design_path=tmp_path) self.nw._convergence_check() - expr = (self.c2.p.val_SI * self.c1.p.design / - (self.c2.p.design * self.c1.p.val_SI)) + expr = ( + self.c2.p.val_SI * self.c1.p.design + / (self.c2.p.design * self.c1.p.val_SI) + ) eta_s = round(eta_s_d * instance.eta_s_char.char_func.evaluate(expr), 3) - msg = ('Value of isentropic efficiency (' + - str(round(instance.eta_s.val, 3)) + - ') must be (' + str(eta_s) + ').') + msg = ( + f'Value of isentropic efficiency ({round(instance.eta_s.val, 3)}) ' + f'must be {eta_s}.' + ) assert eta_s == round(instance.eta_s.val, 3), msg - def test_SteamTurbine(self, temp_path): - pass + def test_SteamTurbine(self, tmp_path): + instance = SteamTurbine('turbine') + self.setup_network(instance) + fl = {'H2O': 1} + self.c1.set_attr(fluid=fl, m=15, p=100, T=500) + self.c2.set_attr(Td_bp=5) + instance.set_attr(eta_s_dry=0.99, alpha=1) + self.nw.solve('design') def test_Turbomachine(self): """Test component properties of turbomachines.""" instance = Turbomachine('turbomachine') - msg = ('Component name must be turbomachine, is ' + - instance.component() + '.') + msg = f'Component name must be turbomachine, is {instance.component()}.' assert 'turbomachine' == instance.component(), msg self.setup_network(instance) fl = {'N2': 0.7556, 'O2': 0.2315, 'Ar': 0.0129} From 47024697bc566a9bd49decf027a343458ffb2e21 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Sat, 8 Feb 2025 16:11:40 +0100 Subject: [PATCH 15/18] Update the changelog --- docs/whats_new/v0-7-9.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/whats_new/v0-7-9.rst b/docs/whats_new/v0-7-9.rst index b6f6df858..95ea9815b 100644 --- a/docs/whats_new/v0-7-9.rst +++ b/docs/whats_new/v0-7-9.rst @@ -7,6 +7,11 @@ New Features i.e. :code:`"l"` for liquid, :code:`"tp"` for two-phase and :code:`"g"` for gaseous. The phase is only reported in subcritical pressure (`PR #592 `__). +- Implement the Baumann correlation for wet expansion in steamturbines. The + feature is available in a new component class, + :py:class:`tespy.components.turbomachinery.steam_turbine.SteamTurbine`. To + use the feature check the documentation of the component class + (`PR #602 `__). Contributors ############ From 0ce62d84f89ea4f99b39e8e1b9095f571f8299d7 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 10 Feb 2025 11:19:17 +0100 Subject: [PATCH 16/18] Add more tests and fix the gas only case --- .../turbomachinery/steam_turbine.py | 52 ++++++++----------- tests/test_components/test_turbomachinery.py | 40 +++++++++++++- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py index 827110bee..e46408b2f 100644 --- a/src/tespy/components/turbomachinery/steam_turbine.py +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -17,9 +17,11 @@ from tespy.components.turbomachinery.turbine import Turbine from tespy.tools.data_containers import ComponentProperties as dc_cp from tespy.tools.data_containers import GroupedComponentProperties as dc_gcp -from tespy.tools.document_models import generate_latex_eq from tespy.tools.fluid_properties import isentropic from tespy.tools.fluid_properties import h_mix_pQ +from tespy.tools.logger import logger +from tespy.tools.helpers import fluidalias_in_list +from tespy.tools.fluid_properties.helpers import single_fluid @component_registry @@ -155,11 +157,25 @@ def get_parameters(self): params["eta_s_dry_group"] = dc_gcp( num_eq=1, elements=["alpha", "eta_s_dry"], func=self.eta_s_wet_func, - deriv=self.eta_s_wet_deriv, - latex=self.eta_s_wet_func_doc) + deriv=self.eta_s_wet_deriv + ) return params + def preprocess(self, num_nw_vars): + + fluid = single_fluid(self.inl[0].fluid_data) + if fluid is None: + msg = "The SteamTurbine can only be used with pure fluids." + logger.error(msg) + raise ValueError(msg) + + if not fluidalias_in_list(fluid, ["water"]): + msg = "The SteamTurbine is intended to be used with water only." + logger.warning(msg) + + return super().preprocess(num_nw_vars) + def eta_s_wet_func(self): r""" Equation for given dry isentropic efficiency of a turbine under wet @@ -194,6 +210,9 @@ def eta_s_wet_func(self): ) else: # superheated vapour + if outl.calc_phase() == "g": + return self.calc_eta_s() - self.eta_s_dry.val + dp = inl.p.val_SI - outl.p.val_SI # compute the pressure and enthalpy at which the expansion @@ -265,30 +284,3 @@ def eta_s_wet_deriv(self, increment_filter, k): self.jacobian[k, i.h.J_col] = self.numeric_deriv(f, "h", i) if self.is_variable(o.h, increment_filter): self.jacobian[k, o.h.J_col] = self.numeric_deriv(f, "h", o) - - def eta_s_wet_func_doc(self, label): - r""" - Equation for given dry isentropic efficiency of a turbine. - - Parameters - ---------- - label : str - Label for equation. - - Returns - ------- - latex : str - LaTeX code of equations applied. - """ - latex = ( - r'\begin{split}' + '\n' - r'0 &=-\left(h_\mathrm{out}-h_\mathrm{in}\right)+\left(' - r'h_\mathrm{out,s}-h_\mathrm{in}\right)\cdot\eta_\mathrm{s}\\' - + '\n' - r'\eta_\mathrm{s} &=\eta_\mathrm{s}^\mathrm{dry}\cdot \left( 1 - ' - r'\alpha \cdot y_\mathrm{m} \right)\\' + '\n' - r'y_\mathrm{m} &=\frac{\left( 1-x_\mathrm{in} \right)+\left( ' - r'1-x_\mathrm{out} \right)}{2}\\' + '\n' - r'\end{split}' - ) - return generate_latex_eq(self, latex, label) diff --git a/tests/test_components/test_turbomachinery.py b/tests/test_components/test_turbomachinery.py index 209500dbf..9035a9581 100644 --- a/tests/test_components/test_turbomachinery.py +++ b/tests/test_components/test_turbomachinery.py @@ -361,10 +361,46 @@ def test_SteamTurbine(self, tmp_path): instance = SteamTurbine('turbine') self.setup_network(instance) fl = {'H2O': 1} + # start in gas, end in gas self.c1.set_attr(fluid=fl, m=15, p=100, T=500) - self.c2.set_attr(Td_bp=5) - instance.set_attr(eta_s_dry=0.99, alpha=1) + self.c2.set_attr(Td_bp=1) + + eta_s_dry = 0.9 + instance.set_attr(eta_s_dry=0.9, alpha=1) + + self.nw.solve('design') + self.nw._convergence_check() + + eta_s = round(instance.eta_s.val, 4) + msg = ( + f"In gas expansion isentropic ({eta_s}) and dry " + f"efficiency ({eta_s_dry}) have to be identical." + ) + assert eta_s_dry == eta_s, msg + + # end in two phase + self.c2.set_attr(Td_bp=None, x=0.9) + self.nw.solve('design') + self.nw._convergence_check() + + eta_s = round(instance.eta_s.val, 4) + msg = ( + f"In gas expansion isentropic ({eta_s}) and dry " + f"efficiency ({eta_s_dry}) cannot be identical." + ) + assert eta_s_dry != eta_s, msg + + # start in two phase + self.c1.set_attr(T=None, x=0.99) self.nw.solve('design') + self.nw._convergence_check() + + eta_s = round(instance.eta_s.val, 4) + msg = ( + f"In gas expansion isentropic ({eta_s}) and dry " + f"efficiency ({eta_s_dry}) cannot be identical." + ) + assert eta_s_dry != eta_s, msg def test_Turbomachine(self): """Test component properties of turbomachines.""" From 4e9488fcb1db3635471ab3ed81145b6c1d5b9501 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 10 Feb 2025 11:20:14 +0100 Subject: [PATCH 17/18] Run isort --- src/tespy/components/turbomachinery/steam_turbine.py | 7 +++---- src/tespy/components/turbomachinery/turbine.py | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py index e46408b2f..1c3d37a9b 100644 --- a/src/tespy/components/turbomachinery/steam_turbine.py +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -12,16 +12,15 @@ from scipy.optimize import brentq - from tespy.components.component import component_registry from tespy.components.turbomachinery.turbine import Turbine from tespy.tools.data_containers import ComponentProperties as dc_cp from tespy.tools.data_containers import GroupedComponentProperties as dc_gcp -from tespy.tools.fluid_properties import isentropic from tespy.tools.fluid_properties import h_mix_pQ -from tespy.tools.logger import logger -from tespy.tools.helpers import fluidalias_in_list +from tespy.tools.fluid_properties import isentropic from tespy.tools.fluid_properties.helpers import single_fluid +from tespy.tools.helpers import fluidalias_in_list +from tespy.tools.logger import logger @component_registry diff --git a/src/tespy/components/turbomachinery/turbine.py b/src/tespy/components/turbomachinery/turbine.py index d7e4a4ac7..941ef86f6 100644 --- a/src/tespy/components/turbomachinery/turbine.py +++ b/src/tespy/components/turbomachinery/turbine.py @@ -12,7 +12,6 @@ import numpy as np - from tespy.components.component import component_registry from tespy.components.turbomachinery.base import Turbomachine from tespy.tools import logger From 431660c97b36fbf0c7793eccd2360640182ea24c Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 10 Feb 2025 11:22:30 +0100 Subject: [PATCH 18/18] Add citation informatino --- docs/references.bib | 9 +++++++++ src/tespy/components/turbomachinery/steam_turbine.py | 2 ++ 2 files changed, 11 insertions(+) diff --git a/docs/references.bib b/docs/references.bib index 92af8f165..b4ebdb259 100644 --- a/docs/references.bib +++ b/docs/references.bib @@ -27,6 +27,15 @@ @misc{Kearney1988 year = {1988}, } +@book{Tanuma2017, + doi = {10.1016/C2014-0-03636-2}, + editor = {T. Tanuma}, + isbn = {9780081003145}, + publisher = {Elsevier}, + title = {Advances in Steam Turbines for Modern Power Plants}, + year = {2017}, +} + @TechReport{Lippke1995, author = {Lippke, Frank}, institution = {Sandia National Lab.}, diff --git a/src/tespy/components/turbomachinery/steam_turbine.py b/src/tespy/components/turbomachinery/steam_turbine.py index 1c3d37a9b..486e43d91 100644 --- a/src/tespy/components/turbomachinery/steam_turbine.py +++ b/src/tespy/components/turbomachinery/steam_turbine.py @@ -109,6 +109,8 @@ class SteamTurbine(Turbine): A steam turbine expands 10 kg/s of superheated steam at 550 °C and 110 bar to 0,5 bar at the outlet. For example, it is possible to calulate the power output and vapour content at the outlet for a given isentropic efficiency. + The :code:`SteamTurbine` class follows the implementation of the Baumann + rule :cite:`Tanuma2017` >>> from tespy.components import Sink, Source, SteamTurbine >>> from tespy.connections import Connection