The Implicit Crank-Nicolson Difference Equation for the Heat Equation#
John S Butler john.s.butler@tudublin.ie Course Notes Github#
Overview#
This notebook will illustrate the Crank-Nicolson Difference method for the Heat Equation.
The Heat Equation#
The Heat Equation is the first order in time (\(t\)) and second order in space (\(x\)) Partial Differential Equation:
The equation describes heat transfer on a domain
with an initial condition at time \(t=0\) for all \(x\) and boundary condition on the left (\(x=0\)) and right side
This notebook will illustrate the Crank-Nicolson Difference method for the Heat Equation with the initial conditions
and boundary condition
Crank-Nicolson Difference method#
The implicit Crank-Nicolson difference equation of the Heat Equation is derived by discretising the
around \((x_i,t_{j+\frac{1}{2}})\) giving the difference equation
Rearranging give the difference equation
for \(i=1,...9\) where \(r=\frac{k}{h^2}\).
# LIBRARY
# vector manipulation
import numpy as np
# math functions
import math
# THIS IS FOR PLOTTING
%matplotlib inline
import matplotlib.pyplot as plt # side-stepping mpl backend
import warnings
warnings.filterwarnings("ignore")
Discete Grid#
The region \(\Omega\) is discretised into a uniform mesh \(\Omega_h\). In the space \(x\) direction into \(N\) steps giving a stepsize of
resulting in
and into \(N_t\) steps in the time \(t\) direction giving a stepsize of
resulting in
The Figure below shows the discrete grid points for \(N=10\) and \(N_t=100\) , the red dots are the unknown values, the green dots are the known boundary conditions and the blue dots are the known initial conditions of the Heat Equation.
N=10
Nt=100
h=1/N
k=1/Nt
r=k/(h*h)
time_steps=15
time=np.arange(0,(time_steps+.5)*k,k)
x=np.arange(0,1.0001,h)
X, Y = np.meshgrid(x, time)
fig = plt.figure()
plt.plot(X,Y,'ro');
plt.plot(x,0*x,'bo',label='Initial Condition');
plt.plot(np.ones(time_steps+1),time,'go',label='Boundary Condition');
plt.plot(x,0*x,'bo');
plt.plot(0*time,time,'go');
plt.xlim((-0.02,1.02))
plt.xlabel('x')
plt.ylabel('time (ms)')
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.title(r'Discrete Grid $\Omega_h,$ h= %s, k=%s'%(h,k),fontsize=24,y=1.08)
plt.show();
Discrete Initial and Boundary Conditions#
The discrete initial conditions are
and the discete boundary conditions are
where \(w[i,j]\) is the numerical approximation of \(U(x[i],t[j])\).
The Figure below plots values of \(w[i,0]\) for the inital (blue) and boundary (red) conditions for \(t[0]=0.\)
w=np.zeros((N+1,time_steps+1))
b=np.zeros(N-1)
# Initial Condition
for i in range (1,N):
w[i,0]=2*x[i]
if x[i]>0.5:
w[i,0]=2*(1-x[i])
# Boundary Condition
for k in range (0,time_steps):
w[0,k]=0
w[N,k]=0
fig = plt.figure(figsize=(8,4))
plt.plot(x,w[:,0],'o:',label='Initial Condition')
plt.plot(x[[0,N]],w[[0,N],0],'go',label='Boundary Condition t[0]=0')
#plt.plot(x[N],w[N,0],'go')
plt.xlim([-0.1,1.1])
plt.ylim([-0.1,1.1])
plt.title('Intitial and Boundary Condition',fontsize=24)
plt.xlabel('x')
plt.ylabel('w')
plt.legend(loc='best')
plt.show()
The Implicit Crank-Nicolson Difference Equation#
The implicit Crank-Nicolson difference equation of the Heat Equation is derived by discretising the
around \((x_i,t_{j+\frac{1}{2}})\) giving the difference equation
Rearranging the equation we get
for \(i=1,...9\) where \(r=\frac{k}{h^2}\).
This gives the formula for the unknown term \(w_{ij+1}\) at the \((ij+1)\) mesh points in terms of \(x[i]\) along the jth time row.
Hence we can calculate the unknown pivotal values of \(w\) along the first row of \(j=1\) in terms of the known boundary conditions.
This can be written in matrix form
for which \(A\) is a \(9\times9\) matrix:
It is assumed that the boundary values \(w_{0j}\) and \(w_{10j}\) are known for \(j=1,2,...\), and \(w_{i0}\) for \(i=0,...,10\) is the initial condition.
The Figure below shows the values of the \(9\times 9\) matrix in colour plot form for \(r=\frac{k}{h^2}\).
A=np.zeros((N-1,N-1))
B=np.zeros((N-1,N-1))
for i in range (0,N-1):
A[i,i]=2+2*r
B[i,i]=2-2*r
for i in range (0,N-2):
A[i+1,i]=-r
A[i,i+1]=-r
B[i+1,i]=r
B[i,i+1]=r
Ainv=np.linalg.inv(A)
fig = plt.figure(figsize=(12,4));
plt.subplot(121)
plt.imshow(A,interpolation='none');
plt.xticks(np.arange(N-1), np.arange(1,N-0.9,1));
plt.yticks(np.arange(N-1), np.arange(1,N-0.9,1));
clb=plt.colorbar();
clb.set_label('Matrix elements values');
#clb.set_clim((-1,1));
plt.title('Matrix A r=%s'%(np.round(r,3)),fontsize=24)
plt.subplot(122)
plt.imshow(B,interpolation='none');
plt.xticks(np.arange(N-1), np.arange(1,N-0.9,1));
plt.yticks(np.arange(N-1), np.arange(1,N-0.9,1));
clb=plt.colorbar();
clb.set_label('Matrix elements values');
#clb.set_clim((-1,1));
plt.title(r'Matrix $B$ r=%s'%(np.round(r,3)),fontsize=24)
fig.tight_layout()
plt.show();
Results#
To numerically approximate the solution at \(t[1]\) the matrix equation becomes
where all the right hand side is known. To approximate solution at time \(t[2]\) we use the matrix equation
Each set of numerical solutions \(w[i,j]\) for all \(i\) at the previous time step is used to approximate the solution \(w[i,j+1]\). The left and right plot below show the numerical approximation \(w[i,j]\) of the Heat Equation using the Crank-Nicolson method for \(x[i]\) for \(i=0,...,10\) and time steps \(t[j]\) for \(j=1,...,15\).
fig = plt.figure(figsize=(12,6))
plt.subplot(121)
for j in range (0,time_steps+1):
b[0]=r*w[0,j-1]+r*w[0,j]
b[N-2]=r*w[N,j-1]+r*w[N,j]
v=np.dot(B,w[1:(N),j-1])
w[1:(N),j]=np.dot(Ainv,v+b)
plt.plot(x,w[:,j],'o:',label='t[%s]=%s'%(j,time[j]))
plt.xlabel('x')
plt.ylabel('w')
#plt.legend(loc='bottom', bbox_to_anchor=(0.5, -0.1))
plt.legend(bbox_to_anchor=(-.4, 1), loc=2, borderaxespad=0.)
plt.subplot(122)
plt.imshow(w.transpose())
plt.xticks(np.arange(len(x)), x)
plt.yticks(np.arange(len(time)), time)
plt.xlabel('x')
plt.ylabel('time')
clb=plt.colorbar()
clb.set_label('Temperature (w)')
plt.suptitle('Numerical Solution of the Heat Equation r=%s'%(np.round(r,3)),fontsize=24,y=1.08)
fig.tight_layout()
plt.show()
Local Trunction Error#
The local truncation error of the Crank-Nicoloson we approximate
with
Subbing the exact answer into the difference equation gives the truncation error
simplify
substitute into equation
By Taylors expansions we have
substitution into the expression for \(T_{ij+\frac{1}{2}}\) then gives
But \(U\) is the solution to the differential equation so
the principal part of the local truncation error is
Hence the truncation error is
Stability Analyis#
To investigating the stability of the fully implicit Crank Nicolson difference method of the Heat Equation, we will use the von Neumann method. The difference equation is:
approximating
at \((ph,(q+\frac{1}{2})k)\). Substituting \(w_{pq}=e^{i\beta x}\xi^{q}\) into the difference equation gives:
where \(r=\frac{k}{h_x^2}\). Divide across by \(e^{i\beta (p)h}\xi^{q}\) leads to
Hence
therefore the equation is unconditionally stable as \(0 < \xi \leq 1\) for all \(r\) and all \(\beta\) .
References#
[1] G D Smith Numerical Solution of Partial Differential Equations: Finite Difference Method Oxford 1992
[2] Butler, J. (2019). John S Butler Numerical Methods for Differential Equations. [online] Maths.dit.ie. Available at: http://www.maths.dit.ie/~johnbutler/Teaching_NumericalMethods.html [Accessed 14 Mar. 2019].
[3] Wikipedia contributors. (2019, February 22). Heat equation. In Wikipedia, The Free Encyclopedia. Available at: https://en.wikipedia.org/w/index.php?title=Heat_equation&oldid=884580138 [Accessed 14 Mar. 2019].