Библиотека ASE


Библиотека ASE — это python-библиотека для проведения атомных манипуляций и вычислений. В данной статье мы будем создавать наночастицы с помощью этой библиотеки.

Установка


Устанавливается ASE стандартно через pip: pip install ase.

Объект Atoms


Основной объект, с которым можно проводить манипуляции — это объект Atoms. Он может хранить в себе различные структуры. Давайте попробуем создать молекулу угарного газа:

from ase import Atoms
from ase.visualize import view
d = 1.1
co = Atoms('CO', positions=[(0, 0, 0), (0, 0, d)])
view(co)

В результате получим структуру следующего вида:

image

Кластеры


Создание кластера — частицы, имеющей кристаллическую решетку, — возможно с помощью модуля ase.cluster. Генерируются кластеры посредством отсечения от бесконечного кристалла кусков плоскостями отсечения. Создадим кластер меди:

from ase.cluster.cubic import FaceCenteredCubic
surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
layers = [6, 9, 5]
lc = 3.61000
atoms = FaceCenteredCubic('Cu', surfaces, layers, latticeconstant=lc)
view(atoms)

где surfaces — плоскости отсечения, layers — число слоев.

Получаем структуру:

image

Наночастицы


И вот мы подходим к основной теме статьи. Наночастицы — это частицы порядка нанометровых размеров в диаметре (до сотен нанометров). Наиболее распространены сферические наночастицы — их мы и будем генерировать. Как это сделать? Давайте будем создавать большой кластер, а затем вырезать из него сферу — по удаленности координаты атома от центра. Для этого сперва необходимо центрировать все координаты:

surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
layers = [50, 50, 50]
lc = 3.61000
atomsCu = FaceCenteredCubic('Cu', surfaces, layers, latticeconstant=lc)

possCu = atomsCu.get_positions()
cCu = atomsCu.get_center_of_mass()

new_possCu = []
for el in possCu:
    new_possCu.append(el - cCu)
new_possCu = np.array(new_possCu)

Удаленность центрированного атома от центра рассчитывается просто:

def get_radius_centered(pos):
    return (pos[0]**2 + pos[1]**2 + pos[2]**2)**0.5

Теперь пишем функцию для вырезания из большого кластера нашей сферической частицы (точнее, их позиции):

def cut_atoms(poss, r):
    poss_cut = []
    for pos in poss:
        if get_radius_centered(pos) < r:
            poss_cut.append(pos)
    poss_cut = np.array(poss_cut)
    return poss_cut

Осталось применить это на практике:

r = 5
possCu = utils.cut_atoms(new_possCu, r)
atoms = Atoms('Cu'+str(len(possCu)), positions=possCu)
view(atoms)

Получаем следующее изображение наночастицы меди с радиусом 5 ангстрем (0.5 нм):

image

Core-shell структуры


На практике зачастую интересны core-shell структуры — с ядром одного типа атомов и оболочкой — другого. Например, в реальной жизни core-shell частицы CuAg (с ядром Cu) могут обладать сравнимой с Ag каталитической активностью, но быть при этом дешевле. Научимся генерировать такие структуры.

Будем придерживаться следующей стратегии: вырежем из частицы дырку и заполним эту дырку частицей с другого типа атомами. Для этого нужна функция, вырезающая дырку:

def cut_atoms_inside(poss, r):
    poss_cut = []
    for pos in poss:
        if get_radius_centered(pos) > r:
            poss_cut.append(pos)
    poss_cut = np.array(poss_cut)
    return poss_cut

Создадим кластер серебра с центрированными координатами атомов:

lc_ag = 4.09
atomsAg = FaceCenteredCubic('Ag', surfaces, layers, latticeconstant=lc_ag)

possAg = atomsAg.get_positions()
cAg = atomsAg.get_center_of_mass()

new_possAg = []
for el in possAg:
    new_possAg.append(el - cAg)
new_possAg = np.array(new_possAg)

и создадим core-shell частицу:

r1 = 7
r2 = 5
possAg = utils.cut_atoms(new_possAg, r1)
atoms = Atoms('Ag'+str(len(possAg)), positions=possAg)

possAg = utils.cut_atoms_inside(possAg, r2)
possCu = utils.cut_atoms(new_possCu, r2)
poss = np.vstack([possCu, possAg])

atoms = Atoms('Cu'+str(len(possCu))+'Ag'+str(len(possAg)), positions=poss)
view(atoms)

Визуализация показывает, что мы добились того, чего хотели:

image
Теперь можем записать координаты в файл:

atoms.write('Cu'+str(r1)+'Ag'+str(r1-r2)+'.xyz')

Релаксация


И, наконец, пример манипуляций, ради которых создавалась библиотека. Отрелаксируем core-shell частицу, т.е. приведем к состоянию с минимальной потенциальной энергией. Задаем калькулятор:

from ase.calculators.emt import EMT
atoms = Atoms(atoms, calculator=EMT())

и запускаем релаксацию:

dyn = BFGS(atoms, trajectory='atoms.traj')
dyn.run(fmax=0.05)

Конечное состояние, как видим, отличается от идеальной структуры выше:

image

Код доступен на гитхабе.