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