Objectives

Experiment-specific objective schemas. Mirrors ionworkspipeline.data_fits.objectives.

Schemas for objectives.

class ionworks_schema.objectives.BaseObjective(options=None, callbacks=None, custom_parameters=None, cost=None, constraints=None, penalties=None, parameters=None)

Bases: BaseSchema

Shared base for every objective. Not used directly.

Concrete subclasses (Pulse, EIS, CycleAgeing, MSMRHalfCell, DesignObjective, …) inherit from here. An objective tells the pipeline what to fit (or optimize) and which data to compare against — pick a concrete subclass that matches your experiment.

Parameters

optionsdict, optional

Objective-specific settings. The supported keys depend on the subclass — see each subclass’s docstring for the list.

callbacksCallback or list of Callback, optional

Callback(s) that run at various points during the fit (logging, plotting, early stopping).

costObjectiveFunction or str, optional

The cost function used to score the fit (e.g. RMSE, MAE, GaussianLogLikelihood). If None, the optimizer’s default cost is used.

constraintslist of Constraint, optional

Equality or inequality constraints that must hold during the fit.

penaltieslist of Penalty, optional

Soft penalties added to the cost.

parametersdict or pybamm.ParameterValues, optional

Parameter overrides applied only to this objective.

Extends: ionworks_schema.base.BaseSchema

options: dict[str, Any] | None
callbacks: Any | None
custom_parameters: dict[str, Any] | None
cost: Annotated[dict[str, Any] | BaseSchema | str | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])] | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.CalendarAgeing(data, options=None, callbacks=None, custom_parameters=None, constraints=None, penalties=None, parameters=None)

Bases: SimulationObjective

Fit degradation parameters (loss of lithium inventory, loss of active material) to calendar-ageing data for a full cell.

Parameters

data_inputDataLoader, DataFrame, str, or dict

The calendar-ageing data — see FittingObjective.

optionsdict, optional

Calendar-ageing settings:

  • model (required): the pybamm model to fit.

  • modes (list of str): which degradation modes to fit. Any

    combination of "LLI [%]", "LAM_ne [%]", "LAM_pe [%]". Defaults to ["LLI [%]"].

  • simulation_kwargs (dict): kwargs forwarded to the

    simulation. Defaults to None.

callbacks, constraints, penalties, parameters

Shared with BaseObjective.

Examples

>>> obj = iws.objectives.CalendarAgeing(
...     data_input="path/to/calendar.csv",
...     options={"modes": ["LLI [%]", "LAM_pe [%]"]},
... )
>>> # slot into a DataFit (parameters omitted for brevity)

Extends: ionworks_schema.objectives.objectives.SimulationObjective

data_input: str | DataFrame | DataFrame | DataLoader | dict[str, Any]
options: dict[str, Any] | None
callbacks: Any | None
custom_parameters: dict[str, Any] | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.CurrentDriven(data, options=None, callbacks=None, custom_parameters=None, constraints=None, penalties=None, parameters=None)

Bases: SimulationObjective

Fit a model against any current-driven experiment — pulse trains, drive cycles, custom load profiles.

Pass in the recorded current/voltage data along with the model and the objective will simulate the same current profile and compare voltages (or any other variable you ask for).

Parameters

data_inputDataLoader, DataFrame, str, or dict

The current/voltage data — see FittingObjective.

optionsdict, optional

Current-driven settings:

  • model (required): the pybamm model to fit.

  • independent variable (str): "time" (default) or

    "voltage". Use "voltage" when matching to a voltage trace makes more physical sense than matching to a time trace.

  • simulation_kwargs (dict): kwargs forwarded to the

    simulation. When no experiment is given, defaults to {"output_variables": ["Voltage [V]", "Current [A]"]}.

  • objective variables (list of str): which variables the fit

    compares. With "time", defaults to ["Voltage [V]"]. With "voltage", defaults to ["Time [s]"] and can’t be changed.

  • interpolant_atol / interpolant_rtol (float): tolerances

    for the current interpolant. Default to the solver’s atol/rtol if a solver is supplied in simulation_kwargs, otherwise 1e-6 and 1e-4.

  • interactive_preprocessing (bool): if True, adjusts

    interpolant_atol/rtol automatically from the data. Default False.

  • solver_max_save_points (int): cap on solver save points.

    Disabled by default.

callbacks, constraints, penalties, parameters

Shared with BaseObjective.

Examples

>>> obj = iws.objectives.CurrentDriven(
...     data_input="path/to/drive_cycle.csv",
...     options={"objective variables": ["Voltage [V]"]},
... )
>>> # slot into a DataFit (parameters omitted for brevity)

Extends: ionworks_schema.objectives.objectives.SimulationObjective

data_input: str | DataFrame | DataFrame | DataLoader | dict[str, Any]
options: dict[str, Any] | None
callbacks: Any | None
custom_parameters: dict[str, Any] | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.CycleAgeing(data, options=None, callbacks=None, custom_parameters=None, constraints=None, penalties=None, parameters=None)

Bases: SimulationObjective

Fit summary variables (capacity, resistance, LLI, LAM) against cell-cycling data.

Each row in the data is one summary measurement across a cycle (or a small group of cycles). The objective runs the requested cycling experiment, extracts the same summary variables from the simulation, and compares the two.

Parameters

data_inputDataLoader, DataFrame, str, or dict

The cycling summary data — see FittingObjective.

optionsdict, optional

Cycle-ageing settings:

  • model (required): the pybamm model to fit.

  • experiment (required): the pybamm.Experiment to simulate.

  • objective variables (required, list of str): which summary

    variables to compare. Must be a subset of the data columns.

  • metrics (dict): mapping from variable name to a

    .by_cycle() metric object that extracts that value from the simulation. Defaults are provided for "LLI [%]", "LAM_ne [%]", and "LAM_pe [%]".

  • simulation_kwargs (dict): kwargs forwarded to the

    simulation. Defaults to None.

callbacks, constraints, penalties, parameters

Shared with BaseObjective.

Examples

>>> obj = iws.objectives.CycleAgeing(
...     data_input="path/to/cycling_summary.csv",
...     options={"objective variables": ["Discharge capacity [A.h]"]},
... )
>>> # slot into a DataFit (parameters omitted for brevity)

Extends: ionworks_schema.objectives.objectives.SimulationObjective

data_input: str | DataFrame | DataFrame | DataLoader | dict[str, Any]
options: dict[str, Any] | None
callbacks: Any | None
custom_parameters: dict[str, Any] | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.DesignObjective(actions, constraints=None, options=None, callbacks=None, custom_parameters=None, cost=None, validate_against_experiment_steps=True, output_variables_full=None, save_at_cycles=None, penalties=None, parameters=None)

Bases: BaseObjective

Objective for design optimisation — maximise (or minimise) one or more cell metrics by adjusting design parameters.

You describe each design target as an Action wrapping a Metric (e.g. maximise energy density, hit a target fast-charge time) and pass them in via actions. The optimiser then searches the parameter space to find the design that best satisfies all of them, subject to any constraints you specify.

The objective handles simulation failures and edge-case parameter combinations automatically — failed simulations receive large penalties so the optimiser steers away from those regions.

Parameters

actionsdict[str, Any]

Mapping of action name to an Action-wrapped Metric defining each design target.

constraintsdict[str, BaseAction] or list of Constraint, optional

Hard limits on the design. For design optimization this may be either a dict of Action-wrapped metrics (e.g. minimum capacity) or a list of Constraint terms on pybamm symbols.

optionsdict, optional

Settings for the design simulation (model, experiment, simulation kwargs).

callbacksCallback or list of Callback, optional

Callback(s) invoked at various points during optimisation.

costObjectiveFunction or str, optional

Cost function used to combine the actions into a single design score. If None, the optimizer’s default design cost is used.

penaltieslist of Penalty, optional

Soft penalties added to the cost.

parametersdict or pybamm.ParameterValues, optional

Parameter overrides applied only to this objective.

Extends: ionworks_schema.objectives.objectives.BaseObjective

actions: dict[str, Any]
constraints: dict[str, Any] | list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
options: dict[str, Any] | None
callbacks: Any | None
custom_parameters: dict[str, Any] | None
cost: Annotated[dict[str, Any] | BaseSchema | str | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])] | None
validate_against_experiment_steps: bool
output_variables_full: list[str] | None
save_at_cycles: list[int] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
parameters: dict[str, Any] | ParameterValues | None
to_config() dict

Build the dict you submit through ionworks-api.

Same as the base to_config, with actions folded under the "options" key so the payload matches the structure the Ionworks API expects for cycling objectives.

model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.EIS(data, options=None, callbacks=None, custom_parameters=None, constraints=None, penalties=None, parameters=None)

Bases: FittingObjective

Fit a model against electrochemical impedance spectroscopy (EIS) data.

Simulates the model response at the supplied frequencies and compares the predicted impedance to the measured spectrum. Uses pybamm’s frequency-domain EIS simulator under the hood.

Parameters

data_inputDataLoader, DataFrame, str, or dict

The EIS data — see FittingObjective.

optionsdict, optional

EIS settings:

  • model (required): the pybamm model to fit.

  • simulation_kwargs (dict): kwargs forwarded to the EIS

    simulator. Defaults to None.

callbacks, constraints, penalties, parameters

Shared with BaseObjective.

Examples

>>> obj = iws.objectives.EIS(data_input="path/to/eis.csv")
>>> # slot into a DataFit (parameters omitted for brevity)

Extends: ionworks_schema.objectives.objectives.FittingObjective

data_input: str | DataFrame | DataFrame | DataLoader | dict[str, Any]
options: dict[str, Any] | None
callbacks: Any | None
custom_parameters: dict[str, Any] | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.ElectrodeBalancing(data, options=None, callbacks=None, custom_parameters=None, constraints=None, penalties=None, parameters=None)

Bases: FittingObjective

Find the electrode capacities and stoichiometry windows that best reconstruct a full-cell OCV curve from the underlying half-cell OCPs.

Parameters

data_inputDataLoader, DataFrame, str, or dict

The full-cell OCV data — see FittingObjective.

optionsdict, optional

Electrode-balancing settings:

  • dUdQ cutoff (float): ignore data points with very large

    dU/dQ (typically near the voltage limits). Default None.

  • direction (str): "charge" or "discharge" — direction

    of the OCV scan. Default None (no direction assumed).

callbacks, constraints, penalties, parameters

Shared with BaseObjective.

Examples

>>> obj = iws.objectives.ElectrodeBalancing(
...     data_input="path/to/full_cell_ocv.csv",
...     options={"direction": "discharge"},
... )
>>> # slot into a DataFit (parameters omitted for brevity)

Extends: ionworks_schema.objectives.objectives.FittingObjective

data_input: str | DataFrame | DataFrame | DataLoader | dict[str, Any]
options: dict[str, Any] | None
callbacks: Any | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
custom_parameters: dict[str, Any] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.ElectrodeBalancingHalfCell(electrode, data, options=None, callbacks=None, custom_parameters=None, constraints=None, penalties=None, parameters=None)

Bases: FittingObjective

Find the starting capacity and total capacity of one electrode, given a known OCV function for that electrode.

Useful for aligning a fresh experiment (for example a GITT scan) to a previously measured OCV curve — the fit slides and scales the data so it lines up with the OCV in stoichiometry.

Parameters

electrodestr

The electrode to fit — "positive" or "negative".

data_inputDataLoader, DataFrame, str, or dict

The half-cell data — see FittingObjective.

optionsdict, optional

Settings:

  • direction (str): "lithiation" or "delithiation"

    direction of the OCP. Default None (no direction assumed).

callbacks, constraints, penalties, parameters

Shared with BaseObjective.

Examples

>>> obj = iws.objectives.ElectrodeBalancingHalfCell(
...     electrode="positive",
...     data_input="path/to/half_cell.csv",
... )
>>> # slot into a DataFit (parameters omitted for brevity)

Extends: ionworks_schema.objectives.objectives.FittingObjective

electrode: Literal['positive', 'negative']
data_input: str | DataFrame | DataFrame | DataLoader | dict[str, Any]
options: dict[str, Any] | None
callbacks: Any | None
custom_parameters: dict[str, Any] | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.FittingObjective(data, options=None, callbacks=None, custom_parameters=None, cost=None, constraints=None, penalties=None, parameters=None)

Bases: BaseObjective

Shared base for objectives that fit a model to measured data.

On top of BaseObjective this adds data_input — the experimental data the model is compared against. Pick a concrete subclass that matches your experiment (Pulse, EIS, OCPHalfCell, CycleAgeing, …).

Parameters

data_inputDataLoader, DataFrame, str, or dict

The measured data. Accepts an ionworksdata.DataLoader (constructed directly from DataFrames, or via DataLoader.from_local(path) / DataLoader.from_db(measurement_id)), a path to a data file, a pandas or polars DataFrame, or a dict with keys data (DataFrame of raw values) and metadata (dict describing the experiment).

optionsdict, optional

Objective-specific settings — see each subclass for supported keys.

callbacksCallback or list of Callback, optional

Callback(s) invoked at various points during the fit.

costObjectiveFunction or str, optional

Cost function used to score the fit. If None, the optimizer’s default cost is used.

constraintslist of Constraint, optional

Equality or inequality constraints that must hold during the fit.

penaltieslist of Penalty, optional

Soft penalties added to the cost.

parametersdict or pybamm.ParameterValues, optional

Parameter overrides applied only to this objective.

Extends: ionworks_schema.objectives.objectives.BaseObjective

data_input: str | DataFrame | DataFrame | DataLoader | dict[str, Any]
options: dict[str, Any] | None
callbacks: Any | None
custom_parameters: dict[str, Any] | None
cost: Annotated[dict[str, Any] | BaseSchema | str | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])] | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.MSMRFullCell(data, options=None, callbacks=None, constraints=None, penalties=None, custom_parameters=None, parameters=None)

Bases: FittingObjective

Fit an MSMR model to full-cell open-circuit voltage data.

Compares both the capacity-vs-voltage curve and the differential voltage dU/dQ of the full cell — fitting both together gives much tighter constraints on the underlying half-cell MSMR parameters than fitting the OCV alone [1].

Parameters

data_inputDataLoader, DataFrame, str, or dict

The full-cell OCV data — see FittingObjective.

optionsdict, optional

MSMR full-cell settings:

  • model: the full-cell OCV model. Defaults to

    MSMRFullCellModel with both electrodes set to MSMRHalfCellModel using "Xj" species format.

  • dUdQ cutoff: clip data points with very large dU/dQ

    near the voltage limits. "none" disables it, a float sets a fixed cutoff, a function is called on the data to derive one. Defaults to an automatic cutoff function.

  • negative voltage limits (required): tuple of voltage

    limits for the negative electrode.

  • positive voltage limits (required): tuple of voltage

    limits for the positive electrode.

callbacks, constraints, penalties, parameters

Shared with BaseObjective.

References

Examples

>>> obj = iws.objectives.MSMRFullCell(
...     data_input="path/to/full_cell_ocv.csv",
...     options={
...         "negative voltage limits": (0.005, 1.5),
...         "positive voltage limits": (3.0, 4.3),
...     },
... )
>>> # slot into a DataFit (parameters omitted for brevity)

Extends: ionworks_schema.objectives.objectives.FittingObjective

data_input: str | DataFrame | DataFrame | DataLoader | dict[str, Any]
options: dict[str, Any] | None
callbacks: Any | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
custom_parameters: dict[str, Any] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.MSMRHalfCell(data, options=None, callbacks=None, constraints=None, penalties=None, custom_parameters=None, parameters=None)

Bases: FittingObjective

Fit an MSMR model to half-cell open-circuit potential data.

Compares the capacity-vs-voltage curve and (optionally) the differential voltage dU/dQ of one electrode to a measured half-cell OCP.

Parameters

data_inputDataLoader, DataFrame, str, or dict

The half-cell OCP data — see FittingObjective.

optionsdict, optional

MSMR half-cell settings:

  • model: the half-cell OCP model. Defaults to

    MSMRHalfCellModel for the specified electrode. The model class controls the species format, direction and capacity function.

  • voltage limits: tuple of voltage limits to fit over.

    Defaults to the range present in the data.

  • dUdQ cutoff: clip data points with very large dU/dQ

    near the voltage limits. "none" disables it, a float sets a fixed cutoff, a function is called on the data to derive one. Defaults to an automatic cutoff function.

  • dQdU cutoff: same idea as dUdQ cutoff but on

    dQ/dU. Defaults to None.

  • objective variables (list of str): variables compared by

    the fit. Defaults to ["Capacity [A.h]", "Differential voltage [V/Ah]"].

  • GITT (bool): set True for sparse GITT data so it can

    be interpolated and upsampled first. Default False.

  • constrain Xj method: how to constrain the Xj parameters to

    sum to 1 — "explicit" (adds an equality constraint) or "reformulate" (eliminates the last Xj by substitution). Default "explicit". Only used with the "Xj" species format.

  • preserve U0j order (bool): if True, add inequality

    constraints to keep the U0j parameters in descending order. Default False — initial values must already be in descending order if this is enabled.

callbacks, constraints, penalties, parameters

Shared with BaseObjective.

Examples

>>> obj = iws.objectives.MSMRHalfCell(
...     data_input="path/to/half_cell.csv",
...     options={"electrode": "positive"},
... )
>>> # slot into a DataFit (parameters omitted for brevity)

Extends: ionworks_schema.objectives.objectives.FittingObjective

data_input: str | DataFrame | DataFrame | DataLoader | dict[str, Any]
options: dict[str, Any] | None
callbacks: Any | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
custom_parameters: dict[str, Any] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.OCPHalfCell(electrode, data, options=None, callbacks=None, custom_parameters=None, constraints=None, penalties=None, parameters=None)

Bases: FittingObjective

Fit a smooth open-circuit-potential function to half-cell OCP data.

Parameters

electrodestr

The electrode to fit — "positive" or "negative".

data_inputDataLoader, DataFrame, str, or dict

The half-cell OCP data — see FittingObjective.

optionsdict, optional

OCP fitting settings:

  • theta_ref (float): reference lithiation used to map

    between capacity and stoichiometry theta.

    For the positive electrode the reference defaults to 1 at the lowest potential in the data (we assume the electrode is fully lithiated there), giving 0 <= theta <= theta_ref.

    For the negative electrode the reference defaults to 0 at the highest potential in the data (we assume the electrode is fully delithiated there), giving theta_ref <= theta <= 1.

  • stoichiometry limits (2-tuple of floats): stoichiometry

    range used by the fit. Default (0, 1) — the whole range. If the fit struggles near the endpoints, narrow this to e.g. (0.02, 0.98) or (0.05, 0.95).

  • voltage limits (2-tuple of floats): voltage range used by

    the fit. Default None (uses the full data range).

  • dUdQ cutoff (float): ignore data points with very large

    dU/dQ near the limits. Default None.

  • direction (str): "lithiation" or "delithiation"

    direction of the OCP. Default None (no direction assumed).

callbacks, constraints, penalties, parameters

Shared with BaseObjective.

Examples

>>> obj = iws.objectives.OCPHalfCell(
...     electrode="positive",
...     data_input="path/to/ocp.csv",
...     options={"stoichiometry limits": (0.05, 0.95)},
... )
>>> # slot into a DataFit (parameters omitted for brevity)

Extends: ionworks_schema.objectives.objectives.FittingObjective

electrode: Literal['positive', 'negative']
data_input: str | DataFrame | DataFrame | DataLoader | dict[str, Any]
options: dict[str, Any] | None
callbacks: Any | None
custom_parameters: dict[str, Any] | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.Objective(data, options=None, callbacks=None, custom_parameters=None, cost=None, constraints=None, penalties=None, parameters=None)

Bases: FittingObjective

Deprecated alias for FittingObjective.

Extends: ionworks_schema.objectives.objectives.FittingObjective

model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.Pulse(data, options=None, callbacks=None, custom_parameters=None, constraints=None, penalties=None, parameters=None)

Bases: SimulationObjective

Fit a model against pulse experiments — GITT, HPPC, ICI, and similar.

By default the fit compares simulated voltage against measured voltage, but you can swap in a different objective variables function to compare overpotentials, resistances, or ICI/GITT features instead.

Parameters

data_inputDataLoader, DataFrame, str, or dict

The pulse data — see FittingObjective.

optionsdict, optional

Pulse-fitting settings:

  • model (required): the pybamm model to fit.

  • simulation_kwargs (dict): kwargs forwarded to the

    simulation. Defaults to None.

  • objective variables (callable): function returning the

    dict of variables the fit compares. Defaults to voltage_objective_variables (compares voltage). Other useful options:

    • overpotential_objective_variables — overpotential

      sampled at various points in the pulses.

    • resistances_objective_variables — resistance

      sampled at various points in the pulses.

    • ici_features_objective_variables — ICI features

      (concentration overpotential and ICI square-root slope).

    • gitt_features_objective_variables — GITT features

      (concentration overpotential, relaxation time, ohmic voltage drop, GITT and ICI square-root slopes).

  • interpolant_atol / interpolant_rtol (float): tolerances

    for the current interpolant. Default to the solver’s atol/rtol if a solver is supplied in simulation_kwargs, otherwise 1e-6 and 1e-4.

  • interactive_preprocessing (bool): if True, adjusts

    interpolant_atol/rtol automatically from the data. Default False.

  • solver_max_save_points (int): cap on solver save points.

    Disabled by default.

callbacks, constraints, penalties, parameters

Shared with BaseObjective.

Examples

>>> obj = iws.objectives.Pulse(data_input="path/to/gitt.csv")
>>> # slot into a DataFit (parameters omitted for brevity)

Extends: ionworks_schema.objectives.objectives.SimulationObjective

data_input: str | DataFrame | DataFrame | DataLoader | dict[str, Any]
options: dict[str, Any] | None
callbacks: Any | None
custom_parameters: dict[str, Any] | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.Resistance(data, options=None, callbacks=None, custom_parameters=None, constraints=None, penalties=None, parameters=None)

Bases: FittingObjective

Fit a cell-resistance curve against measured resistance vs SOC.

Parameters

data_inputDataLoader, DataFrame, str, or dict

Resistance data — see FittingObjective. Must contain columns "SOC" and "Resistance [Ohm]", plus any extra columns the resistance model needs.

optionsdict, optional

Resistance-fit settings:

  • model (required): the pybamm model to fit.

callbacks, constraints, penalties, parameters

Shared with BaseObjective.

Examples

>>> obj = iws.objectives.Resistance(data_input="path/to/resistance.csv")
>>> # slot into a DataFit (parameters omitted for brevity)

Extends: ionworks_schema.objectives.objectives.FittingObjective

data_input: str | DataFrame | DataFrame | DataLoader | dict[str, Any]
options: dict[str, Any] | None
callbacks: Any | None
custom_parameters: dict[str, Any] | None
constraints: list[Annotated[dict[str, Any] | Constraint | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
penalties: list[Annotated[dict[str, Any] | Penalty | Any, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]] | None
parameters: dict[str, Any] | ParameterValues | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks_schema.objectives.SimulationObjective(data, options=None, callbacks=None, custom_parameters=None, cost=None, constraints=None, penalties=None, parameters=None)

Bases: FittingObjective

Shared base for objectives that run a pybamm simulation and compare the output to measured data.

Subclasses such as Pulse, CurrentDriven, CalendarAgeing, and CycleAgeing inherit from here. The arguments are the same as FittingObjective — pick a concrete subclass that matches your experiment.

Extends: ionworks_schema.objectives.objectives.FittingObjective

model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.