Simulations#

The simulation API runs forward battery simulations on the Ionworks cloud. You define a parameterized model and a cycling protocol in Universal Cycler Protocol (UCP) YAML format, and the server returns time-series results.

Running a simulation#

Define a protocol#

Protocols are written in YAML. Each protocol has a global section for initial conditions and a steps list describing the cycling sequence:

protocol_yaml = """global:
  initial_state_type: soc_percentage
  initial_state_value: 50
  initial_temperature: 25.0
steps:
  - Charge:
      mode: C-rate
      value: "0.6"
      ends:
        - Voltage > 4.2
  - Rest:
      duration: 3600
  - Discharge:
      mode: C-rate
      value: "0.5"
      ends:
        - Voltage < 2.5
"""

Submit the simulation#

Use client.simulation.protocol() with a quick model or a full parameterized model:

result = client.simulation.protocol({
    "parameterized_model": {
        "quick_model": {"capacity": 1.0, "chemistry": "NMC/Graphite"},
    },
    "protocol_experiment": {
        "protocol": protocol_yaml,
        "name": "NMC Charge Discharge Protocol",
    },
})
print(f"Simulation ID: {result.simulation_id}")
print(f"Job ID: {result.job_id}")

Wait for completion#

wait_for_completion() polls the server and returns the finished simulation object:

simulation = client.simulation.wait_for_completion(
    result.simulation_id,
    timeout=60,
    poll_interval=2,
    verbose=True,
)

Retrieve results#

data = client.simulation.get_result(result.simulation_id)
time_series = data.get("time_series", {})

Batch simulations#

Run a design-of-experiments (DOE) sweep by replacing design_parameters with design_parameters_doe. Each combination of parameter values produces a separate simulation:

batch_config = {
    "parameterized_model": {
        "quick_model": {"capacity": 1.0, "chemistry": "NMC/Graphite"},
    },
    "protocol_experiment": {
        "protocol": protocol_yaml,
        "name": "DOE Simulation",
    },
    "design_parameters_doe": {
        "sampling": "grid",  # or "random", "latin_hypercube"
        "rows": [
            {"type": "range", "name": "capacity", "min": 0.8, "max": 1.2, "count": 3},
        ],
    },
}

results = client.simulation.protocol_batch(batch_config)
for r in results:
    print(f"Simulation: {r.simulation_id}, Job: {r.job_id}")

Wait for the entire batch:

simulation_ids = [r.simulation_id for r in results]
completed = client.simulation.wait_for_completion(simulation_ids, timeout=120)

Extra variables#

By default simulations return voltage and current. To request additional output variables, add extra_variables to the config:

config = {
    "parameterized_model": {
        "quick_model": {"capacity": 1.0, "chemistry": "NMC/Graphite"},
    },
    "protocol_experiment": {
        "protocol": protocol_yaml,
        "name": "Electrode Potentials",
    },
    "extra_variables": [
        "Negative electrode potential [V]",
        "Positive electrode potential [V]",
    ],
}

result = client.simulation.protocol(config)

Listing simulations#

simulations = client.simulation.list()

Next steps#

See Error Handling for how the client reports problems.