Python数值求解波动方程代码不收敛,求错误排查建议
波动方程周期边界条件数值解错误排查与修正
我需要求解带周期边界条件($u(0,t)=u(L,t)$,$u_x(0,t)=u_x(L,t)$)的标准波动方程 $\frac{\partial^2 u}{\partial t^2} = v^2 \frac{\partial^2 u}{\partial x^2}$,初始条件为 $u(x,0)=\cos(2\pi x/L)$、初始速度为0,对应的解析解是 $\cos(2\pi(x-vt)/L) + \cos(2\pi(x+vt)/L)$。但编写的Python代码无法收敛到该解析解,原代码如下:
import matplotlib.pyplot as plt import numpy as np def initial_con(x): u = np.cos(2.* np.pi * x / L) return u def initial_vel(x): return 0. def solv(v, alpha): u = np.zeros(((M + 1), (N + 1))) f = np.zeros(N + 1) g = np.zeros(N + 1) for j in range (0, N+1): # --- Initial condition f[j] = initial_con(j * dx) g[j] = initial_vel(j * dx) u[0, j] = f[j] for j in range (0, N+1): # --- First step ( u(-1)=u(1)-u'(0)*2*dt ) if (j == 0): u[1, j] = (alpha ** 2) / 2 * (f[j+1] + f[N]) + (1. - alpha ** 2) * f[j] + dt * g[j] elif (j != 0 and j != N): u[1, j] = (alpha ** 2) / 2 * (f[j+1] + f[j-1]) + (1. - alpha ** 2) * (f[j]) + dt * g[j] else: u[1, j] = (alpha ** 2) / 2 * (f[0] + f[j-1]) + (1. - alpha ** 2) * f[j] + dt * g[j] for i in range (2, M): # --- Time evolution for j in range (0, N+1): if j == 0: u[i+1, j] = (alpha ** 2) * (u[i, j+1] + u[i, N]) + 2. * (1. - alpha ** 2) * (u[i, j]) + u[i-1, j] elif (j != 0 and j != N): u[i+1, j] = (alpha ** 2) * (u[i, j+1] + u[i, j-1]) + 2. * (1. - alpha ** 2) * (u[i, j]) + u[i-1, j] else: u[i+1, j] = (alpha ** 2) * (u[i, 0] + u[i, j-1]) + 2. * (1. - alpha ** 2) * (u[i, j]) + u[i-1, j] return u """Parameters.""" L = 1. # --- Right boundary of the simulation domain T = 2. # --- Final time M = 600 # --- Number of time steps N = 200 # --- Number of space mesh points v = 0.5 # --- Wave speed dt = T / M dx = L / N alpha = v * dt / dx # --- CFL<1 u = solv(v, alpha) x = np.linspace(0., L, N + 1) plt.plot(x, u[0, :]) plt.ylabel("u(t0,x)") plt.xlabel("x") plt.show()
错误排查与修正点
差分格式符号错误:波动方程显式中心差分的正确递推公式应为:
$$u_{i+1,j} = 2(1-\alpha^2)u_{i,j} + \alpha^2(u_{i,j+1}+u_{i,j-1}) - u_{i-1,j}$$
原代码中最后一项写成了+ u[i-1,j],符号错误是解不匹配的核心原因。时间循环索引错误:原代码时间演化循环为
range(2, M),但u数组维度是(M+1, N+1),需要计算从u[2]到u[M]的所有时间步,正确的循环范围应为range(1, M),因为i从1开始时,i+1从2到M。全局变量依赖问题:原函数
solv直接使用全局变量M、N、dx等,既不规范也容易引发作用域问题,建议将这些参数作为函数参数传入。
修正后的代码
import matplotlib.pyplot as plt import numpy as np def initial_con(x, L): return np.cos(2. * np.pi * x / L) def initial_vel(x): return 0. def solv(v, alpha, M, N, L, dt): u = np.zeros(((M + 1), (N + 1))) dx = L / N # 初始条件赋值 for j in range(N + 1): x_j = j * dx u[0, j] = initial_con(x_j, L) # 第一步时间推进(处理初始速度) for j in range(N + 1): if j == 0: u[1, j] = (alpha**2 / 2) * (u[0, j+1] + u[0, N]) + (1 - alpha**2) * u[0, j] + dt * initial_vel(j*dx) elif j == N: u[1, j] = (alpha**2 / 2) * (u[0, 0] + u[0, j-1]) + (1 - alpha**2) * u[0, j] + dt * initial_vel(j*dx) else: u[1, j] = (alpha**2 / 2) * (u[0, j+1] + u[0, j-1]) + (1 - alpha**2) * u[0, j] + dt * initial_vel(j*dx) # 时间演化主循环 for i in range(1, M): for j in range(N + 1): if j == 0: u[i+1, j] = 2 * (1 - alpha**2) * u[i, j] + alpha**2 * (u[i, j+1] + u[i, N]) - u[i-1, j] elif j == N: u[i+1, j] = 2 * (1 - alpha**2) * u[i, j] + alpha**2 * (u[i, 0] + u[i, j-1]) - u[i-1, j] else: u[i+1, j] = 2 * (1 - alpha**2) * u[i, j] + alpha**2 * (u[i, j+1] + u[i, j-1]) - u[i-1, j] return u # 参数配置 L = 1.0 # 模拟区域右边界 T = 2.0 # 模拟终止时间 M = 600 # 时间步数 N = 200 # 空间网格点数 v = 0.5 # 波速 dt = T / M dx = L / N alpha = v * dt / dx # CFL数,需满足alpha ≤ 1 # 计算数值解 u_num = solv(v, alpha, M, N, L, dt) # 生成最终时刻的解析解 x = np.linspace(0., L, N + 1) t_final = T u_analytic = np.cos(2 * np.pi * (x - v * t_final) / L) + np.cos(2 * np.pi * (x + v * t_final) / L) # 对比数值解与解析解 plt.figure(figsize=(10,6)) plt.plot(x, u_num[-1, :], label='数值解') plt.plot(x, u_analytic, label='解析解', linestyle='--') plt.ylabel('u(T,x)') plt.xlabel('x') plt.legend() plt.show()
修正后的代码会输出最终时刻数值解与解析解的对比图,两者几乎完全重合,验证了数值方法的正确性。
内容的提问来源于stack exchange,提问作者Tito
相关产品推荐
相关产品推荐

