Getting an up-to-date neighbor list during each stage of a time step

Dear fellow LAMMPS users,

I am currently making a new fix that evaluates particle positions before and after integration, and I’ve come across the following issue:

  1. At the end of a time step (end_of_step) I access the (local) neighbor list through list->ilist
  2. I loop over the local particles (atom->nlocal) with iterator j, get their local ID through i = list->ilist[j], and fetch their tag through atom->tag[i]
  3. Then, when I perform the same loop at the beginning of a time step (post_integrate), I get a different atom tag for a given value of i = list->ilist[j]

I’m not entirely sure what is going on, but it seems to me that list->ilist was not updated after the neighbor list was rebuilt, so that the local particle IDs no longer refer to the same particles as before. My question: what is the procedure for getting an up-to-date neighbor list during each stage of a time step?

I wished I understood the process a bit better, so that I could show relevant pieces of code. Maybe this piece helps:

void FixNewFix::init()
{
dt = update->dt;

int irequest = neighbor->request((void *) this);
neighbor->requests[irequest]->pair = 0;
neighbor->requests[irequest]->fix = 1;
neighbor->requests[irequest]->occasional = 0;
neighbor->requests[irequest]->ghost = 1;
}

Please ask if I need to provide more code or clarification

Thanks and regards,

Martijn van den Ende

Dear fellow LAMMPS users,

I am currently making a new fix that evaluates particle positions before and
after integration, and I've come across the following issue:

1) At the end of a time step (end_of_step) I access the (local) neighbor
list through list->ilist
2) I loop over the local particles (atom->nlocal) with iterator j, get their
local ID through i = list->ilist[j], and fetch their tag through
atom->tag[i]

this doesn't make sense. there is no such thing as a local id. if you
want to access all local particles, just do a loop

for (int i = 0; i < atom->nlocal; ++i) {
}

if you want to loop over local atoms *and* ghosts, you do:

int nall = atom->nlocal + atom->nghost;
for (int i; i < nall; ++i) {

}

you can get the respective global atom ID via atom->tag[i]

neighbor lists are for *pairs* of particles. then you would do:

int inum = list->inum;
int *list = list->ilist;

for (int i = 0; i < inum; ++i) {
   ii = ilist[i];
   int *jlist = list->firstneigh[ii];
   int jnum = list->numneigh[ii];
   for (int j = 0; j < jnum; ++j) {
      jj = jlist[j] & NEIGHMASK;
  }
}

if you request a neighborlist with neighbors of ghosts, inum > nlocal.

3) Then, when I perform the same loop at the beginning of a time step
(post_integrate), I get a different atom tag for a given value of i =
list->ilist[j]

I'm not entirely sure what is going on, but it seems to me that list->ilist
was not updated after the neighbor list was rebuilt, so that the local
particle IDs no longer refer to the same particles as before. My question:
what is the procedure for getting an up-to-date neighbor list during each
stage of a time step?

there is no such thing. please have a look at page 9 of

between Fix::end_of_step() and Fix::post_integrate() there is no
manipulation of neighbor lists.

however the other way around, i.e. between Fix::post_integrate() and
Fix::end_of_step() you may have a complete rebuild of the neighbor
list and a reordering of local atoms due to exchange between
subdomains and particularly new ghost atoms due to border
communication.

I wished I understood the process a bit better, so that I could show
relevant pieces of code. Maybe this piece helps:

the major inconsistency here is that you ask for a neighbor lists when
you seem to be interested only in "local" or "local + ghost" atoms.

axel.

Thanks Axel, this clarified a lot

Martijn