Communication between processors when checking connectivity of particles

Hello All,
I use LAMMPS (SPH Package from the 8 April 2021 version) for modeling electrodeposition and I am currently attempting to implement a connectivity check of particles. My system has a type 1 and type 2 particle. As the simulation cycles some type 2 particles will break off from the origional portion of type 2 particles and become isolated (i.e. surrounded by type 1 particles). I am using a breadth-first search in order to determine the connectivity of particles and this seems to work properly within each processor domain. However, when communicating with other processors, there seems to be an issue. The code is below and the issue can be visualized in the attached image. The biggest issue is that the boundary particles are all being turned to type 3 when they shouldn’t be.

Here is the code:

/* ---------------------------------------------------------------------- */

FixSPHconnectivityBFS::FixSPHconnectivityBFS(LAMMPS *lmp, int narg, char **arg) :
  Fix(lmp, narg, arg) {

  if ((atom->esph_flag != 1) || (atom->rho_flag != 1))
    error->all(FLERR,
  	       "fix sph/precipitation/dissolution/liquidRC/Connectivity/BFS command requires atom_style with both energy and density, e.g. meso");

  if (narg != 6)
    error->all(FLERR,"Illegal number of arguments for fix sph/connectivity/BFS command");

  time_integrate = 0;

  // Obtain the neighbor cutoff distance
  int m = 3;
  dx = atof(arg[m++]);
  cCeq = atof(arg[m++]);
  cAeq = atof(arg[m++]);
  if (dx <= 0)
    error->all(FLERR,"Illegal value for equilibrium cutoff distance");
  if (cCeq <= 0)
    error->all(FLERR,"Illegal value for equilibrium cation concentration");
  if (cAeq <= 0)
    error->all(FLERR,"Illegal value for equilibrium anion concentration");
  
  // find the concentration property
  int fcC;
  int icC = atom->find_custom("cC", fcC);
  if (icC < 0)
    error->all(FLERR,
	       "Can't find property cC for fix sph/connectivity/BFS");
  cC = atom->dvector[icC];

  // find the concentration property
  int fcA;
  int icA = atom->find_custom("cA", fcA);
  if (icA < 0)
    error->all(FLERR,
	       "Can't find property cA for fix sph/connectivity/BFS");
  cA = atom->dvector[icA];

    // find the concentration property
  int fdcA;
  int idcA = atom->find_custom("dcA", fdcA);
  if (idcA < 0)
    error->all(FLERR,
	       "Can't find property dcA for fix sph/connectivity/BFS");
  dcA = atom->dvector[idcA];

  // find the mass of A property
  int fmM;
  int imM = atom->find_custom("mM", fmM);
  if (imM < 0)
    error->all(FLERR,
	       "Can't find property mM for fix sph/connectivity/BFS");
  mM = atom->dvector[imM];

  // find the potential property
  int flocal_pot;
  int ilocal_pot = atom->find_custom("local_pot", flocal_pot);
  if (ilocal_pot < 0)
    error->all(FLERR,
	       "Can't find property local_pot for fix sph/connectivity/BFS");
  local_pot = atom->dvector[ilocal_pot];

  // find the anode particles
  int fisAnode;
  int iisAnode = atom->find_custom("isAnode", fisAnode);
  if (iisAnode < 0)
    error->all(FLERR,
	       "Can't find property isAnode for fix sph/connectivity/BFS");
  isAnode = atom->dvector[iisAnode];

  // set comm size by this fix
  comm_forward = 2;
  comm_reverse = 2;
}

/* ---------------------------------------------------------------------- */

FixSPHconnectivityBFS::~FixSPHconnectivityBFS() {
}

/* ---------------------------------------------------------------------- */

int FixSPHconnectivityBFS::setmask() {
  int mask = 0;
  mask |= END_OF_STEP;
  return mask;
}

/* ---------------------------------------------------------------------- */

void FixSPHconnectivityBFS::init() {
  // need a full neighbor list, built whenever re-neighboring occurs
  int irequest = neighbor->request((void *) this);
  neighbor->requests[irequest]->pair = 0;
  neighbor->requests[irequest]->fix = 1;
  neighbor->requests[irequest]->half = 0;
  neighbor->requests[irequest]->full = 1;
  dtcA = update->dt;
}

/* ---------------------------------------------------------------------- */

void FixSPHconnectivityBFS::init_list(int, NeighList *ptr)
{
  list = ptr;
}

/* ---------------------------------------------------------------------- */

void FixSPHconnectivityBFS::end_of_step()
{
  int i, j, ii, jj, itype, jtype, jshortest, inum, jnum;
  int *ilist, *jlist, *numneigh, **firstneigh;

  double delx, dely, delz;
  double shortest, rsq, r, cAdist;
  double tempDistance;
  double **x = atom->x;
  double **v = atom->v;
  int *type = atom->type;

  int *mask = atom->mask;
  int nlocal = atom->nlocal;
  int nall = nlocal + atom->nghost;
  
  inum = list->inum;
  ilist = list->ilist;
  numneigh = list->numneigh;
  firstneigh = list->firstneigh;
  
  // Create memory for connectivity array
  memory->create(connected, nall, "fix:connected");

  // Communicate the local atom type to the ghost atoms
  comm->forward_comm_fix(this);
  
  for (i = 0; i < nall; i++) {
    connected[i] = 0.0;
  }

  std::queue<int> queue;

  //First pass: mark all anode particles and add them to the queue
  for (ii = 0; ii < inum; ii++) {
    i = ilist[ii];
    itype = type[i];

    if(itype == 2 && isAnode[i] == 1 && i < nlocal) {
      connected[i] = 1;
      queue.push(i); // Add directly connected anode particle to the queue
    }
  }
  
  //Use BFS to propogate connectivity
  while(!queue.empty()) {
    i = queue.front();
    queue.pop();

    if (i >= nlocal) continue;
    
    jnum = numneigh[i];
    jlist = firstneigh[i];

    for (jj = 0; jj < jnum; jj++) {
      j = jlist[jj];
      j &= NEIGHMASK;
      jtype = type[j];

      // Check if the distance between particles i and j is within an acceptable range
      delx = x[i][0] - x[j][0];
      dely = x[i][1] - x[j][1];
      delz = x[i][2] - x[j][2];
      r = sqrt(delx*delx + dely*dely + delz*delz);

      if (jtype == 2 && connected[j] == 0 && r <= 1.75*dx) { 
	connected[j] = 1;
	queue.push(j); // Add newly connected particles to the queue
      }
    }
  }

  // Send the phase change status                                                                                                   
  comm->reverse_comm_fix(this);

  for (i = 0; i < nlocal; i++) {
    if (connected[i] == 0 && type[i] == 2) {
      type[i] = 3;
    }
  }

  // Destroy the memory created for ischangecC
  memory->destroy(connected);
}

/* ---------------------------------------------------------------------- */

int FixSPHconnectivityBFS::pack_forward_comm(int n, int *list, double *buf, int pbc_flag, int *pbc)
{
  int i, j, m;
  m = 0;
  for (i = 0; i < n; i++) {
    j = list[i];
    buf[m++] = atom->type[j];
  }
  return m;
}

/* ---------------------------------------------------------------------- */

void FixSPHconnectivityBFS::unpack_forward_comm(int n, int first, double *buf) 
{
  int i, m, last;
  m = 0;
  last = first + n;
  for (i = first; i < last; i++) {
    atom->type[i] = buf[m++];
  }
}

/* ---------------------------------------------------------------------- */

int FixSPHconnectivityBFS::pack_reverse_comm(int n, int first, double *buf)
{
  int i,m,last;
  int *type = atom->type;

  m = 0;
  last = first + n;
  for (i = first; i < last; i++) {
    buf[m++] = connected[i];

  }
  return m;
}

/* ---------------------------------------------------------------------- */

void FixSPHconnectivityBFS::unpack_reverse_comm(int n, int *list, double *buf)
{
  int i,j,m;
  int *type = atom->type;

  m = 0;
  for (i = 0; i < n; i++) {
    j = list[i];
    connected[j] += buf[m++];

  }
}

Note: I have also attempted to add the atom type to the reverse communication and send the reverse communication following the phase change.

In the above figure the gray particles are type 2, the blue are type 1 and the black are type 3. The red arrows are added to show where the communication seems to be failing. They system is running on 32 processors.

Does anyone understand where I may be going wrong in my communication?

Thanks!

It is next to impossible to give advice on this because

  • your code is based on a very old version of LAMMPS
  • there is no way to study and reproduce what you are seeing, just providing part of the source is not very helpful

There is some documentation about communication patterns in LAMMPS in the Programmer Guide part of the LAMMPS manual, and a lecture with further information, details, and explanations was just given at the ongoing LAMMPS Master Class workshop here in Philly. The lectures from the workshop were live streamed on YouTube and recorded and can be currently watched here: https://www.youtube.com/@templelammps/streams