Calculations

Derived-parameter calculation schemas.

Schemas for calculations.

class ionworks.pipeline.calculations.AreaToSquareWidthHeight(source=None)[source]

Bases: Calculation

Calculate electrode height and width from area assuming square geometry.

Reads "Electrode area [m2]" and produces "Electrode height [m]" and "Electrode width [m]" as sqrt(area). Useful when only total electrode area is known.

Examples

>>> calc = iw.pipeline.calculations.AreaToSquareWidthHeight()
>>> config = iw.pipeline.Pipeline({"geometry": calc}).to_config()
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ArrheniusDiffusivityFromMSMRData(electrode, data, direction='', phase='', options=None)[source]

Bases: Calculation

Calculate the diffusivity from OCP data as

\[D = - \frac{F}{RT_{ref}} D_{ref} x (1-x) \frac{dU}{dx} \exp(E / R (1 / T_{ref} - 1 / T))\]

where \(D_{ref}\) is the reference diffusivity, \(x\) is the stoichiometry, \(U\) is the open-circuit potential, \(R\) is the gas constant, \(F\) is Faraday’s constant, \(T\) is the temperature, \(T_{ref}\) is the reference temperature, and \(E\) is the activation energy. This formula is derived from the transport equation in the MSMR model.

This calculation leaves the following parameter to be defined (e.g. by fitting to data):

  • Negative/Positive particle reference diffusivity [m2.s-1] ($D_{ref}$)

Parameters:
  • electrode (str) – The electrode to calculate the diffusivity for (either “positive” or “negative”).

  • data (DataLoader, DataFrame, str, or dict) –

    OCP data with the following columns:
    • ”Stoichiometry” : array

    • ”Voltage [V]” : array

  • direction (str, optional) – Specifies lithiation or delithiation for hysteresis models. Default is an empty string.

  • phase (str, optional) – Specifies the phase (“primary” or “secondary”) for composite electrode models. Default is an empty string.

  • options (MSMRDiffusivityOptions or dict, optional) – See MSMRDiffusivityOptions for the available keys.

electrode: Literal['positive', 'negative', 'lumped']
data: Annotated[str | DataFrame | DataFrame | Annotated[object, PlainValidator(func=_as_loader, json_schema_input_type=Any)] | TimeSeriesSpec | DataPayloadSpec | MetadataSpec | dict[str, Any], BeforeValidator(func=_route_measurement_input, json_schema_input_type=PydanticUndefined)]
direction: Literal['', 'delithiation', 'lithiation']
phase: Literal['', 'primary', 'secondary']
options: MSMRDiffusivityOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ArrheniusDiffusivityFromMSMRFunction(electrode, voltage_limits, direction='', phase='', options=None)[source]

Bases: Calculation

Calculate the diffusivity from an OCP function as

\[D = - \frac{F}{RT_{ref}} D_{ref} x (1-x) \frac{dU}{dx} \exp(E / R (1 / T_{ref} - 1 / T))\]

where \(D_{ref}\) is the reference diffusivity, \(x\) is the stoichiometry, \(U\) is the open-circuit potential, \(R\) is the gas constant, \(F\) is Faraday’s constant, \(T\) is the temperature, \(T_{ref}\) is the reference temperature, and \(E\) is the activation energy. This formula is derived from the transport equation in the MSMR model.

This calculation leaves the following parameter to be defined (e.g. by fitting to data):

  • Negative/Positive particle reference diffusivity [m2.s-1] ($D_{ref}$)

Parameters:
  • electrode (str) – The electrode to calculate the diffusivity for (either “positive” or “negative”).

  • voltage_limits (tuple[float, float]) – The voltage limits to use for the OCP data.

  • direction (str, optional) – Specifies lithiation or delithiation for hysteresis models. Default is an empty string.

  • phase (str, optional) – Specifies the phase (“primary” or “secondary”) for composite electrode models. Default is an empty string.

  • options (MSMRDiffusivityOptions or dict, optional) – See MSMRDiffusivityOptions for the available keys.

electrode: Literal['positive', 'negative', 'lumped']
voltage_limits: tuple[int | float, int | float] | list[int | float]
direction: Literal['', 'delithiation', 'lithiation']
phase: Literal['', 'primary', 'secondary']
options: MSMRDiffusivityOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ArrheniusLogLinear(data, reference_temperature=None, include_func=False)[source]

Bases: Calculation

Fit an Arrhenius (log-linear) temperature dependence to a quantity measured at several temperatures.

Parameters:
  • data (DataLoader, DataFrame, str, or dict) – Measured values at each temperature.

  • reference_temperature (float, optional) – Reference temperature (in K) used to anchor the Arrhenius fit. If None, a default is chosen from the data.

  • include_func (bool, optional) – If True, also output an interpolant function so the value can be evaluated at any temperature, not just the measured ones. Default False.

Examples

>>> calc = iw.pipeline.calculations.ArrheniusLogLinear(data="path/to/temperature_sweep.csv")
>>> config = iw.pipeline.Pipeline({"arrhenius": calc}).to_config()
data: Annotated[str | DataFrame | DataFrame | Annotated[object, PlainValidator(func=_as_loader, json_schema_input_type=Any)] | TimeSeriesSpec | DataPayloadSpec | MetadataSpec | dict[str, Any], BeforeValidator(func=_route_measurement_input, json_schema_input_type=PydanticUndefined)]
reference_temperature: int | float | None
include_func: bool
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.AverageMSMRParameters(electrode, options=None)[source]

Bases: Calculation

Average the MSMR species parameters across the lithiation and delithiation directions for one electrode.

Useful when you’ve fit the MSMR parameters separately in each direction (to capture hysteresis) but now want a single, direction-independent parameter set for downstream simulations.

Parameters:

Examples

>>> calc = iw.pipeline.calculations.AverageMSMRParameters(electrode="positive")
>>> config = iw.pipeline.Pipeline({"average": calc}).to_config()
electrode: Literal['positive', 'negative', 'lumped']
options: AverageMSMRParametersOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.AverageMSMRParametersOptions(*, species_format: Literal['Xj', 'Qj'] = 'Xj', phase: Literal['primary', 'secondary'] | None = None)[source]

Bases: _CalculationOptions

Options for AverageMSMRParameters.

species_format: Literal['Xj', 'Qj']
phase: Literal['primary', 'secondary'] | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.Calculation(source=None)[source]

Bases: BaseSchema

A pipeline step that derives one or more parameters from others without fitting to data.

A calculation reads existing parameters, applies an algebraic or numerical recipe, and writes new parameters back into the parameter set. Typical uses are computing geometric properties (volume, surface area), capacity-related quantities, stoichiometry windows, or concentrations.

Parameters:

source (str, optional) – Free-text note describing where the calculation method comes from (paper citation, algorithm description). Optional and only used by some subclasses.

source: str | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.CellMass(model_options=None)[source]

Bases: Calculation

Compute the total cell mass in kilograms by summing the masses of the electrodes, separator, and current collectors from their densities, porosities, and thicknesses.

Adds Cell mass [kg] to the parameter set so downstream calculations (e.g. specific heat capacity, gravimetric energy density) can use it.

Parameters:
  • model_options (dict, optional) – The pybamm model options — needed only when the cell has composite electrodes so the right phase masses get summed.

  • credit (Initial)

Examples

>>> calc = iw.pipeline.calculations.CellMass()
>>> config = iw.pipeline.Pipeline({"cell_mass": calc}).to_config()
model_options: dict[str, Any] | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.CyclableLithium(method='electrode capacities', options=None)[source]

Bases: Calculation

Cyclable lithium capacity, from electrode capacities and initial stoichiometries (“electrode capacities”) or from the positive electrode capacity and a formation loss (“formation loss”).

The latter assumes all lithium starts in the fully lithiated positive electrode (the negative fully delithiated) and a fixed fraction, f, is lost in formation:

\[Q_Li = f * Q_p\]

where f is “Formation lithium loss” and Q_p is “Positive electrode capacity [A.h]”.

Parameters:
  • method (str, optional) – The method to use to calculate the cyclable lithium capacity. Either “electrode capacities” or “formation loss”. Default is “electrode capacities”.

  • options (CyclableLithiumOptions or dict, optional) – See CyclableLithiumOptions for the available keys.

Examples

>>> calc = iw.pipeline.calculations.CyclableLithium(method="electrode capacities")
>>> config = iw.pipeline.Pipeline({"cyclable_li": calc}).to_config()
method: Literal['electrode capacities', 'formation loss']
options: CyclableLithiumOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.CyclableLithiumOptions(*, particle_phases: tuple[Literal['1', '2'], Literal['1', '2']] = ('1', '1'))[source]

Bases: _CalculationOptions

Options for CyclableLithium.

particle_phases: tuple[Literal['1', '2'], Literal['1', '2']]
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.DensityFromVolumeAndMass(source=None)[source]

Bases: Calculation

Calculate the density from the mass and volume.

Examples

>>> calc = iw.pipeline.calculations.DensityFromVolumeAndMass()
>>> config = iw.pipeline.Pipeline({"density": calc}).to_config()
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.DiameterToSquareWidthHeight(source=None)[source]

Bases: Calculation

Sets the electrode height and width to be the square root of the electrode cross-sectional area, calculated from the diameter (for a coin cell).

Examples

>>> calc = iw.pipeline.calculations.DiameterToSquareWidthHeight()
>>> config = iw.pipeline.Pipeline({"geometry": calc}).to_config()
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.DiffusivityDataInterpolant(electrode, data, direction='', phase='', options=None)[source]

Bases: Calculation

A pipeline element that creates an interpolant for the diffusivity from an array of diffusivity data. This interpolant can be used to calculate the diffusivity at any point within the range of the data.

Parameters:
  • electrode (str) – The electrode to create the interpolant for (either “positive” or “negative”).

  • data (DataLoader, DataFrame, str, or dict) –

    Diffusivity data with the following columns:

    • ”Stoichiometry”array

      The stoichiometry values.

    • ”Diffusivity [m2.s-1]”array

      The corresponding diffusivity values.

  • direction (str, optional) – Specifies lithiation or delithiation for hysteresis models. Default is an empty string.

  • phase (str, optional) – Specifies the phase (“primary” or “secondary”) for composite electrode models. Default is an empty string.

  • options (DiffusivityDataInterpolantOptions or dict, optional) – See DiffusivityDataInterpolantOptions for the available keys.

electrode: Literal['positive', 'negative', 'lumped']
data: Annotated[str | DataFrame | DataFrame | Annotated[object, PlainValidator(func=_as_loader, json_schema_input_type=Any)] | TimeSeriesSpec | DataPayloadSpec | MetadataSpec | dict[str, Any], BeforeValidator(func=_route_measurement_input, json_schema_input_type=PydanticUndefined)]
direction: Literal['', 'delithiation', 'lithiation']
phase: Literal['', 'primary', 'secondary']
options: DiffusivityDataInterpolantOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.DiffusivityDataInterpolantOptions(*, interpolator: Literal['linear', 'cubic', 'pchip'] = 'linear', transformation: Literal['none', 'log'] = 'none', scale_factor: bool = False)[source]

Bases: _CalculationOptions

Options for DiffusivityDataInterpolant.

interpolator: Literal['linear', 'cubic', 'pchip']
transformation: Literal['none', 'log']
scale_factor: bool
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.DiffusivityFromMSMRData(electrode, data, direction='', phase='', options=None)[source]

Bases: Calculation

Calculate the diffusivity from OCP data as

\[D = - \frac{F}{RT} D_{ref} x (1-x) \frac{dU}{dx}\]

where \(D_{ref}\) is the reference diffusivity, \(x\) is the stoichiometry, \(U\) is the open-circuit potential, \(R\) is the gas constant, \(F\) is Faraday’s constant, and \(T\) is the temperature. This formula is derived from the transport equation in the MSMR model.

This calculation leaves the following parameter to be defined (e.g. by fitting to data):

  • Negative/Positive particle reference diffusivity [m2.s-1] ($D_{ref}$)

Parameters:
  • electrode (str) – The electrode to calculate the diffusivity for (either “positive” or “negative”).

  • data (DataLoader, DataFrame, str, or dict) –

    OCP data with the following columns:
    • ”Stoichiometry” : array

    • ”Voltage [V]” : array

  • direction (str, optional) – Specifies lithiation or delithiation for hysteresis models. Default is an empty string.

  • phase (str, optional) – Specifies the phase (“primary” or “secondary”) for composite electrode models. Default is an empty string.

  • options (MSMRDiffusivityOptions or dict, optional) – See MSMRDiffusivityOptions for the available keys.

electrode: Literal['positive', 'negative', 'lumped']
data: Annotated[str | DataFrame | DataFrame | Annotated[object, PlainValidator(func=_as_loader, json_schema_input_type=Any)] | TimeSeriesSpec | DataPayloadSpec | MetadataSpec | dict[str, Any], BeforeValidator(func=_route_measurement_input, json_schema_input_type=PydanticUndefined)]
direction: Literal['', 'delithiation', 'lithiation']
phase: Literal['', 'primary', 'secondary']
options: MSMRDiffusivityOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.DiffusivityFromMSMRFunction(electrode, voltage_limits, direction='', phase='', options=None)[source]

Bases: Calculation

Calculate the diffusivity from an OCP function as

\[D = - \frac{F}{RT} D_{{ref}} x (1-x) \frac{dU}{dx}\]

where \(D_{ref}\) is the reference diffusivity, \(U\) is the open-circuit potential, \(R\) is the gas constant, \(F\) is Faraday’s constant, and \(T\) is the temperature. This formula is derived from the transport equation in the MSMR model.

This calculation leaves the following parameter to be defined (e.g. by fitting to data):

  • Negative/Positive particle reference diffusivity [m2.s-1] ($D_{ref}$)

Parameters:
  • electrode (str) – The electrode to calculate the diffusivity for (either “positive” or “negative”).

  • voltage_limits (tuple[float, float]) – The voltage limits to use for the OCP data.

  • direction (str, optional) – Specifies lithiation or delithiation for hysteresis models. Default is an empty string.

  • phase (str, optional) – Specifies the phase (“primary” or “secondary”) for composite electrode models. Default is an empty string.

  • options (MSMRDiffusivityOptions or dict, optional) – See MSMRDiffusivityOptions for the available keys.

electrode: Literal['positive', 'negative', 'lumped']
voltage_limits: tuple[int | float, int | float] | list[int | float]
direction: Literal['', 'delithiation', 'lithiation']
phase: Literal['', 'primary', 'secondary']
options: MSMRDiffusivityOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.DiffusivityFromPulse(electrode, data, direction='', phase='', options=None)[source]

Bases: Calculation

A pipeline element that calculates the diffusion coefficient from pulse data by directly using the voltage data from the pulse, without the need for fitting a model. This method uses eqn 26 from Wang et al. [Wang2022] to calculate the diffusion coefficient. Advantages of this method are that it is simple and fast. Disadvantages are that the calculation relies on assumptions that may not be valid in all cases, and the results may be sensitive to noise in the data since we only use 4 data points to calculate the diffusion coefficient.

Parameters:
  • electrode (str) – The electrode to calculate the diffusivity for (either “positive” or “negative”).

  • data (DataLoader, DataFrame, str, or dict) – The pulse data. Must include columns "Current [A]", "Voltage [V]" and "Step number".

  • direction (str, optional) – Specifies lithiation or delithiation for hysteresis models. Default is an empty string.

  • phase (str, optional) – Specifies the phase (“primary” or “secondary”) for composite electrode models. Default is an empty string.

  • options (DiffusivityFromPulseOptions or dict, optional) – See DiffusivityFromPulseOptions for the available keys.

Raises:

ValueError – If no rest step is found after the pulse.

References

[Wang2022]

Wang, A. A., et al. “Review of parameterisation and a novel database (LiionDB) for continuum Li-ion battery models.” Progress in Energy 4.3 (2022): 032004.

electrode: Literal['positive', 'negative', 'lumped']
data: Annotated[str | DataFrame | DataFrame | Annotated[object, PlainValidator(func=_as_loader, json_schema_input_type=Any)] | TimeSeriesSpec | DataPayloadSpec | MetadataSpec | dict[str, Any], BeforeValidator(func=_route_measurement_input, json_schema_input_type=PydanticUndefined)]
direction: Literal['', 'delithiation', 'lithiation']
phase: Literal['', 'primary', 'secondary']
options: DiffusivityFromPulseOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.DiffusivityFromPulseOptions(*, step_number: int | None = None, dt_IR: float | None = None, dt_pulse: float | None = None)[source]

Bases: _CalculationOptions

Options for DiffusivityFromPulse.

step_number: int | None
dt_ir: float | None
dt_pulse: float | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ElectrodeBalanceFromFirstCycleEfficiency(options=None)[source]

Bases: Calculation

Stoichiometry limits and initial concentrations after formation.

\[Q_{Li} = \eta Q_p\]

with \(\eta\) the “First cycle efficiency” and \(Q_p\) the positive electrode’s host capacity, summed over phases, so the loss follows the cathode loading. Limits and 100% SOC initial concentrations are per phase.

Parameters:

options (ElectrodeBalanceFromFirstCycleEfficiencyOptions or dict, optional) – See ElectrodeBalanceFromFirstCycleEfficiencyOptions.

See also

StoichiometryLimitsFromCapacity

for fitted capacities.

Examples

>>> calc = iw.pipeline.calculations.ElectrodeBalanceFromFirstCycleEfficiency(
...     options={"particle phases": ("2", "1")}
... )
>>> config = iw.pipeline.Pipeline({"balance": calc}).to_config()
options: ElectrodeBalanceFromFirstCycleEfficiencyOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ElectrodeBalanceFromFirstCycleEfficiencyOptions(*, particle_phases: tuple[Literal['1', '2'], Literal['1', '2']] = ('1', '1'), open_circuit_potential: str | tuple[str, str] | tuple[str | tuple[str, str], str | tuple[str, str]] = 'single')[source]

Bases: _CalculationOptions

Options for ElectrodeBalanceFromFirstCycleEfficiency.

particle_phases: tuple[Literal['1', '2'], Literal['1', '2']]
open_circuit_potential: str | tuple[str, str] | tuple[str | tuple[str, str], str | tuple[str, str]]
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ElectrodeCapacity(electrode, use_stoich_window=False, method='auto', phase=None)[source]

Bases: Calculation

Solve the electrode capacity equation for its one unknown, picking the method.

c_max * eps * L * A_cc * (stoich_max - stoich_min) * F / 3600 = capacity, with capacity given directly, as loading times area, or from the crystal density, Li fraction per mole and molecular mass of the active material. All but one of c_max, eps, L, A_cc and the stoichiometry limits must be provided (the limits only when use_stoich_window is True).

Parameters:
  • electrode (str) – Electrode to calculate capacity for: “positive” or “negative”.

  • use_stoich_window (bool, default=False) – Whether to use stoichiometry limits in the calculation. Set True if capacity is based on a voltage window (e.g., 0-100% SOC from RPT). Set False if capacity represents the full material limit (e.g., from OCP fitting).

  • method (str, default="auto") – Calculation method. Use “auto” to automatically determine based on inputs.

  • phase (str | None, default=None) – Phase for multi-phase materials: “primary” or “secondary”.

Examples

>>> calc = iw.pipeline.calculations.ElectrodeCapacity(electrode="positive")
>>> config = iw.pipeline.Pipeline({"capacity": calc}).to_config()
electrode: Literal['positive', 'negative', 'lumped']
use_stoich_window: bool
method: str
phase: Literal['', 'primary', 'secondary'] | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ElectrodeSOH(options=None)[source]

Bases: Calculation

Calculate electrode stoichiometry windows from capacity using electrode SOH algorithm.

Reads "Nominal cell capacity [A.h]" (or "Cyclable lithium capacity [A.h]"), the electrode OCPs, and electrode dimensions/concentrations, and produces the stoichiometries at 0% and 100% SOC for both electrodes. Uses the algorithm from Mohtat et al. (2019) [Mohtat2019].

Parameters:

options (ElectrodeSOHOptions or dict, optional) – See ElectrodeSOHOptions for the available keys.

References

[Mohtat2019]

Mohtat, P., Lee, S., Siegel, J. B., & Stefanopoulou, A. G. (2019). Towards better estimability of electrode-specific state of health: Decoding the cell expansion. Journal of Power Sources, 427, 101-111.

Examples

>>> calc = iw.pipeline.calculations.ElectrodeSOH()
>>> config = iw.pipeline.Pipeline({"esoh": calc}).to_config()
options: ElectrodeSOHOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ElectrodeSOHHalfCell(electrode, options=None)[source]

Bases: Calculation

Calculate minimum and maximum electrode stoichiometries for a half-cell using the electrode-specific SOH algorithm.

See ElectrodeSOH for more details.

Parameters:
electrode: Literal['positive', 'negative', 'lumped']
options: ElectrodeSOHHalfCellOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ElectrodeSOHHalfCellOptions(*, simulation_kwargs: dict[str, Any] | None = None)[source]

Bases: _CalculationOptions

Options for ElectrodeSOHHalfCell.

simulation_kwargs: dict[str, Any] | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ElectrodeSOHOptions(*, known_value: Literal['cyclable lithium capacity', 'cell capacity'] = 'cyclable lithium capacity')[source]

Bases: _CalculationOptions

Options for ElectrodeSOH.

known_value: Literal['cyclable lithium capacity', 'cell capacity']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ElectrodeGeometryFromLoading(electrode)[source]

Bases: Calculation

Thickness, active material volume fraction and porosity from loading.

\[L = \frac{m}{\rho_{press}}, \quad \varepsilon_{AM} = \frac{w_{AM} \, \rho_{press}}{\rho_{AM}}, \quad \varepsilon_e = 1 - \frac{\varepsilon_{AM}}{f_{AM}}\]

with \(m\) the coating mass per area, \(\rho_{press}\) the press density, \(w_{AM}\) the active material mass fraction, \(\rho_{AM}\) its crystal density and \(f_{AM}\) its active volume fraction of solid.

Parameters:

electrode (str) – The electrode: “positive” or “negative”.

Examples

>>> calc = iw.pipeline.calculations.ElectrodeGeometryFromLoading("positive")
>>> config = iw.pipeline.Pipeline({"geometry": calc}).to_config()
electrode: Literal['positive', 'negative']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ElectrodeVolumeFractionFromLoading(electrode, method)[source]

Bases: Calculation

Calculate the volume fraction of active material in the electrodes from the loading and maximum concentration or from the coating mass and crystal density.

Parameters:
  • electrode (["positive", "negative", "both"]) – The electrode to calculate the volume fraction for.

  • method (["loading", "coating mass"]) – The method to use to calculate the volume fraction.

electrode: Literal['positive', 'negative', 'lumped']
method: Literal['loading', 'coating mass']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.ElectrodeVolumeFractionFromPorosity(electrode)[source]

Bases: Calculation

Calculate the volume fraction of active material in the electrodes from the porosity and the active volume fraction of solid.

Parameters:

electrode (str) – The electrode to calculate the volume fraction for. Must be either “negative” or “positive”.

electrode: Literal['positive', 'negative', 'lumped']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.EntropicChangeDataInterpolant(electrode, data, options=None)[source]

Bases: Calculation

Create an interpolant for the open-circuit entropic change from data.

Parameters:
  • electrode (str) – The electrode to create the interpolant for, either “positive”, “negative”, or “lumped”. If “lumped”, assumes a single electrode and modifies parameter names accordingly.

  • data (DataLoader, DataFrame, str, or dict) – The data to use for the interpolant. Must have columns "Stoichiometry" and "Entropic change [V.K-1]".

  • options (EntropicChangeInterpolantOptions or dict, optional) – See EntropicChangeInterpolantOptions for the available keys.

electrode: Literal['positive', 'negative', 'lumped']
data: Annotated[str | DataFrame | DataFrame | Annotated[object, PlainValidator(func=_as_loader, json_schema_input_type=Any)] | TimeSeriesSpec | DataPayloadSpec | MetadataSpec | dict[str, Any], BeforeValidator(func=_route_measurement_input, json_schema_input_type=PydanticUndefined)]
options: EntropicChangeInterpolantOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.EntropicChangeFromMSMRFunction(electrode, voltage_limits, options=None)[source]

Bases: Calculation

Create an interpolant for the open-circuit entropic change from MSMR parameters.

Parameters:
electrode: Literal['positive', 'negative', 'lumped']
voltage_limits: tuple[int | float, int | float] | list[int | float]
options: EntropicChangeInterpolantOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.EntropicChangeInterpolantOptions(*, tolerance: float = 1e-06, interpolator: Literal['linear', 'cubic', 'pchip'] = 'linear', phase: Literal['primary', 'secondary'] | None = None, scale_factor: float = 1.0)[source]

Bases: _CalculationOptions

Options for EntropicChangeDataInterpolant and EntropicChangeFromMSMRFunction.

tolerance: float
interpolator: Literal['linear', 'cubic', 'pchip']
phase: Literal['primary', 'secondary'] | None
scale_factor: float
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.HalfCellNominalCapacity(electrode)[source]

Bases: Calculation

Set nominal cell capacity and current function equal to electrode capacity.

For half-cell models, the cell capacity is determined by the working electrode capacity. Reads "Positive electrode capacity [A.h]" (or the negative-electrode equivalent) and writes the same value to "Nominal cell capacity [A.h]" and "Current function [A]", providing the cell-level parameters required by PyBaMM.

Parameters:

electrode (str) – Working electrode: “negative” or “positive”.

electrode: Literal['positive', 'negative', 'lumped']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.InitialConcentrationFromInitialStoichiometryHalfCell(electrode, options=None)[source]

Bases: Calculation

Calculate the initial concentration in the given electrode from the initial stoichiometry and maximum concentration.

Parameters:
electrode: Literal['positive', 'negative', 'lumped']
options: InitialConcentrationFromInitialStoichiometryHalfCellOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.InitialConcentrationFromInitialStoichiometryHalfCellOptions(*, phase: Literal['primary', 'secondary'] | None = None)[source]

Bases: _CalculationOptions

Options for InitialConcentrationFromInitialStoichiometryHalfCell.

phase: Literal['primary', 'secondary'] | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.InitialSOC(soc)[source]

Bases: Calculation

Calculate initial electrode concentrations from target SOC.

Reads the stoichiometry windows ("Negative/Positive electrode stoichiometry at 0%/100% SOC") and maximum concentrations for both electrodes, and produces "Initial stoichiometry in negative/positive electrode" and the corresponding "Initial concentration in negative/positive electrode [mol.m-3]".

Parameters:

soc (float) – Target state of charge (0.0 to 1.0).

Examples

>>> calc = iw.pipeline.calculations.InitialSOC(soc=0.5)
>>> config = iw.pipeline.Pipeline({"initial_soc": calc}).to_config()
soc: int | float | str
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.InitialSOCfromMaximumStoichiometry(options=None)[source]

Bases: Calculation

Calculate the initial concentration in the negative and positive electrodes from the initial SOC and stoichiometry windows in each electrode.

Parameters:

options (InitialSOCfromMaximumStoichiometryOptions or dict, optional) – See InitialSOCfromMaximumStoichiometryOptions for the available keys.

options: InitialSOCfromMaximumStoichiometryOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.InitialSOCfromMaximumStoichiometryOptions(*, particle_phases: tuple[Literal['1', '2'], Literal['1', '2']] = ('1', '1'), include_concentration: Literal['true', 'false'] = 'true')[source]

Bases: _CalculationOptions

Options for InitialSOCfromMaximumStoichiometry.

particle_phases: tuple[Literal['1', '2'], Literal['1', '2']]
include_concentration: Literal['true', 'false']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.InitialSOCHalfCell(electrode, soc, options=None)[source]

Bases: Calculation

Calculate initial electrode concentration from target SOC for half-cell.

Reads the working electrode’s stoichiometry window and maximum concentration, and produces "Initial stoichiometry in positive/negative electrode" and "Initial concentration in positive/negative electrode [mol.m-3]".

Parameters:
electrode: Literal['positive', 'negative', 'lumped']
soc: int | float | str
options: InitialSOCHalfCellOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.InitialSOCHalfCellOptions(*, phase: Literal['primary', 'secondary'] | None = None)[source]

Bases: _CalculationOptions

Options for InitialSOCHalfCell.

phase: Literal['primary', 'secondary'] | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.InitialStoichiometryFromVoltageHalfCell(electrode, options=None)[source]

Bases: Calculation

Calculate the initial stoichiometry in the given electrode from the initial voltage (i.e. find the stoichiometry that gives the correct voltage).

Parameters:
electrode: Literal['positive', 'negative', 'lumped']
options: InitialStoichiometryFromVoltageHalfCellOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.InitialStoichiometryFromVoltageHalfCellOptions(*, simulation_kwargs: dict[str, Any] | None = None, direction: Literal['lithiation', 'delithiation'] | None = None, phase: Literal['primary', 'secondary'] | None = None)[source]

Bases: _CalculationOptions

Options for InitialStoichiometryFromVoltageHalfCell.

simulation_kwargs: dict[str, Any] | None
direction: Literal['lithiation', 'delithiation'] | None
phase: Literal['primary', 'secondary'] | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.InitialStoichiometryFromVoltageMSMRHalfCell(electrode)[source]

Bases: Calculation

Calculate the initial stoichiometry in the given electrode from the initial voltage (i.e. find the stoichiometry that gives the correct voltage) using the MSMR framework.

Parameters:

electrode (str) – The electrode to calculate the initial concentration for, either “positive” or “negative”.

electrode: Literal['positive', 'negative', 'lumped']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.InitialVoltageFromConcentration(electrode)[source]

Bases: Calculation

Calculate initial voltage from target concentration using OCP inversion.

Reads "Positive/Negative electrode lithiation" (the OCP function), "Maximum concentration in positive/negative electrode [mol.m-3]", and "Initial concentration in positive/negative electrode [mol.m-3]", and produces "Initial voltage in positive/negative electrode [V]" by numerically inverting the lithiation function.

Parameters:

electrode (str) – Electrode: “negative” or “positive”.

electrode: Literal['positive', 'negative', 'lumped']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.JellyRollThermalDimensions(source=None)[source]

Bases: Calculation

Calculate thermal properties of cylindrical jelly roll cell.

Reads "Jelly roll height [m]" and "Jelly roll radius [m]" and produces "Cell volume [m3]" and "Cell cooling surface area [m2]" assuming cylindrical geometry for thermal modeling.

Examples

>>> calc = iw.pipeline.calculations.JellyRollThermalDimensions()
>>> config = iw.pipeline.Pipeline({"thermal": calc}).to_config()
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.LumpedHeatCapacityAndDensity(source=None)[source]

Bases: Calculation

Sets the specific heat capacity and density for each cell component to the lumped (cell-level) values.

model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.MSMRDiffusivityOptions(*, interpolator: Literal['linear', 'cubic', 'pchip'] = 'linear', x_tolerance: float = 1e-06, minimum_dudx: float = 1e-06)[source]

Bases: _CalculationOptions

Options for the MSMR-diffusivity calculations (plain/Arrhenius, data/function).

interpolator: Literal['linear', 'cubic', 'pchip']
x_tolerance: float
minimum_dudx: float
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.MSMRElectrodeSOHHalfCell(electrode)[source]

Bases: Calculation

Calculate minimum and maximum electrode stoichiometries for a half-cell by evaluating the extent of lithiation at the minimum and maximum OCV.

See ElectrodeSOH for more details.

Parameters:

electrode (str) – The electrode to calculate the minimum and maximum stoichiometries for.

electrode: Literal['positive', 'negative', 'lumped']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.MSMRFullCellCapacities(data, method='total capacity')[source]

Bases: Calculation

A pipeline element that calculates variables related to the capacity from the MSMR full-cell balance.

Parameters:
  • data (DataLoader, DataFrame, str, or dict) – The data to use to calculate the useable capacity, see FittingObjective.

  • parameter_format (str, optional) – The format of the parameters to fit. One parameter is always the lower excess capacity, and this option determines the other parameter, which can be either the total capacity or the upper excess capacity. Default is “total capacity”. (Q_tot = Q_lowex + Q_use + Q_uppex)

data: Annotated[str | DataFrame | DataFrame | Annotated[object, PlainValidator(func=_as_loader, json_schema_input_type=Any)] | TimeSeriesSpec | DataPayloadSpec | MetadataSpec | dict[str, Any], BeforeValidator(func=_route_measurement_input, json_schema_input_type=PydanticUndefined)]
method: str
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.MSMRFunction(electrode, direction=None, phase=None)[source]

Bases: Calculation

Specifies the MSMR function for the open-circuit potential for the given electrode.

Parameters:
  • electrode (str) – The name of the electrode.

  • direction (str, optional) – The direction of the MSMR function.

  • phase (str, optional) – The phase of the MSMR function.

electrode: Literal['positive', 'negative', 'lumped']
direction: Literal['', 'delithiation', 'lithiation'] | None
phase: Literal['', 'primary', 'secondary'] | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.OCPDataInterpolant(electrode, data, options=None)[source]

Bases: Calculation

Create an interpolant for the open-circuit voltage (OCP) from data.

Parameters:
  • electrode (str) – The electrode to create the interpolant for, either “positive”, “negative”, or “lumped”. If “lumped”, assumes a single electrode and modifies parameter names accordingly.

  • data (DataLoader, DataFrame, str, or dict) – The data to use for the interpolant. Must have columns “Stoichiometry” and “Voltage [V]”.

  • options (OCPDataInterpolantOptions or dict, optional) – See OCPDataInterpolantOptions for the available keys.

electrode: Literal['positive', 'negative', 'lumped']
data: Annotated[str | DataFrame | DataFrame | Annotated[object, PlainValidator(func=_as_loader, json_schema_input_type=Any)] | TimeSeriesSpec | DataPayloadSpec | MetadataSpec | dict[str, Any], BeforeValidator(func=_route_measurement_input, json_schema_input_type=PydanticUndefined)]
options: OCPDataInterpolantOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.OCPDataInterpolantMSMRExtrapolation(electrode, data, voltage_limits, options=None)[source]

Bases: Calculation

Create an interpolant for the open-circuit voltage (OCP) from data and use MSMR parameters to extrapolate outside the data range and to convert capacity to stoichiometry.

The blending uses smooth weight functions to transition between OCP data in the observed stoichiometry range and MSMR extrapolation outside it. Mathematical formulation:

V(x) = w(x)·V_ocp(x) + (1-w(x))·V_msmr_corrected(x)

where w(x) is a smooth bump function based on the error function, and V_msmr_corrected includes a linear offset to ensure C⁰ continuity at the OCP boundaries.

Parameters:
  • electrode (str) – The electrode to create the interpolant for, either “positive” or “negative”.

  • data (DataLoader, DataFrame, str, or dict) – The data to use for the interpolant. Must have columns “Capacity [A.h]” and “Voltage [V]”.

  • voltage_limits (tuple of float) – The voltage limits to use to generate MSMR data for extrapolation.

  • options (OCPDataInterpolantOptions or dict, optional) – See OCPDataInterpolantOptions for the available keys.

electrode: Literal['positive', 'negative', 'lumped']
data: Annotated[str | DataFrame | DataFrame | Annotated[object, PlainValidator(func=_as_loader, json_schema_input_type=Any)] | TimeSeriesSpec | DataPayloadSpec | MetadataSpec | dict[str, Any], BeforeValidator(func=_route_measurement_input, json_schema_input_type=PydanticUndefined)]
voltage_limits: tuple[int | float, int | float] | list[int | float]
options: OCPDataInterpolantOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.OCPDataInterpolantOptions(*, tolerance: float = 1e-06, direction: Literal['lithiation', 'delithiation'] | None = None, phase: Literal['primary', 'secondary'] | None = None, interpolator: Literal['linear', 'cubic', 'pchip'] = 'linear')[source]

Bases: _CalculationOptions

Options for OCPDataInterpolant and OCPDataInterpolantMSMRExtrapolation.

tolerance: float
direction: Literal['lithiation', 'delithiation'] | None
phase: Literal['primary', 'secondary'] | None
interpolator: Literal['linear', 'cubic', 'pchip']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.OCPMSMRInterpolant(electrode, voltage_limits, options=None)[source]

Bases: Calculation

Create an interpolant for the open-circuit voltage (OCP) from MSMR parameters.

Parameters:
electrode: Literal['positive', 'negative', 'lumped']
voltage_limits: tuple[int | float, int | float] | list[int | float]
options: OCPMSMRInterpolantOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.OCPMSMRInterpolantOptions(*, interpolator: Literal['linear', 'cubic', 'pchip'] = 'linear', tolerance: float = 1e-06, direction: Literal['lithiation', 'delithiation'] | None = None, phase: Literal['primary', 'secondary'] | None = None, create_lithiation_function: bool = True)[source]

Bases: _CalculationOptions

Options for OCPMSMRInterpolant.

interpolator: Literal['linear', 'cubic', 'pchip']
tolerance: float
direction: Literal['lithiation', 'delithiation'] | None
phase: Literal['primary', 'secondary'] | None
create_lithiation_function: bool
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.OpenCircuitLimits(source=None)[source]

Bases: Calculation

Set OCV limits equal to voltage cutoffs for SOC calculations.

Reads "Lower voltage cut-off [V]" and "Upper voltage cut-off [V]" and produces "Open-circuit voltage at 0% SOC [V]" and "Open-circuit voltage at 100% SOC [V]", establishing the relationship between SOC and voltage windows.

model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.PorosityFromElectrodeVolumeFraction(electrode)[source]

Bases: Calculation

Calculate the porosity from the active material volume fraction.

Parameters:

electrode (str) – The electrode to calculate the porosity for. Must be either “negative” or “positive”.

electrode: Literal['positive', 'negative', 'lumped']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.PouchCellThermalDimensions(source=None)[source]

Bases: Calculation

Calculate thermal properties of rectangular pouch cell.

Reads "Pouch height [m]", "Pouch width [m]", and "Pouch thickness [m]", and produces "Cell volume [m3]" and "Cell cooling surface area [m2]" assuming rectangular prism geometry for thermal modeling.

Examples

>>> calc = iw.pipeline.calculations.PouchCellThermalDimensions()
>>> config = iw.pipeline.Pipeline({"thermal": calc}).to_config()
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.SlopesToKnots(base_parameter_name, breakpoint_values, breakpoint_parameter_name)[source]

Bases: Calculation

Converts slopes and initial value to knot values for piecewise linear interpolation.

This calculation takes as input: - An initial value at the first breakpoint - Slopes for each segment between consecutive breakpoints

And outputs: - The value at each breakpoint (knot values)

The conversion follows: y_{i+1} = y_i + slope_i * (x_{i+1} - x_i)

Reads the initial value at the first breakpoint and the slopes between consecutive breakpoints (e.g. "Particle diffusion time at SOC 0 [s]", "Particle diffusion time slope from SOC 0 to 0.5 [s]"), and produces the knot value at every breakpoint (e.g. "Particle diffusion time at SOC 0.5 [s]", "Particle diffusion time at SOC 1 [s]").

Parameters:
  • base_parameter_name (str) – The name of the parameter (e.g., “Particle diffusion time [s]”)

  • breakpoint_values (list[float]) – List of breakpoint values (e.g., [0.0, 0.5, 1.0])

  • breakpoint_parameter_name (str) – Name of the breakpoint parameter (e.g., “SOC”, “Temperature [K]”)

base_parameter_name: str
breakpoint_values: list[int | float]
breakpoint_parameter_name: str
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.SlopesToKnots2D(base_parameter_name, breakpoint1_values, breakpoint1_parameter_name, breakpoint2_values, breakpoint2_parameter_name)[source]

Bases: Calculation

Converts slopes and initial value to knot values for 2D piecewise linear interpolation.

This calculation takes as input: - An initial value at (bp1_0, bp2_0) - Slopes along dimension 1 for the first row (at bp2_0) - Slopes along dimension 2 for all subsequent rows

And outputs: - The value at each 2D grid point (knot values)

The conversion follows the same strategy as PiecewiseInterpolation2D slopes mode: 1. Start with initial value at (bp1_0, bp2_0) 2. Use bp1-slopes to fill first row 3. Use bp2-slopes to fill remaining rows

Reads the initial value at (bp1_0, bp2_0), the bp1-slopes along the first row (e.g. "Diffusivity SOC-slope from SOC 0 to 1 at Temperature [K] 273.15 [m2.s-1]"), and the bp2-slopes for each bp1 grid point (e.g. "Diffusivity Temperature [K]-slope from Temperature [K] 273.15 to 323.15 at SOC 0 [m2.s-1]"), and produces the knot value at every 2D grid point.

Parameters:
  • base_parameter_name (str) – The name of the parameter (e.g., “Diffusivity [m2.s-1]”)

  • breakpoint1_values (list[float]) – List of first dimension breakpoint values (e.g., [0.0, 0.5, 1.0])

  • breakpoint1_parameter_name (str) – Name of the first breakpoint parameter (e.g., “SOC”)

  • breakpoint2_values (list[float]) – List of second dimension breakpoint values (e.g., [273.15, 298.15])

  • breakpoint2_parameter_name (str) – Name of the second breakpoint parameter (e.g., “Temperature [K]”)

base_parameter_name: str
breakpoint1_values: list[int | float]
breakpoint1_parameter_name: str
breakpoint2_values: list[int | float]
breakpoint2_parameter_name: str
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.SpecificHeatCapacity(source=None)[source]

Bases: Calculation

Calculate the specific heat capacity from the lumped heat capacity and cell mass.

Examples

>>> calc = iw.pipeline.calculations.SpecificHeatCapacity()
>>> config = iw.pipeline.Pipeline({"cp": calc}).to_config()
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.StoichiometryAtMinimumSOC(electrode)[source]

Bases: Calculation

Calculate the stoichiometry at the minimum SOC based on the stoichiometry at the maximum SOC, the electrode capacities, and the useable capacity.

Parameters:

electrode (str) – The electrode to calculate the initial concentration for, either “positive” or “negative”.

electrode: Literal['positive', 'negative', 'lumped']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.StoichiometryLimitsFromCapacity(options=None)[source]

Bases: Calculation

Calculate the minimum and maximum stoichiometries for each electrode in a full-cell based on the electrode total capacity, lower and upper excess capacities.

Parameters:

options (StoichiometryLimitsFromCapacityOptions or dict, optional) – See StoichiometryLimitsFromCapacityOptions for the available keys.

options: StoichiometryLimitsFromCapacityOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.StoichiometryLimitsFromCapacityOptions(*, direction: Literal['charge', 'discharge'] | None = None, particle_phases: tuple[Literal['1', '2'], Literal['1', '2']] = ('1', '1'), negative_voltage_limits: tuple[float, float] = (0.0, 2.0), positive_voltage_limits: tuple[float, float] = (2.5, 5.0), usable_capacity: float | None = None)[source]

Bases: _CalculationOptions

Options for StoichiometryLimitsFromCapacity.

direction: Literal['charge', 'discharge'] | None
particle_phases: tuple[Literal['1', '2'], Literal['1', '2']]
negative_voltage_limits: tuple[float, float]
positive_voltage_limits: tuple[float, float]
usable_capacity: float | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.pipeline.calculations.SurfaceAreaToVolumeRatio(electrode)[source]

Bases: Calculation

Calculate the surface area to volume ratio for the electrodes from the active material volume fraction and particle radius, assuming spherical particles.

Parameters:

electrode (str) – The electrode to calculate the surface area to volume ratio for, either “positive”, “negative” or “both”.

electrode: Literal['positive', 'negative', 'lumped']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': 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 initialize 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.