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 static ffx.potential.bonded.AminoAcidUtils.assignAminoAcidAtomTypes;
41  import static java.lang.String.format;
42  import static java.lang.System.arraycopy;
43  
44  import ffx.potential.bonded.AminoAcidUtils.AminoAcid3;
45  import ffx.potential.bonded.BondedUtils.MissingAtomTypeException;
46  import ffx.potential.bonded.BondedUtils.MissingHeavyAtomException;
47  import ffx.potential.bonded.NucleicAcidUtils.NucleicAcid3;
48  import ffx.potential.parameters.ForceField;
49  
50  import java.io.Serial;
51  import java.util.ArrayList;
52  import java.util.List;
53  import java.util.Objects;
54  import java.util.logging.Logger;
55  import org.jogamp.java3d.BranchGroup;
56  import org.jogamp.java3d.Material;
57  import org.jogamp.vecmath.Color3f;
58  
59  /**
60   * The MultiResidue class allows switching between residues for uses such as sequence optimization.
61   *
62   * @author Will Tollefson
63   * @author Michael J. Schnieders
64   * @since 1.0
65   */
66  public class MultiResidue extends Residue {
67  
68    @Serial
69    private static final long serialVersionUID = 1L;
70  
71    private static final Logger logger = Logger.getLogger(MultiResidue.class.getName());
72  
73    /** The force field in use. */
74    ForceField forceField;
75    /** The active residue. */
76    private Residue activeResidue;
77    /** List of residues under consideration. */
78    private final List<Residue> consideredResidues;
79    /** Current rotamers. */
80    private Rotamer[] rotamers;
81    /** The original rotamer. */
82    private Rotamer originalRotamer;
83  
84    /**
85     * Constructor for MultiResidue.
86     *
87     * @param residue a {@link ffx.potential.bonded.Residue} object.
88     * @param forceField a {@link ffx.potential.parameters.ForceField} object.
89     */
90    public MultiResidue(Residue residue, ForceField forceField) {
91      super(residue.getResidueNumber() + "-" + "MultiResidue", residue.getResidueNumber(),
92          residue.residueType, residue.getChainID(), residue.getChainID().toString());
93      this.forceField = forceField;
94      activeResidue = residue;
95      // Initialize consideredResidue list.
96      consideredResidues = new ArrayList<>();
97      consideredResidues.add(residue);
98      removeLeaves();
99    }
100 
101   /** {@inheritDoc} */
102   @Override
103   public MSNode addMSNode(MSNode o) {
104     if (o instanceof Residue) {
105       add(o);
106       return o;
107     } else {
108       return null;
109     }
110   }
111 
112   /**
113    * addResidue.
114    *
115    * @param newResidue a {@link ffx.potential.bonded.Residue} object.
116    */
117   public void addResidue(Residue newResidue) {
118     // Add the new residue to list.
119     consideredResidues.add(newResidue);
120 
121     // Get references to nearby residues.
122     Residue prevResidue = activeResidue.getPreviousResidue();
123     Residue nextResidue = activeResidue.getNextResidue();
124     Residue prev2Residue = null;
125     if (prevResidue != null) {
126       prev2Residue = prevResidue.getPreviousResidue();
127     }
128     Residue next2Residue = null;
129     if (nextResidue != null) {
130       next2Residue = nextResidue.getNextResidue();
131     }
132 
133     moveBackBoneAtoms(activeResidue, newResidue);
134 
135     // Pass references of the active Residues' joints to the new Residue.
136     List<Joint> joints = activeResidue.getJoints();
137     for (Joint joint : joints) {
138       newResidue.addJoint(joint);
139     }
140 
141     // Make the new Residue active.
142     activeResidue.removeFromParent();
143     add(newResidue);
144     activeResidue = newResidue;
145 
146     // Build side-chain atoms and assign atom types for the new Residue.
147     try {
148       assignAminoAcidAtomTypes(newResidue, prevResidue, nextResidue, forceField, null);
149       if (nextResidue != null) {
150         Atom C = (Atom) newResidue.getAtomNode("C");
151         Atom nextN = (Atom) nextResidue.getAtomNode("N");
152         for (Joint joint : joints) {
153           Bond bond = joint.getBondList().get(0);
154           if (bond.containsAtom(C) && bond.containsAtom(nextN)) {
155             C.setBond(bond);
156           }
157         }
158       }
159     } catch (MissingHeavyAtomException | MissingAtomTypeException exception) {
160       logger.severe(exception.toString());
161     }
162     newResidue.finalize(true, forceField);
163 
164     updateGeometry(newResidue, prevResidue, nextResidue, prev2Residue, next2Residue);
165   }
166 
167   /** {@inheritDoc} */
168   @Override
169   public void assignBondedTerms(ForceField forceField) {
170     activeResidue.assignBondedTerms(forceField);
171   }
172 
173   /** {@inheritDoc} */
174   @Override
175   public Joint createJoint(Bond bond, MSGroup group1, MSGroup group2, ForceField forceField) {
176     return activeResidue.createJoint(bond, group1, group2, forceField);
177   }
178 
179   /** {@inheritDoc} */
180   @Override
181   public Joint createJoint(MSGroup group1, MSGroup group2, ForceField forceField) {
182     return activeResidue.createJoint(group1, group2, forceField);
183   }
184 
185   /**
186    * {@inheritDoc}
187    *
188    * <p>Overridden equals method that return true if object is not equals to this, is of the same
189    * class, has the same parent Polymer, the same sequence number, the same ResidueType, and the same
190    * AA3/NA3.
191    */
192   @Override
193   public boolean equals(Object o) {
194     if (this == o) {
195       return true;
196     }
197     if (o == null || getClass() != o.getClass()) {
198       return false;
199     }
200     MultiResidue multiResidue = (MultiResidue) o;
201     return Objects.equals(getSegID(), multiResidue.getSegID())
202         && Objects.equals(getResidueNumber(), multiResidue.getResidueNumber())
203         && Objects.equals(getName(), multiResidue.getName());
204   }
205 
206   /** {@inheritDoc} */
207   @Override
208   public void finalize(boolean finalizeGeometry, ForceField forceField) {
209     activeResidue.finalize(finalizeGeometry, forceField);
210   }
211 
212   /** {@inheritDoc} */
213   @Override
214   public void findDangelingAtoms() {
215     activeResidue.findDangelingAtoms();
216   }
217 
218   /**
219    * getActive.
220    *
221    * @return a {@link ffx.potential.bonded.Residue} object.
222    */
223   public Residue getActive() {
224     return activeResidue;
225   }
226 
227   /**
228    * {@inheritDoc}
229    *
230    * <p>Returns the AminoAcid3 of the active residue.
231    */
232   @Override
233   public AminoAcid3 getAminoAcid3() {
234     return activeResidue.getAminoAcid3();
235   }
236 
237   /** {@inheritDoc} */
238   @Override
239   public MSNode getAngles() {
240     return activeResidue.getAngles();
241   }
242 
243   /** {@inheritDoc} */
244   @Override
245   public void setAngles(MSNode t) {
246     activeResidue.setAngles(t);
247   }
248 
249   /** {@inheritDoc} */
250   @Override
251   public MSNode getAtomNode() {
252     return activeResidue.getAtomNode();
253   }
254 
255   /** {@inheritDoc} */
256   @Override
257   public void setAtomNode(MSNode t) {
258     activeResidue.setAtomNode(t);
259   }
260 
261   /** {@inheritDoc} */
262   @Override
263   public MSNode getAtomNode(int index) {
264     return activeResidue.getAtomNode(index);
265   }
266 
267   /** {@inheritDoc} */
268   @Override
269   public MSNode getAtomNode(String n) {
270     return activeResidue.getAtomNode(n);
271   }
272 
273   /** {@inheritDoc} */
274   @Override
275   public List<MSNode> getAtomNodeList() {
276     return activeResidue.getAtomNodeList();
277   }
278 
279   /** {@inheritDoc} */
280   @Override
281   public Bond getBond(String id) {
282     return activeResidue.getBond(id);
283   }
284 
285   /** {@inheritDoc} */
286   @Override
287   public Bond getBond(int index) {
288     return activeResidue.getBond(index);
289   }
290 
291   /** {@inheritDoc} */
292   @Override
293   public MSNode getBonds() {
294     return activeResidue.getBonds();
295   }
296 
297   /** {@inheritDoc} */
298   @Override
299   public void setBonds(MSNode t) {
300     activeResidue.setBonds(t);
301   }
302 
303   /** {@inheritDoc} */
304   @Override
305   public double[] getCenter() {
306     return activeResidue.getCenter();
307   }
308 
309   /** {@inheritDoc} */
310   @Override
311   public void setCenter(double[] d) {
312     activeResidue.setCenter(d);
313   }
314 
315   /**
316    * Returns a copy of this MultiResidue's consideredResidues array.
317    *
318    * @return a new List of the considered residues.
319    */
320   public List<Residue> getConsideredResidues() {
321     return new ArrayList<>(consideredResidues);
322   }
323 
324   /** {@inheritDoc} */
325   @Override
326   public List<Atom> getDanglingAtoms() {
327     return activeResidue.getDanglingAtoms();
328   }
329 
330   /** {@inheritDoc} */
331   @Override
332   public void setDanglingAtoms(List<Atom> a) {
333     activeResidue.setDanglingAtoms(a);
334   }
335 
336   /**
337    * Returns a list of this MultiResidue's inactive residues. Adding/removing from the returned list
338    * does nothing.
339    *
340    * @return a new List of inactive residues.
341    */
342   public List<Residue> getInactive() {
343     List<Residue> ret = new ArrayList<>();
344     for (Residue res : consideredResidues) {
345       if (res != activeResidue) {
346         ret.add(res);
347       }
348     }
349     return ret;
350   }
351 
352   /** {@inheritDoc} */
353   @Override
354   public double[] getMultiScaleCenter(boolean w) {
355     return activeResidue.getMultiScaleCenter(w);
356   }
357 
358   /** {@inheritDoc} */
359   @Override
360   public String getName() {
361     if (activeResidue != null) {
362       return activeResidue.getName();
363     }
364     return super.getName();
365   }
366 
367   /**
368    * getResidueCount.
369    *
370    * @return The number of residues in this MultiResidue.
371    */
372   public int getResidueCount() {
373     if (consideredResidues == null) {
374       return 0;
375     }
376     return consideredResidues.size();
377   }
378 
379   /** {@inheritDoc} */
380   @Override
381   public Rotamer[] getRotamers() {
382     return rotamers;
383   }
384 
385   /** {@inheritDoc} */
386   @Override
387   public Rotamer[] setRotamers(RotamerLibrary library) {
388     List<Rotamer[]> usual = new ArrayList<>();
389     int nRots = 0;
390 
391     for (Residue residue : consideredResidues) {
392       Rotamer[] rotamers = library.getRotamers(residue);
393       if (rotamers != null && rotamers.length > 0) {
394         usual.add(rotamers);
395         nRots += rotamers.length;
396       }
397     }
398 
399     if (library.getUsingOrigCoordsRotamer()) {
400       if (originalRotamer == null
401           && (residueType == Residue.ResidueType.AA || residueType == Residue.ResidueType.NA)) {
402         ResidueState origState = storeState();
403         double[] chi = RotamerLibrary.measureRotamer(activeResidue, false);
404         if (residueType == Residue.ResidueType.AA) {
405           AminoAcid3 aa3 = this.getAminoAcid3();
406           originalRotamer = new Rotamer(aa3, origState, chi);
407         } else if (residueType == Residue.ResidueType.NA) {
408           NucleicAcid3 na3 = this.getNucleicAcid3();
409           originalRotamer = new Rotamer(na3, origState, chi);
410         }
411       }
412       Rotamer[] allRotamers;
413       if (originalRotamer != null) {
414         allRotamers = new Rotamer[nRots + 1];
415         int index = 1;
416         allRotamers[0] = originalRotamer;
417         for (Rotamer[] rotamersI : usual) {
418           int nRotamers = rotamersI.length;
419           arraycopy(rotamersI, 0, allRotamers, index, nRotamers);
420           index += nRotamers;
421         }
422       } else {
423         allRotamers = addAllDefaultRotamers(usual, nRots);
424       }
425       rotamers = allRotamers;
426     } else {
427       rotamers = addAllDefaultRotamers(usual, nRots);
428     }
429     return rotamers;
430   }
431 
432   /** {@inheritDoc} */
433   @Override
434   public List<Atom> getSideChainAtoms() {
435     return activeResidue.getSideChainAtoms();
436   }
437 
438   /** {@inheritDoc} */
439   @Override
440   public MSNode getTermNode() {
441     return activeResidue.getTermNode();
442   }
443 
444   /** {@inheritDoc} */
445   @Override
446   public MSNode getTorsions() {
447     return activeResidue.getTorsions();
448   }
449 
450   /** {@inheritDoc} */
451   @Override
452   public void setTorsions(MSNode t) {
453     activeResidue.setTorsions(t);
454   }
455 
456   /**
457    * {@inheritDoc}
458    *
459    * <p>Returns all atoms (all atoms are variable during DEE).
460    */
461   @Override
462   public List<Atom> getVariableAtoms() {
463     return activeResidue.getAtomList();
464   }
465 
466   /** {@inheritDoc} */
467   @Override
468   public int hashCode() {
469     return Objects.hash(getSegID(), getResidueNumber(), getName());
470   }
471 
472   /** {@inheritDoc} */
473   @Override
474   public boolean isFinalized() {
475     return activeResidue.isFinalized();
476   }
477 
478   /** {@inheritDoc} */
479   @Override
480   public void setFinalized(boolean t) {
481     activeResidue.setFinalized(t);
482   }
483 
484   /** {@inheritDoc} */
485   @Override
486   public void reOrderAtoms() {
487     activeResidue.reOrderAtoms();
488   }
489 
490   /**
491    * Request the ith residue be set active.
492    *
493    * @param i The index of the residue to set active.
494    * @return true if the ith residue was set active, false otherwise.
495    */
496   public boolean setActiveResidue(int i) {
497     if (consideredResidues == null) {
498       return false;
499     }
500     if (i >= consideredResidues.size()) {
501       return false;
502     }
503     return setActiveResidue(consideredResidues.get(i));
504   }
505 
506   /**
507    * Setter for the field <code>activeResidue</code>.
508    *
509    * @param residue a {@link ffx.potential.bonded.Residue} object.
510    * @return a boolean.
511    */
512   public boolean setActiveResidue(Residue residue) {
513     if (!consideredResidues.contains(residue)) {
514       return false;
515     }
516     if (residue == activeResidue) {
517       return true;
518     }
519     Residue prevResidue = activeResidue.getPreviousResidue();
520     Residue nextResidue = activeResidue.getNextResidue();
521     Residue prev2Residue = null;
522     if (prevResidue != null) {
523       prev2Residue = prevResidue.getPreviousResidue();
524     }
525     Residue next2Residue = null;
526     if (nextResidue != null) {
527       next2Residue = nextResidue.getNextResidue();
528     }
529 
530     activeResidue.removeFromParent();
531 
532     // Move backbone atoms to the new active residue.
533     moveBackBoneAtoms(activeResidue, residue);
534     updateGeometry(residue, prevResidue, nextResidue, prev2Residue, next2Residue);
535     activeResidue = residue;
536 
537     setName(toString());
538     add(activeResidue);
539 
540     return true;
541   }
542 
543   /**
544    * Method may be redundant with requestSetActiveResidue. Will not function correctly if there is
545    * more than one residue of type UNK (unknown).
546    *
547    * @param aa a {@link AminoAcid3} object.
548    * @return True if successful
549    */
550   public boolean setActiveResidue(AminoAcid3 aa) {
551     Residue residue = null;
552     for (Residue res : consideredResidues) {
553       if (res.getAminoAcid3() == aa) {
554         residue = res;
555         break;
556       }
557     }
558     if (residue == null) {
559       return false;
560     }
561     return setActiveResidue(residue);
562   }
563 
564   /** {@inheritDoc} */
565   @Override
566   public void setColor(RendererCache.ColorModel newColorModel, Color3f color, Material mat) {
567     activeResidue.setColor(newColorModel, color, mat);
568   }
569 
570   /** {@inheritDoc} */
571   @Override
572   public void setOutOfPlaneBends(MSNode t) {
573     activeResidue.setOutOfPlaneBends(t);
574   }
575 
576   /** {@inheritDoc} */
577   @Override
578   public void setPiOrbitalTorsions(MSNode t) {
579     activeResidue.setPiOrbitalTorsions(t);
580   }
581 
582   /** {@inheritDoc} */
583   @Override
584   public void setStretchBends(MSNode t) {
585     activeResidue.setStretchBends(t);
586   }
587 
588   /** {@inheritDoc} */
589   @Override
590   public void setTerms(MSNode t) {
591     activeResidue.setTerms(t);
592   }
593 
594   /** {@inheritDoc} */
595   @Override
596   public void setTorsionTorsions(MSNode t) {
597     activeResidue.setTorsionTorsions(t);
598   }
599 
600   /** {@inheritDoc} */
601   @Override
602   public void setUreyBradleys(MSNode t) {
603     activeResidue.setUreyBradleys(t);
604   }
605 
606   /** {@inheritDoc} */
607   @Override
608   public void setView(RendererCache.ViewModel newViewModel, List<BranchGroup> newShapes) {
609     activeResidue.setView(newViewModel, newShapes);
610   }
611 
612   /**
613    * {@inheritDoc}
614    *
615    * <p>Publicly accessible method for storing a MultiResidue state.
616    */
617   @Override
618   public ResidueState storeState() {
619     return storeMultiResState();
620   }
621 
622   /** {@inheritDoc} */
623   @Override
624   public String toString() {
625     int resNum = consideredResidues.get(0).getResidueNumber();
626     StringBuilder sb = new StringBuilder();
627     sb.append(resNum).append("-");
628     for (Residue res : consideredResidues) {
629       int num = AminoAcidUtils.getAminoAcidNumber(res.getName());
630       String aa1 = AminoAcidUtils.AminoAcid1.values()[num].toString();
631       if (res == activeResidue) {
632         sb.append("[").append(aa1).append("]");
633       } else {
634         sb.append(aa1);
635       }
636     }
637     return sb.toString();
638   }
639 
640   /** {@inheritDoc} */
641   @Override
642   public void update() {
643     activeResidue.update();
644   }
645 
646   /** {@inheritDoc} */
647   @Override
648   public void updateAtoms() {
649     activeResidue.updateAtoms();
650   }
651 
652   /** {@inheritDoc} */
653   @Override
654   public void updateBonds() {
655     activeResidue.updateBonds();
656   }
657 
658   /**
659    * Non-overrideable implementation method for storeState. Probably unnecessary, as I dropped the
660    * idea of initializing the original-coordinates rotamer in the constructor.
661    *
662    * @return A ResidueState.
663    */
664   private ResidueState storeMultiResState() {
665     return new ResidueState(this, activeResidue);
666   }
667 
668   /**
669    * Returns an array of all standard torsion-based Rotamers for this Multi-Residue.
670    *
671    * @param rotamerList List of Rotamer arrays to flatten
672    * @param nRots Number of rotamers.
673    * @return An array of all standard torsion-based Rotamers for this Multi-Residue.
674    */
675   private Rotamer[] addAllDefaultRotamers(List<Rotamer[]> rotamerList, int nRots) {
676     Rotamer[] allRotamers = new Rotamer[nRots];
677     int index = 0;
678     for (Rotamer[] rotamers : rotamerList) {
679       int nRotamers = rotamers.length;
680       arraycopy(rotamers, 0, allRotamers, index, nRotamers);
681       index += nRotamers;
682     }
683     return allRotamers;
684   }
685 
686   @Override
687   public void revertState(ResidueState state) {
688     Residue res = state.getStateResidue();
689     if (!setActiveResidue(res)) {
690       throw new IllegalArgumentException(
691           format(
692               " Could not revert " + "multi-residue %s to residue identity %s",
693               this, state.getStateResidue().toString()));
694     }
695     for (Atom atom : getAtomList()) {
696       atom.moveTo(state.getAtomCoords(atom));
697     }
698   }
699 
700   private void moveBackBoneAtoms(Residue fromResidue, Residue toResidue) {
701     Residue prevRes = this.getPreviousResidue();
702     Residue nextRes = this.getNextResidue();
703 
704     // Begin with atoms common to all residues
705 
706     // Get references to the backbone atoms.
707     Atom CA = (Atom) fromResidue.getAtomNode("CA");
708     Atom C = (Atom) fromResidue.getAtomNode("C");
709     Atom HA = (Atom) fromResidue.getAtomNode("HA");
710     Atom N = (Atom) fromResidue.getAtomNode("N");
711     Atom O = (Atom) fromResidue.getAtomNode("O");
712 
713     CA.removeFromParent();
714     HA.removeFromParent();
715     C.removeFromParent();
716     O.removeFromParent();
717     N.removeFromParent();
718 
719     // Clear their references to bonded geometry.
720     CA.clearGeometry();
721     HA.clearGeometry();
722     C.clearGeometry();
723     O.clearGeometry();
724     N.clearGeometry();
725 
726     // Change their residue name.
727     String resName = toResidue.getName();
728     CA.setResName(resName);
729     HA.setResName(resName);
730     C.setResName(resName);
731     O.setResName(resName);
732     N.setResName(resName);
733 
734     // Add the backbone atoms to the new Residue.
735     toResidue.addMSNode(CA);
736     toResidue.addMSNode(HA);
737     toResidue.addMSNode(C);
738     toResidue.addMSNode(O);
739     toResidue.addMSNode(N);
740 
741     if (prevRes == null) {
742       Atom H1 = (Atom) fromResidue.getAtomNode("H1");
743       Atom H2 = (Atom) fromResidue.getAtomNode("H2");
744       Atom H3 = (Atom) fromResidue.getAtomNode("H3");
745 
746       H1.removeFromParent();
747       H2.removeFromParent();
748 
749       H1.clearGeometry();
750       H2.clearGeometry();
751       H1.setResName(resName);
752       H2.setResName(resName);
753       toResidue.addMSNode(H1);
754       toResidue.addMSNode(H2);
755 
756       if (H3 != null) {
757         H3.removeFromParent();
758         H3.clearGeometry();
759         H3.setResName(resName);
760         toResidue.addMSNode(H3);
761       }
762     } else {
763       Atom H = (Atom) fromResidue.getAtomNode("H");
764       H.removeFromParent();
765       H.clearGeometry();
766       H.setResName(resName);
767       toResidue.addMSNode(H);
768     }
769 
770     if (nextRes == null) {
771       Atom OXT = (Atom) fromResidue.getAtomNode("OXT");
772       if (OXT != null) {
773         OXT.removeFromParent();
774         OXT.clearGeometry();
775         OXT.setResName(resName);
776         toResidue.addMSNode(OXT);
777       } else {
778         Atom OH = (Atom) fromResidue.getAtomNode("OH");
779         Atom HO = (Atom) fromResidue.getAtomNode("HO");
780         OH.removeFromParent();
781         HO.removeFromParent();
782         OH.clearGeometry();
783         HO.clearGeometry();
784         OH.setResName(resName);
785         HO.setResName(resName);
786         toResidue.addMSNode(OH);
787         toResidue.addMSNode(HO);
788       }
789     }
790   }
791 
792   /**
793    * Update Atom references to local geometry.
794    *
795    * @param residue Current residue.
796    * @param prev Previous residue.
797    * @param next Next residue.
798    * @param prev2 Previous previous residue.
799    * @param next2 Next next residue.
800    */
801   private void updateGeometry(
802       Residue residue, Residue prev, Residue next, Residue prev2, Residue next2) {
803     if (residue == null) {
804       return;
805     }
806 
807     // Update atom references to local geometry.
808     List<Atom> atoms = residue.getAtomList();
809     List<Bond> bonds = residue.getBondList();
810     List<Angle> angles = residue.getAngleList();
811     List<Torsion> torsions = residue.getTorsionList();
812     if (prev != null) {
813       atoms.addAll(prev.getAtomList());
814       bonds.addAll(prev.getBondList());
815       angles.addAll(prev.getAngleList());
816       torsions.addAll(prev.getTorsionList());
817       List<Joint> joints = prev.getJoints();
818       for (Joint joint : joints) {
819         bonds.addAll(joint.getBondList());
820         angles.addAll(joint.getAngleList());
821         torsions.addAll(joint.getTorsionList());
822       }
823     }
824     if (prev2 != null) {
825       bonds.addAll(prev2.getBondList());
826       angles.addAll(prev2.getAngleList());
827       torsions.addAll(prev2.getTorsionList());
828     }
829     if (next != null) {
830       atoms.addAll(next.getAtomList());
831       bonds.addAll(next.getBondList());
832       angles.addAll(next.getAngleList());
833       torsions.addAll(next.getTorsionList());
834       List<Joint> joints = next.getJoints();
835       for (Joint joint : joints) {
836         bonds.addAll(joint.getBondList());
837         angles.addAll(joint.getAngleList());
838         torsions.addAll(joint.getTorsionList());
839       }
840     }
841     if (next2 != null) {
842       bonds.addAll(next2.getBondList());
843       angles.addAll(next2.getAngleList());
844       torsions.addAll(next2.getTorsionList());
845     }
846 
847     for (Atom atom : atoms) {
848       atom.clearGeometry();
849     }
850     for (Atom atom : atoms) {
851       for (Bond b : bonds) {
852         if (b.containsAtom(atom)) {
853           atom.setBond(b);
854         }
855       }
856     }
857     for (Atom atom : atoms) {
858       for (Angle a : angles) {
859         if (a.containsAtom(atom)) {
860           atom.setAngle(a);
861         }
862       }
863     }
864     for (Atom atom : atoms) {
865       for (Torsion t : torsions) {
866         if (t.containsAtom(atom)) {
867           atom.setTorsion(t);
868         }
869       }
870     }
871   }
872 
873 }