rebo neighbors, pages question

Dear lammps users,

I have a question about the rebo neighbors Rebo_Neigh() in the airebo
pair-style, and I think the same construction is implemented in other pair
styles (Short_Neigh() in comb).

In these subroutines, memory is dynamically allocated for two arrays which
contain the number of neighbors of each atom and the index of those
neighbors.

What is the role of page size? I can see where the memory is allocated and
the arrays are assigned in the subroutine, but I don't understand why the
page size is included here.

if (pgsize - npnt < oneatom) {
      npnt = 0;
      npage++;
      if (npage == maxpage) add_pages();
    }
    neighptr = &pages[npage][npnt];
    n = 0;
........
    REBO_firstneigh[i] = neighptr;
    REBO_numneigh[i] = n;
    npnt += n;
    if (npnt >= pgsize)
      error->one(FLERR,"Neighbor list overflow, boost neigh_modify one or
page");

If we are creating an array with a value for each atom i in a pair
potential do we need to include this kind of treatment?

Thanks for the help,
John

Dear lammps users,

I have a question about the rebo neighbors Rebo_Neigh() in the airebo
pair-style, and I think the same construction is implemented in other pair
styles (Short_Neigh() in comb).

mostly, and all other neighbor list constructions.

In these subroutines, memory is dynamically allocated for two arrays which
contain the number of neighbors of each atom and the index of those
neighbors.

nod. that is how a neighbor list works.

What is the role of page size? I can see where the memory is allocated and
the arrays are assigned in the subroutine, but I don't understand why the
page size is included here.

if (pgsize - npnt < oneatom) {
npnt = 0;
npage++;
if (npage == maxpage) add_pages();
}
neighptr = &pages[npage][npnt];
n = 0;
........
REBO_firstneigh[i] = neighptr;
REBO_numneigh[i] = n;
npnt += n;
if (npnt >= pgsize)
error->one(FLERR,"Neighbor list overflow, boost neigh_modify one or
page");

If we are creating an array with a value for each atom i in a pair
potential do we need to include this kind of treatment?

that is not exactly what is happening.

the neighbor list has a *varying* number
of "j" indices listed for each atom "i".

to use pages has the advantage of
not having to do a (costly) malloc()
for each atom. this was particularly
expensive on some supercomputers.

at the same time, you don't have to
massively overestimate the total amount
of storage required (you just increase
storage by a "page") and then just "grow"
the "npages" dimension with realloc()

this also reduces fragmentation of memory,
-> better cache efficiency.

you still need an estimate of the maximum
number of neighbors per atom (the "one"
parameter in the lammps input), but if your
real number of neighbors is much smaller
it doesn't make that much of a difference,
since the "page" is packed tightly for as
long as the size of "one" for the number
of neighbors fits into the page. after that,
you move to the next page.

all pretty straightforward, if you stare at
the code for a while.

axel.

all pretty straightforward, if you stare at
the code for a while.

i should add, that this gets a bit more
complicated when you try to build
neighbor lists in parallel using threads.
for this each thread has to work on its
"own" page and a subset of atoms and
one has to us a mutex to deal with race
conditions when adding new pages to
the storage.

i'd be happy to hear if somebody knows
a smarter, lock-free way to do this.

cheers,
    axel.