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.implicit;
39  
40  import static ffx.numerics.math.DoubleMath.X;
41  import static ffx.numerics.math.DoubleMath.dist;
42  import static ffx.numerics.math.DoubleMath.dist2;
43  import static ffx.numerics.math.DoubleMath.dot;
44  import static ffx.numerics.math.DoubleMath.length;
45  import static ffx.numerics.math.DoubleMath.normalize;
46  import static java.lang.String.format;
47  import static java.lang.System.arraycopy;
48  import static java.util.Arrays.fill;
49  import static org.apache.commons.math3.util.FastMath.PI;
50  import static org.apache.commons.math3.util.FastMath.abs;
51  import static org.apache.commons.math3.util.FastMath.acos;
52  import static org.apache.commons.math3.util.FastMath.asin;
53  import static org.apache.commons.math3.util.FastMath.atan2;
54  import static org.apache.commons.math3.util.FastMath.cos;
55  import static org.apache.commons.math3.util.FastMath.max;
56  import static org.apache.commons.math3.util.FastMath.min;
57  import static org.apache.commons.math3.util.FastMath.sin;
58  import static org.apache.commons.math3.util.FastMath.sqrt;
59  
60  import edu.rit.pj.IntegerForLoop;
61  import edu.rit.pj.ParallelRegion;
62  import edu.rit.pj.ParallelTeam;
63  import edu.rit.pj.reduction.SharedDouble;
64  import ffx.potential.bonded.Atom;
65  import ffx.potential.utils.EnergyException;
66  
67  import java.util.logging.Level;
68  import java.util.logging.Logger;
69  
70  /**
71   * ConnollyRegion uses the algorithms from the AMS/VAM programs of Michael Connolly to compute the
72   * analytical molecular surface area and volume of a collection of spherical atoms; thus it
73   * implements Fred Richards' molecular surface definition as a set of analytically defined spherical
74   * and toroidal polygons.
75   *
76   * <p>Numerical derivatives of the volume are available.
77   *
78   * <p>Literature references: M. L. Connolly, "Analytical Molecular Surface Calculation", Journal of
79   * Applied Crystallography, 16, 548-558 (1983)
80   *
81   * <p>M. L. Connolly, "Computation of Molecular Volume", Journal of the American Chemical Society,
82   * 107, 1118-1124 (1985)
83   *
84   * <p>C. E. Kundrot, J. W. Ponder and F. M. Richards, "Algorithms for Calculating Excluded Volume
85   * and Its Derivatives as a Function of Molecular Conformation and Their Use in Energy
86   * Minimization", Journal of Computational Chemistry, 12, 402-409 (1991)
87   *
88   * @author Guowei Qi
89   * @author Michael J. Schnieders
90   * @since 1.0
91   */
92  public class ConnollyRegion extends ParallelRegion {
93  
94    /**
95     * Default size of a vector to randomly perturb coordinates.
96     */
97    public static final double DEFAULT_WIGGLE = 1.0e-8;
98  
99    private static final Logger logger = Logger.getLogger(ConnollyRegion.class.getName());
100   /**
101    * 3D grid for finding atom neighbors.
102    */
103   private static final int MAXCUBE = 40;
104   /**
105    * maximum number of cycle convex edges.
106    */
107   private static final int MAXCYEP = 30;
108   /**
109    * maximum number of convex face cycles.
110    */
111   private static final int MAXFPCY = 10;
112   /**
113    * Number of atoms in the system.
114    */
115   private final int nAtoms;
116   /**
117    * Base atomic radii without the "exclude" buffer.
118    */
119   private final double[] baseRadius;
120   /**
121    * Atomic radii including the "exclude" buffer.
122    */
123   private final double[] radius;
124   /**
125    * If true, atom is not used.
126    */
127   private final boolean[] skip;
128   /**
129    * Array to store volume gradient
130    */
131   private final double[][] volumeGradient;
132 
133   private final int[] itab;
134   private final ParallelTeam parallelTeam;
135   private final VolumeLoop[] volumeLoop;
136   private final SharedDouble sharedVolume;
137   private final SharedDouble sharedArea;
138   /**
139    * Maximum number of saddle faces.
140    */
141   private final int maxSaddleFace;
142   /**
143    * maximum number of convex edges.
144    */
145   private final int maxConvexEdges;
146   /**
147    * maximum number of neighboring atom pairs.
148    */
149   private final int maxAtomPairs;
150   /**
151    * maximum number of circles.
152    */
153   private final int maxCircles;
154   /**
155    * maximum number of total tori.
156    */
157   private final int maxTori;
158   /**
159    * maximum number of temporary tori.
160    */
161   private final int maxTempTori;
162   /**
163    * maximum number of concave edges.
164    */
165   private final int maxConcaveEdges;
166   /**
167    * maximum number of vertices.
168    */
169   private final int maxVertices;
170   /**
171    * maximum number of probe positions.
172    */
173   private final int maxProbePositions;
174   /**
175    * maximum number of concave faces.
176    */
177   private final int maxConcaveFaces;
178   /**
179    * maximum number of convex faces.
180    */
181   private final int maxConvexFaces;
182   /**
183    * maximum number of cycles.
184    */
185   private final int maxCycles;
186   /**
187    * Copy of the atomic coordinates. [X,Y,Z][Atom Number]
188    */
189   private final double[][] atomCoords;
190 
191   private final double[] x, y, z;
192   /**
193    * If true, atom has no free surface.
194    */
195   private final boolean[] noFreeSurface;
196   /**
197    * Atom free of neighbors.
198    */
199   private final boolean[] atomFreeOfNeighbors;
200   /**
201    * Atom buried.
202    */
203   private final boolean[] atomBuried;
204   /**
205    * Begin and end pointers for atoms neighbors.
206    */
207   private final int[][] atomNeighborPointers;
208   /**
209    * Atom numbers of neighbors.
210    */
211   private final int[] neighborAtomNumbers;
212   /**
213    * Pointer from neighbor to torus.
214    */
215   private final int[] neighborToTorus;
216   /**
217    * Temporary torus atom numbers.
218    */
219   private final int[][] tempToriAtomNumbers;
220   /**
221    * First edge of each temporary torus.
222    */
223   private final int[] tempToriFirstEdge;
224   /**
225    * Last edge of each temporary torus.
226    */
227   private final int[] tempToriLastEdge;
228   /**
229    * Pointer to next edge of temporary torus.
230    */
231   private final int[] tempToriNextEdge;
232   /**
233    * Temporary torus buried.
234    */
235   private final boolean[] tempToriBuried;
236   /**
237    * Temporary torus free.
238    */
239   private final boolean[] tempToriFree;
240   /**
241    * Torus center.
242    */
243   private final double[][] torusCenter;
244   /**
245    * Torus radius.
246    */
247   private final double[] torusRadius;
248   /**
249    * Torus axis.
250    */
251   private final double[][] torusAxis;
252   /**
253    * Torus atom number.
254    */
255   private final int[][] torusAtomNumber;
256   /**
257    * Torus first edge.
258    */
259   private final int[] torusFirstEdge;
260   /**
261    * Torus free edge of neighbor.
262    */
263   private final boolean[] torusNeighborFreeEdge;
264   /**
265    * Probe position coordinates.
266    */
267   private final double[][] probeCoords;
268   /**
269    * Probe position atom numbers.
270    */
271   private final int[][] probeAtomNumbers;
272   /**
273    * Vertex coordinates.
274    */
275   private final double[][] vertexCoords;
276   /**
277    * Vertex atom number.
278    */
279   private final int[] vertexAtomNumbers;
280   /**
281    * Vertex probe number.
282    */
283   private final int[] vertexProbeNumber;
284   /**
285    * Vertex numbers for each concave edge.
286    */
287   private final int[][] concaveEdgeVertexNumbers;
288   /**
289    * Concave face concave edge numbers.
290    */
291   private final int[][] concaveFaceEdgeNumbers;
292   /**
293    * Circle center.
294    */
295   private final double[][] circleCenter;
296   /**
297    * Circle radius.
298    */
299   private final double[] circleRadius;
300   /**
301    * Circle atom number.
302    */
303   private final int[] circleAtomNumber;
304   /**
305    * Circle torus number.
306    */
307   private final int[] circleTorusNumber;
308   /**
309    * Convex edge circle number.
310    */
311   private final int[] convexEdgeCircleNumber;
312   /**
313    * Convex edge vertex number.
314    */
315   private final int[][] convexEdgeVertexNumber;
316   /**
317    * First convex edge of each atom.
318    */
319   private final int[] convexEdgeFirst;
320   /**
321    * Last convex edge of each atom.
322    */
323   private final int[] convexEdgeLast;
324   /**
325    * Pointer to next convex edge of atom.
326    */
327   private final int[] convexEdgeNext;
328   /**
329    * Saddle face concave edge numbers.
330    */
331   private final int[][] saddleConcaveEdgeNumbers;
332   /**
333    * Saddle face convex edge numbers.
334    */
335   private final int[][] saddleConvexEdgeNumbers;
336   /**
337    * Number of convex edges in cycle.
338    */
339   private final int[] convexEdgeCycleNum;
340   /**
341    * Cycle convex edge numbers.
342    */
343   private final int[][] convexEdgeCycleNumbers;
344   /**
345    * Atom number of convex face.
346    */
347   private final int[] convexFaceAtomNumber;
348   /**
349    * Convex face cycle numbers
350    */
351   private final int[][] convexFaceCycleNumbers;
352   /**
353    * Number of cycles bounding convex face.
354    */
355   private final int[] convexFaceNumCycles;
356   /**
357    * True if cube contains active atoms.
358    */
359   private final boolean[] activeCube;
360   /**
361    * True if cube or adjacent cubes have active atoms.
362    */
363   private final boolean[] activeAdjacentCube;
364   /**
365    * Pointer to first atom in list for cube.
366    */
367   private final int[] firstAtomPointer;
368   /**
369    * Integer cube coordinates.
370    */
371   private final int[][] cubeCoordinates;
372   /**
373    * Pointer to next atom in cube.
374    */
375   private final int[] nextAtomPointer;
376   /**
377    * Array of atoms.
378    */
379   private Atom[] atoms;
380   /**
381    * If true, compute the gradient
382    */
383   private boolean gradient = false;
384   /**
385    * Probe is used for molecular (contact/reentrant) volume and surface area.
386    */
387   private double probe = 0.0;
388   /**
389    * Exclude is used for excluded volume and accessible surface area.
390    */
391   private double exclude = 1.4;
392   /**
393    * Size of a vector to randomly perturb coordinates.
394    */
395   private double wiggle = DEFAULT_WIGGLE;
396   /**
397    * Number of temporary tori.
398    */
399   private int nTempTori;
400   /**
401    * Number of tori.
402    */
403   private int nTori;
404   /**
405    * Number of probe positions.
406    */
407   private int nProbePositions;
408   /**
409    * Number of concave faces.
410    */
411   private int nConcaveFaces;
412   /**
413    * Number of vertices.
414    */
415   private int nConcaveVerts;
416   /**
417    * Number of concave edges.
418    */
419   private int nConcaveEdges;
420 
421   // These are from the "nearby" method.
422   /**
423    * Number of circles.
424    */
425   private int nCircles;
426   /**
427    * Number of convex edges.
428    */
429   private int nConvexEdges;
430   /**
431    * Number of saddle faces.
432    */
433   private int nSaddleFaces;
434   /**
435    * Number of cycles.
436    */
437   private int nCycles;
438   /**
439    * Number of convex faces.
440    */
441   private int nConvexFaces;
442 
443   /**
444    * ConnollyRegion constructor.
445    *
446    * @param atoms      Array of atom instances.
447    * @param baseRadius Base radius for each atom (no added probe).
448    * @param nThreads   Number of threads.
449    */
450   public ConnollyRegion(Atom[] atoms, double[] baseRadius, int nThreads) {
451     this.atoms = atoms;
452     this.nAtoms = atoms.length;
453     this.baseRadius = baseRadius;
454 
455     // Parallelization variables.
456     parallelTeam = new ParallelTeam(1);
457     volumeLoop = new VolumeLoop[nThreads];
458     for (int i = 0; i < nThreads; i++) {
459       volumeLoop[i] = new VolumeLoop();
460     }
461     sharedVolume = new SharedDouble();
462     sharedArea = new SharedDouble();
463 
464     // Atom variables.
465     radius = new double[nAtoms];
466     skip = new boolean[nAtoms];
467     atomCoords = new double[3][nAtoms];
468     x = atomCoords[0];
469     y = atomCoords[1];
470     z = atomCoords[2];
471     noFreeSurface = new boolean[nAtoms];
472     atomFreeOfNeighbors = new boolean[nAtoms];
473     atomBuried = new boolean[nAtoms];
474     atomNeighborPointers = new int[2][nAtoms];
475 
476     // Atom pair variables.
477     maxAtomPairs = 240 * nAtoms;
478     neighborAtomNumbers = new int[maxAtomPairs];
479     neighborToTorus = new int[maxAtomPairs];
480 
481     // Temporary Torus variables.
482     maxTempTori = 120 * nAtoms;
483     maxConcaveEdges = 12 * nAtoms;
484     tempToriAtomNumbers = new int[2][maxTempTori];
485     tempToriFirstEdge = new int[maxTempTori];
486     tempToriLastEdge = new int[maxTempTori];
487     tempToriBuried = new boolean[maxTempTori];
488     tempToriFree = new boolean[maxTempTori];
489     tempToriNextEdge = new int[maxConcaveEdges];
490 
491     // Torus variables.
492     maxTori = 4 * nAtoms;
493     torusCenter = new double[3][maxTori];
494     torusRadius = new double[maxTori];
495     torusAxis = new double[3][maxTori];
496     torusAtomNumber = new int[2][maxTori];
497     torusFirstEdge = new int[maxTori];
498     torusNeighborFreeEdge = new boolean[maxTori];
499 
500     // Probe variables.
501     maxProbePositions = 4 * nAtoms;
502     probeCoords = new double[3][maxProbePositions];
503     probeAtomNumbers = new int[3][maxProbePositions];
504 
505     // Vertex variables.
506     maxVertices = 12 * nAtoms;
507     vertexCoords = new double[3][maxVertices];
508     vertexAtomNumbers = new int[maxVertices];
509     vertexProbeNumber = new int[maxVertices];
510 
511     // Concave face variables.
512     maxConcaveFaces = 4 * nAtoms;
513     concaveFaceEdgeNumbers = new int[3][maxConcaveFaces];
514     concaveEdgeVertexNumbers = new int[2][maxConcaveEdges];
515 
516     // Circle variables.
517     maxCircles = 8 * nAtoms;
518     circleCenter = new double[3][maxCircles];
519     circleRadius = new double[maxCircles];
520     circleAtomNumber = new int[maxCircles];
521     circleTorusNumber = new int[maxCircles];
522 
523     // Convex face variables.
524     maxConvexEdges = 12 * nAtoms;
525     maxConvexFaces = 2 * nAtoms;
526     maxCycles = 3 * nAtoms;
527     convexEdgeCircleNumber = new int[maxConvexEdges];
528     convexEdgeVertexNumber = new int[2][maxConvexEdges];
529     convexEdgeFirst = new int[nAtoms];
530     convexEdgeLast = new int[nAtoms];
531     convexEdgeNext = new int[maxConvexEdges];
532     convexEdgeCycleNum = new int[maxCycles];
533     convexEdgeCycleNumbers = new int[MAXCYEP][maxCycles];
534     convexFaceAtomNumber = new int[maxConvexFaces];
535     convexFaceNumCycles = new int[maxConvexFaces];
536     convexFaceCycleNumbers = new int[MAXFPCY][maxConvexFaces];
537 
538     // Saddle face variables.
539     maxSaddleFace = 6 * nAtoms;
540     saddleConcaveEdgeNumbers = new int[2][maxSaddleFace];
541     saddleConvexEdgeNumbers = new int[2][maxSaddleFace];
542 
543     // Domain decomposition
544     activeCube = new boolean[MAXCUBE * MAXCUBE * MAXCUBE];
545     activeAdjacentCube = new boolean[MAXCUBE * MAXCUBE * MAXCUBE];
546     firstAtomPointer = new int[MAXCUBE * MAXCUBE * MAXCUBE];
547     cubeCoordinates = new int[3][nAtoms];
548     nextAtomPointer = new int[nAtoms];
549 
550     // Volume derivative variables.
551     volumeGradient = new double[3][nAtoms];
552     itab = new int[nAtoms];
553   }
554 
555   public double getExclude() {
556     return exclude;
557   }
558 
559   public void setExclude(double exclude) {
560     this.exclude = exclude;
561   }
562 
563   public double getProbe() {
564     return probe;
565   }
566 
567   public void setProbe(double probe) {
568     this.probe = probe;
569   }
570 
571   public double getSurfaceArea() {
572     return sharedArea.get();
573   }
574 
575   public double getVolume() {
576     return sharedVolume.get();
577   }
578 
579   public double[][] getVolumeGradient() {
580     return volumeGradient;
581   }
582 
583   /**
584    * Initialize this VolumeRegion instance for an energy evaluation.
585    *
586    * <p>Currently the number of atoms cannot change.
587    *
588    * @param atoms    Array of atoms.
589    * @param gradient Compute the atomic coordinate gradient.
590    */
591   public void init(Atom[] atoms, boolean gradient) {
592     this.atoms = atoms;
593     this.gradient = gradient;
594   }
595 
596   @Override
597   public void run() {
598     try {
599       execute(0, nAtoms - 1, volumeLoop[getThreadIndex()]);
600     } catch (Exception e) {
601       String message =
602           "Fatal exception computing Volume energy in thread " + getThreadIndex() + "\n";
603       logger.log(Level.SEVERE, message, e);
604     }
605   }
606 
607   /**
608    * Execute the VolumeRegion with a private, single threaded ParallelTeam.
609    */
610   public void runVolume() {
611     try {
612       parallelTeam.execute(this);
613     } catch (Exception e) {
614       String message = " Fatal exception computing the Connolly surface area and volume.";
615       logger.log(Level.SEVERE, message, e);
616     }
617   }
618 
619   /**
620    * Apply a random perturbation to the atomic coordinates to avoid numerical instabilities for
621    * various linear, planar and symmetric structures.
622    *
623    * @param wiggle Size of the random vector move in Angstroms.
624    */
625   public void setWiggle(double wiggle) {
626     this.wiggle = wiggle;
627   }
628 
629   @Override
630   public void start() {
631     sharedVolume.set(0.0);
632     sharedArea.set(0.0);
633     double[] vector = new double[3];
634     for (int i = 0; i < nAtoms; i++) {
635       getRandomVector(vector);
636       Atom atom = atoms[i];
637       atomCoords[0][i] = atom.getX() + (wiggle * vector[0]);
638       atomCoords[1][i] = atom.getY() + (wiggle * vector[1]);
639       atomCoords[2][i] = atom.getZ() + (wiggle * vector[2]);
640     }
641   }
642 
643   /**
644    * Construct the 3-dimensional random unit vector.
645    *
646    * @param vector The generated vector.
647    */
648   private void getRandomVector(double[] vector) {
649     double x, y, s;
650     x = 0;
651     y = 0;
652 
653     // Get a pair of appropriate components in the plane.
654     s = 2.0;
655     while (s >= 1.0) {
656       x = (2.0 * Math.random()) - 1.0;
657       y = (2.0 * Math.random()) - 1.0;
658       s = (x * x) + (y * y);
659     }
660 
661     // Construct the 3-dimensional random unit vector.
662     vector[2] = 1.0 - 2.0 * s;
663     s = 2.0 * sqrt(1.0 - s);
664     vector[1] = s * y;
665     vector[0] = s * x;
666   }
667 
668   /**
669    * Compute Volume energy for a range of atoms.
670    *
671    * @since 1.0
672    */
673   private class VolumeLoop extends IntegerForLoop {
674     /**
675      * 3D grid for numeric volume derivatives.
676      */
677     private static final int MXCUBE = 30;
678     /**
679      * Maximum arcs for volume derivatives.
680      */
681     private static final int MAXARC = 1000;
682     /**
683      * Used by the place method to find probe sites.
684      */
685     private static final int MAXMNB = 500;
686 
687     private double localVolume;
688     private double localSurfaceArea;
689 
690     @Override
691     public void finish() {
692       sharedVolume.addAndGet(localVolume);
693       sharedArea.addAndGet(localSurfaceArea);
694     }
695 
696     @Override
697     public void run(int lb, int ub) {
698       setRadius();
699       computeVolumeAndArea();
700       if (gradient) {
701         computeVolumeGradient();
702       }
703     }
704 
705     @Override
706     public void start() {
707       localVolume = 0.0;
708       localSurfaceArea = 0.0;
709       if (gradient) {
710         fill(volumeGradient[0], 0.0);
711         fill(volumeGradient[1], 0.0);
712         fill(volumeGradient[2], 0.0);
713       }
714     }
715 
716     /**
717      * Assign van der Waals radii to the atoms; note that the radii are incremented by the size of
718      * the probe.
719      */
720     private void setRadius() {
721       for (int i = 0; i < nAtoms; i++) {
722         if (baseRadius[i] == 0.0) {
723           radius[i] = 0.0;
724           skip[i] = true;
725         } else {
726           skip[i] = false;
727           radius[i] = baseRadius[i] + exclude;
728         }
729       }
730     }
731 
732     /**
733      * Find the analytical volume and surface area.
734      */
735     private void computeVolumeAndArea() {
736       nearby();
737       torus();
738       place();
739       compress();
740       saddles();
741       contact();
742       vam();
743     }
744 
745     /**
746      * The getTorus method tests for a possible torus position at the interface between two atoms,
747      * and finds the torus radius, center and axis.
748      *
749      * @param ia          First atom.
750      * @param ja          Second atom.
751      * @param torusCenter Torus center.
752      * @param torusRadius Torus radius.
753      * @param torusAxis   Torus axis.
754      * @return True if a torus is found.
755      */
756     private boolean getTorus(
757         int ia, int ja, double[] torusCenter, double[] torusRadius, double[] torusAxis) {
758       boolean foundTorus = false;
759 
760       // Get the distance between the two atoms.
761       double[] ai = new double[3];
762       double[] aj = new double[3];
763       getVector(ai, atomCoords, ia);
764       getVector(aj, atomCoords, ja);
765       double dij = dist(ai, aj);
766 
767       // Find a unit vector along interatomic (torus) axis.
768       double[] vij = new double[3];
769       double[] uij = new double[3];
770       for (int k = 0; k < 3; k++) {
771         vij[k] = atomCoords[k][ja] - atomCoords[k][ia];
772         uij[k] = vij[k] / dij;
773       }
774 
775       // Find coordinates of the center of the torus.
776       double temp =
777           1.0
778               + ((radius[ia] + probe) * (radius[ia] + probe)
779               - (radius[ja] + probe) * (radius[ja] + probe))
780               / (dij * dij);
781       double[] bij = new double[3];
782       for (int k = 0; k < 3; k++) {
783         bij[k] = atomCoords[k][ia] + 0.5 * vij[k] * temp;
784       }
785 
786       // Skip if atoms too far apart (should not happen).
787       double temp1 =
788           (radius[ia] + radius[ja] + 2.0 * probe) * (radius[ia] + radius[ja] + 2.0 * probe)
789               - dij * dij;
790       if (temp1 >= 0.0) {
791 
792         // Skip if one atom is inside the other.
793         double temp2 = dij * dij - (radius[ia] - radius[ja]) * (radius[ia] - radius[ja]);
794         if (temp2 >= 0.0) {
795 
796           // Store the torus radius, center, and axis.
797           foundTorus = true;
798           torusRadius[0] = sqrt(temp1 * temp2) / (2.0 * dij);
799           for (int k = 0; k < 3; k++) {
800             torusCenter[k] = bij[k];
801             torusAxis[k] = uij[k];
802           }
803         }
804       }
805       return foundTorus;
806     }
807 
808     /**
809      * The nearby method finds all of the through-space neighbors of each atom for use in surface
810      * area and volume calculations.
811      */
812     private void nearby() {
813       int maxclsa = 1000;
814       int[] clsa = new int[maxclsa];
815       // Temporary neighbor list, before sorting.
816       int[] tempNeighborList = new int[maxclsa];
817       // Minimum atomic coordinates (cube corner).
818       double[] minAtomicCoordinates = new double[3];
819       double[] ai = new double[3];
820       double[] aj = new double[3];
821 
822       /*
823        * Ignore all atoms that are completely inside another atom;
824        * may give nonsense results if this step is not taken.
825        */
826       for (int i = 0; i < nAtoms - 1; i++) {
827         if (!skip[i]) {
828           getVector(ai, atomCoords, i);
829           for (int j = i + 1; j < nAtoms; j++) {
830             getVector(aj, atomCoords, j);
831             double d2 = dist2(ai, aj);
832             double r2 = (radius[i] - radius[j]) * (radius[i] - radius[j]);
833             if (!skip[j] && d2 < r2) {
834               if (radius[i] < radius[j]) {
835                 skip[i] = true;
836               } else {
837                 skip[j] = true;
838               }
839             }
840           }
841         }
842       }
843 
844       // Check for new coordinate minima and radii maxima.
845       double radmax = 0.0;
846       for (int k = 0; k < 3; k++) {
847         minAtomicCoordinates[k] = atomCoords[k][0];
848       }
849       for (int i = 0; i < nAtoms; i++) {
850         for (int k = 0; k < 3; k++) {
851           if (atomCoords[k][i] < minAtomicCoordinates[k]) {
852             minAtomicCoordinates[k] = atomCoords[k][i];
853           }
854         }
855         if (radius[i] > radmax) {
856           radmax = radius[i];
857         }
858       }
859 
860       // Calculate width of cube from maximum atom radius and probe radius.
861       double width = 2.0 * (radmax + probe);
862 
863       // Set up cube arrays; first the integer coordinate arrays.
864       for (int i = 0; i < nAtoms; i++) {
865         for (int k = 0; k < 3; k++) {
866           cubeCoordinates[k][i] = (int) ((atomCoords[k][i] - minAtomicCoordinates[k]) / width);
867           if (cubeCoordinates[k][i] < 0) {
868             throw new EnergyException(" Cube Coordinate Too Small");
869           } else if (cubeCoordinates[k][i] > MAXCUBE) {
870             throw new EnergyException(" Cube Coordinate Too Large");
871           }
872         }
873       }
874 
875       // Initialize head pointer and srn=2 arrays.
876       fill(firstAtomPointer, -1);
877       fill(activeCube, false);
878       fill(activeAdjacentCube, false);
879 
880       // Initialize linked list pointers.
881       fill(nextAtomPointer, -1);
882 
883       // Set up head and later pointers for each atom.
884       atomLoop:
885       for (int iatom = 0; iatom < nAtoms; iatom++) {
886         // Skip atoms with surface request numbers of zero.
887         if (skip[iatom]) {
888           continue;
889         }
890 
891         getVector(ai, atomCoords, iatom);
892         int i = cubeCoordinates[0][iatom];
893         int j = cubeCoordinates[1][iatom];
894         int k = cubeCoordinates[2][iatom];
895 
896         if (firstAtomPointer[index(i, j, k)] <= -1) {
897           // First atom in this cube.
898           firstAtomPointer[index(i, j, k)] = iatom;
899         } else {
900           // Add to end of linked list.
901           int iptr = firstAtomPointer[index(i, j, k)];
902           boolean done = false;
903           while (!done) {
904             getVector(aj, atomCoords, iptr);
905             // Check for duplicate atoms, turn off one of them.
906             if (dist2(ai, aj) <= 0.0) {
907               skip[iatom] = true;
908               continue atomLoop;
909             }
910             // Move on down the list.
911             if (nextAtomPointer[iptr] <= -1.0) {
912               done = true;
913             } else {
914               iptr = nextAtomPointer[iptr];
915             }
916           }
917           // Store atom number.
918           nextAtomPointer[iptr] = iatom;
919         }
920 
921         // Check for surfaced atom.
922         if (!skip[iatom]) {
923           activeCube[index(i, j, k)] = true;
924         }
925       }
926 
927       // Check if this cube or any adjacent cube has active atoms.
928       for (int k = 0; k < MAXCUBE; k++) {
929         for (int j = 0; j < MAXCUBE; j++) {
930           for (int i = 0; i < MAXCUBE; i++) {
931             if (firstAtomPointer[index(i, j, k)] != -1) {
932               checkCube(i, j, k);
933             }
934           }
935         }
936       }
937 
938       int ncls = -1;
939 
940       // Zero pointers for atom and find its cube.
941       for (int i = 0; i < nAtoms; i++) {
942         int nclsa = -1;
943         noFreeSurface[i] = skip[i];
944         atomNeighborPointers[0][i] = -1;
945         atomNeighborPointers[1][i] = -1;
946         if (skip[i]) {
947           continue;
948         }
949         int ici = cubeCoordinates[0][i];
950         int icj = cubeCoordinates[1][i];
951         int ick = cubeCoordinates[2][i];
952 
953         // Skip iatom if its cube and adjoining cubes contain only blockers.
954         if (!activeAdjacentCube[index(ici, icj, ick)]) {
955           continue;
956         }
957         double sumi = 2.0 * probe + radius[i];
958 
959         // Check iatom cube and adjacent cubes for neighboring atoms.
960         for (int jck = max(ick - 1, 0); jck < min(ick + 2, MAXCUBE); jck++) {
961           for (int jcj = max(icj - 1, 0); jcj < min(icj + 2, MAXCUBE); jcj++) {
962             // TODO: Try to remove this labeled for loop.
963             for4:
964             for (int jci = max(ici - 1, 0); jci < min(ici + 2, MAXCUBE); jci++) {
965               int j = firstAtomPointer[index(jci, jcj, jck)];
966               int counter = 1;
967               for (int q = 0; q < counter; q++) {
968                 for (int z = 0; z < 1; z++) {
969                   // Check for end of linked list for this cube.
970                   if (j <= -1) {
971                     continue for4;
972                   } else if (i == j) {
973                     continue;
974                   } else if (skip[j]) {
975                     continue;
976                   }
977 
978                   // Distance check.
979                   double sum = sumi + radius[j];
980                   double vect1 = abs(atomCoords[0][j] - atomCoords[0][i]);
981                   if (vect1 >= sum) {
982                     continue;
983                   }
984                   double vect2 = abs(atomCoords[1][j] - atomCoords[1][i]);
985                   if (vect2 >= sum) {
986                     continue;
987                   }
988                   double vect3 = abs(atomCoords[2][j] - atomCoords[2][i]);
989                   if (vect3 >= sum) {
990                     continue;
991                   }
992                   double d2 = (vect1 * vect1) + (vect2 * vect2) + (vect3 * vect3);
993                   if (d2 >= sum * sum) {
994                     continue;
995                   }
996 
997                   // Atoms are neighbors, save atom number in temporary array.
998                   if (!skip[j]) {
999                     noFreeSurface[i] = false;
1000                   }
1001                   nclsa++;
1002                   if (nclsa >= maxclsa) {
1003                     throw new EnergyException(" Too many Neighbors for Atom");
1004                   }
1005                   tempNeighborList[nclsa] = j;
1006                 }
1007 
1008                 // Get number of next atom in cube.
1009                 j = nextAtomPointer[j];
1010                 counter++;
1011               }
1012             }
1013           }
1014         }
1015 
1016         if (noFreeSurface[i]) {
1017           continue;
1018         }
1019 
1020         // Set up neighbors arrays with jatom in increasing order.
1021         int jmold = -1;
1022         for (int juse = 0; juse <= nclsa; juse++) {
1023           int jmin = nAtoms;
1024           int jmincls = 0;
1025           for (int jcls = 0; jcls < nclsa + 1; jcls++) {
1026             // Don't use ones already sorted.
1027             if (tempNeighborList[jcls] > jmold) {
1028               if (tempNeighborList[jcls] < jmin) {
1029                 jmin = tempNeighborList[jcls];
1030                 jmincls = jcls;
1031               }
1032             }
1033           }
1034           jmold = jmin;
1035           int jcls = jmincls;
1036           int jatom = tempNeighborList[jcls];
1037           clsa[juse] = jatom;
1038         }
1039 
1040         // Set up pointers to first and last neighbors of atom.
1041         if (nclsa > -1) {
1042           atomNeighborPointers[0][i] = ncls + 1;
1043           for (int m = 0; m < nclsa + 1; m++) {
1044             ncls++;
1045             if (ncls >= maxAtomPairs) {
1046               throw new EnergyException(" Too many Neighboring Atom Pairs");
1047             }
1048             neighborAtomNumbers[ncls] = clsa[m];
1049           }
1050           atomNeighborPointers[1][i] = ncls;
1051         }
1052       }
1053     }
1054 
1055     /**
1056      * Row major indexing (the last dimension is contiguous in memory).
1057      *
1058      * @param i x index.
1059      * @param j y index.
1060      * @param k z index.
1061      * @return the row major index.
1062      */
1063     private int index(int i, int j, int k) {
1064       return k + MAXCUBE * (j + MAXCUBE * i);
1065     }
1066 
1067     /**
1068      * Check if this cube or any adjacent cube has active atoms.
1069      *
1070      * @param i X-index for activeAdjacentCube.
1071      * @param j Y-index for activeAdjacentCube.
1072      * @param k Z-index for activeAdjacentCube.
1073      */
1074     private void checkCube(int i, int j, int k) {
1075       for (int k1 = max(k - 1, 0); k1 < min(k + 2, MAXCUBE); k1++) {
1076         for (int j1 = max(j - 1, 0); j1 < min(j + 2, MAXCUBE); j1++) {
1077           for (int i1 = max(i - 1, 0); i1 < min(i + 2, MAXCUBE); i1++) {
1078             if (activeCube[index(i1, j1, k1)]) {
1079               activeAdjacentCube[index(i, j, k)] = true;
1080               return;
1081             }
1082           }
1083         }
1084       }
1085     }
1086 
1087     /**
1088      * The torus method sets a list of all of the temporary torus positions by testing for a torus
1089      * between each atom and its neighbors
1090      */
1091     private void torus() {
1092       // No torus is possible if there is only one atom.
1093       nTempTori = -1;
1094       fill(atomFreeOfNeighbors, true);
1095       if (nAtoms <= 1) {
1096         return;
1097       }
1098 
1099       double[] tt = new double[3];
1100       double[] ttax = new double[3];
1101       // Get beginning and end pointers to neighbors of this atom.
1102       for (int ia = 0; ia < nAtoms; ia++) {
1103         if (!noFreeSurface[ia]) {
1104           int ibeg = atomNeighborPointers[0][ia];
1105           int iend = atomNeighborPointers[1][ia];
1106           if (ibeg > -1) {
1107             // Check for no neighbors.
1108             for (int jn = ibeg; jn <= iend; jn++) {
1109               // Clear pointer from neighbor to torus.
1110               neighborToTorus[jn] = -1;
1111               // Get atom number of neighbor.
1112               int ja = neighborAtomNumbers[jn];
1113               // Don't create torus twice.
1114               if (ja >= ia) {
1115                 // Do some solid geometry.
1116                 double[] ttr = {0.0};
1117                 boolean ttok = getTorus(ia, ja, tt, ttr, ttax);
1118                 if (ttok) {
1119                   // We have temporary torus; set up variables.
1120                   nTempTori++;
1121                   if (nTempTori >= maxTempTori) {
1122                     throw new EnergyException(" Too many Temporary Tori");
1123                   }
1124                   // Mark both atoms not free.
1125                   atomFreeOfNeighbors[ia] = false;
1126                   atomFreeOfNeighbors[ja] = false;
1127                   tempToriAtomNumbers[0][nTempTori] = ia;
1128                   tempToriAtomNumbers[1][nTempTori] = ja;
1129                   // Pointer from neighbor to torus.
1130                   neighborToTorus[jn] = nTempTori;
1131                   // Initialize torus as both free and buried.
1132                   tempToriFree[nTempTori] = true;
1133                   tempToriBuried[nTempTori] = true;
1134                   // Clear pointers from torus to first and last concave edges.
1135                   tempToriFirstEdge[nTempTori] = -1;
1136                   tempToriLastEdge[nTempTori] = -1;
1137                 }
1138               }
1139             }
1140           }
1141         }
1142       }
1143     }
1144 
1145     /**
1146      * The place method finds the probe sites by putting the probe sphere tangent to each triple of
1147      * neighboring atoms.
1148      */
1149     private void place() {
1150       int[] mnb = new int[MAXMNB];
1151       int[] ikt = new int[MAXMNB];
1152       int[] jkt = new int[MAXMNB];
1153       int[] lkcls = new int[MAXMNB];
1154       double[] tik = new double[3];
1155       double[] tij = new double[3];
1156       double[] uij = new double[3];
1157       double[] uik = new double[3];
1158       double[] uijk = new double[3];
1159       double[] bij = new double[3];
1160       double[] bijk = new double[3];
1161       double[] aijk = new double[3];
1162       double[] pijk = new double[3];
1163       double[] tijik = new double[3];
1164       double[] tempv = new double[3];
1165       double[] utb = new double[3];
1166       double[] ai = new double[3];
1167       double[] ak = new double[3];
1168       double[] discls = new double[MAXMNB];
1169       double[] sumcls = new double[MAXMNB];
1170       boolean tb, tok, prbok;
1171 
1172       nProbePositions = -1;
1173       nConcaveFaces = -1;
1174       nConcaveEdges = -1;
1175       nConcaveVerts = -1;
1176 
1177       // No possible placement if there are no temporary tori.
1178       if (nTempTori <= -1) {
1179         return;
1180       }
1181 
1182       // Consider each torus in turn.
1183       for (int itt = 0; itt < nTempTori + 1; itt++) {
1184         // Get atom numbers.
1185         int ia = tempToriAtomNumbers[0][itt];
1186         int ja = tempToriAtomNumbers[1][itt];
1187 
1188         // Form mutual neighbor list; clear number of mutual neighbors of atoms ia and ja.
1189         int nmnb = -1;
1190 
1191         // Get beginning and end pointers for each atom's neighbor list.
1192         int iptr = atomNeighborPointers[0][ia];
1193         int jptr = atomNeighborPointers[0][ja];
1194 
1195         // For loops like this help replace go to statements from the Tinker code.
1196         outerloop:
1197         for (int i = 0; i < 1; i++) {
1198           if (iptr <= -1 || jptr <= -1) {
1199             continue;
1200           }
1201           int iend = atomNeighborPointers[1][ia];
1202           int jend = atomNeighborPointers[1][ja];
1203 
1204           // Collect mutual neighbors.
1205           int counter = 1;
1206           int counter2 = 1;
1207           // TODO: Try to turn this into a while loop.
1208           for (int t = 0; t < counter2; t++) {
1209             // TODO: Try to turn this into a while loop.
1210             for (int q = 0; q < counter; q++) {
1211               // Check for end of loop.
1212               if (iptr > iend || jptr > jend) {
1213                 continue outerloop;
1214               }
1215               // Go move the lagging pointer.
1216               if (neighborAtomNumbers[iptr] < neighborAtomNumbers[jptr]) {
1217                 iptr++;
1218                 counter++;
1219                 continue;
1220               }
1221               if (neighborAtomNumbers[jptr] < neighborAtomNumbers[iptr]) {
1222                 jptr++;
1223                 counter++;
1224               }
1225             }
1226 
1227             /*
1228              Both point at same neighbor; one more mutual
1229              neighbor save atom number of mutual neighbor.
1230             */
1231             nmnb++;
1232             if (nmnb >= MAXMNB) {
1233               throw new EnergyException(" Too many Mutual Neighbors");
1234             }
1235             mnb[nmnb] = neighborAtomNumbers[iptr];
1236 
1237             // Save pointers to second and third tori.
1238             ikt[nmnb] = neighborToTorus[iptr];
1239             jkt[nmnb] = neighborToTorus[jptr];
1240             iptr++;
1241             counter2++;
1242           }
1243         }
1244         // We have all the mutual neighbors of ia and ja if no mutual neighbors, skip to end of
1245         // loop.
1246         if (nmnb == -1) {
1247           tempToriBuried[itt] = false;
1248           continue;
1249         }
1250         double[] hij = {0.0};
1251         boolean ttok = getTorus(ia, ja, bij, hij, uij);
1252         for (int km = 0; km < nmnb + 1; km++) {
1253           int ka = mnb[km];
1254           getVector(ak, atomCoords, ka);
1255           discls[km] = dist2(bij, ak);
1256           sumcls[km] = (probe + radius[ka]) * (probe + radius[ka]);
1257           // Initialize link to next farthest out neighbor.
1258           lkcls[km] = -1;
1259         }
1260 
1261         // Set up a linked list of neighbors in order of increasing distance from ia-ja torus
1262         // center.
1263         int lkf = 0;
1264         for (int z = 0; z < 1; z++) {
1265           if (nmnb == 0) {
1266             continue;
1267           }
1268           // Put remaining neighbors in linked list at proper position.
1269           int l;
1270           for (l = 1; l < nmnb + 1; l++) {
1271 
1272             int l1 = -1;
1273             int l2 = lkf;
1274             int counter2 = 1;
1275             for (int w = 0; w < counter2; w++) {
1276               if (discls[l] < discls[l2]) {
1277                 continue;
1278               }
1279               l1 = l2;
1280               l2 = lkcls[l2];
1281               if (l2 != -1) {
1282                 counter2++;
1283               }
1284             }
1285 
1286             // Add to list.
1287             if (l1 == -1) {
1288               lkf = l;
1289             } else {
1290               lkcls[l1] = l;
1291             }
1292             lkcls[l] = l2;
1293           }
1294         }
1295         // Loop through mutual neighbors.
1296         for (int km = 0; km < nmnb + 1; km++) {
1297 
1298           // Get atom number of neighbors.
1299           int ka = mnb[km];
1300           if (skip[ia] && skip[ja] && skip[ka]) {
1301             continue;
1302           }
1303 
1304           // Get tori numbers for neighbor.
1305           int ik = ikt[km];
1306           int jk = jkt[km];
1307 
1308           // Possible new triple, do some geometry to retrieve saddle center, axis and radius.
1309           prbok = false;
1310           tb = false;
1311           double[] rij = {0.0};
1312           double hijk = 0.0;
1313           tok = getTorus(ia, ja, tij, rij, uij);
1314           for (int w = 0; w < 1; w++) {
1315             if (tok) {
1316               getVector(ai, atomCoords, ka);
1317               double dat2 = dist2(ai, tij);
1318               double rad2 = (radius[ka] + probe) * (radius[ka] + probe) - rij[0] * rij[0];
1319 
1320               // If "ka" less than "ja", then all we care about is whether the torus is buried.
1321               if (ka < ja) {
1322                 if (rad2 <= 0.0 || dat2 > rad2) {
1323                   continue;
1324                 }
1325               }
1326 
1327               double[] rik = {0.0};
1328               tok = getTorus(ia, ka, tik, rik, uik);
1329               if (!tok) {
1330                 continue;
1331               }
1332               double dotijk = check(dot(uij, uik));
1333               double wijk = acos(dotijk);
1334               double swijk = sin(wijk);
1335 
1336               // If the three atoms are colinear, then there is no probe placement; but we still
1337               // care
1338               // whether the torus is buried by atom "k".
1339               if (swijk == 0.0) {
1340                 tb = (rad2 > 0.0 && dat2 <= rad2);
1341                 continue;
1342               }
1343               X(uij, uik, uijk);
1344               for (int k = 0; k < 3; k++) {
1345                 uijk[k] = uijk[k] / swijk;
1346               }
1347               X(uijk, uij, utb);
1348               for (int k = 0; k < 3; k++) {
1349                 tijik[k] = tik[k] - tij[k];
1350               }
1351               double dotut = dot(uik, tijik);
1352               double fact = dotut / swijk;
1353               for (int k = 0; k < 3; k++) {
1354                 bijk[k] = tij[k] + utb[k] * fact;
1355               }
1356               getVector(ai, atomCoords, ia);
1357               double dba = dist2(ai, bijk);
1358               double rip2 = (radius[ia] + probe) * (radius[ia] + probe);
1359               double rad = rip2 - dba;
1360               if (rad < 0.0) {
1361                 tb = (rad2 > 0.0 && dat2 <= rad2);
1362               } else {
1363                 prbok = true;
1364                 hijk = sqrt(rad);
1365               }
1366             }
1367           }
1368           if (tb) {
1369             tempToriBuried[itt] = true;
1370             tempToriFree[itt] = false;
1371             continue;
1372           }
1373 
1374           // Check for duplicate triples or any possible probe positions.
1375           if (ka < ja || !prbok) {
1376             continue;
1377           }
1378 
1379           // Altitude vector.
1380           for (int k = 0; k < 3; k++) {
1381             aijk[k] = hijk * uijk[k];
1382           }
1383 
1384           // We try two probe placements.
1385           for3:
1386           for (int ip = 0; ip < 2; ip++) {
1387             for (int k = 0; k < 3; k++) {
1388               if (ip == 0) {
1389                 pijk[k] = bijk[k] + aijk[k];
1390               } else {
1391                 pijk[k] = bijk[k] - aijk[k];
1392               }
1393             }
1394 
1395             // Mark three tori not free.
1396             tempToriFree[itt] = false;
1397             tempToriFree[ik] = false;
1398             tempToriFree[jk] = false;
1399 
1400             // Check for collisions.
1401             int lm = lkf;
1402             int counter3 = 1;
1403             for4:
1404             for (int t = 0; t < counter3; t++) {
1405               for (int p = 0; p < 1; p++) {
1406                 if (lm < 0) {
1407                   continue for4;
1408                 }
1409 
1410                 // Get atom number of mutual neighbor.
1411                 int la = mnb[lm];
1412                 // Must not equal third atom.
1413                 if (la == ka) {
1414                   continue;
1415                 }
1416                 getVector(ak, atomCoords, la);
1417                 // Compare distance to sum of radii.
1418                 if (dist2(pijk, ak) <= sumcls[lm]) {
1419                   continue for3;
1420                 }
1421               }
1422               lm = lkcls[lm];
1423               counter3++;
1424             }
1425             // We have a new probe position.
1426             nProbePositions++;
1427             if (nProbePositions >= maxProbePositions) {
1428               throw new EnergyException(" Too many Probe Positions");
1429             }
1430             // Mark three tori not buried.
1431             tempToriBuried[itt] = false;
1432             tempToriBuried[ik] = false;
1433             tempToriBuried[jk] = false;
1434             // Store probe center.
1435             for (int k = 0; k < 3; k++) {
1436               probeCoords[k][nProbePositions] = pijk[k];
1437             }
1438 
1439             // Calculate vectors from probe to atom centers.
1440             if (nConcaveVerts + 3 >= maxVertices) {
1441               throw new EnergyException(" Too many Vertices");
1442             }
1443             for (int k = 0; k < 3; k++) {
1444               vertexCoords[k][nConcaveVerts + 1] =
1445                   atomCoords[k][ia] - probeCoords[k][nProbePositions];
1446               vertexCoords[k][nConcaveVerts + 2] =
1447                   atomCoords[k][ja] - probeCoords[k][nProbePositions];
1448               vertexCoords[k][nConcaveVerts + 3] =
1449                   atomCoords[k][ka] - probeCoords[k][nProbePositions];
1450             }
1451             // double matrix[] = new double[9];
1452             // int a = 0;
1453             // for (int b = 0; b < 3; b++) {
1454             //    for (int c = 0; c < 3; c++) {
1455             //        matrix[a++] = v[b][nv + c + 1];
1456             //    }
1457             // }
1458 
1459             // Calculate determinant of vectors defining triangle.
1460             double det =
1461                 vertexCoords[0][nConcaveVerts + 1]
1462                     * vertexCoords[1][nConcaveVerts + 2]
1463                     * vertexCoords[2][nConcaveVerts + 3]
1464                     + vertexCoords[0][nConcaveVerts + 2]
1465                     * vertexCoords[1][nConcaveVerts + 3]
1466                     * vertexCoords[2][nConcaveVerts + 1]
1467                     + vertexCoords[0][nConcaveVerts + 3]
1468                     * vertexCoords[1][nConcaveVerts + 1]
1469                     * vertexCoords[2][nConcaveVerts + 2]
1470                     - vertexCoords[0][nConcaveVerts + 3]
1471                     * vertexCoords[1][nConcaveVerts + 2]
1472                     * vertexCoords[2][nConcaveVerts + 1]
1473                     - vertexCoords[0][nConcaveVerts + 2]
1474                     * vertexCoords[1][nConcaveVerts + 1]
1475                     * vertexCoords[2][nConcaveVerts + 3]
1476                     - vertexCoords[0][nConcaveVerts + 1]
1477                     * vertexCoords[1][nConcaveVerts + 3]
1478                     * vertexCoords[2][nConcaveVerts + 2];
1479             // Now add probe coordinates to vertices.
1480             for (int k = 0; k < 3; k++) {
1481               vertexCoords[k][nConcaveVerts + 1] =
1482                   probeCoords[k][nProbePositions]
1483                       + (vertexCoords[k][nConcaveVerts + 1] * probe / (radius[ia] + probe));
1484               vertexCoords[k][nConcaveVerts + 2] =
1485                   probeCoords[k][nProbePositions]
1486                       + (vertexCoords[k][nConcaveVerts + 2] * probe / (radius[ja] + probe));
1487               vertexCoords[k][nConcaveVerts + 3] =
1488                   probeCoords[k][nProbePositions]
1489                       + (vertexCoords[k][nConcaveVerts + 3] * probe / (radius[ka] + probe));
1490             }
1491             // Want the concave face to have counter-clockwise orientation.
1492             if (det > 0.0) {
1493               // Swap second and third vertices.
1494               for (int k = 0; k < 3; k++) {
1495                 tempv[k] = vertexCoords[k][nConcaveVerts + 2];
1496                 vertexCoords[k][nConcaveVerts + 2] = vertexCoords[k][nConcaveVerts + 3];
1497                 vertexCoords[k][nConcaveVerts + 3] = tempv[k];
1498               }
1499               // Set up pointers from probe to atoms.
1500               probeAtomNumbers[0][nProbePositions] = ia;
1501               probeAtomNumbers[1][nProbePositions] = ka;
1502               probeAtomNumbers[2][nProbePositions] = ja;
1503               // Set pointers from vertices to atoms.
1504               vertexAtomNumbers[nConcaveVerts + 1] = ia;
1505               vertexAtomNumbers[nConcaveVerts + 2] = ka;
1506               vertexAtomNumbers[nConcaveVerts + 3] = ja;
1507               // Insert concave edges into linked lists for appropriate tori.
1508               insertEdge(nConcaveEdges + 1, ik);
1509               insertEdge(nConcaveEdges + 2, jk);
1510               insertEdge(nConcaveEdges + 3, itt);
1511             } else {
1512               // Similarly, if face already counter-clockwise.
1513               probeAtomNumbers[0][nProbePositions] = ia;
1514               probeAtomNumbers[1][nProbePositions] = ja;
1515               probeAtomNumbers[2][nProbePositions] = ka;
1516               vertexAtomNumbers[nConcaveVerts + 1] = ia;
1517               vertexAtomNumbers[nConcaveVerts + 2] = ja;
1518               vertexAtomNumbers[nConcaveVerts + 3] = ka;
1519               insertEdge(nConcaveEdges + 1, itt);
1520               insertEdge(nConcaveEdges + 2, jk);
1521               insertEdge(nConcaveEdges + 3, ik);
1522             }
1523             // Set up pointers from vertices to probe.
1524             for (int kv = 1; kv < 4; kv++) {
1525               vertexProbeNumber[nConcaveVerts + kv] = nProbePositions;
1526             }
1527             // Set up concave edges and concave face.
1528             if (nConcaveEdges + 3 >= maxConcaveEdges) {
1529               throw new EnergyException(" Too many Concave Edges");
1530             }
1531             // Edges point to vertices.
1532             concaveEdgeVertexNumbers[0][nConcaveEdges + 1] = nConcaveVerts + 1;
1533             concaveEdgeVertexNumbers[1][nConcaveEdges + 1] = nConcaveVerts + 2;
1534             concaveEdgeVertexNumbers[0][nConcaveEdges + 2] = nConcaveVerts + 2;
1535             concaveEdgeVertexNumbers[1][nConcaveEdges + 2] = nConcaveVerts + 3;
1536             concaveEdgeVertexNumbers[0][nConcaveEdges + 3] = nConcaveVerts + 3;
1537             concaveEdgeVertexNumbers[1][nConcaveEdges + 3] = nConcaveVerts + 1;
1538             if (nConcaveFaces + 1 >= maxConcaveFaces) {
1539               throw new EnergyException(" Too many Concave Faces");
1540             }
1541             // Face points to edges.
1542             for (int ke = 0; ke < 3; ke++) {
1543               concaveFaceEdgeNumbers[ke][nConcaveFaces + 1] = nConcaveEdges + ke + 1;
1544             }
1545             // Increment counters for number of faces, edges, and vertices.
1546             nConcaveFaces++;
1547             nConcaveEdges += 3;
1548             nConcaveVerts += 3;
1549           }
1550         }
1551       }
1552     }
1553 
1554     /**
1555      * The insertEdge method inserts a concave edge into the linked list for its temporary torus.
1556      *
1557      * @param edgeNumber  The concave edge number.
1558      * @param torusNumber The temporary torus.
1559      */
1560     private void insertEdge(int edgeNumber, int torusNumber) {
1561       // Check for a serious error in the calling arguments.
1562       if (edgeNumber <= -1) {
1563         throw new EnergyException(" Bad Edge Number in INEDGE");
1564       }
1565       if (torusNumber <= -1) {
1566         throw new EnergyException(" Bad Torus Number in INEDGE");
1567       }
1568       // Set beginning of list or add to end.
1569       if (tempToriFirstEdge[torusNumber] == -1) {
1570         tempToriFirstEdge[torusNumber] = edgeNumber;
1571       } else {
1572         tempToriNextEdge[tempToriLastEdge[torusNumber]] = edgeNumber;
1573       }
1574       tempToriNextEdge[edgeNumber] = -1;
1575       tempToriLastEdge[torusNumber] = edgeNumber;
1576     }
1577 
1578     /**
1579      * The compress method transfers only the non-buried tori from the temporary tori arrays to the
1580      * final tori arrays.
1581      */
1582     private void compress() {
1583       double[] torCenter = new double[3];
1584       double[] torAxis = new double[3];
1585       // Initialize the number of non-buried tori.
1586       nTori = -1;
1587       if (nTempTori <= -1) {
1588         return;
1589       }
1590       // If torus is free, then it is not buried; skip to end of loop if buried torus.
1591       double[] torRad = {0};
1592       for (int itt = 0; itt <= nTempTori; itt++) {
1593         if (tempToriFree[itt]) {
1594           tempToriBuried[itt] = false;
1595         }
1596         if (!tempToriBuried[itt]) {
1597           // First, transfer information.
1598           nTori++;
1599           if (nTori >= maxTori) {
1600             throw new EnergyException(" Too many non-buried tori.");
1601           }
1602           int ia = tempToriAtomNumbers[0][itt];
1603           int ja = tempToriAtomNumbers[1][itt];
1604           getVector(torCenter, torusCenter, nTori);
1605           getVector(torAxis, torusAxis, nTori);
1606           getTorus(ia, ja, torCenter, torRad, torAxis);
1607           torusCenter[0][nTori] = torCenter[0];
1608           torusCenter[1][nTori] = torCenter[1];
1609           torusCenter[2][nTori] = torCenter[2];
1610           torusAxis[0][nTori] = torAxis[0];
1611           torusAxis[1][nTori] = torAxis[1];
1612           torusAxis[2][nTori] = torAxis[2];
1613           torusRadius[nTori] = torRad[0];
1614           torusAtomNumber[0][nTori] = ia;
1615           torusAtomNumber[1][nTori] = ja;
1616           torusNeighborFreeEdge[nTori] = tempToriFree[itt];
1617           torusFirstEdge[nTori] = tempToriFirstEdge[itt];
1618           // Special check for inconsistent probes.
1619           int iptr = torusFirstEdge[nTori];
1620           int ned = -1;
1621           while (iptr != -1) {
1622             ned++;
1623             iptr = tempToriNextEdge[iptr];
1624           }
1625           if ((ned % 2) == 0) {
1626             iptr = torusFirstEdge[nTori];
1627             while (iptr != -1) {
1628               int iv1 = concaveEdgeVertexNumbers[0][iptr];
1629               int iv2 = concaveEdgeVertexNumbers[1][iptr];
1630               int ip1 = vertexProbeNumber[iv1];
1631               int ip2 = vertexProbeNumber[iv2];
1632               logger.warning(format(" Odd Torus for Probes IP1 %d and IP2 %d", ip1, ip2));
1633               iptr = tempToriNextEdge[iptr];
1634             }
1635           }
1636         }
1637       }
1638     }
1639 
1640     /**
1641      * The saddles method constructs circles, convex edges, and saddle faces.
1642      */
1643     private void saddles() {
1644       final int maxent = 500;
1645       int[] ten = new int[maxent];
1646       int[] nxtang = new int[maxent];
1647       double[] ai = new double[3];
1648       double[] aj = new double[3];
1649       double[] ak = new double[3];
1650       double[] atvect = new double[3];
1651       double[] teang = new double[maxent];
1652       double[][] tev = new double[3][maxent];
1653       boolean[] sdstrt = new boolean[maxent];
1654 
1655       // Zero the number of circles, convex edges, and saddle faces.
1656       nCircles = -1;
1657       nConvexEdges = -1;
1658       nSaddleFaces = -1;
1659       fill(convexEdgeFirst, -1);
1660       fill(convexEdgeLast, -1);
1661       fill(atomBuried, true);
1662 
1663       // No saddle faces if no tori.
1664       if (nTori < 0) {
1665         return;
1666       }
1667 
1668       // Cycle through tori.
1669       for (int it = 0; it <= nTori; it++) {
1670         if (skip[torusAtomNumber[0][it]] && skip[torusAtomNumber[1][it]]) {
1671           continue;
1672         }
1673 
1674         // Set up two circles.
1675         for (int in = 0; in < 2; in++) {
1676           int ia = torusAtomNumber[in][it];
1677 
1678           // Mark atom not buried.
1679           atomBuried[ia] = false;
1680 
1681           // Vector from atom to torus center.
1682           for (int k = 0; k < 3; k++) {
1683             atvect[k] = torusCenter[k][it] - atomCoords[k][ia];
1684           }
1685           double factor = radius[ia] / (radius[ia] + probe);
1686 
1687           // One more circle.
1688           nCircles++;
1689           if (nCircles >= maxCircles) {
1690             throw new EnergyException(" Too many Circles");
1691           }
1692 
1693           // Circle center.
1694           for (int k = 0; k < 3; k++) {
1695             circleCenter[k][nCircles] = atomCoords[k][ia] + factor * atvect[k];
1696           }
1697 
1698           // Pointer from circle to atom.
1699           circleAtomNumber[nCircles] = ia;
1700 
1701           // Pointer from circle to torus.
1702           circleTorusNumber[nCircles] = it;
1703 
1704           // Circle radius.
1705           circleRadius[nCircles] = factor * torusRadius[it];
1706         }
1707 
1708         // Skip to special "free torus" code.
1709         if (!torusNeighborFreeEdge[it]) {
1710           /*
1711            Now we collect all the concave edges for this
1712            torus; for each concave edge, calculate vector
1713            from torus center through probe center and the
1714            angle relative to first such vector.
1715           */
1716 
1717           // Clear the number of concave edges for torus.
1718           int nent = -1;
1719 
1720           // Pointer to start of linked list.
1721           int ien = torusFirstEdge[it];
1722           // 10 Continue
1723           while (ien >= 0) {
1724             // One more concave edge.
1725             nent++;
1726             if (nent >= maxent) {
1727               throw new EnergyException(" Too many Edges for Torus");
1728             }
1729             // First vertex of edge.
1730             int iv = concaveEdgeVertexNumbers[0][ien];
1731 
1732             // Probe number of vertex.
1733             int ip = vertexProbeNumber[iv];
1734             for (int k = 0; k < 3; k++) {
1735               tev[k][nent] = probeCoords[k][ip] - torusCenter[k][it];
1736             }
1737             double dtev = 0.0;
1738             for (int k = 0; k < 3; k++) {
1739               dtev += tev[k][nent] * tev[k][nent];
1740             }
1741             if (dtev <= 0.0) {
1742               throw new EnergyException(" Probe on Torus Axis");
1743             }
1744             dtev = sqrt(dtev);
1745             for (int k = 0; k < 3; k++) {
1746               tev[k][nent] = tev[k][nent] / dtev;
1747             }
1748 
1749             // Store concave edge number.
1750             ten[nent] = ien;
1751             if (nent > 0) {
1752               // Calculate angle between this vector and first vector.
1753               double dt = 0.0;
1754               for (int k = 0; k < 3; k++) {
1755                 dt += tev[k][0] * tev[k][nent];
1756               }
1757               if (dt > 1.0) {
1758                 dt = 1.0;
1759               }
1760               if (dt < -1.0) {
1761                 dt = -1.0;
1762               }
1763 
1764               // Store angle.
1765               teang[nent] = acos(dt);
1766 
1767               ai[0] = tev[0][0];
1768               ai[1] = tev[1][0];
1769               ai[2] = tev[2][0];
1770               aj[0] = tev[0][nent];
1771               aj[1] = tev[1][nent];
1772               aj[2] = tev[2][nent];
1773               ak[0] = torusAxis[0][it];
1774               ak[1] = torusAxis[1][it];
1775               ak[2] = torusAxis[2][it];
1776 
1777               // Get the sign right.
1778               double triple = tripleProduct(ai, aj, ak);
1779               if (triple < 0.0) {
1780                 teang[nent] = 2.0 * Math.PI - teang[nent];
1781               }
1782             } else {
1783               teang[0] = 0.0;
1784             }
1785             // Saddle face starts with this edge if it points parallel to torus axis vector
1786             // (which goes from first to second atom).
1787             sdstrt[nent] = (vertexAtomNumbers[iv] == torusAtomNumber[0][it]);
1788             // Next edge in list.
1789             ien = tempToriNextEdge[ien];
1790           }
1791 
1792           if (nent == -1) {
1793             logger.severe(" No Edges for Non-free Torus");
1794           }
1795 
1796           if ((nent % 2) == 0) {
1797             throw new EnergyException(" Odd Number of Edges for Torus");
1798           }
1799 
1800           // Set up linked list of concave edges in order of increasing angle
1801           // around the torus axis;
1802           // clear second linked (angle-ordered) list pointers.
1803           for (int ient = 0; ient <= nent; ient++) {
1804             nxtang[ient] = -1;
1805           }
1806           for (int ient = 1; ient <= nent; ient++) {
1807             // We have an entry to put into linked list search for place to put it.
1808             int l1 = -1;
1809             int l2 = 0;
1810             while (l2 != -1 && teang[ient] >= teang[l2]) {
1811               l1 = l2;
1812               l2 = nxtang[l2];
1813             }
1814             // We are at end of linked list or between l1 and l2; insert edge.
1815             if (l1 == -1) {
1816               throw new EnergyException(" Logic Error in SADDLES", true);
1817             }
1818             nxtang[l1] = ient;
1819             nxtang[ient] = l2;
1820           }
1821 
1822           // Collect pairs of concave edges into saddles.
1823           // Create convex edges while you're at it.
1824           int l1 = 0;
1825           while (l1 > -1) {
1826             // Check for start of saddle.
1827             if (sdstrt[l1]) {
1828               // One more saddle face.
1829               nSaddleFaces++;
1830               if (nSaddleFaces >= maxSaddleFace) {
1831                 throw new EnergyException(" Too many Saddle Faces");
1832               }
1833               // Get edge number.
1834               ien = ten[l1];
1835               // First concave edge of saddle.
1836               saddleConcaveEdgeNumbers[0][nSaddleFaces] = ien;
1837               // One more convex edge.
1838               nConvexEdges++;
1839               if (nConvexEdges >= maxConvexEdges) {
1840                 throw new EnergyException(" Too many Convex Edges");
1841               }
1842               // First convex edge points to second circle.
1843               convexEdgeCircleNumber[nConvexEdges] = nCircles;
1844               // Atom circle lies on.
1845               int ia = circleAtomNumber[nCircles];
1846               // Insert convex edge into linked list for atom.
1847               insertConvexEdgeForAtom(nConvexEdges, ia);
1848               // First vertex of convex edge is second vertex of concave edge.
1849               convexEdgeVertexNumber[0][nConvexEdges] = concaveEdgeVertexNumbers[1][ien];
1850               // First convex edge of saddle.
1851               saddleConvexEdgeNumbers[0][nSaddleFaces] = nConvexEdges;
1852               // One more convex edge.
1853               nConvexEdges++;
1854               if (nConvexEdges >= maxConvexEdges) {
1855                 throw new EnergyException(" Too many Convex Edges");
1856               }
1857               // Second convex edge points to fist circle.
1858               convexEdgeCircleNumber[nConvexEdges] = nCircles - 1;
1859               ia = circleAtomNumber[nCircles - 1];
1860               // Insert convex edge into linked list for atom.
1861               insertConvexEdgeForAtom(nConvexEdges, ia);
1862               // Second vertex of second convex edge is first vertex of first concave edge.
1863               convexEdgeVertexNumber[1][nConvexEdges] = concaveEdgeVertexNumbers[0][ien];
1864               l1 = nxtang[l1];
1865               // Wrap around.
1866               if (l1 <= -1) {
1867                 l1 = 0;
1868               }
1869               if (sdstrt[l1]) {
1870                 int m1 = nxtang[l1];
1871                 if (m1 <= -1) {
1872                   m1 = 0;
1873                 }
1874                 if (sdstrt[m1]) {
1875                   throw new EnergyException(" Three Starts in a Row");
1876                 }
1877                 int n1 = nxtang[m1];
1878                 // The old switcheroo.
1879                 nxtang[l1] = n1;
1880                 nxtang[m1] = l1;
1881                 l1 = m1;
1882               }
1883               ien = ten[l1];
1884               // Second concave edge for saddle face.
1885               saddleConcaveEdgeNumbers[1][nSaddleFaces] = ien;
1886               // Second vertex of first convex edge is first vertex of second concave edge.
1887               convexEdgeVertexNumber[1][nConvexEdges - 1] = concaveEdgeVertexNumbers[0][ien];
1888               // First vertex of second convex edge is second vertex of second concave edge.
1889               convexEdgeVertexNumber[0][nConvexEdges] = concaveEdgeVertexNumbers[1][ien];
1890               saddleConvexEdgeNumbers[1][nSaddleFaces] = nConvexEdges;
1891               // Quit if we have wrapped around to first edge.
1892               if (l1 == 0) {
1893                 break;
1894               }
1895             }
1896             // Next concave edge.
1897             l1 = nxtang[l1];
1898           }
1899         } else {
1900           // Free torus
1901           // Set up entire circles as convex edges for new saddle surface; one more saddle face.
1902           nSaddleFaces++;
1903           if (nSaddleFaces >= maxSaddleFace) {
1904             throw new EnergyException(" Too many Saddle Faces");
1905           }
1906           // No concave edge for saddle.
1907           saddleConcaveEdgeNumbers[0][nSaddleFaces] = -1;
1908           saddleConcaveEdgeNumbers[1][nSaddleFaces] = -1;
1909           // One more convex edge.
1910           nConvexEdges++;
1911           int ia = circleAtomNumber[nCircles];
1912           // Insert convex edge into linked list of atom.
1913           insertConvexEdgeForAtom(nConvexEdges, ia);
1914           // No vertices for convex edge.
1915           convexEdgeVertexNumber[0][nConvexEdges] = -1;
1916           convexEdgeVertexNumber[1][nConvexEdges] = -1;
1917           // Pointer from convex edge to second circle.
1918           convexEdgeCircleNumber[nConvexEdges] = nCircles;
1919           // First convex edge for saddle face.
1920           saddleConvexEdgeNumbers[0][nSaddleFaces] = nConvexEdges;
1921           // One more convex edge.
1922           nConvexEdges++;
1923           ia = circleAtomNumber[nCircles - 1];
1924           // Insert second convex edge into linked list.
1925           insertConvexEdgeForAtom(nConvexEdges, ia);
1926           // No vertices for convex edge.
1927           convexEdgeVertexNumber[0][nConvexEdges] = -1;
1928           convexEdgeVertexNumber[1][nConvexEdges] = -1;
1929           // Convex edge points to first circle.
1930           convexEdgeCircleNumber[nConvexEdges] = nCircles - 1;
1931           // Second convex edge for saddle face.
1932           saddleConvexEdgeNumbers[1][nSaddleFaces] = nConvexEdges;
1933           // Buried torus; do nothing with it.
1934         }
1935       }
1936 
1937       logger.fine(format(" Number of circles      %d", nCircles + 1));
1938       logger.fine(format(" Number of convex edges %d", nConvexEdges + 1));
1939       logger.fine(format(" Number of saddle faces %d", nSaddleFaces + 1));
1940     }
1941 
1942     /**
1943      * The contact method constructs the contact surface, cycles and convex faces.
1944      */
1945     private void contact() {
1946       final int maxepa = 300;
1947       final int maxcypa = 100;
1948       int[] aic = new int[maxepa];
1949       int[] aia = new int[maxepa];
1950       int[] aep = new int[maxepa];
1951       int[][] av = new int[2][maxepa];
1952       int[] ncyepa = new int[maxcypa];
1953       int[][] cyepa = new int[MAXCYEP][maxcypa];
1954       double[] ai = new double[3];
1955       double[][] acvect = new double[3][maxepa];
1956       double[][] aavect = new double[3][maxepa];
1957       double[] pole = new double[3];
1958       double[] acr = new double[maxepa];
1959       double[] unvect = new double[3];
1960       boolean[] epused = new boolean[maxepa];
1961       boolean[][] cycy = new boolean[maxcypa][maxcypa];
1962       boolean[] cyused = new boolean[maxcypa];
1963       boolean[][] samef = new boolean[maxcypa][maxcypa];
1964 
1965       // Zero out the number of cycles and convex faces.
1966       nCycles = -1;
1967       nConvexFaces = -1;
1968 
1969       // Mark all free atoms not buried.
1970       for (int ia = 0; ia < nAtoms; ia++) {
1971         if (atomFreeOfNeighbors[ia]) {
1972           atomBuried[ia] = false;
1973         }
1974       }
1975 
1976       // Go through all atoms.
1977       atomLoop:
1978       for (int ia = 0; ia < nAtoms; ia++) {
1979         if (skip[ia] || atomBuried[ia]) {
1980           continue;
1981         }
1982 
1983         // Special code for completely solvent-accessible atom.
1984         for (int o = 0; o < 1; o++) {
1985           if (atomFreeOfNeighbors[ia]) {
1986             continue;
1987           }
1988           // Gather convex edges for atom Clear number of convex edges for atom.
1989           int nepa = -1;
1990           // Pointer to first edge.
1991           int iep = convexEdgeFirst[ia];
1992           // Check whether finished gathering.
1993           while (iep > -1) {
1994             // One more edge.
1995             nepa++;
1996             if (nepa >= maxepa) {
1997               throw new EnergyException(" Too many Convex Edges for Atom");
1998             }
1999             // Store vertices of edge.
2000             av[0][nepa] = convexEdgeVertexNumber[0][iep];
2001             av[1][nepa] = convexEdgeVertexNumber[1][iep];
2002             // Store convex edge number.
2003             aep[nepa] = iep;
2004             int ic = convexEdgeCircleNumber[iep];
2005             // Store circle number.
2006             aic[nepa] = ic;
2007             // Get neighboring atom.
2008             int it = circleTorusNumber[ic];
2009             int ia2;
2010             if (torusAtomNumber[0][it] == ia) {
2011               ia2 = torusAtomNumber[1][it];
2012             } else {
2013               ia2 = torusAtomNumber[0][it];
2014             }
2015             // Store other atom number, we might need it sometime.
2016             aia[nepa] = ia2;
2017             /*
2018              Vector from atom to circle center; also vector
2019              from atom to center of neighboring atom sometimes
2020              we use one vector, sometimes the other.
2021             */
2022             for (int k = 0; k < 3; k++) {
2023               acvect[k][nepa] = circleCenter[k][ic] - atomCoords[k][ia];
2024               aavect[k][nepa] = atomCoords[k][ia2] - atomCoords[k][ia];
2025             }
2026             // Circle radius.
2027             acr[nepa] = circleRadius[ic];
2028             // Pointer to next edge.
2029             iep = convexEdgeNext[iep];
2030           }
2031           if (nepa == -1) {
2032             throw new EnergyException(" No Edges for Non-buried, Non-free Atom");
2033           }
2034           // Form cycles; initialize all the convex edges as not used in cycle.
2035           for (int iepa = 0; iepa < nepa + 1; iepa++) {
2036             epused[iepa] = false;
2037           }
2038           // Save old number of cycles.
2039           int ncyold = nCycles;
2040           int nused = -1;
2041           int ncypa = -1;
2042           while (nused < nepa) {
2043             // Look for starting edge.
2044             int iepa;
2045             for (iepa = 0; iepa <= nepa; iepa++) {
2046               if (!epused[iepa]) {
2047                 break;
2048               }
2049             }
2050             if (iepa > nepa) {
2051               // Cannot find starting edge; finished.
2052               break;
2053             }
2054             // Pointer to edge.
2055             iep = aep[iepa];
2056             // One edge so far on this cycle.
2057             int ncyep = 0;
2058             // One more cycle for atom.
2059             ncypa++;
2060             if (ncypa >= maxcypa) {
2061               throw new EnergyException(" Too many Cycles per Atom");
2062             }
2063             // Mark edge used in cycle.
2064             epused[iepa] = true;
2065             nused++;
2066             // One more cycle for molecule.
2067             nCycles++;
2068             if (nCycles >= maxCycles) {
2069               throw new EnergyException(" Too many Cycles");
2070             }
2071             // Index of edge in atom cycle array.
2072             cyepa[ncyep][ncypa] = iepa;
2073             // Store in molecule cycle array a pointer to edge.
2074             convexEdgeCycleNumbers[ncyep][nCycles] = iep;
2075             // Second vertex of this edge is the vertex to look for
2076             // next as the first vertex of another edge.
2077             int lookv = av[1][iepa];
2078             // If no vertex, this cycle is finished.
2079             if (lookv > -1) {
2080               for (int jepa = 0; jepa < nepa + 1; jepa++) {
2081                 if (epused[jepa]) {
2082                   continue;
2083                 }
2084                 // Check second vertex of iepa versus first vertex of jepa.
2085                 if (av[0][jepa] != lookv) {
2086                   continue;
2087                 }
2088                 // Edges are connected pointer to edge.
2089                 iep = aep[jepa];
2090                 // One more edge for this cycle.
2091                 ncyep++;
2092                 if (ncyep >= MAXCYEP) {
2093                   throw new EnergyException(" Too many Edges per Cycle");
2094                 }
2095                 epused[jepa] = true;
2096                 nused++;
2097                 // Store index in local edge array.
2098                 cyepa[ncyep][ncypa] = jepa;
2099                 // Store pointer to edge.
2100                 convexEdgeCycleNumbers[ncyep][nCycles] = iep;
2101                 // New vertex to look for.
2102                 lookv = av[1][jepa];
2103                 // If no vertex, this cycle is in trouble.
2104                 if (lookv <= -1) {
2105                   throw new EnergyException(" Pointer Error in Cycle", true);
2106                 }
2107                 jepa = 0;
2108               }
2109               // It better connect to first edge of cycle.
2110               if (lookv != av[0][iepa]) {
2111                 throw new EnergyException(" Cycle does not Close", true);
2112               }
2113             }
2114             // This cycle is finished, store number of edges in cycle.
2115             ncyepa[ncypa] = ncyep;
2116             convexEdgeCycleNum[nCycles] = ncyep;
2117           }
2118           // Compare cycles for inside/outside relation; check to
2119           // see if cycle i is inside cycle j.
2120           for (int icya = 0; icya <= ncypa; icya++) {
2121             innerLoop:
2122             for (int jcya = 0; jcya <= ncypa; jcya++) {
2123               int jcy = ncyold + jcya + 1;
2124               // Initialize.
2125               cycy[icya][jcya] = true;
2126               // Check for same cycle.
2127               if (icya == jcya || ncyepa[jcya] < 2) {
2128                 continue;
2129               }
2130               // If cycles i and j have a pair of edges belonging to the same circle,
2131               // then they are outside each other.
2132               for (int icyep = 0; icyep <= ncyepa[icya]; icyep++) {
2133                 int iepa = cyepa[icyep][icya];
2134                 int ic = aic[iepa];
2135                 for (int jcyep = 0; jcyep <= ncyepa[jcya]; jcyep++) {
2136                   int jepa = cyepa[jcyep][jcya];
2137                   int jc = aic[jepa];
2138                   if (ic == jc) {
2139                     cycy[icya][jcya] = false;
2140                     continue innerLoop;
2141                   }
2142                 }
2143               }
2144               int iepa = cyepa[0][icya];
2145               ai[0] = aavect[0][iepa];
2146               ai[1] = aavect[1][iepa];
2147               ai[2] = aavect[2][iepa];
2148               double anaa = length(ai);
2149               double factor = radius[ia] / anaa;
2150               // North pole and unit vector pointer south.
2151               for (int k = 0; k < 3; k++) {
2152                 pole[k] = factor * aavect[k][iepa] + atomCoords[k][ia];
2153                 unvect[k] = -aavect[k][iepa] / anaa;
2154               }
2155               cycy[icya][jcya] = ptincy(pole, unvect, jcy);
2156             }
2157           }
2158           // Group cycles into faces; direct comparison for i and j.
2159           for (int icya = 0; icya <= ncypa; icya++) {
2160             for (int jcya = 0; jcya <= ncypa; jcya++) {
2161               // Tentatively say that cycles i and j bound the
2162               // same face if they are inside each other.
2163               samef[icya][jcya] = (cycy[icya][jcya] && cycy[jcya][icya]);
2164             }
2165           }
2166           // If i is in exterior of k, and k is in interior of i
2167           // and j, then i and j do not bound the same face.
2168           for (int icya = 0; icya <= ncypa; icya++) {
2169             for (int jcya = 0; jcya <= ncypa; jcya++) {
2170               if (icya != jcya) {
2171                 for (int kcya = 0; kcya <= ncypa; kcya++) {
2172                   if (kcya != icya && kcya != jcya) {
2173                     if (cycy[kcya][icya] && cycy[kcya][jcya] && (!cycy[icya][kcya])) {
2174                       samef[icya][jcya] = false;
2175                       samef[jcya][icya] = false;
2176                     }
2177                   }
2178                 }
2179               }
2180             }
2181           }
2182           // Fill gaps so that "samef" falls into complete blocks.
2183           for (int icya = 0; icya < ncypa - 1; icya++) {
2184             for (int jcya = icya + 1; jcya < ncypa; jcya++) {
2185               if (samef[icya][jcya]) {
2186                 for (int kcya = jcya + 1; kcya <= ncypa; kcya++) {
2187                   if (samef[jcya][kcya]) {
2188                     samef[icya][kcya] = true;
2189                     samef[kcya][icya] = true;
2190                   }
2191                 }
2192               }
2193             }
2194           }
2195           // Group cycles belonging to the same face.
2196           for (int icya = 0; icya <= ncypa; icya++) {
2197             cyused[icya] = false;
2198           }
2199           // Clear number of cycles used in bounding faces.
2200           nused = -1;
2201           for (int icya = 0; icya <= ncypa; icya++) {
2202             // Check for already used.
2203             if (cyused[icya]) {
2204               continue;
2205             }
2206             // One more convex face.
2207             nConvexFaces++;
2208             if (nConvexFaces >= maxConvexFaces) {
2209               throw new EnergyException(" Too many Convex Faces");
2210             }
2211             // Clear number of cycles for face.
2212             convexFaceNumCycles[nConvexFaces] = -1;
2213             // Pointer from face to atom.
2214             convexFaceAtomNumber[nConvexFaces] = ia;
2215             // Look for all other cycles belonging to same face.
2216             for (int jcya = 0; jcya <= ncypa; jcya++) {
2217               // Check for cycle already used in another face.
2218               if (cyused[jcya]) {
2219                 continue;
2220               }
2221               // Cycles i and j belonging to same face
2222               if (!samef[icya][jcya]) {
2223                 continue;
2224               }
2225               // Mark cycle used.
2226               cyused[jcya] = true;
2227               nused++;
2228               // One more cycle for face.
2229               convexFaceNumCycles[nConvexFaces]++;
2230               if (convexFaceNumCycles[nConvexFaces] >= MAXFPCY) {
2231                 throw new EnergyException(" Too many Cycles bounding Convex Face");
2232               }
2233               int i = convexFaceNumCycles[nConvexFaces];
2234               // Store cycle number.
2235               convexFaceCycleNumbers[i][nConvexFaces] = ncyold + jcya + 1;
2236               // Check for finished.
2237               if (nused >= ncypa) {
2238                 continue atomLoop;
2239               }
2240             }
2241           }
2242           // Should not fall though end of for loops.
2243           throw new EnergyException(" Not all Cycles grouped into Convex Faces", true);
2244         }
2245         // Once face for free atom; no cycles.
2246         nConvexFaces++;
2247         if (nConvexFaces >= maxConvexFaces) {
2248           throw new EnergyException(" Too many Convex Faces");
2249         }
2250         convexFaceAtomNumber[nConvexFaces] = ia;
2251         convexFaceNumCycles[nConvexFaces] = -1;
2252       }
2253     }
2254 
2255     /**
2256      * The vam method takes the analytical molecular surface defined as a collection of spherical
2257      * and toroidal polygons and uses it to compute the volume and surface area
2258      */
2259     private void vam() {
2260       final int maxdot = 1000;
2261       final int maxop = 100;
2262       final int nscale = 20;
2263       int[] ivs = new int[3];
2264       int[] ispind = new int[3];
2265       int[] ispnd2 = new int[3];
2266       int[] ifnop = new int[maxop];
2267       int[] enfs = new int[20 * nAtoms];
2268       double[][] cenop = new double[3][maxop];
2269       double[] sdot = new double[3];
2270       double[] dotv = new double[nscale];
2271       double[] tau = new double[3];
2272       double[] ppm = new double[3];
2273       double[] xpnt1 = new double[3];
2274       double[] xpnt2 = new double[3];
2275       double[] qij = new double[3];
2276       double[] qji = new double[3];
2277       double[][] vects = new double[3][3];
2278       double[] vect1 = new double[3];
2279       double[] vect2 = new double[3];
2280       double[] vect3 = new double[3];
2281       double[] vect4 = new double[3];
2282       double[] vect5 = new double[3];
2283       double[] vect6 = new double[3];
2284       double[] vect7 = new double[3];
2285       double[] vect8 = new double[3];
2286       double[] upp = new double[3];
2287       double[] thetaq = new double[3];
2288       double[] sigmaq = new double[3];
2289       double[] umq = new double[3];
2290       double[] upq = new double[3];
2291       double[] uc = new double[3];
2292       double[] uq = new double[3];
2293       double[] uij = new double[3];
2294       double[] ai = new double[3];
2295       double[] aj = new double[3];
2296       double[] ak = new double[3];
2297       double[] al = new double[3];
2298       double[][] dots = new double[3][maxdot];
2299       double[][] tdots = new double[3][maxdot];
2300       double[] atomArea = new double[nAtoms];
2301       boolean[] ate = new boolean[maxop];
2302       boolean[] vip = new boolean[3];
2303       boolean[] cinsp = new boolean[1];
2304 
2305       int[] nlap = null;
2306       int[][] fnt = null;
2307       int[][] nspt = null;
2308       double[] depths = null;
2309       double[] cora = null;
2310       double[] corv = null;
2311       double[][] alts = null;
2312       double[][] fncen = null;
2313       double[][][] fnvect = null;
2314       boolean[] badav = null;
2315       boolean[] badt = null;
2316       boolean[][] fcins = null;
2317       boolean[][] fcint = null;
2318       boolean[][] fntrev = null;
2319       if (nConcaveFaces >= 0) {
2320         nlap = new int[nConcaveFaces + 1];
2321         fnt = new int[3][nConcaveFaces + 1];
2322         nspt = new int[3][nConcaveFaces + 1];
2323         depths = new double[nConcaveFaces + 1];
2324         cora = new double[nConcaveFaces + 1];
2325         corv = new double[nConcaveFaces + 1];
2326         alts = new double[3][nConcaveFaces + 1];
2327         fncen = new double[3][nConcaveFaces + 1];
2328         fnvect = new double[3][3][nConcaveFaces + 1];
2329         badav = new boolean[nConcaveFaces + 1];
2330         badt = new boolean[nConcaveFaces + 1];
2331         fcins = new boolean[3][nConcaveFaces + 1];
2332         fcint = new boolean[3][nConcaveFaces + 1];
2333         fntrev = new boolean[3][nConcaveFaces + 1];
2334       }
2335 
2336       // Compute the volume of the interior polyhedron.
2337       double polyhedronVolume = 0.0;
2338       for (int i = 0; i <= nConcaveFaces; i++) {
2339         polyhedronVolume += measurePrism(i);
2340       }
2341 
2342       // Compute the area and volume due to convex faces as well as
2343       // the area partitioned among the atoms.
2344       double convexFaceArea = 0.0;
2345       double convexFaceVolume = 0.0;
2346       fill(atomArea, 0.0);
2347       double[] convexFaces = {0.0, 0.0};
2348       for (int i = 0; i <= nConvexFaces; i++) {
2349         measureConvexFace(i, convexFaces);
2350         int ia = convexFaceAtomNumber[i];
2351         atomArea[ia] += convexFaces[0];
2352         convexFaceArea += convexFaces[0];
2353         convexFaceVolume += convexFaces[1];
2354       }
2355 
2356       // Compute the area and volume due to saddle faces
2357       // as well as the spindle correction value.
2358       double saddleFaceArea = 0.0;
2359       double saddleFaceVolume = 0.0;
2360       double spindleArea = 0.0;
2361       double spindleVolume = 0.0;
2362       double[] saddle = {0.0, 0.0, 0.0, 0.0};
2363       for (int i = 0; i <= nSaddleFaces; i++) {
2364         for (int k = 0; k < 2; k++) {
2365           int ien = saddleConcaveEdgeNumbers[k][i];
2366           if (ien > -1) {
2367             enfs[ien] = i;
2368           }
2369         }
2370         measureSaddleFace(i, saddle);
2371         double areas = saddle[0];
2372         double vols = saddle[1];
2373         double areasp = saddle[2];
2374         double volsp = saddle[3];
2375         saddleFaceArea += areas;
2376         saddleFaceVolume += vols;
2377         spindleArea += areasp;
2378         spindleVolume += volsp;
2379         if (areas - areasp < 0.0) {
2380           throw new EnergyException(" Negative Area for Saddle Face", true);
2381         }
2382       }
2383 
2384       // Compute the area and volume due to concave faces.
2385       double concaveFaceArea = 0.0;
2386       double concaveFaceVolume = 0.0;
2387       double[] concaveFaces = {0.0, 0.0};
2388       for (int i = 0; i <= nConcaveFaces; i++) {
2389         measureConcaveFace(i, concaveFaces);
2390         double arean = concaveFaces[0];
2391         double voln = concaveFaces[1];
2392         concaveFaceArea += arean;
2393         concaveFaceVolume += voln;
2394       }
2395 
2396       // Compute the area and volume lens correction values.
2397       double lensArea = 0.0;
2398       double lensAreaNumeric = 0.0;
2399       double lensVolume = 0.0;
2400       double lensVolumeNumeric = 0.0;
2401 
2402       if (probe > 0.0) {
2403         int ndots = genDots(maxdot, dots, probe);
2404         double dota = (4.0 * PI * probe * probe) / ndots;
2405         for (int ifn = 0; ifn <= nConcaveFaces; ifn++) {
2406           nlap[ifn] = -1;
2407           cora[ifn] = 0.0;
2408           corv[ifn] = 0.0;
2409           badav[ifn] = false;
2410           badt[ifn] = false;
2411           for (int k = 0; k < 3; k++) {
2412             nspt[k][ifn] = -1;
2413           }
2414           int ien = concaveFaceEdgeNumbers[0][ifn];
2415           int iv = concaveEdgeVertexNumbers[0][ien];
2416           int ip = vertexProbeNumber[iv];
2417           getVector(ai, alts, ifn);
2418           depths[ifn] = depth(ip, ai);
2419           for (int k = 0; k < 3; k++) {
2420             fncen[k][ifn] = probeCoords[k][ip];
2421           }
2422           // Get vertices and vectors.
2423           for (int ke = 0; ke < 3; ke++) {
2424             ien = concaveFaceEdgeNumbers[ke][ifn];
2425             ivs[ke] = concaveEdgeVertexNumbers[0][ien];
2426             int ia = vertexAtomNumbers[ivs[ke]];
2427             int ifs = enfs[ien];
2428             int iep = saddleConvexEdgeNumbers[0][ifs];
2429             int ic = convexEdgeCircleNumber[iep];
2430             int it = circleTorusNumber[ic];
2431             fnt[ke][ifn] = it;
2432             fntrev[ke][ifn] = (torusAtomNumber[0][it] != ia);
2433           }
2434           for (int ke = 0; ke < 3; ke++) {
2435             for (int k = 0; k < 3; k++) {
2436               vects[k][ke] = vertexCoords[k][ivs[ke]] - probeCoords[k][ip];
2437             }
2438           }
2439           // Calculate normal vectors for the three planes that
2440           // cut out the geodesic triangle.
2441           getVector(ai, vects, 0);
2442           getVector(aj, vects, 1);
2443           X(ai, aj, ak);
2444           normalize(ak, ak);
2445           putVector(ak, fnvect, 0, ifn);
2446           getVector(ak, vects, 2);
2447           X(aj, ak, ai);
2448           normalize(ai, ai);
2449           putVector(ai, fnvect, 1, ifn);
2450           getVector(ai, vects, 0);
2451           X(ak, ai, aj);
2452           normalize(aj, aj);
2453           putVector(aj, fnvect, 2, ifn);
2454         }
2455         for (int ifn = 0; ifn <= nConcaveFaces - 1; ifn++) {
2456           for (int jfn = ifn + 1; jfn <= nConcaveFaces; jfn++) {
2457             getVector(ai, fncen, ifn);
2458             getVector(aj, fncen, jfn);
2459             double dij2 = dist2(ai, aj);
2460             if (dij2 > 4.0 * probe * probe) {
2461               continue;
2462             }
2463             if (depths[ifn] > probe && depths[jfn] > probe) {
2464               continue;
2465             }
2466             // These two probes may have intersecting surfaces.
2467             double dpp = dist(ai, aj);
2468             // Compute the midpoint.
2469             for (int k = 0; k < 3; k++) {
2470               ppm[k] = (fncen[k][ifn] + fncen[k][jfn]) / 2.0;
2471               upp[k] = (fncen[k][jfn] - fncen[k][ifn]) / dpp;
2472             }
2473             double rm = probe * probe - (dpp / 2.0) * (dpp / 2.0);
2474             if (rm < 0.0) {
2475               rm = 0.0;
2476             }
2477             rm = sqrt(rm);
2478             double rat = check(dpp / (2.0 * probe));
2479             double rho = asin(rat);
2480             // Use circle-place intersection routine.
2481             boolean alli = true;
2482             boolean anyi = false;
2483             boolean spindl = false;
2484             for (int k = 0; k < 3; k++) {
2485               ispind[k] = -1;
2486               ispnd2[k] = -1;
2487             }
2488             for (int ke = 0; ke < 3; ke++) {
2489               thetaq[ke] = 0.0;
2490               sigmaq[ke] = 0.0;
2491               tau[ke] = 0.0;
2492               getVector(ai, fncen, ifn);
2493               getVector(aj, fnvect, ke, ifn);
2494               boolean cintp = circlePlane(ppm, rm, upp, ai, aj, cinsp, xpnt1, xpnt2);
2495               fcins[ke][ifn] = cinsp[0];
2496               fcint[ke][ifn] = cintp;
2497               if (!cinsp[0]) {
2498                 alli = false;
2499               }
2500               if (cintp) {
2501                 anyi = true;
2502               }
2503               if (!cintp) {
2504                 continue;
2505               }
2506               int it = fnt[ke][ifn];
2507               if (torusRadius[it] > probe) {
2508                 continue;
2509               }
2510               for (int ke2 = 0; ke2 < 3; ke2++) {
2511                 if (it == fnt[ke2][jfn]) {
2512                   ispind[ke] = it;
2513                   nspt[ke][ifn]++;
2514                   ispnd2[ke2] = it;
2515                   nspt[ke2][jfn]++;
2516                   spindl = true;
2517                 }
2518               }
2519               if (ispind[ke] == -1) {
2520                 continue;
2521               }
2522 
2523               // Check that the two ways of calculating intersection points match.
2524               rat = check(torusRadius[it] / probe);
2525               thetaq[ke] = acos(rat);
2526               double stq = sin(thetaq[ke]);
2527               if (fntrev[ke][ifn]) {
2528                 for (int k = 0; k < 3; k++) {
2529                   uij[k] = -torusAxis[k][it];
2530                 }
2531               } else {
2532                 for (int k = 0; k < 3; k++) {
2533                   uij[k] = torusAxis[k][it];
2534                 }
2535               }
2536               for (int k = 0; k < 3; k++) {
2537                 qij[k] = torusCenter[k][it] - stq * probe * uij[k];
2538                 qji[k] = torusCenter[k][it] + stq * probe * uij[k];
2539               }
2540               for (int k = 0; k < 3; k++) {
2541                 umq[k] = (qij[k] - ppm[k]) / rm;
2542                 upq[k] = (qij[k] - fncen[k][ifn]) / probe;
2543               }
2544               X(uij, upp, vect1);
2545               double dt = check(dot(umq, vect1));
2546               sigmaq[ke] = acos(dt);
2547               getVector(ai, fnvect, ke, ifn);
2548               X(upq, ai, vect1);
2549               normalize(vect1, uc);
2550               X(upp, upq, vect1);
2551               normalize(vect1, uq);
2552               dt = check(dot(uc, uq));
2553               tau[ke] = PI - acos(dt);
2554             }
2555             boolean allj = true;
2556             boolean anyj = false;
2557             for (int ke = 0; ke < 3; ke++) {
2558               getVector(ai, fncen, jfn);
2559               getVector(aj, fnvect, ke, jfn);
2560               boolean cintp = circlePlane(ppm, rm, upp, ai, aj, cinsp, xpnt1, xpnt2);
2561               fcins[ke][jfn] = cinsp[0];
2562               fcint[ke][jfn] = cintp;
2563               if (!cinsp[0]) {
2564                 allj = false;
2565               }
2566               if (cintp) {
2567                 anyj = true;
2568               }
2569             }
2570             boolean case1 = (alli && allj && !anyi && !anyj);
2571             boolean case2 = (anyi && anyj && spindl);
2572             if (!case1 && !case2) {
2573               continue;
2574             }
2575             // This kind of overlap can be handled.
2576             nlap[ifn]++;
2577             nlap[jfn]++;
2578             for (int ke = 0; ke < 3; ke++) {
2579               int ien = concaveFaceEdgeNumbers[ke][ifn];
2580               int iv1 = concaveEdgeVertexNumbers[0][ien];
2581               int iv2 = concaveEdgeVertexNumbers[1][ien];
2582               for (int k = 0; k < 3; k++) {
2583                 vect3[k] = vertexCoords[k][iv1] - fncen[k][ifn];
2584                 vect4[k] = vertexCoords[k][iv2] - fncen[k][ifn];
2585               }
2586               for (int ke2 = 0; ke2 < 3; ke2++) {
2587                 if (ispind[ke] == ispnd2[ke2] || ispind[ke] == -1) {
2588                   continue;
2589                 }
2590                 getVector(ai, fncen, ifn);
2591                 getVector(aj, fnvect, ke, ifn);
2592                 getVector(ak, fncen, jfn);
2593                 getVector(al, fnvect, ke2, jfn);
2594                 boolean cintp = circlePlane(ai, probe, aj, ak, al, cinsp, xpnt1, xpnt2);
2595                 if (!cintp) {
2596                   continue;
2597                 }
2598                 ien = concaveFaceEdgeNumbers[ke2][jfn];
2599                 iv1 = concaveEdgeVertexNumbers[0][ien];
2600                 iv2 = concaveEdgeVertexNumbers[1][ien];
2601                 for (int k = 0; k < 3; k++) {
2602                   vect7[k] = vertexCoords[k][iv1] - fncen[k][jfn];
2603                   vect8[k] = vertexCoords[k][iv2] - fncen[k][jfn];
2604                 }
2605                 // Check whether point lies on spindle arc.
2606                 for (int k = 0; k < 3; k++) {
2607                   vect1[k] = xpnt1[k] - fncen[k][ifn];
2608                   vect2[k] = xpnt2[k] - fncen[k][ifn];
2609                   vect5[k] = xpnt1[k] - fncen[k][jfn];
2610                   vect6[k] = xpnt2[k] - fncen[k][jfn];
2611                 }
2612                 // Continue to next if statement if any of the following are true.
2613                 getVector(ai, fnvect, ke, ifn);
2614                 getVector(aj, fnvect, ke2, jfn);
2615                 if (tripleProduct(vect3, vect1, ai) < 0.0
2616                     || tripleProduct(vect1, vect4, ai) < 0.0
2617                     || tripleProduct(vect7, vect5, aj) < 0.0
2618                     || tripleProduct(vect5, vect8, aj) < 0.0) {
2619                   if (!(tripleProduct(vect3, vect2, ai) < 0.0
2620                       || tripleProduct(vect2, vect4, ai) < 0.0
2621                       || tripleProduct(vect7, vect6, aj) < 0.0
2622                       || tripleProduct(vect6, vect8, aj) < 0.0)) {
2623                     badav[ifn] = true;
2624                   }
2625                 } else {
2626                   badav[ifn] = true;
2627                 }
2628               }
2629             }
2630             for (int ke = 0; ke < 3; ke++) {
2631               int ien = concaveFaceEdgeNumbers[ke][ifn];
2632               int iv1 = concaveEdgeVertexNumbers[0][ien];
2633               int iv2 = concaveEdgeVertexNumbers[1][ien];
2634               for (int k = 0; k < 3; k++) {
2635                 vect3[k] = vertexCoords[k][iv1] - fncen[k][ifn];
2636                 vect4[k] = vertexCoords[k][iv2] - fncen[k][ifn];
2637               }
2638               for (int ke2 = 0; ke2 < 3; ke2++) {
2639                 if (ispind[ke] == ispnd2[ke2] || ispnd2[ke2] == -1) {
2640                   continue;
2641                 }
2642                 getVector(ai, fncen, ifn);
2643                 getVector(aj, fnvect, ke, ifn);
2644                 getVector(ak, fncen, jfn);
2645                 getVector(al, fnvect, ke2, jfn);
2646                 boolean cintp = circlePlane(ak, probe, al, ai, aj, cinsp, xpnt1, xpnt2);
2647                 if (!cintp) {
2648                   continue;
2649                 }
2650                 ien = concaveFaceEdgeNumbers[ke2][jfn];
2651                 iv1 = concaveEdgeVertexNumbers[0][ien];
2652                 iv2 = concaveEdgeVertexNumbers[1][ien];
2653                 for (int k = 0; k < 3; k++) {
2654                   vect7[k] = vertexCoords[k][iv1] - fncen[k][jfn];
2655                   vect8[k] = vertexCoords[k][iv2] - fncen[k][jfn];
2656                 }
2657                 // Check whether point lies on spindle arc.
2658                 for (int k = 0; k < 3; k++) {
2659                   vect1[k] = xpnt1[k] - fncen[k][ifn];
2660                   vect2[k] = xpnt2[k] - fncen[k][ifn];
2661                   vect5[k] = xpnt1[k] - fncen[k][jfn];
2662                   vect6[k] = xpnt2[k] - fncen[k][jfn];
2663                 }
2664                 // Continue to next if statement if any of the following are true.
2665                 getVector(ai, fnvect, ke, ifn);
2666                 getVector(aj, fnvect, ke2, jfn);
2667                 if (tripleProduct(vect3, vect1, ai) < 0.0
2668                     || tripleProduct(vect1, vect4, ai) < 0.0
2669                     || tripleProduct(vect7, vect5, aj) < 0.0
2670                     || tripleProduct(vect5, vect8, aj) < 0.0) {
2671                   if (!(tripleProduct(vect3, vect2, ai) < 0.0
2672                       || tripleProduct(vect2, vect4, ai) < 0.0
2673                       || tripleProduct(vect7, vect6, aj) < 0.0
2674                       || tripleProduct(vect6, vect8, aj) < 0.0)) {
2675                     badav[jfn] = true;
2676                   }
2677                 } else {
2678                   badav[jfn] = true;
2679                 }
2680               }
2681             }
2682 
2683             double sumlam = 0.0;
2684             double sumsig = 0.0;
2685             double sumsc = 0.0;
2686             for (int ke = 0; ke < 3; ke++) {
2687               if (ispind[ke] != -1) {
2688                 sumlam += (PI - tau[ke]);
2689                 sumsig += (sigmaq[ke] - PI);
2690                 sumsc += (sin(sigmaq[ke]) * cos(sigmaq[ke]));
2691               }
2692             }
2693             double alens = 2.0 * probe * probe * (PI - sumlam - sin(rho) * (PI + sumsig));
2694             double vint = alens * probe / 3.0;
2695             double vcone = probe * rm * rm * sin(rho) * (PI + sumsig) / 3.0;
2696             double vpyr = probe * rm * rm * sin(rho) * sumsc / 3.0;
2697             double vlens = vint - vcone + vpyr;
2698             cora[ifn] += alens;
2699             cora[jfn] += alens;
2700             corv[ifn] += vlens;
2701             corv[jfn] += vlens;
2702 
2703             // Check for vertex on opposing probe in face.
2704             outerloop:
2705             for (int kv = 0; kv < 3; kv++) {
2706               vip[kv] = false;
2707               int ien = concaveFaceEdgeNumbers[kv][jfn];
2708               int iv = concaveEdgeVertexNumbers[0][ien];
2709               for (int k = 0; k < 3; k++) {
2710                 vect1[k] = vertexCoords[k][iv] - fncen[k][ifn];
2711               }
2712               normalize(vect1, vect1);
2713               for (int ke = 0; ke < 3; ke++) {
2714                 getVector(ai, fnvect, ke, ifn);
2715                 getVector(aj, vertexCoords, iv);
2716                 double dt = dot(ai, aj);
2717                 if (dt > 0.0) {
2718                   continue outerloop;
2719                 }
2720                 vip[kv] = true;
2721               }
2722             }
2723           }
2724         }
2725 
2726         outerLoop:
2727         for (int ifn = 0; ifn <= nConcaveFaces; ifn++) {
2728           for (int ke = 0; ke < 3; ke++) {
2729             if (nspt[ke][ifn] > 0) {
2730               badt[ifn] = true;
2731               break outerLoop;
2732             }
2733           }
2734         }
2735 
2736         double fourProbe2 = 4.0 * probe * probe;
2737         for (int ifn = 0; ifn <= nConcaveFaces; ifn++) {
2738           if (nlap[ifn] <= -1) {
2739             continue;
2740           }
2741           // Gather all overlapping probes.
2742           int nop = -1;
2743           for (int jfn = 0; jfn <= nConcaveFaces; jfn++) {
2744             if (ifn != jfn) {
2745               getVector(ai, fncen, ifn);
2746               getVector(aj, fncen, jfn);
2747               double dij2 = dist2(ai, aj);
2748               if (dij2 <= fourProbe2 && depths[jfn] <= probe) {
2749                 nop++;
2750                 if (nop >= maxop) {
2751                   throw new EnergyException(" NOP Overflow in VAM");
2752                 }
2753                 ifnop[nop] = jfn;
2754                 for (int k = 0; k < 3; k++) {
2755                   cenop[k][nop] = fncen[k][jfn];
2756                 }
2757               }
2758             }
2759           }
2760 
2761           // Numerical calculation of the correction.
2762           double areado = 0.0;
2763           double voldo = 0.0;
2764           double scinc = 1.0 / nscale;
2765           for (int isc = 0; isc < nscale; isc++) {
2766             double rsc = (isc + 1) - 0.5;
2767             dotv[isc] = probe * dota * rsc * rsc * scinc * scinc * scinc;
2768           }
2769           for (int iop = 0; iop <= nop; iop++) {
2770             ate[iop] = false;
2771           }
2772           int neatmx = 0;
2773           dotLoop:
2774           for (int idot = 0; idot < ndots; idot++) {
2775             for (int ke = 0; ke < 3; ke++) {
2776               getVector(ai, fnvect, ke, ifn);
2777               getVector(aj, dots, idot);
2778               double dt = dot(ai, aj);
2779               if (dt > 0.0) {
2780                 continue dotLoop;
2781               }
2782             }
2783             for (int k = 0; k < 3; k++) {
2784               tdots[k][idot] = fncen[k][ifn] + dots[k][idot];
2785             }
2786             for (int iop = 0; iop <= nop; iop++) {
2787               int jfn = ifnop[iop];
2788               getVector(ai, tdots, idot);
2789               getVector(aj, fncen, jfn);
2790               double ds2 = dist2(ai, aj);
2791               if (ds2 < probe * probe) {
2792                 areado += dota;
2793                 break;
2794               }
2795             }
2796             for (int isc = 0; isc < nscale; isc++) {
2797               double rsc = (isc + 1) - 0.5;
2798               for (int k = 0; k < 3; k++) {
2799                 sdot[k] = fncen[k][ifn] + rsc * scinc * dots[k][idot];
2800               }
2801               int neat = 0;
2802               optLoop:
2803               for (int iop = 0; iop <= nop; iop++) {
2804                 int jfn = ifnop[iop];
2805                 getVector(ai, fncen, jfn);
2806                 double ds2 = dist2(sdot, ai);
2807                 if (ds2 < probe * probe) {
2808                   for (int k = 0; k < 3; k++) {
2809                     vect1[k] = sdot[k] - fncen[k][jfn];
2810                   }
2811                   for (int ke = 0; ke < 3; ke++) {
2812                     getVector(ai, fnvect, ke, jfn);
2813                     double dt = dot(ai, vect1);
2814                     if (dt > 0.0) {
2815                       continue optLoop;
2816                     }
2817                   }
2818                   neat++;
2819                   ate[iop] = true;
2820                 }
2821               }
2822               if (neat > neatmx) {
2823                 neatmx = neat;
2824               }
2825               if (neat > 0) {
2826                 voldo += dotv[isc] * (neat / (1.0 + neat));
2827               }
2828             }
2829           }
2830           double coran = areado;
2831           double corvn = voldo;
2832           int nate = 0;
2833           for (int iop = 0; iop <= nop; iop++) {
2834             if (ate[iop]) {
2835               nate++;
2836             }
2837           }
2838 
2839           // Use either the analytical or numerical correction.
2840           boolean usenum = (nate > nlap[ifn] + 1 || neatmx > 1 || badt[ifn]);
2841           if (usenum) {
2842             cora[ifn] = coran;
2843             corv[ifn] = corvn;
2844             lensAreaNumeric += cora[ifn];
2845             lensVolumeNumeric += corv[ifn];
2846           } else if (badav[ifn]) {
2847             corv[ifn] = corvn;
2848             lensVolumeNumeric += corv[ifn];
2849           }
2850           lensArea += cora[ifn];
2851           lensVolume += corv[ifn];
2852         }
2853       }
2854 
2855       if (logger.isLoggable(Level.FINE)) {
2856         logger.fine(
2857             format(
2858                 " Convex Faces:     %6d Area: %12.6f Volume: %12.6f",
2859                 nConvexFaces + 1, convexFaceArea, convexFaceVolume));
2860         logger.fine(
2861             format(
2862                 " Saddle Faces:     %6d Area: %12.6f Volume: %12.6f",
2863                 nSaddleFaces + 1, saddleFaceArea, saddleFaceVolume));
2864         logger.fine(
2865             format(
2866                 " Concave Faces:    %6d Area: %12.6f Volume: %12.6f",
2867                 nConcaveFaces + 1, concaveFaceArea, concaveFaceVolume));
2868         logger.fine(
2869             format(
2870                 " Buried Polyhedron:                          Volume: %12.6f", polyhedronVolume));
2871         if (probe > 0.0) {
2872           logger.fine(
2873               format(
2874                   "\n Spindle Correction:            Area: %12.6f Volume: %12.6f",
2875                   -spindleArea, -spindleVolume));
2876           logger.fine(
2877               format(
2878                   " Lens Analytic Correction:      Area: %12.6f Volume: %12.6f",
2879                   -lensArea - lensAreaNumeric, lensVolume - lensVolumeNumeric));
2880           logger.fine(
2881               format(
2882                   " Lens Numerical Correction:     Area: %12.6f Volume: %12.6f",
2883                   lensAreaNumeric, lensVolumeNumeric));
2884         }
2885       }
2886 
2887       // Finally, compute the total area and total volume.
2888       double area = convexFaceArea + saddleFaceArea + concaveFaceArea - spindleArea - lensArea;
2889       double volume =
2890           convexFaceVolume
2891               + saddleFaceVolume
2892               + concaveFaceVolume
2893               + polyhedronVolume
2894               - spindleVolume
2895               + lensVolume;
2896 
2897       localVolume += volume;
2898       localSurfaceArea += area;
2899     }
2900 
2901     /**
2902      * The triple method finds the triple product of three vectors; used as a service routine by the
2903      * Connolly surface area and volume computation.
2904      *
2905      * @param x Vector 1.
2906      * @param y Vector 2.
2907      * @param z Vector 3.
2908      * @return The triple product.
2909      */
2910     private double tripleProduct(double[] x, double[] y, double[] z) {
2911       double[] xy = new double[3];
2912       X(x, y, xy);
2913       return dot(xy, z);
2914     }
2915 
2916     /**
2917      * The insertConvexEdgeForAtom method inserts a convex edge into the linked list for an atom.
2918      *
2919      * @param edgeNumber The edge number.
2920      * @param atomNumber The atom number.
2921      */
2922     private void insertConvexEdgeForAtom(int edgeNumber, int atomNumber) {
2923       if (edgeNumber <= -1) {
2924         throw new EnergyException(" Bad Edge Number in IPEDGE", true);
2925       }
2926       if (atomNumber <= -1) {
2927         throw new EnergyException(" Bad Atom Number in IPEDGE", true);
2928       }
2929       // Set beginning of list or add to end.
2930       if (convexEdgeFirst[atomNumber] == -1) {
2931         convexEdgeFirst[atomNumber] = edgeNumber;
2932       } else {
2933         convexEdgeNext[convexEdgeLast[atomNumber]] = edgeNumber;
2934       }
2935       convexEdgeNext[edgeNumber] = -1;
2936       convexEdgeLast[atomNumber] = edgeNumber;
2937     }
2938 
2939     /**
2940      * Check for eaten by neighbor.
2941      *
2942      * @param northPole  North pole vector.
2943      * @param unitVector Unit vector.
2944      * @param icy        Cycle index.
2945      * @return True if the angle sum is greater than 0.
2946      */
2947     private boolean ptincy(double[] northPole, double[] unitVector, int icy) {
2948       double[] acvect = new double[3];
2949       double[] cpvect = new double[3];
2950       double[][] projectedVertsForConvexEdges = new double[3][MAXCYEP];
2951       double[][] unitVectorsAlongEdges = new double[3][MAXCYEP];
2952       // Check for being eaten by neighbor.
2953       int iatom = -1;
2954       for (int ke = 0; ke <= convexEdgeCycleNum[icy]; ke++) {
2955         int iep = convexEdgeCycleNumbers[ke][icy];
2956         int ic = convexEdgeCircleNumber[iep];
2957         int it = circleTorusNumber[ic];
2958         iatom = circleAtomNumber[ic];
2959         int iaoth;
2960         if (torusAtomNumber[0][it] == iatom) {
2961           iaoth = torusAtomNumber[1][it];
2962         } else {
2963           iaoth = torusAtomNumber[0][it];
2964         }
2965         for (int k = 0; k < 3; k++) {
2966           acvect[k] = atomCoords[k][iaoth] - atomCoords[k][iatom];
2967           cpvect[k] = northPole[k] - circleCenter[k][ic];
2968         }
2969         if (dot(acvect, cpvect) >= 0.0) {
2970           return false;
2971         }
2972       }
2973       if (convexEdgeCycleNum[icy] <= 1) {
2974         return true;
2975       }
2976       if (projectedVertexForEachConvexEdge(
2977           northPole, unitVector, icy, iatom, projectedVertsForConvexEdges)) {
2978         return true;
2979       }
2980       int nEdges = convexEdgeCycleNum[icy];
2981       unitVectorsAlongEdges(projectedVertsForConvexEdges, nEdges, unitVectorsAlongEdges);
2982       double angleSum = sumOfAnglesAtVerticesForCycle(unitVectorsAlongEdges, nEdges, unitVector);
2983       return (angleSum > 0.0);
2984     }
2985 
2986     /**
2987      * Projected vertex for each convex edge.
2988      *
2989      * @param northPole                    North pole vector.
2990      * @param unitVector                   Unit vector.
2991      * @param icy                          Cycle number.
2992      * @param ia                           Atom number.
2993      * @param projectedVertsForConvexEdges Projected vertex for each convex edge.
2994      * @return Returns true if the projection terminates before completion.
2995      */
2996     private boolean projectedVertexForEachConvexEdge(
2997         double[] northPole,
2998         double[] unitVector,
2999         int icy,
3000         int ia,
3001         double[][] projectedVertsForConvexEdges) {
3002       double[] polev = new double[3];
3003       for (int ke = 0; ke <= convexEdgeCycleNum[icy]; ke++) {
3004         // Vertex number (use first vertex of edge).
3005         int iep = convexEdgeCycleNumbers[ke][icy];
3006         int iv = convexEdgeVertexNumber[0][iep];
3007         if (iv != -1) {
3008           // Vector from north pole to vertex.
3009           for (int k = 0; k < 3; k++) {
3010             polev[k] = vertexCoords[k][iv] - northPole[k];
3011           }
3012           // Calculate multiplication factor.
3013           double dt = dot(polev, unitVector);
3014           if (dt == 0.0) {
3015             return true;
3016           }
3017           double f = (2 * radius[ia]) / dt;
3018           if (f < 1.0) {
3019             return true;
3020           }
3021           // Projected vertex for this convex edge.
3022           for (int k = 0; k < 3; k++) {
3023             projectedVertsForConvexEdges[k][ke] = northPole[k] + f * polev[k];
3024           }
3025         }
3026       }
3027       return false;
3028     }
3029 
3030     /**
3031      * Sum of angles at vertices of cycle.
3032      *
3033      * @param unitVectorsAlongEdges Input vertices.
3034      * @param nEdges                Number of edges.
3035      * @param unitVector            Input vector.
3036      * @return Sum of angles at vertices of cycle
3037      */
3038     private double sumOfAnglesAtVerticesForCycle(
3039         double[][] unitVectorsAlongEdges, int nEdges, double[] unitVector) {
3040       double[] crs = new double[3];
3041       double[] ai = new double[3];
3042       double[] aj = new double[3];
3043       double sumOfAngles = 0.0;
3044       // Sum of angles at vertices of cycle.
3045       for (int ke = 0; ke <= nEdges; ke++) {
3046         double dt;
3047         if (ke < nEdges) {
3048           getVector(ai, unitVectorsAlongEdges, ke);
3049           getVector(aj, unitVectorsAlongEdges, ke + 1);
3050           dt = dot(ai, aj);
3051           X(ai, aj, crs);
3052         } else {
3053           // Closing edge of cycle.
3054           getVector(ai, unitVectorsAlongEdges, ke);
3055           getVector(aj, unitVectorsAlongEdges, 0);
3056           dt = dot(ai, aj);
3057           X(ai, aj, crs);
3058         }
3059         dt = check(dt);
3060         double ang = acos(dt);
3061         if (dot(crs, unitVector) > 0.0) {
3062           ang = -ang;
3063         }
3064         // Add to total for cycle.
3065         sumOfAngles += ang;
3066       }
3067       return sumOfAngles;
3068     }
3069 
3070     /**
3071      * Calculate unit vectors along edges.
3072      *
3073      * @param projectedVertsForConvexEdges Projected verts for convex edges.
3074      * @param nEdges                       Number of edges.
3075      * @param unitVectorsAlongEdges        Unit vectors along edges.
3076      */
3077     private void unitVectorsAlongEdges(
3078         double[][] projectedVertsForConvexEdges, int nEdges, double[][] unitVectorsAlongEdges) {
3079       double[] ai = new double[3];
3080       // Calculate unit vectors along edges.
3081       for (int ke = 0; ke <= nEdges; ke++) {
3082         // Get index of second edge of corner.
3083         int ke2;
3084         if (ke < nEdges) {
3085           ke2 = ke + 1;
3086         } else {
3087           ke2 = 0;
3088         }
3089         // Unit vector along edge of cycle.
3090         for (int k = 0; k < 3; k++) {
3091           unitVectorsAlongEdges[k][ke] =
3092               projectedVertsForConvexEdges[k][ke2] - projectedVertsForConvexEdges[k][ke];
3093         }
3094         getVector(ai, unitVectorsAlongEdges, ke);
3095         double epun = length(ai);
3096         if (epun <= 0.0) {
3097           throw new EnergyException(" Null Edge in Cycle", true);
3098         }
3099         // Normalize.
3100         if (epun > 0.0) {
3101           for (int k = 0; k < 3; k++) {
3102             unitVectorsAlongEdges[k][ke] = unitVectorsAlongEdges[k][ke] / epun;
3103           }
3104         } else {
3105           for (int k = 0; k < 3; k++) {
3106             unitVectorsAlongEdges[k][ke] = 0.0;
3107           }
3108         }
3109       }
3110       // Vectors for null edges come from following or preceding edges.
3111       for (int ke = 0; ke <= nEdges; ke++) {
3112         getVector(ai, unitVectorsAlongEdges, ke);
3113         if (length(ai) <= 0.0) {
3114           int le = ke - 1;
3115           if (le < 0) {
3116             le = nEdges;
3117           }
3118           for (int k = 0; k < 3; k++) {
3119             unitVectorsAlongEdges[k][ke] = unitVectorsAlongEdges[k][le];
3120           }
3121         }
3122       }
3123     }
3124 
3125     /**
3126      * The measpm method computes the volume of a single prism section of the full interior
3127      * polyhedron.
3128      */
3129     private double measurePrism(int ifn) {
3130       double[][] pav = new double[3][3];
3131       double[] vect1 = new double[3];
3132       double[] vect2 = new double[3];
3133       double[] vect3 = new double[3];
3134       double height = 0.0;
3135       for (int ke = 0; ke < 3; ke++) {
3136         int ien = concaveFaceEdgeNumbers[ke][ifn];
3137         int iv = concaveEdgeVertexNumbers[0][ien];
3138         int ia = vertexAtomNumbers[iv];
3139         height += atomCoords[2][ia];
3140         int ip = vertexProbeNumber[iv];
3141         for (int k = 0; k < 3; k++) {
3142           pav[k][ke] = atomCoords[k][ia] - probeCoords[k][ip];
3143         }
3144       }
3145       height *= 1.0 / 3.0;
3146       for (int k = 0; k < 3; k++) {
3147         vect1[k] = pav[k][1] - pav[k][0];
3148         vect2[k] = pav[k][2] - pav[k][0];
3149       }
3150       X(vect1, vect2, vect3);
3151       return height * vect3[2] / 2.0;
3152     }
3153 
3154     /**
3155      * Compute the area and volume of a convex face.
3156      *
3157      * @param iConvexFace The convex face index.
3158      * @param areaVolume  The measured area and volume.
3159      */
3160     private void measureConvexFace(int iConvexFace, double[] areaVolume) {
3161       double angle;
3162       double[] ai = new double[3];
3163       double[] aj = new double[3];
3164       double[] ak = new double[3];
3165       double[] vect1 = new double[3];
3166       double[] vect2 = new double[3];
3167       double[] acvect = new double[3];
3168       double[] aavect = new double[3];
3169       double[][][] tanv = new double[3][2][MAXCYEP];
3170       double[][] radial = new double[3][MAXCYEP];
3171       double pcurve = 0.0;
3172       double gcurve = 0.0;
3173       int ia = convexFaceAtomNumber[iConvexFace];
3174       int ncycle = convexFaceNumCycles[iConvexFace];
3175       int ieuler;
3176       if (ncycle > -1) {
3177         ieuler = 1 - ncycle;
3178       } else {
3179         ieuler = 2;
3180       }
3181       for (int icyptr = 0; icyptr < ncycle + 1; icyptr++) {
3182         int icy = convexFaceCycleNumbers[icyptr][iConvexFace];
3183         int nedge = convexEdgeCycleNum[icy];
3184         for (int ke = 0; ke < nedge + 1; ke++) {
3185           int iep = convexEdgeCycleNumbers[ke][icy];
3186           int ic = convexEdgeCircleNumber[iep];
3187           int it = circleTorusNumber[ic];
3188           int ia2;
3189           if (ia == torusAtomNumber[0][it]) {
3190             ia2 = torusAtomNumber[1][it];
3191           } else {
3192             ia2 = torusAtomNumber[0][it];
3193           }
3194           for (int k = 0; k < 3; k++) {
3195             acvect[k] = circleCenter[k][ic] - atomCoords[k][ia];
3196             aavect[k] = atomCoords[k][ia2] - atomCoords[k][ia];
3197           }
3198           normalize(aavect, aavect);
3199           double dt = dot(acvect, aavect);
3200           double geo = -dt / (radius[ia] * circleRadius[ic]);
3201           int iv1 = convexEdgeVertexNumber[0][iep];
3202           int iv2 = convexEdgeVertexNumber[1][iep];
3203           if (iv1 == -1 || iv2 == -1) {
3204             angle = 2.0 * PI;
3205           } else {
3206             for (int k = 0; k < 3; k++) {
3207               vect1[k] = vertexCoords[k][iv1] - circleCenter[k][ic];
3208               vect2[k] = vertexCoords[k][iv2] - circleCenter[k][ic];
3209               radial[k][ke] = vertexCoords[k][iv1] - atomCoords[k][ia];
3210             }
3211             getVector(ai, radial, ke);
3212             normalize(ai, ai);
3213             radial[0][ke] = ai[0];
3214             radial[1][ke] = ai[1];
3215             radial[2][ke] = ai[2];
3216             getVector(ai, tanv, 0, ke);
3217             X(vect1, aavect, aj);
3218             normalize(aj, aj);
3219             tanv[0][0][ke] = aj[0];
3220             tanv[1][0][ke] = aj[1];
3221             tanv[2][0][ke] = aj[2];
3222             getVector(ak, tanv, 1, ke);
3223             X(vect2, aavect, ak);
3224             normalize(ak, ak);
3225             tanv[0][1][ke] = ak[0];
3226             tanv[1][1][ke] = ak[1];
3227             tanv[2][1][ke] = ak[2];
3228             angle = vectorAngle(vect1, vect2, aavect, -1.0);
3229           }
3230           gcurve += circleRadius[ic] * angle * geo;
3231           if (nedge != 0) {
3232             if (ke > 0) {
3233               getVector(ai, tanv, 1, ke - 1);
3234               getVector(aj, tanv, 0, ke);
3235               getVector(ak, radial, ke);
3236               angle = vectorAngle(ai, aj, ak, 1.0);
3237               if (angle < 0.0) {
3238                 throw new EnergyException(" Negative Angle in measureConvexFace", true);
3239               }
3240               pcurve += angle;
3241             }
3242           }
3243         }
3244         if (nedge > 0) {
3245           getVector(ai, tanv, 1, nedge);
3246           getVector(aj, tanv, 0, 0);
3247           getVector(ak, radial, 0);
3248           angle = vectorAngle(ai, aj, ak, 1.0);
3249           if (angle < 0.0) {
3250             throw new EnergyException(" Negative Angle in measureConvexFace", true);
3251           }
3252           pcurve += angle;
3253         }
3254       }
3255       double gauss = 2.0 * PI * ieuler - pcurve - gcurve;
3256       double areap = gauss * (radius[ia] * radius[ia]);
3257       double volp = areap * radius[ia] / 3.0;
3258       areaVolume[0] = areap;
3259       areaVolume[1] = volp;
3260     }
3261 
3262     /**
3263      * Compute the area and volume of a saddle face.
3264      *
3265      * @param iSaddleFace The saddle face index.
3266      * @param areaVolume  The measured area and volume.
3267      */
3268     private void measureSaddleFace(int iSaddleFace, double[] areaVolume) {
3269       double areas;
3270       double vols;
3271       double areasp = 0.0;
3272       double volsp = 0.0;
3273       int iep = saddleConvexEdgeNumbers[0][iSaddleFace];
3274       int ic = convexEdgeCircleNumber[iep];
3275       int it = circleTorusNumber[ic];
3276       int ia1 = torusAtomNumber[0][it];
3277       int ia2 = torusAtomNumber[1][it];
3278       double[] aavect = new double[3];
3279       for (int k = 0; k < 3; k++) {
3280         aavect[k] = atomCoords[k][ia2] - atomCoords[k][ia1];
3281       }
3282       normalize(aavect, aavect);
3283       int iv1 = convexEdgeVertexNumber[0][iep];
3284       int iv2 = convexEdgeVertexNumber[1][iep];
3285       double phi;
3286       double[] vect1 = new double[3];
3287       double[] vect2 = new double[3];
3288       if (iv1 == -1 || iv2 == -1) {
3289         phi = 2.0 * PI;
3290       } else {
3291         for (int k = 0; k < 3; k++) {
3292           vect1[k] = vertexCoords[k][iv1] - circleCenter[k][ic];
3293           vect2[k] = vertexCoords[k][iv2] - circleCenter[k][ic];
3294         }
3295         phi = vectorAngle(vect1, vect2, aavect, 1.0);
3296       }
3297       for (int k = 0; k < 3; k++) {
3298         vect1[k] = atomCoords[k][ia1] - torusCenter[k][it];
3299         vect2[k] = atomCoords[k][ia2] - torusCenter[k][it];
3300       }
3301       double d1 = -1.0 * dot(vect1, aavect);
3302       double d2 = dot(vect2, aavect);
3303       double theta1 = atan2(d1, torusRadius[it]);
3304       double theta2 = atan2(d2, torusRadius[it]);
3305 
3306       // Check for cusps.
3307       double thetaq;
3308       boolean cusp;
3309       if (torusRadius[it] < probe && theta1 > 0.0 && theta2 > 0.0) {
3310         cusp = true;
3311         double rat = torusRadius[it] / probe;
3312         if (rat > 1.0) {
3313           rat = 1.0;
3314         } else if (rat < -1.0) {
3315           rat = -1.0;
3316         }
3317         thetaq = acos(rat);
3318       } else {
3319         cusp = false;
3320         thetaq = 0.0;
3321         areasp = 0.0;
3322         volsp = 0.0;
3323       }
3324       double term1 = torusRadius[it] * probe * (theta1 + theta2);
3325       double term2 = (probe * probe) * (sin(theta1) + sin(theta2));
3326       areas = phi * (term1 - term2);
3327       if (cusp) {
3328         double spin = torusRadius[it] * probe * thetaq - probe * probe * sin(thetaq);
3329         areasp = 2.0 * phi * spin;
3330       }
3331 
3332       iep = saddleConvexEdgeNumbers[0][iSaddleFace];
3333       int ic2 = convexEdgeCircleNumber[iep];
3334       iep = saddleConvexEdgeNumbers[1][iSaddleFace];
3335       int ic1 = convexEdgeCircleNumber[iep];
3336       if (circleAtomNumber[ic1] != ia1) {
3337         throw new EnergyException(" Atom number inconsistency in measureSaddleFace", true);
3338       }
3339       for (int k = 0; k < 3; k++) {
3340         vect1[k] = circleCenter[k][ic1] - atomCoords[k][ia1];
3341         vect2[k] = circleCenter[k][ic2] - atomCoords[k][ia2];
3342       }
3343       double w1 = dot(vect1, aavect);
3344       double w2 = -1.0 * dot(vect2, aavect);
3345       double cone1 = phi * ((w1 * circleRadius[ic1] * circleRadius[ic1])) / 6.0;
3346       double cone2 = phi * ((w2 * circleRadius[ic2] * circleRadius[ic2])) / 6.0;
3347       term1 = (torusRadius[it] * torusRadius[it]) * probe * (sin(theta1) + sin(theta2));
3348       term2 = sin(theta1) * cos(theta1) + theta1 + sin(theta2) * cos(theta2) + theta2;
3349       term2 = torusRadius[it] * (probe * probe) * term2;
3350       double term3 =
3351           sin(theta1) * cos(theta1) * cos(theta1)
3352               + 2.0 * sin(theta1)
3353               + sin(theta2) * cos(theta2) * cos(theta2)
3354               + 2.0 * sin(theta2);
3355       term3 = (probe * probe * probe / 3.0) * term3;
3356       double volt = (phi / 2.0) * (term1 - term2 + term3);
3357       vols = volt + cone1 + cone2;
3358       if (cusp) {
3359         term1 = (torusRadius[it] * torusRadius[it]) * probe * sin(thetaq);
3360         term2 = sin(thetaq) * cos(thetaq) + thetaq;
3361         term2 = torusRadius[it] * (probe * probe) * term2;
3362         term3 = sin(thetaq) * cos(thetaq) * cos(thetaq) + 2.0 * sin(thetaq);
3363         term3 = (probe * probe * probe / 3.0) * term3;
3364         volsp = phi * (term1 - term2 + term3);
3365       }
3366       areaVolume[0] = areas;
3367       areaVolume[1] = vols;
3368       areaVolume[2] = areasp;
3369       areaVolume[3] = volsp;
3370     }
3371 
3372     /**
3373      * Compute the area and volume of a concave face.
3374      *
3375      * @param iConcaveFace The concave face index.
3376      * @param areaVolume   The measured area and volume.
3377      */
3378     private void measureConcaveFace(int iConcaveFace, double[] areaVolume) {
3379       double[] ai = new double[3];
3380       double[] aj = new double[3];
3381       double[] ak = new double[3];
3382       double[] angle = new double[3];
3383       double[][] pvv = new double[3][3];
3384       double[][] pav = new double[3][3];
3385       double[][] planev = new double[3][3];
3386       double arean;
3387       double voln;
3388       for (int ke = 0; ke < 3; ke++) {
3389         int ien = concaveFaceEdgeNumbers[ke][iConcaveFace];
3390         int iv = concaveEdgeVertexNumbers[0][ien];
3391         int ia = vertexAtomNumbers[iv];
3392         int ip = vertexProbeNumber[iv];
3393         for (int k = 0; k < 3; k++) {
3394           pvv[k][ke] = vertexCoords[k][iv] - probeCoords[k][ip];
3395           pav[k][ke] = atomCoords[k][ia] - probeCoords[k][ip];
3396         }
3397         if (probe > 0.0) {
3398           getVector(ai, pvv, ke);
3399           normalize(ai, ai);
3400           putVector(ai, pvv, ke);
3401         }
3402       }
3403       if (probe <= 0.0) {
3404         arean = 0.0;
3405       } else {
3406         for (int ke = 0; ke < 3; ke++) {
3407           int je = ke + 1;
3408           if (je > 2) {
3409             je = 0;
3410           }
3411           getVector(ai, pvv, ke);
3412           getVector(aj, pvv, je);
3413           X(ai, aj, ak);
3414           normalize(ak, ak);
3415           putVector(ak, planev, ke);
3416         }
3417         for (int ke = 0; ke < 3; ke++) {
3418           int je = ke - 1;
3419           if (je < 0) {
3420             je = 2;
3421           }
3422           getVector(ai, planev, je);
3423           getVector(aj, planev, ke);
3424           getVector(ak, pvv, ke);
3425           angle[ke] = vectorAngle(ai, aj, ak, -1.0);
3426           if (angle[ke] < 0.0) {
3427             throw new EnergyException(" Negative angle in measureConcaveFace", true);
3428           }
3429         }
3430         double defect = 2.0 * PI - (angle[0] + angle[1] + angle[2]);
3431         arean = (probe * probe) * defect;
3432       }
3433       getVector(ai, pav, 0);
3434       getVector(aj, pav, 1);
3435       getVector(ak, pav, 2);
3436       double simplx = -tripleProduct(ai, aj, ak) / 6.0;
3437       voln = simplx - arean * probe / 3.0;
3438       areaVolume[0] = arean;
3439       areaVolume[1] = voln;
3440     }
3441 
3442     /**
3443      * The gendot method finds the coordinates of a specified number of surface points for a sphere
3444      * with the input radius.
3445      *
3446      * @param ndots  Number of dots to generate.
3447      * @param dots   Coordinates of generaged dots.
3448      * @param radius Sphere radius.
3449      */
3450     private int genDots(int ndots, double[][] dots, double radius) {
3451       int nequat = (int) sqrt(PI * ((double) ndots));
3452       int nvert = (int) (0.5 * (double) nequat);
3453       if (nvert < 1) {
3454         nvert = 1;
3455       }
3456       int k = -1;
3457       outerloop:
3458       for (int i = 0; i <= nvert; i++) {
3459         double fi = (PI * ((double) i)) / ((double) nvert);
3460         double z = cos(fi);
3461         double xy = sin(fi);
3462         int nhoriz = (int) (nequat * xy);
3463         if (nhoriz < 1) {
3464           nhoriz = 1;
3465         }
3466         for (int j = 0; j < nhoriz; j++) {
3467           double fj = (2.0 * PI * ((double) j)) / ((double) nhoriz);
3468           double x = cos(fj) * xy;
3469           double y = sin(fj) * xy;
3470           k++;
3471           dots[0][k] = x * radius;
3472           dots[1][k] = y * radius;
3473           dots[2][k] = z * radius;
3474           if (k >= ndots) {
3475             break outerloop;
3476           }
3477         }
3478       }
3479       return k;
3480     }
3481 
3482     /**
3483      * The cirpln method determines the points of intersection between a specified circle and plane.
3484      */
3485     private boolean circlePlane(
3486         double[] circen,
3487         double cirrad,
3488         double[] cirvec,
3489         double[] plncen,
3490         double[] plnvec,
3491         boolean[] cinsp,
3492         double[] xpnt1,
3493         double[] xpnt2) {
3494       double[] cpvect = new double[3];
3495       double[] pnt1 = new double[3];
3496       double[] vect1 = new double[3];
3497       double[] vect2 = new double[3];
3498       double[] uvect1 = new double[3];
3499       double[] uvect2 = new double[3];
3500       for (int k = 0; k < 3; k++) {
3501         cpvect[k] = plncen[k] - circen[k];
3502       }
3503       double dcp = dot(cpvect, plnvec);
3504       cinsp[0] = (dcp > 0.0);
3505       X(plnvec, cirvec, vect1);
3506       if (length(vect1) > 0.0) {
3507         normalize(vect1, uvect1);
3508         X(cirvec, uvect1, vect2);
3509         if (length(vect2) > 0.0) {
3510           normalize(vect2, uvect2);
3511           double dir = dot(uvect2, plnvec);
3512           if (dir != 0.0) {
3513             double ratio = dcp / dir;
3514             if (abs(ratio) <= cirrad) {
3515               for (int k = 0; k < 3; k++) {
3516                 pnt1[k] = circen[k] + ratio * uvect2[k];
3517               }
3518               double rlen = cirrad * cirrad - ratio * ratio;
3519               if (rlen < 0.0) {
3520                 rlen = 0.0;
3521               }
3522               rlen = sqrt(rlen);
3523               for (int k = 0; k < 3; k++) {
3524                 xpnt1[k] = pnt1[k] - rlen * uvect1[k];
3525                 xpnt2[k] = pnt1[k] + rlen * uvect1[k];
3526               }
3527               return true;
3528             }
3529           }
3530         }
3531       }
3532       return false;
3533     }
3534 
3535     /**
3536      * The vecang method finds the angle between two vectors handed with respect to a coordinate
3537      * axis.
3538      *
3539      * @param v1   First vector.
3540      * @param v2   Second vector.
3541      * @param axis Axis.
3542      * @param hand Hand.
3543      * @return An angle in the range [0, 2*PI].
3544      */
3545     private double vectorAngle(double[] v1, double[] v2, double[] axis, double hand) {
3546       double a1 = length(v1);
3547       double a2 = length(v2);
3548       double dt = dot(v1, v2);
3549       double a12 = a1 * a2;
3550       if (abs(a12) != 0.0) {
3551         dt = dt / a12;
3552       }
3553       dt = check(dt);
3554       double angle = acos(dt);
3555       if (hand * tripleProduct(v1, v2, axis) < 0.0) {
3556         return 2.0 * PI - angle;
3557       } else {
3558         return angle;
3559       }
3560     }
3561 
3562     /**
3563      * Compute the probe depth.
3564      *
3565      * @param ip  The probe number.
3566      * @param alt The depth vector.
3567      * @return The dot product.
3568      */
3569     private double depth(int ip, double[] alt) {
3570       double[] vect1 = new double[3];
3571       double[] vect2 = new double[3];
3572       double[] vect3 = new double[3];
3573       double[] vect4 = new double[3];
3574       int ia1 = probeAtomNumbers[0][ip];
3575       int ia2 = probeAtomNumbers[1][ip];
3576       int ia3 = probeAtomNumbers[2][ip];
3577       for (int k = 0; k < 3; k++) {
3578         vect1[k] = atomCoords[k][ia1] - atomCoords[k][ia3];
3579         vect2[k] = atomCoords[k][ia2] - atomCoords[k][ia3];
3580         vect3[k] = probeCoords[k][ip] - atomCoords[k][ia3];
3581       }
3582       X(vect1, vect2, vect4);
3583       normalize(vect4, vect4);
3584       double dot = dot(vect4, vect3);
3585       arraycopy(vect4, 0, alt, 0, 3);
3586       return dot;
3587     }
3588 
3589     /**
3590      * Constrain angle to be in the range -1.0 <= angle <= 1.0.
3591      *
3592      * @param angle The value to check.
3593      * @return the constrained value.
3594      */
3595     private double check(double angle) {
3596       return max(min(angle, 1.0), -1.0);
3597     }
3598 
3599     /**
3600      * Row major indexing (the last dimension is contiguous in memory).
3601      *
3602      * @param i x index.
3603      * @param j y index.
3604      * @param k z index.
3605      * @return the row major index.
3606      */
3607     private int index2(int i, int j, int k) {
3608       return k + MXCUBE * (j + MXCUBE * i);
3609     }
3610 
3611     private void getVector(double[] ai, double[][] temp, int index) {
3612       ai[0] = temp[0][index];
3613       ai[1] = temp[1][index];
3614       ai[2] = temp[2][index];
3615     }
3616 
3617     private void putVector(double[] ai, double[][] temp, int index) {
3618       temp[0][index] = ai[0];
3619       temp[1][index] = ai[1];
3620       temp[2][index] = ai[2];
3621     }
3622 
3623     private void getVector(double[] ai, double[][][] temp, int index1, int index2) {
3624       ai[0] = temp[0][index1][index2];
3625       ai[1] = temp[1][index1][index2];
3626       ai[2] = temp[2][index1][index2];
3627     }
3628 
3629     private void putVector(double[] ai, double[][][] temp, int index1, int index2) {
3630       temp[0][index1][index2] = ai[0];
3631       temp[1][index1][index2] = ai[1];
3632       temp[2][index1][index2] = ai[2];
3633     }
3634 
3635     private void computeVolumeGradient() {
3636       int[][] cube = new int[2][MXCUBE * MXCUBE * MXCUBE];
3637       int[] inov = new int[MAXARC];
3638       double[] arci = new double[MAXARC];
3639       double[] arcf = new double[MAXARC];
3640       double[] dx = new double[MAXARC];
3641       double[] dy = new double[MAXARC];
3642       double[] dsq = new double[MAXARC];
3643       double[] d = new double[MAXARC];
3644 
3645       // Initialize minimum and maximum range of atoms.
3646       double rmax = 0.0;
3647       double xmin = x[0];
3648       double xmax = x[0];
3649       double ymin = y[0];
3650       double ymax = y[0];
3651       double zmin = z[0];
3652       double zmax = z[0];
3653       for (int i = 0; i < nAtoms; i++) {
3654         if (radius[i] > 0.0) {
3655           if (radius[i] > rmax) {
3656             rmax = radius[i];
3657           }
3658           if (x[i] < xmin) {
3659             xmin = x[i];
3660           }
3661           if (x[i] > xmax) {
3662             xmax = x[i];
3663           }
3664           if (y[i] < ymin) {
3665             ymin = y[i];
3666           }
3667           if (y[i] > ymax) {
3668             ymax = y[i];
3669           }
3670           if (z[i] < zmin) {
3671             zmin = z[i];
3672           }
3673           if (z[i] > zmax) {
3674             zmax = z[i];
3675           }
3676         }
3677       }
3678 
3679       /*
3680        Fix the step size in the z-direction; this value sets the
3681        accuracy of the numerical derivatives; zstep=0.06 is a good
3682        balance between compute time and accuracy.
3683       */
3684       double zstep = 0.0601;
3685 
3686       // Load the cubes based on coarse lattice; first of all set edge
3687       // length to the maximum diameter of any atom.
3688       double edge = 2.0 * rmax;
3689       int nx = (int) ((xmax - xmin) / edge);
3690       int ny = (int) ((ymax - ymin) / edge);
3691       int nz = (int) ((zmax - zmin) / edge);
3692       if (max(max(nx, ny), nz) > MXCUBE) {
3693         throw new EnergyException(" VolumeRegion derivative  --  Increase the Value of mxcube");
3694       }
3695 
3696       // Initialize the coarse lattice of cubes.
3697       fill(cube[0], -1);
3698       fill(cube[1], -1);
3699 
3700       // Find the number of atoms in each cube.
3701       for (int m = 0; m < nAtoms; m++) {
3702         if (!skip[m]) {
3703           int i = (int) ((x[m] - xmin) / edge);
3704           int j = (int) ((y[m] - ymin) / edge);
3705           int k = (int) ((z[m] - zmin) / edge);
3706           cube[0][index2(i, j, k)]++;
3707         }
3708       }
3709 
3710       /*
3711        Determine the highest index in the array "itab" for the atoms
3712        that fall into each cube; the first cube that has atoms
3713        defines the first index for "itab"; the final index for the
3714        atoms in the present cube is the final index of the last cube
3715        plus the number of atoms in the present cube.
3716       */
3717       int isum = 0;
3718       int tcube;
3719       for (int i = 0; i <= nx; i++) {
3720         for (int j = 0; j <= ny; j++) {
3721           for (int k = 0; k <= nz; k++) {
3722             tcube = cube[0][index2(i, j, k)];
3723             if (tcube != -1) {
3724               isum += tcube;
3725               cube[1][index2(i, j, k)] = isum;
3726             }
3727           }
3728         }
3729       }
3730 
3731       // "cube(1,,,)" now contains a pointer to the array "itab"
3732       // giving the position of the last entry for the list of atoms
3733       // in that cube of total number equal to "cube(0,,,)".
3734       for (int m = 0; m < nAtoms; m++) {
3735         if (!skip[m]) {
3736           int i = (int) ((x[m] - xmin) / edge);
3737           int j = (int) ((y[m] - ymin) / edge);
3738           int k = (int) ((z[m] - zmin) / edge);
3739           tcube = cube[1][index2(i, j, k)];
3740           itab[tcube] = m;
3741           cube[1][index2(i, j, k)]--;
3742         }
3743       }
3744 
3745       // Set "cube(1,,,)" to be the starting index in "itab" for atom
3746       // list of that cube; and "cube(0,,,)" to be the stop index.
3747       isum = 0;
3748       for (int i = 0; i <= nx; i++) {
3749         for (int j = 0; j <= ny; j++) {
3750           for (int k = 0; k <= nz; k++) {
3751             tcube = cube[0][index2(i, j, k)];
3752             if (tcube != -1) {
3753               isum += tcube;
3754               cube[0][index2(i, j, k)] = isum;
3755               cube[1][index2(i, j, k)]++;
3756             }
3757           }
3758         }
3759       }
3760 
3761       // Process in turn each atom from the coordinate list; first
3762       // select the potential intersecting atoms.
3763       int ir;
3764       for (ir = 0; ir < nAtoms; ir++) {
3765         double pre_dx = 0.0;
3766         double pre_dy = 0.0;
3767         double pre_dz = 0.0;
3768         if (skip[ir]) {
3769           continue;
3770         }
3771         double rr = radius[ir];
3772         double rrx2 = 2.0 * rr;
3773         double rrsq = rr * rr;
3774         double xr = x[ir];
3775         double yr = y[ir];
3776         double zr = z[ir];
3777         // Find cubes to search for overlaps for current atom.
3778         int istart = (int) ((xr - xmin) / edge);
3779         int istop = min(istart + 2, nx + 1);
3780         istart = max(istart, 1);
3781         int jstart = (int) ((yr - ymin) / edge);
3782         int jstop = min(jstart + 2, ny + 1);
3783         jstart = max(jstart, 1);
3784         int kstart = (int) ((zr - zmin) / edge);
3785         int kstop = min(kstart + 2, nz + 1);
3786         kstart = max(kstart, 1);
3787         // Load all overlapping atoms into "inov".
3788         int io = -1;
3789         int in;
3790         for (int i = istart - 1; i < istop; i++) {
3791           for (int j = jstart - 1; j < jstop; j++) {
3792             for (int k = kstart - 1; k < kstop; k++) {
3793               int mstart = cube[1][index2(i, j, k)];
3794               if (mstart != -1) {
3795                 int mstop = cube[0][index2(i, j, k)];
3796                 for (int m = mstart; m <= mstop; m++) {
3797                   in = itab[m];
3798                   if (in != ir) {
3799                     io++;
3800                     if (io > MAXARC) {
3801                       logger.severe(" VolumeRegion gradient  --  Increase the Value of MAXARC");
3802                     }
3803                     dx[io] = x[in] - xr;
3804                     dy[io] = y[in] - yr;
3805                     dsq[io] = (dx[io] * dx[io]) + (dy[io] * dy[io]);
3806                     double dist2 = dsq[io] + ((z[in] - zr) * (z[in] - zr));
3807                     double vdwsum = (rr + radius[in]) * (rr + radius[in]);
3808                     if (dist2 > vdwsum || dist2 == 0.0) {
3809                       io--;
3810                     } else {
3811                       d[io] = sqrt(dsq[io]);
3812                       inov[io] = in;
3813                     }
3814                   }
3815                 }
3816               }
3817             }
3818           }
3819         }
3820 
3821         // Determine resolution along the z-axis.
3822         if (io != -1) {
3823           double ztop = zr + rr;
3824           double ztopshave = ztop - zstep;
3825           double zgrid = zr - rr;
3826           // Half of the part not covered by the planes.
3827           zgrid += 0.5 * (rrx2 - (((int) (rrx2 / zstep)) * zstep));
3828           double zstart = zgrid;
3829           // Section atom spheres perpendicular to the z-axis.
3830           while (zgrid <= ztop) {
3831             // "rsecr" is radius of circle of intersection of "ir" sphere on the current sphere.
3832             double rsec2r = rrsq - ((zgrid - zr) * (zgrid - zr));
3833             if (rsec2r < 0.0) {
3834               rsec2r = 0.000001;
3835             }
3836             double rsecr = sqrt(rsec2r);
3837             double phi1;
3838             double cos_phi1;
3839             if (zgrid >= ztopshave) {
3840               cos_phi1 = 1.0;
3841               phi1 = 0.0;
3842             } else {
3843               cos_phi1 = (zgrid + (0.5 * zstep) - zr) / rr;
3844               phi1 = acos(cos_phi1);
3845             }
3846             double phi2;
3847             double cos_phi2;
3848             if (zgrid == zstart) {
3849               cos_phi2 = -1.0;
3850               phi2 = PI;
3851             } else {
3852               cos_phi2 = (zgrid - (0.5 * zstep) - zr) / rr;
3853               phi2 = acos(cos_phi2);
3854             }
3855             // Check intersection of neighbor circles.
3856             int narc = -1;
3857             double twoPI = 2.0 * PI;
3858             for (int k = 0; k <= io; k++) {
3859               in = inov[k];
3860               double rinsq = radius[in] * radius[in];
3861               double rsec2n = rinsq - ((zgrid - z[in]) * (zgrid - z[in]));
3862               if (rsec2n > 0.0) {
3863                 double rsecn = sqrt(rsec2n);
3864                 if (d[k] < (rsecr + rsecn)) {
3865                   double rdiff = rsecr - rsecn;
3866                   if (d[k] <= abs(rdiff)) {
3867                     if (rdiff < 0.0) {
3868                       narc = 0;
3869                       arci[narc] = 0.0;
3870                       arcf[narc] = twoPI;
3871                     }
3872                     continue;
3873                   }
3874                   narc++;
3875                   if (narc > MAXARC) {
3876                     logger.severe(" VolumeRegion gradient  --  Increase the Value of MAXARC");
3877                   }
3878                   /*
3879                    Initial and final arc endpoints are
3880                    found for intersection of "ir" circle
3881                    with another circle contained in same
3882                    plane; the initial endpoint of the
3883                    enclosed arc is stored in "arci", the
3884                    final endpoint in "arcf"; get
3885                    "cosine" via law of cosines.
3886                   */
3887                   double cosine = (dsq[k] + rsec2r - rsec2n) / (2.0 * d[k] * rsecr);
3888                   cosine = min(1.0, max(-1.0, cosine));
3889                   /*
3890                    "alpha" is the angle between a line
3891                    containing either point of
3892                    intersection and the reference circle
3893                    center and the line containing both
3894                    circle centers; "beta" is the angle
3895                    between the line containing both
3896                    circle centers and x-axis.
3897                   */
3898                   double alpha = acos(cosine);
3899                   double beta = atan2(dy[k], dx[k]);
3900                   if (dy[k] < 0.0) {
3901                     beta += twoPI;
3902                   }
3903                   double ti = beta - alpha;
3904                   double tf = beta + alpha;
3905                   if (ti < 0.0) {
3906                     ti += twoPI;
3907                   }
3908                   if (tf > twoPI) {
3909                     tf -= twoPI;
3910                   }
3911                   arci[narc] = ti;
3912                   // If the arc crosses zero, then it is broken into two segments; the first
3913                   // ends at two pi and the second begins at zero.
3914                   if (tf < ti) {
3915                     arcf[narc] = twoPI;
3916                     narc++;
3917                     arci[narc] = 0.0;
3918                   }
3919                   arcf[narc] = tf;
3920                 }
3921               }
3922             }
3923 
3924             // Find the pre-area and pre-forces on this section
3925             // (band), "pre-" means a multiplicative factor is
3926             // yet to be applied.
3927             double seg_dz;
3928             if (narc == -1) {
3929               seg_dz = twoPI * ((cos_phi1 * cos_phi1) - (cos_phi2 * cos_phi2));
3930               pre_dz += seg_dz;
3931             } else {
3932               // Sort the arc endpoint arrays, each with
3933               // "narc" entries, in order of increasing values
3934               // of the arguments in "arci".
3935               for (int k = 0; k < narc; k++) {
3936                 double aa = arci[k];
3937                 double bb = arcf[k];
3938                 double temp = 1000000.0;
3939                 int itemp = 0;
3940                 for (int i = k; i <= narc; i++) {
3941                   if (arci[i] <= temp) {
3942                     temp = arci[i];
3943                     itemp = i;
3944                   }
3945                 }
3946                 arci[k] = arci[itemp];
3947                 arcf[k] = arcf[itemp];
3948                 arci[itemp] = aa;
3949                 arcf[itemp] = bb;
3950               }
3951               // Consolidate arcs by removing overlapping arc endpoints.
3952               double temp = arcf[0];
3953               int j = 0;
3954               for (int k = 1; k <= narc; k++) {
3955                 if (temp < arci[k]) {
3956                   arcf[j] = temp;
3957                   j++;
3958                   arci[j] = arci[k];
3959                   temp = arcf[k];
3960                 } else if (temp < arcf[k]) {
3961                   temp = arcf[k];
3962                 }
3963               }
3964               arcf[j] = temp;
3965               narc = j;
3966               if (narc == 0) {
3967                 narc = 1;
3968                 arcf[1] = twoPI;
3969                 arci[1] = arcf[0];
3970                 arcf[0] = arci[0];
3971                 arci[0] = 0.0;
3972               } else {
3973                 temp = arci[0];
3974                 for (int k = 0; k < narc; k++) {
3975                   arci[k] = arcf[k];
3976                   arcf[k] = arci[k + 1];
3977                 }
3978 
3979                 if (temp == 0.0 && arcf[narc] == twoPI) {
3980                   narc--;
3981                 } else {
3982                   arci[narc] = arcf[narc];
3983                   arcf[narc] = temp;
3984                 }
3985               }
3986 
3987               // Compute the numerical pre-derivative values.
3988               for (int k = 0; k <= narc; k++) {
3989                 double theta1 = arci[k];
3990                 double theta2 = arcf[k];
3991                 double dtheta;
3992                 if (theta2 >= theta1) {
3993                   dtheta = theta2 - theta1;
3994                 } else {
3995                   dtheta = (theta2 + twoPI) - theta1;
3996                 }
3997                 double phi_term = phi2 - phi1 - 0.5 * (sin(2.0 * phi2) - sin(2.0 * phi1));
3998                 double seg_dx = (sin(theta2) - sin(theta1)) * phi_term;
3999                 double seg_dy = (cos(theta1) - cos(theta2)) * phi_term;
4000                 seg_dz = dtheta * ((cos_phi1 * cos_phi1) - (cos_phi2 * cos_phi2));
4001                 pre_dx += seg_dx;
4002                 pre_dy += seg_dy;
4003                 pre_dz += seg_dz;
4004               }
4005             }
4006             zgrid += zstep;
4007           }
4008         }
4009         volumeGradient[0][ir] = 0.5 * rrsq * pre_dx;
4010         volumeGradient[1][ir] = 0.5 * rrsq * pre_dy;
4011         volumeGradient[2][ir] = 0.5 * rrsq * pre_dz;
4012       }
4013     }
4014   }
4015 }