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