Skip to main content
June 15, 2026
Global search with ⌘K, surrogate-based Bayesian / TuRBO / SOBER optimizers, ECM parameterization on the worker fleet, inline image previews for file measurements

Global search across the platform

A ⌘K dialog now searches across projects, cell specifications, cell instances, cell measurements, pipelines, datafits, simulations, optimizations, and parameterized models — with grouped, deep-linked results and per-entity-type chip filters. Cell measurements and instances are indexed via full-text search (Postgres tsvector with a backfilled search_vector column), so a partial name or id from any of these surfaces lands on the right detail page in one keystroke. The backend exposes limit, offset, entity_types, and project_id query parameters so the dialog can chip-filter and page server-side.

Bayesian Optimization, TuRBO, and SOBER for parameter estimation

Three surrogate-based optimizers ship in ionworkspipeline on a shared SurrogateBasedAlgorithm base (LHS warm-up, GP fitting with outlier clamping, constraint-GP handling, and EI-based surrogate convergence):
  • BayesianOptimization — sequential or batch BO with (log-)Expected Improvement and a feasibility-weighted acquisition for constrained problems.
  • TuRBO — trust-region BO with Thompson sampling, SCBO-style constraint handling, and anisotropic lengthscale weighting.
  • SOBER — batch BO via Bayesian quadrature with Tchernychova–Lyons kernel recombination, prior-aware importance sampling, and adaptive WKDE prior updates.
Each algorithm has a matching BayesianOptimizationOptions, TuRBOOptions, SOBEROptions schema, and a new method-selection guide in the docs explains when to reach for them over Nelder–Mead, CMA-ES, or differential evolution.

ECM parameterization runs as a background job

Authenticated ECM fits now run as ecm_fit jobs on the Anyscale/Ray worker fleet instead of blocking a FastAPI worker for 10–60 s. POST /ecm/fit-from-file and POST /ecm/fit-from-measurements return 202 {job_id, status}; the public demo POST /ecm/fit stays synchronous so unauthenticated example fits still complete in one round-trip. The new client.ecm Python SDK sub-client covers all three input modes — fit_from_example (sync), fit_from_file and fit_from_measurements (returning an EcmFitJob with wait_for_completion(...) polling), plus save_to_project and detect_and_read — and a new ecm-fitting agent skill walks an agent through picking a mode, running the fit, and persisting it as a parameterized model. Solver ValueErrors from user data now surface as BadRequestError/CONFIGURATION_ERROR instead of paging Sentry.

Inline image previews for file measurements

File-type cell measurements used to render a “Visualization not yet supported” placeholder; the measurement detail page now fetches every attached file, renders images inline (filling the column with a click-to-expand lightbox, Esc or click-out to dismiss), and offers a download link for any file type. The detail-page loader also stopped unconditionally requesting steps_and_cycles / time_series for non-time-series measurements — a 400 from those endpoints used to reject the whole thunk and show “Measurement not found” for file and properties rows.
Improvements
  • Optimizations are decoupled from the internal jobs system: a new /optimizations REST surface returns job-free OptimizationResource DTOs (list, get, config, metadata, runs, statuses, cancel, resubmit, delete) and the optimization list page polls a lightweight /statuses?ids=... projection every 5 s instead of opening one WebSocket per row. The parameterized-model selector also fetches by id rather than scanning the jobs cache.
  • A project-scoped parameterized model listing lets the parameterized-model picker filter to models that belong to the current project.
  • id_text and search_vector columns are hidden in the object-fields panels so detail views no longer surface internal indexing helpers.
  • Datafit results now surface “Validation not supported” as an inline alert when the result payload sets validation_not_supported, instead of leaving the validation tab blank or failing the page.
  • Protocol files that pause or end on a non-error stop condition now render a user-correctable inline message; previously the route returned a generic 500.
  • A new pipelines/validations/{job_id}/plot_data / pipelines/datafits/{job_id}/plot_data consistency pass plus generic backend mapping converts any plain Python error raised inside the web layer into a structured domain error response.
  • The Universal Cycler Protocol simulator pages now list Arbin and Novonix alongside Maccor, Bitrode, and BioLogic as supported cycler protocol formats, and the supported-protocols copy is shared between both UCP pages so the two stay in sync.
  • The StandardDataGrid card now claims an intrinsic width, fixing the collapsed-grid layout when the parent container had no explicit width.
  • Measurement-file uploads to storage now retry on transient errors with capped exponential backoff and a structured 502 once attempts are exhausted.
  • Pipeline list and detail fetches enforce last-wins via requestId guards and pass the thunk’s AbortSignal through to axios, so navigating between pipelines during a slow request no longer reverts the view to a stale pipeline. Pipeline-elements caching now merges scoped entries on refetch instead of wiping the whole entity dict, and the pipeline detail subscription’s polling callback guards against the stale-closure race that fired refetches for the previous pipeline.
  • The measurements list resets to page 1 at render time when the parent entity changes, removing the flash of stale rows that appeared while a new request was in flight.
  • Org-context resolution on hard refresh is now deterministic — the bootstrap ordering avoids the race where the active project briefly reverted to the first project in the list before the persisted selection rehydrated.
Fixes
  • Dark mode now derives selection, focus, and hover colors from the Ionworks brand pink so the focused row, selected nav item, and hovered button no longer look muddy against the dark background.
  • Validation plot axis titles render correctly under Plotly 3.x — the upgrade started requiring the { text: ... } wrapper that earlier versions inferred from a bare string.
  • The cell card on the cell detail page no longer shows redundant metadata fields that duplicated the breadcrumb.
  • The all-measurements page now loads cell specs on cold/direct navigation so the spec-name and spec-anode/cathode columns populate on first paint instead of only after navigating in from elsewhere.
Improvements
  • CycleAgeing defaults experiment_model_mode="unified" when an experiment is supplied, and iwp.Simulation’s compile flag now defaults to True in unified mode. Cycling experiments repeat the same handful of steps many times, so a single switching model collapses build and solve cost compared to one model per step; combined with compilation this is a substantial speedup for cycle- ageing fits. Pinned to PyBaMM 26.6.1.0.
  • CycleAgeing accepts experiment="from data", which builds the cycling experiment from the loaded dataset instead of requiring the user to restate it.
  • CycleAgeing auto-enables store_first_last when every requested metric only needs first/last-cycle quantities, skipping the intermediate-cycle storage that the metrics would never read.
  • save_at_cycles on CycleAgeing is now auto-derived from the requested metric tree when the caller does not pass one explicitly — only the cycles that downstream metrics actually consume are saved.
  • ElectrodeBalancing emits dQ/dV curves alongside the existing capacity outputs, so weighted Wasserstein costs can be computed directly against the dQ/dV peak structure.
  • The Time metric accepts a vector of times and reports the metric at each one, replacing the previous one-scalar-at-a-time loop in callers that needed a few sample points.
  • A generic solve_kwargs passes through every objective into Simulation.solve, complementing last week’s solver_kwargs on the constructor side.
  • Metric-tree introspection moved onto the Metric and Slicer classes so callers can ask a metric what it stores instead of re-deriving the answer from a config dict.
  • SimplePipeline is a new Pipeline subclass with a client-side validator that rejects configs containing more than one expensive element (DataFit, ArrayDataFit, Validation), mirroring the server-side guardrail.
Fixes
  • interactive_preprocessing is rejected at the schema level when passed in objective options, matching the runtime ban and giving a clearer config-time error.
Improvements
  • client.electrolyte.transport_from_dataset(dataset_id, forms=, columns=) turns a material property dataset into a pybamm.ParameterValues of concentration-dependent transport functions ready to hand to iws.direct_entries.DirectEntry(parameters=...). Two function forms are supported per property: tabulated pybamm.Interpolant (default) and Landesfeind–Gasteiger isothermal y = A·exp(B·c) (least-squares on ln y), which stays positive and finite below the lowest measured concentration so high-rate DFN solves don’t hit IDA_ERR_FAIL when the electrolyte depletes.
  • Quantity constructors accept PyBaMM-style unit strings and normalize them on input, so values copied from a PyBaMM parameter set round-trip into and out of cell schemas without a manual unit rewrite.
  • JobClient.get_parameter_trace(job_id) returns the parameter- evaluation trace from an optimization job as a DataFrame for inspection or downstream plotting.
  • client.ecm (introduced above) is the new ECM sub-client.
Fixes
  • POST and PATCH calls retry on transient connection drops with the same capped-backoff policy already used for GETs, removing the spurious ConnectionResetError surfacing during long-running uploads against a flaky link.
Fixes
  • A constant-current discharge segment with the unsigned mixed-mode sign convention no longer mislabels the step direction, and the matching CC-discharge classifier no longer reverses the sign on parquet round-trip.
Improvements
  • Arbin protocols with a SIGN(...) or step-time piecewise-current formula now parse correctly and emit the expected step plan; the previous parser failed on the conditional with a generic syntax error.
  • human_readable_protocol(...) renders drive-cycle steps in the same shorthand notation the simulator uses elsewhere, instead of expanding them inline.
Fixes
  • Pause and end stop reasons now surface as user-correctable inline errors instead of being treated as solver failures.
Improvements
  • The parameterize skill gains a current-driven fit template and a full-cell OCV template, each with an objective / cost decision tree so an agent can route an incoming dataset to the right starting configuration without re-deriving the choice every time.
  • A new electrolyte-transport SDK skill documents the client.electrolyte.transport_from_dataset(...) workflow, including the interpolant vs. Landesfeind-exponential trade-off and the DirectEntry integration pattern.
  • A new ecm-fitting SDK skill walks through the three ECM input modes (example, file, measurements) and the save-to-project flow.
  • The parameterize skill explains the component/material model and the material → cells reverse lookup so an agent can find which cell specs reference a given material before editing it.
June 8, 2026
Brand-aligned theme refresh, semantic zoom on validation plots, Navigator helper and new data agent skills, tunable solver_kwargs with PyBaMM 26.6 support

Brand-aligned theme refresh

Studio is no longer the unmodified Minimals MUI template — it now follows the Ionworks brand guide. Pink (#FF87B7) drives primary CTAs, selection, focus, and active nav/tabs; green-black (#020E0D) anchors text and neutrals; blue (#0C68E9) is reserved for hyperlinks so links stay obviously clickable. Corners are sharpened to 4px, typography is updated, and JSON viewers, plots, tables, and ID chips have been re-skinned to match. All changes are theme-level — no component or data-flow changes — so every screen lifts at once.

Semantic zoom on pipeline validation plots

Validation plots now fetch their data on demand and decimate server-side based on the visible x-range. Box-zoom into a region re-requests denser data for that window; a step-back zoom and a full-reset control are stacked in a zoom history. Pipeline and datafit jobs gain two new endpoints (/pipelines/validations/{job_id}/plot_data and /pipelines/datafits/{job_id}/plot_data) that return decimated traces with stride sampling, cached for one hour per (job_id, organization_id).

Tunable solver_kwargs with PyBaMM 26.6 support

iwp.Simulation accepts a new solver_kwargs dict that merges over the tuned IonworksSolver defaults — e.g. solver_kwargs={"options": {"compile": True}} flips IDAKLU compilation on for long unified-experiment runs without restating the rest of the solver configuration. It threads through every simulation-backed objective (CurrentDriven, Pulse, CalendarAgeing, CycleAgeing). The workspace also upgrades to PyBaMM 26.6 (first release with the serialisation kernel), with a compatibility sniffer that accepts both the new fully-qualified $type tags and the legacy short type tags so historical and freshly-generated payloads both decode.ionworks.Navigator is a new cached walker over the cell_spec → cell_instance → cell_measurement → steps / time_series hierarchy. It memoises every lookup, paginates automatically, returns name-sorted listings for stable iteration, and copies cached DataFrames so callers can’t corrupt the cache. Two new agent skills land alongside it: build-data-report (multi-section markdown + PDF analysis covering rate capability, DCIR, OCV, GITT, entropic, aging, and gap analysis) and validate-data (a pre-upload gate covering header audit, schema, parquet, and strict measurement validators).
Improvements
  • Cell-measurements list endpoints have been consolidated and now use server-side pagination across project, spec, and instance scopes. The frontend resolves spec metadata from the existing cells store and pre-fetches spec-level filters (spec_name, spec_anode, spec_cathode) separately so the main list query stays cheap.
Fixes
  • Deleting a project no longer shows a misleading “The project you are trying to access does not exist…” toast. The delete confirmation dialog also now requires typing the exact project name to enable the Delete project button, matching the destructive-action pattern used elsewhere.
  • Signed-URL generation for measurement files retries on httpx.ReadTimeout with 0.5 s / 1.0 s backoff; a new batch endpoint replaces the N parallel calls in the file-list path with a single POST. File downloads also retry on JSONDecodeError (raised when the storage gateway returns HTML instead of JSON under load) and surface exhausted attempts as a structured 502.
  • Cell-instance measurements grid no longer renders the previous spec’s rows after navigating between specs; CellSelector shows a skeleton while loading and no longer bails when an unrelated cells fetch is in flight; the ECM measurement selector switched from loading every project measurement into memory to true server-side pagination.
  • The list_for_template drive-cycles endpoint now awaits execute() — previously it raised AttributeError on .data at runtime — and has been upgraded to standard limit / offset pagination.
  • RHFAutocomplete now forwards slotProps (listbox, paper, popper, …) as a proper slotProps object instead of spreading them as flat props that MUI silently ignored.
Improvements
  • skip_objective_callbacks is a new DataFit option that skips the pre- and post-fit objective callbacks, each of which runs an extra full objective simulation purely to capture initial/final results that the server does not surface. Backend datafit jobs now set it by default for faster cluster fits; local users keep the existing callbacks unless they opt in.
  • Configuration errors raised inside ionworkspipeline are now UserConfigurationError (the new name for ConfigurationError) instead of bare ValueError, so the job classifier reliably maps them to CONFIGURATION_ERROR rather than SOLVER_ERROR and the Configuration error UI path lights up correctly.
Improvements
  • client.simulation.get_result() returns a typed SimulationResult dataclass with time_series and steps as DataFrames (polars by default, pandas when set_dataframe_backend("pandas") is active) and metrics as a plain dict, replacing the previous raw-dict response. Callers use attribute access and .columns membership checks instead of dict-key lookups.
Improvements
  • DataLoader.data and DataLoader.steps no longer emit the Polars-migration FutureWarning on every access — the polars return type is now the established default.
Improvements
  • New sim-results SDK skill: fetching time-series and step data from a completed simulation, computing SOC via step-boundary cumulation, identifying step types by mean current, deriving DCIR from pulse steps using an OCV baseline plus onset current, and polars-aware plotting conventions.
  • The parameterize skill now documents electrode geometry (thickness, porosity, particle radius, AM fraction, maximum concentration) as a hard build requirement for DFN/SPMe — a physics-based model cannot be assembled without these structural parameters. Geometry lives on the cell spec’s components (not on a measurement), can come from teardown / metrology / vendor datasheet / literature, and FPBM is no longer offered as “available” for Tier-2 cells that lack it.
June 1, 2026
Ionworks solver replaces IDAKLU as the default, structured error details on failed pipelines and optimizations, Wasserstein weighted mode and MSMRFullCell dQ/dU output

Ionworks solver

A new IonworksSolver is now the default solver inside ionworkspipeline.Simulation. It is a drop-in replacement for pybamm.IDAKLUSolver that detects DAE structural properties (quadrature, linear-constant / linear-varying, block-constant / block-varying) and substitutes exact analytical solutions where the structure allows, falling back to IDAKLU for non-analytical models. Runtime C codegen via CasADi’s CodeGenerator plus Numba-JIT integration kernels reduce per-step cost on the analytical paths. Piecewise interpolation now also accepts smoothing=0 so a hard step function can be modelled directly.

Structured error details on failed pipelines and optimizations

Pipeline element and optimization failures now carry a structured error_detail with the exception type and a Sentry deep-link. Two new job error codes — CONFIGURATION_ERROR and SOLVER_ERROR — separate user-fixable config mistakes from solver-side failures. Inside the pipeline package, FuzzyDict / ParameterStore raise the new ParameterNotFoundError (instead of bare KeyError) and ConfigurationError distinguishes invalid configs from runtime errors, so the classifier attaches the right code. In Studio, failed optimizations now show a View in Sentry button (superadmin only) that opens the captured event directly; the exception message itself is stripped before serialization so user-visible diagnostics stay safe to share.

Wasserstein weighted-point-cloud mode and MSMRFullCell dQ/dU output

iws.costs.Wasserstein / iwp.costs.Wasserstein gain optional position_variable and weight_variable fields. When both are set, the cost computes one Wasserstein-1 distance per objective comparing two weighted point clouds — useful for full-cell MSMR fits where peak-location error in Voltage [V] should be measured by the |dQ/dV| weights instead of sample-by-sample. MSMRFullCell also now emits Differential capacity [Ah/V] when listed in objective variables, derived from Full voltage [V] / Full capacity [A.h] and interpolated to the data voltage grid.

Pipelines documentation moved to schema-first Docs section

The runnable “how do I actually run this” pipelines content has moved out of the Guide into a new schema-first Pipelines section under the Documentation tab, with every code sample rewritten to use ionworks-schema + ionworks-api. The Guide retains the theory — equations, parameter tables, intuition — and its existing “Pipelines” group is renamed to Parameterization. The Japanese tab mirrors the new section.
Fixes
  • Project-scoped ECM fitting from existing measurements no longer returns 422: the frontend now sends the nested {measurements: [{id, initial_soc?}], ecm_options: {...}} payload the backend has expected since the per-measurement SOC change, and shows a per-measurement initial-SOC input next to each selected measurement.
Improvements
  • client.simulation.protocol(...) accepts design_parameters again as a flat dict — the single-simulation convenience that was accidentally removed when the protocol-template flow landed. Supplying both design_parameters and design_parameters_doe, or a DOE that expands to more than one simulation, now raises explicitly instead of silently billing for the extra runs.
  • BioLogic .mps protocols with large embedded drive-cycle tables (e.g. ~96k-row Urban Profile traces inlined as YAML block scalars) no longer time out at the edge proxy on /protocols/parse-to-template — the modified YAML is no longer re-parsed with the slow PyYAML path.
  • iws.direct_entries.DirectEntry accepts a pybamm.ParameterValues directly. Callable values (concentration- / temperature-dependent interpolants) are serialized to symbol-JSON via ParameterValues.to_json() automatically, and the in-pipeline DirectEntry.from_schema path deserializes them back into pybamm symbols so local and API consumption paths behave the same.
Fixes
  • Child-module loggers under ionworkspipeline.* (e.g. ionworkspipeline.data_fits.*) are now governed by set_logging_level — the package logger was previously a sibling rather than the parent, so submodule records propagated past it.
Improvements
  • client.simple_pipeline.create(...) accepts a PipelineOptions (e.g. live_progress_updates=False) — the worker already honoured pipeline_options in job params, this wires the request model, service layer, and SDK client through.
Fixes
  • Outbound JSON payloads no longer raise TypeError when a request body contains a pandas DataFrame with datetime columns. pd.Timestamp is serialized via isoformat() and pd.NaT becomes null.
Fixes
  • BioLogic .mps parser no longer hangs on protocols like lfp_gr_cccv.mps where lim*_seq stores an internal sub-cycle pointer ≤ Ns+1. Only forward jumps (seq > Ns+1) emit gotos; backward or equal values are treated as plain step-ends.
Improvements
  • Ionworks skills can now be installed as a Gemini CLI extension via gemini extensions install ~/ionworks-skills; a GEMINI.md context file loads all eight skills as passive context on every session. The Coding agents docs page is updated to cover Gemini CLI, Cursor, and GitHub Copilot install paths alongside Codex and Claude Code.
  • New half-cell MSMR template (assets/half_cell_msmr_template.py) plus discover_half_cell.py, inspect_half_cell_data.py, and validate_half_cell_fit.py helpers in the parameterize skill. The reference page mandates the template and lays out a discover → inspect → scaffold → dry-run → submit → validate workflow so every half-cell OCP fit uses the same canonical priors, multistarts, and Xj method.
Fixes
  • The process-data skill now documents the platform’s actual current sign convention (positive = discharge), matches the validator and set_positive_current_for_discharge transform, and calls out the double-flip trap.
May 25, 2026
Tighter measurement-data validation, broader UCP-to-cycler conversion, Wasserstein cost function

Stronger measurement-data validation and processing

A new strict-mode check, capacity_energy_from_current_power, compares the reported Discharge/Charge capacity [A.h] and …energy [W.h] columns against a per-step trapezoidal integral of Current [A] and Power [W], flagging row-by-row mismatches (10% default tolerance) so a transient mid-step error that later cancels out is still caught. When the reported charge/discharge columns are swapped — common in half-cell exports — fix_swapped_charge_discharge_columns detects and corrects the labels. The current-sign-convention check now weights each step’s vote by the charge it actually passes (∫|I| dt) instead of its row count, so a long near-zero-current voltage hold no longer outvotes genuine discharge pulses and raises a false CURRENT_SIGN_REVERSED.Small-current coin- and half-cell data now processes end-to-end: rest-step detection scales its threshold to the trace’s peak current (sub-mA currents are no longer all classified as rest), the auto-flip retry drops stale capacity/energy columns before recomputing, and caller-supplied Step count labels survive sign correction. A new generic parquet reader uses the same alias-based column detection as the CSV reader, and folder: data references now accept time_series.parquet / steps.parquet (preferred over CSV when both are present).

Broader, validated UCP-to-cycler conversion

The UCP-to-Arbin and UCP-to-Maccor converters handle more end-condition and step types (variable / loop / compound ends, temperature ends, Maccor pause steps) and now validate up front, raising clear errors on features a target cycler genuinely cannot represent instead of emitting files that fail to re-parse.

Wasserstein distance cost function

iwp.costs.Wasserstein adds the 1-Wasserstein (earth mover’s) distance as a data-fitting cost, comparing model output and data as distributions rather than point-by-point. Available in both the pipeline and schema packages.
Improvements
  • Protocols list loads dramatically faster: the experiment-templates list endpoint now omits heavy JSONB columns by default (~7.5 KB vs 7.7 MB on 22 templates), with callers opting into protocol_config, parameters_schema, and similar fields via ?include=.
  • Failed pipelines can be resubmitted from the pipeline details page, which now shows an error banner that deep-links to each failed element; individual elements display an inline error alert.
  • Material property dataset plots now have an always-visible vertical legend with units, positioned to the right of the plot.
Fixes
  • Filtering measurements by ID on the project data page now narrows the list — the id query parameter was previously dropped silently, returning every measurement.
  • Material dataset upload no longer shifts column indices when a CSV has an unnamed leading column, and now requires every column to be named (with a clear error) instead of silently skipping unnamed ones.
  • Project membership management, active-organization switching, and parameterized-model deletion now go through the backend API instead of direct database writes, so they behave consistently under row-level security.
Improvements
  • client.auth_check() and client.whoami() verify which user and organization a configured API key resolves to, backed by a new GET /auth/health endpoint — useful for debugging wrong-org (403) errors and confirming which key is active.
Improvements
  • Maccor protocols with SubRout steps survive UCP YAML round-trips: resolved subroutine steps are namespaced and written back into the YAML, and intra-subroutine goto targets resolve at simulation time, fixing the Subroutine '<name>' not found error in the parse-then-simulate flow.
Improvements
  • New validate-data skill guides agents through the measurement-data validation and column-fix workflow.
  • A Codex plugin manifest was added so the Ionworks skills package installs in Codex alongside Claude Code.
May 18, 2026
Material properties in Studio, native UCP simulation for design optimization, SimplePipeline workflow, global /search endpoint

Material properties in Studio

A new Materials section in the project sidebar lets you create materials within a project and attach measured property datasets (CSV / parquet) to each one. Upload, plot, edit, re-process, replace, and delete are all wired up, backed by a new material_property_datasets table with composite (project_id, organization_id) foreign keys and a dedicated Supabase bucket. The Python SDK gains read-only client.material and client.material_property_dataset sub-clients with list, get, get_units, and get_data (returns a polars.DataFrame); the REST surface supports signed-URL downloads, on-the-fly downsampling, and x-range filtering. A new docs page covers the UI workflow and REST endpoints.

Native UCP simulation for design optimization

DesignObjective gained a backend="ucp" option (also selectable via the IONWORKS_SIMULATION_BACKEND env var) that runs UCP protocols natively inside the optimization loop instead of converting them to a pybamm.Experiment. This preserves UCP features that the conversion dropped — dynamic loops, conditionals, gotos, set_variable, subroutines — and avoids the per-iteration parser overhead. EIS steps are now rejected up front in design optimization (with a clear error both in the frontend form and at the backend), because the UCP backend does not yet support frequency-domain steps; standalone simulations remain the path for EIS.

SimplePipeline workflow

A new lightweight pipeline variant for configs with at most one expensive element (one data fit or one validation). The whole config runs end-to-end as a single Ray job on the batch queue instead of fanning out to child jobs. CRUD endpoints under /simple_pipelines (POST returns 202; LIST supports filters and Supabase operator syntax; PATCH for name/description; cancel and delete), plus a client.simple_pipeline SDK sub-client with create, get, list, update, cancel, delete, and wait_for_completion. Large validation outputs are written to metadata storage instead of the DB record, and distributed evaluation inside the single job is wired through a new _on_setup_complete hook on DataFit.setup().

Global search API

New GET /search endpoint performs prefix full-text + substring search across projects, studies, simulations, models, parameterized models, optimizations, optimization templates, experiment templates, pipelines, cell specifications, and materials within the authenticated organization. Backed by Postgres tsvector columns + GIN indexes per table, with all entity queries fanned out in parallel via asyncio.gather. There is no frontend search bar in Studio yet — the new docs page makes this explicit so you don’t go looking for one.
Improvements
  • Optimizations table: bulk Delete restored next to the existing bulk Cancel action, gated by optimization:delete, with a confirmation dialog that pluralizes correctly and per-row error toasts.
  • Protocol simulator: Download CSV button next to “Configure Plot” exports the full time-series plus step-level columns (cycle count, step number, protocol variables) expanded to match each time point, regardless of zoom.
  • Time-series measurement plot: overlay multiple variables sharing the same unit on either Y-axis via a + button — unit-filtered dropdown, distinct color cycle per series, individual × to remove, and selecting a primary variable with a different unit clears incompatible extras.
  • ECM models now expose Anode potential [V], Cathode potential [V], and their open-circuit counterparts so BioLogic three-electrode EWE / ECE control limits can be reproduced from a simulation. New LFP/Li metal half-cell chemistry added to the parameter library and the cell configuration UI.
  • Hover tooltips standardized to 3 decimal places across Plotly and Highcharts via createBasePlotLayout; documented as a new frontend convention.
  • EIS Nyquist plot renders as markers-only scatter (no connecting lines), with a slightly larger marker.
  • Cell-spec cascade delete batches up to 1000 storage paths per bucket.remove() call and parallelises per-measurement folder deletes with Semaphore(16), with asyncio.gather for measurement-list fetches so one transient DB failure doesn’t abort cleanup for the others.
  • Supabase storage downloads now retry transient 5xx errors with exponential backoff (3 attempts, 0.5 s → 4 s, jittered) — fixes job failures where storage3 crashed on non-JSON 502 response bodies.
  • GET /jobs/{job_id}/metadata route returns the parsed contents of a job’s metadata.json.gz blob, giving the Python SDK a path to large validation payloads (validation_results, validation_plot_config) that the legacy /pipelines/validations/{job_id}/result endpoint could not reach.
  • Simulation submission unified to a two-step pattern (/protocols/parse-to-template/simulations/with-template/batch). Removed the redundant /simulations/protocol, /simulations/protocol/batch, single /simulations/with-template, and /standalone-cycler/simulate endpoints, and dropped the transient cycler_protocol_results table.
Fixes
  • Simulation dedup: removed cycler_protocol_record_id (which changes every parse session) from the simulation_options uniqueness key, so the “simulation already exists” path actually triggers and duplicate rows stop accumulating.
  • Measurement details: Cycles tab is visible again and the cycle filter slider’s range is correct after the Cycle numberCycle count column rename.
  • Measurements of type properties or file now show an informational alert pointing to the details panel or the SDK instead of rendering empty time-series tabs.
Improvements
  • ionworks-schema is now the single validation boundary for parser inputs across the pipeline. Every pipeline class with a schema counterpart (~40 classes) gained a from_schema(schema) classmethod; parsers call iws.X.model_validate(config) and construct runtime objects via from_schema. The auto-generated ConfigMixin.config_schema() is retired, and the SDK now depends on ionworks-schema directly so PipelineClient.create() accepts iws.Pipeline | dict.
  • Standardized user-facing optimiser kwargs across all scipy wrappers: max_iterations replaces maxiter / max_nfev / iters / niter, and population_size replaces popsize on ScipyDifferentialEvolution. Old names continue to work with a DeprecationWarning. DataFit.max_iterations and FunctionTimeout.max_iterations align with the same name.
  • ionworks_ucp.SolverError is now registered in BaseObjective._acceptable_errors, so a transient UCP solver failure during differential evolution lands on the finite-penalty path instead of killing the optimization. Protocol and configuration errors remain ValueError / RuntimeError so static bugs still surface. Fixes
  • pybamm.Experiment period and temperature now round-trip through Serialise.serialise_experiment (fix shipped in pybamm 26.4.3). ExperimentStepConfigSchema and ExperimentConfigSchema accept the new field set (per-step period, temperature, tags, description, direction, start_time, skip_ok; experiment-level period, temperature, termination); duration also accepts human-readable strings like "287 seconds". The previous _apply_dropped_fields workaround is removed.
  • SimplePipeline jobs run DataFit in-process and now establish their own Ray connection (sharing _connect_to_ray_with_retry with the child-job runner) so distributed evaluation actually fires; when the Ray connection fails the evaluator hook is skipped and DataFit falls back to its in-process path. Legacy element-type labels ("Data Fit", "Direct Entry", "datafit") are now canonicalized to wire discriminators at ingress.
Improvements
  • client.simple_pipeline sub-client for the new SimplePipeline workflow.
  • client.protocol.convert(protocol, target) returns a ConvertResult with primary_bytes, text(), and save(dir) helpers — exports a UCP YAML protocol to a native vendor file (Maccor, Arbin, Neware, BioLogic BT-Test, or Novonix). Maccor returns any drive-cycle MWF assets alongside the primary file.
  • client.job.get_metadata(job_id) returns the parsed contents of a job’s metadata.json.gz, giving the SDK access to large validation payloads the legacy result endpoint could not reach.
Improvements
  • BioLogic .mps parser: User Profile (drive cycle) steps are now extracted from embedded Urban Profile Tables, or from sibling .txt files supplied via additional_content when the .mps lacks embedded tables. Current sign is flipped on the way in so positive represents discharge for UCP/PyBaMM.
  • Arbin parser rewritten to keep the step list flat with raw gotos instead of inferring loops from backward-goto patterns — dynamic_experiment already resolves gotos in a flat namespace and guards backward jumps via max_backward_jumps. Fixes sibling backward gotos to the same target, cross-loop goto resolution, and digit-bearing formula labels like F_EIS_10%_capacity_change. Pause steps emit UCP’s first-class auxiliary Pause step.
  • More Arbin / Maccor step types recognized: Arbin Internal ResistanceRest with a UserWarning, av_t / pv_chan_test_time / pv_chan_cv_stage_current mapped to their UCP types, bracketed MV_UD[n] normalised, leading-negative current expressions classified as Discharge. Maccor User Def CYCLE <op> N translated to a UCP VariableEnd against the runtime CYCLE alias.
  • Per-step overhead trimmed substantially on long protocols. When the model lacks Temperature [degC] the per-step lookup skips pybamm’s O(N²) “did you mean…?” difflib search entirely (~41 ms per step); when the protocol contains no derivative ends, set_variable, or variable-driven goto targets, the per-step full-trace evaluation is skipped and _create_minimal_step_df emits a single-row frame with only Time [s].
  • New ionworks_ucp.SolverError exception is raised for genuine pybamm solver failures; protocol and configuration errors keep their original ValueError / RuntimeError types and just gain step context.
Fixes
  • Real Maccor .MWF exports that include the ~28-line preamble plus a Type\tMode\tValue\t… header row now parse — read_waveform scans for the header sentinel and skips up to and including it before handing the rest to pd.read_csv. Files containing only data rows still parse unchanged.
Improvements
  • New run-simple-pipelines skill walks through the SimplePipeline client end-to-end.
  • process-data: clarified that protocol holds test conditions that affect the electrochemical outcome (temperature, C-rate, SoC, DoD, pressure) while test_setup holds physical logistics (cycler model, operator, lab, channel) that do not. test_setup lives only on measurements, not on cell instances.
  • process-data: set_step_count with a step column is now the unambiguous default — it keys off np.sign(np.diff(...)), so decreasing / repeating step ids from GITT or RPT-with-substeps work the same as monotonic ones; set_cumulative_step_number(method="current sign") is reframed as a fallback for when no step column exists at all. Added a caveat for cyclers that emit duplicate Time [s] rows at step transitions.
  • process-data: mandatory header-audit step codifies eight rules (walk every file, group by cohort × column-set, classify Standard / Auxiliary / Drop, diff reader output, preserve aux columns, keep multi-thermocouple channels separate, confirm units / sign per cohort, surface missing-temperature as a finding) and a required confirmation-report shape, so silent column drops between cycler families are caught before any standardized parquet is written.
May 11, 2026
Custom PyBaMM model + Li-S support, ECM capacity co-optimization, default project for the Python SDK, structured validation issues

Custom PyBaMM models with Lithium-Sulfur chemistry

The /models/upload-custom endpoint now accepts a chemistry field (defaulting to lithium_ion), and Li-S models get a chemistry-aware initial-state shim plus tighter IDAKLU tolerances when they run. The manage-projects SDK skill documents the full upload workflow — pybamm.Serialise().save_custom_model(filename=...) → multipart upload → client.model.get(id) returns is_custom_model: true — including the EventType-not-JSON-serializable gotcha that hits anyone trying to json.dumps the dict from serialise_custom_model() directly.

ECM fit: capacity co-optimization and per-segment initial SoC

Several interlocking improvements to the project-scoped ECM fit. Supplying an ocv_soc_curve (and optional bounds_capacity) lets you co-optimize cell capacity Q jointly with the RC beta knots in the outer least-squares loop instead of pinning Q to a single seed; on a 25 °C rate-test trace the fitted capacity now lands within 0.9 % of the coulomb-counting truth across all knot schedules. initial_soc accepts a list (one entry per measurement) so multi-measurement fits reset SoC at each segment boundary instead of integrating coulombs across the gaps; if you omit it, the new auto-seed routine refines each segment’s soc0 by root-finding V[s] = OCV(soc0) − I[s]·R0(soc0). num_knots, num_knots_r0, knot_schedule, and clamp_max_ratio are now first-class parameters on /fit-from-measurements and /fit-from-file. The boundary clamp default also loosened from max_ratio=1.0 to 10.0, which was collapsing R0 and triggering pybamm IDAKLU CONV_FAIL on rate-test forward sims.

Default project for the Python SDK

The Ionworks client now resolves a default project_id at construction time from a project_id= argument or the new IONWORKS_PROJECT_ID environment variable, so callers no longer have to thread project_id through every call. The previous PROJECT_ID env var still works but emits a DeprecationWarning. All client.study.* methods take project_id as an optional keyword (after the resource ID) defaulting to the client value, and pipelines and optimizations auto-inject it into payloads.

Structured measurement-validation issues

MeasurementValidationError.errors is now list[ValidationIssue] — a frozen dataclass carrying a stable IssueCode (StrEnum), severity, human-readable message, and JSON-native payload. Downstream code can branch on check identity via e.has_code(IssueCode.CURRENT_SIGN_REVERSED) instead of grepping the message string. ionworksdata’s auto-fix path now keys off the new codes; IssueCode and ValidationIssue are re-exported from the top-level ionworks package.
Improvements
  • organization_id is now NOT NULL on ~15 tables, all RLS policies have been rewritten to read it directly (replacing the has_permission_via_* function chain), and composite (project_id, organization_id) foreign keys prevent org drift on project-scoped tables.
  • Simulation boards CRUD moved to a proper backend API at /projects/{project_id}/studies/{study_id}/simulation_boards with organization_id resolved server-side, fixing a blank Visualization tab on stage where direct Supabase inserts were silently rejected by the stricter RLS.
  • Optimization Performance Detail and Performance Summary tabs now surface buried validation_warning / validation_not_supported issues as top-level alerts above the tabs, with info “no data” alerts inside the tabs for the rare empty-but-valid case.
  • Defensive UX in the Visualization tab: the Data/Visualization toggle stays visible even when no board is available, with a warning alert prompting the switch back to Data instead of trapping the user on a blank page.
Fixes
  • The single simulation result page no longer flashes “Simulation not found” before the data loads on a fresh navigation.
Improvements
  • ionworks-schema gained Constraint, Penalty, CMAESOptions, PSOOptions, DEOptions, LatinHypercube, and Uniform schema classes that pipeline already had, plus Field(description=...) enrichment on ~40 pilot-touched classes across objectives, data fits, parameter estimators, regularizers, and distribution samplers. A new Sphinx docs skeleton auto-generates a reference page per submodule and cross-links each schema class to the matching ionworkspipeline page via intersphinx.
Fixes
  • EmptySolution AttributeError (raised when SUNDIALS gives up at IC for a bad parameter combination) now routes to the existing fit-failure penalty path instead of crashing the whole datafit. Cloud fits stay alive and the offending sample just gets a huge cost.
Improvements
  • New client.urls.measurement(measurement_id, project_id) helper returns the web app deep link for a measurement, so callers don’t have to hand-build URLs against frontend/src/routes/paths.ts.
Improvements
  • manage-projects documents the /models/upload-custom multipart workflow and the Model / ParameterizedModel disambiguation.
  • upload-data and other validation-aware skills updated to reference the new IssueCode / ValidationIssue API and the e.has_code(...) pattern instead of substring-matching error strings.
May 4, 2026
Full optimization experiment editor, Arbin reader, Maccor coverage, UCP input schema, baseline migration squash

Full editor for optimization experiments

Optimization experiments now use the same protocol editor as the rest of the app. The bare text field has been replaced with a CodeMirror-based editor featuring YAML syntax highlighting, dark-mode support, frontend pre-validation, and the full protocol-builder dialog for picking templates and tuning steps. New protocols pre-load a “Constant Current Charge” template so users have a working starting point.

Iterative metrics for design optimization

Design optimization now supports CyclewiseMetric and StepwiseMetric wrappers end-to-end. The optimization form lets you target a metric on a specific cycle or step with bounded input fields driven by the parsed experiment, mix pipeline variables alongside PyBaMM variables, and round-trips wrapped metrics through save/load. The underlying UCP→PyBaMM parser also fixes a long-standing bug where repeated blocks were flat-expanded, which broke solution.cycles indexing for cycle-wise metrics. Two new templates ship: a simplified Charge optimization and composite system models.

Arbin cycler support

ionworksdata now reads native Arbin exports — CSV, XLSX, and the binary .res format. The .res reader uses mdb-export to extract data from the underlying Access database (no Python MDB library required), sorts rows by DateTime to handle interleaved multi-session recordings, and reads the absolute start time from Global_Table.Start_DateTime. Auto-detection picks the right reader from file headers, and arbin res is also exposed as an explicit reader name.

Maccor protocol coverage

The Maccor parser and simulator now handle several real-world protocol features that previously failed at parse time or during simulation, including per-limit safety gotos, Chg/Dis Func CCCV step types, and robust VAR setvar handling. The Maccor ionworksdata reader also gained support for the compact short-form column header set used by some firmware versions, which previously crashed with ColumnNotFoundError: "Time [s]" and silently dropped capacity/energy columns.

Machine-checkable UCP input schema

The discovery endpoint /discovery/schemas/protocol now exposes a JSON Schema generated from the parser’s own enums, matching the actual YAML authoring format (single-key dicts like {Charge: {...}}) rather than the parsed in-memory shape. Parity tests round-trip 17 good and 6 bad fixtures through both the schema and the parser so they cannot drift. Submitting the legacy kind: shape to POST /protocols/validate now returns a helpful error pointing at the canonical YAML examples, and the discover-api and run-simulations skills show local pre-validation via jsonschema.validate(...).
Improvements
  • Organization ID is now denormalized onto every row that sits under an organization (including join tables), simplifying RLS checks and cross-resource queries.
  • The squashed baseline migration collapses ~100 historical migrations into a single idempotent file, with a CI/CD repair step that handles out-of-order feature-branch deploys on internal environments.
  • The Ionworks Agent tab has been replaced with a static instructions page pointing to the Ionworks Agentic Toolkit; the in-app pydantic-ai chat agent has been removed in favour of the SKILL.md-based workflow.
  • New cell specifications no longer pre-fill a default capacity, so users enter the actual cell capacity from the start.
Fixes
  • Storage permissions are now enforced at the organization level so members of the same org can read each other’s measurement files as expected.
  • created_by_email is now consistently populated on cell-spec, cell-instance, and cell-measurement responses across both get and list endpoints.
  • Simulation usage is aggregated at the organization level (matching the org-level usage limit that the backend already enforces) and is fetched from a dedicated GET /organizations/current/usage endpoint.
Improvements
  • Cost / objective-function layer restructured into focused modules (accumulators, error metrics, normalizers, NaN policies) with several correctness fixes around scalarization, normalization defaults, and partial-solution handling.
  • FunctionParameters are now serialized correctly inside objective and data-fit configs, so configs with function-valued parameters round-trip through JSON.
Fixes
  • Half-cell ECM now exposes anode and cathode potential variables (Anode potential [V], Cathode potential [V]) for plotting and metrics.
  • get_cycle_metrics keeps the Cycle count column name on output instead of renaming it to Cycle number, matching the input convention used throughout the data pipeline.
Improvements
  • validate_measurement_data, CellMeasurement.create, and CellMeasurement.create_or_get accept a skip_checks parameter so callers can relax a single strict validator (e.g. time_gaps) instead of disabling strict mode wholesale. Unknown check names raise ValueError, and the canonical set is exposed as ionworks.validators.STRICT_CHECK_NAMES. validate_strict=True is now the recommended default.
Improvements
  • New Arbin reader covering CSV, XLSX, and native .res exports (see the highlight above).
  • Maccor reader now accepts the compact short-form column header set used by some cycler firmware versions.
Improvements
  • New electrolyte parameterization page with a worked Landesfeind fit example.
  • Optimization experiment editor, iterative metrics, simplified Charge template, and composite system models are documented.
  • Custom variables reference now documents anode/cathode potential exposure on the half-cell ECM (English and Japanese).
  • Maccor native CCCV step type is documented (English and Japanese).
  • Arbin is listed in the supported cyclers table.
  • SEO metadata (keywords, OG tags, tightened descriptions) added across the docs site, including expanded data-fitting guide descriptions.
Improvements
  • Each mirrored package (ionworksdata, ionworks-schema, ionworks-api, iwutil, ionworkspipeline, skills) now ships a per-package CHANGELOG.md in Keep-a-Changelog format, written and validated as part of the release workflows.
  • discover-api and run-simulations skills updated to reference the new UCP input JSON Schema and demonstrate local pre-validation.
  • upload-data skill updated to recommend validate_strict=True and document the new skip_checks parameter.
April 27, 2026
Project-scoped ECM fitting, PyBaMM v26.4.0 support, SDK skills plugin, strict upload validators

Project-scoped ECM fitting

Equivalent-circuit-model fitting is now available directly inside a project. Pick any measurement already attached to one of the project’s cells (or upload a fresh CSV), preview the trace, run the fit, and save the result back to the project as a new model with one click. The new flow replaces the multi-step stepper with stacked cards, adds a model-vs-data plot, an error plot, a fitting-progress indicator, an OCV toggle helper, and auto-scrolls to the results when the fit completes. The standalone ECM demo continues to work for unauthenticated users.

PyBaMM v26.4.0 support

The pipeline and protocol simulator have been updated for PyBaMM v26.4.0, which introduces a dedicated Rest step class in place of the old zero-current step and renames the MSMR electrode-capacity parameters from short keys (e.g. Q_n_1, Q_p_1) to descriptive strings (e.g. “Negative electrode host site occupancy capacity (1) [A.h]”). Existing experiments and MSMR parameter sets continue to parse correctly under the new release.

SDK skills as a Claude Code plugin

The Ionworks Python SDK now ships with a set of installable Claude Code skills covering api-discovery, cell-data, data-upload, simulations, pipelines, and projects-and-studies. Each skill documents the relevant sub-clients, method signatures, and the create-or-get patterns so an AI assistant can drive the SDK against the current API surface. Install via /plugin marketplace add ionworks/ionworks-skills and /plugin install ionworks to get the slash commands and SessionStart hook for any Ionworks project.

Strict upload validators in the Python API

CellMeasurementClient.create and create_or_get now run optional client-side validators before sending data to Studio so processing bugs surface as a local error instead of a confusing upload result. A hard time-gap check (>5 h between consecutive rows) runs by default; passing voltage_window enables a voltage-continuity check (catches chronological misordering from a faulty partition_by), and passing rated_capacity enables a consecutive same-direction full-step check plus a soft warning for any step whose capacity exceeds 5× rated.
Improvements
  • Measurement chart controls collapse into a compact header summary; per-axis variable selectors now allow adding and removing series dynamically with a unified two-column layout.
Fixes
  • Study visualization no longer collapses to an empty state when switching between studies whose experiment-type tabs differ — the filter resets to All when the previous tab isn’t present.
  • Users with a stale Supabase session can recover by signing out via the OTP auth/continue page instead of being stuck on the redirect.
  • The empty cells page now uses the correct organization-level permission check, so users who can create cells no longer see a misleading “contact a project admin” message.
  • Pipeline element details view no longer triggers rapid duplicate fetches for Data Fit and Validation configs — the large job_config is read directly from storage and cached.
Improvements
  • Function compilation is now enabled by default in Simulation (IDAKLU solver), speeding up repeated runs that share the same model.
  • submesh_types and spatial_methods now serialise correctly through SimulationObjective.to_config, fixing JSON round-trip for objectives such as CycleAgeing that carry mesh generators or spatial methods in simulation_kwargs.
Fixes
  • Time-monotonicity validation error message now reports the correct two adjacent values (the previous off-by-one showed the values just before the actual violation).
Fixes
  • Sign-convention detection no longer misclassifies low-current pulse datasets as all-rest; the rest threshold now scales relative to the 95th-percentile current so violations are caught on small-amplitude data.
April 20, 2026
Python SDK resource clients, validation initial conditions, capacity calculation fix

Python SDK resource clients

The Python SDK gains typed sub-clients for managing Studio resources programmatically. You can now create, list, retrieve, update, and delete projects, models, parameterized models, studies, protocols, and optimizations directly from the Ionworks client — for example, client.project.list(), client.model.get(id), or client.study.create(...). A new “Managing resources” section in the Python API docs walks through each sub-client with usage examples, and the backend URL paths for Projects, Models, and Studies have been flattened so the organization is resolved from your auth token instead of being required in the URL.

Validation initial conditions and UX polish

Validations now let you choose how the simulation is initialized: auto-detect from the measurement, a custom starting voltage, or a custom state of charge. Each validation row in the Report tab can be removed individually with a confirmation dialog, and a dedup bug that caused two measurements sharing the same initial voltage to overwrite each other’s drive cycle has been fixed. Validation error plots also now render with grid lines for easier reading.

Capacity calculation fix for multi-electrode cells

ElectrodeCapacity was reading a non-canonical PyBaMM parameter name for the number of electrodes connected in parallel, causing c_max to be over-computed by a factor of N when users set the correct key. The fix aligns all lookups with PyBaMM’s canonical parameter name so multi-electrode cells parameterize correctly.
Improvements
  • Studies with simulations of a single experiment type now auto-select that type so the visualization view is immediately accessible.
  • Failed variable-evaluation jobs now surface an error state with a Retry button instead of spinning indefinitely.
  • Studies list endpoint now returns paginated results with items, count, and total.
  • Parameterized models list endpoint added with pagination.
Fixes
  • Fixed a crash when rendering time-series measurements with thousands of short steps (e.g. HPPC pulse protocols with ~1500 steps) — the dashboard can now load these measurements.
  • Fixed coupled custom variables being silently dropped when their resolution failed, which could remove convenience variables like “Temperature [degC]” from simulation outputs.
  • Capped the models list fallback page size to the backend’s 100-row limit to prevent 422 errors.
Improvements
  • Bumped PyBaMM to 26.3.1, which falls back to pybamm.BaseModel with a warning when a custom model’s base_class references a package that isn’t installed in the worker environment.
  • Refactored OCP objectives (OCPHalfCell, ElectrodeBalancingHalfCell, ElectrodeBalancing) to use the _run pattern so they support raise_on_failure and work correctly in Validation pipelines.
Fixes
  • DataFit now auto-wraps a bare objective in {ClassName: objective} so pipelines submitted with a single unwrapped objective no longer fail serialization.
Improvements
  • Added typed sub-clients for Models, Studies, Optimizations, Projects, Protocols, and ParameterizedModels, replacing raw HTTP requests for resource management.
  • SDK endpoints no longer include organization_id in paths — the organization is resolved from the auth token.
  • Simulation polling now propagates errors immediately instead of swallowing them, and uses typed JobResponse objects with server-computed is_terminal / is_failed fields.
Fixes
  • Fixed the simulation job poll endpoint (was hitting a non-existent /simulations/jobs/{id} route).
Fixes
  • Fixed a sign-detection bug in the ECM validator where a symmetric current profile (charge / discharge / charge returning to the same net charge) was incorrectly rejected despite having a non-zero SOC span.
April 13, 2026
Async optimization, study validation reports, cell data filtering

Asynchronous optimization evaluation

Optimizers now support asynchronous evaluation for both local and distributed workloads. A new ProcessPoolAsyncEvaluator runs evaluations in persistent multiprocessing workers locally, while the existing ActorPoolEvaluator gains a submit/wait-next API for distributed evaluation via Ray. Steady-state async mode keeps all workers saturated without waiting for generation boundaries, and new convergence criteria — including a patience counter and population diversity guards — prevent premature stopping.

Study validation reports

Studies now include a validation workflow for comparing simulation results against experimental measurements. A new Report tab displays overlay plots of simulated vs. measured data along with quantitative error metrics (RMSE, MAE), and a Data tab lets you manage which measurements are paired with which simulations.Find out more →

Cell data filtering and ordering

All cell list endpoints (specifications, instances, and measurements) now accept filter and ordering parameters — including name, creator email, date ranges, and sort direction. The Python SDK exposes matching keyword arguments on each list() method, and the frontend measurement table uses server-side filtering.
Improvements
  • Pipeline and cell measurement tables now support server-side sorting.
  • Added an error screen with auto-retry when the initial user info request fails.
  • Removed SNES optimizer option from the frontend.
Fixes
  • Removed XSS sinks in the notifications drawer and job progress display that rendered untrusted strings via dangerouslySetInnerHTML.
  • Normalized Redux thunk error handling to use extractApiError consistently.
  • Eliminated slow bucket.list() calls from file-measurement endpoints by caching filenames in the database, reducing response times from ~10 s to under 100 ms.
Improvements
  • Renamed the Pints optimizer to AskTellOptimizer across all code, tests, and documentation.
  • Added dedicated algorithm test suites for CMA-ES, XNES, and Nelder-Mead.
  • Added distribution mean evaluation for CMA-ES and XNES, preferring the evaluated mean when it outperforms the best sample.
Fixes
  • Fixed a flaky deterministic design optimization test.
Improvements
  • Added filter and ordering keyword arguments to CellSpecificationClient.list(), CellInstanceClient.list(), and CellMeasurementClient.list().
  • download_files(filenames=) now skips the listing round-trip; new get_file() method for single-file fetch.
Improvements
  • Added temperature column recognition for Biologic files.