数值求解波动方程 [1]
关于这个系列的专栏, 与其说是教程, 不如说更像是笔记, 因为数值求解的内容实在在太多太多了, 我也只是学到什么写什么而已, 希望可以帮到有需要的人?

波动方程
波动方程是一个用于描述像弦振动, 薄膜振动, 声波传播和地震波等现象的方程, 甚至麦克斯韦方程组也可以退化为波动方程, 从而使用波动方程去研究电磁行为.
一般地, 波动方程的形式为 , 其中
为拉普拉斯算子,
为波在介质里传播的速度, 又叫相速度. 那么求解函数
即为所求波的解. 关于波动方程的推导, 数学解等, 在《数理方程》里就有, 而且也不是这个专栏的重点. 因为波动方程是二阶微分方程, 所以在给出初值条件 (一般来说是
的
和
) 时可以求出相应的特解. 另外波动方程具有很多种变形: 边值条件, 强迫振动, 非均匀介质等.
数值求解波动方程就是在已知初值条件时使用计算机求得相应的特解, 主要实现方法有有限元法和有限差分法等. 相较于有限元法, 有限差分法更容易, 所以这系列的专栏是使用有限差分法进行波动方程的数值求解.

有限差分法
由于计算机无法处理无限的数据, 所以当进行数值模拟时必须对模型进行离散化 (避免无限小的间隔) 和截断 (避免无限大的区间).
由导数的定义 可以得出有限差分法的离散化为
, 但
暗含着条件
, 所以有
, 于是更准确的离散化应为
. 类似地可以得出二阶导数的离散化:
. 虽然可以继续推到更高阶导数的离散化, 但是波动方程只需二阶就足够了.
函数在离散化后可以使用数组储存, 假设 在数组表示为
, 其中下标为索引, 那么有
在数组中为
. 所以导数的离散化可以写为
, 二阶导数为
.
有限差分法的区间截断等价于微分问题里的边值条件, 通过控制边界的行为去避免读取区间外的数据, 从而使计算范围限制在区间内. 常用的边值条件有 Dirichlet 条件和 Neumann 条件, 假设 为边界, 那么 Dirichlet 条件表示为
, Neumann 条件表示为
, 其中
为常数.

在使用数值求解时需要注意一个严重的问题: 数值不稳定. 它会造成求解误差较大或者直接发散出现 inf, nan. 由于我自己并没有太深究这个问题, 所以并没有具体分析过数值不稳定, 但从个人经验来说, 数值不稳定与参数选择和模型的具体实现, 甚至初值有关.

实现
下面以一维波动方程为例, 一维波动方程为 , 其中函数
即为所求的解. 方程离散化得
, 移项得
. 其中两块系数可以提前计算以提升效率.
然后需要设置边值条件, 这里在左边界使用 Dirichlet 条件 , 右边界使用 Neumann 条件
, 假设离散化后的数组索引范围为
(大部分编程语言是
, 我都老 julia 人了), 那么根据边值条件有
和
(对于其他语言是
和
).
最后因为波动方程是二阶微分方程, 所以至少需要提供两个初值条件, 这里以 和
为例, 那么有
和
.

在这个特定的实现里, 当满足 时, 会造成空间数值不稳定, 值会以指数速度发散; 当
时为时间数值不稳定, 时间不稳定不会造成值发散, 但会产生类似色散的现象; 只有恰好
时才是数值稳定的. 如下图所示




因为懒得发 gayhub, 直接贴出我的 julia 实现了.


