-1

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.

Philo18
  • 103
  • 4
  • 1
    You already know how to plot at _one_ time. Now use matplotlib's [animation API](https://matplotlib.org/stable/api/animation_api.html) to make the same plot at multiple times – Pranav Hosangadi Jul 16 '22 at 18:51
  • Does this answer your question? [Animating "growing" line plot in Python/Matplotlib](https://stackoverflow.com/questions/28074461/animating-growing-line-plot-in-python-matplotlib) "What" is being plotted is immaterial to the approach you need to animate your plot. – Pranav Hosangadi Jul 16 '22 at 18:51
  • It seems that this does not perfectly corresponds to my purpose. In my animation, when the graph of u_i+1 appears, that of u_i disappears – Philo18 Jul 16 '22 at 19:02
  • 1
    It would help to demonstrate your problem if you added some screenshots / gifs of your animation and use that to describe what is wrong – Pranav Hosangadi Jul 16 '22 at 19:04
  • @PranavHosangadi However, the first link seems to be related. I'm working in maths and I've never coded in my life. My apologies if I ask some trivial questions – Philo18 Jul 16 '22 at 19:05
  • The takeaway from the accepted answer to the question I linked is that the `update` function draws each frame of your animation, with `num` specifying the frame number. You will need to modify that function to plot whatever you need to plot in that frame – Pranav Hosangadi Jul 16 '22 at 19:06
  • @PranavHosangadi I still don't understand. Could you specify a bit more to adapt this solution to my case https://stackoverflow.com/questions/73005345/plot-3d-in-python – Philo18 Jul 16 '22 at 19:13
  • @PranavHosangadi Here each function is identified as (space, sol[i,]), and we have i=0,...,m functions – Philo18 Jul 16 '22 at 19:14
  • I will take a crack at it in a couple of hours – Pranav Hosangadi Jul 16 '22 at 19:19
  • Before I take a crack at it, can you [edit] your question to add what you have done to get your animation, just so I have something to start with? – Pranav Hosangadi Jul 16 '22 at 20:48
  • @PranavHosangadi Edits have been made. Let me know whether the story is clear – Philo18 Jul 16 '22 at 20:58

1 Answers1

1

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

Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70
  • The animation is amazing.While what I expect is the animation in a 1d coordinate, this means the graphs displayed are (space, u_i), instead of (time,space,u). In particular; when it's turn to display (space, u_i+1), the graph of (space, u_i) disappears. Is it possible to realize this? – Philo18 Jul 17 '22 at 07:48
  • As you may observe, the key feature is the singularities of the functions u_i, and I wish to observe the evolution of these singularities with respect to time, which is hard in 2d. So I prefer to plot curve by curve. In particular, I wish the animation is from time 1 to time 0 – Philo18 Jul 17 '22 at 07:52
  • @Philo18 do you want a 2d plot or a 3d plot? In either case, the idea is the same - you set up the figure before you animate it, and in the update function you have your code to make each frame of the animation. It looks like you're asking for a line plot of the value at a single time, which should be easy enough to modify my code to do. I suggest you give it a shot, and I'll modify my answer later today if you can't figure it out – Pranav Hosangadi Jul 17 '22 at 16:14
  • I wish to have the evolutions of graphs in 2d. ` – Philo18 Jul 17 '22 at 16:23
  • @Philo18 see my edit. – Pranav Hosangadi Jul 17 '22 at 23:27