Cell Measurements#
Measurements store time-series data recorded from testing a cell instance. Each measurement includes metadata (protocol, test setup, notes) and the raw time-series data itself.
Uploading a measurement is a single client call, but behind the scenes the client handles a three-step signed-URL upload process automatically.
Preparing time-series data#
Time-series data is passed as a pandas or polars DataFrame (or a plain
dict of lists). The following columns are required:
Column |
Description |
|---|---|
|
Elapsed time in seconds |
|
Cell voltage |
|
Applied current |
|
Step index (zero-based) |
|
Cycle index (zero-based) |
|
Raw step number from the cycler |
|
Raw cycle number from the cycler |
import pandas as pd
time_series = pd.DataFrame({
"Time [s]": [0, 1, 2, 3, 4, 5],
"Voltage [V]": [3.0, 3.2, 3.5, 3.8, 4.0, 4.2],
"Current [A]": [0.002, 0.002, 0.002, 0.002, 0.002, 0.002],
"Step count": [0, 0, 0, 1, 1, 1],
"Cycle count": [0, 0, 0, 0, 0, 0],
"Step from cycler": [1, 1, 1, 2, 2, 2],
"Cycle from cycler": [0, 0, 0, 0, 0, 0],
})
Uploading a measurement#
bundle = client.cell_measurement.create(
cell_instance.id,
{
"measurement": {
"name": "Formation Cycle 1",
"protocol": {
"name": "CC-CV charge at C/10 to 4.2V",
"ambient_temperature_degc": 25,
},
"test_setup": {
"cycler": "Biologic VMP3",
"operator": "Jane Smith",
},
"notes": "Formation cycle — first charge",
},
"time_series": time_series,
},
)
print(f"Measurement ID: {bundle.measurement.name}")
print(f"Steps created: {bundle.steps_created}")
Use create_or_get() to skip the upload when a measurement with the same name
already exists on the instance:
result = client.cell_measurement.create_or_get(cell_instance.id, {
"measurement": {"name": "Formation Cycle 1", ...},
"time_series": time_series,
})
Listing measurements#
measurements = client.cell_measurement.list(cell_instance.id)
for m in measurements:
print(m.name)
Retrieving a measurement#
# Metadata only (no data)
measurement = client.cell_measurement.get(measurement_id)
Full measurement detail#
detail() fetches metadata, steps, cycles, and time-series
data in parallel. Use the include_* flags to skip data you
don’t need:
# Everything (3 parallel requests + download)
detail = client.cell_measurement.detail(measurement_id)
print(detail.steps.shape)
print(detail.cycles.shape)
print(detail.time_series.shape)
# Metadata + steps/cycles only (no download)
detail = client.cell_measurement.detail(
measurement_id, include_time_series=False
)
Accessing steps, cycles, and time series individually#
Each data type can also be fetched on its own:
steps = client.cell_measurement.steps(measurement_id)
cycles = client.cell_measurement.cycles(measurement_id)
ts = client.cell_measurement.time_series(measurement_id)
# Or fetch steps and cycles together (one call,
# more efficient than two separate calls):
sc = client.cell_measurement.steps_and_cycles(measurement_id)
print(sc.steps.shape, sc.cycles.shape)
Updating a measurement#
updated = client.cell_measurement.update(measurement_id, {
"notes": "Updated notes",
})
Deleting a measurement#
client.cell_measurement.delete(measurement_id)
DataFrame backend#
By default, data is returned as polars DataFrames. To use pandas instead, set the backend at client creation or at any time:
# At client creation
client = Ionworks(dataframe_backend="pandas")
# Or at any time
from ionworks import set_dataframe_backend
set_dataframe_backend("pandas")
You can also set the IONWORKS_DATAFRAME_BACKEND environment
variable to "pandas" or "polars".
Next steps#
With data uploaded you can run parameter fitting and analysis workflows in Pipelines, or run forward simulations in Simulations.