View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2021.
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.potential.bonded;
39  
40  import edu.rit.pj.reduction.SharedDouble;
41  import ffx.numerics.Constraint;
42  import ffx.potential.bonded.Atom.Resolution;
43  import java.util.ArrayList;
44  import java.util.Comparator;
45  import java.util.HashMap;
46  import java.util.List;
47  import java.util.Objects;
48  import java.util.logging.Logger;
49  import org.jogamp.java3d.BranchGroup;
50  import org.jogamp.java3d.Material;
51  import org.jogamp.vecmath.Color3f;
52  
53  /**
54   * The BondedTerm class is extended by all Valence Geometry classes (bond, angle, dihedral, torsion,
55   * etc.).
56   *
57   * @author Michael J. Schnieders
58   * @since 1.0
59   */
60  public abstract class BondedTerm extends MSNode implements BondedEnergy, Comparable<BondedTerm> {
61  
62    private static final Logger logger = Logger.getLogger(BondedTerm.class.getName());
63    /**
64     * This method sets the Term's id and key by concatenating the respective id and keys of the Atoms
65     * that are used in forming the term. Order can be reversed for help in assigning force field
66     * parameters for the Term.
67     */
68    private static final StringBuilder idtemp = new StringBuilder();
69    /** Constant <code>bondedComparator</code> */
70    private static final BondedComparator bondedComparator = new BondedComparator();
71    /** ID of this BondedTerm. */
72    protected String id;
73    /** Atoms that are used to form this term. */
74    protected Atom[] atoms;
75    /** Bonds that are used to form this term. */
76    protected Bond[] bonds;
77    /** Value of the term (e.g. bond length, angle, dihedral angle, etc). */
78    protected double value;
79    /** Energy of the term (kcal/mol). */
80    protected double energy;
81    /** If set, derivative components are filed by source type. */
82    private HashMap<Class<? extends BondedTerm>, SharedDouble> decompositionMap = null;
83    /** Flag indicating if this term is constrained. */
84    private boolean isConstrained = false;
85    /** Constraint on this BondedTerm, if any (else null). */
86    private Constraint constraint = null;
87  
88    /** Default Constructor */
89    public BondedTerm() {
90      super("", 1);
91      setAllowsChildren(false);
92    }
93  
94    /**
95     * Constructor which sets the Term's id.
96     *
97     * @param i a {@link java.lang.String} object.
98     */
99    public BondedTerm(String i) {
100     this();
101     id = i;
102   }
103 
104   /**
105    * Check if any atom of this BondedTerm has the Lambda flag set.
106    *
107    * @return True if Lambda is applied to one of the BondedTerm atoms.
108    */
109   public boolean applyLambda() {
110     for (Atom atom : atoms) {
111       if (atom.applyLambda()) {
112         return true;
113       }
114     }
115     return false;
116   }
117 
118   /** {@inheritDoc} */
119   @Override
120   public int compareTo(BondedTerm t) {
121     return Objects.compare(this, t, bondedComparator);
122   }
123 
124   /**
125    * containsHydrogen
126    *
127    * @return a boolean.
128    */
129   public boolean containsHydrogen() {
130     for (Atom atom : atoms) {
131       if (atom.isHydrogen()) {
132         return true;
133       }
134     }
135     return false;
136   }
137 
138   /**
139    * Checks if at least one atom in this BondedTerm is of the given resolution.
140    *
141    * @param resolution a {@link ffx.potential.bonded.Atom.Resolution} object.
142    * @return true if at least one atom in this term is of the specified resolution.
143    */
144   public boolean containsResolution(Resolution resolution) {
145     for (Atom atom : atoms) {
146       if (atom.getResolution() == resolution) {
147         return true;
148       }
149     }
150     return false;
151   }
152 
153   /** {@inheritDoc} */
154   @Override
155   public boolean destroy() {
156     super.destroy();
157     id = null;
158     value = 0;
159     return true;
160   }
161 
162   /**
163    * {@inheritDoc}
164    *
165    * <p>Overridden method that returns true if object is equals to this, is of the same Class and
166    * has the same id.
167    */
168   @Override
169   public final boolean equals(Object object) {
170     if (this == object) {
171       return true;
172     } else if (object == null || getClass() != object.getClass()) {
173       return false;
174     }
175     BondedTerm other = (BondedTerm) object;
176     return getID().equals(other.getID());
177   }
178 
179   /**
180    * Get the constituent Atom specified by index.
181    *
182    * @param index a int.
183    * @return a {@link ffx.potential.bonded.Atom} object.
184    */
185   public Atom getAtom(int index) {
186     if (index >= 0 && index < atoms.length) {
187       return atoms[index];
188     }
189     return null;
190   }
191 
192   /**
193    * Returns all of the Atoms contained in this BondedTerm, regardless of whether they are child
194    * nodes in the tree structure. Returns a new array, not a reference to the original array.
195    *
196    * @return Atoms in this BondedTerm
197    */
198   public Atom[] getAtomArray() {
199     return getAtomArray(true);
200   }
201 
202   /**
203    * Returns all of the Atoms contained in this BondedTerm, regardless of whether they are child
204    * nodes in the tree structure.
205    *
206    * @param returnCopy If true, return a new copy of the Atom array.
207    * @return Atoms in this BondedTerm
208    */
209   public Atom[] getAtomArray(boolean returnCopy) {
210     if (returnCopy) {
211       int nAtoms = atoms.length;
212       Atom[] retAtoms = new Atom[nAtoms];
213       System.arraycopy(atoms, 0, retAtoms, 0, nAtoms);
214       return retAtoms;
215     } else {
216       return atoms;
217     }
218   }
219 
220   /**
221    * Get the constituent Bond specified by index.
222    *
223    * @param index a int.
224    * @return a {@link ffx.potential.bonded.Bond} object.
225    */
226   public Bond getBond(int index) {
227     if (index >= 0 && index < atoms.length) {
228       return bonds[index];
229     }
230     return null;
231   }
232 
233   /**
234    * Get the Term's id.
235    *
236    * @return a {@link java.lang.String} object.
237    */
238   public String getID() {
239     return id;
240   }
241 
242   /**
243    * Sets the Term's id.
244    *
245    * @param i a {@link java.lang.String} object.
246    */
247   public void setID(String i) {
248     id = i;
249   }
250 
251   /**
252    * Get the Term's value.
253    *
254    * @return a double.
255    */
256   public double getValue() {
257     return value;
258   }
259 
260   /**
261    * Sets the Term's value.
262    *
263    * @param v a double.
264    */
265   public void setValue(double v) {
266     value = v;
267   }
268 
269   /** {@inheritDoc} */
270   @Override
271   public int hashCode() {
272     return Objects.hash(getID());
273   }
274 
275   /**
276    * Check if this BondedTerm is constrained.
277    *
278    * @return If constrained.
279    */
280   public boolean isConstrained() {
281     return isConstrained;
282   }
283 
284   /**
285    * Check if this BondedTerm is lambda-sensitive (e.g. a softcored dihedral).
286    *
287    * @return True if Lambda affects the energy of this term.
288    */
289   public boolean isLambdaScaled() {
290     return false;
291   }
292 
293   /**
294    * Checks if all atoms in this BondedTerm are of the given resolution.
295    *
296    * @param resolution a {@link ffx.potential.bonded.Atom.Resolution} object.
297    * @return true if all atoms in this term are at the same resolution.
298    */
299   public boolean isResolution(Resolution resolution) {
300     for (Atom atom : atoms) {
301       if (atom.getResolution() != resolution) {
302         return false;
303       }
304     }
305     return true;
306   }
307 
308   /**
309    * {@inheritDoc}
310    *
311    * <p>Prints the toString method to stdout
312    */
313   @Override
314   public void print() {
315     logger.info(toString());
316   }
317 
318   /**
319    * Add a constituent Atom to the Term.
320    *
321    * @param a an array of {@link ffx.potential.bonded.Atom} objects.
322    */
323   public void setAtoms(Atom[] a) {
324     atoms = a;
325   }
326 
327   /**
328    * Add constituent Bonds to the Term.
329    *
330    * @param b an array of {@link ffx.potential.bonded.Bond} objects.
331    */
332   public void setBonds(Bond[] b) {
333     bonds = b;
334   }
335 
336   /** {@inheritDoc} */
337   @Override
338   public void setColor(RendererCache.ColorModel newColorModel, Color3f color, Material mat) {
339     if (atoms == null) {
340       return;
341     }
342     for (Atom atom : atoms) {
343       atom.setColor(newColorModel, color, mat);
344     }
345   }
346 
347   /**
348    * Sets the Constraint on this bond (clearing it if null). May recursively set the Constraint on
349    * component terms (i.e. an Angle will call setConstraint on its component Bonds).
350    *
351    * @param c Constraint or null to clear.
352    */
353   public void setConstraint(Constraint c) {
354     this.constraint = c;
355     isConstrained = c != null;
356   }
357 
358   /**
359    * setID_Key
360    *
361    * @param reverse a boolean.
362    */
363   public final void setID_Key(boolean reverse) {
364     if (atoms == null) {
365       return;
366     }
367     // Reuse the string buffers
368     idtemp.delete(0, idtemp.length());
369     for (int i = 0; i < atoms.length; i++) {
370       Atom a = (reverse) ? atoms[atoms.length - 1 - i] : atoms[i];
371       if (i != 0) {
372         idtemp.append("  ");
373       }
374       idtemp.append(a.describe(Atom.Descriptions.XyzIndex_Name));
375     }
376     id = idtemp.toString().intern();
377   }
378 
379   /** {@inheritDoc} */
380   @Override
381   public void setSelected(boolean b) {
382     super.setSelected(b);
383     if (atoms == null) {
384       return;
385     }
386     for (Atom a : atoms) {
387       a.setSelected(b);
388     }
389     if (!(this instanceof Bond)) {
390       if (bonds == null) {
391         return;
392       }
393       for (Bond bond : bonds) {
394         bond.setSelected(b);
395       }
396     }
397   }
398 
399   /** {@inheritDoc} */
400   @Override
401   public void setView(RendererCache.ViewModel newViewModel, List<BranchGroup> newShapes) {
402     if (atoms == null) {
403       return;
404     }
405     for (Atom atom : atoms) {
406       atom.setView(newViewModel, newShapes);
407     }
408     if (bonds == null) {
409       return;
410     }
411     for (Bond bond : bonds) {
412       bond.setView(newViewModel, newShapes);
413     }
414   }
415 
416   /**
417    * {@inheritDoc}
418    *
419    * <p>Overidden toString Method returns the Term's id.
420    */
421   @Override
422   public String toString() {
423     return String.format("%s  (%7.2f,%7.2f)", id, value, energy);
424   }
425 
426   /**
427    * Check if all atoms of this BondedTerm have the Lambda flag set.
428    *
429    * @return True if Lambda is applied to all of the BondedTerm atoms.
430    */
431   boolean applyAllLambda() {
432     for (Atom atom : atoms) {
433       if (!atom.applyLambda()) {
434         return false;
435       }
436     }
437     return true;
438   }
439 
440   /**
441    * containsAtom
442    *
443    * @param atom a {@link ffx.potential.bonded.Atom} object.
444    * @return a boolean.
445    */
446   boolean containsAtom(Atom atom) {
447     for (Atom a : atoms) {
448       if (a.equals(atom)) {
449         return true;
450       }
451     }
452     return false;
453   }
454 
455   public static class BondedComparator implements Comparator<BondedTerm> {
456     private static final List<Class<? extends BondedTerm>> naturalOrder =
457         new ArrayList<>() {
458           {
459             add(Bond.class);
460             add(Angle.class);
461             add(StretchBend.class);
462             add(UreyBradley.class);
463             add(OutOfPlaneBend.class);
464             add(Torsion.class);
465             add(ImproperTorsion.class);
466             add(PiOrbitalTorsion.class);
467             add(StretchTorsion.class);
468             add(AngleTorsion.class);
469             add(TorsionTorsion.class);
470           }
471         };
472 
473     private BondedComparator() {} // singleton
474 
475     /** Sort using position in the naturalOrder list; fallback to alphabetical. */
476     @Override
477     public int compare(BondedTerm bondedTerm1, BondedTerm bondedTerm2) {
478       final Class<? extends BondedTerm> class1 = bondedTerm2.getClass();
479       final Class<? extends BondedTerm> class2 = bondedTerm1.getClass();
480       int order1 = naturalOrder.indexOf(bondedTerm1.getClass());
481       int order2 = naturalOrder.indexOf(bondedTerm2.getClass());
482       if (order1 >= 0 && order2 >= 0) {
483         return Integer.compare(order1, order2);
484       } else {
485         return String.CASE_INSENSITIVE_ORDER.compare(class2.toString(), class1.toString());
486       }
487     }
488   }
489 }