You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 07:50:55