I wrote a program to model and plot the kapitza oscillator and as an extra credit i descided to animate it, but the second animation (when the 2nd oscilator is on) is very slow and thus you cant really see whats going on. Is there anything i can do to speed this up? And what is causing it to be slow, is it simply down to computing power, bad optimasation or is there some parameter i can add to tell it to go faster. This is my first time animating anything in python so im not very familiar at all with it. Heres the code:
# Kapitza oscillator
import numpy as np
from solvers import rk4
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def g(t, X):
def A(t):
return A0 * np.sin(100 * omega0 * t)
theta, omega = X
thetaDot = omega
omegaDot = A(t) * np.sin(theta) - gamma * omega - (omega0 ** 2) * np.sin(theta)
Xdot = np.array([thetaDot, omegaDot])
return Xdot
gamma, omega0 = 1, (2 * np.pi)
X0 = np.array([0.9 * np.pi, 0])
A0 = 0
solver2a = rk4(g, X0, 0.001)
ts2a = []
Xsa = []
for t, X in solver2a:
ts2a.append(t)
Xsa.append(X)
if t > 20:
break
A0 = 10 ** 4
solver2b = rk4(g, X0, 1e-4)
ts2b = []
Xsb = []
for t, X in solver2b:
ts2b.append(t)
Xsb.append(X)
if t > 20:
break
Xsa = np.array(Xsa)
Xsb = np.array(Xsb)
plt.plot(ts2a, Xsa[:, 0], label='Angular displacement for fixed A')
plt.plot(ts2b, Xsb[:, 0], label='Angular displacement for A0=10e4')
plt.legend()
#EXTRA: Tried making animations for the oscilators (only work with Qt5 graphics backbone).
#The method I used seems to be rather slow to show the kapitza oscillator as smoothly as i would like
x = ts2a
y = Xsa[:,0]
fig, ax = plt.subplots()
line, = ax.plot(x, y)
def update(num, x, y, line):
line.set_data(x[:num], y[:num])
line.axes.axis([0, 20, -2, 3])
return line,
ani = animation.FuncAnimation(fig, update, len(x), fargs=[x, y, line], interval=1, blit=True)
plt.show()
x = ts2b
y = Xsb[:,0]
fig, ax = plt.subplots()
line, = ax.plot(x, y)
def update(num, x, y, line):
line.set_data(x[:num], y[:num])
line.axes.axis([0, 20, -1, 3.75])
return line,
ani = animation.FuncAnimation(fig, update, len(x), fargs=[x, y, line], interval=1e-10, blit=True)
plt.show()
note that im using runge kutta 4 (rk4) from external solvers saved on my computer that were provided by my lecturer. Here is the code for that:
def rk4(f, x0, dt):
tn = 0
xn = x0
while True:
yield tn,xn
k1 = dt*f(tn,xn)
k2 = dt*f(tn+dt/2,xn+k1/2)
k3 = dt*f(tn+dt/2,xn+k2/2)
k4 = dt*f(tn+dt,xn+k3)
xn = xn + (k1+2*k2+2*k3+k4)/6
tn = tn + dt
By considering only every tenth element of x and y by
x = x[::10]
y = y[::10]
it runs much faster for me, but is still not very smooth. Do you have to show the animation at run time? If not, you could save it as MP4
ani.save('animation.mp4', fps=15)
which actually does run very smoothly. So the choppy run time animation is maybe due to performance issues.
EDIT: I managed to get it working smoothly by changing the interval (delay between frames in ms) to 20 instead of 1. This also seems to be a more reasonable value for an animation (the interval=1 would correspond to 1000 FPS).
Related
This is the real function I am looking to represent in 3D:
y = f(x) = x^2 + 1
The complex function would be as follows:
w = f(z) = z^2 + 1
Where z = x + iy and w = u + iv. These are four dimentions (x, y, u, v), but one can use u for 3D graphing.
We get:
f(x + iy) = x^2 + 2xyi - y^2 + 1
So:
u = x^2 - y^2 + 1
and v = 2xy
This u is what is being used in the code below.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-100, 101, 150)
y = np.linspace(-100, 101, 150)
X, Y = np.meshgrid(x,y)
U = (X**2) - (Y**2) + 1
fig = plt.figure(dpi = 300)
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z)
plt.show()
The following images are the side-view of the 3D function and the 2D plot for reference. I do not think they are alike.
Likewise, here is the comparison between the 3 side-view and the 2D plot of w = z^3 + 1. They seem to differ as well.
I have not been able to find too many resources regarding plotting in 3D using complex numbers. Because of this and the possible discrepancies mentioned before, I think the code must be flawed, but I can't figure out why. I would be grateful if you could correct me or advise me on any changes.
The inspiration came from Welch Labs' 'Imaginary Numbers are Real' YouTube series where he shows a jaw-dropping representation of the complex values of the function I have been tinkering with.
I was just wondering if anybody could point out any flaws in my reasoning or the execution of my idea since this code would be helpful in explaining the importance of complex numbers to HS students.
Thank you very much for your time.
The f(z) = z^2 + 1 projection (that is, side-view) looks OK to me. You can use this technique to add the projections; this code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
def f(z):
return z**2 + 1
def freal(x, y):
return x**2 - y**2 + 1
x = np.linspace(-100, 101, 150)
y = np.linspace(-100, 101, 150)
yproj = 0 # value of y for which to project xu axes
xproj = 0 # value of x to project onto yu axes
X, Y = np.meshgrid(x,y)
Z = X + 1j * Y
W = f(Z)
U = W.real
fig = plt.figure()
ax = plt.axes(projection='3d')
## surface
ax.plot_surface(X, Y, U, alpha=0.7)
# xu projection
xuproj = freal(x, yproj)
ax.plot(x, xuproj, zs=101, zdir='y', color='red', lw=5)
ax.plot(x, xuproj, zs=yproj, zdir='y', color='red', lw=5)
# yu projection
yuproj = freal(xproj, y)
ax.plot(y, yuproj, zs=101, zdir='x', color='green', lw=5)
ax.plot(y, yuproj, zs=xproj, zdir='x', color='green', lw=5)
# partially reproduce https://www.youtube.com/watch?v=T647CGsuOVU&t=107s
x = np.linspace(-3, 3, 150)
y = np.linspace(0, 3, 150)
X, Y = np.meshgrid(x,y)
U = f(X + 1j*Y).real
fig = plt.figure()
ax = plt.axes(projection='3d')
## surface
ax.plot_surface(X, Y, U, cmap=cm.jet)
ax.set_box_aspect( (np.diff(ax.get_xlim())[0],
np.diff(ax.get_ylim())[0],
np.diff(ax.get_zlim())[0]))
#ax.set_aspect('equal')
plt.show()
gives this result:
and
The axis ticks don't look very good: you can investigate plt.xticks or ax.set_xticks (and yticks, zticks) to fix this.
There is a way to visualize complex functions using colour as a fourth dimension; see complex-analysis.com for examples.
I have a numerical simulation that I run with a short python code using animation.Funcanimation. Is is possible to use tikzplotlib to produce a .tex file including the whole simulation (and not just one picture) ? This would mean combining tikz and the animate package, for instance. For the moment my sole idea is to export each of the pictures and try to do so something directly with latex (but even this is not completely clear for me !).
Here is an example of short simulation :
import numpy as np
from scipy.integrate import solve_ivp
from matplotlib import pyplot as plt
from matplotlib import animation
# Sites
M = 10
sites = np.array(range(M))
# Particles
N = 3
# Right hand side
def lotka(t,x):
u = x[0:M]
v = x[M:]
dudt = u-u*u-u*v
dvdt = v-u*v-v*v
dxdt = np.concatenate((dudt,dvdt))
return dxdt
# Initial conditions
u0 = np.zeros(M)
v0 = np.zeros(M)
# Segregated
u0[0] = N
v0[-1] = N
if M%2 == 0:
for i in range(int(M/2)):
u0[i] = N
v0[M-1-i] = N
else:
for i in range(int(np.floor(M/2))-1):
u0[i] = N
v0[M-1-i] = N
x0 = np.concatenate((u0,v0))
# Solving the equation
nt = 500
t = np.linspace(0,0.2,nt)
x = solve_ivp(lotka,[0,4],x0,t_eval=t)
# Getting each species from the solution of solve_ivp
u = np.zeros((nt,M))
v = np.zeros((nt,M))
for i in range(nt):
u[i] = x.y.T[i][0:M]
v[i] = x.y.T[i][M:]
# Animation
data = [u, v]
fig = plt.figure()
ax = plt.axes()
ax.grid()
lines = [ax.plot(sites,u[0])[0], ax.plot(sites,v[0])[0]]
time_template = 'time = % s'
time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)
lines.append(time_text)
def animate(i,lines,data):
lines[0].set_ydata(data[0][i])
lines[1].set_ydata(data[1][i])
lines[2].set_text(time_template % t[i])
return lines
anim = animation.FuncAnimation(fig,
animate,
#frames=200,
fargs=(lines,data),
interval=1,
blit=True)
plt.show()
I've created an animated plot of a wavefunction, psi:
def psi(x, t):
real = 0.4*np.cos(0.4*x - 0.08*t) + 0.6*np.cos(0.6*x - 0.18*t)
imag = 0.4*np.sin(0.4*x - 0.08*t) + 0.6*np.sin(0.6*x - 0.18*t)
square = real**2 + imag**2
return real, imag, square
I've then animated it successfully, however on adding the axvspan fill I've encountered an issue:
x = np.linspace(-10, 1000, 10000)
fig, (ax1, ax2) = plt.subplots(2,1)
line1, = ax1.plot([], [])
line2, = ax1.plot([], [])
line3, = ax2.plot([], [])
line = [line1, line2, line3]
def animate(i):
y1, y2, y3 = psi(x, t=i/2)
line1.set_data(x, y1)
line2.set_data(x, y2)
line3.set_data(x, y3)
spline = UnivariateSpline(x, y3-max(y3)/2, s=0)
r1, r2 = spline.roots()
ax2.axvspan(r1, r2, facecolor='b', alpha=0.5)
plt.legend(['Max Probability = %1.3f' % (max(y3))])
return line,
anim = animation.FuncAnimation(fig, animate, frames=600, interval = 100, blit=False, repeat=False)
It starts like
and it ends like .
Every iteration of the animation function, the fill increases across the page having started off as filling half the graph (I'd include a gif but at the moment that's a struggle I'm having with anaconda). I'm working under the assumption this is because the axes don't clear properly, however with the blit=false I assumed this wouldn't be a problem?
As asked for - the full psi function is detailed below:
n = 15
amp_scale = np.linspace(0, 0.8, n)
amp_init = norm.pdf(amp_scale, 0.4, 0.2)
#normalise wavefunction to prob=1
amp = []
for i in range(n):
amp_val = amp_init[i]/sum(amp_init)
amp.append(amp_val)
k = np.linspace(1.4, 2.6, n)
def psi (x, t=1, n=1, a = 1, k = 1, m = 1):
psi_real = 0
psi_imag = 0
for i in range(n):
a_val = a[i]
k_val = k[i]
w = (k_val**2)/(2*m)
psi_real+=a_val*np.cos(k_val*x - w*t)
psi_imag+=a_val*np.sin(k_val*x - w*t)
psi_squared = psi_real**2 + psi_imag**2
return psi_real, psi_imag, psi_squared
In your current code, a new axvspan() is continually added, never removed. You could explicitly remove the old span inside animate(). Or, similarly to what happens to the lines, update the position. A span is internally represented as a polygon, of which the coordinates can be set via .set_xy().
The function psi in the post doesn't seem to be the same as the function that generated the example plots. This also made that I couldn't get to work to calculate the spline and the roots. I replaced them by some simpler positions to show how the span can be updated during the animation.
The code also adds explicit x and y limits, as they weren't set in the question's code.
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
from scipy.interpolate import UnivariateSpline
def psi(x, t):
# the function from the question is adapted to more resemble the plot
real = (0.4 * np.cos(0.4 * x - 0.08 * t) + 0.6 * np.cos(0.6 * x - 0.18 * t)) * np.exp(- (x - t) ** 2 / 5000)
imag = (0.4 * np.sin(0.4 * x - 0.08 * t) + 0.6 * np.sin(0.6 * x - 0.18 * t)) * np.exp(- (x - t) ** 2 / 5000)
square = real ** 2 + imag ** 2
return real, imag, square
x = np.linspace(-10, 1000, 10000)
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
line1, = ax1.plot([], [])
line2, = ax1.plot([], [])
line3, = ax2.plot([], [])
line = [line1, line2, line3]
span1 = ax2.axvspan(0, 0, facecolor='b', alpha=0.2)
ax1.set_xlim(x[0], x[-1]/2)
ax1.set_ylim(-1, 1)
ax2.set_ylim(0, 1.1)
def animate(i):
y1, y2, y3 = psi(x, t=i / 2)
line1.set_data(x, y1)
line2.set_data(x, y2)
line3.set_data(x, y3)
# this didn't work for me, spline.roots() gave me a long array of values
spline = UnivariateSpline(x, y3 - max(y3) / 2, s=0)
r1, r2 = spline.roots()[[0, -1]] # [[0, -1]] takes the first and the last
# r1, r2 = i - 50, i + 50
span1.set_xy([[r1, 0], [r1, 1], [r2, 1], [r2, 0], [r1, 0]])
plt.legend(['Max Probability = %1.3f' % (max(y3))])
return line, span1, ax2.legend_,
anim = animation.FuncAnimation(fig, animate, frames=600, interval=100, blit=False, repeat=False)
plt.plot()
The resulting end frame looks like (note that a slightly different function is used):
I was trying to do an animation for fun using the mathematical solution for the pendulum. I know that my script is a little bit messy but I would really hope to improve. Below is my attempt to produce an animation using FuncAnimation from Matplotlib. I generated the data information for 3 points but my gif only shows one.
I read the documentation examples and some answers here similar to my problem but I really did not understand how to apply a simple solution in my case. I also saw that it was possible to use a scatter plot for this case but still I was not able to do it.
I would be grateful if you could recommend me a solution considering that I am a newbie using Python. And any other recommendation regarding the script will be kindly appreciated.
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import odeint
from matplotlib.animation import FuncAnimation
%matplotlib inline
def solution(g, length, n, initial_angle, initial_velocity):
# g = 9.81
# l = 9.81
# n = 8
def model(u, t):
return (u[1], - (g / length) * np.sin(u[0] * np.pi / 180))
# Initial angle, and initial velocity
theta0 = [initial_angle, initial_velocity]
# Desired time interval
time = np.arange(0, 2 * n * np.pi, 0.1)
solution = odeint(model, theta0, time)
return solution[:,0]
data = []
for i in range(1, 4):
data.append(solution(9.81, 9.81 * i / 3, n, -17.5, 0))
data = np.array(data)
x_temp = np.sin(data * np.pi / 180)
y_temp = - np.cos(data * np.pi / 180)
# Coordinates for 3 pendulums
for i in range(0, 3):
x_temp[i] = (9.81 * i / 3) * x_temp[i]
y_temp[i] = (9.81 * i / 3) * y_temp[i]
# minimums and maximums based on the longest pendulum
x_min = x_temp[2].min()
x_max = x_temp[2].max()
y_min = y_temp[2].min()
fig, ax = plt.subplots()
ax = plt.axes(xlim=(x_min - 0.5, x_max + 0.5), ylim=(y_min - 0.5, 0))
point, = ax.plot([],[], 'go', lw=3)
# def init():
# point.set_data([], [])
# return point,
def animation_frames(i, x, y):
# point.set_data(x, y)
for j in range(0,3):
point.set_data(x[j][i], y[j][i])
return point,
animation = FuncAnimation(fig, animation_frames, frames=len(x_temp[0]), fargs=(x_temp, y_temp), interval=10)
animation.save('simple_pendulum.gif', writer='imagemagick')
Edit
I generated a simple case as recommended. The lists to be used just before calling the sublopts function are:
x_tem, y_temp
(array([[-0.98330796, -0.97717458, -0.95882857, -0.92843441, -0.88627296,
-0.83275034, -0.76840839, -0.69393471, -0.61017043, -0.51811348,
-0.41891573, -0.31387291, -0.20440652],
[-1.96661593, -1.9604803 , -1.94210039, -1.91155767, -1.86898966,
-1.81459239, -1.74862365, -1.67140657, -1.58333345, -1.48486936,
-1.37655514, -1.25900948, -1.13292978],
[-2.94992389, -2.94378752, -2.92539636, -2.89480454, -2.85210308,
-2.79742099, -2.73092681, -2.65283041, -2.56338497, -2.46288906,
-2.35168869, -2.23017916, -2.0988066 ]]),
array([[-3.11865443, -3.12058165, -3.12626739, -3.13542813, -3.14760548,
-3.16218704, -3.17843492, -3.19552102, -3.21256783, -3.22869299,
-3.2430556 , -3.2549015 , -3.26360506],
[-6.23730886, -6.2392401 , -6.24498567, -6.25440223, -6.26725439,
-6.28322007, -6.30189776, -6.32281583, -6.34544365, -6.36920427,
-6.39348856, -6.41767054, -6.44112336],
[-9.35596329, -9.35789587, -9.36366147, -9.37316418, -9.38624568,
-9.40268769, -9.42221517, -9.44450056, -9.46916879, -9.49580315,
-9.52395193, -9.55313566, -9.58285505]]))
I don't know exactly what your goal is but I tried to imitate your example of animating three points independently as close as possible. I only added different color and marker features, so that the points can be better distinguished:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
n=100
x_temp = [np.linspace(-10, -8, n),
np.linspace(-9, -6, n),
np.linspace(-6, -10, n)]
y_temp = [np.sin(x_temp[0]),
np.cos(x_temp[1]),
np.sin(x_temp[2])]
fig, ax = plt.subplots()
ax = plt.axes(xlim=(-11, -5), ylim=(- 1.5, 1.5))
points = []
for j, (col, mar) in enumerate(zip(["green", "blue", "red"], ["o", "x", "s"])):
newpoint, = ax.plot(x_temp[j][0], y_temp[j][0], color=col, marker=mar)
points.append(newpoint)
def animation_frames(i):
for j in range(0,3):
points[j].set_data(x_temp[j][i], y_temp[j][i])
animation = FuncAnimation(fig, animation_frames, frames=len(x_temp[0]), interval=30)
plt.show()
Sample output:
I have been using the interactive matplotlib widgets to visualise the solution of differential equations. I have got it working with the odeint function in scipy, however have not managed to get it to update with the ode class. I would rather use the latter as it has greater control over which solver is used.
The following code is used to solve a differential that is an exponential decay. The y0 is the amplitude of the decay. The code stops working when solver.integrate(t1) is called inside the update function. I'm not sure why this is.
from scipy.integrate import ode
# solve the system dy/dt = f(t, y)
def f(t, y):
return -y / 10
# Array to save results to
def solout(t, y):
sol.append([t, *y])
solver = ode(f).set_integrator('dopri5')
solver.set_solout(solout)
# Initial conditions
y0 = [1] # Initial amplitude
t0 = 0 # Start time
t1 = 20 # End time
fig = plt.figure(figsize=(10, 6))
fig.subplots_adjust(left=0.25, bottom=0.4)
ax = plt.subplot(111)
# solve the DEs
solver.set_initial_value(y0, t0)
sol = []
solver.integrate(t1)
sol = np.array(sol)
t = sol[:, 0]
y = sol[:, 1]
l, = plt.plot(t, y, lw=2, color='red')
plt.axis([0, 20, 0, 1.1])
plt.xlabel('Time (ms)')
plt.ylabel('n1(t)')
plt.grid()
axcolor = 'lightgoldenrodyellow'
axn1 = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg=axcolor)
sn1 = Slider(axn1, 'y(0)', 0, 1.0, valinit=1)
def update(val):
y0 = [sn1.val]
solver.set_initial_value(y0, t0)
sol = []
solver.integrate(t1)
sol = np.array(sol)
t = sol[:, 0]
y = sol[:, 1]
l.set_data(t, y)
plt.draw()
sn1.on_changed(update)
I guess it's always wise to separate calculations from plotting. Therefore first try to solve the ODE with some given initial conditions. Once that works, try the plotting and interactive stuff.
In your case we would build a function that solves the ODE and then use this function with different initial conditions also in the plotting updates.
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import numpy as np
from scipy.integrate import ode
# solve the system dy/dt = f(t, y)
def f(t, y):
a = np.zeros((1,1))
a[0] = -y / 10.
return a
#define a function to solve the ODE with initial conditions
def solve(t0, t1, y0, steps=210):
solver.set_initial_value([y0], t0)
dt = (t1 - t0) / (steps - 1)
solver.set_initial_value([y0], t0)
t = np.zeros((steps, 1))
Y = np.zeros((steps, 1))
t[0] = t0
Y[0] = y0
k = 1
while solver.successful() and k < steps:
solver.integrate(solver.t + dt)
t[k] = solver.t
Y[k] = solver.y[0]
k += 1
return t, Y
# set the ODE integrator
solver = ode(f).set_integrator("dopri5")
# Initial conditions
y0 = 1. # Initial amplitude
t0 = 0. # Start time
t1 = 20. # End time
#solve once for given initial amplitude
t, Y = solve(t0, t1, y0)
fig = plt.figure(figsize=(10, 6))
fig.subplots_adjust(left=0.25, bottom=0.4)
ax = plt.subplot(111)
l, = plt.plot(t, Y, lw=2, color='red')
plt.axis([0, 20, 0, 1.1])
plt.xlabel('Time (ms)')
plt.ylabel('n1(t)')
plt.grid()
axn1 = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg='#e4e4e4')
sn1 = Slider(axn1, 'y(0)', 0, 1.0, valinit=1)
def update(val):
#solve again each time
t, Y = solve(t0, t1, sn1.val)
l.set_data(t, Y)
plt.draw()
sn1.on_changed(update)
plt.show()