View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2025.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.numerics.math;
39  
40  import javax.annotation.Nullable;
41  
42  import static java.lang.String.format;
43  import static org.apache.commons.math3.util.FastMath.abs;
44  import static org.apache.commons.math3.util.FastMath.acos;
45  import static org.apache.commons.math3.util.FastMath.max;
46  import static org.apache.commons.math3.util.FastMath.min;
47  import static org.apache.commons.math3.util.FastMath.sqrt;
48  
49  import java.util.logging.Logger;
50  
51  /**
52   * The DoubleMath class is a simple math library that operates on 3-coordinate double arrays.
53   *
54   * <p>All methods are static and thread-safe.
55   *
56   * <p>Use instances of Double3 for convenience.
57   *
58   * @author Michael J. Schnieders
59   * @since 1.0
60   */
61  public final class DoubleMath {
62  
63    private static final Logger logger = Logger.getLogger(DoubleMath.class.getName());
64  
65    private DoubleMath() {
66      // Prevent instantiation.
67    }
68  
69    /**
70     * Compute a * b + c and return the result in a new array.
71     *
72     * @param a First vector.
73     * @param b Scalar.
74     * @param c Second vector.
75     * @return Returns a * b + c.
76     */
77    public static double[] fma(double[] a, double b, double[] c) {
78      return fma(a, b, c, new double[3]);
79    }
80  
81    /**
82     * Compute a * b + c.
83     *
84     * @param a First vector.
85     * @param b Scalar.
86     * @param c Second vector.
87     * @param ret Result vector.
88     * @return Returns a * b + c.
89     */
90    public static double[] fma(double[] a, double b, double[] c, double[] ret) {
91      ret[0] = Math.fma(a[0], b, c[0]);
92      ret[1] = Math.fma(a[1], b, c[1]);
93      ret[2] = Math.fma(a[2], b, c[2]);
94      return ret;
95    }
96  
97    /**
98     * Finds the cross-product between two vectors
99     *
100    * @param a First vector
101    * @param b Second vector
102    * @return Returns the cross-product.
103    */
104   public static double[] X(double[] a, double[] b) {
105     return X(a, b, new double[3]);
106   }
107 
108   /**
109    * Finds the cross-product between two vectors
110    *
111    * @param a First vector
112    * @param b Second vector
113    * @param ret The cross-product of a x b.
114    * @return Returns the cross-product ret.
115    */
116   public static double[] X(double[] a, double[] b, double[] ret) {
117     ret[0] = a[1] * b[2] - a[2] * b[1];
118     ret[1] = a[2] * b[0] - a[0] * b[2];
119     ret[2] = a[0] * b[1] - a[1] * b[0];
120     return ret;
121   }
122 
123   /**
124    * sum
125    *
126    * @param a an array of double.
127    * @param b an array of double.
128    * @return Returns the sum array.
129    */
130   public static double[] add(double[] a, double[] b) {
131     return add(a, b, new double[3]);
132   }
133 
134   /**
135    * sum
136    *
137    * @param a an array of double.
138    * @param b an array of double.
139    * @param ret an array of double.
140    * @return Returns the array ret.
141    */
142   public static double[] add(double[] a, double[] b, double[] ret) {
143     ret[0] = a[0] + b[0];
144     ret[1] = a[1] + b[1];
145     ret[2] = a[2] + b[2];
146     return ret;
147   }
148 
149   /**
150    * angle
151    *
152    * @param i an array of double.
153    * @param j an array of double.
154    * @return Returns the angle.
155    */
156   public static double angle(double[] i, double[] j) {
157     var x = dot(normalize(i), normalize(j));
158     if (abs(x) > 1) {
159       if (x > 0) {
160         x = 1;
161       } else {
162         x = -1;
163       }
164     }
165     return acos(x);
166   }
167 
168   /**
169    * Finds the angle formed by three atoms.
170    *
171    * @param i Atom position vector.
172    * @param j Atom position vector (central atom).
173    * @param k Atom position vector.
174    * @return Return the angle in the range [ -pi, pi ].
175    */
176   public static double bondAngle(double[] i, double[] j, double[] k) {
177     return angle(sub(i, j), sub(k, j));
178   }
179 
180   /**
181    * Finds the dihedral angle formed between 4 atoms, a, b, c, d, via position
182    * vectors AB, BC, and CD.
183    *
184    * @param ab Position vector AB.
185    * @param bc Position vector BC.
186    * @param cd Position vector CD.
187    * @return The dihedral angle in the range [ -pi, pi ].
188    */
189   public static double dihedralAngle(double[] ab, double[] bc, double[] cd) {
190     var t = X(ab, bc);
191     var u = X(bc, cd);
192     var rt = dot(t, t);
193     var ru = dot(u, u);
194     var rtu = sqrt(rt * ru);
195     if (rtu != 0.0) {
196       var rcb = length(bc);
197       var cosine = dot(t, u) / rtu;
198       var tu = X(t, u);
199       var sine = dot(bc, tu) / (rcb * rtu);
200       cosine = min(1.0, max(-1.0, cosine));
201       var angle = acos(cosine);
202       if (sine < 0.0) {
203         angle = -angle;
204       }
205       return angle;
206     }
207     return 0;
208   }
209 
210   /**
211    * Finds the dihedral angle formed between 4 atoms.
212    *
213    * @param a Atom position vector.
214    * @param b Atom position vector.
215    * @param c Atom position vector.
216    * @param d Atom position vector.
217    * @return The dihedral angle in the range [ -pi, pi ].
218    */
219   public static double dihedralAngle(double[] a, double[] b, double[] c, double[] d) {
220     return dihedralAngle(sub(b, a), sub(c, b), sub(d, c));
221   }
222 
223   /**
224    * Finds the distance between two vectors.
225    *
226    * @param a First vector.
227    * @param b Second vector.
228    * @return Returns the distance between vectors a and b.
229    */
230   public static double dist(double[] a, double[] b) {
231     return sqrt(dist2(a, b));
232   }
233 
234   /**
235    * Finds the squared distance between two vectors
236    *
237    * @param a First vector.
238    * @param b Second vector.
239    * @return Returns the squared distance between vectors a and b.
240    */
241   public static double dist2(double[] a, double[] b) {
242     var dx = a[0] - b[0];
243     var dy = a[1] - b[1];
244     var dz = a[2] - b[2];
245     return Math.fma(dx, dx, Math.fma(dy, dy, dz * dz));
246   }
247 
248   /**
249    * Finds the dot product between two vectors.
250    *
251    * @param a First vector.
252    * @param b Second vector.
253    * @return Returns the dot product of a and b.
254    */
255   public static double dot(double[] a, double[] b) {
256     return Math.fma(a[0], b[0], Math.fma(a[1], b[1], a[2] * b[2]));
257   }
258 
259   /**
260    * Finds the length of a vector.
261    *
262    * @param d A vector to find the length of.
263    * @return Length of vector d.
264    */
265   public static double length(double[] d) {
266     return sqrt(length2(d));
267   }
268 
269   /**
270    * Finds the length^2 of a vector.
271    *
272    * @param d A vector to find the length of.
273    * @return Returns the length^2 of vector d.
274    */
275   public static double length2(double[] d) {
276     return Math.fma(d[0], d[0], Math.fma(d[1], d[1], d[2] * d[2]));
277   }
278 
279   /**
280    * logVector
281    *
282    * @param v an array of double.
283    */
284   public static void log(double[] v) {
285     logger.info(toString(v));
286   }
287 
288   /**
289    * logVector.
290    *
291    * @param v an array of double values.
292    * @param label a {@link String} object.
293    */
294   public static void log(double[] v, String label) {
295     logger.info(toString(v, label));
296   }
297 
298   /**
299    * Normalizes a vector.
300    *
301    * @param n A vector to be normalized.
302    * @return Returns the normalized vector.
303    */
304   public static double[] normalize(double[] n) {
305     return scale(n, 1.0 / length(n), new double[3]);
306   }
307 
308   /**
309    * Normalizes a vector.
310    *
311    * @param n A vector to be normalized.
312    * @param ret The normalized vector.
313    * @return Returns the normalized vector.
314    */
315   public static double[] normalize(double[] n, double[] ret) {
316     return scale(n, 1.0 / length(n), ret);
317   }
318 
319   /**
320    * Scales a vector.
321    *
322    * @param n A vector to be scaled.
323    * @param a A scalar value.
324    * @return Returns the scaled vector.
325    */
326   public static double[] scale(double[] n, double a) {
327     return scale(n, a, new double[3]);
328   }
329 
330   /**
331    * Scales a vector.
332    *
333    * @param n A vector to be scaled.
334    * @param a A scalar value.
335    * @param ret The scaled vector.
336    * @return Returns the array ret.
337    */
338   public static double[] scale(double[] n, double a, double[] ret) {
339     ret[0] = n[0] * a;
340     ret[1] = n[1] * a;
341     ret[2] = n[2] * a;
342     return ret;
343   }
344 
345   /**
346    * Squares values of a vector.
347    *
348    * @param n A vector to be squared.
349    * @return Returns the squared vector.
350    */
351   public static double[] square(double[] n) {return square(n, new double[3]);}
352 
353   /**
354    * Squares values of a vector.
355    *
356    * @param n A vector to be squared.
357    * @param ret The squared vector.
358    * @return Returns the array ret.
359    */
360   public static double[] square(double[] n, double[] ret) {
361     ret[0] = n[0] * n[0];
362     ret[1] = n[1] * n[1];
363     ret[2] = n[2] * n[2];
364     return ret;
365   }
366 
367   /**
368    * Square root values of a vector.
369    *
370    * @param n A vector to determine square root.
371    * @return Returns the rooted vector.
372    */
373   public static double[] squareRoot(double[] n) {return squareRoot(n, new double[3]);}
374 
375   /**
376    * Square root values of a vector.
377    *
378    * @param n A vector to determine square root.
379    * @param ret The rooted vector.
380    * @return Returns the array ret.
381    */
382   public static double[] squareRoot(double[] n, double[] ret) {
383     ret[0] = sqrt(n[0]);
384     ret[1] = sqrt(n[1]);
385     ret[2] = sqrt(n[2]);
386     return ret;
387   }
388 
389   /**
390    * Finds the difference between two vectors.
391    *
392    * @param a First vector
393    * @param b Second vector
394    * @return Returns the difference.
395    */
396   public static double[] sub(double[] a, double[] b) {
397     return sub(a, b, new double[3]);
398   }
399 
400   /**
401    * Finds the difference between two vectors.
402    *
403    * @param a First vector
404    * @param b Second vector
405    * @param ret Return Values
406    * @return Returns the difference ret.
407    */
408   public static double[] sub(double[] a, double[] b, double[] ret) {
409     ret[0] = a[0] - b[0];
410     ret[1] = a[1] - b[1];
411     ret[2] = a[2] - b[2];
412     return ret;
413   }
414 
415   /**
416    * logVector.
417    *
418    * @param v an array of double.
419    * @return Returns a String description of the vector.
420    */
421   public static String toString(double[] v) {
422     StringBuilder sb = new StringBuilder(" [ ");
423     for (double d : v) {
424       sb.append(format("%16.8f ", d));
425     }
426     sb.append("]");
427     return sb.toString();
428   }
429 
430   /**
431    * vectorToString.
432    *
433    * @param v an array of double values.
434    * @param label a {@link String} object.
435    * @return Returns a String description of the vector.
436    */
437   public static String toString(@Nullable double[] v, @Nullable String label) {
438     if (v == null) {
439       return null;
440     }
441     if (label == null) {
442       label = "v";
443     }
444     StringBuilder sb = new StringBuilder(format(" %16s = [", label));
445     for (double value : v) {
446       sb.append(format(" %16.8f", value));
447     }
448     sb.append(" ]");
449     return sb.toString();
450   }
451 }