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