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