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