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