Skip to main content

What are materials?

A Material is a reusable record describing a physical material used in a cell specification — for example, an NMC811 cathode powder, a graphite anode, or an LP57 electrolyte. Materials and their wrapping cell components (anode, cathode, electrolyte, separator, case) are always scoped to a project. Every material and component belongs to exactly one project, and cell specifications only ever reference materials and components in their own project. Two projects in the same organization each get their own copy of the “same” material — same name, manufacturer, and product ID — so property datasets and cell references stay cleanly separated per project. Reusing a material across projects means creating a matching material in each project.
Listing and creating materials require a project_id, but reading, updating, or deleting a single material by ID is authorized at the organization level — any project member with access to the material’s organization can look up or modify a material by ID, including one that belongs to a different project within that organization.
Any older materials that predated per-project scoping have been split into per-project copies, and the shared “System” material library (Graphite, NMC, LFP, …) is no longer read from at spec creation time — the library definitions are cloned into your own project instead. You will not see cross-project material sharing anywhere in the app.
Each material can have any number of property datasets attached to it. A property dataset is a tabular measurement (CSV or parquet) of one or more physical properties as a function of one or more independent variables — for example, electrolyte conductivity vs. concentration, or anode OCP vs. stoichiometry. Property datasets belong to the same project as their parent material.
Property datasets are stored as data — they are separate from parameter interpolants, which embed lookup tables directly into a parameterized model. Use property datasets to organize and share raw measurements, then turn them into interpolants when you are ready to use them in a simulation.

When to use materials

Use materials when you want to:
  • Keep a single source of truth for properties of a material used across multiple cells (e.g. the same electrolyte in several cell builds).
  • Store raw measurements (OCP, diffusivity, conductivity, transference number, …) alongside the material they were measured on.
  • Compare multiple datasets for the same property — for example, OCP curves measured at different temperatures or by different labs.
  • Track provenance: who uploaded a dataset, when, and from which raw file.

Managing materials in the UI

Each project has a Materials section in the left navigation. From there you can:
  • Create a material — give it a name, and optionally a manufacturer and product ID.
  • Open a material — view its property datasets and metadata.
  • Edit or delete a material from the row actions menu.
Materials created from the cell specification editor inherit the project of the cell spec, so you rarely need to pick a project explicitly — the material is created in the same project as the cell using it.

Uploading a property dataset

From a material’s detail page, click Upload property dataset and:
  1. Pick a file — CSV or parquet. CSVs may include or omit a header row. When you select a file, the dataset name is prefilled from the file name; edit it if you want something different.
  2. Review the dataset name — prefilled from the file name in step 1; change it if you want something different (e.g. Conductivity at 25 °C).
  3. Declare columns — every column detected in the file is listed in order. For each one, provide:
    • Name — the display name stored in the processed dataset (e.g. c_e). Every listed column must have a name before you can submit — drop the row for any column you don’t want to import, or name it and ignore it later. Trailing empty columns (a common Excel “trailing comma” artifact) are trimmed automatically.
    • Unit — the physical unit (e.g. mol/L, S/m). Leave blank for dimensionless quantities.
    • Source column — the column in the uploaded file the values come from. For headerless CSVs this is a position; for files with a header you can pick by name.
  4. Submit. The file is parsed, every value is coerced to a floating-point number (non-numeric cells become NaN), and both the processed parquet and the original raw file are stored.
After upload, the dataset appears in the material’s property list with the number of rows and any NaN counts per column, so you can spot parsing issues quickly.

Plotting a dataset

Click a dataset to open the plot dialog. You can:
  • Pick the x and y columns from the dataset. The legend shows each y column with its unit (e.g. kappa (S/m)) so dual-axis plots are easy to read.
  • Zoom and pan; the plot dynamically downsamples and re-fetches points for the visible range so large datasets stay responsive.
  • Download the processed parquet or the original raw file from the actions menu.

Editing a dataset

The Edit action on a dataset lets you:
  • Rename the dataset.
  • Re-declare column names and units. When columns change, the stored parquet is rebuilt from the preserved original file using the new specs — you do not need to re-upload.
  • Replace the data file entirely while keeping the same dataset ID and metadata. Other records that reference the dataset stay linked.
Each data-changing edit bumps the dataset’s data_version, so downstream consumers can detect when a cached result is stale.

Tracking dataset provenance

Every property dataset can record where it came from so you can trace a curve on a plot back to the pipeline, fit, or analysis that produced it — or to the paper, lab notebook, or vendor sheet it was digitized from. Provenance is captured with four optional fields: At most one of the three source_*_id fields may be set on a given dataset — a dataset has a single upstream Ionworks record, or none. The referenced row must exist, or the request is rejected; the check does not also verify that the row is visible to you (e.g. that it belongs to a project or organization you can access), so a source ID for a record you can’t otherwise see is set but only reads back as unresolved when someone tries to follow the link. source_label is independent and can be set on its own — use it when the source is not a linkable Ionworks record. The Source column on a material’s dataset grid renders source_label when set and otherwise the kind of source; when the source resolves to a pipeline or analysis you can see in Studio, the label is a link straight to the source’s detail page.

Set source when uploading or editing in the UI

The Upload property dataset and Edit dialogs include a Source section:
  1. Pick a source kind — Pipeline, Simple pipeline, Analysis, or leave blank for “no linkable source”.
  2. Paste the corresponding ID. Switching the kind clears any previously entered ID so you don’t end up with more than one source set.
  3. Optionally add a source label — a short free-text note that shows up in the Source column and on the dataset detail view.

Set source via the REST API

Pass any subset of the source fields when creating or updating a dataset. This example records that a dataset was produced by a pipeline run and adds a free-text label:
To change or clear provenance on an existing dataset, PATCH the fields you want to update; send null to clear a source ID or label:
The MaterialPropertyDataset records returned by the Python client and the REST API expose the same four fields, so you can read a dataset’s provenance from anywhere it appears:

Python client

The Python API client exposes materials and their property datasets as two sub-clients:
  • client.material — list and retrieve materials.
  • client.material_property_dataset — list, retrieve, and download property datasets, and create, edit, or delete them.
client.material is read-only; to create or modify a material itself, use the UI or the REST API. Property datasets can be managed from the client — see Creating and editing property datasets.

Listing and retrieving materials

project_id is required when listing materials — every material is scoped to a project, and the list endpoint returns the materials owned by that project. Each material record also carries its project_id. When the client has a default project configured, project_id falls back to it; without either, list() raises a ValueError. Filtering, ordering, and pagination all happen server-side, so you don’t need to fetch a page and match locally:
Text filters (name, manufacturer, product_id) take a bare value for an exact match, or an operator prefix such as "ilike.%graphite%" for a partial one. name_exact is shorthand for an exact match and cannot be combined with name. Time filters created_at and updated_at accept the same operator form (e.g. "gte.2026-01-01") and have _gt / _lt variants for range bounds. Sort with order_by (name, manufacturer, created_at, or updated_at) and order (asc or desc).

Finding which cells use a material

To go the other way — from a material to the cell specifications that use it — filter client.cell_spec.list by material. The match happens in the database across all five component slots, so don’t fetch every spec and inspect its components yourself.
A per-slot parameter is available for each slot — anode_material_id, cathode_material_id, electrolyte_material_id, separator_material_id, and case_material_id. Pass exclude_cell_spec_id to drop one spec from the results. A material reverse lookup is project-scoped, so it needs a project_id — it falls back to the one set on the client or IONWORKS_PROJECT_ID, and raises if neither is set. Because the filtering happens in the database, total counts every matching spec rather than the rows on the current page, so page through the results when there may be more than one page. Given one spec, related_specs finds the others that share a component material with it — useful for “what else did we build with this cathode?”
By default any of the source spec’s slot materials matches in any slot. Passing slots compares each named slot only against the same slot on the source spec. The source spec is excluded from the results unless you pass exclude_self=False, and include_components=True returns each spec with its nested component and material data.

Listing property datasets for a material

Each MaterialPropertyDataset exposes its columns (a list of ColumnSpec records with name, unit, and source_column_index), the data_version that bumps on every edit, and per-column nan_counts.

Downloading dataset values

get_data() downloads the full dataset and returns it as a DataFrame in the configured DataFrame backend:

Creating and editing property datasets

create() uploads tabular data — a polars or pandas DataFrame, or a column-name-to-values dict — as a new dataset. Values are stored as floats, with any non-parseable cell recorded as NaN:
Each column’s display name and unit come from its columns specs. When columns is omitted they are inferred from a trailing [unit] in the column name, as above; pass explicit specs to control the units directly. To edit an existing dataset:
replace_file() never infers columns, so it cannot silently erase stored units. When you omit columns it checks that the replacement’s column layout matches the stored specs and raises a ValueError on a mismatch rather than mislabelling the data. Pass columns explicitly to change the specs along with the file.
get_download_url(dataset_id, kind="parquet") returns a short-lived signed URL for the processed parquet; pass kind="original" for the file as uploaded.

REST API

Material property datasets are managed under /material_property_datasets. Materials themselves are managed under /materials.

Upload a dataset

POST /material_property_datasets accepts a multipart form: At most one of the three source_*_id fields may be set on a given dataset. Set source_label alone when the provenance is not a linkable Ionworks record — for example, “manually digitized from Smith et al. 2023”. See Tracking dataset provenance for the full model. Each column spec is an object:
source_column_index is the 0-based position of the column in the uploaded file. It is required even when the file has a header row — names are matched by position, then renamed to the name you provide. Example upload with curl:
The response is the new dataset record, including its id, storage_path, data_version, and per-column nan_counts.

List datasets for a material

Returns a paginated list of dataset records.

Fetch dataset values as JSON

Returns the dataset as a column-major JSON object:
max_points downsamples uniformly so large datasets remain responsive to plot. x_col, x_min, and x_max restrict the response to a range of one column — useful for zooming charts.

Download the underlying file

Use GET /material_property_datasets/{id}/file to redirect to a short-lived signed URL for the file, or GET /material_property_datasets/{id}/download-url to receive the URL as JSON (handy when you want to open it from the browser). Pass ?kind=parquet (default) to download the processed parquet, or ?kind=original to download the raw file you uploaded.

Update metadata, replace the file, or delete

  • Cells — materials are referenced from the anode, cathode, electrolyte, and separator components of a cell specification.
  • Parameter interpolants — turn measured property data into lookup-table parameters inside a parameterized model.
  • Electrolyte transport from a dataset — build concentration-dependent electrolyte transport parameters from a property dataset and drop them into a pipeline.
  • Data overview — how experimental data is organized in Ionworks Studio.