Skip to content

Commit

Permalink
Merge pull request #99 from nasa/examples/dt
Browse files Browse the repository at this point in the history
Add dt example
  • Loading branch information
teubert committed Sep 15, 2023
2 parents e7d36a3 + 0ac2d54 commit 4772616
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 55 deletions.
211 changes: 211 additions & 0 deletions examples/01_Simulation.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,217 @@
"## Step Size"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The next configurable parameter in simulation is the step size, or `dt`. This is the size of the step taken from one step to the next when simulating. Smaller step sizes will usually more accurately simulate state evolution, but at a computational cost. Conversely, some models can become unstable at large step sizes. Choosing the correct step size is important to the success of a simulation or prediction.\n",
"\n",
"In this section we will introduce the concept of setting simulation step size (`dt`) and discuss some considerations when selecting step sizes.\n",
"\n",
"For this section we will use the `progpy.models.ThrownObject model` (see 3. Included models), imported and created below."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from progpy.models import ThrownObject\n",
"m = ThrownObject()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Basic Step Size"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To set step size, set the dt parameter in the `simulate_to` or `simulate_to_threshold` methods. In this example we will use a large and small step size and compare the results.\n",
"\n",
"First, let's simulate with a large step size, saving the result at every step."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"results = m.simulate_to_threshold(\n",
" threshold_keys='impact',\n",
" dt=2.5,\n",
" save_freq=2.5)\n",
"fig = results.outputs.plot(ylabel='Position (m)')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that the parabola above is jagged. Also note that the estimated time of impact is around 10 seconds and the maximum height is a little over 120 meters. \n",
"\n",
"Now let's run the simulation again with a smaller step size."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"results = m.simulate_to_threshold(\n",
" threshold_keys='impact',\n",
" dt=0.25,\n",
" save_freq=0.25)\n",
"fig = results.outputs.plot(ylabel='Position (m)')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Not only is the curve much smoother with a smaller step size, but the results are significantly different. Now the time of impact is closer to 8 seconds and maximum height closer to 80 meters.\n",
"\n",
"All simulations are approximations. The example with the larger step size accumulates more error in integration. The second example (with a smaller step size) is more accurate to the actual model behavior.\n",
"\n",
"Now let's decrease the step size even more"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"results = m.simulate_to_threshold(\n",
" threshold_keys='impact',\n",
" dt=0.05,\n",
" save_freq=0.05)\n",
"fig = results.outputs.plot(ylabel='Position (m)')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The resulting output is different than the 0.25 second step size run, but not by much. What you see here is the diminishing returns in decreasing step size.\n",
"\n",
"The smaller the step size, the more computational resources required to simulate it. This doesn't matter as much for simulating this simple model over a short horizon, but becomes very important when performing prediction (see 9. Prediction), using a complex model with a long horizon, or when operating in a computationally constrained environment (e.g., embedded)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Dynamic Step Size"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The last section introduced step size and showed how changing the step size effects the simulation results. In the last example step size (`dt`) and `save_freq` were the same, meaning each point was captured exactly. This is not always the case, for example in the case below. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"results = m.simulate_to_threshold(\n",
" threshold_keys='impact',\n",
" dt=1,\n",
" save_freq=1.5)\n",
"print('Times saved: ', results.times)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With a `save_freq` of 1.5 seconds you might expect the times saved to be 0, 1.5, 3, 4.5, ..., but that's not the case. This is because the timestep is 1 second, so the simulation never stops near 1.5 seconds to record it. 'auto' stepsize can help with this.\n",
"\n",
"To use 'auto' stepsize set `dt` to a tuple of ('auto', MAX) where MAX is replaced with the maximum allowable stepsize."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"results = m.simulate_to_threshold(\n",
" threshold_keys='impact',\n",
" dt=('auto', 1),\n",
" save_freq=1.5)\n",
"print('Times saved: ', results.times)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We repeated the simulation using automatic step size with a maximum step size of 1. The result was that the times where state was saved matched what was requested exactly. This is important for simulations with large step sizes where there are specific times that must be captured.\n",
"\n",
"Also note that automatic step size doesn't just adjust for `save_freq`. It will also adjust to meet `save_pts` and any transition points in a Piecewise loading profile."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Custom Step Size"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are times when an advanced user would like more flexibility in selecting step sizes. This can be used to adjust step size dynamically close to events or times of interest. In some models, there are complex behaviors during certain parts of the life of the system that require more precise simulation. For example, the knee point in the voltage profile for a discharged battery. This can be done by providing a function (t, x)->dt instead of a scalar `dt`. \n",
"\n",
"For example, if a user wanted to reduce the step size closer to impact, they could do so like this:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def next_time(t, x):\n",
" # In this example dt is a function of state. Uses a dt of 1 until impact event state 0.25, then 0.25\n",
" event_state = m.event_state(x)\n",
" if event_state['impact'] < 0.25:\n",
" return 0.25\n",
" return 1\n",
"\n",
"results=m.simulate_to_threshold(dt=next_time, save_freq= 0.25, threshold_keys='impact')\n",
"\n",
"print(results.times)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that after 8 seconds the step size decreased to 0.25 seconds, as expected."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Parameters"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down
55 changes: 0 additions & 55 deletions examples/dynamic_step_size.py

This file was deleted.

0 comments on commit 4772616

Please sign in to comment.