Tip

An interactive online version of this notebook is available, which can be accessed via Open this notebook in Google Colab


Alternatively, you may download this notebook and run it offline.

Attention

You are viewing this notebook on the latest version of the documentation, where these notebooks may not be compatible with the stable release of PyBaMM since they can contain features that are not yet released. We recommend viewing these notebooks from the stable version of the documentation. To install the latest version of PyBaMM that is compatible with the latest notebooks, build PyBaMM from source.

Callbacks#

WARNING: This is a new, experimental feature, and the API may change in future.

Callbacks provide hooks for users to interact with the different parts of the PyBaMM pipeline, for example to log, save, or visualize outputs of intermediate functions.

A list of available callbacks can be found in the API docs. Any number of callbacks can be provided as a list, and they will each be called in turn in the order provided.

The base class `pybamm.callbacks.Callback <https://docs.pybamm.org/en/latest/source/api/callbacks.html#pybamm.callbacks.Callback>`__ documents the available callback methods, at which point in the pipeline they are called, and what arguments are passed to them.

[1]:
%pip install "pybamm[plot,cite]" -q
import pybamm

model = pybamm.lithium_ion.DFN()
experiment = pybamm.Experiment(
    [
        (
            "Discharge at C/5 for 10 hours or until 3.3 V",
            "Charge at 1 A until 4.1 V",
            "Hold at 4.1 V until 10 mA",
        ),
    ]
    * 3
)
sim = pybamm.Simulation(model, experiment=experiment)
Note: you may need to restart the kernel to use updated packages.

Logging callback#

The LoggingCallback can be used to log the progress of the simulation. The default is to print to stdout (this callback is automatically created if no other LoggingCallback exists)

[2]:
pybamm.set_logging_level("NOTICE")
sim.solve();
2022-03-10 10:17:57.074 - [NOTICE] callbacks.on_cycle_start(174): Cycle 1/3 (18.917 ms elapsed) --------------------
2022-03-10 10:17:57.074 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V
2022-03-10 10:17:58.139 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 2/3: Charge at 1 A until 4.1 V
2022-03-10 10:17:58.505 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 3/3: Hold at 4.1 V until 10 mA
2022-03-10 10:17:58.952 - [NOTICE] callbacks.on_cycle_start(174): Cycle 2/3 (1.898 s elapsed) --------------------
2022-03-10 10:17:58.953 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V
2022-03-10 10:18:00.203 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 2/3: Charge at 1 A until 4.1 V
2022-03-10 10:18:00.464 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 3/3: Hold at 4.1 V until 10 mA
2022-03-10 10:18:00.675 - [NOTICE] callbacks.on_cycle_start(174): Cycle 3/3 (3.620 s elapsed) --------------------
2022-03-10 10:18:00.675 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V
2022-03-10 10:18:01.963 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 2/3: Charge at 1 A until 4.1 V
2022-03-10 10:18:02.232 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 3/3: Hold at 4.1 V until 10 mA
2022-03-10 10:18:02.452 - [NOTICE] callbacks.on_experiment_end(222): Finish experiment simulation, took 3.620 s

or to a separate file

[3]:
callback = pybamm.callbacks.LoggingCallback("output.log")
sim.solve(callbacks=callback)

# Read the file that has been written, which was saved to callback.logfile
with open(callback.logfile) as f:
    print(f.read())

# Remove the log file
import os

os.remove(callback.logfile)
2022-03-10 10:18:02.483 - [NOTICE] callbacks.on_cycle_start(174): Cycle 1/3 (24.357 ms elapsed) --------------------
2022-03-10 10:18:02.483 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V
2022-03-10 10:18:03.799 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 2/3: Charge at 1 A until 4.1 V
2022-03-10 10:18:04.065 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 3/3: Hold at 4.1 V until 10 mA
2022-03-10 10:18:04.394 - [NOTICE] callbacks.on_cycle_start(174): Cycle 2/3 (1.935 s elapsed) --------------------
2022-03-10 10:18:04.394 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V
2022-03-10 10:18:05.607 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 2/3: Charge at 1 A until 4.1 V
2022-03-10 10:18:05.858 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 3/3: Hold at 4.1 V until 10 mA
2022-03-10 10:18:06.059 - [NOTICE] callbacks.on_cycle_start(174): Cycle 3/3 (3.600 s elapsed) --------------------
2022-03-10 10:18:06.059 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V
2022-03-10 10:18:07.280 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 2/3: Charge at 1 A until 4.1 V
2022-03-10 10:18:07.539 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 3/3: Hold at 4.1 V until 10 mA
2022-03-10 10:18:07.743 - [NOTICE] callbacks.on_experiment_end(222): Finish experiment simulation, took 3.600 s

Custom callbacks#

Custom callbacks should subclass the class pybamm.callbacks.Callback class, and implement a subset of the callback methods on_<event>, which all take as input the dictionary logs.

[4]:
class CustomCallback(pybamm.callbacks.Callback):
    def on_experiment_end(self, logs):
        print(f"We are at the end of the simulation. Logs are {logs}")
[5]:
# Note the default `LoggingCallback` is also called
sim.solve(callbacks=CustomCallback());
2022-03-10 10:18:07.776 - [NOTICE] callbacks.on_cycle_start(174): Cycle 1/3 (24.415 ms elapsed) --------------------
2022-03-10 10:18:07.776 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V
2022-03-10 10:18:09.125 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 2/3: Charge at 1 A until 4.1 V
2022-03-10 10:18:09.390 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 3/3: Hold at 4.1 V until 10 mA
2022-03-10 10:18:09.714 - [NOTICE] callbacks.on_cycle_start(174): Cycle 2/3 (1.963 s elapsed) --------------------
2022-03-10 10:18:09.714 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V
2022-03-10 10:18:11.025 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 2/3: Charge at 1 A until 4.1 V
2022-03-10 10:18:11.288 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 3/3: Hold at 4.1 V until 10 mA
2022-03-10 10:18:11.498 - [NOTICE] callbacks.on_cycle_start(174): Cycle 3/3 (3.747 s elapsed) --------------------
2022-03-10 10:18:11.499 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V
2022-03-10 10:18:12.748 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 2/3: Charge at 1 A until 4.1 V
2022-03-10 10:18:13.007 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 3/3: Hold at 4.1 V until 10 mA
2022-03-10 10:18:13.230 - [NOTICE] callbacks.on_experiment_end(222): Finish experiment simulation, took 3.747 s
We are at the end of the simulation of the simulation. Logs are {'stopping conditions': {'voltage': None, 'capacity': None}, 'cycle number': (3, 3), 'elapsed time': pybamm.TimerTime(3.7469801669999967), 'step number': (3, 3), 'step operating conditions': 'Hold at 4.1 V until 10 mA', 'termination': 'event: Current cut-off (negative) [A] [experiment]', 'summary variables': {'Minimum measured discharge capacity [A.h]': -0.16806782223941116, 'Maximum measured discharge capacity [A.h]': 0.6889979156579616, 'Measured capacity [A.h]': 0.8570657378973728, 'Minimum voltage [V]': 3.300010000000005, 'Maximum voltage [V]': 4.100000000000016, 'Positive electrode capacity [A.h]': 1.9464430360887066, 'Change in positive electrode capacity [A.h]': 0.0, 'Loss of active material in positive electrode [%]': 1.1102230246251565e-14, 'Change in loss of active material in positive electrode [%]': 0.0, 'Loss of lithium inventory [%]': -2.220446049250313e-14, 'Change in loss of lithium inventory [%]': 0.0, 'Loss of lithium inventory, including electrolyte [%]': 0.0, 'Change in loss of lithium inventory, including electrolyte [%]': 0.0, 'Total lithium [mol]': 0.0799932053645051, 'Change in total lithium [mol]': 0.0, 'Total lithium in electrolyte [mol]': 0.002410514999999987, 'Change in total lithium in electrolyte [mol]': -2.168404344971009e-18, 'Total lithium in positive electrode [mol]': 0.037303833837759315, 'Change in total lithium in positive electrode [mol]': 6.938893903907228e-18, 'Total lithium in particles [mol]': 0.07758269036450512, 'Change in total lithium in particles [mol]': 0.0, 'Total lithium lost [mol]': 0.0, 'Change in total lithium lost [mol]': 0.0, 'Total lithium lost from particles [mol]': -1.3877787807814457e-17, 'Change in total lithium lost from particles [mol]': 0.0, 'Total lithium lost from electrolyte [mol]': 1.2576745200831851e-17, 'Change in total lithium lost from electrolyte [mol]': 2.168404344971009e-18, 'Loss of lithium to SEI [mol]': 0.0, 'Change in loss of lithium to SEI [mol]': 0.0, 'Loss of capacity to SEI [A.h]': 0.0, 'Change in loss of capacity to SEI [A.h]': 0.0, 'Total lithium lost to side reactions [mol]': 0.0, 'Change in total lithium lost to side reactions [mol]': 0.0, 'Total capacity lost to side reactions [A.h]': 0.0, 'Change in total capacity lost to side reactions [A.h]': 0.0, 'Local ECM resistance [Ohm]': -0.1921801399643974, 'Change in local ECM resistance [Ohm]': -0.3385111526792064, 'Negative electrode capacity [A.h]': 1.139331489107912, 'Change in negative electrode capacity [A.h]': 0.0, 'Loss of active material in negative electrode [%]': -2.220446049250313e-14, 'Change in loss of active material in negative electrode [%]': 0.0, 'Total lithium in negative electrode [mol]': 0.0402788565267458, 'Change in total lithium in negative electrode [mol]': -6.938893903907228e-18, 'Loss of lithium to lithium plating [mol]': 0.0, 'Change in loss of lithium to lithium plating [mol]': 0.0, 'Loss of capacity to lithium plating [A.h]': 0.0, 'Change in loss of capacity to lithium plating [A.h]': 0.0, 'x_100': 0.9493038520218161, 'y_100': 0.5126064431891194, 'C': 0.8728195070455337, 'x_0': 0.18322346594476246, 'y_0': 0.9610241419672079, 'Un(x_100)': 0.07517904666112207, 'Un(x_0)': 0.2527477655253842, 'Up(y_100)': 4.175179046661121, 'Up(y_0)': 3.357747765525383, 'Up(y_100) - Un(x_100)': 4.099999999999999, 'Up(y_0) - Un(x_0)': 3.1049999999999986, 'n_Li_100': 0.07758269036450512, 'n_Li_0': 0.07758269036450512, 'n_Li': 0.07758269036450512, 'C_n': 1.139331489107912, 'C_p': 1.9464430360887066, 'C_n * (x_100 - x_0)': 0.8728195070455337, 'C_p * (y_100 - y_0)': 0.8728195070455337, 'Capacity [A.h]': 0.8728195070455337}}

References#

The relevant papers for this notebook are:

[6]:
pybamm.print_citations()
[1] Joel A. E. Andersson, Joris Gillis, Greg Horn, James B. Rawlings, and Moritz Diehl. CasADi – A software framework for nonlinear optimization and optimal control. Mathematical Programming Computation, 11(1):1–36, 2019. doi:10.1007/s12532-018-0139-4.
[2] Marc Doyle, Thomas F. Fuller, and John Newman. Modeling of galvanostatic charge and discharge of the lithium/polymer/insertion cell. Journal of the Electrochemical society, 140(6):1526–1533, 1993. doi:10.1149/1.2221597.
[3] Charles R. Harris, K. Jarrod Millman, Stéfan J. van der Walt, Ralf Gommers, Pauli Virtanen, David Cournapeau, Eric Wieser, Julian Taylor, Sebastian Berg, Nathaniel J. Smith, and others. Array programming with NumPy. Nature, 585(7825):357–362, 2020. doi:10.1038/s41586-020-2649-2.
[4] Scott G. Marquis, Valentin Sulzer, Robert Timms, Colin P. Please, and S. Jon Chapman. An asymptotic derivation of a single particle model with electrolyte. Journal of The Electrochemical Society, 166(15):A3693–A3706, 2019. doi:10.1149/2.0341915jes.
[5] Peyman Mohtat, Suhak Lee, Jason B Siegel, and Anna G Stefanopoulou. Towards better estimability of electrode-specific state of health: decoding the cell expansion. Journal of Power Sources, 427:101–111, 2019.
[6] Valentin Sulzer, Scott G. Marquis, Robert Timms, Martin Robinson, and S. Jon Chapman. Python Battery Mathematical Modelling (PyBaMM). Journal of Open Research Software, 9(1):14, 2021. doi:10.5334/jors.309.
[7] Pauli Virtanen, Ralf Gommers, Travis E. Oliphant, Matt Haberland, Tyler Reddy, David Cournapeau, Evgeni Burovski, Pearu Peterson, Warren Weckesser, Jonathan Bright, and others. SciPy 1.0: fundamental algorithms for scientific computing in Python. Nature Methods, 17(3):261–272, 2020. doi:10.1038/s41592-019-0686-2.