0

I'm trying to animate a 3D surface generated out of Jacobi iterative method, after each iteration a matrix UF is generated and stored in a list UFK i was able to plot each iteration by it self but i want to create an animation that shows the evolution and convergence from concave to flat surface smoothly. Thank you.

import numpy as np 
import pandas as pd
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.animation as animation
%matplotlib notebook

Nx = 15        
Ny = 15         
tol = 1e-3      
err = 1        
k = 0         

Uy0 = 200*np.ones((1,Nx)) # Boundry condition at y=0 # lower boundry
UNy = 200*np.ones((1,Nx)) # Boundry condition at y=Ny # Upper boundary
Ux0 = 200*np.ones(Ny) # Boundry condition at x=0 # left boundry 
UNx = 200*np.ones(Ny) # Boundry condition at x=Nx # Right boundary 
# initial the whole matrix: the value at the interior nodes 
U = np.zeros((Ny,Nx))
#Adding boundry conditions to the matrix 
U[0] = UNy
U[Ny-1] = Uy0
U[:,Nx-1] = UNx
U[:,0]= Ux0
# Iterate Jacobi method  
UFK=[]
UFK.append(U.copy())
NFK=[]
UF=U.copy()

while True:
    k=k+1 
    for i in range (1,Nx-1):
        for j in range (1,Ny-1):
            UF[j,i] = (UF[j+1,i]+UF[j,i+1]+UF[j-1,i]+UF[j,i-1])*0.25 #the matrix i want to plot after each iteration 
    UFK.append(UF.copy())
    H = UFK[-1]-UFK[-2]
    N = np.linalg.norm(H)
    NFK.append(N)
    if N <= tol:
        break

def data(t,UFK,surf):
    for t in range(0,k-1):
        L = UFK[t]
        ax.clear()
        surf = ax.plot_surface(XX, YY, L, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
    return surf

fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, Nx)
Y = np.arange(0, Ny)
XX,YY = np.meshgrid(X, Y)
surf = ax.plot_surface(XX, YY, UFK[0],rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
ax.set_zlim(0, 200)
ax.zaxis.set_major_locator(LinearLocator(10))
fig.colorbar(surf, shrink=0.5, aspect=10)
ax.set_xlabel('X nodes - Axis')
ax.set_ylabel('Y nodes - Axis')
ax.set_zlabel('Value')



ani = animation.FuncAnimation(fig, data, fargs=(UFK,surf), interval=10, repeat=True )
plt.show()
yassineCAE
  • 27
  • 1
  • 5
  • The question lacks a clear problem description and the code is not runnable. In order to post a question here, you need to precisely state the problem and ideally provide a verifiable example code. – ImportanceOfBeingErnest Jan 19 '17 at 15:43
  • Thank you for your reply. I added the initialization part of the code, I am trying to animate the plot the Z value is a matrix `UFK [i]`. each iteration in the while loop generate a matrix stored in the list `UFK` which are the Z coordinates. if i plot a single matrix `surf = ax.plot_surface(XX, YY, UFK[5],rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)` it works but i want all the iteration shown one after the other in an animated plot. – yassineCAE Jan 19 '17 at 19:05
  • I want to do something like this [http://www.nugnux.my.id/2015/11/3d-animation-of-2d-diffusion-equation.html] – yassineCAE Jan 19 '17 at 21:20

1 Answers1

1

First of all the call to animation.FuncAnimation must happen before plt.show().

Second, you do not need to give the arguments UFK,surf to the animating function.

Third, the loop inside data is running for each animation step, therefore you would end up with identical plots at the end. Get rid of that loop.

Fourth, restrict the animation to the number of matrices you have calculated.

Here is a working code:

import numpy as np 
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator
import matplotlib.animation as animation


Nx = 15        
Ny = 15         
tol = 1e-3      
err = 1        
k = 0         

Uy0 = 200*np.ones((1,Nx)) # Boundry condition at y=0 # lower boundry
UNy = 200*np.ones((1,Nx)) # Boundry condition at y=Ny # Upper boundary
Ux0 = 200*np.ones(Ny) # Boundry condition at x=0 # left boundry 
UNx = 200*np.ones(Ny) # Boundry condition at x=Nx # Right boundary 
# initial the whole matrix: the value at the interior nodes 
U = np.zeros((Ny,Nx))
#Adding boundry conditions to the matrix 
U[0] = UNy
U[Ny-1] = Uy0
U[:,Nx-1] = UNx
U[:,0]= Ux0
# Iterate Jacobi method  
UFK=[]
UFK.append(U.copy())
NFK=[]
UF=U.copy()

while True:
    k=k+1 
    for i in range (1,Nx-1):
        for j in range (1,Ny-1):
            UF[j,i] = (UF[j+1,i]+UF[j,i+1]+UF[j-1,i]+UF[j,i-1])*0.25 #the matrix i want to plot after each iteration 
    UFK.append(UF.copy())
    H = UFK[-1]-UFK[-2]
    N = np.linalg.norm(H)
    NFK.append(N)
    if N <= tol:
        break

def data(t):
    # remove for loop here
    L = UFK[t]
    ax.clear()
    surf = ax.plot_surface(XX, YY, L, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
    ax.set_zlim([0,200]) # set zlim to be always the same for every frame


fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, Nx)
Y = np.arange(0, Ny)
XX,YY = np.meshgrid(X, Y)
surf = ax.plot_surface(XX, YY, UFK[0],rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
ax.set_zlim(0, 200)
ax.zaxis.set_major_locator(LinearLocator(10))
fig.colorbar(surf, shrink=0.5, aspect=10)
ax.set_xlabel('X nodes - Axis')
ax.set_ylabel('Y nodes - Axis')
ax.set_zlabel('Value')

ani = animation.FuncAnimation(fig, data, len(UFK), interval=50, repeat=True )
plt.show()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • If those three points are the only problem in your code cannot be verified until you provide a [MCVE]. – ImportanceOfBeingErnest Jan 19 '17 at 15:48
  • I also applied those changes to your code and pasted them in my answer. For me this is working. – ImportanceOfBeingErnest Jan 20 '17 at 08:33
  • It works thank you very much i appreciate your help, it must have been the Z axis limits. Can i start the animation from a different frame, i don't want it to start from `UFK[0]` i want it from 100 for example. I saw something in Matplotlib about `init_func`. or else how can i change the frames from `len(UFK)` to a range. Thank you – yassineCAE Jan 20 '17 at 11:51
  • Please have a look at the [FuncAnimation documentation](http://matplotlib.org/api/_as_gen/matplotlib.animation.FuncAnimation.html#matplotlib.animation.FuncAnimation). The frames argument accepts an integer, like in this case `len(UFK)` but also an iterable like a list. If you want to start at 100, you could use `np.arange(100, len(UFK), 1)` – ImportanceOfBeingErnest Jan 20 '17 at 13:51
  • Thank you for your help and patience, it works well ufortunatlly i miss understood the explanation in Matplotlib documentation when i tried to use `range(100, len(UFK)` because it did not work so i tried unsuccessfully to use to use `init_func` and `gen_function()`. Thank you again i will try to find some exemple to learn how to use `init_func` and `gen_function()` it is important for me because i need this type of animation frequently and i am a beginner in python. – yassineCAE Jan 20 '17 at 20:16