diff --git a/examples/03_IncludedModels.ipynb b/examples/03_IncludedModels.ipynb new file mode 100644 index 0000000..dea2193 --- /dev/null +++ b/examples/03_IncludedModels.ipynb @@ -0,0 +1,515 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 3. Models included in Progpy\n", + "ProgPy includes a subpackage with prognostic models developed by ProgPy contributors. These models can be configured, used, and modified by users for their purposes. All models are part of the models subpackage (`progpy.models`). Each model is different, and model documentation can be found [here](https://nasa.github.io/progpy/api_ref/prog_models/IncludedModels.html).\n", + "\n", + "In this section we will briefly go over the specifics of each model, and how it could be used in simulation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ThrownObject" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "ThrownObject is typically the first model individuals are introduced to when learning to use progpy. This is because it's so simple. ThrownObject is a model of an object being thrown directly into the air and returning to the earth. The object is affected by gravity and air resistence in its path. The model includes two events: 1. `falling`- when the object is falling towards the ground, and 2. `impact`- when the object has impacted the ground.\n", + "\n", + "This model has been used extensively in the previous sections. Rather than repeating this here, I direct the user to the sections on Simulating to Threshold a Parameters in 1. Simulating with Prognostics Models." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Battery Models" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The battery models are the most mature and widely used models in the ProgPy package. And there are a lot of them, each serving a different purpose and simulating different phenomenon. We will introduce each of them in this example, describing how to select, configure, and use each.\n", + "\n", + "There are two ways to divide the models included in progpy, the first is by what they're simulating: *discharge* or *degredation*. \n", + "\n", + "*Discharge* is the process of using the batteries energy. When a battery is fully discharged, it no longer can be used for power. This point is typically called End of Discharge (EOD). \n", + "\n", + "*Degredation* is the gradual decrease of performance of a battery with use. This in easiest to see in the decrease in capacity, but it effects other parameters as well. \n", + "\n", + "The second dimension of classification is by model strategy: Equivalent Circuit or Electrochemistry. Equivalent circuit models represent the behavior of the model as an electrical circuit, while electrochemistry models model the chemical phenomenon at play internal to the battery. Electrochemistry models are typically more accurate, but they also are more complex, and therefore slower to run.\n", + "\n", + "The classification of the six models into those categories are illustrated below:\n", + "| | Equivalent Circuit | Electrochemistry | \n", + "| --- | --- | --- |\n", + "| Discharge | [BatteryCircuit](https://nasa.github.io/progpy/api_ref/prog_models/IncludedModels.html#prog_models.models.BatteryCircuit) | [BatteryElectroChemEOD](https://nasa.github.io/progpy/api_ref/prog_models/IncludedModels.html#prog_models.models.BatteryElectroChemEOD) |\n", + "| Degredation | | [BatteryElectroChemEOL](https://nasa.github.io/progpy/api_ref/prog_models/IncludedModels.html#prog_models.models.BatteryElectroChemEOL) | \n", + "| Both | | [BatteryElectroChemEODEOL](https://nasa.github.io/progpy/api_ref/prog_models/IncludedModels.html#prog_models.models.BatteryElectroChemEODEOL) |\n", + "\n", + "These models are described in more detail below:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### BatteryCircuit" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`progpy.models.BatteryCircuit` is a vectorized prognostics model of a battery discharge, where it's behavior is represented by an equivalent circuit, below. The specifics of the model are described in the papaer [*Advanced Methods for Determining Prediction Uncertainty in Model-Based Prognostics with Application to Planetary Rovers*](https://papers.phmsociety.org/index.php/phmconf/article/view/2253).\n", + "\n", + "![Equivalent Circuit](figs/equivcircuit.png)\n", + "\n", + "This is the simplest of the battery models, and as a result, the least computationally expensive. This is also the least accurate or complete of the models. It is a simple approximation of the behavior of a battery during discharge. When deciding wether to use equivalent circuit or electrochemistry model, users should consider what accuracy they need, and what computational resources they have available.\n", + "\n", + "First, we will import and instantiate the `BatteryCircuit` model to describe it further. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from progpy.models import BatteryCircuit\n", + "m = BatteryCircuit()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Battery circuit predicts one [event](https://nasa.github.io/progpy/glossary.html#term-event): End of Discharge, denoted as `EOD`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(m.events)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`EOD` is the point at which the battery no longer has sufficient charge to be used.\n", + "\n", + "The battery [outputs](https://nasa.github.io/progpy/glossary.html#term-output) (i.e, measurable parameters) are voltage and temperature, denoted by `v` and `t`, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(m.outputs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Its one [input](https://nasa.github.io/progpy/glossary.html#term-input) (i.e., how it is loaded) is the current drawn from the battery, denoted by `i`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(m.inputs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `BatteryCircuit` model has four states: temperature (`tb`), and the charge stored in different capacitors in the model (`qb`, `qcp`, `qcs`). For more information on these states, see the paper at the top of this section." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(m.states)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The batteries parameters are listed below. Each of these describe different aspects of the circuit. For details on these parameters, see the paper above, or the model documentation [here](https://nasa.github.io/progpy/api_ref/prog_models/IncludedModels.html#prog_models.models.BatteryCircuit)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(m.parameters)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Generally, for a complex model like this these parameters are not set directly, instead they are estimated using parameter estimation (see 2. Parameter Estimation). VEOD, however, must be set manually. VEOD is the voltage at which the battery no longer can perform its function.\n", + "\n", + "For this model it's also a good idea to re-estimate parameters regularly. The parameters shift as the battery ages, especially qMax (the battery capacity).\n", + "\n", + "To get to know this model better, let's use it in simulation. First we have to define a loading (i.e., current) profile for simulation. In this example we will start with a constant load. See the future load section in 01_Simulation for more details on future loading." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "load = m.InputContainer({'i': 2.5})\n", + "def future_load(t, x=None):\n", + " return load" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, let's use this loading profile to simulate until End of Discharge (EOD). EOD occurs when the voltage of the battery reaches the value of the parameter VEOD. We'll generate plots of the outputs (temperature and voltage)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "results = m.simulate_to_threshold(future_load, dt=('auto', 1), save_freq=1)\n", + "fig = results.outputs.plot(compact=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first thing to notice here is that voltage (bottom graph) decreases with time. At the beginning of the discharge, voltage drops suddenly, this is from the first application of current. Then voltage decreases fairly linerally with time, until it hits a knee point, just before EOD. In general, this is the voltage curve you would expect from a battery during discharge.\n", + "\n", + "The EOD event state decreases linerally with time, as seen below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig = results.event_states.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Remember that this in this case a constant load (i.e., current) was applied to the battery. Let's repeat this with a more complex load profile. In this case we'll use a piecewise profile, defined below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from progpy.loading import Piecewise\n", + "future_load = Piecewise(\n", + " m.InputContainer,\n", + " [600, 900, 1800, 3600],\n", + " {'i': [2, 1, 4, 2, 3]})\n", + "\n", + "results = m.simulate_to_threshold(future_load, dt=('auto', 1), save_freq=1)\n", + "fig = results.inputs.plot()\n", + "fig = results.outputs.plot(compact=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we see voltage following a similar curve from before, but there are jumps as the current changes. Voltage is directly correlated with the current draw. When the current draw decreases, voltage will increase, and vice versa.\n", + "\n", + "There is some sensitivity to timestep in capturing behavior, and for a dt above about 2 seconds, it doesn't work at all." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "results = m.simulate_to_threshold(future_load, dt=('auto', 2), save_freq=2)\n", + "fig = results.outputs.plot(compact=False, title=\"dt = 2\")\n", + "\n", + "results = m.simulate_to_threshold(future_load, dt=('auto', 2.5), save_freq=2.5)\n", + "fig = results.outputs.plot(compact=False, title=\"dt = 2.5\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This instability is very dependent on model parameters, for a differntly configured model it might be stable for a different range of dts." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### BatteryElectroChemEOD" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similar to BatteryCircuit, `progpy.models.BatteryElectroChemEOD` is a vectorized prognostics model of a battery discharge. Except the behavior is modeled not as a circuit, but instead by the electrochemical behavior of a battery.\n", + "\n", + "The specifics of the model are described in the papaer [*Electrochemistry-based Battery Modeling for Prognostics*](https://papers.phmsociety.org/index.php/phmconf/article/view/2252).\n", + "\n", + "First, we will import and instantiate the `BatteryElectroChemEOD` model to describe it further. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from progpy.models import BatteryElectroChemEOD\n", + "m = BatteryElectroChemEOD()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The events, outputs, and inputs are identical to `BatteryCircuit` above" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('events', m.events)\n", + "print('inputs', m.inputs)\n", + "print('outputs', m.outputs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `BatteryElectroChemEOD` model has eight states. For more information on these states, see the paper at the top of this section." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(m.states)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The batteries parameters are listed below. Each of these describe different aspects of the circuit. For details on these parameters, see the paper above, or the model documentation [here](https://nasa.github.io/progpy/api_ref/prog_models/IncludedModels.html#prog_models.models.BatteryElectroChemEOD)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(m.parameters)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that there are significantly more parameters and twice as many states in `BatteryElectroChemEOD` when compared to `BatteryCircuit`. The increased complexity allows the model to better represent battery discharge behavior, especially thermal effects which are not very well represented in the `BatteryCircuit` model.\n", + "\n", + "Generally, for a complex model like this these parameters are not set directly, instead they are estimated using parameter estimation (see 2. Parameter Estimation). VEOD, however, must be set manually. VEOD is the voltage at which the battery no longer can perform its function.\n", + "\n", + "Like with `BatteryCircuit`, for this model it's also a good idea to re-estimate parameters regularly. The parameters shift as the battery ages, especially qMobile, Ro, and tDiffusion.\n", + "\n", + "To get to know this model better, let's use it in simulation. First we have to define a loading (i.e., current) profile for simulation. In this example we will start with a constant load. See the future load section in 01_Simulation for more details on future loading." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "load = m.InputContainer({'i': 2.5})\n", + "def future_load(t, x=None):\n", + " return load\n", + "\n", + "results = m.simulate_to_threshold(future_load, dt=('auto', 1), save_freq=1)\n", + "fig = results.outputs.plot(compact=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note here the same characteristic voltage profile shape (bottom). The crver are not exactly the same. Note the end of discharge occurs 300 seconds earlier in this model than in the batterycircuit. Also, the temperature graph has a different shape. This is where you can see the better performance in the `BatteryElectroChemEOD` model. \n", + "\n", + "Now let's repeat this with the piecewise load profile." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from progpy.loading import Piecewise\n", + "future_load = Piecewise(\n", + " m.InputContainer,\n", + " [600, 900, 1800, 3600],\n", + " {'i': [2, 1, 4, 2, 3]})\n", + "\n", + "results = m.simulate_to_threshold(future_load, dt=('auto', 1), save_freq=1)\n", + "fig = results.inputs.plot()\n", + "fig = results.outputs.plot(compact=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we see voltage following a similar curve from before, but there are jumps as the current changes. Voltage is directly correlated with the current draw. When the current draw decreases, voltage will increase, and vice versa.\n", + "\n", + "Like with `BatteryCircuit`, there is some sensitivity to timestep in capturing behavior, and higher dt's will result in unstable and inaccurate behavior." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "results = m.simulate_to_threshold(future_load, dt=('auto', 3), save_freq=3)\n", + "fig = results.outputs.plot(compact=False, title=\"dt = 3\")\n", + "\n", + "results = m.simulate_to_threshold(future_load, dt=('auto', 10), save_freq=10)\n", + "fig = results.outputs.plot(compact=False, title=\"dt = 10\")\n", + "\n", + "results = m.simulate_to_threshold(future_load, dt=('auto', 12), save_freq=12)\n", + "fig = results.outputs.plot(compact=False, title=\"dt = 12\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### BatteryElectroChemEOL" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### BatteryElectroChemEODEOL" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Centrifugal Pump" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pneumatic Valve" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Electric Powertrain" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aircraft Flight Models" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusions" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.11.0 64-bit", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.0" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/figs/equivcircuit.png b/examples/figs/equivcircuit.png new file mode 100644 index 0000000..cff416b Binary files /dev/null and b/examples/figs/equivcircuit.png differ diff --git a/src/progpy/models/battery_electrochem.py b/src/progpy/models/battery_electrochem.py index 2018237..4ce0262 100644 --- a/src/progpy/models/battery_electrochem.py +++ b/src/progpy/models/battery_electrochem.py @@ -301,8 +301,6 @@ class BatteryElectroChemEOD(PrognosticsModel): 'tb': 292.1 # in K, about 18.95 C }, - 'process_noise': 1e-3, - # End of discharge voltage threshold 'VEOD': 3.0, 'VDropoff': 0.1 # Voltage above EOD after which voltage will be considered in SOC calculation