Home > Enterprise >  How to make animation of the evolution of functions (in Python)?
How to make animation of the evolution of functions (in Python)?

Time:07-18

This post can be seen as a continuation of Plot 3d in Python

Given a 2d array sol=(u(t_i,x_j): 0<=i<=m,-n<=j<=n) which stands for the values of some function u at the grid (t_i:=i/m,x_j:=j/n). Set two arrays time:=(t_i: 0<=i<=m) and space:=(x_j: -n<=j<=n). We may

  1. either plot(space, sol[i,]) for some fixed i, which is the graph of function u_i that is defined as u_i(x_j):=u(t_i,x_j) for -n<=j<=n;
  2. either plot(time, space, sol) (as shown in my previous post).

Now I wish to make animation of the evolution of the functions (u_i: 0<=i<=m). More precisely, can we make a video that displays the the evolution of the functions u_i in the same coordinate system? The functions are constructed as follows :

eps=0.1
m=2000
n=100
dt=1.0/m
dx=1.0/(n*n)
time=np.zeros(m 1)
for i in range(m 1):
  time[i]=i*dt
space=np.zeros(2*n 1)
for j in range(2*n 1):
  space[j]=(j-n)*dx*n
sol=np.zeros((m 1,2*n 1))
for i in range(m):
  index_i=m-1-i
  for j in range(1,2*n):
    sol[index_i, j] =sol[index_i 1, j]-0.5*dt*math.log(eps abs(sol[index_i 1, j 1] sol[index_i 1, j-1]-2*sol[index_i 1, j])/dx)

Many thanks for the help.

CodePudding user response:

Following on from the accepted answer to this question I linked in my comments:

First, let's define a update function that will draw every frame of our animation:

def update(frame, ax, surfaces, xdata, ydata, zdata, anim=False):
    if surfaces and surfaces[0]:
        surfaces[0].remove()
        
    surfaces.clear()
    surf = ax.plot_surface(xdata[:frame, :], ydata[:frame, :], zdata[:frame, :], 
                           cmap=cm.coolwarm, linewidth=0, antialiased=False, animated=anim,
                           vmin=zdata.min(), vmax=zdata.max())
    
    surfaces.append(surf)
    return surfaces

This function assumes that the ith rows of xdata, ydata, and zdata give the values of your data at time t[i]. This is consistent with the way you define your sol matrix. However, remember that in order to get a compatible meshgrid, you will need to pass the indexing='ij' argument to np.meshgrid (Documentation).

import functools

# Indexing is ij, which means each ROW gives value at t[i]
t_mesh, x_mesh = np.meshgrid(time, space, indexing='ij')

fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
ax.set_xlabel('time')
ax.set_xlim3d([time.min(), time.max()])

ax.set_ylabel('space')
ax.set_ylim3d([space.min(), space.max()])

ax.set_zlabel('sol')
ax.set_zlim3d([sol.min(), sol.max()])

ax.view_init(elev=-145, azim=-145)

N = len(time)

surf = []

update_anim = functools.partial(update, ax=ax, surfaces=surf, 
                                xdata=t_mesh, ydata=x_mesh, zdata=sol, anim=True)
# I am _partial_ to this method of specifying arguments for the update function
# over specifying the fargs argument in animation.FuncAnimation because it allows
# us to also specify keyword arguments

ani = animation.FuncAnimation(fig, update_anim, N, interval=25, blit=False)
ani.save('test.gif')

Which gives the following animation: animation


You seem to want a two-dimensional plot, so all you need to do is to change the part that sets up the plot and the update function so that they make a 2D plot.

Remember, there is absolutely nothing special about animating these plots -- You simply have an update function which receives the frame number, and then you need to write the code to draw the frame in this function.

First, we need to set up the plot.

In the previous 3D example, we created the figure and axes, and set X, Y, and Z labels and limits, and set the view. Here, we only need to set X and Y labels and limits:

fig, ax = plt.subplots()
ax.set_xlabel("space")
ax.set_xlim([space.min(), space.max()])

ax.set_ylabel("sol")
ax.set_ylim([sol.min(), sol.max()])

Next, change our update function:

def update2d(frame, ax, line, xdata, ydata, tdata, anim=False):
    if line is None:
        line, = ax.plot(xdata, ydata[frame, :])
    line.set_data(xdata, ydata[frame, :])
    ax.set_title(f"time={tdata[frame]:.3f}")
    return line,

And change the way FuncAnimation calls the update function:

line, = update2d(0, ax, None, space, sol, time, True)

update_anim = functools.partial(update2d, ax=ax, line=line, 
                                xdata=space, ydata=sol, tdata=time, anim=True)
ani = animation.FuncAnimation(fig, update_anim, N, interval=25, blit=False)
ani.save('test2.gif')

Which gives: enter image description here

  • Related