Metrics¶
The metrics module provides classes for computing specific metrics from PyBaMM solutions at particular operating conditions.
Base Class¶
- class ionworkspipeline.data_fits.models.metrics.BaseMetric(variable: str | None = None, step: int | None = None)¶
A base class for computing a metric given a specific operating condition.
Metrics extract values from PyBaMM solutions. Use Action classes (Maximize, Minimize, GreaterThan, LessThan) to define how metrics contribute to optimization objectives.
Metrics support arithmetic operations (
+,-,*,/) to create composed metrics:>>> # Pulse resistance = (V_after - V_before) / I_mean >>> v_after = Time("Voltage [V]", value=1.0, step=2) >>> v_before = Time("Voltage [V]", value=0.0, step=2) >>> i_mean = Mean("Current [A]", step=2) >>> resistance = (v_after - v_before) / i_mean
Metrics also support iteration over steps or cycles using fluent methods:
>>> Mean("Voltage [V]").by_step() # evaluate for each step <...IterativeMetric object at ...> >>> Mean("Voltage [V]").by_cycle(step=1) # evaluate step 1 of each cycle <...IterativeMetric object at ...>
Parameters¶
- variablestr or None
The variable name on which to compute the metric. May be None for wrapper metrics (ComposedMetric, IterativeMetric) that delegate to inner metrics.
- stepint, optional
If provided, filters the solution to the specified step (0-indexed) using
solution.sub_solutions[step]before computing the metric.
Extends:
abc.ABC- by_cycle(step: int | None = None, cycles: list[int] | None = None) IterativeMetric¶
Evaluate this metric for each cycle.
Parameters¶
- stepint, optional
The step index (0-indexed) within each cycle to evaluate. If None, evaluates the metric across the entire cycle (all steps).
- cycleslist of int, optional
Specific cycle indices (0-indexed). If None, evaluates all cycles.
Returns¶
- IterativeMetric
A metric that returns an array with one value per cycle.
Example¶
>>> Mean("Voltage [V]").by_cycle() # all steps of all cycles <...IterativeMetric object at ...> >>> Mean("Voltage [V]").by_cycle(step=1) # step 1 of all cycles <...IterativeMetric object at ...> >>> Maximum("Capacity [A.h]").by_cycle(step=1, cycles=[0, 5, 10]) <...IterativeMetric object at ...>
- by_step(steps: list[int] | None = None) IterativeMetric¶
Evaluate this metric for each step, returning an array of results.
Parameters¶
- stepslist of int, optional
Specific step indices (0-indexed). If None, evaluates all steps.
Returns¶
- IterativeMetric
A metric that returns an array with one value per step.
Example¶
>>> Mean("Voltage [V]").by_step() # all steps <...IterativeMetric object at ...> >>> Mean("Voltage [V]").by_step(steps=[0, 2, 4]) # specific steps <...IterativeMetric object at ...>
- property name: str¶
The name of this metric, used for identification.
- run(solution: Solution, model: BaseModel = None) float | ndarray¶
Compute the metric from the solution.
If a step filter is specified, the solution is filtered to that step’s sub-solution before computing the metric.
Parameters¶
- solutionpybamm.Solution
The solved PyBaMM solution.
- modelpybamm.BaseModel, optional
The PyBaMM model instance.
Returns¶
- float or np.ndarray
The computed metric value(s).
- to_config() dict¶
Convert the metric to a configuration dictionary.
Returns¶
- dict
Parsed configuration dictionary for this metric.
- property variable: str¶
The variable name for this metric.
Raises NotImplementedError for wrapper metrics that must override this.
Time-Based Metrics¶
- class ionworkspipeline.data_fits.models.metrics.Time(variable: str, value: float, step: int | None = None)¶
A metric computed at a specific time.
Parameters¶
- variablestr
The variable name on which to compute the metric.
- valuefloat
The time value (in seconds) to evaluate, also accepts -1 for final time value.
- stepint, optional
If provided, filters the solution to the specified step (0-indexed).
Extends:
ionworkspipeline.data_fits.models.metrics.metrics.BaseMetric
Condition-Based Metrics¶
- class ionworkspipeline.data_fits.models.metrics.SOC(variable: str, soc_variable: str, value: float, step: int | None = None)¶
Metric computed at a specific state-of-charge using root finding for SOC crossings.
Identifies SOC crossings and extracts variable values at those times using scipy.optimize.root_scalar with linear interpolation fallback.
Uses “State of charge” as the SOC variable name.
Parameters¶
- variablestr
The variable name on which to compute the metric.
- soc_variablestr
The variable name for the SOC variable.
- valuefloat
The state-of-charge value (between 0 and 1).
- stepint, optional
If provided, filters the solution to the specified step (0-indexed).
Extends:
ionworkspipeline.data_fits.models.metrics.metrics.BaseMetric
- class ionworkspipeline.data_fits.models.metrics.Voltage(variable: str, value: float, step: int | None = None)¶
Metric computed at a specific voltage using root finding for voltage crossings.
Identifies voltage crossings and extracts variable values at those times using scipy.optimize.root_scalar with linear interpolation fallback.
Parameters¶
- variablestr
The variable name on which to compute the metric.
- valuefloat
The voltage value in Volts.
- stepint, optional
If provided, filters the solution to the specified step (0-indexed).
Extends:
ionworkspipeline.data_fits.models.metrics.metrics.BaseMetric
Aggregation Metrics¶
- class ionworkspipeline.data_fits.models.metrics.Maximum(variable: str, step: int | None = None)¶
A maximum metric computed across multiple solution points.
Parameters¶
- variablestr
The variable name on which to compute the metric.
Extends:
ionworkspipeline.data_fits.models.metrics.metrics.AggregationMetric
- class ionworkspipeline.data_fits.models.metrics.Minimum(variable: str, step: int | None = None)¶
A minimum metric computed across multiple solution points.
Parameters¶
- variablestr
The variable name on which to compute the metric.
Extends:
ionworkspipeline.data_fits.models.metrics.metrics.AggregationMetric
- class ionworkspipeline.data_fits.models.metrics.Mean(variable: str, step: int | None = None)¶
A mean metric computed across multiple solution points.
Parameters¶
- variablestr
The variable name on which to compute the metric.
Extends:
ionworkspipeline.data_fits.models.metrics.metrics.AggregationMetric
- class ionworkspipeline.data_fits.models.metrics.Sum(variable: str, step: int | None = None)¶
A summation metric computed across multiple solution points.
Parameters¶
- variablestr
The variable name on which to compute the metric.
Extends:
ionworkspipeline.data_fits.models.metrics.metrics.AggregationMetric
Point-Based Metrics¶
- class ionworkspipeline.data_fits.models.metrics.PointBased(variable: str, step: int | None = None)¶
A metric that extracts a single constant value from a solution variable.
This metric is designed for variables that have a constant value throughout the solution (e.g., design parameters like electrode thickness, cell cost). It extracts the value at the final time point.
Useful for optimization objectives that involve parameters defined via pybamm.Parameter expressions which don’t vary over time.
Parameters¶
- variablestr
The variable name on which to compute the metric.
- stepint, optional
If provided, filters the solution to the specified step (0-indexed).
Example¶
Define a cost variable in the model and use PointBased to extract it:
model.variables["Cell cost"] = ( pybamm.Parameter("Total cell area [m]") * pybamm.Parameter("Areal cost density [$/m^2]") ) cost_metric = PointBased("Cell cost")
Extends:
ionworkspipeline.data_fits.models.metrics.metrics.BaseMetric
Action Classes¶
Actions wrap metrics and define how they contribute to the optimization cost landscape.
- class ionworkspipeline.data_fits.models.metrics.BaseAction(metric: BaseMetric, weight: float = 1.0)¶
Base class for optimization actions that wrap metrics.
Actions define how a metric contributes to the optimization cost landscape. Each action wraps a metric and transforms its output appropriately for the optimizer (which minimizes).
Parameters¶
- metricBaseMetric
The metric to wrap with this action.
- weightfloat, optional
Weight to apply to this metric in the objective function. Default is 1.0.
Examples¶
>>> from ionworkspipeline.data_fits.models.metrics import Maximum, Minimum >>> from ionworkspipeline.data_fits.models.metrics.actions import ( ... Maximize, Minimize, LessThan, GreaterThan, ... )
Define objective actions: >>> actions = { … “Cell capacity”: Maximize(Maximum(“Discharge capacity [A.h]”), weight=1.5), … }
Define constraint actions: >>> constraints = { … “Max temperature”: LessThan(Maximum(“Temperature [K]”), 323.15), … “Min voltage”: GreaterThan(Minimum(“Voltage [V]”), 2.5), … }
Extends:
abc.ABC- abstractmethod apply(metric_value: float | ndarray) float | ndarray¶
Apply the action transformation to a metric value.
This method applies the action’s transformation (e.g., negation for Maximize) without running the underlying metric. Use this when you already have the raw metric value and want to avoid running the metric twice.
Parameters¶
- metric_valuefloat or np.ndarray
The raw metric value to transform.
Returns¶
- float or np.ndarray
The transformed metric value for the optimizer.
- property name: str¶
Return a descriptive name for this action.
- run(solution: Solution, model: BaseModel | None = None) float | ndarray¶
Execute the action on a solution.
This is a convenience method that runs the metric and applies the action transformation in one step.
Parameters¶
- solutionpybamm.Solution
The solved PyBaMM solution.
- modelpybamm.BaseModel, optional
The PyBaMM model instance.
Returns¶
- float or np.ndarray
The transformed metric value for the optimizer.
- property variable: str¶
Return the underlying metric’s variable name.
- class ionworkspipeline.data_fits.models.metrics.Maximize(metric: BaseMetric, weight: float = 1.0)¶
Maximize the metric value during optimization.
Since optimizers typically minimize, this action negates the metric value so that minimizing the negative is equivalent to maximizing the original.
Parameters¶
- metricBaseMetric
The metric to maximize.
Example¶
Maximize discharge capacity:
Maximize(Maximum("Discharge capacity [A.h]"))
Extends:
ionworkspipeline.data_fits.models.metrics.actions.BaseAction- apply(metric_value: float | ndarray) float | ndarray¶
Return negated metric value (optimizer minimizes, so we negate to maximize).
- to_config() dict¶
Convert to configuration dict with explicit action type.
- class ionworkspipeline.data_fits.models.metrics.Minimize(metric: BaseMetric, weight: float = 1.0)¶
Minimize the metric value during optimization.
This action passes through the metric value unchanged since optimizers already minimize by default.
Parameters¶
- metricBaseMetric
The metric to minimize.
Example¶
Minimize cell cost:
Minimize(PointBased("Cell cost"))
Extends:
ionworkspipeline.data_fits.models.metrics.actions.BaseAction- apply(metric_value: float | ndarray) float | ndarray¶
Return metric value as-is (optimizer already minimizes).
- to_config() dict¶
Convert to configuration dict with explicit action type.
- class ionworkspipeline.data_fits.models.metrics.GreaterThan(metric: BaseMetric, value: float, penalty: float = 1000000.0)¶
Constraint that the metric value should be greater than or equal to a threshold.
Returns 0 when the constraint is satisfied (value >= threshold), and a large penalty value when violated (value < threshold).
Parameters¶
- metricBaseMetric
The metric to constrain.
- valuefloat
The lower bound threshold. Values below this trigger penalties.
- penaltyfloat, optional
The penalty value to return when the constraint is violated. This contribution is
penalty * (1 + violation)to include both a steep violation and a proportion term to directionally inform the optimiser. Default is 1e6.
Example¶
Constrain minimum anode potential to be >= 0V (prevent lithium plating):
GreaterThan(Minimum("Negative electrode potential [V]"), 0.0)
Extends:
ionworkspipeline.data_fits.models.metrics.actions.BaseAction- apply(metric_value: float | ndarray) float¶
Apply the constraint transformation to a metric value.
Returns¶
- float
0.0 if constraint is satisfied (value >= threshold),
penalty * (1 + violation)if violated (value < threshold),np.infif the metric value is empty or contains NaN.
- property name: str¶
Return a descriptive name for this constraint.
- to_config() dict¶
Convert to configuration dict with explicit action type.
- class ionworkspipeline.data_fits.models.metrics.LessThan(metric: BaseMetric, value: float, penalty: float = 1000000.0)¶
Constraint that the metric value should be less than or equal to a threshold.
Returns 0 when the constraint is satisfied (value <= threshold), and a large penalty value when violated (value > threshold).
Parameters¶
- metricBaseMetric
The metric to constrain.
- valuefloat
The upper bound threshold. Values above this trigger penalties.
- penaltyfloat, optional
The penalty value to scale the metric by when the constraint is violated. This contribution is
penalty * (1 + violation)to include both a steep violation and a proportion term to directionally inform the optimiser. Default is 1e6.
Example¶
Constrain maximum temperature to <= 50°C:
LessThan(Maximum("Temperature [K]"), 323.15)
Extends:
ionworkspipeline.data_fits.models.metrics.actions.BaseAction- apply(metric_value: float | ndarray) float¶
Apply the constraint transformation to a metric value.
Returns¶
- float
0.0 if constraint is satisfied (value <= threshold),
penalty * (1 + violation)if violated (value > threshold),np.infif the metric value is empty or contains NaN.
- property name: str¶
Return a descriptive name for this constraint.
- to_config() dict¶
Convert to configuration dict with explicit action type.