1 // ******************************************************************************
2 //
3 // Title: Force Field X.
4 // Description: Force Field X - Software for Molecular Biophysics.
5 // Copyright: Copyright (c) Michael J. Schnieders 2001-2025.
6 //
7 // This file is part of Force Field X.
8 //
9 // Force Field X is free software; you can redistribute it and/or modify it
10 // under the terms of the GNU General Public License version 3 as published by
11 // the Free Software Foundation.
12 //
13 // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 // details.
17 //
18 // You should have received a copy of the GNU General Public License along with
19 // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20 // Place, Suite 330, Boston, MA 02111-1307 USA
21 //
22 // Linking this library statically or dynamically with other modules is making a
23 // combined work based on this library. Thus, the terms and conditions of the
24 // GNU General Public License cover the whole combination.
25 //
26 // As a special exception, the copyright holders of this library give you
27 // permission to link this library with independent modules to produce an
28 // executable, regardless of the license terms of these independent modules, and
29 // to copy and distribute the resulting executable under terms of your choice,
30 // provided that you also meet, for each linked independent module, the terms
31 // and conditions of the license of that module. An independent module is a
32 // module which is not derived from or based on this library. If you modify this
33 // library, you may extend this exception to your version of the library, but
34 // you are not obligated to do so. If you do not wish to do so, delete this
35 // exception statement from your version.
36 //
37 // ******************************************************************************
38 package ffx.numerics.multipole;
39
40 import jdk.incubator.vector.DoubleVector;
41
42 /**
43 * The GeneralizedKirkwoodTensor class contains utilities for generated Generalized Kirkwood
44 * interaction tensors.
45 *
46 * @author Michael J. Schnieders
47 * @since 1.0
48 */
49 public class GKTensorGlobal extends CoulombTensorGlobal {
50
51 /**
52 * The GK tensor can be constructed for monopoles (GB), dipoles or quadrupoles.
53 */
54 protected final GKMultipoleOrder multipoleOrder;
55
56 /**
57 * The Kirkwood dielectric function for the given multipole order.
58 */
59 private final double c;
60
61 private final GKSource gkSource;
62
63 /**
64 * Construct a new GKTensorGlobal object.
65 *
66 * @param multipoleOrder The multipole order.
67 * @param order The number of derivatives to complete.
68 * @param gkSource Generate the source terms for the GK recurrence.
69 * @param Eh Homogeneous dielectric constant.
70 * @param Es Solvent dielectric constant.
71 */
72 public GKTensorGlobal(GKMultipoleOrder multipoleOrder, int order, GKSource gkSource,
73 double Eh, double Es) {
74 super(order);
75 this.multipoleOrder = multipoleOrder;
76 this.gkSource = gkSource;
77
78 // Load the dielectric function
79 c = GKSource.cn(multipoleOrder.getOrder(), Eh, Es);
80 }
81
82 /**
83 * GK Permanent multipole energy.
84 *
85 * @param mI PolarizableMultipole at site I.
86 * @param mK PolarizableMultipole at site K.
87 * @return a double.
88 */
89 @Override
90 public double multipoleEnergy(PolarizableMultipole mI, PolarizableMultipole mK) {
91 return switch (multipoleOrder) {
92 default -> {
93 chargeIPotentialAtK(mI, 2);
94 double eK = multipoleEnergy(mK);
95 chargeKPotentialAtI(mK, 2);
96 double eI = multipoleEnergy(mI);
97 yield c * 0.5 * (eK + eI);
98 }
99 case DIPOLE -> {
100 dipoleIPotentialAtK(mI.dx, mI.dy, mI.dz, 2);
101 double eK = multipoleEnergy(mK);
102 dipoleKPotentialAtI(mK.dx, mK.dy, mK.dz, 2);
103 double eI = multipoleEnergy(mI);
104 yield c * 0.5 * (eK + eI);
105 }
106 case QUADRUPOLE -> {
107 quadrupoleIPotentialAtK(mI, 2);
108 double eK = multipoleEnergy(mK);
109 quadrupoleKPotentialAtI(mK, 2);
110 double eI = multipoleEnergy(mI);
111 yield c * 0.5 * (eK + eI);
112 }
113 };
114 }
115
116 /**
117 * GK Permanent multipole energy and gradient.
118 *
119 * @param mI PolarizableMultipole at site I.
120 * @param mK PolarizableMultipole at site K.
121 * @param Gi Coordinate gradient at site I.
122 * @param Gk Coordinate gradient at site K.
123 * @param Ti Torque at site I.
124 * @param Tk Torque at site K.
125 * @return the permanent multipole GK energy.
126 */
127 @Override
128 public double multipoleEnergyAndGradient(PolarizableMultipole mI, PolarizableMultipole mK,
129 double[] Gi, double[] Gk, double[] Ti, double[] Tk) {
130 return switch (multipoleOrder) {
131 default -> monopoleEnergyAndGradient(mI, mK, Gi, Gk, Ti, Tk);
132 case DIPOLE -> dipoleEnergyAndGradient(mI, mK, Gi, Gk, Ti, Tk);
133 case QUADRUPOLE -> quadrupoleEnergyAndGradient(mI, mK, Gi, Gk, Ti, Tk);
134 };
135 }
136
137 /**
138 * Permanent multipole energy and gradient using the GK monopole tensor.
139 *
140 * @param mI PolarizableMultipole at site I.
141 * @param mK PolarizableMultipole at site K.
142 * @param Gi Coordinate gradient at site I.
143 * @param Gk Coordinate gradient at site K.
144 * @param Ti Torque at site I.
145 * @param Tk Torque at site K.
146 * @return the permanent multipole GK energy.
147 */
148 protected double monopoleEnergyAndGradient(PolarizableMultipole mI, PolarizableMultipole mK,
149 double[] Gi, double[] Gk, double[] Ti, double[] Tk) {
150
151 // Compute the potential due to a multipole component at site I.
152 chargeIPotentialAtK(mI, 3);
153 double eK = multipoleEnergy(mK);
154 multipoleGradient(mK, Gk);
155 multipoleTorque(mK, Tk);
156
157 // Compute the potential due to a multipole component at site K.
158 chargeKPotentialAtI(mK, 3);
159 double eI = multipoleEnergy(mI);
160 multipoleGradient(mI, Gi);
161 multipoleTorque(mI, Ti);
162
163 double scale = c * 0.5;
164 Gi[0] = scale * (Gi[0] - Gk[0]);
165 Gi[1] = scale * (Gi[1] - Gk[1]);
166 Gi[2] = scale * (Gi[2] - Gk[2]);
167 Gk[0] = -Gi[0];
168 Gk[1] = -Gi[1];
169 Gk[2] = -Gi[2];
170
171 Ti[0] = scale * Ti[0];
172 Ti[1] = scale * Ti[1];
173 Ti[2] = scale * Ti[2];
174 Tk[0] = scale * Tk[0];
175 Tk[1] = scale * Tk[1];
176 Tk[2] = scale * Tk[2];
177
178 return scale * (eK + eI);
179 }
180
181 /**
182 * Permanent multipole energy and gradient using the GK dipole tensor.
183 *
184 * @param mI PolarizableMultipole at site I.
185 * @param mK PolarizableMultipole at site K.
186 * @param Gi Coordinate gradient at site I.
187 * @param Gk Coordinate gradient at site K.
188 * @param Ti Torque at site I.
189 * @param Tk Torque at site K.
190 * @return the permanent multipole GK energy.
191 */
192 protected double dipoleEnergyAndGradient(PolarizableMultipole mI, PolarizableMultipole mK,
193 double[] Gi, double[] Gk, double[] Ti, double[] Tk) {
194
195 // Compute the potential due to a multipole component at site I.
196 dipoleIPotentialAtK(mI.dx, mI.dy, mI.dz, 3);
197 double eK = multipoleEnergy(mK);
198 multipoleGradient(mK, Gk);
199 multipoleTorque(mK, Tk);
200
201 // Need the torque on site I pole due to site K multipole.
202 // Only torque on the site I dipole.
203 multipoleKPotentialAtI(mK, 1);
204 dipoleTorque(mI, Ti);
205
206 // Compute the potential due to a multipole component at site K.
207 dipoleKPotentialAtI(mK.dx, mK.dy, mK.dz, 3);
208 double eI = multipoleEnergy(mI);
209 multipoleGradient(mI, Gi);
210 multipoleTorque(mI, Ti);
211
212 // Need the torque on site K pole due to multipole on site I.
213 // Only torque on the site K dipole.
214 multipoleIPotentialAtK(mI, 1);
215 dipoleTorque(mK, Tk);
216
217 double scale = c * 0.5;
218 Gi[0] = scale * (Gi[0] - Gk[0]);
219 Gi[1] = scale * (Gi[1] - Gk[1]);
220 Gi[2] = scale * (Gi[2] - Gk[2]);
221 Gk[0] = -Gi[0];
222 Gk[1] = -Gi[1];
223 Gk[2] = -Gi[2];
224
225 Ti[0] = scale * Ti[0];
226 Ti[1] = scale * Ti[1];
227 Ti[2] = scale * Ti[2];
228 Tk[0] = scale * Tk[0];
229 Tk[1] = scale * Tk[1];
230 Tk[2] = scale * Tk[2];
231
232 return scale * (eK + eI);
233 }
234
235 /**
236 * Permanent multipole energy and gradient using the GK quadrupole tensor.
237 *
238 * @param mI PolarizableMultipole at site I.
239 * @param mK PolarizableMultipole at site K.
240 * @param Gi Coordinate gradient at site I.
241 * @param Gk Coordinate gradient at site K.
242 * @param Ti Torque at site I.
243 * @param Tk Torque at site K.
244 * @return the permanent multipole GK energy.
245 */
246 protected double quadrupoleEnergyAndGradient(PolarizableMultipole mI, PolarizableMultipole mK,
247 double[] Gi, double[] Gk, double[] Ti, double[] Tk) {
248
249 // Compute the potential due to a multipole component at site I.
250 quadrupoleIPotentialAtK(mI, 3);
251 double eK = multipoleEnergy(mK);
252 multipoleGradient(mK, Gk);
253 multipoleTorque(mK, Tk);
254
255 // Need the torque on site I quadrupole due to site K multipole.
256 multipoleKPotentialAtI(mK, 2);
257 quadrupoleTorque(mI, Ti);
258
259 // Compute the potential due to a multipole component at site K.
260 quadrupoleKPotentialAtI(mK, 3);
261 double eI = multipoleEnergy(mI);
262 multipoleGradient(mI, Gi);
263 multipoleTorque(mI, Ti);
264
265 // Need the torque on site K quadrupole due to site I multipole.
266 multipoleIPotentialAtK(mI, 2);
267 quadrupoleTorque(mK, Tk);
268
269 double scale = c * 0.5;
270 Gi[0] = scale * (Gi[0] - Gk[0]);
271 Gi[1] = scale * (Gi[1] - Gk[1]);
272 Gi[2] = scale * (Gi[2] - Gk[2]);
273 Gk[0] = -Gi[0];
274 Gk[1] = -Gi[1];
275 Gk[2] = -Gi[2];
276
277 Ti[0] = scale * Ti[0];
278 Ti[1] = scale * Ti[1];
279 Ti[2] = scale * Ti[2];
280 Tk[0] = scale * Tk[0];
281 Tk[1] = scale * Tk[1];
282 Tk[2] = scale * Tk[2];
283
284 return scale * (eK + eI);
285 }
286
287 /**
288 * GK Permanent multipole Born grad.
289 *
290 * @param mI PolarizableMultipole at site I.
291 * @param mK PolarizableMultipole at site K.
292 * @return a double.
293 */
294 public double multipoleEnergyBornGrad(PolarizableMultipole mI, PolarizableMultipole mK) {
295 return multipoleEnergy(mI, mK);
296 }
297
298 /**
299 * GK Polarization Energy.
300 *
301 * @param mI PolarizableMultipole at site I.
302 * @param mK PolarizableMultipole at site K.
303 * @param scaleEnergy This is ignored, since masking/scaling is not applied to GK interactions
304 * (everything is intermolecular).
305 * @return a double.
306 */
307 public double polarizationEnergy(PolarizableMultipole mI, PolarizableMultipole mK, DoubleVector scaleEnergy) {
308 return polarizationEnergy(mI, mK);
309 }
310
311 /**
312 * GK Polarization Energy.
313 *
314 * @param mI PolarizableMultipole at site I.
315 * @param mK PolarizableMultipole at site K.
316 * @return a double.
317 */
318 public double polarizationEnergy(PolarizableMultipole mI, PolarizableMultipole mK) {
319 return switch (multipoleOrder) {
320 default -> {
321 // Find the GK charge potential of site I at site K.
322 chargeIPotentialAtK(mI, 1);
323 // Energy of induced dipole K in the field of permanent charge I.
324 double eK = polarizationEnergy(mK);
325 // Find the GK charge potential of site K at site I.
326 chargeKPotentialAtI(mK, 1);
327 // Energy of induced dipole I in the field of permanent charge K.
328 double eI = polarizationEnergy(mI);
329 yield c * 0.5 * (eK + eI);
330 }
331 case DIPOLE -> {
332 // Find the GK dipole potential of site I at site K.
333 dipoleIPotentialAtK(mI.dx, mI.dy, mI.dz, 1);
334 // Energy of induced dipole K in the field of permanent dipole I.
335 double eK = polarizationEnergy(mK);
336 // Find the GK induced dipole potential of site I at site K.
337 dipoleIPotentialAtK(mI.ux, mI.uy, mI.uz, 2);
338 // Energy of permanent multipole K in the field of induced dipole I.
339 eK += 0.5 * multipoleEnergy(mK);
340 // Find the GK dipole potential of site K at site I.
341 dipoleKPotentialAtI(mK.dx, mK.dy, mK.dz, 1);
342 // Energy of induced dipole I in the field of permanent dipole K.
343 double eI = polarizationEnergy(mI);
344 // Find the GK induced dipole potential of site K at site I.
345 dipoleKPotentialAtI(mK.ux, mK.uy, mK.uz, 2);
346 // Energy of permanent multipole I in the field of induced dipole K.
347 eI += 0.5 * multipoleEnergy(mI);
348 yield c * 0.5 * (eK + eI);
349 }
350 case QUADRUPOLE -> {
351 // Find the GK quadrupole potential of site I at site K.
352 quadrupoleIPotentialAtK(mI, 1);
353 // Energy of induced dipole K in the field of permanent quadrupole I.
354 double eK = polarizationEnergy(mK);
355 // Find the GK quadrupole potential of site K at site I.
356 quadrupoleKPotentialAtI(mK, 1);
357 // Energy of induced dipole I in the field of permanent quadrupole K.
358 double eI = polarizationEnergy(mI);
359 yield c * 0.5 * (eK + eI);
360 }
361 };
362 }
363
364 /**
365 * GK Polarization Energy.
366 *
367 * @param mI PolarizableMultipole at site I.
368 * @param mK PolarizableMultipole at site K.
369 * @return a double.
370 */
371 public double polarizationEnergyBorn(PolarizableMultipole mI, PolarizableMultipole mK) {
372 return switch (multipoleOrder) {
373 default -> {
374 // Find the GK charge potential of site I at site K.
375 chargeIPotentialAtK(mI, 1);
376 // Energy of induced dipole K in the field of permanent charge I.
377 double eK = polarizationEnergyS(mK);
378 // Find the GK charge potential of site K at site I.
379 chargeKPotentialAtI(mK, 1);
380 // Energy of induced dipole I in the field of permanent charge K.
381 double eI = polarizationEnergyS(mI);
382 yield c * 0.5 * (eK + eI);
383 }
384 case DIPOLE -> {
385 // Find the GK dipole potential of site I at site K.
386 dipoleIPotentialAtK(mI.dx, mI.dy, mI.dz, 1);
387 // Energy of induced dipole K in the field of permanent dipole I.
388 double eK = polarizationEnergyS(mK);
389 // Find the GK induced dipole potential of site I at site K.
390 dipoleIPotentialAtK(mI.sx, mI.sy, mI.sz, 2);
391 // Energy of permanent multipole K in the field of induced dipole I.
392 eK += 0.5 * multipoleEnergy(mK);
393 // Find the GK dipole potential of site K at site I.
394 dipoleKPotentialAtI(mK.dx, mK.dy, mK.dz, 1);
395 // Energy of induced dipole I in the field of permanent dipole K.
396 double eI = polarizationEnergyS(mI);
397 // Find the GK induced dipole potential of site K at site I.
398 dipoleKPotentialAtI(mK.sx, mK.sy, mK.sz, 2);
399 // Energy of permanent multipole I in the field of induced dipole K.
400 eI += 0.5 * multipoleEnergy(mI);
401 yield c * 0.5 * (eK + eI);
402 }
403 case QUADRUPOLE -> {
404 // Find the GK quadrupole potential of site I at site K.
405 quadrupoleIPotentialAtK(mI, 1);
406 // Energy of induced dipole K in the field of permanent quadrupole I.
407 double eK = polarizationEnergyS(mK);
408 // Find the GK quadrupole potential of site K at site I.
409 quadrupoleKPotentialAtI(mK, 1);
410 // Energy of induced dipole I in the field of permanent quadrupole K.
411 double eI = polarizationEnergyS(mI);
412 yield c * 0.5 * (eK + eI);
413 }
414 };
415 }
416
417 /**
418 * Polarization Energy and Gradient.
419 *
420 * @param mI PolarizableMultipole at site I.
421 * @param mK PolarizableMultipole at site K.
422 * @param inductionMask This is ignored, since masking/scaling is not applied to GK
423 * interactions (everything is intermolecular).
424 * @param energyMask This is ignored, since masking/scaling is not applied to GK interactions
425 * (everything is intermolecular).
426 * @param mutualMask This should be set to zero for direction polarization.
427 * @param Gi an array of double values.
428 * @param Ti an array of double values.
429 * @param Tk an array of double values.
430 * @return a double.
431 */
432 @Override
433 public double polarizationEnergyAndGradient(PolarizableMultipole mI, PolarizableMultipole mK,
434 double inductionMask, double energyMask, double mutualMask, double[] Gi, double[] Ti,
435 double[] Tk) {
436 return switch (multipoleOrder) {
437 default -> monopolePolarizationEnergyAndGradient(mI, mK, Gi);
438 case DIPOLE -> dipolePolarizationEnergyAndGradient(mI, mK, mutualMask, Gi, Ti, Tk);
439 case QUADRUPOLE -> quadrupolePolarizationEnergyAndGradient(mI, mK, Gi, Ti, Tk);
440 };
441 }
442
443 /**
444 * Monopole Polarization Energy and Gradient.
445 *
446 * @param mI PolarizableMultipole at site I.
447 * @param mK PolarizableMultipole at site K.
448 * @param Gi an array of double values.
449 * @return a double.
450 */
451 public double monopolePolarizationEnergyAndGradient(PolarizableMultipole mI,
452 PolarizableMultipole mK, double[] Gi) {
453
454 // Find the permanent multipole potential at site k.
455 chargeIPotentialAtK(mI, 2);
456 // Energy of induced dipole k in the field of multipole i.
457 double eK = polarizationEnergy(mK);
458 // Derivative with respect to moving atom k.
459 Gi[0] = -(mK.sx * E200 + mK.sy * E110 + mK.sz * E101);
460 Gi[1] = -(mK.sx * E110 + mK.sy * E020 + mK.sz * E011);
461 Gi[2] = -(mK.sx * E101 + mK.sy * E011 + mK.sz * E002);
462
463 // Find the permanent multipole potential and derivatives at site i.
464 chargeKPotentialAtI(mK, 2);
465 // Energy of induced dipole i in the field of multipole k.
466 double eI = polarizationEnergy(mI);
467 // Derivative with respect to moving atom i.
468 Gi[0] += (mI.sx * E200 + mI.sy * E110 + mI.sz * E101);
469 Gi[1] += (mI.sx * E110 + mI.sy * E020 + mI.sz * E011);
470 Gi[2] += (mI.sx * E101 + mI.sy * E011 + mI.sz * E002);
471
472 double scale = c * 0.5;
473 Gi[0] *= scale;
474 Gi[1] *= scale;
475 Gi[2] *= scale;
476
477 // Total polarization energy.
478 return scale * (eI + eK);
479 }
480
481 /**
482 * Dipole Polarization Energy and Gradient.
483 *
484 * @param mI PolarizableMultipole at site I.
485 * @param mK PolarizableMultipole at site K.
486 * @param mutualMask This should be set to zero for direction polarization.
487 * @param Gi an array of double values.
488 * @param Ti an array of double values.
489 * @param Tk an array of double values.
490 * @return a double.
491 */
492 public double dipolePolarizationEnergyAndGradient(PolarizableMultipole mI, PolarizableMultipole mK,
493 double mutualMask, double[] Gi, double[] Ti, double[] Tk) {
494
495 // Find the permanent multipole potential and derivatives at site k.
496 dipoleIPotentialAtK(mI.dx, mI.dy, mI.dz, 2);
497 // Energy of induced dipole k in the field of multipole i.
498 double eK = polarizationEnergy(mK);
499 // Derivative with respect to moving atom k.
500 Gi[0] = -(mK.sx * E200 + mK.sy * E110 + mK.sz * E101);
501 Gi[1] = -(mK.sx * E110 + mK.sy * E020 + mK.sz * E011);
502 Gi[2] = -(mK.sx * E101 + mK.sy * E011 + mK.sz * E002);
503 // Find the potential at K due to the averaged induced dipole at site i.
504 dipoleIPotentialAtK(mI.sx, mI.sy, mI.sz, 2);
505 dipoleTorque(mK, Tk);
506
507 // Find the GK induced dipole potential of site I at site K.
508 dipoleIPotentialAtK(mI.ux, mI.uy, mI.uz, 3);
509 // Energy of permanent multipole K in the field of induced dipole I.
510 eK += 0.5 * multipoleEnergy(mK);
511 double[] G = new double[3];
512 multipoleGradient(mK, G);
513 Gi[0] -= G[0];
514 Gi[1] -= G[1];
515 Gi[2] -= G[2];
516 multipoleTorque(mK, Tk);
517
518 // Find the permanent multipole potential and derivatives at site i.
519 dipoleKPotentialAtI(mK.dx, mK.dy, mK.dz, 2);
520 // Energy of induced dipole i in the field of multipole k.
521 double eI = polarizationEnergy(mI);
522 // Derivative with respect to moving atom i.
523 Gi[0] += (mI.sx * E200 + mI.sy * E110 + mI.sz * E101);
524 Gi[1] += (mI.sx * E110 + mI.sy * E020 + mI.sz * E011);
525 Gi[2] += (mI.sx * E101 + mI.sy * E011 + mI.sz * E002);
526 // Find the potential at I due to the averaged induced dipole at k.
527 dipoleKPotentialAtI(mK.sx, mK.sy, mK.sz, 2);
528 dipoleTorque(mI, Ti);
529
530 // Find the GK induced dipole potential of site K at site I.
531 dipoleKPotentialAtI(mK.ux, mK.uy, mK.uz, 3);
532 // Energy of permanent multipole I in the field of induced dipole K.
533 eI += 0.5 * multipoleEnergy(mI);
534 G = new double[3];
535 multipoleGradient(mI, G);
536 Gi[0] += G[0];
537 Gi[1] += G[1];
538 Gi[2] += G[2];
539 multipoleTorque(mI, Ti);
540
541 // Get the induced-induced portion of the force (Ud . dC/dX . Up).
542 // This contribution does not exist for direct polarization (mutualMask == 0.0).
543 if (mutualMask != 0.0) {
544 // Find the potential and its derivatives at k due to induced dipole i.
545 dipoleIPotentialAtK(mI.ux, mI.uy, mI.uz, 2);
546 Gi[0] -= mutualMask * (mK.px * E200 + mK.py * E110 + mK.pz * E101);
547 Gi[1] -= mutualMask * (mK.px * E110 + mK.py * E020 + mK.pz * E011);
548 Gi[2] -= mutualMask * (mK.px * E101 + mK.py * E011 + mK.pz * E002);
549
550 // Find the potential and its derivatives at i due to induced dipole k.
551 dipoleKPotentialAtI(mK.ux, mK.uy, mK.uz, 2);
552 Gi[0] += mutualMask * (mI.px * E200 + mI.py * E110 + mI.pz * E101);
553 Gi[1] += mutualMask * (mI.px * E110 + mI.py * E020 + mI.pz * E011);
554 Gi[2] += mutualMask * (mI.px * E101 + mI.py * E011 + mI.pz * E002);
555 }
556
557 // Total polarization energy.
558 double scale = c * 0.5;
559 double energy = scale * (eI + eK);
560 Gi[0] *= scale;
561 Gi[1] *= scale;
562 Gi[2] *= scale;
563 Ti[0] *= scale;
564 Ti[1] *= scale;
565 Ti[2] *= scale;
566 Tk[0] *= scale;
567 Tk[1] *= scale;
568 Tk[2] *= scale;
569
570 return energy;
571 }
572
573 /**
574 * Quadrupole Polarization Energy and Gradient.
575 *
576 * @param mI PolarizableMultipole at site I.
577 * @param mK PolarizableMultipole at site K.
578 * @param Gi an array of double values.
579 * @param Ti an array of double values.
580 * @param Tk an array of double values.
581 * @return a double.
582 */
583 public double quadrupolePolarizationEnergyAndGradient(PolarizableMultipole mI, PolarizableMultipole mK,
584 double[] Gi, double[] Ti, double[] Tk) {
585
586 // Find the permanent multipole potential and derivatives at site k.
587 quadrupoleIPotentialAtK(mI, 2);
588 // Energy of induced dipole k in the field of multipole i.
589 double eK = polarizationEnergy(mK);
590 // Derivative with respect to moving atom k.
591 Gi[0] = -(mK.sx * E200 + mK.sy * E110 + mK.sz * E101);
592 Gi[1] = -(mK.sx * E110 + mK.sy * E020 + mK.sz * E011);
593 Gi[2] = -(mK.sx * E101 + mK.sy * E011 + mK.sz * E002);
594
595 // Find the permanent multipole potential and derivatives at site i.
596 quadrupoleKPotentialAtI(mK, 2);
597 // Energy of induced dipole i in the field of multipole k.
598 double eI = polarizationEnergy(mI);
599 // Derivative with respect to moving atom i.
600 Gi[0] += (mI.sx * E200 + mI.sy * E110 + mI.sz * E101);
601 Gi[1] += (mI.sx * E110 + mI.sy * E020 + mI.sz * E011);
602 Gi[2] += (mI.sx * E101 + mI.sy * E011 + mI.sz * E002);
603
604 double scale = c * 0.5;
605 Gi[0] *= scale;
606 Gi[1] *= scale;
607 Gi[2] *= scale;
608
609 // Find the potential and its derivatives at K due to the averaged induced dipole at site i.
610 dipoleIPotentialAtK(scale * mI.sx, scale * mI.sy, scale * mI.sz, 2);
611 quadrupoleTorque(mK, Tk);
612
613 // Find the potential and its derivatives at I due to the averaged induced dipole at k.
614 dipoleKPotentialAtI(scale * mK.sx, scale * mK.sy, scale * mK.sz, 2);
615 quadrupoleTorque(mI, Ti);
616
617 // Total polarization energy.
618 return scale * (eI + eK);
619 }
620
621 /**
622 * GK Direct Polarization Born grad.
623 *
624 * @param mI PolarizableMultipole at site I.
625 * @param mK PolarizableMultipole at site K.
626 * @return Partial derivative of the Polarization energy with respect to a Born grad.
627 */
628 public double polarizationEnergyBornGrad(PolarizableMultipole mI, PolarizableMultipole mK) {
629 return 2.0 * polarizationEnergyBorn(mI, mK);
630 }
631
632 /**
633 * GK Mutual Polarization Contribution to the Born grad.
634 *
635 * @param mI PolarizableMultipole at site I.
636 * @param mK PolarizableMultipole at site K.
637 * @return Mutual Polarization contribution to the partial derivative with respect to a Born grad.
638 */
639 public double mutualPolarizationEnergyBornGrad(PolarizableMultipole mI, PolarizableMultipole mK) {
640 double db = 0.0;
641 if (multipoleOrder == GKMultipoleOrder.DIPOLE) {
642 // Find the potential and its derivatives at k due to induced dipole i.
643 dipoleIPotentialAtK(mI.ux, mI.uy, mI.uz, 2);
644 db = 0.5 * (mK.px * E100 + mK.py * E010 + mK.pz * E001);
645
646 // Find the potential and its derivatives at i due to induced dipole k.
647 dipoleKPotentialAtI(mK.ux, mK.uy, mK.uz, 2);
648 db += 0.5 * (mI.px * E100 + mI.py * E010 + mI.pz * E001);
649 }
650 return c * db;
651 }
652
653 /**
654 * Generate source terms for the Kirkwood version of the Challacombe et al. recursion.
655 */
656 @Override
657 protected void source(double[] work) {
658 gkSource.source(work, multipoleOrder);
659 }
660
661 }