Skip to main content
June 22, 2026
GITTModel for diffusion-only fits, ionworks-model download for pybamm-only users, full app-URL helpers, and the start of a strict schema-config contract

GITTModel — a first-class diffusion-only model

GITTModel and HalfCellGITTModel are now first-class models in ionworkspipeline (with matching ionworks_schema siblings). GITT fits overwhelmingly use the same lightweight model rather than SPMe: x-averaged particle diffusion per electrode, a single lumped ohmic resistance, fixed OCPs, and no Butler-Volmer kinetics, electrolyte dynamics, or thermal effects. Until now that model lived as near-identical local copies in per-engagement repos, which meant the API route could not run it. Shipping it as a built-in model unblocks server-side GITT fits and backs the new half-cell GITT fit template.

Download ionworks-defined models for plain pybamm

Users who have only ionworks-schema / ionworks-api — and not the licensed ionworkspipeline package — can now obtain ionworks-defined models (ECM, LumpedSPMR, the MSMR models, GITTModel) in a form they can load and run with plain pybamm. A new POST /discovery/ionworks_models/serialize endpoint builds the model server-side (where the license lives) and returns a loadable serialisation, exposed through the Python SDK.

App-URL helpers for every routed resource

The SDK’s client.urls grew from a single .measurement() method to a full set of web-app link builders — project, model, parameterized_model, optimization, pipeline, study, simulation, cell_specification, cell_instance, and more — so callers never hand-construct app.ionworks.com URLs from IDs. The helpers are environment-aware, deriving the right host for whichever API environment the client is configured against.

Stricter, clearer pipeline configuration

Most user-driven errors in ionworkspipeline now raise UserConfigurationError (a ValueError subclass) instead of bare ValueError / KeyError, so configuration mistakes — wrong field types, out-of-range values, missing keys — are routed to a clear Configuration error for the user instead of paging on-call as an internal error. Alongside this, an in-progress schema-contract hardening effort began validating optimizer configs at the boundary: passing SciPy-only keys to a native optimizer (e.g. DifferentialEvolution(popsize=5, maxiter=10)) now hard-errors at submission time instead of being silently swallowed.
Improvements
  • A single calculation structuredict[str, list[str] | None] mapping each objective to the variables to compute — is now the source of truth for objective- and variable-level cost scoping, replacing the per-cost objective_names list. This lets a weighted Wasserstein (position-shifting dQ/dV) and a per-variable SSE share one MultiCost over an objective, with each cost explicitly scoped to the variables it should consume. ElectrodeBalancing gained a dQdU model axis option that emits dQ/dV on the model’s own full-window voltage axis so a weighted Wasserstein can align peaks in voltage rather than on the data grid.
  • Strict optimizer-option validation: unknown algorithm_options keys (and method/options mismatches) are rejected at submission time, and AskTellOptimizer rejects unknown constructor kwargs at runtime with an actionable message — the previous behaviour silently ignored them. The typed option wrappers (CMAESOptions, PSOOptions, DEOptions, XNESOptions, BayesianOptimizationOptions, SOBEROptions, TuRBOOptions) remain the recommended way to pass options.
  • Failure sentinels across the cost and design spaces are unified into a single FAILURE_PENALTY constant, so the fitting and design-optimization paths can no longer drift apart and the penalty value is better-conditioned for surrogate-GP fits.
Fixes
  • ECM coulomb-count capacity reference is now computed per segment. Multi-measurement fits previously accumulated the cumulative integral across concatenated segment boundaries, inflating the capacity estimate to roughly the sum of per-segment throughputs and triggering spurious capacity-sanity-check warnings.
  • A geometry passed through simulation_kwargs with symbolic bounds (e.g. a pybamm.Scalar min and a pybamm.Parameter max) is now deserialised back into pybamm objects before the simulation is built, instead of arriving as raw JSON and choking the solver.
  • parse_model now honours serialised geometry, var_pts, spatial_methods, and submesh_types for custom models, attaching them as per-instance defaults instead of letting pybamm rebuild geometry from model.options.
Improvements
  • client.model.download / serialize (introduced above) return ionworks-defined models in a plain-pybamm-loadable form.
  • client.urls (introduced above) now builds links for every routed resource, environment-aware.