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