ASE结构等价比较实操
用Cu32比较整体旋转、平移、周期换位和原子换序,再识别单个原子的真实位移。
阅读本篇目录
同一结构转成CIF、换一组晶轴或调整原子排序后,坐标矩阵可能完全不同。判断是否还是同一个周期结构,需要比较晶胞与原子环境,而非逐行坐标相等。本例在鸢算科技集群实际运行 ASE3.29.0 的SymmetryEquivalenceCheck,以未优化Cu32说明操作;只做几何比较,不计算能量或稳定相。
1 下载并运行#
下载全部输入、原生结构、脚本、原子对数据和高清图。ASE独立Python3.11环境,NumPy/SciPy/Matplotlib,单进程单线程,无GPU需求。
python3 -m zipfile -e ase-Cu32-structure-equivalence-wave313-*.zip .
cd ase-Cu32-structure-equivalence-wave313
bash preview.sh
bash run.sh
preview/从已保存结构重画;work/重新建模并运行原生比较,随后读取.traj、生成完整周期距离表和图。目录已存在时使用新的解压目录,保留旧结果。
cat work/native/DATA.json
cat work/results/VALIDATION.json
head -n 8 work/results/pair-distances.csv
ls work/native
2 准备三个具体结构#
| 模型 | 实际操作 | 原生严格比较 |
|---|---|---|
| 原始Cu32 | 3.6 Å常规FCC胞重复2×2×2,晶胞7.2 Å | 参考 |
| 刚性变换 | 结构和晶胞一起绕z转37°,平移(0.4,0.5,0.6) Å,wrap后逆转原子顺序 | 等价True |
| 局部位移 | 原胞和其他31个原子不变,第0号原子沿x移动0.2 Å | 不等价False |
完整的建模与原生比较可以直接复制到终端:
module purge
module load ase/3.29.0
python - <<'PYCODE'
import numpy as np
from ase.build import bulk
from ase.io import write
from ase.utils.structure_comparator import SymmetryEquivalenceCheck
original = bulk('Cu', 'fcc', a=3.6, cubic=True) * (2, 2, 2)
rotated = original.copy()
rotated.rotate(37, 'z', rotate_cell=True)
rotated.translate([0.4, 0.5, 0.6])
rotated.wrap()
rotated = rotated[np.arange(32)[::-1]]
displaced = original.copy()
displaced.positions[0] += [0.2, 0, 0]
settings = dict(angle_tol=0.1, ltol=0.001, stol=0.001,
vol_tol=0.001, scale_volume=False, to_primitive=False)
# 比较器会变换内部结构表示,给它副本,保留自己的输入。
print(SymmetryEquivalenceCheck(**settings).compare(original.copy(), rotated.copy()))
print(SymmetryEquivalenceCheck(**settings).compare(original.copy(), displaced.copy()))
write('original.traj', original)
write('rotated.traj', rotated)
write('displaced.traj', displaced)
PYCODE
预期两行分别为True、False。旋转周期结构时别漏掉rotate_cell=True,否则旋转原子却保留原晶胞,不一定还是相同的周期结构。
3 比较前为什么必须先copy#
本例所用版本的原生比较步骤会变换传入结构的坐标或晶胞表示。使用compare(a.copy(), b.copy()),避免比较后再拿已经被改动的a、b去写自己的输入文件。这个细节对后续位移、缺陷和轨迹处理很重要。
angle_tol是角度容差,ltol、stol是相对几何尺度参数,不是力阈值或统一的Å误差;本例取严格的0.001并关闭体积缩放。scale_volume=False可避免将体积变化先缩放掉。to_primitive=False表示这里不先做原胞转换;实际比较不同超胞时,应先按所用工具和化学成分整理到一致的物理表示。

图中画出全部32个原子和晶胞,局部位移用红色标记。不同坐标表示不意味着新材料,单个原子的真实位移也不能因为化学式相同就忽略。
4 读取完整结构与周期距离#
包内.traj与FIELDS.npz保留完整精度的原子序号、坐标、晶胞;extxyz便于检查文本,读图和数值时优先用完整精度文件。
python - <<'PYCODE'
import numpy as np
from ase.io import read
atoms = read('work/native/rotated-permuted.traj')
print(atoms.get_chemical_formula(), atoms.cell.volume)
print(atoms.get_all_distances(mic=True).shape)
data = np.load('work/native/FIELDS.npz')
print(data.files)
PYCODE
本例完整32×32周期原子对距离在恢复原子映射后,刚性变换前后的最大差约 2.66×10⁻¹⁵ Å;单原子位移使某些距离实际改变约 0.2 Å。三种表示的体积均为 373.248 ų。因此同时检查了整个结构,未只比较一个键长。
独立说明脚本对本例立方胞及其刚性旋转枚举27个最近周期像;对任意极端倾斜晶胞,不把这个简化枚举当成通用最小镜像算法,直接使用ASE的get_all_distances(mic=True)或ase.geometry.find_mic等工具。
5 换成自己的两个结构#
python - <<'PYCODE'
from ase.io import read
from ase.utils.structure_comparator import SymmetryEquivalenceCheck
# 把路径换成自己的两个结构文件。
a = read('work/native/reference.traj')
b = read('work/native/rotated-permuted.traj')
compare = SymmetryEquivalenceCheck(scale_volume=False)
print('几何是否等价:', compare.compare(a.copy(), b.copy()))
print('原子数:', len(a), len(b))
print('化学式:', a.get_chemical_formula(), b.get_chemical_formula())
PYCODE
最后这个示例使用原生默认几何容差,比前面0.001的严格演示更宽松。实际使用时要根据热振动、优化残余和缺陷目的设置容差;不要为了得到True而一味放宽。几何等价也不自动证明相同电荷、自旋、占据、势函数或电子态,启动计算前还应核对这些设置。