#
# 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,
}