{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "*This notebook contains course material from [CBE30338](https://jckantor.github.io/CBE30338)\n", "by Jeffrey Kantor (jeff at nd.edu); the content is available [on Github](https://github.com/jckantor/CBE30338.git).\n", "The text is released under the [CC-BY-NC-ND-4.0 license](https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode),\n", "and code is released under the [MIT license](https://opensource.org/licenses/MIT).*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "< [Modular Simulation using Python Generators](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/A.02-Modular-Approach-to-Simulation-using-Python-Generators.ipynb) | [Contents](toc.ipynb) | [A Modular Approach to Simulation using Python Generators](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/A.04-Modular-Simulation-using-Python-Generators.ipynb) >

\"Open

\"Download\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Animation in Jupyter Notebooks\n", "\n", "by Jeffrey Kantor (jeff at nd.edu). The latest version of this notebook is available at [https://github.com/jckantor/CBE30338](https://github.com/jckantor/CBE30338). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary\n", "\n", "Animation features are built into the Python matplotlib library and available through Jupyter notebooks. Unfortunately, however, the documentation is not particularly robust. This short notebook provides simple demonstrations of using animiation for the visualization of simulation results. \n", "\n", "The resulting animations are html video files embedded within the Jupyter notebook. The videos can be viewed in any modern web browser. Since they don't require an active Python kernal, the animations can be seen when the even when notebook is viewed as a static HTML web page.\n", "\n", "The techniques used below come from blog postings [Animating the Lorenz System in 3D](https://jakevdp.github.io/blog/2013/02/16/animating-the-lorentz-system-in-3d/) and [Embedding Matplotlib Animations in Jupyter Notebooks](http://louistiao.me/posts/notebooks/embedding-matplotlib-animations-in-jupyter-notebooks/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Prerequisites\n", "\n", "You should be able to view the animations with any modern web browser. However, to create animiations using the following cells you will need to install a video codec. By default matplotlib is configured to use ffmpeg. You find instructions on installing ffmpeg [here](https://github.com/adaptlearning/adapt_authoring/wiki/Installing-FFmpeg)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step-by-Step Approach to Animation with Matplotlib" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Step 1. Create the background frame.\n", "\n", "The following cell creates a background frame for the animation. These are standard matplotlib graphics commands, but where we have kept a reference to the resulting objects. The objects include `fig` for the figure object, `ax1` and `ax2` for each of the two plotting axes, and then `txt_title`, `line1`, `line2`, `pt1`, and `line3` for graphical elements that will be updated in each frame of the animation." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAtsAAAFKCAYAAADMopPTAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3Xt4FPWh//HPkmQhJOFqSis2WPMDuYkQPApFRKAU5dKA\nIeRSExCOVn4CGhEBj8UgaQICVcsxqK0HAQ8qRC1gPViRHKkcDgIhKFdpKKFYxEiLsBvIbef3h3V/\nBEKyk2RmJ/B+PY/Pw853J/PpZPrdzzM7mXEZhmEIAAAAQKNrFuwAAAAAwJWKsg0AAABYhLINAAAA\nWISyDQAAAFiEsg0AAABYhLINAAAAWCQ02AEAAEDTd/z4cQ0bNkxdunTxLzMMQ+np6Ro3bpzefvtt\nvf/++3rppZdszTVkyBCFhYWpRYsWcrlcqqio0IABAzR79mz97W9/0+jRo7V7925bM+HqQtkGAACN\nokWLFlq3bp3/9cmTJzVq1Cj17NkziKmkxYsX66abbpIklZeXKy0tTatXr9add94Z1Fy4OlC2AQCA\nJTp06KBOnTrp6NGjkqSSkhI98MADOnHihEJCQrRkyRLFxsaqsLBQixYtUnl5uUpKSvTjH/9Y2dnZ\nqqys1Pz581VQUKCwsDBdd911ysnJUUREhAoKCrR48WKdO3dOLpdL06ZN0+DBg+vM5Ha71bdvXx05\ncqRa2f766681d+5cnTp1SiUlJerYsaOee+45tW/fXkOGDNHYsWO1bds2nThxQnfffbcef/xxSdLm\nzZu1bNkyVVRUqEWLFpo1a5b69Oljxe5EE0XZBgAAlti9e7eOHTumm2++Wdu2bdNf//pXPfvss+rU\nqZOysrL0yiuvKDs7WytXrtT06dN12223yev1aujQodq7d6/Onz+vTz75RO+9955cLpcWLVqkQ4cO\nKTY2VnPmzNErr7yi6667TidPntT48eN144036tprr60108mTJ5Wfn69HHnmk2vI//OEP6t27tx54\n4AEZhqEHHnhA69at06RJkyRJpaWlWr16tU6ePKlhw4YpJSVFVVVVevbZZ7Vy5Uq1bdtWhw8f1n33\n3ac//vGPatmypWX7FU0LZRsAADSK8+fPKz4+XpJUVVWltm3batGiRfrBD34gSerVq5c6deokSerW\nrZs++OADSdKCBQu0ZcsWvfjiizpy5IjOnz+v0tJSde3aVSEhIUpMTNTtt9+u4cOHq1evXvroo49U\nUlKihx56yL9tl8ulQ4cO1Vi2H3vsMbVo0UI+n09hYWFKTEzU8OHDdfz4cf97JkyYoJ07d2r58uU6\nevSoDh8+rJtvvtk/PnToUEnfnq1v3769vvnmG+3Zs0dfffWVJk6cWC3HsWPH1LVr10baq2jqKNsA\nAKBRXHzN9sVCQ/9/7XC5XDIMQ5L085//XF27dtXAgQN19913a8+ePTIMQ61atdK6detUUFCg//3f\n/9Ujjzyi9PR0xcTEKDY2VmvXrvX/vJMnT6pdu3Y1bvfCa7YvZ9GiRfr000+VkJCg2267TZWVlf58\nktS8efNLsvt8PvXv31/PPfecf+zEiRP63ve+V+u2cHXh1n8AACBovvnmG+3du1ePPfaYfvrTn+rk\nyZM6duyYfD6f8vPzNXHiRPXp00fTpk3TmDFjdPDgQfXu3VvFxcXasWOHJOnAgQMaPny4vvrqq3rn\n+PjjjzVhwgSNGTNG7du31//8z/+oqqqq1nX69eunrVu3qqioSJL00Ucf6Wc/+5nKysrqnQNXHs5s\nAwCAoGndurUeeOABjR07Vm3atFHbtm0VFxen4uJiJSYmasuWLRo1apRatmyp1q1ba/78+WrXrp1+\n85vf6JlnnlFZWZkMw9Azzzyjjh071jvHQw89pGeeeUa5ubkKCQlRXFycjh07Vus6nTt31tNPP61H\nH31UhmEoNDRUy5Yt43ptVOMyLvyOBAAAAECj4TISAAAAwCKUbQAAAMAitl6zXVFRoSeeeEJffPGF\nysvLNWXKFP+tdKRvbwz/wgsvKDQ0VAkJCRo/fryd8QAAF2HeBoCGsbVsr1+/Xm3atNGiRYt0+vRp\njRkzxj9pV1RUKCcnR3l5eQoPD1dKSoqGDBmia665xs6IAIALMG8DQMPYehnJXXfdpYcffliSZBiG\nQkJC/GNFRUWKiYlR69at/Y9S/e6WPgCA4GDeBoCGsfXMdkREhCTJ4/Fo+vTp1R6V6vF4FBUVVe29\nHo+nzp9pGIZcLlfjhwUANPq8zZwN4Gpj+322T5w4oYceekipqakaPXq0f3lkZKS8Xq//tdfrrTaJ\nX47L5VJJyVlLsjZEdHQUuUwglznkMsfJuZqCxpy3mbPNIZd5Ts1GLnOcnMssWy8j+frrrzVp0iTN\nnDlT48aNqzYWGxur4uJinT59WuXl5dq5c6f69OljZzwAwEWYtwGgYWw9s/3iiy/qzJkzys3NVW5u\nriQpMTFR586dU1JSkmbPnq3JkyfLMAwlJCSoQ4cOdsYDAFyEeRsAGuaKeIKkU79mIFfgyGUOucxx\ncq6rkVN/F+QKnFNzSc7NRi5znJzLLB5qAwAAAFiEsg0AAABYhLINAAAAWISyDQAAAFiEsg0AATp8\n+JCWL/9tsGMAAJoQ2x9qAwBNVefON6pz5xuDHQMA0IRQtgHgMo4dK1ZOzjyFhITK5/PpZz8bq23b\nPta8eTlKTh6rm266WceOFatdu3bKynpGISEhwY4MAHAYyjaAJiE1NVybNjVkyrr03qg/+UmlVq8+\nd9k1duzYrm7deuj//t+HtWfPbh09esQ/9re/faHnn1+mDh2+rylTJunAgf3q2fOmBuQDAFyJuGYb\nAC5j1Kh4RUZGacaMaXrrrTXVzly3bt1GHTp8X5L0ve91UHl5WbBiAgAcjDPbAJqE2s5A16W+TyL7\n+OOPdPPNfTRp0gP64IONevnlXHXv3kOS5HK56p0HAHD1oGwDwGV07dpdWVlPacWKV+Tz+ZSQMF4H\nDuwLdiwAQBNC2QaAy+jY8TotW/ZKjWPr17/v//e8eTl2RQIANDFcsw0AAABYhLINAAAAWISyDQAA\nAFiEsg0AAABYhLINAAAAWISyDQAAAFiEsg0AAABYhLINAAAAWISH2gDAZZSVnVd29jx9+eWXqqio\n0MMPz9C6dW/rb3/7QlVVVUpO/rmGDv2p3n57rf7rv95Vs2bN1K1bdz3yyMxgRwcAOARlG0CT0Cp1\nnJpv+mO914+uYVnZT36qM6vzLrvO73//lr7//Ws1b16O/vrXY/rwwz+qTZs2mjt3vkpLvZo06V71\n7Xur3ntvg2bMmKVu3XronXfyVFlZqdBQplcAAGUbAC7r2LFi9ev3Y0nSD38Yo1OnTumWW26VJLVs\nGaHrr/+RvvjiuJ54Yq5ef/01nTjxvHr0uCmYkQEADkPZBtAk1HYGui7R0VEqKTlrer1OnX6kAwf2\na+DAO/XFF8e1adP7crvDNGjQYJWWelVUVKRrr71WK1b8hx57bI6aN2+uRx+dqs8+26M+ffrWOy8A\n4MpB2QaAy4iPv0c5OU9r6tQHVFVVpSVLfqO3316rKVMmq6ysTJMm3a+2bdspNvb/6KGH7lfLli0V\nHR2t7t17Bjs6AMAhKNsAcBnNmzdXZuavqi2rqUiPHj1Go0ePsSsWAKAJCcqt//bs2aO0tLRLlr/6\n6qsaOXKk0tLSlJaWpiNHjgQhHQDgQszZAFB/tp/Z/u1vf6v169crPDz8krG9e/dq4cKF6tmTr2AB\nwAmYswGgYWw/sx0TE6OlS5fWOLZv3z69/PLLSklJ0UsvvWRzMgDAxZizAaBhbD+zPXz4cB0/frzG\nsZEjRyo1NVWRkZGaOnWq8vPzNXjw4Dp/ZnR0VGPHbBTkModc5pDLHKfmcjrm7OAjl3lOzUYuc5ya\nyyzH/IGkYRiaMGGCoqK+3bGDBg3S/v37A5q463NLL6vV91ZjViOXOeQyh1zmNOUPEuZse5DLPKdm\nI5c5Ts5lVlD+QLImHo9Ho0aNktfrlWEY2r59O9cBAoBDMWcDQGCCfmZ7w4YNKi0tVVJSkjIyMpSe\nni63263+/ftr0KBBwY4HALgAczYAmOMyDMMIdoiGcurXDOQKHLnMIZc5Ts51NXLq74JcgXNqLsm5\n2chljpNzmeWYy0gAAACAKw1lGwAAALAIZRsAAACwCGUbAAAAsAhlGwAAALAIZRsAAACwCGUbAAAA\nsAhlGwAAALAIZRsAAACwCGUbAAAAsAhlGwAAALAIZRsAAACwCGUbAAAAsAhlGwAAALAIZRsAAACw\nCGUbAAAAsAhlGwAAALAIZRsAAACwCGUbAAAAsAhlGwAAALAIZRsAAACwCGUbAAAAsAhlGwAAALAI\nZRsAAACwCGUbAAAAsAhlGwAAALBIUMr2nj17lJaWdsnyzZs3KyEhQUlJSVqzZk0QkgEALsacDQD1\nF2r3Bn/7299q/fr1Cg8Pr7a8oqJCOTk5ysvLU3h4uFJSUjRkyBBdc801dkcEAPwTczYANIztZ7Zj\nYmK0dOnSS5YXFRUpJiZGrVu3ltvtVt++fbVjxw674wEALsCcDQANY/uZ7eHDh+v48eOXLPd4PIqK\nivK/joiIkMfjCehnRkdH1f2mICCXOeQyh1zmODWX0zFnBx+5zHNqNnKZ49RcZtleti8nMjJSXq/X\n/9rr9VabyGtTUnLWqlj1Fh0dRS4TyGUOucxxcq6mijnbHuQyz6nZyGWOk3OZ5Zi7kcTGxqq4uFin\nT59WeXm5du7cqT59+gQ7FgCgBszZABCYoJ/Z3rBhg0pLS5WUlKTZs2dr8uTJMgxDCQkJ6tChQ7Dj\nAQAuwJwNAOa4DMMwgh2ioZz6NQO5Akcuc8hljpNzXY2c+rsgV+CcmktybjZymePkXGY55jISAAAA\n4EpD2QYAAAAsQtkGAAAALELZBgAAACxC2QYAAAAsQtkGAAAALELZBgAAACxC2QYAAAAsQtkGAAAA\nLELZBgAAACxC2QYAAAAsQtkGAAAALELZBgAAACxC2QYAAAAsQtkGAAAALELZBgAAACxC2QYAAAAs\nQtkGAAAALELZBgAAACxC2QYAAAAsQtkGAAAALELZBgAAACxC2QYAAAAsQtkGAAAALELZBgAAACxC\n2QYAAAAsEmrnxnw+nzIzM3Xo0CG53W5lZWWpU6dO/vFXX31Va9euVbt27SRJ8+bN0w033GBnRADA\nBZi3AaBhbC3bmzZtUnl5ud58800VFhZqwYIFWrZsmX987969WrhwoXr27GlnLADAZTBvA0DD2Fq2\nd+3apYEDB0qSevfurb1791Yb37dvn15++WWVlJTozjvv1C9+8Qs74wEALsK8DQANY2vZ9ng8ioyM\n9L8OCQlRZWWlQkO/jTFy5EilpqYqMjJSU6dOVX5+vgYPHlznz42OjrIsc0OQyxxymUMuc5yay+ms\nmLed+rsglzlOzSU5Nxu5zHFqLrNsLduRkZHyer3+1z6fzz9hG4ahCRMmKCrq2x07aNAg7d+/P6Cy\nXVJy1prADRAdHUUuE8hlDrnMcXIup7Ni3nbq74JcgXNqLsm52chljpNzmWXr3Uji4uK0ZcsWSVJh\nYaG6dOniH/N4PBo1apS8Xq8Mw9D27du5BhAAgox5GwAaxtYz28OGDdPWrVuVnJwswzCUnZ2tDRs2\nqLS0VElJScrIyFB6errcbrf69++vQYMG2RkPAHAR5m0AaBiXYRhGsEM0lFO/ZiBX4MhlDrnMcXKu\nq5FTfxfkCpxTc0nOzUYuc5ycyyweagMAAABYhLINAAAAWISyDQAAAFiEsg0AAABYhLINAAAAWMRU\n2f7mm2+sygEAAABccQIq2wcOHNBdd92l+Ph4nTx5UsOGDdO+ffuszgYAAAA0aQGV7aysLL3wwgtq\n06aNOnTooMzMTD311FNWZwMAAACatIDK9rlz5xQbG+t/PWDAAJWXl1sWCgAAALgSBFS227Rpo4MH\nD8rlckmS1q9fr9atW1saDAAAAGjqQgN5U2ZmpmbNmqXDhw/rlltuUadOnbRo0SKrswEAAABNWkBl\nOyYmRq+//rpKS0vl8/kUGRlpdS4AAACgyau1bKelpfkvHanJypUrGz0QAAAAcKWotWxPmzZNkrRm\nzRq1aNFCY8aMUWhoqN59912VlZXZEhAAAABoqmot27feeqskaeHChXrrrbf8y3v37q177rnH2mQA\nAABAExfQ3UjKysr0l7/8xf/60KFDqqystCwUAAAAcCUI6A8kZ8+erbS0NHXo0EE+n09///vftWTJ\nEquzAQAAAE1aQGX79ttv1+bNm/X555/L5XLpxhtvVGhoQKsCAAAAV62AGvOcOXNqXJ6Tk9OoYQAA\nAIArSUBl+7s/lJSkyspKffjhh7rhhhssCwUAAABcCQIq22PHjq32ety4cUpJSbEkEAAAAHClCOhu\nJBcrKirSV1991dhZAAAAgCtKQGe2u3bt6n+SpGEYateunR599FFLgwEAAABNXUBl++DBg5csKy8v\nb/QwAAAAwJUkoMtIkpKSqr32+XxKSEiwJBAAAABwpaj1zHZ6ero++eQTSd9eSuJfKTRUQ4YMsTYZ\nAAAA0MTVWrZXrlwpScrKytKTTz7Z4I35fD5lZmbq0KFDcrvdysrKUqdOnfzjmzdv1gsvvKDQ0FAl\nJCRo/PjxDd4mAFwtSktL1bJly0b9mczbANAwtZbt/Px8DR48WD169NDvf//7S8bHjBljamObNm1S\neXm53nzzTRUWFmrBggVatmyZJKmiokI5OTnKy8tTeHi4UlJSNGTIEF1zzTWmtgEAV6v4+Hjl5OTo\nlltuabSfybwNAA1T6zXbn332mSTpk08+0fbt2y/5z6xdu3Zp4MCBkqTevXtr7969/rGioiLFxMSo\ndevWcrvd6tu3r3bs2GF6GwBwtXrqqac0Z84cLVy4sNH+iJ15GwAaptYz29OnT5fUeI9l93g8ioyM\n9L8OCQlRZWWlQkND5fF4FBUV5R+LiIiQx+MJ6OdGR0fV/aYgIJc55DKHXOY4NVdjuv3227V+/Xo9\n//zzGjdunObOnatrr73WP37hvwNlxbzt1N8Fucxxai7JudnIZY5Tc5kV0K3//vSnP+m5557TN998\nI8Mw/Ms//PBDUxuLjIyU1+v1v/b5fAoNDa1xzOv1VpvEa1NSctZUDjtER0eRywRymUMuc5ycq7GF\nh4fr4Ycf1pdffqkpU6aoVatWMgxDLpfL9JwtWTNvO/V3Qa7AOTWX5Nxs5DLHybnMCqhsZ2Vlafbs\n2ercubP/4Tb1ERcXp/z8fI0YMUKFhYXq0qWLfyw2NlbFxcU6ffq0WrZsqZ07d2ry5Mn13hYAXI3+\n+7//W08//bRuv/125efnVzsrXR/M2wDQMAGV7bZt22rw4MEN3tiwYcO0detWJScnyzAMZWdna8OG\nDSotLVVSUpJmz56tyZMnyzAMJSQkqEOHDg3eJgBcLaZPn679+/frV7/6lfr3798oP5N5GwAaJqCy\n3bdvX+Xk5GjgwIFq3ry5f/m//Mu/mNpYs2bN9PTTT1dbFhsb6//3kCFDuH83ANRTdHS01q9f36i3\n/2PeBoCGCahsf/rpp5Kk/fv3+5e5XC7/fbgBAMH3y1/+MtgRAAAXCahsr1q1yuocAAAAwBUnoLKd\nnp5e7bXL5VKLFi10ww036MEHH1Tr1q0tCQcAAAA0ZQGV7djYWP+jeCXp3Xff1ZdffqkOHTro3/7t\n3/Tv//7vloYEAAAAmqKAyvaePXv09ttv+1937dpVCQkJWrx4cY2PcQcAAABQx+Pav1NRUaHDhw/7\nXx8+fFg+n0/nz59XRUWFZeEAAACApiygM9tPPvmk7r//frVv314+n09nzpzRM888o6VLlyo+Pt7q\njAAAAECTFFDZvu2227Rp0yZ9/vnnatasmWJjYxUWFqa4uLgGPVESAAAAuJIFVLaPHDmi1atXq7S0\nVIZhyOfz6fjx4/rP//xPq/MBAAAATVZA12xnZGSoVatWOnDggLp166ZTp06pc+fOVmcDAAAAmrSA\nzmz7fD5Nnz5dlZWV6t69u5KTk5WcnGx1NgAAAKBJC+jMdnh4uMrLy3X99ddr3759crvdKisrszob\nAAAA0KQFVLbj4+P14IMP6s4779Rrr72mf/3Xf9X3v/99q7MBAAAATVqtl5F898CaiIgIDR48WFu2\nbNHYsWPVsmVLhYWF2RIQAAAAaKpqLduzZ89W+/bt1b9/f8o1AAAAYFKtZfudd97Re++9p61bt6pr\n164aMWKEfvzjH6tZs4CuPgEAAACuarWW7W7duqlbt26aMWOGPvvsM7333nv69a9/rZ49e2rkyJG6\n7bbb7MoJAAAANDkB3fpPkm666SbddNNN2rlzpxYvXqwNGzZo9+7dVmYDAAAAmrQ6y7ZhGNqxY4c2\nbtyoLVu2qFu3bkpLS9PgwYPtyAcAAAA0WbWW7aeeekp/+tOf1L17d91999167LHH1LJlS7uyAQAA\nAE1arWX7zTffVJs2bbR//37t379fv/71r6uNf/jhh5aGAwAAAJqyWss2ZRoAAACov1rLdseOHe3K\nAQAAAFxxuGE2AAAAYBHKNgAAAGARyjYAAABgkYAfatMYzp8/r5kzZ+rUqVOKiIjQwoUL1a5du2rv\nycrKUkFBgSIiIiRJubm5ioqKsjMmAEDM2QDQGGwt26+//rq6dOmiadOm6Q9/+INyc3P15JNPVnvP\nvn379Lvf/e6SCR0AYC/mbABoOFsvI9m1a5cGDhwoSbrjjju0bdu2auM+n0/FxcWaO3eukpOTlZeX\nZ2c8AMAFmLMBoOEsO7O9du1arVixotqy9u3b+79ejIiI0NmzZ6uNl5aW6t5779V9992nqqoqpaen\nq2fPnuratWut24qOduZXluQyh1zmkMscp+ZyCuZscpnl1FySc7ORyxyn5jLLsrKdmJioxMTEasum\nTp0qr9crSfJ6vWrVqlW18fDwcKWnpys8PFyS1K9fPx08eLDOibuk5Gyt48EQHR1FLhPIZQ65zHFy\nLqdgznbuMUIuc5yajVzmODmXWbZeRhIXF6ePPvpIkrRlyxb17du32vjRo0eVkpKiqqoqVVRUqKCg\nQD169LAzIgDgn5izAaDhbP0DyZSUFM2aNUspKSkKCwvTkiVLJEnLly9XTEyMhg4dqvj4eI0fP15h\nYWGKj49X586d7YwIAPgn5mwAaDiXYRhGsEM0lFO/ZiBX4MhlDrnMcXKuq5FTfxfkCpxTc0nOzUYu\nc5ycyyweagMAAABYhLINAAAAWISyDQAAAFiEsg0AAABYhLINAAAAWISyDQAAAFiEsg0AAABYhLIN\nAAAAWISyDQAAAFiEsg0AAABYhLINAAAAWISyDQAAAFiEsg0AAABYhLINAAAAWISyDQAAAFiEsg0A\nAABYhLINAAAAWISyDQAAAFiEsg0AAABYhLINAAAAWISyDQAAAFiEsg0AAABYhLINAAAAWISyDQAA\nAFiEsg0AAABYhLINAAAAWCQoZfuDDz7QjBkzahxbs2aN7rnnHo0fP175+fk2JwMAXIw5GwDqL9Tu\nDWZlZenjjz9Wt27dLhkrKSnRqlWr9NZbb6msrEypqakaMGCA3G633TEBAGLOBoCGsv3MdlxcnDIz\nM2sc+/TTT9WnTx+53W5FRUUpJiZGBw8etDcgAMCPORsAGsayM9tr167VihUrqi3Lzs7WiBEjtH37\n9hrX8Xg8ioqK8r+OiIiQx+Opc1vR0VF1vicYyGUOucwhlzlOzeUUzNnkMsupuSTnZiOXOU7NZZZl\nZTsxMVGJiYmm1omMjJTX6/W/9nq91SbyyykpOWs6n9Wio6PIZQK5zCGXOU7O5RTM2c49RshljlOz\nkcscJ+cyy1F3I+nVq5d27dqlsrIynT17VkVFRerSpUuwYwEAasCcDQB1s/0PJGuyfPlyxcTEaOjQ\noUpLS1NqaqoMw1BGRoaaN28e7HgAgAswZwNA4FyGYRjBDtFQTv2agVyBI5c55DLHybmuRk79XZAr\ncE7NJTk3G7nMcXIusxx1GQkAAABwJaFsAwAAABahbAMAAAAWoWwDAAAAFqFsAwAAABahbAMAAAAW\noWwDAAAAFqFsAwAAABahbAMAAAAWoWwDAAAAFqFsAwAAABahbAMAAAAWoWwDAAAAFqFsAwAAABah\nbAMAAAAWoWwDAAAAFqFsAwAAABahbAMAAAAWoWwDAAAAFqFsAwAAABahbAMAAAAWoWwDAAAAFqFs\nAwAAABahbAMAAAAWoWwDAAAAFqFsAwAAABYJDcZGP/jgA23cuFFLliy5ZCwrK0sFBQWKiIiQJOXm\n5ioqKsruiACAf2LOBoD6s71sZ2Vl6eOPP1a3bt1qHN+3b59+97vfqV27djYnAwBcjDkbABrG9stI\n4uLilJmZWeOYz+dTcXGx5s6dq+TkZOXl5dkbDgBQDXM2ADSMZWe2165dqxUrVlRblp2drREjRmj7\n9u01rlNaWqp7771X9913n6qqqpSenq6ePXuqa9eutW4rOtqZX1mSyxxymUMuc5yayymYs8llllNz\nSc7NRi5znJrLLMvKdmJiohITE02tEx4ervT0dIWHh0uS+vXrp4MHD9Y5cZeUnK13TqtER0eRywRy\nmUMuc5ycyymYs517jJDLHKdmI5c5Ts5llqPuRnL06FGlpKSoqqpKFRUVKigoUI8ePYIdCwBQA+Zs\nAKhbUO5GcrHly5crJiZGQ4cOVXx8vMaPH6+wsDDFx8erc+fOwY4HALgAczYABM5lGIYR7BAN5dSv\nGcgVOHKZQy5znJzrauTU3wW5AufUXJJzs5HLHCfnMstRl5EAAAAAVxLKNgAAAGARyjYAAABgEco2\nAAAAYBHKNgAAAGARyjYAAABgEco2AAAAYBHKNgAAAGARyjYAAABgEco2AAAAYBHKNgAAAGARyjYA\nAABgEco2AAAAYBHKNgAAAGARyjYAAABgEco2AAAAYBHKNgAAAGARyjYAAABgEco2AAAAYBHKNgAA\nAGARyjYAAABgEco2AAAAYBHKNgAAAGARyjYAAABgEco2AAAAYBFby/bZs2f14IMP6t5771VSUpJ2\n7959yXsPvNHhAAALo0lEQVTWrFmje+65R+PHj1d+fr6d8QAAF2DOBoCGC7VzY8uXL1e/fv00ceJE\nHTlyRDNmzNA777zjHy8pKdGqVav01ltvqaysTKmpqRowYIDcbredMQEAYs4GgMZga9meOHGifxKu\nqqpS8+bNq41/+umn6tOnj9xut9xut2JiYnTw4EH16tXLzpgAADFnA0BjsKxsr127VitWrKi2LDs7\nW7169VJJSYlmzpypJ554otq4x+NRVFSU/3VERIQ8Ho9VEQEA/8ScDQDWsKxsJyYmKjEx8ZLlhw4d\n0qOPPqrHH39ct956a7WxyMhIeb1e/2uv11ttIr+c6Oi63xMM5DKHXOaQyxyn5nIK5mxymeXUXJJz\ns5HLHKfmMsvWP5D885//rIcfflhLlizRoEGDLhnv1auXdu3apbKyMp09e1ZFRUXq0qWLnREBAP/E\nnA0ADecyDMOwa2NTpkzRoUOH1LFjR0nfnhVZtmyZli9frpiYGA0dOlRr1qzRm2++KcMw9Itf/ELD\nhw+3Kx4A4ALM2QDQcLaWbQAAAOBqwkNtAAAAAItQtgEAAACLULYBAAAAi9j6UBuzfD6fMjMzdejQ\nIbndbmVlZalTp07+8c2bN+uFF15QaGioEhISNH78+DrXsSPXu+++qxUrVigkJERdunRRZmammjVr\nprFjxyoyMlKSdN111yknJ8fWXK+++qrWrl2rdu3aSZLmzZun66+/Pqj7q6SkRI8++qj/vQcOHNCM\nGTOUkpJi+f76zp49e7R48WKtWrWq2vJgHV915QrW8VVXrmAdX7XlCubxVVFRoSeeeEJffPGFysvL\nNWXKFA0dOtQ/Huzjyy5nz57VzJkz5fF4VFFRodmzZ6tPnz7V3rNmzRq98cYbCg0N1ZQpUzR48GDb\n8n3wwQfauHGjlixZcslYVlaWCgoKFBERIUnKzc0N6NaGVucKxv46f/68Zs6cqVOnTikiIkILFy70\n/3/9O3bur/r0AzvU53P4hhtusCWbZO7zzk5mPlfs2F/1mb9rZTjY+++/b8yaNcswDMPYvXu38eCD\nD/rHysvLjZ/85CfG6dOnjbKyMuOee+4xSkpKal3Hjlznzp0zhg4dapSWlhqGYRgZGRnGpk2bjPPn\nzxvx8fGNniXQXIZhGDNmzDA+++wzU+vYkes7BQUFRlpamlFZWWnL/jIMw3j55ZeNUaNGGYmJidWW\nB/P4qi1XMI+v2nIZRvCOr7pyfcfu4ysvL8/IysoyDMMw/vGPfxiDBg3yjwX7+LLT888/byxfvtww\nDMMoKioyxowZU238q6++MkaNGmWUlZUZZ86c8f/bDvPnzzeGDx9uPPLIIzWOJycnG6dOnbIly4Vq\nyxWs/fUf//Efxm9+8xvDMAzj3XffNebPn3/Je+zcX/XpB8HOZRg1z5N2Mft5F+xchhG8/VWf+bs2\njr6MZNeuXRo4cKAkqXfv3tq7d69/rKioSDExMWrdurXcbrf69u2rHTt21LqOHbncbrfeeOMNhYeH\nS5IqKyvVvHlzHTx4UOfOndOkSZOUnp6uwsJCW3NJ0r59+/Tyyy8rJSVFL730UkDr2JFLkgzD0Pz5\n85WZmamQkBBb9pckxcTEaOnSpZcsD+bxVVuuYB5fteWSgnd81ZVLCs7xddddd+nhhx/2bz8kJMQ/\nFuzjy04TJ05UcnKypLof+R4VFeV/5Lsd4uLilJmZWeOYz+dTcXGx5s6dq+TkZOXl5dmSqa5cwdpf\nFx6bd9xxh7Zt21Zt3O79VZ9+YIf6fA7bxeznXbBzScHbX/WZv2vj6MtIPB6P/2teSQoJCVFlZaVC\nQ0Mv+5jg2taxI1ezZs10zTXXSJJWrVql0tJSDRgwQJ9//rkmT56sxMREHT16VPfff782btxoWy5J\nGjlypFJTUxUZGampU6cqPz8/6PvrO5s3b1bnzp39Xw+1aNHC8v0lScOHD9fx48drzBys46u2XME8\nvmrLJQXv+KorlxSc4+u7r9I9Ho+mT5+uRx55xD8W7OPLKk595Pvlco0YMULbt2+vcZ3S0lLde++9\nuu+++1RVVaX09HT17NlTXbt2DWquYO2v9u3b+7cbERGhs2fPVhu3Y39dqD79wA71+Ry267Ips593\ndjH7uWLH/qrP/F0bR8/gFz8K2Ofz+Q/Yyz0muLZ17Mj13etFixbpL3/5i5YuXSqXy6Uf/ehH6tSp\nk//fbdq0UUlJiX7wgx/YksswDE2YMMF/gAwaNEj79+93xP6SpPXr1ys9Pd3/2o79ZSazncdXXYJ1\nfNUmmMdXIIJ1fJ04cUIPPfSQUlNTNXr0aP9yJx9fDWHnI98bI1dtwsPDlZ6e7v8WqV+/fjp48GCj\nlsf65ArW/po6dap/u16vV61atao2bsf+ulB9+oEd6vM5bOffKNQkmPurNsHeX2bn79o4+jKSuLg4\nbdmyRZJUWFhY7THAsbGxKi4u1unTp1VeXq6dO3eqT58+ta5jRy5Jmjt3rsrKypSbm+ufePLy8rRg\nwQJJ0smTJ+XxeBQdHW1bLo/Ho1GjRsnr9cowDG3fvl09e/Z0xP6SpL179youLs7/2o79VZtgHl91\nCdbxVZtgHl+BCMbx9fXXX2vSpEmaOXOmxo0bV23MycdXY2uqj3w/evSoUlJSVFVVpYqKChUUFKhH\njx7BjhW0/RUXF6ePPvpIkrRlyxb17du32rjd+6s+/cAO9fkcDrZg7q/aBHN/1Wf+ro2jT5kMGzZM\nW7duVXJysgzDUHZ2tjZs2KDS0lIlJSVp9uzZmjx5sgzDUEJCgjp06FDjOnbm6tmzp/Ly8nTLLbdo\nwoQJkqT09HSNGzdOc+bMUUpKilwul7Kzsxv9jFVd+ysjI0Pp6elyu93q37+/Bg0aJJ/PF9T9lZSU\npL///e+KjIyUy+Xyr2PH/qqJE46v2nIF8/iqLVcwj6+6cgXr+HrxxRd15swZ5ebmKjc3V9K3ZwzP\nnTvnuOPLSkuWLFF5ebl+9atfSar5ke9paWlKTU2VYRjKyMi45LpuO12YKz4+XuPHj1dYWJji4+PV\nuXNnR+QKxv5KSUnRrFmzlJKSorCwMP9dUoK1v+rTD+xQn8/hYHHC/qorV7D2V33m79rwuHYAAADA\nIo6+jAQAAABoyijbAAAAgEUo2wAAAIBFKNsAAACARSjbAAAAgEUcfes/oLHNmzdPBQUFqqio0LFj\nxxQbGytJSkpKksvlUkpKSpATAgDM2L59u2bMmKF169apffv2kqRXXnlFhYWFl30MOGAnbv2Hq9Lx\n48eVnp6uzZs3BzsKAKCBFi5cqKNHj2rZsmUqLCzU448/rry8vEuedAkEA2e2Acl/9mPatGkaMGCA\nBg8erJ07dyo6OlqpqalatWqVvvzySy1YsEC33nqriouLlZmZqdOnT6tFixb65S9/qe7duwf5fwUA\nXJ0yMjKUmJiolStX6rXXXtPChQsp2nAMrtkGLvL111/rzjvv1MaNGyVJmzZt0urVqzVt2jStWLFC\nkjRr1izNnDlT77zzjubPn6+MjIxgRgaAq5rb7dbixYu1YMECjRgxwhGPGwe+w5ltoAZ33HGHJKlj\nx47q27evJOnaa6/VmTNn5PV6tXfvXs2ZM8f//tLSUv3jH/9Q27Ztg5IXAK52BQUFatu2rbZt26ap\nU6cqNJSKA2fgzDZQA7fb7f93SEhItTGfzye3261169b5/1u7dq3atGljd0wAgKQ///nPWrp0qd54\n4w253W4tW7Ys2JEAP8o2YFJUVJSuv/56rVu3TpK0detW/fznPw9yKgC4OpWVlSkjI0MzZ87UD3/4\nQy1YsECvvfaaCgsLgx0NkETZBupl0aJFysvL0+jRo7VkyRI9++yzcrlcwY4FAFed7OxsdenSRfHx\n8ZK+vfxvzpw5mjlzprxeb5DTAdz6DwAAALAMZ7YBAAAAi1C2AQAAAItQtgEAAACLULYBAAAAi1C2\nAQAAAItQtgEAAACLULYBAAAAi1C2AQAAAIv8PwhBFE52BhVwAAAAAElFTkSuQmCC\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "\n", "# create a figure and axes\n", "fig = plt.figure(figsize=(12,5))\n", "ax1 = plt.subplot(1,2,1) \n", "ax2 = plt.subplot(1,2,2)\n", "\n", "# set up the subplots as needed\n", "ax1.set_xlim(( 0, 2)) \n", "ax1.set_ylim((-2, 2))\n", "ax1.set_xlabel('Time')\n", "ax1.set_ylabel('Magnitude')\n", "\n", "ax2.set_xlim((-2,2))\n", "ax2.set_ylim((-2,2))\n", "ax2.set_xlabel('X')\n", "ax2.set_ylabel('Y')\n", "ax2.set_title('Phase Plane')\n", "\n", "# create objects that will change in the animation. These are\n", "# initially empty, and will be given new values for each frame\n", "# in the animation.\n", "txt_title = ax1.set_title('')\n", "line1, = ax1.plot([], [], 'b', lw=2) # ax.plot returns a list of 2D line objects\n", "line2, = ax1.plot([], [], 'r', lw=2)\n", "pt1, = ax2.plot([], [], 'g.', ms=20)\n", "line3, = ax2.plot([], [], 'y', lw=2)\n", "\n", "ax1.legend(['sin','cos']);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Step 2. Define a function to draw each frame\n", "\n", "The animiation function is a function you define to draw individual frames in the animation. The variable `n` will be the frame number. The function draws the frame by resetting the data values for the global objects `txt_title`, `line1`, `line2`, `pt1`, and `line3` that were defined above." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# animation function. This is called sequentially\n", "def drawframe(n):\n", " x = np.linspace(0, 2, 1000)\n", " y1 = np.sin(2 * np.pi * (x - 0.01 * n))\n", " y2 = np.cos(2 * np.pi * (x - 0.01 * n))\n", " line1.set_data(x, y1)\n", " line2.set_data(x, y2)\n", " line3.set_data(y1[0:50],y2[0:50])\n", " pt1.set_data(y1[0],y2[0])\n", " txt_title.set_text('Frame = {0:4d}'.format(n))\n", " return (line1,line2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Step 3. Create the Animation Object\n", "\n", "The animation class includes a function `FuncAnimation` that incorporations a user-specified function to update the figure for each frame of the animation. The result is an animation object which is subsequently called to actually produce the desired animation." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "from matplotlib import animation\n", "\n", "# blit=True re-draws only the parts that have changed.\n", "anim = animation.FuncAnimation(fig, drawframe, frames=100, interval=20, blit=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Step 4. Render the Animation\n", "\n", "The final step is to actually render and display the desired animation. This is the compute-intensive step in the procedure. The next cell imports `HTML` which is used to display a HTML elements created in a python script. The animation is rendered to html5 video with the `to_html5_video()` function and then displayed with `HTML()`." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.display import HTML\n", "HTML(anim.to_html5_video())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An alternative to the use of HTML is to set the default rendering of an animation object. " ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from matplotlib import rc\n", "\n", "# equivalent to rcParams['animation.html'] = 'html5'\n", "rc('animation', html='html5')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once the default rendering of the animation object is set to html5, all you have to do is ask to display the animation object. Everything is then done behind the scenes to render and display the desired animation." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "anim" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## Example: Phase Plane Animation for an Exothermic Stirred-Tank Reactor\n", "\n", "This is more complex example that demonstrates the complex dynamics of an exothermic continuous stirred-tank reactor when operated with an unstable steady state. The computational strategy is to compute solutions to the dynamical model for a set of initial conditions, then step through the solutions simulataneously to draw frames in the animation." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import seaborn as sns\n", "from scipy.integrate import odeint\n", "\n", "from matplotlib import animation\n", "from IPython.display import HTML\n", "\n", "# mode parameteres\n", "Ea = 72750 # activation energy J/gmol\n", "R = 8.314 # gas constant J/gmol/K\n", "k0 = 7.2e10 # Arrhenius rate constant 1/min\n", "V = 100.0 # Volume [L]\n", "rho = 1000.0 # Density [g/L]\n", "Cp = 0.239 # Heat capacity [J/g/K]\n", "dHr = -5.0e4 # Enthalpy of reaction [J/mol]\n", "UA = 5.0e4 # Heat transfer [J/min/K]\n", "q = 100.0 # Flowrate [L/min]\n", "cAi = 1.0 # Inlet feed concentration [mol/L]\n", "Ti = 350.0 # Inlet feed temperature [K]\n", "cA0 = 0.5; # Initial concentration [mol/L]\n", "T0 = 350.0; # Initial temperature [K]\n", "Tc = 305.0 # Coolant temperature [K]\n", "\n", "# Arrhenius rate expression\n", "def k(T):\n", " return k0*np.exp(-Ea/R/T)\n", "\n", "def deriv(y,t):\n", " cA,T = y\n", " dcA = (q/V)*(cAi - cA) - k(T)*cA\n", " dT = (q/V)*(Ti - T) + (-dHr/rho/Cp)*k(T)*cA + (UA/V/rho/Cp)*(Tc-T)\n", " return [dcA,dT]\n", "\n", "# create a set of initial conditions\n", "ICs = [[cA0,T0] for cA0 in [0] for T0 in np.linspace(295,480,19)]\n", "ICs += [[cA0,T0] for cA0 in np.linspace(0,1,21) for T0 in [290]]\n", "ICs += [[cA0,T0] for cA0 in [1] for T0 in np.linspace(295,475,18)]\n", "\n", "# perform simulations for each of the initial conditions\n", "t = np.linspace(0,10.0,800)\n", "sols = [odeint(deriv,IC,t) for IC in ICs]\n", "\n", "# create background figure and axes\n", "sns.set(font_scale=1.5)\n", "fig, ax = plt.subplots(figsize=(8,8))\n", "ax.set_xlim((0,1))\n", "ax.set_ylim((290,480))\n", "ax.set_xlabel('Concentration [gmol/liter]')\n", "ax.set_ylabel('Temperature [K]')\n", "ax.set_title('Exothermic Reactor with Tc = {0:.1f} K'.format(Tc))\n", "\n", "# create lists of colors, points, and lines\n", "colors = sns.color_palette(\"husl\",len(sols))\n", "pts = sum([ax.plot([],[],'o',color=colors[k],ms=15) for k in range(0,len(sols))],[])\n", "lines = sum([ax.plot([],[],color=colors[k],lw=2) for k in range(0,len(sols))],[])\n", "\n", "# don't show the plain background\n", "plt.close()\n", "\n", "# define function to draw each frame\n", "def drawframe(n):\n", " for k in range(0,len(sols)):\n", " C,T = sols[k].T\n", " pts[k].set_data(C[n],T[n])\n", " lines[k].set_data(C[:n],T[:n])\n", " return pts + lines\n", "\n", "# create animiation object and render in HTML video\n", "anim = animation.FuncAnimation(fig, drawframe, frames=len(t), interval=20, blit=True)\n", "HTML(anim.to_html5_video())" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "< [Modular Simulation using Python Generators](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/A.02-Modular-Approach-to-Simulation-using-Python-Generators.ipynb) | [Contents](toc.ipynb) | [A Modular Approach to Simulation using Python Generators](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/A.04-Modular-Simulation-using-Python-Generators.ipynb) >

\"Open

\"Download\"" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.7.4" } }, "nbformat": 4, "nbformat_minor": 2 }