PDE Cookbook
A collection of worked examples solving classical partial differential equations with findiff. Each recipe is self-contained.
1D Forced Harmonic Oscillator
Solve
on \([0, 10]\) with \(u(0)=0,\; u(10)=1\).
import numpy as np
from findiff import Diff, Identity, PDE, BoundaryConditions
N = 300
t = np.linspace(0, 10, N)
dt = t[1] - t[0]
alpha = 1.0
omega = np.sqrt(5)
L = Diff(0, dt)**2 - alpha * Diff(0, dt) + omega**2 * Identity()
f = np.cos(2 * t)
bc = BoundaryConditions((N,))
bc[0] = 0
bc[-1] = 1
pde = PDE(L, f, bc)
u = pde.solve()
2D Laplace Equation (Steady-State Heat)
Solve \(\nabla^2 u = 0\) on a unit square with temperature fixed on all four edges:
import numpy as np
from findiff import Diff, PDE, BoundaryConditions
N = 80
x = y = np.linspace(0, 1, N)
dx, dy = x[1] - x[0], y[1] - y[0]
X, Y = np.meshgrid(x, y, indexing='ij')
L = Diff(0, dx)**2 + Diff(1, dy)**2
f = np.zeros_like(X)
bc = BoundaryConditions(X.shape)
bc[0, :] = 0 # left edge: u = 0
bc[-1, :] = 0 # right edge: u = 0
bc[:, 0] = 0 # bottom edge: u = 0
bc[:, -1] = np.sin(np.pi * x) # top edge: u = sin(pi*x)
pde = PDE(L, f, bc)
u = pde.solve()
2D Poisson Equation
Solve \(\nabla^2 u = -2\pi^2 \sin(\pi x)\sin(\pi y)\) on a unit square with \(u=0\) on all boundaries (exact solution: \(u = \sin(\pi x)\sin(\pi y)\)).
import numpy as np
from findiff import Diff, PDE, BoundaryConditions
N = 80
x = y = np.linspace(0, 1, N)
dx, dy = x[1] - x[0], y[1] - y[0]
X, Y = np.meshgrid(x, y, indexing='ij')
L = Diff(0, dx)**2 + Diff(1, dy)**2
f = -2 * np.pi**2 * np.sin(np.pi * X) * np.sin(np.pi * Y)
bc = BoundaryConditions(X.shape)
bc[0, :] = 0
bc[-1, :] = 0
bc[:, 0] = 0
bc[:, -1] = 0
pde = PDE(L, f, bc)
u = pde.solve()
# Check
u_exact = np.sin(np.pi * X) * np.sin(np.pi * Y)
print("Max error:", np.max(np.abs(u - u_exact)))
2D Heat Conduction with Mixed BCs
A plate with a temperature profile on one edge, zero heat flux across the others:
import numpy as np
from findiff import Diff, PDE, BoundaryConditions
N = 100
x = np.linspace(0, 1, N)
y = np.linspace(0, 1, N)
dx, dy = x[1] - x[0], y[1] - y[0]
X, Y = np.meshgrid(x, y, indexing='ij')
L = Diff(0, dx)**2 + Diff(1, dy)**2
f = np.zeros_like(X)
bc = BoundaryConditions(X.shape)
bc[1, :] = Diff(0, dx), 0 # Neumann: du/dx = 0 at x = 0
bc[-1, :] = 300. - 200 * Y # Dirichlet at x = 1
bc[:, 0] = 300. # Dirichlet at y = 0
bc[1:-1, -1] = Diff(1, dy), 0 # Neumann: du/dy = 0 at y = 1
pde = PDE(L, f, bc)
u = pde.solve()
1D Schrodinger Eigenvalue Problem
Find the lowest energy levels of the quantum harmonic oscillator \(-\tfrac{1}{2}\psi'' + \tfrac{1}{2}x^2\psi = E\psi\):
import numpy as np
from findiff import Diff, Identity, BoundaryConditions
N = 300
x = np.linspace(-8, 8, N)
dx = x[1] - x[0]
H = -0.5 * Diff(0, dx)**2 + 0.5 * x**2 * Identity()
bc = BoundaryConditions((N,))
bc[0] = 0
bc[-1] = 0
eigenvalues, eigenvectors = H.eigsh((N,), k=6, which='SM', bc=bc)
print("Eigenvalues:", eigenvalues)
# Expected: ≈ [0.5, 1.5, 2.5, 3.5, 4.5, 5.5]
Advection-Diffusion with Robin BC
Solve
on \([0, 1]\) with \(u(0)=1\) and a Robin condition \(u + \tfrac{D}{v} u' = 0\) at \(x=1\) (outflow condition):
import numpy as np
from findiff import Diff, PDE, BoundaryConditions
N = 200
x = np.linspace(0, 1, N)
dx = x[1] - x[0]
D = 0.01 # diffusion coefficient
v = 1.0 # advection velocity
L = D * Diff(0, dx)**2 - v * Diff(0, dx)
f = np.zeros(N)
bc = BoundaryConditions((N,))
bc[0] = 1 # Dirichlet: u(0) = 1
bc[-1] = (1, Diff(0, dx), D / v, 0) # Robin: u + (D/v)*u' = 0
pde = PDE(L, f, bc)
u = pde.solve()
Large 2D Problem with Iterative Solver
For large grids, iterative solvers with preconditioning can be faster and use less memory than the default direct solver:
import numpy as np
from findiff import Diff, PDE, BoundaryConditions
N = 300
x = y = np.linspace(0, 1, N)
dx, dy = x[1] - x[0], y[1] - y[0]
X, Y = np.meshgrid(x, y, indexing='ij')
L = Diff(0, dx)**2 + Diff(1, dy)**2
f = np.ones_like(X)
bc = BoundaryConditions(X.shape)
bc[0, :] = 0
bc[-1, :] = 0
bc[:, 0] = 0
bc[:, -1] = 0
pde = PDE(L, f, bc)
u = pde.solve(solver='cg', preconditioner='ilu', tol=1e-10)
1D Heat Equation (Time-Dependent)
Solve \(u_t = D\, u_{xx}\) on \([0, 1]\) with \(u_0 = \sin(\pi x)\) and homogeneous Dirichlet conditions:
import numpy as np
from findiff import Diff, TimeDependentPDE, BoundaryConditions
nx = 101
x = np.linspace(0, 1, nx)
dx = x[1] - x[0]
D = 0.01
L = D * Diff(0, dx)**2
u0 = np.sin(np.pi * x)
bc = BoundaryConditions((nx,))
bc[0] = 0
bc[-1] = 0
t = np.linspace(0, 1, 500)
u_final = TimeDependentPDE(L, u0, bc, t).solve()
u_exact = np.sin(np.pi * x) * np.exp(-D * np.pi**2 * t[-1])
print("Max error:", np.max(np.abs(u_final - u_exact)))
2D Diffusion (Time-Dependent)
Solve \(u_t = D\,(\nabla^2 u)\) on a unit square with zero boundary conditions:
import numpy as np
from findiff import Diff, TimeDependentPDE, BoundaryConditions
nx, ny = 51, 51
x = np.linspace(0, 1, nx)
y = np.linspace(0, 1, ny)
dx, dy = x[1] - x[0], y[1] - y[0]
X, Y = np.meshgrid(x, y, indexing='ij')
D = 0.01
L = D * (Diff(0, dx)**2 + Diff(1, dy)**2)
u0 = np.sin(np.pi * X) * np.sin(np.pi * Y)
bc = BoundaryConditions((nx, ny))
bc[0, :] = 0
bc[-1, :] = 0
bc[:, 0] = 0
bc[:, -1] = 0
t = np.linspace(0, 0.5, 200)
u_final = TimeDependentPDE(L, u0, bc, t).solve()
u_exact = (np.sin(np.pi * X) * np.sin(np.pi * Y)
* np.exp(-2 * D * np.pi**2 * t[-1]))
print("Max error:", np.max(np.abs(u_final - u_exact)))