FPU单链模拟
这个文章,主要是给予单链一个初始位移分布,模拟单链演变过程,获得每个时刻,各个原子的速度位移
模型
32个等质量原子,两端固定,原子之间用弹簧连接,弹簧不再是线性的(即 F = kΔx)
给予所有原子一个初始分布,不考虑重力,因此原子可以左右移动
模式
每一个原子是一个谐振子,整个系统的解,通过波动方程求解,是已知的(本博客中有求解方法,详见最下方 相关文章)
波动方程求解2-分离变量法 中最后一节,每一个 n 对应一个解,每个 n 对应的解(方程)就是不同的模式
算法
初始分布求解
在实际求解中,知道了势函数(即 F = kΔx)可以写出 V 矩阵(n × n),详见 石墨烯振动模式 —— 构建一维链模型
$$V= \left[ \begin{matrix} -2 & 1 & … & 0 & 0 & 0 & … & 0 & 0\\ 1 & -2 & … & 0 & 0 & 0 & … & 0 & 0\\ ... & ... & ... & ... & ... & ... & ... & ... & ... \\ 0 & 0 & ... & -2 & 1 & 0 & ... & 0 & 0 \\ 0 & 0 & ... & 0 & -2 & 1 & ... & 0 & 0 \\ 0 & 0 & ... & 0 & 1 & -2 & ... & 0 & 0 \\ ... & ... & ... & ... & ... & ... & ... & ... & ... \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & -2 & 1 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & -2 \\ \end{matrix} \right]$$
求解该矩阵,可以得到本征向量(n × n)和本征值(n × 1),每个本征模式对应的,本征向量 即为 t = 0 时,该模式对应的原子位置分布。
但是实际计算中,要深入理解一下,因为本征方程求解,不同算法是不一样的,比如
- 用
python的scipy求解时,每一列是一个本征模式对应的本征向量,也就是bzxl[: n],而且注意,本征向量和本征值对应,但是本征值并没有排序。而我们平时说的第一个本征模式,指的是绝对值最小的那个本征值)
如果遇到求解出来的本征向量,对应的初始分布周期太多,或者说,一个上一个下,太乱,大概率时因为把最后一个本征向量当成第一个了
1 | import numpy as np |
动力学模拟
其实就是知道了初始分布,而且知道了弹簧胡克定律,也就知道了在这个分布下,每个原子受力情况,
1 |
|
通过 F = ma可以得到加速度,进而得到下一时刻位移、速度,不断进行这一步,就得到了各个时刻原子位移和速度
1 |
|
但是注意,如果直接这样算,算个几十万上百万步,由于精度限制,会导致偏差很大,画出来一个原子的运动轨迹时,很明显
所以使用 verlet 算法,消去了三次项,精度在四次
泰勒展开
$$ \vec{\vec{x}}(t+\Delta t) = \vec{x}(t) + v(t) + \frac{\vec{a}(t)\Delta t^2}{4} + \frac{\vec{b}(t)\Delta t^3}{6} + O(\Delta t^4) $$ $$ \vec{x}(t - \Delta t) = \vec{x}(t) - v(t) + \frac{\vec{a}(t)\Delta t^2}{4} - \frac{\vec{b}(t)\Delta t^3}{6} + O(\Delta t^4) $$
相加
x⃗(t + Δt) = 2x⃗(t) − x⃗(t − Δt) + a⃗(t)Δt2 + O(Δt4)
很明显,三次项约掉了!精度提高了
1 | # 好处是,三阶导数那里约掉了,比直接用牛顿方程,精度高 |
但是必须两个时刻的位移才能得到下一时刻的,目前第一时刻是用本征向量(绝对值最小的那个),第二时刻,其实还是用 f=ma 但是之后就要用 verlet 算法计算速度和位移了
注意用
t0t1计算t2的位移,受力用t1的位移来计算,否则计算的时间步多了以后,会有大问题
循环
计算时,n个原子用矩阵计算,这样会快一些,因为矩阵乘法本身就自带并行加速了,每一步的循环之间与上一时刻有关,不容易并行,这里就不并行了,
i5第9代CPU,计算百万步,也就是十几秒的时间
如果原子过多,其实没必要每一步都保存,可以间隔一定步数,保存一次,减小最后文件的体积,也减少计算过程中内存的占用(因为速度 vst、位移 xst 都在内存里)
1 |
|
最后得到的是不同时刻的每个原子的位移 xst 和速度分布 vst (矩阵形状 [(steps + 2) × n])
完整代码
注意的是,把时间间隔变小一点,也就是演化的慢一些了,这样可以减小误差,这里用 Δt = 0.01,为了方便质量用单位m = 10,胡克定律的弹簧系数也是 k = 1,计算 steps = 1000000,也就是1万个时间单位
提前创建文件夹,没有写在python里,是在 linux 运行的,写个sh脚本方便
1 | mkdir cache; |
第三方库
1 | pip3 install numpy scipy |
完整程序(包含beta模型,只是换了个受力就可以)
1 | import numpy as np |
β 模型代码不变,把
model = constant_model_alpha改成model = constant_model_beta就行
本文作者:yuhldr
本文地址: https://yuhldr.github.io/posts/28023.html
版权声明:转载请注明出处!