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