Note
Go to the end to download the full example code.
A Numerical Solver for Burgers’ Equation in 2 Dimensions
An intro to our loss module’s finite difference utility demonstrating its use to create a simple numerical solver for Burgers’ equation in 2d.
Import the library
We first import our neuralop library and required dependencies.
import torch
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from neuralop.losses.finite_diff import central_diff_2d
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
Defining our problem
We aim to solve the 2D viscous Burger’s equations:
- math:
u_t + u cdot u_x + v cdot u_y = nu (u_xx + u_yy)
- math:
v_t + u cdot v_x + v cdot v_y = nu (v_xx + v_yy)
## Simulation parameters
Lx, Ly = 2.0, 2.0 # Domain lengths
nx, ny = 64, 64 # Grid resolution
T = 1 # Total simulation time
dt = 0.001 # Time step
nu = 0.04 # Viscosity
## Create grid
X = torch.linspace(0, Lx, nx, device=device).repeat(ny, 1).T
Y = torch.linspace(0, Ly, ny, device=device).repeat(nx, 1)
dx = Lx / (nx-1)
dy = Ly / (ny-1)
nt = int(T / dt)
## Initial condition
u = -torch.sin(2 * np.pi * Y).to(device)
v = torch.cos(2 * np.pi * X).to(device)
Simulate evolution using numerical solver
u_evolution = [u.clone()]
v_evolution = [v.clone()]
for _ in range(nt):
# Compute first-order derivatives
u_x, u_y = central_diff_2d(u, [dx, dy])
v_x, v_y = central_diff_2d(v, [dx, dy])
# Compute second-order derivatives
u_xx, _ = central_diff_2d(u_x, [dx, dy])
_, u_yy = central_diff_2d(u_y, [dx, dy])
v_xx, _ = central_diff_2d(v_x, [dx, dy])
_, v_yy = central_diff_2d(v_y, [dx, dy])
# Evolve in time using Euler's method
u_next = u + dt * (-u * u_x - v * u_y + nu * (u_xx + u_yy))
v_next = v + dt * (-u * v_x - v * v_y + nu * (v_xx + v_yy))
u, v = u_next.clone(), v_next.clone()
u_evolution.append(u.clone())
v_evolution.append(v.clone())
u_evolution = torch.stack(u_evolution).cpu().numpy()
v_evolution = torch.stack(v_evolution).cpu().numpy()
Animating the solution
num_frames = 100
frame_indices = torch.linspace(0, len(u_evolution) - 1, num_frames, dtype=torch.int).cpu().numpy()
u_frames = u_evolution[frame_indices]
v_frames = v_evolution[frame_indices]
fig, axs = plt.subplots(1, 2, figsize=(12, 6))
cmap_u = axs[0].imshow(u_frames[0], extent=[0, Lx, 0, Ly], origin="lower", cmap="plasma")
axs[0].set_title("Velocity u")
plt.colorbar(cmap_u, ax=axs[0], shrink=0.75)
cmap_v = axs[1].imshow(v_frames[0], extent=[0, Lx, 0, Ly], origin="lower", cmap="plasma")
axs[1].set_title("Velocity v")
plt.colorbar(cmap_v, ax=axs[1], shrink=0.75)
def update(frame):
cmap_u.set_data(u_frames[frame])
cmap_v.set_data(v_frames[frame])
axs[0].set_title(f"Velocity u (Time: {frame_indices[frame] * dt:.3f})")
axs[1].set_title(f"Velocity v (Time: {frame_indices[frame] * dt:.3f})")
axs[0].set_xticks([])
axs[0].set_yticks([])
axs[1].set_xticks([])
axs[1].set_yticks([])
return cmap_u, cmap_v
ani = animation.FuncAnimation(fig, update, frames=len(u_frames), interval=50, blit=False)
Total running time of the script: (0 minutes 16.450 seconds)