Data Loading¶
A class for loading data for use in other Ionworks packages.
- class ionworks.data.load.DataLoader(time_series: DataFrame | DataFrame | dict, steps: DataFrame | DataFrame | dict | None = None, **kwargs)[source]¶
Bases:
objectLoad time-series data with steps, or plain tabular data such as OCP curves.
dataandstepsare Polars DataFrames; inputs may be pandas or Polars.- Parameters:
time_series (pd.DataFrame | pl.DataFrame | dict) – The data to load. Can be a Pandas/Polars DataFrame or a dict.
steps (pd.DataFrame | pl.DataFrame | dict | None, optional) – Step information. When None, the loader operates in simple (no-steps) mode.
**kwargs –
Options passed directly or via an
optionsdict. The set below is closed — an unrecognised key raises rather than being ignored, so a misspelt option cannot silently skip the preprocessing you asked for. Supported keys:When steps are provided:
- first_step, last_stepint | str | dict
A step index, a SQL query selecting one row of the steps table, or the deprecated cycle/step dict.
first_step_dict, last_step_dict : deprecated spellings of the above
Always:
- capacity_columnstr
Name of the column in
time_seriesto use as the capacity axis (copied to"Capacity [A.h]").
- transformsdict with any of:
- rest_to_ocpbool
See
transform_rest_to_ocp()for details.
- sortbool
See
sort_capacity_and_ocp()for details.
- remove_duplicatesbool
See
remove_duplicate_ocp()for details.
- remove_extremesbool
See
remove_ocp_extremes()for details.
- filtersdict
See
filter_data()for details.
- interpolatefloat | list[float]
See
interpolate_data()for details. An ndarray is accepted and normalised to a list.
- keep_first_ocp_pointbool
If True, prepend the first point (see
transform_rest_to_ocp()). Default False. Ignored if rest_to_ocp is False.
- __init__(time_series: DataFrame | DataFrame | dict, steps: DataFrame | DataFrame | dict | None = None, **kwargs)[source]
- generate_experiment(use_cv: bool = False, termination: str = 'duration', period: str | float | None = None, split_cc_cv: bool = True) pybamm.Experiment[source]
Generate a PyBaMM experiment from the loaded step information.
- Parameters:
use_cv (bool, optional) – Emit constant-voltage steps as voltage holds rather than as a current interpolant. Default False.
termination (str, optional) –
What ends each step. Default
"duration"."duration": every step ends after its measuredDuration [s]."events": every step ends where the measured step ended, in the variable it was controlling — a constant-current or constant-power step at its final voltage, a voltage hold at its final current — and carries no duration, so its length becomes the model’s own. A capacity disagreement between model and cell then reads as capacity to that endpoint rather than as a residual voltage. Rests and steps emitted as a current interpolant keep their duration either way.
"events"replays endpoints, not the cycler’s intent: a step the cycler ended on a time limit is still replayed as ending at the voltage it reached, because the steps table cannot tell the two apart. Use it for endpoint-driven protocols (rate capability, CC-CV cycling) and"duration"for time-driven ones. Where a step’s voltage barely moves, such as a short pulse, the endpoint can already be satisfied at the model’s initial state; PyBaMM warns and skips such a step rather than solving it.period (str or float, optional) – Sampling period for the emitted steps, as
pybamm.Experimenttakes it."from data"gives each step the measurement’s own mean sample interval. Default None, which leaves the output grid to PyBaMM.split_cc_cv (bool, optional) – Emit a step no type fits that ran constant current and then held its voltage, as cyclers often log a CC-CV charge, as a constant-current step followed by a constant-voltage step, rather than as a current interpolant. Default True. A split step becomes two experiment steps, so every later step of its cycle sits one position further along than its row in the steps table; code that reads the experiment’s steps by steps-table position has to allow for it.
- Returns:
One PyBaMM cycle per run of equal
"Cycle count"in the steps table. A table without that column has no cycle structure to recover and becomes a single cycle. The"Cycle count"behind each cycle is left inexperiment_cycle_counts, since the experiment itself numbers its cycles by position, and the steps-table row behind each step inexperiment_step_sources.- Return type:
pybamm.Experiment
- plot_data(show: bool = False) tuple[plt.Figure, plt.Axes][source]
Plot voltage vs time data from the loaded experiment.
- static filter_data(data: DataFrame, filters: dict) DataFrame[source]
Filter a Polars DataFrame using the specified filter functions.
Each key in
filtersis a column name; the value must includefilter_type(e.g."savgol") and any parameters. Used by thefilterstransform option.
- static interpolate_data(data: DataFrame, knots: float | ndarray, x_column: str = 'Time [s]') DataFrame[source]
Interpolate a Polars DataFrame using np.interp.
If
knotsis a float, data is resampled at that regular interval alongx_column. If an array, interpolated at those knots. Used by theinterpolatetransform option.Only numeric columns are interpolated. Non-numeric (e.g. Utf8/String) columns are skipped and do not appear in the returned DataFrame.
- classmethod from_local(data_path, options=None, use_polars=True)[source]
Load data from local filesystem.
- Parameters:
data_path (str) – Path to the directory containing time_series.parquet (or time_series.csv) and optionally steps.parquet (or steps.csv).
options (dict | None, optional) – Options to pass to the DataLoader constructor.
use_polars (bool, optional) – If True (default), read with Polars. If False, read with Pandas (data is still stored as Polars internally).
- Return type:
- experiment_cycle_counts: list | None = None¶
The
"Cycle count"behind each cycle of the last experimentgenerate_experiment()produced, which numbers its cycles by position. None before one is generated, and without that column.
- experiment_step_sources: list[list[int]] | None = None¶
Per cycle, the
"Start index"of the row behind each step, since a CC-CV split or a skipped zero-duration step shifts positions.
- __init__(time_series: DataFrame | DataFrame | dict, steps: DataFrame | DataFrame | dict | None = None, **kwargs)[source]¶
- property data: DataFrame¶
Time-series data as a Polars DataFrame. Use .data.to_pandas() for pandas.
- static filter_data(data: DataFrame, filters: dict) DataFrame[source]¶
Filter a Polars DataFrame using the specified filter functions.
Each key in
filtersis a column name; the value must includefilter_type(e.g."savgol") and any parameters. Used by thefilterstransform option.
- static interpolate_data(data: DataFrame, knots: float | ndarray, x_column: str = 'Time [s]') DataFrame[source]¶
Interpolate a Polars DataFrame using np.interp.
If
knotsis a float, data is resampled at that regular interval alongx_column. If an array, interpolated at those knots. Used by theinterpolatetransform option.Only numeric columns are interpolated. Non-numeric (e.g. Utf8/String) columns are skipped and do not appear in the returned DataFrame.
- property steps: DataFrame | None¶
Step summary as a Polars DataFrame, or None. Use .steps.to_pandas() for pandas.
- property initial_voltage¶
Initial voltage (from first step or previous step end). Lazy-loads steps when from_db.
- set_processed_internal_state(*, transforms=None, measurement_id=None, capacity_column=None, first_step=None, last_step=None, original_time_series=None, original_steps=None)[source]¶
Set internal state when constructing from processed data. Used by from_processed_data.
original_time_series and original_steps may be pandas or Polars DataFrames (or None); they are stored as Polars internally for config export.
- transform_rest_to_ocp()[source]¶
Extract OCP from rest steps: take the last data point of each rest.
Filters steps to those with
Step type == "Rest", computes cumulative net capacity (discharge/charge reset per step), then builds one OCP point (capacity, voltage) at the end of each rest. Iftransforms["keep_first_ocp_point"]is True, prepends the first row of the time series as an extra OCP point. Replacesdatawith the OCP table and clearssteps.
- static remove_duplicate_ocp(data: DataFrame, capacity_column_name='Capacity [A.h]') DataFrame[source]¶
Remove duplicate capacity and voltage points.
Keeps first occurrence of each unique capacity and each unique voltage. Used by the
remove_duplicatestransform option.
- static sort_capacity_and_ocp(data: DataFrame) DataFrame[source]¶
Sort OCP data so voltage is decreasing and capacity is increasing.
Ensures a single capacity column (normalized to start at 0 and non-decreasing), removes duplicates, and reverses rows if voltage is increasing. Used by the
sorttransform option.
- static remove_ocp_extremes(data: DataFrame) DataFrame[source]¶
Remove OCP points at extremes where d²V/dQ² is zero.
Trims the capacity–voltage curve to the range where the second derivative of voltage with respect to capacity is non-zero. Used by the
remove_extremestransform option.
- calculate_dUdQ_cutoff(method: str = 'explicit', show_plot: bool = False, options: dict | None = None) float[source]¶
Calculate the cut-off for dUdQ based on the data.
- Parameters:
method (str, optional) – Method to use for calculating the cut-off. Options are: - “explicit” (default): Uses explicit method based on data range - “quantile”: Uses quantile-based method - “peaks”: Uses peak detection method
show_plot (bool, optional) – Whether to show a plot of the dUdQ values with the cut-off.
options (dict, optional) – Dictionary of options to pass to the method.
- Returns:
Cut-off for dUdQ
- Return type:
- calculate_dQdU_cutoff(method: str = 'explicit', show_plot: bool = False, options: dict | None = None) float[source]¶
Calculate the cut-off for dQdU based on the data.
- Parameters:
method (str, optional) – Method to use for calculating the cut-off. Options are: - “explicit” (default): Uses explicit method based on data range - “quantile”: Uses quantile-based method - “peaks”: Uses peak detection method
show_plot (bool, optional) – Whether to show a plot of the dQdU values with the cut-off.
options (dict, optional) – Dictionary of options to pass to the method.
- Returns:
Cut-off for dQdU
- Return type:
- to_local() DataLoader[source]¶
Convert a DB-backed loader into a fully local one.
Ensures all lazy data (steps and time series) is fetched, then removes the measurement ID so that
to_config()serialises the data inline instead of as adb:reference.Returns
selffor chaining, e.g.loader.to_local().to_config().
- to_config(filter_data: bool = True) dict[source]¶
Convert the DataLoader back to parser configuration format.
- generate_experiment(use_cv: bool = False, termination: str = 'duration', period: str | float | None = None, split_cc_cv: bool = True) pybamm.Experiment[source]¶
Generate a PyBaMM experiment from the loaded step information.
- Parameters:
use_cv (bool, optional) – Emit constant-voltage steps as voltage holds rather than as a current interpolant. Default False.
termination (str, optional) –
What ends each step. Default
"duration"."duration": every step ends after its measuredDuration [s]."events": every step ends where the measured step ended, in the variable it was controlling — a constant-current or constant-power step at its final voltage, a voltage hold at its final current — and carries no duration, so its length becomes the model’s own. A capacity disagreement between model and cell then reads as capacity to that endpoint rather than as a residual voltage. Rests and steps emitted as a current interpolant keep their duration either way.
"events"replays endpoints, not the cycler’s intent: a step the cycler ended on a time limit is still replayed as ending at the voltage it reached, because the steps table cannot tell the two apart. Use it for endpoint-driven protocols (rate capability, CC-CV cycling) and"duration"for time-driven ones. Where a step’s voltage barely moves, such as a short pulse, the endpoint can already be satisfied at the model’s initial state; PyBaMM warns and skips such a step rather than solving it.period (str or float, optional) – Sampling period for the emitted steps, as
pybamm.Experimenttakes it."from data"gives each step the measurement’s own mean sample interval. Default None, which leaves the output grid to PyBaMM.split_cc_cv (bool, optional) – Emit a step no type fits that ran constant current and then held its voltage, as cyclers often log a CC-CV charge, as a constant-current step followed by a constant-voltage step, rather than as a current interpolant. Default True. A split step becomes two experiment steps, so every later step of its cycle sits one position further along than its row in the steps table; code that reads the experiment’s steps by steps-table position has to allow for it.
- Returns:
One PyBaMM cycle per run of equal
"Cycle count"in the steps table. A table without that column has no cycle structure to recover and becomes a single cycle. The"Cycle count"behind each cycle is left inexperiment_cycle_counts, since the experiment itself numbers its cycles by position, and the steps-table row behind each step inexperiment_step_sources.- Return type:
pybamm.Experiment
- generate_interpolant() pybamm.Interpolant[source]¶
Generate a PyBaMM interpolant from the loaded step information.
- plot_data(show: bool = False) tuple[plt.Figure, plt.Axes][source]¶
Plot voltage vs time data from the loaded experiment.
- classmethod from_local(data_path, options=None, use_polars=True)[source]¶
Load data from local filesystem.
- Parameters:
data_path (str) – Path to the directory containing time_series.parquet (or time_series.csv) and optionally steps.parquet (or steps.csv).
options (dict | None, optional) – Options to pass to the DataLoader constructor.
use_polars (bool, optional) – If True (default), read with Polars. If False, read with Pandas (data is still stored as Polars internally).
- Return type:
- classmethod from_db(measurement_id: str, options: dict | None = None, use_cache: bool = True, client=None, time_range: dict | None = None) DataLoader[source]¶
Load data from the Ionworks database (lazy loading).
Data is fetched on demand: accessing
.initial_voltageor.stepsloads only the steps table (small payload) viaclient.cell_measurement.steps(measurement_id). Accessing.dataloads the time series as well viaclient.cell_measurement.time_series(measurement_id), after steps if needed for slicing. This allows reading e.g. initial voltage without downloading the full time series.- Parameters:
measurement_id (str) – The ID of the measurement to load from the database.
options (dict | None, optional) – Options to pass to the DataLoader constructor.
use_cache (bool, optional) – If True (default), use local file cache to avoid repeated API calls. Set to False to force a fresh load from the database.
client (ionworks.api.Client | None, optional) – Pre-configured Ionworks client. If not provided, a default
Client()is created (using env vars).time_range (dict | None, optional) – Optional
{"start": seconds, "end": seconds}window of elapsedTime [s]values, applied afterfirst_step/last_stephave chosen their steps and written intoto_config(). It pins a re-run to the data the first run saw, so later appends do not change the answer. A property of the pipeline config, not of the measurement’s own start/end times.
- Return type:
- classmethod from_processed_data(data, steps, initial_voltage, start_idx, end_idx)[source]¶
Create a DataLoader from already-processed data, bypassing __init__.
- Parameters:
- Return type:
- copy() DataLoader[source]¶
Create a copy of the DataLoader instance.
- slice_to_steps(first_step_idx: int, last_step_idx: int) DataLoader[source]¶
Create a new DataLoader containing only the given step range.
Only requires the steps table to be loaded — the time-series data remains lazy (not loaded) when the source DataLoader is lazy.
- Parameters:
- Return type: