Page MenuHomec4science

fallingobjects.py
No OneTemporary

File Metadata

Created
Wed, Dec 4, 02:07

fallingobjects.py

import numpy as np
import pandas
from ipywidgets import interact, interactive, fixed, interact_manual
from ipywidgets import HBox, VBox, Label, Layout
import ipywidgets as widgets
from IPython.display import set_matplotlib_formats
set_matplotlib_formats('svg')
import matplotlib.pyplot as plt
plt.style.use('seaborn-whitegrid') # global style for plotting
###--- Functions representing the equations of the movement as functions of time and the problem parameters
def accel_time(g, h_0, v_0, m, t):
'''
Computes the acceleration of the object as a function of time
a(t) = -g
:g: gravitational acceleration constant
:h_0: initial height of the object
:v_0: initial velocity of the object
:m: mass of the object
:t: time scale (array of time points at which to compute the equation)
:returns: array of values for acceleration at each point of the time scale
'''
return [-g] * t.size # returning a list of same length as the time interval filled with -g
def veloc_time(g, h_0, v_0, m, t):
'''
Computes the velocity of the object as a function of time
v(t) = -g.t + v_0
:g: gravitational acceleration constant
:h_0: initial height of the object
:v_0: initial velocity of the object
:m: mass of the object
:t: time scale (array of time points at which to compute the equation)
:returns: array of values for velocity at each point of the time scale
'''
return -g * t + v_0
def height_time(g, h_0, v_0, m, t):
'''
Computes the height of the object as a function of time
h(t) = -1/2.g.t^2 + v_0.t + h_0
:g: gravitational acceleration constant
:h_0: initial height of the object
:v_0: initial velocity of the object
:m: mass of the object
:t: time scale (array of time points at which to compute the equation)
:returns: array of values for height at each point of the time scale
'''
return -0.5 * g * (t **2) + v_0 * t + h_0
###--- Static 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'
}]
class FallingObjectsLab:
"""
This class embeds all the necessary code to create a virtual lab to study the movement of objects falling vertically in vacuum.
"""
def __init__(self, g = 9.81, h_0 = 5, v_0 = 0, t = np.linspace(0,3,25), objects = objects,
accel_time = accel_time, veloc_time = veloc_time, height_time = height_time,
show_v_0 = False):
'''
Initiates and displays the virtual lab on falling objects.
:g: gravitational acceleration constant
:h_0: initial height of the objects
:v_0: initial velocity of the objects
:t: time scale (array of time points at which to compute the equation)
:objects: nested dictionnary with the objects to display, which should come with a name, a mass (in kg) and a color (hex code)
:accel_time: function to compute the acceleration of the objects as a function of time -- a(g, h_0, v_0, m, t)
:veloc_time: function to compute the velocity of the objects as a function of time -- v(g, h_0, v_0, m, t)
:height_time: function to compute the height of the objects as a function of time -- h(g, h_0, v_0, m, t)
:show_v_0: when True, a slider to change the initial velocity of objects is displayed in the interface
'''
###--- We define the parameters of our problem:
# The standard acceleration due to gravity
self.g = g # gravity in m/s2
# The initial conditions of our problem
self.h_0 = h_0 # initial height in m
self.v_0 = v_0 # initial velocity in m/s
# To plot the movement of our objects in time we need to define a time scale.
self.t = t # time interval from 0 to x seconds, with n points in the interval
# Functions to compute movement equations
self.accel_time = accel_time
self.veloc_time = veloc_time
self.height_time = height_time
# Create indexed list of objects
self.objects_list = pandas.DataFrame(objects)
self.objects_list.set_index('name', inplace=True) # We index objects by their name to find them easily after
# Initialize list of currently selected objects with first element of the list
self.objs = [self.objects_list.index[0],]
###--- Then we define the elements of the ihm:
# parameters for sliders
self.h_min = 0
self.h_max = 10
self.v_min = 0
self.v_max = 5
# IHM input elements
input_layout=Layout(margin='5px 10px')
self.h_label = Label('Initial height ($m$):', layout=input_layout)
self.h_widget = widgets.FloatSlider(min=self.h_min,max=self.h_max,step=1,value=self.h_0, layout=input_layout)
self.h_input = HBox([self.h_label, self.h_widget])
self.v_label = Label('Initial velocity ($m.s^{-1}$):', layout=input_layout)
self.v_widget = widgets.FloatSlider(min=self.v_min,max=self.v_max,step=1,value=self.v_0, layout=input_layout)
self.v_input = HBox([self.v_label, self.v_widget])
self.obj_m_label = Label('Choice of object(s):', layout=input_layout)
self.obj_m_widget = widgets.SelectMultiple(
options = self.objects_list.index,
value = self.objs,
disabled = False,
layout=input_layout
)
self.obj_m_input = HBox([self.obj_m_label, self.obj_m_widget])
# IHM output elements
self.obj_m_output = widgets.Output(layout=input_layout)
self.graph_output = widgets.Output(layout=input_layout)
# Linking widgets to handlers
self.h_widget.observe(self.h_event_handler, names='value')
self.v_widget.observe(self.v_event_handler, names='value')
self.obj_m_widget.observe(self.obj_m_event_handler, names='value')
# Organize layout
self.ihm = VBox([self.graph_output,
HBox([
VBox([self.obj_m_input, self.obj_m_output]),
VBox([self.h_input, self.v_input]) if show_v_0 else VBox([self.h_input])
])
])
###--- Finally, we display the whole interface and we update it right away so that it plots the graph with current values
display(self.ihm);
self.update_lab()
# Event handlers
def h_event_handler(self, change):
self.h_0 = change.new
self.update_lab()
def v_event_handler(self, change):
self.v_0 = change.new
self.update_lab()
def obj_m_event_handler(self, change):
self.objs = change.new
self.update_lab()
# Display updated output function
def update_lab(self):
# Clear outputs
self.graph_output.clear_output(wait=True)
self.obj_m_output.clear_output(wait=True)
# Create the figure
fig, ax = plt.subplots(1, 3, sharex='col', figsize=(16, 4))
# for each object currently selected
for o in self.objs:
# get mass
m = self.objects_list.at[o,'mass']
# get color
c = self.objects_list.at[o,'color']
# Recompute equations with parameters set by the user
h_t = self.height_time(self.g, self.h_0, self.v_0, m, self.t)
v_t = self.veloc_time(self.g, self.h_0, self.v_0, m, self.t)
a_t = self.accel_time(self.g, self.h_0, self.v_0, m, self.t)
# Plot equations
ax[0].set_title('Height ($m$)')
ax[0].plot(self.t, h_t, color=c, label=o)
ax[0].set_ylim(bottom = 0, top = self.h_max+(self.v_max/2 if self.v_0 > 0 else 1))
ax[1].set_title('Velocity ($m.s^{-1}$)')
ax[1].plot(self.t, v_t, color=c, label=o)
ax[1].set_ylim(top = self.v_max+1)
ax[2].set_title('Acceleration ($m.s^{-2}$)')
ax[2].plot(self.t, a_t, color=c, label=o)
ax[2].set_ylim(top = 0)
# Display weight of object selected
with self.obj_m_output:
print("Mass of the selected object (kg): ", m)
# Add time axis and legend
for a in ax:
a.set_xlabel('Time (s)')
a.legend()
fig.tight_layout()
# Display graph
with self.graph_output:
plt.show();
# EOF

Event Timeline