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