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