-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dev' into ipynb-benchmarking
- Loading branch information
Showing
10 changed files
with
936 additions
and
184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,364 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Welcome to ProgPy's Linear Model Example" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The goal of this notebook is to instruct users on how to use ProgPy Model LinearModel.\n", | ||
"\n", | ||
"This example shows the use of the LinearModel class, a subclass of PrognosticsModel for models that can be described as a linear time series, which can be defined by the following equations:\n", | ||
"\n", | ||
"\n", | ||
"\n", | ||
"#### _<b>The State Equation<b>_:\n", | ||
"$$\n", | ||
"\\frac{dx}{dt} = Ax + Bu + E\n", | ||
"$$\n", | ||
"\n", | ||
"#### _<b>The Output Equation<b>_:\n", | ||
"$$\n", | ||
"z = Cx + D\n", | ||
"$$\n", | ||
"\n", | ||
"#### _<b>The Event State Equation<b>_:\n", | ||
"$$\n", | ||
"es = Fx + G\n", | ||
"$$\n", | ||
"\n", | ||
"$x$ is `state`, $u$ is `input`, $z$ is `output`, and $es$ is `event state`" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Linear Models are defined by creating a new model class that inherits from progpy's LinearModel class and defines the following properties:\n", | ||
"* $A$: 2-D np.array[float], dimensions: n_states x n_states. <font color = 'teal'>The state transition matrix. It dictates how the current state affects the change in state dx/dt.</font>\n", | ||
"* $B$: 2-D np.array[float], optional (zeros by default), dimensions: n_states x n_inputs. <font color = 'teal'>The input matrix. It dictates how the input affects the change in state dx/dt.</font>\n", | ||
"* $C$: 2-D np.array[float], dimensions: n_outputs x n_states. The output matrix. <font color = 'teal'>It determines how the state variables contribute to the output.</font>\n", | ||
"* $D$: 1-D np.array[float], optional (zeros by default), dimensions: n_outputs x 1. <font color = 'teal'>A constant term that can represent any biases or offsets in the output.</font>\n", | ||
"* $E$: 1-D np.array[float], optional (zeros by default), dimensions: n_states x 1. <font color = 'teal'>A constant term, representing any external effects that are not captured by the state and input.</font>\n", | ||
"* $F$: 2-D np.array[float], dimensions: n_es x n_states. <font color = 'teal'>The event state matrix, dictating how state variables contribute to the event state.</font>\n", | ||
"* $G$: 1-D np.array[float], optional (zeros by default), dimensions: n_es x 1. <font color = 'teal'>A constant term that can represent any biases or offsets in the event state.</font>\n", | ||
"* __inputs__: list[str] - `input` keys\n", | ||
"* __states__: list[str] - `state` keys\n", | ||
"* __outputs__: list[str] - `output` keys\n", | ||
"* __events__: list[str] - `event` keys" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"We will now utilize our LinearModel to model the classical physics problem throwing an object into the air! We can create a subclass of LinearModel which will be used to simulate an object thrown, which we will call the ThrownObject Class.\n", | ||
"\n", | ||
"\n", | ||
"First, some definitions for our Model!\n", | ||
"\n", | ||
"#### __Events__: (2)\n", | ||
"* `falling: The object is falling`\n", | ||
"* `impact: The object has hit the ground`\n", | ||
"\n", | ||
"#### __Inputs/Loading__: (0)\n", | ||
"* `None`\n", | ||
"\n", | ||
"#### __States__: (2)\n", | ||
"* `x: Position in space (m)`\n", | ||
"* `v: Velocity in space (m/s)`\n", | ||
"\n", | ||
"#### __Outputs/Measurements__: (1)\n", | ||
"* `x: Position in space (m)`" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Now, for our keyword arguments:\n", | ||
"\n", | ||
"* <font color = green>__thrower_height : Optional, float__</font>\n", | ||
" * Height of the thrower (m). Default is 1.83 m\n", | ||
"* <font color = green>__throwing_speed : Optional, float__</font>\n", | ||
" * Speed at which the ball is thrown (m/s). Default is 40 m/s" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"With our definitions, we can now create the ThrownObject Model.\n", | ||
"\n", | ||
"First, we need to import the necessary packages." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import numpy as np\n", | ||
"from progpy import LinearModel" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Now we'll define some features of a ThrownObject LinearModel. Recall that all LinearModels follow a set of core equations and require some specific properties (see above). In the next step, we'll define our inputs, states, outputs, and events, along with the $A$, $C$, $E$, and $F$ values." | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"First, let's consider state transition. For an object thrown into the air without air resistance, velocity would decrease literally by __-9.81__ \n", | ||
"$\\dfrac{m}{s^2}$ due to the effect of gravity, as described below:\n", | ||
"\n", | ||
" $$\\frac{dv}{dt} = -9.81$$\n", | ||
"\n", | ||
" Position change is defined by velocity (v), as described below:\n", | ||
" \n", | ||
" $$\\frac{dx}{dt} = v$$" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Note: For the above equation x is position not state. Combining these equations to the model $\\frac{dx}{dt}$ equation defined earlier yields the A and E matrix defined below. Note that there is no B defined because this model does not have an inputs." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"class ThrownObject(LinearModel):\n", | ||
" events = ['impact']\n", | ||
" inputs = [] \n", | ||
" states = ['x', 'v']\n", | ||
" outputs = ['x']\n", | ||
" \n", | ||
" A = np.array([[0, 1], [0, 0]])\n", | ||
" C = np.array([[1, 0]])\n", | ||
" E = np.array([[0], [-9.81]])\n", | ||
" F = None" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Note that we defined our `A`, `C`, `E`, and `F` values to fit the dimensions that were stated at the beginning of the notebook! Since the parameter `F` is not optional, we have to explicitly set the value as __None__.\n", | ||
"\n", | ||
"Next, we'll define some default parameters for our ThrownObject model." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"class ThrownObject(ThrownObject): # Continue the ThrownObject class\n", | ||
" default_parameters = {\n", | ||
" 'thrower_height': 1.83,\n", | ||
" 'throwing_speed': 40,\n", | ||
" }" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"In the following cells, we'll define some class functions necessary to perform prognostics on the model." | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The `initialize()` function sets the initial system state. Since we have defined the `x`and `v` values for our ThrownObject model to represent position and velocity in space, our initial values would be the thrower_height, and throwing_speed parameters, respectively." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"class ThrownObject(ThrownObject):\n", | ||
" def initialize(self, u=None, z=None):\n", | ||
" return self.StateContainer({\n", | ||
" 'x': self.parameters['thrower_height'],\n", | ||
" 'v': self.parameters['throwing_speed']\n", | ||
" })" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"For our `threshold_met()`, we define the function to return True for event 'falling' when our thrown object model has a velocity value of less than 0 (object is 'falling') and for event 'impact' when our thrown object has a distance from of the ground of less than or equal to 0 (object is on the ground, or has made 'impact').\n", | ||
"\n", | ||
"`threshold_met()` returns a _dict_ of values, if each entry of the _dict_ is __True__, then our threshold has been met!" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"class ThrownObject(ThrownObject):\n", | ||
" def threshold_met(self, x):\n", | ||
" return {\n", | ||
" 'falling': x['v'] < 0,\n", | ||
" 'impact': x['x'] <= 0\n", | ||
" }" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Finally, for our `event_state()`, we will calculate the measurement of progress towards the events. We normalize our values such that they are in the range of 0 to 1, where 0 means the event has occurred." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"class ThrownObject(ThrownObject):\n", | ||
" def event_state(self, x): \n", | ||
" x_max = x['x'] + np.square(x['v'])/(9.81*2)\n", | ||
" return {\n", | ||
" 'falling': np.maximum(x['v']/self.parameters['throwing_speed'],0),\n", | ||
" 'impact': np.maximum(x['x']/x_max,0) if x['v'] < 0 else 1\n", | ||
" }" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"With these functions created, we can now run our ThrownObject Model!\n", | ||
"\n", | ||
"In this example, we will initialize our ThrownObject as `m`, and we'll use the `simulate_to_threshold()` function to simulate the movement of the thrown object in air. For more information, see the [Simulation](https://nasa.github.io/progpy/prog_models_guide.html#simulation) documentation." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"m = ThrownObject()\n", | ||
"save = m.simulate_to_threshold(print = True, save_freq=1, threshold_keys='impact', dt=0.1)" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"__Note__: Because our model takes in no inputs, we have no need to actually define a future loading function! As a result, we are simply passing in an empty Input Container. However, for most models, there would be inputs, thus a need for a future loading function. For more information on future loading functions and when to use them, please refer to the ProgPy [Future Loading](https://nasa.github.io/progpy/prog_models_guide.html#future-loading) Documentation." | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"We'll also demonstrate how this looks plotted on a graph." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import matplotlib.pyplot as plt\n", | ||
"save.outputs.plot(title='generated model')\n", | ||
"plt.show()" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Notice that that plot resembles a parabola, which represents the position of the ball through space as time progresses!" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"#### Conclusion\n", | ||
"\n", | ||
"In this example, we will initialize our ThrownObject as `m` and use the `simulate_to_threshold()` function to simulate the movement of the thrown object in air. For more information, see the [Linear Model](https://nasa.github.io/progpy/api_ref/prog_models/LinearModel.html) Documentation." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [] | ||
} | ||
], | ||
"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 | ||
} |
Oops, something went wrong.