I'm working with simulating both the heat and wave equation in 2D in a Python code. When simulating the heat equation, I learned about the CFL which I used to get a numerical stable solution.
I found CFL for the 2D wave equation as well, but my solution keeps being numerically unstable no matter how I change the time increments $dt$. I'm presenting the code here:
# Libraries
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib as mpl
def f(X,Y): # f(x,y,0)
return X**2+Y**2
def g(X,Y): # g(x,y,0)
return X
def laplacian(arr, row,col, dx ):
return (arr[row + 1, col]
+ arr[row - 1, col] + arr[row, col + 1]
+ arr[row, col - 1]- 4*arr[row,col])/(dx**2)
def simulation(rect,hs,BC, c, frames, eq, eps = 1e-10):
""" Simulation of the heat equation on a
2D rectangular grid using the Finite Difference Method """
X, Y = np.meshgrid(np.linspace(rect[0],rect[1],hs[0]),
np.linspace(rect[2],rect[3],hs[1])) # 2D meshgrid
# Initial Conditions
Z_init = f(X,Y) # u(x,y,0) = f(x,y,0)
Z_dot_init = g(X,Y) # u_t(x,y,0) = g(x,y,0)
zmax = max(Z_init.max(), BC[0], BC[1], BC[2], BC[3])
zmin = min(Z_init.min(), BC[0], BC[1], BC[2], BC[3])
zs = [Z_init]
# Boundary Conditions
if eq == "Heat":
Z_init[0] = np.ones(len(Z_init[0])) * BC[0]
Z_init[-1] = np.ones(len(Z_init[-1]))* BC[1]
Z_init[:,0] = np.ones(len(Z_init[:,0]))* BC[2]
Z_init[:, -1] = np.ones(len(Z_init[:, -1])) * BC[3]
# Figure settings
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.axes.set_xlim3d(rect[0] + eps, rect[1] - eps)
ax.axes.set_ylim3d(rect[2] + eps, rect[3] - eps)
ax.axes.set_zlim3d(zmin, zmax)
plt.rcParams['mathtext.fontset'] = 'stix'
plt.rcParams['font.family'] = 'STIXGeneral'
# Plots for lines constituting the rectangle
ax.plot([rect[0], rect[1]], [rect[2], rect[2]], [BC[0], BC[0]], color='black', linewidth=2)
ax.plot([rect[1], rect[1]], [rect[2], rect[3]], [BC[3], BC[3]], color='black', linewidth=2)
ax.plot([rect[0], rect[0]], [rect[2], rect[3]], [BC[2], BC[2]], color='black', linewidth=2)
ax.plot([rect[0], rect[1]], [rect[3], rect[3]], [BC[1], BC[1]], color='black', linewidth=2)
ax.plot([rect[0], rect[0]], [rect[2], rect[2]], [BC[0], BC[2]], color='black', linewidth=2)
ax.plot([rect[1], rect[1]], [rect[2], rect[2]], [BC[0], BC[3]], color='black', linewidth=2)
ax.plot([rect[0], rect[0]], [rect[3], rect[3]], [BC[1], BC[2]], color='black', linewidth=2)
ax.plot([rect[1], rect[1]], [rect[3], rect[3]], [BC[1], BC[3]], color='black', linewidth=2)
ax.set_title('Temperature development in a \n'
'rectangular room', fontsize = 18, fontname = 'STIXGeneral')
# Infitesimals
dx = (rect[1] - rect[0]) / ((hs[0] - 1))
if eq == 'Heat':
dt = dx**2/(10*c**2)
else:
dt = dx/(10*c)
surf = ax.plot_surface(X, Y, Z_init, alpha=0.7, cmap=plt.cm.jet, vmin=zmin, vmax=zmax)
cbar = fig.colorbar(surf)
cbar.ax.set_ylabel('Temperature in ' + r'$^\circ\mathrm{C}$', rotation=270,fontsize = 14, labelpad=20)
ax.set_axis_off()
# Finite Difference Method
if eq == 'Wave':
for row in range(1, hs[0] - 1):
for col in range(1, hs[1] - 1):
Z_init[row,col] = Z_init[row,col] - 2 * laplacian(Z_dot_init,row,col, dx) + 1/2 * c ** 2 * dt ** 2 / (
dx) ** 2 * (Z_init[row+1, col] - 4 * Z_init[row, col]
+ Z_init[row-1, col] + Z_init[row, col+1]
+ Z_init[row, col-1])
zs.append(Z_init)
for iteration in range(2,frames):
surf.remove()
for row in range(1, hs[0]-1):
for col in range(1,hs[1]-1):
Z_init[row,col] += 2 * zs[iteration - 1][row, col] - zs[iteration - 2][row, col] + c ** 2 * dt ** 2 / (
dx**2) * (zs[iteration - 1][row + 1, col] - 4 * zs[iteration - 1][row, col]
+ zs[iteration - 1][row - 1, col] + zs[iteration - 1][row, col + 1]
+ zs[iteration - 1][row, col - 1])
zs.append(Z_init)
surf = ax.plot_surface(X, Y, zs[iteration-2], alpha = 0.7, cmap =plt.cm.jet, vmin = zmin, vmax = zmax)
plt.pause(dt * 10)
if eq == 'Heat':
for iteration in range(frames):
surf.remove()
for row in range(1, hs[0]-1):
for col in range(1,hs[1]-1):
Z_init[row,col] = Z_init[row,col] + c**2 * dt / (dx)**2 *(Z_init[row+1,col] - 4*Z_init[row,col]
+ Z_init[row-1,col] + Z_init[row,col+1]
+ Z_init[row,col-1])
# Plotting surface with slight pause
surf = ax.plot_surface(X, Y, Z_init, alpha = 0.7, cmap =plt.cm.jet, vmin = zmin, vmax = zmax)
plt.pause(dt)
simulation(rect = [-1,1,-1,1], hs = [100,100], BC = [0,0,0,0], c = 3, frames = 1000, eq = 'Wave')
I'm adding an image of the user instructions too:
Either way, I've tried searching for how to make my code numerically stable, but with no results. I tried changing the time steps to be even lower, I tried adding more elements into the grid, tried changing the maximum velcity, tried changing the initial conditions. I even tried removing the boundary conditions to only apply for the heat equation.
If you have any ideas on how to help me solve this, it'd be greatly appreciated.