1 // clang-format off
2 /* ----------------------------------------------------------------------
3    LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
4    https://www.lammps.org/, Sandia National Laboratories
5    Steve Plimpton, sjplimp@sandia.gov
6 
7    Copyright (2003) Sandia Corporation.  Under the terms of Contract
8    DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
9    certain rights in this software.  This software is distributed under
10    the GNU General Public License.
11 
12    See the README file in the top-level LAMMPS directory.
13 ------------------------------------------------------------------------- */
14 
15 #include "special.h"
16 
17 #include "accelerator_kokkos.h"  // IWYU pragma: export
18 #include "atom.h"
19 #include "atom_masks.h"
20 #include "atom_vec.h"
21 #include "comm.h"
22 #include "fix.h"
23 #include "force.h"
24 #include "memory.h"
25 #include "modify.h"
26 
27 using namespace LAMMPS_NS;
28 
29 #define RVOUS 1   // 0 for irregular, 1 for all2all
30 
31 /* ---------------------------------------------------------------------- */
32 
Special(LAMMPS * lmp)33 Special::Special(LAMMPS *lmp) : Pointers(lmp)
34 {
35   MPI_Comm_rank(world,&me);
36   MPI_Comm_size(world,&nprocs);
37 
38   onetwo = onethree = onefour = nullptr;
39 }
40 
41 /* ---------------------------------------------------------------------- */
42 
~Special()43 Special::~Special()
44 {
45   memory->destroy(onetwo);
46   memory->destroy(onethree);
47   memory->destroy(onefour);
48 }
49 
50 /* ----------------------------------------------------------------------
51    create 1-2, 1-3, 1-4 lists of topology neighbors
52    store in onetwo, onethree, onefour for each atom
53    store 3 counters in nspecial[i]
54 ------------------------------------------------------------------------- */
55 
build()56 void Special::build()
57 {
58   MPI_Barrier(world);
59   double time1 = MPI_Wtime();
60 
61   if (me == 0) {
62     const double * const special_lj   = force->special_lj;
63     const double * const special_coul = force->special_coul;
64     auto mesg = fmt::format("Finding 1-2 1-3 1-4 neighbors ...\n"
65                             "  special bond factors lj:    {:<8} {:<8} {:<8}\n"
66                             "  special bond factors coul:  {:<8} {:<8} {:<8}\n",
67                             special_lj[1],special_lj[2],special_lj[3],
68                             special_coul[1],special_coul[2],special_coul[3]);
69     utils::logmesg(lmp,mesg);
70   }
71 
72   // initialize nspecial counters to 0
73 
74   int **nspecial = atom->nspecial;
75   int nlocal = atom->nlocal;
76 
77   for (int i = 0; i < nlocal; i++) {
78     nspecial[i][0] = 0;
79     nspecial[i][1] = 0;
80     nspecial[i][2] = 0;
81   }
82 
83   // setup atomIDs and procowner vectors in rendezvous decomposition
84 
85   atom_owners();
86 
87   // tally nspecial[i][0] = # of 1-2 neighbors of atom i
88   // create onetwo[i] = list of 1-2 neighbors for atom i
89 
90   if (force->newton_bond) onetwo_build_newton();
91   else onetwo_build_newton_off();
92 
93   // print max # of 1-2 neighbors
94 
95   if (me == 0)
96     utils::logmesg(lmp,"{:>6} = max # of 1-2 neighbors\n",maxall);
97 
98   // done if special_bond weights for 1-3, 1-4 are set to 1.0
99 
100   if (force->special_lj[2] == 1.0 && force->special_coul[2] == 1.0 &&
101       force->special_lj[3] == 1.0 && force->special_coul[3] == 1.0) {
102     dedup();
103     combine();
104     fix_alteration();
105     memory->destroy(procowner);
106     memory->destroy(atomIDs);
107     timer_output(time1);
108     return;
109   }
110 
111   // tally nspecial[i][1] = # of 1-3 neighbors of atom i
112   // create onethree[i] = list of 1-3 neighbors for atom i
113 
114   onethree_build();
115 
116   // print max # of 1-3 neighbors
117 
118   if (me == 0)
119     utils::logmesg(lmp,"{:>6} = max # of 1-3 neighbors\n",maxall);
120 
121   // done if special_bond weights for 1-4 are set to 1.0
122 
123   if (force->special_lj[3] == 1.0 && force->special_coul[3] == 1.0) {
124     dedup();
125     if (force->special_angle) angle_trim();
126     combine();
127     fix_alteration();
128     memory->destroy(procowner);
129     memory->destroy(atomIDs);
130     timer_output(time1);
131     return;
132   }
133 
134   // tally nspecial[i][2] = # of 1-4 neighbors of atom i
135   // create onefour[i] = list of 1-4 neighbors for atom i
136 
137   onefour_build();
138 
139   // print max # of 1-4 neighbors
140 
141   if (me == 0)
142     utils::logmesg(lmp,"{:>6} = max # of 1-4 neighbors\n",maxall);
143 
144   // finish processing the onetwo, onethree, onefour lists
145 
146   dedup();
147   if (force->special_angle) angle_trim();
148   if (force->special_dihedral) dihedral_trim();
149   combine();
150   fix_alteration();
151   memory->destroy(procowner);
152   memory->destroy(atomIDs);
153 
154   timer_output(time1);
155 }
156 
157 /* ----------------------------------------------------------------------
158    setup atomIDs and procowner
159 ------------------------------------------------------------------------- */
160 
atom_owners()161 void Special::atom_owners()
162 {
163   tagint *tag = atom->tag;
164   int nlocal = atom->nlocal;
165 
166   int *proclist;
167   memory->create(proclist,nlocal,"special:proclist");
168   IDRvous *idbuf = (IDRvous *)
169     memory->smalloc((bigint) nlocal*sizeof(IDRvous),"special:idbuf");
170 
171   // setup input buf for rendezvous comm
172   // one datum for each owned atom: datum = owning proc, atomID
173   // each proc assigned every 1/Pth atom
174 
175   for (int i = 0; i < nlocal; i++) {
176     proclist[i] = tag[i] % nprocs;
177     idbuf[i].me = me;
178     idbuf[i].atomID = tag[i];
179   }
180 
181   // perform rendezvous operation
182 
183   char *buf;
184   comm->rendezvous(RVOUS,nlocal,(char *) idbuf,sizeof(IDRvous),0,proclist,
185                    rendezvous_ids,0,buf,0,(void *) this);
186 
187   memory->destroy(proclist);
188   memory->sfree(idbuf);
189 }
190 
191 /* ----------------------------------------------------------------------
192    onetwo build when newton_bond flag on
193    uses rendezvous comm
194 ------------------------------------------------------------------------- */
195 
onetwo_build_newton()196 void Special::onetwo_build_newton()
197 {
198   int i,j,m;
199 
200   tagint *tag = atom->tag;
201   int *num_bond = atom->num_bond;
202   tagint **bond_atom = atom->bond_atom;
203   int **nspecial = atom->nspecial;
204   int nlocal = atom->nlocal;
205 
206   // nsend = # of my datums to send
207 
208   int nsend = 0;
209   for (i = 0; i < nlocal; i++) {
210     for (j = 0; j < num_bond[i]; j++) {
211       m = atom->map(bond_atom[i][j]);
212       if (m < 0 || m >= nlocal) nsend++;
213     }
214   }
215 
216   int *proclist;
217   memory->create(proclist,nsend,"special:proclist");
218   PairRvous *inbuf = (PairRvous *)
219     memory->smalloc((bigint) nsend*sizeof(PairRvous),"special:inbuf");
220 
221   // setup input buf to rendezvous comm
222   // one datum for each unowned bond partner: bond partner ID, atomID
223   // owning proc for each datum = bond partner ID % nprocs
224 
225   nsend = 0;
226   for (i = 0; i < nlocal; i++) {
227     for (j = 0; j < num_bond[i]; j++) {
228       m = atom->map(bond_atom[i][j]);
229       if (m >= 0 && m < nlocal) continue;
230       proclist[nsend] = bond_atom[i][j] % nprocs;
231       inbuf[nsend].atomID = bond_atom[i][j];
232       inbuf[nsend].partnerID = tag[i];
233       nsend++;
234     }
235   }
236 
237   // perform rendezvous operation
238 
239   char *buf;
240   int nreturn = comm->rendezvous(RVOUS,nsend,(char *) inbuf,sizeof(PairRvous), 0,proclist,
241                                  rendezvous_pairs,0,buf,sizeof(PairRvous), (void *) this);
242   PairRvous *outbuf = (PairRvous *) buf;
243 
244   memory->destroy(proclist);
245   memory->sfree(inbuf);
246 
247   // set nspecial[0] and onetwo for all owned atoms
248   // based on owned info plus rendezvous output info
249   // output datums = pairs of atoms that are 1-2 neighbors
250 
251   for (i = 0; i < nlocal; i++) {
252     for (j = 0; j < num_bond[i]; j++) {
253       nspecial[i][0]++;
254       m = atom->map(bond_atom[i][j]);
255       if (m >= 0 && m < nlocal) nspecial[m][0]++;
256     }
257   }
258 
259   for (m = 0; m < nreturn; m++) {
260     i = atom->map(outbuf[m].atomID);
261     nspecial[i][0]++;
262   }
263 
264   int max = 0;
265   for (i = 0; i < nlocal; i++)
266     max = MAX(max,nspecial[i][0]);
267 
268   MPI_Allreduce(&max,&maxall,1,MPI_INT,MPI_MAX,world);
269   memory->create(onetwo,nlocal,maxall,"special:onetwo");
270 
271   for (i = 0; i < nlocal; i++) nspecial[i][0] = 0;
272 
273   for (i = 0; i < nlocal; i++) {
274     for (j = 0; j < num_bond[i]; j++) {
275       onetwo[i][nspecial[i][0]++] = bond_atom[i][j];
276       m = atom->map(bond_atom[i][j]);
277       if (m >= 0 && m < nlocal) onetwo[m][nspecial[m][0]++] = tag[i];
278     }
279   }
280 
281   for (m = 0; m < nreturn; m++) {
282     i = atom->map(outbuf[m].atomID);
283     onetwo[i][nspecial[i][0]++] = outbuf[m].partnerID;
284   }
285 
286   memory->sfree(outbuf);
287 }
288 
289 /* ----------------------------------------------------------------------
290    onetwo build with newton_bond flag off
291    no need for rendezvous comm
292 ------------------------------------------------------------------------- */
293 
onetwo_build_newton_off()294 void Special::onetwo_build_newton_off()
295 {
296   int i,j;
297 
298   int *num_bond = atom->num_bond;
299   tagint **bond_atom = atom->bond_atom;
300   int **nspecial = atom->nspecial;
301   int nlocal = atom->nlocal;
302 
303   int max = 0;
304   for (i = 0; i < nlocal; i++)
305     max = MAX(max,num_bond[i]);
306 
307   MPI_Allreduce(&max,&maxall,1,MPI_INT,MPI_MAX,world);
308   memory->create(onetwo,nlocal,maxall,"special:onetwo");
309 
310   // nsend = # of my datums to send
311   // include nlocal datums with owner of each atom
312 
313   for (i = 0; i < nlocal; i++) {
314     nspecial[i][0] = num_bond[i];
315     for (j = 0; j < num_bond[i]; j++)
316       onetwo[i][j] = bond_atom[i][j];
317   }
318 }
319 
320 /* ----------------------------------------------------------------------
321    onethree build
322    uses rendezvous comm
323 ------------------------------------------------------------------------- */
324 
onethree_build()325 void Special::onethree_build()
326 {
327   int i,j,k,m,proc;
328 
329   int **nspecial = atom->nspecial;
330   int nlocal = atom->nlocal;
331 
332   // nsend = # of my datums to send
333 
334   int nsend = 0;
335   for (i = 0; i < nlocal; i++) {
336     for (j = 0; j < nspecial[i][0]; j++) {
337       m = atom->map(onetwo[i][j]);
338       if (m < 0 || m >= nlocal) nsend += nspecial[i][0]-1;
339     }
340   }
341 
342   int *proclist;
343   memory->create(proclist,nsend,"special:proclist");
344   PairRvous *inbuf = (PairRvous *)
345     memory->smalloc((bigint) nsend*sizeof(PairRvous),"special:inbuf");
346 
347   // setup input buf to rendezvous comm
348   // datums = pairs of onetwo partners where either is unknown
349   //          these pairs are onethree neighbors
350   //          datum = onetwo ID, onetwo ID
351   // owning proc for each datum = onetwo ID % nprocs
352 
353   nsend = 0;
354   for (i = 0; i < nlocal; i++) {
355     for (j = 0; j < nspecial[i][0]; j++) {
356       m = atom->map(onetwo[i][j]);
357       if (m >= 0 && m < nlocal) continue;
358       proc = onetwo[i][j] % nprocs;
359       for (k = 0; k < nspecial[i][0]; k++) {
360         if (j == k) continue;
361         proclist[nsend] = proc;
362         inbuf[nsend].atomID = onetwo[i][j];
363         inbuf[nsend].partnerID = onetwo[i][k];
364         nsend++;
365       }
366     }
367   }
368 
369   // perform rendezvous operation
370 
371   char *buf;
372   int nreturn = comm->rendezvous(RVOUS,nsend,(char *) inbuf,sizeof(PairRvous), 0,proclist,
373                                  rendezvous_pairs,0,buf,sizeof(PairRvous), (void *) this);
374   PairRvous *outbuf = (PairRvous *) buf;
375 
376   memory->destroy(proclist);
377   memory->sfree(inbuf);
378 
379   // set nspecial[1] and onethree for all owned atoms
380   // based on owned info plus rendezvous output info
381   // output datums = pairs of atoms that are 1-3 neighbors
382 
383   for (i = 0; i < nlocal; i++) {
384     for (j = 0; j < nspecial[i][0]; j++) {
385       m = atom->map(onetwo[i][j]);
386       if (m >= 0 && m < nlocal) nspecial[m][1] += nspecial[i][0]-1;
387     }
388   }
389 
390   for (m = 0; m < nreturn; m++) {
391     i = atom->map(outbuf[m].atomID);
392     nspecial[i][1]++;
393   }
394 
395   int max = 0;
396   for (i = 0; i < nlocal; i++)
397     max = MAX(max,nspecial[i][1]);
398 
399   MPI_Allreduce(&max,&maxall,1,MPI_INT,MPI_MAX,world);
400   memory->create(onethree,nlocal,maxall,"special:onethree");
401 
402   for (i = 0; i < nlocal; i++) nspecial[i][1] = 0;
403 
404   for (i = 0; i < nlocal; i++) {
405     for (j = 0; j < nspecial[i][0]; j++) {
406       m = atom->map(onetwo[i][j]);
407       if (m < 0 || m >= nlocal) continue;
408       for (k = 0; k < nspecial[i][0]; k++) {
409         if (j == k) continue;
410         onethree[m][nspecial[m][1]++] = onetwo[i][k];
411       }
412     }
413   }
414 
415   for (m = 0; m < nreturn; m++) {
416     i = atom->map(outbuf[m].atomID);
417     onethree[i][nspecial[i][1]++] = outbuf[m].partnerID;
418   }
419 
420   memory->sfree(outbuf);
421 }
422 
423 /* ----------------------------------------------------------------------
424    onefour build
425    uses rendezvous comm
426 ------------------------------------------------------------------------- */
427 
onefour_build()428 void Special::onefour_build()
429 {
430   int i,j,k,m,proc;
431 
432   int **nspecial = atom->nspecial;
433   int nlocal = atom->nlocal;
434 
435   // nsend = # of my datums to send
436 
437   int nsend = 0;
438   for (i = 0; i < nlocal; i++) {
439     for (j = 0; j < nspecial[i][1]; j++) {
440       m = atom->map(onethree[i][j]);
441       if (m < 0 || m >= nlocal) nsend += nspecial[i][0];
442     }
443   }
444 
445   int *proclist;
446   memory->create(proclist,nsend,"special:proclist");
447   PairRvous *inbuf = (PairRvous *)
448     memory->smalloc((bigint) nsend*sizeof(PairRvous),"special:inbuf");
449 
450   // setup input buf to rendezvous comm
451   // datums = pairs of onethree and onetwo partners where onethree is unknown
452   //          these pairs are onefour neighbors
453   //          datum = onetwo ID, onetwo ID
454   // owning proc for each datum = onethree ID % nprocs
455 
456   nsend = 0;
457   for (i = 0; i < nlocal; i++) {
458     for (j = 0; j < nspecial[i][1]; j++) {
459       m = atom->map(onethree[i][j]);
460       if (m >= 0 && m < nlocal) continue;
461       proc = onethree[i][j] % nprocs;
462       for (k = 0; k < nspecial[i][0]; k++) {
463         proclist[nsend] = proc;
464         inbuf[nsend].atomID = onethree[i][j];
465         inbuf[nsend].partnerID = onetwo[i][k];
466         nsend++;
467       }
468     }
469   }
470 
471   // perform rendezvous operation
472 
473   char *buf;
474   int nreturn = comm->rendezvous(RVOUS,nsend,(char *) inbuf,sizeof(PairRvous), 0,proclist,
475                                  rendezvous_pairs,0,buf,sizeof(PairRvous), (void *) this);
476   PairRvous *outbuf = (PairRvous *) buf;
477 
478   memory->destroy(proclist);
479   memory->sfree(inbuf);
480 
481   // set nspecial[2] and onefour for all owned atoms
482   // based on owned info plus rendezvous output info
483   // output datums = pairs of atoms that are 1-4 neighbors
484 
485   for (i = 0; i < nlocal; i++) {
486     for (j = 0; j < nspecial[i][1]; j++) {
487       m = atom->map(onethree[i][j]);
488       if (m >= 0 && m < nlocal) nspecial[m][2] += nspecial[i][0];
489     }
490   }
491 
492   for (m = 0; m < nreturn; m++) {
493     i = atom->map(outbuf[m].atomID);
494     nspecial[i][2]++;
495   }
496 
497   int max = 0;
498   for (i = 0; i < nlocal; i++)
499     max = MAX(max,nspecial[i][2]);
500 
501   MPI_Allreduce(&max,&maxall,1,MPI_INT,MPI_MAX,world);
502   memory->create(onefour,nlocal,maxall,"special:onefour");
503 
504   for (i = 0; i < nlocal; i++) nspecial[i][2] = 0;
505 
506   for (i = 0; i < nlocal; i++) {
507     for (j = 0; j < nspecial[i][1]; j++) {
508       m = atom->map(onethree[i][j]);
509       if (m < 0 || m >= nlocal) continue;
510       for (k = 0; k < nspecial[i][0]; k++) {
511         onefour[m][nspecial[m][2]++] = onetwo[i][k];
512       }
513     }
514   }
515 
516   for (m = 0; m < nreturn; m++) {
517     i = atom->map(outbuf[m].atomID);
518     onefour[i][nspecial[i][2]++] = outbuf[m].partnerID;
519   }
520 
521   memory->sfree(outbuf);
522 }
523 
524 /* ----------------------------------------------------------------------
525    remove duplicates within each of onetwo, onethree, onefour individually
526 ------------------------------------------------------------------------- */
527 
dedup()528 void Special::dedup()
529 {
530   int i,j;
531   tagint m;
532 
533   // clear map so it can be used as scratch space
534 
535   atom->map_clear();
536 
537   // use map to cull duplicates
538   // exclude original atom explicitly
539   // adjust onetwo, onethree, onefour values to reflect removed duplicates
540   // must unset map for each atom
541 
542   int **nspecial = atom->nspecial;
543   tagint *tag = atom->tag;
544   int nlocal = atom->nlocal;
545 
546   int unique;
547 
548   for (i = 0; i < nlocal; i++) {
549     unique = 0;
550     atom->map_one(tag[i],0);
551     for (j = 0; j < nspecial[i][0]; j++) {
552       m = onetwo[i][j];
553       if (atom->map(m) < 0) {
554         onetwo[i][unique++] = m;
555         atom->map_one(m,0);
556       }
557     }
558     nspecial[i][0] = unique;
559     atom->map_one(tag[i],-1);
560     for (j = 0; j < unique; j++) atom->map_one(onetwo[i][j],-1);
561   }
562 
563   for (i = 0; i < nlocal; i++) {
564     unique = 0;
565     atom->map_one(tag[i],0);
566     for (j = 0; j < nspecial[i][1]; j++) {
567       m = onethree[i][j];
568       if (atom->map(m) < 0) {
569         onethree[i][unique++] = m;
570         atom->map_one(m,0);
571       }
572     }
573     nspecial[i][1] = unique;
574     atom->map_one(tag[i],-1);
575     for (j = 0; j < unique; j++) atom->map_one(onethree[i][j],-1);
576   }
577 
578   for (i = 0; i < nlocal; i++) {
579     unique = 0;
580     atom->map_one(tag[i],0);
581     for (j = 0; j < nspecial[i][2]; j++) {
582       m = onefour[i][j];
583       if (atom->map(m) < 0) {
584         onefour[i][unique++] = m;
585         atom->map_one(m,0);
586       }
587     }
588     nspecial[i][2] = unique;
589     atom->map_one(tag[i],-1);
590     for (j = 0; j < unique; j++) atom->map_one(onefour[i][j],-1);
591   }
592 
593   // re-create map
594 
595   atom->map_init(0);
596   atom->nghost = 0;
597   atom->map_set();
598 }
599 
600 /* ----------------------------------------------------------------------
601    concatenate onetwo, onethree, onefour into master atom->special list
602    remove duplicates between 3 lists, leave dup in first list it appears in
603    convert nspecial[0], nspecial[1], nspecial[2] into cumulative counters
604 ------------------------------------------------------------------------- */
605 
combine()606 void Special::combine()
607 {
608   int i,j;
609   tagint m;
610 
611   int me;
612   MPI_Comm_rank(world,&me);
613 
614   int **nspecial = atom->nspecial;
615   tagint *tag = atom->tag;
616   int nlocal = atom->nlocal;
617 
618   // ----------------------------------------------------
619   // compute culled maxspecial = max # of special neighs of any atom
620   // ----------------------------------------------------
621 
622   // clear map so it can be used as scratch space
623 
624   atom->map_clear();
625 
626   // unique = # of unique nspecial neighbors of one atom
627   // cull duplicates using map to check for them
628   // exclude original atom explicitly
629   // must unset map for each atom
630 
631   int unique;
632   int maxspecial = 0;
633 
634   for (i = 0; i < nlocal; i++) {
635     unique = 0;
636     atom->map_one(tag[i],0);
637 
638     for (j = 0; j < nspecial[i][0]; j++) {
639       m = onetwo[i][j];
640       if (atom->map(m) < 0) {
641         unique++;
642         atom->map_one(m,0);
643       }
644     }
645     for (j = 0; j < nspecial[i][1]; j++) {
646       m = onethree[i][j];
647       if (atom->map(m) < 0) {
648         unique++;
649         atom->map_one(m,0);
650       }
651     }
652     for (j = 0; j < nspecial[i][2]; j++) {
653       m = onefour[i][j];
654       if (atom->map(m) < 0) {
655         unique++;
656         atom->map_one(m,0);
657       }
658     }
659 
660     maxspecial = MAX(maxspecial,unique);
661 
662     atom->map_one(tag[i],-1);
663     for (j = 0; j < nspecial[i][0]; j++) atom->map_one(onetwo[i][j],-1);
664     for (j = 0; j < nspecial[i][1]; j++) atom->map_one(onethree[i][j],-1);
665     for (j = 0; j < nspecial[i][2]; j++) atom->map_one(onefour[i][j],-1);
666   }
667 
668   // if atom->maxspecial has been updated before, make certain
669   // we do not reset it to a smaller value. Since atom->maxspecial
670   // is initialized to 1, this ensures that it is larger than zero.
671 
672   maxspecial = MAX(atom->maxspecial,maxspecial);
673 
674   // compute global maxspecial
675   // add in extra factor from special_bonds command
676   // allocate correct special array with same nmax, new maxspecial
677   // previously allocated one must be destroyed
678   // must make AtomVec class update its ptr to special
679 
680   MPI_Allreduce(&maxspecial,&atom->maxspecial,1,MPI_INT,MPI_MAX,world);
681   atom->maxspecial += force->special_extra;
682 
683   // add force->special_extra only once
684 
685   force->special_extra = 0;
686 
687   if (me == 0)
688     utils::logmesg(lmp,"{:>6} = max # of special neighbors\n",atom->maxspecial);
689 
690   if (lmp->kokkos) {
691     AtomKokkos* atomKK = (AtomKokkos*) atom;
692     atomKK->modified(Host,SPECIAL_MASK);
693     atomKK->sync(Device,SPECIAL_MASK);
694     MemoryKokkos* memoryKK = (MemoryKokkos*) memory;
695     memoryKK->grow_kokkos(atomKK->k_special,atom->special,
696                         atom->nmax,atom->maxspecial,"atom:special");
697     atomKK->modified(Device,SPECIAL_MASK);
698     atomKK->sync(Host,SPECIAL_MASK);
699     atom->avec->grow_pointers();
700   } else {
701     memory->destroy(atom->special);
702     memory->create(atom->special,atom->nmax,atom->maxspecial,"atom:special");
703   }
704 
705   tagint **special = atom->special;
706 
707   // ----------------------------------------------------
708   // fill special array with 1-2, 1-3, 1-4 neighs for each atom
709   // ----------------------------------------------------
710 
711   // again use map to cull duplicates
712   // exclude original atom explicitly
713   // adjust nspecial[i] values to reflect removed duplicates
714   // nspecial[i][1] and nspecial[i][2] now become cumulative counters
715 
716   for (i = 0; i < nlocal; i++) {
717     unique = 0;
718     atom->map_one(tag[i],0);
719 
720     for (j = 0; j < nspecial[i][0]; j++) {
721       m = onetwo[i][j];
722       if (atom->map(m) < 0) {
723         special[i][unique++] = m;
724         atom->map_one(m,0);
725       }
726     }
727     nspecial[i][0] = unique;
728 
729     for (j = 0; j < nspecial[i][1]; j++) {
730       m = onethree[i][j];
731       if (atom->map(m) < 0) {
732         special[i][unique++] = m;
733         atom->map_one(m,0);
734       }
735     }
736     nspecial[i][1] = unique;
737 
738     for (j = 0; j < nspecial[i][2]; j++) {
739       m = onefour[i][j];
740       if (atom->map(m) < 0) {
741         special[i][unique++] = m;
742         atom->map_one(m,0);
743       }
744     }
745     nspecial[i][2] = unique;
746 
747     atom->map_one(tag[i],-1);
748     for (j = 0; j < nspecial[i][2]; j++) atom->map_one(special[i][j],-1);
749   }
750 
751   // re-create map
752 
753   atom->map_init(0);
754   atom->nghost = 0;
755   atom->map_set();
756 }
757 
758 /* ----------------------------------------------------------------------
759    trim list of 1-3 neighbors by checking defined angles
760    delete a 1-3 neigh if they are not end atoms of a defined angle
761      and if they are not 1,3 or 2,4 atoms of a defined dihedral
762    uses rendezvous comm
763 ------------------------------------------------------------------------- */
764 
angle_trim()765 void Special::angle_trim()
766 {
767   int i,j,k,m;;
768 
769   int *num_angle = atom->num_angle;
770   int *num_dihedral = atom->num_dihedral;
771   tagint **angle_atom1 = atom->angle_atom1;
772   tagint **angle_atom2 = atom->angle_atom2;
773   tagint **angle_atom3 = atom->angle_atom3;
774   tagint **dihedral_atom1 = atom->dihedral_atom1;
775   tagint **dihedral_atom2 = atom->dihedral_atom2;
776   tagint **dihedral_atom3 = atom->dihedral_atom3;
777   tagint **dihedral_atom4 = atom->dihedral_atom4;
778   int **nspecial = atom->nspecial;
779   tagint *tag = atom->tag;
780   int nlocal = atom->nlocal;
781 
782   // stats on old 1-3 neighbor counts
783 
784   double onethreecount = 0.0;
785   for (i = 0; i < nlocal; i++) onethreecount += nspecial[i][1];
786   double allcount;
787   MPI_Allreduce(&onethreecount,&allcount,1,MPI_DOUBLE,MPI_SUM,world);
788 
789   if (me == 0)
790     utils::logmesg(lmp,"  {} = # of 1-3 neighbors before angle trim\n",
791                    allcount);
792 
793   // if angles or dihedrals are defined
794   // rendezvous angle 1-3 and dihedral 1-3,2-4 pairs
795 
796   if ((num_angle && atom->nangles) || (num_dihedral && atom->ndihedrals)) {
797 
798     // nsend = # of my datums to send
799     // latter is only for angles or dihedrlas where I own atom2 (newton bond off)
800 
801     int nsend = 0;
802     for (i = 0; i < nlocal; i++) {
803       if (num_angle) {
804         for (j = 0; j < num_angle[i]; j++) {
805           if (tag[i] != angle_atom2[i][j]) continue;
806           m = atom->map(angle_atom1[i][j]);
807           if (m < 0 || m >= nlocal) nsend++;
808           m = atom->map(angle_atom3[i][j]);
809           if (m < 0 || m >= nlocal) nsend++;
810         }
811       }
812 
813       if (num_dihedral) {
814         for (j = 0; j < num_dihedral[i]; j++) {
815           if (tag[i] != dihedral_atom2[i][j]) continue;
816           m = atom->map(dihedral_atom1[i][j]);
817           if (m < 0 || m >= nlocal) nsend++;
818           m = atom->map(dihedral_atom3[i][j]);
819           if (m < 0 || m >= nlocal) nsend++;
820           m = atom->map(dihedral_atom4[i][j]);
821           if (m < 0 || m >= nlocal) nsend++;
822         }
823       }
824     }
825 
826     int *proclist;
827     memory->create(proclist,nsend,"special:proclist");
828     PairRvous *inbuf = (PairRvous *)
829       memory->smalloc((bigint) nsend*sizeof(PairRvous),"special:inbuf");
830 
831     // setup input buf to rendezvous comm
832     // datums = pairs of onetwo partners where either is unknown
833     //          these pairs are onethree neighbors
834     //          datum = onetwo ID, onetwo ID
835     // owning proc for each datum = onetwo ID % nprocs
836 
837     nsend = 0;
838     for (i = 0; i < nlocal; i++) {
839       if (num_angle) {
840         for (j = 0; j < num_angle[i]; j++) {
841           if (tag[i] != angle_atom2[i][j]) continue;
842 
843           m = atom->map(angle_atom1[i][j]);
844           if (m < 0 || m >= nlocal) {
845             proclist[nsend] = angle_atom1[i][j] % nprocs;
846             inbuf[nsend].atomID = angle_atom1[i][j];
847             inbuf[nsend].partnerID = angle_atom3[i][j];
848             nsend++;
849           }
850 
851           m = atom->map(angle_atom3[i][j]);
852           if (m < 0 || m >= nlocal) {
853             proclist[nsend] = angle_atom3[i][j] % nprocs;
854             inbuf[nsend].atomID = angle_atom3[i][j];
855             inbuf[nsend].partnerID = angle_atom1[i][j];
856             nsend++;
857           }
858         }
859       }
860 
861       if (num_dihedral) {
862         for (j = 0; j < num_dihedral[i]; j++) {
863           if (tag[i] != dihedral_atom2[i][j]) continue;
864 
865           m = atom->map(dihedral_atom1[i][j]);
866           if (m < 0 || m >= nlocal) {
867             proclist[nsend] = dihedral_atom1[i][j] % nprocs;
868             inbuf[nsend].atomID = dihedral_atom1[i][j];
869             inbuf[nsend].partnerID = dihedral_atom3[i][j];
870             nsend++;
871           }
872 
873           m = atom->map(dihedral_atom3[i][j]);
874           if (m < 0 || m >= nlocal) {
875             proclist[nsend] = dihedral_atom3[i][j] % nprocs;
876             inbuf[nsend].atomID = dihedral_atom3[i][j];
877             inbuf[nsend].partnerID = dihedral_atom1[i][j];
878             nsend++;
879           }
880 
881           m = atom->map(dihedral_atom4[i][j]);
882           if (m < 0 || m >= nlocal) {
883             proclist[nsend] = dihedral_atom4[i][j] % nprocs;
884             inbuf[nsend].atomID = dihedral_atom4[i][j];
885             inbuf[nsend].partnerID = dihedral_atom2[i][j];
886             nsend++;
887           }
888         }
889       }
890     }
891 
892     // perform rendezvous operation
893 
894     char *buf;
895     int nreturn = comm->rendezvous(RVOUS,nsend,(char *) inbuf,sizeof(PairRvous), 0,proclist,
896                                    rendezvous_pairs,0,buf,sizeof(PairRvous), (void *) this);
897     PairRvous *outbuf = (PairRvous *) buf;
898 
899     memory->destroy(proclist);
900     memory->sfree(inbuf);
901 
902     // flag all onethree atoms to keep
903 
904     int max = 0;
905     for (i = 0; i < nlocal; i++)
906       max = MAX(max,nspecial[i][1]);
907     MPI_Allreduce(&max,&maxall,1,MPI_INT,MPI_MAX,world);
908 
909     int **flag;
910     memory->create(flag,nlocal,maxall,"special:flag");
911 
912     for (i = 0; i < nlocal; i++)
913       for (j = 0; j < nspecial[i][1]; j++)
914         flag[i][j] = 0;
915 
916     // reset nspecial[1] and onethree for all owned atoms based on output info
917     // based on owned info plus rendezvous output info
918     // output datums = pairs of atoms that are 1-3 neighbors
919 
920     for (i = 0; i < nlocal; i++) {
921       if (num_angle) {
922         for (j = 0; j < num_angle[i]; j++) {
923           if (tag[i] != angle_atom2[i][j]) continue;
924 
925           m = atom->map(angle_atom1[i][j]);
926           if (m >= 0 && m < nlocal) {
927             for (k = 0; k < nspecial[m][1]; k++)
928               if (onethree[m][k] == angle_atom3[i][j]) {
929                 flag[m][k] = 1;
930                 break;
931               }
932           }
933 
934           m = atom->map(angle_atom3[i][j]);
935           if (m >= 0 && m < nlocal) {
936             for (k = 0; k < nspecial[m][1]; k++)
937               if (onethree[m][k] == angle_atom1[i][j]) {
938                 flag[m][k] = 1;
939                 break;
940               }
941           }
942         }
943       }
944 
945       if (num_dihedral) {
946         for (j = 0; j < num_dihedral[i]; j++) {
947           if (tag[i] != dihedral_atom2[i][j]) continue;
948 
949           m = atom->map(dihedral_atom1[i][j]);
950           if (m >= 0 && m < nlocal) {
951             for (k = 0; k < nspecial[m][1]; k++)
952               if (onethree[m][k] == dihedral_atom3[i][j]) {
953                 flag[m][k] = 1;
954                 break;
955               }
956           }
957 
958           m = atom->map(dihedral_atom3[i][j]);
959           if (m >= 0 && m < nlocal) {
960             for (k = 0; k < nspecial[m][1]; k++)
961               if (onethree[m][k] == dihedral_atom1[i][j]) {
962                 flag[m][k] = 1;
963                 break;
964               }
965           }
966 
967           m = atom->map(dihedral_atom4[i][j]);
968           if (m >= 0 && m < nlocal) {
969             for (k = 0; k < nspecial[m][1]; k++)
970               if (onethree[m][k] == dihedral_atom2[i][j]) {
971                 flag[m][k] = 1;
972                 break;
973               }
974           }
975         }
976       }
977     }
978 
979     for (m = 0; m < nreturn; m++) {
980       i = atom->map(outbuf[m].atomID);
981       for (k = 0; k < nspecial[i][1]; k++)
982         if (onethree[i][k] == outbuf[m].partnerID) {
983           flag[i][k] = 1;
984           break;
985         }
986     }
987 
988     memory->destroy(outbuf);
989 
990     // use flag values to compress onefour list for each atom
991 
992     for (i = 0; i < nlocal; i++) {
993       j = 0;
994       while (j < nspecial[i][1]) {
995         if (flag[i][j] == 0) {
996           onethree[i][j] = onethree[i][nspecial[i][1]-1];
997           flag[i][j] = flag[i][nspecial[i][1]-1];
998           nspecial[i][1]--;
999         } else j++;
1000       }
1001     }
1002 
1003     memory->destroy(flag);
1004 
1005     // if no angles or dihedrals are defined, delete all 1-3 neighs
1006 
1007   } else {
1008     for (i = 0; i < nlocal; i++) nspecial[i][1] = 0;
1009   }
1010 
1011   // stats on new 1-3 neighbor counts
1012 
1013   onethreecount = 0.0;
1014   for (i = 0; i < nlocal; i++) onethreecount += nspecial[i][1];
1015   MPI_Allreduce(&onethreecount,&allcount,1,MPI_DOUBLE,MPI_SUM,world);
1016 
1017   if (me == 0)
1018     utils::logmesg(lmp,"  {} = # of 1-3 neighbors after angle trim\n",
1019                    allcount);
1020 }
1021 
1022 /* ----------------------------------------------------------------------
1023    trim list of 1-4 neighbors by checking all defined dihedrals
1024    delete a 1-4 neigh if they are not end atoms of a defined dihedral
1025    uses rendezvous comm
1026 ------------------------------------------------------------------------- */
1027 
dihedral_trim()1028 void Special::dihedral_trim()
1029 {
1030   int i,j,k,m;
1031 
1032   int *num_dihedral = atom->num_dihedral;
1033   tagint **dihedral_atom1 = atom->dihedral_atom1;
1034   tagint **dihedral_atom2 = atom->dihedral_atom2;
1035   tagint **dihedral_atom4 = atom->dihedral_atom4;
1036   int **nspecial = atom->nspecial;
1037   tagint *tag = atom->tag;
1038   int nlocal = atom->nlocal;
1039 
1040   // stats on old 1-4 neighbor counts
1041 
1042   double onefourcount = 0.0;
1043   for (i = 0; i < nlocal; i++) onefourcount += nspecial[i][2];
1044   double allcount;
1045   MPI_Allreduce(&onefourcount,&allcount,1,MPI_DOUBLE,MPI_SUM,world);
1046 
1047   if (me == 0)
1048     utils::logmesg(lmp,"  {} = # of 1-4 neighbors before dihedral trim\n",
1049                    allcount);
1050 
1051   // if dihedrals are defined, rendezvous the dihedral 1-4 pairs
1052 
1053   if (num_dihedral && atom->ndihedrals) {
1054 
1055     // nsend = # of my datums to send
1056 
1057     int nsend = 0;
1058     for (i = 0; i < nlocal; i++) {
1059       for (j = 0; j < num_dihedral[i]; j++) {
1060         if (tag[i] != dihedral_atom2[i][j]) continue;
1061         m = atom->map(dihedral_atom1[i][j]);
1062         if (m < 0 || m >= nlocal) nsend++;
1063         m = atom->map(dihedral_atom4[i][j]);
1064         if (m < 0 || m >= nlocal) nsend++;
1065       }
1066     }
1067 
1068     int *proclist;
1069     memory->create(proclist,nsend,"special:proclist");
1070     PairRvous *inbuf = (PairRvous *)
1071       memory->smalloc((bigint) nsend*sizeof(PairRvous),"special:inbuf");
1072 
1073     // setup input buf to rendezvous comm
1074     // datums = pairs of onefour atom IDs in a dihedral defined for my atoms
1075     //          only dihedrals where I own atom2 (in case newton_bond off)
1076     //          datum = atom1 ID and atom4 ID
1077     //          send the datum twice, to owner of atom1 ID and atom4 ID
1078     // owning procs for each datum = atom1 or atom4 ID % nprocs
1079 
1080     nsend = 0;
1081     for (i = 0; i < nlocal; i++) {
1082       for (j = 0; j < num_dihedral[i]; j++) {
1083         if (tag[i] != dihedral_atom2[i][j]) continue;
1084 
1085         m = atom->map(dihedral_atom1[i][j]);
1086         if (m < 0 || m >= nlocal) {
1087           proclist[nsend] = dihedral_atom1[i][j] % nprocs;
1088           inbuf[nsend].atomID = dihedral_atom1[i][j];
1089           inbuf[nsend].partnerID = dihedral_atom4[i][j];
1090           nsend++;
1091         }
1092 
1093         m = atom->map(dihedral_atom4[i][j]);
1094         if (m < 0 || m >= nlocal) {
1095           proclist[nsend] = dihedral_atom4[i][j] % nprocs;
1096           inbuf[nsend].atomID = dihedral_atom4[i][j];
1097           inbuf[nsend].partnerID = dihedral_atom1[i][j];
1098           nsend++;
1099         }
1100       }
1101     }
1102 
1103     // perform rendezvous operation
1104 
1105     char *buf;
1106     int nreturn = comm->rendezvous(RVOUS,nsend,(char *) inbuf,sizeof(PairRvous), 0,proclist,
1107                                    rendezvous_pairs,0,buf,sizeof(PairRvous), (void *) this);
1108     PairRvous *outbuf = (PairRvous *) buf;
1109 
1110     memory->destroy(proclist);
1111     memory->sfree(inbuf);
1112 
1113     // flag all of my onefour IDs to keep
1114 
1115     int max = 0;
1116     for (i = 0; i < nlocal; i++)
1117       max = MAX(max,nspecial[i][2]);
1118     MPI_Allreduce(&max,&maxall,1,MPI_INT,MPI_MAX,world);
1119 
1120     int **flag;
1121     memory->create(flag,nlocal,maxall,"special:flag");
1122 
1123     for (i = 0; i < nlocal; i++)
1124       for (j = 0; j < nspecial[i][2]; j++)
1125         flag[i][j] = 0;
1126 
1127     for (i = 0; i < nlocal; i++) {
1128       for (j = 0; j < num_dihedral[i]; j++) {
1129         if (tag[i] != dihedral_atom2[i][j]) continue;
1130 
1131         m = atom->map(dihedral_atom1[i][j]);
1132         if (m >= 0 && m < nlocal) {
1133           for (k = 0; k < nspecial[m][2]; k++)
1134             if (onefour[m][k] == dihedral_atom4[i][j]) {
1135               flag[m][k] = 1;
1136               break;
1137             }
1138         }
1139 
1140         m = atom->map(dihedral_atom4[i][j]);
1141         if (m >= 0 && m < nlocal) {
1142           for (k = 0; k < nspecial[m][2]; k++)
1143             if (onefour[m][k] == dihedral_atom1[i][j]) {
1144               flag[m][k] = 1;
1145               break;
1146             }
1147         }
1148       }
1149     }
1150 
1151     for (m = 0; m < nreturn; m++) {
1152       i = atom->map(outbuf[m].atomID);
1153       for (k = 0; k < nspecial[i][2]; k++)
1154         if (onefour[i][k] == outbuf[m].partnerID) {
1155           flag[i][k] = 1;
1156           break;
1157         }
1158     }
1159 
1160     memory->destroy(outbuf);
1161 
1162     // use flag values to compress onefour list for each atom
1163 
1164     for (i = 0; i < nlocal; i++) {
1165       j = 0;
1166       while (j < nspecial[i][2]) {
1167         if (flag[i][j] == 0) {
1168           onefour[i][j] = onefour[i][nspecial[i][2]-1];
1169           flag[i][j] = flag[i][nspecial[i][2]-1];
1170           nspecial[i][2]--;
1171         } else j++;
1172       }
1173     }
1174 
1175     memory->destroy(flag);
1176 
1177   // if no dihedrals are defined, delete all 1-4 neighs
1178 
1179   } else {
1180     for (i = 0; i < nlocal; i++) nspecial[i][2] = 0;
1181   }
1182 
1183   // stats on new 1-4 neighbor counts
1184 
1185   onefourcount = 0.0;
1186   for (i = 0; i < nlocal; i++) onefourcount += nspecial[i][2];
1187   MPI_Allreduce(&onefourcount,&allcount,1,MPI_DOUBLE,MPI_SUM,world);
1188 
1189   if (me == 0)
1190     utils::logmesg(lmp,"  {} = # of 1-4 neighbors after dihedral trim\n",
1191                    allcount);
1192 }
1193 
1194 /* ----------------------------------------------------------------------
1195    process data for atoms assigned to me in rendezvous decomposition
1196    inbuf = list of N IDRvous datums
1197    no outbuf
1198 ------------------------------------------------------------------------- */
1199 
rendezvous_ids(int n,char * inbuf,int & flag,int * &,char * &,void * ptr)1200 int Special::rendezvous_ids(int n, char *inbuf,
1201                             int &flag, int *& /*proclist*/, char *& /*outbuf*/,
1202                             void *ptr)
1203 {
1204   Special *sptr = (Special *) ptr;
1205   Memory *memory = sptr->memory;
1206 
1207   int *procowner;
1208   tagint *atomIDs;
1209 
1210   memory->create(procowner,n,"special:procowner");
1211   memory->create(atomIDs,n,"special:atomIDs");
1212 
1213   IDRvous *in = (IDRvous *) inbuf;
1214 
1215   for (int i = 0; i < n; i++) {
1216     procowner[i] = in[i].me;
1217     atomIDs[i] = in[i].atomID;
1218   }
1219 
1220   // store rendezvous data in Special class
1221 
1222   sptr->nrvous = n;
1223   sptr->procowner = procowner;
1224   sptr->atomIDs = atomIDs;
1225 
1226   // flag = 0: no second comm needed in rendezvous
1227 
1228   flag = 0;
1229   return 0;
1230 }
1231 
1232 
1233 /* ----------------------------------------------------------------------
1234    process data for atoms assigned to me in rendezvous decomposition
1235    inbuf = list of N PairRvous datums
1236    outbuf = same list of N PairRvous datums, routed to different procs
1237 ------------------------------------------------------------------------- */
1238 
rendezvous_pairs(int n,char * inbuf,int & flag,int * & proclist,char * & outbuf,void * ptr)1239 int Special::rendezvous_pairs(int n, char *inbuf, int &flag, int *&proclist,
1240                               char *&outbuf, void *ptr)
1241 {
1242   Special *sptr = (Special *) ptr;
1243   Atom *atom = sptr->atom;
1244   Memory *memory = sptr->memory;
1245 
1246   // clear atom map so it can be used here as a hash table
1247   // faster than an STL map for large atom counts
1248 
1249   atom->map_clear();
1250 
1251   // hash atom IDs stored in rendezvous decomposition
1252 
1253   int nrvous = sptr->nrvous;
1254   tagint *atomIDs = sptr->atomIDs;
1255 
1256   for (int i = 0; i < nrvous; i++)
1257     atom->map_one(atomIDs[i],i);
1258 
1259   // proclist = owner of atomID in caller decomposition
1260 
1261   PairRvous *in = (PairRvous *) inbuf;
1262   int *procowner = sptr->procowner;
1263   memory->create(proclist,n,"special:proclist");
1264 
1265   int m;
1266   for (int i = 0; i < n; i++) {
1267     m = atom->map(in[i].atomID);
1268     proclist[i] = procowner[m];
1269   }
1270 
1271   outbuf = inbuf;
1272 
1273   // re-create atom map
1274 
1275   atom->map_init(0);
1276   atom->nghost = 0;
1277   atom->map_set();
1278 
1279   // flag = 1: outbuf = inbuf
1280 
1281   flag = 1;
1282   return n;
1283 }
1284 
1285 /* ----------------------------------------------------------------------
1286    allow fixes to alter special list
1287    currently, only fix drude does this
1288      so that both the Drude core and electron are same level of neighbor
1289 ------------------------------------------------------------------------- */
1290 
fix_alteration()1291 void Special::fix_alteration()
1292 {
1293   for (int ifix = 0; ifix < modify->nfix; ifix++)
1294     if (modify->fix[ifix]->special_alter_flag)
1295       modify->fix[ifix]->rebuild_special();
1296 }
1297 
1298 /* ----------------------------------------------------------------------
1299    print timing output
1300 ------------------------------------------------------------------------- */
1301 
timer_output(double time1)1302 void Special::timer_output(double time1)
1303 {
1304   if (comm->me == 0)
1305     utils::logmesg(lmp,"  special bonds CPU = {:.3f} seconds\n",
1306                    MPI_Wtime()-time1);
1307 }
1308