Planetary orbit shown as linear graph using rk4 - python

I am trying to simulate the orbit of a planet around a star using the Runge-Kutta 4 method. After speaking to tutors my code should be correct. However, I am not generating my expected 2D orbital plot but instead a linear plot. This is my first time using solve_ivp to solve a second order differential. Can anyone explain why my plots are wrong?
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
# %% Define derivative function
def f(t, z):
x = z[0] # Position x
y = z[1] # Position y
dx = z[2] # Velocity x
dy = z[3] # Velocity y
G = 6.674 * 10**-11 # Gravitational constant
M = 2 # Mass of binary stars in solar masses
c = 2*G*M
r = np.sqrt(y**2 + x**2) # Distance of planet from stars
zdot = np.empty(6) # Array for integration solutions
zdot[0] = x
zdot[1] = y
zdot[2] = dx # Velocity x
zdot[3] = dy #Velocity y
zdot[4] = (-c/(r**3))*(x) # Acceleration x
zdot[5] = (-c/(r**3))*(y) # Acceleration y
return zdot
# %% Define time spans, initial values, and constants
tspan = np.linspace(0., 10000., 100000000)
xy0 = [0.03, -0.2, 0.008, 0.046, 0.2, 0.3] # Initial positions x,y in R and velocities
# %% Solve differential equation
sol = solve_ivp(lambda t, z: f(t, z), [tspan[0], tspan[-1]], xy0, t_eval=tspan)
# %% Plot
#plot
plt.grid()
plt.subplot(2, 2, 1)
plt.plot(sol.y[0],sol.y[1], color='b')
plt.subplot(2, 2, 2)
plt.plot(sol.t,sol.y[2], color='g')
plt.subplot(2, 2, 3)
plt.plot(sol.t,sol.y[4], color='r')
plt.show()

With the ODE function as given, you are solving in the first components the system
xdot = x
ydot = y
which has well-known exponential solutions. As the exponential factor is the same long both solutions, the xy-plot will move along a line through the origin.
The solution is of course to fill zdot[0:2] with dx,dy, and zdot[2:4] with ax,ay or ddx,ddy or however you want to name the components of the acceleration. Then the initial state also has only 4 components. Or you need to make and treat position and velocity as 3-dimensional.
You need to put units to your constants and care that all use the same units. G as cited is in m^3/kg/s^2, so that any M you define will be in kg, any length is in m and any velocity in m/s. Your constants might appear ridiculously small in that context.
It does not matter what the comment in the code says, there will be no magical conversion. You need to use actual conversion computations to get realistic numbers. For instance using the numbers
G = 6.67408e-11 # m^3 s^-2 kg^-1
AU = 149.597e9 # m
Msun = 1.988435e30 # kg
hour = 60*60 # seconds in an hour
day = hour * 24 # seconds in one day
year = 365.25*day # seconds in a year (not very astronomical)
one could guess that for a sensible binary system of two stars of equal mass one has
M = 2*Msun # now actually 2 sun masses
x0 = 0.03*AU
y0 = -0.2*AU
vx0 = 0.008*AU/day
vy0 = 0.046*AU/day
For the position only AU makes sense as unit, the speed could also be in AU/hour. By https://math.stackexchange.com/questions/4033996/developing-keplers-first-law and Cannot get RK4 to solve for position of orbiting body in Python the speed for a circular orbit of radius R=0.2AU around a combined mass of 2*M is
sqrt(2*M*G/R)=sqrt(4*Msun*G/(0.2*AU)) = 0.00320 * AU/hour = 0.07693 AU/day
which is ... not too unreasonable if the given speeds are actually in AU/day. Invoke the computations from https://math.stackexchange.com/questions/4050575/application-of-the-principle-of-conservation to compute if the Kepler ellipse would look sensible
r0 = (x0**2+y0**2)**0.5
dotr0 = (x0*vx0+y0*vy0)/r0
L = x0*vy0-y0*vx0 # r^2*dotphi = L constant, L^2 = G*M_center*R
dotphi0 = L/r0**2
R = L**2/(G*2*M)
wx = R/r0-1; wy = -dotr0*(R/(G*2*M))**0.5
E = (wx*wx+wy*wy)**0.5; psi = m.atan2(wy,wx)
print(f"half-axis R={R/AU} AU, eccentr. E={E}, init. angle psi={psi}")
print(f"min. rad. = {R/(1+E)/AU} AU, max. rad. = {R/(1-E)/AU} AU")
which returns
half-axis R=0.00750258 AU, eccentr. E=0.96934113, init. angle psi=3.02626659
min. rad. = 0.00380969 AU, max. rad. = 0.24471174 AU
This gives an extremely thin ellipse, which is not that astonishing as the initial velocity points almost directly to the gravity center.
orbit variants with half-day steps marked, lengths in AU
If the velocity components were swapped one would get
half-axis R=0.07528741 AU, eccentr. E=0.62778767, init. angle psi=3.12777251
min. rad. = 0.04625137 AU, max. rad. = 0.20227006 AU
This is a little more balanced.

Related

3 Body Problem Outputs a spikey ball rather than an orbital path

I'm trying to solve the 3 body problem with solve_ivp and its runge kutta sim, but instead of a nice orbital path it outputs a spiked ball of death. I've tried changing the step sizes and step lengths all sorts, I have no idea why the graphs are so spikey, it makes no sense to me.
i have now implemented the velocity as was suggested but i may have done it wrong
What am I doing wrong?
Updated Code:
from scipy.integrate import solve_ivp
import numpy as np
import matplotlib.pyplot as plt
R = 150000000 #radius from centre of mass to stars orbit
#G = 1/(4*np.pi*np.pi) #Gravitational constant in AU^3/solar mass * years^2
G = 6.67e-11
M = 5e30 #mass of the stars assumed equal mass in solar mass
Omega = np.sqrt(G*M/R**3.0) #inverse of the orbital period of the stars
t = np.arange(0, 1000, 1)
x = 200000000
y = 200000000
vx0 = -0.0003
vy0 = 0.0003
X1 = R*np.cos(Omega*t)
X2 = -R*np.cos(Omega*t)
Y1 = R*np.sin(Omega*t)
Y2 = -R*np.sin(Omega*t) #cartesian coordinates of both stars 1 and 2
r1 = np.sqrt((x-X1)**2.0+(y-Y1)**2.0) #distance from planet to star 1 or 2
r2 = np.sqrt((x-X2)**2.0+(y-Y2)**2.0)
xacc = -G*M*((1/r1**2.0)*((x-X1)/r1)+(1/r2**2.0)*((x-X2)/r2))
yacc = -G*M*((1/r1**2.0)*((y-Y1)/r1)+(1/r2**2.0)*((y-Y2)/r2)) #x double dot and y double dot equations of motions
#when t = 0 we get the initial contditions
r1_0 = np.sqrt((x-R)**2.0+(y-0)**2.0)
r2_0 = np.sqrt((x+R)**2.0+(y+0)**2.0)
xacc0 = -G*M*((1/r1_0**2.0)*((x-R)/r1_0)+(1/r2_0**2.0)*((x+R)/r2_0))
yacc0 = -G*M*((1/r1_0**2.0)*((y-0)/r1_0)+(1/r2_0**2.0)*((y+0)/r2_0))
#inputs for runge-kutta algorithm
tp = Omega*t
r1p = r1/R
r2p = r2/R
xp = x/R
yp = y/R
X1p = X1/R
X2p = X2/R
Y1p = Y1/R
Y2p = Y2/R
#4 1st ode
#vx = dx/dt
#vy = dy/dt
#dvxp/dtp = -(((xp-X1p)/r1p**3.0)+((xp-X2p)/r2p**3.0))
#dvyp/dtp = -(((yp-Y1p)/r1p**3.0)+((yp-Y2p)/r2p**3.0))
epsilon = x*np.cos(Omega*t)+y*np.sin(Omega*t)
nave = -x*np.sin(Omega*t)+y*np.cos(Omega*t)
# =============================================================================
# def dxdt(x, t):
# return vx
#
# def dydt(y, t):
# return vy
# =============================================================================
def dvdt(t, state):
xp, yp = state
X1p = np.cos(Omega*t)
X2p = -np.cos(Omega*t)
Y1p = np.sin(Omega*t)
Y2p = -np.sin(Omega*t)
r1p = np.sqrt((xp-X1p)**2.0+(yp-Y1p)**2.0)
r2p = np.sqrt((xp-X2p)**2.0+(yp-Y2p)**2.0)
return (-(((xp-X1p)/(r1p**3.0))+((xp-X2p)/(r2p**3.0))),-(((yp-Y1p)/(r1p**3.0))+((yp-Y2p)/(r2p**3.0))))
def vel(t, state):
xp, yp, xv, yv = state
return (np.concatenate([[xv, yv], dvdt(t, [xp, yp]) ]))
p = (R, G, M, Omega)
initial_state = [xp, yp, vx0, vy0]
t_span = (0.0, 1000) #1000 years
result_solve_ivp_dvdt = solve_ivp(vel, t_span, initial_state, atol=0.1) #Runge Kutta
fig = plt.figure()
plt.plot(result_solve_ivp_dvdt.y[0,:], result_solve_ivp_dvdt.y[1,:])
plt.plot(X1p, Y1p)
plt.plot(X2p, Y2p)
Output:
Green is the stars plot and blue remains the velocity
Km and seconds
Years, AU and Solar Masses
You have produced the equation
dv/dt = a(x)
But then you used the acceleration, the derivative of the velocity, as the derivative of the position. This is physically wrong.
You should pass the function
lambda t, xv: np.concantenate([xv[2:], dvdt(xv[:2]) ])
to the solver, with a suitable initial state containing velocity components in addition to the position components.
In the 2-star system with the fixed orbit, the stars have distance 1. This distance, not the distance 0.5 to the center, should enter the computation of the angular velocity.
z_1 = 0.5*exp(2*pi*i*t), z_2 = -z_1 ==> z_1-z_2=2*z_1, abs(z_1-z_2)=1
z_1'' = -GM * (z_1-z_2)/abs(z_1-z_2)^3
-0.5*4*pi^2 = -GM or GM = 2*pi^2
Now insert a satellite into a circular radius at some radius R as if there was only one central mass 2M stationary at the origin
z_3 = R*exp(i*w*t)
z_3'' = -2GM * z_3/abs(z_3)^3
R^3*w^2=2GM
position (R,0), velocity (0,w*R)=(0,sqrt(2GM/R))
In python code
GM = 2*np.pi**2
R = 1.9
def kepler(t,u):
z1 = 0.5*np.exp(2j*np.pi*t)
z3 = u[0]+1j*u[1]
a = -GM*((z3-z1)/abs(z3-z1)**3+(z3+z1)/abs(z3+z1)**3)
return [u[2],u[3],a.real,a.imag]
res = solve_ivp(kepler,(0,17),[R,0,0,2*np.pi*(1/R)**0.5], atol=1e-8, rtol=1e-11)
print(res.message)
This gives a trajectory plot of
The effect of the binary system on the satellite is a continuous sequence of swing-by maneuvers, accelerating the angular speed until escape velocity is reached. With R=1.5 or smaller this happens with the first close encounter of satellite and closest star, so that the satellite is ejected immediately from the system.
Never-the-less, one can still get "spiky-ball" orbits. Setting R=1.6 in the above code, with tighter error tolerances and integrating to t=27 gives the trajectory

Solve a second order ode using numpy [duplicate]

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.

Python Slingshot Model

I am trying to model firing a projectile from a slingshot.
This is my code:
from pylab import *
import numpy as np
from scipy.integrate import odeint
import seaborn
## set initial conditions and parameters
g = 9.81 # acceleration due to gravity
th = 30 # set launch angle
th = th * np.pi/180. # convert launch angle to radians
v0 = 10.0 # set initial speed
c = 0.5 # controls strength of air drag
d = 0.02 # diameter of the spherical rock
A = pi * (d/2)**2 #the cross-sectional area of the spherical rock
ro = 1.2041 #the density of the medium we are perfoming the launch in
m = 0.01 #mass
x0=0 # specify initial conditions
y0=0
vx0 = v0*sin(th)
vy0 = v0*cos(th)
## defining our model
def slingshot_model(state,time):
z = zeros(4) # create array to hold z vector
z[0] = state[2] # z[0] = x component of velocity
z[1] = state[3] # z[1] = y component of velocity
z[2] = - c*A*ro/2*m*sqrt(z[0]**2 + z[1]**2)*z[0] # z[2] = acceleration in x direction
z[3] = -g/m - c*A*ro/2*m*sqrt(z[0]**2 + z[1]**2)*z[1] # z[3] = acceleration in y direction
return z
## set initial state vector and time array
X0 = [x0, y0, vx0, vy0] # set initial state of the system
t0 = 0
tf = 4 #final time
tau = 0.05 #time step
# create time array starting at t0, ending at tf with a spacing tau
t = arange(t0,tf,tau)
## solve ODE using odeint
X = odeint(slingshot_model,X0,t) # returns an 2-dimensional array with the
# first index specifying the time and the
# second index specifying the component of
# the state vector
# putting ':' as an index specifies all of the elements for
# that index so x, y, vx, and vy are arrays at times specified
# in the time array
x = X[:,0]
y = X[:,1]
vx = X[:,2]
vy = X[:,3]
plt.rcParams['figure.figsize'] = [10, 10]
plot(x,y)
But it gives me this plot that doesn't make sense to me:
What am I missing? The values shouldn't come out like they do, but for the life of me I can't see why.
It is probably something trivial, but I have been staring at this too long, so I figured bringing in a fresh set of eyes is the best course of action.
I think there are at least two major problems with your computations:
Usually angle is defined with regard to the X-axis. Therefore
vx0 = v0*cos(th) # not sin
vy0 = v0*sin(th) # not cos
Most importantly, why are you dividing acceleration of the free fall g by the mass? (see z[3] = -g/m...) This makes no sense to me. DO NOT divide by mass!
EDIT:
Based on your comment and linked formulae, it is clear that your code also suffers from a third mistake: air drag terms should be inverse-proportional to mass:

How can I get a fast estimate for the distance between a point and a bicubic spline surface in Python?

How can I get a fast estimate for the distance between a point and a bicubic spline surface in Python? Is there an existing solution that I could leverage in SciPy, NumPy, or some other package?
I've got surface defined by a bicubic interpolation as this:
import numpy as np
import scipy.interpolate
# Define regular grid surface
xmin,xmax,ymin,ymax = 25, 125, -50, 50
x = np.linspace(xmin,xmax, 201)
y = np.linspace(ymin,ymax, 201)
xx, yy = np.meshgrid(x, y)
z_ideal = ( xx**2 + yy**2 ) / 400
z_ideal += z_ideal + np.random.uniform(-0.5, 0.5, z_ideal.shape)
s_ideal = scipy.interpolate.interp2d(x, y, z_ideal, kind='cubic')
and I've got some measured points of that surface:
# Fake some measured points on the surface
z_measured = z_ideal + np.random.uniform(-0.1, 0.1, z_ideal.shape)
s_measured = scipy.interpolate.interp2d(x, y, z_measured, kind='cubic')
p_x = np.random.uniform(xmin,xmax,10000)
p_y = np.random.uniform(ymin,ymax,10000)
p_z = s_measured( p_x, p_y )
I want to find the closest point on the surface s_ideal to each point in p. A general case could have multiple solutions for wildly varying splines, so I'm limiting the problem to surfaces that are known to have only one solution in the vicinity of the point's projection along z.
This isn't a tiny number of measurement or surface definition points, so I'd like to optimize the speed even at the expense of accuracy to maybe 1E-5.
The method that comes to mind is to use a gradient descent approach and do something like for each measurement point p:
Use pt = [p_x, p_y, p_z] as the initial test point, where p_z = s_ideal(pt)
Calculate the slope (tangent) vector m = [ m_x, m_y ] at pt
Calculate the vector r from pt to p: r = p - pt
If the angle theta between r and m is within some threshold of 90 deg, then pt is the final point.
Otherwise, update pt as:
r_len = numpy.linalg.norm(r)
dx = r_len * m_x
dy = r_len * m_y
if theta > 90:
pt = [ p_x + dx, p_y + dy ]
else:
pt = [ p_x - dx, p_y - dy ]
I found this suggesting a method could produce fast results to a very high accuracy for the 1D case, but it's for a single dimension and might be too hard for me to convert to two.
The question seeks to minimize the Euclidian distance between a three-dimensional surface S(x,y,z) and another point x0,y0,z0. The surface is defined on a rectangular (x,y) mesh, where z(x,y) = f(x,y) + random_noise(x,y). The introduction of noise to the "ideal" surface adds considerable complexity to the problem, as it requires the surface to be interpolated using a 2-dimensional third-order spline.
It's not understood why the introduction of noise to the ideal surface is actually necessary. If the ideal surface were truly ideal, it should by understood well enough that a true polynomial fit in x and y could be determined, if not analytically, at least empirically. If the random noise were to simulate an actual measurement, then one only needs to record the measurement enough times until the noise is averaged out to zero. Similarly, the use of signal filtering can help eliminate noise and reveal the signal's true behavior.
To find the closest point on the surface to another point, the distance equation and its derivatives must be used. If the surface can truly only be described using a basis of splines, then one must reconstruct the spline representation and find its derivatives, which is non-trivial. Alternatively, the surface could be evaluated using a fine mesh, but here, one quickly runs into memory issues, which is why interpolation was used in the first place.
However, if we can agree that the surface can be defined using a simple expression in x and y, then the minimization becomes trivial:
For purposes of minimization, it is more convenient to look at the square of the distance d^2(x,y) (z is just a function of x and y) between two points, D(x,y), as it eliminates the square root. To find the critical points of D(x,y), we take its partial derivatives w.r.t x and y and find their roots by setting = 0: d/dx D(x,y) = f1(x,y) = 0 and d/dy D(x,y) = f2(x,y)=0. This is a non-linear system of equations, for which we can solve using scipy.optimize.root. We need only pass root a guess (the projection of the pt of interest onto the surface) and the Jacobian of the system of equations.
import numpy as np
import scipy.interpolate
import scipy.optimize
# Define regular grid surface
xmin,xmax,ymin,ymax = 25, 125, -50, 50
x = np.linspace(xmin,xmax, 201)
y = np.linspace(ymin,ymax, 201)
xx, yy = np.meshgrid(x, y)
z_ideal = ( xx**2 + yy**2 ) / 400
# Fake some measured points on the surface
z_measured = z_ideal + np.random.uniform(-0.1, 0.1, z_ideal.shape)
s_measured = scipy.interpolate.interp2d(x, y, z_measured, kind='cubic')
p_x = np.random.uniform(xmin,xmax,10000)
p_y = np.random.uniform(ymin,ymax,10000)
# z_ideal function
def z(x):
return (x[0] ** 2 + x[1] ** 2) / 400
# returns the system of equations
def f(x,pt):
x0,y0,z0 = pt
f1 = 2*(x[0] - x0) + (z(x)-z0)*x[0]/100
f2 = 2*(x[1] - y0) + (z(x)-z0)*x[1]/100
return [f1,f2]
# returns Jacobian of the system of equations
def jac(x, pt):
x0,y0,z0 = pt
return [[2*x[0]+1/100*(1/400*(z(x)+2*x[0]**2))-z0, x[0]*x[1]/2e4],
[2*x[1]+1/100*(1/400*(z(x)+2*x[1]**2))-z0, x[0]*x[1]/2e4]]
def minimize_distance(pt):
guess = [pt[0],pt[1]]
return scipy.optimize.root(f,guess,jac=jac, args=pt)
# select a random point from the measured data
x0,y0 = p_x[30], p_y[30]
z0 = float(s_measured(x0,y0))
minimize_distance([x0,y0,z0])
Output:
fjac: array([[-0.99419141, -0.1076264 ],
[ 0.1076264 , -0.99419141]])
fun: array([ -1.05033229e-08, -2.63163477e-07])
message: 'The solution converged.'
nfev: 19
njev: 2
qtf: array([ 2.80642738e-07, 2.13792093e-06])
r: array([-2.63044477, -0.48260582, -2.33011149])
status: 1
success: True
x: array([ 110.6726472 , 39.28642206])
Yes! Using K-Means with clustering will do exactly that. So s_ideal will be the target, then you train on p_z. Ultimately you will get centroids that will give you the closest point on the surface s_ideal to each point in p.
Here is a example, it fairly close to what you want.

projectile motion simple simulation using numpy matplotlib python

I am trying to graph a projectile through time at various angles. The angles range from 25 to 60 and each initial angle should have its own line on the graph. The formula for "the total time the projectile is in the air" is the formula for t. I am not sure how this total time comes into play, because I am supposed to graph the projectile at various times with various initial angles. I imagine that I would need x,x1,x2,x3,x4,x5 and the y equivalents in order to graph all six of the various angles. But I am confused on what to do about the time spent.
import numpy as np
import matplotlib.pylab as plot
#initialize variables
#velocity, gravity
v = 30
g = -9.8
#increment theta 25 to 60 then find t, x, y
#define x and y as arrays
theta = np.arange(25,65,5)
t = ((2 * v) * np.sin(theta)) / g #the total time projectile remains in the #air
t1 = np.array(t) #why are some negative
x = ((v * t1) * np.cos(theta))
y = ((v * t1) * np.sin(theta)) - ((0.5 * g) * (t ** 2))
plot.plot(x,y)
plot.show()
First of all g is positive! After fixing that, let's see some equations:
You know this already, but lets take a second and discuss something. What do you need to know in order to get the trajectory of a particle?
Initial velocity and angle, right? The question is: find the position of the particle after some time given that initial velocity is v=something and theta=something. Initial is important! That's the time when we start our experiment. So time is continuous parameter! You don't need the time of flight.
One more thing: Angles can't just be written as 60, 45, etc, python needs something else in order to work, so you need to write them in numerical terms, (0,90) = (0,pi/2).
Let's see the code:
import numpy as np
import matplotlib.pylab as plot
import math as m
#initialize variables
#velocity, gravity
v = 30
g = 9.8
#increment theta 25 to 60 then find t, x, y
#define x and y as arrays
theta = np.arange(m.pi/6, m.pi/3, m.pi/36)
t = np.linspace(0, 5, num=100) # Set time as 'continous' parameter.
for i in theta: # Calculate trajectory for every angle
x1 = []
y1 = []
for k in t:
x = ((v*k)*np.cos(i)) # get positions at every point in time
y = ((v*k)*np.sin(i))-((0.5*g)*(k**2))
x1.append(x)
y1.append(y)
p = [i for i, j in enumerate(y1) if j < 0] # Don't fall through the floor
for i in sorted(p, reverse = True):
del x1[i]
del y1[i]
plot.plot(x1, y1) # Plot for every angle
plot.show() # And show on one graphic
You are making a number of mistakes.
Firstly, less of a mistake, but matplotlib.pylab is supposedly used to access matplotlib.pyplot and numpy together (for a more matlab-like experience), I think it's more suggested to use matplotlib.pyplot as plt in scripts (see also this Q&A).
Secondly, your angles are in degrees, but math functions by default expect radians. You have to convert your angles to radians before passing them to the trigonometric functions.
Thirdly, your current code sets t1 to have a single time point for every angle. This is not what you need: you need to compute the maximum time t for every angle (which you did in t), then for each angle create a time vector from 0 to t for plotting!
Lastly, you need to use the same plotting time vector in both terms of y, since that's the solution to your mechanics problem:
y(t) = v_{0y}*t - g/2*t^2
This assumes that g is positive, which is again wrong in your code. Unless you set the y axis to point downwards, but the word "projectile" makes me think this is not the case.
So here's what I'd do:
import numpy as np
import matplotlib.pyplot as plt
#initialize variables
#velocity, gravity
v = 30
g = 9.81 #improved g to standard precision, set it to positive
#increment theta 25 to 60 then find t, x, y
#define x and y as arrays
theta = np.arange(25,65,5)[None,:]/180.0*np.pi #convert to radians, watch out for modulo division
plt.figure()
tmax = ((2 * v) * np.sin(theta)) / g
timemat = tmax*np.linspace(0,1,100)[:,None] #create time vectors for each angle
x = ((v * timemat) * np.cos(theta))
y = ((v * timemat) * np.sin(theta)) - ((0.5 * g) * (timemat ** 2))
plt.plot(x,y) #plot each dataset: columns of x and columns of y
plt.ylim([0,35])
plot.show()
I made use of the fact that plt.plot will plot the columns of two matrix inputs versus each other, so no loop over angles is necessary. I also used [None,:] and [:,None] to turn 1d numpy arrays to 2d row and column vectors, respectively. By multiplying a row vector and a column vector, array broadcasting ensures that the resulting matrix behaves the way we want it (i.e. each column of timemat goes from 0 to the corresponding tmax in 100 steps)
Result:

Categories