Finned-tube models and their submodels#
PyTES models are fixed compositions of submodels: a fluid, a storage and a coupling, plus the device-level energy. This notebook shows how to inspect them, run a discharge, check the invariant every model meets (exact energy conservation), and set the initial state.
For the geometry, the equations and the options of the finned-tube models, with a sketch of each, see the Finned tube page of the documentation.
Tutorial 2 introduces these models.
import numpy as np
import pybamm
import pytes
model = pytes.SharpFrontModel()
for name, submodel in model.submodels.items():
print(f"{name}: {type(submodel).__name__}")
print(" requires:", submodel.requires)
print(" provides:", submodel.provides)
fluid: AdvectedFluid
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]')
storage: EnthalpyStorage
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]')
coupling: WallCoupling
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')
device energy: DeviceEnergy
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]')
The model is an ordinary PyBaMM model, so pybamm.Simulation applies directly. By default
the battery starts uniform at 70 °C (liquid) and is discharged with water at 10 °C. The model
has no cut-off: it runs to the final time, and cut-offs are the terminations of an experiment (pytes.experiments; see experiments).
sim = pybamm.Simulation(model)
solution = sim.solve([0, 3600], t_interp=np.linspace(0, 3600, 121))
sim.plot()
<pybamm.plotting.quick_plot.QuickPlot at 0x72accd818ad0>
Power is mdot (h_out - h_in), positive on discharge. The change in total thermal energy
(storage plus fluid) is minus the discharged energy, to round-off.
There is no state of charge: the energy a device can hold depends on the temperatures it
works between, which are a choice of the user. pytes.energy_between gives the energy between
two uniform states, here the 10 °C to 70 °C range of this discharge.
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("discharged energy [kWh]:", solution["Discharged energy [kWh]"].entries[-1])
energy_range = pytes.energy_between(model, 273.15 + 10, 273.15 + 70)
print("energy between 10 and 70 degC [kWh]:", energy_range / 3.6e6)
max conservation error [J]: 1.4901161193847656e-08
discharged energy [kWh]: 2.417954249270433
energy between 10 and 70 degC [kWh]: 2.4523701748815085
The initial state is uniform at "Initial temperature [K]", and solid at the melting
temperature. For any other start, for example part way through a discharge, start from the
end of an earlier solution with PyBaMM’s set_initial_conditions_from.
parameter_values = model.default_parameter_values
parameter_values["Initial temperature [K]"] = 273.15 + 30
warm = pybamm.Simulation(model, parameter_values=parameter_values).solve([0, 600])
print(
"initial temperature [degC]:", warm["Initial uniform temperature [degC]"].entries[0]
)
partial = pybamm.Simulation(model).solve([0, 900])
restarted = model.set_initial_conditions_from(partial, inplace=False)
continued = pybamm.Simulation(restarted).solve([0, 900])
print(
"outlet temperature at the end of the first run and at the restart [degC]:",
partial["Outlet temperature [degC]"].entries[-1],
continued["Outlet temperature [degC]"].entries[0],
)
initial temperature [degC]: 30.0
outlet temperature at the end of the first run and at the restart [degC]: 51.71779784644089 51.71779784644089
The default PCM melts over a range of temperatures, "PCM melting range [K]" = 2 K from
solidus to liquidus around its melting temperature, so the outlet temperature slopes through
the melting plateau. With a range of 0, the PCM melts at a sharp melting point and the
outlet stays flatter. "PCM smoothing width [K]" is separate: it only rounds the corners of the
enthalpy-temperature relation, for speed, and may be set to 0.
sharp = model.default_parameter_values
sharp["PCM melting range [K]"] = 0
sharp_solution = pybamm.Simulation(model, parameter_values=sharp).solve(
[0, 3600], t_interp=np.linspace(0, 3600, 121)
)
pybamm.dynamic_plot(
[solution, sharp_solution],
["Outlet temperature [degC]", "Average liquid fraction"],
labels=["2 K melting range (default)", "sharp melting point"],
)
<pybamm.plotting.quick_plot.QuickPlot at 0x72acc57ebe00>
The mush model resolves the fins and the PCM between them, and has the same outputs, so the two compare one to one.
mush = pytes.MushModel()
mush_solution = pybamm.Simulation(
mush, var_pts={mush.x: 10, mush.y: 20, mush.z: 20}
).solve([0, 3600], t_interp=np.linspace(0, 3600, 121))
pybamm.dynamic_plot(
[solution, mush_solution],
["Outlet temperature [degC]", "Power [kW]", "Discharged energy [kWh]"],
)
<pybamm.plotting.quick_plot.QuickPlot at 0x72acc41b82f0>