|
HomeRunWithAirResistance.ipynb
|
#%% md
## Home Run with Air Resistance
**Execute the initialization cell.** All these variables will then be available to the rest of the code.
#%%
# Initial speed of hit ball in meters per second
initial_speed = 60
# Initial angle of hit ball in degrees from horizontal
initial_angle = 30
# Simulation time step in seconds
delta_t = 0.1
# Distance to fence in meters
distance_to_fence = 100
# density_of_air * area_of_ball * drag_coefficient / (2 * mass_of_ball)
# just call the whole combo air_constant_over_m
air_constant_over_m = 0.012
# Below is the way g is usually defined -- the amount is 9.81 m/s^2 downwards.
# People don't put a minus sign in g -- that's a convention, and we are not changing it.
# When you use this constant, you have to remember to put the minus sign in front of it
# to take care of the fact that gravity pulls in the -y direction.
g = 9.81
# Simulation time step in seconds
delta_t = 0.1
from math import sin, cos, atan2, sqrt, pi
# The following import statement makes the plotting library available to us. There is also a
# statement to work around a known Jupyter bug: https://github.com/jupyter/notebook/issues/3670
%matplotlib inline
import matplotlib.pyplot as plt
#%% md
## Function to get Components of a Vector
#%%
# computes horizontal and vertical components of a vector and returns them as a tuple
def vector_components(length, angle_from_horizontal):
# convert angle from degrees to radians
angle = angle_from_horizontal * pi / 180.0
# this line is good
horizontal_component = length * cos(angle)
# fix the next line -- DONE
vertical_component = length * sin(angle)
# return both components as a tuple
return horizontal_component, vertical_component
initial_horizontal_velocity, initial_vertical_velocity = vector_components(initial_speed, initial_angle)
initial_horizontal_velocity, initial_vertical_velocity
#%% md
## Functions to get Angle and Speed from Components
#%%
# sometimes we need to go from components to angle and speed -- instead of from angle and speed to components
# get angle from components using atan2 version of arctangent function
def angle_from_velocity(horizontal_velocity, vertical_velocity):
# use the arctangent function
angle = atan2(vertical_velocity, horizontal_velocity)
# we are working in degrees -- convert radians to degrees
angle_in_degrees = angle * 180.0 / pi
# return the result
return angle_in_degrees
# get speed from components using Pythagorean theorem
def speed_from_velocity(horizontal_velocity, vertical_velocity):
# pythagorean theorem
hypotenuse = sqrt(horizontal_velocity**2 + vertical_velocity**2)
# return the result
return hypotenuse
#%% md
## Function to get Acceleration Components as a Tuple
#%%
# The function will return a tuple consisting of the horizontal acceleration and the vertical acceleration.
def acceleration_with_drag(horizontal_velocity, vertical_velocity):
angle = angle_from_velocity(horizontal_velocity, vertical_velocity)
speed = speed_from_velocity(horizontal_velocity, vertical_velocity)
strength_of_drag = air_constant_over_m * speed**2
direction_of_drag = angle + 180
horizontal_component, vertical_component = vector_components(strength_of_drag, direction_of_drag)
return horizontal_component, vertical_component - g
#%% md
## The While Loop That Does the Work
#%%
# Initialize the x and y velocities
x_velocities = [initial_horizontal_velocity]
y_velocities = [initial_vertical_velocity]
# Initialize the x and y positions
x_positions = [0.0]
y_positions = [1.0]
# Initialize the times
times = [0.0]
# We want to go until the ball is over the fence. If the ball doesn't make it to the fence after 100 steps, stop the program anyway.
while x_positions[-1] < distance_to_fence and len(times) < 100:
#
# get all the before values
#
# velocities
before_x_velocity = x_velocities[-1]
before_y_velocity = y_velocities[-1]
# positions
before_x_position = x_positions[-1]
before_y_position = y_positions[-1]
# time
before_time = times[-1]
#
# use the new acceleration_with_drag function to get the accelerations
#
x_acceleration, y_acceleration = acceleration_with_drag(before_x_velocity, before_y_velocity)
#
# bog-standard Euler update code -- in two dimensions
#
# update the x and y velocities
after_x_velocity = before_x_velocity + delta_t * x_acceleration
after_y_velocity = before_y_velocity + delta_t * y_acceleration
# update the x and y positions
after_x_position = before_x_position + delta_t * before_x_velocity
after_y_position = before_y_position + delta_t * before_y_velocity
# update time
after_time = before_time + delta_t
#
# append all the after values to their lists
#
x_velocities.append(after_x_velocity)
y_velocities.append(after_y_velocity)
x_positions.append(after_x_position)
y_positions.append(after_y_position)
times.append(after_time)
#%% md
## Graphs
There are various interesting graphs we can make out of the x and y positions, the x and y velocities, and the times.
#%%
plt.figure(figsize=(13, 3))
plt.scatter(x_positions, y_positions)
plt.xlabel("Horizontal position (m)")
plt.ylabel("Vertical position (m)")
plt.show()
#%%
plt.figure(figsize=(13, 3))
plt.scatter(times, x_velocities)
plt.xlabel("Time (s)")
plt.ylabel("Horizontal velocity (m/s)")
plt.ylim(0, 60)
plt.show()
#%%
plt.figure(figsize=(13, 3))
plt.scatter(times, y_velocities)
plt.xlabel("Time (s)")
plt.ylabel("Vertical velocity (m/s)")
plt.ylim(-35, 35)
plt.axhline()
plt.show()