SOC calculation

I am working on battery electrochemical modeling for using in microgrid applications purpose and using the pybamm functions to calculate SOC from model variables. But due to my lack of battery chemistry knowledge, I am unable to understand meaning of some features.

One specific problem I am facing is — though my cell voltage is reaching minimum, I am seeing SOC is at 40% at that time which seems not reasonable.
Here is the code:

`
import pybamm
import numpy as np
import matplotlib.pyplot as plt

def estimate_soc_from_capacity(solution):

initial_soc = 1  

# Extract discharge capacity over time
discharge_capacity = solution["Discharge capacity [A.h]"].data
total_capacity = solution["Total lithium capacity [A.h]"].data[0]  # Scalar value
voltage = solution["Battery open-circuit voltage [V]"].data

# Calculate SOC as an array over time
soc = initial_soc - (discharge_capacity / total_capacity)

# Extract time array
time = solution["Time [s]"].data

# Ensure time and SOC arrays have the same shape
if len(time) != len(soc):
    min_length = min(len(time), len(soc))
    time = time[:min_length]
    soc = soc[:min_length]

return soc, time

Run PyBaMM DFN Model

import pybamm

model = pybamm.lithium_ion.DFN() # Doyle-Fuller-Newman model

parameter_values = pybamm.ParameterValues(“Chen2020”)

sim = pybamm.Simulation(model, parameter_values=parameter_values)

solution = sim.solve([0, 3600]) # Solve for 1 hour (3600 seconds)

Calculate SOC over time using capacity method

soc, time = estimate_soc_from_capacity(solution)

Get the size (number of time steps)

size = time.shape # Returns a tuple
print(f"Size of ‘Time [s]’: {size}")

import matplotlib.pyplot as plt

Extract voltage over time from solution

voltage = solution[“Terminal voltage [V]”].data

Convert time to minutes for plotting

time_minutes = time / 60

Plot SOC and Voltage vs Time

fig, ax1 = plt.subplots(figsize=(10, 5))

Primary y-axis: SOC

color_soc = “tab:blue”
ax1.set_xlabel(“Time (minutes)”)
ax1.set_ylabel(“State of Charge (%)”, color=color_soc)
ax1.plot(time_minutes, soc * 100, color=color_soc, label=“SOC (%)”)
ax1.tick_params(axis=“y”, labelcolor=color_soc)

Secondary y-axis: Voltage

ax2 = ax1.twinx()
color_voltage = “tab:red”
ax2.set_ylabel(“Terminal Voltage [V]”, color=color_voltage)
ax2.plot(time_minutes, voltage, color=color_voltage, linestyle=“–”, label=“Voltage (V)”)
ax2.tick_params(axis=“y”, labelcolor=color_voltage)

Title and grid

plt.title(“SOC and Voltage vs Time (Capacity-Based)”)
fig.tight_layout()
plt.grid(True)
plt.show()`

Let me know please if my formula looks okay and if yes, why SOC is not going close to zero?

Hi Syed,

The variable "Total lithium capacity [A.h]" is simply the total amount of lithium in the cell, and is not related to how much of that lithium is actually usable.

To find out what the total usable capacity is, run a slow discharge, then use discharge capacity[-1] as the total capacity. This method will still work if there is degradation in your model, as demonstrated in this notebook I wrote.

Thank you DrSOKane. I followed your advice and did simulations to calculate SOC. Key findings are:

  1. When using discharge capacity[-1] or Nominal Cell Capacity (defined in parameters), I get same output for SOC calculation (the both tests are conducted using 1C rate.) So, should I consider the results reliable and use for my research purpose?

Interesting that the last entry for discharge_capacity[-1] and the nominal capacity are the same. Is that what you would expect for your cell? For the "OKane2022" parameters, I get a capacity of 4.922 [A.h] at 1C, compared to a nominal capacity of 5 [A.h].

Hi DrSOKane,
I am using:
soc = ((initial_soc - discharge_capacity / Nominal_cell_capacity) * 100)

as formula for SOC calculation which matches with my expectation. Thanks for your help!

1 Like