3. LJ流体均方位移与扩散系数

LJ流体均方位移与扩散系数 学习卡片

任务定位与物理量定义

本例在NVE系综下运行三维LJ流体,从平衡轨迹中计算均方位移(MSD),并用Einstein关系拟合扩散系数 D。核心公式为:

MSD(t) = <|r_i(t) - r_i(0)|²>
Einstein关系(三维):D = (1/6) · lim_{t→∞} d[MSD(t)]/dt

关键约束:NVE下总能量守恒,温度由初始速度标定决定;MSD必须使用未折叠坐标(unwrapped),否则粒子跨越周期边界时位移会被错误截断。扩散系数拟合应取MSD进入线性区间的时段,避开弹道区(t ≲ 0.5)和统计噪声主导的长时尾部。

NVE模拟与MSD计算流程 初始构型 + 速度标定 T* = 0.8 / 1.0 / 1.2 NVE积分 dt* = 0.002,50000步 输出未折叠坐标 dump unwrapped,每100步 计算MSD(t) 多时间原点平均 线性拟合斜率 D = slope / 6 MSD(t) 三段特征与拟合窗口 t* MSD 弹道区 MSD ∝ t² 线性扩散区(拟合窗口) MSD ∝ t,斜率 = 6D 统计噪声区 时间原点少 斜率 = 6D 温度效应:T*↑ → 斜率↑ → D↑ T*=0.8(低D) T*=1.0(中D) T*=1.2(高D) 弹道区:t* < 0.5,粒子自由飞行 线性区:t* ∈ [1, 5],Einstein关系成立 噪声区:t* > 5,时间原点不足

LAMMPS输入脚本与关键设置

以下脚本在NVE系综下运行LJ流体,输出未折叠坐标用于MSD计算。单位采用LJ约化单位(σ=ε=m=1)。

# LJ流体NVE模拟 + MSD输出
units           lj
atom_style      atomic
boundary        p p p
region          box block 0 10 0 10 0 10
create_box      1 box
create_atoms    1 random 500 12345 box
mass            1 1.0

pair_style      lj/cut 2.5
pair_coeff      1 1 1.0 1.0 2.5

velocity        all create 1.0 4928459 dist gaussian
fix             1 all nve
timestep        0.002

thermo          500
run             2000

# 重置时间原点,输出未折叠坐标
reset_timestep  0
dump            d1 all custom 100 traj_unwrap.lammpstrj id xu yu zu
dump_modify     d1 sort id
run             50000
关键点:必须使用 xu yu zu(未折叠坐标)而非 x y z。若使用折叠坐标,粒子穿越周期边界时MSD会出现人为的平台或跳变,导致扩散系数严重低估。

MSD计算与Einstein拟合

使用Python对轨迹进行后处理,计算多时间原点平均的MSD,并在线性区间拟合斜率。

import numpy as np

def read_lammps_traj(filename):
    """读取LAMMPS自定义dump,返回坐标数组 (n_frames, n_atoms, 3)"""
    frames = []
    with open(filename) as f:
        while True:
            line = f.readline()
            if not line:
                break
            if 'ITEM: TIMESTEP' in line:
                f.readline()  # timestep
                f.readline()  # ITEM: NUMBER OF ATOMS
                n_atoms = int(f.readline())
                f.readline()  # ITEM: BOX BOUNDS
                for _ in range(3):
                    f.readline()
                f.readline()  # ITEM: ATOMS id xu yu zu
                coords = np.array([f.readline().split()[1:4] for _ in range(n_atoms)], dtype=float)
                frames.append(coords)
    return np.array(frames)

traj = read_lammps_traj('traj_unwrap.lammpstrj')
n_frames, n_atoms, _ = traj.shape
dt = 0.002 * 100  # 每100步输出一次,时间间隔

# 多时间原点平均MSD
msd = np.zeros(n_frames)
count = np.zeros(n_frames)
for lag in range(n_frames):
    disp = traj[lag:] - traj[:n_frames-lag]
    sq_disp = np.sum(disp**2, axis=2)  # (n_frames-lag, n_atoms)
    msd[lag] = np.mean(sq_disp)
    count[lag] = n_frames - lag

time = np.arange(n_frames) * dt

# 选择线性拟合窗口:t* ∈ [1, 5]
fit_mask = (time >= 1.0) & (time <= 5.0)
coeffs = np.polyfit(time[fit_mask], msd[fit_mask], 1)
slope = coeffs[0]
D = slope / 6.0

print(f"拟合斜率 = {slope:.4f}")
print(f"扩散系数 D = {D:.4f} (LJ单位)")

温度对扩散行为的影响

在三个温度下重复上述流程,观察D随T*的变化。以下为教学示例的预期趋势(非实测数据):

温度 T*MSD线性区斜率(示例)扩散系数 D(示例)物理判断
0.8~0.12~0.020低温,粒子迁移慢
1.0~0.30~0.050接近液相典型值
1.2~0.55~0.092高温,扩散加快
注意:上表为教学示例的预期量级,实际数值取决于密度、截断半径和统计时长。NVE下温度由初始速度标定决定,运行中会有涨落,应报告平均温度而非初始设定值。

结果验证与常见问题

  • MSD曲线形状检查:短时应有弹道区(MSD∝t²),中时应进入线性区(MSD∝t),长时应因统计不足而发散。若全程线性,可能时间原点平均不足或输出间隔过大。
  • 扩散系数合理性:LJ液体在T*=1.0、ρ*=0.8附近,D约在0.03–0.08(LJ单位)量级。若D接近0,检查是否误用了折叠坐标。
  • 温度一致性:用 compute temp 输出平均温度,确认与目标T*一致。NVE下温度漂移过大说明步长或截断设置有问题。
  • 拟合窗口选择:建议用MSD对t的log-log图确认线性区起点,避免将弹道区纳入拟合。