I'm having trouble plotting an animation with python. What I want to do is basically an animation that contains the superposition of many complete plots. In such a way that each frame will be a plot given by
plt.plot(r, Sevol[n])
The code and error displayed on the screen are below. Thanks for any help.
UserWarning: Animation was deleted without rendering anything. This is most likely not intended. To prevent deletion, assign the Animation to a variable, e.g. anim
, that exists until you have outputted the Animation using plt.show()
or anim.save()
.
import numpy as np
import math
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
nr = 300
ri = 0
rf = 300
dr = (rf - ri) / (nr - 1)
nt = 1000
dt = 0.1
r = np.linspace(ri, rf, num=nr)
def rk3_step(t, h, y, f, *args):
k1 = h * f(t , y , *args)
k2 = h * f(t h/2, y 1/2 * k1 , *args)
k3 = h * f(t h , y - k1 2 * k2, *args)
return y 1/6*(k1 4*k2 k3)
def rhs_perturb(t, u):
S = u.T[0]
S_dot = u.T[1]
F = u.T[2]
F_dot = u.T[3]
rhs = np.empty((nr, 4))
rhs[0] = np.array([S_dot[0],
(S[2] - 2 * S[1] S[0]) / (dr ** 2), # F[0],
F_dot[0],
- S[0] (F[2] - 2 * F[1] F[0]) / (dr ** 2)])
rhs[-1] = np.array([S_dot[-1],
(S[-1] - 2 * S[-2] S[-3]) / (dr ** 2), # F[-1],
F_dot[-1],
- S[-1] (F[-1] - 2 * F[-2] F[-3]) / (dr ** 2)])
for i in range(1, nr - 1):
rhs[i] = np.array([S_dot[i],
(S[i 1] - 2 * S[i] S[i - 1]) / (dr ** 2), # F[i],
F_dot[i],
- S[i] (F[i 1] - 2 * F[i] F[i - 1]) / (dr ** 2)])
return rhs
sigma = 3
r0 = 100
F = np.empty(nr)
F_dot = np.empty(nr)
S = np.empty(nr)
S_dot = np.empty(nr)
for i in range(nr):
F[i] = 0
F_dot[i] = 0
S_dot[i] = 0
S[i] = math.exp(-(r[i] - r0)**2 / sigma**2)
uin = np.block([[S], [S_dot], [F], [F_dot]]).T
u = np.copy(uin)
uaux = np.copy(uin)
nsave = 10
Sevol = np.empty((math.floor(nt/nsave),nr))
Sevol[0] = S
Fevol = np.empty((math.floor(nt/nsave),nr))
Fevol[0] = F
for n in range(nt):
uaux = rk3_step(n * dt, dt, u, rhs_perturb)
if np.any(np.isnan(uaux)):
break
u = uaux
if (n 1) % nsave == 0:
Sevol[math.floor(n / nsave)] = u.T[0]
Fevol[math.floor(n / nsave)] = u.T[2]
fig = plt.figure()
plt.xlabel('r')
plt.xlabel('S')
plt.grid()
plt.xlim(ri, rf)
def animate(i):
numPlots = i //10 # how many plots (-1) will be shown based on the frame.
for n in range(numPlots):
plt.plot(r[n], Sevol[n], color='gold', markersize=3)
ani = FuncAnimation(fig, animate, frames=100, interval=10, blit = False, repeat = False)
plt.close()
plt.show()
CodePudding user response:
I would suggest initializing your animations with a list of empty placeholders plots. In your case, it looks like you need 100 plots. And then update the data for plot i
at each frame with the actual values.
Below is what the animation code looks like:
import numpy as np
import math
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation, PillowWriter
N_plots=100
fig,ax = plt.subplots()
color = plt.cm.viridis(np.linspace(0, 1, N_plots))
lns=[ax.plot([],[],color=color[i]) for i in range(N_plots)]
lns=[lns[i][0] for i in range(N_plots)]
plt.xlabel('r')
plt.ylabel('S')
plt.grid()
plt.xlim(ri, rf)
plt.ylim(-0.25, 1)
def animate(i):
lns[i].set_data(r,Sevol[i])
return lns
ani = FuncAnimation(fig, animate, frames=N_plots, interval=100)
And the output gives:
EDIT:
Note that if instead of superimposing the curves you just want to replace them by the next, you can simply use set_data
to update your one plot.
See code below:
import numpy as np
import math
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation, PillowWriter
N_plots=100
fig,ax = plt.subplots()
color = plt.cm.viridis(np.linspace(0, 1, N_plots))
ln,=ax.plot([],[])
plt.xlabel('r')
plt.ylabel('S')
plt.grid()
plt.xlim(ri, rf)
plt.ylim(-0.25, 1)
def animate(i):
ln.set_data(r,Sevol[i])
ln.set_color(color[i])
return ln
ani = FuncAnimation(fig, animate, frames=N_plots, interval=100)
And the output gives: