Upload workflow
Create or get a cell specification
The spec defines the cell’s materials, electrical ratings, and components.
Step 1: Create or get a cell specification
Step 2: Create or get a cell instance
| Field | Description |
|---|---|
name | Unique identifier for this cell |
batch | Batch or lot number |
date_manufactured | Manufacturing date (optional) |
measured_properties | Cell-specific measured values that differ from the spec (e.g., actual measured capacity, electrode loadings) |
Step 3: Upload a measurement with time series
Upload validation
Before any data is sent to Ionworks Studio, the Python client validates your time series on your machine. The always-on checks catch the most common processing mistakes:- Positive current corresponds to discharge (voltage decreases); charging uses negative current
- Time starts at 0 and is monotonically non-decreasing
Step countexists, starts at 0, and increments by 1- Cumulative columns (capacity, energy) reset at the start of each step
create and create_or_get raise a
MeasurementValidationError and no upload is initiated. See
Troubleshooting below for how to fix each failure.
Inspecting validation failures
MeasurementValidationError.errors is a list of ValidationIssue records.
Each issue carries a stable code (an IssueCode enum), a severity
("error" or "warning"), a human-readable message, and a structured
payload dict with details such as step indices, column names, observed
values, and thresholds.
Branch on code rather than parsing message — wording can change
between releases, but check identifiers are stable.
Strict validation
We recommend passingvalidate_strict=True on every upload. The strict
checks catch subtle bugs introduced during data processing — for example,
large unrecorded time gaps or a time series whose rows have been reordered by
a faulty groupby or partition_by call — before the data lands in Ionworks
Studio and contaminates downstream simulations and fits.
- Each step contains at least 2 data points
Cycle countis constant within each step- No gap between consecutive time samples exceeds 5 hours
- Voltage is continuous between consecutive rows (requires
voltage_window) - No two consecutive same-direction steps deliver a full charge or discharge
(requires
rated_capacityand astepsdataframe inmeasurement_detail) - No single step exceeds 500 % of the rated capacity — emitted as a
UserWarningrather than raising (requiresrated_capacityandsteps) - Reported cumulative capacity and energy columns agree with the trapezoidal
integral of current (and power) within 10 % at every row — catches
mislabeled
Discharge/Chargecolumns, dropped samples, and sign-convention bugs that survive the always-on checks
Providing cell context for richer checks
The voltage-continuity and step-capacity checks need to know the cell’s rated voltage window and capacity. Pass them as keyword arguments:voltage_window skips only the
voltage-continuity check; omitting rated_capacity skips only the
consecutive-full-step and per-step capacity checks. All other strict-mode
checks still run.
The consecutive-full-step and per-step capacity checks also require a
pre-computed step summary under the
steps key of measurement_detail.
If you do not provide one, Ionworks Studio generates step summaries
server-side after upload, but these two client-side checks are skipped.create_or_get accepts the same rated_capacity and voltage_window
arguments and forwards them to create.
Relaxing a single strict check
If a specific strict check is a known false positive for your dataset — for example, a legitimate multi-day rest period that exceeds the 5-hour time-gap threshold — relax only that check withskip_checks rather than turning
strict mode off entirely. This keeps every other guardrail in place
(least-privilege validation).
ionworks.validators.STRICT_CHECK_NAMES):
minimum_points_per_stepcycle_constant_within_steptime_gapsvoltage_continuityconsecutive_same_direction_full_stepsstep_capacity_within_ratedcapacity_energy_from_current_power
ValueError. Avoid disabling strict mode wholesale unless
you have a documented reason that applies to multiple checks at once.
Idempotent uploads with create_or_get
All three create methods have create_or_get variants that make upload
scripts safely re-runnable — if a resource with the same name already exists,
the client fetches and returns it instead of raising an error.
Duplicate handling
When you callcreate() (not create_or_get) and a resource with the same
name already exists, the API returns a 409 Conflict with the existing
resource’s ID in the detail field:
existing_id lets you fetch the existing record without a separate lookup
if you want to implement your own create-or-get logic.
Inline time series size limit
Time series passed inline (e.g. inside a pipeline configuration) are capped at 1,000 rows — larger datasets must be uploaded as measurements first. See reading data — inline size limit for the error class, workaround, anddb:<measurement_id> reference form.
Automatic payload compression
The Python client automatically compresses request payloads larger than 512 KB using gzip before sending them to the API. This happens transparently — no configuration is needed. For large serialized models or datasets, this can reduce upload sizes significantly.Troubleshooting
Large time gap between consecutive rows
Problem: When uploading withvalidate_strict=True, you receive an error
like:
Time gap of 6.12 h between rows 4201 and 4202 exceeds the maximum allowed gap of 5.0 h.Solution: A gap longer than 5 hours between two consecutive time samples almost always means rows were dropped during processing — for example, a long rest period was recorded as elapsed time in the file but the intermediate rows were stripped. The capacity integral then counts current across the missing interval and reports inflated values. Re-read the raw cycler file without filtering and confirm the time axis is continuous. If your workflow intentionally decimates the data, downsample uniformly rather than removing whole sections, or split the recording into separate measurements at the gap.
Voltage continuity check failed
Problem: When uploading withvalidate_strict=True and a voltage_window,
you receive an error like:
Voltage continuity check failed: 318/3599 (8.8%) of consecutive row pairs have a voltage jump greater than 80% of the rated voltage window.Solution: Ionworks looks at the absolute voltage change between every pair of consecutive rows. If more than 5 % of pairs exceed 80 % of the rated voltage window (
V_max - V_min), the time series is almost certainly out of
chronological order — typically because of a grouping operation like
partition_by("Cycle_raw") that interleaves pulse and rest rows from
different cycles.
Sort your DataFrame by Time [s] before uploading, and avoid any transform
that breaks the natural row order:
Consecutive full-capacity steps in the same direction
Problem: When uploading withvalidate_strict=True, a steps dataframe,
and a rated_capacity, you receive an error like:
Consecutive full-capacity discharge steps detected: step 12 and step 15 each delivered more than 200% of the rated capacity.Solution: “Consecutive” here means the two nearest same-direction steps in the sequence — so step 12 and step 15 are consecutive discharges even if charge steps sit between them. A single measurement should represent one continuous test on a single cell, so two back-to-back full-capacity discharges (or charges) usually mean the file was assembled by concatenating several independent experiments (for example, multiple CC discharges at different C-rates stitched together). Split the source data back into separate measurements before uploading.
If a legitimately long single experiment contains more than two full
charges or discharges in a row — rare, but possible — drop the pre-computed
steps entry from measurement_detail or omit rated_capacity so this
specific check is skipped, and let Ionworks Studio regenerate step
summaries server-side.Step capacity exceeds rated capacity
Problem: When uploading withvalidate_strict=True, a steps dataframe,
and a rated_capacity, you see a UserWarning like:
3 step(s) exceed 500% of the rated capacity. First: step 47 has ‘Discharge capacity [A.h]’ = 0.031 A.h.Solution: This is a soft warning — the upload still proceeds — but it signals that something is probably wrong with your step boundaries or that the capacity integral was inflated across an unrecorded time gap. Inspect the flagged step, verify it represents a single charge or discharge, and re-check the
Step count column for gaps. Fixing the underlying time-gap or step
boundary issue usually clears the warning.
Capacity or energy column disagrees with the current/power integral
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 47.3% (exceeds 10% tolerance). Worst row 8421: reported = 0.0019 A.h, integrated = 0.0010 A.h.This check compares each reported cumulative column against the trapezoidal integral of current (capacity) or power (energy) over time, with a per-step reset that mirrors how the platform reports cumulative columns. With the Ionworks sign convention (positive current = discharge):
Discharge capacity [A.h]≈∫ max(I, 0) dt / 3600Charge capacity [A.h]≈∫ max(-I, 0) dt / 3600Discharge energy [W.h]≈∫ max(P, 0) dt / 3600Charge energy [W.h]≈∫ max(-P, 0) dt / 3600
Power [W] if present, otherwise computed as
Voltage [V] * Current [A]. The corresponding issue codes are
DISCHARGE_CAPACITY_INTEGRAL_MISMATCH, CHARGE_CAPACITY_INTEGRAL_MISMATCH,
DISCHARGE_ENERGY_INTEGRAL_MISMATCH, and CHARGE_ENERGY_INTEGRAL_MISMATCH.
Each issue’s payload carries the worst-row index, the reported and
integrated values at that row, the max cumulative scale, and the relative
error.
Solution: The mismatch usually traces back to one of three causes:
-
Swapped
Discharge/Chargelabels — common with half-cell exports and some cycler configurations. If both the discharge and charge columns fail by similar amounts and their values “look swapped”, use the auto-fix transform: -
Wrong sign convention — if
CURRENT_SIGN_REVERSEDalso fires, fix that first withset_positive_current_for_dischargeand re-validate. -
Unreliable source columns — drop them and let
iwd.transform.set_capacity/set_energyrecompute the cumulative columns fromCurrent [A],Voltage [V], andTime [s]:
skip_checks={"capacity_energy_from_current_power"}.
Next steps
Measurements
The three measurement types — time series, properties, file — in detail.
Reading data
List, filter, paginate, and retrieve measurement data.
Preparing data
Read cycler files into the Ionworks format before uploading.
Data format
Recognized columns, quantity format, and sign conventions.