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 }