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 }