import numpy as np
import pandas as pd
from numpy import linalg as LA
import math
from scipy.sparse import csr_matrix, diags
from scipy.sparse.linalg import spsolve, spsolve_triangular
# Timer
from timeit import default_timer as timer
import warnings
warnings.filterwarnings('ignore')
The wave equation in time domain can be stated as follows, \begin{equation} \frac{\partial^2 u}{\partial t^2}=c^2 \nabla^2 u+f,\qquad x\in \Omega \subset \mathbb{R}^d,~t\in(T_1,T_2] \end{equation} where $\Delta$ is the Laplacian, $f$ is a forcing function (for example our source) and $c$ is the wave velocity at which the time and spatially varying wave $u$ propagates.
Second-Order Finite-Difference Methods for
the Three Dimensional Wave Equation in Time Domain
Let $I=(a,b)\times(c,d)\times(e,f)$ and $J=[a,b]\times[c,d]\times[e,f]$. A three-dimensional form of the wave equation can be found as follows, \begin{align} \begin{cases} \dfrac{\partial^2 u}{\partial t^2} = c^2(x,y,z) \Delta u +f(x,y,z,t),&(x,y,z)\in I,~t\in(T_1,T_2],\\ u(x,y,z,T_1)=g(x,y,z),&(x,y,z)\in J,\\ \dfrac{\partial }{\partial t}u(x,y,z,T_1) = s(x,y,z),&(x,y,z)\in J,\\ u(a,y,z,t) = f_{a}(y,z,t),&(y,z)\in [c,d]\times[e,f],~t \in[T_1,T_2],\\ u(b,y,z,t) = f_{b}(y,z,t),&(y,z)\in [c,d]\times[e,f],~t \in[T_1,T_2],\\ u(x,c,z,t) = f_{c}(x,z,t),&(x,z)\in [a,b]\times[e,f],~t \in[T_1,T_2],\\ u(x,d,z,t) = f_{d}(x,z,t),&(x,z)\in [a,b]\times[e,f],~t \in[T_1,T_2],\\ u(x,y,e,t) = f_{e}(x,y,t),&(x,y)\in [a,b]\times[c,d],~t \in[T_1,T_2],\\ u(x,y,f,t) = f_{f}(x,y,t),&(x,y)\in [a,b]\times[c,d],~t \in[T_1,T_2]. \end{cases} \end{align}
Consider the following notations:
An explicit finite difference scheme for solving the problem (1) can be considered as follows, \begin{align} \frac{1}{\tau^2} \delta_t^2 u_{i,j,k}^{n}&= c_{i,j,k}^2\left(\frac{1}{h_x^2} \delta_x^2+\frac{1}{h_y^2} \delta_y^2+\frac{1}{h_z^2} \delta_z^2\right)u_{i,j,k}^{n}+f_{i,j,k}^n. \end{align} To develop a numerical algorithm, we can rewrite this as follows, \begin{align} u_{i,j,k}^{n+1}&=c_{i,j,k}^2\left[\lambda_x^2\left(u_{i+1,j,k}^{n}+u_{i-1,j,k}^{n}\right)+\lambda_y^2\left(u_{i,j+1,k}^{n}+u_{i,j-1,k}^{n}\right)+\lambda_z^2\left(u_{i,j,k+1}^{n}+u_{i,j,k-1}^{n}\right)\right] \notag \\ & +2\left(1-c_{i,j,k}^2\left(\lambda_x^2+\lambda_y^2+\lambda_z^2\right)\right)u_{i,j,k}^{n}-u_{i,j,k}^{n-1}+\tau^2f_{i,j,k}^n, \end{align}
A general implicit finite difference form of the problem can be expressed as follows, \begin{align} \frac{1}{\tau^2} \delta_t^2 u_{i,j,k}^{n}&= c_{i,j,k}^2 \left(\frac{1}{h_x^2} \delta_x^2+\frac{1}{h_y^2} \delta_y^2 +\frac{1}{h_z^2} \delta_z^2 \right) \left[\alpha u_{i,j,k}^{n+1}+\beta u_{i,j,k}^{n} +\gamma u_{i,j,k}^{n-1}\right]+f_{i,j,k}^n, \end{align} where $\alpha,~\beta$ and $\gamma$ are constants such that $\alpha+\beta+\gamma=1$.
The equation (2) can be expanded as follows, \begin{align} \left(1-\alpha c^2_{i,j,k}\left(\lambda_x^2 \delta_x^2 +\lambda_y^2 \delta_y^2 +\lambda_z^2 \delta_z^2 \right)\right)u_{i,j,k}^{n+1}&= \left[c_{i,j,k}^2 \beta \left(\lambda_x^2 \delta_x^2 +\lambda_y^2 \delta_y^2 +\lambda_z^2 \delta_z^2 \right)+2\right]u_{i,j,k}^{n} \notag \\ & +\left[c_{i,j,k}^2\gamma \left(\lambda_x^2 \delta_x^2 +\lambda_y^2 \delta_y^2 +\lambda_z^2 \delta_z^2 \right)-1\right]u_{i,j,k}^{n-1} +\tau^2f_{i,j,k}^n. \end{align} Note that, \begin{align} &\left(1-\alpha c^2_{i,j,k}\left(\lambda_x^2 \delta_x^2 +\lambda_y^2 \delta_y^2 +\lambda_z^2 \delta_z^2 \right)\right)u_{i,j,k}^{n} -\left(1-\alpha c^2_{i,j,k} \lambda_x^2 \delta_x^2\right)\left(1-\alpha c^2_{i,j,k} \lambda_y^2 \delta_y^2\right)\left(1-\alpha c^2_{i,j,k} \lambda_z^2 \delta_z^2\right)u_{i,j,k}^{n}= \notag \\ & \alpha^2 c^4_{i,j,k} \left(\lambda_x^2 \lambda_y^2 \delta_x^2\delta_y^2+\lambda_x^2 \lambda_z^2 \delta_x^2\delta_z^2 +\lambda_y^2 \lambda_z^2 \delta_y^2\delta_z^2\right)u_{i,j,k}^{n} -\alpha^3 c^6_{i,j,k} \lambda_x^2 \lambda_y^2 \lambda_z^2 \delta_x^2\delta_y^2\delta_z^2 u_{i,j,k}^{n}\approx O(h^4), \qquad \qquad (3). \end{align}
Furthermore, for simplicity, consider \begin{align} \mathbf{F}_{i,j,k}^{n}&= \left[c_{i,j,k}^2 \beta \left(\lambda_x^2 \delta_x^2 +\lambda_y^2 \delta_y^2 +\lambda_z^2 \delta_z^2 \right)+2\right]u_{i,j,k}^{n} +\left[c_{i,j,k}^2\gamma \left(\lambda_x^2 \delta_x^2 +\lambda_y^2 \delta_y^2 +\lambda_z^2 \delta_z^2 \right)-1\right]u_{i,j,k}^{n-1} +\tau^2f_{i,j,k}^n. \end{align}
As a result, \begin{align} \left(1-\alpha c^2_{i,j,k} \lambda_x^2 \delta_x^2\right)\left(1-\alpha c^2_{i,j,k} \lambda_y^2 \delta_y^2\right)\left(1-\alpha c^2_{i,j,k} \lambda_z^2 \delta_z^2\right)u_{i,j,k}^{n+1}=\mathbf{F}_{i,j,k}^{n}. \end{align}
The first time step after the initial time level is needed for proceeding the introduced algorithm and $u(x,y,z,T_1+\tau)$ is approximated by means of Taylor series, \begin{align} u(x,y,z,T_1+\tau)&= g(x,y,z)+s(x,y,z)\tau+\frac{1}{2}[c^2(x,y,z) \Delta g(x,y,z)+f(x,y,z,T_1)]\tau^2 \notag\\ & +\frac{1}{6} [c^2(x,y,z) \Delta s(x,y,z)+f_t(x,y,z,T_1)]\tau^3 \notag\\ & +\frac{1}{24}[c^2(x,y,z) \Delta [c^2(x,y,z) \Delta g(x,y,z)+f(x,y,z,T_1)] +f_{tt}(x,y,z,T_1)]\tau^4 +\mathcal{O}(\tau^5) \end{align}
To numerical algorithm based,, first, for current time step, fix $n$ and solve the following system for $i=1,2,\ldots,N_x-1$, $j=1,2,\ldots,N_y-1$ and $k=1,2,\ldots,N_z-1$, \begin{align} \left(1-\alpha c^2_{i,j,k} \lambda_x^2 \delta_x^2\right)\mathbf{X}_{i,j,k}=\mathbf{F}_{i,j,k}^{n}, \qquad \qquad (4) \end{align} In other words, \begin{align}\label{eq1C1.11} -\alpha c_{i,j,k}^2 \lambda_x^2 \left(\mathbf{X}_{i-1,j,k}+\mathbf{X}_{i+1,j,k}\right)+\left(1 +2 \alpha c_{i,j,k}^2 \lambda_x^2\right) \mathbf{X}_{i,j,k} =\mathbf{F}_{i,j,k}^{n}. \end{align} In matrix form: $$ A_{j,k}=\begin{bmatrix} 1 +2 \alpha c_{1,j,k}^2\lambda_x^2 & -\alpha c_{1,j,k}^2\lambda_x^2 & 0 & \dots & \dots &0 \\ -\alpha c_{2,j,k}^2\lambda_x^2 &1 +2 \alpha c_{2,j,k}^2\lambda_x^2 & -\alpha c_{2,j,k}^2\lambda_x^2 & 0 & \vdots &\vdots \\ 0 &-\alpha c_{3,j,k}^2\lambda_x^2 &1 +2 \alpha c_{3,j,k}^2\lambda_x^2 & -\alpha c_{3,j,k}^2\lambda_x^2 &0 &\vdots \\ 0 &0 & \ddots & \ddots &\ddots &\vdots \\ \vdots &\vdots & -\alpha c_{N_x-3,j,k}^2\lambda_x^2 & 1 +2 \alpha c_{N_x-3,j,k}^2\lambda_x^2 &-\alpha c_{N_x-3,j,k}^2\lambda_x^2 & 0 \\ \vdots &\vdots & 0 & -\alpha c_{N_x-2,j,k}^2\lambda_x^2 & 1 +2 \alpha c_{N_x-2,j,k}^2\lambda_x^2 &-\alpha c^2_{N_x-2,j,k}\lambda_x^2 \\ 0 &0 & \dots & 0 & -\alpha c_{N_x-1,j,k}^2\lambda_x^2 & 1 +2 \alpha c_{N_x-1,j,k}^2\lambda_x^2 \end{bmatrix}, $$ $$ \mathbf{X}_{j,k}=\begin{bmatrix} \mathbf{X}_{1,j,k} \\ \mathbf{X}_{2,j,k}\\ \vdots\\ \mathbf{X}_{N_x-2,j,k}\\ \mathbf{X}_{N_x-1,j,k} \end{bmatrix},~ \mathbf{b}_{j,k}^{n}= \begin{bmatrix} \alpha c^2_{1,j,k} \lambda_x^2 \mathbf{X}_{0,j,k}\\ 0\\ \vdots\\ 0\\ \alpha c^2_{N_x-1,j,k}\lambda_x^2 \mathbf{X}_{N_x,j,k}, \end{bmatrix}, \mathbf{F}_{j,k}^{n}= \begin{bmatrix} \mathbf{F}_{1,j,k}^{n}\\ \mathbf{F}_{2,j,k}^{n}\\ \vdots\\ \mathbf{F}_{N_x-2,j,k}^{n}\\ \mathbf{F}_{N_x-1,j,k}^{n} \end{bmatrix}. $$ where $\mathbf{X}_{0,j,k}$ and $\mathbf{X}_{N_x,j,k}$ for $1\leq j\leq N_x-1$ and $1\leq j\leq N_y-1$ can be stated as follows, \begin{align} \label{eq1C1.12} \mathbf{X}_{0,j,k}&= \left(1-\alpha c^2_{i,j,k} \lambda_y^2 \delta_y^2\right)\left(1-\alpha c^2_{i,j,k} \lambda_z^2 \delta_z^2\right)f_{a,j,k}^{n+1}, \\ \label{eq1C1.13} \mathbf{X}_{N_x,j,k}&=\left(1-\alpha c^2_{i,j,k} \lambda_y^2 \delta_y^2\right)\left(1-\alpha c^2_{i,j,k} \lambda_z^2 \delta_z^2\right)f_{a,j,k}^{n+1}. \end{align} and, $f_{a,j,k}^{n}\approx f_{a}(jh_y,kh_z,n\tau)$ and $f_{b,j,k}^{n}\approx f_{b}(jh_y,kh_z,n\tau)$.
Therefore, \begin{equation*} \label{eq1C1.14}\mathbf{X}_{j,k}=A_j^{-1}\left(\mathbf{F}_{j,k}^{n}+\mathbf{b}_j^n\right). \end{equation*}
Next, we need to solve the following system, \begin{align} \left(1-\alpha c^2_{i,j,k} \lambda_y^2 \delta_y^2\right)\mathbf{Y}_{i,j,k}=\mathbf{X}_{i,j,k}. \end{align} This can be expanded as follows, \begin{align} -\alpha c_{i,j,k}^2 \lambda_y^2 \left(\mathbf{Y}_{i,j-1,k}+\mathbf{Y}_{i,j+1,k}\right)+\left(1 +2 \alpha c_{i,j,k}^2\lambda_y^2\right)\mathbf{Y}_{i,j,k}=\mathbf{X}_{i,j,k}. \end{align} Hence, \begin{equation*} \label{eq1C2.13}\mathbf{Y}_{i,k}=A_{i,k}^{-1}\left(\mathbf{X}_{i,k}+\mathbf{b}_{i,k}^n\right), \end{equation*} where \begin{equation*}\label{eq1C2.14} A_{i,k}=\begin{bmatrix} 1 +2 \alpha c_{i,1,k}^2\lambda_y^2 & -\alpha c_{i,1,k}^2\lambda_y^2 & 0 & \dots & \dots &0 \\ -\alpha c_{i,2,k}^2\lambda_y^2 &1 +2 \alpha c_{i,2,k}^2\lambda_y^2 & -\alpha c_{i,2,k}^2\lambda_y^2 & 0 & \vdots &\vdots \\ 0 &-\alpha c_{i,3,k}^2\lambda_y^2 &1 +2 \alpha c_{i,3,k}^2\lambda_y^2 & -\alpha c_{i,3,k}^2\lambda_y^2 &0 &\vdots \\ 0 &0 & \ddots & \ddots &\ddots &\vdots \\ \vdots &\vdots & -\alpha c_{i,N_y-3,k}^2\lambda_y^2 & 1 +2 \alpha c_{i,N_y-3,k}^2\lambda_y^2 &-\alpha c_{i,N_y-3,k}^2\lambda_y^2 & 0 \\ \vdots &\vdots & 0 & -\alpha c_{i,N_y-2,k}^2\lambda_y^2 & 1 +2 \alpha c_{i,N_y-2,k}^2\lambda_y^2 &-\alpha c^2_{i,N_y-2,k}\lambda_y^2 \\ 0 &0 & \dots & 0 & -\alpha c_{i,N_y-1,k}^2\lambda_y^2 & 1 +2 \alpha c_{i,N_y-1,k}^2\lambda_y^2 \end{bmatrix}, \end{equation*} and \begin{equation*}\label{eq1C2.15} \mathbf{b}_{i,k}^{n}= \begin{bmatrix} \alpha c^2_{i,1,k}\lambda_y^2 \mathbf{Y}_{i,0,k}\\ 0\\ \vdots\\ 0\\ \alpha c^2_{i,N_y-1,k}\lambda_y^2 \mathbf{Y}_{i,N_y,k} \end{bmatrix},~ \mathbf{X}_{i,k}=\begin{bmatrix} \mathbf{X}_{i,1,k} \\ \mathbf{X}_{i,2,k}\\ \vdots\\ \mathbf{X}_{i,N_y-1,k}\\ \mathbf{X}_{i,N_y,k} \end{bmatrix} \text{ and } \mathbf{Y}_{i,k}=\begin{bmatrix} \mathbf{Y}_{i,1,k} \\ \mathbf{Y}_{i,2,k}\\ \vdots\\ \mathbf{Y}_{i,N_y-2,k}\\ \mathbf{Y}_{i,N_y-1,k} \end{bmatrix}. \end{equation*} Moreover, $\mathbf{Y}_{i,0,k}$ and $\mathbf{Y}_{i,N_y,k}$ can be approximated as follows, \begin{align} \label{eq1C1.17}\mathbf{Y}_{i,0,k}&=-\alpha c_{i,0,k}^2 \lambda_z^2 \left(f_{c,i,k-1}^{n+1}+f_{c,i,k+1}^{n+1}\right)+\left(1 +2 \alpha c_{i,0,k}^2\lambda_z^2\right)f_{c,i,k}^{n+1},\\ \label{eq1C1.18}\mathbf{Y}_{i,N_y,k}&=-\alpha c_{i,N_y,k}^2 \lambda_z^2 \left(f_{d,i,k-1}^{n+1}+f_{d,i,k+1}^{n+1}\right)+\left(1 +2 \alpha c_{i,N_y,k}^2\lambda_z^2\right)f_{d,i,k}^{n+1}, \end{align} where $f_{c,i,k}^{n}\approx f_{c}(ih_x,kh_z,n\tau)$ and $f_{d,i,k}^{n}\approx f_{d}(ih_x,kh_z,n\tau)$.
Finally, we need to solve the following system for $u_{i,j,k}^{n}$ for each $i=1,2,\ldots,N_x-1$, $j=1,2,\ldots,N_y-1$ and $k=1,2,\ldots,N_z-1$, \begin{align} \label{eq1C1.19}\left(1-\alpha c^2_{i,j,k} \lambda_z^2 \delta_z^2\right)u_{i,j,k}^{n+1}=\mathbf{Y}_{i,j,k}. \end{align} In other words, \begin{align} \label{eq1C1.20}-\alpha c_{i,j,k}^2 \lambda_z^2 \left(u_{i,j,k-1}^{n+1}+u_{i,j,k+1}^{n+1}\right)+\left(1 +2 \alpha c_{i,j,k}^2\lambda_z^2\right)u_{i,j,k}^{n+1}=\mathbf{Y}_{i,j,k}. \end{align} Hence, \begin{equation*} \label{eq1C1.21}u_{i,j}^{n+1}=A_{i,j}^{-1}\left(\mathbf{Y}_{i,j,k}+\mathbf{b}_{i,j}^n\right), \end{equation*} where \begin{equation*}\label{eq1C1.22} A_{i,j}=\begin{bmatrix} 1 +2 \alpha c_{i,j,1}^2\lambda_z^2 & -\alpha c_{i,j,1}^2\lambda_z^2 & 0 & \dots & \dots &0 \\ -\alpha c_{i,j,2}^2\lambda_z^2 &1 +2 \alpha c_{i,j,2}^2\lambda_z^2 & -\alpha c_{i,j,2}^2\lambda_z^2 & 0 & \vdots &\vdots \\ 0 &-\alpha c_{i,j,3}^2\lambda_z^2 &1 +2 \alpha c_{i,j,3}^2\lambda_z^2 & -\alpha c_{i,j,3}^2\lambda_z^2 &0 &\vdots \\ 0 &0 & \ddots & \ddots &\ddots &\vdots \\ \vdots &\vdots & -\alpha c_{i,j,N_z-3}^2\lambda_z^2 & 1 +2 \alpha c_{i,j,N_z-3}^2\lambda_z^2 &-\alpha c_{i,j,N_z-3}^2\lambda_z^2 & 0 \\ \vdots &\vdots & 0 & -\alpha c_{i,j,N_z-2}^2\lambda_z^2 & 1 +2 \alpha c_{i,j,N_z-2}^2\lambda_z^2 &-\alpha c^2_{i,j,N_z-2}\lambda_y^2 \\ 0 &0 & \dots & 0 & -\alpha c_{i,j,N_z-1}^2\lambda_z^2 & 1 +2 \alpha c_{i,j,N_z-1}^2\lambda_y^2 \end{bmatrix}, \end{equation*} and \begin{equation*} \mathbf{b}_{i,j}^{n}= \begin{bmatrix} \alpha c^2_{i,j,1}\lambda_z^2 f_{e,i,j}^{n+1}\\ 0\\ \vdots\\ 0\\ \alpha c^2_{i,j,N_z-1}\lambda_z^2 f_{f,i,j}^{n+1} \end{bmatrix} \text{ and } \mathbf{u}_{i,j}^{n+1}=\begin{bmatrix} u_{i,j,1}^{n+1} \\ u_{i,j,2}^{n+1}\\ \vdots\\ u_{i,j,N_z-2}^{n+1}\\ u_{i,j,N_z-1}^{n+1} \end{bmatrix}, \end{equation*}
Note that $f_{e,i,j}^{n}\approx f_{e}(ih_x,jh_y,n\tau)$ and $f_{f,i,j}^{n}\approx f_{e}(ih_x,jh_y,n\tau)$.
For Stability and convergence analyses, see this file.
Consider the following problem, \begin{align} \begin{cases} \dfrac{\partial^2 u}{\partial t^2} = \frac{1}{3}e^{x+y+z} \Delta u +e^{-t-x-y-z}-e^{-t},&(x,y,z)\in I,~t\in(0,1],\\ u(x,y,z,0)=g(x,y,z),&(x,y,z)\in J,\\ \dfrac{\partial }{\partial t}u(x,y,z,1) = s(x,y,z),&(x,y,z)\in J,\\ u(0,y,z,t) = e^{-t-y-z},&(y,z)\in [0,1]\times[0,1],~t \in[0,1],\\ u(1,y,z,t) = e^{-t-y-z-1},&(y,z)\in [0,1]\times[0,1],~t \in[0,1],\\ u(x,0,z,t) = e^{-t-x-z},&(x,z)\in [0,1]\times[0,1],~t \in[0,1],\\ u(x,1,z,t) = e^{-t-x-z-1},&(x,z)\in [0,1]\times[0,1],~t \in[0,1],\\ u(x,y,0,t) = e^{-t-x-y},&(x,y)\in [0,1]\times[0,1],~t \in[0,1],\\ u(x,y,1,t) = e^{-t-x-y-1},&(x,y)\in [0,1]\times[0,1],~t \in[0,1]. \end{cases} \end{align}
The exact solution corresponding to the problem can be found as follows, $$ u(x,t)=e^{-t-x-y-z} $$
def Example01_ExSolver3D(N,Nt):
# N = the number of mesh points for space
# Nt = the number of mesh points for time
Nx=N
Ny=N
Nz=N
# (x,y,z) \in [a_1,a_2]*[b_1,b_2]*[c_1,c_2]
a1 = 0.0
a2 = 1.0
b1 = 0.0
b2 = 1.0
c1 = 0.0
c2 = 1.0
# t \in [T_1,T_2]
T1=0
T2=1
# Delta x, Delta y, Delta z and Delta t
hx =(a2-a1)/Nx
hy =(b2-b1)/Ny
hz =(c2-c1)/Nz
ht =(T2-T1)/Nt
# \lambda_x, \lambda_y and \lambda_z
lx2=(ht/hx)**2
ly2=(ht/hy)**2
lz2=(ht/hz)**2
# Initial and Boundary Conditions
C2 =lambda x,y,z: 1/3*np.exp(x+y+z)
g=lambda x,y,z: np.exp(-x-y-z)
fa=lambda y,z,t: np.exp(-y-z-t)
fb=lambda y,z,t: np.exp(-y-z-t-1)
fc=lambda x,z,t: np.exp(-x-z-t)
fd=lambda x,z,t: np.exp(-x-z-t-1)
fe=lambda x,y,t: np.exp(-x-y-t)
ff=lambda x,y,t: np.exp(-x-y-t-1)
f=lambda x,y,z,t: np.exp(- t - x - y - z) - np.exp(-t)
# the exact solution
Ue=lambda x,y,z,t: np.exp(-x-y-z-t)
# Null Matrices
CC=np.zeros((Nx+1,Ny+1,Nz+1), dtype=float)
U_exact=np.zeros((Nx+1,Ny+1,Nz+1), dtype=float)
u0=np.zeros((Nx+1,Ny+1,Nz+1), dtype=float)
u1=np.zeros((Nx+1,Ny+1,Nz+1), dtype=float)
# x, y, z and t
xx=np.linspace(a1, a2, Nx+1)
yy=np.linspace(b1, b2, Ny+1)
zz=np.linspace(c1, c2, Nz+1)
tt=np.linspace(T1, T2, Nt+1)
for k in range(Nz+1):
for j in range(Ny+1):
CC[:,j,k] = C2(xx,yy[j],zz[k])
U_exact[:,j,k] = Ue(xx,yy[j],zz[k],T2)
u0[:,j,k] = g(xx,yy[j],zz[k])
# indeces
mid=list(range(1,Nx))
midp=[i+1 for i in mid]
midm=[i-1 for i in mid]
# Computing the solution at the first time step
GG=lambda x,y,z: np.exp(-x-y-z)-np.exp(-x-y-z)*ht\
+(0.5)*(1+ np.exp(- T1 - x - y - z) - np.exp(-T1))*ht**2\
+(1/6)*(-1 - np.exp(- T1 - x - y - z) + np.exp(-T1))*ht**3\
+(1/24)*(1+ np.exp(- T1 - x - y - z) - np.exp(-T1))*ht**4
for k in mid:
for j in mid:
u1[mid,j,k]=GG(xx[mid],yy[j],zz[k])
for j in range(Ny+1):
u1[:,j,0]=fe(xx,yy[j],tt[1])
u1[:,j,Nz]=ff(xx,yy[j],tt[1])
for k in range(Nz+1):
u1[0,:,k]=fa(yy,zz[k],tt[1])
u1[Nx,:,k]=fb(yy,zz[k],tt[1])
u1[:,0,k]=fc(xx,zz[k],tt[1])
u1[:,Ny,k]=fd(xx,zz[k],tt[1])
# for loop (n>2)
for n in range(1,Nt):
u=np.zeros((Nx+1,Ny+1,Nz+1), dtype=float)
for k in mid:
for j in mid:
u[mid,j,k]=lx2*CC[mid,j,k]*u1[midp,j,k]\
+lx2*CC[mid,j,k]*u1[midm,j,k]\
+ly2*CC[mid,j,k]*u1[mid,j+1,k]\
+ly2*CC[mid,j,k]*u1[mid,j-1,k]\
+lz2*CC[mid,j,k]*u1[mid,j,k+1]\
+lz2*CC[mid,j,k]*u1[mid,j,k-1]\
+2*u1[mid,j,k]-2*lx2*CC[mid,j,k]*u1[mid,j,k]\
-2*ly2*CC[mid,j,k]*u1[mid,j,k]\
-2*lz2*CC[mid,j,k]*u1[mid,j,k]-u0[mid,j,k]\
+(ht**2)*f(xx[mid],yy[j],zz[k],tt[n])
for k in range(Nz+1):
for j in range(Ny+1):
u[:,j,0]=fe(xx,yy[j],tt[n+1])
u[:,j,Nz]=ff(xx,yy[j],tt[n+1])
u[0,:,k]=fa(yy,zz[k],tt[n+1])
u[Nx,:,k]=fb(yy,zz[k],tt[n+1])
u[:,0,k]=fc(xx,zz[k],tt[n+1])
u[:,Ny,k]=fd(xx,zz[k],tt[n+1])
u0=u1
u1=u
# Norm
U_Comp = u
Norm=np.max(np.abs(U_Comp - U_exact))
return Norm, U_exact, U_Comp
it=3
Norm=np.zeros(it, dtype=float)
N0=10;
Nt0=200;
N=np.asarray([N0*2**n for n in range(0,it)])
Nt=np.asarray([Nt0 for n in range(0,it)])
Ratio=np.zeros(it, dtype=float)
LOG=np.zeros(it, dtype=float)
CPU_Time=np.zeros(it, dtype=float)
# iteration
for n in range(it):
start = timer()
Norm[n],_,_ =Example01_ExSolver3D(N[n],Nt[n])
CPU_Time[n] = timer() - start
if (n>0):
Ratio[n]=Norm[n-1]/Norm[n]
LOG[n]=math.log(Ratio[n],2)
data = pd.DataFrame({'Nx': N, 'Ny': N, 'Nz': N, 'Nt': Nt, 'Norm': Norm, 'Ratio': Ratio, 'Log': LOG, 'CPU Time': CPU_Time})
del it, Norm, N0, Nt0, N, Nt, Ratio, LOG, CPU_Time, n, start
display(data.style.format({'Norm': "{:.4e}"}))
Nx | Ny | Nz | Nt | Norm | Ratio | Log | CPU Time | |
---|---|---|---|---|---|---|---|---|
0 | 10 | 10 | 10 | 200 | 3.0141e-05 | 0.000000 | 0.000000 | 1.842219 |
1 | 20 | 20 | 20 | 200 | 6.6633e-06 | 4.523345 | 2.177390 | 10.281707 |
2 | 40 | 40 | 40 | 200 | 1.6328e-06 | 4.080920 | 2.028894 | 42.177225 |
it=3
Norm=np.zeros(it, dtype=float)
N0=5;
Nt0=20;
N=np.asarray([N0*2**n for n in range(0,it)])
Nt=np.asarray([Nt0*2**n for n in range(0,it)])
Ratio=np.zeros(it, dtype=float)
LOG=np.zeros(it, dtype=float)
CPU_Time=np.zeros(it, dtype=float)
# iteration
for n in range(it):
start = timer()
Norm[n],_,_ =Example01_ExSolver3D(N[n],Nt[n])
CPU_Time[n] = timer() - start
if (n>0):
Ratio[n]=Norm[n-1]/Norm[n]
LOG[n]=math.log(Ratio[n],2)
data = pd.DataFrame({'Nx': N, 'Ny': N, 'Nz': N, 'Nt': Nt, 'Norm': Norm, 'Ratio': Ratio, 'Log': LOG, 'CPU Time': CPU_Time})
del it, Norm, N0, Nt0, N, Nt, Ratio, LOG, CPU_Time, n, start
display(data.style.format({'Norm': "{:.4e}"}))
Nx | Ny | Nz | Nt | Norm | Ratio | Log | CPU Time | |
---|---|---|---|---|---|---|---|---|
0 | 5 | 5 | 5 | 20 | 1.3388e-04 | 0.000000 | 0.000000 | 0.051071 |
1 | 10 | 10 | 10 | 40 | 2.9866e-05 | 4.482758 | 2.164387 | 0.387949 |
2 | 20 | 20 | 20 | 80 | 6.6263e-06 | 4.507186 | 2.172227 | 3.433552 |
What stands out from the tables is that the order of the method is 2 (in space and time). Moreover, consider the following experiment to test the stability condition.
it=3
Norm=np.zeros(it, dtype=float)
N0=5;
Nt0=3;
N=np.asarray([N0*2**n for n in range(0,it)])
Nt=np.asarray([Nt0*2**n for n in range(0,it)])
Ratio=np.zeros(it, dtype=float)
LOG=np.zeros(it, dtype=float)
CPU_Time=np.zeros(it, dtype=float)
# iteration
for n in range(it):
start = timer()
Norm[n],_,_ =Example01_ExSolver3D(N[n],Nt[n])
CPU_Time[n] = timer() - start
if (n>0):
Ratio[n]=Norm[n-1]/Norm[n]
LOG[n]=math.log(Ratio[n],2)
data = pd.DataFrame({'Nx': N, 'Ny': N, 'Nz': N, 'Nt': Nt, 'Norm': Norm, 'Ratio': Ratio, 'Log': LOG, 'CPU Time': CPU_Time})
del it, Norm, N0, Nt0, N, Nt, Ratio, LOG, CPU_Time, n, start
display(data.style.format({'Norm': "{:.4e}"}))
Nx | Ny | Nz | Nt | Norm | Ratio | Log | CPU Time | |
---|---|---|---|---|---|---|---|---|
0 | 5 | 5 | 5 | 3 | 3.3647e-03 | 0.000000 | 0.000000 | 0.007329 |
1 | 10 | 10 | 10 | 6 | 1.3526e+02 | 0.000025 | -15.294907 | 0.054640 |
2 | 20 | 20 | 20 | 12 | 3.1001e+13 | 0.000000 | -37.737815 | 0.511150 |
def Example01_ImSolver3D(N,Nt):
# alpha, beta and gamma
alpha=1/4
beta=1/2
gamma=1/4
# N = the number of mesh points for space
# Nt = the number of mesh points for time
Nx=N
Ny=N
Nz=N
# (x,y,z) \in [a_1,a_2]*[b_1,b_2]*[c_1,c_2]
a1 = 0.0
a2 = 1.0
b1 = 0.0
b2 = 1.0
c1 = 0.0
c2 = 1.0
# t \in [T_1,T_2]
T1=0
T2=1
# Delta x, Delta y, Delta z and Delta t
hx =(a2-a1)/Nx
hy =(b2-b1)/Ny
hz =(c2-c1)/Nz
ht =(T2-T1)/Nt
# \lambda_x, \lambda_y and \lambda_z
lx2=(ht/hx)**2
ly2=(ht/hy)**2
lz2=(ht/hz)**2
# Initial and Boundary Conditions
C2 =lambda x,y,z: 1/3*np.exp(x+y+z)
g=lambda x,y,z: np.exp(-x-y-z)
fa=lambda y,z,t: np.exp(-y-z-t)
fb=lambda y,z,t: np.exp(-y-z-t-1)
fc=lambda x,z,t: np.exp(-x-z-t)
fd=lambda x,z,t: np.exp(-x-z-t-1)
fe=lambda x,y,t: np.exp(-x-y-t)
ff=lambda x,y,t: np.exp(-x-y-t-1)
f=lambda x,y,z,t: np.exp(- t - x - y - z) - np.exp(-t)
# the exact solution
Ue=lambda x,y,z,t: np.exp(-x-y-z-t)
# Null Matrices
CC=np.zeros((Nx+1,Ny+1,Nz+1), dtype=float)
U_exact=np.zeros((Nx+1,Ny+1,Nz+1), dtype=float)
u0=np.zeros((Nx+1,Ny+1,Nz+1), dtype=float)
u1=np.zeros((Nx+1,Ny+1,Nz+1), dtype=float)
# x, y, z and t
xx=np.linspace(a1, a2, Nx+1)
yy=np.linspace(b1, b2, Ny+1)
zz=np.linspace(c1, c2, Nz+1)
tt=np.linspace(T1, T2, Nt+1)
for k in range(Nz+1):
for j in range(Ny+1):
CC[:,j,k] = C2(xx,yy[j],zz[k])
U_exact[:,j,k] = Ue(xx,yy[j],zz[k],T2)
u0[:,j,k] = g(xx,yy[j],zz[k])
# indeces
mid=list(range(1,Nx))
midp=[i+1 for i in mid]
midm=[i-1 for i in mid]
sup=list(range(1,Nx-1))
sub=list(range(2,Nx))
# Computing the solution at the first time step
GG=lambda x,y,z: np.exp(-x-y-z)-np.exp(-x-y-z)*ht\
+(0.5)*(1+ np.exp(- T1 - x - y - z) - np.exp(-T1))*ht**2\
+(1/6)*(-1 - np.exp(- T1 - x - y - z) + np.exp(-T1))*ht**3\
+(1/24)*(1+ np.exp(- T1 - x - y - z) - np.exp(-T1))*ht**4
for k in mid:
for j in mid:
u1[mid,j,k]=GG(xx[mid],yy[j],zz[k])
for j in range(Ny+1):
u1[:,j,0]=fe(xx,yy[j],tt[1])
u1[:,j,Nz]=ff(xx,yy[j],tt[1])
for k in range(Nz+1):
u1[0,:,k]=fa(yy,zz[k],tt[1])
u1[Nx,:,k]=fb(yy,zz[k],tt[1])
u1[:,0,k]=fc(xx,zz[k],tt[1])
u1[:,Ny,k]=fd(xx,zz[k],tt[1])
# for loop (n>2)
for n in range(1,Nt):
u=np.zeros((Nx+1,Ny+1,Nz+1), dtype=float)
X=np.zeros((Nx+1,Ny+1,Nz+1), dtype=float)
Y=np.zeros((Nx+1,Ny+1,Nz+1), dtype=float)
# Part 1: Finding the values of Y for current time step
# f_{a,j,k]}^{n+1} and f_{b,j,k]}^{n+1}
FA=np.zeros((Ny+1,Nz+1), dtype=float)
FB=np.zeros((Ny+1,Nz+1), dtype=float)
for k in range(Nz+1):
FA[:,k]=fa(yy,zz[k],tt[n+1])
FB[:,k]=fb(yy,zz[k],tt[n+1])
for k in mid:
for j in mid:
# Matrix A_{j,k]}
Ajk=diags([-alpha*CC[sub,j,k]*lx2,1+2*alpha*CC[mid,j,k]*lx2,-alpha*CC[sup,j,k]*lx2], [-1,0,1])
# F_{j,k}
Fjk=lx2*beta*CC[mid,j,k]*(u1[midp,j,k]+u1[midm,j,k])\
+ly2*beta*CC[mid,j,k]*(u1[mid,j+1,k]+u1[mid,j-1,k])\
+lz2*beta*CC[mid,j,k]*(u1[mid,j,k+1]+u1[mid,j,k-1])\
+(2-2*beta*CC[mid,j,k]*(lx2+ly2+lz2))*u1[mid,j,k]\
+lx2*gamma*CC[mid,j,k]*(u0[midp,j,k]+u0[midm,j,k])\
+ly2*gamma*CC[mid,j,k]*(u0[mid,j+1,k]+u0[mid,j-1,k])\
+lz2*gamma*CC[mid,j,k]*(u0[mid,j,k+1]+u0[mid,j,k-1])\
-(1+2*gamma*CC[mid,j,k]*(lx2+ly2+lz2))*u0[mid,j,k]\
+((ht**2)*f(xx[mid],yy[j],zz[k],tt[n]))
#
X_0jk=(alpha*alpha)*CC[0,j,k]*ly2*lz2*(CC[0,j+1,k]*(FA[j+1,k+1]+FA[j+1,k-1])\
+CC[0,j-1,k]*(FA[j-1,k+1]+FA[j-1,k-1]))\
-alpha*CC[0,j,k]*lz2*(1+2*alpha*CC[0,j,k]*ly2)*(FA[j,k+1]+FA[j,k-1])\
-alpha*CC[0,j,k]*ly2*((1+2*alpha*CC[0,j+1,k]*lz2)*FA[j+1,k]+(1+2*alpha*CC[0,j-1,k]*lz2)*FA[j-1,k])\
+(1+2*alpha*CC[0,j,k]*ly2)*(1+2*alpha*CC[0,j,k]*lz2)*FA[j,k]
#
X_Nxjk=(alpha*alpha)*CC[Nx,j,k]*ly2*lz2*(CC[Nx,j+1,k]*(FB[j+1,k+1]+FB[j+1,k-1])\
+CC[Nx,j-1,k]*(FB[j-1,k+1]+FB[j-1,k-1]))\
-alpha*CC[Nx,j,k]*lz2*(1+2*alpha*CC[Nx,j,k]*ly2)*(FB[j,k+1]+FB[j,k-1])\
-alpha*CC[Nx,j,k]*ly2*((1+2*alpha*CC[Nx,j+1,k]*lz2)*FB[j+1,k]+(1+2*alpha*CC[Nx,j-1,k]*lz2)*FB[j-1,k])\
+(1+2*alpha*CC[Nx,j,k]*ly2)*(1+2*alpha*CC[Nx,j,k]*lz2)*FB[j,k]
# b_{j,k}
bjk=np.zeros(len(mid), dtype=float)
bjk[0]=alpha*CC[1,j,k]*lx2*X_0jk
bjk[Nx-2]=alpha*CC[Nx-1,j,k]*lx2*X_Nxjk
# X
X[mid,j,k]=spsolve(Ajk,Fjk+bjk, use_umfpack=True)
del Ajk, bjk, Fjk, X_0jk, X_Nxjk
# Part 2: Finding the values of Y for current time step
FC=np.zeros((Nx+1,Nz+1), dtype=float)
FD=np.zeros((Nx+1,Nz+1), dtype=float)
for k in range(Nz+1):
# f_{c,j,k}^{n+1} and f_{d,j,k}^{n+1}
FC[:,k]= fc(xx,zz[k],tt[n+1])
FD[:,k]= fd(xx,zz[k],tt[n+1])
for k in mid:
for i in mid:
# Matrix A_{i,k}
Aik=diags([-alpha*CC[i,sub,k]*ly2,1+2*alpha*CC[i,mid,k]*ly2,-alpha*CC[i,sup,k]*ly2], [-1,0,1])
Y_i0k=-alpha*CC[i,0,k]*lz2*(FC[i,k-1]+FC[i,k+1])+(1+2*alpha*CC[i,0,k]*lz2)*FC[i,k]
Y_iNyj=-alpha*CC[i,Ny,k]*lz2*(FD[i,k-1]+FD[i,k+1])+(1+2*alpha*CC[i,Ny,k]*lz2)*FD[i,k]
# b_{i,k]
bik=np.zeros(len(mid), dtype=float)
bik[0]=alpha*CC[i,1,k]*ly2*Y_i0k
bik[Ny-2]=alpha*CC[i,Ny-1,k]*ly2*Y_iNyj
Y[i,mid,k]=spsolve(Aik,X[i,mid,k]+bik, use_umfpack=True)
del Aik, bik, Y_i0k, Y_iNyj
# Part 3: Finding the values of u^{n+1} for current time step
FE=np.zeros((Nx+1,Ny+1), dtype=float)
FF=np.zeros((Nx+1,Ny+1), dtype=float)
for j in range(Ny+1):
# f_{e,i,j}^{n+1} and f_{f,i,j}^{n+1}
FE[:,j]= fc(xx,yy[j],tt[n+1])
FF[:,j]= fd(xx,yy[j],tt[n+1])
for j in mid:
for i in mid:
# Matrix A_{i,j}
Aik=diags([-alpha*CC[i,j,sub]*lz2,1+2*alpha*CC[i,j,mid]*lz2,-alpha*CC[i,j,sup]*lz2], [-1,0,1])
bij=np.zeros(len(mid), dtype=float)
bij[0]=alpha*CC[i,j,1]*ly2*FE[i,j]
bij[Nz-2]=alpha*CC[i,j,Nz-1]*ly2*FF[i,j]
u[i,j,mid]=spsolve(Aik,Y[i,j,mid]+bij, use_umfpack=True)
del Aik, bij
u[0,:,:]=FA
u[Nx,:,:]=FB
u[:,0,:]=FC
u[:,Ny,:]=FD
u[:,:,0]=FE
u[:,:,Nz]=FF
u0=u1
u1=u
# Norm
U_Comp = u
Norm=np.max(np.abs(U_Comp - U_exact))
return Norm, U_exact, U_Comp
it=3
Norm=np.zeros(it, dtype=float)
N0=5
Nt0=100
N=np.asarray([N0*2**n for n in range(0,it)])
Nt=np.asarray([Nt0 for n in range(0,it)])
Ratio=np.zeros(it, dtype=float)
LOG=np.zeros(it, dtype=float)
CPU_Time=np.zeros(it, dtype=float)
# iteration
for n in range(it):
start = timer()
Norm[n],_,_ =Example01_ImSolver3D(N[n],Nt[n])
CPU_Time[n] = timer() - start
if (n>0):
Ratio[n]=Norm[n-1]/Norm[n]
LOG[n]=math.log(Ratio[n],2)
data = pd.DataFrame({'Nx': N, 'Ny': N, 'Nz': N, 'Nt': Nt, 'Norm': Norm, 'Ratio': Ratio, 'Log': LOG, 'CPU Time': CPU_Time})
del it, Norm, N0, Nt0, N, Nt, Ratio, LOG, CPU_Time, n, start
display(data.style.format({'Norm': "{:.4e}"}))
Nx | Ny | Nz | Nt | Norm | Ratio | Log | CPU Time | |
---|---|---|---|---|---|---|---|---|
0 | 5 | 5 | 5 | 100 | 1.3927e-04 | 0.000000 | 0.000000 | 1.337826 |
1 | 10 | 10 | 10 | 100 | 3.1284e-05 | 4.451670 | 2.154347 | 6.636075 |
2 | 20 | 20 | 20 | 100 | 7.4446e-06 | 4.202264 | 2.071167 | 30.909014 |
Das, Sambit, Wenyuan Liao, and Anirudh Gupta. "An efficient fourth-order low dispersive finite difference scheme for a 2-D acoustic wave equation." Journal of computational and Applied Mathematics 258 (2014): 151-167.
Liao, Wenyuan. "On the dispersion, stability and accuracy of a compact higher-order finite difference scheme for 3D acoustic wave equation." Journal of Computational and Applied Mathematics 270 (2014): 571-583.
Liao, Wenyuan. "An implicit fourth-order compact finite difference scheme for one-dimensional Burgers’ equation." Applied Mathematics and Computation 206.2 (2008): 755-764.