Composing a model from submodels#

Every PyTES model is a fixed composition of submodels. print_submodel_info shows them: for each slot, the class, the variables it requires and provides, and its assumptions. To try a new submodel, build a pytes.ComposedModel from the submodels you want. The wiring is checked as for the built-in models, and the result is an ordinary PyBaMM model.

import numpy as np
import pybamm

import pytes

base = pytes.SharpFrontModel()
base.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:
        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]
        Number of tubes
        PCM melting temperature [K]
        Reference temperature [K]
        Tube length [m]
        Tube radius [m]
    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:
        Fin pitch [m]
        Fin thickness [m]
        Metal conductivity [W.m-1.K-1]
        Metal density [kg.m-3]
        Metal specific heat capacity [J.kg-1.K-1]
        Number of tubes
        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:
        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]
        Number of tubes
        Tube radius [m]
    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.
geometry parameters:
    Storage depth [m]
    Tube length [m]
    Tube radius [m]

Here the coupling’s surface flux law is replaced by a nonlinear one. The submodels of the base model are reused, not modified, and defaults_from=base takes its geometry, mesh, solver and parameter values.

class CubicFlux(pytes.submodels.SurfaceFluxLaw):
    """Heat flux c (T_f - T_s)**3, positive into the storage"""

    def heat_flux(self, fluid, fluid_state, T_s):
        return (
            pybamm.Parameter("Cubic flux coefficient [W.m-2.K-3]")
            * (fluid_state.T - T_s) ** 3
        )


storage = base.submodels["storage"]
coupling = pytes.submodels.WallCoupling(
    base.param,
    base.param.fluid,
    storage.label,
    storage.storage_geometry,
    flux_law=CubicFlux(),
)
cubic = pytes.ComposedModel(
    {**base.submodels, "coupling": coupling},
    base.param,
    name="Cubic flux",
    defaults_from=base,
)
parameter_values = cubic.default_parameter_values
parameter_values["Cubic flux coefficient [W.m-2.K-3]"] = 40

Energy is still conserved exactly, whatever the flux law, because the coupling takes the fluid’s heat from the storage’s own energy balance.

t = np.linspace(0, 3600, 121)
solutions = [
    pybamm.Simulation(base).solve([0, 3600], t_interp=t),
    pybamm.Simulation(cubic, parameter_values=parameter_values).solve(
        [0, 3600], t_interp=t
    ),
]
for solution in solutions:
    E = solution["Total thermal energy [J]"].entries
    discharged = solution["Discharged energy [J]"].entries
    print(
        solution.all_models[0].name,
        "- max conservation error [J]:",
        np.max(np.abs(E - E[0] + discharged)),
    )
pybamm.dynamic_plot(solutions, ["Outlet temperature [degC]", "Power [kW]"])
Sharp front model - max conservation error [J]: 1.4901161193847656e-08
Cubic flux - max conservation error [J]: 3.91155481338501e-08
<pybamm.plotting.quick_plot.QuickPlot at 0x7133212e2510>