Source code for pytes.models.finned_tube.base_finned_tube

#
# Base model of the finned-tube family
#
"""Base class of the finned-tube family: the tube, the PCM annulus and the defaults."""

import pybamm

from ... import compat
from ...submodels import AdvectedFluid, FlowPath, HeatTransferCoefficientOptions
from ..base_model import BaseModel
from .parameter_values import get_parameter_values
from .parameters import FinnedTubeParameters


[docs] class BaseFinnedTubeModel(BaseModel): """ Base model of the finned-tube family: heat transfer fluid flowing along a tube that exchanges heat with the surrounding PCM and fins. One tube is modelled; device totals are multiplied by the number of tubes. See :mod:`pytes.models.finned_tube.parameters` for the geometry and the assumptions. Options: - "storage along flow path": "resolved" (default), the storage at every point along the tube; or "averaged", one storage cross-section standing for the average along the tube, exchanging heat with the fluid averaged along it (DESIGN D3, D4). - "heat transfer coefficient": "constant" (default), "Heat transfer coefficient [W.m-2.K-1]" (:class:`pytes.submodels.NewtonCooling`); or a Nusselt correlation for the flow in the tubes, by name: "Gnielinski" (:class:`pytes.submodels.Gnielinski`, issue #6), or any correlation registered for the flow ``"tube"`` with :func:`pytes.submodels.register_correlation`. Parameters ---------- name : str The name of the model. options : dict, optional The model's options. Attributes ---------- y : pybamm.SpatialVariable Distance from the tube axis [m], from the tube radius to the storage outer radius ``R_f + D``, in the "storage depth" domain (cylindrical coordinates), with the "flow path" as its secondary domain unless the storage is averaged. z : pybamm.SpatialVariable Distance along the tube from the inlet [m], in the "flow path" domain. """ allowed_options = { "storage along flow path": ("resolved", "averaged"), "heat transfer coefficient": HeatTransferCoefficientOptions("tube"), } def __init__(self, name, options=None): super().__init__(name=name, options=options) @property def averaged(self): """Whether the storage is averaged along the tube""" return self.options["storage along flow path"] == "averaged"
[docs] def storage_domains(self, *domains): """The given storage domains, then the flow path unless the storage is averaged""" domains = [*domains] if self.averaged else [*domains, "flow path"] return dict(zip(("primary", "secondary", "tertiary"), domains, strict=False))
[docs] def set_spatial_variables(self): self.y = pybamm.SpatialVariable( "y", domains=self.storage_domains("storage depth"), coord_sys="cylindrical polar", ) self.z = pybamm.SpatialVariable( "z", domains={"primary": "flow path"}, coord_sys="cartesian" )
[docs] def get_parameters(self): return FinnedTubeParameters()
@property def flow_path(self): """The flow paths, one per tube""" param = self.param return FlowPath(self.z, param.Z, param.area, param.N_tubes)
[docs] def fluid_submodel(self): """The heat transfer fluid in the tubes""" return AdvectedFluid(self.param, self.param.fluid, self.flow_path)
[docs] def flux_law(self): """The surface flux law at the tube wall, from the "heat transfer coefficient" option""" values = self.allowed_options["heat transfer coefficient"] return values.flux_law( self.options["heat transfer coefficient"], self.param.R_f, self.param.N_tubes, )
[docs] def storage_flow_path(self): """The flow path a storage geometry is averaged along, or None if resolved""" return self.flow_path if self.averaged else None
[docs] def integrate_over_tube(self, symbol): """ Integrate a symbol over one tube: for a "storage depth" symbol, its integral over the annulus around the tube, ``int int symbol 2 pi y dy dz``; for a "flow path" symbol, ``int symbol dz``. A storage averaged along the tube is the same at every ``z``, so its integral along the tube is the tube length times its value. """ if symbol.domain == ["storage depth"]: symbol = pybamm.Integral(symbol, self.y) if symbol.domain == []: return self.param.Z * symbol return pybamm.Integral(symbol, self.z)
@property def default_geometry(self): """ The PCM annulus (``y`` from the tube radius to the storage outer radius ``R_f + D``) and the tube (``z`` from 0 to the tube length) """ return pybamm.Geometry( { "storage depth": {self.y: {"min": self.param.R_f, "max": self.param.H}}, "flow path": {self.z: {"min": pybamm.Scalar(0), "max": self.param.Z}}, } ) @property def default_submesh_types(self): """Uniform 1D meshes in every domain""" return { domain: pybamm.MeshGenerator(pybamm.Uniform1DSubMesh) for domain in self.default_geometry } @property def default_spatial_methods(self): """Finite volumes in every domain; the tube uses the fluid's spatial method""" spatial_methods = { domain: pybamm.FiniteVolume() for domain in self.default_geometry } spatial_methods["flow path"] = compat.fluid_spatial_method() return spatial_methods @property def default_quick_plot_variables(self): """Variables shown by :class:`pybamm.QuickPlot` by default (DESIGN D15)""" return [ ["Inlet temperature [degC]", "Outlet temperature [degC]"], "Heat transfer fluid temperature [degC]", "Power [kW]", "Discharged energy [kWh]", ] @property def default_solver(self): """IDAKLU with ``rtol=1e-5`` and ``atol=1e-8``""" # Tighter than the IDAKLU defaults (rtol=1e-4, atol=1e-6): the PCM temperature is # computed from the enthalpy, so its error is only controlled through the # enthalpy (scaled by the latent heat). These keep the solver error in the PCM # temperature at about 0.05 K or less on the default meshes; the mush model needs # the tighter atol on its default mesh return pybamm.IDAKLUSolver(rtol=1e-5, atol=1e-8) @property def default_parameter_values(self): """See :func:`pytes.models.finned_tube.get_parameter_values`""" return get_parameter_values()