Path: blob/main/latex-templates/templates/numerical-methods/pde_solver.tex
51 views
unlisted
\documentclass[a4paper, 11pt]{report}1\usepackage[utf8]{inputenc}2\usepackage[T1]{fontenc}3\usepackage{amsmath, amssymb}4\usepackage{graphicx}5\usepackage{siunitx}6\usepackage{booktabs}7\usepackage{xcolor}8\usepackage[makestderr]{pythontex}910\definecolor{heat}{RGB}{231, 76, 60}11\definecolor{wave}{RGB}{46, 204, 113}12\definecolor{stable}{RGB}{52, 152, 219}13\definecolor{unstable}{RGB}{241, 196, 15}1415\title{Partial Differential Equations:\\16Finite Difference Methods and Stability Analysis}17\author{Department of Computational Mathematics\\Technical Report NM-2024-002}18\date{\today}1920\begin{document}21\maketitle2223\begin{abstract}24This report presents finite difference methods for solving partial differential equations (PDEs). We implement explicit and implicit schemes for the heat equation and wave equation, analyze stability using von Neumann analysis, and demonstrate the CFL condition. Numerical examples illustrate the importance of stability constraints in time-stepping methods.25\end{abstract}2627\tableofcontents2829\chapter{Introduction}3031Partial differential equations describe physical phenomena involving multiple independent variables. We focus on two canonical PDEs:32\begin{itemize}33\item \textbf{Heat equation} (parabolic): $\frac{\partial u}{\partial t} = \alpha \frac{\partial^2 u}{\partial x^2}$34\item \textbf{Wave equation} (hyperbolic): $\frac{\partial^2 u}{\partial t^2} = c^2 \frac{\partial^2 u}{\partial x^2}$35\end{itemize}3637\section{Finite Difference Approximations}38\begin{align}39\frac{\partial u}{\partial t} &\approx \frac{u_i^{n+1} - u_i^n}{\Delta t} \\40\frac{\partial^2 u}{\partial x^2} &\approx \frac{u_{i+1}^n - 2u_i^n + u_{i-1}^n}{(\Delta x)^2}41\end{align}4243\chapter{Heat Equation}4445\section{Explicit (FTCS) Scheme}46Forward Time, Centered Space:47\begin{equation}48u_i^{n+1} = u_i^n + r(u_{i+1}^n - 2u_i^n + u_{i-1}^n), \quad r = \frac{\alpha \Delta t}{(\Delta x)^2}49\end{equation}5051Stability requires: $r \leq \frac{1}{2}$5253\begin{pycode}54import numpy as np55import matplotlib.pyplot as plt56from mpl_toolkits.mplot3d import Axes3D57from matplotlib import cm58plt.rc('text', usetex=True)59plt.rc('font', family='serif')6061np.random.seed(42)6263def heat_explicit(u0, alpha, dx, dt, num_steps):64r = alpha * dt / dx**265u = u0.copy()66history = [u.copy()]6768for _ in range(num_steps):69u_new = u.copy()70u_new[1:-1] = u[1:-1] + r * (u[2:] - 2*u[1:-1] + u[:-2])71u = u_new72history.append(u.copy())7374return np.array(history), r7576def heat_implicit(u0, alpha, dx, dt, num_steps):77r = alpha * dt / dx**278n = len(u0)79u = u0.copy()80history = [u.copy()]8182# Tridiagonal matrix83A = np.diag([1 + 2*r] * (n-2)) + np.diag([-r] * (n-3), 1) + np.diag([-r] * (n-3), -1)8485for _ in range(num_steps):86b = u[1:-1].copy()87b[0] += r * u[0]88b[-1] += r * u[-1]89u[1:-1] = np.linalg.solve(A, b)90history.append(u.copy())9192return np.array(history), r9394def heat_crank_nicolson(u0, alpha, dx, dt, num_steps):95r = alpha * dt / (2 * dx**2)96n = len(u0)97u = u0.copy()98history = [u.copy()]99100# Matrices for CN scheme101A = np.diag([1 + 2*r] * (n-2)) + np.diag([-r] * (n-3), 1) + np.diag([-r] * (n-3), -1)102B = np.diag([1 - 2*r] * (n-2)) + np.diag([r] * (n-3), 1) + np.diag([r] * (n-3), -1)103104for _ in range(num_steps):105b = B @ u[1:-1]106b[0] += r * (u[0] + u[0])107b[-1] += r * (u[-1] + u[-1])108u[1:-1] = np.linalg.solve(A, b)109history.append(u.copy())110111return np.array(history), r112\end{pycode}113114\section{Numerical Results}115116\begin{pycode}117# Setup118L = 1.0119nx = 51120dx = L / (nx - 1)121x = np.linspace(0, L, nx)122alpha = 0.01123124# Initial condition: sine wave125u0 = np.sin(np.pi * x)126u0[0] = 0127u0[-1] = 0128129# Stable explicit130dt_stable = 0.4 * dx**2 / alpha131num_steps = 200132history_stable, r_stable = heat_explicit(u0, alpha, dx, dt_stable, num_steps)133134# Unstable explicit135dt_unstable = 0.6 * dx**2 / alpha136history_unstable, r_unstable = heat_explicit(u0, alpha, dx, dt_unstable, min(num_steps, 50))137138# Implicit (unconditionally stable)139dt_implicit = 2 * dx**2 / alpha140history_implicit, r_implicit = heat_implicit(u0, alpha, dx, dt_implicit, num_steps)141142fig, axes = plt.subplots(2, 2, figsize=(12, 10))143144# Stable explicit evolution145ax = axes[0, 0]146times_plot = [0, 50, 100, 150, 200]147for t in times_plot:148if t < len(history_stable):149ax.plot(x, history_stable[t], label=f'$t = {t*dt_stable:.3f}$')150ax.set_xlabel('$x$')151ax.set_ylabel('$u(x, t)$')152ax.set_title(f'Explicit (Stable): $r = {r_stable:.3f}$')153ax.legend()154ax.grid(True, alpha=0.3)155156# Unstable explicit157ax = axes[0, 1]158for i, t in enumerate([0, 10, 20, 30, 40]):159if t < len(history_unstable):160ax.plot(x, history_unstable[t], label=f'$t = {t*dt_unstable:.3f}$')161ax.set_xlabel('$x$')162ax.set_ylabel('$u(x, t)$')163ax.set_title(f'Explicit (Unstable): $r = {r_unstable:.3f}$')164ax.legend()165ax.grid(True, alpha=0.3)166167# Implicit evolution168ax = axes[1, 0]169for t in times_plot:170if t < len(history_implicit):171ax.plot(x, history_implicit[t], label=f'$t = {t*dt_implicit:.3f}$')172ax.set_xlabel('$x$')173ax.set_ylabel('$u(x, t)$')174ax.set_title(f'Implicit: $r = {r_implicit:.3f}$')175ax.legend()176ax.grid(True, alpha=0.3)177178# Comparison at final time179ax = axes[1, 1]180t_final = 0.1181n_stable = int(t_final / dt_stable)182n_implicit = int(t_final / dt_implicit)183analytical = np.exp(-np.pi**2 * alpha * t_final) * np.sin(np.pi * x)184185ax.plot(x, history_stable[min(n_stable, len(history_stable)-1)], 'b-', label='Explicit')186ax.plot(x, history_implicit[min(n_implicit, len(history_implicit)-1)], 'g--', label='Implicit')187ax.plot(x, analytical, 'r:', linewidth=2, label='Analytical')188ax.set_xlabel('$x$')189ax.set_ylabel('$u(x, t)$')190ax.set_title(f'Comparison at $t = {t_final}$')191ax.legend()192ax.grid(True, alpha=0.3)193194plt.tight_layout()195plt.savefig('heat_equation.pdf', dpi=150, bbox_inches='tight')196plt.close()197\end{pycode}198199\begin{figure}[htbp]200\centering201\includegraphics[width=0.95\textwidth]{heat_equation.pdf}202\caption{Heat equation solutions: (a) stable explicit, (b) unstable explicit showing oscillations, (c) implicit scheme, (d) comparison with analytical solution.}203\end{figure}204205\chapter{Wave Equation}206207\section{Explicit Scheme}208\begin{equation}209u_i^{n+1} = 2u_i^n - u_i^{n-1} + s^2(u_{i+1}^n - 2u_i^n + u_{i-1}^n), \quad s = \frac{c \Delta t}{\Delta x}210\end{equation}211212CFL condition: $s = \frac{c \Delta t}{\Delta x} \leq 1$213214\begin{pycode}215def wave_explicit(u0, v0, c, dx, dt, num_steps):216s = c * dt / dx217n = len(u0)218219u_prev = u0.copy()220# First time step using initial velocity221u_curr = u0.copy()222u_curr[1:-1] = (u0[1:-1] + dt * v0[1:-1] +2230.5 * s**2 * (u0[2:] - 2*u0[1:-1] + u0[:-2]))224225history = [u_prev.copy(), u_curr.copy()]226227for _ in range(num_steps - 1):228u_next = np.zeros(n)229u_next[1:-1] = (2*u_curr[1:-1] - u_prev[1:-1] +230s**2 * (u_curr[2:] - 2*u_curr[1:-1] + u_curr[:-2]))231u_prev = u_curr.copy()232u_curr = u_next.copy()233history.append(u_curr.copy())234235return np.array(history), s236237# Wave equation setup238c = 1.0239nx = 101240L = 2.0241dx = L / (nx - 1)242x = np.linspace(0, L, nx)243244# Initial condition: Gaussian pulse245u0_wave = np.exp(-100 * (x - 0.5)**2)246u0_wave[0] = 0247u0_wave[-1] = 0248v0_wave = np.zeros(nx)249250# Stable CFL251dt_cfl_stable = 0.8 * dx / c252num_steps_wave = 300253history_wave_stable, s_stable = wave_explicit(u0_wave, v0_wave, c, dx, dt_cfl_stable, num_steps_wave)254255# Unstable CFL256dt_cfl_unstable = 1.2 * dx / c257history_wave_unstable, s_unstable = wave_explicit(u0_wave, v0_wave, c, dx, dt_cfl_unstable, min(num_steps_wave, 100))258259fig, axes = plt.subplots(2, 2, figsize=(12, 10))260261# Stable wave propagation262ax = axes[0, 0]263times_wave = [0, 75, 150, 225, 299]264for t in times_wave:265if t < len(history_wave_stable):266ax.plot(x, history_wave_stable[t], label=f'$t = {t*dt_cfl_stable:.3f}$')267ax.set_xlabel('$x$')268ax.set_ylabel('$u(x, t)$')269ax.set_title(f'Wave Equation (Stable): $s = {s_stable:.2f}$')270ax.legend()271ax.grid(True, alpha=0.3)272273# Unstable wave274ax = axes[0, 1]275for t in [0, 20, 40, 60, 80]:276if t < len(history_wave_unstable):277ax.plot(x, history_wave_unstable[t], label=f'$t = {t*dt_cfl_unstable:.3f}$')278ax.set_xlabel('$x$')279ax.set_ylabel('$u(x, t)$')280ax.set_title(f'Wave Equation (Unstable): $s = {s_unstable:.2f}$')281ax.legend()282ax.grid(True, alpha=0.3)283284# Space-time plot (stable)285ax = axes[1, 0]286t_arr = np.arange(len(history_wave_stable)) * dt_cfl_stable287X, T = np.meshgrid(x, t_arr[:200])288im = ax.pcolormesh(X, T, history_wave_stable[:200], cmap='RdBu', shading='auto')289ax.set_xlabel('$x$')290ax.set_ylabel('$t$')291ax.set_title('Space-Time Evolution')292plt.colorbar(im, ax=ax, label='$u(x, t)$')293294# Energy conservation295ax = axes[1, 1]296energy = []297for i in range(len(history_wave_stable) - 1):298u = history_wave_stable[i]299u_next = history_wave_stable[i + 1]300kinetic = 0.5 * np.sum(((u_next[1:-1] - u[1:-1]) / dt_cfl_stable)**2) * dx301potential = 0.5 * c**2 * np.sum(((u[2:] - u[:-2]) / (2*dx))**2) * dx302energy.append(kinetic + potential)303304ax.plot(np.arange(len(energy)) * dt_cfl_stable, energy, 'b-')305ax.set_xlabel('Time $t$')306ax.set_ylabel('Total Energy')307ax.set_title('Energy Conservation')308ax.grid(True, alpha=0.3)309310plt.tight_layout()311plt.savefig('wave_equation.pdf', dpi=150, bbox_inches='tight')312plt.close()313\end{pycode}314315\begin{figure}[htbp]316\centering317\includegraphics[width=0.95\textwidth]{wave_equation.pdf}318\caption{Wave equation: (a) stable propagation, (b) unstable CFL violation, (c) space-time diagram, (d) energy conservation.}319\end{figure}320321\chapter{Stability Analysis}322323\section{Von Neumann Analysis}324Assume solution of form $u_j^n = G^n e^{ik j \Delta x}$325326\textbf{Heat equation (explicit)}:327\begin{equation}328G = 1 - 4r\sin^2\left(\frac{k\Delta x}{2}\right)329\end{equation}330Stability: $|G| \leq 1 \Rightarrow r \leq \frac{1}{2}$331332\textbf{Wave equation}:333\begin{equation}334G = 1 - 2s^2\sin^2\left(\frac{k\Delta x}{2}\right) \pm is\sin(k\Delta x)\sqrt{1 - s^2\sin^2\left(\frac{k\Delta x}{2}\right)}335\end{equation}336Stability: $|G| = 1$ when $s \leq 1$337338\begin{pycode}339fig, axes = plt.subplots(1, 2, figsize=(12, 4))340341# Heat equation amplification factor342ax = axes[0]343k_dx = np.linspace(0, np.pi, 100)344for r in [0.25, 0.5, 0.6, 0.75]:345G = 1 - 4*r*np.sin(k_dx/2)**2346ax.plot(k_dx, np.abs(G), label=f'$r = {r}$')347ax.axhline(1, color='black', linestyle='--', alpha=0.5)348ax.set_xlabel('$k \\Delta x$')349ax.set_ylabel('$|G|$')350ax.set_title('Heat Equation Amplification Factor')351ax.legend()352ax.grid(True, alpha=0.3)353ax.set_ylim(-0.5, 2)354355# Wave equation amplification factor356ax = axes[1]357for s in [0.5, 0.8, 1.0, 1.2]:358sin2 = np.sin(k_dx/2)**2359# For wave equation |G| = 1 when stable360G_mag = np.sqrt((1 - 2*s**2*sin2)**2 + s**2*np.sin(k_dx)**2*(1 - s**2*sin2))361ax.plot(k_dx, G_mag, label=f'$s = {s}$')362ax.axhline(1, color='black', linestyle='--', alpha=0.5)363ax.set_xlabel('$k \\Delta x$')364ax.set_ylabel('$|G|$')365ax.set_title('Wave Equation Amplification Factor')366ax.legend()367ax.grid(True, alpha=0.3)368369plt.tight_layout()370plt.savefig('stability_analysis.pdf', dpi=150, bbox_inches='tight')371plt.close()372\end{pycode}373374\begin{figure}[htbp]375\centering376\includegraphics[width=0.95\textwidth]{stability_analysis.pdf}377\caption{Von Neumann stability analysis: amplification factors for heat (left) and wave (right) equations.}378\end{figure}379380\chapter{Numerical Results}381382\begin{pycode}383stability_data = [384('Heat Explicit', '$r \\leq 0.5$', 'Conditional'),385('Heat Implicit', 'None', 'Unconditional'),386('Heat Crank-Nicolson', 'None', 'Unconditional'),387('Wave Explicit', '$s \\leq 1$', 'Conditional (CFL)'),388]389\end{pycode}390391\begin{table}[htbp]392\centering393\caption{Stability conditions for finite difference schemes}394\begin{tabular}{@{}lll@{}}395\toprule396Scheme & Stability Condition & Type \\397\midrule398\py{stability_data[0][0]} & \py{stability_data[0][1]} & \py{stability_data[0][2]} \\399\py{stability_data[1][0]} & \py{stability_data[1][1]} & \py{stability_data[1][2]} \\400\py{stability_data[2][0]} & \py{stability_data[2][1]} & \py{stability_data[2][2]} \\401\py{stability_data[3][0]} & \py{stability_data[3][1]} & \py{stability_data[3][2]} \\402\bottomrule403\end{tabular}404\end{table}405406\chapter{Conclusions}407408\begin{enumerate}409\item Explicit schemes are simple but require stability constraints410\item Implicit schemes are unconditionally stable but require solving linear systems411\item CFL condition is essential for hyperbolic PDEs412\item Von Neumann analysis provides stability criteria413\item Choice of method depends on problem type and accuracy requirements414\end{enumerate}415416\end{document}417418419