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.octree;
39
40 import org.apache.commons.lang3.BooleanUtils;
41
42 import java.util.ArrayList;
43 import java.util.logging.Logger;
44
45
46
47
48
49 public class Octree {
50
51 private static final Logger logger = Logger.getLogger(Octree.class.getName());
52
53 private final ArrayList<OctreeCell> leaves = new ArrayList<>();
54
55
56
57
58 private final int nCritical;
59
60 private final ArrayList<OctreeParticle> particles;
61
62 private final double theta;
63
64 private ArrayList<OctreeCell> cells = new ArrayList<>();
65
66
67
68
69
70
71
72 public Octree(ArrayList<OctreeParticle> particles) {
73 this.particles = particles;
74 this.nCritical = 10;
75 this.theta = 0.5;
76 }
77
78
79
80
81
82
83
84 public Octree(int nCritical, ArrayList<OctreeParticle> particles) {
85 this.nCritical = nCritical;
86 this.particles = particles;
87 this.theta = 0.5;
88 }
89
90
91
92
93
94
95
96
97 public Octree(int nCritical, ArrayList<OctreeParticle> particles, double theta) {
98 this.nCritical = nCritical;
99 this.particles = particles;
100 this.theta = theta;
101 }
102
103
104
105
106
107
108
109 public void addChild(int octant, int p) {
110 OctreeCell tempCell = new OctreeCell(nCritical);
111
112
113
114 cells.add(tempCell);
115
116
117 int c = cells.size() - 1;
118
119
120 cells.get(c).setR((cells.get(p).getR()) * 0.5);
121 cells.get(c).setX((cells.get(p).getX()) * ((octant & 1) * 2 - 1));
122 cells.get(c).setY((cells.get(p).getY()) * ((octant & 2) - 1));
123 cells.get(c).setZ((cells.get(p).getZ()) * ((octant & 4) * 0.5 - 1));
124
125
126 cells.get(c).setParentIndex(p);
127 cells.get(c).setChildren(octant, c);
128 cells.get(c).setnChild(cells.get(p).getnChild() | (1 << octant));
129 }
130
131
132
133
134
135
136 public void buildTree(OctreeCell root) {
137
138 cells = new ArrayList<>();
139
140
141 cells.add(root);
142
143
144 int n = particles.size();
145
146 for (int i = 0; i < n; i++) {
147 int current = 0;
148
149 while (cells.get(current).getNumLeaves() >= nCritical) {
150 cells.get(current).setNumLeaves(cells.get(current).getNumLeaves() + 1);
151 int octX = 0;
152 int octY = 0;
153 int octZ = 0;
154
155 if (particles.get(i).getX() > cells.get(current).getX()) {
156 octX = 1;
157 }
158 if (particles.get(i).getY() > cells.get(current).getY()) {
159 octY = 1;
160 }
161 if (particles.get(i).getZ() > cells.get(current).getZ()) {
162 octZ = 1;
163 }
164
165
166 int octant = octX + (octY << 1) + (octZ << 2);
167
168
169 boolean noChildInOctant =
170 BooleanUtils.toBoolean(cells.get(current).getnChild() & (1 << octant));
171 if (noChildInOctant) {
172 addChild(octant, current);
173 }
174
175 current = cells.get(current).getChildAtIndex(octant);
176 }
177
178
179 cells.get(current).setLeaf(cells.get(current).getNumLeaves(), i);
180 cells.get(current).setNumLeaves(cells.get(current).getNumLeaves() + 1);
181
182
183 if (cells.get(current).getNumLeaves() >= nCritical) {
184 splitCell(current);
185 }
186 }
187
188
189 }
190
191
192 public void directSum() {
193 for (int i = 0; i < particles.size(); i++) {
194 for (int j = 0; j < particles.size(); j++) {
195 if (j != i) {
196 double r = particles.get(i).distance(particles.get(j));
197 particles.get(j).addToPhi(particles.get(j).getCharge() / r);
198 }
199 }
200 }
201
202 for (OctreeParticle particle : particles) {
203 particle.addToPhi(0);
204 }
205 }
206
207
208
209
210
211
212
213
214 public double distance(double[] array, OctreePoint point) {
215 return Math.sqrt(
216 Math.pow((array[0] - point.getX()), 2)
217 + Math.pow((array[1] - point.getY()), 2)
218 + Math.pow((array[2] - point.getZ()), 2));
219 }
220
221
222 public void evalPotnetial() {
223 for (int i = 0; i < particles.size(); i++) {
224 evalAtTarget(0, i);
225 }
226 }
227
228
229
230
231
232
233
234 public void l2Error(double[] phiDirect, double[] phiTree) {
235 double errorSumNum = 0.0;
236 double errorSumDenom = 0.0;
237 for (int i = 0; i < phiDirect.length; i++) {
238 errorSumNum = errorSumNum + Math.pow((phiDirect[i] - phiTree[i]), 2);
239 errorSumDenom = errorSumDenom + Math.pow(phiDirect[i], 2);
240 }
241 double error = Math.sqrt(errorSumNum / errorSumDenom);
242 logger.info("L2 Norm Error: " + error);
243 }
244
245
246 public void upwardSweep() {
247 for (int c = (cells.size() - 1); c > 0; c--) {
248 int p = cells.get(c).getParentIndex();
249 M2M(p, c);
250 }
251 }
252
253
254
255
256
257
258 private void splitCell(int p) {
259 for (int i = 0; i < nCritical; i++) {
260 int octX = 0;
261 int octY = 0;
262 int octZ = 0;
263
264 if (particles.get(i).getX() > cells.get(p).getX()) {
265 octX = 1;
266 }
267 if (particles.get(i).getY() > cells.get(p).getY()) {
268 octY = 1;
269 }
270 if (particles.get(i).getZ() > cells.get(p).getZ()) {
271 octZ = 1;
272 }
273
274
275 int octant = octX + (octY << 1) + (octZ << 2);
276
277
278 boolean noChildInOctant = BooleanUtils.toBoolean(cells.get(p).getnChild() & (1 << octant));
279 if (noChildInOctant) {
280 addChild(octant, p);
281 }
282
283
284 int c = cells.get(p).getChildAtIndex(octant);
285 cells.get(c).setLeaf(cells.get(c).getNumLeaves(), 1);
286 cells.get(c).setNumLeaves(cells.get(c).getNumLeaves() + 1);
287
288
289 if (cells.get(c).getNumLeaves() >= nCritical) {
290 splitCell(c);
291 }
292 }
293 }
294
295
296
297
298
299
300
301 private void getMultipole(int p, ArrayList<Integer> leaves) {
302
303 if (cells.get(p).getNumLeaves() >= nCritical) {
304 for (int c = 0; c < 8; c++) {
305 if (BooleanUtils.toBoolean(cells.get(p).getnChild() & (1 << c))) {
306 getMultipole(cells.get(p).getChildAtIndex(c), leaves);
307 }
308 }
309 } else {
310
311 for (int i = 0; i < cells.get(p).getNumLeaves(); i++) {
312 int l = cells.get(p).getLeavesValueAtIndex(i);
313 double dx = cells.get(p).getX() - particles.get(l).getX();
314 double dy = cells.get(p).getY() - particles.get(l).getY();
315 double dz = cells.get(p).getZ() - particles.get(l).getZ();
316
317
318 double charge = particles.get(l).getCharge();
319 double[] calculatedMultipole = new double[10];
320
321 calculatedMultipole[0] = charge;
322
323 calculatedMultipole[1] = dx * charge;
324 calculatedMultipole[2] = dy * charge;
325 calculatedMultipole[3] = dz * charge;
326
327 calculatedMultipole[4] = (Math.pow(dx, 2) * 0.5) * charge;
328 calculatedMultipole[5] = (Math.pow(dy, 2) * 0.5) * charge;
329 calculatedMultipole[6] = (Math.pow(dz, 2) * 0.5) * charge;
330 calculatedMultipole[7] = ((dx * dy) * 0.5) * charge;
331 calculatedMultipole[8] = ((dy * dz) * 0.5) * charge;
332 calculatedMultipole[9] = ((dz * dx) * 0.5) * charge;
333
334
335 cells.get(p).addToMultipole(calculatedMultipole);
336
337
338 leaves.add(p);
339 }
340 }
341 }
342
343
344
345
346
347
348
349 private void M2M(int p, int c) {
350 double dx = cells.get(p).getX() - cells.get(c).getX();
351 double dy = cells.get(p).getY() - cells.get(c).getY();
352 double dz = cells.get(p).getZ() - cells.get(c).getZ();
353
354 double[] Dxyz = new double[] {dx, dy, dz};
355 double[] Dyzx = new double[] {dy, dz, dx};
356
357 cells.get(p).addToMultipole(cells.get(c).getMultipole());
358
359 double[] currentChildMultipole = cells.get(c).getMultipole();
360
361
362 double[] additionalMultipoleTerms = new double[10];
363
364 additionalMultipoleTerms[0] = 0;
365
366 additionalMultipoleTerms[1] = currentChildMultipole[0] * Dxyz[0];
367 additionalMultipoleTerms[2] = currentChildMultipole[0] * Dxyz[1];
368 additionalMultipoleTerms[3] = currentChildMultipole[0] * Dxyz[2];
369
370 additionalMultipoleTerms[4] =
371 currentChildMultipole[1] * Dxyz[0] + 0.5 * currentChildMultipole[0] * Math.pow(Dxyz[0], 2);
372 additionalMultipoleTerms[5] =
373 currentChildMultipole[2] * Dxyz[1] + 0.5 * currentChildMultipole[0] * Math.pow(Dxyz[1], 2);
374 additionalMultipoleTerms[6] =
375 currentChildMultipole[3] * Dxyz[2] + 0.5 * currentChildMultipole[0] * Math.pow(Dxyz[2], 2);
376
377 additionalMultipoleTerms[7] =
378 0.5 * currentChildMultipole[2] * Dyzx[0]
379 + 0.5 * currentChildMultipole[1] * Dxyz[0]
380 + 0.5 * currentChildMultipole[0] * Dxyz[0] * Dyzx[0];
381 additionalMultipoleTerms[8] =
382 0.5 * currentChildMultipole[3] * Dyzx[1]
383 + 0.5 * currentChildMultipole[2] * Dxyz[1]
384 + 0.5 * currentChildMultipole[0] * Dxyz[1] * Dyzx[1];
385 additionalMultipoleTerms[9] =
386 0.5 * currentChildMultipole[1] * Dyzx[2]
387 + 0.5 * currentChildMultipole[3] * Dxyz[2]
388 + 0.5 * currentChildMultipole[0] * Dxyz[2] * Dyzx[2];
389
390 cells.get(p).addToMultipole(additionalMultipoleTerms);
391 }
392
393
394
395
396
397
398
399 private void evalAtTarget(int p, int i) {
400
401
402 if (cells.get(p).getNumLeaves() >= nCritical) {
403
404
405 for (int oct = 0; oct < 8; oct++) {
406 if (BooleanUtils.toBoolean(cells.get(p).getnChild() & (1 << oct))) {
407 int c = cells.get(p).getChildAtIndex(oct);
408 double r = particles.get(i).distance(cells.get(c));
409
410
411 if (cells.get(c).getR() > theta * r) {
412 evalAtTarget(c, i);
413 } else {
414 double dx = particles.get(i).getX() - cells.get(c).getX();
415 double dy = particles.get(i).getY() - cells.get(c).getY();
416 double dz = particles.get(i).getZ() - cells.get(c).getZ();
417 double r3 = Math.pow(r, 3);
418 double r5 = r3 * Math.pow(r, 2);
419
420
421 double[] weight = new double[10];
422 weight[0] = 1 / r;
423 weight[1] = -dx / r3;
424 weight[2] = -dy / r3;
425 weight[3] = -dz / r3;
426 weight[4] = (3 * Math.pow(dx, 2)) / r5 - (1 / r3);
427 weight[5] = (3 * Math.pow(dy, 2)) / r5 - (1 / r3);
428 weight[6] = (3 * Math.pow(dz, 2)) / r5 - (1 / r3);
429 weight[7] = 3 * dx * dy / r5;
430 weight[8] = 3 * dy * dz / r5;
431 weight[9] = 3 * dz * dx / r5;
432
433
434 double dotProduct = 0.0;
435 for (int d = 0; d < weight.length; d++) {
436 double[] multipoleArray = cells.get(c).getMultipole();
437 dotProduct = dotProduct + multipoleArray[d] * weight[d];
438 }
439
440 particles.get(i).addToPhi(dotProduct);
441 }
442 } else {
443
444 for (int j = 0; j < cells.get(p).getNumLeaves(); j++) {
445 OctreeParticle source = particles.get(cells.get(p).getLeavesValueAtIndex(j));
446 double r = particles.get(i).distance(source);
447 if (r != 0) {
448 particles.get(i).addToPhi(source.getCharge() / r);
449 }
450 }
451 }
452 }
453 }
454 }
455 }