Using neighbor list from LAMMPS in Python

Can anyone provide an example of how to generate and access a neighbor list generated with LAMMPS by the python interface? I would like to use it as a replacement for the ase.neighborlist.Neighborlist class which is fairly slow.

This could be a helpful page under the LAMMPS+python documentation as well since I think others would possibly want this as well.

You cannot request a neighbor list directly. To trigger the neighbor list generation you have to set up and initialize a suitable calculation. The simplest, most efficient option would be to use pair style zero for that. If you have LAMMPS version 3Nov2022 or later, it can not only build the default “half” neighbor list, but it is also possible to request the generation of a “full” neighbor list. The format of the data and the majority of the documentation is with the C library interface: 1.1.7. Neighbor list access — LAMMPS documentation

The python module merely has wrappers to access the data. This can be done directly via ctypes arrays or via NumPy arrays. Below is an example:

#!/usr/bin/env python

from lammps import lammps, LAMMPS_INT

# create a system and run "setup" to build a half neighbor list
lmp = lammps(cmdargs=['-nocite', '-log', 'none', '-echo', 'none', '-screen', 'none'])
lmp.commands_string("""
units lj
atom_style atomic
atom_modify map array
lattice fcc 0.8
region box block 0 2 0 2 0 2
create_box 1 box
create_atoms 1 box
mass 1 1.0
pair_style zero 2.5
pair_coeff 1 1
neighbor 0.3 bin
neigh_modify every 1 delay 0 check no
run 0 post no
""")

# look up neighbor list with ctypes arrays
idx = lmp.find_pair_neighlist("zero")
nlist = lmp.get_neighlist(idx)
tag = lmp.extract_atom("id")

print("Ctypes access")
atom_i, numneigh_i, neighbors_i = nlist[0]
print("Atom ID %d:   %d neighbors   first neighbor: %d, second neighbor %d" %
      (tag[atom_i], numneigh_i, tag[neighbors_i[0]], tag[neighbors_i[1]]))
atom_i, numneigh_i, neighbors_i = nlist[1]
print("Atom ID %d:   %d neighbors   first neighbor: %d, second neighbor %d" %
      (tag[atom_i], numneigh_i, tag[neighbors_i[0]], tag[neighbors_i[1]]))

# example using NumPy arrays
nplist = lmp.numpy.get_neighlist(idx)
nlocal = lmp.extract_global("nlocal", LAMMPS_INT)
nghost = lmp.extract_global("nghost", LAMMPS_INT)
ntag = lmp.numpy.extract_atom_iarray("id", nlocal+nghost, dim=1)

print("NumPy access")
atom_i, neighbors_i = nplist[0]
print("Atom ID %d:   %d neighbors   first neighbor: %d, second neighbor %d" %
      (ntag[atom_i], len(neighbors_i), ntag[neighbors_i[0]], ntag[neighbors_i[1]]))
atom_i, neighbors_i = nplist[1]
print("Atom ID %d:   %d neighbors   first neighbor: %d, second neighbor %d" %
      (ntag[atom_i], len(neighbors_i), ntag[neighbors_i[0]], ntag[neighbors_i[1]]))

As with most of the LAMMPS documentation: stuff gets written when there is a need and time. There are far more things that “would be useful” than what is available. The programming guide is still very much under development and there are a lot of examples for a lot of applications still missing (as are detailed unit tests). A lot of the currently available material was written thanks to additional time that because available due to the pandemic. Since we are mostly back to a pre-pandemic situation, progress has slowed down significantly.

The LAMMPS developers thus very much welcome contributions from LAMMPS users that help to improve and expand the documentation. A large part of LAMMPS are contributions from the community. If you want to see something happen, then the best way is to take the initiative and do it yourself. Or at least bring it to the point where it would be little effort to fully integrate it. The core LAMMPS developers usually have to focus on tasks that are related to the projects that provide the funding for their time.

1 Like