diff --git a/PredictionQuestion/FreeFall-withCode.ipynb b/PredictionQuestion/FreeFall-withCode.ipynb new file mode 100644 index 0000000..78e8c48 --- /dev/null +++ b/PredictionQuestion/FreeFall-withCode.ipynb @@ -0,0 +1,474 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook is about demonstrating:\n", + "* prediction questions\n", + "* observation questions\n", + "\n", + "The example chosen is voluntarily *simple* so that anyone can understand what is illustrated and focus the pedagogical features of the example." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Free fall\n", + "\n", + "An object is dropped from a given height with no initial velocity. We are interested in the movement from the object in the case where *resistance from the air is ignored*.\n", + "\n", + "1. Answer the following prediction questions - be sure to write down your answer on a piece of paper:\n", + " * Which object would reach the ground first: a bowling ball (5 kg) or a tennis ball (0.05 kg)?\n", + " * Why? Describe in words your explanation for this behavior.\n", + " * Sketch your prediction for the *height* of the object as a function of time. Describe in words what this graph means.\n", + " * Sketch your prediction for the *velocity* of the object as a function of time. Describe in words what this graph means.\n", + " * Sketch your prediction for the *acceleartion* of the object as a function of time. Describe in words what this graph means.\n", + "\n", + "\n", + "2. Run the demo and answer the following observation questions :\n", + " * When does the bowling ball reaches the ground (time in seconds)?\n", + " * When does the tennis ball reaches the ground (time in seconds)?\n", + " * When does the ostrich feather reaches the ground (time in seconds)?\n", + "\n", + "\n", + "3. Compare the explanation provided in this notebook with your own explanation.\n", + "\n", + "To be fully convinced, look at the video at the end of this notebook!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Demo" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas\n", + "\n", + "from ipywidgets import interact, interactive, fixed, interact_manual\n", + "from ipywidgets import HBox, VBox, Label\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "plt.style.use('seaborn-whitegrid') # global style for plotting" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, let's create the list of objects with which we can experiment.\n", + "\n", + "Objects come with a name, a mass (in $kg$) and a color for identifying them in the graphical display." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
masscolor
name
Bowling ball5.000#DC143C
Tennis ball0.050#2E8B57
Ostrich feather0.005#483D8B
\n", + "
" + ], + "text/plain": [ + " mass color\n", + "name \n", + "Bowling ball 5.000 #DC143C\n", + "Tennis ball 0.050 #2E8B57\n", + "Ostrich feather 0.005 #483D8B" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Types of objects we have with their mass\n", + "objects = [{\n", + " 'name':'Bowling ball',\n", + " 'mass':5.0,\n", + " 'color':'#DC143C'\n", + " },{\n", + " 'name':'Tennis ball',\n", + " 'mass':0.05,\n", + " 'color':'#2E8B57'\n", + " },{ \n", + " 'name':'Ostrich feather',\n", + " 'mass':0.005,\n", + " 'color':'#483D8B'\n", + "}]\n", + "objects_list = pandas.DataFrame(objects, columns = ['name', 'mass', 'color'])\n", + "objects_list.set_index('name', inplace=True) # Index objects by their name to find them easily after\n", + "objects_list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then let's define the parameters of our problem:\n", + "* gravity\n", + "* initial conditions: initial height (in $m$) and initial velocity (in $m.s^{-1}$)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "# Parameters of the problem\n", + "g = 9.81 # gravity in m/s2\n", + "\n", + "# Initial conditions\n", + "h_0 = 5 # initial height in m\n", + "v_0 = 0 # initial velocity in m/s" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To plot the movement of our objects in time we need to define a time interval. " + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "# Time space\n", + "t = np.linspace(0,3,25) # time scale from 0 to x seconds, with n points in the interval" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then we define three functions which compute the movement of our objects in free fall on our time interval." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "# Functions representing the equations of the movement as functions of time and the problem parameters\n", + "def accel_time (t, m, h_0, v_0):\n", + " return [-g]*t.size # returning a list of same length as the time list filled with -g\n", + "\n", + "def veloc_time (t, m, h_0, v_0):\n", + " return -g*t+v_0 \n", + "\n", + "def height_time (t, m, h_0, v_0):\n", + " return -0.5*g*(t**2)+v_0*t+h_0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we create an interactive graph which will plot the equations of the objects depending on the parameters that the user selects." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3ea7624081bf4df29df0be5711fe38f4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Output(), HBox(children=(VBox(children=(HBox(children=(Label(value='Choice of object(s):'), Sel…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# parameters for sliders\n", + "h_min = 0\n", + "h_max = 10\n", + "v_min = 0\n", + "v_max = 10\n", + "\n", + "\n", + "# IHM input elements\n", + "h_label = Label('Initial height ($m$):')\n", + "h_widget = widgets.FloatSlider(min=h_min,max=h_max,step=1,value=h_0)\n", + "h_input = HBox([h_label, h_widget])\n", + "\n", + "v_label = Label('Initial velocity ($m.s^{-1}$):')\n", + "v_widget = widgets.FloatSlider(min=v_min,max=v_max,step=1,value=v_0)\n", + "v_input = HBox([v_label, v_widget])\n", + "\n", + "obj_m_label = Label('Choice of object(s):')\n", + "obj_m_widget = widgets.SelectMultiple(\n", + " options = objects_list.index,\n", + " value = [objects_list.index[0],], # tuple(objects_list['name'])\n", + " disabled = False\n", + ")\n", + "obj_m_input = HBox([obj_m_label, obj_m_widget])\n", + "\n", + "# IHM output elements\n", + "obj_m_output = widgets.Output()\n", + "graph_output = widgets.Output()\n", + "\n", + "# Display updated output function\n", + "def display_updated_output(h_0, v_0, objs):\n", + " # Clear outputs\n", + " graph_output.clear_output(wait=True)\n", + " obj_m_output.clear_output(wait=True)\n", + " \n", + " # Create the figure\n", + " fig, ax = plt.subplots(1, 3, sharex='col', figsize=(16, 4))\n", + "\n", + " # for each object selected (by name)\n", + " for o in objs:\n", + " # get mass\n", + " m = objects_list.loc[objects_list.index == o]['mass'].item()\n", + " # get color\n", + " c = objects_list.loc[objects_list.index == o]['color'].item()\n", + " \n", + " \n", + " # Recompute equations with parameters set by the user\n", + " h_t = height_time(t, m, h_0, v_0)\n", + " v_t = veloc_time(t, m, h_0, v_0)\n", + " a_t = accel_time(t, m, h_0, v_0)\n", + "\n", + " # Plot equations\n", + " ax[0].set_title('Height ($m$)')\n", + " ax[0].plot(t, h_t, color=c, label=o)\n", + " ax[0].set_ylim(bottom = 0) # limit y axis to values >= 0\n", + "\n", + " ax[1].set_title('Velocity ($m.s^{-1}$)')\n", + " ax[1].plot(t, v_t, color=c, label=o)\n", + "\n", + " ax[2].set_title('Acceleration ($m.s^{-2}$)')\n", + " ax[2].plot(t, a_t, color=c, label=o)\n", + " \n", + " # Display weight of object selected\n", + " with obj_m_output:\n", + " print(\"Weight of the object selected (kg): \", m)\n", + "\n", + " # Add time axis and legend\n", + " for a in ax:\n", + " a.set_xlabel('Time (s)')\n", + " a.legend()\n", + "\n", + " fig.tight_layout()\n", + " \n", + " # Display graph \n", + " with graph_output:\n", + " plt.show()\n", + " \n", + "\n", + "# Event handlers\n", + "def h_event_handler(change):\n", + " display_updated_output(change.new, v_widget.value, obj_m_widget.value)\n", + "\n", + "def v_event_handler(change):\n", + " display_updated_output(h_widget.value, change.new, obj_m_widget.value)\n", + "\n", + "def obj_m_event_handler(change):\n", + " display_updated_output(h_widget.value, v_widget.value, change.new) # obj_m_transform(change.new)\n", + "\n", + "\n", + "# Linking widgets to handlers\n", + "h_widget.observe(h_event_handler, names='value')\n", + "v_widget.observe(v_event_handler, names='value')\n", + "obj_m_widget.observe(obj_m_event_handler, names='value')\n", + "\n", + "#ihm = VBox([m_box, output_object, h_box, v_box, output_graph])\n", + "# Organize layout \n", + "ihm = VBox([graph_output,\n", + " HBox([\n", + " VBox([obj_m_input, obj_m_output]),\n", + " VBox([h_input, v_input])])\n", + " ])\n", + "\n", + "# Display whole interface with the graph already plotted\n", + "display(ihm)\n", + "display_updated_output(h_0, v_0, [objects_list.index[0],]) # objects_list['name']\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Additional observation questions: role of the initial velocity\n", + "\n", + "Choose one object. Which initial velocity is it necessary to give it so that it reaches the ground at $t = 2.5 s$?\n", + "\n", + "Choose one object and give it an initial velocity.\n", + "* When does it reaches its maximum height?\n", + "* What is its velocity at that moment? Why?\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Explanation\n", + "\n", + "Let's draw a free body diagram and represent the forces applied on our object.\n", + "\n", + "![Free Body Diagram](Images/FBD-ball.png)\n", + "\n", + "If we ignore the friction from air, the only force applied on the object is the weight: $\\vec p = m \\vec g$\n", + "\n", + "From Newton's second law we can write: $\\sum \\vec F = m \\vec a$\n", + "\n", + "With the weight the only force we have we get: $\\vec p = m \\vec a$\n", + "\n", + "Using the expression of the weight: $m \\vec g = m \\vec a$\n", + "\n", + "Therefore the movement of the object is described by $\\vec a = \\vec g$.\n", + "\n", + "To get the equation of acceleration as a function of time, let's project onto our coordinate system: $a = -g$, therefore $a(t) = -g$.\n", + "This means the ball is under **constant acceleration**\n", + "\n", + "From there we can get the equations for velocity and height by integrating successively:\n", + "\n", + "$\\left\\{\\begin{matrix} a(t) = -g \\\\ v(t) = -g\\,t + v_0 \\\\ h(t) = -\\frac{1}{2}\\,g\\,t^2 + v_0\\,t + h_0\\end{matrix}\\right. $\n", + "\n", + "With the following parameters:\n", + "* the initial height $h_0$ from which the object is dropped\n", + "* the initial velocity of the object $v_0$, with $v_0 = 0$ when the object is dropped with no initial velocity\n", + "* and of course the acceleration due to gravity $g$\n", + "\n", + "We see clearly that **the mass $m$ of the object plays no role at all in the movement**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# And how does that look in real life?" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%%HTML\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Other documents\n", + "\n", + "https://www.rentech.com.tr/wp-content/uploads/2017/09/PWV-06-ball_toss.pdf\n", + "\n", + "https://opentextbc.ca/physicstestbook2/chapter/falling-objects/" + ] + } + ], + "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.6.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/PredictionQuestion/lib/virtuallab.py b/PredictionQuestion/lib/virtuallab.py new file mode 100644 index 0000000..325195e --- /dev/null +++ b/PredictionQuestion/lib/virtuallab.py @@ -0,0 +1,164 @@ +import numpy as np +import pandas + +from ipywidgets import interact, interactive, fixed, interact_manual +from ipywidgets import HBox, VBox, Label +import ipywidgets as widgets + +import matplotlib.pyplot as plt +plt.style.use('seaborn-whitegrid') # global style for plotting + + +###--- First, we create the list of objects with which we can experiment. + +# Objects come with a name, a mass (in kg) and a color for identifying them in the graphical display. +objects = [{ + 'name':'Bowling ball', + 'mass':5.0, + 'color':'#DC143C' + },{ + 'name':'Tennis ball', + 'mass':0.05, + 'color':'#2E8B57' + },{ + 'name':'Ostrich feather', + 'mass':0.005, + 'color':'#483D8B' +}] +objects_list = pandas.DataFrame(objects, columns = ['name', 'mass', 'color']) +objects_list.set_index('name', inplace=True) # We index objects by their name to find them easily after + + +###--- Then we define the parameters of our problem: + +# The standard acceleration due to gravity +g = 9.81 # gravity in m/s2 + +# The initial conditions of our problem +h_0 = 5 # initial height in m +v_0 = 0 # initial velocity in m/s + +# To plot the movement of our objects in time we need to define a time scale. +t = np.linspace(0,3,25) # time interval from 0 to x seconds, with n points in the interval + + +###--- Then we define three functions which compute the movement of our objects in free fall on our time interval. + +# Functions representing the equations of the movement as functions of time and the problem parameters +def accel_time (t, m, h_0, v_0): + return [-g]*t.size # returning a list of same length as the time interval filled with -g + +def veloc_time (t, m, h_0, v_0): + return -g*t+v_0 + +def height_time (t, m, h_0, v_0): + return -0.5*g*(t**2)+v_0*t+h_0 + +###--- Now we create an interactive graph which will plot the equations of the objects depending on the parameters that the user selects. + +# parameters for sliders +h_min = 0 +h_max = 10 +v_min = 0 +v_max = 10 + + +# IHM input elements +h_label = Label('Initial height ($m$):') +h_widget = widgets.FloatSlider(min=h_min,max=h_max,step=1,value=h_0) +h_input = HBox([h_label, h_widget]) + +v_label = Label('Initial velocity ($m.s^{-1}$):') +v_widget = widgets.FloatSlider(min=v_min,max=v_max,step=1,value=v_0) +v_input = HBox([v_label, v_widget]) + +obj_m_label = Label('Choice of object(s):') +obj_m_widget = widgets.SelectMultiple( + options = objects_list.index, + value = [objects_list.index[0],], # tuple(objects_list['name']) + disabled = False +) +obj_m_input = HBox([obj_m_label, obj_m_widget]) + +# IHM output elements +obj_m_output = widgets.Output() +graph_output = widgets.Output() + +# Display updated output function +def display_updated_output(h_0, v_0, objs): + # Clear outputs + graph_output.clear_output(wait=True) + obj_m_output.clear_output(wait=True) + + # Create the figure + fig, ax = plt.subplots(1, 3, sharex='col', figsize=(16, 4)) + + # for each object selected (by name) + for o in objs: + # get mass + m = objects_list.loc[objects_list.index == o]['mass'].item() + # get color + c = objects_list.loc[objects_list.index == o]['color'].item() + + + # Recompute equations with parameters set by the user + h_t = height_time(t, m, h_0, v_0) + v_t = veloc_time(t, m, h_0, v_0) + a_t = accel_time(t, m, h_0, v_0) + + # Plot equations + ax[0].set_title('Height ($m$)') + ax[0].plot(t, h_t, color=c, label=o) + ax[0].set_ylim(bottom = 0) # limit y axis to values >= 0 + + ax[1].set_title('Velocity ($m.s^{-1}$)') + ax[1].plot(t, v_t, color=c, label=o) + + ax[2].set_title('Acceleration ($m.s^{-2}$)') + ax[2].plot(t, a_t, color=c, label=o) + + # Display weight of object selected + with obj_m_output: + print("Weight of the object selected (kg): ", m) + + # Add time axis and legend + for a in ax: + a.set_xlabel('Time (s)') + a.legend() + + fig.tight_layout() + + # Display graph + with graph_output: + plt.show(); + + +# Event handlers +def h_event_handler(change): + display_updated_output(change.new, v_widget.value, obj_m_widget.value) + +def v_event_handler(change): + display_updated_output(h_widget.value, change.new, obj_m_widget.value) + +def obj_m_event_handler(change): + display_updated_output(h_widget.value, v_widget.value, change.new) # obj_m_transform(change.new) + + +# Linking widgets to handlers +h_widget.observe(h_event_handler, names='value') +v_widget.observe(v_event_handler, names='value') +obj_m_widget.observe(obj_m_event_handler, names='value') + +# Organize layout +ihm = VBox([graph_output, + HBox([ + VBox([obj_m_input, obj_m_output]), + VBox([h_input, v_input])]) + ]) + + + +###--- Finally, we display the whole interface and we update it right away so that it plots one default object (the first in our lis) + +display(ihm); +display_updated_output(h_0, v_0, [objects_list.index[0],])