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.numerics.multipole;
39
40 import ffx.utilities.FFXTest;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 import org.junit.runners.Parameterized;
44 import org.junit.runners.Parameterized.Parameters;
45
46 import java.util.Arrays;
47 import java.util.Collection;
48 import java.util.logging.Logger;
49
50 import static ffx.numerics.multipole.MultipoleTensorTest.Qi;
51 import static ffx.numerics.multipole.MultipoleTensorTest.Qk;
52 import static ffx.numerics.multipole.MultipoleTensorTest.Ui;
53 import static ffx.numerics.multipole.MultipoleTensorTest.UiEwald;
54 import static ffx.numerics.multipole.MultipoleTensorTest.Uk;
55 import static ffx.numerics.multipole.MultipoleTensorTest.UkEwald;
56 import static ffx.numerics.multipole.MultipoleTensorTest.permTorqueI;
57 import static ffx.numerics.multipole.MultipoleTensorTest.permTorqueIEwald;
58 import static ffx.numerics.multipole.MultipoleTensorTest.permTorqueK;
59 import static ffx.numerics.multipole.MultipoleTensorTest.permTorqueKEwald;
60 import static ffx.numerics.multipole.MultipoleTensorTest.permanentEnergy;
61 import static ffx.numerics.multipole.MultipoleTensorTest.permanentEnergyEwald;
62 import static ffx.numerics.multipole.MultipoleTensorTest.polarGradICoulomb;
63 import static ffx.numerics.multipole.MultipoleTensorTest.polarGradIEwald;
64 import static ffx.numerics.multipole.MultipoleTensorTest.polarGradIThole;
65 import static ffx.numerics.multipole.MultipoleTensorTest.polarTorqueICoulomb;
66 import static ffx.numerics.multipole.MultipoleTensorTest.polarTorqueIEwald;
67 import static ffx.numerics.multipole.MultipoleTensorTest.polarTorqueIThole;
68 import static ffx.numerics.multipole.MultipoleTensorTest.polarTorqueKCoulomb;
69 import static ffx.numerics.multipole.MultipoleTensorTest.polarTorqueKEwald;
70 import static ffx.numerics.multipole.MultipoleTensorTest.polarTorqueKThole;
71 import static ffx.numerics.multipole.MultipoleTensorTest.polarizationEnergyCoulomb;
72 import static ffx.numerics.multipole.MultipoleTensorTest.polarizationEnergyEwald;
73 import static ffx.numerics.multipole.MultipoleTensorTest.polarizationEnergyThole;
74 import static ffx.numerics.multipole.MultipoleTensorTest.scaleMutual;
75 import static ffx.numerics.multipole.MultipoleTensorTest.xyz;
76 import static java.lang.String.format;
77 import static org.junit.Assert.assertEquals;
78
79
80
81
82
83
84
85 @RunWith(Parameterized.class)
86 public class MultipoleTensorGlobalTest extends FFXTest {
87
88
89
90
91 private static final Logger logger = Logger.getLogger(MultipoleTensorGlobalTest.class.getName());
92
93 private final double tolerance = 1.0e-13;
94 private final double fdTolerance = 1.0e-6;
95 private final double[] r = new double[3];
96 private final double[] tensor;
97 private final double[] noStorageTensor;
98 private final double[] fastTensor;
99 private final int order;
100 private final int tensorCount;
101 private final String info;
102 private final Operator operator;
103 private final double beta = 0.545;
104 private final double thole = 0.39;
105 private final double AiAk = 1.061104559485911;
106
107 public MultipoleTensorGlobalTest(
108 String info,
109 int order,
110 Operator operator) {
111 this.info = info;
112 this.order = order;
113 r[0] = xyz[0];
114 r[1] = xyz[1];
115 r[2] = xyz[2];
116 this.tensorCount = MultipoleUtilities.tensorCount(order);
117 tensor = new double[tensorCount];
118 noStorageTensor = new double[tensorCount];
119 fastTensor = new double[tensorCount];
120 this.operator = operator;
121 }
122
123 @Parameters
124 public static Collection<Object[]> data() {
125 return Arrays.asList(
126 new Object[][]{
127 {
128 "Order 5 Coulomb",
129 5,
130 Operator.COULOMB
131 },
132 {
133 "Order 5 Screened Coulomb",
134 5,
135 Operator.SCREENED_COULOMB
136 },
137 {
138 "Order 4 Thole Field",
139 4,
140 Operator.THOLE_FIELD
141 }
142 });
143 }
144
145 @Test
146 public void codingGenerationTest() {
147
148 if (operator != Operator.COULOMB) {
149 return;
150 }
151
152
153 double[] Qi = {0.11, 0.22, 0.33, 0.44, 0.11, 0.22, -0.33, 0.12, 0.13, 0.14};
154 double[] r = {2.11, 2.12, 2.13};
155 double[] tensor = new double[tensorCount];
156 CoulombTensorGlobal multipoleTensor = new CoulombTensorGlobal(order);
157 logger.info(format(" Writing Cartesian Order %d tensor recursion code:", order));
158 String code = multipoleTensor.codeTensorRecursion(r, tensor);
159 logger.info(format("\n%s", code));
160
161 int filter = 5;
162 logger.info(format(" Writing Cartesian Order %d vectorized tensor recursion code filtered at O=%d:", order, filter));
163 code = multipoleTensor.codeVectorTensorRecursion(filter);
164 logger.info(format("\n%s", code));
165
166 PolarizableMultipole polarizableMultipole = new PolarizableMultipole(Qi, Ui, Ui);
167 StringBuilder sb = new StringBuilder();
168 logger.info(" Writing potential code due to multipole I:");
169 multipoleTensor.codePotentialMultipoleI(polarizableMultipole, tensor, 0, 0, 0, sb);
170 logger.info("\n\n" + sb);
171
172 sb = new StringBuilder();
173 logger.info(" Writing potential vectorized code due to multipole I:");
174 multipoleTensor.codePotentialMultipoleISIMD(polarizableMultipole, tensor, 0, 0, 0, sb);
175 logger.info("\n\n" + sb);
176
177 sb = new StringBuilder();
178 logger.info(" Writing potential code due to multipole K:");
179 multipoleTensor.codePotentialMultipoleK(polarizableMultipole, tensor, 0, 0, 0, sb);
180 logger.info("\n\n" + sb);
181
182 sb = new StringBuilder();
183 logger.info(" Writing potential vectorized code due to multipole K:");
184 multipoleTensor.codePotentialMultipoleKSIMD(polarizableMultipole, tensor, 0, 0, 0, sb);
185 logger.info("\n\n" + sb);
186 }
187
188 @Test
189 public void permanentMultipoleEnergyAndGradTest() {
190
191 if (operator == Operator.THOLE_FIELD) {
192 return;
193 }
194
195 MultipoleTensor multipoleTensor = new CoulombTensorGlobal(order);
196 if (operator == Operator.SCREENED_COULOMB) {
197 multipoleTensor = new EwaldTensorGlobal(order, beta);
198 }
199
200 double delta = 1.0e-5;
201 double delta2 = 2.0 * 1.0e-5;
202 double[] Fi = new double[3];
203 double[] Fk = new double[3];
204 double[] Ti = new double[3];
205 double[] Tk = new double[3];
206
207 PolarizableMultipole mI = new PolarizableMultipole(Qi, Ui, Ui);
208 PolarizableMultipole mK = new PolarizableMultipole(Qk, Uk, Uk);
209 multipoleTensor.generateTensor(r);
210 double e = multipoleTensor.multipoleEnergyAndGradient(mI, mK, Fi, Fk, Ti, Tk);
211
212 if (operator == Operator.COULOMB) {
213 assertEquals(info + " Cartesian Multipole Energy", permanentEnergy, e, tolerance);
214 assertEquals(info + " Cartesian Multipole Torque I", permTorqueI[1], Ti[1], tolerance);
215 assertEquals(info + " Cartesian Multipole Torque K", permTorqueK[1], Tk[1], tolerance);
216 } else if (operator == Operator.SCREENED_COULOMB) {
217 assertEquals(info + " Cartesian Ewald Permanent Energy", permanentEnergyEwald, e, tolerance);
218 assertEquals(info + " Cartesian Ewald Multipole Torque I", permTorqueIEwald[1], Ti[1],
219 tolerance);
220 assertEquals(info + " Cartesian Ewald Multipole Torque K", permTorqueKEwald[1], Tk[1],
221 tolerance);
222 }
223
224
225 double aX = Fk[0];
226 double aY = Fk[1];
227 double aZ = Fk[2];
228
229 r[0] += delta;
230 multipoleTensor.generateTensor(r);
231 double posX = multipoleTensor.multipoleEnergy(mI, mK);
232 r[0] -= delta2;
233 multipoleTensor.generateTensor(r);
234 double negX = multipoleTensor.multipoleEnergy(mI, mK);
235 r[0] += delta;
236 r[1] += delta;
237 multipoleTensor.generateTensor(r);
238 double posY = multipoleTensor.multipoleEnergy(mI, mK);
239 r[1] -= delta2;
240 multipoleTensor.generateTensor(r);
241 double negY = multipoleTensor.multipoleEnergy(mI, mK);
242 r[1] += delta;
243 r[2] += delta;
244 multipoleTensor.generateTensor(r);
245 double posZ = multipoleTensor.multipoleEnergy(mI, mK);
246 r[2] -= delta2;
247 multipoleTensor.generateTensor(r);
248 double negZ = multipoleTensor.multipoleEnergy(mI, mK);
249 r[2] += delta;
250
251 double expect = aX;
252 double actual = (posX - negX) / delta2;
253 assertEquals(info + " Force X", expect, actual, fdTolerance);
254 expect = aY;
255 actual = (posY - negY) / delta2;
256 assertEquals(info + " Force Y", expect, actual, fdTolerance);
257 expect = aZ;
258 actual = (posZ - negZ) / delta2;
259 assertEquals(info + " Force Z", expect, actual, fdTolerance);
260 }
261
262 @Test
263 public void polarizationEnergyAndGradTest() {
264
265 MultipoleTensor multipoleTensor = new CoulombTensorGlobal(order);
266
267 if (operator == Operator.THOLE_FIELD) {
268 multipoleTensor = new TholeTensorGlobal(order, thole, AiAk);
269 } else if (operator == Operator.SCREENED_COULOMB) {
270 multipoleTensor = new EwaldTensorGlobal(order, beta);
271 }
272
273 double delta = 1.0e-5;
274 double delta2 = 2.0 * 1.0e-5;
275 double[] Gi = new double[3];
276 double[] Ti = new double[3];
277 double[] Tk = new double[3];
278
279 PolarizableMultipole mI = new PolarizableMultipole(Qi, Ui, Ui);
280 PolarizableMultipole mK = new PolarizableMultipole(Qk, Uk, Uk);
281 if (operator == Operator.SCREENED_COULOMB) {
282 mI.setInducedDipole(UiEwald, UiEwald);
283 mK.setInducedDipole(UkEwald, UkEwald);
284 }
285
286 multipoleTensor.generateTensor(r);
287 double e = multipoleTensor.polarizationEnergyAndGradient(mI, mK, 1.0, 1.0, scaleMutual, Gi, Ti,
288 Tk);
289
290
291 double aX = Gi[0];
292 double aY = Gi[1];
293 double aZ = Gi[2];
294
295 double energy = polarizationEnergyCoulomb;
296 double[] gradI = polarGradICoulomb;
297 double[] torqueI = polarTorqueICoulomb;
298 double[] torqueK = polarTorqueKCoulomb;
299 if (operator == Operator.THOLE_FIELD) {
300 energy = polarizationEnergyThole;
301 gradI = polarGradIThole;
302 torqueI = polarTorqueIThole;
303 torqueK = polarTorqueKThole;
304 } else if (operator == Operator.SCREENED_COULOMB) {
305 energy = polarizationEnergyEwald;
306 gradI = polarGradIEwald;
307 torqueI = polarTorqueIEwald;
308 torqueK = polarTorqueKEwald;
309 }
310 assertEquals(info + " Polarization Energy", energy, e, tolerance);
311 assertEquals(info + " Polarization GradX", gradI[0], aX, tolerance);
312 assertEquals(info + " Polarization GradY", gradI[1], aY, tolerance);
313 assertEquals(info + " Polarization GradZ", gradI[2], aZ, tolerance);
314 assertEquals(info + " Polarization TorqueX I", torqueI[0], Ti[0], tolerance);
315 assertEquals(info + " Polarization TorqueY I", torqueI[1], Ti[1], tolerance);
316 assertEquals(info + " Polarization TorqueZ I", torqueI[2], Ti[2], tolerance);
317 assertEquals(info + " Polarization TorqueX K", torqueK[0], Tk[0], tolerance);
318 assertEquals(info + " Polarization TorqueY K", torqueK[1], Tk[1], tolerance);
319 assertEquals(info + " Polarization TorqueZ K", torqueK[2], Tk[2], tolerance);
320
321
322 if (scaleMutual == 0.0) {
323
324 r[0] += delta;
325 multipoleTensor.generateTensor(r);
326 double posX = multipoleTensor.polarizationEnergy(mI, mK, 1.0);
327 r[0] -= delta2;
328 multipoleTensor.generateTensor(r);
329 double negX = multipoleTensor.polarizationEnergy(mI, mK, 1.0);
330 r[0] += delta;
331
332 r[1] += delta;
333 multipoleTensor.generateTensor(r);
334 double posY = multipoleTensor.polarizationEnergy(mI, mK, 1.0);
335 r[1] -= delta2;
336 multipoleTensor.generateTensor(r);
337 double negY = multipoleTensor.polarizationEnergy(mI, mK, 1.0);
338 r[1] += delta;
339 r[2] += delta;
340 multipoleTensor.generateTensor(r);
341 double posZ = multipoleTensor.polarizationEnergy(mI, mK, 1.0);
342 r[2] -= delta2;
343 multipoleTensor.generateTensor(r);
344 double negZ = multipoleTensor.polarizationEnergy(mI, mK, 1.0);
345 r[2] += delta;
346
347
348
349 double expect = -aX;
350 double actual = 2.0 * (posX - negX) / delta2;
351 assertEquals(info + " Grad X", expect, actual, fdTolerance);
352 expect = -aY;
353 actual = 2.0 * (posY - negY) / delta2;
354 assertEquals(info + " Grad Y", expect, actual, fdTolerance);
355 expect = -aZ;
356 actual = 2.0 * (posZ - negZ) / delta2;
357 assertEquals(info + " Grad Z", expect, actual, fdTolerance);
358 }
359 }
360
361 @Test
362 public void finiteDifferenceTest() {
363
364 MultipoleTensor multipoleTensor = new CoulombTensorGlobal(order);
365 if (operator == Operator.THOLE_FIELD) {
366 multipoleTensor = new TholeTensorGlobal(order, thole, AiAk);
367 } else if (operator == Operator.SCREENED_COULOMB) {
368 multipoleTensor = new EwaldTensorGlobal(order, beta);
369 }
370
371 multipoleTensor.recursion(r, tensor);
372
373 double[] tensorsPx = new double[tensorCount];
374 double[] tensorsNx = new double[tensorCount];
375 double[] tensorsPy = new double[tensorCount];
376 double[] tensorsNy = new double[tensorCount];
377 double[] tensorsPz = new double[tensorCount];
378 double[] tensorsNz = new double[tensorCount];
379 double delta = 1.0e-5;
380 double delta2 = delta * 2;
381 r[0] += delta;
382 multipoleTensor.noStorageRecursion(r, tensorsPx);
383 r[0] -= delta2;
384 multipoleTensor.noStorageRecursion(r, tensorsNx);
385 r[0] += delta;
386
387 r[1] += delta;
388 multipoleTensor.noStorageRecursion(r, tensorsPy);
389 r[1] -= delta2;
390 multipoleTensor.noStorageRecursion(r, tensorsNy);
391 r[1] += delta;
392
393 r[2] += delta;
394 multipoleTensor.noStorageRecursion(r, tensorsPz);
395 r[2] -= delta2;
396 multipoleTensor.noStorageRecursion(r, tensorsNz);
397 r[2] += delta;
398
399 tensorFiniteDifference(
400 multipoleTensor, delta2, tensorsPx, tensorsNx, tensorsPy, tensorsNy, tensorsPz, tensorsNz);
401
402 multipoleTensor.recursion(r, tensor);
403 tensorFiniteDifference(
404 multipoleTensor, delta2, tensorsPx, tensorsNx, tensorsPy, tensorsNy, tensorsPz, tensorsNz);
405
406 multipoleTensor.generateTensor();
407 multipoleTensor.getTensor(tensor);
408 tensorFiniteDifference(
409 multipoleTensor, delta2, tensorsPx, tensorsNx, tensorsPy, tensorsNy, tensorsPz, tensorsNz);
410 }
411
412
413
414
415
416
417 @Test
418 public void multipoleTensorTest() {
419 MultipoleTensor multipoleTensor = new CoulombTensorGlobal(order);
420 if (operator == Operator.THOLE_FIELD) {
421 multipoleTensor = new TholeTensorGlobal(order, thole, AiAk);
422 } else if (operator == Operator.SCREENED_COULOMB) {
423 multipoleTensor = new EwaldTensorGlobal(order, beta);
424 }
425
426
427 multipoleTensor.noStorageRecursion(r, noStorageTensor);
428 multipoleTensor.recursion(r, tensor);
429 multipoleTensor.generateTensor(r);
430 multipoleTensor.getTensor(fastTensor);
431
432 for (int i = 0; i < tensorCount; i++) {
433 double expect = noStorageTensor[i];
434 double actual = tensor[i];
435 assertEquals(info + " @ " + i, expect, actual, tolerance);
436 if (order >= 2 || order <= 6) {
437 expect = noStorageTensor[i];
438 actual = fastTensor[i];
439 assertEquals(info + " @ " + i, expect, actual, tolerance);
440 }
441 }
442 }
443
444 private void tensorFiniteDifference(MultipoleTensor multipoleTensor,
445 double delta2,
446 double[] tensorsPx, double[] tensorsNx,
447 double[] tensorsPy, double[] tensorsNy,
448 double[] tensorsPz, double[] tensorsNz) {
449
450 int start = 0;
451
452
453 if (operator == Operator.THOLE_FIELD) {
454 start = 1;
455 }
456
457
458 for (int l = start; l < order; l++) {
459
460 double expect = tensor[multipoleTensor.ti(l + 1, 0, 0)];
461 double actual =
462 (tensorsPx[multipoleTensor.ti(l, 0, 0)] - tensorsNx[multipoleTensor.ti(l, 0, 0)]) / delta2;
463 assertEquals(info + " @ " + l, expect, actual, fdTolerance);
464
465 expect = tensor[multipoleTensor.ti(l, 1, 0)];
466 actual =
467 (tensorsPy[multipoleTensor.ti(l, 0, 0)] - tensorsNy[multipoleTensor.ti(l, 0, 0)]) / delta2;
468 assertEquals(info + " @ " + l, expect, actual, fdTolerance);
469
470 expect = tensor[multipoleTensor.ti(l, 0, 1)];
471 actual =
472 (tensorsPz[multipoleTensor.ti(l, 0, 0)] - tensorsNz[multipoleTensor.ti(l, 0, 0)]) / delta2;
473 assertEquals(info + " @ " + l, expect, actual, fdTolerance);
474 }
475 for (int l = 0; l < order; l++) {
476 for (int m = 1; m < order - l; m++) {
477
478 double expect = tensor[multipoleTensor.ti(l + 1, m, 0)];
479 double actual =
480 (tensorsPx[multipoleTensor.ti(l, m, 0)] - tensorsNx[multipoleTensor.ti(l, m, 0)])
481 / delta2;
482 assertEquals(info + " " + l + " " + m, expect, actual, fdTolerance);
483
484 expect = tensor[multipoleTensor.ti(l, m + 1, 0)];
485 actual =
486 (tensorsPy[multipoleTensor.ti(l, m, 0)] - tensorsNy[multipoleTensor.ti(l, m, 0)])
487 / delta2;
488 assertEquals(info + " " + l + " " + m, expect, actual, fdTolerance);
489
490 expect = tensor[multipoleTensor.ti(l, m, 1)];
491 actual =
492 (tensorsPz[multipoleTensor.ti(l, m, 0)] - tensorsNz[multipoleTensor.ti(l, m, 0)])
493 / delta2;
494 assertEquals(info + " " + l + " " + m, expect, actual, fdTolerance);
495 }
496 }
497
498 for (int l = 0; l < order; l++) {
499 for (int m = 0; m < order - l; m++) {
500 for (int n = 1; n < order - l - m; n++) {
501
502 double expect = tensor[multipoleTensor.ti(l + 1, m, n)];
503 double actual =
504 (tensorsPx[multipoleTensor.ti(l, m, n)] - tensorsNx[multipoleTensor.ti(l, m, n)])
505 / delta2;
506 assertEquals(info + " " + l + " " + m, expect, actual, fdTolerance);
507
508 expect = tensor[multipoleTensor.ti(l, m + 1, n)];
509 actual =
510 (tensorsPy[multipoleTensor.ti(l, m, n)] - tensorsNy[multipoleTensor.ti(l, m, n)])
511 / delta2;
512 assertEquals(info + " " + l + " " + m, expect, actual, fdTolerance);
513
514 expect = tensor[multipoleTensor.ti(l, m, n + 1)];
515 actual =
516 (tensorsPz[multipoleTensor.ti(l, m, n)] - tensorsNz[multipoleTensor.ti(l, m, n)])
517 / delta2;
518 assertEquals(info + " " + l + " " + m, expect, actual, fdTolerance);
519 }
520 }
521 }
522 }
523
524 }