在本教程中我们将C、N、O三种原子分别吸附在具有1层,2层和3层的7种不同FCC金属(111)晶面上,然后使用数据库文件储存结果。
大块晶体(Bulk)
首先,我们使用EMT势,分别计算7种元素的体相FCC晶格常数。
from ase.build import bulk
from ase.calculators.emt import EMT
from ase.eos import calculate_eos
from ase.db import connect
db = connect('bulk.db')
for symb in ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au']:
atoms = bulk(symb, 'fcc')
# 寻找能量最小时的晶格常数
atoms.calc = EMT()
eos = calculate_eos(atoms)
v, e, B = eos.fit()
# 在最优体系尽心计算,写入数据库中
atoms.cell *= (v / atoms.get_volume()) ** (1 / 3)
atoms.get_potential_energy()
db.write(atoms, bm=B)
运行bulk.py脚本,得到如下结果:
$ python3 bulk.py
$ ase db bulk.db -c +bm # show also the bulk-modulus column
id|age|formula|calculator|energy| fmax|pbc|volume|charge| mass| bm
1|10s|Al |emt |-0.005|0.000|TTT|15.932| 0.000| 26.982|0.249
2| 9s|Ni |emt |-0.013|0.000|TTT|10.601| 0.000| 58.693|1.105
3| 9s|Cu |emt |-0.007|0.000|TTT|11.565| 0.000| 63.546|0.839
4| 9s|Pd |emt |-0.000|0.000|TTT|14.588| 0.000|106.420|1.118
5| 9s|Ag |emt |-0.000|0.000|TTT|16.775| 0.000|107.868|0.625
6| 9s|Pt |emt |-0.000|0.000|TTT|15.080| 0.000|195.084|1.736
7| 9s|Au |emt |-0.000|0.000|TTT|16.684| 0.000|196.967|1.085
Rows: 7
Keys: bm
$ ase gui bulk.db
bulk.db是一个保存在一个简单文件中的SQLite3数据库
$ file bulk.db
bulk.db: SQLite 3.x database
如果你想要看到文件中的内容,你可以将数据库文件转换为json文件,然后使用文本编辑器将其打开:
$ ase db bulk.db --insert-into bulk.json
Added 0 key-value pairs (0 pairs updated)
Inserted 7 rows
或者,你可以像下面一样查看某一行的内容:
$ ase db bulk.db Cu -j
{"1": {
"calculator": "emt",
"energy": -0.007036492048371201,
"forces": [[0.0, 0.0, 0.0]],
"key_value_pairs": {"bm": 0.8392875566787444},
...
...
}
json文件格式是人类可以直接阅读的格式,但其与SQLite3文件相比,处理效率要低得多;
吸附体系(adsorbates)
现在我们进行吸附计算(运行ads.py脚本)
from ase.calculators.emt import EMT
from ase.db import connect
from ase.build import fcc111, add_adsorbate
from ase.constraints import FixAtoms
from ase.optimize import BFG5
db1 = connect('bulk.db')
db2 = connect('ads.db')
def run(symb, a, n, ads):
"""
symb:吸附表面元素种类(字符)
a :以晶胞为单位指定系统的大小(元组)
n :晶面常数(取1,2,3)
ads :吸附质元素种类(字符)
"""
atoms = fcc111(symb, (1, 1, n), a=a)
add_adsorbate(atoms, ads, height=1.0, position='fcc')
# 将除吸附质以外的所有原子约束住
fixed = list(range(len(atoms) - 1))
atoms.constraints = [FixAtoms(indices=fixed)]
atoms.calc = EMT()
opt = BFG5(atoms, logfile=None)
opt.run(fmax=0.01)
return atoms
for row in db1.select():
a = row.cell[0, 1] * 2
symb = row.symbols[0]
for n in [1, 2, 3]:
for ads in 'CNO':
atoms = run(symb, a, n, ads)
db2.write(atoms, layers=n, surf=symb, ads=ads)
我们现在拥有了一个新的数据库文件,包括63行:
$ ase db ads.db -n
63 rows
如果使用EMT方法,这63次计算只需要很小一段时间。假设你想要在超算上使用DFT方法进行计算,在这种情况下你也许希望在计算机上的不同jobs上运行多个计算。另外,有些工作可能会因为超时而无法完成。在这种情况下,最好对脚本进行一些修改,我们在内循环中添加一行:
for row in db1.select():
a = row.cell[0, 1] * 2
symb = row.symbols[0]
for n in [1, 2, 3]:
for ads in 'CNO':
id = db2.reserve(layers=n, surf=symb, ads=ads)
if id is not None:
atoms = run(symb, a, n, ads)
db2.write(atoms, layers=n, surf=symb, ads=ads)
del db2[id]
上面提到的reserve()方法会检测一行里面是否包含关键词layer=n,surf=symb和ads=ads。如果包含,计算会被跳过;如果不包含,然后包含这些关键词的空行会被写入到文件中,计算开始。计算完成后,包含计算结果的内容写入,空行被删除。修改后的脚本可以在多个并行运行的作业中运行,并且不会进行两次计算。
万一计算崩溃,你可以看到数据库中被保留下来的空行:
$ ase db ads.db natoms=0 -c ++
id|age|user |formula|pbc|charge| mass|ads|layers|surf
17|31s|jensj| |FFF| 0.000|0.000| N| 1| Cu
Rows: 1
Keys: ads, layers, surf
删除这些内容,解决问题,然后再次运行脚本:
$ ase db ads.db natoms=0 --delete
Delete 1 row? (yes/No): yes
Deleted 1 row
$ python ads.py # or sbatch ...
$ ase db ads.db natoms=0
Rows: 0
参考能量
接着,让我们计算清洁吸附表面和孤立吸附质的能量:
from ase import Atoms
from ase.calculators.emt import EMT
from ase.db import connect
from ase.build import fcc111
db1 = connect('bulk.db')
db2 = connect('ads.db')
def run(symb, a, n):
atoms = fcc111(symb, (1, 1, n), a=a)
atoms.calc = EMT()
atoms.get_forces()
return atoms
# 清洁表面体系能量计算
for row in db1.select():
a = row.cell[0, 1] * 2
symb = row.symbols[0]
for n in [1, 2, 3]:
id = db2.reserve(layers=n, surf=symb, ads='clean')
if id is not None:
atoms = run(symb, a, n)
db2.write(atoms, id=id, layers=n, surf=symb, ads='clean')
# 孤立原子
for ads in 'CNO':
a = Atoms(ads)
a.calc = EMT()
a.get_potential_energy()
db2.write(a)
假设我们想要上述24种参比能量(包括清洁表面X21和孤立吸附质X3)保存在refs.db文件中,而不是较大的ads.db文件。我们可以改变上面的refs.db脚本,重新计算。我们也可以使用ase db工具操作得到的文件,首先,移动清洁表面体系:
$ ase db ads.db ads=clean --insert-into refs.db
Added 0 key-value pairs (0 pairs updated)
Inserted 21 rows
$ ase db ads.db ads=clean --delete --yes
Deleted 21 rows
然后,是三个孤立原子(pdc=FFF,无周期性)
$ ase db ads.db pbc=FFF --insert-into refs.db
Added 0 key-value pairs (0 pairs updated)
Inserted 3 rows
$ ase db ads.db pbc=FFF --delete --yes
Deleted 3 rows
$ ase db ads.db -n
63 rows
$ ase db refs.db -n
24 rows
分析(Analysis)
现在我们拥有了计算吸附能和吸附高度所需要的数据:
for ase.db import connect
refs = connect('refs.db')
db = connect('ads.db')
for row in db.select():
ea = (row.energy -
refs.get(formula=row.ads).energy -
refs.get(layers=row.layers, surf=row.surf).energy)
h = row.position[-1, 2] - row.position[-2, 2]
db.update(row.id, height=h, ea=ea)
在这里我们展示三层Pt金属FCC(111)晶面上吸附能的数据:
$ python3 ea.py
$ ase db ads.db Pt,layers=3 -c formula,ea,height
formula| ea|height
Pt3C |-3.715| 1.504
Pt3N |-5.419| 1.534
Pt3O |-4.724| 1.706
Rows: 3
Keys: ads, ea, height, layers, surf
Note:
EMT方法可以很好地描述对Ni,Cu,Pd,Ag,Pt,Au和Al纯金属晶体,但不适用于C、N或O的参杂体系,因此上述代码的计算结果并不具备实际意义。