1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.potential.nonbonded;
39
40 import edu.rit.pj.BarrierAction;
41 import edu.rit.pj.IntegerForLoop;
42 import edu.rit.pj.IntegerSchedule;
43 import edu.rit.pj.ParallelRegion;
44 import edu.rit.pj.ParallelTeam;
45 import edu.rit.pj.reduction.SharedBoolean;
46 import edu.rit.pj.reduction.SharedInteger;
47 import edu.rit.util.Range;
48 import ffx.crystal.Crystal;
49 import ffx.potential.MolecularAssembly;
50 import ffx.potential.bonded.Atom;
51 import ffx.potential.utils.PotentialsUtils;
52
53 import java.io.File;
54 import java.util.ArrayList;
55 import java.util.Collections;
56 import java.util.HashMap;
57 import java.util.HashSet;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.PriorityQueue;
61 import java.util.Set;
62 import java.util.logging.Level;
63 import java.util.logging.Logger;
64
65 import static java.lang.String.format;
66 import static java.lang.System.arraycopy;
67 import static java.util.Arrays.copyOf;
68 import static java.util.Arrays.fill;
69 import static org.apache.commons.math3.util.FastMath.floor;
70 import static org.apache.commons.math3.util.FastMath.min;
71 import static org.apache.commons.math3.util.FastMath.sqrt;
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 public class NeighborList extends ParallelRegion {
101
102
103
104
105 private static final Logger logger = Logger.getLogger(NeighborList.class.getName());
106
107 private final DomainDecomposition domainDecomposition;
108
109
110
111 private final double cutoff;
112
113
114
115
116 private final double buffer;
117
118
119
120 private final double motion2;
121
122
123
124 private final double cutoffPlusBuffer;
125
126
127
128 private final double cutoffPlusBuffer2;
129
130
131
132 private final ParallelTeam parallelTeam;
133
134
135
136 private final int threadCount;
137
138
139
140 private boolean forceRebuild = false;
141
142
143
144 private boolean print = false;
145
146
147
148
149 private final SharedBoolean sharedMotion;
150
151
152
153 private final MotionLoop[] motionLoops;
154
155
156
157 private final ListInitBarrierAction listInitBarrierAction;
158
159
160
161 private final AssignAtomsToCellsLoop[] assignAtomsToCellsLoops;
162
163
164
165 private final VerletListLoop[] verletListLoop;
166 private final GroupVerletListLoop[] groupVerletListLoop;
167
168
169
170 private long motionTime;
171
172
173
174 private long initTime;
175
176
177
178 private long assignAtomsToCellsTime;
179
180
181
182 private long verletListTime;
183
184
185
186 private long groupingTime;
187
188
189
190 private final SharedInteger sharedCount;
191
192
193
194 private final Range[] ranges;
195
196
197
198
199 private Crystal crystal;
200
201
202
203 private int nSymm;
204
205
206
207 private int nAtoms;
208
209
210
211 private double[][] coordinates;
212
213
214
215 private double[] previous;
216
217
218
219 private int[][][] lists;
220
221
222
223 private int M = -1;
224
225
226
227 private int[][] mGroups;
228
229
230
231 private int numGroups;
232
233
234
235 private int[] mGroupID;
236
237
238
239 private int[][][] groupLists;
240
241
242
243 private int[] listCount;
244
245
246
247 private PairwiseSchedule pairwiseSchedule;
248
249
250
251 private int asymmetricUnitCount;
252
253
254
255 private int symmetryMateCount;
256
257
258
259 private int groupAsymmetricUnitCount;
260
261
262
263
264 private int groupSymmetryMateCount;
265
266
267
268 private boolean[] use;
269
270
271
272 private Atom[] atoms;
273
274
275
276 private long time;
277
278
279
280 private boolean intermolecular = true;
281
282
283
284
285
286 private final boolean includeInactivePairs = true;
287
288
289
290 private boolean disableUpdates = false;
291
292
293
294
295
296
297
298
299
300
301
302 public NeighborList(Crystal crystal, Atom[] atoms, double cutoff,
303 double buffer, ParallelTeam parallelTeam) {
304 this.crystal = crystal;
305 this.cutoff = cutoff;
306 this.buffer = buffer;
307 this.parallelTeam = new ParallelTeam(parallelTeam.getThreadCount());
308 this.atoms = atoms;
309 nAtoms = atoms.length;
310
311
312 cutoffPlusBuffer = cutoff + buffer;
313 cutoffPlusBuffer2 = cutoffPlusBuffer * cutoffPlusBuffer;
314 motion2 = (buffer / 2.0) * (buffer / 2.0);
315
316
317 threadCount = parallelTeam.getThreadCount();
318 sharedCount = new SharedInteger();
319 ranges = new Range[threadCount];
320
321 sharedMotion = new SharedBoolean();
322 motionLoops = new MotionLoop[threadCount];
323 listInitBarrierAction = new ListInitBarrierAction();
324 assignAtomsToCellsLoops = new AssignAtomsToCellsLoop[threadCount];
325 verletListLoop = new VerletListLoop[threadCount];
326 groupVerletListLoop = new GroupVerletListLoop[threadCount];
327 for (int i = 0; i < threadCount; i++) {
328 motionLoops[i] = new MotionLoop();
329 verletListLoop[i] = new VerletListLoop();
330 assignAtomsToCellsLoops[i] = new AssignAtomsToCellsLoop();
331 groupVerletListLoop[i] = new GroupVerletListLoop();
332 }
333
334
335 boolean print = logger.isLoggable(Level.FINE);
336 domainDecomposition = new DomainDecomposition(nAtoms, crystal, cutoffPlusBuffer);
337 initNeighborList(print);
338 }
339
340
341
342
343
344
345
346
347
348
349
350
351 public void buildList(final double[][] coordinates, final int[][][] lists, boolean[] use,
352 boolean forceRebuild, boolean print) {
353 if (disableUpdates) {
354 return;
355 }
356 this.coordinates = coordinates;
357 this.lists = lists;
358 this.use = use;
359 this.forceRebuild = forceRebuild;
360 this.print = print;
361
362 try {
363 parallelTeam.execute(this);
364 } catch (Exception e) {
365 String message = "Fatal exception building neighbor list.\n";
366 logger.log(Level.SEVERE, message, e);
367 }
368 }
369
370
371
372
373
374
375
376
377
378 public void setGroupSize(int M) {
379 this.M = M;
380 }
381
382
383
384
385
386
387 public void destroy() throws Exception {
388 parallelTeam.shutdown();
389 }
390
391
392
393
394
395
396
397
398 @Override
399 public void finish() {
400 if (disableUpdates) {
401 return;
402 }
403 if (!forceRebuild && !sharedMotion.get()) {
404 return;
405 }
406
407
408 int atomsWithIteractions = 0;
409 asymmetricUnitCount = 0;
410 symmetryMateCount = 0;
411
412 int[][] list = lists[0];
413 for (int i = 0; i < nAtoms; i++) {
414 asymmetricUnitCount += list[i].length;
415 if (listCount[i] > 0) {
416 atomsWithIteractions++;
417 }
418 }
419 for (int iSymm = 1; iSymm < nSymm; iSymm++) {
420 list = lists[iSymm];
421 for (int i = 0; i < nAtoms; i++) {
422 symmetryMateCount += list[i].length;
423 }
424 }
425
426 groupAsymmetricUnitCount = 0;
427 groupSymmetryMateCount = 0;
428 if (M > 1) {
429
430 list = groupLists[0];
431 for (int i = 0; i < numGroups; i++) {
432 groupAsymmetricUnitCount += list[i].length;
433 }
434
435 for (int iSymm = 1; iSymm < nSymm; iSymm++) {
436 list = groupLists[iSymm];
437 for (int i = 0; i < numGroups; i++) {
438 groupSymmetryMateCount += list[i].length;
439 }
440 }
441 }
442
443
444 if (print) {
445 print();
446 }
447
448
449 long scheduleTime = -System.nanoTime();
450 pairwiseSchedule.updateRanges(sharedCount.get(), atomsWithIteractions, listCount);
451 scheduleTime += System.nanoTime();
452
453 if (logger.isLoggable(Level.FINE)) {
454 time = System.nanoTime() - time;
455 StringBuilder sb = new StringBuilder();
456 sb.append(format(" Motion Check: %6.4f sec\n", motionTime * 1e-9));
457 sb.append(format(" List Initialization: %6.4f sec\n", initTime * 1e-9));
458 sb.append(format(" Assign Atoms to Cells: %6.4f sec\n", assignAtomsToCellsTime * 1e-9));
459 sb.append(format(" Create Vertlet Lists: %6.4f sec\n", verletListTime * 1e-9));
460 sb.append(format(" Parallel Schedule: %6.4f sec\n", scheduleTime * 1e-9));
461 if (M >= 1) {
462 sb.append(format(" Group List Total: %6.4f sec\n", groupingTime * 1e-9));
463 }
464 sb.append(format(" Neighbor List Total: %6.4f sec\n", time * 1e-9));
465 logger.fine(sb.toString());
466 }
467 }
468
469
470
471
472
473
474 public double getCutoff() {
475 return cutoff;
476 }
477
478
479
480
481
482
483
484 public void setDisableUpdates(boolean disableUpdate) {
485 this.disableUpdates = disableUpdate;
486 }
487
488
489
490
491
492
493 public int[][][] getNeighborList() {
494 return lists;
495 }
496
497
498
499
500
501
502 public PairwiseSchedule getPairwiseSchedule() {
503 return pairwiseSchedule;
504 }
505
506
507
508
509
510
511
512
513 @Override
514 public void run() {
515 if (disableUpdates) {
516 return;
517 }
518
519 int threadIndex = getThreadIndex();
520 try {
521 if (!forceRebuild) {
522
523 if (threadIndex == 0) {
524 motionTime = -System.nanoTime();
525 }
526 execute(0, nAtoms - 1, motionLoops[threadIndex]);
527 if (threadIndex == 0) {
528 motionTime += System.nanoTime();
529 }
530 }
531 if (forceRebuild || sharedMotion.get()) {
532
533 barrier(listInitBarrierAction);
534
535 if (threadIndex == 0) {
536 assignAtomsToCellsTime = -System.nanoTime();
537 }
538 execute(0, nAtoms - 1, assignAtomsToCellsLoops[threadIndex]);
539 if (threadIndex == 0) {
540 assignAtomsToCellsTime += System.nanoTime();
541 }
542
543 if (threadIndex == 0) {
544 verletListTime = -System.nanoTime();
545 }
546 execute(0, nAtoms - 1, verletListLoop[threadIndex]);
547 if (threadIndex == 0) {
548 verletListTime += System.nanoTime();
549 }
550
551 if (M > 1) {
552 if (threadIndex == 0) {
553 groupingTime = -System.nanoTime();
554
555 mGroups = buildGroups();
556 numGroups = mGroups.length;
557 mGroupID = assignGroupID(mGroups);
558 groupLists = new int[nSymm][numGroups][];
559 }
560 barrier();
561 execute(0, mGroups.length - 1, groupVerletListLoop[threadIndex]);
562 if (threadIndex == 0) {
563 groupingTime += System.nanoTime();
564 }
565 }
566 }
567 } catch (Exception e) {
568 String message =
569 "Fatal exception building neighbor list in thread: " + getThreadIndex() + "\n";
570 logger.log(Level.SEVERE, message, e);
571 }
572 }
573
574
575
576
577
578
579
580 private int[][] buildGroups() {
581
582 int nA = domainDecomposition.nA;
583 int nB = domainDecomposition.nB;
584 int nC = domainDecomposition.nC;
585
586
587 List<int[]> groupsList = new ArrayList<>();
588 int[] group = new int[M];
589 for (int i = 0; i < nA; i++) {
590 for (int j = 0; j < nB; j++) {
591 PriorityQueue<AtomIndex> queue = new PriorityQueue<>();
592 for (int k = 0; k < nC; k++) {
593
594 Cell cell = domainDecomposition.getCell(i, j, k);
595 for (AtomIndex atomIndex : cell.list) {
596
597 if (atomIndex.iSymm != 0) {
598 continue;
599 }
600 queue.add(atomIndex);
601
602 while (queue.size() >= M) {
603 for (int m = 0; m < M; m++) {
604 AtomIndex atom = queue.poll();
605 group[m] = atom.i;
606 }
607 groupsList.add(group);
608 group = new int[M];
609 }
610 }
611 }
612 if (!queue.isEmpty()) {
613
614 fill(group, -1);
615 for (int l = 0; l < M; l++) {
616 if (!queue.isEmpty()) {
617 AtomIndex atom = queue.poll();
618 group[l] = atom.i;
619 }
620 }
621 groupsList.add(group);
622 group = new int[M];
623 }
624 }
625 }
626
627 int numGroups = groupsList.size();
628 int[][] groups = new int[groupsList.size()][];
629 for (int i = 0; i < numGroups; i++) {
630 groups[i] = groupsList.get(i);
631
632 }
633 return groups;
634 }
635
636
637
638
639
640
641
642 private int[] assignGroupID(int[][] groups) {
643 int nGroups = groups.length;
644 int[] groupID = new int[nAtoms];
645 for (int i = 0; i < nGroups; i++) {
646 int[] group = groups[i];
647 for (int atomIndex : group) {
648
649 if (atomIndex >= 0) {
650 groupID[atomIndex] = i;
651 }
652 }
653 }
654 return groupID;
655 }
656
657
658
659
660
661
662 public void setAtoms(Atom[] atoms) {
663 this.atoms = atoms;
664 this.nAtoms = atoms.length;
665 initNeighborList(false);
666 }
667
668
669
670
671
672
673
674 public void setCrystal(Crystal crystal) {
675 this.crystal = crystal;
676 initNeighborList(false);
677 }
678
679
680
681
682
683
684 public void setIntermolecular(boolean intermolecular) {
685 this.intermolecular = intermolecular;
686 }
687
688
689
690
691
692
693
694
695 @Override
696 public void start() {
697 if (disableUpdates) {
698 return;
699 }
700 time = System.nanoTime();
701 motionTime = 0;
702 initTime = 0;
703 assignAtomsToCellsTime = 0;
704 verletListTime = 0;
705 sharedMotion.set(false);
706 }
707
708 private void initNeighborList(boolean print) {
709
710
711 int newNSymm = crystal.spaceGroup.symOps.size();
712 if (nSymm != newNSymm) {
713 nSymm = newNSymm;
714 }
715
716
717 if (previous == null || previous.length < 3 * nAtoms) {
718 previous = new double[3 * nAtoms];
719 listCount = new int[nAtoms];
720 pairwiseSchedule = new PairwiseSchedule(threadCount, nAtoms, ranges);
721 } else {
722 pairwiseSchedule.setAtoms(nAtoms);
723 }
724
725
726 final double sphere = min(min(crystal.interfacialRadiusA, crystal.interfacialRadiusB),
727 crystal.interfacialRadiusC);
728
729
730
731
732
733 if (!crystal.aperiodic()) {
734 assert (sphere >= cutoffPlusBuffer);
735 }
736
737 domainDecomposition.initDomainDecomposition(nAtoms, crystal);
738 if (print) {
739 domainDecomposition.log();
740 }
741 }
742
743 private void print() {
744 StringBuilder sb = new StringBuilder(
745 format(" Buffer: %5.2f (A)\n", buffer));
746 sb.append(format(" Cut-off: %5.2f (A)\n", cutoff));
747 sb.append(format(" Total: %5.2f (A)\n", cutoffPlusBuffer));
748 sb.append(format(" Neighbors in the asymmetric unit:%11d\n", asymmetricUnitCount));
749 if (nSymm > 1) {
750 sb.append(format(" Neighbors in symmetry mates: %12d\n", symmetryMateCount));
751 int num = (asymmetricUnitCount + symmetryMateCount) * nSymm;
752 sb.append(format(" Neighbors in the unit cell: %12d\n", num));
753 }
754
755 if (M > 1) {
756 sb.append(format(" Number of groups: %9d\n", numGroups));
757 sb.append(format(" Group pairs in the asymmetric unit:%9d\n", groupAsymmetricUnitCount));
758 if (nSymm > 1) {
759 sb.append(format(" Group pairs in symmetry mates: %12d\n", groupSymmetryMateCount));
760 int num = (groupAsymmetricUnitCount + groupSymmetryMateCount) * nSymm;
761 sb.append(format(" Group pairs in the unit cell: %12d\n", num));
762 }
763 }
764
765 logger.info(sb.toString());
766 }
767
768 private static final int XX = 0;
769 private static final int YY = 1;
770 private static final int ZZ = 2;
771
772
773
774
775
776
777 private class MotionLoop extends IntegerForLoop {
778
779 @Override
780 public void run(int lb, int ub) {
781 double[] current = coordinates[0];
782 for (int i = lb; i <= ub; i++) {
783 int i3 = i * 3;
784 int iX = i3 + XX;
785 int iY = i3 + YY;
786 int iZ = i3 + ZZ;
787 double dx = previous[iX] - current[iX];
788 double dy = previous[iY] - current[iY];
789 double dz = previous[iZ] - current[iZ];
790 double dr2 = crystal.image(dx, dy, dz);
791 if (dr2 > motion2) {
792 if (logger.isLoggable(Level.FINE)) {
793 logger.fine(format(" Motion detected for atom %d (%8.6f A).", i, sqrt(dr2)));
794 }
795 sharedMotion.set(true);
796 }
797 }
798 }
799 }
800
801
802
803
804 private class ListInitBarrierAction extends BarrierAction {
805
806 @Override
807 public void run() {
808 initTime = -System.nanoTime();
809
810 sharedCount.set(0);
811
812 domainDecomposition.clear();
813
814
815 for (int iSymm = 0; iSymm < nSymm; iSymm++) {
816 if (lists[iSymm] == null || lists[iSymm].length < nAtoms) {
817 lists[iSymm] = new int[nAtoms][];
818 }
819 }
820 initTime += System.nanoTime();
821 }
822 }
823
824
825
826
827
828
829 private class AssignAtomsToCellsLoop extends IntegerForLoop {
830
831
832
833
834 private final double[] auCart = new double[3];
835
836
837
838 private final double[] cart = new double[3];
839
840
841
842 private final double[] frac = new double[3];
843
844 @Override
845 public void run(int lb, int ub) {
846
847
848 double[] current = coordinates[0];
849 for (int i = lb; i <= ub; i++) {
850 int i3 = i * 3;
851 int iX = i3 + XX;
852 int iY = i3 + YY;
853 int iZ = i3 + ZZ;
854 previous[iX] = current[iX];
855 previous[iY] = current[iY];
856 previous[iZ] = current[iZ];
857 }
858
859 final double[] auXYZ = coordinates[0];
860 final double spCutoff2 = crystal.getSpecialPositionCutoff2();
861 for (int iSymm = 0; iSymm < nSymm; iSymm++) {
862 final double[] xyz = coordinates[iSymm];
863
864 for (int i = lb; i <= ub; i++) {
865 int i3 = i * 3;
866 cart[0] = xyz[i3 + XX];
867 cart[1] = xyz[i3 + YY];
868 cart[2] = xyz[i3 + ZZ];
869 if (iSymm != 0) {
870 auCart[0] = auXYZ[i3 + XX];
871 auCart[1] = auXYZ[i3 + YY];
872 auCart[2] = auXYZ[i3 + ZZ];
873 double dx = auCart[0] - cart[0];
874 double dy = auCart[1] - cart[1];
875 double dz = auCart[2] - cart[2];
876 double dr2 = crystal.image(dx, dy, dz);
877 if (dr2 < spCutoff2) {
878 int molecule = atoms[i].getMoleculeNumber();
879 if (atoms[i].isActive()) {
880 logger.info(format(" Active Atom %s at Special Position in Molecule %d with SymOp %d (%8.6f A).",
881 atoms[i], molecule, iSymm, sqrt(dr2)));
882 } else if (logger.isLoggable(Level.FINE)) {
883 logger.fine(format(" Atom %s at Special Position in Molecule %d with SymOp %d (%8.6f A).",
884 atoms[i], molecule, iSymm, sqrt(dr2)));
885 }
886
887 domainDecomposition.addSpecialPositionExclusion(molecule, iSymm);
888 }
889 }
890
891 crystal.toFractionalCoordinates(cart, frac);
892 domainDecomposition.addAtomToCell(i, iSymm, frac);
893 }
894 }
895 }
896 }
897
898
899
900
901
902
903
904
905 private class VerletListLoop extends IntegerForLoop {
906
907 private final IntegerSchedule schedule;
908 private int n;
909 private int iSymm;
910 private int atomIndex;
911 private boolean iactive = true;
912 private int count;
913 private double[] xyz;
914 private int[] pairs;
915 private Cell cellForCurrentAtom;
916 private int[] pairCellAtoms;
917
918 VerletListLoop() {
919 int len = 1000;
920 pairs = new int[len];
921 schedule = IntegerSchedule.dynamic(10);
922 pairCellAtoms = new int[len];
923 }
924
925 @Override
926 public void finish() {
927 sharedCount.addAndGet(count);
928 }
929
930 @Override
931 public void run(final int lb, final int ub) {
932 int nEdge = domainDecomposition.nEdge;
933 int nA = domainDecomposition.nA;
934 int nB = domainDecomposition.nB;
935 int nC = domainDecomposition.nC;
936 for (iSymm = 0; iSymm < nSymm; iSymm++) {
937 int[][] list = lists[iSymm];
938
939 for (atomIndex = lb; atomIndex <= ub; atomIndex++) {
940 n = 0;
941
942 if (iSymm == 0) {
943 listCount[atomIndex] = 0;
944 int molecule = atoms[atomIndex].getMoleculeNumber();
945 List<Integer> symOpList = domainDecomposition.getSpecialPositionSymOps(molecule);
946 atoms[atomIndex].setSpecialPositionSymOps(symOpList);
947 }
948
949 if (use == null || use[atomIndex]) {
950
951 iactive = atoms[atomIndex].isActive();
952
953 cellForCurrentAtom = domainDecomposition.getCellForAtom(atomIndex);
954 int a = cellForCurrentAtom.a;
955 int b = cellForCurrentAtom.b;
956 int c = cellForCurrentAtom.c;
957 int a1 = a + 1;
958 int b1 = b + 1;
959 int c1 = c + 1;
960
961
962
963
964
965
966 int aStart = nA == 1 ? a : a - nEdge;
967 int aStop = nA == 1 ? a : a + nEdge;
968 int bStart = nB == 1 ? b : b - nEdge;
969 int bStop = nB == 1 ? b : b + nEdge;
970 int cStart = nC == 1 ? c : c - nEdge;
971 int cStop = nC == 1 ? c : c + nEdge;
972
973 if (iSymm == 0) {
974
975 atomCellPairs(cellForCurrentAtom);
976
977
978 for (int bi = b1; bi <= bStop; bi++) {
979 atomCellPairs(domainDecomposition.image(a, bi, c));
980 }
981
982 for (int bi = bStart; bi <= bStop; bi++) {
983 for (int ci = c1; ci <= cStop; ci++) {
984 atomCellPairs(domainDecomposition.image(a, bi, ci));
985 }
986 }
987
988 for (int bi = bStart; bi <= bStop; bi++) {
989 for (int ci = cStart; ci <= cStop; ci++) {
990 for (int ai = a1; ai <= aStop; ai++) {
991 atomCellPairs(domainDecomposition.image(ai, bi, ci));
992 }
993 }
994 }
995 } else {
996
997 for (int ai = aStart; ai <= aStop; ai++) {
998 for (int bi = bStart; bi <= bStop; bi++) {
999 for (int ci = cStart; ci <= cStop; ci++) {
1000 atomCellPairs(domainDecomposition.image(ai, bi, ci));
1001 }
1002 }
1003 }
1004 }
1005 }
1006
1007 list[atomIndex] = new int[n];
1008 listCount[atomIndex] += n;
1009 count += n;
1010 arraycopy(pairs, 0, list[atomIndex], 0, n);
1011 }
1012 }
1013 }
1014
1015 @Override
1016 public IntegerSchedule schedule() {
1017 return schedule;
1018 }
1019
1020 @Override
1021 public void start() {
1022 xyz = coordinates[0];
1023 count = 0;
1024 }
1025
1026 private void atomCellPairs(final Cell cell) {
1027 if (pairCellAtoms.length < cell.getCount()) {
1028 pairCellAtoms = new int[cell.getCount()];
1029 }
1030
1031
1032 int num = cell.getSymOpAtoms(iSymm, pairCellAtoms);
1033 if (num == 0) {
1034 return;
1035 }
1036
1037 final int moleculeIndex = atoms[atomIndex].getMoleculeNumber();
1038
1039 boolean logClosePairs = logger.isLoggable(Level.FINE);
1040
1041 final int i3 = atomIndex * 3;
1042 final double xi = xyz[i3 + XX];
1043 final double yi = xyz[i3 + YY];
1044 final double zi = xyz[i3 + ZZ];
1045 final double[] pair = coordinates[iSymm];
1046
1047
1048 for (int j = 0; j < num; j++) {
1049 final int aj = pairCellAtoms[j];
1050
1051
1052 if (iSymm == 0 && cell == cellForCurrentAtom) {
1053
1054 if (aj <= atomIndex) {
1055 continue;
1056 }
1057 }
1058
1059 if (use != null && !use[aj]) {
1060 continue;
1061 }
1062
1063 if (!includeInactivePairs && !iactive) {
1064 if (!atoms[aj].isActive()) {
1065 continue;
1066 }
1067 }
1068
1069 int moleculeIndexJ = atoms[aj].getMoleculeNumber();
1070
1071 if (!intermolecular && (moleculeIndex != moleculeIndexJ)) {
1072 continue;
1073 }
1074
1075
1076 if (iSymm != 0 && (moleculeIndex == moleculeIndexJ)) {
1077 if (domainDecomposition.isSpecialPositionExclusion(moleculeIndex, iSymm)) {
1078 if (logger.isLoggable(Level.FINEST)) {
1079 logger.finest(
1080 format(" Excluding Interaction for Atoms %d and %d in Molecule %d for SymOp %d.",
1081 atomIndex, aj, moleculeIndex, iSymm));
1082 }
1083 continue;
1084 }
1085 }
1086
1087 if (iSymm == 0 || aj >= atomIndex) {
1088 int aj3 = aj * 3;
1089 final double xj = pair[aj3 + XX];
1090 final double yj = pair[aj3 + YY];
1091 final double zj = pair[aj3 + ZZ];
1092 final double xr = xi - xj;
1093 final double yr = yi - yj;
1094 final double zr = zi - zj;
1095 final double d2 = crystal.image(xr, yr, zr);
1096 if (d2 <= cutoffPlusBuffer2) {
1097 if (logClosePairs && d2 < crystal.getSpecialPositionCutoff2()) {
1098
1099 logger.fine(
1100 format(" Close interaction (%6.3f) between atoms (iSymm = %d):\n %s\n %s\n",
1101 sqrt(d2), iSymm, atoms[atomIndex].toString(), atoms[aj].toString()));
1102 }
1103
1104
1105 try {
1106 pairs[n++] = aj;
1107 } catch (Exception e) {
1108 n = pairs.length;
1109 pairs = copyOf(pairs, n + 100);
1110 pairs[n++] = aj;
1111 }
1112 }
1113 }
1114 }
1115 }
1116
1117 }
1118
1119
1120
1121
1122 private class GroupVerletListLoop extends IntegerForLoop {
1123
1124 private final IntegerSchedule schedule = IntegerSchedule.dynamic(10);
1125
1126 @Override
1127 public IntegerSchedule schedule() {
1128 return schedule;
1129 }
1130
1131 @Override
1132 public void run(final int lb, final int ub) {
1133
1134 List<Integer> neighborGroups = new ArrayList<>();
1135 for (int iSymm = 0; iSymm < nSymm; iSymm++) {
1136
1137 for (int groupIndex = lb; groupIndex <= ub; groupIndex++) {
1138
1139 for (int a = 0; a < M; a++) {
1140 int atomIndex = mGroups[groupIndex][a];
1141
1142 if (atomIndex == -1) {
1143 continue;
1144 }
1145
1146 int[] neighborList = lists[iSymm][atomIndex];
1147 for (int value : neighborList) {
1148
1149 int neighborGroupIndex = mGroupID[value];
1150 if (!neighborGroups.contains(neighborGroupIndex)) {
1151 if (groupIndex <= neighborGroupIndex) {
1152
1153
1154 neighborGroups.add(neighborGroupIndex);
1155 } else if (neighborGroupIndex >= lb) {
1156
1157
1158 int[] list = groupLists[iSymm][neighborGroupIndex];
1159 boolean included = false;
1160 for (int neighbor : list) {
1161
1162 if (neighbor == groupIndex) {
1163 included = true;
1164 break;
1165 }
1166 }
1167 if (!included) {
1168
1169 neighborGroups.add(neighborGroupIndex);
1170 }
1171 } else if (!isIncludedInLowerIDList(iSymm, neighborGroupIndex, groupIndex)) {
1172
1173 neighborGroups.add(neighborGroupIndex);
1174 }
1175 }
1176 }
1177 }
1178
1179
1180
1181 groupLists[iSymm][groupIndex] = neighborGroups.stream().mapToInt(Integer::intValue).toArray();
1182
1183 neighborGroups.clear();
1184 }
1185 }
1186 }
1187 }
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197 private boolean isIncludedInLowerIDList(int symOp, int smallerGroupIndex, int secondGroup) {
1198 if (smallerGroupIndex >= secondGroup) {
1199
1200 return false;
1201 }
1202
1203 for (int a = 0; a < M; a++) {
1204 int atomID = mGroups[smallerGroupIndex][a];
1205
1206 if (atomID == -1) {
1207 continue;
1208 }
1209
1210 int[] neighborList = lists[symOp][atomID];
1211 for (int value : neighborList) {
1212
1213 int neighborGroupID = mGroupID[value];
1214 if (neighborGroupID == secondGroup) {
1215
1216 return true;
1217 }
1218 }
1219 }
1220 return false;
1221 }
1222
1223
1224
1225
1226 private static class DomainDecomposition {
1227
1228
1229
1230
1231 private Crystal crystal;
1232
1233
1234
1235
1236 private final double targetInterfacialRadius;
1237
1238
1239
1240
1241
1242
1243
1244
1245 private final int nEdge;
1246
1247
1248
1249
1250 private final int nSearch;
1251
1252
1253
1254 private int nA;
1255
1256
1257
1258 private int nB;
1259
1260
1261
1262 private int nC;
1263
1264
1265
1266 private int nAtoms;
1267
1268
1269
1270 private int[] cellA;
1271
1272
1273
1274 private int[] cellB;
1275
1276
1277
1278 private int[] cellC;
1279
1280
1281
1282 private Cell[][][] cells;
1283
1284
1285
1286 private Map<Integer, List<Integer>> specialPositionExclusionMap;
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296 public DomainDecomposition(int nAtoms, Crystal crystal, double cutoffPlusBuffer) {
1297 this.nEdge = 2;
1298 this.nSearch = 2 * nEdge + 1;
1299 targetInterfacialRadius = cutoffPlusBuffer / (double) nEdge;
1300 initDomainDecomposition(nAtoms, crystal);
1301 }
1302
1303
1304
1305
1306 public void initDomainDecomposition(int nAtoms, Crystal crystal) {
1307 this.nAtoms = nAtoms;
1308 this.crystal = crystal;
1309
1310
1311 if (cellA == null || cellA.length < nAtoms) {
1312 cellA = new int[nAtoms];
1313 cellB = new int[nAtoms];
1314 cellC = new int[nAtoms];
1315 }
1316
1317
1318 int maxA = (int) floor(2 * crystal.interfacialRadiusA / targetInterfacialRadius);
1319 int maxB = (int) floor(2 * crystal.interfacialRadiusB / targetInterfacialRadius);
1320 int maxC = (int) floor(2 * crystal.interfacialRadiusC / targetInterfacialRadius);
1321
1322
1323
1324
1325
1326 if (!crystal.aperiodic()) {
1327 assert (maxA >= nSearch - 1);
1328 assert (maxB >= nSearch - 1);
1329 assert (maxC >= nSearch - 1);
1330 }
1331
1332
1333
1334
1335
1336 nA = maxA < nSearch ? 1 : maxA;
1337 nB = maxB < nSearch ? 1 : maxB;
1338 nC = maxC < nSearch ? 1 : maxC;
1339
1340
1341 if (cells == null || cells.length != nA || cells[0].length != nB || cells[0][0].length != nC) {
1342 cells = new Cell[nA][nB][nC];
1343 for (int i = 0; i < nA; i++) {
1344 for (int j = 0; j < nB; j++) {
1345 for (int k = 0; k < nC; k++) {
1346 cells[i][j][k] = new Cell(i, j, k);
1347 }
1348 }
1349 }
1350 } else {
1351 clear();
1352 }
1353
1354 }
1355
1356
1357
1358
1359 public void clear() {
1360
1361 for (int i = 0; i < nA; i++) {
1362 for (int j = 0; j < nB; j++) {
1363 for (int k = 0; k < nC; k++) {
1364 cells[i][j][k].clear();
1365 }
1366 }
1367 }
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377 }
1378
1379
1380
1381
1382
1383
1384
1385 public void addSpecialPositionExclusion(int molecule, int symOpIndex) {
1386
1387 if (specialPositionExclusionMap == null) {
1388 specialPositionExclusionMap = new HashMap<>();
1389 }
1390
1391 List<Integer> list = specialPositionExclusionMap.get(molecule);
1392 if (list == null) {
1393 list = new ArrayList<>();
1394 specialPositionExclusionMap.put(molecule, list);
1395 }
1396
1397 if (!list.contains(symOpIndex)) {
1398 list.add(symOpIndex);
1399 }
1400 }
1401
1402
1403
1404
1405
1406
1407
1408
1409 public boolean isSpecialPositionExclusion(int molecule, int symOpIndex) {
1410 if (specialPositionExclusionMap == null) {
1411 return false;
1412 }
1413 List<Integer> list = specialPositionExclusionMap.get(molecule);
1414 if (list == null) {
1415 return false;
1416 }
1417 return list.contains(symOpIndex);
1418 }
1419
1420
1421
1422
1423
1424
1425
1426 public List<Integer> getSpecialPositionSymOps(int molecule) {
1427 if (specialPositionExclusionMap == null) {
1428 return null;
1429 }
1430 if (specialPositionExclusionMap.containsKey(molecule)) {
1431 List<Integer> list = specialPositionExclusionMap.get(molecule);
1432 if (list.isEmpty()) {
1433 return null;
1434 } else {
1435 return list;
1436 }
1437 }
1438 return null;
1439 }
1440
1441
1442
1443
1444 public void log() {
1445 int nCells = nA * nB * nC;
1446 int nSymm = crystal.spaceGroup.getNumberOfSymOps();
1447 StringBuilder sb = new StringBuilder(" Neighbor List Builder\n");
1448 sb.append(format(" Total Cells: %8d\n", nCells));
1449 if (nCells > 1) {
1450 sb.append(format(" Interfacial radius %8.3f A\n", targetInterfacialRadius));
1451 int searchA = nA == 1 ? 1 : nSearch;
1452 int searchB = nB == 1 ? 1 : nSearch;
1453 int searchC = nC == 1 ? 1 : nSearch;
1454 sb.append(format(" Domain Decomposition: %8d %4d %4d\n", nA, nB, nC));
1455 sb.append(format(" Neighbor Search: %8d x%3d x%3d\n", searchA, searchB, searchC));
1456 sb.append(format(" Mean Atoms per Cell: %8d", nAtoms * nSymm / nCells));
1457 }
1458 logger.info(sb.toString());
1459 }
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471 public Cell image(int i, int j, int k) {
1472 if (i >= nA) {
1473 i -= nA;
1474 } else if (i < 0) {
1475 i += nA;
1476 }
1477 if (j >= nB) {
1478 j -= nB;
1479 } else if (j < 0) {
1480 j += nB;
1481 }
1482 if (k >= nC) {
1483 k -= nC;
1484 } else if (k < 0) {
1485 k += nC;
1486 }
1487 return cells[i][j][k];
1488 }
1489
1490
1491
1492
1493
1494
1495
1496
1497 public void addAtomToCell(int i, int iSymm, double[] frac) {
1498 double xu = frac[0];
1499 double yu = frac[1];
1500 double zu = frac[2];
1501
1502 while (xu < 0.0) {
1503 xu += 1.0;
1504 }
1505 while (xu >= 1.0) {
1506 xu -= 1.0;
1507 }
1508 while (yu < 0.0) {
1509 yu += 1.0;
1510 }
1511 while (yu >= 1.0) {
1512 yu -= 1.0;
1513 }
1514 while (zu < 0.0) {
1515 zu += 1.0;
1516 }
1517 while (zu >= 1.0) {
1518 zu -= 1.0;
1519 }
1520
1521 final int a = (int) floor(xu * nA);
1522 final int b = (int) floor(yu * nB);
1523 final int c = (int) floor(zu * nC);
1524
1525 if (iSymm == 0) {
1526
1527 cellA[i] = a;
1528 cellB[i] = b;
1529 cellC[i] = c;
1530 }
1531 cells[a][b][c].add(i, iSymm, zu);
1532 }
1533
1534
1535
1536
1537
1538
1539
1540 public Cell getCellForAtom(int i) {
1541 return cells[cellA[i]][cellB[i]][cellC[i]];
1542 }
1543
1544 public Cell getCell(int a, int b, int c) {
1545 if (a < 0 || a >= nA || b < 0 || b >= nB || c < 0 || c >= nC) {
1546 return null;
1547 }
1548 return cells[a][b][c];
1549 }
1550 }
1551
1552
1553
1554
1555 public static class AtomIndex implements Comparable<AtomIndex> {
1556
1557 public final int iSymm;
1558 public final int i;
1559 public final double z;
1560
1561 public AtomIndex(int iSymm, int i, double z) {
1562 this.iSymm = iSymm;
1563 this.i = i;
1564 this.z = z;
1565 }
1566
1567 @Override
1568 public int compareTo(AtomIndex o) {
1569 return Double.compare(this.z, o.z);
1570 }
1571 }
1572
1573
1574
1575
1576 public static class Cell {
1577
1578
1579
1580
1581 final int a;
1582
1583
1584
1585 final int b;
1586
1587
1588
1589 final int c;
1590
1591
1592
1593
1594 final List<AtomIndex> list;
1595
1596
1597
1598 final Set<Integer> groupList;
1599
1600 public Cell(int a, int b, int c) {
1601 this.a = a;
1602 this.b = b;
1603 this.c = c;
1604 list = Collections.synchronizedList(new ArrayList<>());
1605 groupList = Collections.synchronizedSet(new HashSet<>());
1606 }
1607
1608
1609
1610
1611
1612
1613
1614 public void add(int atomIndex, int symOpIndex, double z) {
1615 list.add(new AtomIndex(symOpIndex, atomIndex, z));
1616 }
1617
1618 public void groupAdd(int groupIndex) {
1619 groupList.add(groupIndex);
1620 }
1621
1622 public AtomIndex get(int index) {
1623 if (index >= list.size()) {
1624 return null;
1625 }
1626 return list.get(index);
1627 }
1628
1629 public int getCount() {
1630 return list.size();
1631 }
1632
1633 public int getGroupCount() {
1634 return groupList.size();
1635 }
1636
1637
1638
1639
1640
1641
1642
1643
1644 public int getSymOpAtoms(int symOpIndex, int[] index) {
1645 int count = 0;
1646 for (AtomIndex atomIndex : list) {
1647 if (atomIndex.iSymm == symOpIndex) {
1648 if (count >= index.length) {
1649 throw new IndexOutOfBoundsException(
1650 "Index out of bounds: count " + count + " " + index.length + " " + list.size());
1651 }
1652 index[count++] = atomIndex.i;
1653 }
1654 }
1655 return count;
1656 }
1657
1658
1659
1660
1661 public void clear() {
1662 list.clear();
1663 }
1664
1665 }
1666
1667
1668
1669
1670
1671
1672 public static void main(String[] args) {
1673 PotentialsUtils potentialsUtils = new PotentialsUtils();
1674 File xyzFile = new File("./examples/waterbox.xyz");
1675 if (!xyzFile.exists()) {
1676 System.out.println("File does not exist");
1677 }
1678 MolecularAssembly molecularAssembly = potentialsUtils.open(xyzFile);
1679 molecularAssembly.getPotentialEnergy().energy(null, true);
1680 }
1681 }