【Atomic Simulation Environment文档】使用ASE库进行表面吸附研究

在本教程中我们将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=symbads=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的参杂体系,因此上述代码的计算结果并不具备实际意义。

posted on 2021-01-25 14:23  小强要努力变乔  阅读(2046)  评论(0编辑  收藏  举报