Cell Measurement#

Client for managing cell measurements and time series data uploads.

For usage examples and guides, see Data API on docs.ionworks.com.

Cell measurement client for managing battery cell test data.

This module provides the CellMeasurementClient for uploading, retrieving, and managing measurement data from battery cell testing. It supports time series data (signed URL upload, redirect-based download), file measurements (signed URL upload, redirect-based download), and properties measurements (direct JSON).

ionworks.cell_measurement.PROCESSING_TIMEOUT_SECONDS = 600.0#

How long to wait for the server to derive an upload’s steps before giving up. Generous: the work is queued, so it covers both a busy queue and a large file, and giving up only stops the waiting, never the processing.

ionworks.cell_measurement.PROCESSING_POLL_SECONDS = 2.0#

Seconds between processing_status polls while waiting.

class ionworks.cell_measurement.CellMeasurementClient(client)[source]#

Bases: object

Client for managing cell measurement data.

Parameters:

client (Any)

UPLOAD_TIMEOUT: tuple[float, float] = (10, 300)#

Default timeout for signed URL uploads as (connect, read) in seconds.

__init__(client)[source]#

Initialize the CellMeasurementClient.

Parameters:

client (Any) – The HTTP client instance for making API calls.

Return type:

None

list(cell_instance_id, limit=None, offset=None, measurement_type=None, *, name=None, name_exact=None, created_by_email=None, started_after=None, started_before=None, created_after=None, created_before=None, updated_after=None, updated_before=None, order_by=None, order=None)[source]#

List cell measurements for a cell instance with optional filtering.

Always returns a PaginatedList which behaves like a regular list. Use limit and offset to control the page.

Parameters:
  • cell_instance_id (str) – The ID of the cell instance.

  • limit (int | None, optional) – Maximum number of measurements to return per page.

  • offset (int | None, optional) – Number of measurements to skip for pagination.

  • measurement_type (str | None, optional) – Filter by measurement type (e.g. "time_series", "file", "properties"). If None, returns all types.

  • name (str | None, optional) – Case-insensitive substring match on measurement name.

  • name_exact (str | None, optional) – Exact match on measurement name. Takes precedence over name.

  • created_by_email (str | None, optional) – Case-insensitive substring match on the creator’s email.

  • started_after (str | None, optional) – ISO datetime; return measurements started after this time.

  • started_before (str | None, optional) – ISO datetime; return measurements started before this time.

  • created_after (str | None, optional) – ISO datetime; return measurements created after this time.

  • created_before (str | None, optional) – ISO datetime; return measurements created before this time.

  • updated_after (str | None, optional) – ISO datetime; return measurements updated after this time.

  • updated_before (str | None, optional) – ISO datetime; return measurements updated before this time.

  • order_by (str | None, optional) – Column to sort by ("name", "created_at", "updated_at", "start_time").

  • order (str | None, optional) – Sort direction: "asc" or "desc".

Returns:

All measurements for the cell instance.

Return type:

PaginatedList[CellMeasurement]

get(measurement_id)[source]#

Get a specific cell measurement by its ID only.

Parameters:

measurement_id (str)

Return type:

CellMeasurement

detail(measurement_id, use_cache=True, include_steps=True, include_cycles=True, include_time_series=True)[source]#

Fetch measurement data.

Automatically adapts to the measurement type:

  • time_series: fetches steps, cycles, and time series data

  • file: fetches signed download URLs for files

  • properties: returns properties from the measurement metadata

Uses flat endpoints with parallel requests and downloads time series and steps via signed URL.

Use the include_* flags to skip fetching data you don’t need, which avoids unnecessary requests. These flags only apply to time_series-type measurements.

Parameters:
  • measurement_id (str) – The ID of the cell measurement.

  • use_cache (bool, optional) – If True (default), check the local file cache before making API calls and store results for future use. Applies to time_series measurements (steps, cycles, and time series data). File URLs are never cached because signed URLs are short-lived.

  • include_steps (bool, optional) – Whether to fetch step data. Defaults to True. Only applies to time_series measurements.

  • include_cycles (bool, optional) – Whether to fetch cycle metrics. Defaults to True. Only applies to time_series measurements.

  • include_time_series (bool, optional) – Whether to fetch time series data. Defaults to True. Only applies to time_series measurements.

Returns:

Measurement details with requested data fields. Fields not requested will be None.

Return type:

CellMeasurementDetail

steps(measurement_id, use_cache=True)[source]#

Download step data via redirect.

Downloads the raw parquet file directly from storage.

Parameters:
  • measurement_id (str) – The ID of the cell measurement.

  • use_cache (bool, optional) – If True (default), check the local file cache before making an API call and store the result for future use.

Returns:

Step data (polars or pandas based on config).

Return type:

DataFrame

cycles(measurement_id, use_cache=True)[source]#

Get cycle metrics for a measurement.

Parameters:
  • measurement_id (str) – The ID of the cell measurement.

  • use_cache (bool, optional) – If True (default), check the local file cache before making an API call and store the result for future use.

Returns:

Cycle metrics (polars or pandas based on config).

Return type:

DataFrame

steps_and_cycles(measurement_id, use_cache=True)[source]#

Get steps and cycles in one call.

More efficient than calling steps() and cycles() separately since cycles are derived from steps on the server.

Parameters:
  • measurement_id (str) – The ID of the cell measurement.

  • use_cache (bool, optional) – If True (default), check the local file cache before making an API call and store the result for future use.

Returns:

Object with steps and cycles DataFrames.

Return type:

StepsAndCycles

time_series(measurement_id, use_cache=True)[source]#

Download full time series as a parquet file.

The API endpoint redirects to storage; the HTTP client follows the redirect automatically and returns the raw parquet bytes.

Parameters:
  • measurement_id (str) – The ID of the cell measurement.

  • use_cache (bool, optional) – If True (default), check the local file cache before making an API call and store the result for future use.

Returns:

Full time series data.

Return type:

DataFrame

update(measurement_id, data)[source]#

Update an existing cell measurement.

Parameters:
  • measurement_id (str) – The ID of the cell measurement to update.

  • data (dict[str, Any]) – Dictionary containing the fields to update. This is the path for attaching/detaching a channel_id (pass None to detach) and for setting end_time to mark a run complete.

Returns:

The updated cell measurement.

Return type:

CellMeasurement

Raises:

IonworksError – When attaching a channel_id breaks a channel rule: BAD_REQUEST (400) for a cross-project channel, a non-time_series measurement, or a missing start_time; CONFLICT (409) for an out-of-commission channel or a time span overlapping another run already on that channel.

delete(measurement_id)[source]#

Delete a cell measurement by measurement ID only.

Parameters:

measurement_id (str)

Return type:

None

attach_raw_data(cell_measurement_id, raw_data_ids)[source]#

Attach raw-data records to this measurement (bulk, idempotent).

Parameters:
  • cell_measurement_id (str) – The ID of the cell measurement to attach raw data to.

  • raw_data_ids (list[str]) – IDs of the raw-data records to link to this measurement.

Return type:

None

detach_raw_data(cell_measurement_id, raw_data_id)[source]#

Remove one raw-data link from this measurement.

Parameters:
  • cell_measurement_id (str) – The ID of the cell measurement to detach raw data from.

  • raw_data_id (str) – ID of the raw-data record to unlink from this measurement.

Return type:

None

watch(measurement_id)[source]#

Start watching a measurement (the “watch this channel” action).

A watch is per-user and keyed on the measurement, but reads as watching the channel the measurement is running on: it powers the lab “my channels” view (client.lab.watched_channels) and clears itself once the test finishes, so you are no longer watching a channel whose test has ended. Idempotent – watching an already-watched measurement is a no-op.

Parameters:

measurement_id (str) – The ID of the (live) measurement to watch – typically the one on test on a channel (channel.measurement.id in the lab status).

Raises:

IonworksError – NOT_FOUND (404) if the measurement does not exist.

Return type:

None

unwatch(measurement_id)[source]#

Stop watching a measurement (the “unwatch this channel” action).

Idempotent – unwatching a measurement you are not watching is a no-op.

Parameters:

measurement_id (str) – The ID of the measurement to stop watching.

Return type:

None

list_raw_data(cell_measurement_id, limit=100, offset=0)[source]#

List raw-data records linked to this measurement.

Parameters:
  • cell_measurement_id (str) – The ID of the cell measurement whose raw-data links to list.

  • limit (int, optional) – Maximum number of raw-data records to return per page. Defaults to 100.

  • offset (int, optional) – Number of raw-data records to skip for pagination. Defaults to 0.

Returns:

Raw-data records linked to this measurement.

Return type:

PaginatedList[RawData]

wait_for_processing(measurement_ids, timeout=600.0)[source]#

Block until the server finishes deriving uploads’ steps.

Polls processing_status until every measurement reaches a terminal state. Steps are excluded from the reads: requesting them is exactly what the server refuses while processing is in flight.

Pass every id of a batch in one call rather than waiting on each in turn. The server processes uploads concurrently, so a batch takes about as long as its slowest member — and timeout is one deadline shared across the batch, where per-measurement waits would each carry their own and a stuck batch could hang for a multiple of it.

Waiting is not fail-fast: a measurement the server rejects does not stop the others being waited on. Every failure in the batch is reported together, once, when the batch settles.

Parameters:
  • measurement_ids (str | Iterable[str]) – The measurement, or measurements, to wait on.

  • timeout (float, optional) – Seconds to wait before giving up on whatever is still processing. Shared across the whole batch. Defaults to PROCESSING_TIMEOUT_SECONDS.

Raises:

MeasurementProcessingError – If the server rejected any upload, or if any did not finish within timeout. Inspect failures for the per-measurement reasons. A timeout does not cancel the work — it only stops waiting — so a measurement may still complete afterwards.

Return type:

None

create(cell_instance_id, measurement_detail, validate_strict=False, rated_capacity=None, voltage_window=None, skip_checks=None, wait_for_processing=True)[source]#

Create a new cell measurement with steps and time series data.

Uses signed URL upload for better performance with large datasets. Data is uploaded directly to storage as parquet, bypassing backend JSON parsing. No database record is created until the upload is confirmed, preventing orphaned records if upload fails.

Parameters:
  • cell_instance_id (str) – The ID of the cell instance to create the measurement for.

  • measurement_detail (dict[str, Any]) –

    Dictionary containing ‘measurement’, ‘steps’, and ‘time_series’.

    • measurement: dict with ‘name’ (required) and ‘notes’. An optional ‘data_type’ key routes validation: "ocp" for open-circuit potential data and "eis" for impedance spectra (validates the EIS columns and, in strict mode, the Z_Im sign and impedance magnitude). It is a client-side validation hint only and is not persisted. May also carry channel_id (the physical lab channel the test ran on) and start_time. If channel_id is set, this measurement is a channel-linked run, which requires start_time and forbids overlapping another run on the same channel — see the Raises note below and the manage-equipment skill.

    • time_series: pandas DataFrame or dict with time series data

    • steps: optional dict with pre-calculated steps data

  • validate_strict (bool, optional) – If False (default), runs only the always-on checks (positive current convention, time starts at 0, time monotonic, step count sequential, cumulative capacity/energy reset per step). If True, additionally runs: minimum points per step, cycle constant within step, time-gap check (> 5 h), and — when the corresponding inputs are provided — voltage continuity and consecutive-full-step / per-step capacity checks.

  • rated_capacity (float, optional) – Rated (nominal) cell capacity in A.h. Used only when validate_strict=True together with a steps entry in measurement_detail; enables the consecutive same-direction full-step check (hard) and the per-step capacity 500 % soft warning.

  • voltage_window (tuple[float, float], optional) – Rated (V_min, V_max) voltage window of the cell. Used only when validate_strict=True; enables the voltage-continuity check that detects time series rows that are out of chronological order.

  • skip_checks (Iterable[str], optional) – Names of strict-mode checks to skip while keeping validate_strict=True for everything else. Prefer this over disabling strict mode entirely when only a single check is a known false positive for the dataset. See ionworks.validators.STRICT_CHECK_NAMES for valid names.

  • wait_for_processing (bool, optional) –

    Block until the server has finished deriving the steps, raising if it could not. Defaults to True, because this call returning is otherwise no evidence the upload was usable: the record is created before the file is parsed, so a file the server cannot read still yields a successful response.

    Pass False when uploading a batch. The server processes uploads concurrently, so submitting all of them and then waiting on every id at once with wait_for_processing() takes about as long as the slowest single upload, where waiting inside each call would take the sum.

Returns:

Response containing the created measurement, steps count, and file path. steps_created is always 0 — steps are derived after the call returns, so the count is not known yet.

Return type:

CellMeasurementBundleResponse

Raises:
  • MeasurementProcessingError – If wait_for_processing and the server could not process the upload, or did not finish in time.

  • MeasurementValidationError – If data validation fails. Non-strict checks cover the sign convention, time axis, step-count monotonicity, and cumulative reset. Strict-only checks additionally cover time gaps, voltage continuity, and consecutive same-direction full steps.

  • IonworksError – When a channel_id in measurement breaks a channel rule: BAD_REQUEST (HTTP 400) if the channel is in another project, the measurement is not time_series, or start_time is missing; CONFLICT (HTTP 409) if the channel is out of commission or the run’s time span overlaps another measurement already on that channel.

create_or_get(cell_instance_id, measurement_detail, validate_strict=False, rated_capacity=None, voltage_window=None, skip_checks=None, wait_for_processing=True)[source]#

Create a new cell measurement or get existing.

Always returns a CellMeasurement regardless of whether the measurement was newly created or already existed.

Parameters:
  • cell_instance_id (str) – The ID of the cell instance.

  • measurement_detail (dict[str, Any]) – Dictionary containing measurement and time_series (same as create()).

  • validate_strict (bool, optional) – If False (default), skips strict validation. If True, runs strict validation including minimum points per step.

  • rated_capacity (float, optional) – Rated (nominal) cell capacity in A.h. Forwarded to create() to enable the consecutive-full-step and per-step capacity checks.

  • voltage_window (tuple[float, float], optional) – Rated (V_min, V_max) voltage window. Forwarded to create() to enable the voltage-continuity check.

  • skip_checks (Iterable[str], optional) – Names of strict-mode checks to skip. Forwarded to create(). Prefer skipping a specific check over disabling strict mode entirely.

  • wait_for_processing (bool, optional) – Forwarded to create(). Defaults to True. Pass False when uploading a batch and wait on the ids together afterwards with wait_for_processing(). Note that a measurement returned because it already existed has processed long ago, so only the newly created ids are worth waiting on.

Returns:

The measurement (newly created or existing).

Return type:

CellMeasurement

create_properties(cell_instance_id, name, properties, *, notes=None, protocol=None, start_time=None, end_time=None, test_setup=None)[source]#

Create a properties-type measurement (no file upload).

Use this for manual measurements like thickness, weight, or capacity that are stored as key-value pairs with units.

Parameters:
  • cell_instance_id (str) – The ID of the parent cell instance.

  • name (str) – Name for the measurement.

  • properties (dict[str, Any]) – Key-value measurements using Quantity format for numerics. Example: {"thickness": {"value": 0.52, "unit": "mm"}}.

  • notes (str | None, optional) – Optional notes for the measurement.

  • protocol (dict[str, Any] | None, optional) – Protocol information.

  • start_time (str | None, optional) – ISO-formatted start time for the measurement.

  • end_time (str | None, optional) – ISO-formatted end time. Set once the test is complete; leave None while it is still running.

  • test_setup (dict[str, Any] | None, optional) – Test setup metadata.

Returns:

The created measurement.

Return type:

CellMeasurement

ALLOWED_IMAGE_EXTENSIONS: frozenset[str] = frozenset({'bmp', 'gif', 'jpeg', 'jpg', 'png', 'tiff', 'webp'})#

Allowed image file extensions for upload.

create_file(cell_instance_id, name, filepaths, *, validate_images=False, notes=None, protocol=None, start_time=None, end_time=None, test_setup=None)[source]#

Create a file-type measurement by uploading files.

Uses a three-step signed URL flow: initiate (get signed URLs), upload each file, then confirm (create DB record). Supports any file type (PDFs, numpy arrays, images, etc.).

Set validate_images=True to opt into client-side image validation when uploading image files specifically.

Parameters:
  • cell_instance_id (str) – The ID of the parent cell instance.

  • name (str) – Name for the measurement.

  • filepaths (list[str]) – Local file paths to upload.

  • validate_images (bool, optional) – When True, validate that each file is a real image with an allowed extension before uploading. Defaults to False.

  • notes (str | None, optional) – Optional notes for the measurement.

  • protocol (dict[str, Any] | None, optional) – Protocol information.

  • start_time (str | None, optional) – ISO-formatted start time for the measurement.

  • end_time (str | None, optional) – ISO-formatted end time. Set once the test is complete; leave None while it is still running.

  • test_setup (dict[str, Any] | None, optional) – Test setup metadata.

Returns:

Response containing the created measurement and steps_created (0).

Return type:

CellMeasurementBundleResponse

Raises:
  • IonworksError – If validate_images is True and any file is not a valid image, or if upload fails.

  • FileNotFoundError – If any file path does not exist.

list_files(measurement_id)[source]#

List filenames in a file-type measurement.

Parameters:

measurement_id (str) – The ID of a file-type cell measurement.

Returns:

Filenames stored in the measurement.

Return type:

list[str]

Raises:

IonworksError – If the measurement is not a file type or does not exist.

download_files(measurement_id, use_cache=True, filenames=None)[source]#

Download files for a file-type measurement.

When filenames is provided the list_files round-trip is skipped, which avoids the slow storage-listing endpoint. Use this when you already know the filenames (e.g. from a previous list_files call or from the upload response).

Parameters:
  • measurement_id (str) – The ID of a file-type cell measurement.

  • use_cache (bool, optional) – If True (default), check the local file cache before downloading and store results for future use.

  • filenames (list[str] | None, optional) – Explicit list of filenames to download. When provided, the GET /cell_measurements/{id}/files listing call is skipped.

Returns:

Mapping of filename to file content bytes.

Return type:

dict[str, bytes]

Raises:

IonworksError – If the measurement is not a file type, does not exist, or a download fails.

get_file(measurement_id, filename)[source]#

Download a single file from a file-type measurement.

This is a convenience wrapper that fetches one file directly without any listing call, making it the fastest way to retrieve a known file.

Parameters:
  • measurement_id (str) – The ID of a file-type cell measurement.

  • filename (str) – Name of the file to download.

Returns:

Raw file content.

Return type:

bytes

Raises:

IonworksError – If the measurement or file does not exist.