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.
(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: 0x7f5910928ce0name: 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: 0x7f59066476e0active_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.0summary 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 |
| 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);

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);

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