Source code for tespy.components.piping.valve

# -*- coding: utf-8

"""Module of class Valve.


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/piping.py

SPDX-License-Identifier: MIT
"""

import numpy as np

from tespy.components.component import Component
from tespy.components.component import component_registry
from tespy.tools import logger
from tespy.tools.data_containers import ComponentCharacteristics as dc_cc
from tespy.tools.data_containers import ComponentProperties as dc_cp
from tespy.tools.document_models import generate_latex_eq


[docs] @component_registry class Valve(Component): r""" The Valve throttles a fluid without changing enthalpy. **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.component.Component.zeta_func` - :py:meth:`tespy.components.piping.valve.Valve.dp_char_func` Inlets/Outlets - in1 - out1 Image .. image:: /api/_images/Valve.svg :alt: flowsheet of the valve :align: center :class: only-light .. image:: /api/_images/Valve_darkmode.svg :alt: flowsheet of the valve :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. pr : float, dict, :code:`"var"` Outlet to inlet pressure ratio, :math:`pr/1` zeta : float, dict, :code:`"var"` Geometry independent friction coefficient, :math:`\frac{\zeta}{D^4}/\frac{1}{\text{m}^4}`. dp_char : tespy.tools.characteristics.CharLine, dict Characteristic line for difference pressure to mass flow. Example ------- A mass flow of 1 kg/s methane is throttled from 80 bar to 15 bar in a valve. The inlet temperature is at 50 °C. It is possible to determine the outlet temperature as the throttling does not change enthalpy. >>> from tespy.components import Sink, Source, Valve >>> from tespy.connections import Connection >>> from tespy.networks import Network >>> import shutil >>> nw = Network(p_unit='bar', T_unit='C', iterinfo=False) >>> so = Source('source') >>> si = Sink('sink') >>> v = Valve('valve') >>> v.component() 'valve' >>> so_v = Connection(so, 'out1', v, 'in1') >>> v_si = Connection(v, 'out1', si, 'in1') >>> nw.add_conns(so_v, v_si) >>> v.set_attr(offdesign=['zeta']) >>> so_v.set_attr(fluid={'CH4': 1}, m=1, T=50, p=80, design=['m']) >>> v_si.set_attr(p=15) >>> nw.solve('design') >>> nw.save('tmp') >>> round(v_si.T.val, 1) 26.3 >>> round(v.pr.val, 3) 0.188 The simulation determined the area independent zeta value :math:`\frac{\zeta}{D^4}`. This zeta remains constant if the cross sectional area of the valve opening does not change. Using the zeta value we can determine the pressure ratio at a different feed pressure. >>> so_v.set_attr(p=70) >>> nw.solve('offdesign', design_path='tmp') >>> round(so_v.m.val, 1) 0.9 >>> round(v_si.T.val, 1) 30.0 >>> shutil.rmtree('./tmp', ignore_errors=True) """
[docs] @staticmethod def component(): return 'valve'
[docs] def get_parameters(self): return { 'pr': dc_cp( min_val=1e-4, max_val=1, num_eq=1, deriv=self.pr_deriv, func=self.pr_func, func_params={'pr': 'pr'}, latex=self.pr_func_doc), 'zeta': dc_cp( min_val=0, max_val=1e15, num_eq=1, deriv=self.zeta_deriv, func=self.zeta_func, func_params={'zeta': 'zeta'}, latex=self.zeta_func_doc), 'dp_char': dc_cc( param='m', num_eq=1, deriv=self.dp_char_deriv, func=self.dp_char_func, char_params={'type': 'abs'}, latex=self.dp_char_func_doc) }
[docs] def get_mandatory_constraints(self): return { 'enthalpy_equality_constraints': { 'func': self.enthalpy_equality_func, 'deriv': self.enthalpy_equality_deriv, 'constant_deriv': True, 'latex': self.enthalpy_equality_func_doc, 'num_eq': 1} }
[docs] @staticmethod def inlets(): return ['in1']
[docs] @staticmethod def outlets(): return ['out1']
[docs] def dp_char_func(self): r""" Equation for characteristic line of difference pressure to mass flow. Returns ------- residual : ndarray Residual value of equation. .. math:: 0=p_\mathrm{in}-p_\mathrm{out}-f\left( expr \right) """ p = self.dp_char.param expr = self.get_char_expr(p, **self.dp_char.char_params) if not expr: msg = ('Please choose a valid parameter, you want to link the ' 'pressure drop to at component ' + self.label + '.') logger.error(msg) raise ValueError(msg) return ( self.inl[0].p.val_SI - self.outl[0].p.val_SI - self.dp_char.char_func.evaluate(expr))
[docs] def dp_char_func_doc(self, label): r""" Equation for characteristic line of difference pressure to mass flow. Parameters ---------- label : str Label for equation. Returns ------- latex : str LaTeX code of equations applied. """ p = self.dp_char.param expr = self.get_char_expr_doc(p, **self.dp_char.char_params) if not expr: msg = ('Please choose a valid parameter, you want to link the ' 'pressure drop to at component ' + self.label + '.') logger.error(msg) raise ValueError(msg) latex = ( r'0=p_\mathrm{in}-p_\mathrm{out}-f\left(' + expr + r'\right)') return generate_latex_eq(self, latex, label)
[docs] def dp_char_deriv(self, increment_filter, k): r""" Calculate partial derivatives of difference pressure characteristic. Parameters ---------- increment_filter : ndarray Matrix for filtering non-changing variables. k : int Position of derivatives in Jacobian matrix (k-th equation). """ f = self.dp_char_func i = self.inl[0] o = self.outl[0] if self.is_variable(i.m, increment_filter): self.jacobian[k, i.m.J_col] = self.numeric_deriv(f, 'm', i) if self.dp_char.param == 'v': if self.is_variable(i.p, increment_filter): self.jacobian[k, i.p.J_col] = self.numeric_deriv( self.dp_char_func, 'p', i ) if self.is_variable(i.h, increment_filter): self.jacobian[k, i.h.J_col] = self.numeric_deriv( self.dp_char_func, 'h', i ) else: if self.is_variable(i.p, increment_filter): self.jacobian[k, i.p.J_col] = 1 if self.is_variable(o.p): self.jacobian[k, o.p.J_col] = -1
[docs] def initialise_source(self, c, key): r""" Return a starting value for pressure and enthalpy at outlet. Parameters ---------- c : tespy.connections.connection.Connection Connection to perform initialisation on. key : str Fluid property to retrieve. Returns ------- val : float Starting value for pressure/enthalpy in SI units. .. math:: val = \begin{cases} 4 \cdot 10^5 & \text{key = 'p'}\\ 5 \cdot 10^5 & \text{key = 'h'} \end{cases} """ if key == 'p': return 4e5 elif key == 'h': return 5e5
[docs] def initialise_target(self, c, key): r""" Return a starting value for pressure and enthalpy at inlet. Parameters ---------- c : tespy.connections.connection.Connection Connection to perform initialisation on. key : str Fluid property to retrieve. Returns ------- val : float Starting value for pressure/enthalpy in SI units. .. math:: val = \begin{cases} 5 \cdot 10^5 & \text{key = 'p'}\\ 5 \cdot 10^5 & \text{key = 'h'} \end{cases} """ if key == 'p': return 5e5 elif key == 'h': return 5e5
[docs] def calc_parameters(self): r"""Postprocessing parameter calculation.""" i = self.inl[0] o = self.outl[0] self.pr.val = o.p.val_SI / i.p.val_SI self.zeta.val = self.calc_zeta(i, o)
[docs] def entropy_balance(self): r""" Calculate entropy balance of a valve. Note ---- The entropy balance makes the follwing parameter available: .. math:: \text{S\_irr}=\dot{m} \cdot \left(s_\mathrm{out}-s_\mathrm{in} \right)\\ """ self.S_irr = self.inl[0].m.val_SI * ( self.outl[0].s.val_SI - self.inl[0].s.val_SI )
[docs] def exergy_balance(self, T0): r""" Calculate exergy balance of a valve. Parameters ---------- T0 : float Ambient temperature T0 / K. Note ---- .. math:: \dot{E}_\mathrm{P} = \begin{cases} \text{not defined (nan)} & T_\mathrm{in}, T_\mathrm{out} \geq T_0\\ \dot{E}_\mathrm{out}^\mathrm{T} & T_\mathrm{in} > T_0 \geq T_\mathrm{out}\\ \dot{E}_\mathrm{out}^\mathrm{T} - \dot{E}_\mathrm{in}^\mathrm{T} & T_0 \geq T_\mathrm{in}, T_\mathrm{out}\\ \end{cases} \dot{E}_\mathrm{F} = \begin{cases} \dot{E}_\mathrm{in}^\mathrm{PH} - \dot{E}_\mathrm{out}^\mathrm{PH} & T_\mathrm{in}, T_\mathrm{out} \geq T_0\\ \dot{E}_\mathrm{in}^\mathrm{T} + \dot{E}_\mathrm{in}^\mathrm{M}- \dot{E}_\mathrm{out}^\mathrm{M} & T_\mathrm{in} > T_0 \geq T_\mathrm{out}\\ \dot{E}_\mathrm{in}^\mathrm{M} - \dot{E}_\mathrm{out}^\mathrm{M} & T_0 \geq T_\mathrm{in}, T_\mathrm{out}\\ \end{cases} """ if self.inl[0].T.val_SI > T0 and self.outl[0].T.val_SI > T0: self.E_P = np.nan self.E_F = self.inl[0].Ex_physical - self.outl[0].Ex_physical elif self.outl[0].T.val_SI <= T0 and self.inl[0].T.val_SI > T0: self.E_P = self.outl[0].Ex_therm self.E_F = self.inl[0].Ex_therm + ( self.inl[0].Ex_mech - self.outl[0].Ex_mech) elif self.inl[0].T.val_SI <= T0 and self.outl[0].T.val_SI <= T0: self.E_P = self.outl[0].Ex_therm - self.inl[0].Ex_therm self.E_F = self.inl[0].Ex_mech - self.outl[0].Ex_mech else: msg = ('Exergy balance of a valve, where outlet temperature is ' 'larger than inlet temperature is not implmented.') logger.warning(msg) self.E_P = np.nan self.E_F = np.nan self.E_bus = { "chemical": np.nan, "physical": np.nan, "massless": np.nan } if np.isnan(self.E_P): self.E_D = self.E_F else: self.E_D = self.E_F - self.E_P self.epsilon = self._calc_epsilon()
[docs] def get_plotting_data(self): """Generate a dictionary containing FluProDia plotting information. Returns ------- data : dict A nested dictionary containing the keywords required by the :code:`calc_individual_isoline` method of the :code:`FluidPropertyDiagram` class. First level keys are the connection index ('in1' -> 'out1', therefore :code:`1` etc.). """ return { 1: { 'isoline_property': 'h', 'isoline_value': self.inl[0].h.val, 'isoline_value_end': self.outl[0].h.val, 'starting_point_property': 'v', 'starting_point_value': self.inl[0].vol.val, 'ending_point_property': 'v', 'ending_point_value': self.outl[0].vol.val } }