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 }