Projectile motion - gravity varying with height - python

The question is having to make a projectile motion where gravity isn't constant. So the position s(t) = -0.5 g t2 + v0 t and g(s) = G∙ME / (RE + s)2. Where G, ME, and RE are all constants. Therefore the new equation is s(t) = -0.5 g(s) t2 + v0 t.
I get to make the assumption that every .005 seconds velocity is constant so the equation has to update itself every .005 seconds. So s(t) = s(t-∆t) + v(t)∙∆t where v(t) = v(t-∆t) - g(s(t-∆t)) ∙ ∆t.
My code right now is
# Assigning Variables
G = 6.6742*10**(-11) # Gravitational Constant
M_e = 5.9736*10**(24) # Mass of Earth
R_e = 6371000 # Radius of Earth
t = float(input('Time (in seconds)')) # Asking user to input total time, t
v_0 = float(input('initial velocity')) # Asking user to input initial velocity
t_0 = .005 # Time before first recalculation
g = 9.81 # initial gravity
# Derivative of s(t) = s't = v(t)
# v(t) = -g(s)*t+v_o
while t != t_0:
s = v_0*t_0
g = (g*M_e)/(R_e+s)**2
v = -g*s*t_0+v_0
t_0=t_0+.005
if int(t_0) == t_0:
print'Gravity is {%f}, position is {%f}, and velocity is {%f} at time {%.0f}' %(g, s, v, t_0)
print'Projectile has reached your time {%f}, with gravity {%f}, position {%f}, and velocity {%f}'%(t,g,s,v)
I really don't know how I am supposed to change it so it will work.
So I updated it as the suggestions that I got. Now when I run it my program asks for time and initial velocity and time (in seconds). However it does not even produce an output.
Time (in seconds)5
initial velocity5
That's how the result looks like when I input 5 for both.

I've added comments to your code, along with some changes so the program will run (at least on 2.7.6). However, while it will run, it won't truly work. You should look at your functions for s, g, and v - they are not correct. For example R_e * s will not give you the distance from the center of the earth, as its units are now metres^2.
# Assigning Variables
G = 6.6742*10**(-11) # Gravitational Constant
M_e = 5.9736*10**(24) # Mass of Earth
##### In your code the commas are making this a tuple, not an integer - it needs to be defined without commas.
R_e = 6371000 # Radius of Earth
t = float(input('Time (in seconds)'))
v_0 = float(input('initial velocity'))
t_0 = .005
#You need to define an initial g
g = 9.81
while t != t_0:
####In your code you had a typo here - t_o instead of t_0
s = v_0*t_0
####If you don't initialise g, this function does not know what g is.
g = (g*M_e)/(R_e*s)**2
v = -g*s*t_0+v_0
t_0=t_0+.005
#####If you want to check if the type of t_0 is an integer, you need to use the type function. It will also never be an integer, as you are always adding a float to a float in the line above this.
if type(t_0) == int:
print('Gravity is {%f}, position is {%f}, and velocity is {%f} at time {%.0f}' %(g, s, v, t_0))
####No need for an if statement to tell if the while loop is finished - just put the print statement after the while loop.
print('Projectile has reached your time {%f}, with gravity {%f}, position {%f}, and velocity {%f}'%(t,g,s,v))

Related

pygame.Rect objects keep getting stuck at top = 0 and don't move when .move or .move_ip is used

This is a small part of some more complicated code but basically. I've narrowed down the problem to this one bit of code.
I'm using the formula s = ut + 1/2 at^2 to get the distance traveled in one instance to time, in this case, 0.001 seconds which will then further be used to draw the rects on a window.
import pygame
run = True
clock = pygame.time.Clock()
ex_s = pygame.Rect((500, 625, 50, 50))
start_height = ex_s.top
print(start_height, "m")
g = -9.8
v = 400
t = 0.001
while run:
ex_s.top -= (v * t) + ((g / 2) * (t ** 2))
print(ex_s.top - start_height)
if (ex_s.top - start_height) * -1 <= -10:
run = False
clock.tick(30)
I want the rectangles to move upwards/downwards based on their velocity.
The rectangles move up and get stuck whenever the top reaches y = 0. This doesn't happen if the value of "t" is increased to o.o1 or above but I need it to be 0.001 for the precision. The problem is also solved when the value of v is increased by an order of magnitude, changing g does nothing.
I tried using both move and move_ip and the y coordinate did not change at all.
I've also tried manually pushing the objects above y = 0 when they hit it but it doesn't do anything. The fact that the problem changes with decimal places has led me to believe it could be something to do with the integer coordinate system of pygame but I don't see how this would cause things to move first and then get stuck only at 0.
See Pygame doesn't let me use float for rect.move, but I need it. The problem is that a pygame.Rect object can only store integer data. So when you add a value less than 1.0 to the coordinate of a rectangle, than the position stays the same. You need to store the position of the rectangle in separate variables and update the position of the rectangle using these variables.
ex_s = pygame.Rect((500, 625, 50, 50))
ex_s_y = ex_s.top
# [...]
while run:
# [...]
# change position
ex_s_y = .....
ex_s.top = round(ex_s_y)
You didn't change the time t variable, and your formula for the motion is wrong. Also your initial time, initial velocity and g are wrong.
If you want to use an exact solution for the position, do like this.
dt = 0.05
t = 0
g = 9.8 # It's positive by definition.
v = -100 # It's upward.
while run:
y = 625 + v*t + 0.5*g*t**2 # The acceleration is +g, downward.
t += dt
ex_s.top = y
...
If you want to solve the equation of motion approximately and iteratively, do like this inside the loop.
...
y = 625
while run:
v += g * dt
y += v * dt
t += dt
ex_s.top = y
...
The above method is called sympletic Euler method. There're other more accurate methods, such as Runge-Kutta method.

Ordinary differential equations with unknown initial value in python

I try to solve projectile motion problem under air resistance and wind effect. When knowing the initial conditions includes initial velocity, position and angle of projectile, I can solve the problem using scipy.integrate.solve_ivp. In the next step, I want to find out the projectile motion knowing only initial velocity (but not initial angle) or only initial angle (but not initial velocity) of projectile and 1 more condition that this motion will go through point C(xC, yC) at time T. I did refer to Ordinary differential equation - Odeint with unknown initial value in python, jupyter but I still cannot apply it in my problem. Here are my code trying to find initial value of angle knowing initial value of velocity v0:
def dSdt(t,S):
x, vx, y, vy = S
wind_speed_x, wind_speed_y = wind_speed
g = 9.81
return [vx,
-B*np.sqrt((vx-wind_speed_x)**2+(vy-wind_speed_y)**2)*(vx-wind_speed_x),
vy,
-g-B*np.sqrt((vx-wind_speed_x)**2+(vy-wind_speed_y)**2)*(vy-wind_speed_y)]
def bvp_ode(s,u,T): return T*np.asarray(dSdt(s*T,u))
def bvp_bc(uA,uB,T):
xA, yA = uA[0],uA[2]
xB, yB = uB[0],uB[2]
return np.array([xA-posA[0],yA-posA[1],xB-posB[0],yB-posB[1], velA**2-v0**2])
if __name__ == '__main__':
posA = [0,0]
posB = [100,10]
velA = 50
v0 = 50
T = 100 # or dis(posA,posB)/v0#
timeInterval = [0,100]
time_interval = np.linspace(timeInterval[0],timeInterval[1],1000)
y_0 = np.zeros((4,1000))
y_0[0][0] = posA[0]
y_0[2][0] = posA[1]
y_0[0][999] = posB[0]
y_0[2][999] = posB[1]
B = 25
wind_speed = [10,10]
res = solve_bvp(bvp_ode,bvp_bc,time_interval,y_0, p=[T])

Projectile Motion in Python: How to generate list giving vertical velocity over time?

I'm trying to find how the vertical velocity Vy of a spherical particle falling vertically with drag changes as a function of time t. I would like to generate a list of values that state what Vy is for several points in time from the time at which is is released to some t_max as well as plot Vy vs time.
The equation I've been given to solve for ∆Vy is:
dVy = -g*dt - (b/m)*Vy*dt
and I've been given a suggested algorithm for finding the values
"The code should ask for the values of m, g, b and ∆t. Once these values are defined, the code should provide a series of values of Vy for each time t."
Here's my attempt so far:
import numpy as np
import matplotlib.pyplot as plt
import math
g = 9.81 #acceleration due to gravity
b = 1.6*10**-8 #linear drag coefficient
m = 1.04*10**-9 #mass of particle
dt = 10**-3 #time step
t_max = 10 #final time
t = 0 #initial time
Vy = 0 #initial velocity
t_list = [t]
Vy_list = [Vy]
dVy = -g*dt - (b/m)*Vy*dt
while t <= t_max:
t += dt
Vy += dVy
t_list.append(t)
Vy_list.append(Vy)
print(t, Vy)
plt.plot(t_list, Vy_list)
plt.show()
I'm not sure that the values it generates are quite right considering that no matter what size I set the mass to the change in velocity remains the same when I'd expect it to affect the velocity quite a bit for infinitesimally smaller masses.
I'll admit that I'm replicating a solution that was given to me for a completely different problem, but am I right in thinking that a while loop is the way to go?
You need to update dVy in the while loop:
while t <= t_max:
dVy = -g*dt - (b/m)*Vy*dt
t += dt
Vy += dVy
Otherwise it will be a constant and not update as you expect over time.

Solving ODE numerically with Python

I am solving an ODE for an harmonic oscillator numerically with Python. When I add a driving force it makes no difference, so I'm guessing something is wrong with the code. Can anyone see the problem? The (h/m)*f0*np.cos(wd*i) part is the driving force.
import numpy as np
import matplotlib.pyplot as plt
# This code solves the ODE mx'' + bx' + kx = F0*cos(Wd*t)
# m is the mass of the object in kg, b is the damping constant in Ns/m
# k is the spring constant in N/m, F0 is the driving force in N,
# Wd is the frequency of the driving force and x is the position
# Setting up
timeFinal= 16.0 # This is how far the graph will go in seconds
steps = 10000 # Number of steps
dT = timeFinal/steps # Step length
time = np.linspace(0, timeFinal, steps+1)
# Creates an array with steps+1 values from 0 to timeFinal
# Allocating arrays for velocity and position
vel = np.zeros(steps+1)
pos = np.zeros(steps+1)
# Setting constants and initial values for vel. and pos.
k = 0.1
m = 0.01
vel0 = 0.05
pos0 = 0.01
freqNatural = 10.0**0.5
b = 0.0
F0 = 0.01
Wd = 7.0
vel[0] = vel0 #Sets the initial velocity
pos[0] = pos0 #Sets the initial position
# Numerical solution using Euler's
# Splitting the ODE into two first order ones
# v'(t) = -(k/m)*x(t) - (b/m)*v(t) + (F0/m)*cos(Wd*t)
# x'(t) = v(t)
# Using the definition of the derivative we get
# (v(t+dT) - v(t))/dT on the left side of the first equation
# (x(t+dT) - x(t))/dT on the left side of the second
# In the for loop t and dT will be replaced by i and 1
for i in range(0, steps):
vel[i+1] = (-k/m)*dT*pos[i] + vel[i]*(1-dT*b/m) + (dT/m)*F0*np.cos(Wd*i)
pos[i+1] = dT*vel[i] + pos[i]
# Ploting
#----------------
# With no damping
plt.plot(time, pos, 'g-', label='Undampened')
# Damping set to 10% of critical damping
b = (freqNatural/50)*0.1
# Using Euler's again to compute new values for new damping
for i in range(0, steps):
vel[i+1] = (-k/m)*dT*pos[i] + vel[i]*(1-(dT*(b/m))) + (F0*dT/m)*np.cos(Wd*i)
pos[i+1] = dT*vel[i] + pos[i]
plt.plot(time, pos, 'b-', label = '10% of crit. damping')
plt.plot(time, 0*time, 'k-') # This plots the x-axis
plt.legend(loc = 'upper right')
#---------------
plt.show()
The problem here is with the term np.cos(Wd*i). It should be np.cos(Wd*i*dT), that is note that dT has been added into the correct equation, since t = i*dT.
If this correction is made, the simulation looks reasonable. Here's a version with F0=0.001. Note that the driving force is clear in the continued oscillations in the damped condition.
The problem with the original equation is that np.cos(Wd*i) just jumps randomly around the circle, rather than smoothly moving around the circle, causing no net effect in the end. This can be best seen by plotting it directly, but the easiest thing to do is run the original form with F0 very large. Below is F0 = 10 (ie, 10000x the value used in the correct equation), but using the incorrect form of the equation, and it's clear that the driving force here just adds noise as it randomly moves around the circle.
Note that your ODE is well behaved and has an analytical solution. So you could utilize sympy for an alternate approach:
import sympy as sy
sy.init_printing() # Pretty printer for IPython
t,k,m,b,F0,Wd = sy.symbols('t,k,m,b,F0,Wd', real=True) # constants
consts = {k: 0.1, # values
m: 0.01,
b: 0.0,
F0: 0.01,
Wd: 7.0}
x = sy.Function('x')(t) # declare variables
dx = sy.Derivative(x, t)
d2x = sy.Derivative(x, t, 2)
# the ODE:
ode1 = sy.Eq(m*d2x + b*dx + k*x, F0*sy.cos(Wd*t))
sl1 = sy.dsolve(ode1, x) # solve ODE
xs1 = sy.simplify(sl1.subs(consts)).rhs # substitute constants
# Examining the solution, we note C3 and C4 are superfluous
xs2 = xs1.subs({'C3':0, 'C4':0})
dxs2 = xs2.diff(t)
print("Solution x(t) = ")
print(xs2)
print("Solution x'(t) = ")
print(dxs2)
gives
Solution x(t) =
C1*sin(3.16227766016838*t) + C2*cos(3.16227766016838*t) - 0.0256410256410256*cos(7.0*t)
Solution x'(t) =
3.16227766016838*C1*cos(3.16227766016838*t) - 3.16227766016838*C2*sin(3.16227766016838*t) + 0.179487179487179*sin(7.0*t)
The constants C1,C2 can be determined by evaluating x(0),x'(0) for the initial conditions.

VPython: Iterating a function to create a new curve every run

I'm creating a program to find the optimum angle to launch a projectile at a given speed, in order to hit a target a given distance away.
i have this code:
def Trajectory(angle):
position = vector(0,0,0)
poscurve.plot(pos=position)
velocity = v0 * vector(cos(angle*pi/180), sin(angle*pi/180), 0)
while (position.y > 0) or (velocity.y > 0):
dx = velocity * dt # update dx and dv
dv = g * dt
position = position + dx # apply the updates to velocity and position
velocity = velocity + dv
poscurve.plot(pos=position)
return position
The problem is, this program repeats this function multiple times, with different angles. When it repeats it, it connects the first point of the new curve to the last point of the previous curve, because the function uses the same poscurve.plot(pos=position) every time. How do I make the function generate a new curve each time it iterates this function?
(there are variables defined outside of this code snippet, the program conceptually works fine, just how the graph looks)
Add poscurve as a parameter:
def Trajectory(angle,poscurve):
position = vector(0,0,0)
poscurve.plot(pos=position)
velocity = v0 * vector(cos(angle*pi/180), sin(angle*pi/180), 0)
while (position.y > 0) or (velocity.y > 0):
dx = velocity * dt # update dx and dv
dv = g * dt
position = position + dx # apply the updates to velocity and position
velocity = velocity + dv
poscurve.plot(pos=position)
return position
Then, pass in a new poscurve every time. You can just call gcurve again to create a new object.

Categories

Resources