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.IntegerForLoop;
41 import edu.rit.pj.IntegerSchedule;
42 import edu.rit.pj.ParallelRegion;
43 import ffx.crystal.Crystal;
44 import ffx.potential.bonded.Atom;
45
46 import java.nio.DoubleBuffer;
47 import java.util.logging.Level;
48 import java.util.logging.Logger;
49
50 import static java.lang.String.format;
51 import static java.util.Arrays.fill;
52 import static org.apache.commons.math3.util.FastMath.floor;
53
54
55
56
57
58
59
60 public class SpatialDensityRegion extends ParallelRegion {
61
62
63 protected static final Logger logger = Logger.getLogger(SpatialDensityRegion.class.getName());
64
65 public final int nThreads;
66 protected final int nSymm;
67 public int[] actualCount;
68 public int nAtoms;
69
70 protected int nA;
71
72 protected int nB;
73
74 protected int nC;
75
76
77
78 protected int actualWork;
79
80 protected double[][][] coordinates;
81 protected boolean[][] select;
82 protected Crystal crystal;
83 protected SpatialDensityLoop[] spatialDensityLoop;
84
85 int[] actualA;
86
87 int[] actualB;
88
89 int[] actualC;
90
91 int[][] cellList;
92
93 int[][] cellCount;
94
95 int[][] cellStart;
96
97 private int basisSize;
98
99 private int minWork;
100
101 private int nAB;
102
103 private int nCells;
104
105 private int nWork;
106
107 private int[][] cellIndex;
108
109 private int[] workA;
110
111 private int[] workB;
112
113 private int[] workC;
114
115
116
117
118 private int[][] cellOffset;
119
120 private double[] xf;
121 private double[] yf;
122 private double[] zf;
123 private int gridSize;
124 private double[] grid = null;
125 private DoubleBuffer gridBuffer;
126 private double initValue = 0.0;
127 private GridInitLoop gridInitLoop;
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144 public SpatialDensityRegion(
145 int gX,
146 int gY,
147 int gZ,
148 double[] grid,
149 int basisSize,
150 int nSymm,
151 int minWork,
152 int threadCount,
153 Crystal crystal,
154 Atom[] atoms,
155 double[][][] coordinates) {
156 this(gX, gY, gZ, basisSize, nSymm, minWork, threadCount, crystal, atoms, coordinates);
157 this.grid = grid;
158 if (grid != null) {
159 gridBuffer = DoubleBuffer.wrap(grid);
160 }
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179 private SpatialDensityRegion(
180 int gX,
181 int gY,
182 int gZ,
183 int basisSize,
184 int nSymm,
185 int minWork,
186 int threadCount,
187 Crystal crystal,
188 Atom[] atoms,
189 double[][][] coordinates) {
190 this.coordinates = coordinates;
191 this.nSymm = nSymm;
192 this.nAtoms = atoms.length;
193 this.nThreads = threadCount;
194 this.basisSize = basisSize;
195 this.minWork = minWork;
196 gridInitLoop = new GridInitLoop();
197 setCrystal(crystal.getUnitCell(), gX, gY, gZ);
198 }
199
200
201
202
203
204 public void assignAtomsToCells() {
205
206 selectAtoms();
207
208 for (int iSymm = 0; iSymm < nSymm; iSymm++) {
209 final int[] cellIndexs = cellIndex[iSymm];
210 final int[] cellCounts = cellCount[iSymm];
211 final int[] cellStarts = cellStart[iSymm];
212 final int[] cellLists = cellList[iSymm];
213 final int[] cellOffsets = cellOffset[iSymm];
214 final boolean[] selected = select[iSymm];
215
216
217 for (int i = 0; i < nCells; i++) {
218 cellCounts[i] = 0;
219 }
220
221
222 final double[][] xyz = coordinates[iSymm];
223 final double[] x = xyz[0];
224 final double[] y = xyz[1];
225 final double[] z = xyz[2];
226 crystal.toFractionalCoordinates(nAtoms, x, y, z, xf, yf, zf);
227
228
229 for (int i = 0; i < nAtoms; i++) {
230 if (!selected[i]) {
231 continue;
232 }
233
234 final int index = getCellIndex(i);
235 cellIndexs[i] = index;
236
237
238 cellOffsets[i] = cellCounts[index]++;
239 }
240
241
242 cellStarts[0] = 0;
243 for (int i = 1; i < nCells; i++) {
244 final int i1 = i - 1;
245 cellStarts[i] = cellStarts[i1] + cellCounts[i1];
246 }
247
248
249 for (int i = 0; i < nAtoms; i++) {
250 if (!selected[i]) {
251 continue;
252 }
253 final int index = cellIndexs[i];
254 cellLists[cellStarts[index]++] = i;
255 }
256
257
258 cellStarts[0] = 0;
259 for (int i = 1; i < nCells; i++) {
260 final int i1 = i - 1;
261 cellStarts[i] = cellStarts[i1] + cellCounts[i1];
262 }
263 }
264
265
266 actualWork = 0;
267 for (int icell = 0; icell < nWork; icell++) {
268 int ia = workA[icell];
269 int ib = workB[icell];
270 int ic = workC[icell];
271 int ii = count(ia, ib, ic);
272
273 if (nC > 1) {
274 ii += count(ia, ib, ic + 1);
275
276 if (nB > 1) {
277 ii += count(ia, ib + 1, ic);
278 ii += count(ia, ib + 1, ic + 1);
279
280 if (nA > 1) {
281 ii += count(ia + 1, ib, ic);
282 ii += count(ia + 1, ib, ic + 1);
283 ii += count(ia + 1, ib + 1, ic);
284 ii += count(ia + 1, ib + 1, ic + 1);
285 }
286 }
287 }
288
289
290 if (ii > 0) {
291 actualA[actualWork] = ia;
292 actualB[actualWork] = ib;
293 actualC[actualWork] = ic;
294 actualCount[actualWork++] = ii;
295 }
296 }
297
298 if (logger.isLoggable(Level.FINEST)) {
299 logger.finest(format(" Empty chunks: %d out of %d.", nWork - actualWork, nWork));
300 }
301 }
302
303 private int getCellIndex(int i) {
304 double xu = xf[i];
305 double yu = yf[i];
306 double zu = zf[i];
307
308
309 while (xu >= 1.0) {
310 xu -= 1.0;
311 }
312 while (xu < 0.0) {
313 xu += 1.0;
314 }
315 while (yu >= 1.0) {
316 yu -= 1.0;
317 }
318 while (yu < 0.0) {
319 yu += 1.0;
320 }
321 while (zu >= 1.0) {
322 zu -= 1.0;
323 }
324 while (zu < 0.0) {
325 zu += 1.0;
326 }
327
328
329 int a = (int) floor(xu * nA);
330 int b = (int) floor(yu * nB);
331 int c = (int) floor(zu * nC);
332
333
334 if (a >= nA) {
335 a = nA - 1;
336 }
337 if (b >= nB) {
338 b = nB - 1;
339 }
340 if (c >= nC) {
341 c = nC - 1;
342 }
343
344
345 final int index = a + b * nA + c * nAB;
346 return index;
347 }
348
349
350
351
352
353
354 public double[] getGrid() {
355 return grid;
356 }
357
358
359
360
361
362
363 public int getNsymm() {
364 return nSymm;
365 }
366
367
368
369
370
371
372
373
374
375 public int index(int ia, int ib, int ic) {
376 return ia + ib * nA + ic * nAB;
377 }
378
379
380 @Override
381 public void run() {
382 int ti = getThreadIndex();
383 int actualWork1 = actualWork - 1;
384 SpatialDensityLoop loop = spatialDensityLoop[ti];
385
386
387 loop.setNsymm(nSymm);
388
389 try {
390 execute(0, gridSize - 1, gridInitLoop);
391 execute(0, actualWork1, loop.setOctant(0));
392
393 if (nC > 1) {
394 execute(0, actualWork1, loop.setOctant(1));
395
396 if (nB > 1) {
397 execute(0, actualWork1, loop.setOctant(2));
398 execute(0, actualWork1, loop.setOctant(3));
399
400 if (nA > 1) {
401 execute(0, actualWork1, loop.setOctant(4));
402 execute(0, actualWork1, loop.setOctant(5));
403 execute(0, actualWork1, loop.setOctant(6));
404 execute(0, actualWork1, loop.setOctant(7));
405 }
406 }
407 }
408 } catch (Exception e) {
409 String message = " Exception in SpatialDensityRegion.";
410 logger.log(Level.SEVERE, message, e);
411 }
412 }
413
414
415
416
417
418
419 public void selectAtoms() {}
420
421
422
423
424
425
426 public void setAtoms(Atom[] atoms) {
427 nAtoms = atoms.length;
428 if (select == null || select.length < nSymm || select[0].length < nAtoms) {
429 select = new boolean[nSymm][nAtoms];
430 for (int i = 0; i < nSymm; i++) {
431 fill(select[i], true);
432 }
433 cellList = new int[nSymm][nAtoms];
434 cellIndex = new int[nSymm][nAtoms];
435 cellOffset = new int[nSymm][nAtoms];
436 }
437 if (xf == null || xf.length < nAtoms) {
438 xf = new double[nAtoms];
439 yf = new double[nAtoms];
440 zf = new double[nAtoms];
441 }
442 }
443
444
445
446
447
448
449
450
451
452 public final void setCrystal(Crystal crystal, int gX, int gY, int gZ) {
453
454 if (this.crystal != null && this.crystal.equals(crystal.getUnitCell())) {
455 return;
456 }
457
458 this.crystal = crystal.getUnitCell();
459 if (xf == null || xf.length < nAtoms) {
460 xf = new double[nAtoms];
461 yf = new double[nAtoms];
462 zf = new double[nAtoms];
463 }
464
465 gridSize = gX * gY * gZ * 2;
466 int nX = gX / basisSize;
467 int nY = gY / basisSize;
468 int nZ = gZ / basisSize;
469 if (nThreads > 1 && nZ > 1) {
470 if (nZ % 2 != 0) {
471 nZ--;
472 }
473 nC = nZ;
474 int div = 2;
475 int currentWork = nC / div / nThreads;
476
477 if (currentWork >= minWork || nY < 2) {
478 nA = 1;
479 nB = 1;
480
481 while (currentWork >= 2 * minWork) {
482 nC -= 2;
483 currentWork = nC / div / nThreads;
484 }
485
486 } else {
487 if (nY % 2 != 0) {
488 nY--;
489 }
490 nB = nY;
491 div = 4;
492 currentWork = nB * nC / div / nThreads;
493
494 if (currentWork >= minWork || nX < 2) {
495 nA = 1;
496 while (currentWork >= 2 * minWork) {
497 nB -= 2;
498 currentWork = nB * nC / div / nThreads;
499 }
500 } else {
501 if (nX % 2 != 0) {
502 nX--;
503 }
504 nA = nX;
505 div = 8;
506 currentWork = nA * nB * nC / div / nThreads;
507 while (currentWork >= 2 * minWork) {
508 nA -= 2;
509 currentWork = nA * nB * nC / div / nThreads;
510 }
511 }
512 }
513 nAB = nA * nB;
514 nCells = nAB * nC;
515 nWork = nCells / div;
516 } else {
517 nA = 1;
518 nB = 1;
519 nC = 1;
520 nAB = 1;
521 nCells = 1;
522 nWork = 1;
523 }
524
525 logger.fine(format(" Spatial cells: (%3d,%3d,%3d)", nA, nB, nC));
526 logger.fine(format(" Spatial work: %4d", nWork));
527
528 if (workA == null || workA.length < nWork) {
529 workA = new int[nWork];
530 workB = new int[nWork];
531 workC = new int[nWork];
532 actualA = new int[nWork];
533 actualB = new int[nWork];
534 actualC = new int[nWork];
535 actualCount = new int[nWork];
536 int index = 0;
537 for (int h = 0; h < nA; h += 2) {
538 for (int k = 0; k < nB; k += 2) {
539 for (int l = 0; l < nC; l += 2) {
540 workA[index] = h;
541 workB[index] = k;
542 workC[index++] = l;
543 }
544 }
545 }
546 }
547 if (select == null || select.length < nSymm || select[0].length < nAtoms) {
548 select = new boolean[nSymm][nAtoms];
549 for (int i = 0; i < nSymm; i++) {
550 fill(select[i], true);
551 }
552 cellList = new int[nSymm][nAtoms];
553 cellIndex = new int[nSymm][nAtoms];
554 cellOffset = new int[nSymm][nAtoms];
555 }
556 if (cellStart == null || cellStart.length < nSymm || cellStart[0].length < nCells) {
557 cellStart = new int[nSymm][nCells];
558 cellCount = new int[nSymm][nCells];
559 }
560 }
561
562
563
564
565
566
567 public void setDensityLoop(SpatialDensityLoop[] loops) {
568 spatialDensityLoop = loops;
569 }
570
571
572
573
574
575
576 public void setInitValue(double initValue) {
577 this.initValue = initValue;
578 }
579
580
581
582
583
584
585 void setGridBuffer(DoubleBuffer grid) {
586 gridBuffer = grid;
587 }
588
589 private int count(int ia, int ib, int ic) {
590 int count = 0;
591 for (int iSymm = 0; iSymm < nSymm; iSymm++) {
592 final int index = index(ia, ib, ic);
593 final int start = cellStart[iSymm][index];
594 final int stop = start + cellCount[iSymm][index];
595 count += (stop - start);
596 }
597 return count;
598 }
599
600 private class GridInitLoop extends IntegerForLoop {
601
602 private final IntegerSchedule schedule = IntegerSchedule.fixed();
603
604 @Override
605 public void run(int lb, int ub) {
606 if (gridBuffer != null) {
607
608 for (int i = lb; i <= ub; i++) {
609
610 gridBuffer.put(i, initValue);
611 }
612 }
613 }
614
615 @Override
616 public IntegerSchedule schedule() {
617 return schedule;
618 }
619 }
620 }