Skip to main content
Have you ever started a multi-day battery test, only to realize later that the protocol you used was incorrect? The Battery Cycler Simulator helps you prevent this by allowing you to upload a protocol file and run a quick physics-based simulation to verify its behavior before you start a real-world experiment.

How It Works

The process is simple: upload your protocol, configure your cell, and run the simulation.
  1. Upload: Upload your protocol file in the raw format (e.g. XML or JSON). The system automatically detects the format and parses it.
  2. Configure: Set the parameters for the cell you want to simulate.
  3. Simulate: Run the simulation and analyze the results.

Step 1: Upload Your Protocol File

You can start by uploading a protocol file from your computer. We currently support files from:
  • Arbin
  • BioLogic (.mps, .bttest)
  • Maccor (.xml, .csv)
  • Neware
  • Novonix (.pro2)
  • PyBaMM experiment strings (.txt)
Once you upload a file, the system immediately parses it and displays the translated steps.
Gamry .dta files are not protocol files and cannot be uploaded here. To import EIS measurement data from Gamry instruments, use the ionworksdata library — see Data format for details.

Required Additional Files

Some protocols reference external files for complex steps, such as custom drive cycle waveforms or reusable subroutines. If your protocol requires such files, the simulator will detect this and prompt you to upload them.

Step 2: Review and Configure

After parsing, you can review the protocol and set up the simulation.

Protocol Steps

The simulator displays the parsed protocol in three different formats, accessible via tabs:
  • Human-Readable: A simplified, easy-to-read summary of the steps in your protocol.
  • UCP (YAML): The full protocol translated into our Universal Cycler Protocol format. This shows the detailed underlying structure that will be executed.
  • Raw: The raw text content of the file you uploaded.

Cell Configuration

To run a meaningful simulation, you need to provide some basic information about the battery cell you want to test. This configures the underlying Equivalent Circuit Model (ECM) with one RC pair, using OCV and resistance parameters matched to your cell.
  • Chemistry: Select the cell chemistry from a list of pre-configured options. Full-cell chemistries (e.g., NMC/Graphite, LFP/Graphite) and Li-metal half cells (e.g., NMC/Li metal, Graphite/Li metal, LFP/Li metal) are available. This determines the OCV curve, anode/cathode open-circuit potentials, and resistance values used by the model.
  • Cell capacity (Ah): The nominal capacity of your cell. The ECM parameters are scaled to match this capacity.
  • Resistance scale (%): Adjusts the model’s internal resistance relative to the default value for the selected chemistry. The default is 100% (no change). For example, set this to 200 to double the resistance or 50 to halve it. Useful for approximating cells with higher or lower impedance than the chemistry default.
  • Initial SOC (%): The State of Charge of the cell at the beginning of the simulation.
  • Temperature (°C): The ambient temperature for the simulation.
Check carefully that the cell configuration matches the cell for which the protocol was designed. If the cell configuration is incorrect, the simulation may not be able to run.

Advanced configuration

Under the Advanced section, you can configure optional rules that modify simulation behavior at runtime.

Termination conditions

Termination conditions let you stop a simulation early when a variable reaches a target value. This is useful for long cycling protocols where you only need to simulate a limited number of cycles or a specific amount of time. Each condition specifies a variable, a comparison operator (==, !=, >, <, >=, <=), and a value. The simulation stops as soon as any condition is met. The variable dropdown is organized into two groups:
  • Built-in — variables automatically provided by the simulation engine regardless of the protocol
  • Protocol variables — variables defined in and extracted from the parsed protocol
Total time
Total time is a built-in variable that tracks the cumulative elapsed simulation time in seconds. Use it to cap how long a simulation runs, independent of the protocol’s own step logic. When you select Total time, a unit picker appears next to the value field so you can enter the threshold in seconds, minutes, hours, or days. The value is automatically converted to seconds for the simulation engine. For example, to stop a long cycling protocol after 2 hours, add a termination condition where Total time >= 2 hours.
If the protocol defines its own variable named total_time, the protocol’s variable takes precedence and the built-in value is not injected.
Protocol variables
Protocol variables are extracted from your uploaded protocol file. For example, to stop an Arbin protocol after 3 cycles, you can add a condition where PV_CHAN_Cycle_Index >= 4. Arbin increments PV_CHAN_Cycle_Index at the start of each cycle, so the index reaches 4 only when the 4th cycle begins — meaning 3 full cycles have already completed. When a termination condition triggers, the reason is displayed in the simulation metrics using the human-readable form (e.g., “Early termination reason: Total time >= 2 hours”).

Variable callback rules

Variable callback rules let you dynamically update protocol variables during the simulation based on conditions. Each rule specifies a condition (variable, operator, value) and a set of variable updates to apply when the condition is met. For example, you could create a rule that increases the C-rate after a certain number of cycles: when PV_CHAN_Cycle_Index >= 3, update Current(A) to a higher value. This lets you simulate multi-phase protocols where charging or discharging parameters change at specific points during cycling.

Step 3: Run Simulation & Analyze Results

Once everything is configured, click Run Simulation. The simulator will execute the protocol against the configured cell model. You can cancel a running simulation at any time by clicking the Cancel button on the simulation page. This is useful for long-running protocols where you can already see the results you need.

Simulation results

Plots

The primary output is an interactive plot of your simulation data over time. By default, Voltage and Current are shown, but you can configure exactly which variables appear. Click Configure Plot to open the plot settings drawer. Under Time Series, toggle any of the available variables on or off:
VariableUnitDescription
VoltageVCell terminal voltage
CurrentAApplied current
Temperature°CCell temperature (when available)
Charge capacityAhCumulative charge capacity
Discharge capacityAhCumulative discharge capacity
State of charge%Cell state of charge (when available)
Step countOverall step index
Cycle countCurrent cycle number
Step count (within cycle)Step number within the current cycle
Each enabled variable is displayed in its own subplot, stacked vertically and sharing a common time axis. Charge capacity and discharge capacity are grouped into a single subplot for easy comparison.
If your protocol defines numeric variables (e.g., C-rate, temperature setpoints), you can also plot these as time series. In the Configure Plot drawer, use the Additional Variables dropdown to select any numeric protocol variable. The selected variables appear as additional subplots alongside the built-in time series.

Key metrics

Below the plot, you will find key performance indicators calculated from the simulation, including:
  • Total Time
  • Charge Throughput (Ah)
  • Energy Throughput (Wh)
  • Early termination reason (if a termination condition was triggered)

Fullscreen mode

Click the fullscreen icon in the top-right corner of the plot to expand it to fill the screen. The fullscreen view includes the same Configure Plot button, so you can adjust which variables are shown without leaving fullscreen.

Download CSV

Click Download CSV next to Configure Plot to export the full simulation time series as a CSV file (simulation_data.csv). The download always contains the complete simulation, even when you’ve zoomed into a window on the plot. The file includes every time-series variable produced by the simulation (Voltage, Current, Temperature, capacities, Step count, Cycle count, and any additional protocol variables), plus step-level columns expanded to match each time point. Use it to bring results into Excel, pandas, MATLAB, or your own analysis tooling without re-running the simulation. By simulating your protocols, you can catch errors, validate your experimental design, and gain confidence before committing time and resources to a real test.

Exporting UCP to a vendor protocol file

The conversion also runs in reverse: you can take a protocol authored in UCP and export it as a native file for Maccor, Neware, Arbin, BioLogic, or Novonix. This is useful when you’ve designed and validated a protocol in Ionworks and want to run it on physical hardware. Use the client.protocol.convert method in the Python API to perform the conversion programmatically. Because vendor formats vary in expressiveness, not every UCP construct round-trips perfectly. See Differences between commercial protocols below for the major gotchas, and Export-time validation for the specific UCP features each target format rejects upfront. We recommend re-importing the converted file using the upload flow above to confirm it parses back into the steps you expect before running it on a cycler.

Export-time validation

When you convert a UCP protocol to Arbin or Maccor, the converter walks the protocol first and rejects features that can’t be faithfully represented in the target format. You get a ValueError naming the offending step rather than a file that silently misbehaves on the cycler. Arbin rejects:
  • EIS steps — Arbin schedules have no impedance-sweep step type.
  • Subroutine steps — there’s no equivalent step type.
  • Compound And / Or end conditions — Arbin step limits accept a single equation per limit.
  • Maccor user variables (VAR1, VAR2, …) referenced in step values or VariableEnd expressions — Arbin has no runtime variable namespace for them.
Maccor rejects:
  • Zero-duration Rest steps — Maccor requires every step to have at least one nonzero termination. (This is most commonly hit when round-tripping an Arbin “Internal Resistance” step through UCP.)
If you hit one of these, restructure the protocol to avoid the feature, or export to a different target format that supports it.

Cross-cycler variable translation

When a UCP protocol was originally parsed from one vendor and is exported to another, the converter rewrites variable names and cycle-index comparisons so the semantics survive translation:
  • PV_CHAN_Cycle_Index, PV_CHAN_Voltage, PV_CHAN_Current, PV_CHAN_Charge_Capacity / PV_CHAN_Discharge_Capacity, PV_CHAN_Step_Time (Arbin) ↔ CYCLE, VOLTAGE, CURRENT, CAPACITY, STIME (Maccor). Both Arbin capacity variants map to Maccor’s single CAPACITY token; the reverse path (Maccor → Arbin) emits PV_CHAN_Capacity.
  • Arbin’s PV_CHAN_Cycle_Index is 1-indexed while Maccor’s CYCLE is 0-indexed. Numeric cycle-index comparisons are shifted by 1 in either direction so the same set of cycles is matched. For example, an Arbin end PV_CHAN_Cycle_Index <= 2 becomes a Maccor User Def end CYCLE <= 1.
  • UCP-canonical short names (Capacity, Voltage, Current, Duration, Energy) used in set_variable expressions are expanded back to Arbin’s PV_CHAN_* tokens on Arbin export so the round-trip parser resolves them correctly.
Compound And / Or ends and VariableEnd expressions exported to Maccor are emitted as a single User Def end with & / | operators.

Differences between Commercial Protocols

Much of the challenge in converting between the different protocols is not the syntax, but the fundamental differences in how the cyclers define the logic to follow the same sequence of steps. In this section, we’ll cover some implementation differences between the Ionworks Universal Cycler Protocol, and the protocols from the cyclers we support.

CCCV steps

Most cyclers define CCCV steps as one step with voltage as a “limit” field:
  • Maccor: Uses a constant current step, with voltage as a “limit”. Maccor also supports a native single-step Chg Func CCCV / Dis Func CCCV step type that combines the CC and CV phases (with the CV cutoff specified as a current limit).
  • Neware: Specifies both current and voltage as a “limit”
  • Novonix: Specifies both current and voltage as fields, and uses step type to differentiate between CC (voltage is a cutoff) and CCCV (transition to voltage and hold until a current cutoff)
In UCP, for maximum modularity, we use a step block with two steps, CC with a voltage cut-off and CV with a current cut-off, with total duration and any other end conditions defined at the step block level. When parsing a Maccor Chg Func CCCV / Dis Func CCCV step, the parser expands it into the equivalent two-step UCP block automatically.

Header metadata

Some formats (e.g., Novonix .pro2) include top-level header data (Version, LastUpdated, Charger). This metadata is preserved in UCP under header and is used for round-trips when converting back to the original format.

Functional expressions

Maccor supports functional expressions (e.g., VAR1*0.5) for step values and end conditions. These are carried through UCP as strings and round-tripped using vendor-specific constructs (e.g., Maccor “User Def” end entries), so the meaning is preserved even if another format lacks an exact equivalent.

Nested loops

For formats that use Do/Loop constructs (Maccor), nested loops are numbered (Do 1/Loop 1, Do 2/Loop 2, …) to reflect structure. UCP expresses loops with repeat on a block; when converting back to Maccor, Do/Loop numbering is generated from the block nesting depth.

Report/Record/Save data/Resolution

This is the field that defines how often the cycler will save data to the output time series.
  • Maccor uses the “Report” field and allows time, current, voltage, and temperature
  • Neware uses the “Record” field and allows time, current, and voltage
  • Novonix uses StepConditions entries with ConditionType: "Save data" and allows Δt, ΔV, and ΔI.
  • UCP uses the “Resolution” field, which can be set globally and overridden for each step, and currently only supports time

Loops

For loops, there are two fundamental approaches:
  1. Nested Steps: Define the loop as a step block with a nested step, with a repeat parameter specifying the number of times to repeat the loop. This is the more modern approach, similar to how loops are defined in modern programming languages such as Python.
The following protocols use this approach:
  • UCP (using step blocks)
  • Novonix (using ChildProtocolStepList with TimesToLoop).

Increment cycle number

  • Novonix uses StepType = 6 to increment the cycle counter. In UCP this maps to an auxiliary Increment cycle number step.
  • Arbin uses the built-in PV_CHAN_Cycle_Index variable to track the current cycle. When this variable is incremented via a Set Variable(s) step, it maps to both a set_variable action and an Increment cycle number auxiliary step in UCP.
  1. Goto/State machine: Define special steps like “start loop” and “end loop”. This is the more legacy approach, similar to older programming languages such as Fortran.
The following protocols use this approach:
  • Maccor (using a Do step to start the loop, and a Loop step to end the loop)
  • Neware (using a special step type to loop back to a specified previous step a certain number of times)
  • Arbin (using limit conditions with goto targets to jump between steps, and Set Variable(s) steps to manage counters)

Cycle-index branching (Arbin)

Arbin protocols commonly use PV_CHAN_Cycle_Index to implement cycle-dependent branching, where different parameters are applied on different cycles. When parsed into UCP, this pattern is represented using control steps with set_variable actions and goto targets.

Drive cycles (BioLogic)

BioLogic .mps protocols use User Profile steps to apply drive cycles (for example, a driving current profile). The waveform is embedded directly in the .mps file as an Urban Profile Table appended after the technique block, so no additional upload is required. When parsed into UCP, a User Profile step maps to a Drive step: the table’s time column defines the step duration, and the value column drives the cell in the corresponding control mode (e.g., Current).

PyBaMM experiment strings

You can upload a plain text file containing PyBaMM experiment strings directly into the Battery Cycler Simulator. The system auto-detects the format and converts the steps into UCP, with list repetition blocks mapping to step blocks with a repeat count.

Basic syntax

For plain steps, each line in the file is a single PyBaMM step string. The supported step types are:
  • Charge at <value> <unit> — constant current, C-rate, or power charge
  • Discharge at <value> <unit> — constant current, C-rate, or power discharge
  • Hold at <value> V — constant voltage hold
  • Rest for <duration> — open-circuit rest period
Steps can include termination conditions with until and duration constraints with for:
Charge at 1C until 4.2V
Discharge at 0.5C for 1 hour
Hold at 4.2V until C/50
Rest for 10 minutes

List repetition

To repeat a sequence of steps multiple times, wrap them in square brackets and multiply with * N. This is equivalent to Python’s list repetition syntax.
["Charge at 1C until 4.2V", "Discharge at 1C until 2.5V"] * 100
This produces a single repeated block in UCP with repeat: 100, rather than duplicating the steps 100 times. You can mix repeated blocks with plain steps:
Charge at 1C until 4.2V
Hold at 4.2V until C/50
["Discharge at 0.5C until 3.0V", "Rest for 10 minutes"] * 50
Rest for 1 hour

Nested repetition

Repeated blocks can be nested for more complex protocols:
[["Charge at 1C until 4.2V", "Rest for 5 minutes"] * 2, "Discharge at 1C until 2.5V"] * 30
This creates an outer block that repeats 30 times, where each iteration runs the charge-rest pair twice followed by a single discharge.

Cycle groups

Use parentheses inside a list to mark a group of steps as a cycle. This automatically inserts an “Increment cycle number” step at the end of each repetition, so cycle-level metrics are tracked correctly:
[("Charge at 1C until 4.2V", "Discharge at 1C until 2.5V")] * 100
Tuple cycle groups must always be inside a list. Use [(...)] * N — not (...) * N.

Multiline format

For readability, you can split a repeated block across multiple lines:
[
    "Charge at 1C until 4.2V",
    "Hold at 4.2V until C/50",
    "Discharge at 1C until 2.5V",
] * 50