{ "cells": [ { "cell_type": "markdown", "id": "8bf0c3d7", "metadata": {}, "source": [ "# Double pendulum\n", "\n", "This example uses the `sequence()` and `subdraw()` animations to animate `plot` plot objects.\n", "\n", "This example is a Diplotocus implementation of [this animation from the matplotlib docs](https://matplotlib.org/stable/gallery/animation/double_pendulum.html).\n", "\n", "We first define a function `get_pendulum_positions()` that returns the double pendulum nodes positions for a range of times." ] }, { "cell_type": "code", "execution_count": 1, "id": "ef3943c0", "metadata": { "tags": [ "hide-input" ] }, "outputs": [], "source": [ "import numpy as np\n", "\n", "def get_pendulum_positions(G,L1,L2,M1,M2,t_stop):\n", " def derivs(state):\n", " dydx = np.zeros_like(state)\n", "\n", " dydx[0] = state[1]\n", "\n", " delta = state[2] - state[0]\n", " den1 = (M1+M2) * L1 - M2 * L1 * np.cos(delta) * np.cos(delta)\n", " dydx[1] = ((M2 * L1 * state[1] * state[1] * np.sin(delta) * np.cos(delta)\n", " + M2 * G * np.sin(state[2]) * np.cos(delta)\n", " + M2 * L2 * state[3] * state[3] * np.sin(delta)\n", " - (M1+M2) * G * np.sin(state[0]))\n", " / den1)\n", "\n", " dydx[2] = state[3]\n", "\n", " den2 = (L2/L1) * den1\n", " dydx[3] = ((- M2 * L2 * state[3] * state[3] * np.sin(delta) * np.cos(delta)\n", " + (M1+M2) * G * np.sin(state[0]) * np.cos(delta)\n", " - (M1+M2) * L1 * state[1] * state[1] * np.sin(delta)\n", " - (M1+M2) * G * np.sin(state[2]))\n", " / den2)\n", "\n", " return dydx\n", "\n", " # create a time array from 0..t_stop sampled at 0.01 second steps\n", " dt = 0.01\n", " t = np.arange(0, t_stop, dt)\n", "\n", " # th1 and th2 are the initial angles (degrees)\n", " # w10 and w20 are the initial angular velocities (degrees per second)\n", " th1 = 120.0\n", " w1 = 0.0\n", " th2 = -10.0\n", " w2 = 0.0\n", "\n", " # initial state\n", " state = np.radians([th1, w1, th2, w2])\n", "\n", " # integrate the ODE using Euler's method\n", " y = np.empty((len(t), 4))\n", " y[0] = state\n", " for i in range(1, len(t)):\n", " y[i] = y[i - 1] + derivs(y[i - 1]) * dt\n", "\n", " x0 = np.zeros(len(t))\n", " y0 = np.zeros(len(t))\n", "\n", " x1 = L1*np.sin(y[:, 0])\n", " y1 = -L1*np.cos(y[:, 0])\n", "\n", " x2 = L2*np.sin(y[:, 2]) + x1\n", " y2 = -L2*np.cos(y[:, 2]) + y1\n", "\n", " return x0,x1,x2,y0,y1,y2" ] }, { "cell_type": "markdown", "id": "5ca684cb", "metadata": {}, "source": [ "Then we generate the positions, create a `plot()` object `p` that displays the double pendulum and another `plot()` object `trace` that displays the last 5 positions of its end node:" ] }, { "cell_type": "code", "execution_count": null, "id": "b8ecc773", "metadata": {}, "outputs": [], "source": [ "import diplotocus as dpl\n", "\n", "G = 9.8 # acceleration due to gravity, in m/s^2\n", "L1 = 1.0 # length of pendulum 1 in m\n", "L2 = 1.0 # length of pendulum 2 in m\n", "L = L1 + L2 # maximal length of the combined pendulum\n", "M1 = 1.0 # mass of pendulum 1 in kg\n", "M2 = 1.0 # mass of pendulum 2 in kg\n", "t_stop = 10 # how many seconds to simulate\n", "\n", "x0,x1,x2,y0,y1,y2 = get_pendulum_positions(G,L1,L2,M1,M2,t_stop)\n", "\n", "xs = np.stack((x0,x1,x2),axis=-1)\n", "ys = np.stack((y0,y1,y2),axis=-1)\n", "\n", "tl = dpl.Timeline(xlim=(-L,L),ylim=(-L,1))\n", "\n", "p = dpl.plot(xs,ys,marker='o',lw=2,ls='-',zorder=2)\n", "p.sequence(600)\n", "\n", "trace = dpl.plot(x2,y2,lw=4,ls='-',zorder=1)\n", "trace.subdraw(-5,0,600)\n", "\n", "tl.animate((p,trace))\n", "tl.save_video('../../_static/examples/pendulum.mp4')" ] }, { "cell_type": "code", "execution_count": 5, "id": "af93efc6", "metadata": { "tags": [ "remove-input" ] }, "outputs": [ { "data": { "text/html": [ "\n", " \n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from IPython.display import HTML, display\n", "display(HTML(\"\"\"\n", " \n", "\"\"\"))" ] } ], "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.13.3" } }, "nbformat": 4, "nbformat_minor": 5 }