{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Oscillations: Simple Pendulum\n",
"\n",
"This Notebook aims to demonstrate the behaviour a simple pendulum, freely oscillating.
\n",
"The pendulum may, or may not be damped.\n",
"\n",
"Its motion is governed by the second order ordinary differential equation (ODE):\n",
"\n",
"\\begin{equation}\n",
"\\ddot{\\theta} + \\frac{b}{m}\\dot{\\theta} + \\frac{g}{l}sin\\theta = 0\n",
"\\end{equation}\n",
"\n",
"Where $l$ is the length of the pendulum, $b$ is the damping constant, $m$ is the mass, and $g$ is the gravitational field strength."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Matplotlib will be used to generate the graphs and animations."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import matplotlib\n",
"matplotlib.use('tkagg')\n",
"from matplotlib import pyplot as plt\n",
"from matplotlib import animation\n",
"%matplotlib notebook\n",
"\n",
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The following are imported to provide the sliders and widgets."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from IPython.display import HTML\n",
"from IPython.display import display\n",
"import ipywidgets as widgets\n",
"from ipywidgets import interact, FloatSlider"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To do the integration of equation (1), we will use the function odeint from the scipy module."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from scipy.integrate import odeint"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In order to use odeint, one has to convert the second order equation, into a first order equation.\n",
"\n",
"This can be achieved by rewriting the SHM equation in terms of the angular frequency:\n",
"\n",
"\\begin{equation}\n",
" \\omega = \\dot{\\theta}\n",
"\\end{equation}\n",
"\n",
"To obtain:\n",
"\n",
"\\begin{equation}\n",
" \\dot{\\omega} = -\\frac{b}{m}\\omega - \\frac{g}{l}sin\\theta\n",
"\\end{equation}\n",
"\n",
"Now, we use this to define the function pendulum."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def pendulum(y, t, gamma, w0):\n",
" theta, omega = y\n",
" dydt = [omega, -gamma*omega - w0*np.sin(theta)]\n",
" return dydt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we have to set the initial conditions $\\theta_0$ and $\\omega _ 0$ of the pendulum.
\n",
"Let's say that these are 0.1$^c$ and 0 (the pendulum is at rest) respectively. Additionally, let the initial damping be zero.\n",
"\n",
"We'll also set the physical constants here too. If one wished, some constants can be imported from the scipy.constants library."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Physical Constants & Properties of the Pendulum\n",
"from scipy.constants import g\n",
"friction = 0\n",
"length = 0.5\n",
"mass = 1.0\n",
"\n",
"# Initial conditions\n",
"y0 = [-0.1*np.pi, 3.0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We need to know how long to simulate for. Let's aim for 1001 evenly spaced intervals of time."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"start_time = 0.0\n",
"end_time = 10.0\n",
"steps = 1001\n",
"\n",
"t = np.linspace(start_time, end_time, steps)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To solve the equations, we simply call odeint!"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"solution = odeint(pendulum, y0, t, args=(friction/mass, g/length))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Some definitions which will help us with the simulation."
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def kinetic_energy(m, v):\n",
" return 0.5 * m * v ** 2\n",
"\n",
"def potential_energy(m, th):\n",
" return m * g * length * (1 - np.cos(th))\n",
"\n",
"def array_arg(complex_array):\n",
" return np.angle(complex_array[0] + 1j*complex_array[1], deg=False)\n",
"\n",
"def array_abs(complex_array):\n",
" return np.sqrt(complex_array[0] ** 2 + complex_array[1] ** 2)"
]
},
{
"cell_type": "code",
"execution_count": 71,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('