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 java.util.ArrayList;
41  import java.util.Collections;
42  import java.util.Enumeration;
43  import java.util.List;
44  import java.util.ListIterator;
45  import java.util.Objects;
46  import javax.swing.tree.DefaultMutableTreeNode;
47  import javax.swing.tree.TreeNode;
48  import org.jogamp.java3d.BranchGroup;
49  import org.jogamp.java3d.Canvas3D;
50  import org.jogamp.java3d.J3DGraphics2D;
51  import org.jogamp.java3d.Material;
52  import org.jogamp.java3d.Node;
53  import org.jogamp.vecmath.Color3f;
54  
55  /**
56   * The MSNode class forms the basic unit that all data classes extend.
57   *
58   * @author Michael J. Schnieders
59   * @since 1.0
60   */
61  @SuppressWarnings("CloneableImplementsClone")
62  public class MSNode extends DefaultMutableTreeNode implements ROLS {
63  
64    /** The multiscale level of this node. */
65    private final int MultiScaleLevel;
66    /** True if this node is selected. */
67    protected boolean selected = false;
68    /** The name of this node. */
69    private String name;
70    /** Total mass of this node and its children. */
71    private double totalMass;
72  
73    /** Default MSNode Constructor */
74    public MSNode() {
75      name = "";
76      MultiScaleLevel = ROLS.MaxLengthScale;
77    }
78  
79    /**
80     * Constructs a MSNode with the name n.
81     *
82     * @param n a {@link java.lang.String} object.
83     */
84    public MSNode(String n) {
85      name = n;
86      MultiScaleLevel = ROLS.MaxLengthScale;
87    }
88  
89    /**
90     * Constructor for MSNode.
91     *
92     * @param n a {@link java.lang.String} object.
93     * @param multiScaleLevel a int.
94     */
95    public MSNode(String n, int multiScaleLevel) {
96      this.name = n;
97      this.MultiScaleLevel = multiScaleLevel;
98    }
99  
100   /**
101    * If <code>this</code> MSNode or any MSNode below it <code>equals</code> the argument, that
102    * MSNode is returned.
103    *
104    * @param msNode a {@link ffx.potential.bonded.MSNode} object.
105    * @return a {@link ffx.potential.bonded.MSNode} object.
106    */
107   public MSNode contains(MSNode msNode) {
108     @SuppressWarnings("unchecked")
109     Enumeration<TreeNode> e = depthFirstEnumeration();
110     List<TreeNode> list = Collections.list(e);
111     for (TreeNode node : list) {
112       if (node.equals(msNode)) {
113         return (MSNode) node;
114       }
115     }
116     return null;
117   }
118 
119   /**
120    * destroy
121    *
122    * @return a boolean.
123    */
124   public boolean destroy() {
125     if (getParent() != null) {
126       removeFromParent();
127     }
128     name = null;
129     selected = false;
130     return true;
131   }
132 
133   /** {@inheritDoc} */
134   @Override
135   public void drawLabel(Canvas3D graphics, J3DGraphics2D g2d, Node node) {
136     if (!isSelected()) {
137       return;
138     }
139     MSNode dataNode;
140     for (Enumeration<?> e = children(); e.hasMoreElements(); ) {
141       dataNode = (MSNode) e.nextElement();
142       dataNode.drawLabel(graphics, g2d, node);
143     }
144   }
145 
146   /**
147    * {@inheritDoc}
148    *
149    * <p>MSNode equality := same class and same name. Consider replacing with a
150    * Comparator&lt;MSNode&gt; for cases where non-reference equality is desired.
151    */
152   @Override
153   public boolean equals(Object o) {
154     if (this == o) return true;
155     if (o == null || getClass() != o.getClass()) return false;
156     MSNode msNode = (MSNode) o;
157     return Objects.equals(name, msNode.getName());
158   }
159 
160   /**
161    * Returns an List of all Angles below the present MSNode.
162    *
163    * @return a {@link java.util.List} object.
164    */
165   public List<Angle> getAngleList() {
166     return getList(Angle.class, new ArrayList<>());
167   }
168 
169   /**
170    * Returns an List of all AngleTorsions below the present MSNode.
171    *
172    * @return a {@link java.util.List} object.
173    */
174   public List<AngleTorsion> getAngleTorsionList() {
175     return getList(AngleTorsion.class, new ArrayList<>());
176   }
177 
178   /**
179    * Returns an List of all Atoms below the present MSNode.
180    *
181    * @return a new {@link java.util.List} object.
182    */
183   public List<Atom> getAtomList() {
184     @SuppressWarnings("unchecked")
185     Enumeration<TreeNode> e = depthFirstEnumeration();
186     List<TreeNode> list = Collections.list(e);
187     List<Atom> arrayList = new ArrayList<>();
188     for (TreeNode node : list) {
189       if (node instanceof Atom) {
190         arrayList.add((Atom) node);
191       }
192     }
193 
194     Collections.sort(arrayList);
195     return arrayList;
196   }
197 
198   /**
199    * getAtomList.
200    *
201    * @param originalOrder a boolean.
202    * @return a {@link java.util.List} object.
203    */
204   public List<Atom> getAtomList(boolean originalOrder) {
205     // As of now, for generic MSNode objects, atoms remain in their original
206     // order. It is presently only a concern for MultiResidue.
207     return getAtomList();
208   }
209 
210   /**
211    * Returns an List of all Bonds below the present MSNode.
212    *
213    * @return a {@link java.util.List} object.
214    */
215   public List<Bond> getBondList() {
216     return getList(Bond.class, new ArrayList<>());
217   }
218 
219   /** {@inheritDoc} */
220   @Override
221   public double[] getCenter(boolean w) {
222     double[] Rc = {0, 0, 0};
223     double sum = 0, mass = 1;
224     List<Atom> atomList = getAtomList();
225     for (Atom a : atomList) {
226       if (w) {
227         mass = a.getMass();
228         sum += mass;
229       }
230       Rc[0] += mass * a.getX();
231       Rc[1] += mass * a.getY();
232       Rc[2] += mass * a.getZ();
233     }
234     if (!w) {
235       sum = atomList.size();
236     }
237     for (int i = 0; i < 3; i++) {
238       Rc[i] /= sum;
239     }
240     return Rc;
241   }
242 
243   /**
244    * Returns an List of the MSNode's Children (instead of using an Enumeration).
245    *
246    * @return a {@link java.util.List} object.
247    */
248   public List<MSNode> getChildList() {
249     List<MSNode> l = new ArrayList<>();
250     Enumeration<?> e = children();
251     while (e.hasMoreElements()) {
252       l.add((MSNode) e.nextElement());
253     }
254     return l;
255   }
256 
257   /**
258    * Request all descendants of the given type; returned list will automatically conform to any
259    * superclass thereof.
260    *
261    * @param c a {@link java.lang.Class} object.
262    * @param <U> a U object.
263    * @param <T> a T object.
264    * @return a {@link java.util.List} object.
265    */
266   public <U extends MSNode, T extends U> List<U> getDescendants(Class<T> c) {
267     List<U> nodes = new ArrayList<>();
268     castDescendants(c, nodes);
269     return nodes;
270   }
271 
272   /**
273    * getExtent
274    *
275    * @return a double.
276    */
277   public double getExtent() {
278     double extent = 0.0;
279     for (Enumeration<?> e = children(); e.hasMoreElements(); ) {
280       MSNode node = (MSNode) e.nextElement();
281       double temp = node.getExtent();
282       if (temp > extent) {
283         extent = temp;
284       }
285     }
286     return extent;
287   }
288 
289   /**
290    * Returns an List of all ImproperTorsions below the present MSNode.
291    *
292    * @return a {@link java.util.List} object.
293    */
294   public List<ImproperTorsion> getImproperTorsionList() {
295     return getList(ImproperTorsion.class, new ArrayList<>());
296   }
297 
298   /** {@inheritDoc} */
299   @Override
300   public <T> List<T> getList(Class<T> c, List<T> nodes) {
301     if (c.isInstance(this)) {
302       nodes.add(c.cast(this));
303     }
304     if (isLeaf() || !canBeChild(c)) {
305       return nodes;
306     }
307     for (Enumeration<?> e = children(); e.hasMoreElements(); ) {
308       MSNode node = (MSNode) e.nextElement();
309       node.getList(c, nodes);
310     }
311     return nodes;
312   }
313 
314   /** {@inheritDoc} */
315   @Override
316   public <T> long getMSCount(Class<T> c, long count) {
317     if (c.isInstance(this)) {
318       count++;
319     }
320     if (!canBeChild(c)) {
321       return count;
322     }
323     for (Enumeration<?> e = children(); e.hasMoreElements(); ) {
324       MSNode node = (MSNode) e.nextElement();
325       count += node.getMSCount(c, count);
326     }
327     return count;
328   }
329 
330   /** {@inheritDoc} */
331   @Override
332   public <T> T getMSNode(Class<T> c) {
333     TreeNode[] nodes = getPath();
334     for (TreeNode n : nodes) {
335       if (c.isInstance(n)) {
336         return c.cast(n);
337       }
338     }
339     return null;
340   }
341 
342   /** {@inheritDoc} */
343   @Override
344   public double getMW() {
345     double weight = 0.0;
346     for (Atom atom : getAtomList()) {
347       weight += atom.getMass();
348     }
349     return weight;
350   }
351 
352   /**
353    * getMultiScaleLevel
354    *
355    * @return a int.
356    */
357   public int getMultiScaleLevel() {
358     return MultiScaleLevel;
359   }
360 
361   /**
362    * Returns the name of this MSNode.
363    *
364    * @return a {@link java.lang.String} object.
365    */
366   public String getName() {
367     return name;
368   }
369 
370   /**
371    * Sets the name of this NodeObect to n.
372    *
373    * @param n a {@link java.lang.String} object.
374    */
375   public void setName(String n) {
376     name = n;
377   }
378 
379   /**
380    * Returns an List of all Out-of-Plane Bends below the present MSNode.
381    *
382    * @return a {@link java.util.List} object.
383    */
384   public List<OutOfPlaneBend> getOutOfPlaneBendList() {
385     return getList(OutOfPlaneBend.class, new ArrayList<>());
386   }
387 
388   /**
389    * Returns an List of all Pi-Orbital Torsions below the present MSNode.
390    *
391    * @return a {@link java.util.List} object.
392    */
393   public List<PiOrbitalTorsion> getPiOrbitalTorsionList() {
394     return getList(PiOrbitalTorsion.class, new ArrayList<>());
395   }
396 
397   /**
398    * Returns an List of all Stretch-Bends below the present MSNode.
399    *
400    * @return a {@link java.util.List} object.
401    */
402   public List<StretchBend> getStretchBendList() {
403     return getList(StretchBend.class, new ArrayList<>());
404   }
405 
406   /**
407    * Returns an List of all StretchTorsions below the present MSNode.
408    *
409    * @return a {@link java.util.List} object.
410    */
411   public List<StretchTorsion> getStretchTorsionList() {
412     return getList(StretchTorsion.class, new ArrayList<>());
413   }
414 
415   /**
416    * Returns an List of all Torsions below the present MSNode.
417    *
418    * @return a {@link java.util.List} object.
419    */
420   public List<Torsion> getTorsionList() {
421     return getList(Torsion.class, new ArrayList<>());
422   }
423 
424   /**
425    * Returns an List of all Torsion-Torsions below the present MSNode.
426    *
427    * @return a {@link java.util.List} object.
428    */
429   public List<TorsionTorsion> getTorsionTorsionList() {
430     return getList(TorsionTorsion.class, new ArrayList<>());
431   }
432 
433   /**
434    * Returns the total mass of all atoms in the MolecularAssembly, calculating the mass if it has
435    * not already been done, defaulting to simple addition.
436    *
437    * @return Total mass of atoms in system.
438    */
439   public double getTotalMass() {
440     if (totalMass == 0.0) {
441       return getTotalMass(true, false);
442     }
443     return totalMass;
444   }
445 
446   /**
447    * Returns an List of all Urey-Bradleys below the present MSNode.
448    *
449    * @return a {@link java.util.List} object.
450    */
451   public List<UreyBradley> getUreyBradleyList() {
452     return getList(UreyBradley.class, new ArrayList<>());
453   }
454 
455   /** {@inheritDoc} */
456   @Override
457   public int hashCode() {
458     return Objects.hash(name);
459   }
460 
461   /**
462    * isSelected
463    *
464    * @return a boolean.
465    */
466   public boolean isSelected() {
467     return selected;
468   }
469 
470   /**
471    * Setter for the field <code>selected</code>.
472    *
473    * @param b a boolean.
474    */
475   public void setSelected(boolean b) {
476     selected = b;
477     for (Enumeration<?> e = children(); e.hasMoreElements(); ) {
478       MSNode node = (MSNode) e.nextElement();
479       node.setSelected(b);
480     }
481   }
482 
483   /** Prints the MSNode's name */
484   public void print() {
485     System.out.println(name);
486   }
487 
488   /**
489    * removeChild.
490    *
491    * @param child a {@link ffx.potential.bonded.MSNode} object.
492    */
493   public void removeChild(MSNode child) {
494     if (child != null && child.getParent() == this) {
495       remove(child);
496     }
497   }
498 
499   /** {@inheritDoc} */
500   @Override
501   public void setColor(RendererCache.ColorModel colorModel, Color3f color, Material mat) {
502     for (Enumeration<?> e = children(); e.hasMoreElements(); ) {
503       MSNode node = (MSNode) e.nextElement();
504       node.setColor(colorModel, color, mat);
505     }
506   }
507 
508   /** {@inheritDoc} */
509   @Override
510   public void setView(RendererCache.ViewModel viewModel, List<BranchGroup> newShapes) {
511     for (Enumeration<?> e = children(); e.hasMoreElements(); ) {
512       MSNode node = (MSNode) e.nextElement();
513       node.setView(viewModel, newShapes);
514     }
515   }
516 
517   /**
518    * {@inheritDoc}
519    *
520    * <p>Overidden toString method returns the MSNode's name
521    */
522   @Override
523   public String toString() {
524     return name;
525   }
526 
527   /** {@inheritDoc} */
528   @Override
529   public void update() {
530     for (Enumeration<?> e = children(); e.hasMoreElements(); ) {
531       MSNode node = (MSNode) e.nextElement();
532       node.update();
533     }
534   }
535 
536   /**
537    * Returns a ListIterator containing this MSNode's children.
538    *
539    * @return a {@link java.util.ListIterator} object.
540    */
541   ListIterator<MSNode> getChildListIterator() {
542     return getChildList().listIterator();
543   }
544 
545   /**
546    * @param c a {@link java.lang.Class} object.
547    * @param nodes Nodes
548    * @param <U> a U object.
549    * @param <T> a T object.
550    */
551   private <U extends MSNode, T extends U> void castDescendants(Class<T> c, List<U> nodes) {
552     if (c.isInstance(this)) {
553       nodes.add(c.cast(this));
554     }
555     if (isLeaf()) {
556       return;
557     }
558     for (Enumeration<?> e = children(); e.hasMoreElements(); ) {
559       MSNode node = (MSNode) e.nextElement();
560       node.castDescendants(c, nodes);
561     }
562   }
563 
564   /**
565    * Calculates the total mass of all atoms in the MolecularAssembly, using either a simple addition
566    * or the Kahan summation algorithm. The simple algorithm is a standard loop to add up the masses.
567    *
568    * @param recalculate Force recalculation
569    * @param useKahan Use Kahan or simple addition algorithms
570    * @return Total mass of all atoms in system.
571    */
572   private double getTotalMass(boolean recalculate, boolean useKahan) {
573     if (recalculate) {
574       List<Atom> atoms = getAtomList();
575       if (atoms.isEmpty()) {
576         totalMass = 0.0;
577       } else if (useKahan) {
578         totalMass = kahanSumMasses(atoms);
579       } else {
580         totalMass = sumMasses(atoms);
581       }
582     }
583     return totalMass;
584   }
585 
586   /**
587    * Iterative summation of atomic masses.
588    *
589    * @param atoms List of atoms.
590    * @return Mass of atoms.
591    */
592   private double sumMasses(List<Atom> atoms) {
593     double sumMasses = 0.0;
594     for (Atom atom : atoms) {
595       sumMasses += atom.getMass();
596     }
597     return sumMasses;
598   }
599 
600   /**
601    * Implements the Kahan algorithm to very accurately sum the masses of all the atoms provided,
602    * minimizing rounding error.
603    *
604    * @param atoms Atoms to sum the mass of.
605    * @return Total mass.
606    */
607   private double kahanSumMasses(List<Atom> atoms) {
608     double sum = 0.0;
609     double comp = 0.0; // Running compensation
610     for (Atom atom : atoms) {
611       double atomMass = atom.getMass() - comp;
612       double temp = sum + atomMass;
613       comp = (temp - sum) - atomMass;
614       sum = temp;
615     }
616     return sum;
617   }
618 
619   /**
620    * Returns true if Class c can be below this Class in the Hierarchy
621    *
622    * @param c Class
623    * @return boolean
624    */
625   private boolean canBeChild(Class<?> c) {
626     try {
627       int multiScaleLevel = c.getDeclaredField("MultiScaleLevel").getInt(null);
628       if (multiScaleLevel >= this.MultiScaleLevel) {
629         return false;
630       }
631     } catch (NoSuchFieldException
632         | SecurityException
633         | IllegalArgumentException
634         | IllegalAccessException e) {
635       return true;
636     }
637     return true;
638   }
639 }