Page MenuHomec4science

suspendedobjects.py
No OneTemporary

File Metadata

Created
Wed, Dec 4, 01:47

suspendedobjects.py

import numpy as np
from scipy.interpolate import interp1d
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 depending on the problem parameters
def get_angle_from_masses(m_object, m_counterweight):
"""
Compute the angle that the cable makes with the horizon depending on the counterweight chosen
angle = arcsin(1/2 * m_object / m_counterweight)
:m_object: mass of the object
:m_counterweight: mass of the counterweight
:returns: angle that the cable makes with the horizon
"""
# default angle value
angle = np.pi / 2
# let's check that there is actually a counterweight
if m_counterweight > 0:
# then we compute the ratio of masses
ratio = 0.5 * m_object / m_counterweight
# we check if the ratio of masses is in the domaine of validity of arcsin ([-1;1]) and compute the angle
if ratio >= -1 and ratio <= 1:
angle = np.arcsin(ratio)
return angle
def get_object_coordinates(angle, x_origin, y_origin, distance, height):
"""
Computes the position of the object on the cable taking into account the angle determined by the counterweight and the dimensions of the hanging system.
By default:
- the object is supposed to be suspended exactly in the middle of the cable
- the object is on considered the ground for all values of the angle
:angle: angle that the cable makes with the horizon
:x_origin: x coordinate of the bottom of the left pole (origin of the coordinate system)
:y_origin: y coordinate of the bottom of the left pole (origin of the coordinate system)
:distance: horizontal distance between the two poles
:height: height of the poles (same height for both)
:returns: coordinates of the point at which the object are hanged
"""
# the jean is midway between the poles
x_object = x_origin + 0.5 * distance
# default y value: the jean is on the ground
y_object = y_origin
# we check that the angle is comprised between horizontal (greater than 0) and vertical (smaller than pi/2)
if angle > 0 and angle < (np.pi / 2):
# we compute the delta between the horizon and the point given by the angle
delta = (0.5 * distance * np.tan(angle))
# we check that the delta is smaller than the height of the poles (otherwise it just means the jean is on the ground)
if delta <= height:
y_object = y_origin + height - delta
return [x_object, y_object]
class SuspendedObjectsLab:
"""
This class embeds all the necessary code to create a virtual lab to study the static equilibrium of an object suspended on a clothesline with a counterweight.
"""
def __init__(self, m_object = 3, distance = 5, height = 1.5, x_origin = 0, y_origin = 0,
get_angle_from_masses = get_angle_from_masses, get_object_coordinates = get_object_coordinates):
'''
Initiates and displays the virtual lab on suspended objects.
:m_object: mass of the suspended object
:distance: horizontal distance between the two poles
:height: height of the poles (same height for both)
:x_origin: x coordinate of the bottom of the left pole (origin of the coordinate system)
:y_origin: y coordinate of the bottom of the left pole (origin of the coordinate system)
:get_angle_from_masses: function to compute the angle that the cable makes with the horizon, as a function of the respective masses of the object and the counterweight -- angle(m_object, m_counterweight)
:get_object_coordinates: function to compute the coordinates of the point at which the object is suspended on the cable -- coord(angle, x_origin, y_origin, distance, height)
'''
###--- Parameters of the situation
self.m_object = m_object # mass of the wet object, in kg
self.distance = distance # distance between the poles, in m
self.height = height # height of the poles, in m
self.x_origin = x_origin # x coordinate of point of origin of the figure = x position of the left pole, in m
self.y_origin = y_origin # y coordinate of point of origin of the figure = y position of the lower point (ground), in m
# Functions to compute equations
self.get_angle_from_masses = get_angle_from_masses
self.get_object_coordinates = get_object_coordinates
###--- Then we define the elements of the ihm:
# parameters for sliders
self.m_counterweight_min = 0
self.m_counterweight_max = 100
self.m_counterweight = self.m_counterweight_min # initial mass of the counterweight (0 by default, no counterweight at the beginning)
# IHM input elements
input_layout=Layout(margin='5px 10px')
self.m_counterweight_label = Label('Mass of the counterweight ($kg$):', layout=input_layout)
self.m_counterweight_widget = widgets.FloatSlider(min=self.m_counterweight_min,max=self.m_counterweight_max,step=1,value=self.m_counterweight, layout=input_layout)
self.m_counterweight_input = HBox([self.m_counterweight_label, self.m_counterweight_widget])
# IHM output elements
self.y_object_output = widgets.Output(layout=input_layout)
self.graph_output = widgets.Output(layout=input_layout)
# Linking widgets to handlers
self.m_counterweight_widget.observe(self.m_counterweight_event_handler, names='value')
# Organize layout
self.ihm = VBox([self.m_counterweight_input, self. y_object_output, self.graph_output])
###--- 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 m_counterweight_event_handler(self, change):
self.m_counterweight = change.new
self.update_lab()
# Create visualisation
def update_lab(self):
# Clear outputs
self.graph_output.clear_output(wait=True)
self.y_object_output.clear_output(wait=True)
# get angle of cable then coordinates of jean
alpha = self.get_angle_from_masses(self.m_object, self.m_counterweight)
coord_object = self.get_object_coordinates(alpha, self.x_origin, self.y_origin, self.distance, self.height)
# Create the figure
fig, ax = plt.subplots(1, 3, figsize=(16, 4))
###--- First display the clothesline
ax[0].set_title('Clothesline')
# Fix graph to problem boundaries
ax[0].set_ylim(bottom = self.y_origin) # limit bottom of y axis to ground
ax[0].set_ylim(top = self.y_origin + self.height + .2) # limit top of y axis to values just above height
# Draw poles
x_pole1 = np.array([self.x_origin, self.x_origin])
y_pole1 = np.array([self.y_origin, self.y_origin+self.height])
ax[0].plot(x_pole1, y_pole1, "k-", linewidth=7)
x_pole2 = np.array([self.x_origin+self.distance, self.x_origin+self.distance])
y_pole2 = np.array([self.y_origin, self.y_origin+self.height])
ax[0].plot(x_pole2, y_pole2, "k-", linewidth=7)
# Draw the hanging cable
x = np.array([self.x_origin, coord_object[0], self.x_origin+self.distance])
y = np.array([self.y_origin+self.height, coord_object[1], self.y_origin+self.height])
ax[0].plot(x, y, "g-")
# Draw the point at which the object is suspended
ax[0].scatter(coord_object[0], coord_object[1], s=80, c="r", zorder=15)
###--- Then display the the angle and the height as functions from the mass of the counterweight
ax[1].set_title('Angle of the cable compared to horizon (degrees$^\circ$)')
ax[2].set_title('Height of the point at which the object is hanged ($m$)')
# Create all possible values of the mass of the counterweight
m_cw = np.linspace(self.m_counterweight_min, self.m_counterweight_max, 100)
# Compute the angle (in degrees) and height for all these values
angle = []
height = []
for m in m_cw:
a = self.get_angle_from_masses(self.m_object, m)
angle.append(a*180/np.pi)
c = self.get_object_coordinates(a, self.x_origin, self.y_origin, self.distance, self.height)
height.append(c[1])
# Display the functions on the graphs
ax[1].set_xlabel('Mass of the counterweight (kg)')
ax[1].plot(m_cw, angle, "b")
ax[2].set_xlabel('Mass of the counterweight (kg)')
ax[2].plot(m_cw, height, "b")
# Add the current angle from the counterweight selected by the user
ax[1].scatter(self.m_counterweight, alpha*180/np.pi, s=80, c="r", zorder=15)
# Add the current height from the counterweight selected by the user
ax[2].scatter(self.m_counterweight, coord_object[1], s=80, c="r", zorder=15)
# Display height of object selected
with self.y_object_output:
print("Height of the point at which the object is hanged (m):", coord_object[1])
# Display graph
with self.graph_output:
plt.show();
# EOF

Event Timeline