Source code for tespy.components.piping.pipe

# -*- coding: utf-8

"""Module of class Pipe.


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

SPDX-License-Identifier: MIT
"""

import math

from tespy.components.component import component_registry
from tespy.components.heat_exchangers.simple import SimpleHeatExchanger
from tespy.tools.data_containers import ComponentProperties as dc_cp
from tespy.tools.data_containers import GroupedComponentProperties as dc_gcp
from tespy.tools.data_containers import SimpleDataContainer as dc_simple
from tespy.tools.fluid_properties.wrappers import CoolPropWrapper
from tespy.tools.logger import logger


[docs] @component_registry class Pipe(SimpleHeatExchanger): r""" The Pipe is a subclass of a SimpleHeatExchanger. There are two different types of pipes available: An at the surface and a subsurface buried pipe. The implementation is based on :cite:`gnielinski1975` (surface) and :cite:`wallenten1991` (subsurface). **Mandatory Equations** - fluid: :py:meth:`tespy.components.component.Component.variable_equality_structure_matrix` - mass flow: :py:meth:`tespy.components.component.Component.variable_equality_structure_matrix` **Optional Equations** - :py:meth:`tespy.components.component.Component.dp_structure_matrix` - :py:meth:`tespy.components.component.Component.pr_structure_matrix` - :py:meth:`tespy.components.component.Component.zeta_func` - :py:meth:`tespy.components.heat_exchangers.simple.SimpleHeatExchanger.energy_balance_func` - :py:meth:`tespy.components.heat_exchangers.simple.SimpleHeatExchanger.darcy_func` - :py:meth:`tespy.components.heat_exchangers.simple.SimpleHeatExchanger.hazen_williams_func` - :py:meth:`tespy.components.heat_exchangers.simple.SimpleHeatExchanger.kA_group_func` - :py:meth:`tespy.components.heat_exchangers.simple.SimpleHeatExchanger.kA_char_group_func` - :py:meth:`tespy.components.piping.pipe.Pipe.ohc_surface_group_func` - :py:meth:`tespy.components.piping.pipe.Pipe.ohc_subsurface_group_func` Inlets/Outlets - in1 - out1 Optional inlets/outlets - heat Image .. image:: /api/_images/Pipe.svg :alt: flowsheet of the pipe :align: center :class: only-light .. image:: /api/_images/Pipe_darkmode.svg :alt: flowsheet of the pipe :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. Q : float, dict Heat transfer, :math:`Q/\text{W}`. pr : float, dict Outlet to inlet pressure ratio, :math:`pr/1`. zeta : float, dict Geometry independent friction coefficient, :math:`\frac{\zeta}{D^4}/\frac{1}{\text{m}^4}`. D : float, dict, :code:`"var"` Diameter of the pipes, :math:`D/\text{m}`. L : float, dict, :code:`"var"` Length of the pipes, :math:`L/\text{m}`. ks : float, dict, :code:`"var"` Pipe's roughness, :math:`ks/\text{m}`. darcy_group : str, dict Parametergroup for pressure drop calculation based on pipes dimensions using darcy weissbach equation. ks_HW : float, dict, :code:`"var"` Pipe's roughness, :math:`ks/\text{1}`. hw_group : str, dict Parametergroup for pressure drop calculation based on pipes dimensions using hazen williams equation. kA : float, dict, :code:`"var"` Area independent heat transfer coefficient, :math:`kA/\frac{\text{W}}{\text{K}}`. kA_char : tespy.tools.characteristics.CharLine, dict Characteristic line for heat transfer coefficient. Tamb : float, dict Ambient temperature, provide parameter in network's temperature unit, :math:`Tamb/\text{K}`. kA_group : str, dict Parametergroup for heat transfer calculation from ambient temperature and area independent heat transfer coefficient kA. insulation_thickness: float thickness of insulation, :math:`insulation_thickness/\text{m}`. insulation_tc: float thermal conductivity insulation, :math:`insulation_tc/\frac{\text{W}}{\text{m}\text{K}}`. material: str, float material of pipe: 'Steel', 'Carbon Steel', 'Cast Iron', 'Stainless Steel', 'PVC', 'CommercialCopper' or user-specified heat conductivity of material: float pipe_thickness: float thickness of pipe, :math:`pipe_thickness/\text{m}`. environment_media: str environment media around the pipe: 'air', 'gravel', 'stones', 'dry soil', 'moist soil'. wind_velocity: float Mean velocity of the wind. Needs to be greater than zero, :math:`wind_velocity/\frac{\text{m}}{\text{s}}`. pipe_depth: float pipe depth in the ground, :math:`pipe_depth/\text{m}` Example ------- A mass flow of 10 kg/s hot ethanol is transported in a pipeline. The pipe is considered adiabatic, in the first approach and has a length of 100 meters. We can calculate the diameter required at a given pressure loss of 2.5 %. After we determined the required diameter, we can predict pressure loss at a different mass flow through the pipeline. Afterwards heat losses can be calculated by defining insulation and environment parameters. The heat losses of a subsurface pipe can be compared to heat losses of a surface pipe. >>> from tespy.components import Sink, Source, Pipe >>> from tespy.connections import Connection >>> from tespy.networks import Network >>> import os >>> nw = Network(iterinfo=False) >>> nw.units.set_defaults(**{ ... "pressure": "bar", "temperature": "degC", "enthalpy": "kJ/kg" ... }) >>> so = Source('source 1') >>> si = Sink('sink 1') >>> pi = Pipe('pipeline') >>> pi.set_attr(pr=0.975, Q=0, L=100, D='var', ks=5e-5) >>> inc = Connection(so, 'out1', pi, 'in1') >>> outg = Connection(pi, 'out1', si, 'in1') >>> nw.add_conns(inc, outg) >>> inc.set_attr(fluid={'ethanol': 1}, m=10, T=30, p=3) >>> nw.solve('design') >>> round(pi.D.val, 3) 0.119 >>> round(outg.p.val / inc.p.val, 3) == round(pi.pr.val, 3) True >>> inc.set_attr(m=15) >>> pi.set_attr(pr=None) >>> pi.set_attr(D=pi.D.val) >>> nw.solve('design') >>> round(pi.pr.val, 2) 0.94 In the second section the example shows how to calculate the heat losses of the pipe to the ambient considering insulation. For this, we will look at a pipe transporting hot water. Since we change the fluid, we should also give a reasonable guess value for the outflow connection of the pipe as the initial guess originates from the previous calculation using ethanol as fluid. >>> inc.set_attr(fluid={'water': 1, 'ethanol': 0}, T=100) >>> outg.set_attr(h0=300) >>> pi.set_attr( ... D='var', Q=None, pr=0.975, ... Tamb=0, environment_media='dry soil', pipe_depth=5, ... insulation_thickness=0.1, insulation_tc=0.035, ... pipe_thickness=0.003, material='Steel' ... ) >>> nw.solve('design') >>> round(pi.Q.val, 2) -1780.74 We can reuse many of the given parameters of the pipe. By unsetting the pipe's depth and setting the environment media and wind velocity instead the analogous method for surface pipes is applied. Observe, how the overall heat loss increases. >>> pi.Q_ohc_group_subsurface.is_set = False >>> pi.set_attr( ... pipe_depth=None, environment_media='air', wind_velocity=2.0 ... ) >>> nw.solve('design') >>> round(pi.Q.val, 2) -2434.12 """ def _preprocess(self, row_idx): self.air = CoolPropWrapper('air') if self.wind_velocity.is_set: if self.wind_velocity.val < self.wind_velocity.min_val: msg = ( f"Minimum wind velocity is {self.wind_velocity.min_val} " "for numerical reasons. The value is changed to the " "specified minimum." ) logger.debug(msg) self.wind_velocity.val = self.wind_velocity.min_val super()._preprocess(row_idx)
[docs] def get_parameters(self): parameters=super().get_parameters() parameters['Q_ohc_group_surface']=dc_gcp( elements=[ 'insulation_thickness', 'insulation_tc', 'Tamb', 'material', 'pipe_thickness', 'environment_media', 'wind_velocity' ], num_eq_sets=1, func=self.ohc_surface_group_func, dependents=self.ohc_surface_group_dependents, description="equation for heat loss of surface pipes" ) parameters['Q_ohc_group_subsurface']=dc_gcp( elements=[ 'insulation_thickness', 'insulation_tc', 'Tamb', 'material', 'pipe_thickness', 'environment_media','pipe_depth' ], num_eq_sets=1, func=self.ohc_subsurface_group_func, dependents=self.ohc_subsurface_group_dependents, description="equation for heat loss of buried pipes" ) parameters['insulation_thickness']=dc_cp( min_val=1e-3, max_val=1e1, quantity="length", description="thickness of pipe insulation" ) parameters['insulation_tc']=dc_cp( min_val=1e-3, max_val=1e2, quantity="thermal_conductivity", description="thermal conductivity of insulation" ) parameters['material']=dc_simple(val='Steel') parameters['pipe_thickness']=dc_cp( min_val=0, max_val=1, quantity="length", description="wall thickness of pipe" ) parameters['environment_media']=dc_simple(val='soil') parameters['wind_velocity']=dc_cp( min_val=1e-6, max_val=20, quantity="speed", description="velocity of wind at insulation surface" ) parameters['pipe_depth']= dc_cp( min_val=1e-2, max_val=1e2, quantity="length", description="depth of buried pipe" ) return parameters
[docs] def ohc_surface_group_func(self): r"""Heat transfer calculation based on pipe material, insulation and surrounding ambient conditions fur surface pipes. Valid for forced convection. Returns ------- float Residual value of equation .. math:: 0 = \dot m \cdot \left(h_\text{out}-h_\text{in}\right)- \Delta T_\text{log} \cdot A \cdot U U = R_\text{conductance} + \frac{1}{\alpha_\text{outer}}} \alpha_\text{outer} = \frac{Nu_\text{l} \cdot \lambda}{l} Nu_\text{l}= 0.3 + \sqrt{Nu_\text{l, lam}^{2} + Nu_\text{l, turb}^{2}} Nu_\text{l, turb} = \frac{0.037 Re_l^{0.8} \cdot Pr}{1+2.443 \cdot Re_l^{-0.1}\cdot (Pr^{2/3}-1)} Nu_\text{l, lam} = 0.664 \sqrt{Re_l}\cdot \sqrt[3]{Pr} Reference: :cite:`gnielinski1975` """ diameters= [ self.D.val_SI, self.D.val_SI + 2 * self.pipe_thickness.val_SI, self.D.val_SI + 2 * self.pipe_thickness.val_SI + 2 * self.insulation_thickness.val_SI ] # outer surface area per definition area = self.L.val_SI * math.pi * diameters[2] # heat transfer resistance R_sum = [] ''' inner heat transfer resistance neglected yet R_int = 1/alpha_i *Diameters[2]/ Diameters[0] R_sum.append(R_int) ''' # pipe wall heat transfer resistance pipe_tc ={ 'Steel':46.5, 'Carbon Steel':46, 'Cast Iron':48.8, 'Stainless Steel':21, 'PVC':0.23, 'Copper': 380 } if diameters[1] > diameters[0]: if isinstance(self.material.val, str): wall_conductivity = pipe_tc[self.material.val] else: wall_conductivity = self.material.val R_sum.append( diameters[1] / wall_conductivity * math.log(diameters[1] / diameters[0]) / 2 ) # insulation heat transfer resistance if self.insulation_thickness.val_SI != 0: R_sum.append( diameters[2] / self.insulation_tc.val_SI * math.log(diameters[2] / diameters[1]) / 2 ) # external heat transfer resistance (to environment) Re = ( self.wind_velocity.val_SI * math.pi / 2 * (diameters[1] + self.insulation_thickness.val_SI * 2) / self.air.viscosity_pT(101300, self.Tamb.val_SI) * self.air.d_pT(101300, self.Tamb.val_SI) ) Pr = self.air.AS.Prandtl() Nu_lam = 0.664 * Re ** 0.5 *Pr ** (1 / 3) Nu_turb = ( 0.037 * Re ** 0.8 * Pr / (1+ 2.443 * Re** (-0.1) * (Pr ** (2 / 3) - 1)) ) Nu_ext = 0.3 + (Nu_lam ** 2 + Nu_turb ** 2) ** 0.5 alpha_ext = ( Nu_ext / (math.pi / 2 * (diameters[1] + self.insulation_thickness.val_SI *2)) * self.air.AS.conductivity() ) #W/m²/K R_sum.append(1 / alpha_ext) if len(R_sum) == 0: raise ValueError("No heat transfer resistance. Check input values.") i = self.inl[0] o = self.outl[0] return ( i.m.val_SI * (o.h.val_SI - i.h.val_SI) + area / sum(R_sum) * self._calculate_td_log() )
[docs] def ohc_surface_group_dependents(self): return ( [self.inl[0].m] + [var for c in self.inl + self.outl for var in [c.p, c.h]] + [self.D, self.L] )
[docs] def ohc_subsurface_group_func(self): r"""Heat transfer calculation based on pipe material, insulation and surrounding ambient conditions for subsurface pipes. Returns ------- float Residual value of equation .. math:: 0 = \dot m \cdot \left(h_\text{out}-h_\text{in}\right)- \Delta T_\text{log} \cdot A \cdot U First order approximation of multipole method for a single pipe in the ground. Reference: :cite:`wallenten1991` """ diameters= [ self.D.val_SI, self.D.val_SI + 2 * self.pipe_thickness.val_SI, self.D.val_SI + 2 * self.pipe_thickness.val_SI + 2 * self.insulation_thickness.val_SI ] ''' inner heat transfer resistance neglected yet R_int = 1/alpha_i *Diameters[2]/ Diameters[0] R_sum.append(R_int) ''' # external heat transfer resistance (to environment) ground_conductivity ={ 'gravel': 1.1, 'stones': 1.95, 'dry soil': 0.5, 'moist soil': 2.2 } # conductivity of the pipe neglected according to the original publication Beta = ( ground_conductivity[self.environment_media.val] / self.insulation_tc.val_SI * math.log(diameters[2] / diameters[0]) ) _h = ( math.log(2 * self.pipe_depth.val_SI / diameters[0]) + Beta + 1 / ( 1 - (2 * self.pipe_depth.val_SI / diameters[0]) ** 2 * (1 + Beta) / (1 - Beta) ) ) R_soil = ( _h / (2 * math.pi * ground_conductivity[self.environment_media.val]) ) i = self.inl[0] o = self.outl[0] # here the resistance is a per meter of pipe resistance, therefore # we only multiply be length return ( i.m.val_SI * (o.h.val_SI - i.h.val_SI) + 1 / R_soil * self._calculate_td_log() * self.L.val_SI )
[docs] def ohc_subsurface_group_dependents(self): return ( [self.inl[0].m] + [var for c in self.inl + self.outl for var in [c.p, c.h]] + [self.D, self.L] )