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.math.ScalarMath.doubleFactorial;
41 import static ffx.numerics.multipole.GKTensorMode.POTENTIAL;
42 import static java.lang.System.arraycopy;
43 import static java.util.Arrays.fill;
44 import static org.apache.commons.math3.util.FastMath.exp;
45 import static org.apache.commons.math3.util.FastMath.pow;
46 import static org.apache.commons.math3.util.FastMath.sqrt;
47
48
49
50
51 public class GKSource {
52
53 private GKTensorMode mode = POTENTIAL;
54
55
56
57
58 private double rb2;
59
60
61
62
63 private double expTerm;
64
65
66
67
68 private double f;
69
70
71
72
73 private double f1;
74
75
76
77
78 private double f2;
79
80
81
82
83 private final double gc;
84
85
86
87
88 private final double igc;
89
90
91
92
93 private double gcAiAj;
94
95
96
97
98 private double ratio;
99
100
101
102
103 private double fr;
104
105
106
107
108 private double r2;
109
110
111
112
113 private final int order;
114
115
116
117
118 private final double[] kirkwoodSource;
119
120
121
122
123 private final double[][] anmc;
124
125
126
127
128
129 private final double[] fn;
130
131
132
133
134
135 protected final double[] bn;
136
137
138
139
140 private final double[][] anm;
141
142
143
144
145
146 private final double[][] bnm;
147
148
149
150
151
152
153
154 public GKSource(int order, double gc) {
155 this.order = order;
156 this.gc = gc;
157 this.igc = 1.0 / gc;
158
159
160 kirkwoodSource = new double[order + 1];
161 for (short n = 0; n <= order; n++) {
162 kirkwoodSource[n] = pow(-1, n) * doubleFactorial(2 * n - 1);
163 }
164
165 anmc = new double[order + 1][];
166 for (int n = 0; n <= order; n++) {
167 anmc[n] = anmc(n);
168 }
169
170 fn = new double[order + 1];
171 anm = new double[order + 1][order + 1];
172 bn = new double[order + 1];
173 bnm = new double[order + 1][order + 1];
174 }
175
176
177
178
179
180
181
182 protected void source(double[] work, GKMultipoleOrder multipoleOrder) {
183 int mpoleOrder = multipoleOrder.getOrder();
184 fill(work, 0, mpoleOrder, 0.0);
185 if (mode == POTENTIAL) {
186
187 int derivatives = order - mpoleOrder;
188 arraycopy(anm[mpoleOrder], 0, work, mpoleOrder, derivatives + 1);
189 } else {
190
191 int derivatives = (order - 1) - mpoleOrder;
192 arraycopy(bnm[mpoleOrder], 0, work, mpoleOrder, derivatives + 1);
193 }
194 }
195
196
197
198
199
200
201
202
203
204
205 public void generateSource(GKTensorMode mode, GKMultipoleOrder multipole, double r2,
206 double ai, double aj) {
207 int multipoleOrder = multipole.getOrder();
208 this.mode = mode;
209
210 this.r2 = r2;
211 rb2 = ai * aj;
212 gcAiAj = gc * rb2;
213 ratio = -r2 / gcAiAj;
214 expTerm = exp(ratio);
215 fr = -2.0 / gcAiAj;
216
217 f = sqrt(r2 + rb2 * expTerm);
218 f1 = 1.0 - expTerm * igc;
219 f2 = 2.0 * expTerm / (gc * gcAiAj);
220
221 if (mode == POTENTIAL) {
222
223 anm(order, order - multipoleOrder);
224 } else {
225
226 bnm(order - 1, (order - 1) - multipoleOrder);
227 }
228 }
229
230
231
232
233
234
235
236
237
238
239
240 private void anm(int n, int derivatives) {
241
242 fn(n);
243
244
245 an0(n);
246
247
248 for (int d = 1; d <= derivatives; d++) {
249
250 var coef = anmc[d];
251
252 int limit = n - d;
253
254 for (int order = 0; order <= limit; order++) {
255
256 var terms = anm[order + 1];
257 var sum = 0.0;
258 for (int i = 1; i <= d; i++) {
259 sum += coef[i - 1] * fn[i] * terms[d - i];
260 }
261 anm[order][d] = sum;
262 }
263 }
264 }
265
266
267
268
269
270
271
272
273
274
275 private void bnm(int n, int derivatives) {
276
277 bn(n);
278
279
280 bn0(n);
281
282
283 for (int d = 1; d <= derivatives; d++) {
284
285 var coef = anmc[d];
286
287 int limit = n - d;
288
289 for (int order = 0; order <= limit; order++) {
290
291 var terma = anm[order + 1];
292 var termb = bnm[order + 1];
293 var sum = 0.0;
294 for (int i = 1; i <= d; i++) {
295 sum += coef[i - 1] * bn[i] * terma[d - i];
296 sum += coef[i - 1] * fn[i] * termb[d - i];
297 }
298 bnm[order][d] = sum;
299 }
300 }
301 }
302
303
304
305
306
307
308
309 private void fn(int n) {
310 switch (n) {
311 case 0 -> fn[0] = f;
312 case 1 -> {
313 fn[0] = f;
314 fn[1] = f1;
315 }
316 case 2 -> {
317 fn[0] = f;
318 fn[1] = f1;
319 fn[2] = f2;
320 }
321 case 3 -> {
322 fn[0] = f;
323 fn[1] = f1;
324 fn[2] = f2;
325 fn[3] = fr * f2;
326 }
327 case 4 -> {
328 fn[0] = f;
329 fn[1] = f1;
330 fn[2] = f2;
331 fn[3] = fr * f2;
332 fn[4] = fr * fn[3];
333 }
334 case 5 -> {
335 fn[0] = f;
336 fn[1] = f1;
337 fn[2] = f2;
338 fn[3] = fr * f2;
339 fn[4] = fr * fn[3];
340 fn[5] = fr * fn[4];
341 }
342 case 6 -> {
343 fn[0] = f;
344 fn[1] = f1;
345 fn[2] = f2;
346 fn[3] = fr * f2;
347 fn[4] = fr * fn[3];
348 fn[5] = fr * fn[4];
349 fn[6] = fr * fn[5];
350 }
351 default -> {
352 fn[0] = f;
353 fn[1] = f1;
354 fn[2] = f2;
355 for (int i = 3; i <= n; i++) {
356 fn[i] = fr * fn[i - 1];
357 }
358 }
359 }
360 }
361
362
363
364
365
366
367
368 protected void bn(int n) {
369 var b2 = 2.0 * expTerm / (gcAiAj * gcAiAj) * (-ratio - 1.0);
370 switch (n) {
371 case 0 -> bn[0] = 0.5 * expTerm * (1.0 - ratio);
372 case 1 -> {
373 bn[0] = 0.5 * expTerm * (1.0 - ratio);
374 bn[1] = -r2 * expTerm / (gcAiAj * gcAiAj);
375 }
376 case 2 -> {
377 bn[0] = 0.5 * expTerm * (1.0 - ratio);
378 bn[1] = -r2 * expTerm / (gcAiAj * gcAiAj);
379 bn[2] = b2;
380 }
381 default -> {
382 bn[0] = 0.5 * expTerm * (1.0 - ratio);
383 bn[1] = -r2 * expTerm / (gcAiAj * gcAiAj);
384 bn[2] = b2;
385 var br = 2.0 / (gcAiAj * rb2);
386 var f2 = 2.0 / (gc * gcAiAj) * expTerm;
387 var frA = 1.0;
388 var frB = fr;
389 for (int i = 3; i < n; i++) {
390
391 bn[i] = (i - 2) * frA * br * f2 + frB * b2;
392 frA *= fr;
393 frB *= fr;
394 }
395 }
396 }
397 }
398
399
400
401
402
403
404 private void an0(int n) {
405 double inverseF = 1.0 / f;
406 double inverseF2 = inverseF * inverseF;
407 for (int i = 0; i <= n; i++) {
408 anm[i][0] = kirkwoodSource[i] * inverseF;
409 inverseF *= inverseF2;
410 }
411 }
412
413
414
415
416
417
418 private void bn0(int n) {
419 for (int i = 0; i <= n; i++) {
420 bnm[i][0] = bn[0] * anm[i + 1][0];
421 }
422 }
423
424
425
426
427
428
429
430 protected static double[] anmc(int n) {
431 double[] ret = new double[n + 1];
432 ret[0] = 1.0;
433 switch (n) {
434 case 0 -> {
435 return ret;
436 }
437 case 1 -> {
438 ret[1] = 1.0;
439 return ret;
440 }
441 default -> {
442 ret[1] = 1.0;
443 var prev = new double[n];
444 prev[0] = 1.0;
445 prev[1] = 1.0;
446 for (int i = 3; i <= n; i++) {
447 for (int j = 2; j <= i - 1; j++) {
448 ret[j - 1] = prev[j - 2] + prev[j - 1];
449 }
450 ret[i - 1] = 1.0;
451 arraycopy(ret, 0, prev, 0, i);
452 }
453 return ret;
454 }
455 }
456 }
457
458
459
460
461
462
463
464
465
466 public static double cn(int n, double Eh, double Es) {
467 var ret = (n + 1) * (Eh - Es) / ((n + 1) * Es + n * Eh);
468 return ret / Eh;
469 }
470
471
472
473
474
475
476
477
478
479
480 public static double selfEnergy(PolarizableMultipole polarizableMultipole,
481 double ai, double Eh, double Es) {
482 double q2 = polarizableMultipole.q * polarizableMultipole.q;
483 double dx = polarizableMultipole.dx;
484 double dy = polarizableMultipole.dy;
485 double dz = polarizableMultipole.dz;
486 double dx2 = dx * dx;
487 double dy2 = dy * dy;
488 double dz2 = dz * dz;
489 double ux = polarizableMultipole.ux;
490 double uy = polarizableMultipole.uy;
491 double uz = polarizableMultipole.uz;
492 double qxy2 = polarizableMultipole.qxy * polarizableMultipole.qxy;
493 double qxz2 = polarizableMultipole.qxz * polarizableMultipole.qxz;
494 double qyz2 = polarizableMultipole.qyz * polarizableMultipole.qyz;
495 double qxx2 = polarizableMultipole.qxx * polarizableMultipole.qxx;
496 double qyy2 = polarizableMultipole.qyy * polarizableMultipole.qyy;
497 double qzz2 = polarizableMultipole.qzz * polarizableMultipole.qzz;
498
499 double a2 = ai * ai;
500 double a3 = ai * a2;
501 double a5 = a2 * a3;
502
503
504 double e0 = cn(0, Eh, Es) * q2 / ai;
505
506 double e1 = cn(1, Eh, Es) * (dx2 + dy2 + dz2) / a3;
507
508 double e2 = cn(2, Eh, Es) * (3.0 * (qxy2 + qxz2 + qyz2) + 6.0 * (qxx2 + qyy2 + qzz2)) / a5;
509
510
511 double ei = cn(1, Eh, Es) * (dx * ux + dy * uy + dz * uz) / a3;
512
513 return 0.5 * (e0 + e1 + e2 + ei);
514 }
515
516 }