ionworksdata library reads files from
common battery cyclers, auto-detects formats, and normalizes units, timestamps,
and column names.
Supported cyclers
ionworksdata auto-detects the file format when possible and produces a polars
DataFrame with the standard columns.
One-call read pipeline
The format-specific readers (read.neware, read.biologic, …) return a raw
canonical-subset frame — no step count, capacity, energy, or step summary.
Prefer the high-level entrypoints, which run the full standard pipeline in a
single call and accept the same reader, extra_column_mappings, and options
arguments:
read.time_series— parses the file, derives step and cycle counts, and computes capacity and energy. Returns the processed time-series DataFrame.read.time_series_and_steps— does everythingtime_seriesdoes, then adds the per-step summary DataFrame and runs a validation-gated current-sign auto-fix.
options={"validate": True}, time_series_and_steps
validates the frame against the same checks the Ionworks API applies on upload.
If validation reports a reversed or indeterminate current-sign convention, it
flips the sign, recomputes capacity and energy, and re-validates — so the
returned data already matches Ionworks’ positive = discharge convention.
Every read path normalizes current sign to Ionworks’ positive = discharge
convention —
set_positive_current_for_discharge runs inside the standard
processing applied by read.<format>, read.time_series, and
read.time_series_and_steps alike. What time_series_and_steps adds on top is
a validation-gated re-check: if the upload validator still reports a reversed
or indeterminate convention, it flips the sign, recomputes capacity and energy,
and re-validates. The transform is detect-then-fix — it only flips when it
detects that positive current corresponds to charge — so re-applying
iwd.transform.set_positive_current_for_discharge to already-normalized data
leaves the convention unchanged rather than double-flipping it (see
current sign convention troubleshooting).time_series derives one from
current-sign transitions automatically — no manual step-index pass needed.
Custom column mappings
If your CSV uses non-standard column names, map them to the standard names withextra_column_mappings. The mapping overrides the reader’s built-in
rename table for any raw column you name, so auto-detection is skipped for it.
The standard name you map to still drives the reader’s normal unit
handling — mapping a column to
Current [mA] declares its values are
milliamps and the reader converts them to Current [A]. Pick the target name
that matches the column’s real unit, not the one its header claims (see
overriding misleading current-unit headers).Generic parquet files
For parquet files that don’t follow the BDF spec, use the generic parquet reader. It mirrors the CSV reader’s column-detection strategy — recognizing common aliases for voltage, current, time, and temperature — but skips text-parsing concerns since parquet is strongly typed and has unambiguous column names. No separator, encoding, or quote handling is needed. Use it when you have cycler data already exported to parquet (for example, from an internal pipeline or another tool) and want it normalized into the Ionworks data format. Any.parquet file (except .bdf.parquet) is auto-detected; you can also
select the reader explicitly:
extra_column_mappings
just like with the CSV reader:
BDF parquet files (
.bdf.parquet) are still routed to the BDF reader —
the generic parquet reader is only used as a fallback for .parquet
files that aren’t BDF.Battery Data Format (BDF)
ionworksdata can read and write files in the
Battery Data Format (BDF)
defined by the Battery Data Alliance. CSV, gzipped CSV, and parquet variants
are all supported. Files are auto-detected by their header or extension
(.bdf, .bdf.gz, .bdf.parquet).
BDF does not mandate a current sign convention. The reader normalises current
to the Ionworks convention (positive = discharge) on load, so third-party
BDF files that follow the opposite IEC convention are flipped automatically.
The writer emits whatever convention is in the input DataFrame — pass the
data through
transform.set_positive_current_for_discharge first if you
need to guarantee discharge-positive output.EIS and impedance data
Impedance data is read into columns
Frequency [Hz], Z_Re [Ohm],
Z_Im [Ohm], Z_Mod [Ohm], and Z_Phase [deg].
Reader gotchas
A few cycler exports need a small nudge to read cleanly.Multi-sheet Neware Excel (BTSDA)
A Neware BTSDA.xlsx export splits into unit, test, cycle, step, and
record sheets, with the time series on the record sheet. The reader picks
record automatically when it is present, so no options are needed:
record sheet often has no step or cycle column — step count is derived
from current-sign transitions automatically.
For workbooks whose time series sheet is named something else (older .xls
exports use Detail_1, Detail_1_1, …), name the sheets explicitly. The
reader concatenates multiple sheets and sorts by timestamp:
sheets also accepts {"type": "name", "value": "record"} for a single named
sheet (or a list of names), and {"type": "all"} to read every sheet.
Overriding misleading current-unit headers
The Neware reader maps amp-headed columns (Current (A), Current(A)) to
Current [A] and milliamp-headed columns (Current (mA), Cur(mA)) to
Current [mA]. Trust the values, not the header text: after reading,
sanity-check the current range against the cell’s expected C-rate. If a header
lies about its unit (for example, an amp-headed column that actually carries
milliamps), override the target mapping with extra_column_mappings:
extra_column_mappings is merged on top of the reader’s built-in rename table,
overriding the built-in entry for any raw column you map. Mapping to
Current [mA] is a deliberate unit declaration, not a raw passthrough — the
reader still applies its normal milliamp-to-amp conversion (dividing by 1000 to
produce Current [A]), which is exactly what corrects the mislabeled column.
Vendor and MES re-exports
When a platform like Voltaiq re-exports a cycler file (extra derived columns plus a timezone-awareTimestamp), the native cycler reader can choke on the
timezone with a datetime parse error. Fall back to the generic CSV reader and
map the already-elapsed time column so no timestamp parsing is needed:
Troubleshooting
Incorrect current sign convention
Problem: When uploading measurement data, you receive an error like:Current sign convention error: positive current appears to be charge, not discharge.Solution: Ionworks expects positive current = discharge and negative current = charge. If your cycler uses the opposite convention, convert the data before uploading:
Ambiguous current sign convention
Problem: When uploading measurement data, you receive an error like:Current sign convention error: the sign convention is ambiguous.This happens when all current values have the same sign, so the validator cannot determine whether positive means charge or discharge. Solution: Use the same transform — it uses voltage-response analysis (fitting an OCV-R equivalent circuit model under both sign conventions) to infer charge vs. discharge direction even when all currents share the same sign:
Unsigned (magnitude-only) current
Problem: When uploading measurement data, you receive an error like:
Current sign convention error: the current appears to be unsigned
(magnitude-only) but contains both charge and discharge steps. Sign it using
the cycler mode column via
ionworksdata.transform.set_positive_current_for_discharge(data) before
validation.
This fires when every non-rest current sample is positive yet the data clearly
contains both charge and discharge steps — a fingerprint of a cycler that
records current as a magnitude and encodes direction in a separate mode column
rather than in the sign of Current [A]. The issue is distinct from
ambiguous sign convention because it has
a concrete, deterministic fix: re-sign the current from the cycler’s own
charge/discharge labels.
Solution: Run set_positive_current_for_discharge. When it detects an
all-positive non-rest current alongside both charge and discharge steps, it
flips the sign of charge-step samples using the cycler mode column instead of
falling back to the voltage-response heuristic:
CURRENT_SIGN_UNSIGNED — branch on it explicitly if you
want to apply the auto-fix without prompting:
Swapped charge and discharge cumulative columns
Problem: When uploading withvalidate_strict=True, you receive an error
like:
Column ‘Discharge capacity [A.h]’ disagrees with the running integral of ‘max(I, 0)’ over time by up to 96.4% (exceeds 10% tolerance).…and the reported
Discharge capacity [A.h] looks like it tracks the charge
half-wave, with the same flipped behaviour on Charge capacity [A.h]. This
happens with some half-cell exports and cycler configurations that label the
two cumulative columns inversely.
Solution: Use fix_swapped_charge_discharge_columns to compare each
column against the trapezoidal integral of current (and power, for energy)
and rename the pair when the swapped assignment is within tolerance and the
as-is assignment is not.
Time [s], Step count, and Current [A] columns,
and uses Power [W] (or Voltage [V] * Current [A] when Power [W] is
absent) for the energy pair. The default tolerance is 10 % relative
error; pass a different value if your data needs a tighter or looser
threshold:
The function refuses to swap labels when positive current does not
correspond to discharge — without a known sign convention there is no way
to tell which column is which, and swapping would mask a real sign-
convention bug. Run
set_positive_current_for_discharge first.Time not cumulative
Problem: Time resets to 0 for each cycle. Solution: Track a cumulative time offset:Step count not cumulative
Problem: Step count resets for each cycle. Solution: Track a step count offset across cycles:Missing capacity columns
Problem: Capacity calculation fails. Solution: Ensure you haveTime [s], Current [A], and Voltage [V]
columns before calculating capacity.
Non-UTF-8 CSV files (e.g. Neware)
Problem: Reading a Neware CSV file fails with an encoding error. Solution: The Neware reader automatically falls back to Latin-1 if UTF-8 decoding fails, so no action is needed in most cases:Next steps
Data format
Full reference for recognized columns, units, and sign conventions.
Uploading data
Upload prepared data as cell specs, instances, and measurements.
ionworksdata API reference
Complete reference for
read, write, transform, steps, and load.ionworksdata on GitHub
Report issues or browse the source.