Neighbor lists

A neighbor list is an object containing information about which atoms are located near each other, taking the boundary conditions into account. Neighbor lists are an essential tool to obtain good performance in the Asap potentials, but are also made available to the user’s scripts to aid in analysing the simulations.

Neighbor lists take a while to create, and use a significant amount of memory (the main memory consumption in Asap is usually the neighbor list associated with the potential). For that reason, it is possible to reuse the neighbor list already used by the potential, although one should be aware that it may not be of the correct type.

Since a neighbor list contains information about where the atoms are relative to each other, it may be invalidated when atoms are moved. The neighbor list objects therefore have a method which checks if it is still valid, and updates it if not.

Types of neighbor lists

The main distinction is between half and full neighbor list. In a full neighbor list, if atoms A and B are neighbors, A appears on B’s list and B appears on A’s list. In a half neighbor list, either A appears on B’s list or B appears on A’s list, but not both. A full neighbor list is thus useful when analysing the neighborhood of a given atom, whereas a half neighbor list is useful when looping over all bonds between atoms, since each bond appears only once. In most cases, the neighbor list associated with a potential will be a half neighbor list.

Creating neighbor lists

Three kinds of neighbor lists can be created

FullNeighborList

Creates a full neighbor list, using the “cell algorithm”. This is the most useful neighbor list object for e.g. analysis.

NeighborCellLocator

Creates a half neighbor list, using the “cell algorithm”.

NeighborList

Creates a half neighbor list, using stored lists. Will not work in parallel simulations, see below.

The difference between NeighborCellLocator and NeighborList is mainly performance. NeighborCellLocator uses much less memory, and is faster to updated, but repeatedly accessing the neighbor list while only moving the atoms slightly (or not at all) may be faster with NeighborList. Also, NeighborList objects created parallel simulations will not work in parallel simulations (may crash or give wrong results).

Creating the neighbor list objects work in the same way for the three kinds:

nblist = FullNeighborList(cutoff, atoms=None, driftfactor=0.05)

where cutoff is the cutoff distance in Ångström. If atoms is specified, the list is initialized immediately. driftfactor specifies how far atoms may move (as a fraction of the cutoff distance) before internal data structures have to be updated. This influences performance, but not the behaviour.

Using the neighbor list objects

The main methods when using a neighbor list object are the check_and_update method and list indexing. check_and_update must be called every time the atoms object has been updated, before using any other method. Failing to do so will result in wrong results (do not expect an error message!)

nblist.check_and_update(atoms)

Checks if the internal data needs updating, and update if so. Also call this method to initialize the list if no atoms object was specified when creating it. This must be called every time the atoms object has changed. The atoms object may be replaced between calls, in this case the neighbor list will start working on the new atoms object.

nblist[i]

Returns the neighbors of atom number i. Returns a numpy array of integers.

nblist.get_neighbors(i, cutoff=-1.0)

Returns information about the neighbors of atom i. Three numpy arrays are returned. The first is the indices of the neighbors (the same array as returned by nblist[i]). The second is a list of relative distances to the neighbors as vectors (taking periodic boundary conditions into account). The third is the squares of the norm of these distances. If the optional cutoff parameter is specified (and positive), only neighbors closer than this distance are returned. The distance must not be larger than the cutoff specified when creating the list.

nblist.is_full_list()

Returns True if a full neighbor list, False if it is a half neighbor list.

nblist.get_wrapped_positions()

Returns the positions of all atoms wrapped into the computational box if there are periodic boundary conditions.

Reusing neighbor lists

Most ASAP Potentials use some kind of neighbor list internally, these lists can be accessed and used for analysis. However, be aware that you do not control the type (half or full) and cutoff distance of the neighbor list, you have to make do with what you get.

You obtain the neighbor list by calling the potential.get_neighborlist() method. The type of the object will depend on the type of the potential: EMT and LennardJones return a NeighborList object (a half neighbor list based on stored lists). MonteCarloEMT and BrennerPotential return a full neighbor list based on stored lists, such an object cannot be created directly.

Parallel simulations

In parallel simulations, atoms are distributed amongst the processors. Neighbor list objects will also contain atoms on neighboring processors. The index to these atoms (as returned by nblist[] or in the first array returned by get_neighbors) will be larger than the actual number of atoms on this processor, and cannot be used to access the atoms. The distances returned by get_neighbors are more useful, as they are correct for both local and non-local neighbors.

If you need to access the atomic number of the neighbors on other processors, you first need to concatenate the array of atomic numbers of the real atoms and the ghost atoms, and then access that array:

nb = nblist[n]
z = numpy.concatenate(atoms.get_atomic_numbers(), atoms.get_ghost_atomic_numbers())
z_nb = z[nb]

Neighbor list objects reused from the potential does not include any neighbors to ghost atoms (but real atoms will have ghost atoms as neighbors). Neighbor list objects created from Python include neighbors to ghost atoms, but the list is “incomplete” in the way that the corresponding real atom (on another processor) may have neighbors that are not present as ghost atoms on this processor, and thus not included in the neighbor list.

Creating the NeighborList object in a parallel simulation is not recommended: The neighbor list will not include the ghost atoms, typically leading to incorrect data analysis; and it will not always be updated correctly if the atoms change, leading to crashes. Use NeighborCellLocator or FullNeighborList instead.