斯托默—韦尔莱法
斯托默—韦尔莱法(Störmer–Verlet method)是求解位置和动量演化的二阶时间积分法,常用于经典力学和分子动力学。对可分离 Hamiltonian ,它交替推进动量与位置,保持辛结构,并且具有时间可逆性。它通常使能量在一个小范围内振荡,而不是在每一步都精确保留原能量。
一个弹簧振子的三次更新
考虑无量纲系统 ,初始位置 、动量 。精确轨迹为 。把时间步长记为 。动量—位置—动量次序的 Verlet 法为 半步动量不是额外的物理粒子,而是组合两个半步作用时使用的中间值。
取 ,首步先得 ,所以 ,再得 。第二步为 、、。这些数值可与 比较。两次半步都要使用各自时刻的位置;将末次力误用成旧位置,会退化成另一种方法。
从力学方程得到一般格式
对于质量 、位置 、动量 ,设 ,势能 可微。方程为 。保持位置不动时,动量在时间 内精确改变 ;保持动量不动时,位置在时间 内精确改变 。组合这两个可精确完成的子问题,得到 这也是速度 Verlet 的动量写法。一般可分离动能可将中间式改成 。有速度依赖力、摩擦或约束时,需要重新推导算法,不能直接套这组三式。Hairer、Lubich 与 Wanner 的论文将其作为对称辛分裂的基本例子。[1]
二阶精度、辛性与可逆性
先在一维说明精度。令 ,并假设真解和力足够光滑。位置更新给出 与真解的 Taylor 展开到二阶一致。把 代入力的展开,得到 因此动量更新为 也与真解到二阶一致。局部状态误差为 ;在固定有限时间、解留在光滑有界区域并有通常的稳定性估计时,全局误差为 。
辛性可以由每个子步直接证明。一维动量剪切的 Jacobian 为 位置剪切的 Jacobian 为 。二者均满足 ,其中 。组合的 Jacobian 是三个因子的乘积;逐层代入等式,就仍有 。多维时用 Hessian 的对称性完成同样计算。
将 换成 ,三个子步恰好逆序抵消原来的三个子步,故 。这一可逆性指精确算术下的离散映射,不保证浮点舍入可以逐位倒退,也不意味着带耗散的真实过程应当时间可逆。
谐振子的稳定性与修正能量
消去半步变量,更新矩阵为 行列式等于一。其特征根在 时为单位圆上的不同共轭根; 则有根模大于一, 的重根也可能产生线性增长。所以辛结构并不替代步长限制。
记 、,则 。由 展开,可见交叉项相消,且 因此 精确保留。初值下它等于 ,而首步原能量为 。在 时,修正二次型正定并控制轨迹;到稳定边界它失去正定性,正好解释步长过大时的失败。
一个可自行重建的变式是常力 、。两个半步动量分别得到 ,中间位置为 ,与真解 完全一致。这来自常加速度的二次位置函数,不是一般力场中的精确性。
可运行的标量实现
代码输入势能导数、质量、初态、步长和步数,返回位置动量序列,不修改输入。每步调用两次势能导数;缓存相邻步共有的力可省去一次。若一次求力视为常数成本,时间为 ,轨迹存储为 。多粒子模拟的主要代价往往在求力本身。
import math
def verlet(grad_v, mass, q, p, h, steps):
mass, q, p, h = map(float, (mass, q, p, h))
if not all(math.isfinite(x) for x in (mass, q, p, h)) or mass <= 0 or h <= 0:
raise ValueError("finite data, positive mass and step required")
if not isinstance(steps, int) or steps < 0:
raise ValueError("nonnegative integer steps required")
states = [(q, p)]
for _ in range(steps):
force0 = -float(grad_v(q))
half = p + h * force0 / 2
q_new = q + h * half / mass
if not all(math.isfinite(x) for x in (force0, half, q_new)):
raise OverflowError("nonfinite intermediate state")
p_new = half - h * float(grad_v(q_new)) / 2
if not math.isfinite(p_new):
raise OverflowError("nonfinite momentum")
q, p = q_new, p_new
states.append((q, p))
return states
states = verlet(lambda q: q, 1, 1, 0, 0.2, 2)
print(states) # [(1.0, 0.0), (0.98, -0.198), (0.9208, -0.38808)]
print(verlet(lambda q: -2, 1, 0, 0, 0.2, 1)[-1]) # (0.04, 0.4)对非线性势能,通常需要用修正方程分析长期行为,不能把上面的精确二次不变量照搬。固定步长的相位误差也会累积:轨道保持有界,并不表示每个时刻的位置都接近无限长时间的真解。
参考资料
- ↑ Ernst Hairer, Christian Lubich, Gerhard Wanner. “Geometric numerical integration illustrated by the Störmer–Verlet method”. Acta Numerica 12 (2003), 399–450. DOI:10.1017/S0962492902000144。大学课程提供的原论文,§1.2、1.4、2.1—2.2 及 §5.1。