Skip to content

Loading BatMo BDF CSV data

This notebook shows how to load a BatMo BDF CSV file with the built-in batmo_bdf loader, inspect the resulting cellpy data object, extract useful pandas DataFrames, make a simple voltage-capacity plot, and export the processed data to other formats.

from pathlib import Path
import sys

import matplotlib.pyplot as plt
import pandas as pd

# Changes to the cellpy repo can directly be used without installing the package. This is useful for development and testing.
repo_root = next(
    (path for path in [Path.cwd(), Path.cwd().parent] if (path / "cellpy" / "__init__.py").exists()),
    None,
)
if repo_root is not None and str(repo_root) not in sys.path:
    sys.path.insert(0, str(repo_root))

import cellpy

%matplotlib inline

Locate the example file

The notebook first looks for batmo_bdf.csv in examples/data. In a source checkout, the same test file is also available in testdata/data.

candidates = [
    Path("data/batmo_bdf.csv"),
    Path("examples/data/batmo_bdf.csv"),
    Path("../testdata/data/batmo_bdf.csv"),
    Path("testdata/data/batmo_bdf.csv"),
]

raw_file = next((path for path in candidates if path.exists()), None)
if raw_file is None:
    raise FileNotFoundError("Could not find batmo_bdf.csv in examples/data or testdata/data")

raw_file
PosixPath('../testdata/data/batmo_bdf.csv')

Load with the BatMo loader

BatMo BDF CSV files are loaded by passing instrument="batmo_bdf". The example data starts with a discharge step, so cycle_mode="anode" is used here.

c = cellpy.get(
    raw_file,
    instrument="batmo_bdf",
    cycle_mode="anode",
    mass=1.0,
)

c
(cellpy) - parsing with pandas.read_csv: /tmp/batmo_bdf.csv
(cellpy) - parameters: self.sep=',', self.skiprows=0, self.header=0, self.encoding='utf-8', self.decimal='.'
(cellpy) - running post-processor: rename_headers
(cellpy) - running post-processor: cumulate_capacity_within_cycle
(cellpy) - running post-processor: set_index

CellpyCell-object

id: 0x7f5910928ce0
name: batmo_bdf
tester: batmo
cycle_mode: anode
sep: ;
cellpy_datadir: /home/meg/cellpy_data/cellpyfiles
raw_datadir: /home/meg/cellpy_data/raw

capacity_modifiers: ['reset']
empty: False
ensure_step_table: False
filestatuschecker: size
force_step_table_creation: True
forced_errors: 0
limit_loaded_cycles: None
profile: False
cellpy_units: CellpyUnits(current='A', charge='mAh', voltage='V', time='sec', resistance='ohm', power='W', energy='Wh', frequency='hz', mass='mg', nominal_capacity='mAh/g', specific_gravimetric='g', specific_areal='cm**2', specific_volumetric='cm**3', length='cm', area='cm**2', volume='cm**3', temperature='C', pressure='bar')
select_minimal: False
selected_scans: []

data

Data-object

id: 0x7f59066476e0

active_electrode_area: 1.0
cell_name: batmo_bdf
channel_index: None
creator: None
empty: False
has_data: True
has_steps: True
has_summary: True
loaded_from: ../testdata/data/batmo_bdf.csv
loading: 1.0
mass: 1.0
material: silicon
meta_common: CellpyMetaCommon(cell_name='batmo_bdf', start_datetime=Timestamp('1970-01-01 00:08:20'), time_zone=None, comment='', file_errors=None, raw_id=None, cellpy_file_version=8, tester_ID=None, tester_server_software_version=None, tester_client_software_version=None, tester_calibration_date=None, material='silicon', mass=1.0, tot_mass=1.0, nom_cap=1.0, nom_cap_specifics='gravimetric', active_electrode_area=1.0, active_electrode_thickness=1.0, active_electrode_loading=1.0, electrolyte_volume=1.0, electrolyte_type='standard', active_electrode_type='standard', counter_electrode_type='standard', reference_electrode_type='standard', experiment_type='cycling', cell_type='standard', separator_type='standard', active_electrode_current_collector='standard', reference_electrode_current_collector='standard')
meta_test_dependent: CellpyMetaIndividualTest(channel_index=None, creator=None, test_type=None, voltage_lim_low=0.0, voltage_lim_high=1.0, cycle_mode='anode', test_ID=None)
nom_cap: 1.0
raw data files: [batmo_bdf.csv]
raw_data_files_length: [21206]
raw_id: None
raw_limits: CellpyLimits(current_hard=1e-13, current_soft=1e-05, stable_current_hard=2.0, stable_current_soft=4.0, stable_voltage_hard=2.0, stable_voltage_soft=4.0, stable_charge_hard=0.9, stable_charge_soft=5.0, ir_change=1e-05)
raw_units: CellpyUnits(current='A', charge='Ah', voltage='V', time='sec', resistance='Ohm', power='W', energy='Wh', frequency='hz', mass='g', nominal_capacity='mAh/g', specific_gravimetric='g', specific_areal='cm**2', specific_volumetric='cm**3', length='cm', area='cm**2', volume='cm**3', temperature='C', pressure='bar')
schedule_file_name: None
start_datetime: 1970-01-01 00:08:20
test_ID: None
test_name: None
tot_mass: 1.0

summary data-frame (summary)

data_point test_time date_time end_voltage_charge end_voltage_discharge charge_capacity discharge_capacity coulombic_efficiency cumulated_coulombic_efficiency cumulated_charge_capacity ... cumulated_charge_capacity_absolute cumulated_discharge_capacity_absolute coulombic_difference_absolute cumulated_coulombic_difference_absolute discharge_capacity_loss_absolute charge_capacity_loss_absolute cumulated_discharge_capacity_loss_absolute cumulated_charge_capacity_loss_absolute shifted_charge_capacity_absolute shifted_discharge_capacity_absolute
count 109.000000 1.090000e+02 109 109.000000 109.000000 109.000000 109.000000 109.000000 109.000000 109.000000 ... 109.000000 109.000000 109.000000 109.000000 108.000000 108.000000 108.000000 108.000000 109.000000 109.000000
mean 10885.697248 8.374226e+05 1970-01-10 16:37:02.568807339 3.590381 2.512286 0.132781 0.133794 99.660461 5467.332177 7.326264 ... 7326.264370 7430.245198 1.013051 103.980828 1.178949 0.088772 144.216483 26.409553 103.980828 237.774513
min 553.000000 2.624200e+05 1970-01-04 00:53:40 3.581278 2.363354 0.131195 0.131195 57.446809 57.446809 0.158948 ... 158.947898 276.687081 -8.325842 103.358183 -16.147088 -10.091930 127.326517 3.784474 103.358183 234.553273
25% 6053.000000 5.717800e+05 1970-01-07 14:49:40.000000001 3.590478 2.507750 0.131195 0.131195 100.000000 2767.780143 3.784726 ... 3784.726054 3888.084237 0.000000 103.358183 0.000000 0.000000 145.491991 27.752808 103.358183 234.553273
50% 10886.000000 8.374600e+05 1970-01-10 16:37:40.000000001 3.590478 2.507750 0.131195 0.131195 100.000000 5467.780143 7.326993 ... 7326.993489 7430.351672 0.000000 103.358183 0.000000 0.000000 145.491991 27.752808 103.358183 234.553273
75% 15719.000000 1.103140e+06 1970-01-13 18:25:40.000000001 3.590478 2.507750 0.131195 0.131195 100.000000 8167.780143 10.869261 ... 10869.260925 10972.619108 0.000000 103.358183 0.000000 0.000000 145.491991 27.752808 103.358183 234.553273
max 21206.000000 1.411480e+06 1970-01-17 08:04:40.000000001 3.596597 2.619610 0.158948 0.276687 105.670103 10862.990274 14.473089 ... 14473.089133 14583.511667 117.739184 117.739184 129.849500 10.091930 145.491991 27.752808 117.739184 394.426265
std 5709.996761 3.144652e+05 NaN 0.001768 0.026906 0.005277 0.014522 4.168069 3161.151011 4.152657 ... 4152.656567 4152.436458 11.347850 2.201780 12.704656 1.895045 4.502405 4.653151 2.201780 16.377038

8 rows × 61 columns

summary data-frame (head)

data_point test_time date_time end_voltage_charge end_voltage_discharge charge_capacity discharge_capacity coulombic_efficiency cumulated_coulombic_efficiency cumulated_charge_capacity ... cumulated_charge_capacity_absolute cumulated_discharge_capacity_absolute coulombic_difference_absolute cumulated_coulombic_difference_absolute discharge_capacity_loss_absolute charge_capacity_loss_absolute cumulated_discharge_capacity_loss_absolute cumulated_charge_capacity_loss_absolute shifted_charge_capacity_absolute shifted_discharge_capacity_absolute
cycle_index
1 553 262420.0 1970-01-04 00:53:40 3.595000 2.363354 0.158948 0.276687 57.446809 57.446809 0.158948 ... 158.947898 276.687081 117.739184 117.739184 NaN NaN NaN NaN 117.739184 394.426265
2 905 283300.0 1970-01-04 06:41:40 3.581278 2.619608 0.155163 0.146838 105.670103 163.116912 0.314111 ... 314.111322 423.524663 -8.325842 109.413341 129.849500 3.784474 129.849500 3.784474 109.413341 256.250923
3 1248 303820.0 1970-01-04 12:23:40 3.582400 2.588512 0.147342 0.147847 99.658703 262.775615 0.461453 ... 461.453500 571.371438 0.504597 109.917938 -1.009193 7.821246 128.840307 11.605720 109.917938 257.764712
4 1593 324460.0 1970-01-04 18:07:40 3.587022 2.534302 0.148351 0.148856 99.661017 362.436632 0.609805 ... 609.804871 720.227405 0.504597 110.422534 -1.009193 -1.009193 127.831114 10.596527 110.422534 259.278502
5 1939 345220.0 1970-01-04 23:53:40 3.593878 2.607985 0.149361 0.149361 100.000000 462.436632 0.759165 ... 759.165435 869.587970 0.000000 110.422534 -0.504597 -1.009193 127.326517 9.587334 110.422534 259.783098

5 rows × 61 columns

steps data-frame (summary)

index cycle step sub_step point_avr point_std point_min point_max point_first point_last ... charge_last charge_delta discharge_avr discharge_std discharge_min discharge_max discharge_first discharge_last discharge_delta rate_avr
count 445.000000 445.000000 445.000000 445.0 445.000000 445.000000 445.000000 445.000000 445.000000 445.000000 ... 445.000000 445.000000 445.000000 445.000000 445.000000 445.000000 445.000000 445.000000 445.000000 445.000000
mean 222.000000 54.887640 223.000000 1.0 10772.908989 13.899711 10749.582022 10796.235955 10749.582022 10796.235955 ... 0.129738 3.252380 0.049591 0.009668 0.033201 0.065973 0.033201 0.065973 3.548456 56646.343352
std 128.604691 32.054092 128.604691 0.0 5804.283991 9.017974 5805.001962 5803.607964 5805.001962 5803.607964 ... 0.020331 5.722632 0.055361 0.016907 0.057374 0.066934 0.057374 0.066934 8.514928 59351.778471
min 0.000000 1.000000 1.000000 1.0 70.500000 4.472136 1.000000 140.000000 1.000000 140.000000 ... 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
25% 111.000000 27.000000 112.000000 1.0 5810.500000 4.760952 5778.000000 5843.000000 5778.000000 5843.000000 ... 0.131195 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
50% 222.000000 55.000000 223.000000 1.0 10781.500000 9.092121 10774.000000 10789.000000 10774.000000 10789.000000 ... 0.131195 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
75% 333.000000 83.000000 334.000000 1.0 15752.500000 19.196354 15720.000000 15785.000000 15720.000000 15785.000000 ... 0.131195 0.000000 0.116898 0.000000 0.116898 0.131195 0.116898 0.131195 0.000000 121103.160180
max 444.000000 109.000000 445.000000 1.0 21198.000000 55.281100 21190.000000 21206.000000 21190.000000 21206.000000 ... 0.158948 15.894790 0.196793 0.046491 0.149361 0.276687 0.149361 0.276687 136.690647 121103.160180

8 rows × 54 columns

steps data-frame (head)

index cycle step sub_step point_avr point_std point_min point_max point_first point_last ... discharge_std discharge_min discharge_max discharge_first discharge_last discharge_delta rate_avr type sub_type info
0 0 1 1 1 70.5 40.558600 1 140 1 140 ... 0.034110 0.000000 0.116898 0.000000 0.116898 11.689819 6055.15801 discharge None
1 1 1 2 1 148.5 4.760952 141 156 141 156 ... 0.000000 0.116898 0.116898 0.116898 0.116898 0.000000 0.00000 rest None
2 2 1 3 1 251.5 54.992424 157 346 157 346 ... 0.000000 0.116898 0.116898 0.116898 0.116898 0.000000 6055.15801 charge None
3 3 1 4 1 354.5 4.760952 347 362 347 362 ... 0.000000 0.116898 0.116898 0.116898 0.116898 0.000000 0.00000 rest None
4 4 1 5 1 458.0 55.281100 363 553 363 553 ... 0.046491 0.116898 0.276687 0.116898 0.276687 136.690647 6055.15801 discharge None

5 rows × 57 columns

raw data-frame (summary)

test_time current voltage cycle_index step_index charge_capacity discharge_capacity data_point step_time date_time
count 2.120600e+04 21206.000000 21206.000000 21206.000000 21206.000000 21206.000000 21206.000000 21206.00000 21206.000000 21206
mean 8.189593e+05 -0.000023 3.249575 54.046921 219.445016 0.106896 0.048041 10603.50000 2871.466330 1970-01-10 11:29:19.256106761
min 5.000000e+02 -0.121103 2.363354 1.000000 1.000000 0.000000 0.000000 1.00000 0.000000 1970-01-01 00:08:20
25% 5.312350e+05 -0.121103 3.125167 24.000000 100.000000 0.084772 0.000000 5302.25000 480.000000 1970-01-07 03:33:55
50% 8.223100e+05 0.000000 3.229508 54.000000 219.000000 0.131195 0.016147 10603.50000 1500.000000 1970-01-10 12:25:10
75% 1.113385e+06 0.121103 3.420788 84.000000 338.000000 0.131195 0.105755 15904.75000 2940.000000 1970-01-13 21:16:25
max 1.411480e+06 0.121103 3.596597 109.000000 445.000000 0.158948 0.276687 21206.00000 95000.000000 1970-01-17 08:04:40.000000001
std 3.434444e+05 0.097693 0.200384 33.616957 135.058798 0.042333 0.055659 6121.78924 7904.683952 NaN

raw data-frame (head)

test_time current voltage Protocol Name / 1 Step Type / 1 cycle_index step_index charge_capacity discharge_capacity data_point step_time date_time
data_point
1 500.0 -0.006055 3.311215 0 discharge 1 1 0.0 0.000000 1 0.0 1970-01-01 00:08:20
2 1000.0 -0.006055 3.310550 0 discharge 1 1 0.0 0.000841 2 500.0 1970-01-01 00:16:40
3 1500.0 -0.006055 3.309933 0 discharge 1 1 0.0 0.001682 3 1000.0 1970-01-01 00:25:00
4 2000.0 -0.006055 3.309573 0 discharge 1 1 0.0 0.002523 4 1500.0 1970-01-01 00:33:20
5 2500.0 -0.006055 3.309229 0 discharge 1 1 0.0 0.003364 5 2000.0 1970-01-01 00:41:40

custom info
None

Inspect the processed data

After loading, cellpy has generated the raw data table, the step table, and the summary table.

raw = c.data.raw
steps = c.data.steps
summary = c.data.summary

print(f"Raw points: {len(raw):,}")
print(f"Cycles: {len(c.get_cycle_numbers())}")
print(f"Step types: {steps[c.schema.steps.step_type].value_counts().to_dict()}")
Raw points: 21,206
Cycles: 109
Step types: {'rest': 117, 'discharge': 110, 'charge': 109, 'ocvrlx_up': 109}
r = c.schema.raw
raw[[
    r.datapoint_num,
    r.test_time,
    r.step_time,
    r.current,
    r.potential,
    r.step_num,
    r.cycle_num,
    r.cumulative_charge_capacity,
    r.cumulative_discharge_capacity,
]].head()
data_point test_time step_time current voltage step_index cycle_index charge_capacity discharge_capacity
data_point
1 1 500.0 0.0 -0.006055 3.311215 1 1 0.0 0.000000
2 2 1000.0 500.0 -0.006055 3.310550 1 1 0.0 0.000841
3 3 1500.0 1000.0 -0.006055 3.309933 1 1 0.0 0.001682
4 4 2000.0 1500.0 -0.006055 3.309573 1 1 0.0 0.002523
5 5 2500.0 2000.0 -0.006055 3.309229 1 1 0.0 0.003364
st = c.schema.steps
steps[[
    st.cycle_num,
    st.step_num,
    st.step_type,
    st.datapoint_num_first,
    st.datapoint_num_last,
    st.potential_first,
    st.potential_last,
]].head(10)
cycle step type point_min point_max voltage_first voltage_last
0 1 1 discharge 1 140 3.311215 2.596938
1 1 2 rest 141 156 2.754796 2.780684
2 1 3 charge 157 346 2.818439 3.595000
3 1 4 rest 347 362 3.581786 3.581428
4 1 5 discharge 363 553 3.565638 2.363354
5 2 6 ocvrlx_up 554 568 2.471637 2.561520
6 2 7 charge 569 725 2.984307 3.581278
7 2 8 rest 726 741 3.470661 3.450568
8 2 9 discharge 742 888 3.336315 2.619608
9 2 10 ocvrlx_up 889 905 2.896960 3.041850
summary.head()
data_point test_time date_time end_voltage_charge end_voltage_discharge charge_capacity discharge_capacity coulombic_efficiency cumulated_coulombic_efficiency cumulated_charge_capacity ... cumulated_charge_capacity_absolute cumulated_discharge_capacity_absolute coulombic_difference_absolute cumulated_coulombic_difference_absolute discharge_capacity_loss_absolute charge_capacity_loss_absolute cumulated_discharge_capacity_loss_absolute cumulated_charge_capacity_loss_absolute shifted_charge_capacity_absolute shifted_discharge_capacity_absolute
cycle_index
1 553 262420.0 1970-01-04 00:53:40 3.595000 2.363354 0.158948 0.276687 57.446809 57.446809 0.158948 ... 158.947898 276.687081 117.739184 117.739184 NaN NaN NaN NaN 117.739184 394.426265
2 905 283300.0 1970-01-04 06:41:40 3.581278 2.619608 0.155163 0.146838 105.670103 163.116912 0.314111 ... 314.111322 423.524663 -8.325842 109.413341 129.849500 3.784474 129.849500 3.784474 109.413341 256.250923
3 1248 303820.0 1970-01-04 12:23:40 3.582400 2.588512 0.147342 0.147847 99.658703 262.775615 0.461453 ... 461.453500 571.371438 0.504597 109.917938 -1.009193 7.821246 128.840307 11.605720 109.917938 257.764712
4 1593 324460.0 1970-01-04 18:07:40 3.587022 2.534302 0.148351 0.148856 99.661017 362.436632 0.609805 ... 609.804871 720.227405 0.504597 110.422534 -1.009193 -1.009193 127.831114 10.596527 110.422534 259.278502
5 1939 345220.0 1970-01-04 23:53:40 3.593878 2.607985 0.149361 0.149361 100.000000 462.436632 0.759165 ... 759.165435 869.587970 0.000000 110.422534 -0.504597 -1.009193 127.326517 9.587334 110.422534 259.783098

5 rows × 61 columns

Make a quick raw-data plot

The raw table is a normal pandas DataFrame, so you can use pandas, matplotlib, seaborn, plotly, or the cellpy plotting helpers.

fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(raw[c.schema.raw.test_time] / 3600, raw[c.schema.raw.potential], lw=0.8)
ax.set_xlabel("Test time / h")
ax.set_ylabel("Voltage / V")
ax.set_title("BatMo BDF raw voltage trace")
ax.grid(alpha=0.25);

png

Extract voltage-capacity curves

get_cap() returns tidy pandas DataFrames that are convenient for plotting or further analysis. Here mode="absolute" keeps the capacities in absolute units.

cycles = [6, 10]
curve = c.get_cap(
    cycles=cycles,
    method="forth-and-forth",
    categorical_column=True,
    label_cycle_number=True,
    mode="absolute",
)

curve.head()
cycle voltage capacity direction
2735 10 3.226422 0.000000 -1
2736 10 3.213438 2.018386 -1
2737 10 3.209859 4.036772 -1
2738 10 3.206538 6.055158 -1
2739 10 3.203454 8.073544 -1
fig, ax = plt.subplots(figsize=(7, 5))

for (cycle, direction), frame in curve.groupby(["cycle_num", "direction"]):
    label = f"cycle {cycle} {'charge' if direction > 0 else 'discharge'}"
    ax.plot(frame["capacity"], frame["potential"], label=label, lw=1.2)

ax.set_xlabel("Capacity / mAh")
ax.set_ylabel("Voltage / V")
ax.set_title("Selected BatMo voltage-capacity curves")
ax.legend(fontsize=8)
ax.grid(alpha=0.25);

png

Export to other formats

The processed cellpy object can be saved as a cellpy HDF5 file and exported to CSV or Excel. The CSV export below keeps the output compact by exporting summary and cycle data only.

out_dir = Path("out/batmo_bdf")
csv_dir = out_dir / "csv"
csv_dir.mkdir(parents=True, exist_ok=True)

cellpy_file = out_dir / "batmo_bdf.cellpy"
excel_file = out_dir / "batmo_bdf.xlsx"

c.save(cellpy_file)
c.to_csv(datadir=csv_dir, raw=False, summary=True, cycles=True, last_cycle=5)
c.to_excel(excel_file, cycles=[1, 2, 10], raw=False)

sorted(path.name for path in out_dir.iterdir())
<ApiModule 'cellpy.readers.externals'>





['batmo_bdf.cellpy', 'batmo_bdf.xlsx', 'csv']

Reload the saved cellpy file

Once saved as a cellpy file, loading is faster and does not require specifying the BatMo raw-data loader again.

c2 = cellpy.get(cellpy_file)

print(f"Reloaded raw points: {len(c2.data.raw):,}")
print(f"Reloaded cycles: {len(c2.get_cycle_numbers())}")
Reloaded raw points: 21,206
Reloaded cycles: 109