""".. _diffusion tutorial: ============================================================================= Surface diffusion energy barriers using the Nudged Elastic Band (NEB) method ============================================================================= """ # %% import matplotlib.pyplot as plt from ase.build import add_adsorbate, fcc100 from ase.calculators.emt import EMT from ase.constraints import FixAtoms from ase.io import read from ase.mep import NEB from ase.optimize import BFGS, QuasiNewton from ase.parallel import world from ase.visualize.plot import plot_atoms # %% # First, set up the initial and final states: # 2x2-Al(001) surface with 3 layers and an # Au atom adsorbed in a hollow site: # %% slab = fcc100('Al', size=(2, 2, 3)) add_adsorbate(slab, 'Au', 1.7, 'hollow') slab.center(axis=2, vacuum=4.0) # %% # Make sure the structure is correct: # %% fig, ax = plt.subplots() plot_atoms(slab, ax) ax.set_axis_off() # %% # Fix second and third layers: # %% mask = [atom.tag > 1 for atom in slab] print(mask) slab.set_constraint(FixAtoms(mask=mask)) # %% # Use EMT potential: # %% slab.calc = EMT() # %% # Initial state: # %% qn = QuasiNewton(slab, trajectory='initial.traj') qn.run(fmax=0.05) # %% # Final state: # %% slab[-1].x += slab.get_cell()[0, 0] / 2 qn = QuasiNewton(slab, trajectory='final.traj') qn.run(fmax=0.05) # %% # .. note:: Notice how the tags are used to select the constrained atoms # # Now, do the NEB calculation: # %% initial = read('initial.traj') final = read('final.traj') constraint = FixAtoms(mask=[atom.tag > 1 for atom in initial]) images = [initial] for i in range(3): image = initial.copy() image.calc = EMT() image.set_constraint(constraint) images.append(image) images.append(final) neb = NEB(images, method='improvedtangent') neb.interpolate() qn = BFGS(neb, trajectory='neb.traj') qn.run(fmax=0.05) # %% # Visualize the results with:: fig, ax = plt.subplots() plot_atoms(final, ax) ax.set_axis_off() # %% # or from the command line: # # $ ase gui neb.traj@-5: # # and select Tools->NEB. # %% # You can also create a series of plots like above, that show the progression # of the NEB relaxation, directly at the command line:: # # $ ase nebplot --share-x --share-y neb.traj # # For more customizable analysis of the output of many NEB jobs, you can use # the :class:`ase.mep.NEBTools` class. Some examples of its use are below; the # final example was used to make the figure you see above. # %% import matplotlib.pyplot as plt from ase.io import read from ase.mep import NEBTools images = read('neb.traj@-5:') nebtools = NEBTools(images) # %% # Get the calculated barrier and the energy change of the reaction. # %% Ef, dE = nebtools.get_barrier() # %% # Get the barrier without any interpolation between highest images. # %% Ef, dE = nebtools.get_barrier(fit=False) # %% # Get the actual maximum force at this point in the simulation. # %% max_force = nebtools.get_fmax() # %% # Create a figure like that coming from ASE-GUI. # %% fig = nebtools.plot_band() # %% # Create a figure with custom parameters. # %% fig = plt.figure(figsize=(5.5, 4.0)) ax = fig.add_axes((0.15, 0.15, 0.8, 0.75)) nebtools.plot_band(ax) # %% # .. note:: # # For this reaction, the reaction coordinate is very simple: The # *x*-coordinate of the Au atom. In such cases, the NEB method is # overkill, and a simple constraint method should be used like in this # tutorial: :ref:`constraints diffusion tutorial`. # # .. seealso:: # # * :mod:`ase.mep.neb` # * :mod:`ase.constraints` # * :ref:`constraints diffusion tutorial` # * :func:`~ase.build.fcc100` # # %% # Restarting NEB # ============== # # Restart NEB from the trajectory file: # %% # read the last structures (of 5 images used in NEB) # %% images = read('neb.traj@-5:') for i in range(1, len(images) - 1): images[i].calc = EMT() neb = NEB(images, method='improvedtangent') qn = BFGS(neb, trajectory='neb_restart.traj') qn.run(fmax=0.005) # %% # Parallelizing over images with MPI # ================================== # # Instead of having one process do the calculations for all three # internal images in turn, it will be faster to have three processes do # one image each. In order to be able to run python with MPI # you need a special parallel python interpreter, for example gpaw python # (see `GPAW parallel runs `_) # and set ``parallel=True`` in the NEB calculation. # # The example below can then be run # with ``gpaw -P3 python diffusion_parallel.py``: # %% initial = read('initial.traj') final = read('final.traj') constraint = FixAtoms(mask=[atom.tag > 1 for atom in initial]) n_images = 1 # Set to number of processes you use with mpiexec images = [initial] j = world.rank * n_images // world.size # my image number for i in range(n_images): image = initial.copy() if i == j: image.calc = EMT() image.set_constraint(constraint) images.append(image) images.append(final) neb = NEB(images, parallel=True, method='improvedtangent') neb.interpolate() qn = BFGS(neb, trajectory='neb.traj') qn.run(fmax=0.05)