从分子动力学建模开始
原子建模基础
对于较大原子系统,从量子力学出发的计算是不太可能的。通过经典近似来描述原子系统的运动,能够捕捉到宏观系统的许多特征。 在这样的近似中,利用牛顿运动定律和对原子间作用力的恰当描述来寻找原子的运动。 这些力由给定位置的原子的势能来描述。原则上,势能函数可以从量子力学计算中确定,因为作用力和势能取决于电子的状态 --电子形成了原子间的键,是原子间作用力的来源。
然而,在经典近似中,我们将这个问题参数化:我们为相互作用构建简化模型,这些模型提供了相互作用的最重要特征。
Lennard-Jones势
此处,我们主要是用简单的模型,因为统计和热效应并没有强烈依赖系统的细节。 对于两个惰性气体原子间的相互作用力主要有两个来源:
- 由于偶极相互作用产生的吸引力:这种相互作用的势正比于\((1/r)^6\),其中\(r\)为原子间的距离。即范德瓦尔斯相互作用
- 当两个原子被推挤在一起时,由于电子轨道重叠的可能性,存在一种排斥力,这是一种量子力学效应。用\((1/r)^n\)来表示。对于氩来说,\(n=12\)是比较好的近似。
该组合模型称为Lennard-Jones势:
\[\begin{equation} U(r)=4 \epsilon\left(\left(\frac{\sigma}{r}\right)^{12}-\left(\frac{\sigma}{r}\right)^6\right) \end{equation}\]
当L-J势最小时:
\[\begin{equation} F(r)=-\frac{d}{d r} U(r)=0, \end{equation}\]
则有:
\[\begin{equation} F(r)=24 \epsilon_0\left((\sigma / r)^6-2(\sigma / r)^{12}\right)=0 \Rightarrow \frac{r}{\sigma}=2^{1 / 6} \end{equation}\]
L-J不仅可以模拟氩原子之间的相互作用,还可以作为需要仿真的基础模型。
初始条件
MD模拟从原子的初始构型开始,并确定所有原子的运动轨迹。初始条件由初始时刻\(t_0\)的所有位置\(r_i(t_0)\)和速度\(v_i(t_0)\)组成。 特别地,由于大多数势随着原子间距趋于零而迅速增加,因此不要将原子放置的太近。 因此,我们经常将原子有规律地放置在空间中,在一个晶格上,初始速度是随机的。
我们首先构造一个单胞,并将其复制到晶格的每一个位置,从而形成规则的单胞模式。 通过上述方法可以构造出晶格。(单胞可以包含一个以上的原子)。
此处,我们使用立方晶胞。对于单胞中只有一个原子且长度为\(b\)的立方单胞,我们可以将原子放置在单胞中的(0,0,0)处,然后生成原子间距为\(b\)的晶格(具体示意图参见原书Fig. 3.1)。
然而,对于L-J体系,从理论实验等研究可知,平衡晶体结构不是简立方,而是面心立方。我们模拟的系统将由\(L\times L\times L\)个原胞组成,每个原胞尺寸为\(b\times b\times b\),且有4个原子。
边界条件
为了利用较小的模型得到更为宏观系统的信息,我们必须采用周期性边界条件。
对运动方程进行积分
我们通过求解运动方程来确定系统的行为。对于分子动力学模拟,通常使用Celocity-Verlet算法。 这与前向欧拉法近似,但非常适用于保守力。 速度在\(t\)时刻和中间时刻\(t+\Delta t/2\)计算,其中\(\Delta t\)为时间步长。 而力仅在全时间步长:\(t\)、\(t+\Delta t\)以及\(t+2\Delta t\)等计算。
计算中最耗时的部分是力的计算。因此,我们希望限制我们计算力的次数,并且在我们的计算中仍然具有尽可能高的精度。Velocity-Verlet算法保证了积分算法中精度和力计算次数之间的良好折衷。
在Velocity-Verlet算法中,所有原子(i = 1,..,N)的位置\(r_i( t )\)和速度\(v_i(t)\)在时间上向前传播:
\[\begin{equation} \begin{aligned} \mathbf{v}_i(t+\Delta t / 2) & =\mathbf{v}(t)+\mathbf{F}_i(t) / m_i \Delta t / 2 \\ \mathbf{r}_i(t+\Delta t) & \left.=\mathbf{r}_{(} t\right)+\mathbf{v}_i(t+\Delta t / 2) \\ \mathbf{F}_i(t+\Delta t) & =-\nabla V\left(\mathbf{r}_i(t+\Delta t)\right) \\ \mathbf{v}_i(t+\Delta t) & =\mathbf{v}(t+\Delta t / 2)+\mathbf{F}_i(t+\Delta t) / m_i \Delta t / 2 \end{aligned} \end{equation}\]
这种方法在能量守恒方面具有很好的性质,当然,它也能很好地preserve momentum。
分子动力学模拟器的简单实现
我们需要设置原子的初始构型,对给定的若干时间步的运动进行积分,然后将结果输出为标准可视化程序可以读取的文件。
无量纲运动方程
对于伦纳德-琼斯模型,我们通常使用模型的固有长度(\(\sigma\))和能量尺度(\(\epsilon_0\))作为长度和能量的基本单位。 因此模拟矢量\(\mathbf{r}'_i\)与真实世界的\(\mathbf{r}_i\)有如下关系:
\[\begin{equation} \mathbf{r}_i=\sigma \mathbf{r}_i^{\prime} \Leftrightarrow \mathbf{r}_i^{\prime}=\mathbf{r}_i / \sigma \end{equation}\]
类似的,可以引入L-J时间,\(\tau = \sigma\sqrt{m/\epsilon}\)。其中,\(m\) 是原子质量,以及L-J温度 \(T_0 = \epsilon / k_B\)。
使用这些符号,我们可以使用无量纲位置和时间重写L-J系统的运动方程。 \(\mathbf{r}_i^{\prime}=\mathbf{r}_i/\sigma\)和\(t^{\prime}=t/\tau\):
\[\begin{equation} m \frac{d^2}{d t^2} \mathbf{r}_i=\sum_j 24 \epsilon_0\left(\left(\sigma / r_{i j}\right)^6-2\left(\sigma / r_{i j}\right)^{12}\right)\left(\mathbf{r}_{i j} / r_{i j}^2\right) \end{equation}\]
变成:
\[\begin{equation} \frac{d^2}{d\left(t^{\prime}\right)^2} \mathbf{r}_i^{\prime}=\sum_j 24\left(r_{i j}^{-6}-2 r_{i j}^{-12}\right)\left(\mathbf{r}_{i j}^{\prime} / r_{i j}^{\prime 2}\right) \end{equation}\]
对于氩气,\(\sigma\) $ = 0.3405$ \(\mu\) m,以及 \(\epsilon\) $= 1.0318^{-2} $ eV。 对于其它原子,你需要找到相应的参数值。
量 | 方程 | 转换系数 | 氩的值 |
---|---|---|---|
长度 | \(x'=x/L_0\) | \(L_0=\sigma\) | 0.3405 \(\mu m\) |
时间 | \(t'=t/\tau\) | \(\tau=\sigma \sqrt{m/\epsilon}\) | \(2.1569\times 10^3\) fs |
力 | \(F'=F/F_0\) | \(F_0=m \sigma / \tau^2=\epsilon / \sigma\) | \(3.0303 \times 10^{-1}\) |
能量 | \(E'=E/E_0\) | $E_0= $ | $ 1.0318 ^{-2} $ eV |
温度 | \(T'=T/T_0\) | $T_0 = / k_B $ | 119.74 K |
系统初始化
初始化系统:生成一组具有随机速度的原子晶格。该晶格由尺寸为\(b\) (单位为\(\sigma\))的单胞组成。 若单胞的起始为\(r_0\),则单胞中的四个原子的坐标为\(r_0\)、 \(r_0 + (b/2, b/2, 0)\)、\(r_0 + (b/2, 0, b/2)\)、 以及 \(r_0 + (0, b/2, b/2)\)。
1 | import numpy as np |
将数据写入文件的函数定义如下:
1 | import numpy as np |
从而可以得到: 1
2
3
4
5
6
7
8
9
10
11
12
13
14ITEM: TIMESTEP
0
ITEM: NUMBER OF ATOMS
500
ITEM: BOX BOUNDS pp pp pp
0.0 10.0
0.0 10.0
0.0 10.0
ITEM: ATOMS id type x y z vx vy vz
1 1 0.0 0.0 0.0 -0.37806747920980305 0.6198930431434015 -0.37486891843336545
2 1 1.0 1.0 0.0 -0.9735100703336169 0.11617643190081425 -1.7552597516341568
3 1 1.0 0.0 1.0 0.27186380165078294 -0.2889571390770045 0.7686946959643555
4 1 0.0 1.0 1.0 -0.8620072632510575 0.18294851178046748 -0.21705159596758267
...
运动方程的积分
为了获取初始状态,需要对运动方程进行积分。 首先是读取lammps文件,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56from lammpResultsRead import getSimulationSet # 自定义函数
# 获取模拟尺寸
_, bounds_list = getSimulationSet('mymdinit.lammpstrj')
[Lx,Ly,Lz] = bounds_list[:, 1] - bounds_list[:, 0]
t = 3.0; dt = 0.001; n = math.ceil(t/dt);
a = np.zeros((N, 3)) # 存储计算出的加速度
import numpy as np
def cal(a,N,r,L,v,dt):
a = np.zeros_like(a)
for i1 in range(N):
for i2 in range(i1+1, N):
dr = r[i1,:] - r[i2,:]
for k in range(3): # 周期性边界条件
while dr[k] > L[k] / 2:
dr[k] = dr[k] - L[k]
while dr[k] < -L[k] / 2:
dr[k] = dr[k] + L[k]
rr = np.dot(dr, dr)
#print(rr)
aa = -24 * (2 * (1/rr)**6 - (1/rr)**3) * dr / rr
a[i1, :] = a[i1, :] + aa[0] # from i2 on i1
a[i2, :] = a[i2, :] - aa[1] # from i1 on i2
v = v + a * dt
r = r + v * dt
# 周期性边界条件
for i1 in range(N):
for k in range(3):
while r[i1, k] > L[k]:
r[i1, k] = r[i1, k] - L[k]
while r[i1, k] < 0:
r[i1, k] = r[i1, k] + L[k]
return r, v
for i in range(n-1):
r,v = cal(a,N,r,L,v,dt)
if i%10==0:
print(i)
writelammps('mymdinit.lammpstrj',i, L[0], L[1], L[2], r, v)
在上述代码中,我们重新定义了writelammps
函数为:
1 | def writelammps(filename,timestep, Lx, Ly, Lz, r, v): |
需要注意的是上述代码速度在140步以后会发散,原作者写的matlab代码只是为了展示分子动力学程序的结构和原理,以及如何处理数据和将数据存储在文件中。如果没有加速,源代码是异常慢的。因此是没有必要完全运行该代码的。
进行了分子动力学模拟
安装Lammps
由于学习的时候,我的电脑本身就安装了Lammps,因此该部分略去。
在LAMMPS中进行仿真
1 | # 2d L-J gas |
Advanced models
Coarsening
对于上述的模型,如果仍以原子均匀分布的状态启动系统个,但初始能量较低,会发生什么呢? 此外,我们保持系统的平均动能大致恒定(这意味着我们大约保持系统内温度恒定)。
1 | units lj |
这会导致相分离:体系分离成了液体和气体相,展示一种名为“准晶分解”的现象。 该现象后续会讨论。
硅晶体的断裂
原书待续
练习
暂时不看