Battery curve-shape explorer

How the voltage curves of four ageing batteries become rows that keep only their shape, and what PCA, UMAP, a held-out-battery test and ΔQ(V) show about how that shape changes with age.

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 CALCE files. The four batteries’ archives hold 106 exported workbooks; after removing the duplicate final exports, the analysis uses 104 workbooks (test sessions) and 1,119,328 samples logged every 30 seconds, roughly 280,000 per battery.

PropertyStated valueMeasured in the filesCheck

CALCE CS2 batteries · PC5251 final project (2024) · 2010-08-16 to 2011-02-03 · four batteries · within-cycle voltage/current samples and 128 standardised shape coordinates.

Battery-by-battery inventory
batteryrecorded samplesrecord date spansessions usedlogged cyclesrows in Xcycles checked against Statistics sheetmedian relative differenceover 1%
CS2_35260,9312010-08-16 → 2011-02-03248867745592.504e-90
CS2_36269,6562010-08-16 → 2011-02-02269767605952.982e-80
CS2_37290,9982010-08-16 → 2011-02-02271,0438456430.000e+00
CS2_38297,7432010-08-16 → 2011-02-03271,0328666264.198e-70

What the raw files required

The CALCE exports are not a ready-made table of cycles. These findings shaped how the cycles were rebuilt and screened.

The files are test sessions, not a table of cycles

Files must be ordered by the timestamps inside them; cycle numbers restart in every file, and capacity columns keep adding up within a file.

104 files · 3,937 logged cycles

So the analysis rebuilds one continuous cycle count and measures each charge and discharge from its own start and end.

Two files are duplicate exports

Two batteries’ final sessions were exported twice under different dates: the files differ byte for byte, but the measurements in them are identical.

2 duplicate exports · 50 cycles each

Duplicates are removed by comparing the measurements, before any analysis.

Every removed cycle has a named reason

Missing halves, off-protocol cycles, truncated cycles and cycles with too few samples are counted separately.

16 half missing · 4 off protocol · 120 truncated · 552 too few samples

The final matrix keeps 3,245 of 3,937 logged cycles.

Recomputed capacities match the cycler’s own records

Where the cycler’s Statistics sheet records a capacity for a cycle, the capacity recomputed from the samples agrees with it.

2,423 checked cycles · largest median relative difference 4.198e-7

The relative difference stays within 10⁻⁶.

From raw exports to a table of curve shapes

A design matrix is the table a model learns from: one row per example, one column per input value. Here each row is one charge–discharge cycle, and each column is the voltage at one fixed point in that cycle, after the curve’s level and spread have been removed.

Size of the table (design matrix) given to PCA

—

Each row is one screened charge–discharge cycle; each column is one fixed point in the charge or the discharge, measured as a fraction of that half’s duration.

4batteries
1,119,328raw samples
692cycles excluded

How a row is built

  • Order the files by the timestamps inside them, rebuild one continuous cycle count, and remove duplicate exports.
  • Find the charge where the current is above 0.02 A and the discharge where it stays below -0.5 A, using the current rather than the logged step number.
  • Resample the charge and the discharge separately to 64 points each, so the switch from charge to discharge always falls at column 64.
  • Standardise each row (subtract its mean voltage, divide by its spread) so only the shape remains. The removed level and spread, the durations, the capacity and the coulombic efficiency are kept as labels beside X.

Fig 1 · One full cycle, as the cycler logged it

CS2_35, cycle 30 · SOH 0.98 · 30 s sampling

Voltage (upper) and current (lower) on one time axis

How one cycle runs. The cycler charges at a constant 0.55 A until the voltage reaches 4.2 V (constant current, CC), then holds the voltage while the current falls to 0.05 A (constant voltage, CV). After a rest, it discharges at 1.1 A until the voltage drops to 2.7 V. The upper chart is the battery’s voltage; the lower chart is the current the cycler applies.

What the export files do. File names carry the export date, not the date of the data; cycle counters restart in every file; capacity adds up within a file; and each duplicate final export would add 50 cycles that were already counted.

Fig 2 · How the discharge curve changes with age

Discharge voltage over time, V(t): seven cycles across life

The same cycles against charge delivered, V(Q)

cycle 18cycle 161cycle 301cycle 439cycle 581cycle 724cycle 869 · not in X

SOH over life; dots mark the seven cycles above

The curve shrinks and sags. Later discharges are shorter, and the sloping middle sits lower. In V(Q) the curves separate first at low voltage, the region that ΔQ(V) measures later on this page. In “SOH over life”, ticks mark the start of each test session; SOH dips and partly recovers around them, so it falls steadily only on average. Dashed late curves are complete but have fewer than 64 samples, so they are left out of X.

The selected raw curves as a table

Fig 3 · Cycles removed by each screening rule, per battery

Counts are cycles; each removed cycle fails one of four rules.
batteryfilesloggedhalf missingoff protocoltruncatedtoo few samplesrows in Xkept
CS2_352488640288077487.4%
CS2_3626976302918476077.9%
CS2_37271,043522916284581.0%
CS2_38271,032423412686683.9%
total1043,9371641205523,24582.4%

The rules, in order. “Half missing”: a file boundary cut the cycle, so only the charge or only the discharge is present. “Off protocol”: the cycle does not follow the test schedule; the batteries arrived already formed, so this check replaces a rule about initial conditioning cycles, and it removes only 4 cycles. “Truncated”: the discharge does not reach 2.75 V or lower, or the constant-voltage hold does not taper to 0.07 A or lower. “Too few samples”: a half has fewer than 64 logged samples, so filling 64 points would mean inventing data. This last rule is why the matrix stops near SOH 0.52: late discharges are short enough to fall below 64 samples.

Fig 4 · From one logged cycle to one row: split, resample, standardise

1 · Find the two halvesUse the current, not the logged step number.
2 · Rescale timeGive each half its own 64-point time axis.
3 · Standardise the rowRemove the average level and spread so only the shape remains.
4 · Keep what was removedStore the mean, spread, durations, capacity and CE as labels.

Real time: halves of unequal length

Resampled voltage: the switch fixed at column 64

Standardised row: shape only

The same recipe at every age. CS2_35 cycle 40 has SOH 0.97, charge duration 2.31 h and discharge duration 0.96 h. After resampling, each half has exactly 64 columns. Standardising removes the mean 3.840 V and the spread 0.249 V; the capacity 1.070 Ah and CE 1.009 are kept as labels.

Why two separate time axes? Giving the charge and the discharge their own time axes lines up, in every row, the corner where constant current switches to constant voltage and the knee near the end of the discharge, so PCA does not have to spend components on differences in duration. Every kept half has at least 64 samples, so resampling only ever reduces the number of points. During the constant-voltage hold, samples can be minutes apart, but the voltage is held at 4.200 V, so interpolating voltage across those gaps is justified.

Fig 5 · The design matrix

Click the heatmap or use either slider; every linked view and table follows.

The whole matrix

Rows are stacked battery by battery, each in order of cycle. Colour shows the standardised voltage of each entry. The vertical line is the switch from charge to discharge, at the same column in every row.

X[i,:] X[:,j] x_ij

x[425,83] = -0.40 z = 3.720 V

X[i,:]one cycle

Across one row

Shown in volts, by undoing the standardisation with this row’s stored mean and spread.

X[425,:] → CS2_35, cycle 438; reconstructed with v_mean 3.831 V and v_std 0.278 V.

X[:,j]one normalized instant

Down one column

Every battery across cycle number; the selected battery is emphasised.

X[:,83] → 30% through normalized discharge time across 3,245 rows.

Labels sit beside X, not inside it. Capacity, CE, durations, mean voltage and voltage spread appear in the linked tables below; PCA never receives them.
The matrix as tables: click a row or column, or use the keyboard, to select it
ibatterycycleSOHq_dis
jhalfpositionzV
Labels stored beside the selected row

Rows and columns answer different questions. A row is everything PCA knows about one cycle. A column shows how the voltage at one point of the cycle changes across life. The selected mid-discharge column already shows a pattern with age; PCA finds the weighted combination of all 128 columns that varies most.

Transformation: the shape rows given to the model

Every retained cycle is represented by 128 standardised voltage values: 64 for the charge and 64 for the discharge. Cycle number, capacity, duration and battery identity are kept out of the model.

Modelling: letting PCA find the main patterns in the shapes

PCA (principal component analysis) receives only the shape rows. It finds the combinations of columns along which the rows differ most, called principal components; a row’s position along one of them is its score. Cycle number and capacity are kept aside and used only afterwards, to check what the components mean. UMAP, a nonlinear method, gives a second view of the same rows.

Fig 6 · PCA on all four batteries together: the first component carries 86.4% of the shape variance

Can the four batteries be analysed together? Only if PC1 does not simply tell one battery from another. Here η²(battery), the share of PC1’s variation explained by which battery a cycle came from, is 0.018, so battery identity explains little of PC1. In an earlier analysis of lithium-metal batteries with different chemistries and test schedules, PC1 fitted on all of them instead separated the chemistries (η² = 0.99), and two truncated cycles changed the rank correlation from -0.95 to -0.56; this is why truncated cycles are screened out here.

Variance explained

PC1 and PC2 loadings

Every cycle on PC1–PC2

early cyclelate cycle

What PC1 measures. A loading is the weight a component gives each column. PC1 weights the charge half and the discharge half with opposite signs, so it measures the gap between them: as resistance rises with age, charging voltage rides higher and discharge voltage sags lower. The same contrast appeared in both chemistries of the earlier lithium-metal analysis and appears here in a third, LCO. In “Every cycle on PC1–PC2”, cycles move from early to late along PC1, with the four batteries overlapping; PC2 carries 3.4% of the variance, mostly ripples within a test session.

Numbers behind the PCA: components, scores, loadings and rank correlations
PCA scores as a table
rowbatterycycleSOHPC1PC2

Fig 7 · A separate PCA on each battery finds the same first component

PC1 loadings from four separate PCAs

CS2_35CS2_36CS2_37CS2_38dashed line = switch from charge to discharge
battery (own PCA)PC1 varianceρ(PC1, cycle)ρ(PC1, capacity)
CS2_3588.3%0.911-0.953
CS2_3687.9%0.943-0.960
CS2_3788.8%0.937-0.960
CS2_3883.2%0.637 *-0.670
* Same loading; this battery also has a reversible episode unrelated to ageing (see Fig 9).

Four separate fits, one loading. Cosine similarity measures how closely two loading curves point the same way (1 = identical); the lowest value between any two batteries is 0.997. CS2_38 finds the same PC1 but orders its cycles by age less cleanly, because its polarisation rises and then recovers during one episode.

Numbers behind the per-battery PCAs

Fig 8 · UMAP: a second, nonlinear view

Islands ordered by age, with the batteries mixed. Each point is one cycle. Every island contains cycles from all four batteries, unlike the earlier lithium-metal UMAP, where the chemistries separated. The islands follow test sessions and the partial recovery after each rest. Parameters are n_neighbors = 15, min_dist = 0.1, seed = 42.

UMAP coordinates as a table
rowbatterycycleUMAP-1UMAP-2

Evaluation: bringing back cycle number and capacity

The main test fits PCA on three batteries and applies it to the fourth, which the fit never saw. The resulting scores are then compared with cycle number and with capacity, neither of which was part of X. Use “Evaluation view” to switch between this test and the ΔQ(V) features.

Fig 9 · Leave one battery out: fit PCA on three, project the fourth

CS2_38 projected from the other threeρ with cycle 0.644ρ with capacity -0.677
held-out batteryPC1 variance (fit on the other three)ρ(PC1, cycle)ρ(PC1, capacity)|ρ| with its own PC1
CS2_3585.88%0.910-0.9531.000
CS2_3686.43%0.943-0.9601.000
CS2_3785.71%0.937-0.9601.000
CS2_3887.82%0.644-0.6771.000
Why hold out whole batteries? Neighbouring cycles of one battery are nearly identical. If rows were split at random, every test row would have a near-twin among the training rows. Holding out a whole battery avoids that.

Each point is one cycle of the held-out battery: its PC1 score against its cycle number. Ticks on the horizontal axis mark the start of each test session. CS2_38 has a reversible episode. During cycles 60–150, its discharge voltage sits about 40 mV lower and its charge voltage about 30 mV higher, then relaxes back, while capacity keeps falling at its usual rate. PC1 follows this voltage change, so ρ with cycle drops to 0.64, against 0.91–0.94 for the other batteries. The projection still works; PC1 tracks polarisation rather than age itself.

Fig 10 · PC1 against capacity, which PCA never saw

Shape tracks capacity. Capacity was computed from the current and never entered X, and standardising each row also removed the voltage level. Even so, the rank correlation ρ between PC1 and capacity (−1 means a perfectly opposite ordering) is -0.95, -0.96, -0.96 for the first three batteries. CS2_38 reaches only -0.68, because its temporary polarisation episode moves PC1 while capacity stays on trend. PC1 measures the gap between charge and discharge voltage, which ageing usually, but not always, widens steadily.

PC1 and capacity as a table
rowbatterycyclecapacityPC1

What this analysis establishes

The main results, most important first.

One curve-shape direction dominates all four batteries and carries over between them

PCA receives no cycle numbers or capacity, yet its first component orders most of each battery’s life and reappears when PCA is fitted to each battery separately.

PC1 variance = 86.4% · minimum loading cosine = 0.997

PCA fitted on three batteries works on the fourth

Components fitted on three batteries reproduce the fourth battery’s own PC1 almost exactly. Holding out whole batteries keeps nearly identical neighbouring cycles out of the test.

agreement with each battery’s own PC1 = 1.000 · 1.000 · 1.000 · 1.000

PC1 tracks capacity, which PCA never saw

Capacity is computed from the current and never enters X. Three batteries show a strong inverse rank correlation; CS2_38’s is weaker.

ρ(PC1, capacity) = CS2_35 -0.953 · CS2_36 -0.960 · CS2_37 -0.960 · CS2_38 -0.676

PC1 measures polarisation, not age itself

CS2_38’s reversible episode changes the curve shape while capacity keeps fading. That PC1 follows the voltage gap here, not capacity, supports reading it as polarisation.

CS2_38 ρ(PC1, cycle) = 0.644 · episode = cycles 60–150

ΔQ(V) grows at low voltage and tracks remaining life within each battery

The difference curves grow most at low voltage, and within each battery their log variance tracks remaining life; the comparison across batteries has four points.

CS2_38 ρ = -0.809 · n = 4

The analysis covers life down to about SOH 0.52

Late cycles with fewer than 64 samples in a half are left out rather than filled in, so the last part of life is not in X.

matrix rows = 3,245 · coverage floor ≈ SOH 0.52

Glossary and scientific background

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

Battery and protocol

Battery

One of the four CALCE CS2 test batteries, CS2_35 to CS2_38. Each is a single 1.1 Ah prismatic cell, so “battery” and “cell” mean the same thing here.

LCO

Lithium cobalt oxide, the chemistry of these batteries. Its discharge voltage slopes continuously rather than staying on a long flat plateau.

LFP

Lithium iron phosphate, the flatter-voltage chemistry used in the comparison studies discussed on this page.

CC charge

Constant-current charging at the protocol’s fixed current until the upper voltage limit is reached.

CV hold

Constant-voltage charging at the upper voltage limit while current tapers to the cutoff.

C-rate

Current relative to rated capacity; here the discharge current equals one nominal capacity per hour.

Capacity

The charge delivered in one discharge, obtained by adding up the current over time. Here it is kept as a label, not used as an input.

SOH

State of health: the capacity delivered in one discharge divided by the rated capacity, 1.1 Ah. (The Battery SOH demo divides by a fixed 4,100 A·s, about 1.14 Ah, instead.)

CE

Coulombic efficiency: discharged charge relative to charged charge for one cycle; stored as a label.

EOL80

End of life at 80%: the first cycle from which SOH stays at or below 0.80 for 5 consecutive cycles.

Session

One exported workbook covering a stretch of testing. Cycle counters restart in each one.

Polarisation

The charge branch riding higher and the discharge branch lower under load, producing a voltage gap associated with resistance.

Rest recovery

Partial relaxation of voltage and capacity after idle time, visible as session-aligned sawteeth rather than true rejuvenation.

Cyclable-lithium inventory

Lithium still available to shuttle during cycling. Its loss shortens the delivered-capacity span.

Internal resistance

Opposition inside the battery that raises charging voltage and lowers discharging voltage under current.

Rows and transformations

Cycle curve

The complete recorded round trip of charge, hold, rest and discharge for one cycle.

Half-cycle

The behavior-located charge or discharge portion treated separately during time normalization.

Normalized time

Position from start to end of one half-cycle, independent of its real duration.

Resampling

Interpolation onto the shared normalized-time columns. Retained rows are downsampled, never upsampled.

Seam

The fixed boundary between the charge and discharge column blocks in every row.

Z-score

For each row, subtract its own mean and divide by its own standard deviation, removing level and scale.

Design matrix X

The sample-by-feature array given to PCA: one retained cycle per row and one normalized-time voltage-shape value per column.

Feature

A value inside X that the model is allowed to use.

Label

Context stored beside X, such as cycle, capacity, duration and battery identity, revealed only for evaluation.

Truncated cycle

A record that contains apparent phases but does not reach the protocol’s termination span.

Too-few-samples rule

Removes a cycle if either half has fewer than 64 logged samples, too few to fill the 64 columns without inventing points.

Representation and evaluation

PCA

Principal component analysis, fitted here only to X to find orthogonal directions of greatest shape covariance.

Principal component

A weighted direction through the shape columns. PC1 is the leading direction.

Loading

A column’s weight in a principal component; its sign pattern shows what curve contrast the component measures.

Score

A row’s coordinate after projection onto a component.

Explained variance

The share of total shape variance carried by one principal component.

η²

The share of a component’s variance explained by which battery a cycle came from; used here to check whether PCA on all four batteries mostly separates them.

Spearman ρ

Rank correlation used to ask whether scores order cycles or capacity without assuming a straight-line relation.

Cosine similarity

Agreement between loading directions after their arbitrary sign is aligned.

Leave one battery out

Fit on every row from three batteries and project every row from the fourth.

UMAP

A nonlinear method that places rows with similar shapes near each other in two dimensions; used here as a second view alongside PCA.

ΔQ(V)

The delivered-capacity curve at a later cycle minus an early reference curve on a shared voltage grid.

Log variance

The base-ten logarithm of variance across one ΔQ(V) curve, reducing it to one scalar per cycle.

Remaining life

Cycles from the current cycle to the sustained EOL80 crossing in the within-battery ΔQ(V) check.