/* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ #include #include #include "special.h" #include "atom.h" #include "atom_vec.h" #include "force.h" #include "comm.h" #include "modify.h" #include "fix.h" #include "accelerator_kokkos.h" #include "memory.h" #include "error.h" using namespace LAMMPS_NS; // allocate space for static class variable Special *Special::sptr; /* ---------------------------------------------------------------------- */ Special::Special(LAMMPS *lmp) : Pointers(lmp) { MPI_Comm_rank(world,&me); MPI_Comm_size(world,&nprocs); onetwo = onethree = onefour = NULL; } /* ---------------------------------------------------------------------- */ Special::~Special() { memory->destroy(onetwo); memory->destroy(onethree); memory->destroy(onefour); } /* ---------------------------------------------------------------------- create 1-2, 1-3, 1-4 lists of topology neighbors store in onetwo, onethree, onefour for each atom store 3 counters in nspecial[i] ------------------------------------------------------------------------- */ void Special::build() { int i,j,k,size; int max,maxall,nbuf; tagint *buf; MPI_Barrier(world); int nlocal = atom->nlocal; tagint *tag = atom->tag; int *num_bond = atom->num_bond; tagint **bond_atom = atom->bond_atom; int **nspecial = atom->nspecial; if (me == 0 && screen) { const double * const special_lj = force->special_lj; const double * const special_coul = force->special_coul; fprintf(screen,"Finding 1-2 1-3 1-4 neighbors ...\n" " Special bond factors lj: %-10g %-10g %-10g\n" " Special bond factors coul: %-10g %-10g %-10g\n", special_lj[1],special_lj[2],special_lj[3], special_coul[1],special_coul[2],special_coul[3]); } // initialize nspecial counters to 0 for (i = 0; i < nlocal; i++) { nspecial[i][0] = 0; nspecial[i][1] = 0; nspecial[i][2] = 0; } // ----------------------------------------------------- // compute nspecial[i][0] = # of 1-2 neighbors of atom i // ----------------------------------------------------- // bond partners stored by atom itself for (i = 0; i < nlocal; i++) nspecial[i][0] = num_bond[i]; // if newton_bond off, then done // else only counted 1/2 of all bonds, so count other half if (force->newton_bond) { // nbufmax = largest buffer needed to hold info from any proc // info for each atom = global tag of 2nd atom in each bond nbuf = 0; for (i = 0; i < nlocal; i++) nbuf += num_bond[i]; memory->create(buf,nbuf,"special:buf"); // fill buffer with global tags of bond partners of my atoms size = 0; for (i = 0; i < nlocal; i++) for (j = 0; j < num_bond[i]; j++) buf[size++] = bond_atom[i][j]; // cycle buffer around ring of procs back to self // when receive buffer, scan tags for atoms I own // when find one, increment nspecial count for that atom sptr = this; comm->ring(size,sizeof(tagint),buf,1,ring_one,NULL); memory->destroy(buf); } // ---------------------------------------------------- // create onetwo[i] = list of 1-2 neighbors for atom i // ---------------------------------------------------- max = 0; for (i = 0; i < nlocal; i++) max = MAX(max,nspecial[i][0]); MPI_Allreduce(&max,&maxall,1,MPI_INT,MPI_MAX,world); if (me == 0) { if (screen) fprintf(screen," %d = max # of 1-2 neighbors\n",maxall); if (logfile) fprintf(logfile," %d = max # of 1-2 neighbors\n",maxall); } memory->create(onetwo,nlocal,maxall,"special:onetwo"); // count = accumulating counter memory->create(count,nlocal,"special:count"); for (i = 0; i < nlocal; i++) count[i] = 0; // add bond partners stored by atom to onetwo list for (i = 0; i < nlocal; i++) for (j = 0; j < num_bond[i]; j++) onetwo[i][count[i]++] = bond_atom[i][j]; // if newton_bond off, then done // else only stored 1/2 of all bonds, so store other half if (force->newton_bond) { // nbufmax = largest buffer needed to hold info from any proc // info for each atom = 2 global tags in each bond nbuf = 0; for (i = 0; i < nlocal; i++) nbuf += 2*num_bond[i]; memory->create(buf,nbuf,"special:buf"); // fill buffer with global tags of both atoms in bond size = 0; for (i = 0; i < nlocal; i++) for (j = 0; j < num_bond[i]; j++) { buf[size++] = tag[i]; buf[size++] = bond_atom[i][j]; } // cycle buffer around ring of procs back to self // when receive buffer, scan 2nd-atom tags for atoms I own // when find one, add 1st-atom tag to onetwo list for 2nd atom sptr = this; comm->ring(size,sizeof(tagint),buf,2,ring_two,NULL); memory->destroy(buf); } memory->destroy(count); // ----------------------------------------------------- // done if special_bond weights for 1-3, 1-4 are set to 1.0 // ----------------------------------------------------- if (force->special_lj[2] == 1.0 && force->special_coul[2] == 1.0 && force->special_lj[3] == 1.0 && force->special_coul[3] == 1.0) { dedup(); combine(); fix_alteration(); return; } // ----------------------------------------------------- // compute nspecial[i][1] = # of 1-3 neighbors of atom i // ----------------------------------------------------- // nbufmax = largest buffer needed to hold info from any proc // info for each atom = 2 scalars + list of 1-2 neighbors nbuf = 0; for (i = 0; i < nlocal; i++) nbuf += 2 + nspecial[i][0]; memory->create(buf,nbuf,"special:buf"); // fill buffer with: // (1) = counter for 1-3 neighbors, initialized to 0 // (2) = # of 1-2 neighbors // (3:N) = list of 1-2 neighbors size = 0; for (i = 0; i < nlocal; i++) { buf[size++] = 0; buf[size++] = nspecial[i][0]; for (j = 0; j < nspecial[i][0]; j++) buf[size++] = onetwo[i][j]; } // cycle buffer around ring of procs back to self // when receive buffer, scan list of 1-2 neighbors for atoms I own // when find one, increment 1-3 count by # of 1-2 neighbors of my atom, // subtracting one since my list will contain original atom sptr = this; comm->ring(size,sizeof(tagint),buf,3,ring_three,buf); // extract count from buffer that has cycled back to me // nspecial[i][1] = # of 1-3 neighbors of atom i j = 0; for (i = 0; i < nlocal; i++) { nspecial[i][1] = buf[j]; j += 2 + nspecial[i][0]; } memory->destroy(buf); // ---------------------------------------------------- // create onethree[i] = list of 1-3 neighbors for atom i // ---------------------------------------------------- max = 0; for (i = 0; i < nlocal; i++) max = MAX(max,nspecial[i][1]); MPI_Allreduce(&max,&maxall,1,MPI_INT,MPI_MAX,world); if (me == 0) { if (screen) fprintf(screen," %d = max # of 1-3 neighbors\n",maxall); if (logfile) fprintf(logfile," %d = max # of 1-3 neighbors\n",maxall); } memory->create(onethree,nlocal,maxall,"special:onethree"); // nbufmax = largest buffer needed to hold info from any proc // info for each atom = 4 scalars + list of 1-2 neighs + list of 1-3 neighs nbuf = 0; for (i = 0; i < nlocal; i++) nbuf += 4 + nspecial[i][0] + nspecial[i][1]; memory->create(buf,nbuf,"special:buf"); // fill buffer with: // (1) = global tag of original atom // (2) = # of 1-2 neighbors // (3) = # of 1-3 neighbors // (4) = counter for 1-3 neighbors, initialized to 0 // (5:N) = list of 1-2 neighbors // (N+1:2N) space for list of 1-3 neighbors size = 0; for (i = 0; i < nlocal; i++) { buf[size++] = tag[i]; buf[size++] = nspecial[i][0]; buf[size++] = nspecial[i][1]; buf[size++] = 0; for (j = 0; j < nspecial[i][0]; j++) buf[size++] = onetwo[i][j]; size += nspecial[i][1]; } // cycle buffer around ring of procs back to self // when receive buffer, scan list of 1-2 neighbors for atoms I own // when find one, add its neighbors to 1-3 list // increment the count in buf(i+4) // exclude the atom whose tag = original // this process may include duplicates but they will be culled later sptr = this; comm->ring(size,sizeof(tagint),buf,4,ring_four,buf); // fill onethree with buffer values that have been returned to me // sanity check: accumulated buf[i+3] count should equal // nspecial[i][1] for each atom j = 0; for (i = 0; i < nlocal; i++) { if (buf[j+3] != nspecial[i][1]) error->one(FLERR,"1-3 bond count is inconsistent"); j += 4 + nspecial[i][0]; for (k = 0; k < nspecial[i][1]; k++) onethree[i][k] = buf[j++]; } memory->destroy(buf); // done if special_bond weights for 1-4 are set to 1.0 if (force->special_lj[3] == 1.0 && force->special_coul[3] == 1.0) { dedup(); if (force->special_angle) angle_trim(); combine(); fix_alteration(); return; } // ----------------------------------------------------- // compute nspecial[i][2] = # of 1-4 neighbors of atom i // ----------------------------------------------------- // nbufmax = largest buffer needed to hold info from any proc // info for each atom = 2 scalars + list of 1-3 neighbors nbuf = 0; for (i = 0; i < nlocal; i++) nbuf += 2 + nspecial[i][1]; memory->create(buf,nbuf,"special:buf"); // fill buffer with: // (1) = counter for 1-4 neighbors, initialized to 0 // (2) = # of 1-3 neighbors // (3:N) = list of 1-3 neighbors size = 0; for (i = 0; i < nlocal; i++) { buf[size++] = 0; buf[size++] = nspecial[i][1]; for (j = 0; j < nspecial[i][1]; j++) buf[size++] = onethree[i][j]; } // cycle buffer around ring of procs back to self // when receive buffer, scan list of 1-3 neighbors for atoms I own // when find one, increment 1-4 count by # of 1-2 neighbors of my atom // may include duplicates and original atom but they will be culled later sptr = this; comm->ring(size,sizeof(tagint),buf,5,ring_five,buf); // extract count from buffer that has cycled back to me // nspecial[i][2] = # of 1-4 neighbors of atom i j = 0; for (i = 0; i < nlocal; i++) { nspecial[i][2] = buf[j]; j += 2 + nspecial[i][1]; } memory->destroy(buf); // ---------------------------------------------------- // create onefour[i] = list of 1-4 neighbors for atom i // ---------------------------------------------------- max = 0; for (i = 0; i < nlocal; i++) max = MAX(max,nspecial[i][2]); MPI_Allreduce(&max,&maxall,1,MPI_INT,MPI_MAX,world); if (me == 0) { if (screen) fprintf(screen," %d = max # of 1-4 neighbors\n",maxall); if (logfile) fprintf(logfile," %d = max # of 1-4 neighbors\n",maxall); } memory->create(onefour,nlocal,maxall,"special:onefour"); // nbufmax = largest buffer needed to hold info from any proc // info for each atom = 3 scalars + list of 1-3 neighs + list of 1-4 neighs nbuf = 0; for (i = 0; i < nlocal; i++) nbuf += 3 + nspecial[i][1] + nspecial[i][2]; memory->create(buf,nbuf,"special:buf"); // fill buffer with: // (1) = # of 1-3 neighbors // (2) = # of 1-4 neighbors // (3) = counter for 1-4 neighbors, initialized to 0 // (4:N) = list of 1-3 neighbors // (N+1:2N) space for list of 1-4 neighbors size = 0; for (i = 0; i < nlocal; i++) { buf[size++] = nspecial[i][1]; buf[size++] = nspecial[i][2]; buf[size++] = 0; for (j = 0; j < nspecial[i][1]; j++) buf[size++] = onethree[i][j]; size += nspecial[i][2]; } // cycle buffer around ring of procs back to self // when receive buffer, scan list of 1-3 neighbors for atoms I own // when find one, add its neighbors to 1-4 list // incrementing the count in buf(i+4) // this process may include duplicates but they will be culled later sptr = this; comm->ring(size,sizeof(tagint),buf,6,ring_six,buf); // fill onefour with buffer values that have been returned to me // sanity check: accumulated buf[i+2] count should equal // nspecial[i][2] for each atom j = 0; for (i = 0; i < nlocal; i++) { if (buf[j+2] != nspecial[i][2]) error->one(FLERR,"1-4 bond count is inconsistent"); j += 3 + nspecial[i][1]; for (k = 0; k < nspecial[i][2]; k++) onefour[i][k] = buf[j++]; } memory->destroy(buf); dedup(); if (force->special_angle) angle_trim(); if (force->special_dihedral) dihedral_trim(); combine(); fix_alteration(); } /* ---------------------------------------------------------------------- remove duplicates within each of onetwo, onethree, onefour individually ------------------------------------------------------------------------- */ void Special::dedup() { int i,j; tagint m; // clear map so it can be used as scratch space atom->map_clear(); // use map to cull duplicates // exclude original atom explicitly // adjust onetwo, onethree, onefour values to reflect removed duplicates // must unset map for each atom int **nspecial = atom->nspecial; tagint *tag = atom->tag; int nlocal = atom->nlocal; int unique; for (i = 0; i < nlocal; i++) { unique = 0; atom->map_one(tag[i],0); for (j = 0; j < nspecial[i][0]; j++) { m = onetwo[i][j]; if (atom->map(m) < 0) { onetwo[i][unique++] = m; atom->map_one(m,0); } } nspecial[i][0] = unique; atom->map_one(tag[i],-1); for (j = 0; j < unique; j++) atom->map_one(onetwo[i][j],-1); } for (i = 0; i < nlocal; i++) { unique = 0; atom->map_one(tag[i],0); for (j = 0; j < nspecial[i][1]; j++) { m = onethree[i][j]; if (atom->map(m) < 0) { onethree[i][unique++] = m; atom->map_one(m,0); } } nspecial[i][1] = unique; atom->map_one(tag[i],-1); for (j = 0; j < unique; j++) atom->map_one(onethree[i][j],-1); } for (i = 0; i < nlocal; i++) { unique = 0; atom->map_one(tag[i],0); for (j = 0; j < nspecial[i][2]; j++) { m = onefour[i][j]; if (atom->map(m) < 0) { onefour[i][unique++] = m; atom->map_one(m,0); } } nspecial[i][2] = unique; atom->map_one(tag[i],-1); for (j = 0; j < unique; j++) atom->map_one(onefour[i][j],-1); } // re-create map atom->nghost = 0; atom->map_set(); } /* ---------------------------------------------------------------------- concatenate onetwo, onethree, onefour into master atom->special list remove duplicates between 3 lists, leave dup in first list it appears in convert nspecial[0], nspecial[1], nspecial[2] into cumulative counters ------------------------------------------------------------------------- */ void Special::combine() { int i,j; tagint m; int me; MPI_Comm_rank(world,&me); int **nspecial = atom->nspecial; tagint *tag = atom->tag; int nlocal = atom->nlocal; // ---------------------------------------------------- // compute culled maxspecial = max # of special neighs of any atom // ---------------------------------------------------- // clear map so it can be used as scratch space atom->map_clear(); // unique = # of unique nspecial neighbors of one atom // cull duplicates using map to check for them // exclude original atom explicitly // must unset map for each atom int unique; int maxspecial = 0; for (i = 0; i < nlocal; i++) { unique = 0; atom->map_one(tag[i],0); for (j = 0; j < nspecial[i][0]; j++) { m = onetwo[i][j]; if (atom->map(m) < 0) { unique++; atom->map_one(m,0); } } for (j = 0; j < nspecial[i][1]; j++) { m = onethree[i][j]; if (atom->map(m) < 0) { unique++; atom->map_one(m,0); } } for (j = 0; j < nspecial[i][2]; j++) { m = onefour[i][j]; if (atom->map(m) < 0) { unique++; atom->map_one(m,0); } } maxspecial = MAX(maxspecial,unique); atom->map_one(tag[i],-1); for (j = 0; j < nspecial[i][0]; j++) atom->map_one(onetwo[i][j],-1); for (j = 0; j < nspecial[i][1]; j++) atom->map_one(onethree[i][j],-1); for (j = 0; j < nspecial[i][2]; j++) atom->map_one(onefour[i][j],-1); } // compute global maxspecial, must be at least 1 // add in extra factor from special_bonds command // allocate correct special array with same nmax, new maxspecial // previously allocated one must be destroyed // must make AtomVec class update its ptr to special MPI_Allreduce(&maxspecial,&atom->maxspecial,1,MPI_INT,MPI_MAX,world); atom->maxspecial += force->special_extra; atom->maxspecial = MAX(atom->maxspecial,1); if (me == 0) { if (screen) fprintf(screen," %d = max # of special neighbors\n",atom->maxspecial); if (logfile) fprintf(logfile," %d = max # of special neighbors\n",atom->maxspecial); } if (lmp->kokkos) { AtomKokkos* atomKK = (AtomKokkos*) atom; memory->grow_kokkos(atomKK->k_special,atom->special, atom->nmax,atom->maxspecial,"atom:special"); } else { memory->destroy(atom->special); memory->create(atom->special,atom->nmax,atom->maxspecial,"atom:special"); } atom->avec->grow_reset(); tagint **special = atom->special; // ---------------------------------------------------- // fill special array with 1-2, 1-3, 1-4 neighs for each atom // ---------------------------------------------------- // again use map to cull duplicates // exclude original atom explicitly // adjust nspecial[i] values to reflect removed duplicates // nspecial[i][1] and nspecial[i][2] now become cumulative counters for (i = 0; i < nlocal; i++) { unique = 0; atom->map_one(tag[i],0); for (j = 0; j < nspecial[i][0]; j++) { m = onetwo[i][j]; if (atom->map(m) < 0) { special[i][unique++] = m; atom->map_one(m,0); } } nspecial[i][0] = unique; for (j = 0; j < nspecial[i][1]; j++) { m = onethree[i][j]; if (atom->map(m) < 0) { special[i][unique++] = m; atom->map_one(m,0); } } nspecial[i][1] = unique; for (j = 0; j < nspecial[i][2]; j++) { m = onefour[i][j]; if (atom->map(m) < 0) { special[i][unique++] = m; atom->map_one(m,0); } } nspecial[i][2] = unique; atom->map_one(tag[i],-1); for (j = 0; j < nspecial[i][2]; j++) atom->map_one(special[i][j],-1); } // re-create map atom->nghost = 0; atom->map_set(); } /* ---------------------------------------------------------------------- trim list of 1-3 neighbors by checking defined angles delete a 1-3 neigh if they are not end atoms of a defined angle and if they are not 1,3 or 2,4 atoms of a defined dihedral ------------------------------------------------------------------------- */ void Special::angle_trim() { int i,j,m,n; int *num_angle = atom->num_angle; int *num_dihedral = atom->num_dihedral; tagint **angle_atom1 = atom->angle_atom1; tagint **angle_atom3 = atom->angle_atom3; tagint **dihedral_atom1 = atom->dihedral_atom1; tagint **dihedral_atom2 = atom->dihedral_atom2; tagint **dihedral_atom3 = atom->dihedral_atom3; tagint **dihedral_atom4 = atom->dihedral_atom4; int **nspecial = atom->nspecial; int nlocal = atom->nlocal; // stats on old 1-3 neighbor counts double onethreecount = 0.0; for (i = 0; i < nlocal; i++) onethreecount += nspecial[i][1]; double allcount; MPI_Allreduce(&onethreecount,&allcount,1,MPI_DOUBLE,MPI_SUM,world); if (me == 0) { if (screen) fprintf(screen, " %g = # of 1-3 neighbors before angle trim\n",allcount); if (logfile) fprintf(logfile, " %g = # of 1-3 neighbors before angle trim\n",allcount); } // if angles or dihedrals are defined, // flag each 1-3 neigh if it appears in an angle or dihedral if ((num_angle && atom->nangles) || (num_dihedral && atom->ndihedrals)) { // dflag = flag for 1-3 neighs of all owned atoms int maxcount = 0; for (i = 0; i < nlocal; i++) maxcount = MAX(maxcount,nspecial[i][1]); memory->create(dflag,nlocal,maxcount,"special::dflag"); for (i = 0; i < nlocal; i++) { n = nspecial[i][1]; for (j = 0; j < n; j++) dflag[i][j] = 0; } // nbufmax = largest buffer needed to hold info from any proc // info for each atom = list of 1,3 atoms in each angle stored by atom // and list of 1,3 and 2,4 atoms in each dihedral stored by atom int nbuf = 0; for (i = 0; i < nlocal; i++) { if (num_angle && atom->nangles) nbuf += 2*num_angle[i]; if (num_dihedral && atom->ndihedrals) nbuf += 2*2*num_dihedral[i]; } int *buf; memory->create(buf,nbuf,"special:buf"); // fill buffer with list of 1,3 atoms in each angle // and with list of 1,3 and 2,4 atoms in each dihedral int size = 0; if (num_angle && atom->nangles) for (i = 0; i < nlocal; i++) for (j = 0; j < num_angle[i]; j++) { buf[size++] = angle_atom1[i][j]; buf[size++] = angle_atom3[i][j]; } if (num_dihedral && atom->ndihedrals) for (i = 0; i < nlocal; i++) for (j = 0; j < num_dihedral[i]; j++) { buf[size++] = dihedral_atom1[i][j]; buf[size++] = dihedral_atom3[i][j]; buf[size++] = dihedral_atom2[i][j]; buf[size++] = dihedral_atom4[i][j]; } // cycle buffer around ring of procs back to self // when receive buffer, scan list of 1,3 atoms looking for atoms I own // when find one, scan its 1-3 neigh list and mark I,J as in an angle sptr = this; comm->ring(size,sizeof(tagint),buf,7,ring_seven,NULL); // delete 1-3 neighbors if they are not flagged in dflag for (i = 0; i < nlocal; i++) { m = 0; for (j = 0; j < nspecial[i][1]; j++) if (dflag[i][j]) onethree[i][m++] = onethree[i][j]; nspecial[i][1] = m; } // clean up memory->destroy(dflag); memory->destroy(buf); // if no angles or dihedrals are defined, delete all 1-3 neighs } else { for (i = 0; i < nlocal; i++) nspecial[i][1] = 0; } // stats on new 1-3 neighbor counts onethreecount = 0.0; for (i = 0; i < nlocal; i++) onethreecount += nspecial[i][1]; MPI_Allreduce(&onethreecount,&allcount,1,MPI_DOUBLE,MPI_SUM,world); if (me == 0) { if (screen) fprintf(screen, " %g = # of 1-3 neighbors after angle trim\n",allcount); if (logfile) fprintf(logfile, " %g = # of 1-3 neighbors after angle trim\n",allcount); } } /* ---------------------------------------------------------------------- trim list of 1-4 neighbors by checking defined dihedrals delete a 1-4 neigh if they are not end atoms of a defined dihedral ------------------------------------------------------------------------- */ void Special::dihedral_trim() { int i,j,m,n; int *num_dihedral = atom->num_dihedral; tagint **dihedral_atom1 = atom->dihedral_atom1; tagint **dihedral_atom4 = atom->dihedral_atom4; int **nspecial = atom->nspecial; int nlocal = atom->nlocal; // stats on old 1-4 neighbor counts double onefourcount = 0.0; for (i = 0; i < nlocal; i++) onefourcount += nspecial[i][2]; double allcount; MPI_Allreduce(&onefourcount,&allcount,1,MPI_DOUBLE,MPI_SUM,world); if (me == 0) { if (screen) fprintf(screen, " %g = # of 1-4 neighbors before dihedral trim\n",allcount); if (logfile) fprintf(logfile, " %g = # of 1-4 neighbors before dihedral trim\n",allcount); } // if dihedrals are defined, flag each 1-4 neigh if it appears in a dihedral if (num_dihedral && atom->ndihedrals) { // dflag = flag for 1-4 neighs of all owned atoms int maxcount = 0; for (i = 0; i < nlocal; i++) maxcount = MAX(maxcount,nspecial[i][2]); memory->create(dflag,nlocal,maxcount,"special::dflag"); for (i = 0; i < nlocal; i++) { n = nspecial[i][2]; for (j = 0; j < n; j++) dflag[i][j] = 0; } // nbufmax = largest buffer needed to hold info from any proc // info for each atom = list of 1,4 atoms in each dihedral stored by atom int nbuf = 0; for (i = 0; i < nlocal; i++) nbuf += 2*num_dihedral[i]; int *buf; memory->create(buf,nbuf,"special:buf"); // fill buffer with list of 1,4 atoms in each dihedral int size = 0; for (i = 0; i < nlocal; i++) for (j = 0; j < num_dihedral[i]; j++) { buf[size++] = dihedral_atom1[i][j]; buf[size++] = dihedral_atom4[i][j]; } // cycle buffer around ring of procs back to self // when receive buffer, scan list of 1,4 atoms looking for atoms I own // when find one, scan its 1-4 neigh list and mark I,J as in a dihedral sptr = this; comm->ring(size,sizeof(tagint),buf,8,ring_eight,NULL); // delete 1-4 neighbors if they are not flagged in dflag for (i = 0; i < nlocal; i++) { m = 0; for (j = 0; j < nspecial[i][2]; j++) if (dflag[i][j]) onefour[i][m++] = onefour[i][j]; nspecial[i][2] = m; } // clean up memory->destroy(dflag); memory->destroy(buf); // if no dihedrals are defined, delete all 1-4 neighs } else { for (i = 0; i < nlocal; i++) nspecial[i][2] = 0; } // stats on new 1-4 neighbor counts onefourcount = 0.0; for (i = 0; i < nlocal; i++) onefourcount += nspecial[i][2]; MPI_Allreduce(&onefourcount,&allcount,1,MPI_DOUBLE,MPI_SUM,world); if (me == 0) { if (screen) fprintf(screen, " %g = # of 1-4 neighbors after dihedral trim\n",allcount); if (logfile) fprintf(logfile, " %g = # of 1-4 neighbors after dihedral trim\n",allcount); } } /* ---------------------------------------------------------------------- when receive buffer, scan tags for atoms I own when find one, increment nspecial count for that atom ------------------------------------------------------------------------- */ void Special::ring_one(int ndatum, char *cbuf) { Atom *atom = sptr->atom; int **nspecial = atom->nspecial; int nlocal = atom->nlocal; tagint *buf = (tagint *) cbuf; int m; for (int i = 0; i < ndatum; i++) { m = atom->map(buf[i]); if (m >= 0 && m < nlocal) nspecial[m][0]++; } } /* ---------------------------------------------------------------------- when receive buffer, scan 2nd-atom tags for atoms I own when find one, add 1st-atom tag to onetwo list for 2nd atom ------------------------------------------------------------------------- */ void Special::ring_two(int ndatum, char *cbuf) { Atom *atom = sptr->atom; int nlocal = atom->nlocal; tagint **onetwo = sptr->onetwo; int *count = sptr->count; tagint *buf = (tagint *) cbuf; int m; for (int i = 1; i < ndatum; i += 2) { m = atom->map(buf[i]); if (m >= 0 && m < nlocal) onetwo[m][count[m]++] = buf[i-1]; } } /* ---------------------------------------------------------------------- when receive buffer, scan list of 1-2 neighbors for atoms I own when find one, increment 1-3 count by # of 1-2 neighbors of my atom, subtracting one since my list will contain original atom ------------------------------------------------------------------------- */ void Special::ring_three(int ndatum, char *cbuf) { Atom *atom = sptr->atom; int **nspecial = atom->nspecial; int nlocal = atom->nlocal; tagint *buf = (tagint *) cbuf; int i,j,m,n,num12; i = 0; while (i < ndatum) { n = buf[i]; num12 = buf[i+1]; for (j = 0; j < num12; j++) { m = atom->map(buf[i+2+j]); if (m >= 0 && m < nlocal) n += nspecial[m][0] - 1; } buf[i] = n; i += 2 + num12; } } /* ---------------------------------------------------------------------- when receive buffer, scan list of 1-2 neighbors for atoms I own when find one, add its neighbors to 1-3 list increment the count in buf(i+4) exclude the atom whose tag = original this process may include duplicates but they will be culled later ------------------------------------------------------------------------- */ void Special::ring_four(int ndatum, char *cbuf) { Atom *atom = sptr->atom; int **nspecial = atom->nspecial; int nlocal = atom->nlocal; tagint **onetwo = sptr->onetwo; tagint *buf = (tagint *) cbuf; tagint original; int i,j,k,m,n,num12,num13; i = 0; while (i < ndatum) { original = buf[i]; num12 = buf[i+1]; num13 = buf[i+2]; n = buf[i+3]; for (j = 0; j < num12; j++) { m = atom->map(buf[i+4+j]); if (m >= 0 && m < nlocal) for (k = 0; k < nspecial[m][0]; k++) if (onetwo[m][k] != original) buf[i+4+num12+(n++)] = onetwo[m][k]; } buf[i+3] = n; i += 4 + num12 + num13; } } /* ---------------------------------------------------------------------- when receive buffer, scan list of 1-3 neighbors for atoms I own when find one, increment 1-4 count by # of 1-2 neighbors of my atom may include duplicates and original atom but they will be culled later ------------------------------------------------------------------------- */ void Special::ring_five(int ndatum, char *cbuf) { Atom *atom = sptr->atom; int **nspecial = atom->nspecial; int nlocal = atom->nlocal; tagint *buf = (tagint *) cbuf; int i,j,m,n,num13; i = 0; while (i < ndatum) { n = buf[i]; num13 = buf[i+1]; for (j = 0; j < num13; j++) { m = atom->map(buf[i+2+j]); if (m >= 0 && m < nlocal) n += nspecial[m][0]; } buf[i] = n; i += 2 + num13; } } /* ---------------------------------------------------------------------- when receive buffer, scan list of 1-3 neighbors for atoms I own when find one, add its neighbors to 1-4 list incrementing the count in buf(i+4) this process may include duplicates but they will be culled later ------------------------------------------------------------------------- */ void Special::ring_six(int ndatum, char *cbuf) { Atom *atom = sptr->atom; int **nspecial = atom->nspecial; int nlocal = atom->nlocal; tagint **onetwo = sptr->onetwo; tagint *buf = (tagint *) cbuf; int i,j,k,m,n,num13,num14; i = 0; while (i < ndatum) { num13 = buf[i]; num14 = buf[i+1]; n = buf[i+2]; for (j = 0; j < num13; j++) { m = atom->map(buf[i+3+j]); if (m >= 0 && m < nlocal) for (k = 0; k < nspecial[m][0]; k++) buf[i+3+num13+(n++)] = onetwo[m][k]; } buf[i+2] = n; i += 3 + num13 + num14; } } /* ---------------------------------------------------------------------- when receive buffer, scan list of 1,3 atoms looking for atoms I own when find one, scan its 1-3 neigh list and mark I,J as in an angle ------------------------------------------------------------------------- */ void Special::ring_seven(int ndatum, char *cbuf) { Atom *atom = sptr->atom; int **nspecial = atom->nspecial; int nlocal = atom->nlocal; tagint **onethree = sptr->onethree; int **dflag = sptr->dflag; tagint *buf = (tagint *) cbuf; tagint iglobal,jglobal; int i,m,ilocal,jlocal; i = 0; while (i < ndatum) { iglobal = buf[i]; jglobal = buf[i+1]; ilocal = atom->map(iglobal); jlocal = atom->map(jglobal); if (ilocal >= 0 && ilocal < nlocal) for (m = 0; m < nspecial[ilocal][1]; m++) if (jglobal == onethree[ilocal][m]) { dflag[ilocal][m] = 1; break; } if (jlocal >= 0 && jlocal < nlocal) for (m = 0; m < nspecial[jlocal][1]; m++) if (iglobal == onethree[jlocal][m]) { dflag[jlocal][m] = 1; break; } i += 2; } } /* ---------------------------------------------------------------------- when receive buffer, scan list of 1,4 atoms looking for atoms I own when find one, scan its 1-4 neigh list and mark I,J as in a dihedral ------------------------------------------------------------------------- */ void Special::ring_eight(int ndatum, char *cbuf) { Atom *atom = sptr->atom; int **nspecial = atom->nspecial; int nlocal = atom->nlocal; tagint **onefour = sptr->onefour; int **dflag = sptr->dflag; tagint *buf = (tagint *) cbuf; tagint iglobal,jglobal; int i,m,ilocal,jlocal; i = 0; while (i < ndatum) { iglobal = buf[i]; jglobal = buf[i+1]; ilocal = atom->map(iglobal); jlocal = atom->map(jglobal); if (ilocal >= 0 && ilocal < nlocal) for (m = 0; m < nspecial[ilocal][2]; m++) if (jglobal == onefour[ilocal][m]) { dflag[ilocal][m] = 1; break; } if (jlocal >= 0 && jlocal < nlocal) for (m = 0; m < nspecial[jlocal][2]; m++) if (iglobal == onefour[jlocal][m]) { dflag[jlocal][m] = 1; break; } i += 2; } } /* ---------------------------------------------------------------------- allow fixes to alter special list currently, only fix drude does this so that both the Drude core and electron are same level of neighbor ------------------------------------------------------------------------- */ void Special::fix_alteration() { for (int ifix = 0; ifix < modify->nfix; ifix++) if (modify->fix[ifix]->special_alter_flag) modify->fix[ifix]->rebuild_special(); }