Cellpy Core Harmonized_Raw¶
Authoritative definition — this is the single source of truth for the harmonized raw
format. (Moved here from docs/harmonized_raw_definition.md; it supersedes the older
2025-09-08 draft that was removed in issue #10.)
Date: 2025-09-17 (reviewed in issue #10)
SPEED-16: "Define the object that goes into cellpy core"
Purpose¶
Original cycler files are converted to this format, in order to unify the format so that only one importer has to be used wherever it will be imported.
Conventions¶
- The cell voltage column is named
potential(notvoltage). - Time columns (
test_time, etc.) are in seconds. - The cycler step mode column is
step_mode. There is nochannel_statuscolumn (it was dropped in issue #10). epoch_time_utcis int64 nanoseconds since the Unix epoch, UTC. This matches the internal representation ofpolars/pandas/Arrow timestamps, so round-trips to native datetime types are lossless (float epoch seconds cannot hold nanosecond resolution). When an importer only has the cycler's wall-clock timestamp (often a naive, timezone-less datetime, e.g. legacy Arbindate_time), it is converted to epoch nanoseconds by treating the naive timestamp as UTC. (Example:dev/make_harmonized_raw.pydoes this when building thecycler_cc_harmonized_raw.parquettest fixture.) Conversion helpers to/from float seconds UTC and native datetime live incellpycore.timestamps.- Capacity / energy columns are cumulative per cycle, per direction — see Capacity
convention below. (Renamed from
step_cumulative_*in issue #13; the old name was misleading.)
Column Headers¶
Set up a flexible structure that allows for more columns.
| Column name | Data type | Unit | Sample data | Comment |
|---|---|---|---|---|
| datapoint_num | int | - | 1234 | index, corrected sequential datapoints |
| source_datapoint_num | int | - | 1234 | original data point number from data collection |
| mask | boolean | - | True | default: True (meaning: this value is selected and used) |
| epoch_time_utc | int64 | nanosecond | 1715609528578140000 | nanoseconds since the Unix epoch, UTC; convert to seconds via cellpycore.timestamps |
| test_time | float | second | 12.43212 | - |
| step_time | float | second | 12.43212 | optional value; time elapsed since the start of the current step |
| source_type | str(10) | - | "Neware" | - |
| source_uuid | str(36) | - | "e15b46ca-e584-467f-a176-8bf98b8090e5" | will not be used, only kept for info and tracability |
| test_id | int | - | 0 | compact per-test key within a (possibly merged) object; 0 for a single test. Group keys are (test_id, cycle_num, step_num, ...). Global identity: source_uuid |
| step_num | int | - | 123 | updated unique and sequential step number |
| source_step_num | int | - | 123 | original step number |
| step_type | str(10) | - | "charge", "discharge", "rest", etc. | optional value |
| step_type_detail | str(10) | - | - | optional value; (to be used to give additional info about steps, for example when a test is interrupted) |
| step_mode | str(10) | - | "CV", "CC", "CP", "None" | optional value |
| cycle_num | int | - | 12 | - |
| cycle_type | str(36) | - | "Standard", "GITT", "ICI", "Characterization" | categorial column; in first version: pre-defined input |
| potential | float | Volt | 3.6500 | - |
| current | float | Ampere | 96.4413 | - |
| cumulative_charge_capacity | float | Ah | 34.5678 | cumulative per cycle, per direction (resets at each cycle boundary); per-step / per-cycle values are derived downstream — see Capacity convention below |
| cumulative_discharge_capacity | float | Ah | 34.5678 | cumulative per cycle, per direction (see Capacity convention) |
| cumulative_charge_energy | float | Wh | 34.5678 | ** ; cumulative per cycle, per direction (see Capacity convention) |
| cumulative_discharge_energy | float | Wh | 34.5678 | ** ; cumulative per cycle, per direction (see Capacity convention) |
| step_charge_power | float | W | 34.5678 | ** |
| step_discharge_power | float | W | 34.5678 | ** |
| internal_resistance | float | ohm | 56.4866 | optional value; instrument-reported internal resistance |
| ref_potential | float | Volt | 3.4500 | optional value; reference-electrode potential (3-electrode setups); added in issue #43 |
| aux_temperature_cell | float | degrees celcius | 25.3 | - |
| aux_temperature_chamber | float | degrees celcius | 25.0 | - |
| aux_pressure_cell | float | mbar | 123.4 | - |
** calculate if empty; keep if filled (can be overridden by argument)
Capacity convention¶
cumulative_charge_capacity / cumulative_discharge_capacity (and the matching energy
columns) are cumulative per cycle, per direction: within a cycle they accumulate across
that cycle's steps for their direction, and reset to 0 at each cycle boundary. This matches
how legacy cellpy raw data is supplied (verified on real Arbin data in issue #13) and is what
the summary path relies on (the per-cycle capacity is read from the cycle's last datapoint).
Downstream values are derived, not stored in raw: - per-step capacity = the in-step delta (last − first within a step group), produced in the step table; - per-cycle capacity = the cycle-end cumulative value, produced in the cycle/summary table.
Importers must deliver cycle-cumulative capacity. (Accepting step-/test-cumulative raw and normalizing the reset granularity inside the engine is a possible future extension.)
Auxillary columns¶
Option for more auxillary columns; naming scheme: - aux_temperature_[arbitrary name] - aux_potential_[] - aux_pressure_[] - aux_resistance_[]
Test metadata (TestMeta)¶
Per-test metadata, one record per test, keyed by test_id (the same test_id carried
on every raw row). This is where test-level descriptors live so they are not repeated on
every raw row; it is what lets a single object hold many merged test files efficiently (see
.issueflows/04-designs-and-guides/test-metadata-and-merging.md). When several tests are
merged, TestMeta holds one row per test_id.
Test-level descriptors that are constant within a test (e.g. test_family, test_type,
cycle_mode) belong here, not in the raw columns.
This spec remains authoritative for the field set. The fields below now also have a code home
as the TestMeta / CellMeta dataclasses in cellpycore.metadata (scaffolding; see
.issueflows/04-designs-and-guides/metadata-scaffolding.md, issue #30).
| Field | Data type | Unit | Sample data | Comment |
|---|---|---|---|---|
| test_id | int | - | 0 | key; matches raw.test_id (the per-test grouping key) |
| uuid | str(36) | - | "e15b46ca-e584-467f-a176-8bf98b8090e5" | stable, globally-unique id for this test run |
| cell_name | str | - | "cell_001" | human-readable cell / test name |
| test_family | str(36) | - | "rate test" | broad classification of the overall test |
| test_type | str(36) | - | "GITT" | detailed classification within a family |
| cycle_mode | str(10) | - | "anode" | "anode" / "cathode" / "full" |
| source_kind | str(10) | - | "file" | where the data came from: "file", "db", "api", ... |
| source_type | str(20) | - | "Neware" | cycler / instrument type |
| source_uri | str | - | "/data/run1.ndax" or "db://cellpydb/tests/123" | file path or DB/API locator for the source |
| source_uuid | str(36) | - | "..." | original identifier from the source (matches raw.source_uuid) |
| raw_file_names | list[str] | - | ["run1.ndax"] | original raw file(s) backing this test |
| schedule_file_name | str | - | "rate_test.sdu" | cycler schedule / protocol file |
| creator | str | - | "jdoe" | who produced / imported the test |
| channel | str | - | "12" | tester channel id |
| tester_id | str | - | "neware-01" | tester / server identity |
| start_datetime | datetime (ISO 8601) | - | "2026-06-14T10:00:00+02:00" | test start time (from the cycler) |
| time_zone | str | - | "Europe/Oslo" | time zone for naive timestamps |
| loaded_datetime | datetime (ISO 8601) | - | "2026-06-14T12:30:00Z" | when the data was obtained / imported into cellpy |
| comment | str | - | "" | free text |
Cell / material metadata (optional). Physical cell properties (mass, total mass,
nominal capacity + specifics, active-electrode area / loading / thickness, electrode and
electrolyte types, experiment type, ...) are also per-test/per-cell. Legacy cellpy already
defines these in cellpy.parameters.internal_settings.CellpyMetaCommon (cell/material/
geometry) and CellpyMetaIndividualTest (test-dependent: channel_index, creator,
schedule_file_name, voltage_lim_low/high, cycle_mode, test_ID). Mine those for the
full field set; they can sit in TestMeta or a sibling CellMeta record (decision
deferred — see the design note).
Follow-ups¶
- step_types
- 'charge', 'discharge', 'rest'
- limit to those or allow for others?
- how to deal with 'IR' steps in Arbin or other cycler specific step-types?
- Reference vocabulary now defined as
cellpycore.config.StepType(issue #24); it is a non-validating reference set, so other values are still allowed. - step_type_detail
- how is this is intended to be used
- step_mode
- "CV", "CC", "CP", "None"
- similar to step_type: limit to those, or allow for more?
- Reference vocabulary now defined as
cellpycore.config.StepMode(CC/CV/CP); "None" means a null/missing value, not a stored string (issue #24). - Substep Number
- in our previous notes we listed "substep number"
- do we need this? and if so, what would it be used for?
- epoch_time_utc
should this be a float or a datetime object?Resolved (issue #32): stored as int64 nanoseconds since the Unix epoch, UTC. Convert to float seconds UTC or a native datetime with the helpers incellpycore.timestamps.- cycle_num
- usually provided by the tester
- enough with keeping it, or do we need an updated cycle number?
- cycle_type
- details to be discussed
- Reference vocabulary now defined as
cellpycore.config.CycleType(Standard/GITT/ICI/Characterization); may later migrate to test metadata astest_type(issue #24). - test_family / test_type (now in TestMeta, not raw columns)
test_familyis the broad classification (e.g. "reference capacity test", "rate test");test_typeis the detailed one within a family (e.g. "GITT", "current interrupt").- limit to a controlled vocabulary, or allow free text? (same open question as
cycle_type/step_mode) - channel_status — Resolved (issue #10): removed. Not part of the harmonized raw format.
- source_uuid
- does this contain info about the channel?
- if not, should we add a channel number/identifyer? (string)
- test_id / merging
test_idis the compact per-row key that lets a single object hold many merged test files; downstream grouping must use composite keys(test_id, cycle_num, step_num, ...)becausecycle_num/step_numcollide across files.- test-level descriptors (
test_family,test_type, mass, nominal capacity, cycle_mode, ...) should live in a normalized per-test metadata table keyed bytest_idrather than be repeated on every raw row. See.issueflows/04-designs-and-guides/test-metadata-and-merging.md.
Other discussion points¶
- Units
- Units (based on Rasmus' suggestion) are included in the header table above
- EIS, cyclic voltametry, 3-electrode setup
- Data resolution (for definition of step types)