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.potential.MolecularAssembly;
41  import ffx.potential.bonded.AminoAcidUtils.AminoAcid3;
42  import ffx.potential.bonded.NucleicAcidUtils.NucleicAcid3;
43  import ffx.potential.bonded.Residue.ResidueType;
44  import ffx.potential.parameters.AngleType;
45  import ffx.potential.parameters.TitrationUtils;
46  import org.apache.commons.math3.util.FastMath;
47  
48  import java.io.BufferedReader;
49  import java.io.File;
50  import java.io.FileReader;
51  import java.io.IOException;
52  import java.util.ArrayList;
53  import java.util.Arrays;
54  import java.util.HashMap;
55  import java.util.List;
56  import java.util.Map;
57  import java.util.Optional;
58  import java.util.logging.Level;
59  import java.util.logging.Logger;
60  
61  import static ffx.numerics.math.ScalarMath.mod;
62  import static ffx.potential.bonded.BondedUtils.determineIntxyz;
63  import static ffx.potential.bonded.BondedUtils.intxyz;
64  import static java.lang.String.format;
65  import static java.util.Arrays.fill;
66  
67  /**
68   * The Rotamer Library Class manages a library of side-chain Rotamers for amino acids, and a library
69   * of backbone Rotamers for nucleic acids.
70   *
71   * @author Ava M. Lynn
72   * @author Shibo Gao
73   * @author Jacob M. Litman
74   * @since 1.0
75   */
76  public class RotamerLibrary {
77  
78    private static final Logger logger = Logger.getLogger(RotamerLibrary.class.getName());
79    /**
80     * Number of amino acid residues types currently recognized, although there are not rotamer
81     * libraries for each yet.
82     */
83    private static final int numberOfAminoAcids = AminoAcidUtils.AminoAcid3.values().length;
84    /**
85     * Number of nucleic acid residues types currently recognized, although there are not rotamer
86     * libraries for each yet.
87     */
88    private static final int numberOfNucleicAcids = NucleicAcidUtils.NucleicAcid3.values().length;
89  
90    private static final ProteinLibrary DEFAULT_PROTEIN_LIB = ProteinLibrary.Richardson;
91    private static final NucleicAcidLibrary DEFAULT_NA_LIB = NucleicAcidLibrary.RICHARDSON;
92    private static final Map<String, NonstandardRotLibrary> nonstdRotCache = new HashMap<>();
93    private static final RotamerLibrary defaultRotamerLibrary = new RotamerLibrary(
94        ProteinLibrary.PonderAndRichards, false);
95    private static final boolean useIdealRingGeometries = Boolean.parseBoolean(
96        System.getProperty("useIdealRingGeo", "true"));
97    private static final Map<AminoAcid3, Map<String, Double>> idealAngleGeometries;
98  
99    static {
100     idealAngleGeometries = Map.of(AminoAcid3.PHE,
101         Map.of("CD1-CG-CB", 120.3, "CD2-CG-CB", 120.3, "CE1-CD1-CG", 120.3, "CE2-CD2-CG", 120.3,
102             "CZ-CE1-CD1", 120.0, "CZ-CE2-CD2", 120.0), AminoAcid3.TYR,
103         Map.of("CD1-CG-CB", 120.3, "CD2-CG-CB", 120.3, "CE1-CD1-CG", 120.3, "CE2-CD2-CG", 120.3,
104             "CZ-CE1-CD1", 120.0, "CZ-CE2-CD2", 120.0), AminoAcid3.TYD,
105         Map.of("CD1-CG-CB", 120.5, "CD2-CG-CB", 120.5, "CE1-CD1-CG", 120.4, "CE2-CD2-CG", 120.4,
106             "CZ-CE1-CD1", 120.8, "CZ-CE2-CD2", 120.8), AminoAcid3.HIS,
107         Map.of("ND1-CG-CB", 122.1, "CD2-CG-CB", 131.0, "CD2-CG-ND1", 106.8, "CE1-ND1-CG", 109.5,
108             "NE2-CD2-CG", 107.1), AminoAcid3.HID,
109         Map.of("ND1-CG-CB", 123.5, "CD2-CG-CB", 132.3, "CD2-CG-ND1", 104.2, "CE1-ND1-CG", 108.8,
110             "NE2-CD2-CG", 111.2), AminoAcid3.HIE,
111         Map.of("ND1-CG-CB", 120.2, "CD2-CG-CB", 129.1, "CD2-CG-ND1", 110.7, "CE1-ND1-CG", 105.1,
112             "NE2-CD2-CG", 104.6), AminoAcid3.TRP,
113         Map.of("CD1-CG-CB", 126.4, "CD2-CG-CB", 126.5, "CD2-CG-CD1", 107.1, "NE1-CD1-CG", 106.9,
114             "CE2-NE1-CD1", 109.4, "CE3-CD2-CE2", 121.6, "CZ2-CE2-CD2", 123.5, "CZ3-CE3-CD2", 116.7,
115             "CH2-CZ2-CE2", 116.2));
116   }
117 
118   /**
119    * The first time rotamers are requested for an amino acid type, they are instantiated into an
120    * array, which is stored in the cache. Subsequently, the reference is simply returned.
121    */
122   private final Rotamer[][] aminoAcidRotamerCache = new Rotamer[numberOfAminoAcids][];
123   /**
124    * The first time rotamers are requested for a nucleic acid type, they are instantiated into an
125    * array, which is stored in the cache. Subsequently, the reference is simply returned.
126    */
127   private final Rotamer[][] nucleicAcidRotamerCache = new Rotamer[numberOfNucleicAcids][];
128   /**
129    * The idealized amino acid rotamer library in use. Defaults to the Richardson library.
130    */
131   private final ProteinLibrary proteinLibrary;
132   /**
133    * The idealized nucleic acid rotamer library in use. Defaults to the Richardson library as there's
134    * no other library.
135    */
136   private final NucleicAcidLibrary nucleicAcidLibrary;
137   /**
138    * Use the original coordinates as an additional rotamer.
139    */
140   private boolean useOrigCoordsRotamer;
141 
142   /**
143    * Constructor for RotamerLibrary.
144    *
145    * @param origCoords Whether to use original-coordinates rotamers.
146    */
147   public RotamerLibrary(boolean origCoords) {
148     this(DEFAULT_PROTEIN_LIB, DEFAULT_NA_LIB, origCoords);
149   }
150 
151   /**
152    * Constructor for RotamerLibrary.
153    *
154    * @param protLibrary A {@link ffx.potential.bonded.RotamerLibrary.ProteinLibrary} to use as
155    *                    the idealized amino acid rotamer library.
156    * @param origCoords  Whether to use original-coordinates rotamers.
157    */
158   public RotamerLibrary(ProteinLibrary protLibrary, boolean origCoords) {
159     this(protLibrary, DEFAULT_NA_LIB, origCoords);
160   }
161 
162   /**
163    * Constructor for RotamerLibrary.
164    *
165    * @param naLibrary  A {@link ffx.potential.bonded.RotamerLibrary.NucleicAcidLibrary} to use as
166    *                   the idealized nucleic acid rotamer library.
167    * @param origCoords Whether to use original-coordinates rotamers.
168    */
169   public RotamerLibrary(NucleicAcidLibrary naLibrary, boolean origCoords) {
170     this(DEFAULT_PROTEIN_LIB, naLibrary, origCoords);
171   }
172 
173   /**
174    * Constructor for RotamerLibrary.
175    *
176    * @param protLibrary A {@link ffx.potential.bonded.RotamerLibrary.ProteinLibrary} to use as
177    *                    the idealized amino acid rotamer library.
178    * @param naLibrary   A {@link ffx.potential.bonded.RotamerLibrary.NucleicAcidLibrary} to use as
179    *                    the idealized nucleic acid rotamer library.
180    * @param origCoords  Whether to use original-coordinates rotamers.
181    */
182   public RotamerLibrary(ProteinLibrary protLibrary, NucleicAcidLibrary naLibrary,
183                         boolean origCoords) {
184     proteinLibrary = protLibrary;
185     nucleicAcidLibrary = naLibrary;
186     useOrigCoordsRotamer = origCoords;
187   }
188 
189   /**
190    * addRotPatch.
191    *
192    * @param rotFileName a {@link java.lang.String} object.
193    * @return a boolean.
194    */
195   public static boolean addRotPatch(String rotFileName) {
196     File rotPatchFile = new File(rotFileName);
197     if (rotPatchFile.exists() && rotPatchFile.canRead()) {
198       return addRotPatch(new File(rotFileName));
199     }
200     return false;
201   }
202 
203   /**
204    * Applies a Rotamer to a Residue by calling applyAARotamer or applyNARotamer.
205    *
206    * @param residue the Residue whose side-chain will be moved.
207    * @param rotamer the Rotamer defining the move.
208    */
209   public static void applyRotamer(Residue residue, Rotamer rotamer) {
210     applyRotamer(residue, rotamer, false);
211   }
212 
213   /**
214    * Version of applyRotamer which allows for chain context-independent drawing of nucleic acid
215    * Rotamers. Solely used in saveRotamers at this point, although it may be useful for debugging.
216    *
217    * @param residue     the Residue to be moved.
218    * @param rotamer     Rotamer to be applied.
219    * @param independent Whether to draw Rotamer independent of chain context.
220    */
221   public static void applyRotamer(Residue residue, Rotamer rotamer, boolean independent) {
222     residue.setRotamer(rotamer);
223 
224     /*
225      If the rotamer represents a titration state, update force field parameters for the
226      side-chain atoms of the residue.
227 
228      This is done before applying the rotamer, so that the conformation is based on the appropriate
229      equilibrium bond and angle values for the titration state of the rotamer.
230      */
231     if (rotamer.isTitrating) {
232       rotamer.updateParameters(residue);
233     }
234 
235     if (rotamer.isState) {
236       applyState(residue, rotamer);
237     } else {
238       switch (residue.getResidueType()) {
239         case AA -> applyAARotamer(residue, rotamer);
240         case NA -> applyNARotamer(residue, rotamer, independent);
241         default -> {
242         }
243       }
244     }
245 
246   }
247 
248   /**
249    * If place is true, builds C2', C3', and O3' based on delta(i) and returns an empty double[]; if
250    * place is false, returns a double[] filled with the coordinates at which O3' would be placed by
251    * the specified pucker.
252    *
253    * <p>Presently uses default locations for C1', O4', and C4' to build these atoms.
254    *
255    * @param residue Nucleic acid Residue to which the pucker is to bea applied
256    * @param pucker  An int specifying pucker (1=North, 2=South).
257    * @param isDeoxy Boolean
258    * @param place   Flag for usage case.
259    * @return A double[] with O3' coordinates (place=false), or null (place=true).
260    */
261   public static double[] applySugarPucker(Residue residue, NucleicSugarPucker pucker,
262                                           boolean isDeoxy, boolean place) {
263     // Torsions from http://ndb-mirror-2.rutgers.edu/ndbmodule/archives/proj/valence/table6.html
264     // SP is short for Sugar Pucker (torsion).
265     final double C2_SP_SOUTH_RNA = 24.2;
266     final double C3_SP_SOUTH_RNA = 357.7;
267     final double O3_SP_SOUTH_RNA = 268.1;
268     final double C2_SP_NORTH_RNA = 324.7;
269     final double C3_SP_NORTH_RNA = 20.4;
270     final double O3_SP_NORTH_RNA = 201.8;
271 
272     final double C2_SP_SOUTH_DNA = 22.6;
273     final double C3_SP_SOUTH_DNA = 357.7;
274     final double O3_SP_SOUTH_DNA = 265.8;
275     final double C2_SP_NORTH_DNA = 327.7;
276     final double C3_SP_NORTH_DNA = 16.4;
277     final double O3_SP_NORTH_DNA = 205.4;
278 
279     // Ret will only be filled if place is false.
280     double[] ret = new double[3];
281 
282     // Constituents of the sugar
283     Atom C1s = (Atom) residue.getAtomNode("C1'");
284     // C2' will only be used if place is true.
285     Atom C3s = (Atom) residue.getAtomNode("C3'");
286     Atom O4s = (Atom) residue.getAtomNode("O4'");
287     Atom C4s = (Atom) residue.getAtomNode("C4'");
288     Atom O3s = (Atom) residue.getAtomNode("O3'");
289 
290     // Bonds and angles necessary to draw C3' and O3'.
291     Bond C3s_C4s = C4s.getBond(C3s);
292     double dC3s_C4s = C3s_C4s.bondType.distance;
293     Angle C3s_C4s_O4s = O4s.getAngle(C4s, C3s);
294     double dC3s_C4s_O4s = C3s_C4s_O4s.angleType.angle[C3s_C4s_O4s.nh];
295 
296     Bond C3s_O3s = C3s.getBond(O3s);
297     double dC3s_O3s = C3s_O3s.bondType.distance;
298     Angle C4s_C3s_O3s = C4s.getAngle(C3s, O3s);
299     double dC4s_C3s_O3s = C4s_C3s_O3s.angleType.angle[C4s_C3s_O3s.nh];
300 
301     /*
302      * If place is true, place C3', C2', and O3'. Else, determine the
303      * coordinates of O3' based on the invariant atoms.
304      *
305      * If deoxy, a different set of sugar pucker torsions is
306      * applied.
307      *
308      * Then, if a North pucker (delta (i) is in the range of 78-90),
309      * apply Cx_SP_NORTH torsion to place C3 and C2.  Else,
310      * assume a South pucker (delta is between 140 and 152).
311      *
312      * Then, use O3_SP_NORTH or SOUTH to place O3.
313      */
314     if (place) {
315       Atom C2s = (Atom) residue.getAtomNode("C2'");
316       Bond C3s_C2s = C3s.getBond(C2s);
317       double dC3s_C2s = C3s_C2s.bondType.distance;
318       Angle C4s_C3s_C2s = C4s.getAngle(C3s, C2s);
319       double dC4s_C3s_C2s = C4s_C3s_C2s.angleType.angle[C4s_C3s_C2s.nh];
320 
321       if (isDeoxy) {
322         if (pucker == NucleicSugarPucker.C3_ENDO) {
323           intxyz(C3s, C4s, dC3s_C4s, O4s, dC3s_C4s_O4s, C1s, C3_SP_NORTH_DNA, 0);
324           intxyz(C2s, C3s, dC3s_C2s, C4s, dC4s_C3s_C2s, O4s, C2_SP_NORTH_DNA, 0);
325           intxyz(O3s, C3s, dC3s_O3s, C4s, dC4s_C3s_O3s, O4s, O3_SP_NORTH_DNA, 0);
326         } // TODO: else-if for 3'-exo configuration (DNA only)
327         else {
328           intxyz(C3s, C4s, dC3s_C4s, O4s, dC3s_C4s_O4s, C1s, C3_SP_SOUTH_DNA, 0);
329           intxyz(C2s, C3s, dC3s_C2s, C4s, dC4s_C3s_C2s, O4s, C2_SP_SOUTH_DNA, 0);
330           intxyz(O3s, C3s, dC3s_O3s, C4s, dC4s_C3s_O3s, O4s, O3_SP_SOUTH_DNA, 0);
331         }
332       } else {
333         if (pucker == NucleicSugarPucker.C3_ENDO) {
334           intxyz(C3s, C4s, dC3s_C4s, O4s, dC3s_C4s_O4s, C1s, C3_SP_NORTH_RNA, 0);
335           intxyz(C2s, C3s, dC3s_C2s, C4s, dC4s_C3s_C2s, O4s, C2_SP_NORTH_RNA, 0);
336           intxyz(O3s, C3s, dC3s_O3s, C4s, dC4s_C3s_O3s, O4s, O3_SP_NORTH_RNA, 0);
337         } else {
338           intxyz(C3s, C4s, dC3s_C4s, O4s, dC3s_C4s_O4s, C1s, C3_SP_SOUTH_RNA, 0);
339           intxyz(C2s, C3s, dC3s_C2s, C4s, dC4s_C3s_C2s, O4s, C2_SP_SOUTH_RNA, 0);
340           intxyz(O3s, C3s, dC3s_O3s, C4s, dC4s_C3s_O3s, O4s, O3_SP_SOUTH_RNA, 0);
341         }
342       }
343     } else {
344       double[] C1sXYZ = new double[3];
345       double[] C3sXYZ;
346       double[] O4sXYZ = new double[3];
347       double[] C4sXYZ = new double[3];
348       C1s.getXYZ(C1sXYZ);
349       O4s.getXYZ(O4sXYZ);
350       C4s.getXYZ(C4sXYZ);
351 
352       // O3s coordinates will be filled into ret.
353       if (isDeoxy) {
354         if (pucker == NucleicSugarPucker.C3_ENDO) {
355           C3sXYZ = determineIntxyz(C4sXYZ, dC3s_C4s, O4sXYZ, dC3s_C4s_O4s, C1sXYZ, C3_SP_NORTH_DNA,
356               0);
357           ret = determineIntxyz(C3sXYZ, dC3s_O3s, C4sXYZ, dC4s_C3s_O3s, O4sXYZ, O3_SP_NORTH_DNA, 0);
358         } // TODO: else-if for 3'-exo configuration (DNA only)
359         else {
360           C3sXYZ = determineIntxyz(C4sXYZ, dC3s_C4s, O4sXYZ, dC3s_C4s_O4s, C1sXYZ, C3_SP_SOUTH_DNA,
361               0);
362           ret = determineIntxyz(C3sXYZ, dC3s_O3s, C4sXYZ, dC4s_C3s_O3s, O4sXYZ, O3_SP_SOUTH_DNA, 0);
363         }
364       } else {
365         if (pucker == NucleicSugarPucker.C3_ENDO) {
366           C3sXYZ = determineIntxyz(C4sXYZ, dC3s_C4s, O4sXYZ, dC3s_C4s_O4s, C1sXYZ, C3_SP_NORTH_RNA,
367               0);
368           ret = determineIntxyz(C3sXYZ, dC3s_O3s, C4sXYZ, dC4s_C3s_O3s, O4sXYZ, O3_SP_NORTH_RNA, 0);
369         } else {
370           C3sXYZ = determineIntxyz(C4sXYZ, dC3s_C4s, O4sXYZ, dC3s_C4s_O4s, C1sXYZ, C3_SP_SOUTH_RNA,
371               0);
372           ret = determineIntxyz(C3sXYZ, dC3s_O3s, C4sXYZ, dC4s_C3s_O3s, O4sXYZ, O3_SP_SOUTH_RNA, 0);
373         }
374       }
375     }
376     return ret;
377   }
378 
379   /**
380    * getDefaultLibrary.
381    *
382    * @return a {@link ffx.potential.bonded.RotamerLibrary} object.
383    */
384   public static RotamerLibrary getDefaultLibrary() {
385     return defaultRotamerLibrary;
386   }
387 
388   /**
389    * Initializes default coordinates (presently PDB coordinates) for key atoms in all nucleic acid
390    * Residues. This is necessary to preserve rotamer independence while still adjusting rotamers to
391    * correctly meet prior residues; C4', O4', and C1' are used to build the rest of the nucleic acid
392    * base, but are moved with each Rotamer to take part of the strain of correctly meeting O3' of
393    * residue i-1.
394    *
395    * <p>It also initializes the location of O3' in both North and South puckers; while in theory
396    * this could be recalculated each time based off of C4', O4', and C1', it is easier to just store
397    * these two locations and call them when needed.
398    *
399    * <p>This MUST be called before any applyRotamer calls are made, else invalid coordinates will be
400    * stored.
401    *
402    * @param polymers the Polymer array to examine.
403    */
404   public static void initializeDefaultAtomicCoordinates(Polymer[] polymers) {
405     for (Polymer polymer : polymers) {
406       List<Residue> current = polymer.getResidues();
407       for (Residue residuej : current) {
408         if (residuej.getResidueType() == ResidueType.NA) {
409           residuej.initializeDefaultAtomicCoordinates();
410         }
411       }
412     }
413   }
414 
415   /**
416    * Measures the torsions of an amino acid Residue's current configuration.
417    *
418    * @param residue To be measured.
419    * @param chi     Array to be filled with torsion values.
420    * @param print   Verbosity flag.
421    * @return The number of rotamers this Residue has.
422    */
423   public static int measureAARotamer(Residue residue, double[] chi, boolean print) {
424     if (residue instanceof MultiResidue) {
425       residue = ((MultiResidue) residue).getActive();
426     }
427     AminoAcid3 name = residue.getAminoAcid3();
428     switch (name) {
429       case VAL -> {
430         Atom N = (Atom) residue.getAtomNode("N");
431         Atom CA = (Atom) residue.getAtomNode("CA");
432         Atom CB = (Atom) residue.getAtomNode("CB");
433         Atom CG1 = (Atom) residue.getAtomNode("CG1");
434         for (Torsion torsion : residue.getTorsionList()) {
435           if (torsion.compare(N, CA, CB, CG1)) {
436             chi[0] = torsion.measure();
437             if (print) {
438               logger.info(torsion.toString());
439             }
440             break;
441           }
442         }
443         return 1;
444       }
445       case LEU -> {
446         Atom N = (Atom) residue.getAtomNode("N");
447         Atom CA = (Atom) residue.getAtomNode("CA");
448         Atom CB = (Atom) residue.getAtomNode("CB");
449         Atom CG = (Atom) residue.getAtomNode("CG");
450         Atom CD1 = (Atom) residue.getAtomNode("CD1");
451         for (Torsion torsion : residue.getTorsionList()) {
452           if (torsion.compare(N, CA, CB, CG)) {
453             chi[0] = torsion.measure();
454             if (print) {
455               logger.info(torsion.toString());
456             }
457           }
458           if (torsion.compare(CA, CB, CG, CD1)) {
459             chi[1] = torsion.measure();
460             if (print) {
461               logger.info(torsion.toString());
462             }
463           }
464         }
465         return 2;
466       }
467       case ILE -> {
468         Atom N = (Atom) residue.getAtomNode("N");
469         Atom CA = (Atom) residue.getAtomNode("CA");
470         Atom CB = (Atom) residue.getAtomNode("CB");
471         Atom CD1 = (Atom) residue.getAtomNode("CD1");
472         Atom CG1 = (Atom) residue.getAtomNode("CG1");
473         for (Torsion torsion : residue.getTorsionList()) {
474           if (torsion.compare(N, CA, CB, CG1)) {
475             chi[0] = torsion.measure();
476             if (print) {
477               logger.info(torsion.toString());
478             }
479           }
480           if (torsion.compare(CA, CB, CG1, CD1)) {
481             chi[1] = torsion.measure();
482             if (print) {
483               logger.info(torsion.toString());
484             }
485           }
486         }
487         return 2;
488       }
489       case SER -> {
490         Atom N = (Atom) residue.getAtomNode("N");
491         Atom CA = (Atom) residue.getAtomNode("CA");
492         Atom CB = (Atom) residue.getAtomNode("CB");
493         Atom OG = (Atom) residue.getAtomNode("OG");
494         Atom HG = (Atom) residue.getAtomNode("HG");
495         for (Torsion torsion : residue.getTorsionList()) {
496           if (torsion.compare(N, CA, CB, OG)) {
497             chi[0] = torsion.measure();
498             if (print) {
499               logger.info(torsion.toString());
500             }
501           }
502           if (torsion.compare(CA, CB, OG, HG)) {
503             chi[1] = torsion.measure();
504             if (Double.isNaN(chi[1])) {
505               chi[1] = 180.0; // Possible numeric instability?
506             }
507             if (print) {
508               logger.info(torsion.toString());
509             }
510           }
511         }
512         return 2;
513       }
514       case THR -> {
515         Atom N = (Atom) residue.getAtomNode("N");
516         Atom CA = (Atom) residue.getAtomNode("CA");
517         Atom CB = (Atom) residue.getAtomNode("CB");
518         Atom OG1 = (Atom) residue.getAtomNode("OG1");
519         Atom HG1 = (Atom) residue.getAtomNode("HG1");
520         for (Torsion torsion : residue.getTorsionList()) {
521           if (torsion.compare(N, CA, CB, OG1)) {
522             chi[0] = torsion.measure();
523             if (print) {
524               logger.info(torsion.toString());
525             }
526           }
527           if (torsion.compare(CA, CB, OG1, HG1)) {
528             chi[1] = torsion.measure();
529             if (Double.isNaN(chi[1])) {
530               chi[1] = 180.0; // Possible numeric instability?
531             }
532             if (print) {
533               logger.info(torsion.toString());
534             }
535           }
536         }
537         return 2;
538       }
539       case CYS, CYX, CYD -> {
540         Atom N = (Atom) residue.getAtomNode("N");
541         Atom CA = (Atom) residue.getAtomNode("CA");
542         Atom CB = (Atom) residue.getAtomNode("CB");
543         Atom SG = (Atom) residue.getAtomNode("SG");
544         for (Torsion torsion : residue.getTorsionList()) {
545           if (torsion.compare(N, CA, CB, SG)) {
546             chi[0] = torsion.measure();
547             if (print) {
548               logger.info(torsion.toString());
549             }
550             break;
551           }
552         }
553         return 1;
554       }
555       case PHE -> {
556         Atom N = (Atom) residue.getAtomNode("N");
557         Atom CA = (Atom) residue.getAtomNode("CA");
558         Atom CB = (Atom) residue.getAtomNode("CB");
559         Atom CG = (Atom) residue.getAtomNode("CG");
560         for (Torsion torsion : residue.getTorsionList()) {
561           if (torsion.compare(N, CA, CB, CG)) {
562             chi[0] = torsion.measure();
563             if (print) {
564               logger.info(torsion.toString());
565             }
566             break;
567           }
568         }
569         return 1;
570       }
571       case PRO -> {
572         Atom N = (Atom) residue.getAtomNode("N");
573         Atom CA = (Atom) residue.getAtomNode("CA");
574         Atom CB = (Atom) residue.getAtomNode("CB");
575         Atom CD = (Atom) residue.getAtomNode("CD");
576         Atom CG = (Atom) residue.getAtomNode("CG");
577         for (Torsion torsion : residue.getTorsionList()) {
578           if (torsion.compare(N, CA, CB, CG)) {
579             chi[0] = torsion.measure();
580             if (print) {
581               logger.info(torsion.toString());
582             }
583           }
584           if (torsion.compare(CA, CB, CG, CD)) {
585             chi[1] = torsion.measure();
586             if (print) {
587               logger.info(torsion.toString());
588             }
589           }
590         }
591         return 2;
592       }
593       case TYR -> {
594         Atom N = (Atom) residue.getAtomNode("N");
595         Atom CA = (Atom) residue.getAtomNode("CA");
596         Atom CB = (Atom) residue.getAtomNode("CB");
597         Atom CD1 = (Atom) residue.getAtomNode("CD1");
598         Atom CE2 = (Atom) residue.getAtomNode("CE2");
599         Atom CG = (Atom) residue.getAtomNode("CG");
600         Atom CZ = (Atom) residue.getAtomNode("CZ");
601         Atom OH = (Atom) residue.getAtomNode("OH");
602         Atom HH = (Atom) residue.getAtomNode("HH");
603         for (Torsion torsion : residue.getTorsionList()) {
604           if (torsion.compare(N, CA, CB, CG)) {
605             chi[0] = torsion.measure();
606             if (print) {
607               logger.info(torsion.toString());
608             }
609           }
610           if (torsion.compare(CA, CB, CG, CD1)) {
611             chi[1] = torsion.measure();
612             if (print) {
613               logger.info(torsion.toString());
614             }
615           }
616           if (torsion.compare(CE2, CZ, OH, HH)) {
617             chi[2] = torsion.measure();
618             if (Double.isNaN(chi[2])) {
619               chi[2] = 180.0; // Possible numeric instability?
620             }
621             if (print) {
622               logger.info(torsion.toString());
623             }
624           }
625         }
626         return 3;
627       }
628       case TYD, TRP -> {
629         Atom N = (Atom) residue.getAtomNode("N");
630         Atom CA = (Atom) residue.getAtomNode("CA");
631         Atom CB = (Atom) residue.getAtomNode("CB");
632         Atom CD1 = (Atom) residue.getAtomNode("CD1");
633         Atom CG = (Atom) residue.getAtomNode("CG");
634         for (Torsion torsion : residue.getTorsionList()) {
635           if (torsion.compare(N, CA, CB, CG)) {
636             chi[0] = torsion.measure();
637             if (print) {
638               logger.info(torsion.toString());
639             }
640           }
641           if (torsion.compare(CA, CB, CG, CD1)) {
642             chi[1] = torsion.measure();
643             if (print) {
644               logger.info(torsion.toString());
645             }
646           }
647         }
648         return 2;
649       }
650       case HIS, HIE, HID -> {
651         Atom N = (Atom) residue.getAtomNode("N");
652         Atom CA = (Atom) residue.getAtomNode("CA");
653         Atom CB = (Atom) residue.getAtomNode("CB");
654         Atom CG = (Atom) residue.getAtomNode("CG");
655         Atom ND1 = (Atom) residue.getAtomNode("ND1");
656         for (Torsion torsion : residue.getTorsionList()) {
657           if (torsion.compare(N, CA, CB, CG)) {
658             chi[0] = torsion.measure();
659             if (print) {
660               logger.info(torsion.toString());
661             }
662           }
663           if (torsion.compare(CA, CB, CG, ND1)) {
664             chi[1] = torsion.measure();
665             if (print) {
666               logger.info(torsion.toString());
667             }
668           }
669         }
670         return 2;
671       }
672       case ASP -> {
673         Atom N = (Atom) residue.getAtomNode("N");
674         Atom CA = (Atom) residue.getAtomNode("CA");
675         Atom CB = (Atom) residue.getAtomNode("CB");
676         Atom CG = (Atom) residue.getAtomNode("CG");
677         for (Torsion torsion : residue.getTorsionList()) {
678           if (torsion.compare(N, CA, CB, CG)) {
679             chi[0] = torsion.measure();
680             if (print) {
681               logger.info(torsion.toString());
682             }
683             break;
684           }
685         }
686         return 1;
687       }
688       case ASH, ASN -> {
689         Atom N = (Atom) residue.getAtomNode("N");
690         Atom CA = (Atom) residue.getAtomNode("CA");
691         Atom CB = (Atom) residue.getAtomNode("CB");
692         Atom CG = (Atom) residue.getAtomNode("CG");
693         Atom OD1 = (Atom) residue.getAtomNode("OD1");
694         for (Torsion torsion : residue.getTorsionList()) {
695           if (torsion.compare(N, CA, CB, CG)) {
696             chi[0] = torsion.measure();
697             if (print) {
698               logger.info(torsion.toString());
699             }
700           }
701           if (torsion.compare(CA, CB, CG, OD1)) {
702             chi[1] = torsion.measure();
703             if (print) {
704               logger.info(torsion.toString());
705             }
706           }
707         }
708         return 2;
709       }
710       case GLU, GLN, GLH -> {
711         Atom N = (Atom) residue.getAtomNode("N");
712         Atom CA = (Atom) residue.getAtomNode("CA");
713         Atom CB = (Atom) residue.getAtomNode("CB");
714         Atom CG = (Atom) residue.getAtomNode("CG");
715         Atom CD = (Atom) residue.getAtomNode("CD");
716         Atom OE1 = (Atom) residue.getAtomNode("OE1");
717         for (Torsion torsion : residue.getTorsionList()) {
718           if (torsion.compare(N, CA, CB, CG)) {
719             chi[0] = torsion.measure();
720             if (print) {
721               logger.info(torsion.toString());
722             }
723           }
724           if (torsion.compare(CA, CB, CG, CD)) {
725             chi[1] = torsion.measure();
726             if (print) {
727               logger.info(torsion.toString());
728             }
729           }
730           if (torsion.compare(CB, CG, CD, OE1)) {
731             chi[2] = torsion.measure();
732             if (print) {
733               logger.info(torsion.toString());
734             }
735           }
736         }
737         return 3;
738       }
739       case MET -> {
740         Atom N = (Atom) residue.getAtomNode("N");
741         Atom CA = (Atom) residue.getAtomNode("CA");
742         Atom CB = (Atom) residue.getAtomNode("CB");
743         Atom CG = (Atom) residue.getAtomNode("CG");
744         Atom CE = (Atom) residue.getAtomNode("CE");
745         Atom SD = (Atom) residue.getAtomNode("SD");
746         for (Torsion torsion : residue.getTorsionList()) {
747           if (torsion.compare(N, CA, CB, CG)) {
748             chi[0] = torsion.measure();
749             if (print) {
750               logger.info(torsion.toString());
751             }
752           }
753           if (torsion.compare(CA, CB, CG, SD)) {
754             chi[1] = torsion.measure();
755             if (print) {
756               logger.info(torsion.toString());
757             }
758           }
759           if (torsion.compare(CB, CG, SD, CE)) {
760             chi[2] = torsion.measure();
761             if (print) {
762               logger.info(torsion.toString());
763             }
764           }
765         }
766         return 3;
767       }
768       case LYS, LYD -> {
769         Atom N = (Atom) residue.getAtomNode("N");
770         Atom CA = (Atom) residue.getAtomNode("CA");
771         Atom CB = (Atom) residue.getAtomNode("CB");
772         Atom CD = (Atom) residue.getAtomNode("CD");
773         Atom CE = (Atom) residue.getAtomNode("CE");
774         Atom CG = (Atom) residue.getAtomNode("CG");
775         Atom NZ = (Atom) residue.getAtomNode("NZ");
776         for (Torsion torsion : residue.getTorsionList()) {
777           if (torsion.compare(N, CA, CB, CG)) {
778             chi[0] = torsion.measure();
779             if (print) {
780               logger.info(torsion.toString());
781             }
782           }
783           if (torsion.compare(CA, CB, CG, CD)) {
784             chi[1] = torsion.measure();
785             if (print) {
786               logger.info(torsion.toString());
787             }
788           }
789           if (torsion.compare(CB, CG, CD, CE)) {
790             chi[2] = torsion.measure();
791             if (print) {
792               logger.info(torsion.toString());
793             }
794           }
795           if (torsion.compare(CG, CD, CE, NZ)) {
796             chi[3] = torsion.measure();
797             if (print) {
798               logger.info(torsion.toString());
799             }
800           }
801         }
802         return 4;
803       }
804       case ARG -> {
805         Atom N = (Atom) residue.getAtomNode("N");
806         Atom CA = (Atom) residue.getAtomNode("CA");
807         Atom CB = (Atom) residue.getAtomNode("CB");
808         Atom CD = (Atom) residue.getAtomNode("CD");
809         Atom CG = (Atom) residue.getAtomNode("CG");
810         Atom CZ = (Atom) residue.getAtomNode("CZ");
811         Atom NE = (Atom) residue.getAtomNode("NE");
812         for (Torsion torsion : residue.getTorsionList()) {
813           if (torsion.compare(N, CA, CB, CG)) {
814             chi[0] = torsion.measure();
815             if (print) {
816               logger.info(torsion.toString());
817             }
818           }
819           if (torsion.compare(CA, CB, CG, CD)) {
820             chi[1] = torsion.measure();
821             if (print) {
822               logger.info(torsion.toString());
823             }
824           }
825           if (torsion.compare(CB, CG, CD, NE)) {
826             chi[2] = torsion.measure();
827             if (print) {
828               logger.info(torsion.toString());
829             }
830           }
831           if (torsion.compare(CG, CD, NE, CZ)) {
832             chi[3] = torsion.measure();
833             if (print) {
834               logger.info(torsion.toString());
835             }
836           }
837         }
838         return 4;
839       }
840       case UNK -> {
841         chi = new double[7];
842         String resName = residue.getName().toUpperCase();
843         if (nonstdRotCache.containsKey(resName)) {
844           return nonstdRotCache.get(resName).measureNonstdRot(residue, chi, print);
845           // nonstdRotCache.get(resName).applyNonstdRotamer(residue, rotamer);
846         } else {
847           throw new IllegalArgumentException(format("(IAE) valueOf(%s)", residue.getName()));
848         }
849       }
850       default -> {
851         return 0;
852       }
853     }
854   }
855 
856   /**
857    * Measures the delta torsion (sugar pucker) of a nucleic acid Residue.
858    *
859    * @param residue To be measured
860    * @return Delta torsion (sugar pucker angle).
861    */
862   public static double measureDelta(Residue residue) {
863     Atom C5s = (Atom) residue.getAtomNode("C5'");
864     Atom C4s = (Atom) residue.getAtomNode("C4'");
865     Atom C3s = (Atom) residue.getAtomNode("C3'");
866     Atom O3s = (Atom) residue.getAtomNode("O3'");
867     Torsion torsion = O3s.getTorsion(C3s, C4s, C5s);
868     return torsion.measure();
869   }
870 
871   /**
872    * Measures the torsional angles of a residue's side chain.
873    *
874    * @param residue a {@link ffx.potential.bonded.Residue} object.
875    * @param print   a boolean.
876    * @return an array of {@link double} objects.
877    */
878   public static double[] measureRotamer(Residue residue, boolean print) {
879     if (residue != null) {
880       switch (residue.getResidueType()) {
881         case AA:
882           double[] chi = new double[4];
883           try {
884             measureRotamer(residue, chi, print);
885           } catch (ArrayIndexOutOfBoundsException e) {
886             String message = " Array passed to measureRotamer was not of sufficient size.";
887             logger.log(Level.WARNING, message, e);
888           }
889           return chi;
890         case NA:
891           chi = new double[7];
892           try {
893             measureRotamer(residue, chi, print);
894           } catch (ArrayIndexOutOfBoundsException e) {
895             String message = " Array passed to measureRotamer was not of sufficient size.";
896             logger.log(Level.WARNING, message, e);
897           }
898           return chi;
899         default:
900           return null;
901       }
902     }
903     return null;
904   }
905 
906   /**
907    * Measures the torsion angles of a Residue.
908    *
909    * @param residue To be measured
910    * @param chi     Array to be filled with torsion values
911    * @param print   Verbosity flag
912    * @return The number of rotamers this Residue has.
913    */
914   public static int measureRotamer(Residue residue, double[] chi, boolean print) {
915     if (residue == null) {
916       return -1;
917     }
918     int nRot = -1;
919     switch (residue.getResidueType()) {
920       case AA -> {
921         try {
922           nRot = measureAARotamer(residue, chi, print);
923         } catch (ArrayIndexOutOfBoundsException e) {
924           String message = " Array passed to measureRotamer was not of sufficient size.";
925           logger.log(Level.WARNING, message, e);
926         }
927       }
928       case NA -> {
929         try {
930           nRot = measureNARotamer(residue, chi, print);
931         } catch (ArrayIndexOutOfBoundsException e) {
932           String message = "Array passed to measureRotamer was not of sufficient size.";
933           logger.log(Level.WARNING, message, e);
934         }
935       }
936       default -> {
937         try {
938           measureUNKRotamer(residue, chi, print);
939         } catch (ArrayIndexOutOfBoundsException e) {
940           String message = "Array passed to measureRotamer was not of sufficient size.";
941           logger.log(Level.WARNING, message, e);
942         }
943       }
944     }
945     return nRot;
946   }
947 
948   /**
949    * Measures the torsions in a list of Residues.
950    *
951    * @param residueList Residues to be measured.
952    * @param print       Verbosity flag.
953    */
954   public static void measureRotamers(List<Residue> residueList, boolean print) {
955     double[] chi = new double[7];
956     if (residueList.isEmpty()) {
957       return;
958     }
959     logger.info("\n Residue    Current Torsional Angles");
960     for (Residue residue : residueList) {
961       fill(chi, 0.0);
962       measureRotamer(residue, chi, print);
963       switch (residue.getResidueType()) {
964         case AA -> logger.info(format(" %c %8s %8.3f %8.3f %8.3f %8.3f", residue.getChainID(), residue, chi[0], chi[1], chi[2], chi[3]));
965         case NA -> logger.info(format(" %c %8s %8.3f %8.3f %8.3f %8.3f %8.3f %8.3f %8.3f",
966                 residue.getChainID(), residue, chi[0], chi[1], chi[2], chi[3], chi[4], chi[5], chi[6]));
967         default -> logger.info(" Not recognized as a nucleic or amino acid residue");
968       }
969     }
970   }
971 
972   public static void readRotFile(File rotamerFile, MolecularAssembly assembly) throws IOException {
973     readRotFile(rotamerFile, assembly, 1);
974   }
975 
976   private static boolean addRotPatch(File rpatchFile) {
977     try (BufferedReader br = new BufferedReader(new FileReader(rpatchFile))) {
978       String resName = null;
979       List<String> applyLines = new ArrayList<>();
980       List<String> rotLines = new ArrayList<>();
981       ResidueType rType = Residue.ResidueType.AA;
982       String line = br.readLine();
983       while (line != null) {
984         line = line.trim();
985         if (line.startsWith("PLACE")) {
986           applyLines.add(line);
987         } else if (line.startsWith("RESNAME")) {
988           String[] toks = line.split("\\s+");
989           resName = toks[1];
990         } else if (line.startsWith("RESTYPE")) {
991           String[] toks = line.split("\\s+");
992           rType = switch (toks[1]) {
993             case "AA" -> ResidueType.AA;
994             case "NA" -> ResidueType.NA;
995             default -> ResidueType.UNK;
996           };
997         } else if (line.startsWith("ROTAMER")) {
998           rotLines.add(line);
999         }
1000         line = br.readLine();
1001       }
1002       if (resName != null) {
1003         List<Rotamer> rotamers = new ArrayList<>();
1004         for (String string : rotLines) {
1005           String[] toks = string.split("\\s+");
1006           int nVals = toks.length - 1;
1007           double[] values = new double[nVals];
1008           for (int i = 0; i < nVals; i++) {
1009             values[i] = Double.parseDouble(toks[i + 1]);
1010           }
1011           switch (rType) {
1012             case AA -> rotamers.add(new Rotamer(AminoAcid3.UNK, values));
1013             case NA -> rotamers.add(new Rotamer(NucleicAcid3.UNK, values));
1014             default -> rotamers.add(new Rotamer(values));
1015           }
1016         }
1017 
1018         if (nonstdRotCache.containsKey(resName)) {
1019           logger.warning(
1020               format(" Rotamer library already contains " + "rotamer definition for residue %s!",
1021                   resName));
1022         } else {
1023           NonstandardRotLibrary nrlib = new NonstandardRotLibrary(resName,
1024               applyLines.toArray(new String[0]), rotamers.toArray(new Rotamer[0]));
1025           nonstdRotCache.put(resName, nrlib);
1026         }
1027         return true;
1028       } else {
1029         return false;
1030       }
1031     } catch (IOException ex) {
1032       logger.warning(
1033           format(" Exception in parsing rotamer patch " + "file %s: %s", rpatchFile.getName(), ex));
1034       return false;
1035     }
1036   }
1037 
1038   /**
1039    * Obtains an idealized angle using the force field. Note: can be misleading! Often, idealized
1040    * angles are not equilibrium angles; for example, AMOEBA 2018 has phenylalanine ring interior
1041    * angles that sum to > 720 degrees. This does not mean that an idealized phenylalanine ring
1042    * violates geometry, it means that the ring is always under at least a little strain.
1043    *
1044    * @param a1 An Atom.
1045    * @param a2 Another Atom.
1046    * @param a3 Another Atom.
1047    * @return Force field-defined a1-a2-a3 angle in degrees.
1048    */
1049   private static double angleFromForceField(Atom a1, Atom a2, Atom a3) {
1050     Angle a = a1.getAngle(a2, a3);
1051     AngleType at = a.getAngleType();
1052     return at.angle[a.nh];
1053   }
1054 
1055   /**
1056    * Applies an amino acid Rotamer.
1057    *
1058    * @param residue Residue
1059    * @param rotamer Rotamer to be applied to Residue
1060    */
1061   private static void applyAARotamer(Residue residue, Rotamer rotamer) {
1062     if (residue == null || rotamer == null) {
1063       return;
1064     }
1065     AminoAcid3 name;
1066     if (residue instanceof MultiResidue) {
1067       name = rotamer.aminoAcid3;
1068       if (!((MultiResidue) residue).setActiveResidue(name)) {
1069         logger.warning(format(" Could not set residue %s for multi-residue %s", name, residue));
1070       }
1071     } else {
1072       name = rotamer.aminoAcid3;
1073     }
1074     switch (name) {
1075       case VAL -> {
1076         Atom CA = (Atom) residue.getAtomNode("CA");
1077         Atom CB = (Atom) residue.getAtomNode("CB");
1078         Atom N = (Atom) residue.getAtomNode("N");
1079         Atom CG1 = (Atom) residue.getAtomNode("CG1");
1080         Atom CG2 = (Atom) residue.getAtomNode("CG2");
1081         Atom HB = (Atom) residue.getAtomNode("HB");
1082         Atom HG11 = (Atom) residue.getAtomNode("HG11");
1083         Atom HG12 = (Atom) residue.getAtomNode("HG12");
1084         Atom HG13 = (Atom) residue.getAtomNode("HG13");
1085         Atom HG21 = (Atom) residue.getAtomNode("HG21");
1086         Atom HG22 = (Atom) residue.getAtomNode("HG22");
1087         Atom HG23 = (Atom) residue.getAtomNode("HG23");
1088         Bond CG_CB = CB.getBond(CG1);
1089         Bond HB_CB = CB.getBond(HB);
1090         Bond HG_CG = HG11.getBond(CG1);
1091         double dCG_CB = CG_CB.bondType.distance;
1092         double dHB_CB = HB_CB.bondType.distance;
1093         double dHG_CG = HG_CG.bondType.distance;
1094         Angle CG_CB_CA = CG1.getAngle(CB, CA);
1095         Angle HB_CB_CA = HB.getAngle(CB, CA);
1096         Angle HG_CG_CB = HG11.getAngle(CG1, CB);
1097         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
1098         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
1099         double dHG_CG_CB = HG_CG_CB.angleType.angle[HG_CG_CB.nh];
1100         intxyz(CG1, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1101         intxyz(CG2, CB, dCG_CB, CA, dCG_CB_CA, CG1, 109.5, -1);
1102         intxyz(HB, CB, dHB_CB, CA, dHB_CB_CA, CG1, 109.4, 1);
1103         intxyz(HG11, CG1, dHG_CG, CB, dHG_CG_CB, CA, 180.0, 0);
1104         intxyz(HG12, CG1, dHG_CG, CB, dHG_CG_CB, HG11, 109.4, 1);
1105         intxyz(HG13, CG1, dHG_CG, CB, dHG_CG_CB, HG11, 109.4, -1);
1106         intxyz(HG21, CG2, dHG_CG, CB, dHG_CG_CB, CA, 180.0, 0);
1107         intxyz(HG22, CG2, dHG_CG, CB, dHG_CG_CB, HG21, 109.4, 1);
1108         intxyz(HG23, CG2, dHG_CG, CB, dHG_CG_CB, HG21, 109.4, -1);
1109       }
1110       case LEU -> {
1111         Atom CA = (Atom) residue.getAtomNode("CA");
1112         Atom CB = (Atom) residue.getAtomNode("CB");
1113         Atom N = (Atom) residue.getAtomNode("N");
1114         Atom CG = (Atom) residue.getAtomNode("CG");
1115         Atom CD1 = (Atom) residue.getAtomNode("CD1");
1116         Atom CD2 = (Atom) residue.getAtomNode("CD2");
1117         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1118         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1119         Atom HG = (Atom) residue.getAtomNode("HG");
1120         Atom HD11 = (Atom) residue.getAtomNode("HD11");
1121         Atom HD12 = (Atom) residue.getAtomNode("HD12");
1122         Atom HD13 = (Atom) residue.getAtomNode("HD13");
1123         Atom HD21 = (Atom) residue.getAtomNode("HD21");
1124         Atom HD22 = (Atom) residue.getAtomNode("HD22");
1125         Atom HD23 = (Atom) residue.getAtomNode("HD23");
1126         Bond CG_CB = CG.getBond(CB);
1127         Bond CD_CG = CD1.getBond(CG);
1128         Bond HB_CB = HB2.getBond(CB);
1129         Bond HG_CG = HG.getBond(CG);
1130         Bond HD_CD = HD11.getBond(CD1);
1131         double dCG_CB = CG_CB.bondType.distance;
1132         double dCD_CG = CD_CG.bondType.distance;
1133         double dHB_CB = HB_CB.bondType.distance;
1134         double dHG_CG = HG_CG.bondType.distance;
1135         double dHD_CD = HD_CD.bondType.distance;
1136         Angle CG_CB_CA = CG.getAngle(CB, CA);
1137         Angle CD_CG_CB = CD1.getAngle(CG, CB);
1138         Angle HB_CB_CA = HB2.getAngle(CB, CA);
1139         Angle HG_CG_CB = HG.getAngle(CG, CB);
1140         Angle HD_CD_CG = HD11.getAngle(CD1, CG);
1141         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
1142         double dCD_CG_CB = CD_CG_CB.angleType.angle[CD_CG_CB.nh];
1143         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
1144         double dHG_CG_CB = HG_CG_CB.angleType.angle[HG_CG_CB.nh];
1145         double dHD_CD_CG = HD_CD_CG.angleType.angle[HD_CD_CG.nh];
1146         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1147         intxyz(CD1, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2, 0);
1148         intxyz(CD2, CG, dCD_CG, CB, dCD_CG_CB, CD1, 109.5, -1);
1149         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
1150         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
1151         intxyz(HG, CG, dHG_CG, CB, dHG_CG_CB, CD1, 109.4, 1);
1152         intxyz(HD11, CD1, dHD_CD, CG, dHD_CD_CG, CB, 180.0, 0);
1153         intxyz(HD12, CD1, dHD_CD, CG, dHD_CD_CG, HD11, 109.4, 1);
1154         intxyz(HD13, CD1, dHD_CD, CG, dHD_CD_CG, HD11, 109.4, -1);
1155         intxyz(HD21, CD2, dHD_CD, CG, dHD_CD_CG, CB, 180.0, 0);
1156         intxyz(HD22, CD2, dHD_CD, CG, dHD_CD_CG, HD21, 109.4, 1);
1157         intxyz(HD23, CD2, dHD_CD, CG, dHD_CD_CG, HD21, 109.4, -1);
1158       }
1159       case ILE -> {
1160         Atom CA = (Atom) residue.getAtomNode("CA");
1161         Atom CB = (Atom) residue.getAtomNode("CB");
1162         Atom N = (Atom) residue.getAtomNode("N");
1163         Atom CG1 = (Atom) residue.getAtomNode("CG1");
1164         Atom CG2 = (Atom) residue.getAtomNode("CG2");
1165         Atom CD1 = (Atom) residue.getAtomNode("CD1");
1166         Atom HB = (Atom) residue.getAtomNode("HB");
1167         Atom HG12 = (Atom) residue.getAtomNode("HG12");
1168         Atom HG13 = (Atom) residue.getAtomNode("HG13");
1169         Atom HG21 = (Atom) residue.getAtomNode("HG21");
1170         Atom HG22 = (Atom) residue.getAtomNode("HG22");
1171         Atom HG23 = (Atom) residue.getAtomNode("HG23");
1172         Atom HD11 = (Atom) residue.getAtomNode("HD11");
1173         Atom HD12 = (Atom) residue.getAtomNode("HD12");
1174         Atom HD13 = (Atom) residue.getAtomNode("HD13");
1175         Bond CG1_CB = CG1.getBond(CB);
1176         Bond CG2_CB = CG2.getBond(CB);
1177         Bond CD1_CG1 = CD1.getBond(CG1);
1178         Bond HB_CB = HB.getBond(CB);
1179         Bond HG1_CG = HG12.getBond(CG1);
1180         Bond HG2_CG = HG22.getBond(CG2);
1181         Bond HD_CD = HD12.getBond(CD1);
1182         double dCG1_CB = CG1_CB.bondType.distance;
1183         double dCG2_CB = CG2_CB.bondType.distance;
1184         double dCD1_CG1 = CD1_CG1.bondType.distance;
1185         double dHB_CB = HB_CB.bondType.distance;
1186         double dHG1_CG = HG1_CG.bondType.distance;
1187         double dHG2_CG = HG2_CG.bondType.distance;
1188         double dHD_CD = HD_CD.bondType.distance;
1189         Angle CG1_CB_CA = CG1.getAngle(CB, CA);
1190         Angle CG2_CB_CA = CG2.getAngle(CB, CA);
1191         Angle CD1_CG1_CB = CD1.getAngle(CG1, CB);
1192         Angle HB_CB_CA = HB.getAngle(CB, CA);
1193         Angle HG1_CG_CB = HG12.getAngle(CG1, CB);
1194         Angle HG2_CG_CB = HG21.getAngle(CG2, CB);
1195         Angle HD_CD1_CG1 = HD11.getAngle(CD1, CG1);
1196         double dCG1_CB_CA = CG1_CB_CA.angleType.angle[CG1_CB_CA.nh];
1197         double dCG2_CB_CA = CG2_CB_CA.angleType.angle[CG2_CB_CA.nh];
1198         double dCD1_CG1_CB = CD1_CG1_CB.angleType.angle[CD1_CG1_CB.nh];
1199         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
1200         double dHG1_CG_CB = HG1_CG_CB.angleType.angle[HG1_CG_CB.nh];
1201         double dHG2_CG_CB = HG2_CG_CB.angleType.angle[HG2_CG_CB.nh];
1202         double dHD_CD1_CG1 = HD_CD1_CG1.angleType.angle[HD_CD1_CG1.nh];
1203         intxyz(CG1, CB, dCG1_CB, CA, dCG1_CB_CA, N, rotamer.chi1, 0);
1204         intxyz(CG2, CB, dCG2_CB, CA, dCG2_CB_CA, CG1, 109.5, 1);
1205         intxyz(CD1, CG1, dCD1_CG1, CB, dCD1_CG1_CB, CA, rotamer.chi2, 0);
1206         intxyz(HB, CB, dHB_CB, CA, dHB_CB_CA, CG2, 109.4, 1);
1207         intxyz(HG12, CG1, dHG1_CG, CB, dHG1_CG_CB, CD1, 109.4, 1);
1208         intxyz(HG13, CG1, dHG1_CG, CB, dHG1_CG_CB, CD1, 109.4, -1);
1209         intxyz(HG21, CG2, dHG2_CG, CB, dHG2_CG_CB, CG1, 180.0, 0);
1210         intxyz(HG22, CG2, dHG2_CG, CB, dHG2_CG_CB, HG21, 109.0, 1);
1211         intxyz(HG23, CG2, dHG2_CG, CB, dHG2_CG_CB, HG21, 109.0, -1);
1212         intxyz(HD11, CD1, dHD_CD, CG1, dHD_CD1_CG1, CB, 180.0, 0);
1213         intxyz(HD12, CD1, dHD_CD, CG1, dHD_CD1_CG1, HD11, 109.0, 1);
1214         intxyz(HD13, CD1, dHD_CD, CG1, dHD_CD1_CG1, HD11, 109.0, -1);
1215       }
1216       case SER -> {
1217         Atom CA = (Atom) residue.getAtomNode("CA");
1218         Atom CB = (Atom) residue.getAtomNode("CB");
1219         Atom N = (Atom) residue.getAtomNode("N");
1220         Atom OG = (Atom) residue.getAtomNode("OG");
1221         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1222         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1223         Atom HG = (Atom) residue.getAtomNode("HG");
1224         Bond OG_CB = OG.getBond(CB);
1225         Bond HB_CB = HB2.getBond(CB);
1226         Bond HG_OG = HG.getBond(OG);
1227         double dOG_CB = OG_CB.bondType.distance;
1228         double dHB_CB = HB_CB.bondType.distance;
1229         double dHG_OG = HG_OG.bondType.distance;
1230         Angle OG_CB_CA = OG.getAngle(CB, CA);
1231         Angle HB_CB_CA = HB2.getAngle(CB, CA);
1232         Angle HG_OG_CB = HG.getAngle(OG, CB);
1233         double dOG_CB_CA = OG_CB_CA.angleType.angle[OG_CB_CA.nh];
1234         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
1235         double dHG_OG_CB = HG_OG_CB.angleType.angle[HG_OG_CB.nh];
1236         intxyz(OG, CB, dOG_CB, CA, dOG_CB_CA, N, rotamer.chi1, 0);
1237         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, OG, 106.7, 1);
1238         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, OG, 106.7, -1);
1239         intxyz(HG, OG, dHG_OG, CB, dHG_OG_CB, CA, 180.0, 0);
1240         if (rotamer.length == 2) {
1241           intxyz(HG, OG, dHG_OG, CB, dHG_OG_CB, CA, rotamer.chi2, 0);
1242         } else {
1243           intxyz(HG, OG, dHG_OG, CB, dHG_OG_CB, CA, 180.0, 0);
1244         }
1245       }
1246       case THR -> {
1247         Atom CA = (Atom) residue.getAtomNode("CA");
1248         Atom CB = (Atom) residue.getAtomNode("CB");
1249         Atom N = (Atom) residue.getAtomNode("N");
1250         Atom OG1 = (Atom) residue.getAtomNode("OG1");
1251         Atom CG2 = (Atom) residue.getAtomNode("CG2");
1252         Atom HB = (Atom) residue.getAtomNode("HB");
1253         Atom HG1 = (Atom) residue.getAtomNode("HG1");
1254         Atom HG21 = (Atom) residue.getAtomNode("HG21");
1255         Atom HG22 = (Atom) residue.getAtomNode("HG22");
1256         Atom HG23 = (Atom) residue.getAtomNode("HG23");
1257         Bond OG1_CB = OG1.getBond(CB);
1258         Bond CG2_CB = CG2.getBond(CB);
1259         Bond HB_CB = HB.getBond(CB);
1260         Bond HG1_OG1 = HG1.getBond(OG1);
1261         Bond HG2_CG2 = HG21.getBond(CG2);
1262         double dOG1_CB = OG1_CB.bondType.distance;
1263         double dCG2_CB = CG2_CB.bondType.distance;
1264         double dHB_CB = HB_CB.bondType.distance;
1265         double dHG1_OG1 = HG1_OG1.bondType.distance;
1266         double dHG2_CG2 = HG2_CG2.bondType.distance;
1267         Angle OG1_CB_CA = OG1.getAngle(CB, CA);
1268         Angle CG2_CB_CA = CG2.getAngle(CB, CA);
1269         Angle HB_CB_CA = HB.getAngle(CB, CA);
1270         Angle HG1_OG1_CB = HG1.getAngle(OG1, CB);
1271         Angle HG2_CG2_CB = HG21.getAngle(CG2, CB);
1272         double dOG1_CB_CA = OG1_CB_CA.angleType.angle[OG1_CB_CA.nh];
1273         double dCG2_CB_CA = CG2_CB_CA.angleType.angle[CG2_CB_CA.nh];
1274         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
1275         double dHG1_OG1_CB = HG1_OG1_CB.angleType.angle[HG1_OG1_CB.nh];
1276         double dHG2_CG2_CB = HG2_CG2_CB.angleType.angle[HG2_CG2_CB.nh];
1277         intxyz(OG1, CB, dOG1_CB, CA, dOG1_CB_CA, N, rotamer.chi1, 0);
1278         intxyz(CG2, CB, dCG2_CB, CA, dCG2_CB_CA, OG1, 107.7, 1);
1279         intxyz(HB, CB, dHB_CB, CA, dHB_CB_CA, OG1, 106.7, -1);
1280         intxyz(HG1, OG1, dHG1_OG1, CB, dHG1_OG1_CB, CA, 180.0, 0);
1281         if (rotamer.length == 2) {
1282           intxyz(HG1, OG1, dHG1_OG1, CB, dHG1_OG1_CB, CA, rotamer.chi2, 0);
1283         } else {
1284           intxyz(HG1, OG1, dHG1_OG1, CB, dHG1_OG1_CB, CA, 180, 0);
1285         }
1286         intxyz(HG21, CG2, dHG2_CG2, CB, dHG2_CG2_CB, CA, 180.0, 0);
1287         intxyz(HG22, CG2, dHG2_CG2, CB, dHG2_CG2_CB, HG21, 109.0, 1);
1288         intxyz(HG23, CG2, dHG2_CG2, CB, dHG2_CG2_CB, HG21, 109.0, -1);
1289       }
1290       case CYS, CYX -> {
1291         Atom CA = (Atom) residue.getAtomNode("CA");
1292         Atom CB = (Atom) residue.getAtomNode("CB");
1293         Atom N = (Atom) residue.getAtomNode("N");
1294         Atom SG = (Atom) residue.getAtomNode("SG");
1295         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1296         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1297         Atom HG = (Atom) residue.getAtomNode("HG");
1298         if (CA == null || CB == null || N == null || SG == null || HB2 == null || HB3 == null
1299             || HG == null) {
1300           break;
1301         }
1302         Bond SG_CB = SG.getBond(CB);
1303         Bond HB_CB = HB2.getBond(CB);
1304         Bond HG_SG = HG.getBond(SG);
1305         double dSG_CB = SG_CB.bondType.distance;
1306         double dHB_CB = HB_CB.bondType.distance;
1307         double dHG_SG = HG_SG.bondType.distance;
1308         Angle SG_CB_CA = SG.getAngle(CB, CA);
1309         Angle HB_CB_CA = HB2.getAngle(CB, CA);
1310         Angle HG_SG_CB = HG.getAngle(SG, CB);
1311         double dSG_CB_CA = SG_CB_CA.angleType.angle[SG_CB_CA.nh];
1312         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
1313         double dHG_SG_CB = HG_SG_CB.angleType.angle[HG_SG_CB.nh];
1314         intxyz(SG, CB, dSG_CB, CA, dSG_CB_CA, N, rotamer.chi1, 0);
1315         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, SG, 112.0, 1);
1316         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, SG, 112.0, -1);
1317         intxyz(HG, SG, dHG_SG, CB, dHG_SG_CB, CA, 180.0, 0);
1318       }
1319       case CYD -> {
1320         Atom CA = (Atom) residue.getAtomNode("CA");
1321         Atom CB = (Atom) residue.getAtomNode("CB");
1322         Atom N = (Atom) residue.getAtomNode("N");
1323         Atom SG = (Atom) residue.getAtomNode("SG");
1324         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1325         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1326         Bond SG_CB = SG.getBond(CB);
1327         Bond HB_CB = HB2.getBond(CB);
1328         double dSG_CB = SG_CB.bondType.distance;
1329         double dHB_CB = HB_CB.bondType.distance;
1330         Angle SG_CB_CA = SG.getAngle(CB, CA);
1331         Angle HB_CB_CA = HB2.getAngle(CB, CA);
1332         double dSG_CB_CA = SG_CB_CA.angleType.angle[SG_CB_CA.nh];
1333         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
1334         intxyz(SG, CB, dSG_CB, CA, dSG_CB_CA, N, rotamer.chi1, 0);
1335         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, SG, 112.0, 1);
1336         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, SG, 112.0, -1);
1337       }
1338       case PHE -> {
1339         Atom CA = (Atom) residue.getAtomNode("CA");
1340         Atom CB = (Atom) residue.getAtomNode("CB");
1341         Atom N = (Atom) residue.getAtomNode("N");
1342         Atom CG = (Atom) residue.getAtomNode("CG");
1343         Atom CD1 = (Atom) residue.getAtomNode("CD1");
1344         Atom CD2 = (Atom) residue.getAtomNode("CD2");
1345         Atom CE1 = (Atom) residue.getAtomNode("CE1");
1346         Atom CE2 = (Atom) residue.getAtomNode("CE2");
1347         Atom CZ = (Atom) residue.getAtomNode("CZ");
1348         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1349         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1350         Atom HD1 = (Atom) residue.getAtomNode("HD1");
1351         Atom HD2 = (Atom) residue.getAtomNode("HD2");
1352         Atom HE1 = (Atom) residue.getAtomNode("HE1");
1353         Atom HE2 = (Atom) residue.getAtomNode("HE2");
1354         Atom HZ = (Atom) residue.getAtomNode("HZ");
1355         Bond CG_CB = CG.getBond(CB);
1356         Bond CD_CG = CD1.getBond(CG);
1357         Bond CE_CD = CE1.getBond(CD1);
1358         Bond HB_CB = HB2.getBond(CB);
1359         double dCG_CB = CG_CB.bondType.distance;
1360         double dCD_CG = CD_CG.bondType.distance;
1361         double dCE_CD = CE_CD.bondType.distance;
1362         double dHB_CB = HB_CB.bondType.distance;
1363 
1364         double dHD_CD = HD1.getBond(CD1).bondType.distance;
1365         double dHE_CE = HE1.getBond(CE1).bondType.distance;
1366         double dHZ_CZ = HZ.getBond(CZ).bondType.distance;
1367 
1368         double dCG_CB_CA = getAngle(name, CG, CB, CA);
1369         double dCD_CG_CB = getAngle(name, CD1, CG, CB);
1370         double dCE_CD_CG = getAngle(name, CE1, CD1, CG);
1371         double dHB_CB_CA = getAngle(name, HB2, CB, CA);
1372 
1373         double dHD_CD_CG = getAngle(name, HD1, CD1, CG);
1374         double dHD_CD_CE = getAngle(name, HD1, CD1, CE1);
1375         double dHE_CE_CD = getAngle(name, HE1, CE1, CD1);
1376         double dHE_CE_CZ = getAngle(name, HE1, CE1, CZ);
1377         double dHZ_CZ_CE = getAngle(name, HZ, CZ, CE1);
1378 
1379         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1380         intxyz(CD1, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2, 0);
1381         intxyz(CD2, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2 + 180, 0);
1382         intxyz(CE1, CD1, dCE_CD, CG, dCE_CD_CG, CB, 180, 0);
1383         intxyz(CE2, CD2, dCE_CD, CG, dCE_CD_CG, CB, 180, 0);
1384         applyCZ(name, CZ, CG, CE1, CD1, CE2, CD2);
1385         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
1386         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
1387 
1388         intxyz(HD1, CD1, dHD_CD, CG, dHD_CD_CG, CE1, dHD_CD_CE, 3);
1389         intxyz(HD2, CD2, dHD_CD, CG, dHD_CD_CG, CE2, dHD_CD_CE, 3);
1390         intxyz(HE1, CE1, dHE_CE, CD1, dHE_CE_CD, CZ, dHE_CE_CZ, 3);
1391         intxyz(HE2, CE2, dHE_CE, CD2, dHE_CE_CD, CZ, dHE_CE_CZ, 3);
1392         intxyz(HZ, CZ, dHZ_CZ, CE1, dHZ_CZ_CE, CE2, dHZ_CZ_CE, 3);
1393       }
1394       case PRO -> {
1395         Atom CA = (Atom) residue.getAtomNode("CA");
1396         Atom CB = (Atom) residue.getAtomNode("CB");
1397         Atom N = (Atom) residue.getAtomNode("N");
1398         Atom CG = (Atom) residue.getAtomNode("CG");
1399         Atom CD = (Atom) residue.getAtomNode("CD");
1400         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1401         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1402         Atom HG2 = (Atom) residue.getAtomNode("HG2");
1403         Atom HG3 = (Atom) residue.getAtomNode("HG3");
1404         Atom HD2 = (Atom) residue.getAtomNode("HD2");
1405         Atom HD3 = (Atom) residue.getAtomNode("HD3");
1406         Bond CG_CB = CG.getBond(CB);
1407         Bond CD_CG = CD.getBond(CG);
1408         Bond HB_CB = HB2.getBond(CB);
1409         Bond HG_CG = HG2.getBond(CG);
1410         Bond HD_CD = HD2.getBond(CD);
1411         double dCG_CB = CG_CB.bondType.distance;
1412         double dCD_CG = CD_CG.bondType.distance;
1413         double dHB_CB = HB_CB.bondType.distance;
1414         double dHG_CG = HG_CG.bondType.distance;
1415         double dHD_CD = HD_CD.bondType.distance;
1416         Angle CG_CB_CA = CG.getAngle(CB, CA);
1417         Angle CD_CG_CB = CD.getAngle(CG, CB);
1418         Angle HB_CB_CA = HB2.getAngle(CB, CA);
1419         Angle HG_CG_CB = HG2.getAngle(CG, CB);
1420         Angle HD_CD_CG = HD2.getAngle(CD, CG);
1421         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
1422         double dCD_CG_CB = CD_CG_CB.angleType.angle[CD_CG_CB.nh];
1423         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
1424         double dHG_CG_CB = HG_CG_CB.angleType.angle[HG_CG_CB.nh];
1425         double dHD_CD_CG = HD_CD_CG.angleType.angle[HD_CD_CG.nh];
1426         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1427         intxyz(CD, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2, 0);
1428         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
1429         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
1430         intxyz(HG2, CG, dHG_CG, CB, dHG_CG_CB, CD, 109.4, 1);
1431         intxyz(HG3, CG, dHG_CG, CB, dHG_CG_CB, CD, 109.4, -1);
1432         intxyz(HD2, CD, dHD_CD, CG, dHD_CD_CG, N, 109.4, 1);
1433         intxyz(HD3, CD, dHD_CD, CG, dHD_CD_CG, N, 109.4, -1);
1434       }
1435       case TYR -> {
1436         Atom CA = (Atom) residue.getAtomNode("CA");
1437         Atom CB = (Atom) residue.getAtomNode("CB");
1438         Atom N = (Atom) residue.getAtomNode("N");
1439         Atom CG = (Atom) residue.getAtomNode("CG");
1440         Atom CD1 = (Atom) residue.getAtomNode("CD1");
1441         Atom CD2 = (Atom) residue.getAtomNode("CD2");
1442         Atom CE1 = (Atom) residue.getAtomNode("CE1");
1443         Atom CE2 = (Atom) residue.getAtomNode("CE2");
1444         Atom CZ = (Atom) residue.getAtomNode("CZ");
1445         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1446         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1447         Atom HD1 = (Atom) residue.getAtomNode("HD1");
1448         Atom HD2 = (Atom) residue.getAtomNode("HD2");
1449         Atom HE1 = (Atom) residue.getAtomNode("HE1");
1450         Atom HE2 = (Atom) residue.getAtomNode("HE2");
1451         Atom OH = (Atom) residue.getAtomNode("OH");
1452         Atom HH = (Atom) residue.getAtomNode("HH");
1453         Bond CG_CB = CG.getBond(CB);
1454         Bond CD_CG = CD1.getBond(CG);
1455         Bond CE_CD = CE1.getBond(CD1);
1456         Bond HB_CB = HB2.getBond(CB);
1457         double dCG_CB = CG_CB.bondType.distance;
1458         double dCD_CG = CD_CG.bondType.distance;
1459         double dCE_CD = CE_CD.bondType.distance;
1460         double dHB_CB = HB_CB.bondType.distance;
1461 
1462         double dHD_CD = HD1.getBond(CD1).bondType.distance;
1463         double dHE_CE = HE1.getBond(CE1).bondType.distance;
1464         double dOH_CZ = OH.getBond(CZ).bondType.distance;
1465         double dHH_OH = HH.getBond(OH).bondType.distance;
1466 
1467         double dCG_CB_CA = getAngle(name, CG, CB, CA);
1468         double dCD_CG_CB = getAngle(name, CD1, CG, CB);
1469         double dCE_CD_CG = getAngle(name, CE1, CD1, CG);
1470         double dHB_CB_CA = getAngle(name, HB2, CB, CA);
1471 
1472         double dHD_CD_CG = getAngle(name, HD1, CD1, CG);
1473         double dHD_CD_CE = getAngle(name, HD1, CD1, CE1);
1474         double dHE_CE_CD = getAngle(name, HE1, CE1, CD1);
1475         double dHE_CE_CZ = getAngle(name, HE1, CE1, CZ);
1476         double dOH_CZ_CE = getAngle(name, OH, CZ, CE1);
1477         double dHH_OH_CZ = getAngle(name, HH, OH, CZ);
1478 
1479         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1480         intxyz(CD1, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2, 0);
1481         intxyz(CD2, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2 + 180, 0);
1482         intxyz(CE1, CD1, dCE_CD, CG, dCE_CD_CG, CB, 180, 0);
1483         intxyz(CE2, CD2, dCE_CD, CG, dCE_CD_CG, CB, 180, 0);
1484         applyCZ(name, CZ, CG, CE1, CD1, CE2, CD2);
1485         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
1486         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
1487 
1488         intxyz(HD1, CD1, dHD_CD, CG, dHD_CD_CG, CE1, dHD_CD_CE, 3);
1489         intxyz(HD2, CD2, dHD_CD, CG, dHD_CD_CG, CE2, dHD_CD_CE, 3);
1490         intxyz(HE1, CE1, dHE_CE, CD1, dHE_CE_CD, CZ, dHE_CE_CZ, 3);
1491         intxyz(HE2, CE2, dHE_CE, CD2, dHE_CE_CD, CZ, dHE_CE_CZ, 3);
1492         intxyz(OH, CZ, dOH_CZ, CE1, dOH_CZ_CE, CE2, dOH_CZ_CE, 3);
1493 
1494         if (rotamer.length == 3) {
1495           intxyz(HH, OH, dHH_OH, CZ, dHH_OH_CZ, CE2, rotamer.chi3, 0);
1496         } else {
1497           intxyz(HH, OH, dHH_OH, CZ, dHH_OH_CZ, CE2, 0.0, 0);
1498         }
1499       }
1500       case TYD -> {
1501         Atom CA = (Atom) residue.getAtomNode("CA");
1502         Atom CB = (Atom) residue.getAtomNode("CB");
1503         Atom N = (Atom) residue.getAtomNode("N");
1504         Atom CG = (Atom) residue.getAtomNode("CG");
1505         Atom CD1 = (Atom) residue.getAtomNode("CD1");
1506         Atom CD2 = (Atom) residue.getAtomNode("CD2");
1507         Atom CE1 = (Atom) residue.getAtomNode("CE1");
1508         Atom CE2 = (Atom) residue.getAtomNode("CE2");
1509         Atom CZ = (Atom) residue.getAtomNode("CZ");
1510         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1511         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1512         Atom HD1 = (Atom) residue.getAtomNode("HD1");
1513         Atom HD2 = (Atom) residue.getAtomNode("HD2");
1514         Atom HE1 = (Atom) residue.getAtomNode("HE1");
1515         Atom HE2 = (Atom) residue.getAtomNode("HE2");
1516         Atom OH = (Atom) residue.getAtomNode("OH");
1517         Bond CG_CB = CG.getBond(CB);
1518         Bond CD_CG = CD1.getBond(CG);
1519         Bond CE_CD = CE1.getBond(CD1);
1520         Bond HB_CB = HB2.getBond(CB);
1521         double dCG_CB = CG_CB.bondType.distance;
1522         double dCD_CG = CD_CG.bondType.distance;
1523         double dCE_CD = CE_CD.bondType.distance;
1524         double dHB_CB = HB_CB.bondType.distance;
1525 
1526         double dHD_CD = HD1.getBond(CD1).bondType.distance;
1527         double dHE_CE = HE1.getBond(CE1).bondType.distance;
1528         double dOH_CZ = OH.getBond(CZ).bondType.distance;
1529 
1530         double dCG_CB_CA = getAngle(name, CG, CB, CA);
1531         double dCD_CG_CB = getAngle(name, CD1, CG, CB);
1532         double dCE_CD_CG = getAngle(name, CE1, CD1, CG);
1533         double dHB_CB_CA = getAngle(name, HB2, CB, CA);
1534 
1535         double dHD_CD_CG = getAngle(name, HD1, CD1, CG);
1536         double dHD_CD_CE = getAngle(name, HD1, CD1, CE1);
1537         double dHE_CE_CD = getAngle(name, HE1, CE1, CD1);
1538         double dHE_CE_CZ = getAngle(name, HE1, CE1, CZ);
1539         double dOH_CZ_CE = getAngle(name, OH, CZ, CE1);
1540 
1541         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1542         intxyz(CD1, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2, 0);
1543         intxyz(CD2, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2 + 180, 0);
1544         intxyz(CE1, CD1, dCE_CD, CG, dCE_CD_CG, CB, 180, 0);
1545         intxyz(CE2, CD2, dCE_CD, CG, dCE_CD_CG, CB, 180, 0);
1546         applyCZ(name, CZ, CG, CE1, CD1, CE2, CD2);
1547         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
1548         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
1549 
1550         intxyz(HD1, CD1, dHD_CD, CG, dHD_CD_CG, CE1, dHD_CD_CE, 3);
1551         intxyz(HD2, CD2, dHD_CD, CG, dHD_CD_CG, CE2, dHD_CD_CE, 3);
1552         intxyz(HE1, CE1, dHE_CE, CD1, dHE_CE_CD, CZ, dHE_CE_CZ, 3);
1553         intxyz(HE2, CE2, dHE_CE, CD2, dHE_CE_CD, CZ, dHE_CE_CZ, 3);
1554         intxyz(OH, CZ, dOH_CZ, CE1, dOH_CZ_CE, CE2, dOH_CZ_CE, 3);
1555       }
1556       case TRP -> {
1557         Atom CA = (Atom) residue.getAtomNode("CA");
1558         Atom CB = (Atom) residue.getAtomNode("CB");
1559         Atom N = (Atom) residue.getAtomNode("N");
1560         Atom CG = (Atom) residue.getAtomNode("CG");
1561         Atom CD1 = (Atom) residue.getAtomNode("CD1");
1562         Atom CD2 = (Atom) residue.getAtomNode("CD2");
1563         Atom NE1 = (Atom) residue.getAtomNode("NE1");
1564         Atom CE2 = (Atom) residue.getAtomNode("CE2");
1565         Atom CE3 = (Atom) residue.getAtomNode("CE3");
1566         Atom CZ2 = (Atom) residue.getAtomNode("CZ2");
1567         Atom CZ3 = (Atom) residue.getAtomNode("CZ3");
1568         Atom CH2 = (Atom) residue.getAtomNode("CH2");
1569         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1570         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1571         Atom HD1 = (Atom) residue.getAtomNode("HD1");
1572         Atom HE1 = (Atom) residue.getAtomNode("HE1");
1573         Atom HE3 = (Atom) residue.getAtomNode("HE3");
1574         Atom HZ2 = (Atom) residue.getAtomNode("HZ2");
1575         Atom HZ3 = (Atom) residue.getAtomNode("HZ3");
1576         Atom HH2 = (Atom) residue.getAtomNode("HH2");
1577         Bond CG_CB = CG.getBond(CB);
1578         Bond CD1_CG = CD1.getBond(CG);
1579         Bond CD2_CG = CD2.getBond(CG);
1580         Bond NE1_CD1 = NE1.getBond(CD1);
1581         Bond CE2_NE1 = CE2.getBond(NE1);
1582         Bond CE3_CD2 = CE3.getBond(CD2);
1583         Bond CZ2_CE2 = CZ2.getBond(CE2);
1584         Bond CZ3_CE3 = CZ3.getBond(CE3);
1585         Bond CH2_CZ2 = CH2.getBond(CZ2);
1586         Bond HB_CB = HB2.getBond(CB);
1587         Bond HD1_CD1 = HD1.getBond(CD1);
1588         Bond HE1_NE1 = HE1.getBond(NE1);
1589         Bond HE3_CE3 = HE3.getBond(CE3);
1590         Bond HZ2_CZ2 = HZ2.getBond(CZ2);
1591         Bond HZ3_CZ3 = HZ3.getBond(CZ3);
1592         Bond HH2_CH2 = HH2.getBond(CH2);
1593         double dCG_CB = CG_CB.bondType.distance;
1594         double dCD1_CG = CD1_CG.bondType.distance;
1595         double dCD2_CG = CD2_CG.bondType.distance;
1596         double dNE1_CD1 = NE1_CD1.bondType.distance;
1597         double dCE2_NE1 = CE2_NE1.bondType.distance;
1598         double dCE3_CD2 = CE3_CD2.bondType.distance;
1599         double dCZ2_CE2 = CZ2_CE2.bondType.distance;
1600         double dCZ3_CE3 = CZ3_CE3.bondType.distance;
1601         double dCH2_CZ2 = CH2_CZ2.bondType.distance;
1602         double dHB_CB = HB_CB.bondType.distance;
1603         double dHD1_CD1 = HD1_CD1.bondType.distance;
1604         double dHE1_NE1 = HE1_NE1.bondType.distance;
1605         double dHE3_CE3 = HE3_CE3.bondType.distance;
1606         double dHZ2_CZ2 = HZ2_CZ2.bondType.distance;
1607         double dHZ3_CZ3 = HZ3_CZ3.bondType.distance;
1608         double dHH2_CH2 = HH2_CH2.bondType.distance;
1609 
1610         double dCG_CB_CA = getAngle(name, CG, CB, CA);
1611         double dCD1_CG_CB = getAngle(name, CD1, CG, CB);
1612         double dCD2_CG_CB = getAngle(name, CD2, CG, CB);
1613         double dNE1_CD1_CG = getAngle(name, NE1, CD1, CG);
1614         double dCE2_NE1_CD1 = getAngle(name, CE2, NE1, CD1);
1615         double dCE3_CD2_CE2 = getAngle(name, CE3, CD2, CE2);
1616         double dCZ2_CE2_CD2 = getAngle(name, CZ2, CE2, CD2);
1617         double dCZ3_CE3_CD2 = getAngle(name, CZ3, CE3, CD2);
1618         double dCH2_CZ2_CE2 = getAngle(name, CH2, CZ2, CE2);
1619         double dHB_CB_CA = getAngle(name, HB2, CB, CA);
1620         double dHD1_CD1_CG = getAngle(name, HD1, CD1, CG);
1621         double dHD1_CD1_NE1 = getAngle(name, HD1, CD1, NE1);
1622         double dHE1_NE1_CD1 = getAngle(name, HE1, NE1, CD1);
1623         double dHE1_NE1_CE2 = getAngle(name, HE1, NE1, CE2);
1624         double dHE3_CE3_CD2 = getAngle(name, HE3, CE3, CD2);
1625         double dHE3_CE3_CZ3 = getAngle(name, HE3, CE3, CZ3);
1626         double dHZ2_CZ2_CE2 = getAngle(name, HZ2, CZ2, CE2);
1627         double dHZ2_CZ2_CH2 = getAngle(name, HZ2, CZ2, CH2);
1628         double dHZ3_CZ3_CE3 = getAngle(name, HZ3, CZ3, CE3);
1629         double dHZ3_CZ3_CH2 = getAngle(name, HZ3, CZ3, CH2);
1630         double dHH2_CH2_CZ2 = getAngle(name, HH2, CH2, CZ2);
1631         double dHH2_CH2_CZ3 = getAngle(name, HH2, CH2, CZ3);
1632 
1633         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1634         intxyz(CD1, CG, dCD1_CG, CB, dCD1_CG_CB, CA, rotamer.chi2, 0);
1635         intxyz(CD2, CG, dCD2_CG, CB, dCD2_CG_CB, CA, rotamer.chi2 + 180, 0);
1636         intxyz(NE1, CD1, dNE1_CD1, CG, dNE1_CD1_CG, CD2, 0.0, 0);
1637         intxyz(CE2, NE1, dCE2_NE1, CD1, dCE2_NE1_CD1, CG, 0.0, 0);
1638         intxyz(CE3, CD2, dCE3_CD2, CE2, dCE3_CD2_CE2, NE1, 180.0, 0);
1639         intxyz(CZ2, CE2, dCZ2_CE2, CD2, dCZ2_CE2_CD2, CE3, 0.0, 0);
1640         intxyz(CZ3, CE3, dCZ3_CE3, CD2, dCZ3_CE3_CD2, CE2, 0.0, 0);
1641         intxyz(CH2, CZ2, dCH2_CZ2, CE2, dCH2_CZ2_CE2, CD2, 0.0, 0);
1642         // Continue using 109.4 degrees for tetrahedral hydrogen.
1643         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
1644         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
1645         intxyz(HD1, CD1, dHD1_CD1, CG, dHD1_CD1_CG, NE1, dHD1_CD1_NE1, 3);
1646         intxyz(HE1, NE1, dHE1_NE1, CD1, dHE1_NE1_CD1, CE2, dHE1_NE1_CE2, 3);
1647         intxyz(HE3, CE3, dHE3_CE3, CD2, dHE3_CE3_CD2, CZ3, dHE3_CE3_CZ3, 3);
1648         intxyz(HZ2, CZ2, dHZ2_CZ2, CE2, dHZ2_CZ2_CE2, CH2, dHZ2_CZ2_CH2, 3);
1649         intxyz(HZ3, CZ3, dHZ3_CZ3, CE3, dHZ3_CZ3_CE3, CH2, dHZ3_CZ3_CH2, 3);
1650         intxyz(HH2, CH2, dHH2_CH2, CZ2, dHH2_CH2_CZ2, CZ3, dHH2_CH2_CZ3, 3);
1651       }
1652       case HIS -> {
1653         Atom CA = (Atom) residue.getAtomNode("CA");
1654         Atom CB = (Atom) residue.getAtomNode("CB");
1655         Atom N = (Atom) residue.getAtomNode("N");
1656         Atom CG = (Atom) residue.getAtomNode("CG");
1657         Atom ND1 = (Atom) residue.getAtomNode("ND1");
1658         Atom CD2 = (Atom) residue.getAtomNode("CD2");
1659         Atom CE1 = (Atom) residue.getAtomNode("CE1");
1660         Atom NE2 = (Atom) residue.getAtomNode("NE2");
1661         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1662         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1663         Atom HD1 = (Atom) residue.getAtomNode("HD1");
1664         Atom HD2 = (Atom) residue.getAtomNode("HD2");
1665         Atom HE1 = (Atom) residue.getAtomNode("HE1");
1666         Atom HE2 = (Atom) residue.getAtomNode("HE2");
1667         Bond CG_CB = CG.getBond(CB);
1668         Bond ND1_CG = ND1.getBond(CG);
1669         Bond CD2_CG = CD2.getBond(CG);
1670         Bond CE1_ND1 = CE1.getBond(ND1);
1671         Bond NE2_CD2 = NE2.getBond(CD2);
1672         Bond HB_CB = HB2.getBond(CB);
1673         Bond HD1_ND1 = HD1.getBond(ND1);
1674         Bond HD2_CD2 = HD2.getBond(CD2);
1675         Bond HE1_CE1 = HE1.getBond(CE1);
1676         Bond HE2_NE2 = HE2.getBond(NE2);
1677         double dCG_CB = CG_CB.bondType.distance;
1678         double dND1_CG = ND1_CG.bondType.distance;
1679         double dCD2_CG = CD2_CG.bondType.distance;
1680         double dCE1_ND1 = CE1_ND1.bondType.distance;
1681         double dNE2_CD2 = NE2_CD2.bondType.distance;
1682         double dHB_CB = HB_CB.bondType.distance;
1683         double dHD1_ND1 = HD1_ND1.bondType.distance;
1684         double dHD2_CD2 = HD2_CD2.bondType.distance;
1685         double dHE1_CE1 = HE1_CE1.bondType.distance;
1686         double dHE2_NE2 = HE2_NE2.bondType.distance;
1687 
1688         double dCG_CB_CA = getAngle(name, CG, CB, CA);
1689         double dND1_CG_CB = getAngle(name, ND1, CG, CB);
1690         double dCD2_CG_CB = getAngle(name, CD2, CG, CB);
1691         double dCE1_ND1_CG = getAngle(name, CE1, ND1, CG);
1692         double dNE2_CD2_CG = getAngle(name, NE2, CD2, CG);
1693         double dHB_CB_CA = getAngle(name, HB2, CB, CA);
1694         double dHD1_ND1_CG = getAngle(name, HD1, ND1, CG);
1695         double dHD1_ND1_CE1 = getAngle(name, HD1, ND1, CE1);
1696         double dHD2_CD2_CG = getAngle(name, HD2, CD2, CG);
1697         double dHD2_CD2_NE2 = getAngle(name, HD2, CD2, NE2);
1698         double dHE1_CE1_ND1 = getAngle(name, HE1, CE1, ND1);
1699         double dHE1_CE1_NE2 = getAngle(name, HE1, CE1, NE2);
1700         double dHE2_NE2_CD2 = getAngle(name, HE2, NE2, CD2);
1701         double dHE2_NE2_CE1 = getAngle(name, HE2, NE2, CE1);
1702 
1703         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1704         intxyz(ND1, CG, dND1_CG, CB, dND1_CG_CB, CA, rotamer.chi2, 0);
1705         intxyz(CD2, CG, dCD2_CG, CB, dCD2_CG_CB, CA, rotamer.chi2 + 180, 0);
1706         intxyz(CE1, ND1, dCE1_ND1, CG, dCE1_ND1_CG, CD2, 0.0, 0);
1707         intxyz(NE2, CD2, dNE2_CD2, CG, dNE2_CD2_CG, ND1, 0.0, 0);
1708         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
1709         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
1710         intxyz(HD1, ND1, dHD1_ND1, CG, dHD1_ND1_CG, CE1, dHD1_ND1_CE1, 3);
1711         intxyz(HD2, CD2, dHD2_CD2, CG, dHD2_CD2_CG, NE2, dHD2_CD2_NE2, 3);
1712         intxyz(HE1, CE1, dHE1_CE1, ND1, dHE1_CE1_ND1, NE2, dHE1_CE1_NE2, 3);
1713         intxyz(HE2, NE2, dHE2_NE2, CD2, dHE2_NE2_CD2, CE1, dHE2_NE2_CE1, 3);
1714       }
1715       case HID -> {
1716         Atom CA = (Atom) residue.getAtomNode("CA");
1717         Atom CB = (Atom) residue.getAtomNode("CB");
1718         Atom N = (Atom) residue.getAtomNode("N");
1719         Atom CG = (Atom) residue.getAtomNode("CG");
1720         Atom ND1 = (Atom) residue.getAtomNode("ND1");
1721         Atom CD2 = (Atom) residue.getAtomNode("CD2");
1722         Atom CE1 = (Atom) residue.getAtomNode("CE1");
1723         Atom NE2 = (Atom) residue.getAtomNode("NE2");
1724         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1725         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1726         Atom HD1 = (Atom) residue.getAtomNode("HD1");
1727         Atom HD2 = (Atom) residue.getAtomNode("HD2");
1728         Atom HE1 = (Atom) residue.getAtomNode("HE1");
1729         Bond CG_CB = CG.getBond(CB);
1730         Bond ND1_CG = ND1.getBond(CG);
1731         Bond CD2_CG = CD2.getBond(CG);
1732         Bond CE1_ND1 = CE1.getBond(ND1);
1733         Bond NE2_CD2 = NE2.getBond(CD2);
1734         Bond HB_CB = HB2.getBond(CB);
1735         Bond HD1_ND1 = HD1.getBond(ND1);
1736         Bond HD2_CD2 = HD2.getBond(CD2);
1737         Bond HE1_CE1 = HE1.getBond(CE1);
1738         double dCG_CB = CG_CB.bondType.distance;
1739         double dND1_CG = ND1_CG.bondType.distance;
1740         double dCD2_CG = CD2_CG.bondType.distance;
1741         double dCE1_ND1 = CE1_ND1.bondType.distance;
1742         double dNE2_CD2 = NE2_CD2.bondType.distance;
1743         double dHB_CB = HB_CB.bondType.distance;
1744         double dHD1_ND1 = HD1_ND1.bondType.distance;
1745         double dHD2_CD2 = HD2_CD2.bondType.distance;
1746         double dHE1_CE1 = HE1_CE1.bondType.distance;
1747 
1748         double dCG_CB_CA = getAngle(name, CG, CB, CA);
1749         double dND1_CG_CB = getAngle(name, ND1, CG, CB);
1750         double dCD2_CG_CB = getAngle(name, CD2, CG, CB);
1751         double dCE1_ND1_CG = getAngle(name, CE1, ND1, CG);
1752         double dNE2_CD2_CG = getAngle(name, NE2, CD2, CG);
1753         double dHB_CB_CA = getAngle(name, HB2, CB, CA);
1754         double dHD1_ND1_CG = getAngle(name, HD1, ND1, CG);
1755         double dHD1_ND1_CE1 = getAngle(name, HD1, ND1, CE1);
1756         double dHD2_CD2_CG = getAngle(name, HD2, CD2, CG);
1757         double dHD2_CD2_NE2 = getAngle(name, HD2, CD2, NE2);
1758         double dHE1_CE1_ND1 = getAngle(name, HE1, CE1, ND1);
1759         double dHE1_CE1_NE2 = getAngle(name, HE1, CE1, NE2);
1760 
1761         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1762         intxyz(ND1, CG, dND1_CG, CB, dND1_CG_CB, CA, rotamer.chi2, 0);
1763         intxyz(CD2, CG, dCD2_CG, CB, dCD2_CG_CB, CA, rotamer.chi2 + 180, 0);
1764         intxyz(CE1, ND1, dCE1_ND1, CG, dCE1_ND1_CG, CD2, 0.0, 0);
1765         intxyz(NE2, CD2, dNE2_CD2, CG, dNE2_CD2_CG, ND1, 0.0, 0);
1766         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
1767         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
1768         intxyz(HD1, ND1, dHD1_ND1, CG, dHD1_ND1_CG, CE1, dHD1_ND1_CE1, 3);
1769         intxyz(HD2, CD2, dHD2_CD2, CG, dHD2_CD2_CG, NE2, dHD2_CD2_NE2, 3);
1770         intxyz(HE1, CE1, dHE1_CE1, ND1, dHE1_CE1_ND1, NE2, dHE1_CE1_NE2, 3);
1771         if (rotamer.isTitrating) {
1772           // Place the HE2 atom (whose non-bonded interactions will be turned off).
1773           // This is to ensure the bond and angle energy values are constant.
1774           Atom HE2 = (Atom) residue.getAtomNode("HE2");
1775           Bond HE2_NE2 = HE2.getBond(NE2);
1776           double dHE2_NE2 = HE2_NE2.bondType.distance;
1777           double dHE2_NE2_CD2 = getAngle(name, HE2, NE2, CD2);
1778           double dHE2_NE2_CE1 = getAngle(name, HE2, NE2, CE1);
1779           intxyz(HE2, NE2, dHE2_NE2, CD2, dHE2_NE2_CD2, CE1, dHE2_NE2_CE1, 3);
1780         }
1781       }
1782       case HIE -> {
1783         Atom CA = (Atom) residue.getAtomNode("CA");
1784         Atom CB = (Atom) residue.getAtomNode("CB");
1785         Atom N = (Atom) residue.getAtomNode("N");
1786         Atom CG = (Atom) residue.getAtomNode("CG");
1787         Atom ND1 = (Atom) residue.getAtomNode("ND1");
1788         Atom CD2 = (Atom) residue.getAtomNode("CD2");
1789         Atom CE1 = (Atom) residue.getAtomNode("CE1");
1790         Atom NE2 = (Atom) residue.getAtomNode("NE2");
1791         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1792         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1793         Atom HD2 = (Atom) residue.getAtomNode("HD2");
1794         Atom HE1 = (Atom) residue.getAtomNode("HE1");
1795         Atom HE2 = (Atom) residue.getAtomNode("HE2");
1796         Bond CG_CB = CG.getBond(CB);
1797         Bond ND1_CG = ND1.getBond(CG);
1798         Bond CD2_CG = CD2.getBond(CG);
1799         Bond CE1_ND1 = CE1.getBond(ND1);
1800         Bond NE2_CD2 = NE2.getBond(CD2);
1801         Bond HB_CB = HB2.getBond(CB);
1802         Bond HD2_CD2 = HD2.getBond(CD2);
1803         Bond HE1_CE1 = HE1.getBond(CE1);
1804         Bond HE2_NE2 = HE2.getBond(NE2);
1805         double dCG_CB = CG_CB.bondType.distance;
1806         double dND1_CG = ND1_CG.bondType.distance;
1807         double dCD2_CG = CD2_CG.bondType.distance;
1808         double dCE1_ND1 = CE1_ND1.bondType.distance;
1809         double dNE2_CD2 = NE2_CD2.bondType.distance;
1810         double dHB_CB = HB_CB.bondType.distance;
1811         double dHD2_CD2 = HD2_CD2.bondType.distance;
1812         double dHE1_CE1 = HE1_CE1.bondType.distance;
1813         double dHE2_NE2 = HE2_NE2.bondType.distance;
1814 
1815         double dCG_CB_CA = getAngle(name, CG, CB, CA);
1816         double dND1_CG_CB = getAngle(name, ND1, CG, CB);
1817         double dCD2_CG_CB = getAngle(name, CD2, CG, CB);
1818         double dCE1_ND1_CG = getAngle(name, CE1, ND1, CG);
1819         double dNE2_CD2_CG = getAngle(name, NE2, CD2, CG);
1820         double dHB_CB_CA = getAngle(name, HB2, CB, CA);
1821         double dHD2_CD2_CG = getAngle(name, HD2, CD2, CG);
1822         double dHD2_CD2_NE2 = getAngle(name, HD2, CD2, NE2);
1823         double dHE1_CE1_ND1 = getAngle(name, HE1, CE1, ND1);
1824         double dHE1_CE1_NE2 = getAngle(name, HE1, CE1, NE2);
1825         double dHE2_NE2_CD2 = getAngle(name, HE2, NE2, CD2);
1826         double dHE2_NE2_CE1 = getAngle(name, HE2, NE2, CE1);
1827 
1828         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1829         intxyz(ND1, CG, dND1_CG, CB, dND1_CG_CB, CA, rotamer.chi2, 0);
1830         intxyz(CD2, CG, dCD2_CG, CB, dCD2_CG_CB, CA, rotamer.chi2 + 180, 0);
1831         intxyz(CE1, ND1, dCE1_ND1, CG, dCE1_ND1_CG, CD2, 0.0, 0);
1832         intxyz(NE2, CD2, dNE2_CD2, CG, dNE2_CD2_CG, ND1, 0.0, 0);
1833         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
1834         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
1835         intxyz(HD2, CD2, dHD2_CD2, CG, dHD2_CD2_CG, NE2, dHD2_CD2_NE2, 3);
1836         intxyz(HE1, CE1, dHE1_CE1, ND1, dHE1_CE1_ND1, NE2, dHE1_CE1_NE2, 3);
1837         intxyz(HE2, NE2, dHE2_NE2, CD2, dHE2_NE2_CD2, CE1, dHE2_NE2_CE1, 3);
1838         if (rotamer.isTitrating) {
1839           // Place the HD1 atom (whose non-bonded interactions will be turned off).
1840           // This is to ensure the bond and angle energy values are constant.
1841           Atom HD1 = (Atom) residue.getAtomNode("HD1");
1842           Bond HD1_ND1 = HD1.getBond(ND1);
1843           double dHD1_ND1 = HD1_ND1.bondType.distance;
1844           double dHD1_ND1_CG = getAngle(name, HD1, ND1, CG);
1845           double dHD1_ND1_CE1 = getAngle(name, HD1, ND1, CE1);
1846           intxyz(HD1, ND1, dHD1_ND1, CG, dHD1_ND1_CG, CE1, dHD1_ND1_CE1, 3);
1847         }
1848       }
1849       case ASP -> {
1850         Atom CA = (Atom) residue.getAtomNode("CA");
1851         Atom CB = (Atom) residue.getAtomNode("CB");
1852         Atom N = (Atom) residue.getAtomNode("N");
1853         Atom CG = (Atom) residue.getAtomNode("CG");
1854         Atom OD1 = (Atom) residue.getAtomNode("OD1");
1855         Atom OD2 = (Atom) residue.getAtomNode("OD2");
1856         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1857         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1858         Bond CG_CB = CG.getBond(CB);
1859         Bond OD1_CG = OD1.getBond(CG);
1860         Bond OD2_CG = OD2.getBond(CG);
1861         Bond HB_CB = HB2.getBond(CB);
1862         double dCG_CB = CG_CB.bondType.distance;
1863         double dOD1_CG = OD1_CG.bondType.distance;
1864         double dOD2_CG = OD2_CG.bondType.distance;
1865         double dHB_CB = HB_CB.bondType.distance;
1866 
1867         Angle CG_CB_CA = CG.getAngle(CB, CA);
1868         Angle OD1_CG_CB = OD1.getAngle(CG, CB);
1869         Angle OD2_CG_CB = OD2.getAngle(CG, CB);
1870         Angle HB_CB_CA = HB2.getAngle(CB, CA);
1871         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
1872         double dOD1_CG_CB = OD1_CG_CB.angleType.angle[OD1_CG_CB.nh];
1873         double dOD2_CG_CB = OD2_CG_CB.angleType.angle[OD2_CG_CB.nh];
1874         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
1875 
1876         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1877         intxyz(OD1, CG, dOD1_CG, CB, dOD1_CG_CB, CA, 0.0, 0);
1878         intxyz(OD2, CG, dOD2_CG, CB, dOD2_CG_CB, OD1, 126.0, 1);
1879         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 107.9, 1);
1880         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 107.9, -1);
1881         if (rotamer.isTitrating) {
1882           // Place the HD2 atom (whose non-bonded interactions will be turned off).
1883           // This is to ensure the bond and angle energy values are constant.
1884           Atom HD2 = (Atom) residue.getAtomNode("HD2");
1885           Bond HD2_OD2 = HD2.getBond(OD2);
1886           double dHD2_OD2 = HD2_OD2.bondType.distance;
1887           Angle HD2_OD2_CG = HD2.getAngle(OD2, CG);
1888           double dHD2_OD2_CG = HD2_OD2_CG.angleType.angle[HD2_OD2_CG.nh];
1889           intxyz(HD2, OD2, dHD2_OD2, CG, dHD2_OD2_CG, OD1, 0.0, 0);
1890         }
1891       }
1892       case ASH -> {
1893         Atom CA = (Atom) residue.getAtomNode("CA");
1894         Atom CB = (Atom) residue.getAtomNode("CB");
1895         Atom N = (Atom) residue.getAtomNode("N");
1896         Atom CG = (Atom) residue.getAtomNode("CG");
1897         Atom OD1 = (Atom) residue.getAtomNode("OD1");
1898         Atom OD2 = (Atom) residue.getAtomNode("OD2");
1899         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1900         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1901         Atom HD2 = (Atom) residue.getAtomNode("HD2");
1902         Bond CG_CB = CG.getBond(CB);
1903         Bond OD1_CG = OD1.getBond(CG);
1904         Bond OD2_CG = OD2.getBond(CG);
1905         Bond HB_CB = HB2.getBond(CB);
1906         Bond HD2_OD2 = HD2.getBond(OD2);
1907         double dCG_CB = CG_CB.bondType.distance;
1908         double dOD1_CG = OD1_CG.bondType.distance;
1909         double dOD2_CG = OD2_CG.bondType.distance;
1910         double dHB_CB = HB_CB.bondType.distance;
1911         double dHD2_OD2 = HD2_OD2.bondType.distance;
1912 
1913         Angle CG_CB_CA = CG.getAngle(CB, CA);
1914         Angle OD1_CG_CB = OD1.getAngle(CG, CB);
1915         Angle OD2_CG_CB = OD2.getAngle(CG, CB);
1916         Angle HB_CB_CA = HB2.getAngle(CB, CA);
1917         Angle HD2_OD2_CG = HD2.getAngle(OD2, CG);
1918 
1919         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
1920         double dOD1_CG_CB = OD1_CG_CB.angleType.angle[OD1_CG_CB.nh];
1921         double dOD2_CG_CB = OD2_CG_CB.angleType.angle[OD2_CG_CB.nh];
1922         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
1923         double dHD2_OD2_CG = HD2_OD2_CG.angleType.angle[HD2_OD2_CG.nh];
1924         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1925         intxyz(OD1, CG, dOD1_CG, CB, dOD1_CG_CB, CA, rotamer.chi2, 0);
1926         intxyz(OD2, CG, dOD2_CG, CB, dOD2_CG_CB, OD1, 126.0, 1);
1927         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 107.9, 1);
1928         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 107.9, -1);
1929         intxyz(HD2, OD2, dHD2_OD2, CG, dHD2_OD2_CG, OD1, 0, 0);
1930       }
1931       case ASN -> {
1932         Atom CA = (Atom) residue.getAtomNode("CA");
1933         Atom CB = (Atom) residue.getAtomNode("CB");
1934         Atom N = (Atom) residue.getAtomNode("N");
1935         Atom CG = (Atom) residue.getAtomNode("CG");
1936         Atom OD1 = (Atom) residue.getAtomNode("OD1");
1937         Atom ND2 = (Atom) residue.getAtomNode("ND2");
1938         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1939         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1940         Atom HD21 = (Atom) residue.getAtomNode("HD21");
1941         Atom HD22 = (Atom) residue.getAtomNode("HD22");
1942         Bond CG_CB = CG.getBond(CB);
1943         Bond OD1_CG = OD1.getBond(CG);
1944         Bond ND2_CG = ND2.getBond(CG);
1945         Bond HB_CB = HB2.getBond(CB);
1946         Bond HD2_ND2 = HD21.getBond(ND2);
1947         double dCG_CB = CG_CB.bondType.distance;
1948         double dOD1_CG = OD1_CG.bondType.distance;
1949         double dND2_CG = ND2_CG.bondType.distance;
1950         double dHB_CB = HB_CB.bondType.distance;
1951         double dHD2_ND2 = HD2_ND2.bondType.distance;
1952         Angle CG_CB_CA = CG.getAngle(CB, CA);
1953         Angle OD1_CG_CB = OD1.getAngle(CG, CB);
1954         Angle ND2_CG_CB = ND2.getAngle(CG, CB);
1955         Angle HB_CB_CA = HB2.getAngle(CB, CA);
1956         Angle HD2_ND2_CG = HD21.getAngle(ND2, CG);
1957         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
1958         double dOD1_CG_CB = OD1_CG_CB.angleType.angle[OD1_CG_CB.nh];
1959         double dND2_CG_CB = ND2_CG_CB.angleType.angle[ND2_CG_CB.nh];
1960         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
1961         double dHD2_ND2_CG = HD2_ND2_CG.angleType.angle[HD2_ND2_CG.nh];
1962         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
1963         intxyz(OD1, CG, dOD1_CG, CB, dOD1_CG_CB, CA, rotamer.chi2, 0);
1964         intxyz(ND2, CG, dND2_CG, CB, dND2_CG_CB, OD1, 124.0, 1);
1965         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 107.9, 1);
1966         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 107.9, -1);
1967         intxyz(HD21, ND2, dHD2_ND2, CG, dHD2_ND2_CG, CB, 0.0, 0);
1968         intxyz(HD22, ND2, dHD2_ND2, CG, dHD2_ND2_CG, HD21, 120.0, 1);
1969       }
1970       case GLU -> {
1971         Atom CA = (Atom) residue.getAtomNode("CA");
1972         Atom CB = (Atom) residue.getAtomNode("CB");
1973         Atom N = (Atom) residue.getAtomNode("N");
1974         Atom CG = (Atom) residue.getAtomNode("CG");
1975         Atom CD = (Atom) residue.getAtomNode("CD");
1976         Atom OE1 = (Atom) residue.getAtomNode("OE1");
1977         Atom OE2 = (Atom) residue.getAtomNode("OE2");
1978         Atom HB2 = (Atom) residue.getAtomNode("HB2");
1979         Atom HB3 = (Atom) residue.getAtomNode("HB3");
1980         Atom HG2 = (Atom) residue.getAtomNode("HG2");
1981         Atom HG3 = (Atom) residue.getAtomNode("HG3");
1982         Bond CG_CB = CG.getBond(CB);
1983         Bond CD_CG = CD.getBond(CG);
1984         Bond OE1_CD = OE1.getBond(CD);
1985         Bond OE2_CD = OE2.getBond(CD);
1986         Bond HB_CB = HB2.getBond(CB);
1987         Bond HG_CG = HG2.getBond(CG);
1988         double dCG_CB = CG_CB.bondType.distance;
1989         double dCD_CG = CD_CG.bondType.distance;
1990         double dOE1_CD = OE1_CD.bondType.distance;
1991         double dOE2_CD = OE2_CD.bondType.distance;
1992         double dHB_CB = HB_CB.bondType.distance;
1993         double dHG_CG = HG_CG.bondType.distance;
1994         Angle CG_CB_CA = CG.getAngle(CB, CA);
1995         Angle CD_CG_CB = CD.getAngle(CG, CB);
1996         Angle OE1_CD_CG = OE1.getAngle(CD, CG);
1997         Angle OE2_CD_CG = OE2.getAngle(CD, CG);
1998         Angle HB_CB_CA = HB2.getAngle(CB, CA);
1999         Angle HG_CG_CB = HG2.getAngle(CG, CB);
2000         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
2001         double dCD_CG_CB = CD_CG_CB.angleType.angle[CD_CG_CB.nh];
2002         double dOE1_CD_CG = OE1_CD_CG.angleType.angle[OE1_CD_CG.nh];
2003         double dOE2_CD_CG = OE2_CD_CG.angleType.angle[OE2_CD_CG.nh];
2004         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
2005         double dHG_CG_CB = HG_CG_CB.angleType.angle[HG_CG_CB.nh];
2006         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
2007         intxyz(CD, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2, 0);
2008         intxyz(OE1, CD, dOE1_CD, CG, dOE1_CD_CG, CB, rotamer.chi3, 0);
2009         intxyz(OE2, CD, dOE2_CD, CG, dOE2_CD_CG, OE1, 126.0, 1);
2010         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
2011         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
2012         intxyz(HG2, CG, dHG_CG, CB, dHG_CG_CB, CD, 107.9, 1);
2013         intxyz(HG3, CG, dHG_CG, CB, dHG_CG_CB, CD, 107.9, -1);
2014         if (rotamer.isTitrating) {
2015           // Place the HE2 atom (whose non-bonded interactions will be turned off).
2016           // This is to ensure the bond and angle energy values are constant.
2017           Atom HE2 = (Atom) residue.getAtomNode("HE2");
2018           Bond HE2_OE2 = HE2.getBond(OE2);
2019           double dHE2_OE2 = HE2_OE2.bondType.distance;
2020           Angle HE2_OE2_CD = HE2.getAngle(OE2, CD);
2021           double dHE2_OE2_CD = HE2_OE2_CD.angleType.angle[HE2_OE2_CD.nh];
2022           intxyz(HE2, OE2, dHE2_OE2, CD, dHE2_OE2_CD, OE1, 0.0, 0);
2023         }
2024       }
2025       case GLH -> {
2026         Atom CA = (Atom) residue.getAtomNode("CA");
2027         Atom CB = (Atom) residue.getAtomNode("CB");
2028         Atom N = (Atom) residue.getAtomNode("N");
2029         Atom CG = (Atom) residue.getAtomNode("CG");
2030         Atom CD = (Atom) residue.getAtomNode("CD");
2031         Atom OE1 = (Atom) residue.getAtomNode("OE1");
2032         Atom OE2 = (Atom) residue.getAtomNode("OE2");
2033         Atom HB2 = (Atom) residue.getAtomNode("HB2");
2034         Atom HB3 = (Atom) residue.getAtomNode("HB3");
2035         Atom HG2 = (Atom) residue.getAtomNode("HG2");
2036         Atom HG3 = (Atom) residue.getAtomNode("HG3");
2037         Atom HE2 = (Atom) residue.getAtomNode("HE2");
2038         Bond CG_CB = CG.getBond(CB);
2039         Bond CD_CG = CD.getBond(CG);
2040         Bond OE1_CD = OE1.getBond(CD);
2041         Bond OE2_CD = OE2.getBond(CD);
2042         Bond HB_CB = HB2.getBond(CB);
2043         Bond HG_CG = HG2.getBond(CG);
2044         Bond HE2_OE2 = HE2.getBond(OE2);
2045         double dCG_CB = CG_CB.bondType.distance;
2046         double dCD_CG = CD_CG.bondType.distance;
2047         double dOE1_CD = OE1_CD.bondType.distance;
2048         double dOE2_CD = OE2_CD.bondType.distance;
2049         double dHB_CB = HB_CB.bondType.distance;
2050         double dHG_CG = HG_CG.bondType.distance;
2051         double dHE2_OE2 = HE2_OE2.bondType.distance;
2052         Angle CG_CB_CA = CG.getAngle(CB, CA);
2053         Angle CD_CG_CB = CD.getAngle(CG, CB);
2054         Angle OE1_CD_CG = OE1.getAngle(CD, CG);
2055         Angle OE2_CD_CG = OE2.getAngle(CD, CG);
2056         Angle HB_CB_CA = HB2.getAngle(CB, CA);
2057         Angle HG_CG_CB = HG2.getAngle(CG, CB);
2058         Angle HE2_OE2_CD = HE2.getAngle(OE2, CD);
2059         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
2060         double dCD_CG_CB = CD_CG_CB.angleType.angle[CD_CG_CB.nh];
2061         double dOE1_CD_CG = OE1_CD_CG.angleType.angle[OE1_CD_CG.nh];
2062         double dOE2_CD_CG = OE2_CD_CG.angleType.angle[OE2_CD_CG.nh];
2063         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
2064         double dHG_CG_CB = HG_CG_CB.angleType.angle[HG_CG_CB.nh];
2065         double dHE2_OE2_CD = HE2_OE2_CD.angleType.angle[HE2_OE2_CD.nh];
2066         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
2067         intxyz(CD, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2, 0);
2068         intxyz(OE1, CD, dOE1_CD, CG, dOE1_CD_CG, CB, rotamer.chi3, 0);
2069         intxyz(OE2, CD, dOE2_CD, CG, dOE2_CD_CG, OE1, 126.0, 1);
2070         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
2071         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
2072         intxyz(HG2, CG, dHG_CG, CB, dHG_CG_CB, CD, 107.9, 1);
2073         intxyz(HG3, CG, dHG_CG, CB, dHG_CG_CB, CD, 107.9, -1);
2074         intxyz(HE2, OE2, dHE2_OE2, CD, dHE2_OE2_CD, OE1, 0.0, 0);
2075       }
2076       case GLN -> {
2077         Atom CA = (Atom) residue.getAtomNode("CA");
2078         Atom CB = (Atom) residue.getAtomNode("CB");
2079         Atom N = (Atom) residue.getAtomNode("N");
2080         Atom CG = (Atom) residue.getAtomNode("CG");
2081         Atom CD = (Atom) residue.getAtomNode("CD");
2082         Atom OE1 = (Atom) residue.getAtomNode("OE1");
2083         Atom NE2 = (Atom) residue.getAtomNode("NE2");
2084         Atom HB2 = (Atom) residue.getAtomNode("HB2");
2085         Atom HB3 = (Atom) residue.getAtomNode("HB3");
2086         Atom HG2 = (Atom) residue.getAtomNode("HG2");
2087         Atom HG3 = (Atom) residue.getAtomNode("HG3");
2088         Atom HE21 = (Atom) residue.getAtomNode("HE21");
2089         Atom HE22 = (Atom) residue.getAtomNode("HE22");
2090         Bond CG_CB = CG.getBond(CB);
2091         Bond CD_CG = CD.getBond(CG);
2092         Bond OE1_CD = OE1.getBond(CD);
2093         Bond NE2_CD = NE2.getBond(CD);
2094         Bond HB_CB = HB2.getBond(CB);
2095         Bond HG_CG = HG2.getBond(CG);
2096         Bond HE2_NE2 = HE21.getBond(NE2);
2097         double dCG_CB = CG_CB.bondType.distance;
2098         double dCD_CG = CD_CG.bondType.distance;
2099         double dOE1_CD = OE1_CD.bondType.distance;
2100         double dNE2_CD = NE2_CD.bondType.distance;
2101         double dHB_CB = HB_CB.bondType.distance;
2102         double dHG_CG = HG_CG.bondType.distance;
2103         double dHE2_NE2 = HE2_NE2.bondType.distance;
2104         Angle CG_CB_CA = CG.getAngle(CB, CA);
2105         Angle CD_CG_CB = CD.getAngle(CG, CB);
2106         Angle OE1_CD_CG = OE1.getAngle(CD, CG);
2107         Angle NE2_CD_CG = NE2.getAngle(CD, CG);
2108         Angle HB_CB_CA = HB2.getAngle(CB, CA);
2109         Angle HG_CG_CB = HG2.getAngle(CG, CB);
2110         Angle HE2_NE2_CD = HE21.getAngle(NE2, CD);
2111         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
2112         double dCD_CG_CB = CD_CG_CB.angleType.angle[CD_CG_CB.nh];
2113         double dOE1_CD_CG = OE1_CD_CG.angleType.angle[OE1_CD_CG.nh];
2114         double dNE2_CD_CG = NE2_CD_CG.angleType.angle[NE2_CD_CG.nh];
2115         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
2116         double dHG_CG_CB = HG_CG_CB.angleType.angle[HG_CG_CB.nh];
2117         double dHE2_NE2_CD = HE2_NE2_CD.angleType.angle[HE2_NE2_CD.nh];
2118         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
2119         intxyz(CD, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2, 0);
2120         intxyz(OE1, CD, dOE1_CD, CG, dOE1_CD_CG, CB, rotamer.chi3, 0);
2121         intxyz(NE2, CD, dNE2_CD, CG, dNE2_CD_CG, OE1, 124.0, 1);
2122         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
2123         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
2124         intxyz(HG2, CG, dHG_CG, CB, dHG_CG_CB, CD, 107.9, 1);
2125         intxyz(HG3, CG, dHG_CG, CB, dHG_CG_CB, CD, 107.9, -1);
2126         intxyz(HE21, NE2, dHE2_NE2, CD, dHE2_NE2_CD, CG, 0.0, 0);
2127         intxyz(HE22, NE2, dHE2_NE2, CD, dHE2_NE2_CD, HE21, 120.0, 1);
2128       }
2129       case MET -> {
2130         Atom CA = (Atom) residue.getAtomNode("CA");
2131         Atom CB = (Atom) residue.getAtomNode("CB");
2132         Atom N = (Atom) residue.getAtomNode("N");
2133         Atom CG = (Atom) residue.getAtomNode("CG");
2134         Atom SD = (Atom) residue.getAtomNode("SD");
2135         Atom CE = (Atom) residue.getAtomNode("CE");
2136         Atom HB2 = (Atom) residue.getAtomNode("HB2");
2137         Atom HB3 = (Atom) residue.getAtomNode("HB3");
2138         Atom HG2 = (Atom) residue.getAtomNode("HG2");
2139         Atom HG3 = (Atom) residue.getAtomNode("HG3");
2140         Atom HE1 = (Atom) residue.getAtomNode("HE1");
2141         Atom HE2 = (Atom) residue.getAtomNode("HE2");
2142         Atom HE3 = (Atom) residue.getAtomNode("HE3");
2143         Bond CG_CB = CG.getBond(CB);
2144         Bond SD_CG = SD.getBond(CG);
2145         Bond CE_SD = CE.getBond(SD);
2146         Bond HB_CB = HB2.getBond(CB);
2147         Bond HG_CG = HG2.getBond(CG);
2148         Bond HE_CE = HE1.getBond(CE);
2149         double dCG_CB = CG_CB.bondType.distance;
2150         double dSD_CG = SD_CG.bondType.distance;
2151         double dCE_SD = CE_SD.bondType.distance;
2152         double dHB_CB = HB_CB.bondType.distance;
2153         double dHG_CG = HG_CG.bondType.distance;
2154         double dHE_CE = HE_CE.bondType.distance;
2155         Angle CG_CB_CA = CG.getAngle(CB, CA);
2156         Angle SD_CG_CB = SD.getAngle(CG, CB);
2157         Angle CE_SD_CG = CE.getAngle(SD, CG);
2158         Angle HB_CB_CA = HB2.getAngle(CB, CA);
2159         Angle HG_CG_CB = HG2.getAngle(CG, CB);
2160         Angle HE_CE_SD = HE1.getAngle(CE, SD);
2161         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
2162         double dSD_CG_CB = SD_CG_CB.angleType.angle[SD_CG_CB.nh];
2163         double dCE_SD_CG = CE_SD_CG.angleType.angle[CE_SD_CG.nh];
2164         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
2165         double dHG_CG_CB = HG_CG_CB.angleType.angle[HG_CG_CB.nh];
2166         double dHE_CE_SD = HE_CE_SD.angleType.angle[HE_CE_SD.nh];
2167         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
2168         intxyz(SD, CG, dSD_CG, CB, dSD_CG_CB, CA, rotamer.chi2, 0);
2169         intxyz(CE, SD, dCE_SD, CG, dCE_SD_CG, CB, rotamer.chi3, 0);
2170         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
2171         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
2172         intxyz(HG2, CG, dHG_CG, CB, dHG_CG_CB, SD, 112.0, 1);
2173         intxyz(HG3, CG, dHG_CG, CB, dHG_CG_CB, SD, 112.0, -1);
2174         intxyz(HE1, CE, dHE_CE, SD, dHE_CE_SD, CG, 180.0, 0);
2175         intxyz(HE2, CE, dHE_CE, SD, dHE_CE_SD, HE1, 109.4, 1);
2176         intxyz(HE3, CE, dHE_CE, SD, dHE_CE_SD, HE1, 109.4, -1);
2177       }
2178       case LYS -> {
2179         Atom CA = (Atom) residue.getAtomNode("CA");
2180         Atom CB = (Atom) residue.getAtomNode("CB");
2181         Atom N = (Atom) residue.getAtomNode("N");
2182         Atom CG = (Atom) residue.getAtomNode("CG");
2183         Atom CD = (Atom) residue.getAtomNode("CD");
2184         Atom CE = (Atom) residue.getAtomNode("CE");
2185         Atom NZ = (Atom) residue.getAtomNode("NZ");
2186         Atom HB2 = (Atom) residue.getAtomNode("HB2");
2187         Atom HB3 = (Atom) residue.getAtomNode("HB3");
2188         Atom HG2 = (Atom) residue.getAtomNode("HG2");
2189         Atom HG3 = (Atom) residue.getAtomNode("HG3");
2190         Atom HD2 = (Atom) residue.getAtomNode("HD2");
2191         Atom HD3 = (Atom) residue.getAtomNode("HD3");
2192         Atom HE2 = (Atom) residue.getAtomNode("HE2");
2193         Atom HE3 = (Atom) residue.getAtomNode("HE3");
2194         Atom HZ1 = (Atom) residue.getAtomNode("HZ1");
2195         Atom HZ2 = (Atom) residue.getAtomNode("HZ2");
2196         Atom HZ3 = (Atom) residue.getAtomNode("HZ3");
2197         Bond CG_CB = CG.getBond(CB);
2198         Bond CD_CG = CD.getBond(CG);
2199         Bond CE_CD = CE.getBond(CD);
2200         Bond NZ_CE = NZ.getBond(CE);
2201         Bond HB_CB = HB2.getBond(CB);
2202         Bond HG_CG = HG2.getBond(CG);
2203         Bond HD_CD = HD2.getBond(CD);
2204         Bond HE_CE = HE2.getBond(CE);
2205         Bond HZ_NZ = HZ1.getBond(NZ);
2206         double dCG_CB = CG_CB.bondType.distance;
2207         double dCD_CG = CD_CG.bondType.distance;
2208         double dCE_CD = CE_CD.bondType.distance;
2209         double dNZ_CE = NZ_CE.bondType.distance;
2210         double dHB_CB = HB_CB.bondType.distance;
2211         double dHG_CG = HG_CG.bondType.distance;
2212         double dHD_CD = HD_CD.bondType.distance;
2213         double dHE_CE = HE_CE.bondType.distance;
2214         double dHZ_NZ = HZ_NZ.bondType.distance;
2215         Angle CG_CB_CA = CG.getAngle(CB, CA);
2216         Angle CD_CG_CB = CD.getAngle(CG, CB);
2217         Angle CE_CD_CG = CE.getAngle(CD, CG);
2218         Angle NZ_CE_CD = NZ.getAngle(CE, CD);
2219         Angle HB_CB_CA = HB2.getAngle(CB, CA);
2220         Angle HG_CG_CB = HG2.getAngle(CG, CB);
2221         Angle HD_CD_CG = HD2.getAngle(CD, CG);
2222         Angle HE_CE_CD = HE2.getAngle(CE, CD);
2223         Angle HZ_NZ_CE = HZ1.getAngle(NZ, CE);
2224         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
2225         double dCD_CG_CB = CD_CG_CB.angleType.angle[CD_CG_CB.nh];
2226         double dCE_CD_CG = CE_CD_CG.angleType.angle[CE_CD_CG.nh];
2227         double dNZ_CE_CD = NZ_CE_CD.angleType.angle[NZ_CE_CD.nh];
2228         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
2229         double dHG_CG_CB = HG_CG_CB.angleType.angle[HG_CG_CB.nh];
2230         double dHD_CD_CG = HD_CD_CG.angleType.angle[HD_CD_CG.nh];
2231         double dHE_CE_CD = HE_CE_CD.angleType.angle[HE_CE_CD.nh];
2232         double dHZ_NZ_CE = HZ_NZ_CE.angleType.angle[HZ_NZ_CE.nh];
2233         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
2234         intxyz(CD, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2, 0);
2235         intxyz(CE, CD, dCE_CD, CG, dCE_CD_CG, CB, rotamer.chi3, 0);
2236         intxyz(NZ, CE, dNZ_CE, CD, dNZ_CE_CD, CG, rotamer.chi4, 0);
2237         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
2238         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
2239         intxyz(HG2, CG, dHG_CG, CB, dHG_CG_CB, CD, 109.4, 1);
2240         intxyz(HG3, CG, dHG_CG, CB, dHG_CG_CB, CD, 109.4, -1);
2241         intxyz(HD2, CD, dHD_CD, CG, dHD_CD_CG, CE, 109.4, 1);
2242         intxyz(HD3, CD, dHD_CD, CG, dHD_CD_CG, CE, 109.4, -1);
2243         intxyz(HE2, CE, dHE_CE, CD, dHE_CE_CD, NZ, 108.8, 1);
2244         intxyz(HE3, CE, dHE_CE, CD, dHE_CE_CD, NZ, 108.8, -1);
2245         intxyz(HZ1, NZ, dHZ_NZ, CE, dHZ_NZ_CE, CD, 180.0, 0);
2246         intxyz(HZ2, NZ, dHZ_NZ, CE, dHZ_NZ_CE, HZ1, 109.5, 1);
2247         intxyz(HZ3, NZ, dHZ_NZ, CE, dHZ_NZ_CE, HZ1, 109.5, -1);
2248       }
2249       case LYD -> {
2250         Atom CA = (Atom) residue.getAtomNode("CA");
2251         Atom CB = (Atom) residue.getAtomNode("CB");
2252         Atom N = (Atom) residue.getAtomNode("N");
2253         Atom CG = (Atom) residue.getAtomNode("CG");
2254         Atom CD = (Atom) residue.getAtomNode("CD");
2255         Atom CE = (Atom) residue.getAtomNode("CE");
2256         Atom NZ = (Atom) residue.getAtomNode("NZ");
2257         Atom HB2 = (Atom) residue.getAtomNode("HB2");
2258         Atom HB3 = (Atom) residue.getAtomNode("HB3");
2259         Atom HG2 = (Atom) residue.getAtomNode("HG2");
2260         Atom HG3 = (Atom) residue.getAtomNode("HG3");
2261         Atom HD2 = (Atom) residue.getAtomNode("HD2");
2262         Atom HD3 = (Atom) residue.getAtomNode("HD3");
2263         Atom HE2 = (Atom) residue.getAtomNode("HE2");
2264         Atom HE3 = (Atom) residue.getAtomNode("HE3");
2265         Atom HZ1 = (Atom) residue.getAtomNode("HZ1");
2266         Atom HZ2 = (Atom) residue.getAtomNode("HZ2");
2267         Bond CG_CB = CG.getBond(CB);
2268         Bond CD_CG = CD.getBond(CG);
2269         Bond CE_CD = CE.getBond(CD);
2270         Bond NZ_CE = NZ.getBond(CE);
2271         Bond HB_CB = HB2.getBond(CB);
2272         Bond HG_CG = HG2.getBond(CG);
2273         Bond HD_CD = HD2.getBond(CD);
2274         Bond HE_CE = HE2.getBond(CE);
2275         Bond HZ_NZ = HZ1.getBond(NZ);
2276         double dCG_CB = CG_CB.bondType.distance;
2277         double dCD_CG = CD_CG.bondType.distance;
2278         double dCE_CD = CE_CD.bondType.distance;
2279         double dNZ_CE = NZ_CE.bondType.distance;
2280         double dHB_CB = HB_CB.bondType.distance;
2281         double dHG_CG = HG_CG.bondType.distance;
2282         double dHD_CD = HD_CD.bondType.distance;
2283         double dHE_CE = HE_CE.bondType.distance;
2284         double dHZ_NZ = HZ_NZ.bondType.distance;
2285         Angle CG_CB_CA = CG.getAngle(CB, CA);
2286         Angle CD_CG_CB = CD.getAngle(CG, CB);
2287         Angle CE_CD_CG = CE.getAngle(CD, CG);
2288         Angle NZ_CE_CD = NZ.getAngle(CE, CD);
2289         Angle HB_CB_CA = HB2.getAngle(CB, CA);
2290         Angle HG_CG_CB = HG2.getAngle(CG, CB);
2291         Angle HD_CD_CG = HD2.getAngle(CD, CG);
2292         Angle HE_CE_CD = HE2.getAngle(CE, CD);
2293         Angle HZ_NZ_CE = HZ1.getAngle(NZ, CE);
2294         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
2295         double dCD_CG_CB = CD_CG_CB.angleType.angle[CD_CG_CB.nh];
2296         double dCE_CD_CG = CE_CD_CG.angleType.angle[CE_CD_CG.nh];
2297         double dNZ_CE_CD = NZ_CE_CD.angleType.angle[NZ_CE_CD.nh];
2298         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
2299         double dHG_CG_CB = HG_CG_CB.angleType.angle[HG_CG_CB.nh];
2300         double dHD_CD_CG = HD_CD_CG.angleType.angle[HD_CD_CG.nh];
2301         double dHE_CE_CD = HE_CE_CD.angleType.angle[HE_CE_CD.nh];
2302         double dHZ_NZ_CE = HZ_NZ_CE.angleType.angle[HZ_NZ_CE.nh];
2303         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
2304         intxyz(CD, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2, 0);
2305         intxyz(CE, CD, dCE_CD, CG, dCE_CD_CG, CB, rotamer.chi3, 0);
2306         intxyz(NZ, CE, dNZ_CE, CD, dNZ_CE_CD, CG, rotamer.chi4, 0);
2307         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
2308         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
2309         intxyz(HG2, CG, dHG_CG, CB, dHG_CG_CB, CD, 109.4, 1);
2310         intxyz(HG3, CG, dHG_CG, CB, dHG_CG_CB, CD, 109.4, -1);
2311         intxyz(HD2, CD, dHD_CD, CG, dHD_CD_CG, CE, 109.4, 1);
2312         intxyz(HD3, CD, dHD_CD, CG, dHD_CD_CG, CE, 109.4, -1);
2313         intxyz(HE2, CE, dHE_CE, CD, dHE_CE_CD, NZ, 108.8, 1);
2314         intxyz(HE3, CE, dHE_CE, CD, dHE_CE_CD, NZ, 108.8, -1);
2315         intxyz(HZ1, NZ, dHZ_NZ, CE, dHZ_NZ_CE, CD, 180.0, 0);
2316         intxyz(HZ2, NZ, dHZ_NZ, CE, dHZ_NZ_CE, HZ1, 109.5, 1);
2317         if (rotamer.isTitrating) {
2318           // Place the HZ3 atom (whose non-bonded interactions will be turned off).
2319           // This is to ensure the bond and angle energy values are constant.
2320           Atom HZ3 = (Atom) residue.getAtomNode("HZ3");
2321           intxyz(HZ3, NZ, dHZ_NZ, CE, dHZ_NZ_CE, HZ1, 109.5, -1);
2322         }
2323       }
2324       case ARG -> {
2325         Atom CA = (Atom) residue.getAtomNode("CA");
2326         Atom CB = (Atom) residue.getAtomNode("CB");
2327         Atom N = (Atom) residue.getAtomNode("N");
2328         Atom CG = (Atom) residue.getAtomNode("CG");
2329         Atom CD = (Atom) residue.getAtomNode("CD");
2330         Atom NE = (Atom) residue.getAtomNode("NE");
2331         Atom CZ = (Atom) residue.getAtomNode("CZ");
2332         Atom NH1 = (Atom) residue.getAtomNode("NH1");
2333         Atom NH2 = (Atom) residue.getAtomNode("NH2");
2334         Atom HB2 = (Atom) residue.getAtomNode("HB2");
2335         Atom HB3 = (Atom) residue.getAtomNode("HB3");
2336         Atom HG2 = (Atom) residue.getAtomNode("HG2");
2337         Atom HG3 = (Atom) residue.getAtomNode("HG3");
2338         Atom HD2 = (Atom) residue.getAtomNode("HD2");
2339         Atom HD3 = (Atom) residue.getAtomNode("HD3");
2340         Atom HE = (Atom) residue.getAtomNode("HE");
2341         Atom HH11 = (Atom) residue.getAtomNode("HH11");
2342         Atom HH12 = (Atom) residue.getAtomNode("HH12");
2343         Atom HH21 = (Atom) residue.getAtomNode("HH21");
2344         Atom HH22 = (Atom) residue.getAtomNode("HH22");
2345         Bond CG_CB = CG.getBond(CB);
2346         Bond CD_CG = CD.getBond(CG);
2347         Bond NE_CD = NE.getBond(CD);
2348         Bond CZ_NE = CZ.getBond(NE);
2349         Bond NH_CZ = NH1.getBond(CZ);
2350         Bond HB_CB = HB2.getBond(CB);
2351         Bond HG_CG = HG2.getBond(CG);
2352         Bond HD_CD = HD2.getBond(CD);
2353         Bond HE_NE = HE.getBond(NE);
2354         Bond HH_NH = HH11.getBond(NH1);
2355         double dCG_CB = CG_CB.bondType.distance;
2356         double dCD_CG = CD_CG.bondType.distance;
2357         double dNE_CD = NE_CD.bondType.distance;
2358         double dCZ_NE = CZ_NE.bondType.distance;
2359         double dNH_CZ = NH_CZ.bondType.distance;
2360         double dHB_CB = HB_CB.bondType.distance;
2361         double dHG_CG = HG_CG.bondType.distance;
2362         double dHD_CD = HD_CD.bondType.distance;
2363         double dHE_NE = HE_NE.bondType.distance;
2364         double dHH_NH = HH_NH.bondType.distance;
2365         Angle CG_CB_CA = CG.getAngle(CB, CA);
2366         Angle CD_CG_CB = CD.getAngle(CG, CB);
2367         Angle NE_CD_CG = NE.getAngle(CD, CG);
2368         Angle CZ_NE_CD = CZ.getAngle(NE, CD);
2369         Angle NH_CZ_NE = NH1.getAngle(CZ, NE);
2370         Angle HB_CB_CA = HB2.getAngle(CB, CA);
2371         Angle HG_CG_CB = HG2.getAngle(CG, CB);
2372         Angle HD_CD_CG = HD2.getAngle(CD, CG);
2373         Angle HE_NE_CD = HE.getAngle(NE, CD);
2374         Angle HH_NH_CZ = HH11.getAngle(NH1, CZ);
2375         double dCG_CB_CA = CG_CB_CA.angleType.angle[CG_CB_CA.nh];
2376         double dCD_CG_CB = CD_CG_CB.angleType.angle[CD_CG_CB.nh];
2377         double dNE_CD_CG = NE_CD_CG.angleType.angle[NE_CD_CG.nh];
2378         double dCZ_NE_CD = CZ_NE_CD.angleType.angle[CZ_NE_CD.nh];
2379         double dNH_CZ_NE = NH_CZ_NE.angleType.angle[NH_CZ_NE.nh];
2380         double dHB_CB_CA = HB_CB_CA.angleType.angle[HB_CB_CA.nh];
2381         double dHG_CG_CB = HG_CG_CB.angleType.angle[HG_CG_CB.nh];
2382         double dHD_CD_CG = HD_CD_CG.angleType.angle[HD_CD_CG.nh];
2383         double dHE_NE_CD = HE_NE_CD.angleType.angle[HE_NE_CD.nh];
2384         double dHH_NH_CZ = HH_NH_CZ.angleType.angle[HH_NH_CZ.nh];
2385         intxyz(CG, CB, dCG_CB, CA, dCG_CB_CA, N, rotamer.chi1, 0);
2386         intxyz(CD, CG, dCD_CG, CB, dCD_CG_CB, CA, rotamer.chi2, 0);
2387         intxyz(NE, CD, dNE_CD, CG, dNE_CD_CG, CB, rotamer.chi3, 0);
2388         intxyz(CZ, NE, dCZ_NE, CD, dCZ_NE_CD, CG, rotamer.chi4, 0);
2389         intxyz(NH1, CZ, dNH_CZ, NE, dNH_CZ_NE, CD, 180, 0);
2390         intxyz(NH2, CZ, dNH_CZ, NE, dNH_CZ_NE, NH1, 120.0, 1);
2391         intxyz(HB2, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, 1);
2392         intxyz(HB3, CB, dHB_CB, CA, dHB_CB_CA, CG, 109.4, -1);
2393         intxyz(HG2, CG, dHG_CG, CB, dHG_CG_CB, CD, 109.4, 1);
2394         intxyz(HG3, CG, dHG_CG, CB, dHG_CG_CB, CD, 109.4, -1);
2395         intxyz(HD2, CD, dHD_CD, CG, dHD_CD_CG, NE, 109.4, 1);
2396         intxyz(HD3, CD, dHD_CD, CG, dHD_CD_CG, NE, 109.4, -1);
2397         intxyz(HE, NE, dHE_NE, CD, dHE_NE_CD, CZ, 120.0, 1);
2398         intxyz(HH11, NH1, dHH_NH, CZ, dHH_NH_CZ, NE, 180.0, 0);
2399         intxyz(HH12, NH1, dHH_NH, CZ, dHH_NH_CZ, HH11, 120.0, 1);
2400         intxyz(HH21, NH2, dHH_NH, CZ, dHH_NH_CZ, NE, 180.0, 0);
2401         intxyz(HH22, NH2, dHH_NH, CZ, dHH_NH_CZ, HH21, 120.0, 1);
2402       }
2403       case UNK -> {
2404         String resName = residue.getName().toUpperCase();
2405         if (nonstdRotCache.containsKey(resName)) {
2406           nonstdRotCache.get(resName).applyNonstdRotamer(residue, rotamer);
2407         }
2408       }
2409       default -> {
2410       }
2411     }
2412   }
2413 
2414   /**
2415    * Moves CZ of Phe/Tyr/Tyd to a mean position determined by both branches of the ring.
2416    *
2417    * @param resName Residue containing CZ.
2418    * @param CZ      CZ to be placed.
2419    * @param CG      CG atom.
2420    * @param CE1     CE1 atom.
2421    * @param CD1     CE2 atom.
2422    * @param CE2     CE2 atom.
2423    * @param CD2     CD2 atom.
2424    */
2425   private static void applyCZ(AminoAcid3 resName, Atom CZ, Atom CG, Atom CE1, Atom CD1, Atom CE2,
2426                               Atom CD2) {
2427     CZ.moveTo(drawCZ(resName, CZ, CG, CE1, CD1, CE2, CD2));
2428   }
2429 
2430   /**
2431    * Draws the backbone of a nucleic acid Residue.
2432    *
2433    * @param residue     Residue.
2434    * @param rotamer     Rotamer being applied to Residue.
2435    * @param prevResidue Residue 5' of residue.
2436    */
2437   private static void applyNABackbone(Residue residue, Rotamer rotamer, Residue prevResidue) {
2438     Atom O3s = (Atom) residue.getAtomNode("O3'");
2439     Atom C3s = (Atom) residue.getAtomNode("C3'");
2440     Atom C4s = (Atom) residue.getAtomNode("C4'");
2441     Atom C5s = (Atom) residue.getAtomNode("C5'");
2442     Atom O5s = (Atom) residue.getAtomNode("O5'");
2443     /*
2444      * Two of the following atoms will be null, depending on whether
2445      * there is a previous residue, whether it is a 5' PO4 cap, or whether
2446      * it is a 5' OH cap.
2447      */
2448     Atom P = (Atom) residue.getAtomNode("P");
2449     Atom OP3 = (Atom) residue.getAtomNode("OP3");
2450     Atom HO5s = (Atom) residue.getAtomNode("HO5'");
2451 
2452     Bond C4s_C5s = C4s.getBond(C5s);
2453     double dC4s_C5s = C4s_C5s.bondType.distance;
2454     Angle C3s_C4s_C5s = C3s.getAngle(C4s, C5s);
2455     double dC3s_C4s_C5s = C3s_C4s_C5s.angleType.angle[C3s_C4s_C5s.nh];
2456     intxyz(C5s, C4s, dC4s_C5s, C3s, dC3s_C4s_C5s, O3s, rotamer.chi7, 0);
2457 
2458     Bond C5s_O5s = C5s.getBond(O5s);
2459     double dC5s_O5s = C5s_O5s.bondType.distance;
2460     Angle C4s_C5s_O5s = C4s.getAngle(C5s, O5s);
2461     double dC4s_C5s_O5s = C4s_C5s_O5s.angleType.angle[C4s_C5s_O5s.nh];
2462     intxyz(O5s, C5s, dC5s_O5s, C4s, dC4s_C5s_O5s, C3s, rotamer.chi6, 0);
2463 
2464     if (prevResidue == null) {
2465       // If capped by HO5s, draw HO5s and return. Else, assume OP3 capped.
2466       if (HO5s != null) {
2467         Bond O5s_HO5s = O5s.getBond(HO5s);
2468         double dO5s_HO5s = O5s_HO5s.bondType.distance;
2469         Angle C5s_O5s_HO5s = C5s.getAngle(O5s, HO5s);
2470         double dC5s_O5s_HO5s = C5s_O5s_HO5s.angleType.angle[C5s_O5s_HO5s.nh];
2471         intxyz(HO5s, O5s, dO5s_HO5s, C5s, dC5s_O5s_HO5s, C4s, rotamer.chi5, 0);
2472       } else {
2473         Bond O5s_P = O5s.getBond(P);
2474         double dO5s_P = O5s_P.bondType.distance;
2475         Angle C5s_O5s_P = C5s.getAngle(O5s, P);
2476         double dC5s_O5s_P = C5s_O5s_P.angleType.angle[C5s_O5s_P.nh];
2477         intxyz(P, O5s, dO5s_P, C5s, dC5s_O5s_P, C4s, rotamer.chi5, 0);
2478 
2479         Bond P_OP3 = P.getBond(OP3);
2480         double dP_OP3 = P_OP3.bondType.distance;
2481         Angle O5s_P_OP3 = C5s.getAngle(O5s, P);
2482         double dO5s_P_OP3 = O5s_P_OP3.angleType.angle[O5s_P_OP3.nh];
2483         intxyz(OP3, P, dP_OP3, O5s, dO5s_P_OP3, C5s, rotamer.chi4, 0);
2484       }
2485     } else {
2486       Bond O5s_P = O5s.getBond(P);
2487       double dO5s_P = O5s_P.bondType.distance;
2488       Angle C5s_O5s_P = C5s.getAngle(O5s, P);
2489       double dC5s_O5s_P = C5s_O5s_P.angleType.angle[C5s_O5s_P.nh];
2490       intxyz(P, O5s, dO5s_P, C5s, dC5s_O5s_P, C4s, rotamer.chi5, 0);
2491     }
2492   }
2493 
2494   /**
2495    * Applies Cartesian translations to nucleic acid backbone atoms to allow P to correctly join up
2496    * with O3' of the prior Residue.
2497    *
2498    * @param residue         Residue.
2499    * @param prevResidue     Residue 5' of residue.
2500    * @param rotamer         Rotamer being applied to residue.
2501    * @param prevSugarPucker Expected sugar pucker of prevResidue.
2502    * @return The magnitude of any applied correction.
2503    */
2504   private static double applyNACorrections(Residue residue, Residue prevResidue, Rotamer rotamer,
2505                                            NucleicSugarPucker prevSugarPucker, boolean isDeoxy, boolean is3sTerminal) {
2506     // Backbone atoms of this residue to be adjusted
2507     Atom C3s = (Atom) residue.getAtomNode("C3'");
2508     Atom O4s = (Atom) residue.getAtomNode("O4'");
2509     Atom C4s = (Atom) residue.getAtomNode("C4'");
2510     Atom C5s = (Atom) residue.getAtomNode("C5'");
2511     Atom O5s = (Atom) residue.getAtomNode("O5'");
2512     Atom P = (Atom) residue.getAtomNode("P");
2513     Atom C1s = (Atom) residue.getAtomNode("C1'");
2514     Atom C2s = (Atom) residue.getAtomNode("C2'");
2515 
2516     // This reference being used solely to get ideal bond lengths & angles.
2517     Atom O3sPrev = (Atom) prevResidue.getAtomNode("O3'");
2518     double[] O3sPriorCoords;
2519 
2520     // Original position of O3' (i-1). Will be used to draw the correction
2521     // vector.
2522     if (prevSugarPucker == NucleicSugarPucker.C3_ENDO) {
2523       O3sPriorCoords = prevResidue.getO3sNorth();
2524     } else {
2525       O3sPriorCoords = prevResidue.getO3sSouth();
2526     } // TODO: Else-if block for the C3'-exo configuration of DNA sugars.
2527 
2528     Bond P_O3sPrev = P.getBond(O3sPrev);
2529     double dP_O3sPrev = P_O3sPrev.bondType.distance;
2530     Angle O5s_P_O3sPrev = O5s.getAngle(P, O3sPrev);
2531     double dO5s_P_O3sPrev = O5s_P_O3sPrev.angleType.angle[O5s_P_O3sPrev.nh];
2532     double[] O3sHypCoords = determineIntxyz(P.getXYZ(null), dP_O3sPrev, O5s.getXYZ(null),
2533         dO5s_P_O3sPrev, C5s.getXYZ(null), rotamer.chi4, 0);
2534 
2535     // Index 5 will be full correction, and indices 0-4 will be 1/6 to 5/6
2536     // of the full correction in increasing order.  Index 6 is a 1/12
2537     // correction applied to other atoms in the sugar.
2538     double[][] corrections = new double[7][3];
2539     for (int i = 0; i < 3; i++) {
2540       corrections[5][i] = O3sPriorCoords[i] - O3sHypCoords[i];
2541       corrections[0][i] = (1.0 / 6.0) * corrections[5][i];
2542       corrections[1][i] = (1.0 / 3.0) * corrections[5][i];
2543       corrections[2][i] = (1.0 / 2.0) * corrections[5][i];
2544       corrections[3][i] = (2.0 / 3.0) * corrections[5][i];
2545       corrections[4][i] = (5.0 / 6.0) * corrections[5][i];
2546       corrections[6][i] = (1.0 / 12.0) * corrections[5][i];
2547     }
2548 
2549     /*
2550      * Move backbone atoms by an appropriate fraction of the correction
2551      * vector. Do this before checking the threshold, so that atoms are moved
2552      * in case that is needed before the exception gets thrown.
2553      */
2554     O4s.move(corrections[0]);
2555     C3s.move(corrections[0]);
2556     C4s.move(corrections[1]);
2557     C5s.move(corrections[2]);
2558     O5s.move(corrections[3]);
2559     P.move(corrections[4]);
2560     C1s.move(corrections[6]);
2561     C2s.move(corrections[6]);
2562 
2563     return ((corrections[5][0] * corrections[5][0]) + (corrections[5][1] * corrections[5][1]) + (
2564         corrections[5][2] * corrections[5][2]));
2565   }
2566 
2567   /**
2568    * Applies a nucleic acid Rotamer, returning the magnitude of the correction applied to make
2569    * residue i join residue i-1.
2570    *
2571    * <p>Note that the independent flag is separate from DEE independence: DEE independence is
2572    * preserved by applying corrections based on a non-variable set of coordinates, and is wholly
2573    * independent of what is happening to residue i-1.
2574    *
2575    * <p>Cannot presently handle 3' phosphate caps: I do not know what they would be labeled as in
2576    * PDB files. A template for how to handle 3' phosphate caps is written but commented out.
2577    *
2578    * @param residue     Residue.
2579    * @param rotamer     Rotamer to be applied to Residue.
2580    * @param independent Whether to draw NA rotamer independent of chain context.
2581    * @return Magnitude of the correction vector.
2582    */
2583   private static double applyNARotamer(Residue residue, Rotamer rotamer, boolean independent) {
2584     if (rotamer.isState) {
2585       applyState(residue, rotamer);
2586       return 0;
2587     }
2588     Residue prevResidue = residue.getPreviousResidue();
2589     boolean is3sTerminal = residue.getNextResidue() == null; // 3' terminal
2590     boolean isDeoxy = residue.getAtomNode("O2'") == null;
2591 
2592     // Note: chi values will generally be applied from chi7 to chi1.
2593     // Will have to add an else-if to handle DNA C3'-exo configurations.
2594     NucleicSugarPucker sugarPucker = NucleicSugarPucker.checkPucker(rotamer.chi7, isDeoxy);
2595     NucleicSugarPucker prevSugarPucker = NucleicSugarPucker.checkPucker(rotamer.chi1, isDeoxy);
2596 
2597     // Revert C1', O4', and C4' coordinates to PDB defaults.
2598     Atom C1s = (Atom) residue.getAtomNode("C1'");
2599     C1s.moveTo(residue.getC1sCoords());
2600     Atom O4s = (Atom) residue.getAtomNode("O4'");
2601     O4s.moveTo(residue.getO4sCoords());
2602     Atom C4s = (Atom) residue.getAtomNode("C4'");
2603     C4s.moveTo(residue.getC4sCoords());
2604 
2605     // Presently, the exterior method loadPriorAtomicCoordinates() directly
2606     // calls applySugarPucker instead of going through applyRotamer().
2607     applySugarPucker(residue, sugarPucker, isDeoxy, true);
2608     applyNABackbone(residue, rotamer, prevResidue);
2609 
2610     double naCorrection = 0;
2611     if (prevResidue != null && !independent) {
2612       naCorrection = applyNACorrections(residue, prevResidue, rotamer, prevSugarPucker, isDeoxy,
2613           is3sTerminal);
2614     } /* else if (!independent) {
2615       startingResidueConsistencyCheck(residue, rotamer, correctionThreshold);
2616       } */
2617 
2618     applyNASideAtoms(residue, rotamer, prevResidue, isDeoxy, is3sTerminal, prevSugarPucker);
2619     return naCorrection;
2620   }
2621 
2622   /**
2623    * Draws nucleic acid Atoms outside the backbone. Called after corrections have been applied, so
2624    * that these Atoms are drawn with ideal bond lengths and angles.
2625    *
2626    * @param residue         Residue.
2627    * @param rotamer         If 5' capped by HO5s, uses chi5 to draw HO5s.
2628    * @param prevResidue     NA residue at the 5' end of residue.
2629    * @param isDeoxy         If Residue is DNA; false means RNA.
2630    * @param is3sTerminal    If Residue is at a 3' end.
2631    * @param prevSugarPucker Sugar pucker for prevResidue specified by Rotamer.
2632    */
2633   private static void applyNASideAtoms(Residue residue, Rotamer rotamer, Residue prevResidue,
2634                                        boolean isDeoxy, boolean is3sTerminal, NucleicSugarPucker prevSugarPucker) {
2635     Atom C1s = (Atom) residue.getAtomNode("C1'");
2636     Atom C2s = (Atom) residue.getAtomNode("C2'");
2637     Atom C3s = (Atom) residue.getAtomNode("C3'");
2638     Atom C4s = (Atom) residue.getAtomNode("C4'");
2639     Atom O4s = (Atom) residue.getAtomNode("O4'");
2640     Atom O3s = (Atom) residue.getAtomNode("O3'");
2641     // O2s will be null in DNA.
2642     Atom O2s = (Atom) residue.getAtomNode("O2'");
2643 
2644     // Hydrogen attached to the sugar
2645     // Hydrogen in DNA.  Will be null in RNA.
2646     Atom H2ss = (Atom) residue.getAtomNode("H2''");
2647     // Hydrogen in RNA.  Will be null in DNA.
2648     Atom H2s = (Atom) residue.getAtomNode("H2'");
2649     Atom HO2s = (Atom) residue.getAtomNode("HO2'");
2650     // Common Hydrogen
2651     Atom H3s = (Atom) residue.getAtomNode("H3'");
2652     Atom H4s = (Atom) residue.getAtomNode("H4'");
2653     Atom H1s = (Atom) residue.getAtomNode("H1'");
2654     Atom H5s = (Atom) residue.getAtomNode("H5'");
2655     Atom H5ss = (Atom) residue.getAtomNode("H5''");
2656 
2657     Atom C5s = (Atom) residue.getAtomNode("C5'");
2658     Atom O5s = (Atom) residue.getAtomNode("O5'");
2659     Atom P = (Atom) residue.getAtomNode("P");
2660     Atom OP1 = (Atom) residue.getAtomNode("OP1");
2661     Atom OP2 = (Atom) residue.getAtomNode("OP2");
2662 
2663     // Build attachments to C2'.
2664     Bond C2s_H2s = C2s.getBond(H2s);
2665     double dC2s_H2s = C2s_H2s.bondType.distance;
2666     Angle C3s_C2s_H2s = C3s.getAngle(C2s, H2s);
2667     double dC3s_C2s_H2s = C3s_C2s_H2s.angleType.angle[C3s_C2s_H2s.nh];
2668 
2669     if (isDeoxy) {
2670       intxyz(H2s, C2s, dC2s_H2s, C3s, dC3s_C2s_H2s, C1s, 109.4, 1);
2671       Bond C2s_H2ss = C2s.getBond(H2ss);
2672       double dC2s_H2ss = C2s_H2ss.bondType.distance;
2673       Angle C3s_C2s_H2ss = C3s.getAngle(C2s, H2ss);
2674       double dC3s_C2s_H2ss = C3s_C2s_H2ss.angleType.angle[C3s_C2s_H2ss.nh];
2675       intxyz(H2ss, C2s, dC2s_H2ss, C3s, dC3s_C2s_H2ss, C1s, 109.4, -1);
2676     } else {
2677       intxyz(H2s, C2s, dC2s_H2s, C3s, dC3s_C2s_H2s, C1s, 109.4, -1);
2678 
2679       Bond C2s_O2s = C2s.getBond(O2s);
2680       double dC2s_O2s = C2s_O2s.bondType.distance;
2681       Angle C3s_C2s_O2s = C3s.getAngle(C2s, O2s);
2682       double dC3s_C2s_O2s = C3s_C2s_O2s.angleType.angle[C3s_C2s_O2s.nh];
2683       intxyz(O2s, C2s, dC2s_O2s, C3s, dC3s_C2s_O2s, C1s, 109.4, 1);
2684 
2685       /*
2686        * The placement of HO2' may eventually become a rotameric
2687        * function, but is presently being defaulted to 70 degrees.
2688        *
2689        * 70 degrees was just what I got from looking at 3ZD7 in
2690        * PyMol
2691        */
2692       Bond O2s_HO2s = O2s.getBond(HO2s);
2693       double dO2s_HO2s = O2s_HO2s.bondType.distance;
2694       Angle C2s_O2s_HO2s = C2s.getAngle(O2s, HO2s);
2695       double dC2s_O2s_HO2s = C2s_O2s_HO2s.angleType.angle[C2s_O2s_HO2s.nh];
2696       intxyz(HO2s, O2s, dO2s_HO2s, C2s, dC2s_O2s_HO2s, C1s, 70, 0);
2697     }
2698 
2699     Bond C1s_H1s = C1s.getBond(H1s);
2700     double dC1s_H1s = C1s_H1s.bondType.distance;
2701     Angle C2s_C1s_H1s = C2s.getAngle(C1s, H1s);
2702     double dC2s_C1s_H1s = C2s_C1s_H1s.angleType.angle[C2s_C1s_H1s.nh];
2703     intxyz(H1s, C1s, dC1s_H1s, C2s, dC2s_C1s_H1s, O4s, 109.4, 1);
2704 
2705     Bond C3s_H3s = C3s.getBond(H3s);
2706     double dC3s_H3s = C3s_H3s.bondType.distance;
2707     Angle C2s_C3s_H3s = C2s.getAngle(C3s, H3s);
2708     double dC2s_C3s_H3s = C2s_C3s_H3s.angleType.angle[C2s_C3s_H3s.nh];
2709     intxyz(H3s, C3s, dC3s_H3s, C2s, dC2s_C3s_H3s, C4s, 109.4, 1);
2710 
2711     Bond C4s_H4s = C4s.getBond(H4s);
2712     double dC4s_H4s = C4s_H4s.bondType.distance;
2713     Angle C3s_C4s_H4s = C3s.getAngle(C4s, H4s);
2714     double dC3s_C4s_H4s = C3s_C4s_H4s.angleType.angle[C3s_C4s_H4s.nh];
2715     intxyz(H4s, C4s, dC4s_H4s, C3s, dC3s_C4s_H4s, O4s, 109.4, -1);
2716 
2717     Bond C5s_H5s = C5s.getBond(H5s);
2718     double dC5s_H5s = C5s_H5s.bondType.distance;
2719     Angle C4s_C5s_H5s = C4s.getAngle(C5s, H5s);
2720     double dC4s_C5s_H5s = C4s_C5s_H5s.angleType.angle[C4s_C5s_H5s.nh];
2721     intxyz(H5s, C5s, dC5s_H5s, C4s, dC4s_C5s_H5s, O5s, 109.4, -1);
2722 
2723     Bond C5s_H5ss = C5s.getBond(H5ss);
2724     double dC5s_H5ss = C5s_H5ss.bondType.distance;
2725     Angle C4s_C5s_H5ss = C4s.getAngle(C5s, H5ss);
2726     double dC4s_C5s_H5ss = C4s_C5s_H5ss.angleType.angle[C4s_C5s_H5ss.nh];
2727     intxyz(H5ss, C5s, dC5s_H5ss, C4s, dC4s_C5s_H5ss, O5s, 109.4, 1);
2728 
2729     if (is3sTerminal) {
2730       // TODO: Determine proper labels for 3' phosphate caps so they may be implemented.
2731       Atom HO3s = (Atom) residue.getAtomNode("HO3'");
2732       // if (HO3s != null) {
2733       Bond O3s_HO3s = O3s.getBond(HO3s);
2734       double dO3s_HO3s = O3s_HO3s.bondType.distance;
2735       Angle C3s_O3s_HO3s = C3s.getAngle(O3s, HO3s);
2736       double dC3s_O3s_HO3s = C3s_O3s_HO3s.angleType.angle[C3s_O3s_HO3s.nh];
2737       // HO3s defaulted to the antiperiplanar value of 180 degrees.
2738       intxyz(HO3s, O3s, dO3s_HO3s, C3s, dC3s_O3s_HO3s, C4s, 180, 0);
2739     }
2740 
2741     if (P != null) {
2742       double[] PXYZ = new double[3];
2743       P.getXYZ(PXYZ);
2744       double[] O5sXYZ = new double[3];
2745       O5s.getXYZ(O5sXYZ);
2746       double[] C5sXYZ = new double[3];
2747       C5s.getXYZ(C5sXYZ);
2748 
2749       Bond P_OP1 = P.getBond(OP1);
2750       double dP_OP1 = P_OP1.bondType.distance;
2751       Angle O5s_P_OP1 = C5s.getAngle(O5s, P);
2752       double dO5s_P_OP1 = O5s_P_OP1.angleType.angle[O5s_P_OP1.nh];
2753 
2754       Bond P_OP2 = P.getBond(OP2);
2755       double dP_OP2 = P_OP2.bondType.distance;
2756       Angle O5s_P_OP2 = C5s.getAngle(O5s, P);
2757       double dO5s_P_OP2 = O5s_P_OP2.angleType.angle[O5s_P_OP2.nh];
2758 
2759       // TODO: Handle Hydrogen attached to 5'-terminal phosphates.
2760 
2761       /*
2762        * If there is a prior residue, draw tetrahedrally based on O3'
2763        * (i-1).  Else, draw based on OP3.
2764        */
2765       if (prevResidue != null) {
2766         double[] O3sPriorCoords;
2767         if (prevSugarPucker == NucleicSugarPucker.C3_ENDO) {
2768           O3sPriorCoords = prevResidue.getO3sNorth();
2769         } else {
2770           O3sPriorCoords = prevResidue.getO3sSouth();
2771         }
2772         double[] OP1XYZ = determineIntxyz(PXYZ, dP_OP1, O5sXYZ, dO5s_P_OP1, O3sPriorCoords, 109.4,
2773             1);
2774         double[] OP2XYZ = determineIntxyz(PXYZ, dP_OP2, O5sXYZ, dO5s_P_OP2, O3sPriorCoords, 109.4,
2775             -1);
2776         OP1.moveTo(OP1XYZ);
2777         OP2.moveTo(OP2XYZ);
2778       } else {
2779         Atom OP3 = (Atom) residue.getAtomNode("OP3");
2780         double[] OP3XYZ = new double[3];
2781         OP3.getXYZ(OP3XYZ);
2782         double[] OP1XYZ = determineIntxyz(PXYZ, dP_OP1, O5sXYZ, dO5s_P_OP1, OP3XYZ, 109.4, 1);
2783         double[] OP2XYZ = determineIntxyz(PXYZ, dP_OP2, O5sXYZ, dO5s_P_OP2, OP3XYZ, 109.4, -1);
2784         OP1.moveTo(OP1XYZ);
2785         OP2.moveTo(OP2XYZ);
2786       }
2787     } else {
2788       Atom HO5s = (Atom) residue.getAtomNode("HO5'");
2789       Bond O5s_HO5s = O5s.getBond(HO5s);
2790       double dO5s_HO5s = O5s_HO5s.bondType.distance;
2791       Angle C5s_O5s_HO5s = C5s.getAngle(O5s, HO5s);
2792       double dC5s_O5s_HO5s = C5s_O5s_HO5s.angleType.angle[C5s_O5s_HO5s.nh];
2793       intxyz(HO5s, O5s, dO5s_HO5s, C5s, dC5s_O5s_HO5s, C4s, rotamer.chi5, 0);
2794     }
2795   }
2796 
2797   /**
2798    * Applies a coordinates-based Rotamer (defined by Cartesian coordinates instead of by a set of
2799    * torsion angles); intended for use with original coordinates Rotamers and possibly other future
2800    * cases.
2801    *
2802    * @param residue Residue to apply Rotamer for
2803    * @param rotamer Coordinates-based Rotamer
2804    */
2805   private static void applyState(Residue residue, Rotamer rotamer) {
2806     if (rotamer.isState) {
2807       residue.revertState(rotamer.originalState);
2808     } else {
2809       logger.warning(format(
2810           " Attempting to apply a ResidueState for " + "a torsion-based rotamer %s for residue %s",
2811           rotamer, residue));
2812     }
2813   }
2814 
2815   /**
2816    * Draws CZ of Phe/Tyr/Tyd twice (from each branch of the ring), the cuts it down the middle.
2817    *
2818    * @param resName Residue containing CZ.
2819    * @param CZ      CZ to be placed.
2820    * @param CG      CG atom.
2821    * @param CE1     CE1 atom.
2822    * @param CD1     CD1 atom.
2823    * @param CE2     CE2 atom.
2824    * @param CD2     CD2 atom.
2825    * @return Mean coordinates for CZ based on internal geometry.
2826    */
2827   private static double[] drawCZ(AminoAcid3 resName, Atom CZ, Atom CG, Atom CE1, Atom CD1, Atom CE2,
2828                                  Atom CD2) {
2829     double bondLen = CZ.getBond(CE1).bondType.distance;
2830     double ang = getAngle(resName, CZ, CE1, CD1);
2831     double[] xCG = new double[3];
2832     xCG = CG.getXYZ(xCG);
2833 
2834     double[] xCE = new double[3];
2835     xCE = CE1.getXYZ(xCE);
2836     double[] xCD = new double[3];
2837     xCD = CD1.getXYZ(xCD);
2838     double[] xyz1 = determineIntxyz(xCE, bondLen, xCD, ang, xCG, 0.0, 0);
2839 
2840     xCE = CE2.getXYZ(xCE);
2841     xCD = CD2.getXYZ(xCD);
2842     double[] xyz2 = determineIntxyz(xCE, bondLen, xCD, ang, xCG, 0, 0);
2843 
2844     for (int i = 0; i < 3; i++) {
2845       xyz1[i] += xyz2[i];
2846       xyz1[i] *= 0.5;
2847     }
2848     return xyz1;
2849   }
2850 
2851   /**
2852    * Gets an Angle, using the default as set by property for whether to use idealized ring geometry
2853    * (default), or based on force field.
2854    *
2855    * @param resName AminoAcid3 for a1-a3.
2856    * @param a1      An Atom.
2857    * @param a2      Another Atom.
2858    * @param a3      A third Atom.
2859    * @return a1-a2-a3 angle for internal geometry.
2860    */
2861   private static double getAngle(AminoAcid3 resName, Atom a1, Atom a2, Atom a3) {
2862     if (useIdealRingGeometries) {
2863       return idealGeometryAngle(resName, a1, a2, a3);
2864     } else {
2865       return angleFromForceField(a1, a2, a3);
2866     }
2867   }
2868 
2869   /**
2870    * Obtains an idealized angle using a lookup map of stored, idealized geometries, with a fallback
2871    * to using the force field. This is intended for use with ring systems, where ring constraints
2872    * mean that optimum-energy rings may not have the values defined by the force field.
2873    *
2874    * <p>Current lookup map is only for PHE, TYR, TYD, HIS, HID, HIE, and TRP, using values obtained
2875    * from a tight bonded-terms-only optimization under AMOEBA BIO 2018.
2876    *
2877    * @param resName Name of the Residue containing a1-a3.
2878    * @param a1      An atom.
2879    * @param a2      Another Atom.
2880    * @param a3      Another Atom.
2881    * @return Stored idealized a1-a2-a3 angle in degrees.
2882    */
2883   private static double idealGeometryAngle(AminoAcid3 resName, Atom a1, Atom a2, Atom a3) {
2884     StringBuilder sb = new StringBuilder(a1.getName());
2885     sb.append("-").append(a2.getName()).append("-").append(a3.getName());
2886     Map<String, Double> resMap = idealAngleGeometries.get(resName);
2887     String atomString = sb.toString();
2888 
2889     if (resMap.containsKey(atomString)) {
2890       return resMap.get(atomString);
2891     } else {
2892       sb = new StringBuilder(a3.getName());
2893       sb.append("-").append(a2.getName()).append("-").append(a1.getName());
2894       atomString = sb.toString();
2895       if (resMap.containsKey(atomString)) {
2896         return resMap.get(atomString);
2897       } else {
2898         logger.finest(
2899             format(" Could not find an ideal-geometry angle for %s %s-%s-%s", resName, a1, a2, a3));
2900         return angleFromForceField(a1, a2, a3);
2901       }
2902     }
2903   }
2904 
2905   /**
2906    * Checks if a Residue has a PTM.
2907    *
2908    * @param residue The Residue to check.
2909    * @return True if this is a modified residue.
2910    */
2911   private static boolean isModRes(Residue residue) {
2912     List<Atom> resAtoms = residue.getAtomList();
2913     return (resAtoms != null && !resAtoms.isEmpty() && resAtoms.get(0).isModRes());
2914   }
2915 
2916   /**
2917    * Measure the current torsions of a nucleic acid Residue, starting from the 5'-most torsion.
2918    *
2919    * <p>Chi[0]-chi[6] in order are: delta (i-1), epsilon (i-1), zeta (i-1), alpha (i), beta (i),
2920    * gamma (i) and delta (i) for residue i.
2921    *
2922    * @param residue Residue to be measured.
2923    * @param chi     Array to be filled with torsion values.
2924    * @param print   Verbosity flag.
2925    * @return The number of rotamers this Residue has.
2926    */
2927   private static int measureNARotamer(Residue residue, double[] chi, boolean print) {
2928     NucleicAcid3 name = NucleicAcidUtils.NucleicAcid3.valueOf(residue.getName());
2929     Residue prevResidue = residue.getPreviousResidue();
2930     Torsion torsion;
2931 
2932     Atom C5s = (Atom) residue.getAtomNode("C5'");
2933     Atom C4s = (Atom) residue.getAtomNode("C4'");
2934     Atom C3s = (Atom) residue.getAtomNode("C3'");
2935     Atom O3s = (Atom) residue.getAtomNode("O3'");
2936     Atom O5s = (Atom) residue.getAtomNode("O5'");
2937     Atom P = (Atom) residue.getAtomNode("P");
2938 
2939     int nRot = 7;
2940 
2941     /*
2942      * Start by measuring delta (i-1) if available, working up to delta.  If
2943      * there is no prior residue, start measuring from the 5'-most torsion.
2944      */
2945     if (prevResidue == null) {
2946       switch (name) {
2947         case GUA, ADE, DGU, DAD, CYT, URI, THY, DCY, DTY -> {
2948           /*
2949            * If there is an HO5s, measure alpha based on HO5s.  Else,
2950            * measure zeta (i-1) based on OP3 and alpha on P.
2951            */
2952           Atom HO5s = (Atom) residue.getAtomNode("HO5'");
2953           if (HO5s != null) {
2954             torsion = HO5s.getTorsion(O5s, C5s, C4s);
2955             chi[4] = torsion.measure();
2956             if (print) {
2957               logger.info(torsion.toString());
2958             }
2959             nRot = 3;
2960           } else {
2961             Atom OP3 = (Atom) residue.getAtomNode("OP3");
2962             nRot = 3;
2963             if (OP3 != null) {
2964               torsion = OP3.getTorsion(P, O5s, C5s);
2965               chi[3] = torsion.measure();
2966               if (print) {
2967                 logger.info(torsion.toString());
2968               }
2969               nRot = 4;
2970             }
2971 
2972             torsion = P.getTorsion(O5s, C5s, C4s);
2973             chi[4] = torsion.measure();
2974             if (print) {
2975               logger.info(torsion.toString());
2976             }
2977           }
2978         }
2979         default -> {
2980         }
2981       }
2982     } else {
2983       switch (name) {
2984         case GUA, ADE, DGU, DAD, CYT, URI, THY, DCY, DTY -> {
2985           Atom O3sPrev = (Atom) prevResidue.getAtomNode("O3'");
2986           Atom C3sPrev = (Atom) prevResidue.getAtomNode("C3'");
2987           Atom C4sPrev = (Atom) prevResidue.getAtomNode("C4'");
2988           Atom C5sPrev = (Atom) prevResidue.getAtomNode("C5'");
2989           torsion = C5sPrev.getTorsion(C4sPrev, C3sPrev, O3sPrev);
2990           chi[0] = torsion.measure();
2991           if (print) {
2992             logger.info(torsion.toString());
2993           }
2994           torsion = C4sPrev.getTorsion(C3sPrev, O3sPrev, P);
2995           chi[1] = torsion.measure();
2996           if (print) {
2997             logger.info(torsion.toString());
2998           }
2999           torsion = C3sPrev.getTorsion(O3sPrev, P, O5s);
3000           chi[2] = torsion.measure();
3001           if (print) {
3002             logger.info(torsion.toString());
3003           }
3004           torsion = O3sPrev.getTorsion(P, O5s, C5s);
3005           chi[3] = torsion.measure();
3006           if (print) {
3007             logger.info(torsion.toString());
3008           }
3009           torsion = P.getTorsion(O5s, C5s, C4s);
3010           chi[4] = torsion.measure();
3011           if (print) {
3012             logger.info(torsion.toString());
3013           }
3014         }
3015         default -> {
3016         }
3017       }
3018     }
3019     /*
3020      * Measure torsions common to all nucleic acids (gamma, delta).
3021      */
3022     torsion = O5s.getTorsion(C5s, C4s, C3s);
3023     chi[5] = torsion.measure();
3024     if (print) {
3025       logger.info(torsion.toString());
3026     }
3027 
3028     torsion = C5s.getTorsion(C4s, C3s, O3s);
3029     chi[6] = torsion.measure();
3030     if (print) {
3031       logger.info(torsion.toString());
3032     }
3033     return nRot;
3034   }
3035 
3036   /**
3037    * measureUNKRotamer.
3038    *
3039    * @param residue a {@link ffx.potential.bonded.Residue} object.
3040    * @param chi     an array of {@link double} objects.
3041    * @param print   a boolean.
3042    */
3043   private static void measureUNKRotamer(Residue residue, double[] chi, boolean print) {
3044     String resName = residue.getName().toUpperCase();
3045     if (nonstdRotCache.containsKey(resName)) {
3046       nonstdRotCache.get(resName).measureNonstdRot(residue, chi, print);
3047     } else {
3048       logger.warning(format(" Could not measure chi angles " + "for residue %s", residue));
3049     }
3050   }
3051 
3052   private static void readRotFile(File rotamerFile, MolecularAssembly assembly, int boxWindowIndex)
3053       throws IOException {
3054     try (BufferedReader br = new BufferedReader(new FileReader(rotamerFile))) {
3055       Polymer[] polys = assembly.getChains();
3056       Residue currentRes = null;
3057       ResidueState origState = null;
3058 
3059       String line = br.readLine();
3060       boolean doRead = false;
3061       while (line != null) {
3062         String[] toks = line.trim().split(":");
3063         if (toks[0].equals("ALGORITHM")) {
3064           doRead = Integer.parseInt(toks[2]) == boxWindowIndex;
3065           logger.info(format(" Readabilifications %b with %s", doRead, line));
3066         } else if (doRead && !toks[0].startsWith("#")) {
3067           switch (toks[0]) {
3068             case "RES" -> {
3069               String segID = toks[2];
3070               int resnum = Integer.parseInt(toks[4]);
3071               for (Polymer poly : polys) {
3072                 if (poly.getName().equals(segID)) {
3073                   currentRes = poly.getResidue(resnum);
3074                   break;
3075                 }
3076               }
3077             }
3078             case "ENDROT" -> {
3079               // TODO: Publish rotamer & revert coordinates.
3080               currentRes.addRotamers(Rotamer.defaultRotamerFactory(currentRes));
3081               currentRes.revertState(origState);
3082               logger.info(format(" Adding a rotamer to %s", currentRes));
3083             }
3084             case "ROT" -> origState = currentRes.storeState();
3085             case "ATOM" -> {
3086               String name = toks[1];
3087               Atom atom = (Atom) currentRes.getAtomNode(name);
3088               double[] xyz = new double[3];
3089               for (int i = 0; i < 3; i++) {
3090                 xyz[i] = Double.parseDouble(toks[i + 2]);
3091               }
3092               atom.setXYZ(xyz);
3093             }
3094             default -> logger.warning(" Unrecognized line! " + line);
3095           }
3096         }
3097         line = br.readLine();
3098       }
3099     }
3100   }
3101 
3102   /**
3103    * Get the protein rotamer library.
3104    *
3105    * @return the ProteinLibrary in use.
3106    */
3107   public ProteinLibrary getLibrary() {
3108     return proteinLibrary;
3109   }
3110 
3111   /**
3112    * Return an array of Rotamers for the given amino acid.
3113    *
3114    * @param name The name of the amino acid.
3115    * @return An array of Rotamers.
3116    */
3117   public Rotamer[] getRotamers(AminoAcid3 name) {
3118     return getRotamers(name, null);
3119   }
3120 
3121   /**
3122    * Return an array of Rotamers for the given amino acid.
3123    *
3124    * @param name The name of the amino acid.
3125    * @return An array of Rotamers.
3126    */
3127   public Rotamer[] getRotamers(AminoAcid3 name, TitrationUtils titrationUtils) {
3128     return switch (proteinLibrary) {
3129       case PonderAndRichards -> getPonderAndRichardsRotamers(name, titrationUtils);
3130       case Richardson -> getRichardsonRotamers(name, titrationUtils);
3131       case None -> null;
3132     };
3133   }
3134 
3135   /**
3136    * Return an array of Rotamers for the given nucleic acid.
3137    *
3138    * @param name The name of the nucleic acid.
3139    * @return An array of Rotamers.
3140    */
3141   public Rotamer[] getRotamers(NucleicAcid3 name) {
3142     return getRichardsonRNARotamers(name);
3143   }
3144 
3145   /**
3146    * getUsingOrigCoordsRotamer.
3147    *
3148    * @return a boolean.
3149    */
3150   public boolean getUsingOrigCoordsRotamer() {
3151     return useOrigCoordsRotamer;
3152   }
3153 
3154   /**
3155    * Guess at what rotamer a residue is currently in.
3156    *
3157    * @param residue Residue to check.
3158    * @return Index of the rotamer it is in.
3159    */
3160   public RotamerGuess guessRotamer(Residue residue) {
3161     assert useOrigCoordsRotamer == false;
3162     if (residue == null) {
3163       throw new IllegalArgumentException(" Residue cannot be null!");
3164     }
3165     Rotamer[] rotamers = residue.getRotamers();
3166     if (rotamers == null) {
3167       rotamers = residue.setRotamers(this);
3168     }
3169     if (rotamers == null || rotamers.length == 0) {
3170       throw new IllegalArgumentException(format(" Residue %s does not have rotamers!", residue));
3171     }
3172     int nRot = rotamers.length;
3173 
3174     double[] currChi =
3175         residue.getResidueType().equals(Residue.ResidueType.AA) ? new double[4] : new double[7];
3176     int numChis = measureRotamer(residue, currChi, false);
3177     double[] chiRMSD = new double[nRot];
3178     double lowestRMSD = Double.MAX_VALUE;
3179     int indexLowest = -1;
3180 
3181     for (int i = 0; i < nRot; i++) {
3182       double rmsd = 0;
3183       Rotamer roti = rotamers[i];
3184       double[] rotChi = roti.angles;
3185 
3186       for (int j = 0; j < numChis; j++) {
3187         double dChi = Math.abs(currChi[j] - rotChi[j]);
3188         if (dChi > 180) {
3189           dChi = 360.0 - dChi;
3190         }
3191         dChi *= dChi;
3192         rmsd += dChi;
3193       }
3194 
3195       rmsd /= numChis;
3196       rmsd = FastMath.sqrt(rmsd);
3197       chiRMSD[i] = rmsd;
3198       if (rmsd < lowestRMSD) {
3199         lowestRMSD = rmsd;
3200         indexLowest = i;
3201       }
3202     }
3203 
3204     if (indexLowest < 0) {
3205       logger.warning(format(" Residue %s could not be IDd!", residue));
3206       logger.info(Arrays.toString(currChi));
3207       logger.info(format("%12.5g", lowestRMSD));
3208       logger.info(format("%d", nRot));
3209     }
3210 
3211     return new RotamerGuess(residue, rotamers[indexLowest], indexLowest, lowestRMSD);
3212   }
3213 
3214   /**
3215    * Setter for the field <code>useOrigCoordsRotamer</code>.
3216    *
3217    * @param set a boolean.
3218    */
3219   public void setUseOrigCoordsRotamer(boolean set) {
3220     useOrigCoordsRotamer = set;
3221   }
3222 
3223   /**
3224    * Return rotamer array for the given AA or NA residue.
3225    *
3226    * @param residue the Residue to examine.
3227    * @return Array of Rotamers for Residue's type.
3228    */
3229   Rotamer[] getRotamers(Residue residue) {
3230     return getRotamers(residue, null);
3231   }
3232 
3233   /**
3234    * Return rotamer array for the given AA or NA residue.
3235    *
3236    * @param residue the Residue to examine.
3237    * @return Array of Rotamers for Residue's type.
3238    */
3239   Rotamer[] getRotamers(Residue residue, TitrationUtils titrationUtils) {
3240     // Package-private; intended to be accessed only by Residue and extensions
3241     // thereof. Otherwise, use Residue.getRotamers(RotamerLibrary library).
3242     if (residue == null) {
3243       return null;
3244     }
3245     if (isModRes(residue)) {
3246       if (nonstdRotCache.containsKey(residue.getName().toUpperCase())) {
3247         return nonstdRotCache.get(residue.getName().toUpperCase()).getRotamers();
3248       }
3249       return null;
3250     }
3251     switch (residue.getResidueType()) {
3252       case AA -> {
3253         AminoAcid3 aa = AminoAcid3.valueOf(residue.getName());
3254         // Now check for cysteines in a disulfide bond.
3255         switch (aa) {
3256           case CYS, CYX, CYD -> {
3257             List<Atom> cysAtoms = residue.getAtomList();
3258             // First find the sulfur on atomic number, then on starting with S.
3259             Optional<Atom> s = cysAtoms.stream().filter(a -> a.getAtomType().atomicNumber == 16)
3260                 .findAny();
3261             if (s.isEmpty()) {
3262               s = cysAtoms.stream().filter(a -> a.getName().startsWith("S")).findAny();
3263             }
3264             if (s.isPresent()) {
3265               Atom theS = s.get();
3266               boolean attachedS = theS.getBonds().stream().map(b -> b.get1_2(theS))
3267                   .anyMatch(a -> a.getAtomType().atomicNumber == 16 || a.getName().startsWith("S"));
3268               if (attachedS) {
3269                 // Return a null rotamer array if it's disulfide-bonded.
3270                 return null;
3271               }
3272             } else {
3273               logger.warning(
3274                   format(" No sulfur atom found attached to %s residue %s!", aa, residue));
3275             }
3276           }
3277 
3278           // Default: no-op (we are checking for cysteine disulfide bonds).
3279         }
3280         return getRotamers(aa, titrationUtils);
3281       }
3282       case NA -> {
3283         NucleicAcid3 na = NucleicAcid3.valueOf(residue.getName());
3284         return getRotamers(na);
3285       }
3286       default -> {
3287         if (nonstdRotCache.containsKey(residue.getName().toUpperCase())) {
3288           return nonstdRotCache.get(residue.getName().toUpperCase()).getRotamers();
3289         }
3290         return null;
3291       }
3292     }
3293   }
3294 
3295   /**
3296    * Fills the idealized amino acid rotamer cache with the Ponder and Richards rotamers.
3297    *
3298    * <p>Ponder, J. W.; Richards, F. M., Tertiary templates for proteins: Use of packing criteria in
3299    * the enumeration of allowed sequences for different structural classes Journal of Molecular
3300    * Biology 1987, 193 (4), 775-791
3301    *
3302    * @param name           Type of amino acid.
3303    * @param titrationUtils TitrationUtils for rotamers whose titration state can change.
3304    * @return Rotamer cache (double[] of torsions).
3305    */
3306   private Rotamer[] getPonderAndRichardsRotamers(AminoAcid3 name, TitrationUtils titrationUtils) {
3307     int n = name.ordinal();
3308     if (aminoAcidRotamerCache[n] != null) {
3309       return aminoAcidRotamerCache[n];
3310     }
3311     switch (name) {
3312       case VAL -> {
3313         aminoAcidRotamerCache[n] = new Rotamer[3];
3314         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,173.5, 9.0);
3315         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-63.4, 8.1);
3316         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,69.3, 9.6);
3317       }
3318       case LEU -> {
3319         aminoAcidRotamerCache[n] = new Rotamer[4];
3320         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-64.9, 8.2, 176.0, 9.9);
3321         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-176.4, 10.2, 63.1, 8.2);
3322         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-165.3, 10.0, 168.2, 34.2);
3323         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,44.3, 20.0, 60.4, 18.8);
3324       }
3325       case ILE -> {
3326         aminoAcidRotamerCache[n] = new Rotamer[5];
3327         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-60.9, 7.5, 168.7, 11.6);
3328         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-59.6, 9.6, -64.1, 14.3);
3329         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,61.7, 5.0, 163.8, 16.4);
3330         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-166.6, 10.1, 166.0, 8.9);
3331         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-174.8, 24.9, 72.1, 10.5);
3332       }
3333       case SER -> {
3334         aminoAcidRotamerCache[n] = new Rotamer[3];
3335         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,64.7, 16.1);
3336         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-69.7, 14.6);
3337         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-176.1, 20.2);
3338       }
3339       case THR -> {
3340         aminoAcidRotamerCache[n] = new Rotamer[3];
3341         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62.7, 8.5);
3342         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-59.7, 9.4);
3343         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-169.5, 6.6);
3344       }
3345       case CYS, CYD -> {
3346         if (titrationUtils == null) {
3347           aminoAcidRotamerCache[n] = new Rotamer[3];
3348           aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-65.2, 10.1);
3349           aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-179.6, 9.5);
3350           aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,63.5, 9.6);
3351         } else {
3352           aminoAcidRotamerCache[n] = new Rotamer[6];
3353           aminoAcidRotamerCache[n][0] = new Rotamer(AminoAcid3.CYS, titrationUtils, 0,-65.2, 10.1);
3354           aminoAcidRotamerCache[n][1] = new Rotamer(AminoAcid3.CYS, titrationUtils, 1,-179.6, 9.5);
3355           aminoAcidRotamerCache[n][2] = new Rotamer(AminoAcid3.CYS, titrationUtils, 2,63.5, 9.6);
3356           aminoAcidRotamerCache[n][3] = new Rotamer(AminoAcid3.CYD, titrationUtils, 3,-65.2, 10.1);
3357           aminoAcidRotamerCache[n][4] = new Rotamer(AminoAcid3.CYD, titrationUtils, 4,-179.6, 9.5);
3358           aminoAcidRotamerCache[n][5] = new Rotamer(AminoAcid3.CYD, titrationUtils, 5,63.5, 9.6);
3359         }
3360       }
3361       /*
3362        * TODO: Figure out proline rotamers.  I have dihedrals from
3363        * the Richardson lab/Kinemages website (downloaded PDB, used
3364        * the get_dihedral function to extract the dihedrals), and they
3365        * conflict with these rotamers.  Plus, this library only
3366        * specifies one of the two necessary dihedrals.
3367        */
3368       case PRO -> {
3369         aminoAcidRotamerCache[n] = new Rotamer[3];
3370         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,24.0, 8.0);
3371         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,0.0, 8.0);
3372         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-24.0, 8.0);
3373       }
3374       case PHE -> {
3375         aminoAcidRotamerCache[n] = new Rotamer[4];
3376         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-66.3, 10.2, 94.3, 19.5);
3377         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-179.2, 9.3, 78.9, 8.9);
3378         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,66.0, 12.0, 90.7, 9.4);
3379         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-71.9, 16.3, -0.4, 26.1);
3380       }
3381       case TYR -> {
3382         aminoAcidRotamerCache[n] = new Rotamer[8];
3383         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-66.5, 11.4, 96.6, 21.8, 0, 0);
3384         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-179.7, 12.6, 71.9, 13.4, 0, 0);
3385         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,63.3, 9.4, 89.1, 13.0, 0, 0);
3386         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-67.2, 13.2, -1.0, 20.1, 0, 0);
3387         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-66.5, 11.4, 96.6, 21.8, 180.0, 0);
3388         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-179.7, 12.6, 71.9, 13.4, 180.0, 0);
3389         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,63.3, 9.4, 89.1, 13.0, 180.0, 0);
3390         aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-67.2, 13.2, -1.0, 20.1, 180.0, 0);
3391       }
3392       case TYD -> {
3393         aminoAcidRotamerCache[n] = new Rotamer[4];
3394         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-66.5, 11.4, 96.6, 21.8);
3395         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-179.7, 12.6, 71.9, 13.4);
3396         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,63.3, 9.4, 89.1, 13.0);
3397         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-67.2, 13.2, -1.0, 20.1);
3398       }
3399       case TRP -> {
3400         aminoAcidRotamerCache[n] = new Rotamer[6];
3401         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-70.4, 7.0, 100.5, 18.2);
3402         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,64.8, 13.0, -88.9, 5.3);
3403         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-177.3, 7.9, -95.1, 7.6);
3404         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-179.5, 3.4, 87.5, 3.8);
3405         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-73.3, 6.5, -87.7, 8.1);
3406         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,62.2, 10.0, 112.5, 15.0);
3407       }
3408       case HIS, HIE, HID -> {
3409         if (titrationUtils == null) {
3410           aminoAcidRotamerCache[n] = new Rotamer[6];
3411           aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-62.8, 10.0, -74.3, 17.2);
3412           aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-175.2, 15.4, -88.7, 43.5);
3413           aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-69.8, 5.9, 96.1, 32.2);
3414           aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,67.9, 17.4, -80.5, 40.7);
3415           aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-177.3, 6.3, 100.5, 14.0);
3416           aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,48.8, 10.0, 89.5, 30.0);
3417         } else {
3418           // 6 Rotamers x 3 states (HIS, HIE, HID)
3419           aminoAcidRotamerCache[n] = new Rotamer[18];
3420           // HIS
3421           aminoAcidRotamerCache[n][0] = new Rotamer(AminoAcid3.HIS, titrationUtils, 0,-62.8, 10.0,
3422               -74.3, 17.2);
3423           aminoAcidRotamerCache[n][1] = new Rotamer(AminoAcid3.HIS, titrationUtils, 1,-175.2, 15.4,
3424               -88.7, 43.5);
3425           aminoAcidRotamerCache[n][2] = new Rotamer(AminoAcid3.HIS, titrationUtils, 2,-69.8, 5.9, 96.1,
3426               32.2);
3427           aminoAcidRotamerCache[n][3] = new Rotamer(AminoAcid3.HIS, titrationUtils, 3,67.9, 17.4,
3428               -80.5, 40.7);
3429           aminoAcidRotamerCache[n][4] = new Rotamer(AminoAcid3.HIS, titrationUtils, 4,-177.3, 6.3,
3430               100.5, 14.0);
3431           aminoAcidRotamerCache[n][5] = new Rotamer(AminoAcid3.HIS, titrationUtils, 5,48.8, 10.0, 89.5,
3432               30.0);
3433           // HIE
3434           aminoAcidRotamerCache[n][6] = new Rotamer(AminoAcid3.HIE, titrationUtils, 6,-62.8, 10.0,
3435               -74.3, 17.2);
3436           aminoAcidRotamerCache[n][7] = new Rotamer(AminoAcid3.HIE, titrationUtils, 7,-175.2, 15.4,
3437               -88.7, 43.5);
3438           aminoAcidRotamerCache[n][8] = new Rotamer(AminoAcid3.HIE, titrationUtils, 8,-69.8, 5.9, 96.1,
3439               32.2);
3440           aminoAcidRotamerCache[n][9] = new Rotamer(AminoAcid3.HIE, titrationUtils, 9,67.9, 17.4,
3441               -80.5, 40.7);
3442           aminoAcidRotamerCache[n][10] = new Rotamer(AminoAcid3.HIE, titrationUtils, 10,-177.3, 6.3,
3443               100.5, 14.0);
3444           aminoAcidRotamerCache[n][11] = new Rotamer(AminoAcid3.HIE, titrationUtils, 11,48.8, 10.0,
3445               89.5, 30.0);
3446           // HID
3447           aminoAcidRotamerCache[n][12] = new Rotamer(AminoAcid3.HID, titrationUtils, 12,-62.8, 10.0,
3448               -74.3, 17.2);
3449           aminoAcidRotamerCache[n][13] = new Rotamer(AminoAcid3.HID, titrationUtils, 13,-175.2, 15.4,
3450               -88.7, 43.5);
3451           aminoAcidRotamerCache[n][14] = new Rotamer(AminoAcid3.HID, titrationUtils, 14,-69.8, 5.9,
3452               96.1, 32.2);
3453           aminoAcidRotamerCache[n][15] = new Rotamer(AminoAcid3.HID, titrationUtils, 15,67.9, 17.4,
3454               -80.5, 40.7);
3455           aminoAcidRotamerCache[n][16] = new Rotamer(AminoAcid3.HID, titrationUtils, 16,-177.3, 6.3,
3456               100.5, 14.0);
3457           aminoAcidRotamerCache[n][17] = new Rotamer(AminoAcid3.HID, titrationUtils, 17,48.8, 10.0,
3458               89.5, 30.0);
3459         }
3460       }
3461       case ASH -> {
3462         if (titrationUtils == null) {
3463           aminoAcidRotamerCache[n] = new Rotamer[12];
3464           aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-68.3, 9.2, -25.7, 31.1, 0, 0);
3465           aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-169.1, 9.5, 3.9, 38.9, 0, 0);
3466           aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,63.7, 9.9, 2.4, 29.4, 0, 0);
3467           aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-68.3, 9.2, 154.3, 31.1, 0, 0);
3468           aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-169.1, 9.5, -176.1, 38.9, 0, 0);
3469           aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,63.7, 9.9, -177.6, 29.4, 0, 0);
3470           aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-68.3, 9.2, -25.7, 31.1, 180, 0);
3471           aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-169.1, 9.5, 3.9, 38.9, 180, 0);
3472           aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,63.7, 9.9, 2.4, 29.4, 180, 0);
3473           aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,-68.3, 9.2, 154.3, 31.1, 180, 0);
3474           aminoAcidRotamerCache[n][10] = new Rotamer(name, 10,-169.1, 9.5, -176.1, 38.9, 180, 0);
3475           aminoAcidRotamerCache[n][11] = new Rotamer(name, 11,63.7, 9.9, -177.6, 29.4, 180, 0);
3476 
3477         } else {
3478           // ASH Rotamers
3479           aminoAcidRotamerCache[n] = new Rotamer[15];
3480           aminoAcidRotamerCache[n][0] = new Rotamer(AminoAcid3.ASH, titrationUtils, 0, -68.3, 9.2,
3481               -25.7, 31.1, 0, 0);
3482           aminoAcidRotamerCache[n][1] = new Rotamer(AminoAcid3.ASH, titrationUtils, 1, -169.1, 9.5, 3.9,
3483               38.9, 0, 0);
3484           aminoAcidRotamerCache[n][2] = new Rotamer(AminoAcid3.ASH, titrationUtils, 2, 63.7, 9.9, 2.4,
3485               29.4, 0, 0);
3486           aminoAcidRotamerCache[n][3] = new Rotamer(AminoAcid3.ASH, titrationUtils, 3, -68.3, 9.2,
3487               154.3, 31.1, 0, 0);
3488           aminoAcidRotamerCache[n][4] = new Rotamer(AminoAcid3.ASH, titrationUtils, 4, -169.1, 9.5,
3489               -176.1, 38.9, 0, 0);
3490           aminoAcidRotamerCache[n][5] = new Rotamer(AminoAcid3.ASH, titrationUtils, 5, 63.7, 9.9,
3491               -177.6, 29.4, 0, 0);
3492           aminoAcidRotamerCache[n][6] = new Rotamer(AminoAcid3.ASH, titrationUtils, 6, -68.3, 9.2,
3493               -25.7, 31.1, 180, 0);
3494           aminoAcidRotamerCache[n][7] = new Rotamer(AminoAcid3.ASH, titrationUtils, 7, -169.1, 9.5, 3.9,
3495               38.9, 180, 0);
3496           aminoAcidRotamerCache[n][8] = new Rotamer(AminoAcid3.ASH, titrationUtils, 8, 63.7, 9.9, 2.4,
3497               29.4, 180, 0);
3498           aminoAcidRotamerCache[n][9] = new Rotamer(AminoAcid3.ASH, titrationUtils, 9, -68.3, 9.2,
3499               154.3, 31.1, 180, 0);
3500           aminoAcidRotamerCache[n][10] = new Rotamer(AminoAcid3.ASH, titrationUtils, 10, -169.1, 9.5,
3501               -176.1, 38.9, 180, 0);
3502           aminoAcidRotamerCache[n][11] = new Rotamer(AminoAcid3.ASH, titrationUtils, 11, 63.7, 9.9,
3503               -177.6, 29.4, 180, 0);
3504           // ASP Rotamers
3505           aminoAcidRotamerCache[n][12] = new Rotamer(AminoAcid3.ASP, titrationUtils, 12,-68.3, 9.2,
3506               -25.7, 31.1);
3507           aminoAcidRotamerCache[n][13] = new Rotamer(AminoAcid3.ASP, titrationUtils, 13, -169.1, 9.5,
3508               3.9, 38.9);
3509           aminoAcidRotamerCache[n][14] = new Rotamer(AminoAcid3.ASP, titrationUtils, 14, 63.7, 9.9, 2.4,
3510               29.4);
3511         }
3512       }
3513       case ASP -> {
3514         aminoAcidRotamerCache[n] = new Rotamer[3];
3515         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-68.3, 9.2, -25.7, 31.1);
3516         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-169.1, 9.5, 3.9, 38.9);
3517         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,63.7, 9.9, 2.4, 29.4);
3518       }
3519       case ASN -> {
3520         aminoAcidRotamerCache[n] = new Rotamer[6];
3521         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-68.3, 12.3, -36.8, 25.2);
3522         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-177.1, 8.8, 1.3, 34.1);
3523         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-67.2, 10.8, 128.8, 24.2);
3524         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,63.9, 3.7, -6.8, 13.5);
3525         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-174.9, 17.9, -156.8, 58.9);
3526         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,63.6, 6.6, 53.8, 17.1);
3527       }
3528       case GLU -> {
3529         aminoAcidRotamerCache[n] = new Rotamer[7];
3530         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-69.6, 19.2, -177.2, 21.7, -11.4, 44.8);
3531         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-176.2, 14.9, 175.4, 10.6, -6.7, 39.0);
3532         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-64.6, 13.5, -69.1, 17.3, -33.4, 27.4);
3533         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-55.6, 10.6, 77.0, 6.8, 25.3, 32.6);
3534         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,69.8, 10.6, -179.0, 23.7, 6.6, 64.2);
3535         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-173.6, 14.6, 70.6, 8.7, 14.0, 37.1);
3536         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,63.0, 4.3, -80.4, 13.9, 16.3, 20.8);
3537       }
3538       case GLH -> {
3539         if (titrationUtils == null) {
3540           aminoAcidRotamerCache[n] = new Rotamer[28];
3541           aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-69.6, 19.2, -177.2, 21.7, -11.4, 44.8, 0,
3542               0);
3543           aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-176.2, 14.9, 175.4, 10.6, -6.7, 39.0, 0,
3544               0);
3545           aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-64.6, 13.5, -69.1, 17.3, -33.4, 27.4, 0,
3546               0);
3547           aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-55.6, 10.6, 77.0, 6.8, 25.3, 32.6, 0, 0);
3548           aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,69.8, 10.6, -179.0, 23.7, 6.6, 64.2, 0, 0);
3549           aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-173.6, 14.6, 70.6, 8.7, 14.0, 37.1, 0, 0);
3550           aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,63.0, 4.3, -80.4, 13.9, 16.3, 20.8, 0, 0);
3551           aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-69.6, 19.2, -177.2, 21.7, 168.6, 44.8, 0,
3552               0);
3553           aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,-176.2, 14.9, 175.4, 10.6, 175.3, 39.0, 0,
3554               0);
3555           aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,-64.6, 13.5, -69.1, 17.3, 146.6, 27.4, 0,
3556               0);
3557           aminoAcidRotamerCache[n][10] = new Rotamer(name, 10,-55.6, 10.6, 77.0, 6.8, -154.7, 32.6, 0,
3558               0);
3559           aminoAcidRotamerCache[n][11] = new Rotamer(name, 11,69.8, 10.6, -179.0, 23.7, -173.4, 64.2, 0,
3560               0);
3561           aminoAcidRotamerCache[n][12] = new Rotamer(name, 12,-173.6, 14.6, 70.6, 8.7, -166.0, 37.1, 0,
3562               0);
3563           aminoAcidRotamerCache[n][13] = new Rotamer(name, 13,63.0, 4.3, -80.4, 13.9, -163.7, 20.8, 0,
3564               0);
3565           aminoAcidRotamerCache[n][14] = new Rotamer(name, 14,-69.6, 19.2, -177.2, 21.7, -11.4, 44.8,
3566               180, 0);
3567           aminoAcidRotamerCache[n][15] = new Rotamer(name, 15,-176.2, 14.9, 175.4, 10.6, -6.7, 39.0,
3568               180, 0);
3569           aminoAcidRotamerCache[n][16] = new Rotamer(name, 16,-64.6, 13.5, -69.1, 17.3, -33.4, 27.4,
3570               180, 0);
3571           aminoAcidRotamerCache[n][17] = new Rotamer(name, 17,-55.6, 10.6, 77.0, 6.8, 25.3, 32.6, 180,
3572               0);
3573           aminoAcidRotamerCache[n][18] = new Rotamer(name, 18,69.8, 10.6, -179.0, 23.7, 6.6, 64.2, 180,
3574               0);
3575           aminoAcidRotamerCache[n][19] = new Rotamer(name, 19,-173.6, 14.6, 70.6, 8.7, 14.0, 37.1, 180,
3576               0);
3577           aminoAcidRotamerCache[n][20] = new Rotamer(name, 20,63.0, 4.3, -80.4, 13.9, 16.3, 20.8, 180,
3578               0);
3579           aminoAcidRotamerCache[n][21] = new Rotamer(name, 21,-69.6, 19.2, -177.2, 21.7, 168.6, 44.8,
3580               180, 0);
3581           aminoAcidRotamerCache[n][22] = new Rotamer(name, 22,-176.2, 14.9, 175.4, 10.6, 175.3, 39.0,
3582               180, 0);
3583           aminoAcidRotamerCache[n][23] = new Rotamer(name, 23,-64.6, 13.5, -69.1, 17.3, 146.6, 27.4,
3584               180, 0);
3585           aminoAcidRotamerCache[n][24] = new Rotamer(name, 24,-55.6, 10.6, 77.0, 6.8, -154.7, 32.6, 180,
3586               0);
3587           aminoAcidRotamerCache[n][25] = new Rotamer(name, 25,69.8, 10.6, -179.0, 23.7, -173.4, 64.2,
3588               180, 0);
3589           aminoAcidRotamerCache[n][26] = new Rotamer(name, 26,-173.6, 14.6, 70.6, 8.7, -166.0, 37.1,
3590               180, 0);
3591           aminoAcidRotamerCache[n][27] = new Rotamer(name, 27,63.0, 4.3, -80.4, 13.9, -163.7, 20.8, 180,
3592               0);
3593         } else {
3594           aminoAcidRotamerCache[n] = new Rotamer[35];
3595           // GLH rotamers
3596           aminoAcidRotamerCache[n][0] = new Rotamer(AminoAcid3.GLH, titrationUtils, 0,-69.6, 19.2,
3597               -177.2, 21.7, -11.4, 44.8, 0, 0);
3598           aminoAcidRotamerCache[n][1] = new Rotamer(AminoAcid3.GLH, titrationUtils, 1,-176.2, 14.9,
3599               175.4, 10.6, -6.7, 39.0, 0, 0);
3600           aminoAcidRotamerCache[n][2] = new Rotamer(AminoAcid3.GLH, titrationUtils, 2,-64.6, 13.5,
3601               -69.1, 17.3, -33.4, 27.4, 0, 0);
3602           aminoAcidRotamerCache[n][3] = new Rotamer(AminoAcid3.GLH, titrationUtils, 3,-55.6, 10.6,
3603               77.0, 6.8, 25.3, 32.6, 0, 0);
3604           aminoAcidRotamerCache[n][4] = new Rotamer(AminoAcid3.GLH, titrationUtils, 4,69.8, 10.6,
3605               -179.0, 23.7, 6.6, 64.2, 0, 0);
3606           aminoAcidRotamerCache[n][5] = new Rotamer(AminoAcid3.GLH, titrationUtils, 5,-173.6, 14.6,
3607               70.6, 8.7, 14.0, 37.1, 0, 0);
3608           aminoAcidRotamerCache[n][6] = new Rotamer(AminoAcid3.GLH, titrationUtils, 6,63.0, 4.3, -80.4,
3609               13.9, 16.3, 20.8, 0, 0);
3610           aminoAcidRotamerCache[n][7] = new Rotamer(AminoAcid3.GLH, titrationUtils, 7,-69.6, 19.2,
3611               -177.2, 21.7, 168.6, 44.8, 0, 0);
3612           aminoAcidRotamerCache[n][8] = new Rotamer(AminoAcid3.GLH, titrationUtils, 8,-176.2, 14.9,
3613               175.4, 10.6, 175.3, 39.0, 0, 0);
3614           aminoAcidRotamerCache[n][9] = new Rotamer(AminoAcid3.GLH, titrationUtils, 9,-64.6, 13.5,
3615               -69.1, 17.3, 146.6, 27.4, 0, 0);
3616           aminoAcidRotamerCache[n][10] = new Rotamer(AminoAcid3.GLH, titrationUtils, 10,-55.6, 10.6,
3617               77.0, 6.8, -154.7, 32.6, 0, 0);
3618           aminoAcidRotamerCache[n][11] = new Rotamer(AminoAcid3.GLH, titrationUtils, 11,69.8, 10.6,
3619               -179.0, 23.7, -173.4, 64.2, 0, 0);
3620           aminoAcidRotamerCache[n][12] = new Rotamer(AminoAcid3.GLH, titrationUtils, 12,-173.6, 14.6,
3621               70.6, 8.7, -166.0, 37.1, 0, 0);
3622           aminoAcidRotamerCache[n][13] = new Rotamer(AminoAcid3.GLH, titrationUtils, 13,63.0, 4.3,
3623               -80.4, 13.9, -163.7, 20.8, 0, 0);
3624           aminoAcidRotamerCache[n][14] = new Rotamer(AminoAcid3.GLH, titrationUtils, 14,-69.6, 19.2,
3625               -177.2, 21.7, -11.4, 44.8, 180, 0);
3626           aminoAcidRotamerCache[n][15] = new Rotamer(AminoAcid3.GLH, titrationUtils, 15,-176.2, 14.9,
3627               175.4, 10.6, -6.7, 39.0, 180, 0);
3628           aminoAcidRotamerCache[n][16] = new Rotamer(AminoAcid3.GLH, titrationUtils, 16,-64.6, 13.5,
3629               -69.1, 17.3, -33.4, 27.4, 180, 0);
3630           aminoAcidRotamerCache[n][17] = new Rotamer(AminoAcid3.GLH, titrationUtils, 17,-55.6, 10.6,
3631               77.0, 6.8, 25.3, 32.6, 180, 0);
3632           aminoAcidRotamerCache[n][18] = new Rotamer(AminoAcid3.GLH, titrationUtils, 18,69.8, 10.6,
3633               -179.0, 23.7, 6.6, 64.2, 180, 0);
3634           aminoAcidRotamerCache[n][19] = new Rotamer(AminoAcid3.GLH, titrationUtils, 19,-173.6, 14.6,
3635               70.6, 8.7, 14.0, 37.1, 180, 0);
3636           aminoAcidRotamerCache[n][20] = new Rotamer(AminoAcid3.GLH, titrationUtils, 20,63.0, 4.3,
3637               -80.4, 13.9, 16.3, 20.8, 180, 0);
3638           aminoAcidRotamerCache[n][21] = new Rotamer(AminoAcid3.GLH, titrationUtils, 21,-69.6, 19.2,
3639               -177.2, 21.7, 168.6, 44.8, 180, 0);
3640           aminoAcidRotamerCache[n][22] = new Rotamer(AminoAcid3.GLH, titrationUtils, 22,-176.2, 14.9,
3641               175.4, 10.6, 175.3, 39.0, 180, 0);
3642           aminoAcidRotamerCache[n][23] = new Rotamer(AminoAcid3.GLH, titrationUtils, 23,-64.6, 13.5,
3643               -69.1, 17.3, 146.6, 27.4, 180, 0);
3644           aminoAcidRotamerCache[n][24] = new Rotamer(AminoAcid3.GLH, titrationUtils, 24,-55.6, 10.6,
3645               77.0, 6.8, -154.7, 32.6, 180, 0);
3646           aminoAcidRotamerCache[n][25] = new Rotamer(AminoAcid3.GLH, titrationUtils, 25,69.8, 10.6,
3647               -179.0, 23.7, -173.4, 64.2, 180, 0);
3648           aminoAcidRotamerCache[n][26] = new Rotamer(AminoAcid3.GLH, titrationUtils, 26,-173.6, 14.6,
3649               70.6, 8.7, -166.0, 37.1, 180, 0);
3650           aminoAcidRotamerCache[n][27] = new Rotamer(AminoAcid3.GLH, titrationUtils, 27,63.0, 4.3,
3651               -80.4, 13.9, -163.7, 20.8, 180, 0);
3652           // GLU rotamers
3653           aminoAcidRotamerCache[n][28] = new Rotamer(AminoAcid3.GLU, titrationUtils, 28,-69.6, 19.2,
3654               -177.2, 21.7, -11.4, 44.8);
3655           aminoAcidRotamerCache[n][29] = new Rotamer(AminoAcid3.GLU, titrationUtils, 29,-176.2, 14.9,
3656               175.4, 10.6, -6.7, 39.0);
3657           aminoAcidRotamerCache[n][30] = new Rotamer(AminoAcid3.GLU, titrationUtils, 30,-64.6, 13.5,
3658               -69.1, 17.3, -33.4, 27.4);
3659           aminoAcidRotamerCache[n][31] = new Rotamer(AminoAcid3.GLU, titrationUtils, 31,-55.6, 10.6,
3660               77.0, 6.8, 25.3, 32.6);
3661           aminoAcidRotamerCache[n][32] = new Rotamer(AminoAcid3.GLU, titrationUtils, 32,69.8, 10.6,
3662               -179.0, 23.7, 6.6, 64.2);
3663           aminoAcidRotamerCache[n][33] = new Rotamer(AminoAcid3.GLU, titrationUtils, 33,-173.6, 14.6,
3664               70.6, 8.7, 14.0, 37.1);
3665           aminoAcidRotamerCache[n][34] = new Rotamer(AminoAcid3.GLU, titrationUtils, 34,63.0, 4.3,
3666               -80.4, 13.9, 16.3, 20.8);
3667         }
3668       }
3669       case GLN -> {
3670         aminoAcidRotamerCache[n] = new Rotamer[10];
3671         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-66.7, 14.1, -178.5, 14.9, -24.0, 38.0);
3672         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-66.7, 14.1, -178.5, 14.9, 156.0, 38.0);
3673         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-174.6, 11.5, -177.7, 17.2, -24.0, 38.0);
3674         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-174.6, 11.5, -177.7, 17.2, 156.0, 38.0);
3675         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-58.7, 11.2, -63.8, 16.1, -46.3, 27.7);
3676         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-51.3, 7.3, -90.4, 22.8, 165.0, 38.2);
3677         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-179.4, 21.5, 67.3, 7.9, 26.8, 38.4);
3678         aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,167.5, 14.8, 70.9, 3.7, 174.2, 7.1);
3679         aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,70.8, 13.0, -165.6, 9.5, -24.0, 38.0);
3680         aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,70.8, 13.0, -165.6, 9.5, 156.0, 38.0);
3681       }
3682       case MET -> {
3683         aminoAcidRotamerCache[n] = new Rotamer[13];
3684         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-64.5, 12.7, -68.5, 6.0, -75.6, 14.1);
3685         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-78.3, 5.4, -174.7, 15.7, 65.0, 20.0);
3686         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-78.3, 5.4, -174.7, 15.7, 180.0, 20.0);
3687         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-78.3, 5.4, -174.7, 15.7, -65.0, 20.0);
3688         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,178.9, 8.7, 179.0, 13.4, 65.0, 20.0);
3689         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,178.9, 8.7, 179.0, 13.4, 65.0, 20.0);
3690         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,178.9, 8.7, 179.0, 13.4, -65.0, 20.0);
3691         aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-70.0, 21.0, -65.0, 20.0, 65.0, 20.0);
3692         aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,-170.0, 24.0, 65.0, 20.0, 180.0, 20.0);
3693         aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,-170.0, 24.0, -65.0, 20.0, 180.0, 20.0);
3694         aminoAcidRotamerCache[n][10] = new Rotamer(name, 10,-70.0, 21.0, 65.0, 20.0, 180.0, 20.0);
3695         aminoAcidRotamerCache[n][11] = new Rotamer(name, 11,-70.0, 21.0, -65.0, 20.0, 180.0, 20.0);
3696         aminoAcidRotamerCache[n][12] = new Rotamer(name, 12,61.0, 21.0, 65.0, 20.0, 180.0, 20.0);
3697       }
3698       case LYS, LYD -> {
3699         if (titrationUtils == null) {
3700           aminoAcidRotamerCache[n] = new Rotamer[12];
3701           aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,-170.0, 24.0, 180.0, 20.0, 65.0, 20.0,
3702               180.0, 20.0);
3703           aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-170.0, 24.0, 180.0, 20.0, 180.0, 20.0,
3704               65.0, 20.0);
3705           aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-170.0, 24.0, 180.0, 20.0, 180.0, 20.0,
3706               180.0, 20.0);
3707           aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-170.0, 24.0, 180.0, 20.0, -65.0, 20.0,
3708               180.0, 20.0);
3709           aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-170.0, 24.0, -65.0, 20.0, 180.0, 20.0,
3710               180.0, 20.0);
3711           aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-70.0, 21.0, 65.0, 20.0, 180.0, 20.0,
3712               180.0, 20.0);
3713           aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-70.0, 21.0, 180.0, 20.0, 65.0, 20.0,
3714               180.0, 20.0);
3715           aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-70.0, 21.0, 180.0, 20.0, 180.0, 20.0,
3716               180.0, 20.0);
3717           aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,-70.0, 21.0, 180.0, 20.0, 180.0, 20.0,
3718               -65.0, 20.0);
3719           aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,-70.0, 21.0, 180.0, 20.0, -65.0, 20.0,
3720               180.0, 20.0);
3721           aminoAcidRotamerCache[n][10] = new Rotamer(name, 10,-70.0, 21.0, -65.0, 20.0, 180.0, 20.0,
3722               180.0, 20.0);
3723           aminoAcidRotamerCache[n][11] = new Rotamer(name, 11,-70.0, 21.0, -65.0, 20.0, 180.0, 20.0,
3724               -65.0, 20.0);
3725         } else {
3726           aminoAcidRotamerCache[n] = new Rotamer[24];
3727           // LYS
3728           aminoAcidRotamerCache[n][0] = new Rotamer(AminoAcid3.LYS, titrationUtils, 0,-170.0, 24.0,
3729               180.0, 20.0, 65.0, 20.0, 180.0, 20.0);
3730           aminoAcidRotamerCache[n][1] = new Rotamer(AminoAcid3.LYS, titrationUtils, 1,-170.0, 24.0,
3731               180.0, 20.0, 180.0, 20.0, 65.0, 20.0);
3732           aminoAcidRotamerCache[n][2] = new Rotamer(AminoAcid3.LYS, titrationUtils, 2,-170.0, 24.0,
3733               180.0, 20.0, 180.0, 20.0, 180.0, 20.0);
3734           aminoAcidRotamerCache[n][3] = new Rotamer(AminoAcid3.LYS, titrationUtils, 3,-170.0, 24.0,
3735               180.0, 20.0, -65.0, 20.0, 180.0, 20.0);
3736           aminoAcidRotamerCache[n][4] = new Rotamer(AminoAcid3.LYS, titrationUtils, 4,-170.0, 24.0,
3737               -65.0, 20.0, 180.0, 20.0, 180.0, 20.0);
3738           aminoAcidRotamerCache[n][5] = new Rotamer(AminoAcid3.LYS, titrationUtils, 5,-70.0, 21.0,
3739               65.0, 20.0, 180.0, 20.0, 180.0, 20.0);
3740           aminoAcidRotamerCache[n][6] = new Rotamer(AminoAcid3.LYS, titrationUtils, 6,-70.0, 21.0,
3741               180.0, 20.0, 65.0, 20.0, 180.0, 20.0);
3742           aminoAcidRotamerCache[n][7] = new Rotamer(AminoAcid3.LYS, titrationUtils, 7,-70.0, 21.0,
3743               180.0, 20.0, 180.0, 20.0, 180.0, 20.0);
3744           aminoAcidRotamerCache[n][8] = new Rotamer(AminoAcid3.LYS, titrationUtils, 8,-70.0, 21.0,
3745               180.0, 20.0, 180.0, 20.0, -65.0, 20.0);
3746           aminoAcidRotamerCache[n][9] = new Rotamer(AminoAcid3.LYS, titrationUtils, 9,-70.0, 21.0,
3747               180.0, 20.0, -65.0, 20.0, 180.0, 20.0);
3748           aminoAcidRotamerCache[n][10] = new Rotamer(AminoAcid3.LYS, titrationUtils, 10,-70.0, 21.0,
3749               -65.0, 20.0, 180.0, 20.0, 180.0, 20.0);
3750           aminoAcidRotamerCache[n][11] = new Rotamer(AminoAcid3.LYS, titrationUtils, 11,-70.0, 21.0,
3751               -65.0, 20.0, 180.0, 20.0, -65.0, 20.0);
3752           // LYD
3753           aminoAcidRotamerCache[n][12] = new Rotamer(AminoAcid3.LYD, titrationUtils, 12,-170.0, 24.0,
3754               180.0, 20.0, 65.0, 20.0, 180.0, 20.0);
3755           aminoAcidRotamerCache[n][13] = new Rotamer(AminoAcid3.LYD, titrationUtils, 13,-170.0, 24.0,
3756               180.0, 20.0, 180.0, 20.0, 65.0, 20.0);
3757           aminoAcidRotamerCache[n][14] = new Rotamer(AminoAcid3.LYD, titrationUtils, 14,-170.0, 24.0,
3758               180.0, 20.0, 180.0, 20.0, 180.0, 20.0);
3759           aminoAcidRotamerCache[n][15] = new Rotamer(AminoAcid3.LYD, titrationUtils, 15,-170.0, 24.0,
3760               180.0, 20.0, -65.0, 20.0, 180.0, 20.0);
3761           aminoAcidRotamerCache[n][16] = new Rotamer(AminoAcid3.LYD, titrationUtils, 16,-170.0, 24.0,
3762               -65.0, 20.0, 180.0, 20.0, 180.0, 20.0);
3763           aminoAcidRotamerCache[n][17] = new Rotamer(AminoAcid3.LYD, titrationUtils, 17,-70.0, 21.0,
3764               65.0, 20.0, 180.0, 20.0, 180.0, 20.0);
3765           aminoAcidRotamerCache[n][18] = new Rotamer(AminoAcid3.LYD, titrationUtils, 18,-70.0, 21.0,
3766               180.0, 20.0, 65.0, 20.0, 180.0, 20.0);
3767           aminoAcidRotamerCache[n][19] = new Rotamer(AminoAcid3.LYD, titrationUtils, 19,-70.0, 21.0,
3768               180.0, 20.0, 180.0, 20.0, 180.0, 20.0);
3769           aminoAcidRotamerCache[n][20] = new Rotamer(AminoAcid3.LYD, titrationUtils, 20,-70.0, 21.0,
3770               180.0, 20.0, 180.0, 20.0, -65.0, 20.0);
3771           aminoAcidRotamerCache[n][21] = new Rotamer(AminoAcid3.LYD, titrationUtils, 21,-70.0, 21.0,
3772               180.0, 20.0, -65.0, 20.0, 180.0, 20.0);
3773           aminoAcidRotamerCache[n][22] = new Rotamer(AminoAcid3.LYD, titrationUtils, 22,-70.0, 21.0,
3774               -65.0, 20.0, 180.0, 20.0, 180.0, 20.0);
3775           aminoAcidRotamerCache[n][23] = new Rotamer(AminoAcid3.LYD, titrationUtils, 23,-70.0, 21.0,
3776               -65.0, 20.0, 180.0, 20.0, -65.0, 20.0);
3777         }
3778       }
3779       case ARG -> {
3780         aminoAcidRotamerCache[n] = new Rotamer[14];
3781         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,61.0, 25.0, 180.0, 20.0, 65.0, 20.0, 90.0,
3782             20.0);
3783         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,61.0, 25.0, 180.0, 20.0, 180.0, 20.0, 180.0,
3784             20.0);
3785         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-170.0, 24.0, 180.0, 20.0, 65.0, 20.0, 90.0,
3786             20.0);
3787         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-170.0, 24.0, 180.0, 20.0, 180.0, 20.0, 90.0,
3788             20.0);
3789         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-170.0, 24.0, 180.0, 20.0, 180.0, 20.0,
3790             180.0, 20.0);
3791         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-170.0, 24.0, 180.0, 20.0, 180.0, 20.0,
3792             -90.0, 20.0);
3793         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-170.0, 24.0, 180.0, 20.0, -65.0, 20.0,
3794             180.0, 20.0);
3795         aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-70.0, 21.0, 180.0, 20.0, 65.0, 20.0, 90.0,
3796             20.0);
3797         aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,-70.0, 21.0, 180.0, 20.0, 65.0, 20.0, 180.0,
3798             20.0);
3799         aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,-70.0, 21.0, 180.0, 20.0, 180.0, 20.0, 180.0,
3800             20.0);
3801         aminoAcidRotamerCache[n][10] = new Rotamer(name, 10,-70.0, 21.0, 180.0, 20.0, 180.0, 20.0,
3802             -90.0, 20.0);
3803         aminoAcidRotamerCache[n][11] = new Rotamer(name, 11,-70.0, 21.0, 180.0, 20.0, -65.0, 20.0,
3804             180.0, 20.0);
3805         aminoAcidRotamerCache[n][12] = new Rotamer(name, 12,-170.0, 21.0, 65.0, 20.0, 65.0, 20.0, 180.0,
3806             20.0);
3807         aminoAcidRotamerCache[n][13] = new Rotamer(name, 13,-70.0, 21.0, -65.0, 20.0, -65.0, 20.0,
3808             180.0, 20.0);
3809       }
3810       default -> {
3811       }
3812       // Handles GLY, ALA, CYX, ...
3813     }
3814     return aminoAcidRotamerCache[n];
3815   }
3816 
3817   /**
3818    * Fills the idealized amino acid rotamer cache with the Richardson rotamers.
3819    *
3820    * <p>Lovell, S. C.; Word, J. M.; Richardson, J. S.; Richardson, D. C., The penultimate rotamer
3821    * library. Proteins-Structure, Function, and Genetics 2000, 40 (3), 389-408.
3822    *
3823    * @param name Type of amino acid.
3824    * @return Rotamer cache (double[] of torsions).
3825    */
3826   private Rotamer[] getRichardsonRotamers(AminoAcid3 name, TitrationUtils titrationUtils) {
3827     int n = name.ordinal();
3828     if (aminoAcidRotamerCache[n] != null) {
3829       return aminoAcidRotamerCache[n];
3830     }
3831     switch (name) {
3832       case VAL -> {
3833         aminoAcidRotamerCache[n] = new Rotamer[3];
3834         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,64, 0);
3835         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1, 175, 0);
3836         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-60, 0);
3837       }
3838       case LEU -> {
3839         aminoAcidRotamerCache[n] = new Rotamer[5];
3840         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 80, 0);
3841         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-177, 0, 65, 0);
3842         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-172, 0, 145, 0);
3843         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-85, 0, 65, 0);
3844         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-65, 0, 175, 0);
3845       }
3846       case ILE -> {
3847         aminoAcidRotamerCache[n] = new Rotamer[7];
3848         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 100, 0);
3849         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,62, 0, 170, 0);
3850         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-177, 0, 66, 0);
3851         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-177, 0, 165, 0);
3852         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-65, 0, 100, 0);
3853         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-65, 0, 170, 0);
3854         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-57, 0, -60, 0);
3855       }
3856       case SER -> {
3857         aminoAcidRotamerCache[n] = new Rotamer[18];
3858         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 0, 0);
3859         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,62, 0, 60, 0);
3860         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,62, 0, 120, 0);
3861         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,62, 0, 180, 0);
3862         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,62, 0, -60, 0);
3863         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,62, 0, -120, 0);
3864         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-177, 0, 0, 0);
3865         aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-177, 0, 60, 0);
3866         aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,-177, 0, 120, 0);
3867         aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,-177, 0, 180, 0);
3868         aminoAcidRotamerCache[n][10] = new Rotamer(name, 10,-177, 0, -60, 0);
3869         aminoAcidRotamerCache[n][11] = new Rotamer(name, 11,-177, 0, -120, 0);
3870         aminoAcidRotamerCache[n][12] = new Rotamer(name, 12,-65, 0, 0, 0);
3871         aminoAcidRotamerCache[n][13] = new Rotamer(name, 13,-65, 0, 60, 0);
3872         aminoAcidRotamerCache[n][14] = new Rotamer(name, 14,-65, 0, 120, 0);
3873         aminoAcidRotamerCache[n][15] = new Rotamer(name, 15,-65, 0, 180, 0);
3874         aminoAcidRotamerCache[n][16] = new Rotamer(name, 16,-65, 0, -60, 0);
3875         aminoAcidRotamerCache[n][17] = new Rotamer(name, 17,-65, 0, -120, 0);
3876       }
3877       case THR -> {
3878         aminoAcidRotamerCache[n] = new Rotamer[18];
3879         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0, 62, 0, 0, 0);
3880         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,62, 0, 60, 0);
3881         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,62, 0, 120, 0);
3882         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,62, 0, 180, 0);
3883         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,62, 0, -60, 0);
3884         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,62, 0, -120, 0);
3885         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-175, 0, 0, 0);
3886         aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-175, 0, 60, 0);
3887         aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,-175, 0, 120, 0);
3888         aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,-175, 0, 180, 0);
3889         aminoAcidRotamerCache[n][10] = new Rotamer(name, 10,-175, 0, -60, 0);
3890         aminoAcidRotamerCache[n][11] = new Rotamer(name, 11,-175, 0, -120, 0);
3891         aminoAcidRotamerCache[n][12] = new Rotamer(name, 12,-65, 0, 0, 0);
3892         aminoAcidRotamerCache[n][13] = new Rotamer(name, 13,-65, 0, 60, 0);
3893         aminoAcidRotamerCache[n][14] = new Rotamer(name, 14,-65, 0, 120, 0);
3894         aminoAcidRotamerCache[n][15] = new Rotamer(name, 15,-65, 0, 180, 0);
3895         aminoAcidRotamerCache[n][16] = new Rotamer(name, 16,-65, 0, -60, 0);
3896         aminoAcidRotamerCache[n][17] = new Rotamer(name, 17,-65, 0, -120, 0);
3897       }
3898       case CYS, CYD -> {
3899         if (titrationUtils == null) {
3900           aminoAcidRotamerCache[n] = new Rotamer[3];
3901           aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0);
3902           aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-177, 0);
3903           aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-65, 0);
3904         } else {
3905           aminoAcidRotamerCache[n] = new Rotamer[9];
3906           aminoAcidRotamerCache[n][0] = new Rotamer(AminoAcid3.CYS, titrationUtils, 0,62, 0);
3907           aminoAcidRotamerCache[n][1] = new Rotamer(AminoAcid3.CYS, titrationUtils, 1,-177, 0);
3908           aminoAcidRotamerCache[n][2] = new Rotamer(AminoAcid3.CYS, titrationUtils, 2,-65, 0);
3909           aminoAcidRotamerCache[n][3] = new Rotamer(AminoAcid3.CYS, titrationUtils, 3,62, 180);
3910           aminoAcidRotamerCache[n][4] = new Rotamer(AminoAcid3.CYS, titrationUtils, 4,-177, 180);
3911           aminoAcidRotamerCache[n][5] = new Rotamer(AminoAcid3.CYS, titrationUtils, 5,-65, 18);
3912           aminoAcidRotamerCache[n][6] = new Rotamer(AminoAcid3.CYD, titrationUtils, 6,62, 0);
3913           aminoAcidRotamerCache[n][7] = new Rotamer(AminoAcid3.CYD, titrationUtils, 7,-177, 0);
3914           aminoAcidRotamerCache[n][8] = new Rotamer(AminoAcid3.CYD, titrationUtils, 8,-65, 0);
3915 
3916         }
3917       }
3918       case PHE, TYD -> {
3919         aminoAcidRotamerCache[n] = new Rotamer[4];
3920         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 90, 0);
3921         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,-177, 0, 80, 0);
3922         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-65, 0, -85, 0);
3923         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-65, 0, -30, 0);
3924       }
3925       case TYR -> {
3926         aminoAcidRotamerCache[n] = new Rotamer[8];
3927         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 90, 0, 0, 0);
3928         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,62, 0, 90, 0, 180, 0);
3929         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-177, 0, 80, 0, 0, 0);
3930         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-177, 0, 80, 0, 180, 0);
3931         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-65, 0, -85, 0, 0, 0);
3932         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-65, 0, -85, 0, 180, 0);
3933         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-65, 0, -30, 0, 0, 0);
3934         aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-65, 0, -30, 0, 180, 0);
3935       }
3936       case TRP -> {
3937         aminoAcidRotamerCache[n] = new Rotamer[7];
3938         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, -90, 0);
3939         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,62, 0, 90, 0);
3940         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-177, 0, -105, 0);
3941         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-177, 0, 90, 0);
3942         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-65, 0, -90, 0);
3943         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-65, 0, -5, 0);
3944         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-65, 0, 95, 0);
3945       }
3946       case HIS, HIE, HID -> {
3947         if (titrationUtils == null) {
3948           aminoAcidRotamerCache[n] = new Rotamer[8];
3949           aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, -75, 0);
3950           aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,62, 0, 80, 0);
3951           aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-177, 0, -165, 0);
3952           aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-177, 0, -80, 0);
3953           aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-177, 0, 60, 0);
3954           aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-65, 0, -70, 0);
3955           aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-65, 0, 165, 0);
3956           aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-65, 0, 80, 0);
3957         } else {
3958           // 8 Rotamers x 3 states (HIS, HIE, HID)
3959           aminoAcidRotamerCache[n] = new Rotamer[24];
3960           //HIS
3961           aminoAcidRotamerCache[n][0] = new Rotamer(AminoAcid3.HIS, titrationUtils, 0, 62, 0, -75, 0);
3962           aminoAcidRotamerCache[n][1] = new Rotamer(AminoAcid3.HIS, titrationUtils, 1, 62, 0, 80, 0);
3963           aminoAcidRotamerCache[n][2] = new Rotamer(AminoAcid3.HIS, titrationUtils, 2, -177, 0, -165,
3964               0);
3965           aminoAcidRotamerCache[n][3] = new Rotamer(AminoAcid3.HIS, titrationUtils, 3, -177, 0, -80, 0);
3966           aminoAcidRotamerCache[n][4] = new Rotamer(AminoAcid3.HIS, titrationUtils, 4, -177, 0, 60, 0);
3967           aminoAcidRotamerCache[n][5] = new Rotamer(AminoAcid3.HIS, titrationUtils, 5, -65, 0, -70, 0);
3968           aminoAcidRotamerCache[n][6] = new Rotamer(AminoAcid3.HIS, titrationUtils, 6, -65, 0, 165, 0);
3969           aminoAcidRotamerCache[n][7] = new Rotamer(AminoAcid3.HIS, titrationUtils, 7, -65, 0, 80, 0);
3970           //HID
3971           aminoAcidRotamerCache[n][8] = new Rotamer(AminoAcid3.HID, titrationUtils, 8, 62, 0, -75, 0);
3972           aminoAcidRotamerCache[n][9] = new Rotamer(AminoAcid3.HID, titrationUtils, 9, 62, 0, 80, 0);
3973           aminoAcidRotamerCache[n][10] = new Rotamer(AminoAcid3.HID, titrationUtils, 10, -177, 0, -165,
3974               0);
3975           aminoAcidRotamerCache[n][11] = new Rotamer(AminoAcid3.HID, titrationUtils, 11, -177, 0, -80,
3976               0);
3977           aminoAcidRotamerCache[n][12] = new Rotamer(AminoAcid3.HID, titrationUtils, 12, -177, 0, 60, 0);
3978           aminoAcidRotamerCache[n][13] = new Rotamer(AminoAcid3.HID, titrationUtils, 13, -65, 0, -70, 0);
3979           aminoAcidRotamerCache[n][14] = new Rotamer(AminoAcid3.HID, titrationUtils, 14,-65, 0, 165, 0);
3980           aminoAcidRotamerCache[n][15] = new Rotamer(AminoAcid3.HID, titrationUtils, 15, -65, 0, 80, 0);
3981           //HIE
3982           aminoAcidRotamerCache[n][16] = new Rotamer(AminoAcid3.HIE, titrationUtils, 16,62, 0, -75, 0);
3983           aminoAcidRotamerCache[n][17] = new Rotamer(AminoAcid3.HIE, titrationUtils, 17, 62, 0, 80, 0);
3984           aminoAcidRotamerCache[n][18] = new Rotamer(AminoAcid3.HIE, titrationUtils, 18, -177, 0, -165,
3985               0);
3986           aminoAcidRotamerCache[n][19] = new Rotamer(AminoAcid3.HIE, titrationUtils, 19, -177, 0, -80,
3987               0);
3988           aminoAcidRotamerCache[n][20] = new Rotamer(AminoAcid3.HIE, titrationUtils, 20, -177, 0, 60, 0);
3989           aminoAcidRotamerCache[n][21] = new Rotamer(AminoAcid3.HIE, titrationUtils, 21, -65, 0, -70, 0);
3990           aminoAcidRotamerCache[n][22] = new Rotamer(AminoAcid3.HIE, titrationUtils, 22, -65, 0, 165, 0);
3991           aminoAcidRotamerCache[n][23] = new Rotamer(AminoAcid3.HIE, titrationUtils, 23, -65, 0, 80, 0);
3992         }
3993       }
3994       case ASP -> {
3995         aminoAcidRotamerCache[n] = new Rotamer[5];
3996         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 10, 0);
3997         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,62, 0, 30, 0);
3998         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-177, 0, 0, 0);
3999         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-177, 0, 65, 0);
4000         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-70, 0, -15, 0);
4001       }
4002       case ASH -> {
4003         if (titrationUtils == null) {
4004           aminoAcidRotamerCache[n] = new Rotamer[20];
4005           aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 10, 0, 0, 0);
4006           aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,62, 0, 30, 0, 0, 0);
4007           aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-177, 0, 0, 0, 0, 0);
4008           aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-177, 0, 65, 0, 0, 0);
4009           aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-70, 0, -15, 0, 0, 0);
4010           aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,62, 0, -170, 0, 0, 0);
4011           aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,62, 0, -150, 0, 0, 0);
4012           aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-177, 0, -180, 0, 0, 0);
4013           aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,-177, 0, -115, 0, 0, 0);
4014           aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,-70, 0, 165, 0, 0, 0);
4015           aminoAcidRotamerCache[n][10] = new Rotamer(name, 10,62, 0, 10, 0, 180, 0);
4016           aminoAcidRotamerCache[n][11] = new Rotamer(name, 11,62, 0, 30, 0, 180, 0);
4017           aminoAcidRotamerCache[n][12] = new Rotamer(name, 12,-177, 0, 0, 0, 180, 0);
4018           aminoAcidRotamerCache[n][13] = new Rotamer(name, 13,-177, 0, 65, 0, 180, 0);
4019           aminoAcidRotamerCache[n][14] = new Rotamer(name, 14,-70, 0, -15, 0, 180, 0);
4020           aminoAcidRotamerCache[n][15] = new Rotamer(name, 15,62, 0, -170, 0, 180, 0);
4021           aminoAcidRotamerCache[n][16] = new Rotamer(name, 16,62, 0, -150, 0, 180, 0);
4022           aminoAcidRotamerCache[n][17] = new Rotamer(name, 17,-177, 0, -180, 0, 180, 0);
4023           aminoAcidRotamerCache[n][18] = new Rotamer(name, 18,-177, 0, -115, 0, 180, 0);
4024           aminoAcidRotamerCache[n][19] = new Rotamer(name, 19,-70, 0, 165, 0, 180, 0);
4025         } else {
4026           aminoAcidRotamerCache[n] = new Rotamer[25];
4027           //ASH
4028           aminoAcidRotamerCache[n][0] = new Rotamer(AminoAcid3.ASH, titrationUtils, 0, 62, 0, 10, 0, 0,
4029               0);
4030           aminoAcidRotamerCache[n][1] = new Rotamer(AminoAcid3.ASH, titrationUtils, 1, 62, 0, 30, 0, 0,
4031               0);
4032           aminoAcidRotamerCache[n][2] = new Rotamer(AminoAcid3.ASH, titrationUtils, 2, -177, 0, 0, 0, 0,
4033               0);
4034           aminoAcidRotamerCache[n][3] = new Rotamer(AminoAcid3.ASH, titrationUtils, 3, -177, 0, 65, 0,
4035               0, 0);
4036           aminoAcidRotamerCache[n][4] = new Rotamer(AminoAcid3.ASH, titrationUtils, 4, -70, 0, -15, 0,
4037               0, 0);
4038           aminoAcidRotamerCache[n][5] = new Rotamer(AminoAcid3.ASH, titrationUtils, 5, 62, 0, -170, 0,
4039               0, 0);
4040           aminoAcidRotamerCache[n][6] = new Rotamer(AminoAcid3.ASH, titrationUtils, 6, 62, 0, -150, 0,
4041               0, 0);
4042           aminoAcidRotamerCache[n][7] = new Rotamer(AminoAcid3.ASH, titrationUtils, 7, -177, 0, -180, 0,
4043               0, 0);
4044           aminoAcidRotamerCache[n][8] = new Rotamer(AminoAcid3.ASH, titrationUtils, 8, -177, 0, -115, 0,
4045               0, 0);
4046           aminoAcidRotamerCache[n][9] = new Rotamer(AminoAcid3.ASH, titrationUtils, 9, -70, 0, 165, 0,
4047               0, 0);
4048           aminoAcidRotamerCache[n][10] = new Rotamer(AminoAcid3.ASH, titrationUtils, 10, 62, 0, 10, 0,
4049               180, 0);
4050           aminoAcidRotamerCache[n][11] = new Rotamer(AminoAcid3.ASH, titrationUtils, 11, 62, 0, 30, 0,
4051               180, 0);
4052           aminoAcidRotamerCache[n][12] = new Rotamer(AminoAcid3.ASH, titrationUtils, 12, -177, 0, 0, 0,
4053               180, 0);
4054           aminoAcidRotamerCache[n][13] = new Rotamer(AminoAcid3.ASH, titrationUtils, 13, -177, 0, 65, 0,
4055               180, 0);
4056           aminoAcidRotamerCache[n][14] = new Rotamer(AminoAcid3.ASH, titrationUtils, 14, -70, 0, -15, 0,
4057               180, 0);
4058           aminoAcidRotamerCache[n][15] = new Rotamer(AminoAcid3.ASH, titrationUtils, 15, 62, 0, -170, 0,
4059               180, 0);
4060           aminoAcidRotamerCache[n][16] = new Rotamer(AminoAcid3.ASH, titrationUtils, 16, 62, 0, -150, 0,
4061               180, 0);
4062           aminoAcidRotamerCache[n][17] = new Rotamer(AminoAcid3.ASH, titrationUtils, 17, -177, 0, -180,
4063               0, 180, 0);
4064           aminoAcidRotamerCache[n][18] = new Rotamer(AminoAcid3.ASH, titrationUtils, 18, -177, 0, -115,
4065               0, 180, 0);
4066           aminoAcidRotamerCache[n][19] = new Rotamer(AminoAcid3.ASH, titrationUtils, 19, -70, 0, 165, 0,
4067               180, 0);
4068           //ASP
4069           aminoAcidRotamerCache[n][20] = new Rotamer(AminoAcid3.ASP, titrationUtils, 20,62, 0, 10, 0);
4070           aminoAcidRotamerCache[n][21] = new Rotamer(AminoAcid3.ASP, titrationUtils, 21,62, 0, 30, 0);
4071           aminoAcidRotamerCache[n][22] = new Rotamer(AminoAcid3.ASP, titrationUtils, 22, -177, 0, 0, 0);
4072           aminoAcidRotamerCache[n][23] = new Rotamer(AminoAcid3.ASP, titrationUtils, 23, -177, 0, 65, 0);
4073           aminoAcidRotamerCache[n][24] = new Rotamer(AminoAcid3.ASP, titrationUtils, 24, -70, 0, -15, 0);
4074         }
4075       }
4076       case ASN -> {
4077         aminoAcidRotamerCache[n] = new Rotamer[7];
4078         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, -10, 0);
4079         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,62, 0, 30, 0);
4080         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-174, 0, -20, 0);
4081         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-177, 0, 30, 0);
4082         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-65, 0, -20, 0);
4083         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-65, 0, -75, 0);
4084         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-65, 0, 120, 0);
4085       }
4086       case GLU -> {
4087         aminoAcidRotamerCache[n] = new Rotamer[8];
4088         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 180, 0, -20, 0);
4089         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,70, 0, -80, 0, 0, 0);
4090         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-177, 0, 65, 0, 10, 0);
4091         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-177, 0, 180, 0, 0, 0);
4092         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-177, 0, -80, 0, -25, 0);
4093         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-65, 0, 85, 0, 0, 0);
4094         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-67, 0, -180, 0, -10, 0);
4095         aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-65, 0, -65, 0, -40, 0);
4096       }
4097       case GLH -> {
4098         if (titrationUtils == null) {
4099           aminoAcidRotamerCache[n] = new Rotamer[32];
4100           aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 180, 0, -20, 0, 0, 0);
4101           aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,70, 0, -80, 0, 0, 0, 0, 0);
4102           aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-177, 0, 65, 0, 10, 0, 0, 0);
4103           aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-177, 0, 180, 0, 0, 0, 0, 0);
4104           aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-177, 0, -80, 0, -25, 0, 0, 0);
4105           aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-65, 0, 85, 0, 0, 0, 0, 0);
4106           aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-67, 0, -180, 0, -10, 0, 0, 0);
4107           aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-65, 0, -65, 0, -40, 0, 0, 0);
4108           aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,62, 0, 180, 0, 160, 0, 0, 0);
4109           aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,70, 0, -80, 0, -180, 0, 0, 0);
4110           aminoAcidRotamerCache[n][10] = new Rotamer(name, 10,-177, 0, 65, 0, -170, 0, 0, 0);
4111           aminoAcidRotamerCache[n][11] = new Rotamer(name, 11,-177, 0, 180, 0, -180, 0, 0, 0);
4112           aminoAcidRotamerCache[n][12] = new Rotamer(name, 12,-177, 0, -80, 0, 155, 0, 0, 0);
4113           aminoAcidRotamerCache[n][13] = new Rotamer(name, 13,-65, 0, 85, 0, -180, 0, 0, 0);
4114           aminoAcidRotamerCache[n][14] = new Rotamer(name, 14,-67, 0, -180, 0, 170, 0, 0, 0);
4115           aminoAcidRotamerCache[n][15] = new Rotamer(name, 15,-65, 0, -65, 0, 140, 0, 0, 0);
4116           aminoAcidRotamerCache[n][16] = new Rotamer(name, 16,62, 0, 180, 0, -20, 0, 180, 0);
4117           aminoAcidRotamerCache[n][17] = new Rotamer(name, 17,70, 0, -80, 0, 0, 0, 180, 0);
4118           aminoAcidRotamerCache[n][18] = new Rotamer(name, 18,-177, 0, 65, 0, 10, 0, 180, 0);
4119           aminoAcidRotamerCache[n][19] = new Rotamer(name, 19,-177, 0, 180, 0, 0, 0, 180, 0);
4120           aminoAcidRotamerCache[n][20] = new Rotamer(name, 20,-177, 0, -80, 0, -25, 0, 180, 0);
4121           aminoAcidRotamerCache[n][21] = new Rotamer(name, 21,-65, 0, 85, 0, 0, 0, 180, 0);
4122           aminoAcidRotamerCache[n][22] = new Rotamer(name, 22,-67, 0, -180, 0, -10, 0, 180, 0);
4123           aminoAcidRotamerCache[n][23] = new Rotamer(name, 23,-65, 0, -65, 0, -40, 0, 180, 0);
4124           aminoAcidRotamerCache[n][24] = new Rotamer(name, 24,62, 0, 180, 0, 160, 0, 180, 0);
4125           aminoAcidRotamerCache[n][25] = new Rotamer(name, 25,70, 0, -80, 0, -180, 0, 180, 0);
4126           aminoAcidRotamerCache[n][26] = new Rotamer(name, 26,-177, 0, 65, 0, -170, 0, 180, 0);
4127           aminoAcidRotamerCache[n][27] = new Rotamer(name, 27,-177, 0, 180, 0, -180, 0, 180, 0);
4128           aminoAcidRotamerCache[n][28] = new Rotamer(name, 28,-177, 0, -80, 0, 155, 0, 180, 0);
4129           aminoAcidRotamerCache[n][29] = new Rotamer(name, 29,-65, 0, 85, 0, -180, 0, 180, 0);
4130           aminoAcidRotamerCache[n][30] = new Rotamer(name, 30,-67, 0, -180, 0, 170, 0, 180, 0);
4131           aminoAcidRotamerCache[n][31] = new Rotamer(name, 31,-65, 0, -65, 0, 140, 0, 180, 0);
4132         } else {
4133           aminoAcidRotamerCache[n] = new Rotamer[40];
4134           //GLH
4135           aminoAcidRotamerCache[n][0] = new Rotamer(AminoAcid3.GLH, titrationUtils, 0,62, 0, 180, 0,
4136               -20, 0, 0, 0);
4137           aminoAcidRotamerCache[n][1] = new Rotamer(AminoAcid3.GLH, titrationUtils, 1,70, 0, -80, 0, 0,
4138               0, 0, 0);
4139           aminoAcidRotamerCache[n][2] = new Rotamer(AminoAcid3.GLH, titrationUtils, 2,-177, 0, 65, 0,
4140               10, 0, 0, 0);
4141           aminoAcidRotamerCache[n][3] = new Rotamer(AminoAcid3.GLH, titrationUtils, 3,-177, 0, 180, 0,
4142               0, 0, 0, 0);
4143           aminoAcidRotamerCache[n][4] = new Rotamer(AminoAcid3.GLH, titrationUtils, 4,-177, 0, -80, 0,
4144               -25, 0, 0, 0);
4145           aminoAcidRotamerCache[n][5] = new Rotamer(AminoAcid3.GLH, titrationUtils, 5,-65, 0, 85, 0, 0,
4146               0, 0, 0);
4147           aminoAcidRotamerCache[n][6] = new Rotamer(AminoAcid3.GLH, titrationUtils, 6,-67, 0, -180, 0,
4148               -10, 0, 0, 0);
4149           aminoAcidRotamerCache[n][7] = new Rotamer(AminoAcid3.GLH, titrationUtils, 7,-65, 0, -65, 0,
4150               -40, 0, 0, 0);
4151           aminoAcidRotamerCache[n][8] = new Rotamer(AminoAcid3.GLH, titrationUtils, 8,62, 0, 180, 0,
4152               160, 0, 0, 0);
4153           aminoAcidRotamerCache[n][9] = new Rotamer(AminoAcid3.GLH, titrationUtils, 9,70, 0, -80, 0,
4154               -180, 0, 0, 0);
4155           aminoAcidRotamerCache[n][10] = new Rotamer(AminoAcid3.GLH, titrationUtils, 10,-177, 0, 65, 0,
4156               -170, 0, 0, 0);
4157           aminoAcidRotamerCache[n][11] = new Rotamer(AminoAcid3.GLH, titrationUtils, 11,-177, 0, 180, 0,
4158               -180, 0, 0, 0);
4159           aminoAcidRotamerCache[n][12] = new Rotamer(AminoAcid3.GLH, titrationUtils, 12,-177, 0, -80, 0,
4160               155, 0, 0, 0);
4161           aminoAcidRotamerCache[n][13] = new Rotamer(AminoAcid3.GLH, titrationUtils, 13,-65, 0, 85, 0,
4162               -180, 0, 0, 0);
4163           aminoAcidRotamerCache[n][14] = new Rotamer(AminoAcid3.GLH, titrationUtils, 14,-67, 0, -180, 0,
4164               170, 0, 0, 0);
4165           aminoAcidRotamerCache[n][15] = new Rotamer(AminoAcid3.GLH, titrationUtils, 15,-65, 0, -65, 0,
4166               140, 0, 0, 0);
4167           aminoAcidRotamerCache[n][16] = new Rotamer(AminoAcid3.GLH, titrationUtils, 16,62, 0, 180, 0,
4168               -20, 0, 180, 0);
4169           aminoAcidRotamerCache[n][17] = new Rotamer(AminoAcid3.GLH, titrationUtils, 17,70, 0, -80, 0,
4170               0, 0, 180, 0);
4171           aminoAcidRotamerCache[n][18] = new Rotamer(AminoAcid3.GLH, titrationUtils, 18,-177, 0, 65, 0,
4172               10, 0, 180, 0);
4173           aminoAcidRotamerCache[n][19] = new Rotamer(AminoAcid3.GLH, titrationUtils, 19,-177, 0, 180, 0,
4174               0, 0, 180, 0);
4175           aminoAcidRotamerCache[n][20] = new Rotamer(AminoAcid3.GLH, titrationUtils, 20,-177, 0, -80, 0,
4176               -25, 0, 180, 0);
4177           aminoAcidRotamerCache[n][21] = new Rotamer(AminoAcid3.GLH, titrationUtils, 21,-65, 0, 85, 0,
4178               0, 0, 180, 0);
4179           aminoAcidRotamerCache[n][22] = new Rotamer(AminoAcid3.GLH, titrationUtils, 22,-67, 0, -180, 0,
4180               -10, 0, 180, 0);
4181           aminoAcidRotamerCache[n][23] = new Rotamer(AminoAcid3.GLH, titrationUtils, 23,-65, 0, -65, 0,
4182               -40, 0, 180, 0);
4183           aminoAcidRotamerCache[n][24] = new Rotamer(AminoAcid3.GLH, titrationUtils, 24, 62, 0, 180, 0,
4184               160, 0, 180, 0);
4185           aminoAcidRotamerCache[n][25] = new Rotamer(AminoAcid3.GLH, titrationUtils, 25,70, 0, -80, 0,
4186               -180, 0, 180, 0);
4187           aminoAcidRotamerCache[n][26] = new Rotamer(AminoAcid3.GLH, titrationUtils, 26,-177, 0, 65, 0,
4188               -170, 0, 180, 0);
4189           aminoAcidRotamerCache[n][27] = new Rotamer(AminoAcid3.GLH, titrationUtils, 27,-177, 0, 180, 0,
4190               -180, 0, 180, 0);
4191           aminoAcidRotamerCache[n][28] = new Rotamer(AminoAcid3.GLH, titrationUtils, 28,-177, 0, -80, 0,
4192               155, 0, 180, 0);
4193           aminoAcidRotamerCache[n][29] = new Rotamer(AminoAcid3.GLH, titrationUtils, 29,-65, 0, 85, 0,
4194               -180, 0, 180, 0);
4195           aminoAcidRotamerCache[n][30] = new Rotamer(AminoAcid3.GLH, titrationUtils, 30,-67, 0, -180, 0,
4196               170, 0, 180, 0);
4197           aminoAcidRotamerCache[n][31] = new Rotamer(AminoAcid3.GLH, titrationUtils, 31,-65, 0, -65, 0,
4198               140, 0, 180, 0);
4199           //GLU
4200           aminoAcidRotamerCache[n][32] = new Rotamer(AminoAcid3.GLU, titrationUtils, 32, 62, 0, 180, 0,
4201               -20, 0);
4202           aminoAcidRotamerCache[n][33] = new Rotamer(AminoAcid3.GLU, titrationUtils, 33,70, 0, -80, 0,
4203               0, 0);
4204           aminoAcidRotamerCache[n][34] = new Rotamer(AminoAcid3.GLU, titrationUtils, 34,-177, 0, 65, 0,
4205               10, 0);
4206           aminoAcidRotamerCache[n][35] = new Rotamer(AminoAcid3.GLU, titrationUtils, 35,-177, 0, 180, 0,
4207               0, 0);
4208           aminoAcidRotamerCache[n][36] = new Rotamer(AminoAcid3.GLU, titrationUtils, 36,-177, 0, -80, 0,
4209               -25, 0);
4210           aminoAcidRotamerCache[n][37] = new Rotamer(AminoAcid3.GLU, titrationUtils, 37,-65, 0, 85, 0,
4211               0, 0);
4212           aminoAcidRotamerCache[n][38] = new Rotamer(AminoAcid3.GLU, titrationUtils, 38,-67, 0, -180, 0,
4213               -10, 0);
4214           aminoAcidRotamerCache[n][39] = new Rotamer(AminoAcid3.GLU, titrationUtils, 39,-65, 0, -65, 0,
4215               -40, 0);
4216         }
4217       }
4218       case GLN -> {
4219         aminoAcidRotamerCache[n] = new Rotamer[9];
4220         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 180, 0, 20, 0);
4221         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,70, 0, -75, 0, 0, 0);
4222         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-177, 0, 65, 0, -100, 0);
4223         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-177, 0, 65, 0, 60, 0);
4224         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-177, 0, 180, 0, 0, 0);
4225         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-65, 0, 85, 0, 0, 0);
4226         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-67, 0, 180, 0, -25, 0);
4227         aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-65, 0, -65, 0, -40, 0);
4228         aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,-65, 0, -65, 0, 100, 0);
4229       }
4230       case MET -> {
4231         aminoAcidRotamerCache[n] = new Rotamer[13];
4232         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 180, 0, 75, 0);
4233         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,62, 0, 180, 0, -75, 0);
4234         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,-177, 0, 65, 0, 75, 0);
4235         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,-177, 0, 65, 0, 180, 0);
4236         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,-177, 0, 180, 0, 75, 0);
4237         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-177, 0, 180, 0, 180, 0);
4238         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-177, 0, 180, 0, -75, 0);
4239         aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-67, 0, 180, 0, 75, 0);
4240         aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,-67, 0, 180, 0, 180, 0);
4241         aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,-67, 0, 180, 0, -75, 0);
4242         aminoAcidRotamerCache[n][10] = new Rotamer(name, 10,-65, 0, -65, 0, 103, 0);
4243         aminoAcidRotamerCache[n][11] = new Rotamer(name, 11,-65, 0, -65, 0, 180, 0);
4244         aminoAcidRotamerCache[n][12] = new Rotamer(name, 12,-65, 0, -65, 0, -70, 0);
4245       }
4246       case LYS, LYD -> {
4247         if (titrationUtils == null) {
4248           aminoAcidRotamerCache[n] = new Rotamer[27];
4249           aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 180, 0, 68, 0, 180, 0);
4250           aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,62, 0, 180, 0, 180, 0, 65.0, 0);
4251           aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,62, 0, 180, 0, 180, 0, 180, 0);
4252           aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,62, 0, 180, 0, 180, 0, -65, 0);
4253           aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,62, 0, 180, 0, -68, 0, 180, 0);
4254           aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,-177, 0, 68, 0, 180, 0, 65, 0);
4255           aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,-177, 0, 68, 0, 180, 0, 180, 0);
4256           aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-177, 0, 68, 0, 180, 0, -65, 0);
4257           aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,-177, 0, 180, 0, 68, 0, 65, 0);
4258           aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,-177, 0, 180, 0, 68, 0, 180, 0);
4259           aminoAcidRotamerCache[n][10] = new Rotamer(name, 10,-177, 0, 180, 0, 180, 0, 65, 0);
4260           aminoAcidRotamerCache[n][11] = new Rotamer(name, 11,-177, 0, 180, 0, 180, 0, 180, 0);
4261           aminoAcidRotamerCache[n][12] = new Rotamer(name, 12,-177, 0, 180, 0, 180, 0, -65, 0);
4262           aminoAcidRotamerCache[n][13] = new Rotamer(name, 13,-177, 0, 180, 0, -68, 0, 180, 0);
4263           aminoAcidRotamerCache[n][14] = new Rotamer(name, 14,-177, 0, 180, 0, -68, 0, -65, 0);
4264           aminoAcidRotamerCache[n][15] = new Rotamer(name, 15,-90, 0, 68, 0, 180, 0, 180);
4265           aminoAcidRotamerCache[n][16] = new Rotamer(name, 16, -67, 0, 180, 0, 68, 0, -65, 0);
4266           aminoAcidRotamerCache[n][17] = new Rotamer(name, 17,-67, 0, 180, 0, 68, 0, 180, 0);
4267           aminoAcidRotamerCache[n][18] = new Rotamer(name, 18,-67, 0, 180, 0, 180, 0, 65, 0);
4268           aminoAcidRotamerCache[n][19] = new Rotamer(name, 19,-67, 0, 180, 0, 180, 0, 180, 0);
4269           aminoAcidRotamerCache[n][20] = new Rotamer(name, 20,-67, 0, 180, 0, 180, 0, -65, 0);
4270           aminoAcidRotamerCache[n][21] = new Rotamer(name, 21,-67, 0, 180, 0, -68, 0, 180, 0);
4271           aminoAcidRotamerCache[n][22] = new Rotamer(name, 22,-67, 0, 180, 0, -68, 0, -65, 0);
4272           aminoAcidRotamerCache[n][23] = new Rotamer(name, 23,-62, 0, -68, 0, 180, 0, 65, 0);
4273           aminoAcidRotamerCache[n][24] = new Rotamer(name, 24,-62, 0, -68, 0, 180, 0, 180, 0);
4274           aminoAcidRotamerCache[n][25] = new Rotamer(name, 25,-62, 0, -68, 0, 180, 0, -65, 0);
4275           aminoAcidRotamerCache[n][26] = new Rotamer(name, 26,-62, 0, -68, 0, -68, 0, 180, 0);
4276         } else {
4277           // 27 Rotamers X 2 states (LYD, LYS)
4278           aminoAcidRotamerCache[n] = new Rotamer[54];
4279           //LYD
4280           aminoAcidRotamerCache[n][0] = new Rotamer(AminoAcid3.LYD, titrationUtils, 0,62, 0, 180, 0,
4281               68, 0, 180, 0);
4282           aminoAcidRotamerCache[n][1] = new Rotamer(AminoAcid3.LYD, titrationUtils, 1,62, 0, 180, 0,
4283               180, 0, 65.0, 0);
4284           aminoAcidRotamerCache[n][2] = new Rotamer(AminoAcid3.LYD, titrationUtils, 2,62, 0, 180, 0,
4285               180, 0, 180, 0);
4286           aminoAcidRotamerCache[n][3] = new Rotamer(AminoAcid3.LYD, titrationUtils, 3,62, 0, 180, 0,
4287               180, 0, -65, 0);
4288           aminoAcidRotamerCache[n][4] = new Rotamer(AminoAcid3.LYD, titrationUtils, 4,62, 0, 180, 0,
4289               -68, 0, 180, 0);
4290           aminoAcidRotamerCache[n][5] = new Rotamer(AminoAcid3.LYD, titrationUtils, 5,-177, 0, 68, 0,
4291               180, 0, 65, 0);
4292           aminoAcidRotamerCache[n][6] = new Rotamer(AminoAcid3.LYD, titrationUtils, 6,-177, 0, 68, 0,
4293               180, 0, 180, 0);
4294           aminoAcidRotamerCache[n][7] = new Rotamer(AminoAcid3.LYD, titrationUtils, 7,-177, 0, 68, 0,
4295               180, 0, -65, 0);
4296           aminoAcidRotamerCache[n][8] = new Rotamer(AminoAcid3.LYD, titrationUtils, 8,-177, 0, 180, 0,
4297               68, 0, 65, 0);
4298           aminoAcidRotamerCache[n][9] = new Rotamer(AminoAcid3.LYD, titrationUtils, 9,-177, 0, 180, 0,
4299               68, 0, 180, 0);
4300           aminoAcidRotamerCache[n][10] = new Rotamer(AminoAcid3.LYD, titrationUtils, 10,-177, 0, 180, 0,
4301               180, 0, 65, 0);
4302           aminoAcidRotamerCache[n][11] = new Rotamer(AminoAcid3.LYD, titrationUtils, 11,-177, 0, 180, 0,
4303               180, 0, 180, 0);
4304           aminoAcidRotamerCache[n][12] = new Rotamer(AminoAcid3.LYD, titrationUtils, 12,-177, 0, 180, 0,
4305               180, 0, -65, 0);
4306           aminoAcidRotamerCache[n][13] = new Rotamer(AminoAcid3.LYD, titrationUtils, 13,-177, 0, 180, 0,
4307               -68, 0, 180, 0);
4308           aminoAcidRotamerCache[n][14] = new Rotamer(AminoAcid3.LYD, titrationUtils, 14,-177, 0, 180, 0,
4309               -68, 0, -65, 0);
4310           aminoAcidRotamerCache[n][15] = new Rotamer(AminoAcid3.LYD, titrationUtils, 15,-90, 0, 68, 0,
4311               180, 0, 180);
4312           aminoAcidRotamerCache[n][16] = new Rotamer(AminoAcid3.LYD, titrationUtils, 16,-67, 0, 180, 0,
4313               68, 0, -65, 0);
4314           aminoAcidRotamerCache[n][17] = new Rotamer(AminoAcid3.LYD, titrationUtils, 17,-67, 0, 180, 0,
4315               68, 0, 180, 0);
4316           aminoAcidRotamerCache[n][18] = new Rotamer(AminoAcid3.LYD, titrationUtils, 18,-67, 0, 180, 0,
4317               180, 0, 65, 0);
4318           aminoAcidRotamerCache[n][19] = new Rotamer(AminoAcid3.LYD, titrationUtils, 19,-67, 0, 180, 0,
4319               180, 0, 180, 0);
4320           aminoAcidRotamerCache[n][20] = new Rotamer(AminoAcid3.LYD, titrationUtils, 20,-67, 0, 180, 0,
4321               180, 0, -65, 0);
4322           aminoAcidRotamerCache[n][21] = new Rotamer(AminoAcid3.LYD, titrationUtils, 21,-67, 0, 180, 0,
4323               -68, 0, 180, 0);
4324           aminoAcidRotamerCache[n][22] = new Rotamer(AminoAcid3.LYD, titrationUtils, 22,-67, 0, 180, 0,
4325               -68, 0, -65, 0);
4326           aminoAcidRotamerCache[n][23] = new Rotamer(AminoAcid3.LYD, titrationUtils, 23,-62, 0, -68, 0,
4327               180, 0, 65, 0);
4328           aminoAcidRotamerCache[n][24] = new Rotamer(AminoAcid3.LYD, titrationUtils, 24,-62, 0, -68, 0,
4329               180, 0, 180, 0);
4330           aminoAcidRotamerCache[n][25] = new Rotamer(AminoAcid3.LYD, titrationUtils, 25,-62, 0, -68, 0,
4331               180, 0, -65, 0);
4332           aminoAcidRotamerCache[n][26] = new Rotamer(AminoAcid3.LYD, titrationUtils, 26,-62, 0, -68, 0,
4333               -68, 0, 180, 0);
4334           //LYS
4335           aminoAcidRotamerCache[n][27] = new Rotamer(AminoAcid3.LYS, titrationUtils, 27,62, 0, 180, 0,
4336               68, 0, 180, 0);
4337           aminoAcidRotamerCache[n][28] = new Rotamer(AminoAcid3.LYS, titrationUtils, 28,62, 0, 180, 0,
4338               180, 0, 65.0, 0);
4339           aminoAcidRotamerCache[n][29] = new Rotamer(AminoAcid3.LYS, titrationUtils, 29,62, 0, 180, 0,
4340               180, 0, 180, 0);
4341           aminoAcidRotamerCache[n][30] = new Rotamer(AminoAcid3.LYS, titrationUtils, 30,62, 0, 180, 0,
4342               180, 0, -65, 0);
4343           aminoAcidRotamerCache[n][31] = new Rotamer(AminoAcid3.LYS, titrationUtils, 31,62, 0, 180, 0,
4344               -68, 0, 180, 0);
4345           aminoAcidRotamerCache[n][32] = new Rotamer(AminoAcid3.LYS, titrationUtils, 32,-177, 0, 68, 0,
4346               180, 0, 65, 0);
4347           aminoAcidRotamerCache[n][33] = new Rotamer(AminoAcid3.LYS, titrationUtils, 33,-177, 0, 68, 0,
4348               180, 0, 180, 0);
4349           aminoAcidRotamerCache[n][34] = new Rotamer(AminoAcid3.LYS, titrationUtils, 34,-177, 0, 68, 0,
4350               180, 0, -65, 0);
4351           aminoAcidRotamerCache[n][35] = new Rotamer(AminoAcid3.LYS, titrationUtils, 35,-177, 0, 180, 0,
4352               68, 0, 65, 0);
4353           aminoAcidRotamerCache[n][36] = new Rotamer(AminoAcid3.LYS, titrationUtils, 36,-177, 0, 180, 0,
4354               68, 0, 180, 0);
4355           aminoAcidRotamerCache[n][37] = new Rotamer(AminoAcid3.LYS, titrationUtils, 37,-177, 0, 180, 0,
4356               180, 0, 65, 0);
4357           aminoAcidRotamerCache[n][38] = new Rotamer(AminoAcid3.LYS, titrationUtils, 38,-177, 0, 180, 0,
4358               180, 0, 180, 0);
4359           aminoAcidRotamerCache[n][39] = new Rotamer(AminoAcid3.LYS, titrationUtils, 39,-177, 0, 180, 0,
4360               180, 0, -65, 0);
4361           aminoAcidRotamerCache[n][40] = new Rotamer(AminoAcid3.LYS, titrationUtils, 40,-177, 0, 180, 0,
4362               -68, 0, 180, 0);
4363           aminoAcidRotamerCache[n][41] = new Rotamer(AminoAcid3.LYS, titrationUtils, 41,-177, 0, 180, 0,
4364               -68, 0, -65, 0);
4365           aminoAcidRotamerCache[n][42] = new Rotamer(AminoAcid3.LYS, titrationUtils, 42,-90, 0, 68, 0,
4366               180, 0, 180);
4367           aminoAcidRotamerCache[n][43] = new Rotamer(AminoAcid3.LYS, titrationUtils, 43,-67, 0, 180, 0,
4368               68, 0, -65, 0);
4369           aminoAcidRotamerCache[n][44] = new Rotamer(AminoAcid3.LYS, titrationUtils, 44,-67, 0, 180, 0,
4370               68, 0, 180, 0);
4371           aminoAcidRotamerCache[n][45] = new Rotamer(AminoAcid3.LYS, titrationUtils, 45,-67, 0, 180, 0,
4372               180, 0, 65, 0);
4373           aminoAcidRotamerCache[n][46] = new Rotamer(AminoAcid3.LYS, titrationUtils, 46,-67, 0, 180, 0,
4374               180, 0, 180, 0);
4375           aminoAcidRotamerCache[n][47] = new Rotamer(AminoAcid3.LYS, titrationUtils, 47,-67, 0, 180, 0,
4376               180, 0, -65, 0);
4377           aminoAcidRotamerCache[n][48] = new Rotamer(AminoAcid3.LYS, titrationUtils, 48,-67, 0, 180, 0,
4378               -68, 0, 180, 0);
4379           aminoAcidRotamerCache[n][49] = new Rotamer(AminoAcid3.LYS, titrationUtils, 49,-67, 0, 180, 0,
4380               -68, 0, -65, 0);
4381           aminoAcidRotamerCache[n][50] = new Rotamer(AminoAcid3.LYS, titrationUtils, 50,-62, 0, -68, 0,
4382               180, 0, 65, 0);
4383           aminoAcidRotamerCache[n][51] = new Rotamer(AminoAcid3.LYS, titrationUtils, 51,-62, 0, -68, 0,
4384               180, 0, 180, 0);
4385           aminoAcidRotamerCache[n][52] = new Rotamer(AminoAcid3.LYS, titrationUtils, 52,-62, 0, -68, 0,
4386               180, 0, -65, 0);
4387           aminoAcidRotamerCache[n][53] = new Rotamer(AminoAcid3.LYS, titrationUtils, 53,-62, 0, -68, 0,
4388               -68, 0, 180, 0);
4389         }
4390       }
4391       case ARG -> {
4392         aminoAcidRotamerCache[n] = new Rotamer[34];
4393         aminoAcidRotamerCache[n][0] = new Rotamer(name, 0,62, 0, 180, 0, 65, 0, 85, 0);
4394         aminoAcidRotamerCache[n][1] = new Rotamer(name, 1,62, 0, 180, 0, 65, 0, -175, 0);
4395         aminoAcidRotamerCache[n][2] = new Rotamer(name, 2,62, 0, 180, 0, 180, 0, 85, 0);
4396         aminoAcidRotamerCache[n][3] = new Rotamer(name, 3,62, 0, 180, 0, 180, 0, 180, 0);
4397         aminoAcidRotamerCache[n][4] = new Rotamer(name, 4,62, 0, 180, 0, 180, 0, -85, 0);
4398         aminoAcidRotamerCache[n][5] = new Rotamer(name, 5,62, 0, 180, 0, -65, 0, 175, 0);
4399         aminoAcidRotamerCache[n][6] = new Rotamer(name, 6,62, 0, 180, 0, -65, 0, -85, 0);
4400         aminoAcidRotamerCache[n][7] = new Rotamer(name, 7,-177, 0, 65, 0, 65, 0, 85, 0);
4401         aminoAcidRotamerCache[n][8] = new Rotamer(name, 8,-177, 0, 65, 0, 65, 0, -175, 0);
4402         aminoAcidRotamerCache[n][9] = new Rotamer(name, 9,-177, 0, 65, 0, 180, 0, 85, 0);
4403         aminoAcidRotamerCache[n][10] = new Rotamer(name, 10,-177, 0, 65, 0, 180, 0, 180, 0);
4404         aminoAcidRotamerCache[n][11] = new Rotamer(name, 11,-177, 0, 180, 0, 65, 0, 85, 0);
4405         aminoAcidRotamerCache[n][12] = new Rotamer(name, 12,-177, 0, 180, 0, 65, 0, -175, 0);
4406         aminoAcidRotamerCache[n][13] = new Rotamer(name, 13,-177, 0, 180, 0, 65, 0, -105, 0);
4407         aminoAcidRotamerCache[n][14] = new Rotamer(name, 14,-177, 0, 180, 0, 180, 0, 85, 0);
4408         aminoAcidRotamerCache[n][15] = new Rotamer(name, 15,-177, 0, 180, 0, 180, 0, 180, 0);
4409         aminoAcidRotamerCache[n][16] = new Rotamer(name, 16,-177, 0, 180, 0, 180, 0, -85, 0);
4410         aminoAcidRotamerCache[n][17] = new Rotamer(name, 17,-177, 0, 180, 0, -65, 0, 105, 0);
4411         aminoAcidRotamerCache[n][18] = new Rotamer(name, 18,-177, 0, 180, 0, -65, 0, 175, 0);
4412         aminoAcidRotamerCache[n][19] = new Rotamer(name, 19,-177, 0, 180, 0, -65, 0, -85, 0);
4413         aminoAcidRotamerCache[n][20] = new Rotamer(name, 20,-67, 0, 180, 0, 65, 0, 85, 0);
4414         aminoAcidRotamerCache[n][21] = new Rotamer(name, 21,-67, 0, 180, 0, 65, 0, -175, 0);
4415         aminoAcidRotamerCache[n][22] = new Rotamer(name, 22,-67, 0, 180, 0, 65, 0, -105, 0);
4416         aminoAcidRotamerCache[n][23] = new Rotamer(name, 23,-67, 0, 180, 0, 180, 0, 85, 0);
4417         aminoAcidRotamerCache[n][24] = new Rotamer(name, 24,-67, 0, 180, 0, 180, 0, 180, 0);
4418         aminoAcidRotamerCache[n][25] = new Rotamer(name, 25,-67, 0, 180, 0, 180, 0, -85, 0);
4419         aminoAcidRotamerCache[n][26] = new Rotamer(name, 26,-67, 0, 180, 0, -65, 0, 105, 0);
4420         aminoAcidRotamerCache[n][27] = new Rotamer(name, 27,-67, 0, 180, 0, -65, 0, 175, 0);
4421         aminoAcidRotamerCache[n][28] = new Rotamer(name, 28,-67, 0, -167, 0, -65, 0, -85, 0);
4422         aminoAcidRotamerCache[n][29] = new Rotamer(name,29, -62, 0, -68, 0, 180, 0, 85, 0);
4423         aminoAcidRotamerCache[n][30] = new Rotamer(name, 30,-62, 0, -68, 0, 180, 0, 180, 0);
4424         aminoAcidRotamerCache[n][31] = new Rotamer(name, 31,-62, 0, -68, 0, 180, 0, -85, 0);
4425         aminoAcidRotamerCache[n][32] = new Rotamer(name, 32,-62, 0, -68, 0, -65, 0, 175, 0);
4426         aminoAcidRotamerCache[n][33] = new Rotamer(name, 33,-62, 0, -68, 0, -65, 0, -85, 0);
4427       }
4428       default -> {
4429       }
4430       // Handles GLY, ALA, CYX, PRO, ...
4431     }
4432     return aminoAcidRotamerCache[n];
4433   }
4434 
4435   /**
4436    * Returns the Rotamers for a specified nucleic acid type. Torsion angles are listed from delta
4437    * (i-1) to delta (i), along with standard deviations calculated by Richardson et al.
4438    *
4439    * <p>Citation: Richardson, J.S., et al., RNA backbone: Consensus all-angle conformers and modular
4440    * string nomenclature (an RNA Ontology Consortium contribution). Rna-a Publication of the Rna
4441    * Society, 2008. 14(3): p. 465-481.
4442    *
4443    * @param name Type of nucleic acid.
4444    * @return Rotamer cache (double[] of torsions).
4445    */
4446   private Rotamer[] getRichardsonRNARotamers(NucleicAcid3 name) {
4447     int n = name.ordinal();
4448     if (nucleicAcidRotamerCache[n] != null) {
4449       return nucleicAcidRotamerCache[n];
4450     }
4451     /*
4452      * Comments on rotamers can be found in Richardson et al., 2008.
4453      * Rotamers 0-45 are these rotamers in order.
4454      *
4455      * In the future, subsequent sets of 46 could be these rotamers with
4456      * rotations of either the base as a whole, or portions of the base
4457      * (such as the C6 amino group of adenine).  One suggestion is to use
4458      * %46 if only the backbone information is needed, or integer division
4459      * by 46 if the backbone information is unnecessary.
4460      *
4461      * Torsions are in order delta (i-1), epsilon (i-1), zeta (i-1), alpha,
4462      * beta, gamma, delta.  However, at this moment, only delta through alpha
4463      * (reverse order) are used to build the backbone, and delta (i-1) as
4464      * a binary function to determine what previous sugar pucker to expect.
4465      *
4466      * 1a, 1m, 1L, &a, 7a, 3a, 9a, 1g, 7d, 3d, 5d, 1e, 1c, 1f, 5j, 1b, 1{,
4467      * 3b, 1z, 5z, 7p, 1t, 5q, 1o, 7r, 2a, 4a, 0a, #a, 4g, 6g, 8d, 4d, 6d,
4468      * 2h, 4n, 0i, 6n, 6j, 2[, 4b, 0b, 4p, 6p, 4s, 2o
4469      */
4470     switch (name) {
4471       case ADE, GUA, CYT, URI, DAD, DGU, DCY, DTY, THY -> {
4472         nucleicAcidRotamerCache[n] = new Rotamer[46];
4473         nucleicAcidRotamerCache[n][0] = new Rotamer(name, 81, 4, -148, 10, -71, 7, -65, 8, 174, 8,
4474             54, 6, 81, 3);
4475         nucleicAcidRotamerCache[n][1] = new Rotamer(name, 84, 5, -142, 16, -68, 15, -68, 16, -138,
4476             12, 58, 10, 86, 7);
4477         nucleicAcidRotamerCache[n][2] = new Rotamer(name, 86, 4, -115, 6, -92, 13, -56, 8, 138, 4,
4478             62, 10, 79, 5);
4479         nucleicAcidRotamerCache[n][3] = new Rotamer(name, 82, 5, -169, 7, -95, 6, -64, 9, -178, 10,
4480             51, 7, 82, 5);
4481         nucleicAcidRotamerCache[n][4] = new Rotamer(name, 83, 4, -143, 23, -138, 14, -57, 9, 161, 15,
4482             49, 6, 82, 3);
4483         nucleicAcidRotamerCache[n][5] = new Rotamer(name, 85, 4, -144, 24, 173, 14, -71, 12, 164, 16,
4484             46, 7, 85, 6); //
4485         nucleicAcidRotamerCache[n][6] = new Rotamer(name, 83, 2, -150, 15, 121, 13, -71, 12, 157, 23,
4486             49, 6, 81, 3);
4487         nucleicAcidRotamerCache[n][7] = new Rotamer(name, 81, 3, -141, 8, -69, 9, 167, 8, 160, 16,
4488             51, 5, 85, 3);
4489         nucleicAcidRotamerCache[n][8] = new Rotamer(name, 84, 4, -121, 16, -103, 12, 70, 10, 170, 23,
4490             53, 6, 85, 3);
4491         nucleicAcidRotamerCache[n][9] = new Rotamer(name, 85, 4, -116, 15, -156, 15, 66, 19, -179,
4492             23, 55, 6, 86, 4);
4493         nucleicAcidRotamerCache[n][10] = new Rotamer(name, 80, 4, -158, 7, 63, 14, 68, 12, 143, 30,
4494             50, 7, 83, 2);
4495         nucleicAcidRotamerCache[n][11] = new Rotamer(name, 81, 3, -159, 8, -79, 6, -111, 9, 83, 11,
4496             168, 6, 86, 4);
4497         nucleicAcidRotamerCache[n][12] = new Rotamer(name, 80, 3, -163, 9, -69, 10, 153, 12, -166,
4498             12, 179, 10, 84, 3);
4499         nucleicAcidRotamerCache[n][13] = new Rotamer(name, 81, 2, -157, 14, -66, 11, 172, 11, 139,
4500             13, 176, 10, 84, 3);
4501         nucleicAcidRotamerCache[n][14] = new Rotamer(name, 87, 7, -136, 23, 80, 15, 67, 9, 109, 10,
4502             176, 6, 84, 4);
4503         nucleicAcidRotamerCache[n][15] = new Rotamer(name, 84, 4, -145, 10, -71, 10, -60, 9, 177, 12,
4504             58, 7, 145, 7);
4505         nucleicAcidRotamerCache[n][16] = new Rotamer(name, 83, 4, -140, 10, -71, 10, -63, 8, -138, 9,
4506             54, 7, 144, 8);
4507         nucleicAcidRotamerCache[n][17] = new Rotamer(name, 85, 3, -134, 18, 168, 17, -67, 15, 178,
4508             22, 49, 5, 148, 3);
4509         nucleicAcidRotamerCache[n][18] = new Rotamer(name, 83, 3, -154, 18, -82, 19, -164, 14, 162,
4510             25, 51, 5, 145, 5);
4511         nucleicAcidRotamerCache[n][19] = new Rotamer(name, 83, 3, -154, 5, 53, 7, 164, 5, 148, 10,
4512             50, 5, 148, 4);
4513         nucleicAcidRotamerCache[n][20] = new Rotamer(name, 84, 3, -123, 24, -140, 15, 68, 12, -160,
4514             30, 54, 7, 146, 6);
4515         nucleicAcidRotamerCache[n][21] = new Rotamer(name, 81, 3, -161, 20, -71, 8, 180, 17, -165,
4516             14, 178, 9, 147, 5);
4517         nucleicAcidRotamerCache[n][22] = new Rotamer(name, 82, 8, -155, 6, 69, 14, 63, 9, 115, 17,
4518             176, 6, 146, 4);
4519         nucleicAcidRotamerCache[n][23] = new Rotamer(name, 84, 4, -143, 17, -73, 15, -63, 7, -135,
4520             39, -66, 7, 151, 13);
4521         nucleicAcidRotamerCache[n][24] = new Rotamer(name, 85, 4, -127, 13, -112, 19, 63, 13, -178,
4522             27, -64, 4, 150, 7);
4523         nucleicAcidRotamerCache[n][25] = new Rotamer(name, 145, 8, -100, 12, -71, 18, -72, 13, -167,
4524             17, 53, 7, 84, 5);
4525         nucleicAcidRotamerCache[n][26] = new Rotamer(name, 146, 7, -100, 15, 170, 14, -62, 19, 170,
4526             34, 51, 8, 84, 5);
4527         nucleicAcidRotamerCache[n][27] = new Rotamer(name, 149, 7, -137, 11, 139, 25, -75, 11, 158,
4528             20, 48, 6, 84, 4);
4529         nucleicAcidRotamerCache[n][28] = new Rotamer(name, 148, 3, -168, 5, 146, 6, -71, 7, 151, 12,
4530             42, 4, 85, 3);
4531         nucleicAcidRotamerCache[n][29] = new Rotamer(name, 148, 8, -103, 14, 165, 21, -155, 14, 165,
4532             15, 49, 7, 83, 4);
4533         nucleicAcidRotamerCache[n][30] = new Rotamer(name, 145, 7, -97, 18, 80, 16, -156, 29, -170,
4534             23, 58, 5, 85, 7);
4535         nucleicAcidRotamerCache[n][31] = new Rotamer(name, 149, 6, -89, 10, -119, 17, 62, 10, 176,
4536             23, 54, 4, 87, 3);
4537         nucleicAcidRotamerCache[n][32] = new Rotamer(name, 150, 6, -110, 26, -172, 7, 80, 20, -162,
4538             20, 61, 8, 89, 4);
4539         nucleicAcidRotamerCache[n][33] = new Rotamer(name, 147, 6, -119, 23, 89, 16, 59, 14, 161, 23,
4540             52, 7, 83, 4);
4541         nucleicAcidRotamerCache[n][34] = new Rotamer(name, 148, 4, -99, 8, -70, 12, -64, 10, 177, 17,
4542             176, 14, 87, 4);
4543         nucleicAcidRotamerCache[n][35] = new Rotamer(name, 144, 7, -133, 14, -156, 14, 74, 12, -143,
4544             20, -166, 9, 81, 3);
4545         nucleicAcidRotamerCache[n][36] = new Rotamer(name, 149, 2, -85, 20, 100, 13, 81, 11, -112,
4546             12, -178, 3, 83, 2);
4547         nucleicAcidRotamerCache[n][37] = new Rotamer(name, 150, 6, -92, 11, 85, 8, 64, 5, -169, 8,
4548             177, 9, 86, 5);
4549         nucleicAcidRotamerCache[n][38] = new Rotamer(name, 142, 8, -116, 28, 66, 15, 72, 8, 122, 22,
4550             -178, 6, 84, 3);
4551         nucleicAcidRotamerCache[n][39] = new Rotamer(name, 146, 8, -101, 16, -69, 17, -68, 12, -150,
4552             21, 54, 7, 148, 7);
4553         nucleicAcidRotamerCache[n][40] = new Rotamer(name, 145, 7, -115, 20, 163, 13, -66, 6, 172,
4554             14, 46, 6, 146, 6);
4555         nucleicAcidRotamerCache[n][41] = new Rotamer(name, 148, 4, -112, 20, 112, 14, -85, 17, 165,
4556             16, 57, 12, 146, 6);
4557         nucleicAcidRotamerCache[n][42] = new Rotamer(name, 150, 10, -100, 16, -146, 19, 72, 13, -152,
4558             27, 57, 14, 148, 4);
4559         nucleicAcidRotamerCache[n][43] = new Rotamer(name, 146, 7, -102, 21, 90, 15, 68, 12, 173, 18,
4560             56, 8, 148, 4);
4561         nucleicAcidRotamerCache[n][44] = new Rotamer(name, 150, 2, -112, 16, 170, 12, -82, 13, 84, 7,
4562             176, 6, 148, 2);
4563         nucleicAcidRotamerCache[n][45] = new Rotamer(name, 147, 6, -104, 15, -64, 16, -73, 4, -165,
4564             26, -66, 7, 150, 3);
4565       }
4566       default -> {
4567         // Do nothing.
4568       }
4569     }
4570     return nucleicAcidRotamerCache[n];
4571   }
4572 
4573   public enum ProteinLibrary {
4574     PonderAndRichards(1), Richardson(2), None(-1);
4575 
4576     private final int oldIntegerConstant;
4577 
4578     ProteinLibrary(int oldConst) {
4579       this.oldIntegerConstant = oldConst;
4580     }
4581 
4582     /**
4583      * Parses a String input to a ProteinLibrary. Can be either a name, or an integer constant.
4584      *
4585      * <p>The name is preferred, but the integer constant is allowed for legacy reasons.
4586      *
4587      * @param input Input to parse.
4588      * @return A ProteinLibrary.
4589      * @throws IllegalArgumentException If no matching ProteinLibrary found.
4590      */
4591     public static ProteinLibrary getProteinLibrary(String input) throws IllegalArgumentException {
4592       if (input.matches("^\\d+$")) {
4593         return int2Library(Integer.parseInt(input));
4594       } else {
4595         return Arrays.stream(ProteinLibrary.values())
4596             .filter((ProteinLibrary pl) -> pl.toString().equalsIgnoreCase(input)).findAny()
4597             .orElseThrow(() -> new IllegalArgumentException(
4598                 " No protein library found that corresponds to " + input));
4599       }
4600     }
4601 
4602     /**
4603      * Converts an integer to a corresponding ProteinLibrary.
4604      *
4605      * @param library Index of the library.
4606      * @return A ProteinLibrary.
4607      * @throws IllegalArgumentException If no matching ProteinLibrary found.
4608      */
4609     public static ProteinLibrary intToProteinLibrary(int library) throws IllegalArgumentException {
4610       return int2Library(library);
4611     }
4612 
4613     /**
4614      * Converts an integer to a corresponding ProteinLibrary.
4615      *
4616      * @param library Index of the library.
4617      * @return A ProteinLibrary.
4618      * @throws IllegalArgumentException If no matching ProteinLibrary found.
4619      */
4620     private static ProteinLibrary int2Library(int library) throws IllegalArgumentException {
4621       for (ProteinLibrary lib : ProteinLibrary.values()) {
4622         if (library == lib.oldIntegerConstant) {
4623           return lib;
4624         }
4625       }
4626       throw new IllegalArgumentException(
4627           format(" Could not find a " + "protein rotamer library to correspond with %d!", library));
4628     }
4629   }
4630 
4631   public enum NucleicAcidLibrary {
4632     RICHARDSON
4633   }
4634 
4635   public enum NucleicSugarPucker {
4636     // Used to be 2, 1, and 3 respectively.
4637     C2_ENDO("south"), C3_ENDO("north"), C3_EXO();
4638 
4639     NucleicSugarPucker() {
4640     }
4641 
4642     NucleicSugarPucker(String aName) {
4643     }
4644 
4645     /**
4646      * Returns the sugar pucker associated with a delta torsion. This currently does not support the
4647      * C3'-exo DNA-only pucker.
4648      *
4649      * @param delta   Delta torsion to check
4650      * @param isDeoxy If DNA (vs. RNA). Presently ignored.
4651      * @return Pucker
4652      */
4653     public static NucleicSugarPucker checkPucker(double delta, boolean isDeoxy) {
4654       /*
4655        * Midpoint between North, South is 115 degrees.
4656        *
4657        * 0-360: North is 0-115 or 295-360.
4658        * -180 to 180: North is -65 to 115.
4659        */
4660       delta = mod(delta, 360.0);
4661       if (delta <= 115 || delta > 295) {
4662         return C3_ENDO;
4663       } else {
4664         return C2_ENDO;
4665       } // TODO: Add else-if to handle C3'-exo pucker.
4666     }
4667   }
4668 
4669   public static class RotamerGuess {
4670 
4671     private final Residue residue;
4672     private final Rotamer rotamer;
4673     private final int rotIndex;
4674     private final double rmsd;
4675 
4676     RotamerGuess(Residue res, Rotamer rot, int index, double rmsDev) {
4677       residue = res;
4678       rotamer = rot;
4679       rotIndex = index;
4680       rmsd = rmsDev;
4681     }
4682 
4683     public double getRMSD() {
4684       return rmsd;
4685     }
4686 
4687     public Residue getResidue() {
4688       return residue;
4689     }
4690 
4691     public Rotamer getRotamer() {
4692       return rotamer;
4693     }
4694 
4695     public String toString() {
4696       return format(
4697           " Residue %7s is most likely in rotamer %2d (%s), with an RMSD of %9.5f degrees.", residue,
4698           rotIndex, rotamer, rmsd);
4699     }
4700   }
4701 
4702   /**
4703    * Class contains rotamer information for a nonstandard amino acid.
4704    */
4705   private static class NonstandardRotLibrary {
4706 
4707     private final String resName;
4708     private final String[] placeRecords; // Must be ordered correctly!
4709     private final List<Rotamer> stdRotamers; // "Library" rotamers for this residue.
4710 
4711     NonstandardRotLibrary(String resname, String[] placeRecords, Rotamer[] stdRotamers) {
4712       this.resName = resname.toUpperCase();
4713       this.placeRecords = Arrays.copyOf(placeRecords, placeRecords.length);
4714       this.stdRotamers = new ArrayList<>(Arrays.asList(stdRotamers));
4715     }
4716 
4717     Rotamer[] getRotamers() {
4718       return stdRotamers.toArray(new Rotamer[0]);
4719     }
4720 
4721     /**
4722      * Checks the angles for a nonstandard residue.
4723      *
4724      * @param residue Residue to measure.
4725      * @param chi     Array to be filled up with chi values.
4726      * @param print   Verbosity flag.
4727      * @return Number of dihedral angles.
4728      */
4729     int measureNonstdRot(Residue residue, double[] chi, boolean print) {
4730       for (String placeRec : placeRecords) {
4731         String[] toks = placeRec.split("\\s+");
4732         if (toks[0].equalsIgnoreCase("PLACECHI")) {
4733           int chiNum = Integer.parseInt(toks[5]) - 1;
4734           Atom at1 = (Atom) residue.getAtomNode(toks[1]);
4735           Atom at2 = (Atom) residue.getAtomNode(toks[2]);
4736           Atom at3 = (Atom) residue.getAtomNode(toks[3]);
4737           Atom at4 = (Atom) residue.getAtomNode(toks[4]);
4738           Torsion tors = at1.getTorsion(at2, at3, at4);
4739           chi[chiNum] = tors.getValue();
4740           if (print) {
4741             logger.info(tors.toString());
4742           }
4743         }
4744       }
4745       return placeRecords.length;
4746     }
4747 
4748     /**
4749      * Applies a nonstandard rotamer to an amino acid.
4750      *
4751      * @param residue Residue to apply rotamer to.
4752      * @param rotamer The rotamer to apply.
4753      */
4754     void applyNonstdRotamer(Residue residue, Rotamer rotamer) {
4755       if (!residue.getName().equalsIgnoreCase(resName)) {
4756         throw new IllegalArgumentException(
4757             format(" Residue %s is " + "not of type %s", residue, resName));
4758       }
4759       for (String record : placeRecords) {
4760         String[] toks = record.split("\\s+");
4761         Atom at1 = (Atom) residue.getAtomNode(toks[1]);
4762         Atom at2 = (Atom) residue.getAtomNode(toks[2]);
4763         Atom at3 = (Atom) residue.getAtomNode(toks[3]);
4764         Atom at4 = (Atom) residue.getAtomNode(toks[4]);
4765         Bond b12 = at1.getBond(at2);
4766         double dbond = b12.bondType.distance;
4767         Angle a123 = at1.getAngle(at2, at3);
4768         double dang = a123.angleType.angle[a123.nh];
4769         double dtors;
4770         if (toks[0].equalsIgnoreCase("PLACECHI")) {
4771           int chiNum = Integer.parseInt(toks[5]);
4772           dtors = rotamer.angles[chiNum - 1];
4773         } else {
4774           dtors = Double.parseDouble(toks[5]);
4775         }
4776         int chirality = Integer.parseInt(toks[6]);
4777         intxyz(at1, at2, dbond, at3, dang, at4, dtors, chirality);
4778       }
4779     }
4780   }
4781 }