Source code for stream.pipe_geometry

r"""
Pipe Geometry
=============
A pipe geometry interface used by different calculations, defined through
:class:`EffectivePipe`.
"""

from dataclasses import dataclass, field
from typing import Literal, TypeVar

import numpy as np

from stream.units import Meter, Meter2

__all__ = ["EffectivePipe"]

_T = TypeVar("_T", bound="EffectivePipe")


[docs] @dataclass(frozen=True, slots=True) class EffectivePipe: # noinspection PyUnresolvedReferences r""" A simple constant-cross-sectional pipe's parameters, where the parameters are "effectively" fitting to a circular pipe, and there is a distinction between "wet" (for fluid flow) and "heated" (for heat transfer) geometries. Essentially, for both perimeters, :math:`P_\text{heated}, P_\text{wet}`, the appropriate diameter is given using the flow area :math:`A`: .. math:: D = 4A / P Parameters ---------- length: Meter Pipe Length heated_perimeter: Meter The cross-sectional heated perimeter wet_perimeter: Meter The cross-sectional wet (hydraulic) perimeter area: Meter2 Flow cross-section heated_parts: tuple[Meter, ...] or None For a cross-section of the pipe, there may be several distinct regions which contribute flux (e.g. in MTR there are 2 in most cases). This is also the default case. width: Meter | None Channel width. When this is None, that means one should not use this attribute. This property is currently only used in the Sudo & Kaminaga CHF for one of its terms. See that usage in :func:`~stream.physical_models.thresholds.Sudo_Kaminaga_CHF`. For rectangular channels this is the longer side of the channel, which is commonly also the heated one. In annular channels, this is the hydraulic diameter of the channel. The reference for why this is so is by [#Mishima]_. depth: Meter | None Channel depth. This property is in use in the Elenbaas correlation for natural convection between parallel plates `stream.physical_models.heat_transfer_coeffcient.natural_convection.Elenbaas_h_spl` For rectangular channels this is the shorter side of the channel cross-section. References ---------- .. [#Mishima] K. Mishima, H. Nishihara, T. Shibata, "CHF Correlations Related to the Core Cooling of a Research Reactor", JAERI-M 84-073, International Meeting on Reduced Enrichment for Research and Test Reactors, Tokai, Japan, 1983. """ length: Meter heated_perimeter: Meter wet_perimeter: Meter area: Meter2 heated_parts: tuple[Meter, ...] = () width: Meter | None = None depth: Meter | None = None heated_diameter: Meter = field(init=False) hydraulic_diameter: Meter = field(init=False) def __post_init__(self): object.__setattr__(self, "heated_diameter", 4 * self.area / self.heated_perimeter) object.__setattr__(self, "hydraulic_diameter", 4 * self.area / self.wet_perimeter) object.__setattr__( self, "heated_parts", (self.heated_parts if self.heated_parts else (self.heated_perimeter / 2, self.heated_perimeter / 2)), ) assert np.isclose(np.sum(self.heated_parts), self.heated_perimeter, rtol=1e-15), ( f"The partitions of P_heated: {self.heated_parts} do not sum up to the total within relative tolerance 1e-15" )
[docs] @classmethod def rectangular( cls, length: Meter, edge1: Meter, edge2: Meter, heated_edge: Meter, one_sided: Literal["left", "right", None] = None, ) -> "EffectivePipe": """Create an EffectivePipe geometry for a channel with a rectangular cross-section, which is heated on two opposite sides. Parameters ---------- length: Meter Length along the flow. edge1, edge2: Meter Edges of the rectangular cross-section. heated_edge: Meter The heated section length which may differ from the edge itself. one_sided: bool A flag for whether heating happens on two opposite edges (False) or on just one edge (True). Returns ------- EffectivePipe """ parts = { "left": (heated_edge, 0.0), "right": (0.0, heated_edge), None: (heated_edge, heated_edge), }[one_sided] width = max(edge1, edge2) depth = min((edge1, edge2)) return cls( length=length, heated_perimeter=heated_edge if one_sided else 2 * heated_edge, wet_perimeter=2 * (edge1 + edge2), area=edge1 * edge2, heated_parts=parts, width=width, depth=depth, )
[docs] @classmethod def circular(cls, length: Meter, diameter: Meter): """Create an EffectivePipe geometry for a channel with a circular cross-section, which is fully heated. Parameters ---------- length: Meter Length along the flow. diameter: Meter Diameter of the pipe. Returns ------- EffectivePipe """ perimeter = diameter * np.pi return cls( length=length, heated_perimeter=perimeter, wet_perimeter=perimeter, area=np.pi * diameter**2 / 4, heated_parts=(perimeter, 0.0), width=diameter, )