Validation#
There are multiple ways to check if a file or dataframe are compliant with the BDF recommendations using the validate() function. The validate function take as an argument:
a filepath
a URL
a dataframe
It compares the content of the object with the BDF and returns a report.
# Import the package
import bdf
# Read from a local file path and convert to bdf
filepath = "../../data/SINTEF__LiGrR2032__2024-04-30__25degC__Landt.csv"
df, meta = bdf.read(filepath)
df = df.to_pandas()
df.head()
/home/runner/work/battery-data-format/battery-data-format/src/bdf/table_parsers.py:195: UserWarning: tz defaulted to UTC; pass tz=... if data was recorded in a different timezone
result = self.normalizer.normalize(
| Test Time / s | Voltage / V | Current / A | Unix Time / s | Cycle Count / 1 | Step ID | Step Type | Record Index / 1 | Step Time / s | Step Charging Capacity / Ah | Step Discharging Capacity / Ah | Step Charging Energy / Wh | Step Discharging Energy / Wh | Temperature T1 / degC | Temperature T2 / degC | Temperature T3 / degC | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.02 | 2.9215 | 0.0 | 1.714488e+09 | 1 | 1 | rest | 1 | 0.02 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 1 | 15.02 | 2.9215 | 0.0 | 1.714488e+09 | 1 | 1 | rest | 2 | 15.02 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 2 | 30.02 | 2.9214 | 0.0 | 1.714488e+09 | 1 | 1 | rest | 3 | 30.02 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 45.02 | 2.9214 | 0.0 | 1.714488e+09 | 1 | 1 | rest | 4 | 45.02 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 60.02 | 2.9213 | 0.0 | 1.714488e+09 | 1 | 1 | rest | 5 | 60.02 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
# Validate the dataframe and print the report
report = bdf.validate(df, report=True)
print(report)
✅ BDF validation passed
rows: 25,162 cols: 16
{'ok': True, 'missing': [], 'extras': [], 'required': ('Voltage / V', 'Test Time / s', 'Current / A'), 'optional': ('Absolute Impedance / ohm', 'AC Internal Resistance / ohm', 'Ambient Pressure / Pa', 'Ambient Temperature / degC', 'Applied Pressure / Pa', 'Cumulative Capacity / Ah', 'Cumulative Energy / Wh', 'Cycle Cumulative Capacity / Ah', 'Cycle Cumulative Energy / Wh', 'Cycle Net Capacity / Ah', 'Cycle Net Energy / Wh', 'DC Internal Resistance / ohm', 'Frequency / Hz', 'Net Capacity / Ah', 'Net Energy / Wh', 'Phase / deg', 'Record Index / 1', 'Schedule Charging Capacity / Ah', 'Schedule Charging Energy / Wh', 'Schedule Discharging Capacity / Ah', 'Schedule Discharging Energy / Wh', 'Step Count / 1', 'Step ID', 'Step Net Capacity / Ah', 'Step Net Energy / Wh', 'Step Type', 'Surface Pressure / Pa', 'Surface Temperature / degC', 'Temperature T1 / degC', 'Temperature T2 / degC', 'Temperature T3 / degC', 'Temperature T4 / degC', 'Temperature T5 / degC', 'Charging Capacity / Ah', 'Charging Energy / Wh', 'Cycle Charging Capacity / Ah', 'Cycle Charging Energy / Wh', 'Cycle Discharging Capacity / Ah', 'Cycle Discharging Energy / Wh', 'Discharging Capacity / Ah', 'Discharging Energy / Wh', 'Step Charging Capacity / Ah', 'Step Charging Energy / Wh', 'Step Cumulative Capacity / Ah', 'Step Cumulative Energy / Wh', 'Step Discharging Capacity / Ah', 'Step Discharging Energy / Wh', 'Step Record Index / 1', 'Unix Time / s', 'Imaginary Impedance / ohm', 'Internal Resistance / ohm', 'Real Impedance / ohm', 'Cycle Count / 1', 'Step Time / s', 'Power / W'), 'legacy_labels': [], 'n_rows': 25162, 'n_cols': 16, 'time_stats': {'present': True, 'monotonic': True, 'violations': 0, 'min_drop': 0.0, 'first_bad_index': None, 'epsilon': 1.0}, 'derived': {'issues': [], 'details': []}}
# Validate the file prior to bdf conversion
report = bdf.validate(filepath, report=True)
Validation failed: SINTEF__LiGrR2032__2024-04-30__25degC__Landt.csv does not look like a BDF artifact. Matched plugin 'landt_csv'.
# Validate all reference BDF files in examples/reference
from pathlib import Path
candidates = [Path('examples/reference'), Path('reference'), Path('../examples/reference')]
reference_dir = next((p.resolve() for p in candidates if p.exists()), candidates[0].resolve())
files = sorted(reference_dir.glob('*.csv'))
print(f'Found {len(files)} reference files in {reference_dir}')
results = []
for path in files:
report = bdf.validate(path, report=False, raise_on_error=False)
results.append((path.name, bool(report['ok']), report.get('missing', []), report.get('extras', [])))
for name, ok, missing, extras in results:
status = 'PASS' if ok else 'FAIL'
print(f'{status} | {name} | missing={len(missing)} | extras={len(extras)}')
n_pass = sum(1 for _, ok, _, _ in results if ok)
print(f'\nSummary: {n_pass}/{len(results)} passed')
if n_pass != len(results):
failed = [name for name, ok, _, _ in results if not ok]
print('Failed files:', failed)
Found 10 reference files in /home/runner/work/battery-data-format/battery-data-format/docs/examples/reference
/home/runner/work/battery-data-format/battery-data-format/src/bdf/table_parsers.py:195: UserWarning: Legacy BDF column labels detected and normalized to preferred labels: 'step_index' -> 'Step Record Index / 1', 'step_capacity_ah' -> 'Step Cumulative Capacity / Ah', 'step_energy_wh' -> 'Step Cumulative Energy / Wh'
result = self.normalizer.normalize(
/home/runner/work/battery-data-format/battery-data-format/src/bdf/table_parsers.py:195: UserWarning: Legacy BDF column labels detected and normalized to preferred labels: 'step_index' -> 'Step Record Index / 1'
result = self.normalizer.normalize(
/home/runner/work/battery-data-format/battery-data-format/src/bdf/_validate.py:473: RuntimeWarning: Derived-column inconsistencies detected (values do not match their ontology definitions):
- 'charging_capacity_ah' is not monotonically non-decreasing (2 drops).
- 'discharging_capacity_ah' is not monotonically non-decreasing (3 drops).
- 'charging_energy_wh' is not monotonically non-decreasing (2 drops).
- 'discharging_energy_wh' is not monotonically non-decreasing (3 drops).
- 'step_record_index' has 17579 transitions that neither increment by 1 nor reset to 1.
return validate_df(df, report=report, raise_on_error=raise_on_error)
/home/runner/work/battery-data-format/battery-data-format/src/bdf/_validate.py:473: RuntimeWarning: Derived-column inconsistencies detected (values do not match their ontology definitions):
- 'charging_capacity_ah' is not monotonically non-decreasing (1 drops).
- 'discharging_capacity_ah' is not monotonically non-decreasing (1 drops).
- 'step_record_index' has 22279 transitions that neither increment by 1 nor reset to 1.
return validate_df(df, report=report, raise_on_error=raise_on_error)
/home/runner/work/battery-data-format/battery-data-format/src/bdf/table_parsers.py:195: UserWarning: Legacy BDF column labels detected and normalized to preferred labels: 'step_capacity_ah' -> 'Step Cumulative Capacity / Ah'
result = self.normalizer.normalize(
/home/runner/work/battery-data-format/battery-data-format/src/bdf/_validate.py:473: RuntimeWarning: Derived-column inconsistencies detected (values do not match their ontology definitions):
- 'cumulative_capacity_ah' != charging_capacity_ah + discharging_capacity_ah in 102068/112834 rows (worst |Δ| = 16.96).
- 'cumulative_energy_wh' != charging_energy_wh + discharging_energy_wh in 102066/112834 rows (worst |Δ| = 80.38).
- 'cumulative_capacity_ah' is not monotonically non-decreasing (58967 drops).
- 'cumulative_energy_wh' is not monotonically non-decreasing (58967 drops).
- 'charging_capacity_ah' is not monotonically non-decreasing (3 drops).
- 'discharging_capacity_ah' is not monotonically non-decreasing (3 drops).
- 'charging_energy_wh' is not monotonically non-decreasing (2 drops).
- 'discharging_energy_wh' is not monotonically non-decreasing (3 drops).
return validate_df(df, report=report, raise_on_error=raise_on_error)
/home/runner/work/battery-data-format/battery-data-format/src/bdf/_validate.py:473: RuntimeWarning: Derived-column inconsistencies detected (values do not match their ontology definitions):
- 'cumulative_capacity_ah' != charging_capacity_ah + discharging_capacity_ah in 86875/157985 rows (worst |Δ| = 11.09).
- 'cumulative_energy_wh' != charging_energy_wh + discharging_energy_wh in 86873/157985 rows (worst |Δ| = 66.24).
- 'cumulative_capacity_ah' is not monotonically non-decreasing (7526 drops).
- 'cumulative_energy_wh' is not monotonically non-decreasing (7514 drops).
- 'charging_capacity_ah' is not monotonically non-decreasing (1 drops).
return validate_df(df, report=report, raise_on_error=raise_on_error)
PASS | DLR__LiGrHydra0b__20221114__GITT__25degC__Basytec.bdf.csv | missing=0 | extras=0
PASS | DLR__LiGrHydra0b__20230131__POCV__25degC__Basytec.bdf.csv | missing=0 | extras=0
PASS | DLR__LiLNMOHydra0b__20221125__POCV__25degC__Basytec.bdf.csv | missing=0 | extras=0
PASS | DLR__LiLNMOHydra0b__20221130__GITT__25degC__Basytec.bdf.csv | missing=0 | extras=0
FAIL | FZJ__INR21700__20250606__HPPC__25degC__Digatron.bdf.csv | missing=0 | extras=0
PASS | SINTEF__G20M7-202512-Gru6mV__20251228__C30__25degC__Neware.bdf.csv | missing=0 | extras=0
PASS | SINTEF__LiGrR2032__2024-04-30__25degC__Landt.bdf.csv | missing=0 | extras=0
PASS | SINTEF__NaCR32140-MP10-04__2025-08-25__CCCV_0p02C_25degC__BioLogic__Outlier_Bug.bdf.csv | missing=0 | extras=0
PASS | SINTEF__NaCR32140-MP10-04__2025-08-25__GITT_0p05C_25degC__BioLogic.bdf.csv | missing=0 | extras=0
PASS | SINTEF__SLPBA842124HV__2024-10-23__Rate_25degC__Neware__Time_Bug.bdf.csv | missing=0 | extras=0
Summary: 9/10 passed
Failed files: ['FZJ__INR21700__20250606__HPPC__25degC__Digatron.bdf.csv']
/home/runner/work/battery-data-format/battery-data-format/src/bdf/_validate.py:473: RuntimeWarning: Non-monotonic 'Test Time / s' detected: 19 drops (min Δ = -125193 s). Consider bdf.repair.fix_time(...).
return validate_df(df, report=report, raise_on_error=raise_on_error)
/home/runner/work/battery-data-format/battery-data-format/src/bdf/_validate.py:473: RuntimeWarning: Derived-column inconsistencies detected (values do not match their ontology definitions):
- 'step_record_index' has 12346 transitions that neither increment by 1 nor reset to 1.
return validate_df(df, report=report, raise_on_error=raise_on_error)