Session 04 Background: Full SPM Implementation & Voltage Coupling
Read this file to seamlessly continue learning in a new conversation This file follows the
note_specification.mdstandard Teaching Mode: Zero-baseline friendly, all figures generated as PNG via matplotlib
1. Recap (Previously Learned)
1.1 Chapter 1: Electrochemistry Basics & OCV
Three SPM assumptions, parameter naming conventions, SOC & N/P ratio, OCV curves (Nernst + graphite tanh + NMC polynomial).
1.2 Chapter 2: Diffusion Equation (Fick's Second Law)
| Knowledge Point | Key Points |
|---|---|
| Fick's First Law | J = −D·∂c/∂r |
| Fick's Second Law (spherical) | ∂c/∂t = D·[∂²c/∂r² + (2/r)·∂c/∂r] |
| Implicit Euler | (I − DΔt·L)·c_new = c_old |
| Tridiagonal Matrix | scipy solve_banded |
1.3 Chapter 3: Butler-Volmer Kinetics — Just Completed
| Knowledge Point | Key Points |
|---|---|
| Overpotential η | Extra driving force needed to cross the interface energy barrier |
| BV Forward | j = j₀·[e^(αFη/RT) − e^(−(1−α)Fη/RT)] |
| j₀ Dependence | j₀ = k·√c_e·√c_s·√(c_max−c_s), max at SOC=50% |
| arcsinh Inversion (α=0.5) | η = (2RT/F)·arcsinh(j/2j₀) |
| Linear Approximation | |
| Newton Iteration (α≠0.5) | 3-step convergence, quadratic convergence rate |
| Independent Implementation | bv_my.py, 0 nV difference vs model.py |
1.4 Review of the Four SPM Equations
Loop 1 (outer): current → flux → overpotential → voltage
Current i_app
↓
j = i_app / (a_s·L·F) ← current→flux (Ch1)
↓
η = BV(j, c_s_surf) ← flux→overpotential (Ch3)
↓ ↑
c_s_surf ← diffusion updates c(r) ← concentration←diffusion (Ch2)
↓
V = U(OCV) + η_p − η_n ← voltage coupling (this chapter)
You now possess all four SPM equations. This chapter's task is to "connect them" so the model runs in full.
2. Current Code Status
2.1 File Inventory
| File | Purpose | Status |
|---|---|---|
spm/parameters.py |
Battery physical parameters + SOC auto-conversion | Complete |
spm/model.py |
SPM core engine (four equations + step + simulate) | Exists, to be studied |
learning/learning_notes_03_kinetics/bv_my.py |
Independent BV implementation | Complete |
learning/learning_notes_02_diffusion/diffusion_solver_my.py |
Independent diffusion solver | Complete |
2.2 Code Directly Related to This Chapter
SPMModel.__init__() Initialization Logic:
Function: SPMModel.__init__(params)
Purpose: Initialize the SPM simulation engine
Algorithm:
1. Store parameter object reference
2. Build radial grid r = linspace(0, R, Nr) for each electrode
3. Set initial concentration uniform at c_s_init for each electrode
4. Compute active specific surface area: a_s = 3 * eps_s / R
5. Pre-build tridiagonal diffusion matrix A_base for each electrode
step() Method Logic:
Function: step(i_app)
Purpose: Advance the SPM state by one time step dt
Algorithm:
1. Step 1 (Current → Flux):
j_n = -i_app / (a_s_n * L_n * F)
j_p = i_app / (a_s_p * L_p * F)
2. Step 2 (Flux → Overpotential):
eta_n = butler_volmer_overpotential(j_n, k_n, c_e, c_s_surf_n, ...)
eta_p = butler_volmer_overpotential(j_p, k_p, c_e, c_s_surf_p, ...)
3. Step 3 (Diffusion → Update Concentration):
Build RHS with surface flux boundary condition
Solve banded linear system for each electrode
4. Step 4 (Voltage Coupling):
V_cell = U_p + eta_p - U_n - eta_n
Returns: V_cell
simulate() Method Logic:
Function: simulate(t_end, i_app)
Purpose: Run a full discharge/charge simulation over t_end seconds
Algorithm:
1. n_steps = int(t_end / dt)
2. For k = 1 to n_steps:
a. V = step(i_app)
b. Record V, eta_n, eta_p, soc_n, soc_p, ...
3. Return results dictionary with time series arrays
2.3 Key Parameter Quick Reference
| Parameter | Value | Meaning |
|---|---|---|
i_app |
Typical 10 A/m² (1C) | Applied current density (positive = discharge) |
A_electrode |
1.0 m² | Electrode area |
t_end |
3600 s | Typical simulation duration (1C = 1h) |
dt |
1.0 s | Time step |
Nr |
50 | Particle mesh points |
3. This Session's Learning Topics
Full SPM Implementation — Connecting Four Equations to Run a Simulation
The first three chapters separately learned the three "components" of SPM: OCV, Diffusion, and BV. This chapter's task is to understand how they combine into a complete working engine.
Core Questions:
- How does
step()connect the four equations within each Δt? - How does c_s_surf from diffusion feed back to BV? How does η from BV form voltage?
- How does
simulate()drive the entire simulation loop? - How do we interpret the components of discharge/charge curves?
4. This Session's Study Plan
4.1 Learning Objectives
- Understand SPM
step()'s four-step cyclic dataflow - Master the voltage coupling equation V_cell = U_p + η_p − U_n − η_n
- Understand
simulate()'s loop structure and result recording - Be able to run full simulations and interpret discharge curve components
- Understand the effect of different currents (C-rates) on voltage curves
4.2 Learning Path
A["Four-equation dataflow<br/>step() line-by-line reading"]
↓
B["Voltage coupling<br/>Breakdown of V = U + η"]
↓
C["simulate() loop<br/>Time advancement and state management"]
↓
D["Run simulation<br/>Discharge/charge curves"]
↓
E["C-rate sensitivity<br/>Behavior at different currents"]
4.3 Detailed Content
Part A: step() Four-Step Dataflow
SPM's step() is the heart of the model — each call advances time by dt seconds:
Step 1: i_app → j_n, j_p
┌────────────────────────────────┐
│ j_n = −i_app / (a_s_n·L_n·F) │
│ j_p = i_app / (a_s_p·L_p·F) │
└────────────────────────────────┘
↓
Step 2: j + c_s_surf → η
┌────────────────────────────────┐
│ η_n = BV(j_n, c_s_surf_n, ...) │
│ η_p = BV(j_p, c_s_surf_p, ...) │
└────────────────────────────────┘
↓
Step 3: j → update c(r) (diffusion)
┌────────────────────────────────┐
│ Implicit Euler + tridiagonal solve│
│ Surface BC: D·(c_surf−c_near)/dr=j │
└────────────────────────────────┘
↓ ↑
new c_s_surf feeds back to next step's Step 2
↓
Step 4: Voltage coupling
┌────────────────────────────────┐
│ V_cell = U_p + η_p − U_n − η_n │
└────────────────────────────────┘
Key insight: Step 3 updates c_s_surf, and this new c_s_surf is used in the next time step's Step 2 as the BV input. This is the coupling between diffusion and BV — concentration changes affect overpotential through BV, and overpotential in turn affects voltage.
Part B: Voltage Coupling Equation
In SPM, the cell terminal voltage is composed of four terms:
| Term | Meaning | Sign During Discharge |
|---|---|---|
| U_p | Cathode OCV | ~4.0 V (varies with SOC) |
| U_n | Anode OCV | ~0.1 V (varies with SOC) |
| η_p | Cathode overpotential | < 0 (penalty) |
| η_n | Anode overpotential | > 0 (penalty) |
During discharge:
Overpotential is a "penalty term" — the larger the current, the larger the overpotential, and the lower the voltage.
Part C: simulate() Time Advancement
Loop structure: simulate() = step() × n_steps + result recording.
For each step:
1. Call step(i_app) — advance state by dt
2. Record V, eta_n, eta_p, soc_n, soc_p, ...
3. Self.time increments by dt
Part D: Generate Complete Discharge Curve
Use matplotlib to plot voltage vs time and decompose OCV and overpotential contributions on the same plot.
Subplot 1: V_cell vs time (complete discharge curve) Subplot 2: Voltage breakdown — individual contributions of U_p, U_n, η_p, η_n Subplot 3: SOC evolution — surface SOC vs average SOC (showing diffusion lag)
Part E: C-rate Sensitivity Analysis
Battery rate performance: voltage curve differences at various currents (C/5, C/2, 1C, 2C).
| C-rate | Current | Discharge Time | Overpotential Magnitude | Voltage Level |
|---|---|---|---|---|
| C/5 | Small | 5h | Small | High (close to OCV) |
| 1C | Medium | 1h | Medium | Medium |
| 2C | Large | 30min | Large | Low (severe polarization) |
4.4 Practice Tasks
| # | Task | Related Code |
|---|---|---|
| 1 | Line-by-line understanding of step()'s four-step flow |
model.py#step |
| 2 | Hand-derive voltage coupling formula (sign verification) | model.py Step 4 |
| 3 | Run full simulation, plot discharge curve | model.py#simulate |
| 4 | Plot voltage breakdown (OCV + η individual contributions) | Visualization |
| 5 | Run comparison simulations at different C-rates | Parameter experiment |
| 6 | Understand η and c_s_surf evolution in the loop | Data analysis |
4.5 Generated Figures
spm_discharge.png (Discharge Curve · 3 Subplots)
Subplot 1: V_cell vs Time
- X-axis: time (s), Y-axis: V
- Mark discharge endpoint (cutoff voltage 2.5V or SOC=0)
Subplot 2: Voltage Breakdown
- V_cell, U_p, U_n, η_p, η_n on the same plot
- Label direction and magnitude of each contribution
Subplot 3: SOC Evolution
- Surface SOC vs average SOC (one curve each for anode and cathode)
- Label diffusion lag effect
crate_comparison.png (C-rate Comparison)
Subplot: Discharge curves at different C-rates overlaid
- Label: higher rate → lower voltage, shorter discharge time
4.6 Preview Questions
- Why are the four steps in
step()in this particular order? What would happen if diffusion were computed before BV? - During discharge, η_n > 0 and η_p < 0. When substituted into V = U_p + η_p − U_n − η_n, do η_p and η_n affect voltage in the same or opposite direction?
- Why is the voltage curve significantly lower at high current than at low current? Besides overpotential, what other reasons are there?
5. Three-Chapter Summary: Your SPM Knowledge Graph
┌─────────────────────────────────────────────────────────────┐
│ The Four SPM Equations │
│ │
│ Equation ①: U(θ) — OCV curve │
│ Equation ②: ∂c/∂t — Diffusion in spherical particles │
│ Equation ③: j ↔ η — Butler-Volmer interface kinetics │
│ Equation ④: V_cell — Voltage coupling = Σ parts │
│ │
│ Coupling: c_s_surf changing → η changing → V changing │
│ C-rate: large current → large η → low V │
│ │
│ Missing from SPM: electrolyte concentration gradient, │
│ ohmic drop (→ SPMe / P2D supplements) │
└─────────────────────────────────────────────────────────────┘
6. Reference Index
| Reference | Chapter | Content |
|---|---|---|
| [Brett] | Ch2 §2.2-2.4 | Cell potential calculation |
| [Brett] | Ch1 §1.3 | Thermodynamics & kinetics, overpotential |
| [Rechargeable] | Ch1 §1.2.2 | Electrode kinetics & polarization losses |
Ready to begin Session 4. Start with Session 4.1: step() four-step dataflow.