Source code for pytes.submodels.coupling

#
# Coupling submodels
#
"""
Coupling submodels: the coupling slot of DESIGN D5.

A coupling takes the fluid state and the storage surface state, provides the heat the fluid
gains per unit length of flow path, and provides the condition the storage applies on its
inner side (a heat flux into it, or a temperature). Where it can, the heat is taken from the
receiving side's discrete energy balance (the ledger, DESIGN D6), so that energy is
conserved exactly. The heat flux across a fluid-storage surface comes from a replaceable
surface flux law (:mod:`pytes.submodels.flux_laws`), Newton's law of cooling by default.
"""

import pybamm

from .. import deprecation
from .base_submodel import BaseSubModel
from .fluid import FLUID_ENTHALPY, FLUID_TEMPERATURE, HEAT_GAIN
from .flux_laws import FluidState, NewtonCooling

# The old names of the couplings (LedgerWallCoupling, FinPCMCoupling) warn and give the
# new ones
__getattr__ = deprecation.module_getattr(__name__)


[docs] class WallCoupling(BaseSubModel): """ A storage in direct contact with the flow path wall, exchanging heat with the fluid. The heat flux into the storage is ``flux_factor`` times the surface flux law's flux from the fluid state and the storage surface temperature ``T_s`` (by default ``h (T_f - T_s)``). The heat the fluid gains is the heat the storage loses through its boundaries, computed from the storage's own discrete energy balance: minus the integral of its conduction heating rate over the storage cross-section (the ledger, DESIGN D6). This telescopes to the discrete flux at the wall, so energy is conserved exactly, whatever the flux law or the conductivity. It is also the flux times the wall area: the storage's wall condition uses the conductivity on the wall face, as its conduction does (F-056). The coupling also provides the flux law's own variables (e.g. "Heat transfer coefficient [W.m-2.K-1]", see :meth:`pytes.submodels.SurfaceFluxLaw.variables`), and requires the variables the law reads (:attr:`pytes.submodels.SurfaceFluxLaw.coupled_variables`). For a storage averaged along the flow path (``geometry.averaged``), the flux law sees the fluid state averaged along the flow path, and the heat the fluid gains is spread uniformly along it; energy stays exact, as the storage integrates over the flow path length. Parameters ---------- param : object The model's parameters. fluid : :class:`pytes.fluids.ConstantPropertyLiquid` The fluid-property object. label : str The storage's label (see :class:`pytes.submodels.EnthalpyStorage`). geometry : :class:`pytes.submodels.StorageGeometry` or \ :class:`pytes.submodels.LumpedGeometry` The storage's geometry. flux_law : :class:`pytes.submodels.SurfaceFluxLaw`, optional The surface flux law (default :class:`pytes.submodels.NewtonCooling`). flux_factor : pybamm.Symbol or float, optional Ratio of the wall area to the storage's inner surface area (default 1). """ def __init__( self, param, fluid, label, geometry, flux_law=None, flux_factor=1, name="Wall coupling", ): self.fluid = fluid self.label = label self.storage_geometry = geometry self.flux_law = flux_law or NewtonCooling() self.flux_factor = flux_factor super().__init__(param, name)
[docs] def build(self): label = self.label geometry = self.storage_geometry for name, variable in self.flux_law.coupled_variables.items(): self.coupled(name, variable.domain) conduction = self.coupled( f"{label} conduction heating rate [W.m-3]", geometry.primary_domain ) h_f = self.coupled(FLUID_ENTHALPY, "flow path") T_f = self.coupled(FLUID_TEMPERATURE, "flow path") T_s = self.coupled(f"{label} surface temperature [K]", geometry.boundary_domain) heat_gain = -geometry.cross_section_integral(conduction) if geometry.averaged: path = geometry.flow_path h_f = pybamm.Integral(h_f, path.z) / path.length T_f = pybamm.Integral(T_f, path.z) / path.length heat_gain = pybamm.PrimaryBroadcast(heat_gain, "flow path") fluid_state = FluidState( h=h_f, T=T_f, mass_flow_rate=self.coupled("Mass flow rate [kg.s-1]") ) heat_flux = self.flux_law.heat_flux(self.fluid, fluid_state, T_s) self.variables = { HEAT_GAIN: heat_gain, f"{label} boundary heat flux [W.m-2]": self.flux_factor * heat_flux, **self.flux_law.variables(self.fluid, fluid_state), }
[docs] class FinRootCoupling(WallCoupling): """ Fins whose roots are on the flow path wall, exchanging heat with the fluid. The heat the fluid exchanges over one fin pitch of wall enters through the fin root alone, so the heat flux into the fin root is ``d_m / d_c`` times the surface flux law's flux. The heat the fluid gains is taken from the fins' discrete energy balance, as in :class:`WallCoupling`, so energy is conserved exactly. Parameters ---------- param : object The model's parameters. Uses ``d_c`` and ``d_m``. fluid : :class:`pytes.fluids.ConstantPropertyLiquid` The fluid-property object. label : str The fins' label (see :class:`pytes.submodels.EnthalpyStorage`). geometry : :class:`pytes.submodels.StorageGeometry` The fins' geometry. flux_law : :class:`pytes.submodels.SurfaceFluxLaw`, optional The surface flux law (default :class:`pytes.submodels.NewtonCooling`). """ def __init__( self, param, fluid, label, geometry, flux_law=None, name="Fin root coupling" ): super().__init__( param, fluid, label, geometry, flux_law=flux_law, flux_factor=param.d_m / param.d_c, name=name, )
[docs] class FinStorageCoupling(BaseSubModel): """ A storage material between two fins, at the fin temperature where it touches a fin. The material may change phase or not: the coupling is the same. The storage's boundary temperature is the fin temperature. The heat the fin gains per unit fin volume is minus the heat the storage on both sides gains through the fin face, computed from the storage's own discrete energy balance: ``-(2 / d_c)`` times the integral of its conduction heating rate across the half-gap (the flux at the midplane is zero). This telescopes to the discrete flux at the fin face, so energy is conserved exactly. Parameters ---------- param : object The model's parameters. Uses ``d_c``. pcm_label : str The label of the storage between the fins (see :class:`pytes.submodels.EnthalpyStorage`). pcm_geometry : :class:`pytes.submodels.StorageGeometry` Its geometry (across the half-gap). fin_label : str, optional The fins' label (default "Fin"). """ def __init__( self, param, pcm_label, pcm_geometry, fin_label="Fin", name="Fin-storage coupling", ): self.pcm_label = pcm_label self.pcm_geometry = pcm_geometry self.fin_label = fin_label super().__init__(param, name)
[docs] def build(self): geometry = self.pcm_geometry conduction = self.coupled( f"{self.pcm_label} conduction heating rate [W.m-3]", geometry.primary_domain, ) T_c = self.coupled( f"{self.fin_label} temperature [K]", geometry.boundary_domain ) fin_source = ( -2 / self.param.d_c * pybamm.Integral(conduction, geometry.variable) ) self.variables = { f"{self.fin_label} heat source [W.m-3]": fin_source, f"{self.pcm_label} boundary temperature [K]": T_c, }