Solve an elliptic equation using the ADI algorithm

  • Using ADI 2 $$\alpha\frac{\partial^2f}{\partial x^2} + \alpha\frac{\partial^2f}{\partial y^2} = S.$$ $$\frac{\alpha}{\Delta x^2}(f_{i-1,j}-2f_{i,j}+f_{i+1,j}) + \frac{\alpha}{\Delta y^2}(f_{i,j-1}-2f_{i,j}+f_{i,j+1}) = S_{i,j}$$

  • couple x direction $$\left(\frac{\alpha}{\Delta x^2}\right)f_{i-1,j}+\alpha\left(-\frac{2}{\Delta x^2}-\frac{2}{\Delta y^2}\right)f_{i,j}+\left(\frac{\alpha}{\Delta x^2}\right)f_{i+1,j} = S_{i,j} - \left(\frac{\alpha}{\Delta y^2}\right)f_{i,j-1} - \left(\frac{\alpha}{\Delta y^2}\right)f_{i,j+1}$$

  • couple y direction $$\left(\frac{\alpha}{\Delta y^2}\right)f_{i,j-1}+\alpha\left(-\frac{2}{\Delta x^2}-\frac{2}{\Delta y^2}\right)f_{i,j}+\left(\frac{\alpha}{\Delta y^2}\right)f_{i,j+1} = S_{i,j} - \left(\frac{\alpha}{\Delta x^2}\right)f_{i-1,j} - \left(\frac{\alpha}{\Delta x^2}\right)f_{i+1,j}$$

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
ix= np.ix_
nx = 42                # including boundaries
ny = 42                # including boundaries
Lx = 1.0
Ly = 1.0
dx = Lx/(nx-1)
dy = Ly/(ny-1)
α  = 1.0

S  = np.full((nx,ny), -10.0)

maxit = 10000
tol   = 1.0E-4

#-----------------------------------------------------------------

def check_convergence():
    i = np.arange(1,nx-1)
    j = np.arange(1,ny-1)
    res = α*(f[ix(i-1,j)]-2.0*f[ix(i,j)]+f[ix(i+1,j)])/dx/dx + \
          α*(f[ix(i,j-1)]-2.0*f[ix(i,j)]+f[ix(i,j+1)])/dy/dy - \
          S[ix(i,j)]
    err = np.linalg.norm(res)
    ref = np.linalg.norm(S)
    if err/ref < tol :
        print("number of iterations =", it)
        return True
    else:
        return False
    
#-----------------------------------------------------------------
    
def plot_results():
    x = np.linspace(0,Lx,nx)
    y = np.linspace(0,Ly,ny)
    X,Y = np.meshgrid(x,y)
    
    plt.rc("font", size=14)
    plt.contourf(X,Y,f,20)
    plt.colorbar()
    plt.xlabel('x')
    plt.ylabel('y');
    plt.gca().set_aspect('equal', adjustable='box')
def thomas(l,aa,u,bb) :
    
    a = aa.copy()        # a is modified, but we are reusing a, so make a copy
    b = bb.copy()        # b too
    
    n = np.size(a)
    x = np.zeros(n)
    for i in range(1,n) :
        a[i] = a[i] - l[i]/a[i-1]*u[i-1]
        b[i] = b[i] - l[i]/a[i-1]*b[i-1]
    
    x[n-1] = b[n-1]/a[n-1]
    for i in range(n-2,-1,-1) :
        x[i] = (b[i]-u[i]*x[i+1])/a[i]
    
    return x

#======================================================

f  = np.zeros((nx,ny))

a_x = np.full(nx-2, α*(-2.0/dy/dy-2.0/dx/dx))
u_x = np.full(nx-2, α/dx/dx)
l_x = np.full(nx-2, α/dx/dx)

a_y = np.full(ny-2, α*(-2.0/dy/dy-2.0/dx/dx))
u_y = np.full(ny-2, α/dy/dy)
l_y = np.full(ny-2, α/dy/dy)

for it in range(maxit) :
    
    #------ sweep i lines  (soln in y dir)

    j = np.arange(1,ny-1)
    for i in range(1,nx-1) :
        b_y = S[i,j] - α/dx/dx*( f[i-1,j] + f[i+1,j] )
        f[i,j] = thomas(l_y,a_y,u_y,b_y)

    #------ sweep j lines  (soln in x dir)

    i = np.arange(1,nx-1)
    for j in range(1,ny-1) :
        b_x = S[i,j] - α/dy/dy*( f[i,j-1] + f[i,j+1] )
        f[i,j] = thomas(l_x,a_x,u_x,b_x)

    if check_convergence():
        break

plot_results()
number of iterations = 384

Jacobi method

f  = np.zeros((nx,ny))

i = np.arange(1,nx-1)
j = np.arange(1,ny-1)

for it in range(maxit) :
    f[ix(i,j)] = (S[ix(i,j)] - \
                 α*(f[ix(i-1,j)]+f[ix(i+1,j)])/dx/dx - \
                 α*(f[ix(i,j-1)]+f[ix(i,j+1)])/dy/dy) / \
                 (-2*α/dx/dx - 2*α/dy/dy)
    
    if check_convergence():
        break

plot_results()
number of iterations = 3054