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.xray;
39
40 import static java.lang.String.format;
41 import static java.util.Arrays.fill;
42 import static org.apache.commons.math3.util.FastMath.floor;
43 import static org.apache.commons.math3.util.FastMath.min;
44
45 import edu.rit.pj.IntegerForLoop;
46 import edu.rit.pj.IntegerSchedule;
47 import edu.rit.pj.ParallelRegion;
48 import edu.rit.pj.ParallelTeam;
49 import edu.rit.pj.reduction.SharedBooleanArray;
50 import ffx.crystal.Crystal;
51 import ffx.potential.bonded.Atom;
52 import java.util.logging.Level;
53 import java.util.logging.Logger;
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 public class BulkSolventList extends ParallelRegion {
75
76 private static final Logger logger = Logger.getLogger(BulkSolventList.class.getName());
77
78 private final Crystal crystal;
79
80 private final int nSymm;
81
82 private final int nAtoms;
83
84 private final SharedBooleanArray[] sharedSelect;
85
86 private final int nEdgeA, nEdgeB, nEdgeC;
87
88 private final int nAB;
89
90 private final int nCells;
91
92 private final int[][] cellIndex;
93
94 private final int[] cellA;
95
96 private final int[] cellB;
97
98 private final int[] cellC;
99
100 private final int[][] cellList;
101
102
103
104
105 private final int[][] cellOffset;
106
107 private final int[][] cellCount;
108
109 private final int[][] cellStart;
110
111 private final double cutoff;
112
113 private final double cutoff2;
114
115 private final double[][] frac;
116
117 private final ParallelTeam parallelTeam;
118
119 private final SelectionListLoop[] selectionListLoop;
120
121 private final double toSeconds = 1.0e-9;
122
123 private double[][][] coordinates;
124
125 private boolean[][] selected;
126
127 private int nSelected;
128
129 private int nA;
130
131 private int nB;
132
133 private int nC;
134
135 private long time;
136 private long cellTime, selectTime, totalTime;
137
138
139
140
141
142
143
144
145
146
147 public BulkSolventList(Crystal crystal, Atom[] atoms, double cutoff, ParallelTeam parallelTeam) {
148 this.crystal = crystal;
149 this.cutoff = cutoff;
150 this.parallelTeam = new ParallelTeam(parallelTeam.getThreadCount());
151 nAtoms = atoms.length;
152 nSymm = crystal.spaceGroup.symOps.size();
153
154 cutoff2 = cutoff * cutoff;
155 final double side = min(min(crystal.a, crystal.b), crystal.c);
156
157 assert (side > 2.0 * cutoff);
158
159
160 nEdgeA = 2;
161 nEdgeB = nEdgeA;
162 nEdgeC = nEdgeA;
163 double minLengthA = cutoff / (double) nEdgeA;
164 double minLengthB = cutoff / (double) nEdgeB;
165 double minLengthC = cutoff / (double) nEdgeC;
166
167 nA = (int) floor(crystal.a / minLengthA);
168 nB = (int) floor(crystal.b / minLengthB);
169 nC = (int) floor(crystal.c / minLengthC);
170
171 if (nA < nEdgeA * 2 + 1) {
172 nA = 1;
173 }
174 if (nB < nEdgeB * 2 + 1) {
175 nB = 1;
176 }
177 if (nC < nEdgeC * 2 + 1) {
178 nC = 1;
179 }
180
181 nAB = nA * nB;
182 nCells = nAB * nC;
183
184 cellList = new int[nSymm][nAtoms];
185 cellIndex = new int[nSymm][nAtoms];
186 cellOffset = new int[nSymm][nAtoms];
187 cellStart = new int[nSymm][nCells];
188 cellCount = new int[nSymm][nCells];
189 cellA = new int[nAtoms];
190 cellB = new int[nAtoms];
191 cellC = new int[nAtoms];
192 frac = new double[3][nAtoms];
193
194 int threadCount = parallelTeam.getThreadCount();
195 selectionListLoop = new SelectionListLoop[threadCount];
196 for (int i = 0; i < threadCount; i++) {
197 selectionListLoop[i] = new SelectionListLoop();
198 }
199 sharedSelect = new SharedBooleanArray[nSymm];
200 for (int i = 0; i < nSymm; i++) {
201 sharedSelect[i] = new SharedBooleanArray(nAtoms);
202 }
203 }
204
205
206
207
208
209
210
211
212
213 public void buildList(final double[][][] coordinates, final boolean[][] selected, boolean log) {
214 this.coordinates = coordinates;
215 this.selected = selected;
216
217 cellTime = -System.nanoTime();
218 assignAtomsToCells();
219 cellTime += System.nanoTime();
220
221 selectTime = -System.nanoTime();
222 selectAtoms();
223 selectTime += System.nanoTime();
224 totalTime = cellTime + selectTime;
225
226 if (log) {
227 log();
228 }
229 }
230
231
232
233
234
235
236
237
238 @Override
239 public void finish() {
240 if (logger.isLoggable(Level.FINE)) {
241 logger.fine(
242 format("Parallel Neighbor List: %10.3f seconds", (System.nanoTime() - time) * 1e-9));
243 }
244 }
245
246
247
248
249
250
251
252
253 @Override
254 public void run() {}
255
256
257
258
259
260
261
262
263 @Override
264 public void start() {
265 time = System.nanoTime();
266 }
267
268 private void log() {
269 StringBuilder sb = new StringBuilder(format(" The cutoff is %5.2f angstroms.\n", cutoff));
270 sb.append(format(" Assignment to cells: %8.3f\n", cellTime * toSeconds));
271 sb.append(format(" Selection: %8.3f\n", selectTime * toSeconds));
272 sb.append(format(" Total: %8.3f (sec)\n", totalTime * toSeconds));
273 if (nSymm > 1) {
274 double speedup = (double) (nAtoms * nSymm) / (double) (nAtoms + nSelected);
275 sb.append(format(" Atoms in the asymmetric unit: %12d\n", nAtoms));
276 sb.append(format(" Selected symmetry mate atoms: %12d\n", nSelected));
277 sb.append(format(" Babinet speed up factor: %12.3f\n", speedup));
278 }
279 sb.append("\n");
280 logger.info(sb.toString());
281 }
282
283
284
285
286
287
288
289 private void assignAtomsToCells() {
290 for (int iSymm = 0; iSymm < nSymm; iSymm++) {
291 final int[] cellIndexs = cellIndex[iSymm];
292 final int[] cellCounts = cellCount[iSymm];
293 final int[] cellStarts = cellStart[iSymm];
294 final int[] cellLists = cellList[iSymm];
295 final int[] cellOffsets = cellOffset[iSymm];
296
297 for (int i = 0; i < nCells; i++) {
298 cellCounts[i] = 0;
299 }
300
301 final double[][] xyz = coordinates[iSymm];
302
303 crystal.toFractionalCoordinates(nAtoms, xyz[0], xyz[1], xyz[2], frac[0], frac[1], frac[2]);
304
305
306 for (int i = 0; i < nAtoms; i++) {
307 double xu = frac[0][i];
308 double yu = frac[1][i];
309 double zu = frac[2][i];
310
311 while (xu >= 1.0) {
312 xu -= 1.0;
313 }
314 while (xu < 0.0) {
315 xu += 1.0;
316 }
317 while (yu >= 1.0) {
318 yu -= 1.0;
319 }
320 while (yu < 0.0) {
321 yu += 1.0;
322 }
323 while (zu >= 1.0) {
324 zu -= 1.0;
325 }
326 while (zu < 0.0) {
327 zu += 1.0;
328 }
329
330 final int a = (int) floor(xu * nA);
331 final int b = (int) floor(yu * nB);
332 final int c = (int) floor(zu * nC);
333 if (iSymm == 0) {
334 cellA[i] = a;
335 cellB[i] = b;
336 cellC[i] = c;
337 }
338
339 final int index = a + b * nA + c * nAB;
340 cellIndexs[i] = index;
341
342 cellOffsets[i] = cellCounts[index]++;
343 }
344
345 cellStarts[0] = 0;
346 for (int i = 1; i < nCells; i++) {
347 final int i1 = i - 1;
348 cellStarts[i] = cellStarts[i1] + cellCounts[i1];
349 }
350
351 for (int i = 0; i < nAtoms; i++) {
352 final int index = cellIndexs[i];
353 cellLists[cellStarts[index]++] = i;
354 }
355
356 cellStarts[0] = 0;
357 for (int i = 1; i < nCells; i++) {
358 final int i1 = i - 1;
359 cellStarts[i] = cellStarts[i1] + cellCounts[i1];
360 }
361 }
362 }
363
364
365
366
367
368
369 private void selectAtoms() {
370 for (int iSymm = 0; iSymm < nSymm; iSymm++) {
371 if (selected[iSymm] == null) {
372 selected[iSymm] = new boolean[nAtoms];
373 }
374 for (int i = 0; i < nAtoms; i++) {
375 sharedSelect[iSymm].set(i, false);
376 }
377 }
378 try {
379 parallelTeam.execute(this);
380 } catch (Exception e) {
381 String message = "Fatal exception building neighbor list.\n";
382 logger.log(Level.SEVERE, message, e);
383 }
384 nSelected = 0;
385 fill(selected[0], false);
386 for (int iSymm = 1; iSymm < nSymm; iSymm++) {
387 boolean[] select = selected[iSymm];
388 SharedBooleanArray shared = sharedSelect[iSymm];
389 for (int i = 0; i < nAtoms; i++) {
390 select[i] = shared.get(i);
391 if (select[i]) {
392 nSelected++;
393 }
394 }
395 }
396 }
397
398
399
400
401
402
403
404
405 private class SelectionListLoop extends IntegerForLoop {
406
407 private final IntegerSchedule schedule;
408 private int iSymm;
409 private int atomIndex;
410 private double[][] xyz;
411 private SharedBooleanArray select;
412
413 public SelectionListLoop() {
414 super();
415 schedule = IntegerSchedule.dynamic(10);
416 }
417
418 @Override
419 public void run(final int lb, final int ub) {
420 for (iSymm = 1; iSymm < nSymm; iSymm++) {
421 select = sharedSelect[iSymm];
422
423 for (atomIndex = lb; atomIndex <= ub; atomIndex++) {
424 final int a = cellA[atomIndex];
425 final int b = cellB[atomIndex];
426 final int c = cellC[atomIndex];
427
428 int aStart = a - nEdgeA;
429 int aStop = a + nEdgeA;
430 int bStart = b - nEdgeB;
431 int bStop = b + nEdgeB;
432 int cStart = c - nEdgeC;
433 int cStop = c + nEdgeC;
434
435
436
437
438
439 if (nA == 1) {
440 aStart = a;
441 aStop = a;
442 }
443 if (nB == 1) {
444 bStart = b;
445 bStop = b;
446 }
447 if (nC == 1) {
448 cStart = c;
449 cStop = c;
450 }
451
452
453 for (int ai = aStart; ai <= aStop; ai++) {
454 for (int bi = bStart; bi <= bStop; bi++) {
455 for (int ci = cStart; ci <= cStop; ci++) {
456 selectAsymmetricAtoms(image(ai, bi, ci));
457 }
458 }
459 }
460 }
461 }
462 }
463
464 @Override
465 public IntegerSchedule schedule() {
466 return schedule;
467 }
468
469 @Override
470 public void start() {
471 xyz = coordinates[0];
472 }
473
474
475
476
477
478
479
480
481
482
483
484 private int image(int i, int j, int k) {
485 if (i >= nA) {
486 i -= nA;
487 } else if (i < 0) {
488 i += nA;
489 }
490 if (j >= nB) {
491 j -= nB;
492 } else if (j < 0) {
493 j += nB;
494 }
495 if (k >= nC) {
496 k -= nC;
497 } else if (k < 0) {
498 k += nC;
499 }
500 return i + j * nA + k * nAB;
501 }
502
503 private void selectAsymmetricAtoms(final int pairCellIndex) {
504 final double xi = xyz[0][atomIndex];
505 final double yi = xyz[1][atomIndex];
506 final double zi = xyz[2][atomIndex];
507 final int[] pairList = cellList[iSymm];
508 int start = cellStart[iSymm][pairCellIndex];
509 final int pairStop = start + cellCount[iSymm][pairCellIndex];
510 final double[][] pair = coordinates[iSymm];
511
512 for (int j = start; j < pairStop; j++) {
513 final int aj = pairList[j];
514
515 if (select.get(aj)) {
516 continue;
517 }
518 final double xj = pair[0][aj];
519 final double yj = pair[1][aj];
520 final double zj = pair[2][aj];
521 final double xr = xi - xj;
522 final double yr = yi - yj;
523 final double zr = zi - zj;
524 final double d2 = crystal.image(xr, yr, zr);
525 if (d2 <= cutoff2) {
526 select.set(aj, true);
527 }
528 }
529 }
530 }
531 }