Simulation¶
Client for running battery simulations using UCP protocol and PyBaMM.
For usage examples and guides, see Simulate API on docs.ionworks.com.
Simulation client for running battery simulations.
This module provides the SimulationClient for running battery
simulations using the Universal Cycler Protocol (UCP) format. It supports
single simulations, batch simulations with design of experiments (DOE),
and PyBaMM-based modeling.
- class ionworks.api.simulation.SimulationResult(time_series: DataFrame | DataFrame, steps: DataFrame | DataFrame, metrics: dict[str, Any])[source]¶
Bases:
objectTyped result returned by
SimulationClient.get_result().- time_series: DataFrame | DataFrame¶
Time-series data with one row per time point. Column names follow the platform convention (e.g.
"Time [s]","Voltage [V]"). Returns a polars DataFrame by default; a pandas DataFrame whenset_dataframe_backend("pandas")is active.
- class ionworks.api.simulation.QuickModelConfig(*, capacity: float = 1.0, chemistry: str = 'NMC/Graphite')[source]¶
Bases:
BaseModelQuick model configuration for protocol-based simulations.
A quick model builds a system ECM from a nominal capacity and a chemistry name — it does not take a base
model_id(that is what a full parameterized model is for).- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class ionworks.api.simulation.ProtocolExperimentConfig(*, protocol: str, name: str)[source]¶
Bases:
_StrictModelProtocol experiment configuration.
- model_config = {'extra': 'forbid'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class ionworks.api.simulation.DOERow(*, type: str, name: str, min: float | None = None, max: float | None = None, count: int | None = None, values: list[float] | None = None, mean: float | None = None, std: float | None = None)[source]¶
Bases:
_StrictModelDesign of experiments row configuration.
- model_config = {'extra': 'forbid'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class ionworks.api.simulation.DesignParametersDOE(*, sampling: str, rows: list[DOERow], count: int | None = None)[source]¶
Bases:
_StrictModelDesign of experiments configuration.
- model_config = {'extra': 'forbid'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class ionworks.api.simulation.ParameterTable(*, x: list[float], y: list[float], interpolator: Literal['linear', 'cubic', 'pchip'] = 'linear')[source]¶
Bases:
_StrictModelA function-valued parameter given as a lookup table, interpolated in
x.Replaces a function with an interpolant in the function’s first input, the others ignored: a table of electrolyte conductivity against concentration replaces a function of concentration and temperature.
- interpolator: Literal['linear', 'cubic', 'pchip']¶
- model_config = {'extra': 'forbid'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- ionworks.api.simulation.DesignParameterValue = float | str | ionworks.api.simulation.ParameterTable¶
a number, or a table for a function-valued one.
- Type:
A design-parameter override
- class ionworks.api.simulation.SimulationCondition(*, experiment_parameters: dict[str, float | str]=<factory>, design_parameters: dict[str, float | str | ~ionworks.api.simulation.ParameterTable]=<factory>)[source]¶
Bases:
_StrictModelThe inputs of one simulation in an explicit list of runs.
- design_parameters: dict[str, DesignParameterValue]¶
- model_config = {'extra': 'forbid'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class ionworks.api.simulation.ProtocolSimulationBatchRequest(*, parameterized_model: Any, protocol_experiment: ProtocolExperimentConfig | None = None, protocol_id: str | None = None, design_parameters_doe: DesignParametersDOE | None = None, conditions: list[SimulationCondition] | None = None, experiment_parameters: dict[str, float | str] | None = None, max_backward_jumps: int | None = None, study_id: str | None = None, project_id: str | None = None, extra_variables: list[str] | None = None, simulation_settings: dict[str, Any] | None = None)[source]¶
Bases:
_StrictModelRequest model for batch protocol-based simulation.
- parameterized_model: Any¶
- protocol_experiment: ProtocolExperimentConfig | None¶
- design_parameters_doe: DesignParametersDOE | None¶
- conditions: list[SimulationCondition] | None¶
- model_config = {'extra': 'forbid'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class ionworks.api.simulation.SimulationResponse(*, simulation_id: str, job_id: str, protocol_id: str | None = None, already_existed: bool = False)[source]¶
Bases:
BaseModelResponse model for simulation creation.
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- ionworks.api.simulation.expected_doe_simulation_count(doe: DesignParametersDOE) int[source]¶
Return the number of simulations a DOE will produce.
For
gridsampling, this is the product of the per-row sample counts (len(values)for discrete rows,countfor range rows). Forrandomandlatin_hypercubesampling, it is the top-levelcount(defaulting to 1 when unset). Used bySimulationClient.protocol()to refuse multi-simulation DOEs before billing them.
- class ionworks.api.simulation.SimulationClient(client: Any)[source]¶
Bases:
objectClient for running simulations.
- __init__(client: Any) None[source]¶
Initialize the SimulationClient.
- Parameters:
client (Any) – The HTTP client instance for making API requests.
- protocol(config: dict[str, Any]) SimulationResponse[source]¶
Create a single protocol-based simulation.
Delegates to
protocol_batch()and returns the single result. Pass the same config fields asprotocol_batch(), plus the single-simulation convenience fielddesign_parameters— a flat mapping of numbers, sent as a one-row discrete DOE, or with anyParameterTablevalue, sent as a single condition.- Parameters:
Configuration dictionary containing:
- parameterized_model: one of
a quick-model dict
{"capacity": <Ah>, "chemistry": <name>}(builds a system ECM; no basemodel_id),a
QuickModelConfig,a full model dict
{"model_id": ..., "parameters": {...}}, ora parameterized-model ID string.
protocol_experiment: ProtocolExperimentConfig dict with protocol and name fields
protocol_id: id of a saved protocol, in place of
protocol_experiment(provide exactly one)experiment_parameters: Optional dict with initial_soc and initial_temperature
design_parameters: Optional
dict[str, float | ParameterTable]— design parameter overrides for this single simulation. A function-valued parameter takes a table{"x": [...], "y": [...]}(or aParameterTable).max_backward_jumps: Optional int
study_id: Optional str
extra_variables: Optional list[str] — extra variables to include in simulation output
simulation_settings: Optional dict or
SimulationSettings— per-run numerics (solver/var_pts/submesh_types/spatial_methods/discretisation_kwargs) overriding the parameterized model’s own settings key by key, for this call’s runs only. Send only the keys you mean to change: restating the model’s own settings still counts as different numerics, so the run is not deduplicated against one that sent none.
- Returns:
Response containing simulation_id and job_id.
- Return type:
- Raises:
ValueError – If the configuration is invalid, both
design_parametersanddesign_parameters_doeare supplied, or a multi-rowdesign_parameters_doeis supplied (useprotocol_batch()for multi-simulation DOE).
- protocol_batch(config: dict[str, Any]) list[SimulationResponse][source]¶
Create multiple protocol-based simulations using DOE.
Uses a two-step flow: first parses the protocol and creates an experiment template (
POST /protocols/parse-to-template), then runs the batch (POST /simulations/with-template/batch). A savedprotocol_idis already a template, so it skips the parse step.- Parameters:
Configuration dictionary containing:
- parameterized_model: one of
a quick-model dict
{"capacity": <Ah>, "chemistry": <name>}(builds a system ECM; no basemodel_id),a
QuickModelConfig,a full model dict
{"model_id": ..., "parameters": {...}}, ora parameterized-model ID string.
protocol_experiment: ProtocolExperimentConfig dict with protocol and name fields
protocol_id: id of a saved protocol, in place of
protocol_experiment(provide exactly one)design_parameters_doe: DesignParametersDOE dict
conditions: Optional list of
{"experiment_parameters": {...}, "design_parameters": {...}}dicts — one simulation per entry, in place ofdesign_parameters_doe. Top-levelexperiment_parametersfill in whatever a condition omits.experiment_parameters: Optional dict
max_backward_jumps: Optional int
study_id: Optional str
extra_variables: Optional list[str] — extra variables to include in simulation output
simulation_settings: Optional dict or
SimulationSettings— per-run numerics (solver/var_pts/submesh_types/spatial_methods/discretisation_kwargs) overriding the parameterized model’s own settings key by key, for this call’s runs only. Send only the keys you mean to change: restating the model’s own settings still counts as different numerics, so the run is not deduplicated against one that sent none.
- Returns:
List of responses, each containing simulation_id and job_id.
- Return type:
- Raises:
ValueError – If the configuration is invalid.
- list(parameterized_model_id: str | None = None, study_id: str | None = None, *, protocol_id: str | None = None, state: Literal['queued', 'running', 'completed', 'failed'] | None = None, summary: bool = False, limit: int | None = None, offset: int | None = None) list[dict[str, Any]] | PaginatedList[dict[str, Any]][source]¶
List simulations of a parameterized model, a study, or a protocol.
Scope by at most one of
parameterized_model_idorstudy_id, optionally narrowed to one saved protocol withprotocol_id; or byprotocol_idalone. Newest first.- Parameters:
parameterized_model_id (str, optional) – Filter simulations belonging to this parameterized model.
study_id (str, optional) – Filter simulations assigned to this study.
protocol_id (str, optional) – Filter simulations that ran this saved protocol. Combines with either of the other two, e.g. every run of one model on one protocol.
state ({"queued", "running", "completed", "failed"}, optional) – Only runs in this state.
failedincludes canceled runs.summary (bool, optional) – Leave out each run’s
metrics, which are most of a listing’s size. Defaults to False.limit (int, optional) – Maximum simulations to return (1-1000). The server defaults to 100.
offset (int, optional) – Number of simulations to skip.
- Returns:
Simulation summaries. A
PaginatedListcarrying.totalwhenlimitoroffsetis given, otherwise a plain list.- Return type:
- Raises:
ValueError – If both
parameterized_model_idandstudy_idare given, or none of the three scopes is.
- get(simulation_id: str, summary: bool = False) dict[str, Any][source]¶
Get a specific simulation by ID.
- Parameters:
- Returns:
Simulation object with full joined data including model, experiment, and simulation_data (null if not completed).
- Return type:
- delete(simulation_id: str) None[source]¶
Delete a simulation and its stored result data.
- Parameters:
simulation_id (str) – The UUID of the simulation to delete.
- get_statuses(simulation_ids: list[str]) list[dict[str, Any]][source]¶
Get the job status of many simulations in one lightweight request.
Much cheaper than calling
get()per simulation when polling a batch: only the status of each simulation’s job is returned, not the joined simulation record.- Parameters:
simulation_ids (list[str]) – Ids of the simulations to check.
- Returns:
One
{"simulation_id", "status", "error", "error_code", "updated_at"}item per simulation that exists, is visible to the caller, and has a job. Ids that match none are omitted, so compare against the ids you asked for.statusis the job status (pending,waiting,processing,completed,failedorcanceled).- Return type:
- get_result(simulation_id: str) SimulationResult[source]¶
Get simulation data/result for a completed simulation.
Downloads
time_series.parquetandsteps.parquetvia signed-URL redirects, and loadsmetricsfrom the simulation record. Callers see the sameSimulationResultshape as before.- Parameters:
simulation_id (str) – The UUID of the simulation.
- Returns:
Typed result with
time_seriesandstepsas DataFrames andmetricsas a plain dict. DataFrame type (polars or pandas) follows the active backend set viaset_dataframe_backend().- Return type:
- Raises:
IonworksError – If the API request fails. A 404 typically means the result is not yet available (simulation still running, queued, or legacy data not yet upgraded to parquet). Other status codes indicate authentication failures, server errors, or a missing simulation ID.
- get_steps(simulation_id: str) DataFrame | DataFrame[source]¶
Get a completed simulation’s step-level summary, one row per step.
The
stepsframe ofget_result()without downloading the time series, which is far larger.- Parameters:
simulation_id (str) – The UUID of the simulation.
- Returns:
Step summary with
Delta capacity [A.h]andDelta energy [W.h]added, in the active dataframe backend.- Return type:
DataFrame
- Raises:
IonworksError – If the request fails; a 404 means the result is not ready yet.
- wait_for_completion(simulation_id: str | list[str], timeout: int = 60, poll_interval: int = 2, verbose: bool = True, raise_on_failure: bool = True) dict[str, Any] | list[dict[str, Any]][source]¶
Wait for simulation(s) to complete by polling until done or timeout.
- Parameters:
simulation_id (str | list[str]) – Single simulation ID or list of simulation IDs to wait for. Can also be a
SimulationResponseor list of them (thejob_idwill be extracted automatically for failure detection).timeout (int) – Maximum time to wait in seconds (default: 60).
poll_interval (int) – Time between polls in seconds (default: 2).
verbose (bool) – Whether to print status updates (default: True).
raise_on_failure (bool) – Whether to raise
IonworksErrorwhen a simulation’s job fails or is canceled (default: True).
- Returns:
Completed simulation(s). Returns single dict if single ID provided, list of dicts if list of IDs provided. Only returns completed simulations if timeout is reached.
- Return type:
- Raises:
TimeoutError – If timeout is reached before all simulations complete.
IonworksError – If a simulation fails and raise_on_failure is True.