> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ionworks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Measurements

> The three measurement types — time series, properties, and file — with fields, creation examples, and when to use each

A **cell measurement** represents a single experiment or test performed on a
[cell instance](/core-concepts/cells). Every measurement has a `measurement_type`
that determines what data it stores and how you create it.

## Measurement types

| Type          | Stores                                                                                                                  | Typical use                                                           |
| ------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `time_series` | High-resolution cycling data with voltage, current, and time columns. Includes auto-generated step and cycle summaries. | Battery cycling tests, EIS measurements, charge/discharge experiments |
| `properties`  | Key-value pairs with optional units. No file upload.                                                                    | Manual measurements like thickness, weight, internal resistance       |
| `file`        | One or more uploaded files (images, PDFs, numpy arrays, etc.) attached to the record.                                   | Microscopy images, SEM photos, post-mortem analysis documents         |

## Common fields

Every measurement — regardless of type — accepts these optional metadata fields:

| Field                               | Description                                                                                                                                                                                                                       |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`                              | Name for the measurement                                                                                                                                                                                                          |
| `protocol.name`                     | Protocol description                                                                                                                                                                                                              |
| `protocol.ambient_temperature_degc` | Test temperature                                                                                                                                                                                                                  |
| `test_setup`                        | `cycler`, `operator`, `lab`, `channel_number`                                                                                                                                                                                     |
| `start_time`                        | ISO 8601 start time, e.g. `"2026-03-15T09:30:00Z"`                                                                                                                                                                                |
| `channel_id`                        | ID of the physical [channel](/operate/lab-view) the test ran on. Must belong to the same project as the measurement.                                                                                                              |
| `program_id`                        | Optional [program](/operate/planned-measurements#programs) this test belongs to. Copied automatically from the linked planned measurement, or set directly on the measurement. Must reference a program in the same organization. |
| `estimated_end_time`                | ISO 8601 estimate of when the test will finish. Optional. Shown on channel cards in the [Lab view](/operate/lab-view) while the test is running so you can see when a channel is expected to free up.                             |
| `notes`                             | Free-form notes about the test                                                                                                                                                                                                    |

## Time series

Time series is the default measurement type. It carries high-resolution cycling
data and auto-generates step and cycle summaries on upload.

The time series DataFrame must follow the [data format](/data/format) — recognized
columns include `Time [s]`, `Voltage [V]`, `Current [A]`, `Step count`, and
`Cycle count`.

```python theme={null}
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],
})

bundle = client.cell_measurement.create(
    cell_instance.id,
    {"measurement": {"name": "Formation Cycle 1"}, "time_series": time_series},
)
```

`create()` returns a `MeasurementBundle` containing the measurement record plus
metadata like `steps_created`. See [uploading data](/data/uploading) for the
full upload workflow — protocol and test-setup metadata, on-machine validation,
and idempotent `create_or_get` — and [reading data](/data/reading) to fetch a
measurement back with its full time series, steps, and cycles.

## Properties

Properties measurements store key-value pairs directly in the record — no file
upload. Use them for manual or one-off measurements like thickness, weight,
or internal resistance.

**Properties-specific fields:**

| Field        | Description                                                                                                                              |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `properties` | Key-value pairs. Use the [quantity format](/data/format#quantity-format) for numeric values. Plain strings and numbers are also allowed. |

```python theme={null}
measurement = client.cell_measurement.create_properties(
    cell_instance.id,
    name="Post-formation properties",
    properties={
        "thickness": {"value": 0.52, "unit": "mm"},
        "weight": {"value": 45.3, "unit": "g"},
        "internal_resistance": {"value": 12.5, "unit": "mOhm"},
        "visual_condition": "No visible damage",
    },
    notes="Measured after formation cycling",
)
```

You can include any of the common metadata fields (`protocol`, `test_setup`,
`start_time`, `notes`) alongside the properties.

<Note>
  `create_properties` returns a `Measurement` directly, not a `MeasurementBundle`.
  Properties measurements have no steps or file uploads, so the bundle wrapper
  isn't needed.
</Note>

## File

File measurements attach files (images, PDFs, numpy arrays, or any other file
type) to a measurement record. Useful for microscopy images, SEM photos,
X-ray CT scans, or post-mortem analysis documents.

**File-specific fields:**

| Field             | Description                                                                                   |
| ----------------- | --------------------------------------------------------------------------------------------- |
| `filepaths`       | List of local file paths to upload                                                            |
| `validate_images` | When `True`, validates file headers match the extension before upload                         |
| `file_metadata`   | Populated automatically by the server after upload (MIME types, dimensions, etc.) — read-only |

```python theme={null}
bundle = client.cell_measurement.create_file(
    cell_instance.id,
    name="Post-cycling SEM images",
    filepaths=[
        "images/anode_surface_1000x.png",
        "images/cathode_cross_section_500x.png",
    ],
    notes="SEM images taken after 500 cycles",
)
```

To enable client-side image validation:

```python theme={null}
bundle = client.cell_measurement.create_file(
    cell_instance.id,
    name="Electrode images",
    filepaths=["photo.png", "scan.tiff"],
    validate_images=True,
)
```

To access files from an existing file measurement:

```python theme={null}
# List filenames in the measurement
filenames = client.cell_measurement.list_files(measurement_id)

# Download file contents directly
files = client.cell_measurement.download_files(measurement_id)
for filename, content in files.items():
    print(f"{filename}: {len(content)} bytes")
```

<Note>
  File measurements use a signed URL upload flow internally. The client handles
  the multi-step process (initiate, upload, confirm) automatically.
</Note>

### Previewing files in Studio

When you open a file measurement in Studio, attached files are shown as a
gallery beneath the measurement details:

* **Images** (`.png`, `.jpg`, `.webp`, etc.) render as inline thumbnails. Click
  a thumbnail to open it in a full-screen lightbox.
* **Non-image files** (PDFs, numpy arrays, CSVs, etc.) appear as cards with a
  download button.

This makes it easy to review SEM photos, X-ray CT scans, and other microscopy
images directly in the browser without downloading them first.

## Next steps

<CardGroup cols={2}>
  <Card title="Uploading data" icon="upload" href="/data/uploading">
    End-to-end upload workflow: specs, instances, and measurements.
  </Card>

  <Card title="Reading data" icon="download" href="/data/reading">
    List, filter, and retrieve measurements with their full data.
  </Card>
</CardGroup>
