Skip to main content
PyBaMM provides several plotting utilities built on Matplotlib. Matplotlib is an optional dependency — it is imported lazily inside plot methods so that PyBaMM can run on headless systems without a display.
Install Matplotlib with: pip install pybamm[plot]

Quick plotting with sim.plot()

After solving, call sim.plot() to open an interactive slider plot of the default output variables:
import pybamm

model = pybamm.lithium_ion.SPM()
sim = pybamm.Simulation(model)
sim.solve([0, 3600])

sim.plot()
This creates a pybamm.QuickPlot and calls dynamic_plot(), which renders a matplotlib figure with a time slider.

Choosing variables to plot

sim.plot([
    "Battery voltage [V]",
    "Current [A]",
    "Cell temperature [K]",
    "Negative particle surface concentration [mol.m-3]",
])

QuickPlot

pybamm.QuickPlot gives you full control over the plot configuration. It accepts solutions or simulation objects.
fig, axes = pybamm.QuickPlot(
    sim,
    output_variables=[
        "Battery voltage [V]",
        "Electrolyte concentration [mol.m-3]",
        "Negative particle surface concentration [mol.m-3]",
        "Positive particle surface concentration [mol.m-3]",
    ],
    labels=["SPM"],
    time_unit="hours",
    spatial_unit="um",
    figsize=(12, 8),
    n_rows=2,
)

Constructor options

ParameterDescriptionDefault
solutionsSolution(s) or Simulation(s) to plotrequired
output_variablesList of variable namesmodel defaults
labelsLegend labels for each solutionmodel names
colorsColor cycle["r", "b", "k", "g", "m", "c"]
linestylesLinestyle cycle["-", ":", "--", "-."]
figsizeFigure size (width, height)auto
n_rowsNumber of subplot rowsauto (square layout)
time_unit"hours", "minutes", or "seconds"auto
spatial_unit"m", "mm", or "um""um"
variable_limitsAxis limits: "fixed", "tight", or a dict"fixed"

Comparing multiple solutions

Pass a list of solutions or simulations to overlay them:
model_spm = pybamm.lithium_ion.SPM()
model_dfn = pybamm.lithium_ion.DFN()
param = pybamm.ParameterValues("Chen2020")

sim_spm = pybamm.Simulation(model_spm, parameter_values=param)
sim_dfn = pybamm.Simulation(model_dfn, parameter_values=param)

sim_spm.solve([0, 3600])
sim_dfn.solve([0, 3600])

pybamm.dynamic_plot(
    [sim_spm, sim_dfn],
    output_variables=["Battery voltage [V]", "Current [A]"],
    labels=["SPM", "DFN"],
)

Dynamic (interactive) plot

Call dynamic_plot() directly for the interactive slider:
quick_plot = pybamm.QuickPlot(sim)
quick_plot.dynamic_plot()
Or use the module-level shortcut:
pybamm.dynamic_plot(sim)

Static plot at a fixed time

Use plot(t) to render the figure at a single time point (in the solution’s time unit):
quick_plot = pybamm.QuickPlot(sim)
quick_plot.plot(t=1800)  # plot at t = 1800 s

Voltage components

pybamm.plot_voltage_components breaks down the terminal voltage into its contributing overpotentials:
pybamm.plot_voltage_components(sim)
You can also pass a Solution object:
pybamm.plot_voltage_components(sim.solution)
Options:
pybamm.plot_voltage_components(
    sim,
    show_legend=True,
    split_by_electrode=True,         # separate negative and positive contributions
    electrode_phases=("primary", "primary"),
)
This is accessible from the Simulation object too:
sim.plot_voltage_components()

Summary variables

For multi-cycle experiments, pybamm.plot_summary_variables plots cycle-level degradation metrics:
# After a long cycling experiment
pybamm.plot_summary_variables(sol)
Default variables plotted:
  • Capacity [A.h]
  • Loss of lithium inventory [%]
  • Total capacity lost to side reactions [A.h]
  • Loss of active material in negative electrode [%]
  • Loss of active material in positive electrode [%]
  • Stoichiometric limits x_100, x_0, y_100, y_0
Custom variables:
pybamm.plot_summary_variables(
    sol,
    output_variables=["Capacity [A.h]", "Loss of lithium inventory [%]"],
    labels=["Simulation 1"],
)
Compare multiple cycling simulations:
pybamm.plot_summary_variables([sol1, sol2], labels=["Cell A", "Cell B"])

2D plots

For spatially-resolved models (dimensionality > 0), use pybamm.plot2D to visualise a variable as a function of two spatial coordinates:
# Evaluate variable at a given time
t_plot = 1800  # seconds

x = sol["x [m]"](t=t_plot)
y = sol["y [m]"](t=t_plot)
z = sol["Electrolyte concentration [mol.m-3]"](t=t_plot)

pybamm.plot2D(x, y, z)
The x, y, and z arguments must be pybamm.Array objects. plot2D calls matplotlib.pyplot.contourf under the hood.
pybamm.plot2D(
    x, y, z,
    ax=my_axis,       # optional: plot on an existing axis
    show_plot=False,  # suppress plt.show() call
)

Creating a GIF

Generate an animated GIF of the solution evolving over time:
sim.create_gif(
    number_of_images=80,
    duration=0.1,               # seconds per frame
    output_filename="discharge.gif",
)
Or via the QuickPlot object:
qp = pybamm.QuickPlot(sim)
qp.create_gif(number_of_images=60, duration=0.15, output_filename="animation.gif")

Working with Matplotlib directly

You can access the underlying Matplotlib figure and axes from a QuickPlot:
import matplotlib.pyplot as plt

qp = pybamm.QuickPlot(sim, output_variables=["Battery voltage [V]"])
qp.plot(t=0)  # render once

fig = qp.fig
ax = qp.axes[0][0]

ax.set_title("DFN Discharge")
ax.set_xlabel("Time (s)")
fig.savefig("voltage.png", dpi=150, bbox_inches="tight")
plt.show()
Set show_plot=False in plot_voltage_components, plot_summary_variables, or plot2D when you want to combine multiple figures before calling plt.show() once at the end.

Build docs developers (and LLMs) love