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 }