Show movement with "ghost" atoms

by Andrew Peterson

Displaying the pathway of a trajectory, such as a minimum energy path, on the page can be difficult. One solution is to use what I'll call "ghost atoms" -- that is, transparent atoms along the reaction pathway. It won't work for very complicated motions, but often looks nice for simple motions.

I'll show an example of how to accomplish this for a simple example of the diffusion of a Au atom across a surface, similar to the NEB example on the ASE website. The trajetory file we'll use is located here.

We use our own POV module from our pgroup repository, similar to in other tutorials, which gives a bit better control than the one built in to ASE.

Situate the image

It's easiest to set up the camera, etc., while dealing with a single image, which is accomplished in the script below.

from ase import io
from ase.all import view
from pgroup.ase.io import POV

traj = io.Trajectory('Au-diffusion.traj')

fixed = -1
atoms = traj[fixed].repeat((3, 3, 1))
view(atoms)

# 'lookat' is where the camera focuses. This was chosen by
# viewing the atoms in ase-gui and choosing a central point.
lookat = (atoms[89].position + atoms[92].position) / 2.
cameralocation = lookat + (0., -6., 12.)

pov = POV(atoms,
          pixelwidth=0.5*640,
          radii=1.0,
          look_at=lookat,
          cameralocation=cameralocation,
         )
pov.write('test.png')

Resulting in the figure test.png:

test.png

Create the image with ghost atoms

Next, we use write the pov file, but don't run povray; then we just use our script to add some lines to the povray file and raytrace.

from ase import io
from ase.all import view
from pgroup.ase.io import POV

traj = io.Trajectory('Au-diffusion.traj')
fixed = len(traj) - 1  # the image to show as solid
movingatoms = [94,]  # list of moving atoms we want to capture
repeat = (3, 3, 1)  # periodic extension

atoms = traj[fixed].repeat(repeat)
lookat = (atoms[89].position + atoms[92].position) / 2.
cameralocation = lookat + (0., -7., 12.)

pov = POV(atoms,
          pixelwidth=2.0*640,
          radii=1.0,
          look_at=lookat,
          cameralocation=cameralocation,
         )
pov.write('ghost.pov', run_povray=False)


def add_transparent_image(pov, atoms, movingatoms, transmit=0.7):
    """Adds the atoms to the pov file, but in a transparent manner.
    Only those atoms indexed in movingatoms are added. transmit is
    level of transparency."""
    for index in movingatoms:
        pos = atoms[index].position.copy()
        radius = pov._radii[index]
        color = pov._colors[index]
        line = ('sphere{<%.2f,%.2f,%.2f>, %.2f texture{pigment{rgb'
                ' <%.2f,%.2f,%.2f> transmit %.2f}}}'
                % (pos[0], pos[1], pos[2], radius,
                   color[0], color[1], color[2], transmit))
        with open(pov._filename, 'a') as f:
            f.write(line)

for count, atoms in enumerate(traj):
    print(count)
    if count == fixed:
        continue
    atoms = atoms.repeat(repeat)
    add_transparent_image(pov, atoms, movingatoms, transmit=0.7)

pov.raytrace()

ghost.png

Here is another example I made for a hypothetical proton transfer to an adsorbate:

proton-transfer.png