Packed-bed model#

PackedBedModel describes a bed of spherical PCM capsules with the heat transfer fluid flowing through the voids. At each point along the bed, heat conducts across a capsule (resolved radially) and into the fluid at its surface. It is built from the same submodels as the finned-tube models, so it has the same outputs, conserves energy exactly and runs the same experiments.

The default parameters are the experiments of Nallusamy et al. (2007): a 47 l tank of paraffin capsules in water, charged with water at 70 °C from 32 °C.

For the geometry, the equations and the options of the packed-bed model, with sketches, see the Packed bed page of the documentation.

Tutorial 3 introduces this model.

import matplotlib.pyplot as plt
import numpy as np
import pybamm

import pytes

model = pytes.PackedBedModel()
model.print_submodel_info()
fluid: AdvectedFluid (Heat transfer fluid)
    requires:
        Heat transfer fluid heat gain per unit length [W.m-1]
        Initial uniform temperature [K]
    provides:
        Heat transfer fluid specific enthalpy [J.kg-1]
        Inlet specific enthalpy [J.kg-1]
        Outlet specific enthalpy [J.kg-1]
        Mass flow rate [kg.s-1]
        Heat transfer fluid thermal energy [J]
        Heat transfer fluid temperature [K]
        Inlet temperature [K]
        Outlet temperature [K]
        Inlet-outlet temperature difference [K]
        Heat transfer fluid velocity [m.s-1]
        Volume flow rate [m3.s-1]
        Pumped volume [m3]
    parameters:
        Bed cross-sectional area [m2]
        Bed length [m]
        Bed porosity
        Heat transfer fluid density [kg.m-3]
        Heat transfer fluid specific heat capacity [J.kg-1.K-1]
        Inlet temperature [K]
        Mass flow rate [kg.s-1]
        PCM melting temperature [K]
        Reference temperature [K]
    assumptions:
        The fluid is incompressible, with constant density.
        Axial conduction and dispersion in the fluid are neglected.
        The flow is the same in every flow path.
storage: EnthalpyStorage (Phase-change material storage)
    requires:
        Phase-change material boundary heat flux [W.m-2]
        Initial uniform temperature [K]
    provides:
        Phase-change material enthalpy [J.m-3]
        Phase-change material enthalpy rate of change [W.m-3]
        Phase-change material conduction heating rate [W.m-3]
        Phase-change material thermal energy [J]
        Liquid fraction
        Phase
        Average liquid fraction
        Phase-change material temperature [K]
        Phase-change material average temperature [K]
        Phase-change material cross-section average temperature [K]
        Phase-change material surface temperature [K]
        Phase-change material thermal conductivity [W.m-1.K-1]
    parameters:
        Bed cross-sectional area [m2]
        Bed porosity
        Capsule radius [m]
        PCM latent heat [J.kg-1]
        PCM liquid phase conductivity [W.m-1.K-1]
        PCM liquid phase density [kg.m-3]
        PCM liquid phase specific heat capacity [J.kg-1.K-1]
        PCM melting range [K]
        PCM melting temperature [K]
        PCM smoothing width [K]
        PCM solid phase conductivity [W.m-1.K-1]
        PCM solid phase density [kg.m-3]
        PCM solid phase specific heat capacity [J.kg-1.K-1]
        Reference temperature [K]
    assumptions:
        Heat flows along the primary spatial variable only.
        Heat flows by conduction only (no convection in the liquid).
        The volume change on melting is neglected.
coupling: WallCoupling (Wall coupling)
    requires:
        Phase-change material conduction heating rate [W.m-3]
        Heat transfer fluid specific enthalpy [J.kg-1]
        Heat transfer fluid temperature [K]
        Phase-change material surface temperature [K]
        Mass flow rate [kg.s-1]
    provides:
        Heat transfer fluid heat gain per unit length [W.m-1]
        Phase-change material boundary heat flux [W.m-2]
        Heat transfer coefficient [W.m-2.K-1]
        Surface Reynolds number
        Surface Prandtl number
    parameters:
        Bed cross-sectional area [m2]
        Bed porosity
        Capsule radius [m]
        Heat transfer coefficient [W.m-2.K-1]
        Heat transfer fluid conductivity [W.m-1.K-1]
        Heat transfer fluid specific heat capacity [J.kg-1.K-1]
        Heat transfer fluid viscosity [Pa.s]
    assumptions:
        (none)
device energy: DeviceEnergy (Device energy)
    requires:
        Mass flow rate [kg.s-1]
        Outlet specific enthalpy [J.kg-1]
        Inlet specific enthalpy [J.kg-1]
        Phase-change material thermal energy [J]
        Heat transfer fluid thermal energy [J]
        Phase-change material average temperature [K]
        Phase-change material cross-section average temperature [K]
    provides:
        Power [W]
        Discharged energy [J]
        Charged energy [J]
        Discharged throughput [J]
        Charged throughput [J]
        Storage thermal energy [J]
        Total thermal energy [J]
        Heat loss power [W]
        Storage average temperature [K]
        Storage cross-section average temperature [K]
        Initial uniform temperature [K]
    parameters:
        Initial temperature [K]
    assumptions:
        There are no heat losses to the surroundings.
per-area totals: PerAreaTotals (Per-area totals)
    requires:
        Power [W]
        Discharged energy [J]
        Storage thermal energy [J]
        Total thermal energy [J]
    provides:
        Power per unit area [W.m-2]
        Discharged energy per unit area [J.m-2]
        Storage thermal energy per unit area [J.m-2]
        Total thermal energy per unit area [J.m-2]
    parameters:
        Bed cross-sectional area [m2]
    assumptions:
        (none)
geometry parameters:
    Bed length [m]
    Capsule radius [m]
t_end = 160 * 60
solution = pybamm.Simulation(model).solve(
    [0, t_end], t_interp=np.linspace(0, t_end, 321)
)
E = solution["Total thermal energy [J]"].entries
discharged = solution["Discharged energy [J]"].entries
print("max conservation error [J]:", np.max(np.abs(E - E[0] + discharged)))
print("stored energy [kWh]:", E[-1] / 3.6e6)
print(
    "energy between 32 and 70 degC [kWh]:",
    pytes.energy_between(model, 273.15 + 32, 273.15 + 70) / 3.6e6,
)
print(
    "stored energy per unit area [MJ.m-2]:",
    solution["Total thermal energy per unit area [J.m-2]"].entries[-1] / 1e6,
)
max conservation error [J]: 2.60770320892334e-08
stored energy [kWh]: 2.3031771680909383
energy between 32 and 70 degC [kWh]: 2.635389843901675
stored energy per unit area [MJ.m-2]: 81.45822913734806

The measured fluid temperatures at a quarter, half, three quarters and the full height of the bed (points) against the model (lines).

Z = model.default_parameter_values["Bed length [m]"]
fig, ax = plt.subplots(figsize=(6, 4))
for position, fraction in [("0.25L", 0.25), ("0.5L", 0.5), ("0.75L", 0.75), ("1L", 1)]:
    t_data, T_data = np.loadtxt(
        f"data/Nallusamy2007_HTF_{position}.csv", delimiter=",", skiprows=1, unpack=True
    )
    t = solution["Time [s]"].entries
    T = solution["Heat transfer fluid temperature [degC]"](t=t, z=fraction * Z)
    (line,) = ax.plot(t / 60, T, label=f"{fraction:g} L")
    ax.plot(t_data, T_data, "o", color=line.get_color(), markersize=3)
ax.set_xlabel("Time [min]")
ax.set_ylabel("Fluid temperature [°C]")
ax.legend()
plt.show()
../_images/5d767447f6ae6230fba22f72516c282dd85e0b93b9662b251c7990806bbd249d.png

The fluid temperatures agree with the measurements to 1 K to 2 K (root mean square). Inside the capsules the agreement is looser, about 5 K at 0.8 of the capsule radius, which depends on where the thermocouples sat and on the capsule shell, which the model neglects.

With options={"storage along flow path": "averaged"}, one capsule stands for the average along the bed and exchanges heat with the fluid averaged along it: much faster, but it cannot capture the thermal front moving through the bed. Every model has this option.

Note the dip of the averaged outlet below the initial 32 °C early on: the heat the capsule takes from the averaged fluid is taken uniformly along the bed, including near the outlet, where the fluid is still cold. This is a known artefact of this formulation, the published reduced model; energy is still conserved exactly.

averaged = pytes.PackedBedModel(options={"storage along flow path": "averaged"})
averaged_solution = pybamm.Simulation(averaged).solve(
    [0, t_end], t_interp=np.linspace(0, t_end, 321)
)
print(
    f"solve time: resolved {solution.solve_time}, averaged {averaged_solution.solve_time}"
)

t_data, T_data = np.loadtxt(
    "data/Nallusamy2007_HTF_1L.csv", delimiter=",", skiprows=1, unpack=True
)
fig, ax = plt.subplots(figsize=(6, 4))
for sol, label in [(solution, "resolved"), (averaged_solution, "averaged")]:
    ax.plot(
        sol["Time [min]"].entries, sol["Outlet temperature [degC]"].entries, label=label
    )
ax.plot(t_data, T_data, "ko", markersize=3, label="measured")
ax.set_xlabel("Time [min]")
ax.set_ylabel("Outlet temperature [°C]")
ax.legend()
plt.show()
solve time: resolved 1.532 s, averaged 75.950 ms
../_images/4ff8ceae97351ba4c16faac7379db0c474f3313ecc0e11d367500fefd9e7e560.png