ProgPy is distributed with a few pre-constructed models that can be used in simulation or prognostics. These models for batteries, pumps, valves, among others, are included in the progpy.models
package.
In this notebook, we will be exploring a generalized overview of each included model. For more in-depth descriptions of the included models, please refer to the Included Models documentation.
We will start by introducing the battery models: BatteryCircuit
, BatteryElectroChemEOD
, BatteryElectroChemEOL
, combined BatteryElectroChem
(BatteryElectroChemEODEOL
), and SimplifiedBattery
.
In the following battery models, with the exception of SimplifiedBattery
, the default model parameters included are for Li-ion batteries, specifically 18650-type cells. Experimental discharge curves for these cells can be downloaded from the Prognostics Center of Excellence Data Repository.
In this first example, we will demonstrate how to set up, configure, and use the BatteryCiruit
model. The BatteryCircuit
model is a vectorized prognostics model for a battery, represented by an equivalent circuit model as described in [Daigle Sankararaman 2013].
We will start by importing the model and initializing a battery instance with default settings.
from progpy.models import BatteryCircuit
batt = BatteryCircuit()
Information is passed to and from the model using containers that function like dictionaries. The keys of the containers are specific to the model. Let's look at the inputs (loading) and outputs (measurements) for the BatteryCircuit
model.
print("inputs:", batt.inputs)
print("outputs:", batt.outputs)
If we refer to the Circuit
tab under the battery models section in the documentation, we can see that the input i
refers to the current draw on the battery. The outputs t
refers to the temperature in units Kelvin and v
refers to voltage.
We can also print out what events we're predicting and the internal states the model uses to represent the system.
print("event(s): ", batt.events)
print("states: ", batt.states)
We can see that this particular model only predicts one event, called EOD
(End of Discharge). The states listed include tb
, the battery temperature in K; qb
, the charge stored in Capacitor Cb of the equivalent circuit model; qcp
, the charge stored in Capacitor Ccp of the equivalent circuit model; and qcs
, the charge stored in Capacitor Ccs of the equivalent circuit model.
Let's now look at the model's configuration parameters, which describe the specific system (in this case, the battery) that the model is simulating.
from pprint import pprint
print("Model configuration:")
pprint(batt.parameters)
Let's now use the model to do a simulation. To do this, we will first need to set a configuration and define a future load. For more details on future loading, refer to the related section in 01 Simulation.
config = {"save_freq": 100, "dt": 2, "t0": 700}
def future_loading(t, x=None):
if t < 600:
i = 2
elif t < 900:
i = 1
elif t < 1800:
i = 4
elif t < 3000:
i = 2
else:
i = 3
return batt.InputContainer({"i": i})
Let's run the simulation and plot the inputs and outputs. We can do this using the built-in plot method based on matplotlib or with other imported plotting libraries.
simulated_results = batt.simulate_to_threshold(future_loading, **config)
In the input plot, we can see the current drawn change based on the logic we defined in the future loading function.
fig = simulated_results.inputs.plot(
xlabel="time (s)", ylabel="current draw (amps)", title="BatteryCircuit Input"
)
In the output plots, we can observe how different input current draws affect the temperature and voltage curves. Generally, the graphs indicate that drawing a higher current leads to higher temperatures and lower voltage.
fig = simulated_results.outputs.plot(
keys=["t"],
xlabel="time (s)",
ylabel="temperature (K)",
figsize=(10, 4),
title="BatteryCircuit Outputs",
)
fig2 = simulated_results.outputs.plot(
keys=["v"], xlabel="time (s)", ylabel="voltage (V)", figsize=(10, 4)
)
BatteryElectroChemEOD
is a vectorized prognostics model for a battery, represented by an electrochemical equations as described in [Daigle 2013]. This model predicts the end of discharge event. Let's start by examining the model inputs, outputs, event(s), and states. We can refer to the ElectroChem (EOD)
tab under the battery models section in the documentation for more details.
from progpy.models import BatteryElectroChemEOD
batt = BatteryElectroChemEOD()
print("inputs:", batt.inputs)
print("outputs:", batt.outputs)
print("event(s): ", batt.events)
print("states:", batt.states)
Let's now run a simulation until EOD
, or end of discharge. We wil use the same future loading function as the previous example and specify the configuration threshold event as EOD
.
config = {"save_freq": 100, "dt": 2, "events": ["EOD"]}
simulated_results = batt.simulate_to_threshold(future_loading, **config)
In the input plot, we can see the current draw change based on the future loading function we defined.
fig = simulated_results.inputs.plot(
xlabel="time (s)", ylabel="current draw (amps)", title="BatteryElectroChemEOD Input"
)
In the output plots, we can see changes in voltage and temperature. We can also print parameters like VEOD
, or the end of discharge voltage threshold. This value is the voltage at which a battery is considered fully discharged.
fig = simulated_results.outputs.plot(
keys=["v"],
xlabel="time (s)",
ylabel="voltage (V)",
figsize=(10, 4),
title="BatteryElectroChemEOD Outputs",
)
print("End of discharge voltage threshold:", batt.parameters["VEOD"])
fig2 = simulated_results.outputs.plot(
keys=["t"], xlabel="time (s)", ylabel="temperature (°C)", figsize=(10, 4)
)
In the event state plot, we can see EOD
decline until it reaches 0, or when the end of discharge event has occurred. This event occurence is when the simulation reached threshold and ended.
fig = simulated_results.event_states.plot(
xlabel="time (s)",
ylabel="event state",
labels={"EOD"},
title="BatteryElectroChemEOD Event State",
)
BatteryElectroChemEOL
is a vectorized prognostics model for battery degradation, represented by an electrochemical model as described in [Daigle 2016]. Let's go ahead and import the model, initialize a battery instance, and take a closer look at the details. We can also refer to the ElectroChem (EOL)
tab under the battery model section in the documentation. Note that the model has no outputs.
from progpy.models import BatteryElectroChemEOL
batt = BatteryElectroChemEOL()
print("inputs:", batt.inputs)
print("outputs:", batt.outputs)
print("event(s): ", batt.events)
print("states:", batt.states)
Let's now run a simulation to predict when we will reach insufficient battery capacity. We will use the same future loading function as the previous examples and specify the configuration threshold event as InsufficientCapacity
.
config = {"save_freq": 100, "dt": 2, "events": ["InsufficientCapacity"]}
simulated_results = batt.simulate_to_threshold(future_loading, **config)
In the input plot, we can once again see the current draw change based on the future loading function we defined.
fig = simulated_results.inputs.plot(
xlabel="time (s)", ylabel="current draw (amps)", title="BatteryElectroChemEOL Input"
)
In the event state plot, we can see InsufficientCapacity
linearly decrease until it reaches 0, or when the event has occurred.
fig = simulated_results.event_states.plot(
xlabel="time (s)",
ylabel="event state",
labels={"InsufficientCapacity"},
title="BatteryElectroChemEOL Event State",
)
BatteryElectroChemEODEOL
is a prognostics model for battery degradation and discharge, represented by an electrochemical model as described in [Daigle 2013] and [Daigle 2016]. This model combines both the BatteryElectroChemEOL
and BatteryElectroChemEOD
models.
We will start by importing the model, initializing a battery instance, and examining the model details. We can refer to the ElectroChem (Combo)
tab under the battery model section in the documentation for more details.
from progpy.models import BatteryElectroChem
batt = BatteryElectroChem()
print("inputs:", batt.inputs)
print("outputs:", batt.outputs)
print("event(s): ", batt.events)
print("states:", batt.states)
In this example, we will simulate a battery until EOL
(End of Life). As battery capacity decreases with use, EOL
is reached when the battery capacity falls below some acceptable threshold (i.e., what we define as useful capacity).
We will now set the configuration and define a future loading function. As we want to simulate until EOL
, we will set the configuration event to InsufficientCapacity
. The future loading function is designed to charge the battery until EOD
is 0.95 and then discharge until EOD
is 0.05. Note that states represent the progress towards the event occurring. An event state of 0 indicates the event has occurred and 1 indicates no progress towards the event.
config = {
"save_freq": 1000,
"dt": 2,
"events": "InsufficientCapacity",
}
def future_loading(t, x=None):
load = 1
if x is not None:
event_state = batt.event_state(x)
if event_state["EOD"] > 0.95:
load = 1 # Discharge
elif event_state["EOD"] < 0.05:
load = -1 # Charge
return batt.InputContainer({"i": load})
We will now simulate to the threshold and print the results.
simulated_results = batt.simulate_to_threshold(future_loading, **config)
We will now plot the inputs, outputs, and event states. In the input plot, we can see the current drawn fluctuates between -1 and 1 based on the current load we defined in the future loading function.
fig = simulated_results.inputs.plot(
xlabel="time (s)", ylabel="current drawn (amps)", title="BatteryElectroChem Input"
)
In the output plots, we can see changes in the voltage and temperature.
fig = simulated_results.outputs.plot(
keys=["v"],
xlabel="time (s)",
ylabel="voltage (V)",
figsize=(10, 4),
title="BatteryElectroChem Outputs",
)
fig2 = simulated_results.outputs.plot(
keys=["t"], xlabel="time (s)", ylabel="temperature (°C)", figsize=(10, 4)
)
In the event states plot, we can see EOD
incrementally spiking and InsufficientCapacity
linearly declining until it reaches 0, or when the event has occurred.
fig = simulated_results.event_states.plot(
xlabel="time (s)",
ylabel="event states",
labels={"EOD", "InsufficientCapacity"},
title="BatteryElectroChem Event States",
)
SimplifiedBattery
is a model from [Sierra 2019]. It was initially introduced in the 2024 PHM Tutorial. Unlike the previous models, the default parameters are for a Tattu battery. We can refer to the Simplified
tab under the battery model section in the documentation for more details.
Let's start by importing the model, initializing an instance, and examining it.
from progpy.models import SimplifiedBattery
from progpy.loading import Piecewise
batt = SimplifiedBattery()
print("inputs:", batt.inputs)
print("outputs:", batt.outputs)
print("event(s): ", batt.events)
print("states:", batt.states)
We will now define future loading based on a piecewise function and simulate to a set time.
future_loading = Piecewise(
dict, [600, 900, 1800, 3000, float("inf")], {"P": [25, 12, 50, 25, 33]}
)
simulated_results = batt.simulate_to(200, future_loading, {"v": 4.183})
Let's look at the event states plot, where we can see EOD
and Low V
.
fig = simulated_results.event_states.plot(
xlabel="time (s)", ylabel="event state", title="SimplifiedBattery Event States"
)
A version of this section will be added in release v1.9
A version of this section will be added in release v1.9
A version of this section will be added in release v1.9
A version of this section will be added in release v1.9