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-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.potential.nonbonded.implicit;
39  
40  import edu.rit.pj.IntegerForLoop;
41  import edu.rit.pj.ParallelRegion;
42  import edu.rit.pj.reduction.SharedDouble;
43  import ffx.crystal.Crystal;
44  import ffx.numerics.atomic.AtomicDoubleArray3D;
45  import ffx.potential.bonded.Atom;
46  import ffx.potential.parameters.ForceField;
47  import ffx.potential.parameters.VDWType;
48  import ffx.potential.parameters.VDWType.EPSILON_RULE;
49  import ffx.potential.parameters.VDWType.RADIUS_RULE;
50  
51  import java.util.logging.Level;
52  import java.util.logging.Logger;
53  
54  import static ffx.potential.nonbonded.VanDerWaalsForm.getCombinedEps;
55  import static ffx.potential.nonbonded.VanDerWaalsForm.getCombinedRadius;
56  import static org.apache.commons.math3.util.FastMath.PI;
57  import static org.apache.commons.math3.util.FastMath.max;
58  import static org.apache.commons.math3.util.FastMath.min;
59  import static org.apache.commons.math3.util.FastMath.pow;
60  import static org.apache.commons.math3.util.FastMath.sqrt;
61  
62  /**
63   * Parallel calculation of continuum dispersion energy via pairwise descreening.
64   *
65   * @author Michael J. Schnieders
66   * @since 1.0
67   */
68  public class DispersionRegion extends ParallelRegion {
69  
70    /** The dispersion integral begins for each atom at: Rmin_ij + DISPERSION_OFFSET */
71    public static final double DEFAULT_DISPERSION_OFFSET = 1.056;
72    /** Each solute atom blocks dispersion interactions with solvent: Rmin + SOLUTE_OFFSET */
73    public static final double DEFAULT_SOLUTE_OFFSET = 0.0;
74  
75    private static final Logger logger = Logger.getLogger(DispersionRegion.class.getName());
76    /** The dispersion integral HCT overlap scale factor. */
77    private static final double DEFAULT_DISP_OVERLAP_FACTOR = 0.75;
78    /**
79     * Conversion between solute-solvent dispersion enthalpy and solute-solvent dispersion free
80     * energy.
81     */
82    private static final double SLEVY = 1.0;
83    /** Number density of water (water per A^3). */
84    private static final double AWATER = 0.033428;
85    /** AMOEBA '03 Water oxygen epsilon. */
86    private static final double EPSO = 0.1100;
87    /** AMOEBA '03 Water hydrogen epsilon. */
88    private static final double EPSH = 0.0135;
89    /** AMOEBA '03 Water oxygen Rmin (A). */
90    private static final double RMINO = 1.7025;
91    /** AMOEBA '03 Water hydrogen Rmin (A). */
92    private static final double RMINH = 1.3275;
93    /** AMOEBA epsilon rule. */
94    private static final EPSILON_RULE epsilonRule = VDWType.EPSILON_RULE.HHG;
95    /** AMOEBA radius rule. */
96    private static final RADIUS_RULE radiusRule = VDWType.RADIUS_RULE.CUBIC_MEAN;
97  
98    private final DispersionLoop[] dispersionLoop;
99    private final SharedDouble sharedDispersion;
100   /** An ordered array of atoms in the system. */
101   protected Atom[] atoms;
102   /** Periodic boundary conditions and symmetry. */
103   private Crystal crystal;
104   /** Flag to indicate if an atom should be included. */
105   private boolean[] use = null;
106   /** Neighbor lists for each atom and symmetry operator. */
107   private int[][][] neighborLists;
108   /** Cartesian coordinates of each atom. */
109   private double[] x, y, z;
110   /** GK cut-off distance squared. */
111   private double cut2;
112   /** Boolean flag to indicate GK will be scaled by the lambda state variable. */
113   private boolean gradient = false;
114   /** Gradient array for each thread. */
115   private AtomicDoubleArray3D grad;
116   /** Where the dispersion integral begins for each atom (A): Rmin + dispersionOffset */
117   private double dispersionOffest;
118   /**
119    * Each solute atom blocks dispersion interactions with solvent with this radius (A): Rmin +
120    * soluteOffset
121    */
122   private double soluteOffset;
123   /** The dispersion integral HCT overlap scale factor (unitless). */
124   private double dispersionOverlapFactor;
125 
126   /**
127    * DispersionRegion constructor.
128    *
129    * @param nt Number of threads.
130    * @param atoms Atom array.
131    * @param forceField ForceField in use.
132    */
133   public DispersionRegion(int nt, Atom[] atoms, ForceField forceField) {
134     dispersionLoop = new DispersionLoop[nt];
135     for (int i = 0; i < nt; i++) {
136       dispersionLoop[i] = new DispersionLoop();
137     }
138     sharedDispersion = new SharedDouble();
139 
140     dispersionOffest = forceField.getDouble("DISPERSION_OFFSET", DEFAULT_DISPERSION_OFFSET);
141     soluteOffset = forceField.getDouble("SOLUTE_OFFSET", DEFAULT_SOLUTE_OFFSET);
142     dispersionOverlapFactor =
143         forceField.getDouble("DISP_OVERLAP_FACTOR", DEFAULT_DISP_OVERLAP_FACTOR);
144 
145     allocate(atoms);
146   }
147 
148   /**
149    * Compute a Buffered-14-7 tail correction.
150    *
151    * @param r0 The separation distance where the intregal begins.
152    * @param eps The mixed eps value.
153    * @param rmin The mixed rmin value.
154    * @return The tail correction.
155    */
156   private static double tailCorrection(double r0, double eps, double rmin) {
157     if (r0 < rmin) {
158       double r03 = r0 * r0 * r0;
159       double rmin3 = rmin * rmin * rmin;
160       return -eps * 4.0 * PI * (rmin3 - r03) / 3.0 - eps * PI * 18.0 * rmin3 / 11.0;
161     } else {
162       double ri2 = r0 * r0;
163       double ri4 = ri2 * ri2;
164       double ri7 = r0 * ri2 * ri4;
165       double ri11 = ri7 * ri4;
166       double rmin2 = rmin * rmin;
167       double rmin4 = rmin2 * rmin2;
168       double rmin7 = rmin * rmin2 * rmin4;
169       return 2.0 * PI * eps * rmin7 * (2.0 * rmin7 - 11.0 * ri7) / (11.0 * ri11);
170     }
171   }
172 
173   /**
174    * Allocate storage given the Atom array.
175    *
176    * @param atoms Atom array in use.
177    */
178   public void allocate(Atom[] atoms) {
179     this.atoms = atoms;
180   }
181 
182   /**
183    * The dispersion integral begins offset from the vdW radius.
184    *
185    * @param dispersionOffest The dispersion integral offset.
186    */
187   public void setDispersionOffest(double dispersionOffest) {
188     this.dispersionOffest = dispersionOffest;
189   }
190 
191   /**
192    * The dispersion integral begins offset from the vdW radius.
193    *
194    * @return the dispersion integral offset.
195    */
196   public double getDispersionOffset() {
197     return dispersionOffest;
198   }
199 
200   public double getDispersionOverlapFactor() {
201     return dispersionOverlapFactor;
202   }
203 
204   /**
205    * Set the dispersion overlap HCT scale factor.
206    *
207    * @param dispersionOverlapFactor The dispersion integral HCT scale factor.
208    */
209   public void setDispersionOverlapFactor(double dispersionOverlapFactor) {
210     this.dispersionOverlapFactor = dispersionOverlapFactor;
211   }
212 
213   public double getEnergy() {
214     return sharedDispersion.get();
215   }
216 
217   public double getSoluteOffset() {
218     return soluteOffset;
219   }
220 
221   public void setSoluteOffset(double soluteOffset) {
222     this.soluteOffset = soluteOffset;
223   }
224 
225   /**
226    * Initialize the DispersionRegion for energy calculation.
227    *
228    * @param atoms Atom array.
229    * @param crystal Crystal for periodic boundary conditions.
230    * @param use Flag to indicate an atom is to be used.
231    * @param neighborLists Neighbor-list for each atom.
232    * @param x X-coordinate array.
233    * @param y Y-coordinate array.
234    * @param z Z-coordinate array.
235    * @param cut2 The cut-off distance squared.
236    * @param gradient If true, compute the gradient.
237    * @param grad Array to store the gradient.
238    */
239   public void init(
240       Atom[] atoms,
241       Crystal crystal,
242       boolean[] use,
243       int[][][] neighborLists,
244       double[] x,
245       double[] y,
246       double[] z,
247       double cut2,
248       boolean gradient,
249       AtomicDoubleArray3D grad) {
250     this.atoms = atoms;
251     this.crystal = crystal;
252     this.use = use;
253     this.neighborLists = neighborLists;
254     this.x = x;
255     this.y = y;
256     this.z = z;
257     this.cut2 = cut2;
258     this.gradient = gradient;
259     this.grad = grad;
260   }
261 
262   /** {@inheritDoc} */
263   @Override
264   public void run() {
265     try {
266       int nAtoms = atoms.length;
267       execute(0, nAtoms - 1, dispersionLoop[getThreadIndex()]);
268     } catch (Exception e) {
269       String message =
270           "Fatal exception computing Dispersion energy in thread " + getThreadIndex() + "\n";
271       logger.log(Level.SEVERE, message, e);
272     }
273   }
274 
275   /** {@inheritDoc} */
276   @Override
277   public void start() {
278     sharedDispersion.set(0.0);
279   }
280 
281   /**
282    * Compute Dispersion energy for a range of atoms via pairwise descreening.
283    *
284    * @since 1.0
285    */
286   private class DispersionLoop extends IntegerForLoop {
287 
288     private final double[] dx_local;
289     private double edisp;
290     private double r, r2, r3;
291     private double xr, yr, zr;
292     private int threadID;
293 
294     DispersionLoop() {
295       dx_local = new double[3];
296     }
297 
298     @Override
299     public void finish() {
300       sharedDispersion.addAndGet(edisp);
301     }
302 
303     @Override
304     public void run(int lb, int ub) {
305       for (int i = lb; i <= ub; i++) {
306         if (!use[i]) {
307           continue;
308         }
309 
310         //  Compute the limit of atom alone in solvent.
311         VDWType type = atoms[i].getVDWType();
312         double epsi = type.wellDepth;
313         double rmini = type.radius / 2.0;
314         double cDisp = 0.0;
315         if (rmini > 0.0 && epsi > 0.0) {
316           double emixo = getCombinedEps(EPSO, epsi, RMINO, rmini, epsilonRule);
317           double rmixo = getCombinedRadius(RMINO, rmini, radiusRule);
318           // Start of integration of dispersion for atom i with water oxygen.
319           double riO = rmixo / 2.0 + dispersionOffest;
320           cDisp = tailCorrection(riO, emixo, rmixo);
321           double emixh = getCombinedEps(EPSH, epsi, RMINH, rmini, epsilonRule);
322           double rmixh = getCombinedRadius(RMINH, rmini, radiusRule);
323           // Start of integration of dispersion for atom i with water hydrogen.
324           double riH = rmixh / 2.0 + dispersionOffest;
325           cDisp += 2.0 * tailCorrection(riH, emixh, rmixh);
326           cDisp = SLEVY * AWATER * cDisp;
327         }
328 
329         edisp += cDisp;
330 
331         // Now descreen over neighbors.
332         double sum = 0.0;
333         final double xi = x[i];
334         final double yi = y[i];
335         final double zi = z[i];
336         int[] list = neighborLists[0][i];
337         for (int k : list) {
338           if (!use[k] || i == k) {
339             continue;
340           }
341           if (atoms[k].getVDWType().radius > 0.0) {
342             dx_local[0] = xi - x[k];
343             dx_local[1] = yi - y[k];
344             dx_local[2] = zi - z[k];
345             r2 = crystal.image(dx_local);
346             if (r2 > cut2) {
347               continue;
348             }
349             xr = dx_local[0];
350             yr = dx_local[1];
351             zr = dx_local[2];
352             r = sqrt(r2);
353             r3 = r * r2;
354             // Atom i descreened by atom k.
355             sum += removeSoluteDispersion(i, k);
356             // Flip the sign on {xr, yr, zr};
357             xr = -xr;
358             yr = -yr;
359             zr = -zr;
360             // Atom k descreened by atom i.
361             sum += removeSoluteDispersion(k, i);
362           }
363         }
364         // Subtract descreening.
365         edisp -= SLEVY * AWATER * sum;
366       }
367     }
368 
369     @Override
370     public void start() {
371       threadID = getThreadIndex();
372       edisp = 0;
373     }
374 
375     /**
376      * Remove solute dispersion between atom i and solvent due to blocking of solveny by atom k.
377      *
378      * @param i Atom i index.
379      * @param k Atom k index.
380      * @return Reduction of dispersion.
381      */
382     private double removeSoluteDispersion(int i, int k) {
383 
384       // Van der Waals parameters for atom i.
385       VDWType type = atoms[i].getVDWType();
386       double epsi = type.wellDepth;
387       double rmini = type.radius / 2.0;
388 
389       // Van der Waals radius for atom k.
390       double rmink = atoms[k].getVDWType().radius / 2.0;
391 
392       // Parameters for atom i with water oxygen.
393       double emixo = getCombinedEps(EPSO, epsi, RMINO, rmini, epsilonRule);
394       double rmixo = getCombinedRadius(RMINO, rmini, radiusRule);
395       // Start of integration of dispersion for atom i with water oxygen.
396       double riO = rmixo / 2.0 + dispersionOffest;
397       double nO = 1.0;
398 
399       // Parameters for atom i with water hydrogen.
400       double emixh = getCombinedEps(EPSH, epsi, RMINH, rmini, epsilonRule);
401       double rmixh = getCombinedRadius(RMINH, rmini, radiusRule);
402       // Start of integration of dispersion for atom i with water oxygen.
403       double riH = rmixh / 2.0 + dispersionOffest;
404       double nH = 2.0;
405 
406       // Atom k blocks the interaction of atom i with solvent.
407       double sk = (rmink + soluteOffset) * dispersionOverlapFactor;
408       return interact(i, k, nO, riO, sk, rmixo, emixo) + interact(i, k, nH, riH, sk, rmixh, emixh);
409     }
410 
411     /**
412      * Solvent dispersion blocking of atom i by atom k.
413      *
414      * @param i Index of atom i.
415      * @param k Index of atom k.
416      * @param factor Apply this factor to the interaction.
417      * @param ri Start of the dispersion integral.
418      * @param sk Size of the solvent blocking atom.
419      * @param rmix Combined Rmin for atom i and solvent atom.
420      * @param emix Combined Eps for atom i and solvent atom.
421      * @return Contribution of atom k to removing dispersion.
422      */
423     private double interact(
424         int i, int k, double factor, double ri, double sk, double rmix, double emix) {
425       double sum = 0.0;
426       // Nothing to do if the integral begins beyond r + sk (i.e. atom k does not exclude solvent)
427       if (ri < r + sk) {
428         // Zero out the derivative contribution of atom k.
429         double de = 0.0;
430         double sk2 = sk * sk;
431         // Compute the maximum of 1) the beginning of the integral and 2) closest edge of atom K.
432         double iStart = max(ri, r - sk);
433         // Use this as the lower limit for integrating the constant eps value below Rmin.
434         double lik = iStart;
435         // Interaction with water from lik to Rmin; nothing to do if the lower limit is greater than
436         // Rmin.
437         if (lik < rmix) {
438           double lik2 = lik * lik;
439           double lik3 = lik2 * lik;
440           double lik4 = lik3 * lik;
441           // Upper limit is the minimum of Rmin and the farthest edge of atom K.
442           double uik = min(r + sk, rmix);
443           double uik2 = uik * uik;
444           double uik3 = uik2 * uik;
445           double uik4 = uik3 * uik;
446           sum = integralBeforeRMin(emix, r, r2, sk2, lik2, lik3, lik4, uik2, uik3, uik4);
447           if (gradient) {
448             de =
449                 integralBeforeRminDerivative(
450                     ri, emix, rmix, r, r2, r3, sk, sk2, lik, lik2, lik3, uik, uik2, uik3);
451           }
452         }
453         // Upper limit the variable part of Uwca always the farthest edge of atom K.
454         double uik = r + sk;
455         // Interaction with water beyond Rmin, from lik to uik = r + sk.
456         if (uik > rmix) {
457           // Start the integral at the max of 1) iStart and 2) Rmin.
458           lik = max(iStart, rmix);
459           double lik2 = lik * lik;
460           double lik3 = lik2 * lik;
461           double lik4 = lik3 * lik;
462           double lik5 = lik4 * lik;
463           double lik6 = lik5 * lik;
464           double lik10 = lik5 * lik5;
465           double lik11 = lik10 * lik;
466           double lik12 = lik11 * lik;
467           double uik2 = uik * uik;
468           double uik3 = uik2 * uik;
469           double uik4 = uik3 * uik;
470           double uik5 = uik4 * uik;
471           double uik10 = uik5 * uik5;
472           double uik11 = uik10 * uik;
473           double uik12 = uik11 * uik;
474           double rmix7 = pow(rmix, 7);
475           sum +=
476               integratlAfterRmin(
477                   emix, rmix7, r, r2, sk2, lik, lik2, lik3, lik4, lik5, lik10, lik11, lik12, uik,
478                   uik2, uik3, uik4, uik5, uik10, uik11, uik12);
479           if (gradient) {
480             double lik13 = lik12 * lik;
481             double uik6 = uik5 * uik;
482             double uik13 = uik12 * uik;
483             de +=
484                 integratlAfterRminDerivative(
485                     ri, emix, rmix, rmix7, iStart, r, r2, r3, sk, sk2, lik, lik2, lik3, lik5, lik6,
486                     lik12, lik13, uik, uik2, uik3, uik6, uik13);
487           }
488         }
489         // Increment the individual dispersion gradient components.
490         if (gradient) {
491           de = -de / r * factor * SLEVY * AWATER;
492           double dedx = de * xr;
493           double dedy = de * yr;
494           double dedz = de * zr;
495           grad.add(threadID, i, dedx, dedy, dedz);
496           grad.sub(threadID, k, dedx, dedy, dedz);
497         }
498       }
499       return factor * sum;
500     }
501 
502     /**
503      * Integrate over the constant portion of the WCA disperion interaction Uwca(x) = eps; x < rmin.
504      *
505      * @param eps The well depth.
506      * @param rij The separation between the current atom and solvent blocking atom.
507      * @param rij2 The separation squared.
508      * @param rho2 The scaled size of the solvent blocking atom squared.
509      * @param lik2 The beginning of the integral squared.
510      * @param lik3 The beginning of the integral to the third.
511      * @param lik4 The beginning of the integral to the fourth.
512      * @param uik2 The end of the integral squared.
513      * @param uik3 The end of the integral to the third.
514      * @param uik4 The end of the integral to the fourth.
515      * @return The integral.
516      */
517     private double integralBeforeRMin(
518         double eps,
519         double rij,
520         double rij2,
521         double rho2,
522         double lik2,
523         double lik3,
524         double lik4,
525         double uik2,
526         double uik3,
527         double uik4) {
528       return -eps
529           * (4.0
530           * PI
531           * (3.0 * (lik4 - uik4) - 8.0 * rij * (lik3 - uik3) + 6.0 * (rij2 - rho2) * (lik2 - uik2))
532           / (48.0 * rij));
533     }
534 
535     /**
536      * Derivative of the integral over the constant portion of the WCA disperion interaction Uwca(x)
537      * = eps; x < rmin.
538      *
539      * @param ri The beginning of the integral.
540      * @param eps The well depth.
541      * @param rmin The Rmin value.
542      * @param rij The separation between the current atom and solvent blocking atom.
543      * @param rij2 The separation squared.
544      * @param rij3 The separation to the third.
545      * @param rho The scaled size of the solvent blocking atom.
546      * @param rho2 The scaled size of the solvent blocking atom squared.
547      * @param lik The beginning of the integral.
548      * @param lik2 The beginning of the integral squared.
549      * @param lik3 The beginning of the integral to the third.
550      * @param uik The end of the integral.
551      * @param uik2 The end of the integral squared.
552      * @param uik3 The end of the integral to the third.
553      * @return The derivative of the integral.
554      */
555     private double integralBeforeRminDerivative(
556         double ri,
557         double eps,
558         double rmin,
559         double rij,
560         double rij2,
561         double rij3,
562         double rho,
563         double rho2,
564         double lik,
565         double lik2,
566         double lik3,
567         double uik,
568         double uik2,
569         double uik3) {
570       double dl;
571       if (ri > rij - rho) {
572         dl = (-lik2 + 2.0 * rij2 + 2.0 * rho2) * lik2;
573       } else {
574         dl =
575             (-lik3 + 4.0 * lik2 * rij - 6.0 * lik * rij2 + 2.0 * lik * rho2 + 4.0 * rij3
576                 - 4.0 * rij * rho2)
577                 * lik;
578       }
579       double du;
580       if (rij + rho > rmin) {
581         du = -(-uik2 + 2.0 * rij2 + 2.0 * rho2) * uik2;
582       } else {
583         du =
584             -(-uik3 + 4.0 * uik2 * rij - 6.0 * uik * rij2 + 2.0 * uik * rho2 + 4.0 * rij3
585                 - 4.0 * rij * rho2)
586                 * uik;
587       }
588       return -eps * PI * (dl + du) / (4.0 * rij2);
589     }
590 
591     /**
592      * @param eps The well depth.
593      * @param rmin7 The rmin value to the seventh.
594      * @param rij The separation between the current atom and solvent blocking atom.
595      * @param rij2 The separation squared.
596      * @param rho2 The scaled size of the solvent blocking atom squared.
597      * @param lik The beginning of the integral.
598      * @param lik2 The beginning of the integral squared.
599      * @param lik3 The beginning of the integral to the third.
600      * @param lik4 The beginning of the integral to the fourth.
601      * @param lik5 The beginning of the integral to the fifth.
602      * @param lik10 The beginning of the integral to the tenth.
603      * @param lik11 The beginning of the integral to the eleventh.
604      * @param lik12 The beginning of the integral to the twelfth.
605      * @param uik The end of the integral.
606      * @param uik2 The end of the integral squared.
607      * @param uik3 The end of the integral to the third.
608      * @param uik4 The end of the integral to the fourth.
609      * @param uik5 The end of the integral to the fifth.
610      * @param uik10 The end of the integral to the tenth.
611      * @param uik11 The end of the integral to the eleventh.
612      * @param uik12 The end of the integral to the twelfth.
613      * @return The value of the integral.
614      */
615     private double integratlAfterRmin(
616         double eps,
617         double rmin7,
618         double rij,
619         double rij2,
620         double rho2,
621         double lik,
622         double lik2,
623         double lik3,
624         double lik4,
625         double lik5,
626         double lik10,
627         double lik11,
628         double lik12,
629         double uik,
630         double uik2,
631         double uik3,
632         double uik4,
633         double uik5,
634         double uik10,
635         double uik11,
636         double uik12) {
637       double er7 = eps * rmin7;
638       double term = (15.0 * uik * lik * rij * (uik4 - lik4)
639           - 10.0 * uik2 * lik2 * (uik3 - lik3)
640           + 6.0 * (rho2 - rij2) * (uik5 - lik5)) / (120.0 * rij * lik5 * uik5);
641       double term2 = (120.0 * uik * lik * rij * (uik11 - lik11)
642           - 66.0 * uik2 * lik2 * (uik10 - lik10)
643           + 55.0 * (rho2 - rij2) * (uik12 - lik12)) / (2640.0 * rij * lik12 * uik12);
644       double idisp = -2.0 * er7 * term;
645       double irep = er7 * rmin7 * term2;
646       return 4.0 * PI * (irep + idisp);
647     }
648 
649     /**
650      * @param ri The beginning of the integral.
651      * @param eps The eps value.
652      * @param rmin The rmin value to the seventh.
653      * @param rmin7 The rmin value to the seventh.
654      * @param rij The separation between the current atom and solvent blocking atom.
655      * @param rij2 The separation squared.
656      * @param rij3 The separation cubed.
657      * @param rho The scaled size of the solvent blocking atom.
658      * @param rho2 The scaled size of the solvent blocking atom squared.
659      * @param lik The beginning of the integral.
660      * @param lik2 The beginning of the integral squared.
661      * @param lik3 The beginning of the integral to the third.
662      * @param lik5 The beginning of the integral to the fifth.
663      * @param lik6 The beginning of the integral to the sixth.
664      * @param lik12 The beginning of the integral to the twelfth.
665      * @param lik13 The beginning of the integral to the thirteenth.
666      * @param uik The end of the integral.
667      * @param uik2 The end of the integral squared.
668      * @param uik3 The end of the integral to the third.
669      * @param uik6 The end of the integral to the sixth.
670      * @param uik13 The end of the integral to the thirteenth.
671      * @return The value of the integral derivative.
672      */
673     private double integratlAfterRminDerivative(
674         double ri,
675         double eps,
676         double rmin,
677         double rmin7,
678         double rmax,
679         double rij,
680         double rij2,
681         double rij3,
682         double rho,
683         double rho2,
684         double lik,
685         double lik2,
686         double lik3,
687         double lik5,
688         double lik6,
689         double lik12,
690         double lik13,
691         double uik,
692         double uik2,
693         double uik3,
694         double uik6,
695         double uik13) {
696       double er7 = eps * rmin7;
697       double lowerTerm = lik2 * rij + rij3 - rij * rho2;
698       double upperTerm = uik2 * rij + rij3 - rij * rho2;
699 
700       double dl;
701       if (ri > rij - rho || rmax < rmin) {
702         dl = -(-5.0 * lik2 + 3.0 * rij2 + 3.0 * rho2) / lik5;
703       } else {
704         dl = (5.0 * lik3 - 33.0 * lik * rij2 - 3.0 * lik * rho2 + 15.0 * lowerTerm) / lik6;
705       }
706       double du = -(5.0 * uik3 - 33.0 * uik * rij2 - 3.0 * uik * rho2 + 15.0 * upperTerm) / uik6;
707       double de = -2.0 * PI * er7 * (dl + du) / (15.0 * rij2);
708 
709       if (ri > rij - rho || rmax < rmin) {
710         dl = -(-6.0 * lik2 + 5.0 * rij2 + 5.0 * rho2) / lik12;
711       } else {
712         dl = (6.0 * lik3 - 125.0 * lik * rij2 - 5.0 * lik * rho2 + 60.0 * lowerTerm) / lik13;
713       }
714       du = -(6.0 * uik3 - 125.0 * uik * rij2 - 5.0 * uik * rho2 + 60.0 * upperTerm) / uik13;
715       de += PI * er7 * rmin7 * (dl + du) / (60.0 * rij2);
716 
717       return de;
718     }
719   }
720 }