The code outputs a graph that is nowhere near what you'd expect of a projectile motion type of graph. In addition, if you change the step number to around 2, the graph doesn't output much of anything.
import numpy as np
import matplotlib.pyplot as plt
grav = 9.8
airDen = 1.225 # kg/m^3
areaProj = 0.8643 # m^2
v0 = 198.1 # m/s
angle = 60.0
dragCoef = 0.62 #Approximate value
mass = 15.0 #kg
step = 0.02 #time step in seconds
t = [0]
vx = [v0*np.cos(angle*np.pi/180)]
vy = [v0*np.sin(angle*np.pi/180)]
x = [0]
y = [0]
dragForce = 0.5*dragCoef*areaProj*(v0**2)*airDen
ax = [-(dragForce*np.cos(angle/180*np.pi))/mass]
ay = [-grav-(dragForce*np.sin(angle/180*np.pi)/mass)]
counter = 0
while(y[counter] >= 0):
t.append(t[counter]+step)
vx.append(vx[counter]+step*ax[counter])
vy.append(vy[counter]+step*ay[counter])
x.append(x[counter]+step*vx[counter])
y.append(y[counter]+step*vy[counter])
vel = np.sqrt(vx[counter+1]**2 + vy[counter+1]**2)
dragForce = 0.5*dragCoef*areaProj*(vel**2)*airDen
ax.append(-(dragForce*np.cos(angle/180*np.pi))/mass)
ay.append(-grav-(dragForce*np.sin(angle/180*np.pi)/mass))
counter=counter+1
plt.plot(x,y)
plt.ylabel("y (m)")
plt.xlabel("x (m)")
Graph-example
Looks like you're not updating your angle, so instead of reaching zero vx it accelerates backwards since it thinks you're still going at 60 degrees initial angle. Adding an updated angle based on the current velocity vector results in the following:
import numpy as np
import matplotlib.pyplot as plt
grav = 9.8
airDen = 1.225 # kg/m^3
areaProj = 0.8643 # m^2
v0 = 198.1 # m/s
angle = 60.0
dragCoef = 0.62 #Approximate value
mass = 15.0 #kg
step = 0.02 #time step in seconds
t = [0]
vx = [v0*np.cos(angle*np.pi/180)]
vy = [v0*np.sin(angle*np.pi/180)]
x = [0]
y = [0]
dragForce = 0.5*dragCoef*areaProj*(v0**2)*airDen
ax = [-(dragForce*np.cos(angle/180*np.pi))/mass]
ay = [-grav-(dragForce*np.sin(angle/180*np.pi)/mass)]
counter = 0
while(y[counter] >= 0):
t.append(t[counter]+step)
vx.append(vx[counter]+step*ax[counter])
vy.append(vy[counter]+step*ay[counter])
x.append(x[counter]+step*vx[counter])
y.append(y[counter]+step*vy[counter])
vel = np.sqrt(vx[counter+1]**2 + vy[counter+1]**2)
dragForce = 0.5*dragCoef*areaProj*(vel**2)*airDen
angle = np.arctan(vy[counter]/vx[counter]) * (180 / np.pi)
ax.append(-(dragForce*np.cos(angle/180*np.pi))/mass)
ay.append(-grav-(dragForce*np.sin(angle/180*np.pi)/mass))
counter=counter+1
plt.plot(x,y)
plt.ylabel("y (m)")
plt.xlabel("x (m)")
Related
I want to generate random coordinates for spheres in a box geometry. I'm using while loop and i have 2 condition. First one is the distance of coordinates. General distance formula was used so that the coordinates do not overlap. Second one is the porosity. When porosity is less than 0.45 generating should stop. My code is working correctly but when i reduce porosity condition less than 0.80 the algorithm stucks. It cannot reach that porosity even after hours. How can I improve it to generate coordinates faster? Any suggestions are appreciated.
#dist = math.sqrt(((x2-x1)**2) + ((y2-y1)**2) + ((z2-z1)**2))
import math
import random
import numpy as np
import matplotlib.pyplot as plt
A = 0.04 # x border.
B = 0.04 # y border.
C = 0.125 # z border.
V_total = A*B*C # volume
r = 0.006 # min distance of spheres.
radius = 0.003 # radius of spheres.
wall_distance = 0.003
Porosity = 1.0
coordinates = np.array([])
while Porosity >= 0.90:
# coordinates
x = random.uniform(wall_distance, A-wall_distance)
y = random.uniform(wall_distance, B-wall_distance)
z = random.uniform(wall_distance, C-wall_distance)
coord1 = (x,y,z)
if coordinates.shape[0] == 0: # add first one without condition
coordinates = np.array([coord1])
else:
coordinates = np.vstack((coordinates, coord1))
# seperate x,y,z and convert list for control
d_x = coordinates[:,0]
x = d_x.tolist()
d_y = coordinates[:,1]
y = d_y.tolist()
d_z = coordinates[:,2]
z = d_z.tolist()
for j in range(len(y)):
for k in range(j+1, len(z)):
dist = math.sqrt(((x[j]-x[k])**2) + ((y[j]-y[k])**2) + ((z[j]-z[k])**2))
if dist <= r:
coordinates = coordinates[:-1, :] # if distance is less than r, remove last coordinate
# check porosity
V_spheres = (4/3) * (np.pi) * (radius**3) * len(coordinates)
V_void = V_total - V_spheres
Porosity = V_void / V_total
print("Porosity: {}".format(Porosity))
print("Number of spheres: {}".format(len(coordinates)))
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.set_xlim([0, A])
ax.set_ylim([0, B])
ax.set_zlim([0, C])
ax.set_title('Coordinates for spheres')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
p = ax.scatter(coordinates[:,0], coordinates[:,1], coordinates[:,2])
fig.colorbar(p)
plt.show()
There are a number of things you can do to improve your performance here. See my modified code below, with explanations
import math
import random
import numpy as np
import matplotlib.pyplot as plt
A = 0.04 # x border.
B = 0.04 # y border.
C = 0.125 # z border.
V_total = A*B*C # volume
r = 0.006 # min distance of spheres.
radius = 0.003 # radius of spheres.
wall_distance = 0.003
Porosity = 1.0
coordinates = np.empty((0,3)) # initialize array with correct shape
while Porosity >= 0.70:
# coordinates
x = random.uniform(wall_distance, A-wall_distance)
y = random.uniform(wall_distance, B-wall_distance)
z = random.uniform(wall_distance, C-wall_distance)
is_invalid = (True in [
math.sqrt(((x - coordinates[i_coor,0])**2) +
((y - coordinates[i_coor,1])**2) +
((z - coordinates[i_coor,2])**2)) <= r
for i_coor in range(coordinates.shape[0]) ])
if not is_invalid:
coordinates = np.append(coordinates,[[x,y,z]], axis = 0)
else:
continue
V_spheres = (4/3) * (np.pi) * (radius**3) * len(coordinates)
V_void = V_total - V_spheres
Porosity = V_void / V_total
print(f"placed coord {len(coordinates)}, por = {Porosity}")
print("Porosity: {}".format(Porosity))
print("Number of spheres: {}".format(len(coordinates)))
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.set_xlim([0, A])
ax.set_ylim([0, B])
ax.set_zlim([0, C])
ax.set_title('Coordinates for spheres')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
p = ax.scatter(coordinates[:,0], coordinates[:,1], coordinates[:,2])
np.savetxt('out.csv', coordinates)
fig.colorbar(p)
plt.show()
the main thing I changed is this double for loop
for j in range(len(y)):
for k in range(j+1, len(z)):
dist = math.sqrt(((x[j]-x[k])**2) + ((y[j]-y[k])**2) + ((z[j]-z[k])**2))
This was checking every pair of points for overlap EACH TIME YOU ADD A SINGLE POINT. That took unnecessarily long. By only checking if the new point intersects with the old points, you reduce your runtime from O(n^3) to O(n^2). I was able to pretty quickly run this with 0.5 perosity.
I'm trying to visualize the solution to my code by plotting - in python - a graph of the number of predators versus preys, and using a black circle to mark the current state. My code runs okay except I can't get the plotting portion of the graph to print the plots on the graph.
This is my current python code:
import matplotlib.pyplot as plt
import numpy as np
from trajectoryplotsupport import *
def ExactTrajectory(dt, ntimesteps):
tt = np.linspace(0, dt*ntimesteps, ntimesteps+1)
x = np.cos(2*np.pi*tt)
y = np.sin(2*np.pi*tt)
return x,y
def GetVelocity(pos, tn):
u =-2.0*np.pi * pos[1]
v = 2.0*np.pi * pos[0]
vel = np.array([u,v])
return vel
def RK3(p, t):
vel = GetVelocity(p, t) # stage 1 velocity
pt = p + 0.5*dt*vel # stage 1 provisional position
vel = GetVelocity(pt, t+dt) # stage 2 velocity
pt = 0.25 * p + 0.75*( pt + dt*vel ) # stage 2 provisional position
vel = GetVelocity(pt, t+0.5*dt) # stage 3 velocity
p = ( p + 2.0*(pt + dt*vel) )/3.0 # stage 3 provisional position
return p
def PreyPred(p, t):
b = 0.5 # prey birth rate
m = 0.2 # predator mortality
a = 0.04 # effect of predator abundance on prey
r = 0.01 # effect of prey abundance on predator
h, w = p[0], p[1] # initial densities of prey and predator are h(0) = 4.0 and w(0) = 8.0
dht = h*(b - a*w)
dwt = w*(-m + r*h)
return np.array([dht, dwt])
#################MAIN CODE#############
p = np.array([4.0, 8.0]) # initial position
ntimesteps = 120 # number of time steps
finalTime = 480 # final time
dt = 1 # time step
xe,ye = ExactTrajectory(dt, ntimesteps) # call exact trajectory
# Check on computations. Create 2 array to track the positions and initialize
pos = np.zeros(2) # create current position array
pos[0] = xe[0]; pos[1]=ye[0] # initialize current position
fig, lastPosition = InitialPlot(p, xe,ye)
for it in range(finalTime): # time loop
t = it*dt # set current time
p = RK3(p, t)
print(t, p)
UpdatePlot(fig,lastPosition,p,t) # update the plot
ttpoints = np.arange(0, 480, dt) #edited
hpoints, wpoints = [], []
p = np.array([0, 0], float)
for tt in ttpoints:
hpoints.append(p[0])
wpoints.append(p[1])
p += RK3(p, tt)
plt.plot(ttpoints, hpoints)
plt.plot(ttpoints, wpoints)
plt.xlabel("Prey")
plt.ylabel("Predator")
plt.plot(hpoints, wpoints)
plt.show()
I am trying to create trajectory plot in matplotlib to show how planet(comet or any body with mass) moving around the star(sun). In my context it is assumed that sun is on position (0, 0). Unfortunately, there is something off in my calculation or code or maybe both, thus I cant get the final result right.
This code below producing this
Which is clearly wrong. I have tried to google and playing around with code, but still cannot achieve the expected plot.
The expected result should be
These are the formulas I was using.
import matplotlib.pyplot as plt
from math import cos, sin, hypot
# constants
G = 6.67
dt = 0.005
# planet
planet_m = 5.97
planet_x = 1
planet_y = 0
# star
star_m = 1.98
star_x = 0
star_y = 0
# velocities
vx = 0
vy = 8
#
dx = 0
dy = 0
F = 0
t = 0
fxl = []
fyl = []
for i in range(1, 50):
t += i * dt
dx += (star_x-planet_x)
dy += (star_y-planet_y)
d = hypot(dx, dy)# the same as sqrt(dx**2 + dy**2)
F += G * planet_x * star_m / (d**2)
fx = cos(dx) * -F
fy = sin(dy) * -F
vx += fx / planet_m * t
vy += fy / planet_m * t
planet_x += vx * dt
planet_y += vy * dt
fyl.append(fx)
fxl.append(fy)
print(f'Position: {planet_x} {planet_y}')
print(f'Velocities: {vx} {vy}')
plt.plot(star_x, star_y, 'yo', fxl, fyl, '-')
plt.grid()
plt.show()
Well, I think there are a lot of strange things going on in your code. I tried to fix it in a minimal way, but there's still a very big room for improvements:
import matplotlib.pyplot as plt
from math import cos, sin, hypot
# constants
G = 6.67
dt = 0.0001
# planet
planet_m = 5.97
planet_x = 1
planet_y = 0
# star
star_m = 1.98
star_x = 0
star_y = 0
# velocities
vx = 0
vy = 1
pos_x = []
pos_y = []
for i in range(1, 100000):
dx = (star_x-planet_x)
dy = (star_y-planet_y)
d = hypot(dx, dy)
F = G * star_m / (d**2)
fx = F * dx / d
fy = F * dy / d
vx += fx / planet_m * dt
vy += fy / planet_m * dt
planet_x += vx * dt
planet_y += vy * dt
pos_x.append(planet_x)
pos_y.append(planet_y)
plt.plot(star_x, star_y, 'yo', pos_x, pos_y, '-')
plt.show()
I am trying to make a plot of a projectile motion of a mass which is under the effect of gravitational, buoyancy and drag force. Basically, I want to show effects of the buoyancy and drag force on flight distance, flight time and velocity change on plotting.
import matplotlib.pyplot as plt
import numpy as np
V_initial = 30 # m/s
theta = np.pi/6 # 30
g = 3.711
m =1
C = 0.47
r = 0.5
S = np.pi*pow(r, 2)
ro_mars = 0.0175
t_flight = 2*(V_initial*np.sin(theta)/g)
t = np.linspace(0, t_flight, 200)
# Drag force
Ft = 0.5*C*S*ro_mars*pow(V_initial, 2)
# Buoyant Force
Fb = ro_mars*g*(4/3*np.pi*pow(r, 3))
x_loc = []
y_loc = []
for time in t:
x = V_initial*time*np.cos(theta)
y = V_initial*time*np.sin(theta) - (1/2)*g*pow(time, 2)
x_loc.append(x)
y_loc.append(y)
x_vel = []
y_vel = []
for time in t:
vx = V_initial*np.cos(theta)
vy = V_initial*np.sin(theta) - g*time
x_vel.append(vx)
y_vel.append(vy)
v_ch = [pow(i**2+ii**2, 0.5) for i in x_vel for ii in y_vel]
tau = []
for velocity in v_ch:
Ft = 0.5*C*S*ro_mars*pow(velocity, 2)
tau.append(Ft)
buoy = []
for velocity in v_ch:
Fb = ro_mars*g*(4/3*np.pi*pow(r, 3))
buoy.append(Fb)
after this point, I couldn't figure out how to plot to a projectile motion under this forces. In other words, I'm trying to compare the projectile motion of the mass under three circumstances
Mass only under the effect of gravity
Mass under the effect of gravity and air resistance
Mass under the effect of gravity, air resistance, and buoyancy
You must calculate each location based on the sum of forces at the given time. For this it is better to start from calculating the net force at any time and using this to calculate the acceleration, velocity and then position. For the following calculations, it is assumed that buoyancy and gravity are constant (which is not true in reality but the effect of their variability is negligible in this case), it is also assumed that the initial position is (0,0) though this can be trivially changed to any initial position.
F_x = tau_x
F_y = tau_y + bouyancy + gravity
Where tau_x and tau_y are the drag forces in the x and y directions, respectively. The velocities, v_x and v_y, are then given by
v_x = v_x + (F_x / (2 * m)) * dt
v_y = v_y + (F_y / (2 * m)) * dt
So the x and y positions, r_x and r_y, at any time t are given by the summation of
r_x = r_x + v_x * dt
r_y = r_y + v_y * dt
In both cases this must be evaluated from 0 to t for some dt where dt * n = t if n is the number of steps in summation.
r_x = r_x + V_i * np.cos(theta) * dt + (F_x / (2 * m)) * dt**2
r_y = r_y + V_i * np.sin(theta) * dt + (F_y / (2 * m)) * dt**2
The entire calculation can actually be done in two lines,
r_x = r_x + V_i * np.cos(theta) * dt + (tau_x / (2 * m)) * dt**2
r_y = r_y + V_i * np.sin(theta) * dt + ((tau_y + bouyancy + gravity) / (2 * m)) * dt**2
Except that v_x and v_y require updating at every time step. To loop over this and calculate the x and y positions across a range of times you can simply follow the below (edited) example.
The following code includes corrections to prevent negative y positions, since the given value of g is for the surface or Mars I assume this is appropriate - when you hit zero y and try to keep going you may end up with a rapid unscheduled disassembly, as we physicists call it.
Edit
In response to the edited question, the following example has been modified to plot all three cases requested - gravity, gravity plus drag, and gravity plus drag and buoyancy. Plot setup code has also been added
Complete example (edited)
import numpy as np
import matplotlib.pyplot as plt
def projectile(V_initial, theta, bouyancy=True, drag=True):
g = 9.81
m = 1
C = 0.47
r = 0.5
S = np.pi*pow(r, 2)
ro_mars = 0.0175
time = np.linspace(0, 100, 10000)
tof = 0.0
dt = time[1] - time[0]
bouy = ro_mars*g*(4/3*np.pi*pow(r, 3))
gravity = -g * m
V_ix = V_initial * np.cos(theta)
V_iy = V_initial * np.sin(theta)
v_x = V_ix
v_y = V_iy
r_x = 0.0
r_y = 0.0
r_xs = list()
r_ys = list()
r_xs.append(r_x)
r_ys.append(r_y)
# This gets a bit 'hand-wavy' but as dt -> 0 it approaches the analytical solution.
# Just make sure you use sufficiently small dt (dt is change in time between steps)
for t in time:
F_x = 0.0
F_y = 0.0
if (bouyancy == True):
F_y = F_y + bouy
if (drag == True):
F_y = F_y - 0.5*C*S*ro_mars*pow(v_y, 2)
F_x = F_x - 0.5*C*S*ro_mars*pow(v_x, 2) * np.sign(v_y)
F_y = F_y + gravity
r_x = r_x + v_x * dt + (F_x / (2 * m)) * dt**2
r_y = r_y + v_y * dt + (F_y / (2 * m)) * dt**2
v_x = v_x + (F_x / m) * dt
v_y = v_y + (F_y / m) * dt
if (r_y >= 0.0):
r_xs.append(r_x)
r_ys.append(r_y)
else:
tof = t
r_xs.append(r_x)
r_ys.append(r_y)
break
return r_xs, r_ys, tof
v = 30
theta = np.pi/4
fig = plt.figure(figsize=(8,4), dpi=300)
r_xs, r_ys, tof = projectile(v, theta, True, True)
plt.plot(r_xs, r_ys, 'g:', label="Gravity, Buoyancy, and Drag")
r_xs, r_ys, tof = projectile(v, theta, False, True)
plt.plot(r_xs, r_ys, 'b:', label="Gravity and Drag")
r_xs, r_ys, tof = projectile(v, theta, False, False)
plt.plot(r_xs, r_ys, 'k:', label="Gravity")
plt.title("Trajectory", fontsize=14)
plt.xlabel("Displacement in x-direction (m)")
plt.ylabel("Displacement in y-direction (m)")
plt.ylim(bottom=0.0)
plt.legend()
plt.show()
Note that this preserves and returns the time-of-flight in the variable tof.
Using vector notation, and odeint.
import numpy as np
from scipy.integrate import odeint
import scipy.constants as SPC
import matplotlib.pyplot as plt
V_initial = 30 # m/s
theta = np.pi/6 # 30
g = 3.711
m = 1 # I assume this is your mass
C = 0.47
r = 0.5
ro_mars = 0.0175
t_flight = 2*(V_initial*np.sin(theta)/g)
t = np.linspace(0, t_flight, 200)
pos0 = [0, 0]
v0 = [np.cos(theta) * V_initial, np.sin(theta) * V_initial]
def f(vector, t, C, r, ro_mars, apply_bouyancy=True, apply_resistance=True):
x, y, x_prime, y_prime = vector
# volume and surface
V = np.pi * 4/3 * r**3
S = np.pi*pow(r, 2)
# net weight bouyancy
if apply_bouyancy:
Fb = (ro_mars * V - m) * g *np.array([0,1])
else:
Fb = -m * g * np.array([0,1])
# velocity vector
v = np.array([x_prime, y_prime])
# drag force - corrected to be updated based on current velocity
# Ft = -0.5*C*S*ro_mars*pow(V_initial, 2)
if apply_resistance:
Ft = -0.5*C*S*ro_mars* v *np.linalg.norm(v)
else:
Ft = np.array([0, 0])
# resulting acceleration
x_prime2, y_prime2 = (Fb + Ft) / m
return x_prime, y_prime, x_prime2, y_prime2
sol = odeint(f, pos0 + v0 , t, args=(C, r, ro_mars))
plt.plot(sol[:,0], sol[:, 1], 'g', label='tray')
plt.legend(loc='best')
plt.xlabel('x')
plt.ylabel('y')
plt.grid()
plt.show()
Note that I corrected your drag force to use the actual (not initial) velocity, I do not know if that was your mistake or it was on purpose.
Also please check the documentation for odeint to understand better how to turn a second order ODE (like the one in your problem) to a first order vector ODE.
To remove air resistance or bouyancy, set apply_bouyancy and apply_resistance to True or False by adding them to the args=(...)
I have following python code, and would like to:
Plot the same function in 1 (only one) figure with many different (lets say 4) 'v0' and 'theta' values, each trajectory in a different color.
Make 4 plots in 4 different figures, so that it looks like a square with 4 plots of 4 different 'v0' and 'theta' values
Make a widget to vary the v0 and theta values as the user wants with the mouse.
import numpy as np
import scipy.integrate as integrate
import matplotlib.pyplot as plt
%matplotlib inline
theta = 45.
theta = theta * np.pi/180.
v0 = 20.0
g = 9.81
R = 0.035
m = 0.057
rho = 1.2041
C = 0.5
k = (0.5*np.pi*R**2*C*rho)/m
x0=0
y0=10
vx0 = v0*np.sin(theta)
vy0 =
v0*np.cos(theta)
print(vx0)
print(vy0)
def f_func(X_vek,time):
f = np.zeros(4)
f[0] = X_vek[2]
f[1] = X_vek[3]
f[2] = - k*(f[0]**2 + f[1]**2)**(0.5)*f[0]
f[3] = -g - k*(f[0]**2 + f[1]**2)**(0.5)*f[1]
return f
X0 = [ x0, y0, vx0, vy0]
t0 = 0. tf = 10
tau = 0.05
t = np.arange(t0,tf,tau)
X = integrate.odeint(f_func,X0,t)
x = X[:,0]
y = X[:,1]
vx = X[:,2]
vy = X[:,3]
mask = y >= 0
plt.scatter(x[mask],y[mask])
plt.scatter(x[mask],y[mask])
plt.xlabel('x') plt.ylabel('y') plt.show()
I could do point 1 and 2 of my question with changing the values after plotting, then calculate vx0 and vy0 again and then call the integrate function and finally plot again, but that's kinda weird and not clean. Is there any better way to do that? like an array of different v0 and theta values or something?
Thanks!
Make your code as a function:
def func(theta=45, v0=20):
theta = theta * np.pi/180.
g = 9.81
R = 0.035
m = 0.057
rho = 1.2041
C = 0.5
k = (0.5*np.pi*R**2*C*rho)/m
x0=0
y0=10
vx0 = v0*np.sin(theta)
vy0 = v0*np.cos(theta)
def f_func(X_vek,time):
f0, f1 = X_vek[2:4].tolist()
f2 = - k*(f0**2 + f1**2)**(0.5)*f0
f3 = -g - k*(f0**2 + f1**2)**(0.5)*f1
return [f0, f1, f2, f3]
X0 = [ x0, y0, vx0, vy0]
t0 = 0.
tf = 10
tau = 0.05
t = np.arange(t0,tf,tau)
X = integrate.odeint(f_func,X0,t)
x = X[:,0]
y = X[:,1]
vx = X[:,2]
vy = X[:,3]
mask = y >= 0
return x[mask], y[mask]
then you can plot it with different parameters:
plt.plot(*func())
plt.plot(*func(theta=30))
plt.xlabel('x')
plt.ylabel('y')
plt.show()
I suggest you use Holoviews to make dynamic graph:
import holoviews as hv
hv.extension("bokeh")
hv.DynamicMap(
lambda theta, v0:hv.Curve(func(theta, v0)).redim.range(x=(0, 50), y=(0, 50)),
kdims=[hv.Dimension("theta", range=(0, 80), default=40),
hv.Dimension("v0", range=(1, 40), default=20)])
Here is the result: