I have finished my orbiting program in 2D, using real physics, and it is pretty cool. One thing I want to do now, is add a "z" force, acceleration, and velocity to the equation. I have done this, but now the program is just a vertical line orbiting a vertical line:
Since theres no code for creating a sphere like there is for circle, I needed to use trig:
Here's the code:
import numpy as np
import matplotlib.pyplot as plt
import math
import matplotlib.animation as animation
import pdb
from mpl_toolkits.mplot3d import Axes3D
er = 6378100*10#m
mr = 1737400*10#m
em = 5.97219*10**24#kg
mm = 7.34767309*10**22#kg
G = 6.67384*10**(-11)
mv = -1023#m/s
nts = 10000
ts = 10000
def simData():
tmax = ts*nts
jx = 0.0
t = 0.0
d = 384400000
mx = d
my = 0
mz = 0
vx = 0
vy = -mv
vz = 0
while t < tmax:
Fg = G*(em*mm)/d**2
Fgx = -Fg*mx/d
Fgy = -Fg*my/d
Fgz = -Fg*mz/d
mAx = Fgx/mm
mAy = Fgy/mm
mAz = Fgz/mm
vx = vx + mAx*ts
vy = vy + mAy*ts
vz = vz + mAz*ts
mx = mx + vx*ts
my = my + vy*ts
mz = mz + vz*ts
u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]
x=np.cos(u)*np.sin(v)+mx
y=np.sin(u)*np.sin(v)+my
z=np.cos(v)+mz
ax.plot_wireframe(x, y, z, color="grey")
d = math.sqrt(mx**2+my**2+mz**2)
t = t + ts
yield jx, t
def simPoints(simData):
jx, t = simData[0], simData[1]
time_text.set_text(time_template%(t))
line.set_data(t, jx)
return line, time_text
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_aspect("equal")
line, = ax.plot([], [], 'bo', ms=10)
eu, ev = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]
eax=np.cos(eu)*np.sin(ev)
eay=np.sin(eu)*np.sin(ev)
eaz=np.cos(ev)
ax.plot_wireframe(eax, eay, eaz, color="b")
time_template = 'Time = %.1f s' # prints running simulation time
time_text = ax.text(0.05, 0.9, 0.9,'', transform=ax.transAxes)
ani = animation.FuncAnimation(fig, simPoints, simData, blit=False,\
interval=10, repeat=True)
plt.show()
I have tested the trig for making circles without this program and it works. Also, any suggestions to making surfaced spheres instead of wireframe spheres?
Related
I wanted to solve this system of differential equations using Python, and I was wondering how to do it. I've tried nothing yet since I'm new to these systems, though I have already solved some individual diff. eqs.. Right now, I'm familiar with solve_ivp and that's practically it.
Here's the code:
import numpy as np
from numpy import sin,cos,sign,sqrt,pi
from scipy.integrate import solve_ivp
from matplotlib import pyplot as plt
from matplotlib.pyplot import figure
g = 9.807
h = 1.8
l = 0.56 * h
R_sc = 14
mu = 0.1
k = 0.3
m = 80
v_0 = 12
phi_0 = np.pi/4
def velphi(t,y):
dy=np.zeros([3])
dy[1] = y[2]
dy[0] = -mu*np.sqrt(g**2 + ((y[0])**2/(R_sc*np.cos(y[1])))**2)-(k/m)*(y[0])**2
dy[2] = (g/l)*np.sin(y[1])-((y[0])**2/(l*R_sc))*np.sign(y[1])
return dy
y0 = np.array([v_0, phi_0, 0])
time = np.linspace(0, 10, 10000)
sol = solve_ivp(velphi, (0,10), y0, method='RK45', t_eval=time, dense_output=True, rtol=1e-8, atol=1e-10)
t, pos, vel, phi, omega = sol.t, sol.y[0], sol.y[1], sol.y[2], sol.y[3]
plt.plot(t.T, phi.T,"b",linewidth = 0.65, label = "${\Phi}$")
plt.plot(t.T, omega.T,"g",linewidth = 0.65, label = "${\Omega}$")
s = 'Condicions inicials: ${\Phi}_{o}$ ='+str(np.degrees(phi_0))+'º, ${\Omega}_{o}$ = 0 rad/s'
plt.title('Pèndol centrífug invertit | '+ s)
plt.xlabel('Temps (s)')
plt.ylabel(u'${\Phi}$ (rad) | ${\Omega}$ (rad / s)')
plt.grid(False)
plt.legend(loc = "upper right")
plt.show()
plt.plot(t.T, vel.T,"r",linewidth = 0.65, label = "v")
s = 'Condicions inicials: ${\Phi}_{o}$ ='+str(np.degrees(phi_0))+'º, ${\Omega}_{o}$ = 0 rad/s'
plt.title('Pèndol centrífug invertit | '+ s)
plt.xlabel('Temps (s)')
plt.ylabel('v (m/s)')
plt.grid(False)
plt.legend(loc = "upper right")
plt.show()
And here the solutions:
Velocity
Phi and the angular velocity
I'm trying to place a plane on the surface of a sphere, although I think the math is correct, the resulting figure displays the plane at some point else.
Here is the code to compute and visualize it;
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
plt.style.use('dark_background')
# point and the unit vector
point = np.array([ 349370.39178182, 5570903.05977037, 3085958.36621096])
unit_vector = point/np.linalg.norm(point)
# the math
print(f'plane equation:\nAx + By + Cz + D = 0')
D = np.sum(unit_vector * point)
print(f'D=-(point * unit_vector) = {D:.2f}')
print(f'plane equation:\n{unit_vector[0]:1.4f}x + {unit_vector[1]:1.4f}y + {unit_vector[2]:1.4f}z + {D:.1f} = 0')
print(f'{-1*unit_vector[2]:1.4f}z = {unit_vector[0]:1.4f}x + {unit_vector[1]:1.4f}y + {D:.1f}')
print(f'z = ({unit_vector[0]:1.4f}x + {unit_vector[1]:1.4f}y + {D:.1f}) / {-1*unit_vector[2]:1.4f}')
x = np.linspace(-3e6,+3e6,100)
y = np.linspace(-3e6,+3e6,100)
X,Y = np.meshgrid(x,y)
Z = (0.05477656*X +0.87344241*Y + 6378100.0)/-0.48383662
# plotting stuff
def set_axis_equal_scale(ax, ticks_off=True):
xl = ax.set_xlim()
yl = ax.set_ylim()
zl = ax.set_zlim()
maxx=max(max(xl), max(yl), max(zl))
minn=min(min(xl), min(yl), min(zl))
ax.set_xlim(minn, maxx)
ax.set_ylim(minn, maxx)
ax.set_zlim(minn, maxx)
if ticks_off:
ax.set_xticks([])
ax.set_yticks([])
ax.set_zticks([])
fig = plt.figure(figsize=(5,5))
ax = fig.add_subplot(111, projection='3d')
# plot wireframe
radius = 6.3781e6 # in units m
uu, vv = np.mgrid[0:2*np.pi:200j, 0:np.pi:100j]
xE = radius * np.cos(uu)*np.sin(vv)
yE = radius * np.sin(uu)*np.sin(vv)
zE = radius * np.cos(vv)
ax.plot_wireframe(xE,yE,zE, color='w', alpha=0.1)
ax.scatter(point[0], point[1], point[2], s=500, color='r')
ax.plot([0,point[0]], [0,point[1]], [0,point[2]], color='w', lw=2)
surf = ax.plot_surface(X, Y, Z)
ax.scatter(0,0,0, marker='o', s=900, color='b')
ax.view_init(25, -190)
ax.axis('off')
set_axis_equal_scale(ax)
I expect the plane to be on where the red marker is and perpendicular to the white line connecting the center and the red marker.
[here is the image][1]
[1]: https://i.stack.imgur.com/LHPzW.png
import numpy as np
import matplotlib.pyplot as plot
from IPython.display import HTML
from matplotlib import animation
#setup fig with axis
fig, ax = plot.subplots(figsize=(8,8))
#set axis limits
ax.set(xlim=(-2,2), ylim=(0,600), xlabel="position, metres", ylabel="height, metres", title="falling apple")
#initial params
T = 100.
m = 3
g = 9.81
v0x = 10
H = 553.
#setting calc interval
dt = 0.1
N = int(T/dt)
#arrays
v = np.zeros((N+1 , 2))
x = np.zeros((N+1 , 2))
f = np.zeros((N+1 , 2))
#array start [x ,y] format
v[0] = np.array([0. , H])
x[0] = np.array([v0x , 0.])
# the only force is gravity
f[:] = np.array([0., m * g])
#running the dynamics sim
for n in range(N):
v[n+1] = v[n] + ((f[n]/m) * dt)
x[n+1] = x[n] + (v[n+1] * dt)
#scatter plot
scat_plt = ax.scatter(x[0,0], x[0,1], marker='o', c='#1f77b4', s=200)
## animating
def animate(i):
scat_plt.set_offsets(x[i])
ani = animation.FuncAnimation(fig, func=animate, frames=N)
ani.save('ball.html', writer=animation.HTMLWriter(fps= 1//dt))
plot.close()
ani.save('ball.mp4', fps= 1//dt)
HTML('ball.html')
The out put is just a circle going straight up where as this is supposed to simulate a ball being thrown horizontally off a tower
It would be highly appreciated if someone could suggest any changes to be made to the logic/physics or the code.
Thank you!!
I think you mixed x with v at some point. Also the force should be negative in y. I tried this and it seems to work:
import numpy as np
import matplotlib.pyplot as plot
from IPython.display import HTML
from matplotlib import animation
#setup fig with axis
fig, ax = plot.subplots(figsize=(8,8))
#set axis limits
ax.set(xlim=(-200,200), ylim=(0,600), xlabel="position, metres", ylabel="height, metres", title="falling apple")
#initial params
T = 100.
m = 3
g = 9.81
v0x = 10
H = 553.
#setting calc interval
dt = 0.1
N = int(T/dt)
#arrays
v = np.zeros((N+1 , 2))
x = np.zeros((N+1 , 2))
f = np.zeros((N+1 , 2))
#array start [x ,y] format
x[0] = np.array([0. , H])
v[0] = np.array([v0x , 0.])
# the only force is gravity
f[:] = np.array([0., -m * g])
#running the dynamics sim
for n in range(N):
v[n+1] = v[n] + ((f[n]/m) * dt)
x[n+1] = x[n] + (v[n+1] * dt)
#scatter plot
scat_plt = ax.scatter(x[0,0], x[0,1], marker='o', c='#1f77b4', s=200)
## animating
def animate(i):
scat_plt.set_offsets(x[i])
ani = animation.FuncAnimation(fig, func=animate, frames=N)
ani.save('ball.html', writer=animation.HTMLWriter(fps= 1//dt))
plot.close()
ani.save('ball.gif', fps= 1//dt)
HTML('ball.html')
I was able to simulate a mass-spring system under damped oscillations. However, I wanted to add a subplot of position vs time and another subplot velocity vs position (phase path) so that I will be having three animations. How can I add them? The source code that I used is shown below
Update: I tried adding the first subplot position vs time but I cannot get the desired curve for it.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
#Constants
w = np.pi #angular frequency
b = np.pi * 0.2 #damping parameter
#Function that implements rk4 integration
def rk4(t, x, f, dt):
dx1 = f(t, x)*dt
dx2 = f(t+0.5*dt, x+0.5*dx1)*dt
dx3 = f(t+0.5*dt, x+0.5*dx2)*dt
dx4 = f(t+dt, x+dx3)*dt
return x+dx1/6.0+dx2/3.0+dx3/3.0+dx4/6.0
#Function that returns dX/dt for the linearly damped oscillator
def dXdt(t, X):
x = X[0]
vx = X[1]
ax = -2*b*vx - w**2*x
return np.array([vx, ax])
#Initialize Variables
x0 = 5.0 #Initial x position
vx0 = 1.0 #Initial x Velocity
#Setting time array for graph visualization
dt = 0.05
N = 200
x = np.zeros(N)
vx = np.zeros(N)
y = []
# integrate equations of motion using rk4;
# X is a vector that contains the positions and velocities being integrated
X = np.array([x0, vx0])
for i in range(N):
x[i] = X[0]
vx[i] = X[1]
y.append(0)
# update the vector X to the next time step
X = rk4(i*dt, X, dXdt, dt)
fig, (ax1, ax2) = plt.subplots(2,1, figsize=(8,6))
fig.suptitle(r' Damped Oscillation with $\beta$$\approx$' + str(round(b,2)) + r' and $\omega$$\approx$'
+ str(round(w,2)), fontsize=16)
line1, = ax1.plot([], [], lw=10,c="blue",ls="-",ms=50,marker="s",mfc="gray",fillstyle="none",mec="black",markevery=2)
line2, = ax2.plot([], [], lw=2, color='r')
time_template = '\nTime = %.1fs'
time_text = ax1.text(0.1, 0.9, '', transform=ax1.transAxes)
for ax in [ax1, ax2]:
ax1.set_xlim(1.2*min(x), 1.2*max(x))
ax2.set_ylim(1.2*min(x), 1.2*max(x),1)
ax2.set_xlim(0, N*dt)
ax1.set_yticklabels([])
def init():
line1.set_data([], [])
line2.set_data([], [])
time_text.set_text('')
return line1, line2, time_text
def animate(i):
thisx1 = [x[i],0]
thisy1 = [y[i],0]
thisx2 = [i*dt,0]
thisy2 = [x[i],0]
line1.set_data(thisx1, thisy1)
line2.set_data(thisx2, thisy2)
time_text.set_text(time_template % (i*dt))
return line1, line2, time_text
ani = animation.FuncAnimation(fig, animate, np.arange(1, N),
interval=50, blit=True, init_func=init,repeat=False)
After some minors changes to your initial code, the most noteworthy being:
thisx1 = [x[i],0]
thisy1 = [y[i],0]
thisx2 = [i*dt,0]
thisy2 = [x[i],0]
line1.set_data(thisx1, thisy1)
line2.set_data(thisx2, thisy2)
# should be written like this
line1.set_data([x[i],0], [y[i],0])
line2.set_data(t[:i], x[:i])
line3.set_data(x[:i], vx[:i])
The working version, with phase space plot in green, is as follows:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
#Constants
w = np.pi #angular frequency
b = np.pi * 0.2 #damping parameter
#Function that implements rk4 integration
def rk4(t, x, f, dt):
dx1 = f(t, x)*dt
dx2 = f(t+0.5*dt, x+0.5*dx1)*dt
dx3 = f(t+0.5*dt, x+0.5*dx2)*dt
dx4 = f(t+dt, x+dx3)*dt
return x+dx1/6.0+dx2/3.0+dx3/3.0+dx4/6.0
#Function that returns dX/dt for the linearly damped oscillator
def dXdt(t, X):
x = X[0]
vx = X[1]
ax = -2*b*vx - w**2*x
return np.array([vx, ax])
#Initialize Variables
x0 = 5.0 #Initial x position
vx0 = 1.0 #Initial x Velocity
#Setting time array for graph visualization
dt = 0.05
N = 200
t = np.linspace(0,N*dt,N,endpoint=False)
x = np.zeros(N)
vx = np.zeros(N)
y = np.zeros(N)
# integrate equations of motion using rk4;
# X is a vector that contains the positions and velocities being integrated
X = np.array([x0, vx0])
for i in range(N):
x[i] = X[0]
vx[i] = X[1]
# update the vector X to the next time step
X = rk4(i*dt, X, dXdt, dt)
fig, (ax1, ax2, ax3) = plt.subplots(3,1, figsize=(8,12))
fig.suptitle(r' Damped Oscillation with $\beta$$\approx$' + str(round(b,2)) + r' and $\omega$$\approx$'
+ str(round(w,2)), fontsize=16)
line1, = ax1.plot([], [], lw=10,c="blue",ls="-",ms=50,marker="s",mfc="gray",fillstyle="none",mec="black",markevery=2)
line2, = ax2.plot([], [], lw=1, color='r')
line3, = ax3.plot([], [], lw=1, color='g')
time_template = '\nTime = %.1fs'
time_text = ax1.text(0.1, 0.9, '', transform=ax1.transAxes)
ax1.set_xlim(1.2*min(x), 1.2*max(x))
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax2.set_ylim(1.2*min(x), 1.2*max(x),1)
ax2.set_xlim(0, N*dt)
ax2.set_xlabel('t')
ax2.set_ylabel('x')
ax3.set_xlim(1.2*min(x), 1.2*max(x),1)
ax3.set_ylim(1.2*min(vx), 1.2*max(vx),1)
ax3.set_xlabel('x')
ax3.set_ylabel('vx')
def init():
line1.set_data([], [])
line2.set_data([], [])
line3.set_data([], [])
time_text.set_text('')
return line1, line2, line3, time_text
def animate(i):
line1.set_data([x[i],0], [y[i],0])
line2.set_data(t[:i], x[:i])
line3.set_data(x[:i], vx[:i])
time_text.set_text(time_template % (i*dt))
return line1, line2, line3, time_text
ani = animation.FuncAnimation(fig, animate, np.arange(1, N),
interval=50, blit=True, init_func=init,repeat=False)
ani.save('anim.gif')
and gives:
I am currently trying to animate the dynamics of a typical saddle node bifurcation ode: dx/dt = r + x^2. Snapshots at specific values of r are realised with the streamplot function from r = -1 to 1. Unfortunately the init function and the animate function are not working properly because .set_array does not work for streamplots. I am also not sure how to update the streams at each iteration in the animate function. My question is how I should modify the animate and init function so that the funcanimation function gives a proper animated plot of the flows.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
nx, ny = .02, .02
x = np.arange(-15, 15, nx)
y = np.arange(-10, 10, ny)
X, Y = np.meshgrid(x, y)
dy = -1 + Y**2
dx = np.ones(dy.shape)
dyu = dy / np.sqrt(dy**2 + dx**2)
dxu = dx / np.sqrt(dy**2 + dx**2)
color = dyu
fig, ax = plt.subplots()
stream = ax.streamplot(X,Y,dxu, dyu, color=color, density=2, cmap='jet',arrowsize=1)
ax.set_xlabel('t')
ax.set_ylabel('x')
def init():
stream.set_array([])
return stream
def animate(iter):
dy = -1 + iter * 0.01 + Y**2
dx = np.ones(dy.shape)
dyu = dy / np.sqrt(dy**2 + dx**2)
dxu = dx / np.sqrt(dy**2 + dx**2)
stream.set_array(dyu.ravel())
return stream
anim = animation.FuncAnimation(fig, animate, frames=100, interval=50, blit=False, repeat=False)
plt.show()
I worked around this by clearing the lines and arrows in every iteration:
ax.collections = [] # clear lines streamplot
ax.patches = [] # clear arrowheads streamplot
So, I modified your code like this:
#!/usr/bin/env python3
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
nx, ny = .02, .02
x = np.arange(-15, 15, nx)
y = np.arange(-10, 10, ny)
X, Y = np.meshgrid(x, y)
dy = -1 + Y**2
dx = np.ones(dy.shape)
dyu = dy / np.sqrt(dy**2 + dx**2)
dxu = dx / np.sqrt(dy**2 + dx**2)
color = dyu
fig, ax = plt.subplots()
stream = ax.streamplot(X,Y,dxu, dyu, color=color, density=2, cmap='jet',arrowsize=1)
ax.set_xlabel('t')
ax.set_ylabel('x')
def animate(iter):
ax.collections = [] # clear lines streamplot
ax.patches = [] # clear arrowheads streamplot
dy = -1 + iter * 0.01 + Y**2
dx = np.ones(dy.shape)
dyu = dy / np.sqrt(dy**2 + dx**2)
dxu = dx / np.sqrt(dy**2 + dx**2)
stream = ax.streamplot(X,Y,dxu, dyu, color=color, density=2, cmap='jet',arrowsize=1)
print(iter)
return stream
anim = animation.FuncAnimation(fig, animate, frames=100, interval=50, blit=False, repeat=False)
anim.save('./animation.gif', writer='imagemagick', fps=60)
# plt.show()
CAUTION: #SebastianBeyer's previously working answer no longer works in 2022. For unknown (and presumably indefensible) reasons, Matplotlib now prohibits attempts to manually replace the axes.patches list by raising a non-human-readable exception resembling:
AttributeError: can't set attribute 'patches'
Thankfully, yet another working workaround exists. Inspired by #Sheldore's working answer here, you must now iteratively search for and remove all matplotlib.patches.FancyArrowPatch child artists from the streamplot's axes: e.g.,
# Rather than this...
ax.patches = [] # clear arrowheads streamplot
# ...you must now do this.
from matplotlib.patches import FancyArrowPatch
for artist in ax.get_children():
if isinstance(artist, FancyArrowPatch):
artist.remove()
In full, the post-2020 working solution is now:
#!/usr/bin/env python3
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
from matplotlib.patches import FancyArrowPatch
nx, ny = .02, .02
x = np.arange(-15, 15, nx)
y = np.arange(-10, 10, ny)
X, Y = np.meshgrid(x, y)
dy = -1 + Y**2
dx = np.ones(dy.shape)
dyu = dy / np.sqrt(dy**2 + dx**2)
dxu = dx / np.sqrt(dy**2 + dx**2)
color = dyu
fig, ax = plt.subplots()
stream = ax.streamplot(X,Y,dxu, dyu, color=color, density=2, cmap='jet',arrowsize=1)
ax.set_xlabel('t')
ax.set_ylabel('x')
def animate(iter):
ax.collections = [] # clear lines streamplot
# Clear arrowheads streamplot.
for artist in ax.get_children():
if isinstance(artist, FancyArrowPatch):
artist.remove()
dy = -1 + iter * 0.01 + Y**2
dx = np.ones(dy.shape)
dyu = dy / np.sqrt(dy**2 + dx**2)
dxu = dx / np.sqrt(dy**2 + dx**2)
stream = ax.streamplot(X,Y,dxu, dyu, color=color, density=2, cmap='jet',arrowsize=1)
print(iter)
return stream
anim = animation.FuncAnimation(fig, animate, frames=100, interval=50, blit=False, repeat=False)
anim.save('./animation.gif', writer='imagemagick', fps=60)
# plt.show()
Thanks alot, post-2020 matplotlib. </facepalm>