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.potential.nonbonded.pme;
39  
40  import static ffx.numerics.special.Erf.erfc;
41  import static ffx.potential.parameters.MultipoleType.t000;
42  import static ffx.potential.parameters.MultipoleType.t001;
43  import static ffx.potential.parameters.MultipoleType.t002;
44  import static ffx.potential.parameters.MultipoleType.t010;
45  import static ffx.potential.parameters.MultipoleType.t011;
46  import static ffx.potential.parameters.MultipoleType.t020;
47  import static ffx.potential.parameters.MultipoleType.t100;
48  import static ffx.potential.parameters.MultipoleType.t101;
49  import static ffx.potential.parameters.MultipoleType.t110;
50  import static ffx.potential.parameters.MultipoleType.t200;
51  import static java.lang.Double.isInfinite;
52  import static java.lang.Double.isNaN;
53  import static java.lang.String.format;
54  import static java.util.Arrays.fill;
55  import static org.apache.commons.math3.util.FastMath.exp;
56  import static org.apache.commons.math3.util.FastMath.min;
57  import static org.apache.commons.math3.util.FastMath.sqrt;
58  
59  import edu.rit.pj.IntegerForLoop;
60  import edu.rit.pj.IntegerSchedule;
61  import edu.rit.pj.ParallelRegion;
62  import edu.rit.pj.ParallelTeam;
63  import edu.rit.pj.reduction.SharedDouble;
64  import edu.rit.pj.reduction.SharedInteger;
65  import ffx.crystal.Crystal;
66  import ffx.crystal.SymOp;
67  import ffx.numerics.atomic.AtomicDoubleArray3D;
68  import ffx.potential.bonded.Atom;
69  import ffx.potential.extended.ExtendedSystem;
70  import ffx.potential.nonbonded.MaskingInterface;
71  import ffx.potential.parameters.ForceField;
72  import ffx.potential.parameters.MultipoleType.MultipoleFrameDefinition;
73  import ffx.potential.utils.EnergyException;
74  
75  import java.util.List;
76  import java.util.logging.Level;
77  import java.util.logging.Logger;
78  
79  /**
80   * Parallel evaluation of the PME real space energy and gradient.
81   *
82   * @author Michael J. Schnieders
83   * @since 1.0
84   */
85  public class RealSpaceEnergyRegion extends ParallelRegion implements MaskingInterface {
86      private static final Logger logger = Logger.getLogger(RealSpaceEnergyRegion.class.getName());
87      /**
88       * Constant applied to multipole interactions.
89       */
90      private static final double oneThird = 1.0 / 3.0;
91      private final int maxThreads;
92      private final double electric;
93      private final SharedInteger sharedInteractions;
94      private final ForceField.ELEC_FORM elecForm;
95      private final RealSpaceEnergyLoop[] realSpaceEnergyLoop;
96      private final FixedChargeEnergyLoop[] fixedChargeEnergyLoop;
97      /**
98       * Specify inter-molecular softcore.
99       */
100     private final boolean intermolecularSoftcore;
101     /**
102      * Specify intra-molecular softcore.
103      */
104     private final boolean intramolecularSoftcore;
105     /**
106      * Dimensions of [nsymm][nAtoms][3]
107      */
108     public double[][][] inducedDipole;
109     public double[][][] inducedDipoleCR;
110     /**
111      * Polarization groups.
112      */
113     protected int[][] ip11;
114     /**
115      * An ordered array of atoms in the system.
116      */
117     private Atom[] atoms;
118     /**
119      * Unit cell and spacegroup information.
120      */
121     private Crystal crystal;
122     /**
123      * Dimensions of [nsymm][xyz][nAtoms].
124      */
125     private double[][][] coordinates;
126     /**
127      * Multipole frame definition.
128      */
129     private MultipoleFrameDefinition[] frame;
130     /**
131      * Multipole frame defining atoms.
132      */
133     private int[][] axisAtom;
134     /**
135      * Dimensions of [nsymm][nAtoms][10]
136      */
137     private double[][][] globalMultipole;
138     private double[][][] titrationMultipole;
139     private double[][][] tautomerMultipole;
140 
141     private ExtendedSystem extendedSystem = null;
142     private boolean esvTerm = false;
143     /**
144      * When computing the polarization energy at Lambda there are 3 pieces.
145      *
146      * <p>1.) Upol(1) = The polarization energy computed normally (ie. system with ligand).
147      *
148      * <p>2.) Uenv = The polarization energy of the system without the ligand.
149      *
150      * <p>3.) Uligand = The polarization energy of the ligand by itself.
151      *
152      * <p>Upol(L) = L*Upol(1) + (1-L)*(Uenv + Uligand)
153      *
154      * <p>Set the "use" array to true for all atoms for part 1. Set the "use" array to true for all
155      * atoms except the ligand for part 2. Set the "use" array to true only for the ligand atoms for
156      * part 3.
157      *
158      * <p>The "use" array can also be employed to turn off atoms for computing the electrostatic
159      * energy of sub-structures.
160      */
161     private boolean[] use;
162     /**
163      * Molecule number for each atom.
164      */
165     private int[] molecule;
166     /**
167      * Flag for ligand atoms.
168      */
169     private boolean[] isSoft;
170     private double[] ipdamp;
171     private double[] thole;
172     /**
173      * Masking of 1-2, 1-3, 1-4 and 1-5 interactions.
174      */
175     private int[][] mask12;
176     private int[][] mask13;
177     private int[][] mask14;
178     private int[][] mask15;
179     /**
180      * Neighbor lists, without atoms beyond the real space cutoff. [nSymm][nAtoms][nIncludedNeighbors]
181      */
182     private int[][][] realSpaceLists;
183     /**
184      * Number of neighboring atoms within the real space cutoff. [nSymm][nAtoms]
185      */
186     private int[][] realSpaceCounts;
187     /**
188      * Pairwise schedule for load balancing.
189      */
190     private IntegerSchedule realSpaceSchedule;
191     private long[] realSpaceEnergyTime;
192     /**
193      * Atomic Gradient array.
194      */
195     private AtomicDoubleArray3D grad;
196     /**
197      * Atomic Torque array.
198      */
199     private AtomicDoubleArray3D torque;
200     /**
201      * Partial derivative of the gradient with respect to Lambda.
202      */
203     private AtomicDoubleArray3D lambdaGrad;
204     /**
205      * Partial derivative of the torque with respect to Lambda.
206      */
207     private AtomicDoubleArray3D lambdaTorque;
208     /**
209      * Partial derivative with respect to Lambda.
210      */
211     private SharedDouble shareddEdLambda;
212     /**
213      * Second partial derivative with respect to Lambda.
214      */
215     private SharedDouble sharedd2EdLambda2;
216     /**
217      * If true, compute coordinate gradient.
218      */
219     private boolean gradient;
220     /**
221      * If lambdaTerm is true, some ligand atom interactions with the environment are being turned
222      * on/off.
223      */
224     private boolean lambdaTerm;
225     /**
226      * If nnTerm is true, all intramolecular interactions are treated with a neural network.
227      */
228     private boolean nnTerm;
229 
230     /**
231      * The current LambdaMode of this PME instance (or OFF for no lambda dependence).
232      */
233     private LambdaMode lambdaMode = LambdaMode.OFF;
234     private Polarization polarization;
235     /**
236      * lAlpha = α*(1 - L)^2
237      */
238     private double lAlpha = 0.0;
239     private double dlAlpha = 0.0;
240     private double d2lAlpha = 0.0;
241     private double dEdLSign = 1.0;
242     /**
243      * lPowPerm = L^permanentLambdaExponent
244      */
245     private double dlPowPerm = 0.0;
246     private double d2lPowPerm = 0.0;
247     private boolean doPermanentRealSpace;
248     private double permanentScale = 1.0;
249     /**
250      * lPowPol = L^polarizationLambdaExponent
251      */
252     private double lPowPol = 1.0;
253     private double dlPowPol = 0.0;
254     private double d2lPowPol = 0.0;
255     private boolean doPolarization;
256     /**
257      * When computing the polarization energy at L there are 3 pieces.
258      *
259      * <p>1.) Upol(1) = The polarization energy computed normally (ie. system with ligand).
260      *
261      * <p>2.) Uenv = The polarization energy of the system without the ligand.
262      *
263      * <p>3.) Uligand = The polarization energy of the ligand by itself.
264      *
265      * <p>Upol(L) = L*Upol(1) + (1-L)*(Uenv + Uligand)
266      *
267      * <p>Set polarizationScale to L for part 1. Set polarizationScale to (1-L) for parts 2 & 3.
268      */
269     private double polarizationScale = 1.0;
270     // *************************************************************************
271     // Mutable Particle Mesh Ewald constants.
272     private double aewald;
273     private double an0;
274     private double an1;
275     private double an2;
276     private double an3;
277     private double an4;
278     private double an5;
279     private double permanentEnergy;
280     private double polarizationEnergy;
281     private ScaleParameters scaleParameters;
282 
283     public RealSpaceEnergyRegion(ForceField.ELEC_FORM elecForm, int nt, ForceField forceField, boolean lambdaTerm, double electric) {
284         maxThreads = nt;
285         this.electric = electric;
286         this.elecForm = elecForm;
287         sharedInteractions = new SharedInteger();
288         if (elecForm == ForceField.ELEC_FORM.FIXED_CHARGE) {
289             fixedChargeEnergyLoop = new FixedChargeEnergyLoop[nt];
290             realSpaceEnergyLoop = null;
291         } else {
292             fixedChargeEnergyLoop = null;
293             realSpaceEnergyLoop = new RealSpaceEnergyLoop[nt];
294         }
295 
296         // Flag to indicate application of an intermolecular softcore potential.
297         if (lambdaTerm) {
298             intermolecularSoftcore = forceField.getBoolean("INTERMOLECULAR_SOFTCORE", false);
299             intramolecularSoftcore = forceField.getBoolean("INTRAMOLECULAR_SOFTCORE", false);
300         } else {
301             intermolecularSoftcore = false;
302             intramolecularSoftcore = false;
303         }
304     }
305 
306     @Override
307     public void applyMask(int i, boolean[] is14, double[]... masks) {
308         if (ip11[i] != null) {
309             // AMOEBA
310             double[] permanentEnergyMask = masks[0];
311             double[] permanentFieldMask = masks[1];
312             for (int value : mask12[i]) {
313                 permanentFieldMask[value] = scaleParameters.p12scale;
314                 permanentEnergyMask[value] = scaleParameters.m12scale;
315             }
316             for (int value : mask13[i]) {
317                 permanentFieldMask[value] = scaleParameters.p13scale;
318                 permanentEnergyMask[value] = scaleParameters.m13scale;
319             }
320             for (int value : mask14[i]) {
321                 permanentFieldMask[value] = scaleParameters.p14scale;
322                 permanentEnergyMask[value] = scaleParameters.m14scale;
323                 for (int k : ip11[i]) {
324                     if (k == value) {
325                         permanentFieldMask[value] = scaleParameters.intra14Scale * scaleParameters.p14scale;
326                         break;
327                     }
328                 }
329             }
330             for (int value : mask15[i]) {
331                 permanentEnergyMask[value] = scaleParameters.m15scale;
332             }
333             // Apply group based polarization masking rule.
334             double[] polarizationGroupMask = masks[2];
335             for (int j : ip11[i]) {
336                 polarizationGroupMask[j] = scaleParameters.d11scale;
337             }
338         } else {
339             // Fixed Charge Force Fields.
340             double[] permanentEnergyMask = masks[0];
341             for (int value : mask12[i]) {
342                 permanentEnergyMask[value] = scaleParameters.m12scale;
343             }
344             for (int value : mask13[i]) {
345                 permanentEnergyMask[value] = scaleParameters.m13scale;
346             }
347             for (int value : mask14[i]) {
348                 permanentEnergyMask[value] = scaleParameters.m14scale;
349             }
350         }
351     }
352 
353     /**
354      * Execute the RealSpaceEnergyRegion with the passed ParallelTeam.
355      *
356      * @param parallelTeam The ParallelTeam instance to execute with.
357      */
358     public void executeWith(ParallelTeam parallelTeam) {
359         try {
360             parallelTeam.execute(this);
361         } catch (Exception e) {
362             String message = " Exception computing the electrostatic energy.\n";
363             logger.log(Level.SEVERE, message, e);
364         }
365     }
366 
367     @Override
368     public void finish() {
369         permanentEnergy = 0.0;
370         polarizationEnergy = 0.0;
371         if (elecForm == ForceField.ELEC_FORM.FIXED_CHARGE) {
372             for (int i = 0; i < maxThreads; i++) {
373                 permanentEnergy += fixedChargeEnergyLoop[i].permanentEnergy;
374             }
375         } else {
376             for (int i = 0; i < maxThreads; i++) {
377                 permanentEnergy += realSpaceEnergyLoop[i].permanentEnergy;
378                 polarizationEnergy += realSpaceEnergyLoop[i].inducedEnergy;
379             }
380         }
381         permanentEnergy *= electric;
382         polarizationEnergy *= electric;
383         if (isNaN(permanentEnergy)) {
384             throw new EnergyException(
385                     format(" The permanent multipole energy is %16.8f", permanentEnergy));
386         }
387         if (isNaN(polarizationEnergy)) {
388             throw new EnergyException(
389                     format(" The polarization energy is %16.8f", polarizationEnergy));
390         }
391     }
392 
393     public int getCount(int i) {
394         if (elecForm == ForceField.ELEC_FORM.FIXED_CHARGE) {
395             return fixedChargeEnergyLoop[i].getCount();
396         } else {
397             return realSpaceEnergyLoop[i].getCount();
398         }
399     }
400 
401     public int getInteractions() {
402         return sharedInteractions.get();
403     }
404 
405     public double getPermanentEnergy() {
406         return permanentEnergy;
407     }
408 
409     public double getPolarizationEnergy() {
410         return polarizationEnergy;
411     }
412 
413     public void init(
414             Atom[] atoms,
415             Crystal crystal,
416             ExtendedSystem extendedSystem,
417             boolean esvTerm,
418             double[][][] coordinates,
419             MultipoleFrameDefinition[] frame,
420             int[][] axisAtom,
421             double[][][] globalMultipole,
422             double[][][] titrationMultipole,
423             double[][][] tautomerMultipole,
424             double[][][] inducedDipole,
425             double[][][] inducedDipoleCR,
426             boolean[] use,
427             int[] molecule,
428             int[][] ip11,
429             int[][] mask12,
430             int[][] mask13,
431             int[][] mask14,
432             int[][] mask15,
433             boolean[] isSoft,
434             double[] ipdamp,
435             double[] thole,
436             RealSpaceNeighborParameters realSpaceNeighborParameters,
437             boolean gradient,
438             boolean lambdaTerm,
439             boolean nnTerm,
440             LambdaMode lambdaMode,
441             Polarization polarization,
442             EwaldParameters ewaldParameters,
443             ScaleParameters scaleParameters,
444             AlchemicalParameters alchemicalParameters,
445             long[] realSpaceEnergyTime,
446             AtomicDoubleArray3D grad,
447             AtomicDoubleArray3D torque,
448             AtomicDoubleArray3D lambdaGrad,
449             AtomicDoubleArray3D lambdaTorque,
450             SharedDouble shareddEdLambda,
451             SharedDouble sharedd2EdLambda2) {
452         // Input
453         this.atoms = atoms;
454         this.crystal = crystal;
455         this.extendedSystem = extendedSystem;
456         this.esvTerm = esvTerm;
457         this.coordinates = coordinates;
458         this.frame = frame;
459         this.axisAtom = axisAtom;
460         this.globalMultipole = globalMultipole;
461         this.titrationMultipole = titrationMultipole;
462         this.tautomerMultipole = tautomerMultipole;
463         this.inducedDipole = inducedDipole;
464         this.inducedDipoleCR = inducedDipoleCR;
465         this.use = use;
466         this.molecule = molecule;
467         this.ip11 = ip11;
468         this.mask12 = mask12;
469         this.mask13 = mask13;
470         this.mask14 = mask14;
471         this.mask15 = mask15;
472         this.isSoft = isSoft;
473         this.ipdamp = ipdamp;
474         this.thole = thole;
475         this.realSpaceLists = realSpaceNeighborParameters.realSpaceLists;
476         this.realSpaceCounts = realSpaceNeighborParameters.realSpaceCounts;
477         this.realSpaceSchedule = realSpaceNeighborParameters.realSpaceSchedule;
478         this.gradient = gradient;
479         this.lambdaTerm = lambdaTerm;
480         this.nnTerm = nnTerm;
481         this.lambdaMode = lambdaMode;
482         this.polarization = polarization;
483         this.lAlpha = alchemicalParameters.lAlpha;
484         this.dlAlpha = alchemicalParameters.dlAlpha;
485         this.d2lAlpha = alchemicalParameters.d2lAlpha;
486         this.dEdLSign = alchemicalParameters.dEdLSign;
487         this.dlPowPerm = alchemicalParameters.dlPowPerm;
488         this.d2lPowPerm = alchemicalParameters.d2lPowPerm;
489         this.doPermanentRealSpace = alchemicalParameters.doPermanentRealSpace;
490         this.permanentScale = alchemicalParameters.permanentScale;
491         this.lPowPol = alchemicalParameters.lPowPol;
492         this.dlPowPol = alchemicalParameters.dlPowPol;
493         this.d2lPowPol = alchemicalParameters.d2lPowPol;
494         this.doPolarization = alchemicalParameters.doPolarization;
495         this.polarizationScale = alchemicalParameters.polarizationScale;
496         this.aewald = ewaldParameters.aewald;
497         this.an0 = ewaldParameters.an0;
498         this.an1 = ewaldParameters.an1;
499         this.an2 = ewaldParameters.an2;
500         this.an3 = ewaldParameters.an3;
501         this.an4 = ewaldParameters.an4;
502         this.an5 = ewaldParameters.an5;
503         this.scaleParameters = scaleParameters;
504         // Output
505         this.realSpaceEnergyTime = realSpaceEnergyTime;
506         this.grad = grad;
507         this.torque = torque;
508         this.lambdaGrad = lambdaGrad;
509         this.lambdaTorque = lambdaTorque;
510         this.shareddEdLambda = shareddEdLambda;
511         this.sharedd2EdLambda2 = sharedd2EdLambda2;
512     }
513 
514     @Override
515     public void removeMask(int i, boolean[] is14, double[]... masks) {
516         if (ip11[i] != null) {
517             // AMOEBA
518             double[] permanentEnergyMask = masks[0];
519             double[] permanentFieldMask = masks[1];
520             for (int value : mask12[i]) {
521                 permanentFieldMask[value] = 1.0;
522                 permanentEnergyMask[value] = 1.0;
523             }
524             for (int value : mask13[i]) {
525                 permanentFieldMask[value] = 1.0;
526                 permanentEnergyMask[value] = 1.0;
527             }
528             for (int value : mask14[i]) {
529                 permanentFieldMask[value] = 1.0;
530                 permanentEnergyMask[value] = 1.0;
531                 for (int k : ip11[i]) {
532                     if (k == value) {
533                         permanentFieldMask[value] = 1.0;
534                         break;
535                     }
536                 }
537             }
538             for (int value : mask15[i]) {
539                 permanentEnergyMask[value] = 1.0;
540             }
541             // Apply group based polarization masking rule.
542             double[] polarizationGroupMask = masks[2];
543             for (int j : ip11[i]) {
544                 polarizationGroupMask[j] = 1.0;
545             }
546         } else {
547             // Fixed Charge Force Fields.
548             double[] permanentEnergyMask = masks[0];
549             for (int value : mask12[i]) {
550                 permanentEnergyMask[value] = 1.0;
551             }
552             for (int value : mask13[i]) {
553                 permanentEnergyMask[value] = 1.0;
554             }
555             for (int value : mask14[i]) {
556                 permanentEnergyMask[value] = 1.0;
557             }
558         }
559     }
560 
561     @Override
562     public void run() {
563         int threadIndex = getThreadIndex();
564         if (elecForm == ForceField.ELEC_FORM.FIXED_CHARGE) {
565             if (fixedChargeEnergyLoop[threadIndex] == null) {
566                 fixedChargeEnergyLoop[threadIndex] = new FixedChargeEnergyLoop();
567             }
568             try {
569                 int nAtoms = atoms.length;
570                 execute(0, nAtoms - 1, fixedChargeEnergyLoop[threadIndex]);
571             } catch (Exception e) {
572                 String message =
573                         "Fatal exception computing the real space energy in thread " + getThreadIndex() + "\n";
574                 logger.log(Level.SEVERE, message, e);
575             }
576         } else {
577             if (realSpaceEnergyLoop[threadIndex] == null) {
578                 realSpaceEnergyLoop[threadIndex] = new RealSpaceEnergyLoop();
579             }
580             try {
581                 int nAtoms = atoms.length;
582                 execute(0, nAtoms - 1, realSpaceEnergyLoop[threadIndex]);
583             } catch (Exception e) {
584                 String message =
585                         "Fatal exception computing the real space energy in thread " + getThreadIndex() + "\n";
586                 logger.log(Level.SEVERE, message, e);
587             }
588         }
589     }
590 
591     @Override
592     public void start() {
593         sharedInteractions.set(0);
594     }
595 
596     /**
597      * Log the real space electrostatics interaction.
598      *
599      * @param i   Atom i.
600      * @param k   Atom j.
601      * @param r   The distance rij.
602      * @param eij The interaction energy.
603      * @since 1.0
604      */
605     private void log(int i, int k, double r, double eij, int count, double total) {
606         logger.info(
607                 format(
608                         "%s  %6d  %6d  %10.4f  %10.4f %8d %16.8f",
609                         "ELEC", atoms[i].getIndex(), atoms[k].getIndex(), r, eij, count, total));
610     }
611 
612     /**
613      * The Real Space Gradient Loop class contains methods and thread local variables to parallelize
614      * the evaluation of the real space permanent and polarization energies and gradients.
615      */
616     private class RealSpaceEnergyLoop extends IntegerForLoop {
617 
618         private final double[] dx_local;
619         private final double[][] rot_local;
620         private final Torque torques;
621         private double ci;
622         private double dix, diy, diz;
623         private double qixx, qiyy, qizz, qixy, qixz, qiyz;
624         private double ck;
625         private double dkx, dky, dkz;
626         private double qkxx, qkyy, qkzz, qkxy, qkxz, qkyz;
627         private double uix, uiy, uiz;
628         private double pix, piy, piz;
629         private double xr, yr, zr;
630         private double ukx, uky, ukz;
631         private double pkx, pky, pkz;
632         private double bn0, bn1, bn2, bn3, bn4, bn5, bn6;
633         private double rr1, rr3, rr5, rr7, rr9, rr11, rr13;
634         private double scale, scale3, scale5, scale7;
635         private double scalep, scaled;
636         private double ddsc3x, ddsc3y, ddsc3z;
637         private double ddsc5x, ddsc5y, ddsc5z;
638         private double ddsc7x, ddsc7y, ddsc7z;
639         private double l2;
640         private boolean soft;
641         private double selfScale;
642         private double permanentEnergy;
643         private double inducedEnergy;
644         private double dUdL, d2UdL2;
645         private int i, k, iSymm, count;
646         private double[] gxk_local, gyk_local, gzk_local;
647         private double[] txk_local, tyk_local, tzk_local;
648         private double[] lxk_local, lyk_local, lzk_local;
649         private double[] ltxk_local, ltyk_local, ltzk_local;
650         private double[] masking_local;
651         private double[] maskingp_local;
652         private double[] maskingd_local;
653         private int threadID;
654 
655         RealSpaceEnergyLoop() {
656             super();
657             dx_local = new double[3];
658             rot_local = new double[3][3];
659             torques = new Torque();
660         }
661 
662         @Override
663         public void finish() {
664             sharedInteractions.addAndGet(count);
665             if (lambdaTerm) {
666                 shareddEdLambda.addAndGet(dUdL * electric);
667                 sharedd2EdLambda2.addAndGet(d2UdL2 * electric);
668             }
669             realSpaceEnergyTime[getThreadIndex()] += System.nanoTime();
670         }
671 
672         public int getCount() {
673             return count;
674         }
675 
676         @Override
677         public void run(int lb, int ub) {
678             List<SymOp> symOps = crystal.spaceGroup.symOps;
679             int nSymm = symOps.size();
680             int nAtoms = atoms.length;
681             for (iSymm = 0; iSymm < nSymm; iSymm++) {
682                 SymOp symOp = symOps.get(iSymm);
683                 if (gradient) {
684                     fill(gxk_local, 0.0);
685                     fill(gyk_local, 0.0);
686                     fill(gzk_local, 0.0);
687                     fill(txk_local, 0.0);
688                     fill(tyk_local, 0.0);
689                     fill(tzk_local, 0.0);
690                 }
691                 if (lambdaTerm) {
692                     fill(lxk_local, 0.0);
693                     fill(lyk_local, 0.0);
694                     fill(lzk_local, 0.0);
695                     fill(ltxk_local, 0.0);
696                     fill(ltyk_local, 0.0);
697                     fill(ltzk_local, 0.0);
698                 }
699                 realSpaceChunk(lb, ub);
700                 if (gradient) {
701                     // Turn symmetry mate torques into gradients
702                     int[] frameIndex = {-1, -1, -1, -1};
703                     double[][] g = new double[4][3];
704                     double[] trq = new double[3];
705                     for (int i = 0; i < nAtoms; i++) {
706                         fill(frameIndex, -1);
707                         for (int j = 0; j < 4; j++) {
708                             fill(g[j], 0.0);
709                         }
710                         trq[0] = txk_local[i];
711                         trq[1] = tyk_local[i];
712                         trq[2] = tzk_local[i];
713                         torques.torque(i, iSymm, trq, frameIndex, g);
714                         for (int j = 0; j < 4; j++) {
715                             int index = frameIndex[j];
716                             if (index >= 0) {
717                                 double[] gj = g[j];
718                                 gxk_local[index] += gj[0];
719                                 gyk_local[index] += gj[1];
720                                 gzk_local[index] += gj[2];
721                             }
722                         }
723                     }
724                     // Rotate symmetry mate gradients
725                     if (iSymm != 0) {
726                         crystal.applyTransSymRot(
727                                 nAtoms, gxk_local, gyk_local, gzk_local, gxk_local, gyk_local, gzk_local, symOp,
728                                 rot_local);
729                     }
730                     // Sum symmetry mate gradients into asymmetric unit gradients
731                     for (int j = 0; j < nAtoms; j++) {
732                         grad.add(threadID, j, gxk_local[j], gyk_local[j], gzk_local[j]);
733                     }
734                 }
735                 if (lambdaTerm) {
736                     // Turn symmetry mate torques into gradients
737                     int[] frameIndex = {-1, -1, -1, -1};
738                     double[][] g = new double[4][3];
739                     double[] trq = new double[3];
740                     for (int i = 0; i < nAtoms; i++) {
741                         fill(frameIndex, -1);
742                         for (int j = 0; j < 4; j++) {
743                             fill(g[j], 0.0);
744                         }
745                         trq[0] = ltxk_local[i];
746                         trq[1] = ltyk_local[i];
747                         trq[2] = ltzk_local[i];
748                         torques.torque(i, iSymm, trq, frameIndex, g);
749                         for (int j = 0; j < 4; j++) {
750                             int index = frameIndex[j];
751                             if (index >= 0) {
752                                 double[] gj = g[j];
753                                 lxk_local[index] += gj[0];
754                                 lyk_local[index] += gj[1];
755                                 lzk_local[index] += gj[2];
756                             }
757                         }
758                     }
759                     // Rotate symmetry mate gradients
760                     if (iSymm != 0) {
761                         crystal.applyTransSymRot(
762                                 nAtoms, lxk_local, lyk_local, lzk_local, lxk_local, lyk_local, lzk_local, symOp,
763                                 rot_local);
764                     }
765                     // Sum symmetry mate gradients into asymmetric unit gradients
766                     for (int j = 0; j < nAtoms; j++) {
767                         lambdaGrad.add(threadID, j, lxk_local[j], lyk_local[j], lzk_local[j]);
768                     }
769                 }
770             }
771         }
772 
773         @Override
774         public IntegerSchedule schedule() {
775             return realSpaceSchedule;
776         }
777 
778         @Override
779         public void start() {
780             init();
781             threadID = getThreadIndex();
782             realSpaceEnergyTime[threadID] -= System.nanoTime();
783             permanentEnergy = 0.0;
784             inducedEnergy = 0.0;
785             count = 0;
786             if (lambdaTerm) {
787                 dUdL = 0.0;
788                 d2UdL2 = 0.0;
789             }
790             torques.init(axisAtom, frame, coordinates);
791         }
792 
793         private void init() {
794             int nAtoms = atoms.length;
795             if (masking_local == null || masking_local.length < nAtoms) {
796                 txk_local = new double[nAtoms];
797                 tyk_local = new double[nAtoms];
798                 tzk_local = new double[nAtoms];
799                 gxk_local = new double[nAtoms];
800                 gyk_local = new double[nAtoms];
801                 gzk_local = new double[nAtoms];
802                 lxk_local = new double[nAtoms];
803                 lyk_local = new double[nAtoms];
804                 lzk_local = new double[nAtoms];
805                 ltxk_local = new double[nAtoms];
806                 ltyk_local = new double[nAtoms];
807                 ltzk_local = new double[nAtoms];
808                 masking_local = new double[nAtoms];
809                 maskingp_local = new double[nAtoms];
810                 maskingd_local = new double[nAtoms];
811                 fill(masking_local, 1.0);
812                 fill(maskingp_local, 1.0);
813                 fill(maskingd_local, 1.0);
814             }
815         }
816 
817         /**
818          * Evaluate the real space permanent energy and polarization energy for a chunk of atoms.
819          *
820          * @param lb The lower bound of the chunk.
821          * @param ub The upper bound of the chunk.
822          */
823         private void realSpaceChunk(final int lb, final int ub) {
824             final double[] x = coordinates[0][0];
825             final double[] y = coordinates[0][1];
826             final double[] z = coordinates[0][2];
827             final double[][] mpole = globalMultipole[0];
828             final double[] zeropole = new double[10];
829             final double[][] ind = inducedDipole[0];
830             final double[][] indp = inducedDipoleCR[0];
831             final int[][] lists = realSpaceLists[iSymm];
832             final double[] neighborX = coordinates[iSymm][0];
833             final double[] neighborY = coordinates[iSymm][1];
834             final double[] neighborZ = coordinates[iSymm][2];
835             final double[][] neighborMultipole = globalMultipole[iSymm];
836             final double[][] neighborInducedDipole = inducedDipole[iSymm];
837             final double[][] neighborInducedDipolep = inducedDipoleCR[iSymm];
838             for (i = lb; i <= ub; i++) {
839                 if (!use[i]) {
840                     continue;
841                 }
842                 final int moleculei = molecule[i];
843                 if (iSymm == 0) {
844                     applyMask(i, null, masking_local, maskingp_local, maskingd_local);
845                 }
846                 final double xi = x[i];
847                 final double yi = y[i];
848                 final double zi = z[i];
849                 final double[] globalMultipolei = mpole[i];
850                 final double[] inducedDipolei = ind[i];
851                 final double[] inducedDipolepi = indp[i];
852                 setMultipoleI(globalMultipolei);
853                 setInducedI(inducedDipolei);
854                 setInducedpI(inducedDipolepi);
855                 final boolean softi = isSoft[i];
856                 final double pdi = ipdamp[i];
857                 final double pti = thole[i];
858                 final int[] list = lists[i];
859                 final int npair = realSpaceCounts[iSymm][i];
860                 for (int j = 0; j < npair; j++) {
861                     k = list[j];
862                     if (!use[k]) {
863                         continue;
864                     }
865                     boolean sameMolecule = (moleculei == molecule[k]);
866                     if (lambdaMode == LambdaMode.VAPOR) {
867                         if ((intermolecularSoftcore && !sameMolecule)
868                                 || (intramolecularSoftcore && sameMolecule)) {
869                             continue;
870                         }
871                     }
872                     selfScale = 1.0;
873                     if (i == k) {
874                         selfScale = 0.5;
875                     }
876                     double beta = 0.0;
877                     l2 = 1.0;
878                     soft = (softi || isSoft[k]);
879                     if (soft && doPermanentRealSpace) {
880                         beta = lAlpha;
881                         l2 = permanentScale;
882                     } else if (nnTerm) {
883                         l2 = permanentScale;
884                     }
885                     final double xk = neighborX[k];
886                     final double yk = neighborY[k];
887                     final double zk = neighborZ[k];
888                     dx_local[0] = xk - xi;
889                     dx_local[1] = yk - yi;
890                     dx_local[2] = zk - zi;
891                     double r2 = crystal.image(dx_local);
892                     xr = dx_local[0];
893                     yr = dx_local[1];
894                     zr = dx_local[2];
895                     final double[] globalMultipolek = neighborMultipole[k];
896                     final double[] inducedDipolek = neighborInducedDipole[k];
897                     final double[] inducedDipolepk = neighborInducedDipolep[k];
898                     setMultipoleK(globalMultipolek);
899                     setInducedK(inducedDipolek);
900                     setInducedpK(inducedDipolepk);
901                     final double pdk = ipdamp[k];
902                     final double ptk = thole[k];
903                     scale = masking_local[k];
904                     scalep = maskingp_local[k];
905                     scaled = maskingd_local[k];
906                     // logger.info(format("Permanent %3.1f, U Field %3.1f, U Energy: %3.1f", scale, scaled, scalep));
907                     scale3 = 1.0;
908                     scale5 = 1.0;
909                     scale7 = 1.0;
910                     double r = sqrt(r2 + beta);
911                     double ralpha = aewald * r;
912                     double exp2a = exp(-ralpha * ralpha);
913                     rr1 = 1.0 / r;
914                     double rr2 = rr1 * rr1;
915                     bn0 = erfc(ralpha) * rr1;
916                     bn1 = (bn0 + an0 * exp2a) * rr2;
917                     bn2 = (3.0 * bn1 + an1 * exp2a) * rr2;
918                     bn3 = (5.0 * bn2 + an2 * exp2a) * rr2;
919                     bn4 = (7.0 * bn3 + an3 * exp2a) * rr2;
920                     bn5 = (9.0 * bn4 + an4 * exp2a) * rr2;
921                     bn6 = (11.0 * bn5 + an5 * exp2a) * rr2;
922                     rr3 = rr1 * rr2;
923                     rr5 = 3.0 * rr3 * rr2;
924                     rr7 = 5.0 * rr5 * rr2;
925                     rr9 = 7.0 * rr7 * rr2;
926                     rr11 = 9.0 * rr9 * rr2;
927                     rr13 = 11.0 * rr11 * rr2;
928                     ddsc3x = 0.0;
929                     ddsc3y = 0.0;
930                     ddsc3z = 0.0;
931                     ddsc5x = 0.0;
932                     ddsc5y = 0.0;
933                     ddsc5z = 0.0;
934                     ddsc7x = 0.0;
935                     ddsc7y = 0.0;
936                     ddsc7z = 0.0;
937                     double damp = pdi * pdk;
938                     double thole = min(pti, ptk);
939                     // logger.info(format(" %d %d Thole: %17.15f AiAk: %17.15f", i, k, thole, damp));
940                     double rdamp = r * damp;
941                     damp = -thole * rdamp * rdamp * rdamp;
942                     if (damp > -50.0) {
943                         final double expdamp = exp(damp);
944                         scale3 = 1.0 - expdamp;
945                         scale5 = 1.0 - expdamp * (1.0 - damp);
946                         scale7 = 1.0 - expdamp * (1.0 - damp + 0.6 * damp * damp);
947                         final double temp3 = -3.0 * damp * expdamp * rr2;
948                         final double temp5 = -damp;
949                         final double temp7 = -0.2 - 0.6 * damp;
950                         ddsc3x = temp3 * xr;
951                         ddsc3y = temp3 * yr;
952                         ddsc3z = temp3 * zr;
953                         ddsc5x = temp5 * ddsc3x;
954                         ddsc5y = temp5 * ddsc3y;
955                         ddsc5z = temp5 * ddsc3z;
956                         ddsc7x = temp7 * ddsc5x;
957                         ddsc7y = temp7 * ddsc5y;
958                         ddsc7z = temp7 * ddsc5z;
959                     }
960                     if (doPermanentRealSpace) {
961                         double ei = permanentPair(gradient, lambdaTerm);
962                         //logger.info(format(" Permanent %d %d %17.15f", i, k, ei));
963                         //logger.info(format(" %16.14f %16.14f %16.14f", xr, yr, zr));
964                         //logger.info(format(" %d multipole:  %s", i, Arrays.toString(globalMultipolei)));
965                         //logger.info(format(" %d multipole:  %s", k, Arrays.toString(globalMultipolek)));
966                         if (isNaN(ei) || isInfinite(ei)) {
967                             String message =
968                                     format(
969                                             " %s\n %s\n %s\n The permanent multipole energy between "
970                                                     + "atoms %d and %d (%d) is %16.8f at %16.8f A.",
971                                             crystal.getUnitCell().toString(), atoms[i].toString(), atoms[k].toString(),
972                                             i, k, iSymm, ei, r);
973                             throw new EnergyException(message);
974                         }
975 
976                         // List<Atom> i12 = atoms[i].get12List();
977                         // List<Atom> i13 = atoms[i].get13List();
978                         // List<Atom> i14 = atoms[i].get14List();
979                         // List<Atom> i15 = atoms[i].get15List();
980                         // Atom aK = atoms[k];
981                         // if (!i12.contains(aK) && !i13.contains(aK) && !i14.contains(aK) && !i15.contains(aK)) {
982                         //  logger.info(format(" PERM %s %s %7.4f %7.4f", atoms[i], atoms[k], r, ei * electric));
983                         // }
984 
985                         if (ei != 0.0) {
986                             permanentEnergy += ei;
987                             count++;
988                         }
989                         if (esvTerm) {
990                             if (extendedSystem.isTitrating(i)) {
991                                 double titrdUdL = 0.0;
992                                 double tautdUdL = 0.0;
993                                 final double[][] titrMpole = titrationMultipole[0];
994                                 final double[] titrMultipolei = titrMpole[i];
995                                 setMultipoleI(titrMultipolei);
996                                 titrdUdL = electric * permanentPair(false, false);
997                                 if (extendedSystem.isTautomerizing(i)) {
998                                     final double[][] tautMpole = tautomerMultipole[0];
999                                     final double[] tautMultipolei = tautMpole[i];
1000                                     setMultipoleI(tautMultipolei);
1001                                     tautdUdL = electric * permanentPair(false, false);
1002                                 }
1003                                 extendedSystem.addPermElecDeriv(i, titrdUdL, tautdUdL);
1004                                 // Reset Multipoles between titration and tautomer ESVs
1005                                 setMultipoleI(globalMultipolei);
1006                             }
1007                             if (extendedSystem.isTitrating(k)) {
1008                                 double titrdUdL = 0.0;
1009                                 double tautdUdL = 0.0;
1010                                 final double[][] titrNeighborMpole = titrationMultipole[iSymm];
1011                                 final double[] titrMultipolek = titrNeighborMpole[k];
1012                                 setMultipoleK(titrMultipolek);
1013                                 titrdUdL = electric * permanentPair(false, false);
1014                                 if (extendedSystem.isTautomerizing(k)) {
1015                                     final double[][] tautNeighborMpole = tautomerMultipole[iSymm];
1016                                     final double[] tautMultipolek = tautNeighborMpole[k];
1017                                     setMultipoleK(tautMultipolek);
1018                                     tautdUdL = electric * permanentPair(false, false);
1019                                 }
1020                                 extendedSystem.addPermElecDeriv(k, titrdUdL, tautdUdL);
1021                                 // Reset Multipoles between titration and tautomer ESVs
1022                                 setMultipoleK(globalMultipolek);
1023                             }
1024                         }
1025                     }
1026                     if (polarization != Polarization.NONE && doPolarization) {
1027                         // Polarization does not use the softcore tensors.
1028                         if (soft && doPermanentRealSpace) {
1029                             scale3 = 1.0;
1030                             scale5 = 1.0;
1031                             scale7 = 1.0;
1032                             r = sqrt(r2);
1033                             ralpha = aewald * r;
1034                             exp2a = exp(-ralpha * ralpha);
1035                             rr1 = 1.0 / r;
1036                             rr2 = rr1 * rr1;
1037                             bn0 = erfc(ralpha) * rr1;
1038                             bn1 = (bn0 + an0 * exp2a) * rr2;
1039                             bn2 = (3.0 * bn1 + an1 * exp2a) * rr2;
1040                             bn3 = (5.0 * bn2 + an2 * exp2a) * rr2;
1041                             bn4 = (7.0 * bn3 + an3 * exp2a) * rr2;
1042                             bn5 = (9.0 * bn4 + an4 * exp2a) * rr2;
1043                             bn6 = (11.0 * bn5 + an5 * exp2a) * rr2;
1044                             rr3 = rr1 * rr2;
1045                             rr5 = 3.0 * rr3 * rr2;
1046                             rr7 = 5.0 * rr5 * rr2;
1047                             rr9 = 7.0 * rr7 * rr2;
1048                             rr11 = 9.0 * rr9 * rr2;
1049                             ddsc3x = 0.0;
1050                             ddsc3y = 0.0;
1051                             ddsc3z = 0.0;
1052                             ddsc5x = 0.0;
1053                             ddsc5y = 0.0;
1054                             ddsc5z = 0.0;
1055                             ddsc7x = 0.0;
1056                             ddsc7y = 0.0;
1057                             ddsc7z = 0.0;
1058                             damp = pdi * pdk;
1059                             thole = min(pti, ptk);
1060                             rdamp = r * damp;
1061                             damp = -thole * rdamp * rdamp * rdamp;
1062                             if (damp > -50.0) {
1063                                 final double expdamp = exp(damp);
1064                                 scale3 = 1.0 - expdamp;
1065                                 scale5 = 1.0 - expdamp * (1.0 - damp);
1066                                 scale7 = 1.0 - expdamp * (1.0 - damp + 0.6 * damp * damp);
1067                                 final double temp3 = -3.0 * damp * expdamp * rr2;
1068                                 final double temp5 = -damp;
1069                                 final double temp7 = -0.2 - 0.6 * damp;
1070                                 ddsc3x = temp3 * xr;
1071                                 ddsc3y = temp3 * yr;
1072                                 ddsc3z = temp3 * zr;
1073                                 ddsc5x = temp5 * ddsc3x;
1074                                 ddsc5y = temp5 * ddsc3y;
1075                                 ddsc5z = temp5 * ddsc3z;
1076                                 ddsc7x = temp7 * ddsc5x;
1077                                 ddsc7y = temp7 * ddsc5y;
1078                                 ddsc7z = temp7 * ddsc5z;
1079                             }
1080                         }
1081                         double ei = polarizationPair(gradient, lambdaTerm);
1082 //            logger.info(format(" Total Polarization %d %d %17.15f", i, k, ei));
1083 //            logger.info(format(" %d induced:    %s", i, Arrays.toString(inducedDipolei)));
1084 //            logger.info(format(" %d induced cr: %s", i, Arrays.toString(inducedDipolepi)));
1085 //            logger.info(format(" %d induced:    %s", k, Arrays.toString(inducedDipolek)));
1086 //            logger.info(format(" %d induced cr: %s", k, Arrays.toString(inducedDipolepk)));
1087                         if (isNaN(ei) || isInfinite(ei)) {
1088                             String message =
1089                                     format(
1090                                             " %s\n"
1091                                                     + " %s\n with induced dipole: %8.3f %8.3f %8.3f\n"
1092                                                     + " %s\n with induced dipole: %8.3f %8.3f %8.3f\n"
1093                                                     + " The polarization energy due to atoms "
1094                                                     + "%d and %d (%d) is %10.6f at %10.6f A.",
1095                                             crystal.getUnitCell(),
1096                                             atoms[i],
1097                                             uix,
1098                                             uiy,
1099                                             uiz,
1100                                             atoms[k],
1101                                             ukx,
1102                                             uky,
1103                                             ukz,
1104                                             i + 1,
1105                                             k + 1,
1106                                             iSymm,
1107                                             ei,
1108                                             r);
1109                             throw new EnergyException(message);
1110                         }
1111                         inducedEnergy += ei;
1112                         if (esvTerm) {
1113                             int esvI = extendedSystem.getTitrationESVIndex(i);
1114                             int esvK = extendedSystem.getTitrationESVIndex(k);
1115                             if (extendedSystem.isTitrating(i)) {
1116                                 double titrdUdL = 0.0;
1117                                 double tautdUdL = 0.0;
1118                                 final double[][] titrMpole = titrationMultipole[0];
1119                                 final double[] titrMultipolei = titrMpole[i];
1120                                 setMultipoleI(titrMultipolei);
1121                                 // Check if atom i and atom k are controlled by the same ESV.
1122                                 if (extendedSystem.isTitrating(k) && esvI == esvK) {
1123                                     final double[][] titrNeighborMpole = titrationMultipole[iSymm];
1124                                     final double[] titrMultipolek = titrNeighborMpole[k];
1125                                     setMultipoleK(titrMultipolek);
1126                                 } else {
1127                                     setMultipoleK(zeropole);
1128                                 }
1129                                 titrdUdL = polarizationPair(false, false);
1130                                 // Swap induced dipoles and masking rules
1131                                 setInducedI(inducedDipolepi);
1132                                 setInducedK(inducedDipolepk);
1133                                 scaled = maskingp_local[k];
1134                                 scalep = maskingd_local[k];
1135                                 titrdUdL += polarizationPair(false, false);
1136                                 // Reset
1137                                 setInducedI(inducedDipolei);
1138                                 setInducedK(inducedDipolek);
1139                                 scalep = maskingp_local[k];
1140                                 scaled = maskingd_local[k];
1141 
1142                                 if (extendedSystem.isTautomerizing(i)) {
1143                                     final double[][] tautMpole = tautomerMultipole[0];
1144                                     final double[] tautMultipolei = tautMpole[i];
1145                                     setMultipoleI(tautMultipolei);
1146                                     // Check if atom i and atom k are controlled by the same ESV.
1147                                     if (extendedSystem.isTautomerizing(k) && esvI == esvK) {
1148                                         final double[][] tautNeighborMpole = tautomerMultipole[iSymm];
1149                                         final double[] tautMultipolek = tautNeighborMpole[k];
1150                                         setMultipoleK(tautMultipolek);
1151                                     } else {
1152                                         setMultipoleK(zeropole);
1153                                     }
1154                                     tautdUdL = polarizationPair(false, false);
1155                                     // Swap induced dipoles and masking
1156                                     setInducedI(inducedDipolepi);
1157                                     setInducedK(inducedDipolepk);
1158                                     scaled = maskingp_local[k];
1159                                     scalep = maskingd_local[k];
1160                                     tautdUdL += polarizationPair(false, false);
1161                                     // Reset induced dipoles and masking
1162                                     setInducedI(inducedDipolei);
1163                                     setInducedK(inducedDipolek);
1164                                     scalep = maskingp_local[k];
1165                                     scaled = maskingd_local[k];
1166                                 }
1167                                 extendedSystem.addIndElecDeriv(i, titrdUdL * electric, tautdUdL * electric);
1168                                 // Reset permanent multipoles
1169                                 setMultipoleI(globalMultipolei);
1170                                 setMultipoleK(globalMultipolek);
1171                             }
1172                             // Collect further terms if atom i and atom k are controlled by different ESVs.
1173                             if (extendedSystem.isTitrating(k) && esvI != esvK) {
1174                                 double titrdUdL = 0.0;
1175                                 double tautdUdL = 0.0;
1176                                 final double[][] titrNeighborMpole = titrationMultipole[iSymm];
1177                                 final double[] titrMultipolek = titrNeighborMpole[k];
1178                                 setMultipoleK(titrMultipolek);
1179                                 setMultipoleI(zeropole);
1180                                 titrdUdL = polarizationPair(false, false);
1181                                 // Swap induced dipoles and masking
1182                                 setInducedI(inducedDipolepi);
1183                                 setInducedK(inducedDipolepk);
1184                                 scaled = maskingp_local[k];
1185                                 scalep = maskingd_local[k];
1186                                 titrdUdL += polarizationPair(false, false);
1187                                 // Reset induced dipoles and masking
1188                                 setInducedI(inducedDipolei);
1189                                 setInducedK(inducedDipolek);
1190                                 scalep = maskingp_local[k];
1191                                 scaled = maskingd_local[k];
1192                                 if (extendedSystem.isTautomerizing(k)) {
1193                                     final double[][] tautNeighborMpole = tautomerMultipole[iSymm];
1194                                     final double[] tautMultipolek = tautNeighborMpole[k];
1195                                     setMultipoleK(tautMultipolek);
1196                                     tautdUdL = polarizationPair(false, false);
1197                                     // Swap induced dipoles and masking
1198                                     setInducedI(inducedDipolepi);
1199                                     setInducedK(inducedDipolepk);
1200                                     scaled = maskingp_local[k];
1201                                     scalep = maskingd_local[k];
1202                                     tautdUdL += polarizationPair(false, false);
1203                                     // Reset induced dipoles and masking
1204                                     setInducedI(inducedDipolei);
1205                                     setInducedK(inducedDipolek);
1206                                     scalep = maskingp_local[k];
1207                                     scaled = maskingd_local[k];
1208                                 }
1209                                 extendedSystem.addIndElecDeriv(k, titrdUdL * electric, tautdUdL * electric);
1210                                 // Reset permanent multipoles
1211                                 setMultipoleI(globalMultipolei);
1212                                 setMultipoleK(globalMultipolek);
1213                             }
1214                         }
1215                     }
1216                 }
1217                 if (iSymm == 0) {
1218                     removeMask(i, null, masking_local, maskingp_local, maskingd_local);
1219                 }
1220             }
1221         }
1222 
1223         /**
1224          * Evaluate the real space permanent energy for a pair of multipole sites.
1225          *
1226          * @return the permanent multipole energy.
1227          */
1228         private double permanentPair(boolean gradientLocal, boolean lambdaTermLocal) {
1229             final double dixdkx = diy * dkz - diz * dky;
1230             final double dixdky = diz * dkx - dix * dkz;
1231             final double dixdkz = dix * dky - diy * dkx;
1232             final double dixrx = diy * zr - diz * yr;
1233             final double dixry = diz * xr - dix * zr;
1234             final double dixrz = dix * yr - diy * xr;
1235             final double dkxrx = dky * zr - dkz * yr;
1236             final double dkxry = dkz * xr - dkx * zr;
1237             final double dkxrz = dkx * yr - dky * xr;
1238             final double qirx = qixx * xr + qixy * yr + qixz * zr;
1239             final double qiry = qixy * xr + qiyy * yr + qiyz * zr;
1240             final double qirz = qixz * xr + qiyz * yr + qizz * zr;
1241             final double qkrx = qkxx * xr + qkxy * yr + qkxz * zr;
1242             final double qkry = qkxy * xr + qkyy * yr + qkyz * zr;
1243             final double qkrz = qkxz * xr + qkyz * yr + qkzz * zr;
1244             final double qiqkrx = qixx * qkrx + qixy * qkry + qixz * qkrz;
1245             final double qiqkry = qixy * qkrx + qiyy * qkry + qiyz * qkrz;
1246             final double qiqkrz = qixz * qkrx + qiyz * qkry + qizz * qkrz;
1247             final double qkqirx = qkxx * qirx + qkxy * qiry + qkxz * qirz;
1248             final double qkqiry = qkxy * qirx + qkyy * qiry + qkyz * qirz;
1249             final double qkqirz = qkxz * qirx + qkyz * qiry + qkzz * qirz;
1250             final double qixqkx =
1251                     qixy * qkxz + qiyy * qkyz + qiyz * qkzz - qixz * qkxy - qiyz * qkyy - qizz * qkyz;
1252             final double qixqky =
1253                     qixz * qkxx + qiyz * qkxy + qizz * qkxz - qixx * qkxz - qixy * qkyz - qixz * qkzz;
1254             final double qixqkz =
1255                     qixx * qkxy + qixy * qkyy + qixz * qkyz - qixy * qkxx - qiyy * qkxy - qiyz * qkxz;
1256             final double rxqirx = yr * qirz - zr * qiry;
1257             final double rxqiry = zr * qirx - xr * qirz;
1258             final double rxqirz = xr * qiry - yr * qirx;
1259             final double rxqkrx = yr * qkrz - zr * qkry;
1260             final double rxqkry = zr * qkrx - xr * qkrz;
1261             final double rxqkrz = xr * qkry - yr * qkrx;
1262             final double rxqikrx = yr * qiqkrz - zr * qiqkry;
1263             final double rxqikry = zr * qiqkrx - xr * qiqkrz;
1264             final double rxqikrz = xr * qiqkry - yr * qiqkrx;
1265             final double rxqkirx = yr * qkqirz - zr * qkqiry;
1266             final double rxqkiry = zr * qkqirx - xr * qkqirz;
1267             final double rxqkirz = xr * qkqiry - yr * qkqirx;
1268             final double qkrxqirx = qkry * qirz - qkrz * qiry;
1269             final double qkrxqiry = qkrz * qirx - qkrx * qirz;
1270             final double qkrxqirz = qkrx * qiry - qkry * qirx;
1271             final double qidkx = qixx * dkx + qixy * dky + qixz * dkz;
1272             final double qidky = qixy * dkx + qiyy * dky + qiyz * dkz;
1273             final double qidkz = qixz * dkx + qiyz * dky + qizz * dkz;
1274             final double qkdix = qkxx * dix + qkxy * diy + qkxz * diz;
1275             final double qkdiy = qkxy * dix + qkyy * diy + qkyz * diz;
1276             final double qkdiz = qkxz * dix + qkyz * diy + qkzz * diz;
1277             final double dixqkrx = diy * qkrz - diz * qkry;
1278             final double dixqkry = diz * qkrx - dix * qkrz;
1279             final double dixqkrz = dix * qkry - diy * qkrx;
1280             final double dkxqirx = dky * qirz - dkz * qiry;
1281             final double dkxqiry = dkz * qirx - dkx * qirz;
1282             final double dkxqirz = dkx * qiry - dky * qirx;
1283             final double rxqidkx = yr * qidkz - zr * qidky;
1284             final double rxqidky = zr * qidkx - xr * qidkz;
1285             final double rxqidkz = xr * qidky - yr * qidkx;
1286             final double rxqkdix = yr * qkdiz - zr * qkdiy;
1287             final double rxqkdiy = zr * qkdix - xr * qkdiz;
1288             final double rxqkdiz = xr * qkdiy - yr * qkdix;
1289 
1290             // Calculate the scalar products for permanent multipoles.
1291             final double sc2 = dix * dkx + diy * dky + diz * dkz;
1292             final double sc3 = dix * xr + diy * yr + diz * zr;
1293             final double sc4 = dkx * xr + dky * yr + dkz * zr;
1294             final double sc5 = qirx * xr + qiry * yr + qirz * zr;
1295             final double sc6 = qkrx * xr + qkry * yr + qkrz * zr;
1296             final double sc7 = qirx * dkx + qiry * dky + qirz * dkz;
1297             final double sc8 = qkrx * dix + qkry * diy + qkrz * diz;
1298             final double sc9 = qirx * qkrx + qiry * qkry + qirz * qkrz;
1299             final double sc10 =
1300                     2.0 * (qixy * qkxy + qixz * qkxz + qiyz * qkyz) + qixx * qkxx + qiyy * qkyy + qizz * qkzz;
1301 
1302             // Calculate the gl functions for permanent multipoles.
1303             final double gl0 = ci * ck;
1304             final double gl1 = ck * sc3 - ci * sc4;
1305             final double gl2 = ci * sc6 + ck * sc5 - sc3 * sc4;
1306             final double gl3 = sc3 * sc6 - sc4 * sc5;
1307             final double gl4 = sc5 * sc6;
1308             final double gl5 = -4.0 * sc9;
1309             final double gl6 = sc2;
1310             final double gl7 = 2.0 * (sc7 - sc8);
1311             final double gl8 = 2.0 * sc10;
1312 
1313             // Compute the energy contributions for this interaction.
1314             final double scale1 = 1.0 - scale;
1315             final double ereal =
1316                     gl0 * bn0 + (gl1 + gl6) * bn1 + (gl2 + gl7 + gl8) * bn2 + (gl3 + gl5) * bn3 + gl4 * bn4;
1317             final double efix =
1318                     scale1
1319                             * (gl0 * rr1
1320                             + (gl1 + gl6) * rr3
1321                             + (gl2 + gl7 + gl8) * rr5
1322                             + (gl3 + gl5) * rr7
1323                             + gl4 * rr9);
1324             final double e = selfScale * l2 * (ereal - efix);
1325             if (gradientLocal) {
1326                 final double gf1 =
1327                         bn1 * gl0 + bn2 * (gl1 + gl6) + bn3 * (gl2 + gl7 + gl8) + bn4 * (gl3 + gl5) + bn5 * gl4;
1328                 final double gf2 = -ck * bn1 + sc4 * bn2 - sc6 * bn3;
1329                 final double gf3 = ci * bn1 + sc3 * bn2 + sc5 * bn3;
1330                 final double gf4 = 2.0 * bn2;
1331                 final double gf5 = 2.0 * (-ck * bn2 + sc4 * bn3 - sc6 * bn4);
1332                 final double gf6 = 2.0 * (-ci * bn2 - sc3 * bn3 - sc5 * bn4);
1333                 final double gf7 = 4.0 * bn3;
1334 
1335                 // Get the permanent force with screening.
1336                 double ftm2x =
1337                         gf1 * xr
1338                                 + gf2 * dix
1339                                 + gf3 * dkx
1340                                 + gf4 * (qkdix - qidkx)
1341                                 + gf5 * qirx
1342                                 + gf6 * qkrx
1343                                 + gf7 * (qiqkrx + qkqirx);
1344                 double ftm2y =
1345                         gf1 * yr
1346                                 + gf2 * diy
1347                                 + gf3 * dky
1348                                 + gf4 * (qkdiy - qidky)
1349                                 + gf5 * qiry
1350                                 + gf6 * qkry
1351                                 + gf7 * (qiqkry + qkqiry);
1352                 double ftm2z =
1353                         gf1 * zr
1354                                 + gf2 * diz
1355                                 + gf3 * dkz
1356                                 + gf4 * (qkdiz - qidkz)
1357                                 + gf5 * qirz
1358                                 + gf6 * qkrz
1359                                 + gf7 * (qiqkrz + qkqirz);
1360 
1361                 // Get the permanent torque with screening.
1362                 double ttm2x =
1363                         -bn1 * dixdkx
1364                                 + gf2 * dixrx
1365                                 + gf4 * (dixqkrx + dkxqirx + rxqidkx - 2.0 * qixqkx)
1366                                 - gf5 * rxqirx
1367                                 - gf7 * (rxqikrx + qkrxqirx);
1368                 double ttm2y =
1369                         -bn1 * dixdky
1370                                 + gf2 * dixry
1371                                 + gf4 * (dixqkry + dkxqiry + rxqidky - 2.0 * qixqky)
1372                                 - gf5 * rxqiry
1373                                 - gf7 * (rxqikry + qkrxqiry);
1374                 double ttm2z =
1375                         -bn1 * dixdkz
1376                                 + gf2 * dixrz
1377                                 + gf4 * (dixqkrz + dkxqirz + rxqidkz - 2.0 * qixqkz)
1378                                 - gf5 * rxqirz
1379                                 - gf7 * (rxqikrz + qkrxqirz);
1380                 double ttm3x =
1381                         bn1 * dixdkx
1382                                 + gf3 * dkxrx
1383                                 - gf4 * (dixqkrx + dkxqirx + rxqkdix - 2.0 * qixqkx)
1384                                 - gf6 * rxqkrx
1385                                 - gf7 * (rxqkirx - qkrxqirx);
1386                 double ttm3y =
1387                         bn1 * dixdky
1388                                 + gf3 * dkxry
1389                                 - gf4 * (dixqkry + dkxqiry + rxqkdiy - 2.0 * qixqky)
1390                                 - gf6 * rxqkry
1391                                 - gf7 * (rxqkiry - qkrxqiry);
1392                 double ttm3z =
1393                         bn1 * dixdkz
1394                                 + gf3 * dkxrz
1395                                 - gf4 * (dixqkrz + dkxqirz + rxqkdiz - 2.0 * qixqkz)
1396                                 - gf6 * rxqkrz
1397                                 - gf7 * (rxqkirz - qkrxqirz);
1398 
1399                 // Handle the case where scaling is used.
1400                 if (scale1 != 0.0) {
1401                     final double gfr1 =
1402                             rr3 * gl0
1403                                     + rr5 * (gl1 + gl6)
1404                                     + rr7 * (gl2 + gl7 + gl8)
1405                                     + rr9 * (gl3 + gl5)
1406                                     + rr11 * gl4;
1407                     final double gfr2 = -ck * rr3 + sc4 * rr5 - sc6 * rr7;
1408                     final double gfr3 = ci * rr3 + sc3 * rr5 + sc5 * rr7;
1409                     final double gfr4 = 2.0 * rr5;
1410                     final double gfr5 = 2.0 * (-ck * rr5 + sc4 * rr7 - sc6 * rr9);
1411                     final double gfr6 = 2.0 * (-ci * rr5 - sc3 * rr7 - sc5 * rr9);
1412                     final double gfr7 = 4.0 * rr7;
1413 
1414                     // Get the permanent force without screening.
1415                     final double ftm2rx =
1416                             gfr1 * xr
1417                                     + gfr2 * dix
1418                                     + gfr3 * dkx
1419                                     + gfr4 * (qkdix - qidkx)
1420                                     + gfr5 * qirx
1421                                     + gfr6 * qkrx
1422                                     + gfr7 * (qiqkrx + qkqirx);
1423                     final double ftm2ry =
1424                             gfr1 * yr
1425                                     + gfr2 * diy
1426                                     + gfr3 * dky
1427                                     + gfr4 * (qkdiy - qidky)
1428                                     + gfr5 * qiry
1429                                     + gfr6 * qkry
1430                                     + gfr7 * (qiqkry + qkqiry);
1431                     final double ftm2rz =
1432                             gfr1 * zr
1433                                     + gfr2 * diz
1434                                     + gfr3 * dkz
1435                                     + gfr4 * (qkdiz - qidkz)
1436                                     + gfr5 * qirz
1437                                     + gfr6 * qkrz
1438                                     + gfr7 * (qiqkrz + qkqirz);
1439 
1440                     // Get the permanent torque without screening.
1441                     final double ttm2rx =
1442                             -rr3 * dixdkx
1443                                     + gfr2 * dixrx
1444                                     + gfr4 * (dixqkrx + dkxqirx + rxqidkx - 2.0 * qixqkx)
1445                                     - gfr5 * rxqirx
1446                                     - gfr7 * (rxqikrx + qkrxqirx);
1447                     final double ttm2ry =
1448                             -rr3 * dixdky
1449                                     + gfr2 * dixry
1450                                     + gfr4 * (dixqkry + dkxqiry + rxqidky - 2.0 * qixqky)
1451                                     - gfr5 * rxqiry
1452                                     - gfr7 * (rxqikry + qkrxqiry);
1453                     final double ttm2rz =
1454                             -rr3 * dixdkz
1455                                     + gfr2 * dixrz
1456                                     + gfr4 * (dixqkrz + dkxqirz + rxqidkz - 2.0 * qixqkz)
1457                                     - gfr5 * rxqirz
1458                                     - gfr7 * (rxqikrz + qkrxqirz);
1459                     final double ttm3rx =
1460                             rr3 * dixdkx
1461                                     + gfr3 * dkxrx
1462                                     - gfr4 * (dixqkrx + dkxqirx + rxqkdix - 2.0 * qixqkx)
1463                                     - gfr6 * rxqkrx
1464                                     - gfr7 * (rxqkirx - qkrxqirx);
1465                     final double ttm3ry =
1466                             rr3 * dixdky
1467                                     + gfr3 * dkxry
1468                                     - gfr4 * (dixqkry + dkxqiry + rxqkdiy - 2.0 * qixqky)
1469                                     - gfr6 * rxqkry
1470                                     - gfr7 * (rxqkiry - qkrxqiry);
1471                     final double ttm3rz =
1472                             rr3 * dixdkz
1473                                     + gfr3 * dkxrz
1474                                     - gfr4 * (dixqkrz + dkxqirz + rxqkdiz - 2.0 * qixqkz)
1475                                     - gfr6 * rxqkrz
1476                                     - gfr7 * (rxqkirz - qkrxqirz);
1477                     ftm2x -= scale1 * ftm2rx;
1478                     ftm2y -= scale1 * ftm2ry;
1479                     ftm2z -= scale1 * ftm2rz;
1480                     ttm2x -= scale1 * ttm2rx;
1481                     ttm2y -= scale1 * ttm2ry;
1482                     ttm2z -= scale1 * ttm2rz;
1483                     ttm3x -= scale1 * ttm3rx;
1484                     ttm3y -= scale1 * ttm3ry;
1485                     ttm3z -= scale1 * ttm3rz;
1486                 }
1487                 double prefactor = electric * selfScale * l2;
1488                 grad.add(threadID, i, prefactor * ftm2x, prefactor * ftm2y, prefactor * ftm2z);
1489                 torque.add(threadID, i, prefactor * ttm2x, prefactor * ttm2y, prefactor * ttm2z);
1490 
1491 //        logger.info(format(" %d %d I Perm Force %17.15f %17.15f %17.15f", i, k, ftm2x, ftm2y, ftm2z));
1492 //        logger.info(format(" %d %d I Perm Torque %17.15f %17.15f %17.15f", i, k, ttm2x, ttm2y, ttm2z));
1493 //        logger.info(format(" %d %d K Perm Torque %17.15f %17.15f %17.15f", i, k, ttm3x, ttm3y, ttm3z));
1494 
1495                 gxk_local[k] -= prefactor * ftm2x;
1496                 gyk_local[k] -= prefactor * ftm2y;
1497                 gzk_local[k] -= prefactor * ftm2z;
1498                 txk_local[k] += prefactor * ttm3x;
1499                 tyk_local[k] += prefactor * ttm3y;
1500                 tzk_local[k] += prefactor * ttm3z;
1501 
1502                 // This is dU/dL/dX for the first term of dU/dL: d[dlPow * ereal]/dx
1503                 if (lambdaTerm && soft) {
1504                     prefactor = electric * selfScale * dEdLSign * dlPowPerm;
1505                     lambdaGrad.add(threadID, i, prefactor * ftm2x, prefactor * ftm2y, prefactor * ftm2z);
1506                     lambdaTorque.add(threadID, i, prefactor * ttm2x, prefactor * ttm2y, prefactor * ttm2z);
1507                     lxk_local[k] -= prefactor * ftm2x;
1508                     lyk_local[k] -= prefactor * ftm2y;
1509                     lzk_local[k] -= prefactor * ftm2z;
1510                     ltxk_local[k] += prefactor * ttm3x;
1511                     ltyk_local[k] += prefactor * ttm3y;
1512                     ltzk_local[k] += prefactor * ttm3z;
1513                 }
1514             }
1515             if (lambdaTermLocal && soft) {
1516                 double dRealdL =
1517                         gl0 * bn1 + (gl1 + gl6) * bn2 + (gl2 + gl7 + gl8) * bn3 + (gl3 + gl5) * bn4 + gl4 * bn5;
1518                 double d2RealdL2 =
1519                         gl0 * bn2 + (gl1 + gl6) * bn3 + (gl2 + gl7 + gl8) * bn4 + (gl3 + gl5) * bn5 + gl4 * bn6;
1520 
1521                 dUdL += selfScale * (dEdLSign * dlPowPerm * ereal + l2 * dlAlpha * dRealdL);
1522                 d2UdL2 +=
1523                         selfScale
1524                                 * (dEdLSign
1525                                 * (d2lPowPerm * ereal
1526                                 + dlPowPerm * dlAlpha * dRealdL
1527                                 + dlPowPerm * dlAlpha * dRealdL)
1528                                 + l2 * d2lAlpha * dRealdL
1529                                 + l2 * dlAlpha * dlAlpha * d2RealdL2);
1530 
1531                 double dFixdL =
1532                         gl0 * rr3
1533                                 + (gl1 + gl6) * rr5
1534                                 + (gl2 + gl7 + gl8) * rr7
1535                                 + (gl3 + gl5) * rr9
1536                                 + gl4 * rr11;
1537                 double d2FixdL2 =
1538                         gl0 * rr5
1539                                 + (gl1 + gl6) * rr7
1540                                 + (gl2 + gl7 + gl8) * rr9
1541                                 + (gl3 + gl5) * rr11
1542                                 + gl4 * rr13;
1543                 dFixdL *= scale1;
1544                 d2FixdL2 *= scale1;
1545                 dUdL -= selfScale * (dEdLSign * dlPowPerm * efix + l2 * dlAlpha * dFixdL);
1546                 d2UdL2 -=
1547                         selfScale
1548                                 * (dEdLSign
1549                                 * (d2lPowPerm * efix
1550                                 + dlPowPerm * dlAlpha * dFixdL
1551                                 + dlPowPerm * dlAlpha * dFixdL)
1552                                 + l2 * d2lAlpha * dFixdL
1553                                 + l2 * dlAlpha * dlAlpha * d2FixdL2);
1554 
1555                 // Collect terms for dU/dL/dX for the second term of dU/dL: d[fL2*dfL1dL*dRealdL]/dX
1556                 final double gf1 =
1557                         bn2 * gl0 + bn3 * (gl1 + gl6) + bn4 * (gl2 + gl7 + gl8) + bn5 * (gl3 + gl5) + bn6 * gl4;
1558                 final double gf2 = -ck * bn2 + sc4 * bn3 - sc6 * bn4;
1559                 final double gf3 = ci * bn2 + sc3 * bn3 + sc5 * bn4;
1560                 final double gf4 = 2.0 * bn3;
1561                 final double gf5 = 2.0 * (-ck * bn3 + sc4 * bn4 - sc6 * bn5);
1562                 final double gf6 = 2.0 * (-ci * bn3 - sc3 * bn4 - sc5 * bn5);
1563                 final double gf7 = 4.0 * bn4;
1564 
1565                 // Get the permanent force with screening.
1566                 double ftm2x =
1567                         gf1 * xr
1568                                 + gf2 * dix
1569                                 + gf3 * dkx
1570                                 + gf4 * (qkdix - qidkx)
1571                                 + gf5 * qirx
1572                                 + gf6 * qkrx
1573                                 + gf7 * (qiqkrx + qkqirx);
1574                 double ftm2y =
1575                         gf1 * yr
1576                                 + gf2 * diy
1577                                 + gf3 * dky
1578                                 + gf4 * (qkdiy - qidky)
1579                                 + gf5 * qiry
1580                                 + gf6 * qkry
1581                                 + gf7 * (qiqkry + qkqiry);
1582                 double ftm2z =
1583                         gf1 * zr
1584                                 + gf2 * diz
1585                                 + gf3 * dkz
1586                                 + gf4 * (qkdiz - qidkz)
1587                                 + gf5 * qirz
1588                                 + gf6 * qkrz
1589                                 + gf7 * (qiqkrz + qkqirz);
1590 
1591                 // Get the permanent torque with screening.
1592                 double ttm2x =
1593                         -bn2 * dixdkx
1594                                 + gf2 * dixrx
1595                                 + gf4 * (dixqkrx + dkxqirx + rxqidkx - 2.0 * qixqkx)
1596                                 - gf5 * rxqirx
1597                                 - gf7 * (rxqikrx + qkrxqirx);
1598                 double ttm2y =
1599                         -bn2 * dixdky
1600                                 + gf2 * dixry
1601                                 + gf4 * (dixqkry + dkxqiry + rxqidky - 2.0 * qixqky)
1602                                 - gf5 * rxqiry
1603                                 - gf7 * (rxqikry + qkrxqiry);
1604                 double ttm2z =
1605                         -bn2 * dixdkz
1606                                 + gf2 * dixrz
1607                                 + gf4 * (dixqkrz + dkxqirz + rxqidkz - 2.0 * qixqkz)
1608                                 - gf5 * rxqirz
1609                                 - gf7 * (rxqikrz + qkrxqirz);
1610                 double ttm3x =
1611                         bn2 * dixdkx
1612                                 + gf3 * dkxrx
1613                                 - gf4 * (dixqkrx + dkxqirx + rxqkdix - 2.0 * qixqkx)
1614                                 - gf6 * rxqkrx
1615                                 - gf7 * (rxqkirx - qkrxqirx);
1616                 double ttm3y =
1617                         bn2 * dixdky
1618                                 + gf3 * dkxry
1619                                 - gf4 * (dixqkry + dkxqiry + rxqkdiy - 2.0 * qixqky)
1620                                 - gf6 * rxqkry
1621                                 - gf7 * (rxqkiry - qkrxqiry);
1622                 double ttm3z =
1623                         bn2 * dixdkz
1624                                 + gf3 * dkxrz
1625                                 - gf4 * (dixqkrz + dkxqirz + rxqkdiz - 2.0 * qixqkz)
1626                                 - gf6 * rxqkrz
1627                                 - gf7 * (rxqkirz - qkrxqirz);
1628 
1629                 // Handle the case where scaling is used.
1630                 if (scale1 != 0.0) {
1631                     final double gfr1 =
1632                             rr5 * gl0
1633                                     + rr7 * (gl1 + gl6)
1634                                     + rr9 * (gl2 + gl7 + gl8)
1635                                     + rr11 * (gl3 + gl5)
1636                                     + rr13 * gl4;
1637                     final double gfr2 = -ck * rr5 + sc4 * rr7 - sc6 * rr9;
1638                     final double gfr3 = ci * rr5 + sc3 * rr7 + sc5 * rr9;
1639                     final double gfr4 = 2.0 * rr7;
1640                     final double gfr5 = 2.0 * (-ck * rr7 + sc4 * rr9 - sc6 * rr11);
1641                     final double gfr6 = 2.0 * (-ci * rr7 - sc3 * rr9 - sc5 * rr11);
1642                     final double gfr7 = 4.0 * rr9;
1643 
1644                     // Get the permanent force without screening.
1645                     final double ftm2rx =
1646                             gfr1 * xr
1647                                     + gfr2 * dix
1648                                     + gfr3 * dkx
1649                                     + gfr4 * (qkdix - qidkx)
1650                                     + gfr5 * qirx
1651                                     + gfr6 * qkrx
1652                                     + gfr7 * (qiqkrx + qkqirx);
1653                     final double ftm2ry =
1654                             gfr1 * yr
1655                                     + gfr2 * diy
1656                                     + gfr3 * dky
1657                                     + gfr4 * (qkdiy - qidky)
1658                                     + gfr5 * qiry
1659                                     + gfr6 * qkry
1660                                     + gfr7 * (qiqkry + qkqiry);
1661                     final double ftm2rz =
1662                             gfr1 * zr
1663                                     + gfr2 * diz
1664                                     + gfr3 * dkz
1665                                     + gfr4 * (qkdiz - qidkz)
1666                                     + gfr5 * qirz
1667                                     + gfr6 * qkrz
1668                                     + gfr7 * (qiqkrz + qkqirz);
1669 
1670                     // Get the permanent torque without screening.
1671                     final double ttm2rx =
1672                             -rr5 * dixdkx
1673                                     + gfr2 * dixrx
1674                                     + gfr4 * (dixqkrx + dkxqirx + rxqidkx - 2.0 * qixqkx)
1675                                     - gfr5 * rxqirx
1676                                     - gfr7 * (rxqikrx + qkrxqirx);
1677                     final double ttm2ry =
1678                             -rr5 * dixdky
1679                                     + gfr2 * dixry
1680                                     + gfr4 * (dixqkry + dkxqiry + rxqidky - 2.0 * qixqky)
1681                                     - gfr5 * rxqiry
1682                                     - gfr7 * (rxqikry + qkrxqiry);
1683                     final double ttm2rz =
1684                             -rr5 * dixdkz
1685                                     + gfr2 * dixrz
1686                                     + gfr4 * (dixqkrz + dkxqirz + rxqidkz - 2.0 * qixqkz)
1687                                     - gfr5 * rxqirz
1688                                     - gfr7 * (rxqikrz + qkrxqirz);
1689                     final double ttm3rx =
1690                             rr5 * dixdkx
1691                                     + gfr3 * dkxrx
1692                                     - gfr4 * (dixqkrx + dkxqirx + rxqkdix - 2.0 * qixqkx)
1693                                     - gfr6 * rxqkrx
1694                                     - gfr7 * (rxqkirx - qkrxqirx);
1695                     final double ttm3ry =
1696                             rr5 * dixdky
1697                                     + gfr3 * dkxry
1698                                     - gfr4 * (dixqkry + dkxqiry + rxqkdiy - 2.0 * qixqky)
1699                                     - gfr6 * rxqkry
1700                                     - gfr7 * (rxqkiry - qkrxqiry);
1701                     final double ttm3rz =
1702                             rr5 * dixdkz
1703                                     + gfr3 * dkxrz
1704                                     - gfr4 * (dixqkrz + dkxqirz + rxqkdiz - 2.0 * qixqkz)
1705                                     - gfr6 * rxqkrz
1706                                     - gfr7 * (rxqkirz - qkrxqirz);
1707                     ftm2x -= scale1 * ftm2rx;
1708                     ftm2y -= scale1 * ftm2ry;
1709                     ftm2z -= scale1 * ftm2rz;
1710                     ttm2x -= scale1 * ttm2rx;
1711                     ttm2y -= scale1 * ttm2ry;
1712                     ttm2z -= scale1 * ttm2rz;
1713                     ttm3x -= scale1 * ttm3rx;
1714                     ttm3y -= scale1 * ttm3ry;
1715                     ttm3z -= scale1 * ttm3rz;
1716                 }
1717 
1718                 // Add in dU/dL/dX for the second term of dU/dL: d[lPow*dlAlpha*dRealdL]/dX
1719                 double prefactor = electric * selfScale * l2 * dlAlpha;
1720                 lambdaGrad.add(threadID, i, prefactor * ftm2x, prefactor * ftm2y, prefactor * ftm2z);
1721                 lambdaTorque.add(threadID, i, prefactor * ttm2x, prefactor * ttm2y, prefactor * ttm2z);
1722                 lxk_local[k] -= prefactor * ftm2x;
1723                 lyk_local[k] -= prefactor * ftm2y;
1724                 lzk_local[k] -= prefactor * ftm2z;
1725                 ltxk_local[k] += prefactor * ttm3x;
1726                 ltyk_local[k] += prefactor * ttm3y;
1727                 ltzk_local[k] += prefactor * ttm3z;
1728             }
1729 
1730             return e;
1731         }
1732 
1733         /**
1734          * Evaluate the polarization energy for a pair of polarizable multipole sites.
1735          *
1736          * @return the polarization energy.
1737          */
1738         private double polarizationPair(boolean gradientLocal, boolean lambdaTermLocal) {
1739             final double dsc3 = 1.0 - scale3 * scaled;
1740             final double dsc5 = 1.0 - scale5 * scaled;
1741             final double dsc7 = 1.0 - scale7 * scaled;
1742             final double psc3 = 1.0 - scale3 * scalep;
1743             final double psc5 = 1.0 - scale5 * scalep;
1744             final double psc7 = 1.0 - scale7 * scalep;
1745             final double usc3 = 1.0 - scale3;
1746             final double usc5 = 1.0 - scale5;
1747 
1748             final double dixukx = diy * ukz - diz * uky;
1749             final double dixuky = diz * ukx - dix * ukz;
1750             final double dixukz = dix * uky - diy * ukx;
1751             final double dkxuix = dky * uiz - dkz * uiy;
1752             final double dkxuiy = dkz * uix - dkx * uiz;
1753             final double dkxuiz = dkx * uiy - dky * uix;
1754             final double dixukpx = diy * pkz - diz * pky;
1755             final double dixukpy = diz * pkx - dix * pkz;
1756             final double dixukpz = dix * pky - diy * pkx;
1757             final double dkxuipx = dky * piz - dkz * piy;
1758             final double dkxuipy = dkz * pix - dkx * piz;
1759             final double dkxuipz = dkx * piy - dky * pix;
1760             final double dixrx = diy * zr - diz * yr;
1761             final double dixry = diz * xr - dix * zr;
1762             final double dixrz = dix * yr - diy * xr;
1763             final double dkxrx = dky * zr - dkz * yr;
1764             final double dkxry = dkz * xr - dkx * zr;
1765             final double dkxrz = dkx * yr - dky * xr;
1766             final double qirx = qixx * xr + qixy * yr + qixz * zr;
1767             final double qiry = qixy * xr + qiyy * yr + qiyz * zr;
1768             final double qirz = qixz * xr + qiyz * yr + qizz * zr;
1769             final double qkrx = qkxx * xr + qkxy * yr + qkxz * zr;
1770             final double qkry = qkxy * xr + qkyy * yr + qkyz * zr;
1771             final double qkrz = qkxz * xr + qkyz * yr + qkzz * zr;
1772             final double rxqirx = yr * qirz - zr * qiry;
1773             final double rxqiry = zr * qirx - xr * qirz;
1774             final double rxqirz = xr * qiry - yr * qirx;
1775             final double rxqkrx = yr * qkrz - zr * qkry;
1776             final double rxqkry = zr * qkrx - xr * qkrz;
1777             final double rxqkrz = xr * qkry - yr * qkrx;
1778             final double qiukx = qixx * ukx + qixy * uky + qixz * ukz;
1779             final double qiuky = qixy * ukx + qiyy * uky + qiyz * ukz;
1780             final double qiukz = qixz * ukx + qiyz * uky + qizz * ukz;
1781             final double qkuix = qkxx * uix + qkxy * uiy + qkxz * uiz;
1782             final double qkuiy = qkxy * uix + qkyy * uiy + qkyz * uiz;
1783             final double qkuiz = qkxz * uix + qkyz * uiy + qkzz * uiz;
1784             final double qiukpx = qixx * pkx + qixy * pky + qixz * pkz;
1785             final double qiukpy = qixy * pkx + qiyy * pky + qiyz * pkz;
1786             final double qiukpz = qixz * pkx + qiyz * pky + qizz * pkz;
1787             final double qkuipx = qkxx * pix + qkxy * piy + qkxz * piz;
1788             final double qkuipy = qkxy * pix + qkyy * piy + qkyz * piz;
1789             final double qkuipz = qkxz * pix + qkyz * piy + qkzz * piz;
1790             final double uixqkrx = uiy * qkrz - uiz * qkry;
1791             final double uixqkry = uiz * qkrx - uix * qkrz;
1792             final double uixqkrz = uix * qkry - uiy * qkrx;
1793             final double ukxqirx = uky * qirz - ukz * qiry;
1794             final double ukxqiry = ukz * qirx - ukx * qirz;
1795             final double ukxqirz = ukx * qiry - uky * qirx;
1796             final double uixqkrpx = piy * qkrz - piz * qkry;
1797             final double uixqkrpy = piz * qkrx - pix * qkrz;
1798             final double uixqkrpz = pix * qkry - piy * qkrx;
1799             final double ukxqirpx = pky * qirz - pkz * qiry;
1800             final double ukxqirpy = pkz * qirx - pkx * qirz;
1801             final double ukxqirpz = pkx * qiry - pky * qirx;
1802             final double rxqiukx = yr * qiukz - zr * qiuky;
1803             final double rxqiuky = zr * qiukx - xr * qiukz;
1804             final double rxqiukz = xr * qiuky - yr * qiukx;
1805             final double rxqkuix = yr * qkuiz - zr * qkuiy;
1806             final double rxqkuiy = zr * qkuix - xr * qkuiz;
1807             final double rxqkuiz = xr * qkuiy - yr * qkuix;
1808             final double rxqiukpx = yr * qiukpz - zr * qiukpy;
1809             final double rxqiukpy = zr * qiukpx - xr * qiukpz;
1810             final double rxqiukpz = xr * qiukpy - yr * qiukpx;
1811             final double rxqkuipx = yr * qkuipz - zr * qkuipy;
1812             final double rxqkuipy = zr * qkuipx - xr * qkuipz;
1813             final double rxqkuipz = xr * qkuipy - yr * qkuipx;
1814 
1815             // Calculate the scalar products for permanent multipoles.
1816             final double sc3 = dix * xr + diy * yr + diz * zr;
1817             final double sc4 = dkx * xr + dky * yr + dkz * zr;
1818             final double sc5 = qirx * xr + qiry * yr + qirz * zr;
1819             final double sc6 = qkrx * xr + qkry * yr + qkrz * zr;
1820 
1821             // Calculate the scalar products for polarization components.
1822             final double sci1 = uix * dkx + uiy * dky + uiz * dkz + dix * ukx + diy * uky + diz * ukz;
1823             final double sci3 = uix * xr + uiy * yr + uiz * zr;
1824             final double sci4 = ukx * xr + uky * yr + ukz * zr;
1825             final double sci7 = qirx * ukx + qiry * uky + qirz * ukz;
1826             final double sci8 = qkrx * uix + qkry * uiy + qkrz * uiz;
1827             final double scip1 = pix * dkx + piy * dky + piz * dkz + dix * pkx + diy * pky + diz * pkz;
1828             final double scip2 = uix * pkx + uiy * pky + uiz * pkz + pix * ukx + piy * uky + piz * ukz;
1829             final double scip3 = pix * xr + piy * yr + piz * zr;
1830             final double scip4 = pkx * xr + pky * yr + pkz * zr;
1831             final double scip7 = qirx * pkx + qiry * pky + qirz * pkz;
1832             final double scip8 = qkrx * pix + qkry * piy + qkrz * piz;
1833 
1834             // Calculate the gl functions for polarization components.
1835             final double gli1 = ck * sci3 - ci * sci4;
1836             final double gli2 = -sc3 * sci4 - sci3 * sc4;
1837             final double gli3 = sci3 * sc6 - sci4 * sc5;
1838             final double gli6 = sci1;
1839             final double gli7 = 2.0 * (sci7 - sci8);
1840             final double glip1 = ck * scip3 - ci * scip4;
1841             final double glip2 = -sc3 * scip4 - scip3 * sc4;
1842             final double glip3 = scip3 * sc6 - scip4 * sc5;
1843             final double glip6 = scip1;
1844             final double glip7 = 2.0 * (scip7 - scip8);
1845 
1846             // Compute the energy contributions for this interaction.
1847             final double ereal = (gli1 + gli6) * bn1 + (gli2 + gli7) * bn2 + gli3 * bn3;
1848             final double efix =
1849                     (gli1 + gli6) * rr3 * psc3 + (gli2 + gli7) * rr5 * psc5 + gli3 * rr7 * psc7;
1850             final double e = selfScale * 0.5 * (ereal - efix);
1851 
1852             // logger.info(format(" %d %d Polarization Energy (real): %17.15f", i, k, 0.5 * ereal));
1853             // logger.info(format(" %d %d Polarization Energy (fix) : %17.15f", i, k, -0.5 * efix));
1854 
1855             if (!(gradientLocal || lambdaTermLocal)) {
1856                 return polarizationScale * e;
1857             }
1858             boolean dorli = false;
1859             if (psc3 != 0.0 || dsc3 != 0.0 || usc3 != 0.0) {
1860                 dorli = true;
1861             }
1862 
1863             // Get the induced force with screening.
1864             final double gfi1 =
1865                     0.5 * bn2 * (gli1 + glip1 + gli6 + glip6)
1866                             + 0.5 * bn2 * scip2
1867                             + 0.5 * bn3 * (gli2 + glip2 + gli7 + glip7)
1868                             - 0.5 * bn3 * (sci3 * scip4 + scip3 * sci4)
1869                             + 0.5 * bn4 * (gli3 + glip3);
1870             final double gfi2 = -ck * bn1 + sc4 * bn2 - sc6 * bn3;
1871             final double gfi3 = ci * bn1 + sc3 * bn2 + sc5 * bn3;
1872             final double gfi4 = 2.0 * bn2;
1873             final double gfi5 = bn3 * (sci4 + scip4);
1874             final double gfi6 = -bn3 * (sci3 + scip3);
1875             double ftm2ix =
1876                     gfi1 * xr
1877                             + 0.5
1878                             * (gfi2 * (uix + pix)
1879                             + bn2 * (sci4 * pix + scip4 * uix)
1880                             + gfi3 * (ukx + pkx)
1881                             + bn2 * (sci3 * pkx + scip3 * ukx)
1882                             + (sci4 + scip4) * bn2 * dix
1883                             + (sci3 + scip3) * bn2 * dkx
1884                             + gfi4 * (qkuix + qkuipx - qiukx - qiukpx))
1885                             + gfi5 * qirx
1886                             + gfi6 * qkrx;
1887             double ftm2iy =
1888                     gfi1 * yr
1889                             + 0.5
1890                             * (gfi2 * (uiy + piy)
1891                             + bn2 * (sci4 * piy + scip4 * uiy)
1892                             + gfi3 * (uky + pky)
1893                             + bn2 * (sci3 * pky + scip3 * uky)
1894                             + (sci4 + scip4) * bn2 * diy
1895                             + (sci3 + scip3) * bn2 * dky
1896                             + gfi4 * (qkuiy + qkuipy - qiuky - qiukpy))
1897                             + gfi5 * qiry
1898                             + gfi6 * qkry;
1899             double ftm2iz =
1900                     gfi1 * zr
1901                             + 0.5
1902                             * (gfi2 * (uiz + piz)
1903                             + bn2 * (sci4 * piz + scip4 * uiz)
1904                             + gfi3 * (ukz + pkz)
1905                             + bn2 * (sci3 * pkz + scip3 * ukz)
1906                             + (sci4 + scip4) * bn2 * diz
1907                             + (sci3 + scip3) * bn2 * dkz
1908                             + gfi4 * (qkuiz + qkuipz - qiukz - qiukpz))
1909                             + gfi5 * qirz
1910                             + gfi6 * qkrz;
1911 
1912             // Get the induced torque with screening.
1913             final double gti2 = 0.5 * bn2 * (sci4 + scip4);
1914             final double gti3 = 0.5 * bn2 * (sci3 + scip3);
1915             final double gti4 = gfi4;
1916             final double gti5 = gfi5;
1917             final double gti6 = gfi6;
1918             double ttm2ix =
1919                     -0.5 * bn1 * (dixukx + dixukpx)
1920                             + gti2 * dixrx
1921                             - gti5 * rxqirx
1922                             + 0.5 * gti4 * (ukxqirx + rxqiukx + ukxqirpx + rxqiukpx);
1923             double ttm2iy =
1924                     -0.5 * bn1 * (dixuky + dixukpy)
1925                             + gti2 * dixry
1926                             - gti5 * rxqiry
1927                             + 0.5 * gti4 * (ukxqiry + rxqiuky + ukxqirpy + rxqiukpy);
1928             double ttm2iz =
1929                     -0.5 * bn1 * (dixukz + dixukpz)
1930                             + gti2 * dixrz
1931                             - gti5 * rxqirz
1932                             + 0.5 * gti4 * (ukxqirz + rxqiukz + ukxqirpz + rxqiukpz);
1933             double ttm3ix =
1934                     -0.5 * bn1 * (dkxuix + dkxuipx)
1935                             + gti3 * dkxrx
1936                             - gti6 * rxqkrx
1937                             - 0.5 * gti4 * (uixqkrx + rxqkuix + uixqkrpx + rxqkuipx);
1938             double ttm3iy =
1939                     -0.5 * bn1 * (dkxuiy + dkxuipy)
1940                             + gti3 * dkxry
1941                             - gti6 * rxqkry
1942                             - 0.5 * gti4 * (uixqkry + rxqkuiy + uixqkrpy + rxqkuipy);
1943             double ttm3iz =
1944                     -0.5 * bn1 * (dkxuiz + dkxuipz)
1945                             + gti3 * dkxrz
1946                             - gti6 * rxqkrz
1947                             - 0.5 * gti4 * (uixqkrz + rxqkuiz + uixqkrpz + rxqkuipz);
1948             double ftm2rix = 0.0;
1949             double ftm2riy = 0.0;
1950             double ftm2riz = 0.0;
1951             double ttm2rix = 0.0;
1952             double ttm2riy = 0.0;
1953             double ttm2riz = 0.0;
1954             double ttm3rix = 0.0;
1955             double ttm3riy = 0.0;
1956             double ttm3riz = 0.0;
1957             if (dorli) {
1958                 // Get the induced force without screening.
1959                 final double gfri1 =
1960                         0.5 * rr5 * ((gli1 + gli6) * psc3 + (glip1 + glip6) * dsc3 + scip2 * usc3)
1961                                 + 0.5
1962                                 * rr7
1963                                 * ((gli7 + gli2) * psc5
1964                                 + (glip7 + glip2) * dsc5
1965                                 - (sci3 * scip4 + scip3 * sci4) * usc5)
1966                                 + 0.5 * rr9 * (gli3 * psc7 + glip3 * dsc7);
1967                 final double gfri4 = 2.0 * rr5;
1968                 final double gfri5 = rr7 * (sci4 * psc7 + scip4 * dsc7);
1969                 final double gfri6 = -rr7 * (sci3 * psc7 + scip3 * dsc7);
1970                 ftm2rix =
1971                         gfri1 * xr
1972                                 + 0.5
1973                                 * (-rr3 * ck * (uix * psc3 + pix * dsc3)
1974                                 + rr5 * sc4 * (uix * psc5 + pix * dsc5)
1975                                 - rr7 * sc6 * (uix * psc7 + pix * dsc7))
1976                                 + (rr3 * ci * (ukx * psc3 + pkx * dsc3)
1977                                 + rr5 * sc3 * (ukx * psc5 + pkx * dsc5)
1978                                 + rr7 * sc5 * (ukx * psc7 + pkx * dsc7))
1979                                 * 0.5
1980                                 + rr5 * usc5 * (sci4 * pix + scip4 * uix + sci3 * pkx + scip3 * ukx) * 0.5
1981                                 + 0.5 * (sci4 * psc5 + scip4 * dsc5) * rr5 * dix
1982                                 + 0.5 * (sci3 * psc5 + scip3 * dsc5) * rr5 * dkx
1983                                 + 0.5 * gfri4 * ((qkuix - qiukx) * psc5 + (qkuipx - qiukpx) * dsc5)
1984                                 + gfri5 * qirx
1985                                 + gfri6 * qkrx;
1986                 ftm2riy =
1987                         gfri1 * yr
1988                                 + 0.5
1989                                 * (-rr3 * ck * (uiy * psc3 + piy * dsc3)
1990                                 + rr5 * sc4 * (uiy * psc5 + piy * dsc5)
1991                                 - rr7 * sc6 * (uiy * psc7 + piy * dsc7))
1992                                 + (rr3 * ci * (uky * psc3 + pky * dsc3)
1993                                 + rr5 * sc3 * (uky * psc5 + pky * dsc5)
1994                                 + rr7 * sc5 * (uky * psc7 + pky * dsc7))
1995                                 * 0.5
1996                                 + rr5 * usc5 * (sci4 * piy + scip4 * uiy + sci3 * pky + scip3 * uky) * 0.5
1997                                 + 0.5 * (sci4 * psc5 + scip4 * dsc5) * rr5 * diy
1998                                 + 0.5 * (sci3 * psc5 + scip3 * dsc5) * rr5 * dky
1999                                 + 0.5 * gfri4 * ((qkuiy - qiuky) * psc5 + (qkuipy - qiukpy) * dsc5)
2000                                 + gfri5 * qiry
2001                                 + gfri6 * qkry;
2002                 ftm2riz =
2003                         gfri1 * zr
2004                                 + 0.5
2005                                 * (-rr3 * ck * (uiz * psc3 + piz * dsc3)
2006                                 + rr5 * sc4 * (uiz * psc5 + piz * dsc5)
2007                                 - rr7 * sc6 * (uiz * psc7 + piz * dsc7))
2008                                 + (rr3 * ci * (ukz * psc3 + pkz * dsc3)
2009                                 + rr5 * sc3 * (ukz * psc5 + pkz * dsc5)
2010                                 + rr7 * sc5 * (ukz * psc7 + pkz * dsc7))
2011                                 * 0.5
2012                                 + rr5 * usc5 * (sci4 * piz + scip4 * uiz + sci3 * pkz + scip3 * ukz) * 0.5
2013                                 + 0.5 * (sci4 * psc5 + scip4 * dsc5) * rr5 * diz
2014                                 + 0.5 * (sci3 * psc5 + scip3 * dsc5) * rr5 * dkz
2015                                 + 0.5 * gfri4 * ((qkuiz - qiukz) * psc5 + (qkuipz - qiukpz) * dsc5)
2016                                 + gfri5 * qirz
2017                                 + gfri6 * qkrz;
2018 
2019                 // Get the induced torque without screening.
2020                 final double gtri2 = 0.5 * rr5 * (sci4 * psc5 + scip4 * dsc5);
2021                 final double gtri3 = 0.5 * rr5 * (sci3 * psc5 + scip3 * dsc5);
2022                 final double gtri4 = gfri4;
2023                 final double gtri5 = gfri5;
2024                 final double gtri6 = gfri6;
2025                 ttm2rix =
2026                         -rr3 * (dixukx * psc3 + dixukpx * dsc3) * 0.5
2027                                 + gtri2 * dixrx
2028                                 - gtri5 * rxqirx
2029                                 + gtri4 * ((ukxqirx + rxqiukx) * psc5 + (ukxqirpx + rxqiukpx) * dsc5) * 0.5;
2030                 ttm2riy =
2031                         -rr3 * (dixuky * psc3 + dixukpy * dsc3) * 0.5
2032                                 + gtri2 * dixry
2033                                 - gtri5 * rxqiry
2034                                 + gtri4 * ((ukxqiry + rxqiuky) * psc5 + (ukxqirpy + rxqiukpy) * dsc5) * 0.5;
2035                 ttm2riz =
2036                         -rr3 * (dixukz * psc3 + dixukpz * dsc3) * 0.5
2037                                 + gtri2 * dixrz
2038                                 - gtri5 * rxqirz
2039                                 + gtri4 * ((ukxqirz + rxqiukz) * psc5 + (ukxqirpz + rxqiukpz) * dsc5) * 0.5;
2040                 ttm3rix =
2041                         -rr3 * (dkxuix * psc3 + dkxuipx * dsc3) * 0.5
2042                                 + gtri3 * dkxrx
2043                                 - gtri6 * rxqkrx
2044                                 - gtri4 * ((uixqkrx + rxqkuix) * psc5 + (uixqkrpx + rxqkuipx) * dsc5) * 0.5;
2045                 ttm3riy =
2046                         -rr3 * (dkxuiy * psc3 + dkxuipy * dsc3) * 0.5
2047                                 + gtri3 * dkxry
2048                                 - gtri6 * rxqkry
2049                                 - gtri4 * ((uixqkry + rxqkuiy) * psc5 + (uixqkrpy + rxqkuipy) * dsc5) * 0.5;
2050                 ttm3riz =
2051                         -rr3 * (dkxuiz * psc3 + dkxuipz * dsc3) * 0.5
2052                                 + gtri3 * dkxrz
2053                                 - gtri6 * rxqkrz
2054                                 - gtri4 * ((uixqkrz + rxqkuiz) * psc5 + (uixqkrpz + rxqkuipz) * dsc5) * 0.5;
2055             }
2056 
2057             // Account for partially excluded induced interactions.
2058             double temp3 = 0.5 * rr3 * ((gli1 + gli6) * scalep + (glip1 + glip6) * scaled);
2059             double temp5 = 0.5 * rr5 * ((gli2 + gli7) * scalep + (glip2 + glip7) * scaled);
2060             final double temp7 = 0.5 * rr7 * (gli3 * scalep + glip3 * scaled);
2061             final double fridmpx = temp3 * ddsc3x + temp5 * ddsc5x + temp7 * ddsc7x;
2062             final double fridmpy = temp3 * ddsc3y + temp5 * ddsc5y + temp7 * ddsc7y;
2063             final double fridmpz = temp3 * ddsc3z + temp5 * ddsc5z + temp7 * ddsc7z;
2064 
2065             // Find some scaling terms for induced-induced force.
2066             temp3 = 0.5 * rr3 * scip2;
2067             temp5 = -0.5 * rr5 * (sci3 * scip4 + scip3 * sci4);
2068             final double findmpx = temp3 * ddsc3x + temp5 * ddsc5x;
2069             final double findmpy = temp3 * ddsc3y + temp5 * ddsc5y;
2070             final double findmpz = temp3 * ddsc3z + temp5 * ddsc5z;
2071 
2072 //      logger.info(format(" %d %d Ewald Polarization grad (i):   %17.15f %17.15f %17.15f", i, k, ftm2ix, ftm2iy, ftm2iz));
2073 //      logger.info(format(" %d %d Ewald Polarization torque (i): %17.15f %17.15f %17.15f", i, k, ttm2ix, ttm2iy, ttm2iz));
2074 //      logger.info(format(" %d %d Ewald Polarization torque (k): %17.15f %17.15f %17.15f", i, k, ttm3ix, ttm3iy, ttm3iz));
2075 
2076             // Modify the forces for partially excluded interactions.
2077             ftm2ix = ftm2ix - fridmpx - findmpx;
2078             ftm2iy = ftm2iy - fridmpy - findmpy;
2079             ftm2iz = ftm2iz - fridmpz - findmpz;
2080 
2081             // Correction to convert mutual to direct polarization force.
2082             if (polarization == Polarization.DIRECT) {
2083                 final double gfd = 0.5 * (bn2 * scip2 - bn3 * (scip3 * sci4 + sci3 * scip4));
2084                 final double gfdr = 0.5 * (rr5 * scip2 * usc3 - rr7 * (scip3 * sci4 + sci3 * scip4) * usc5);
2085                 ftm2ix =
2086                         ftm2ix - gfd * xr - 0.5 * bn2 * (sci4 * pix + scip4 * uix + sci3 * pkx + scip3 * ukx);
2087                 ftm2iy =
2088                         ftm2iy - gfd * yr - 0.5 * bn2 * (sci4 * piy + scip4 * uiy + sci3 * pky + scip3 * uky);
2089                 ftm2iz =
2090                         ftm2iz - gfd * zr - 0.5 * bn2 * (sci4 * piz + scip4 * uiz + sci3 * pkz + scip3 * ukz);
2091                 final double fdirx =
2092                         gfdr * xr + 0.5 * usc5 * rr5 * (sci4 * pix + scip4 * uix + sci3 * pkx + scip3 * ukx);
2093                 final double fdiry =
2094                         gfdr * yr + 0.5 * usc5 * rr5 * (sci4 * piy + scip4 * uiy + sci3 * pky + scip3 * uky);
2095                 final double fdirz =
2096                         gfdr * zr + 0.5 * usc5 * rr5 * (sci4 * piz + scip4 * uiz + sci3 * pkz + scip3 * ukz);
2097                 ftm2ix = ftm2ix + fdirx + findmpx;
2098                 ftm2iy = ftm2iy + fdiry + findmpy;
2099                 ftm2iz = ftm2iz + fdirz + findmpz;
2100             }
2101 
2102             // Correction for OPT induced dipoles.
2103             //            if (scfAlgorithm == ParticleMeshEwald.SCFAlgorithm.EPT) {
2104             //                double eptx = 0.0;
2105             //                double epty = 0.0;
2106             //                double eptz = 0.0;
2107             //                for (int jj = 0; jj < optOrder; jj++) {
2108             //                    double optix = optDipole[jj][i][0];
2109             //                    double optiy = optDipole[jj][i][1];
2110             //                    double optiz = optDipole[jj][i][2];
2111             //                    double optixp = optDipoleCR[jj][i][0];
2112             //                    double optiyp = optDipoleCR[jj][i][1];
2113             //                    double optizp = optDipoleCR[jj][i][2];
2114             //                    double uirm = optix * xr + optiy * yr + optiz * zr;
2115             //                    for (int mm = 0; mm < optOrder - jj; mm++) {
2116             //                        double optkx = optDipole[mm][k][0];
2117             //                        double optky = optDipole[mm][k][1];
2118             //                        double optkz = optDipole[mm][k][2];
2119             //                        double optkxp = optDipoleCR[mm][k][0];
2120             //                        double optkyp = optDipoleCR[mm][k][1];
2121             //                        double optkzp = optDipoleCR[mm][k][2];
2122             //                        double ukrm = optkx * xr + optky * yr + optkz * zr;
2123             //                        double term1 = bn2 - usc3 * rr5;
2124             //                        double term2 = bn3 - usc5 * rr7;
2125             //                        double term3 = usr5 + term1;
2126             //                        double term4 = rr3;
2127             //                        double term5 = -xr * term3 + ddsc3x * term4;
2128             //                        double term6 = -(bn2 - usc5 * rr5) + xr * xr * term2 - rr5 * xr *
2129             // ddsc5x;
2130             //                        double tixx = optix * term5 + uirm * term6;
2131             //                        double tkxx = optkx * term5 + ukrm * term6;
2132             //                        term5 = -yr * term3 + ddsc3y * term4;
2133             //                        term6 = -usr5 + yr * yr * term2 - rr5 * yr * ddsc5y;
2134             //                        double tiyy = optiy * term5 + uirm * term6;
2135             //                        double tkyy = optky * term5 + ukrm * term6;
2136             //                        term5 = -zr * term3 + ddsc3z * term4;
2137             //                        term6 = -usr5 + zr * zr * term2 - rr5 * zr * ddsc5z;
2138             //                        double tizz = optiz * term5 + uirm * term6;
2139             //                        double tkzz = optkz * term5 + ukrm * term6;
2140             //                        term4 = -usr5 * yr;
2141             //                        term5 = -xr * term1 + rr3 * ddsc3x;
2142             //                        term6 = xr * yr * term2 - rr5 * yr * ddsc5x;
2143             //                        double tixy = optix * term4 + optiy * term5 + uirm * term6;
2144             //                        double tkxy = optkx * term4 + optky * term5 + ukrm * term6;
2145             //                        term4 = -usr5 * zr;
2146             //                        term6 = xr * zr * term2 - rr5 * zr * ddsc5x;
2147             //                        double tixz = optix * term4 + optiz * term5 + uirm * term6;
2148             //                        double tkxz = optkx * term4 + optkz * term5 + ukrm * term6;
2149             //                        term5 = -yr * term1 + rr3 * ddsc3y;
2150             //                        term6 = yr * zr * term2 - rr5 * zr * ddsc5y;
2151             //                        double tiyz = optiy * term4 + optiz * term5 + uirm * term6;
2152             //                        double tkyz = optkz * term4 + optkz * term5 + ukrm * term6;
2153             //                        double depx = tixx * optkxp + tkxx * optixp
2154             //                                + tixy * optkyp + tkxy * optiyp
2155             //                                + tixz * optkzp + tkxz * optizp;
2156             //                        double depy = tixy * optkxp + tkxy * optixp
2157             //                                + tiyy * optkyp + tkyy * optiyp
2158             //                                + tiyz * optkzp + tkyz * optizp;
2159             //                        double depz = tixz * optkxp + tkxz * optixp
2160             //                                + tiyz * optkyp + tkyz * optiyp
2161             //                                + tizz * optkzp + tkzz * optizp;
2162             //                        double optCoefSum = optRegion.optCoefficientsSum[jj + mm + 1];
2163             //                        double fx = optCoefSum * depx;
2164             //                        double fy = optCoefSum * depy;
2165             //                        double fz = optCoefSum * depz;
2166             //                        eptx += fx;
2167             //                        epty += fy;
2168             //                        eptz += fz;
2169             //                    }
2170             //                }
2171             //                if (i == 0 && k == 1) {
2172             //                    logger.info(format(" %s %16.8f %16.8f %16.8f", "ept force", eptx, epty,
2173             // eptz));
2174             //                }
2175             //                ftm2ix += eptx;
2176             //                ftm2iy += epty;
2177             //                ftm2iz += eptz;
2178             //            }
2179 
2180             // Handle the case where scaling is used.
2181 
2182 //      logger.info(format(" %d %d Polarization Coulomb grad (i):   %17.15f %17.15f %17.15f", i, k,
2183 //          -ftm2rix - fridmpx - findmpx, -ftm2riy - fridmpy - findmpy, -ftm2riz - fridmpz - findmpz));
2184 //      logger.info(format(" %d %d Polarization Thole torque (i): %17.15f %17.15f %17.15f", i, k, -ttm2rix, -ttm2riy, -ttm2riz));
2185 //      logger.info(format(" %d %d Polarization Thole torque (k): %17.15f %17.15f %17.15f", i, k, -ttm3rix, -ttm3riy, -ttm3riz));
2186 
2187             ftm2ix = ftm2ix - ftm2rix;
2188             ftm2iy = ftm2iy - ftm2riy;
2189             ftm2iz = ftm2iz - ftm2riz;
2190             ttm2ix = ttm2ix - ttm2rix;
2191             ttm2iy = ttm2iy - ttm2riy;
2192             ttm2iz = ttm2iz - ttm2riz;
2193             ttm3ix = ttm3ix - ttm3rix;
2194             ttm3iy = ttm3iy - ttm3riy;
2195             ttm3iz = ttm3iz - ttm3riz;
2196 
2197             double scalar = electric * polarizationScale * selfScale;
2198 
2199             grad.add(threadID, i, scalar * ftm2ix, scalar * ftm2iy, scalar * ftm2iz);
2200             torque.add(threadID, i, scalar * ttm2ix, scalar * ttm2iy, scalar * ttm2iz);
2201             gxk_local[k] -= scalar * ftm2ix;
2202             gyk_local[k] -= scalar * ftm2iy;
2203             gzk_local[k] -= scalar * ftm2iz;
2204             txk_local[k] += scalar * ttm3ix;
2205             tyk_local[k] += scalar * ttm3iy;
2206             tzk_local[k] += scalar * ttm3iz;
2207             if (lambdaTermLocal) {
2208                 dUdL += dEdLSign * dlPowPol * e;
2209                 d2UdL2 += dEdLSign * d2lPowPol * e;
2210                 scalar = electric * dEdLSign * dlPowPol * selfScale;
2211                 lambdaGrad.add(threadID, i, scalar * ftm2ix, scalar * ftm2iy, scalar * ftm2iz);
2212                 lambdaTorque.add(threadID, i, scalar * ttm2ix, scalar * ttm2iy, scalar * ttm2iz);
2213                 lxk_local[k] -= scalar * ftm2ix;
2214                 lyk_local[k] -= scalar * ftm2iy;
2215                 lzk_local[k] -= scalar * ftm2iz;
2216                 ltxk_local[k] += scalar * ttm3ix;
2217                 ltyk_local[k] += scalar * ttm3iy;
2218                 ltzk_local[k] += scalar * ttm3iz;
2219             }
2220             return polarizationScale * e;
2221         }
2222 
2223         private void setMultipoleI(double[] globalMultipolei) {
2224             ci = globalMultipolei[t000];
2225             dix = globalMultipolei[t100];
2226             diy = globalMultipolei[t010];
2227             diz = globalMultipolei[t001];
2228             qixx = globalMultipolei[t200] * oneThird;
2229             qiyy = globalMultipolei[t020] * oneThird;
2230             qizz = globalMultipolei[t002] * oneThird;
2231             qixy = globalMultipolei[t110] * oneThird;
2232             qixz = globalMultipolei[t101] * oneThird;
2233             qiyz = globalMultipolei[t011] * oneThird;
2234         }
2235 
2236         private void setMultipoleK(double[] globalMultipolek) {
2237             ck = globalMultipolek[t000];
2238             dkx = globalMultipolek[t100];
2239             dky = globalMultipolek[t010];
2240             dkz = globalMultipolek[t001];
2241             qkxx = globalMultipolek[t200] * oneThird;
2242             qkyy = globalMultipolek[t020] * oneThird;
2243             qkzz = globalMultipolek[t002] * oneThird;
2244             qkxy = globalMultipolek[t110] * oneThird;
2245             qkxz = globalMultipolek[t101] * oneThird;
2246             qkyz = globalMultipolek[t011] * oneThird;
2247         }
2248 
2249         private void setInducedI(double[] inducedDipolei) {
2250             uix = inducedDipolei[0];
2251             uiy = inducedDipolei[1];
2252             uiz = inducedDipolei[2];
2253         }
2254 
2255         private void setInducedK(double[] inducedDipolek) {
2256             ukx = inducedDipolek[0];
2257             uky = inducedDipolek[1];
2258             ukz = inducedDipolek[2];
2259         }
2260 
2261         private void setInducedpI(double[] inducedDipolepi) {
2262             pix = inducedDipolepi[0];
2263             piy = inducedDipolepi[1];
2264             piz = inducedDipolepi[2];
2265         }
2266 
2267         private void setInducedpK(double[] inducedDipolepk) {
2268             pkx = inducedDipolepk[0];
2269             pky = inducedDipolepk[1];
2270             pkz = inducedDipolepk[2];
2271         }
2272 
2273     }
2274 
2275     /**
2276      * The Real Space Gradient Loop class contains methods and thread local variables to parallelize
2277      * the evaluation of the real space permanent and polarization energies and gradients.
2278      */
2279     private class FixedChargeEnergyLoop extends IntegerForLoop {
2280 
2281         private final double[] dx_local;
2282         private final double[][] rot_local;
2283         private double ci;
2284         private double ck;
2285         private double xr, yr, zr;
2286         private double bn0, bn1, bn2;
2287         private double rr1, rr3, rr5;
2288         private double scale;
2289         private double l2;
2290         private boolean soft;
2291         private double selfScale;
2292         private double permanentEnergy;
2293         private double inducedEnergy;
2294         private double dUdL, d2UdL2;
2295         private int i, k, iSymm, count;
2296         private double[] gxk_local, gyk_local, gzk_local;
2297         private double[] lxk_local, lyk_local, lzk_local;
2298         private double[] masking_local;
2299         private int threadID;
2300 
2301         FixedChargeEnergyLoop() {
2302             super();
2303             dx_local = new double[3];
2304             rot_local = new double[3][3];
2305         }
2306 
2307         @Override
2308         public void finish() {
2309             sharedInteractions.addAndGet(count);
2310             if (lambdaTerm) {
2311                 shareddEdLambda.addAndGet(dUdL * electric);
2312                 sharedd2EdLambda2.addAndGet(d2UdL2 * electric);
2313             }
2314             realSpaceEnergyTime[getThreadIndex()] += System.nanoTime();
2315         }
2316 
2317         public int getCount() {
2318             return count;
2319         }
2320 
2321         @Override
2322         public void run(int lb, int ub) {
2323             List<SymOp> symOps = crystal.spaceGroup.symOps;
2324             int nSymm = symOps.size();
2325             int nAtoms = atoms.length;
2326             for (iSymm = 0; iSymm < nSymm; iSymm++) {
2327                 SymOp symOp = symOps.get(iSymm);
2328                 if (gradient) {
2329                     fill(gxk_local, 0.0);
2330                     fill(gyk_local, 0.0);
2331                     fill(gzk_local, 0.0);
2332                 }
2333                 if (lambdaTerm) {
2334                     fill(lxk_local, 0.0);
2335                     fill(lyk_local, 0.0);
2336                     fill(lzk_local, 0.0);
2337                 }
2338                 realSpaceChunk(lb, ub);
2339                 if (gradient) {
2340                     // Rotate symmetry mate gradients
2341                     if (iSymm != 0) {
2342                         crystal.applyTransSymRot(nAtoms, gxk_local, gyk_local, gzk_local, gxk_local, gyk_local, gzk_local, symOp, rot_local);
2343                     }
2344                     // Sum symmetry mate gradients into asymmetric unit gradients
2345                     for (int j = 0; j < nAtoms; j++) {
2346                         grad.add(threadID, j, gxk_local[j], gyk_local[j], gzk_local[j]);
2347                     }
2348                 }
2349                 if (lambdaTerm) {
2350                     // Rotate symmetry mate gradients
2351                     if (iSymm != 0) {
2352                         crystal.applyTransSymRot(
2353                                 nAtoms, lxk_local, lyk_local, lzk_local, lxk_local, lyk_local, lzk_local, symOp,
2354                                 rot_local);
2355                     }
2356                     // Sum symmetry mate gradients into asymmetric unit gradients
2357                     for (int j = 0; j < nAtoms; j++) {
2358                         lambdaGrad.add(threadID, j, lxk_local[j], lyk_local[j], lzk_local[j]);
2359                     }
2360                 }
2361             }
2362         }
2363 
2364         @Override
2365         public IntegerSchedule schedule() {
2366             return realSpaceSchedule;
2367         }
2368 
2369         @Override
2370         public void start() {
2371             init();
2372             threadID = getThreadIndex();
2373             realSpaceEnergyTime[threadID] -= System.nanoTime();
2374             permanentEnergy = 0.0;
2375             inducedEnergy = 0.0;
2376             count = 0;
2377             if (lambdaTerm) {
2378                 dUdL = 0.0;
2379                 d2UdL2 = 0.0;
2380             }
2381         }
2382 
2383         private void init() {
2384             int nAtoms = atoms.length;
2385             if (masking_local == null || masking_local.length < nAtoms) {
2386                 gxk_local = new double[nAtoms];
2387                 gyk_local = new double[nAtoms];
2388                 gzk_local = new double[nAtoms];
2389                 lxk_local = new double[nAtoms];
2390                 lyk_local = new double[nAtoms];
2391                 lzk_local = new double[nAtoms];
2392                 masking_local = new double[nAtoms];
2393                 fill(masking_local, 1.0);
2394             }
2395         }
2396 
2397         /**
2398          * Evaluate the real space permanent energy and polarization energy for a chunk of atoms.
2399          *
2400          * @param lb The lower bound of the chunk.
2401          * @param ub The upper bound of the chunk.
2402          */
2403         private void realSpaceChunk(final int lb, final int ub) {
2404             final double[] x = coordinates[0][0];
2405             final double[] y = coordinates[0][1];
2406             final double[] z = coordinates[0][2];
2407             final double[][] mpole = globalMultipole[0];
2408             final int[][] lists = realSpaceLists[iSymm];
2409             final double[] neighborX = coordinates[iSymm][0];
2410             final double[] neighborY = coordinates[iSymm][1];
2411             final double[] neighborZ = coordinates[iSymm][2];
2412             final double[][] neighborMultipole = globalMultipole[iSymm];
2413             for (i = lb; i <= ub; i++) {
2414                 if (!use[i]) {
2415                     continue;
2416                 }
2417                 final int moleculei = molecule[i];
2418                 if (iSymm == 0) {
2419                     applyMask(i, null, masking_local);
2420                 }
2421                 final double xi = x[i];
2422                 final double yi = y[i];
2423                 final double zi = z[i];
2424                 final double[] globalMultipolei = mpole[i];
2425                 setMultipoleI(globalMultipolei);
2426                 final boolean softi = isSoft[i];
2427                 final int[] list = lists[i];
2428                 final int npair = realSpaceCounts[iSymm][i];
2429                 for (int j = 0; j < npair; j++) {
2430                     k = list[j];
2431                     if (!use[k]) {
2432                         continue;
2433                     }
2434                     boolean sameMolecule = (moleculei == molecule[k]);
2435                     if (lambdaMode == LambdaMode.VAPOR) {
2436                         if ((intermolecularSoftcore && !sameMolecule)
2437                                 || (intramolecularSoftcore && sameMolecule)) {
2438                             continue;
2439                         }
2440                     }
2441                     selfScale = 1.0;
2442                     if (i == k) {
2443                         selfScale = 0.5;
2444                     }
2445                     double beta = 0.0;
2446                     l2 = 1.0;
2447                     soft = (softi || isSoft[k]);
2448                     if (soft && doPermanentRealSpace) {
2449                         beta = lAlpha;
2450                         l2 = permanentScale;
2451                     } else if (nnTerm) {
2452                         l2 = permanentScale;
2453                     }
2454                     final double xk = neighborX[k];
2455                     final double yk = neighborY[k];
2456                     final double zk = neighborZ[k];
2457                     dx_local[0] = xk - xi;
2458                     dx_local[1] = yk - yi;
2459                     dx_local[2] = zk - zi;
2460                     double r2 = crystal.image(dx_local);
2461                     xr = dx_local[0];
2462                     yr = dx_local[1];
2463                     zr = dx_local[2];
2464                     final double[] globalMultipolek = neighborMultipole[k];
2465                     setMultipoleK(globalMultipolek);
2466                     scale = masking_local[k];
2467                     double r = sqrt(r2 + beta);
2468                     double ralpha = aewald * r;
2469                     double exp2a = exp(-ralpha * ralpha);
2470                     rr1 = 1.0 / r;
2471                     double rr2 = rr1 * rr1;
2472                     bn0 = erfc(ralpha) * rr1;
2473                     bn1 = (bn0 + an0 * exp2a) * rr2;
2474                     bn2 = (3.0 * bn1 + an1 * exp2a) * rr2;
2475                     rr3 = rr1 * rr2;
2476                     rr5 = 3.0 * rr3 * rr2;
2477                     if (doPermanentRealSpace) {
2478                         double ei = permanentPair(gradient, lambdaTerm);
2479                         if (isNaN(ei) || isInfinite(ei)) {
2480                             String message = format(" %s\n %s\n %s\n The permanent multipole energy between "
2481                                             + "atoms %d and %d (%d) is %16.8f at %16.8f A.",
2482                                     crystal.getUnitCell().toString(), atoms[i].toString(), atoms[k].toString(),
2483                                     i, k, iSymm, ei, r);
2484                             throw new EnergyException(message);
2485                         }
2486                         if (ei != 0.0) {
2487                             permanentEnergy += ei;
2488                             count++;
2489                         }
2490                         if (esvTerm) {
2491                             if (extendedSystem.isTitrating(i)) {
2492                                 double titrdUdL = 0.0;
2493                                 double tautdUdL = 0.0;
2494                                 final double[][] titrMpole = titrationMultipole[0];
2495                                 final double[] titrMultipolei = titrMpole[i];
2496                                 setMultipoleI(titrMultipolei);
2497                                 titrdUdL = electric * permanentPair(false, false);
2498                                 if (extendedSystem.isTautomerizing(i)) {
2499                                     final double[][] tautMpole = tautomerMultipole[0];
2500                                     final double[] tautMultipolei = tautMpole[i];
2501                                     setMultipoleI(tautMultipolei);
2502                                     tautdUdL = electric * permanentPair(false, false);
2503                                 }
2504                                 extendedSystem.addPermElecDeriv(i, titrdUdL, tautdUdL);
2505                                 // Reset Multipoles between titration and tautomer ESVs
2506                                 setMultipoleI(globalMultipolei);
2507                             }
2508                             if (extendedSystem.isTitrating(k)) {
2509                                 double titrdUdL = 0.0;
2510                                 double tautdUdL = 0.0;
2511                                 final double[][] titrNeighborMpole = titrationMultipole[iSymm];
2512                                 final double[] titrMultipolek = titrNeighborMpole[k];
2513                                 setMultipoleK(titrMultipolek);
2514                                 titrdUdL = electric * permanentPair(false, false);
2515                                 if (extendedSystem.isTautomerizing(k)) {
2516                                     final double[][] tautNeighborMpole = tautomerMultipole[iSymm];
2517                                     final double[] tautMultipolek = tautNeighborMpole[k];
2518                                     setMultipoleK(tautMultipolek);
2519                                     tautdUdL = electric * permanentPair(false, false);
2520                                 }
2521                                 extendedSystem.addPermElecDeriv(k, titrdUdL, tautdUdL);
2522                                 // Reset Multipoles between titration and tautomer ESVs
2523                                 setMultipoleK(globalMultipolek);
2524                             }
2525                         }
2526                     }
2527                 }
2528                 if (iSymm == 0) {
2529                     removeMask(i, null, masking_local);
2530                 }
2531             }
2532         }
2533 
2534         /**
2535          * Evaluate the real space permanent energy for a pair of multipole sites.
2536          *
2537          * @return the permanent multipole energy.
2538          */
2539         private double permanentPair(boolean gradientLocal, boolean lambdaTermLocal) {
2540 
2541             // Calculate the gl functions for permanent multipoles.
2542             final double gl0 = ci * ck;
2543 
2544             // Compute the energy contributions for this interaction.
2545             final double scale1 = 1.0 - scale;
2546             final double ereal = gl0 * bn0;
2547             final double efix = scale1 * (gl0 * rr1);
2548             final double e = selfScale * l2 * (ereal - efix);
2549             if (gradientLocal) {
2550                 final double gf1 = bn1 * gl0;
2551                 // Get the permanent force with screening.
2552                 double ftm2x = gf1 * xr;
2553                 double ftm2y = gf1 * yr;
2554                 double ftm2z = gf1 * zr;
2555 
2556                 // Handle the case where scaling is used.
2557                 if (scale1 != 0.0) {
2558                     final double gfr1 = rr3 * gl0;
2559                     // Get the permanent force without screening.
2560                     final double ftm2rx = gfr1 * xr;
2561                     final double ftm2ry = gfr1 * yr;
2562                     final double ftm2rz = gfr1 * zr;
2563                     ftm2x -= scale1 * ftm2rx;
2564                     ftm2y -= scale1 * ftm2ry;
2565                     ftm2z -= scale1 * ftm2rz;
2566                 }
2567                 double prefactor = electric * selfScale * l2;
2568                 grad.add(threadID, i, prefactor * ftm2x, prefactor * ftm2y, prefactor * ftm2z);
2569                 gxk_local[k] -= prefactor * ftm2x;
2570                 gyk_local[k] -= prefactor * ftm2y;
2571                 gzk_local[k] -= prefactor * ftm2z;
2572 
2573                 // This is dU/dL/dX for the first term of dU/dL: d[dlPow * ereal]/dx
2574                 if (lambdaTerm && soft) {
2575                     prefactor = electric * selfScale * dEdLSign * dlPowPerm;
2576                     lambdaGrad.add(threadID, i, prefactor * ftm2x, prefactor * ftm2y, prefactor * ftm2z);
2577                     lxk_local[k] -= prefactor * ftm2x;
2578                     lyk_local[k] -= prefactor * ftm2y;
2579                     lzk_local[k] -= prefactor * ftm2z;
2580                 }
2581             }
2582 
2583             if (lambdaTermLocal && soft) {
2584                 double dRealdL = gl0 * bn1;
2585                 double d2RealdL2 = gl0 * bn2;
2586 
2587                 dUdL += selfScale * (dEdLSign * dlPowPerm * ereal + l2 * dlAlpha * dRealdL);
2588                 d2UdL2 += selfScale * (dEdLSign * (d2lPowPerm * ereal
2589                         + dlPowPerm * dlAlpha * dRealdL
2590                         + dlPowPerm * dlAlpha * dRealdL)
2591                         + l2 * d2lAlpha * dRealdL
2592                         + l2 * dlAlpha * dlAlpha * d2RealdL2);
2593 
2594                 double dFixdL = gl0 * rr3;
2595                 double d2FixdL2 = gl0 * rr5;
2596                 dFixdL *= scale1;
2597                 d2FixdL2 *= scale1;
2598                 dUdL -= selfScale * (dEdLSign * dlPowPerm * efix + l2 * dlAlpha * dFixdL);
2599                 d2UdL2 -= selfScale * (dEdLSign * (d2lPowPerm * efix
2600                         + dlPowPerm * dlAlpha * dFixdL
2601                         + dlPowPerm * dlAlpha * dFixdL)
2602                         + l2 * d2lAlpha * dFixdL
2603                         + l2 * dlAlpha * dlAlpha * d2FixdL2);
2604 
2605                 // Collect terms for dU/dL/dX for the second term of dU/dL: d[fL2*dfL1dL*dRealdL]/dX
2606                 final double gf1 = bn2 * gl0;
2607                 // Get the permanent force with screening.
2608                 double ftm2x = gf1 * xr;
2609                 double ftm2y = gf1 * yr;
2610                 double ftm2z = gf1 * zr;
2611 
2612                 // Handle the case where scaling is used.
2613                 if (scale1 != 0.0) {
2614                     final double gfr1 = rr5 * gl0;
2615                     // Get the permanent force without screening.
2616                     final double ftm2rx = gfr1 * xr;
2617                     final double ftm2ry = gfr1 * yr;
2618                     final double ftm2rz = gfr1 * zr;
2619                     ftm2x -= scale1 * ftm2rx;
2620                     ftm2y -= scale1 * ftm2ry;
2621                     ftm2z -= scale1 * ftm2rz;
2622                 }
2623 
2624                 // Add in dU/dL/dX for the second term of dU/dL: d[lPow*dlAlpha*dRealdL]/dX
2625                 double prefactor = electric * selfScale * l2 * dlAlpha;
2626                 lambdaGrad.add(threadID, i, prefactor * ftm2x, prefactor * ftm2y, prefactor * ftm2z);
2627                 lxk_local[k] -= prefactor * ftm2x;
2628                 lyk_local[k] -= prefactor * ftm2y;
2629                 lzk_local[k] -= prefactor * ftm2z;
2630             }
2631             return e;
2632         }
2633 
2634         private void setMultipoleI(double[] globalMultipolei) {
2635             ci = globalMultipolei[t000];
2636         }
2637 
2638         private void setMultipoleK(double[] globalMultipolek) {
2639             ck = globalMultipolek[t000];
2640         }
2641 
2642     }
2643 
2644 }