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 FloatMath class is a simple math library that operates on 3-coordinate float arrays.
53   *
54   * <p>All methods are static and thread-safe.
55   *
56   * <p>Use instances of Float3 for convenience.
57   *
58   * @author Michael J. Schnieders
59   * @since 1.0
60   */
61  public class FloatMath {
62  
63    private static final Logger logger = Logger.getLogger(FloatMath.class.getName());
64  
65    private FloatMath() {
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 float[] fma(float[] a, float b, float[] c) {
78      return fma(a, b, c, new float[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 in the vector ret.
89     */
90    public static float[] fma(float[] a, float b, float[] c, float[] 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 float[] X(float[] a, float[] b) {
105     return X(a, b, new float[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 float[] X(float[] a, float[] b, float[] 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 float.
127    * @param b an array of float.
128    * @return Returns the array ret.
129    */
130   public static float[] add(float[] a, float[] b) {
131     return add(a, b, new float[3]);
132   }
133 
134   /**
135    * sum
136    *
137    * @param a   an array of float.
138    * @param b   an array of float.
139    * @param ret an array of float.
140    * @return Returns the array ret.
141    */
142   public static float[] add(float[] a, float[] b, float[] 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 float.
153    * @param j an array of float.
154    * @return Returns the angle.
155    */
156   public static float angle(float[] i, float[] j) {
157     var x = dot(normalize(i), normalize(j));
158     if (abs(x) > 1) {
159       logger.warning(format(" Angle: abs(dot) > 1 %10.6f", x));
160       if (x > 0) {
161         x = 1;
162       } else {
163         x = -1;
164       }
165     }
166     return (float) acos(x);
167   }
168 
169   /**
170    * Finds the angle formed by three atoms
171    *
172    * @param i Atom position vector.
173    * @param j Atom position vector (central atom).
174    * @param k Atom position vector.
175    * @return Returns the angle in the range [ -pi, pi ].
176    */
177   public static float bondAngle(float[] i, float[] j, float[] k) {
178     return angle(sub(i, j), sub(k, j));
179   }
180 
181   /**
182    * Finds the dihedral angle formed between 4 atoms
183    *
184    * @param a Atom position vector.
185    * @param b Atom position vector.
186    * @param c Atom position vector.
187    * @param d Atom position vector.
188    * @return The dihedral angle in the range [ -pi, pi ].
189    */
190   public static float dihedralAngle(float[] a, float[] b, float[] c, float[] d) {
191     var ba = sub(b, a);
192     var cb = sub(c, b);
193     var dc = sub(d, c);
194     var t = X(ba, cb);
195     var u = X(cb, dc);
196     var rt = dot(t, t);
197     var ru = dot(u, u);
198     var rtu = (float) sqrt(rt * ru);
199     if (rtu != 0.0) {
200       var rcb = length(cb);
201       var cosine = dot(t, u) / rtu;
202       var tu = X(t, u);
203       var sine = dot(cb, tu) / (rcb * rtu);
204       cosine = min(1.0f, max(-1.0f, cosine));
205       var angle = (float) acos(cosine);
206       if (sine < 0.0) {
207         angle = -angle;
208       }
209       return angle;
210     }
211     return 0;
212   }
213 
214   /**
215    * Finds the distance between two vectors.
216    *
217    * @param a First vector.
218    * @param b Second vector.
219    * @return Returns the distance between vectors a and b.
220    */
221   public static float dist(float[] a, float[] b) {
222     return (float) sqrt(dist2(a, b));
223   }
224 
225   /**
226    * Finds the squared distance between two vectors.
227    *
228    * @param a First vector.
229    * @param b Second vector.
230    * @return Returns the squared distance between vectors a and b.
231    */
232   public static float dist2(float[] a, float[] b) {
233     var dx = a[0] - b[0];
234     var dy = a[1] - b[1];
235     var dz = a[2] - b[2];
236     return Math.fma(dx, dx, Math.fma(dy, dy, dz * dz));
237   }
238 
239   /**
240    * Finds the dot product between two vectors.
241    *
242    * @param a First vector.
243    * @param b Second vector.
244    * @return Returns the dot product of a and b.
245    */
246   public static float dot(float[] a, float[] b) {
247     return Math.fma(a[0], b[0], Math.fma(a[1], b[1], a[2] * b[2]));
248   }
249 
250   /**
251    * Finds the length of a vector.
252    *
253    * @param d A vector to find the length of.
254    * @return Returns the length of vector d.
255    */
256   public static float length(float[] d) {
257     return (float) sqrt(length2(d));
258   }
259 
260   /**
261    * Finds the length of a vector squared.
262    *
263    * @param d A vector to find the length of squared.
264    * @return Returns the length of vector d squared.
265    */
266   public static float length2(float[] d) {
267     return Math.fma(d[0], d[0], Math.fma(d[1], d[1], d[2] * d[2]));
268   }
269 
270   /**
271    * logVector
272    *
273    * @param v an array of float.
274    */
275   public static void log(float[] v) {
276     logger.info(toString(v));
277   }
278 
279   /**
280    * logVector.
281    *
282    * @param v     an array of float values.
283    * @param label a {@link String} object.
284    */
285   public static void log(float[] v, String label) {
286     logger.info(toString(v, label));
287   }
288 
289   /**
290    * Normalizes a vector.
291    *
292    * @param n A vector to be normalized.
293    * @return Returns the normalized vector.
294    */
295   public static float[] normalize(float[] n) {
296     return scale(n, (float) 1.0 / length(n), new float[3]);
297   }
298 
299   /**
300    * Normalizes a vector.
301    *
302    * @param n   A vector to be normalized.
303    * @param ret The normalized vector.
304    * @return Returns the normalized vector.
305    */
306   public static float[] normalize(float[] n, float[] ret) {
307     return scale(n, (float) 1.0 / length(n), ret);
308   }
309 
310   /**
311    * Scales a vector.
312    *
313    * @param n A vector to be scaled.
314    * @param a A scalar value.
315    * @return Returns the scaled vector.
316    */
317   public static float[] scale(float[] n, float a) {
318     return scale(n, a, new float[3]);
319   }
320 
321   /**
322    * Scales a vector.
323    *
324    * @param n   A vector to be scaled.
325    * @param a   A scalar value.
326    * @param ret The scaled Vector.
327    * @return Returns the array ret.
328    */
329   public static float[] scale(float[] n, float a, float[] ret) {
330     ret[0] = n[0] * a;
331     ret[1] = n[1] * a;
332     ret[2] = n[2] * a;
333     return ret;
334   }
335 
336 
337   /**
338    * Squares values of a vector.
339    *
340    * @param n A vector to be squared.
341    * @return Returns the squared vector.
342    */
343   public static float[] square(float[] n) {
344     return square(n, new float[3]);
345   }
346 
347   /**
348    * Squares values of a vector.
349    *
350    * @param n   A vector to be squared.
351    * @param ret The squared vector.
352    * @return Returns the array ret.
353    */
354   public static float[] square(float[] n, float[] ret) {
355     ret[0] = n[0] * n[0];
356     ret[1] = n[1] * n[1];
357     ret[2] = n[2] * n[2];
358     return ret;
359   }
360 
361   /**
362    * Square root values of a vector.
363    *
364    * @param n A vector to determine square root.
365    * @return Returns the rooted vector.
366    */
367   public static float[] squareRoot(float[] n) {
368     return squareRoot(n, new float[3]);
369   }
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 float[] squareRoot(float[] n, float[] ret) {
379     ret[0] = (float) sqrt(n[0]);
380     ret[1] = (float) sqrt(n[1]);
381     ret[2] = (float) 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 ret.
391    */
392   public static float[] sub(float[] a, float[] b) {
393     return sub(a, b, new float[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 float[] sub(float[] a, float[] b, float[] 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 float.
415    * @return Returns a String description of the vector.
416    */
417   public static String toString(float[] v) {
418     StringBuilder sb = new StringBuilder("Vector ( ");
419     for (float d : v) {
420       sb.append(format("%g ", d));
421     }
422     sb.append(")");
423     return sb.toString();
424   }
425 
426   /**
427    * vectorToString.
428    *
429    * @param v     an array of float values.
430    * @param label a {@link String} object.
431    * @return Returns a String description of the vector.
432    */
433   public static String toString(@Nullable float[] 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 (float value : v) {
442       sb.append(format(" %16.8f", value));
443     }
444     sb.append(" ]");
445     return sb.toString();
446   }
447 }