Operating Limits

This example finds operating limits of a cell, each with one bisection search over a single protocol input: the peak power of a 10 s pulse after part of a discharge, and the longest cruise of an eVTOL mission that still lands above a voltage floor.

Key concepts demonstrated:

  • Defining a limit with a UCP protocol that reads the searched value as input[...]

  • PeakPower for the highest pulse power that runs its full duration

  • OperatingLimit for any input, objective and constraints

  • Reading the limit from the returned solution’s metrics

Complete Operating Limits Example
"""Operating Limits Example.

Finds two operating limits of a cell, each by one bisection search over a protocol
input: the peak power of a 10 s pulse after 30 minutes at 1C, and the longest
eVTOL cruise that still lands above 3.0 V. The result is the protocol's solution
at the limit, with the limit in its ``metrics``.
"""

import ionworkspipeline as iwp
from ionworkspipeline.data_fits.models.metrics import Last, Minimum
from ionworkspipeline.data_fits.models.metrics.actions import GreaterThan, Maximize
import pybamm

model = pybamm.lithium_ion.SPMe()
parameter_values = pybamm.ParameterValues("Chen2020")

# PeakPower maximizes input["Power [W]"] subject to the run reaching its full end
# time, with the lower voltage cut-off armed as a safety limit that ends the run.
peak_power = iwp.analysis.PeakPower(
    """
global:
  initial_state_type: soc_percentage
  initial_state_value: 100
steps:
  - Discharge: {mode: C-rate, value: 1, duration: 1800}
  - Discharge: {mode: Power, value: 'input["Power [W]"]', duration: 10}
"""
)
solution = peak_power.solve(model, parameter_values)
print(f"Peak power: {solution.metrics['Peak power [W]']:.1f} W")

max_flight = iwp.analysis.OperatingLimit(
    protocol="""
global:
  initial_state_type: soc_percentage
  initial_state_value: 100
steps:
  - Discharge: {mode: Power, value: 30, duration: 60}                          # takeoff
  - Discharge: {mode: Power, value: 10, duration: 'input["Cruise time [s]"]'}   # cruise
  - Discharge: {mode: Power, value: 35, duration: 90}                          # landing
""",
    parameter=iwp.Parameter("Cruise time [s]", bounds=(1, 7200)),
    objective=Maximize(Last("Time [s]")),  # the longest mission
    constraints={"Voltage floor": GreaterThan(Minimum("Voltage [V]"), 3.0)},
    name="Max cruise [s]",
)
solution = max_flight.solve(model, parameter_values)
print(f"Max cruise: {solution.metrics['Max cruise [s]']:.0f} s")

# The solution is the mission flown at the limit; its metrics are saved with it.
print(f"Lowest voltage: {solution['Voltage [V]'].data.min():.3f} V")
solution.save("max_flight.parquet")