Warren SmithIndependent checks of technical results Ask for a check

Cases / Battery simulation

A battery simulation finished normally while 2.44% of its lithium went unaccounted for

It said
The simulation finished normally.
Actually
An independent count found 2.44% of the lithium unaccounted for.
Then
A developer had reported a loss. I measured it, traced it to one missing term in an equation, and fixed it. The fix is released.

PyBaMM is open-source software for simulating batteries. A developer had reported that one of its models loses lithium, which a real battery cannot do, but not why. I counted the lithium myself, showed the loss came from one missing term in an equation rather than from rounding, and wrote the fix.

The technical record

What it reported

Solved, on every revision and every mesh.

What the check found

2.44% of the lithium unaccounted for, traced to one missing equation term.

A PyBaMM developer had reported that the BasicDFN battery model does not conserve lithium when the transference number varies, and the cause was open: equation or discretisation? I counted the lithium independently across three revisions and seven mesh and tolerance settings, measured a 2.44% loss every time, and showed it matches the rate predicted if one equation term is missing. My fix for that term is in the 26.9.0.0 release.

Refine the mesh and see if the loss goes away

If the missing lithium were numerical error, finer meshes and tighter tolerances would shrink it. Pick a setting. Values are the largest fraction of lithium unaccounted for during the run, across three revisions of the library.

BasicDFN, variable transference (the case under test)
BasicDFN, constant transference (control)
Full DFN model (control)

10−1510−1010−510−1

Across all 21 runs of the case under test (three revisions, seven settings), the unaccounted-for lithium stayed between 2.4407% and 2.4417%. It is not numerical error.

Did the counting instrument work? Five planted defects and two correct cases
Planted defectResidual the count reported
closed transfer1.8e-15 mol (correct case, balances)
boundary and source conserving3.3e-15 mol (correct case, balances)
altered inventory0.020 mol (caught)
wrong boundary sign0.600 mol (caught)
omitted source0.200 mol (caught)
wrong volume factor0.005 mol (caught)
synthetic imbalance0.010 mol (caught)

Source: controls.json and summary.csv in the public reproduction package at commit adaa877.

How the finding was reached

  1. Claim

    The model conserves lithium: the total in the electrolyte and particles stays constant over a cycle, up to solver tolerance.

  2. Test

    I froze three revisions (1,228 files hashed before any run) and counted the lithium from the mesh, the parameters and the raw concentrations. The count uses PyBaMM’s mesh and solution, but none of its averaging or total-lithium diagnostics. A second implementation of the count, written separately with explicit loops, was compared with the first on every run. 84 jobs in total.

  3. What happened

    With a concentration-dependent transference number, 2.44% of the lithium went missing on every revision, at every refinement.

  4. Trying to break the finding

    Is my instrument right? Five planted defects (an altered inventory, a reversed boundary sign, an omitted source, a wrong volume factor, a synthetic imbalance) each produced residuals of 0.005 to 0.6 mol, while correct cases stayed near 1e-15 mol. Is it numerical error? Tightening the tolerance to 1e-10 and refining to 80 cells changed only the fourth digit. Is it the whole model family? The constant-transference and full DFN models, run in the same harness, conserved to about one part in 1014.

  5. Evidence

    The loss rate matched the rate predicted by one missing transference-gradient term, instant by instant, to within 1.2e-17 mol/s in every run. The developer who opened #5700 confirmed the missing term and asked for a separate report. Separately, a half-cell diagnostic used the full-cell thickness where the electrolyte occupies 67 µm, overweighting it by a factor of 11.45.

  6. What survives

    For BasicDFN with a variable transference number, the equation, not the discretisation, was the cause of the loss. My PR #5747 fixed it; a maintainer’s PR #5765 fixed the diagnostic. Both shipped in PyBaMM 26.9.0.0. The wider discretisation question in #5700 is still open and is not addressed here.

What this does not show

One library, two models, one protocol, one parameter set. The full DFN model conserved, and nothing is said against it. Eleven half-cell jobs failed inside the solver at tight tolerance, so no convergence order is claimed. No physical battery was measured.

If this had been your system

If this were your model, you would have received the independent inventory code, the 84-job table, the five planted-defect results proving the count works, the named missing term, and a conservation test your CI can run on every release.