Battery SOH forecast explorer

How the capacity records of four batteries become a forecasting experiment: a model learns from three batteries, then forecasts the fourth in two ways, one cycle ahead from the real history, and many cycles ahead from its own predictions.

Data source. CALCE CS2 lithium-ion cycling data with a derived per-cycle state-of-health series. Detailed dataset provenance and usage conditions are on the Dataset page.

Notebook lineage

Source / context
PC5251 final project, National University of Singapore, 2024.
Notebook
Report.ipynb · 2024-05-03
Our changes
Our changes — 3 deviations from the notebook
  • Added persistence and linear-trend baselines, recursive forecasts on the held-out battery, remaining-life scoring with a sustained threshold, and training with three seeds.
  • Added the raw voltage and current views and the curve-shape analyses (PCA, UMAP, leave-one-battery-out and ΔQ(V)) from the earlier explorer page.
  • Made the spike filter backward-looking (causal) and used the window and learning rate that the notebook’s code runs (30 and 0.01).

What the data files contain

Each value stated in the source record is set beside a count made directly from the files: the four per-battery CSV files of SOH values and the raw CALCE workbooks.

PropertyStated valueMeasured in the filesCheck

CALCE CS2 batteries · PC5251 final project (2024) · 2010-08-16 to 2011-02-03 · four batteries · supplied per-cycle rows: CS2_35 882 · CS2_36 936 · CS2_37 972 · CS2_38 996 · SOH ratio from fixed 4,100 A·s denominator.

The data: state of health for every cycle of four batteries

Click a battery’s button to highlight its curve and show it in the spike-filter chart below.

State of health (SOH) is the charge a battery delivers in one discharge divided by a fixed reference of 4,100 ampere-seconds (about 1.14 Ah). It starts just below 1 and falls as the battery ages. One cycle is one full charge followed by one full discharge.

Training set

CS2_35 (882 cycles), CS2_36 (936 cycles), CS2_37 (972 cycles). The model learns only from windows cut from these three complete records.

Test set

CS2_38 (996 cycles). The model never sees this battery during training; every test score on this page is measured on it.

Why hold out a whole battery? Neighbouring windows from one battery overlap almost entirely. If windows were split at random, the test set would contain near-copies of training windows. Holding out all of CS2_38 tests whether what the model learned carries over to a battery it has never seen.

Removing isolated spikes with a backward-looking filter

Optional: the main forecasts on this page use the unfiltered SOH.

Some cycles show a sudden one-off dip in SOH, a measurement glitch rather than ageing. For each cycle, the filter fits a straight line to the preceding cycles, extends it to the current cycle, and replaces the value if it lies too far from that line, measured in median absolute deviations (MAD; a Hampel test). Because it uses only earlier cycles, it can run as each new cycle arrives. A plain running median would instead mistake a steady decline for a run of outliers. Methods that clean the whole curve at once, such as CEEMDAN, use later values, so they cannot be applied cycle by cycle.

Battery
Trailing window
Threshold
raw SOHfiltered SOHreplaced spike

On CS2_38, a length-7 trailing window at 3× MAD replaces 11 of 996 cycles; the largest correction is at cycle 86 (0.135 SOH). Every replacement uses only earlier cycles.

Retraining the model and scoring it on the filtered series (30-cycle window, seed 123) lowers the one-step error (MSE) from 1.30e-4 to 8.40e-5 and the mean recursive-forecast error from 2.40e-3 to 2.10e-3. Both errors are explained under Forecasting and evaluation.

From curve to design matrix: slide a window, collect a row

A design matrix is the table a model learns from: one row per example, one column per input value. Here each row is a run of consecutive SOH values from one battery, and the model learns to predict the value that comes next.

Training rows with the notebook’s 30-cycle window

—

Rows from the 3 training batteries with a window of w = 30 cycles. 2,430 rows train the model; the last 270, all late windows of CS2_37, are held back to monitor training (validation).

852CS2_35window → next-cycle pairs
906CS2_36window → next-cycle pairs
942CS2_37window → next-cycle pairs

How a row is built

  • Copy 30 consecutive SOH values from one battery into row X[i,:].
  • Store the next measured SOH beside the row as its label y_i. The model must predict it, so it is not part of X.
  • Move the window on by one cycle and repeat, staying within one battery; then stack the three batteries’ blocks into one table.

The matrix itself

The three views below are linked. “The whole matrix” shows the three training batteries stacked into one table; “Across one row” shows one window and the label that follows it; “Down one column” shows one position in the window across all rows, which traces the same SOH series shifted by one cycle per column. Press Slide rows, or move either slider, to change the selection in all three views.

The whole matrix

Each row is one window of past SOH values. The three batteries’ blocks are stacked one after another, and the labels y sit in a separate column beside X.

X[i,:]X[:,j]y_i

Selected entry x[300,30] = 0.84387; the label of this row is y[300] = 0.85065.

X[i,:]selected row

Across one row

These 30 values, in order, are what the model receives; the point after them is the next measured SOH, the label the model learns to predict.

CS2_35 cycles 301–330 become row 300 in that battery block; cycle 331 supplies y = 0.85065.

X[:,j]selected column

Down one column

A column is one position inside the window. The neighbouring column (dashed line) traces the same SOH series shifted by one cycle, so the columns are positions in time, not different measurements.

Column 30 holds position 30 of every window. Moving to the next column advances the same SOH series by one cycle; neighbouring rows likewise share 29 of 30 values.

No rescaling. SOH is already a ratio without units, so the values enter the table unchanged; cutting the series into windows is the only transformation.
The selected row and column as numbers

Window length: how many past cycles go into each row?

The window length w is the number of past SOH values in each row. Choosing a different w switches every result below to the run trained with that window: the number of rows, the saved training states, the one-step predictions and the recursive forecasts. The Design matrix section always shows w = 30, the notebook’s setting.

Window length w

With w = 30, the three training batteries yield 2,700 training rows and CS2_38 yields 966 test rows.

LSTM · the trained model

A recurrent neural network that reads the window one value at a time while carrying a memory (50 hidden units, 1 layer); a final linear layer outputs the next SOH. Training uses the Adam optimiser with learning rate 0.01, mean squared error as the loss, 300 full-batch epochs and seed 123, as in the notebook’s code.

Persistence · no training

Predicts that the next value equals the last value in the window. It has no fitted parameters, and on a slowly changing series it is hard to beat.

Linear trend · no training

Fits a least-squares line to the current window and extrapolates it. It carries a local slope but cannot bend or change regimes.

Watch the saved LSTM learn

16 stored model states

Press Replay, or drag the slider, to step through the saved stages of training, from the untrained model to the final one. “Prediction for unseen CS2_38” shows the model’s one-step predictions at that stage; “Training and validation loss” marks the stage on the error curves. Both charts keep fixed axes, so every change you see is a change in the model.

Prediction for unseen CS2_38

measured SOHLSTM snapshot

Training and validation loss

training MSEvalidation MSE

After 300 updates: training MSE 1.31e-4; the CS2_38 points shown score 1.54e-4. Most of the visible fit is already present; the numerical tail keeps improving more than the picture changes.

Final training MSE 1.31e-4 and validation MSE 1.45e-4. Each epoch is one update computed from all training rows at once, so 300 epochs are 300 updates. Testing uses the whole CS2_38 curve.

Which past values the LSTM relies on

Mean absolute sensitivity to each input position.

A nudge test changes one past value in the window slightly and measures how much the prediction moves. The chart shows the average size of that change for each position, counted back from the most recent cycle (1).

The most recent cycle accounts for about 22% of summed sensitivity and the last five for about 76%. One-step MSE spreads only 3% across w = 5, 10, 20, 30. Earlier positions still matter: flattening or shuffling them gives worse scores in the ablation table below.

Ablation test: scores with real, flattened and shuffled history

Protocol A: one step ahead, from the real history

The test used in the original notebook.

For every cycle of CS2_38, the model receives the measured values of the preceding window and predicts only the next cycle. Each prediction starts again from real measurements, so errors cannot build up.

Window length · same setting as above
measured SOHLSTM one-step
modelone-step test MSERMSE (SOH points)vs persistence
LSTM · trained on three batteries1.31e-41.140.94×
Persistence · no training1.39e-41.181.00×
Linear trend · no training1.68e-41.301.20×
The LSTM’s one-step error is small, but persistence, which simply repeats the last value, scores 1.39e-4 on the same task (LSTM ÷ persistence = 0.94×). A small one-step error mostly reflects how smooth the curve is; on its own it does not show that the LSTM learned how batteries degrade.

Protocol B: forecasting many cycles ahead

The test that matters for predicting remaining life.

Each forecast starts at an anchor cycle, the last measured value it is given. After that, each model’s own prediction is added to its input and used for the next step, so no further measurement corrects it (a recursive forecast). Persistence stays flat, the linear trend stays straight, and any bend in the LSTM forecast must come from what it learned. Move the anchor slider or pick an example below; choose a failure threshold to see when each forecast says the battery will reach it.

Failure threshold
measured SOHLSTM rolloutpersistencelinear trend
modelRMSE @ +10 cyclesRMSE @ +25 cyclesRMSE @ +50 cyclesRMSE @ +100 cyclesRMSE @ +200 cyclespredicted crossingRUL error
LSTM rollout0.440.590.961.531.65727-26
Persistence0.480.490.570.572.16never reachesno crossing
Linear trend0.390.621.212.202.75744-9

Measured crossing: cycle 753, so the true remaining useful life (RUL) from anchor 400 is 353 cycles. A crossing counts only when SOH stays at or below the threshold for 5 consecutive cycles; a positive RUL error means the forecast promises more life than the battery had.

After cycle 400, no measurement corrects the models. Persistence stays flat and the linear trend cannot bend; the LSTM beats persistence over the first 10 forecast cycles. Move the anchor: early starts must forecast much further ahead, while starts near the end have little future left to miss.

How forecast error is measured

One anchor's gaps, then the mean over every stored anchor.

one rollout MSE = mean[(forecast − measured)²] over its future cycles; mean rollout MSE = the average over 38 stored anchors.

measured SOHLSTM forecastgap band

At anchor 400, the widest absolute gap is 0.135 SOH. Squaring and averaging all 596 forecast-cycle gaps gives this rollout MSE 8.51e-4; averaging the same calculation over 38 stored anchors gives w = 30 mean rollout MSE 2.40e-3.

Window size under both protocols

Each window length is scored with both tests. The two tests have different error scales, so compare how much each line changes, not which line is higher.

Protocol AProtocol B

Across these windows, the one-step MSE changes by 3%, while the mean recursive MSE changes by a factor of 14.0.

The full window sweep is U-shaped

Recursive forecasts were also scored for longer windows, up to w = 150. Longer windows leave fewer training rows and add mostly repeated history.

The lowest mean recursive MSE among the windows tested is at w = 30 (2.40e-3); at w = 150 it is 6.46e-3, 2.7 times higher.

Repeating training with different seeds

The model was trained three times with the same data and settings but different random starting weights (seeds 123, 124 and 125). The table compares their one-step test scores; recursive forecasts were saved for seed 123.

training seedone-step test MSERMSE (SOH points)
seed 123 · used throughout1.31e-41.14
seed 1241.29e-41.14
seed 1251.32e-41.15

Notebook hyperparameter sweeps

The notebook also tried other settings, one at a time. Each table lists the one-step test MSE for each setting; at learning rate 0.2, training fails (MSE 0.032).

learning rate

5e-42.20e-4
5e-31.40e-4
1e-21.30e-4
5e-21.30e-4
2e-13.20e-2

hidden width

251.40e-4
501.30e-4
1001.20e-4
2001.30e-4

layers

11.30e-4
21.60e-4
31.70e-4

window

201.30e-4
301.30e-4
401.30e-4

What this analysis establishes

The numbers below follow the window, anchor and threshold you selected above.

A small one-step error does not show that the model learned how batteries degrade.

Persistence, which just repeats the last measured value, scores almost the same on the same task.

w = 30 · LSTM MSE 1.31e-4 · persistence 1.39e-4 · ratio 0.94×

The fitted network relies mainly on the most recent few cycles.

In the nudge test, most of the sensitivity sits in the last few values of each window. This is why the one-step score barely changes with window length.

most recent cycle ≈ 22% · last five ≈ 76% of summed sensitivity

Predicting remaining life needs the recursive test.

After the anchor, no measurement corrects the forecast, so errors build up; when the forecast crosses the threshold, and the shape of the long forecast, show how much.

anchor 400 · threshold 0.7 · measured crossing 753 · LSTM crossing 727

More history helps only up to a point.

Over the full range of windows tested, the mean recursive error falls and then rises: long windows leave fewer training rows and add repeated history.

lowest mean recursive MSE at w = 30 · 2.40e-3 · longest w = 150 gives 6.46e-3

Glossary

Each term is defined as it is used on this page.

Battery and measurement

CALCE CS2 battery

One of the four test batteries, CS2_35 to CS2_38: a 1.1 Ah lithium-cobalt-oxide (LCO) prismatic cell tested by the CALCE group at the University of Maryland, charged at constant current, then held at constant voltage, and discharged at room temperature. Each battery is a single cell, so “battery” and “cell” mean the same thing here.

State of health (SOH)

The charge a battery delivers in one discharge, C_t, divided by a fixed reference C₀ = 4,100 A·s (about 1.14 Ah). The rated capacity, 1.1 Ah, is about 3,960 A·s, so SOH starts slightly below 1. (The Battery curve shape demo divides by the rated 1.1 Ah instead.)

Capacity by current integration

The discharge current added up over the time of one discharge, which gives the charge delivered. Dividing it by the fixed reference gives that cycle’s SOH.

Degradation

The capacity loss built up through cycling, including changes in the electrodes and growth of the solid-electrolyte interphase. This page tracks its result, the fall in SOH.

Remaining useful life (RUL)

The number of cycles from the forecast anchor until SOH crosses the chosen failure threshold (see Sustained crossing). If the battery never crosses it, or crossed it before the anchor, there is no RUL to score.

From series to supervised data

Design matrix X

The table the model learns from: the stacked sliding windows. With w = 30 it has 2,700 rows and 30 columns, one per position in the window.

Window

A consecutive run of past SOH measurements used as one model input. Sliding it forward by one cycle creates the next, heavily overlapping row.

Row X[i,:]

One window of past SOH values: everything the model receives for one prediction. Its label is stored separately.

Column X[:,j]

One relative position inside every window. Adjacent columns are the same underlying sequence shifted by one cycle, not different physical sensors.

Label y_i

The measured SOH immediately after a row's window. Supervised regression trains the output to match this value.

Leakage

Information from the test battery, or from later cycles, reaching the model during training or preprocessing. Holding out a whole battery and using a backward-looking filter prevent it here.

Models and training

LSTM

A recurrent neural network with a memory state. Here one layer with 50 hidden units reads the ordered window and a linear head predicts one SOH value.

Persistence baseline

A forecast without training that copies the last measured value. It is the simplest baseline to compare a one-step forecast with.

Linear-trend baseline

A least-squares line fitted within the current window and extrapolated forward. Recursive use continues that line without curvature.

Full-batch epoch

One optimizer update computed from all training rows. The submitted loop therefore performs 300 parameter updates, not 300 mini-batch passes.

Seed

A number that fixes the model’s random starting weights. One-step results are stored for 3 seeds, recursive forecasts for seed 123.

Sensitivity profile

The average absolute change in the prediction when the value at one position in the window is nudged.

Ablation

A controlled corruption of input history—flattening or shuffling here—to test whether ordered earlier values contain information beyond the last point.

Forecast evaluation

One-step / teacher-forced protocol

Every prediction receives the real preceding window and forecasts only the next cycle, so measurement continuously resets accumulated error.

Recursive forecast (rollout)

After an anchor, each prediction is fed back as the next input. This exposes compounding error and is the protocol needed to draw a future trajectory.

Anchor and horizon

The anchor is the final measured cycle allowed to a rollout; the horizon is how many forecast cycles beyond it are scored.

MSE and RMSE

Mean squared error averages squared prediction gaps. RMSE is its square root; this page multiplies RMSE by 100 to report SOH percentage points.

Sustained crossing

A threshold event counts only after SOH remains at or below it for 5 consecutive cycles. This prevents isolated noise dips from becoming false end-of-life alarms.

Backward-looking (causal) spike filter

A Hampel-style filter that uses only earlier cycles: it fits a local trend to the last 7 cycles and replaces a value more than 3 scaled median absolute deviations away from it, with a residual floor of 0.01 (the default settings).

CEEMDAN

A whole-series decomposition mentioned by the source work. Because later values affect earlier components, it is useful for retrospective cleaning but leaks future information if used as an online forecast input.