iws.DataFit describes a parameter fit: which experiments to compare against, which parameters are free, and how to search. The schema is submitted as one element of a pipeline. For the theory (cost functions, identifiability, multi-start), see the Data Fitting Guide.
A minimal fit
Configuration mistakes inside a
DataFit (bad parameter names, malformed objectives, …) surface as UserConfigurationError. The job classifier maps these to a Configuration error in Studio so they’re easy to distinguish from solver failures.Multiple objectives
Pass multiple objectives to fit against several experiments simultaneously (e.g. discharge at different C-rates or temperatures):Optimizers
iws.optimizers exposes the optimisers available to DataFit. Pick the one that fits your problem:
See Objective Functions for the cost-function options.
The surrogate optimisers (They are imported lazily, so installs that only use the population-based or SciPy optimisers do not pay this dependency cost.
BayesianOptimization, TuRBO, SOBER) require the optional surrogate install extra, which adds torch, botorch, and gpytorch:TuRBO for expensive parallel problems
When each evaluation is expensive and you have workers to spare,TuRBO proposes a batch of candidates per round and adapts a trust region around the current best point. Worker count is now owned by the execution engine (see the note below), so size the warm-up (n_initial) to the capacity you give DataFit.run(execution=ExecutionConfig(...)) — that way the first round fully uses the available workers:
DataFit no longer takes parallel, num_workers, or max_batch_size (and AskTellOptimizer no longer takes async_mode). Parallelism is owned by the execution engine and is configured at run time — pass an ExecutionConfig to DataFit.run(execution=...). Stored configs that still carry the removed fields are migrated automatically at parse time, so no action is required for existing saved fits.algorithm_options keys for surrogate optimisers include n_initial (warm-up sample count), noise_floor ("low", "standard", or a (lo, hi) interval), and — for TuRBO — trust-region controls (tr_length_init, tr_length_min, tr_length_max, tr_success_tolerance, tr_failure_tolerance, n_candidates).
Strict option validation
algorithm_options is validated against the optimiser you chose. Unknown keys — including typos — are rejected at submission time rather than silently ignored, so misconfigured fits fail fast instead of running with default behaviour:
CMAESOptions, PSOOptions, DEOptions, XNESOptions — for the XNES optimizer, available via iws.optimizers.XNES() / AskTellOptimizer(method="XNES") — BayesianOptimizationOptions, SOBEROptions, TuRBOOptions). Prefer the typed wrappers for editor autocomplete and inline documentation of each option. The only exception is CMAESOptions, which remains a passthrough to pycma’s own option surface.
No SciPy-style kwargs on native optimizers
Native ask/tell optimizers (CMAES, DifferentialEvolution, PSO, XNES, BayesianOptimization, TuRBO, SOBER, and the underlying AskTellOptimizer) also reject unknown top-level keyword arguments at construction. SciPy-style keys such as maxiter, popsize, seed, and tol are not accepted — they previously had no effect and now fail validation immediately, so misconfigured fits surface at construction rather than silently:
ScipyMinimize, ScipyLeastSquares, ScipyDifferentialEvolution), which forward them directly to the underlying SciPy call:
max_iterations, population_size, population_convergence_tol); put algorithm internals in algorithm_options; and keep SciPy keywords on the Scipy* optimizers.
Strict objective validation
DataFit.objectives (and Validation.objectives) validates each entry against a discriminated union keyed on the objective’s type. Configuration mistakes are rejected at submission time with a clear ValidationError, instead of being silently ignored or surfacing later as an opaque runtime crash.
Objective instances (e.g. iws.objectives.CurrentDriven(...)) keep working unchanged — both the positional and keyword constructors are preserved. The strict-validation rules apply when you pass raw config dicts, which is common for configs loaded from JSON or produced by to_config():
In a raw config dict the data key is
data, not data_input. The Python constructor exposes it as the data_input keyword argument, but to_config() serialises it to data — so hand-written dicts and JSON configs must use data. Passing data_input in a dict is now rejected as an unknown key.DesignObjective configs are not accepted on the DataFit runtime path: they run on the separate design-optimization pipeline. Passing a DesignObjective dict to DataFit.from_schema raises a UserConfigurationError pointing you at the design-optimization pipeline instead of crashing later.
Multi-start
For problems with multiple local minima, run several optimisations from different starting points:Choosing the initial-guess sampler
By default multi-start uses Latin Hypercube sampling, which spreads initial guesses evenly across the parameter bounds. Override the sampler withinitial_guess_sampler when you want a different sampling scheme — for example, plain uniform sampling for a baseline comparison:
The sampler is validated against a discriminated union at submission time: passing an unknown sampler
type or an unrecognised key raises a ValidationError immediately instead of failing later in the run.
Nested (variable-projection) fits
iws.optimizers.Nested is a bi-level optimizer: an outer optimizer searches over a chosen subset of parameters, and for every outer trial point an inner optimizer is run to optimality over the remaining parameters. This is the variable-projection formulation — the outer sees a concentrated objective and the inner absorbs the parameters that are cheap to fit conditionally.
Use Nested when:
- One subset of parameters enters the model linearly (or near-linearly) and can be fit in closed form with a bounded least-squares inner solve, while the rest are nonlinear.
- The full joint search is ill-conditioned or slow, but the reduced outer problem is well-behaved.
- You want a global outer search (e.g. CMA-ES or
Nelder-Mead) with a fast local inner refinement at every point.
parameters lists the fit-parameter names this level’s outer optimizer controls; every other free parameter in the DataFit is delegated to inner. inner can itself be a Nested for deeper nesting.
The concentrated objective can be non-smooth where the inner optimum is non-unique, so derivative-free or finite-difference outer optimizers (
Nelder-Mead, CMAES, DifferentialEvolution) are recommended for the outer slot. Samplers are rejected at construction — both slots must be optimizers.Bounded linear inner solves with ScipyLsqLinear
When the inner parameters enter the residual linearly (for example, a linear combination of basis functions with bounded coefficients), pair the Nested estimator with iws.optimizers.ScipyLsqLinear as the inner solver. It reaches the bounded optimum in a single scipy.optimize.lsq_linear call rather than iterating with Gauss-Newton, and warm-starts its active set across successive outer evaluations so the concentrated objective stays smooth:
ScipyLsqLinear only when the residual really is linear in the inner parameters; for nonlinear inner residuals, keep ScipyLeastSquares.
Analytic parameter Jacobian
When the outer or inner slot is a least-squares optimizer (ScipyLeastSquares or ScipyLsqLinear), the pipeline supplies an analytic residual Jacobian built from the model’s parameter sensitivities. That replaces SciPy’s finite-difference Jacobian, so each iteration takes one solve plus one sensitivity evaluation instead of solves. In practice this makes gradient-based least-squares fits substantially faster and more robust to noisy finite-difference steps, especially inside a Nested variable-projection loop.
No configuration is required — the analytic Jacobian is used automatically when the objective and model support it, and the fit transparently falls back to finite differences otherwise. Look for using analytic residual Jacobian in the fit logs to confirm it’s active.
Runtime options
iws.DataFit accepts an options dict that tunes the optimisation loop without changing the schema. All keys are optional.
Pipelines submitted to the Ionworks cluster enable
skip_objective_callbacks by default to reduce simulation cost. Set it explicitly to False in options if you need the initial- and final-fit simulation results returned with the run.Objective-level parallelism
DataFit exposes objective_parallelism as a top-level field — a debugging escape hatch for objective-level scheduling. It is a direct DataFit argument, not a Runtime options key:
"auto" unless you have a specific reason to override. The setting only affects scheduling — it does not change the cost the optimizer sees.
Troubleshooting model failures
If an objective’s model can’t be set up or evaluated during a fit — for example because the parameter set is incomplete, a custom model can’t be discretised, or a state-of-charge initialisation fails — the pipeline raises aModelError that names the offending objective and the underlying cause:
ModelError is distinct from a generic CONFIGURATION_ERROR: the failed job is tagged with the MODEL_ERROR code so the message can be shown to you directly without being routed through internal error monitoring. The fix is almost always to your fit configuration — supply the missing parameter, widen unrealistic bounds, or adjust the model options on the objective — rather than something to report.
Already-clear errors pass through unchanged: solver-side numerical failures still surface as pybamm.SolverError / SOLVER_ERROR, and parameter-lookup failures still surface as ParameterNotFoundError. Those continue to be handled by the optimizer’s normal fallback (NaN cost, skipped step) when they occur inside an evaluation, so a single bad iteration won’t kill the whole fit.
Retrieving results
result.element_results["fit"] is a dict keyed by the data-fit’s outputs (best parameter values, final cost, and any logged trajectories). See packages/ionworks-api/examples/pipeline/datafit.py for an end-to-end example.
Data Fitting (theory)
Cost-function math, identifiability, multi-start strategy.
Objective Functions
Pick the right cost for your data shape.
Regularization
Stabilise fits with Gaussian priors.
Sensitivity Analysis
Quantify which parameters the fit actually constrains.