File: dynamic_neb.py

package info (click to toggle)
python-ase 3.17.0-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 16,340 kB
  • sloc: python: 117,348; makefile: 91
file content (88 lines) | stat: -rw-r--r-- 2,328 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import numpy as np
from ase import Atoms
from ase.build import fcc111
from ase.optimize import BFGS
from ase.calculators.emt import EMT as OrigEMT
from ase.neb import NEB

# Global counter of force evaluations:
force_evaluations = [0]

class EMT(OrigEMT):
    def calculate(self, *args, **kwargs):
        force_evaluations[0] += 1
        OrigEMT.calculate(self, *args, **kwargs)


# Build Pt(111) slab with six surface atoms and add oxygen adsorbate
initial = fcc111('Pt', size=(3, 2, 3), orthogonal=True)
initial.center(axis=2, vacuum=10)
oxygen = Atoms('O')
oxygen.translate(initial[7].position + (0., 0., 3.5))
initial.extend(oxygen)

# EMT potential
calc = EMT()
initial.set_calculator(EMT())

# Optimize initial state
opt = BFGS(initial)
opt.run(fmax=0.03)

# Move oxygen adsorbate to neighboring hollow site
final = initial.copy()
final[18].x += 2.8
final[18].y += 1.8

final.set_calculator(EMT())

opt = BFGS(final)
opt.run(fmax=0.03)

# NEB with five interior images
images = [initial]
for i in range(5):
    images.append(initial.copy())
images.append(final)

fmax = 0.03  # Same for NEB and optimizer

for i in range(1, len(images)-1):
    calc = EMT()
    images[i].set_calculator(calc)

# Dynamic NEB
neb = NEB(images, fmax=fmax, dynamic_relaxation=True)
neb.interpolate()

# Optimize and check number of calculations with dynamic NEB.
# We use a hack with a global counter to count the force evaluations:
force_evaluations[0] = 0
opt = BFGS(neb)
opt.run(fmax=fmax)
ncalculations_dyn = force_evaluations[0]

# Get potential energy of transition state
Emax_dyn = np.sort([image.get_potential_energy()
                    for image in images[1:-1]])[-1]

# Default NEB
neb = NEB(images, dynamic_relaxation=False)
neb.interpolate()

# Optimize and check number of calculations for default NEB:
force_evaluations[0] = 0
opt = BFGS(neb)
opt.run(fmax=fmax)
ncalculations_default = force_evaluations[0]

# Get potential energy of transition state
Emax_def = np.sort([image.get_potential_energy()
                    for image in images[1:-1]])[-1]

# Check force calculation count for default and dynamic NEB implementations
print(ncalculations_dyn, ncalculations_default)
assert ncalculations_dyn < ncalculations_default

# Assert reaction barriers are within 1 meV of each other
assert(abs(Emax_dyn - Emax_def) < 1e-3)