1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2007-11-23 12:49:25 -0600 (Fri, 23 Nov 2007) $ 4 * $Revision: 8655 $ 5 * 6 * Copyright (C) 2003-2005 The Jmol Development Team 7 * 8 * Contact: jmol-developers@lists.sf.net 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 23 */ 24 25 package org.jmol.minimize.forcefield; 26 27 28 import java.io.BufferedReader; 29 import java.io.IOException; 30 31 import javajs.util.PT; 32 33 import javajs.util.BS; 34 import org.jmol.minimize.MinAngle; 35 import org.jmol.minimize.MinAtom; 36 import org.jmol.minimize.MinBond; 37 import org.jmol.minimize.MinPosition; 38 import org.jmol.minimize.MinTorsion; 39 import org.jmol.minimize.Minimizer; 40 import org.jmol.minimize.Util; 41 import org.jmol.util.Logger; 42 import org.jmol.viewer.FileManager; 43 import org.jmol.viewer.JmolAsyncException; 44 import org.jmol.viewer.Viewer; 45 46 abstract public class ForceField { 47 48 // same flags as for openBabel: 49 50 // terms 51 final static int ENERGY = (1 << 0); //!< all terms 52 final static int EBOND = (1 << 1); //!< bond term 53 final static int EANGLE = (1 << 2); //!< angle term 54 final static int ESTRBND = (1 << 3); //!< strbnd term 55 final static int ETORSION = (1 << 4); //!< torsion term 56 final static int EOOP = (1 << 5); //!< oop term 57 final static int EVDW = (1 << 6); //!< vdw term 58 final static int EELECTROSTATIC = (1 << 7); //!< electrostatic term 59 60 // indexes into the int[] array for angles and torsions for MMFF94 61 62 public final static int ABI_IJ = 3; // minBond index for IJ in IJK 63 public final static int ABI_JK = 4; // minBond index for JK in IJK 64 65 public final static int TBI_AB = 4; // minBond index for AB in ABCD 66 public final static int TBI_BC = 5; // minBond index for BC in ABCD 67 public final static int TBI_CD = 6; // minBond index for CD in ABCD 68 69 // indexes into vRings vector list of rings from SMARTS search in MMFF94 70 71 public static final int R3 = 0; 72 public static final int R4 = 1; 73 public static final int R5 = 2; 74 public static final int Raromatic = 3; 75 76 public String name; 77 78 Calculations calc; 79 80 private double criterion, e0, dE; 81 int currentStep; 82 private int stepMax; 83 private double[][] coordSaved; 84 85 int minAtomCount; 86 int minBondCount; 87 88 MinAtom[] minAtoms; 89 MinBond[] minBonds; 90 MinAngle[] minAngles; 91 MinTorsion[] minTorsions; 92 MinPosition[] minPositions; 93 BS bsFixed; 94 95 Minimizer minimizer; 96 clear()97 abstract public void clear(); setModel(BS bsElements, int elemnoMax)98 abstract public boolean setModel(BS bsElements, int elemnoMax) throws JmolAsyncException; 99 setModelFields()100 protected void setModelFields() { 101 this.minAtoms = minimizer.minAtoms; 102 this.minBonds = minimizer.minBonds; 103 this.minAngles = minimizer.minAngles; 104 this.minTorsions = minimizer.minTorsions; 105 // this.minPositions = minimizer.minPositions; // not implemented 106 this.bsFixed = minimizer.bsMinFixed; 107 minAtomCount = minAtoms.length; 108 minBondCount = minBonds.length; 109 } 110 setConstraints(Minimizer m)111 public void setConstraints(Minimizer m) { 112 this.bsFixed = m.bsMinFixed; 113 calc.setConstraints(m.constraints); 114 coordSaved = null; 115 } 116 117 ////////////////////////////////////////////////////////////////////////////////// 118 // 119 // Energy Minimization 120 // 121 ////////////////////////////////////////////////////////////////////////////////// 122 123 ////////////// calculation ///////////////// 124 steepestDescentInitialize(int stepMax, double criterion)125 public void steepestDescentInitialize(int stepMax, double criterion) { 126 this.stepMax = stepMax;//1000 127 // The criterion must be in the units of the calculation. 128 // However, the user is setting this, so they will be in Minimizer units. 129 // 130 this.criterion = criterion / toUserUnits(1); //1e-3 131 currentStep = 0; 132 clearForces(); 133 calc.setLoggingEnabled(true); 134 calc.setLoggingEnabled(stepMax == 0 || Logger.isActiveLevel(Logger.LEVEL_DEBUGHIGH)); 135 String s = name + " " + calc.getDebugHeader(-1) + "Jmol Minimization Version " + Viewer.getJmolVersion() + "\n"; 136 calc.appendLogData(s); 137 Logger.info(s); 138 calc.getConstraintList(); 139 if (calc.loggingEnabled) 140 calc.appendLogData(calc.getAtomList("S T E E P E S T D E S C E N T")); 141 dE = 0; 142 calc.setPreliminary(stepMax > 0); 143 e0 = energyFull(false, false); 144 s = PT.sprintf(" Initial " + name + " E = %10.3f " + minimizer.units + "/mol criterion = %8.6f max steps = " + stepMax, 145 "ff", new Object[] {Float.valueOf(toUserUnits(e0)), Float.valueOf(toUserUnits(criterion)) }); 146 minimizer.report(s, false); 147 calc.appendLogData(s); 148 } 149 clearForces()150 private void clearForces() { 151 for (int i = 0; i < minAtomCount; i++) 152 minAtoms[i].force[0] = minAtoms[i].force[1] = minAtoms[i].force[2] = 0; 153 } 154 155 //Vector3d dir = new Vector3d(); steepestDescentTakeNSteps(int n)156 public boolean steepestDescentTakeNSteps(int n) { 157 if (stepMax == 0) 158 return false; 159 boolean isPreliminary = true; 160 for (int iStep = 1; iStep <= n; iStep++) { 161 currentStep++; 162 calc.setSilent(true); 163 for (int i = 0; i < minAtomCount; i++) 164 if (bsFixed == null || !bsFixed.get(i)) 165 setForcesUsingNumericalDerivative(minAtoms[i], ENERGY); 166 linearSearch(); 167 calc.setSilent(false); 168 169 if (calc.loggingEnabled) 170 calc.appendLogData(calc.getAtomList("S T E P " + currentStep)); 171 172 double e1 = energyFull(false, false); 173 dE = e1 - e0; 174 boolean done = Util.isNear3(e1, e0, criterion); 175 176 if (done || currentStep % 10 == 0 || stepMax <= currentStep) { 177 String s = PT.sprintf(name + " Step %-4d E = %10.6f dE = %8.6f ", 178 "Fi", new Object[] {new float[] { toUserUnits(e1), toUserUnits(dE) }, 179 Integer.valueOf(currentStep) }); 180 minimizer.report(s, false); 181 calc.appendLogData(s); 182 } 183 e0 = e1; 184 if (done || stepMax <= currentStep) { 185 if (calc.loggingEnabled) 186 calc.appendLogData(calc.getAtomList("F I N A L G E O M E T R Y")); 187 if (done) { 188 String s = PT.formatStringF( 189 "\n " + name + " STEEPEST DESCENT HAS CONVERGED: E = %8.5f " + minimizer.units + "/mol after " + currentStep + " steps", "f", 190 toUserUnits(e1)); 191 calc.appendLogData(s); 192 minimizer.report(s, true); 193 Logger.info(s); 194 } 195 return false; 196 } 197 //System.out.println(isPreliminary + " " + getNormalizedDE() + " " + currentStep); 198 if (isPreliminary && getNormalizedDE() >= 2) { 199 // looking back at this after some time, I don't exactly see why I wanted 200 // this to stay in preliminary mode unless |DE| >= 2 * crit. 201 // It's hard to ever have |DE| NOT >= 2 * crit -- that would be very close to the criterion. 202 // And when that IS the case, why would you want to STAY in preliminary mode? Hmm. 203 calc.setPreliminary(isPreliminary = false); 204 e0 = energyFull(false, false); 205 } 206 } 207 return true; // continue 208 } 209 210 /** 211 * Get the energy of a given type or types. 212 * 213 * Note: gradients is always false 214 * 215 * @param terms 216 * @param gradients ignored (false) 217 * @return energy 218 */ getEnergies(int terms, boolean gradients)219 private double getEnergies(int terms, boolean gradients) { 220 if ((terms & ENERGY) != 0) 221 return energyFull(gradients, true); 222 double e = 0.0; 223 if ((terms & EBOND) != 0) 224 e += energyBond(gradients); 225 if ((terms & EANGLE) != 0) 226 e += energyAngle(gradients); 227 if ((terms & ESTRBND) != 0) 228 e += energyStretchBend(gradients); 229 if ((terms & EOOP) != 0) 230 e += energyOOP(gradients); 231 if ((terms & ETORSION) != 0) 232 e += energyTorsion(gradients); 233 if ((terms & EVDW) != 0) 234 e += energyVDW(gradients); 235 if ((terms & EELECTROSTATIC) != 0) 236 e += energyES(gradients); 237 return e; 238 } 239 240 // 241 // f(x + delta) - f(x) 242 // f'(x) = ------------------- 243 // delta 244 // setForcesUsingNumericalDerivative(MinAtom atom, int terms)245 private void setForcesUsingNumericalDerivative(MinAtom atom, int terms) { 246 double delta = 1.0e-5; 247 atom.force[0] = -getDE(atom, terms, 0, delta); 248 atom.force[1] = -getDE(atom, terms, 1, delta); 249 atom.force[2] = -getDE(atom, terms, 2, delta); 250 //if (atom.atom.getAtomIndex() == 2) 251 //System.out.println(" atom + " + atom.atom.getAtomIndex() + " force=" + atom.force[0] + " " + atom.force[1] + " " + atom.force[2] ); 252 return; 253 } 254 getDE(MinAtom atom, int terms, int i, double delta)255 private double getDE(MinAtom atom, int terms, int i, double delta) { 256 // get energy derivative 257 atom.coord[i] += delta; 258 double e = getEnergies(terms, false); 259 atom.coord[i] -= delta; 260 //if (atom.atom.getAtomIndex() == 2) 261 //System.out.println ((i==0 ? "\n" : "") + "atom 3: " + atom.atom.getInfo() + " " + i + " " + (e - e0) + " " + (Point3f)atom.atom + "{" + atom.coord[0] + " " + atom.coord[1] + " " + atom.coord[2] + "} delta=" + delta); 262 return (e - e0) / delta; 263 } 264 265 /* 266 // 267 // f(x + 2delta) - 2f(x + delta) + f(x) 268 // f''(x) = ------------------------------------ 269 // (delta)^2 270 // 271 void getNumericalSecondDerivative(MinAtom atom, int terms, Vector3d dir) { 272 double delta = 1.0e-5; 273 double e0 = getEnergy(terms, false); 274 double dx = getDx2(atom, terms, 0, e0, delta); 275 double dy = getDx2(atom, terms, 1, e0, delta); 276 double dz = getDx2(atom, terms, 2, e0, delta); 277 dir.set(dx, dy, dz); 278 } 279 280 private double getDx2(MinAtom atom, int terms, int i, 281 double e0, double delta) { 282 // calculate f(1) 283 atom.coord[i] += delta; 284 double e1 = getEnergy(terms, false); 285 // calculate f(2) 286 atom.coord[i] += delta; 287 double e2 = getEnergy(terms, false); 288 atom.coord[i] -= 2 * delta; 289 return (e2 - 2 * e1 + e0) / (delta * delta); 290 } 291 292 */ energyFull(boolean gradients, boolean isSilent)293 public double energyFull(boolean gradients, boolean isSilent) { 294 double energy; 295 296 if (gradients) 297 clearForces(); 298 299 energy = energyBond(gradients) + 300 energyAngle(gradients) 301 + energyTorsion(gradients) 302 + energyStretchBend(gradients) 303 + energyOOP(gradients) 304 + energyVDW(gradients) 305 + energyES(gradients); 306 307 if (!isSilent && calc.loggingEnabled) 308 calc.appendLogData(PT.sprintf("\nTOTAL %s ENERGY = %8.3f %s/mol\n", 309 "sfs", new Object[] {name, Float.valueOf(toUserUnits(energy)), minimizer.units })); 310 return energy; 311 } 312 313 /** 314 * 315 * @param gradients 316 * @return energy 317 */ energyStretchBend(boolean gradients)318 double energyStretchBend(boolean gradients) { 319 return calc.energyStretchBend(gradients); 320 } 321 energyBond(boolean gradients)322 double energyBond(boolean gradients) { 323 return calc.energyBond(gradients); 324 } 325 energyAngle(boolean gradients)326 double energyAngle(boolean gradients) { 327 return calc.energyAngle(gradients); 328 } 329 energyTorsion(boolean gradients)330 double energyTorsion(boolean gradients) { 331 return calc.energyTorsion(gradients); 332 } 333 energyOOP(boolean gradients)334 double energyOOP(boolean gradients) { 335 return calc.energyOOP(gradients); 336 } 337 338 // double energyPosition(boolean gradients) { 339 // return calc.energyPos(gradients); 340 // } 341 energyVDW(boolean gradients)342 double energyVDW(boolean gradients) { 343 return calc.energyVDW(gradients); 344 } 345 energyES(boolean gradients)346 double energyES(boolean gradients) { 347 return calc.energyES(gradients); 348 } 349 350 // linearSearch 351 // 352 // atom: coordinates of atom at iteration k (x_k) 353 // direction: search direction ( d = -grad(x_0) ) 354 // 355 // ALGORITHM: 356 // 357 // step = 1 358 // for (i = 1 to 100) { max steps = 100 359 // e_k = energy(x_k) energy of current iteration 360 // x_k = x_k + step * d update coordinates 361 // e_k+1 = energy(x_k+1) energy of next iteration 362 // 363 // if (e_k+1 < e_k) 364 // step = step * 1.2 increase step size 365 // if (e_k+1 > e_k) { 366 // x_k = x_k - step * d reset coordinates to previous iteration 367 // step = step * 0.5 reduce step size 368 // } 369 // if (e_k+1 == e_k) 370 // end convergence criteria reached, stop 371 // } 372 linearSearch()373 private void linearSearch() { 374 375 //double alpha = 0.0; // Scale factor along direction vector 376 double step = 0.23; 377 double trustRadius = 0.3; // don't move further than 0.3 Angstroms 378 double trustRadius2 = trustRadius * trustRadius; 379 380 double e1 = energyFull(false, true); 381 382 for (int iStep = 0; iStep < 10; iStep++) { 383 saveCoordinates(); 384 for (int i = 0; i < minAtomCount; ++i) 385 if (bsFixed == null || !bsFixed.get(i)) { 386 double[] force = minAtoms[i].force; 387 double[] coord = minAtoms[i].coord; 388 double f2 = (force[0] * force[0] + force[1] * force[1] + force[2] 389 * force[2]); 390 if (f2 > trustRadius2 / step / step) { 391 f2 = trustRadius / Math.sqrt(f2) / step; 392 // if (i == 2) 393 //System.out.println("atom 3: force/coord " + force[0] + " " + 394 // force[1] + " " + force[2] + "/" + coord[0] + " " + coord[1] + " " 395 // + coord[2] + " " + f2); 396 force[0] *= f2; 397 force[1] *= f2; 398 force[2] *= f2; 399 } 400 /* 401 * if (i == 2) f.println("#atom 3; draw " + "{" + coord[0] + " " + 402 * coord[1] + " " + coord[2] + "} " + "{" + (coord[0] + force[0]) + 403 * " " + (coord[1] + force[1]) + " " + (coord[2] + force[2]) +"}" ); 404 */for (int j = 0; j < 3; ++j) { 405 if (Util.isFinite(force[j])) { 406 double tempStep = force[j] * step; 407 if (tempStep > trustRadius) 408 coord[j] += trustRadius; 409 else if (tempStep < -trustRadius) 410 coord[j] -= trustRadius; 411 else 412 coord[j] += tempStep; 413 } 414 } 415 } 416 417 double e2 = energyFull(false, true); 418 419 //System.out.println("step is " + step + " " + (e2 < e1) + " " + e1 + " " 420 // + e2); 421 if (Util.isNear3(e2, e1, 1.0e-3)) 422 break; 423 if (e2 > e1) { 424 step *= 0.1; 425 restoreCoordinates(); 426 } else if (e2 < e1) { 427 e1 = e2; 428 //alpha += step; 429 step *= 2.15; 430 if (step > 1.0) 431 step = 1.0; 432 } 433 } 434 //System.out.println("alpha = " + alpha); 435 } 436 saveCoordinates()437 private void saveCoordinates() { 438 if (coordSaved == null) 439 coordSaved = new double[minAtomCount][3]; 440 for (int i = 0; i < minAtomCount; i++) 441 for (int j = 0; j < 3; j++) 442 coordSaved[i][j] = minAtoms[i].coord[j]; 443 } 444 restoreCoordinates()445 private void restoreCoordinates() { 446 for (int i = 0; i < minAtomCount; i++) 447 for (int j = 0; j < 3; j++) 448 minAtoms[i].coord[j] = coordSaved[i][j]; 449 } 450 detectExplosion()451 public boolean detectExplosion() { 452 for (int i = 0; i < minAtomCount; i++) { 453 MinAtom atom = minAtoms[i]; 454 for (int j = 0; j < 3; j++) 455 if (!Util.isFinite(atom.coord[j])) 456 return true; 457 } 458 for (int i = 0; i < minBondCount; i++) { 459 MinBond bond = minBonds[i]; 460 if (Util.distance2(minAtoms[bond.data[0]].coord, 461 minAtoms[bond.data[1]].coord) > 900.0) 462 return true; 463 } 464 return false; 465 } 466 getCurrentStep()467 public int getCurrentStep() { 468 return currentStep; 469 } 470 getEnergy()471 public double getEnergy() { 472 return e0; 473 } 474 getAtomList(String title)475 public String getAtomList(String title) { 476 return calc.getAtomList(title); 477 } 478 getEnergyDiff()479 public double getEnergyDiff() { 480 return dE; 481 } 482 getLogData()483 public String getLogData() { 484 return calc.getLogData(); 485 } 486 getNormalizedDE()487 double getNormalizedDE() { 488 return Math.abs(dE/criterion); 489 } 490 toUserUnits(double energy)491 public float toUserUnits(double energy) { 492 return toUnits(energy, calc.getUnits()); 493 } 494 toUnits(double energy, String units)495 private float toUnits(double energy, String units) { 496 return (float) (units.equalsIgnoreCase(minimizer.units) ? energy : energy 497 * (minimizer.units.equals("kJ") ? Calculations.KCAL_TO_KJ 498 : 1 / Calculations.KCAL_TO_KJ)); 499 } 500 log(String s)501 public void log(String s) { 502 calc.appendLogData(s); 503 } 504 getBufferedReader(String resourceName)505 protected BufferedReader getBufferedReader(String resourceName) throws IOException { 506 return FileManager.getBufferedReaderForResource(minimizer.vwr, this, 507 "org/jmol/minimize/forcefield/", "data/" + resourceName); 508 } 509 510 } 511