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 static ffx.numerics.math.DoubleMath.dist;
41  import static ffx.potential.bonded.BondedUtils.buildBond;
42  import static ffx.potential.bonded.BondedUtils.buildHeavy;
43  import static ffx.potential.bonded.BondedUtils.buildH;
44  import static ffx.potential.bonded.BondedUtils.intxyz;
45  import static ffx.potential.bonded.AminoAcidUtils.ResiduePosition.FIRST_RESIDUE;
46  import static ffx.potential.bonded.AminoAcidUtils.ResiduePosition.LAST_RESIDUE;
47  import static ffx.potential.bonded.AminoAcidUtils.ResiduePosition.MIDDLE_RESIDUE;
48  import static java.lang.String.format;
49  import static org.apache.commons.math3.util.FastMath.toDegrees;
50  
51  import ffx.numerics.math.DoubleMath;
52  import ffx.potential.bonded.BondedUtils.MissingAtomTypeException;
53  import ffx.potential.bonded.BondedUtils.MissingHeavyAtomException;
54  import ffx.potential.bonded.AminoAcidUtils.ResiduePosition;
55  import ffx.potential.parameters.AtomType;
56  import ffx.potential.parameters.ForceField;
57  import java.util.Arrays;
58  import java.util.HashMap;
59  import java.util.List;
60  import java.util.Map;
61  import java.util.logging.Level;
62  import java.util.logging.Logger;
63  
64  /**
65   * Utilities for creating Nucleic Acid residues.
66   *
67   * @author Michael Schnieders
68   * @since 1.0
69   */
70  public class NucleicAcidUtils {
71  
72    private static final Logger logger = Logger.getLogger(NucleicAcidUtils.class.getName());
73  
74    /**
75     * The 4 RNA bases, 4 DNA bases, and mono- di- and triphosphate.
76     */
77    public enum NA {
78      ADENINE, CYTOSINE, GUANINE, URACIL, DEOXYADENINE, DEOXYCYTOSINE, DEOXYGUANINE, THYMINE, MONOPHOSPHATE, DIPHOSPHATE, TRIPHOSPHATE
79    }
80  
81    public enum NucleicAcid1 {
82      A, G, C, U, D, B, I, T, O, W, H, X
83    }
84  
85    /**
86     * Since enumeration values must start with a letter, an 'M' is added to modified bases whose IUPAC
87     * name starts with an integer.
88     */
89    public enum NucleicAcid3 {
90      ADE, GUA, CYT, URI, DAD, DGU, DCY, DTY, THY, MP1, DP2, TP3, UNK, M2MG, H2U, M2G, OMC, OMG, PSU, M5MC, M7MG, M5MU, M1MA, YYG;
91  
92      /**
93       * Best-guess parse of a String to an NA3.
94       *
95       * @param name Parse to NA3.
96       * @return Corresponding NA3.
97       * @throws IllegalArgumentException For 'DU', which has no implemented NA3.
98       */
99      public static NucleicAcid3 parse(String name) throws IllegalArgumentException {
100       // Only semi-abnormal cases: THY parses to DT instead of T, and DU throws an exception.
101       return switch (name.toUpperCase()) {
102         case "ADE", "A" -> NucleicAcid3.ADE;
103         case "CYT", "C" -> NucleicAcid3.CYT;
104         case "GUA", "G" -> NucleicAcid3.GUA;
105         case "URI", "U" -> NucleicAcid3.URI;
106         case "DAD", "DA" -> NucleicAcid3.DAD;
107         case "DCY", "DC" -> NucleicAcid3.DCY;
108         case "DGU", "DG" -> NucleicAcid3.DGU;
109         case "DTY", "THY", "DT" -> NucleicAcid3.DTY;
110         case "MPO" -> NucleicAcid3.MP1;
111         case "DPO" -> NucleicAcid3.DP2;
112         case "TPO" -> NucleicAcid3.TP3;
113         case "DU" -> throw new IllegalArgumentException(" No NA3 value exists for deoxy-uracil!");
114         default -> NucleicAcid3.UNK;
115       };
116     }
117   }
118 
119   /**
120    * Biotype keys for nucleic acid backbone atom types. These are consistent with parameter files
121    * from TINKER v. 6.1 (June 2012).
122    */
123   public static final int[] NA_O5 = {1001, 1031, 1062, 1090, 1117, 1146, 1176, 1203, 0, 0, 0, 0,
124       1300, 1334, 1363, 1400, 1431, 1465, 1492, 1523, 1559, 1589, 1624};
125   /** Constant <code>NA_C5</code> */
126   public static final int[] NA_C5 = {1002, 1032, 1063, 1091, 1118, 1147, 1177, 1204, 0, 0, 0, 0,
127       1301, 1335, 1364, 1401, 1432, 1466, 1493, 1524, 1560, 1590, 1625};
128   /** Constant <code>NA_H51</code> */
129   public static final int[] NA_H51 = {1003, 1033, 1064, 1092, 1119, 1148, 1178, 1205, 0, 0, 0, 0,
130       1302, 1336, 1365, 1402, 1433, 1467, 1494, 1525, 1561, 1591, 1626};
131   /** Constant <code>NA_H52</code> */
132   public static final int[] NA_H52 = {1004, 1034, 1065, 1093, 1120, 1149, 1179, 1206, 0, 0, 0, 0,
133       1303, 1337, 1366, 1403, 1434, 1468, 1495, 1526, 1562, 1592, 1627};
134   /** Constant <code>NA_C4</code> */
135   public static final int[] NA_C4 = {1005, 1035, 1066, 1094, 1121, 1150, 1180, 1207, 0, 0, 0, 0,
136       1304, 1338, 1367, 1404, 1435, 1469, 1496, 1527, 1563, 1593, 1628};
137   /** Constant <code>NA_H4</code> */
138   public static final int[] NA_H4 = {1006, 1036, 1067, 1095, 1122, 1151, 1181, 1208, 0, 0, 0, 0,
139       1305, 1339, 1368, 1405, 1436, 1470, 1497, 1528, 1564, 1594, 1629};
140   /** Constant <code>NA_O4</code> */
141   public static final int[] NA_O4 = {1007, 1037, 1068, 1096, 1123, 1152, 1182, 1209, 0, 0, 0, 0,
142       1306, 1340, 1369, 1406, 1437, 1471, 1498, 1529, 1565, 1595, 1630};
143   /** Constant <code>NA_C1</code> */
144   public static final int[] NA_C1 = {1008, 1038, 1069, 1097, 1124, 1153, 1183, 1210, 0, 0, 0, 0,
145       1307, 1341, 1370, 1407, 1438, 1472, 1499, 1530, 1566, 1596, 1631};
146   /** Constant <code>NA_H1</code> */
147   public static final int[] NA_H1 = {1009, 1039, 1070, 1098, 1125, 1154, 1184, 1211, 0, 0, 0, 0,
148       1308, 1342, 1371, 1408, 1439, 1473, 1500, 1531, 1567, 1597, 1632};
149   /** Constant <code>NA_C3</code> */
150   public static final int[] NA_C3 = {1010, 1040, 1071, 1099, 1126, 1155, 1185, 1212, 0, 0, 0, 0,
151       1309, 1343, 1372, 1409, 1440, 1474, 1501, 1532, 1568, 1598, 1633};
152   /** Constant <code>NA_H3</code> */
153   public static final int[] NA_H3 = {1011, 1041, 1072, 1100, 1127, 1156, 1186, 1213, 0, 0, 0, 0,
154       1310, 1344, 1373, 1410, 1441, 1475, 1502, 1533, 1569, 1599, 1634};
155   /** Constant <code>NA_C2</code> */
156   public static final int[] NA_C2 = {1012, 1042, 1073, 1101, 1128, 1157, 1187, 1214, 0, 0, 0, 0,
157       1311, 1345, 1374, 1411, 1442, 1476, 1503, 1534, 1570, 1600, 1635};
158   /** Constant <code>NA_H21</code> */
159   public static final int[] NA_H21 = {1013, 1043, 1074, 1102, 1129, 1158, 1188, 1215, 0, 0, 0, 0,
160       1312, 1346, 1375, 1412, 1443, 1477, 1504, 1535, 1571, 1601, 1636};
161   /** Constant <code>NA_O2</code> */
162   public static final int[] NA_O2 = {1014, 1044, 1075, 1103, 0, 0, 0, 0, 0, 0, 0, 0, 1313, 1347,
163       1376, 1413, 1444, 1478, 1505, 1536, 1572, 1602, 1637};
164   /** Constant <code>NA_H22</code> */
165   public static final int[] NA_H22 = {1015, 1045, 1076, 1104, 1130, 1159, 1189, 1216, 0, 0, 0, 0,
166       1314, 1348, 1377, 0, 0, 1479, 1506, 1537, 1573, 1603, 1638};
167   /** Constant <code>NA_O3</code> */
168   public static final int[] NA_O3 = {1016, 1046, 1077, 1105, 1131, 1160, 1190, 1217, 0, 0, 0, 0,
169       1315, 1349, 1378, 1414, 1445, 1480, 1507, 1538, 1574, 1604, 1639};
170   /** Constant <code>NA_P</code> */
171   public static final int[] NA_P = {1230, 1230, 1230, 1230, 1242, 1242, 1242, 1242, 0, 0, 0, 0, 1230,
172       1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230};
173   /** Constant <code>NA_OP</code> */
174   public static final int[] NA_OP = {1231, 1231, 1231, 1231, 1243, 1243, 1243, 1243, 0, 0, 0, 0,
175       1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231};
176   /**
177    * Should be NA_HO5' (' replaced by T in the name).
178    *
179    * <p>Constant <code>NA_HO5T</code>
180    */
181   public static final int[] NA_HO5T = {1233, 1233, 1233, 1233, 1245, 1245, 1245, 1245, 0, 0, 0, 0,
182       1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233, 1233};
183   /**
184    * Should be NA_HO3' (' replaced by T in the name).
185    *
186    * <p>Constant <code>NA_HO3T</code>
187    */
188   public static final int[] NA_HO3T = {1238, 1238, 1238, 1238, 1250, 1250, 1250, 1250, 0, 0, 0, 0,
189       1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238};
190 
191   /** Constant <code>nucleicAcidList</code> */
192   public static final List<NucleicAcid3> nucleicAcidList = Arrays.asList(NucleicAcid3.values());
193 
194   /** Constant <code>NA1toNA3</code> */
195   public static final HashMap<NucleicAcid1, NucleicAcid3> NA1toNA3 = new HashMap<>();
196 
197   /** Substitutes from old PDB nucleic acid atom names to new ones. */
198   private static final Map<String, String> newNucleicAcidAtomNames;
199 
200   /** Repeating atomic numbers of a nucleic acid chain. */
201   public static final int[] NAPATTERN = {8, 6, 6, 6, 8};
202 
203   static {
204     newNucleicAcidAtomNames = Map.of("HO'", "HO2'", "H3T", "HO3'", "H5'1", "H5'", "H5'2", "H5''",
205         "H5T", "HO5'", "H2'1", "H2'", "H2'2", "H2''");
206 
207     NucleicAcid1[] na1 = NucleicAcid1.values();
208     NucleicAcid3[] na3 = NucleicAcid3.values();
209     for (int i = 0; i < NucleicAcid1.values().length; i++) {
210       NA1toNA3.put(na1[i], na3[i]);
211     }
212   }
213 
214   /**
215    * Assign atom types for a nucleic acid polymer.
216    *
217    * @param residues A list of residues that form the nucleic acid polymer.
218    * @param forceField The ForceField in use.
219    * @param bondList A list of created bonds.
220    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
221    * @throws ffx.potential.bonded.BondedUtils.MissingAtomTypeException if any.
222    */
223   public static void assignNucleicAcidAtomTypes(List<Residue> residues, ForceField forceField,
224       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
225 
226     // A reference to the O3* atom of the previous base.
227     Atom pSugarO3 = null;
228 
229     // Loop over residues.
230     int numberOfResidues = residues.size();
231     for (int residueNumber = 0; residueNumber < numberOfResidues; residueNumber++) {
232 
233       // Match the residue name to a known nucleic acid residue.
234       Residue residue = residues.get(residueNumber);
235       String residueName = residue.getName().toUpperCase();
236       NucleicAcid3 nucleicAcid = null;
237       int naNumber = -1;
238       for (NucleicAcid3 nucleic : nucleicAcidList) {
239         naNumber++;
240         String nuc3 = nucleic.toString();
241         nuc3 = nuc3.substring(nuc3.length() - 3);
242         if (nuc3.equalsIgnoreCase(residueName)) {
243           nucleicAcid = nucleic;
244           break;
245         }
246       }
247 
248       // Do atom name conversions.
249       List<Atom> resAtoms = residue.getAtomList();
250       int nAtoms = resAtoms.size();
251       for (Atom atom : resAtoms) {
252         String name = atom.getName();
253         name = name.replace('*', '\'');
254         name = newNucleicAcidAtomNames.getOrDefault(name, name);
255         atom.setName(name);
256       }
257 
258       // Set a position flag.
259       ResiduePosition position = MIDDLE_RESIDUE;
260       boolean lastRes = false;
261       if (residueNumber == 0) {
262         position = FIRST_RESIDUE;
263       }
264 
265       if (residueNumber == numberOfResidues - 1) {
266         lastRes = true;
267         if (position != FIRST_RESIDUE) {
268           position = LAST_RESIDUE;
269         }
270       }
271 
272       // Check if this is a lone 3'-terminal phosphate cap; if so, will skip a lot of logic.
273       boolean threePrimeCap = isThreePrimeCap(nAtoms, resAtoms);
274 
275       // Check if the sugar is deoxyribose and change the residue name if necessary.
276       boolean isDNA = false;
277       Residue resToCheck = threePrimeCap ? residues.get(residueNumber - 1) : residue;
278       Atom sugarO2 = (Atom) resToCheck.getAtomNode("O2'");
279 
280       if (sugarO2 == null) {
281         // Assume deoxyribose (DNA) since there is an O2* atom.
282         isDNA = true;
283         if (!residueName.startsWith("D")) {
284           switch (nucleicAcid) {
285             case ADE -> {
286               nucleicAcid = NucleicAcid3.DAD;
287               residueName = "DAD";
288               residue.setName(residueName);
289             }
290             case CYT -> {
291               nucleicAcid = NucleicAcid3.DCY;
292               residueName = "DCY";
293               residue.setName(residueName);
294             }
295             case GUA -> {
296               nucleicAcid = NucleicAcid3.DGU;
297               residueName = "DGU";
298               residue.setName(residueName);
299             }
300             case THY -> {
301               nucleicAcid = NucleicAcid3.DTY;
302               residueName = "DTY";
303               residue.setName(residueName);
304             }
305             default -> {
306             }
307           }
308         }
309       } else {
310         // Assume ribose (RNA) since there is no O2* atom.
311         if (residueName.startsWith("D")) {
312           switch (nucleicAcid) {
313             case DAD -> {
314               nucleicAcid = NucleicAcid3.ADE;
315               residueName = "ADE";
316               residue.setName(residueName);
317             }
318             case DCY -> {
319               nucleicAcid = NucleicAcid3.CYT;
320               residueName = "CYT";
321               residue.setName(residueName);
322             }
323             case DGU -> {
324               nucleicAcid = NucleicAcid3.GUA;
325               residueName = "GUA";
326               residue.setName(residueName);
327             }
328             case DTY -> {
329               nucleicAcid = NucleicAcid3.THY;
330               residueName = "THY";
331               residue.setName(residueName);
332             }
333             default -> {
334             }
335           }
336         }
337       }
338 
339       // Build the phosphate atoms of the current residue.
340       Atom phosphate = null;
341       Atom sugarO5 = null;
342       Atom sugarO3 = null;
343 
344       if (threePrimeCap) {
345         if (logger.isLoggable(Level.FINE)) {
346           logger.fine(format(" EXPERIMENTAL: Adding 3'-terminal phosphate cap %s", residue));
347         }
348         Residue priorResidue = residues.get(residueNumber - 1);
349         int phosType = isDNA ? 1252 : 1240;
350         int oxygenType = isDNA ? 1253 : 1241;
351 
352         phosphate = buildHeavy(residue, "P", pSugarO3, phosType, forceField, bondList);
353         buildHeavy(residue, "OP1", phosphate, oxygenType, forceField, bondList);
354         buildHeavy(residue, "OP2", phosphate, oxygenType, forceField, bondList);
355         switch (nAtoms) {
356           case 3 ->
357               buildOP3(residue, phosphate, oxygenType, forceField, bondList, priorResidue, false);
358           case 4 -> {
359             MSNode bogusO5s = residue.getAtomNode("O5'");
360             if (bogusO5s != null) {
361               bogusO5s.setName("OP3");
362             }
363             buildHeavy(residue, "OP3", phosphate, oxygenType, forceField, bondList);
364           }
365           case 5, 6 -> logger.severe(" Currently, FFX does not support partially-protonated "
366               + "3'-terminal phosphate caps from PDB files!");
367         }
368       } else {
369         if (position == FIRST_RESIDUE) {
370           /*
371            * The 5' O5' oxygen of the nucleic acid is generally terminated by
372            * 1.) A phosphate group PO3 (-3).
373            * 2.) A hydrogen.
374            * If the base has phosphate atom we will assume a PO3 group.
375            */
376           phosphate = (Atom) residue.getAtomNode("P");
377           if (phosphate != null) {
378             Residue nextRes = lastRes ? null : residues.get(residueNumber + 1);
379             if (isDNA) {
380               phosphate = buildHeavy(residue, "P", null, 1247, forceField, bondList);
381               buildHeavy(residue, "OP1", phosphate, 1248, forceField, bondList);
382               buildHeavy(residue, "OP2", phosphate, 1248, forceField, bondList);
383               buildOP3(residue, phosphate, 1248, forceField, bondList, nextRes, true);
384               sugarO5 = buildHeavy(residue, "O5'", phosphate, 1246, forceField, bondList);
385             } else {
386               phosphate = buildHeavy(residue, "P", null, 1235, forceField, bondList);
387               buildHeavy(residue, "OP1", phosphate, 1236, forceField, bondList);
388               buildHeavy(residue, "OP2", phosphate, 1236, forceField, bondList);
389               buildOP3(residue, phosphate, 1236, forceField, bondList, nextRes, true);
390               sugarO5 = buildHeavy(residue, "O5'", phosphate, 1234, forceField, bondList);
391             }
392           } else if (isDNA) {
393             Atom O5 = residue.getAtomByName("O5'", true);
394             if (O5 == null) {
395               sugarO5 = buildSugarO5(residue, 1244, forceField);
396             } else {
397               sugarO5 = buildHeavy(residue, "O5'", null, 1244, forceField, bondList);
398             }
399           } else {
400             Atom O5 = residue.getAtomByName("O5'", true);
401             if (O5 == null) {
402               sugarO5 = buildSugarO5(residue, 1232, forceField);
403             } else {
404               sugarO5 = buildHeavy(residue, "O5'", null, 1232, forceField, bondList);
405             }
406           }
407         } else {
408           phosphate = buildHeavy(residue, "P", pSugarO3, NA_P[naNumber], forceField, bondList);
409           buildHeavy(residue, "OP1", phosphate, NA_OP[naNumber], forceField, bondList);
410           buildHeavy(residue, "OP2", phosphate, NA_OP[naNumber], forceField, bondList);
411           sugarO5 = buildHeavy(residue, "O5'", phosphate, NA_O5[naNumber], forceField, bondList);
412         }
413 
414         // Build the ribose sugar atoms of the current base.
415         Atom sugarC5 = buildHeavy(residue, "C5'", sugarO5, NA_C5[naNumber], forceField, bondList);
416         Atom sugarC4 = buildHeavy(residue, "C4'", sugarC5, NA_C4[naNumber], forceField, bondList);
417         Atom sugarO4 = buildHeavy(residue, "O4'", sugarC4, NA_O4[naNumber], forceField, bondList);
418         Atom sugarC1 = buildHeavy(residue, "C1'", sugarO4, NA_C1[naNumber], forceField, bondList);
419         Atom sugarC3 = buildHeavy(residue, "C3'", sugarC4, NA_C3[naNumber], forceField, bondList);
420         Atom sugarC2 = buildHeavy(residue, "C2'", sugarC3, NA_C2[naNumber], forceField, bondList);
421         buildBond(sugarC2, sugarC1, forceField, bondList);
422         if (position == LAST_RESIDUE || numberOfResidues == 1) {
423           if (isDNA) {
424             sugarO3 = buildHeavy(residue, "O3'", sugarC3, 1249, forceField, bondList);
425           } else {
426             sugarO3 = buildHeavy(residue, "O3'", sugarC3, 1237, forceField, bondList);
427           }
428         } else {
429           boolean nextResIsCap = (residues.get(residueNumber + 1).getAtomList().size() < 7);
430           int o3Type = NA_O3[naNumber];
431           if (nextResIsCap) {
432             logger.fine(" Applying a 3'-terminal-phos-cap O3' atom type to residue " + residue);
433             o3Type = isDNA ? 1251 : 1239;
434           }
435           sugarO3 = buildHeavy(residue, "O3'", sugarC3, o3Type, forceField, bondList);
436         }
437         if (!isDNA) {
438           sugarO2 = buildHeavy(residue, "O2'", sugarC2, NA_O2[naNumber], forceField, bondList);
439         }
440 
441         // Build the backbone hydrogen atoms.
442         if (position == FIRST_RESIDUE && phosphate == null) {
443           buildH(residue, "HO5'", sugarO5, 1.00e0, sugarC5, 109.5e0, sugarC4, 180.0e0, 0,
444               NA_HO5T[naNumber], forceField, bondList);
445         }
446         buildH(residue, "H5'", sugarC5, 1.09e0, sugarO5, 109.5e0, sugarC4, 109.5e0, 1,
447             NA_H51[naNumber], forceField, bondList);
448         buildH(residue, "H5''", sugarC5, 1.09e0, sugarO5, 109.5e0, sugarC4, 109.5e0, -1,
449             NA_H52[naNumber], forceField, bondList);
450         buildH(residue, "H4'", sugarC4, 1.09e0, sugarC5, 109.5e0, sugarC3, 109.5e0, -1,
451             NA_H4[naNumber], forceField, bondList);
452         buildH(residue, "H3'", sugarC3, 1.09e0, sugarC4, 109.5e0, sugarC2, 109.5e0, -1,
453             NA_H3[naNumber], forceField, bondList);
454         if (isDNA) {
455           buildH(residue, "H2'", sugarC2, 1.09e0, sugarC3, 109.5e0, sugarC1, 109.5e0, -1,
456               NA_H21[naNumber], forceField, bondList);
457           buildH(residue, "H2''", sugarC2, 1.09e0, sugarC3, 109.5e0, sugarC1, 109.5e0, 1,
458               NA_H22[naNumber], forceField, bondList);
459         } else {
460           buildH(residue, "H2'", sugarC2, 1.09e0, sugarC3, 109.5e0, sugarC1, 109.5e0, -1,
461               NA_H21[naNumber], forceField, bondList);
462           // Add the NA_O2' Methyl for OMC and OMG
463           // Note: may be completely out-of-date with the current Chemical Component Dictionary.
464           if (nucleicAcid == NucleicAcid3.OMC || nucleicAcid == NucleicAcid3.OMG) {
465             Atom CM2 = buildHeavy(residue, "CM2", sugarO2, 1427, forceField, bondList);
466             Atom HM21 = buildH(residue, "HM21", CM2, 1.08e0, sugarO2, 109.5e0, sugarC2, 0.0e0, 0,
467                 1428, forceField, bondList);
468             buildH(residue, "HM22", CM2, 1.08e0, sugarO2, 109.5e0, HM21, 109.5e0, 1, 1429,
469                 forceField, bondList);
470             buildH(residue, "HM23", CM2, 1.08e0, sugarO2, 109.5e0, HM21, 109.5e0, -1, 1430,
471                 forceField, bondList);
472           } else {
473             buildH(residue, "HO2'", sugarO2, 1.00e0, sugarC2, 109.5e0, sugarC3, 180.0e0, 0,
474                 NA_H22[naNumber], forceField, bondList);
475           }
476         }
477         buildH(residue, "H1'", sugarC1, 1.09e0, sugarO4, 109.5e0, sugarC2, 109.5e0, -1,
478             NA_H1[naNumber], forceField, bondList);
479         if (position == LAST_RESIDUE || numberOfResidues == 1) {
480           buildH(residue, "HO3'", sugarO3, 1.00e0, sugarC3, 109.5e0, sugarC4, 180.0e0, 0,
481               NA_HO3T[naNumber], forceField, bondList);
482           // Else, if it is terminated by a 3' phosphate cap:
483           // Will need to see how PDB would label a 3' phosphate cap.
484         }
485 
486         // Build the nucleic acid base.
487         assignNucleicAcidBaseAtomTypes(nucleicAcid, residue, sugarC1, sugarO4, sugarC2, forceField,
488             bondList);
489       }
490 
491       // Do some checks on the current base to make sure all atoms have been assigned an atom type.
492       resAtoms = residue.getAtomList();
493       for (Atom atom : resAtoms) {
494         AtomType atomType = atom.getAtomType();
495         if (atomType == null) {
496           throw new MissingAtomTypeException(residue, atom);
497         }
498         int numberOfBonds = atom.getNumBonds();
499         if (numberOfBonds != atomType.valence) {
500           if (atom == sugarO3 && numberOfBonds == atomType.valence - 1 && position != LAST_RESIDUE
501               && numberOfResidues != 1) {
502             continue;
503           }
504           logger.warning(
505               format(" An atom for residue %s has the wrong number of bonds:\n %s", residueName,
506                   atom));
507           logger.info(format(" Expected: %d Actual: %d.", atomType.valence, numberOfBonds));
508           for (Bond bond : atom.getBonds()) {
509             logger.info(" " + bond.toString());
510           }
511         }
512       }
513 
514       // Save a reference to the current O3* oxygen.
515       pSugarO3 = sugarO3;
516     }
517   }
518 
519   private static boolean isThreePrimeCap(int nAtoms, List<Atom> resAtoms) {
520     boolean threePrimeCap = false;
521     if (nAtoms < 7) {
522       boolean unrecognized = false;
523       for (Atom atom : resAtoms) {
524         String atomName = atom.getName();
525         // Recognized: P, OP[1-3], O5' (will be renamed to OP3), HOP[1-3], and DOP[1-3])
526         if (!(atomName.equals("P") || atomName.matches("[HD]?OP[1-3]") || atomName.matches(
527             "O5'"))) {
528           unrecognized = true;
529           break;
530         }
531       }
532       threePrimeCap = !unrecognized;
533     }
534     return threePrimeCap;
535   }
536 
537   /**
538    * Assign atom types to the nucleic acid base.
539    *
540    * @param nucleicAcid The nucleic acid base to use.
541    * @param residue The residue node.
542    * @param C1s The C1* attachment atom.
543    * @param O4s The O4* attachment atom.
544    * @param C2s The C2* attachment atom.
545    * @param forceField The ForceField in use.
546    * @param bondList List of created bonds.
547    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
548    * @since 1.0
549    */
550   private static void assignNucleicAcidBaseAtomTypes(NucleicAcid3 nucleicAcid, Residue residue,
551       Atom C1s, Atom O4s, Atom C2s, ForceField forceField, List<Bond> bondList)
552       throws MissingHeavyAtomException, MissingAtomTypeException {
553     double glyco = 0.0; // DEFAULT - should it be more like 240?
554 
555     // glycosyl torsion: pyrimidines:O4′-C1′-N1-C2 | purines:O4′-C1′-N9-C4
556     Atom o4p = residue.getAtomByName("O4'", true);
557     Atom c1p = residue.getAtomByName("C1'", true);
558     Atom baseN = null;
559     Atom baseC = null;
560     if (nucleicAcid == NucleicAcid3.DAD || nucleicAcid == NucleicAcid3.DGU) {
561       baseN = residue.getAtomByName("N9", true);
562       baseC = residue.getAtomByName("C4", true);
563     } else if (nucleicAcid == NucleicAcid3.DCY || nucleicAcid == NucleicAcid3.DTY) {
564       baseN = residue.getAtomByName("N1", true);
565       baseC = residue.getAtomByName("C2", true);
566     }
567 
568     if (o4p != null && c1p != null && baseN != null && baseC != null) {
569       double angle = DoubleMath.dihedralAngle(o4p.getXYZ(null), c1p.getXYZ(null), baseN.getXYZ(null), baseC.getXYZ(null));
570       glyco = toDegrees(angle);
571     }
572 
573     switch (nucleicAcid) {
574       case ADE -> buildADE(residue, C1s, O4s, C2s, glyco, forceField, bondList);
575       case M1MA -> buildM1MA(residue, C1s, forceField, bondList);
576       case CYT -> buildCYT(residue, C1s, O4s, C2s, glyco, forceField, bondList);
577       case OMC -> buildOMC(residue, C1s, O4s, C2s, glyco, forceField, bondList);
578       case M5MC -> buildM5MC(residue, C1s, forceField, bondList);
579       case GUA -> buildGUA(residue, C1s, O4s, C2s, glyco, forceField, bondList);
580       case OMG -> buildOMG(residue, C1s, O4s, C2s, glyco, forceField, bondList);
581       case YYG -> buildYYG(residue, C1s, forceField, bondList);
582       case M2MG -> buildM2MG(residue, C1s, forceField, bondList);
583       case M2G -> buildM2G(residue, C1s, forceField, bondList);
584       case M7MG -> buildM7MG(residue, C1s, forceField, bondList);
585       case URI -> buildURI(residue, C1s, O4s, C2s, glyco, forceField, bondList);
586       case PSU -> buildPSU(residue, C1s, forceField, bondList);
587       case H2U -> buildH2U(residue, C1s, forceField, bondList);
588       case M5MU -> buildM5MU(residue, C1s, forceField, bondList);
589       case DAD -> buildDAD(residue, C1s, O4s, C2s, glyco, forceField, bondList);
590       case DCY -> buildDCY(residue, C1s, O4s, C2s, glyco, forceField, bondList);
591       case DGU -> buildDGU(residue, C1s, O4s, C2s, glyco, forceField, bondList);
592       case DTY -> buildDTY(residue, C1s, O4s, C2s, glyco, forceField, bondList);
593     }
594   }
595 
596   /**
597    * buildADE.
598    *
599    * @param residue a {@link ffx.potential.bonded.Residue} object.
600    * @param C1s a {@link ffx.potential.bonded.Atom} object.
601    * @param O4s a {@link ffx.potential.bonded.Atom} object.
602    * @param C2s a {@link ffx.potential.bonded.Atom} object.
603    * @param glyco a double.
604    * @param forceField a {@link ForceField} object.
605    * @param bondList a {@link java.util.List} object.
606    * @return a {@link ffx.potential.bonded.Residue} object.
607    */
608   private static Residue buildADE(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
609       ForceField forceField, List<Bond> bondList) {
610     Atom N9 = buildHeavy(residue, "N9", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1017, forceField,
611         bondList);
612     Atom C8 = buildHeavy(residue, "C8", N9, 1.37, C1s, 128.4, O4s, glyco + 180.0, 0, 1021,
613         forceField, bondList);
614     Atom N7 = buildHeavy(residue, "N7", C8, 1.30, N9, 113.8, C1s, 180.0, 0, 1020, forceField,
615         bondList);
616     Atom C5 = buildHeavy(residue, "C5", N7, 1.39, C8, 104.0, N9, 0.0, 0, 1019, forceField, bondList);
617     Atom C6 = buildHeavy(residue, "C6", C5, 1.40, N7, 132.4, C8, 180.0, 0, 1025, forceField,
618         bondList);
619     Atom N6 = buildHeavy(residue, "N6", C6, 1.34, C5, 123.5, N7, 0.0, 0, 1027, forceField, bondList);
620     Atom N1 = buildHeavy(residue, "N1", C6, 1.35, C5, 117.4, N7, 180.0, 0, 1024, forceField,
621         bondList);
622     Atom C2 = buildHeavy(residue, "C2", N1, 1.33, C6, 118.8, C5, 0.0, 0, 1023, forceField, bondList);
623     Atom N3 = buildHeavy(residue, "N3", C2, 1.32, N1, 129.2, C6, 0.0, 0, 1022, forceField, bondList);
624     Atom C4 = buildHeavy(residue, "C4", N3, 1.35, C2, 110.9, N1, 0.0, 0, 1018, forceField, bondList);
625     buildBond(C4, C5, forceField, bondList);
626     buildBond(C4, N9, forceField, bondList);
627     buildH(residue, "H8", C8, 1.08e0, N7, 123.1e0, C5, 180.0e0, 0, 1030, forceField, bondList);
628     buildH(residue, "H61", N6, 1.00e0, C6, 120.0e0, N7, 180.0e0, 0, 1028, forceField, bondList);
629     buildH(residue, "H62", N6, 1.00e0, C6, 120.0e0, N7, 0.0e0, 0, 1029, forceField, bondList);
630     buildH(residue, "H2", C2, 1.08e0, N3, 115.4e0, C4, 180.0e0, 0, 1026, forceField, bondList);
631     return residue;
632   }
633 
634   /**
635    * buildCYT.
636    *
637    * @param residue a {@link ffx.potential.bonded.Residue} object.
638    * @param C1s a {@link ffx.potential.bonded.Atom} object.
639    * @param O4s a {@link ffx.potential.bonded.Atom} object.
640    * @param C2s a {@link ffx.potential.bonded.Atom} object.
641    * @param glyco a double.
642    * @param forceField a {@link ForceField} object.
643    * @param bondList a {@link java.util.List} object.
644    * @return a {@link ffx.potential.bonded.Residue} object.
645    */
646   private static Residue buildCYT(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
647       ForceField forceField, List<Bond> bondList) {
648     Atom N1 = buildHeavy(residue, "N1", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1078, forceField,
649         bondList);
650     Atom C2 = buildHeavy(residue, "C2", N1, 1.37, C1s, 117.8, O4s, glyco + 180, 0, 1079, forceField,
651         bondList);
652     Atom O2 = buildHeavy(residue, "O2", C2, 1.24, N1, 118.9, C1s, 0.0, 0, 1084, forceField,
653         bondList);
654     Atom N3 = buildHeavy(residue, "N3", C2, 1.38, N1, 118.7, C1s, 180.0, 0, 1080, forceField,
655         bondList);
656     Atom C4 = buildHeavy(residue, "C4", N3, 1.34, C2, 120.6, N1, 0.0, 0, 1081, forceField, bondList);
657     Atom N4 = buildHeavy(residue, "N4", C4, 1.32, N3, 118.3, O2, 180.0, 0, 1085, forceField,
658         bondList);
659     Atom C5 = buildHeavy(residue, "C5", C4, 1.43, N3, 121.6, C2, 0.0, 0, 1082, forceField, bondList);
660     Atom C6 = buildHeavy(residue, "C6", C5, 1.36, C4, 116.9, N3, 0.0, 0, 1083, forceField, bondList);
661     buildBond(C6, N1, forceField, bondList);
662     buildH(residue, "H41", N4, 1.00e0, C4, 120.0e0, N3, 0.0e0, 0, 1086, forceField, bondList);
663     buildH(residue, "H42", N4, 1.00e0, C4, 120.0e0, N3, 180.0e0, 0, 1087, forceField, bondList);
664     buildH(residue, "H5", C5, 1.08e0, C4, 121.6e0, N3, 180.0e0, 0, 1088, forceField, bondList);
665     buildH(residue, "H6", C6, 1.08e0, C5, 119.4e0, C4, 180.0e0, 0, 1089, forceField, bondList);
666     return residue;
667   }
668 
669   /**
670    * buildDAD.
671    *
672    * @param residue a {@link ffx.potential.bonded.Residue} object.
673    * @param C1s a {@link ffx.potential.bonded.Atom} object.
674    * @param O4s a {@link ffx.potential.bonded.Atom} object.
675    * @param C2s a {@link ffx.potential.bonded.Atom} object.
676    * @param glyco a double.
677    * @param forceField a {@link ForceField} object.
678    * @param bondList a {@link java.util.List} object.
679    * @return a {@link ffx.potential.bonded.Residue} object.
680    */
681   private static Residue buildDAD(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
682       ForceField forceField, List<Bond> bondList) {
683 
684     Atom N9 = buildHeavy(residue, "N9", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1132, forceField,
685         bondList);
686     Atom C8 = buildHeavy(residue, "C8", N9, 1.37, C1s, 128.4, O4s, glyco + 180, 0, 1136, forceField,
687         bondList);
688     Atom N7 = buildHeavy(residue, "N7", C8, 1.30, N9, 113.8, C1s, 180.0, 0, 1135, forceField,
689         bondList);
690     Atom C5 = buildHeavy(residue, "C5", N7, 1.39, C8, 104.0, N9, 0.0, 0, 1134, forceField, bondList);
691     Atom C6 = buildHeavy(residue, "C6", C5, 1.40, N7, 132.4, C8, 180.0, 0, 1140, forceField,
692         bondList);
693     Atom N6 = buildHeavy(residue, "N6", C6, 1.34, C5, 123.5, N7, 0.0, 0, 1142, forceField, bondList);
694     Atom N1 = buildHeavy(residue, "N1", C6, 1.35, C5, 117.4, N7, 180.0, 0, 1139, forceField,
695         bondList);
696     Atom C2 = buildHeavy(residue, "C2", N1, 1.33, C6, 118.8, C5, 0.0, 0, 1138, forceField, bondList);
697     Atom N3 = buildHeavy(residue, "N3", C2, 1.32, N1, 129.2, C6, 0.0, 0, 1137, forceField, bondList);
698     Atom C4 = buildHeavy(residue, "C4", N3, 1.35, C2, 110.9, N1, 0.0, 0, 1133, forceField, bondList);
699     buildBond(C4, C5, forceField, bondList);
700     buildBond(C4, N9, forceField, bondList);
701     buildH(residue, "H8", C8, 1.08e0, N7, 123.1e0, C5, 180.0e0, 0, 1145, forceField, bondList);
702     buildH(residue, "H61", N6, 1.00e0, C6, 120.0e0, N7, 180.0e0, 0, 1143, forceField, bondList);
703     buildH(residue, "H62", N6, 1.00e0, C6, 120.0e0, N7, 0.0e0, 0, 1144, forceField, bondList);
704     buildH(residue, "H2", C2, 1.08e0, N3, 115.4e0, C4, 180.0e0, 0, 1141, forceField, bondList);
705     return residue;
706   }
707 
708   /**
709    * buildDCY.
710    *
711    * @param residue a {@link ffx.potential.bonded.Residue} object.
712    * @param C1s a {@link ffx.potential.bonded.Atom} object.
713    * @param O4s a {@link ffx.potential.bonded.Atom} object.
714    * @param C2s a {@link ffx.potential.bonded.Atom} object.
715    * @param glyco a double.
716    * @param forceField a {@link ForceField} object.
717    * @param bondList a {@link java.util.List} object.
718    * @return a {@link ffx.potential.bonded.Residue} object.
719    */
720   private static Residue buildDCY(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
721       ForceField forceField, List<Bond> bondList) {
722 
723     Atom N1 = buildHeavy(residue, "N1", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1191, forceField,
724         bondList);
725     Atom C2 = buildHeavy(residue, "C2", N1, 1.37, C1s, 117.8, O4s, glyco, 0, 1192, forceField,
726         bondList);
727     Atom O2 = buildHeavy(residue, "O2", C2, 1.24, N1, 118.9, C1s, 0.0, 0, 1197, forceField,
728         bondList);
729     Atom N3 = buildHeavy(residue, "N3", C2, 1.38, N1, 118.7, C1s, 180, 0, 1193, forceField,
730         bondList);
731     Atom C4 = buildHeavy(residue, "C4", N3, 1.34, C2, 120.6, N1, 0.0, 0, 1194, forceField, bondList);
732     Atom N4 = buildHeavy(residue, "N4", C4, 1.32, N3, 118.3, C2, 180.0, 0, 1198, forceField,
733         bondList);
734     Atom C5 = buildHeavy(residue, "C5", C4, 1.43, N3, 121.6, C2, 0.0, 0, 1195, forceField, bondList);
735     Atom C6 = buildHeavy(residue, "C6", C5, 1.36, C4, 116.9, N3, 0.0, 0, 1196, forceField, bondList);
736     buildBond(C6, N1, forceField, bondList);
737     buildH(residue, "H41", N4, 1.00e0, C4, 120.0e0, N3, 0.0e0, 0, 1199, forceField, bondList);
738     buildH(residue, "H42", N4, 1.00e0, C4, 120.0e0, N3, 180.0e0, 0, 1200, forceField, bondList);
739     buildH(residue, "H5", C5, 1.08e0, C4, 121.6e0, N3, 180.0e0, 0, 1201, forceField, bondList);
740     buildH(residue, "H6", C6, 1.08e0, C5, 119.4e0, C4, 180.0e0, 0, 1202, forceField, bondList);
741     return residue;
742   }
743 
744   /**
745    * buildDGU.
746    *
747    * @param residue a {@link ffx.potential.bonded.Residue} object.
748    * @param C1s a {@link ffx.potential.bonded.Atom} object.
749    * @param O4s a {@link ffx.potential.bonded.Atom} object.
750    * @param C2s a {@link ffx.potential.bonded.Atom} object.
751    * @param glyco a double.
752    * @param forceField a {@link ForceField} object.
753    * @param bondList a {@link java.util.List} object.
754    * @return a {@link ffx.potential.bonded.Residue} object.
755    */
756   private static Residue buildDGU(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
757       ForceField forceField, List<Bond> bondList) {
758 
759     Atom N9 = buildHeavy(residue, "N9", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1161, forceField,
760         bondList);
761     Atom C8 = buildHeavy(residue, "C8", N9, 1.38, C1s, 128.4, O4s, glyco + 180, 0, 1165, forceField,
762         bondList);
763     Atom N7 = buildHeavy(residue, "N7", C8, 1.31, N9, 114.0, C1s, 180.0, 0, 1164, forceField,
764         bondList);
765     Atom C5 = buildHeavy(residue, "C5", N7, 1.39, C8, 103.8, N9, 0.0, 0, 1163, forceField, bondList);
766     Atom C6 = buildHeavy(residue, "C6", C5, 1.40, N7, 130.1, C8, 180.0, 0, 1169, forceField,
767         bondList);
768     Atom O6 = buildHeavy(residue, "O6", C6, 1.23, C5, 128.8, N7, 0.0, 0, 1174, forceField, bondList);
769     Atom N1 = buildHeavy(residue, "N1", C6, 1.40, C5, 111.4, N7, 180.0, 0, 1168, forceField,
770         bondList);
771     Atom C2 = buildHeavy(residue, "C2", N1, 1.38, C6, 125.2, C5, 0.0, 0, 1167, forceField, bondList);
772     Atom N2 = buildHeavy(residue, "N2", C2, 1.34, N1, 116.1, C6, 180.0, 0, 1171, forceField,
773         bondList);
774     Atom N3 = buildHeavy(residue, "N3", C2, 1.33, N1, 123.3, O6, 0.0, 0, 1166, forceField, bondList);
775     Atom C4 = buildHeavy(residue, "C4", N3, 1.36, C2, 112.3, N1, 0.0, 0, 1162, forceField, bondList);
776     buildBond(C4, C5, forceField, bondList);
777     buildBond(C4, N9, forceField, bondList);
778     buildH(residue, "H8", C8, 1.08e0, N7, 123.0e0, C5, 180.0e0, 0, 1175, forceField, bondList);
779     buildH(residue, "H1", N1, 1.00e0, C6, 117.4e0, C5, 180.0e0, 0, 1170, forceField, bondList);
780     buildH(residue, "H21", N2, 1.00e0, C2, 120.0e0, N1, 0.0e0, 0, 1172, forceField, bondList);
781     buildH(residue, "H22", N2, 1.00e0, C2, 120.0e0, N1, 180.0e0, 0, 1173, forceField, bondList);
782     return residue;
783   }
784 
785   /**
786    * buildDTY.
787    *
788    * @param residue a {@link ffx.potential.bonded.Residue} object.
789    * @param C1s a {@link ffx.potential.bonded.Atom} object.
790    * @param O4s a {@link ffx.potential.bonded.Atom} object.
791    * @param C2s a {@link ffx.potential.bonded.Atom} object.
792    * @param glyco a double.
793    * @param forceField a {@link ForceField} object.
794    * @param bondList a {@link java.util.List} object.
795    * @return a {@link ffx.potential.bonded.Residue} object.
796    */
797   private static Residue buildDTY(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
798       ForceField forceField, List<Bond> bondList) {
799     Atom N1 = buildHeavy(residue, "N1", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1218, forceField,
800         bondList);
801     Atom C2 = buildHeavy(residue, "C2", N1, 1.37, C1s, 117.1, O4s, glyco, 0, 1219, forceField,
802         bondList);
803     Atom O2 = buildHeavy(residue, "O2", C2, 1.22, N1, 122.9, C1s, 0.0, 0, 1224, forceField,
804         bondList);
805     Atom N3 = buildHeavy(residue, "N3", C2, 1.38, N1, 115.4, C1s, 180.0, 0, 1220, forceField,
806         bondList);
807     Atom C4 = buildHeavy(residue, "C4", N3, 1.38, C2, 126.4, N1, 0.0, 0, 1221, forceField, bondList);
808     Atom O4 = buildHeavy(residue, "O4", C4, 1.23, N3, 120.5, C2, 180.0, 0, 1226, forceField,
809         bondList);
810     Atom C5 = buildHeavy(residue, "C5", C4, 1.44, N3, 114.1, C2, 0.0, 0, 1222, forceField, bondList);
811     Atom C7 = buildHeavy(residue, "C7", C5, 1.50, C4, 117.5, N3, 180.0, 0, 1227, forceField,
812         bondList);
813     Atom C6 = buildHeavy(residue, "C6", C5, 1.34, C4, 120.8, N3, 0.0, 0, 1223, forceField, bondList);
814     buildBond(C6, N1, forceField, bondList);
815     buildH(residue, "H3", N3, 1.00e0, C2, 116.8e0, N1, 180.0e0, 0, 1225, forceField, bondList);
816     Atom H = buildH(residue, "H71", C7, 1.09e0, C5, 109.5e0, C4, 0.0e0, 0, 1228, forceField,
817         bondList);
818     buildH(residue, "H72", C7, 1.09e0, C5, 109.5e0, H, 109.5e0, 1, 1228, forceField, bondList);
819     buildH(residue, "H73", C7, 1.09e0, C5, 109.5e0, H, 109.5e0, -1, 1228, forceField, bondList);
820     buildH(residue, "H6", C6, 1.08e0, C5, 119.4e0, C4, 180.0e0, 0, 1229, forceField, bondList);
821     return residue;
822   }
823 
824   /**
825    * buildGUA.
826    *
827    * @param residue a {@link ffx.potential.bonded.Residue} object.
828    * @param C1s a {@link ffx.potential.bonded.Atom} object.
829    * @param O4s a {@link ffx.potential.bonded.Atom} object.
830    * @param C2s a {@link ffx.potential.bonded.Atom} object.
831    * @param glyco a double.
832    * @param forceField a {@link ForceField} object.
833    * @param bondList a {@link java.util.List} object.
834    * @return a {@link ffx.potential.bonded.Residue} object.
835    */
836   private static Residue buildGUA(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
837       ForceField forceField, List<Bond> bondList) {
838     Atom N9 = buildHeavy(residue, "N9", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1047, forceField,
839         bondList);
840     Atom C8 = buildHeavy(residue, "C8", N9, 1.38, C1s, 128.4, O4s, glyco + 180, 0, 1051, forceField,
841         bondList);
842     Atom N7 = buildHeavy(residue, "N7", C8, 1.31, N9, 114.0, C1s, 180.0, 0, 1050, forceField,
843         bondList);
844     Atom C5 = buildHeavy(residue, "C5", N7, 1.39, C8, 103.8, N9, 0.0, 0, 1049, forceField, bondList);
845     Atom C6 = buildHeavy(residue, "C6", C5, 1.40, N7, 130.1, C8, 180.0, 0, 1055, forceField,
846         bondList);
847     Atom O6 = buildHeavy(residue, "O6", C6, 1.23, C5, 128.8, N7, 0.0, 0, 1060, forceField, bondList);
848     Atom N1 = buildHeavy(residue, "N1", C6, 1.40, C5, 111.4, N7, 180.0, 0, 1054, forceField,
849         bondList);
850     Atom C2 = buildHeavy(residue, "C2", N1, 1.38, C6, 125.2, C5, 0.0, 0, 1053, forceField, bondList);
851     Atom N2 = buildHeavy(residue, "N2", C2, 1.34, N1, 116.1, C6, 180.0, 0, 1057, forceField,
852         bondList);
853     Atom N3 = buildHeavy(residue, "N3", C2, 1.33, N1, 123.3, O6, 0.0, 0, 1052, forceField, bondList);
854     Atom C4 = buildHeavy(residue, "C4", N3, 1.36, C2, 112.3, N1, 0.0, 0, 1048, forceField, bondList);
855     buildBond(C4, C5, forceField, bondList);
856     buildBond(C4, N9, forceField, bondList);
857     buildH(residue, "H8", C8, 1.08e0, N7, 123.0e0, C5, 180.0e0, 0, 1061, forceField, bondList);
858     buildH(residue, "H1", N1, 1.00e0, C6, 117.4e0, C5, 180.0e0, 0, 1056, forceField, bondList);
859     buildH(residue, "H21", N2, 1.00e0, C2, 120.0e0, N1, 0.0e0, 0, 1058, forceField, bondList);
860     buildH(residue, "H22", N2, 1.00e0, C2, 120.0e0, N1, 180.0e0, 0, 1059, forceField, bondList);
861     return residue;
862   }
863 
864   /**
865    * buildH2U.
866    *
867    * @param residue a {@link ffx.potential.bonded.Residue} object.
868    * @param C1s a {@link ffx.potential.bonded.Atom} object.
869    * @param forceField a {@link ForceField} object.
870    * @param bondList a {@link java.util.List} object.
871    * @return a {@link ffx.potential.bonded.Residue} object.
872    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
873    */
874   private static Residue buildH2U(Residue residue, Atom C1s, ForceField forceField,
875       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
876 
877     Atom N1 = buildHeavy(residue, "N1", C1s, 1350, forceField, bondList);
878     Atom C2 = buildHeavy(residue, "C2", N1, 1351, forceField, bondList);
879     Atom O2 = buildHeavy(residue, "O2", C2, 1356, forceField, bondList);
880     Atom N3 = buildHeavy(residue, "N3", C2, 1352, forceField, bondList);
881     Atom C4 = buildHeavy(residue, "C4", N3, 1353, forceField, bondList);
882     Atom O4 = buildHeavy(residue, "O4", C4, 1358, forceField, bondList);
883     Atom C5 = buildHeavy(residue, "C5", C4, 1354, forceField, bondList);
884     Atom C6 = buildHeavy(residue, "C6", C5, 1355, forceField, bondList);
885     buildBond(C6, N1, forceField, bondList);
886     buildH(residue, "H3", N3, 1.00e0, C2, 116.5e0, N1, 180.0e0, 0, 1357, forceField, bondList);
887     buildH(residue, "H51", C5, 1.08e0, C4, 109.5e0, C6, 109.5e0, 1, 1359, forceField, bondList);
888     buildH(residue, "H52", C5, 1.08e0, C4, 109.5e0, C6, 109.5e0, -1, 1360, forceField, bondList);
889     buildH(residue, "H61", C6, 1.08e0, C5, 109.5e0, N1, 109.5e0, 1, 1361, forceField, bondList);
890     buildH(residue, "H62", C6, 1.08e0, C5, 109.5e0, N1, 109.5e0, -1, 1362, forceField, bondList);
891     return residue;
892   }
893 
894   /**
895    * buildM1MA.
896    *
897    * @param residue a {@link ffx.potential.bonded.Residue} object.
898    * @param C1s a {@link ffx.potential.bonded.Atom} object.
899    * @param forceField a {@link ForceField} object.
900    * @param bondList a {@link java.util.List} object.
901    * @return a {@link ffx.potential.bonded.Residue} object.
902    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
903    */
904   private static Residue buildM1MA(Residue residue, Atom C1s, ForceField forceField,
905       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
906     Atom N9 = buildHeavy(residue, "N9", C1s, 1605, forceField, bondList);
907     Atom C8 = buildHeavy(residue, "C8", N9, 1609, forceField, bondList);
908     Atom N7 = buildHeavy(residue, "N7", C8, 1608, forceField, bondList);
909     Atom C5 = buildHeavy(residue, "C5", N7, 1607, forceField, bondList);
910     Atom C6 = buildHeavy(residue, "C6", C5, 1613, forceField, bondList);
911     Atom N6 = buildHeavy(residue, "N6", C6, 1615, forceField, bondList);
912     Atom N1 = buildHeavy(residue, "N1", C6, 1612, forceField, bondList);
913     Atom C2 = buildHeavy(residue, "C2", N1, 1611, forceField, bondList);
914     Atom N3 = buildHeavy(residue, "N3", C2, 1610, forceField, bondList);
915     Atom C4 = buildHeavy(residue, "C4", N3, 1606, forceField, bondList);
916     Atom CM1 = buildHeavy(residue, "CM1", N1, 1619, forceField, bondList);
917     buildBond(C4, C5, forceField, bondList);
918     buildBond(C4, N9, forceField, bondList);
919     buildH(residue, "H2", C2, 1.08e0, N3, 115.4e0, C4, 180.0e0, 0, 1614, forceField, bondList);
920     buildH(residue, "H6", C6, 1.08e0, C5, 109.5e0, C4, 180.0e0, 0, 1623, forceField, bondList);
921     buildH(residue, "H8", C8, 1.08e0, N7, 123.1e0, C5, 180.0e0, 0, 1618, forceField, bondList);
922     buildH(residue, "HN61", N6, 1.00e0, C6, 109.5e0, C5, 0.0e0, 0, 1616, forceField, bondList);
923     buildH(residue, "HN62", N6, 1.00e0, C6, 109.5e0, C5, 109.5e0, 0, 1617, forceField, bondList);
924     Atom HM11 = buildH(residue, "HM11", CM1, 1.08e0, N1, 109.5e0, C2, 0.0e0, 0, 1620, forceField,
925         bondList);
926     buildH(residue, "HM12", CM1, 1.08e0, N1, 109.5e0, HM11, 109.5e0, 1, 1621, forceField, bondList);
927     buildH(residue, "HM13", CM1, 1.08e0, N1, 109.5e0, HM11, 109.5e0, -1, 1622, forceField, bondList);
928     return residue;
929   }
930 
931   /**
932    * buildM2G.
933    *
934    * @param residue a {@link ffx.potential.bonded.Residue} object.
935    * @param C1s a {@link ffx.potential.bonded.Atom} object.
936    * @param forceField a {@link ForceField} object.
937    * @param bondList a {@link java.util.List} object.
938    * @return a {@link ffx.potential.bonded.Residue} object.
939    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
940    */
941   private static Residue buildM2G(Residue residue, Atom C1s, ForceField forceField,
942       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
943     Atom N9 = buildHeavy(residue, "N9", C1s, 1379, forceField, bondList);
944     Atom C8 = buildHeavy(residue, "C8", N9, 1383, forceField, bondList);
945     Atom N7 = buildHeavy(residue, "N7", C8, 1382, forceField, bondList);
946     Atom C5 = buildHeavy(residue, "C5", N7, 1381, forceField, bondList);
947     Atom C6 = buildHeavy(residue, "C6", C5, 1387, forceField, bondList);
948     Atom O6 = buildHeavy(residue, "O6", C6, 1390, forceField, bondList);
949     Atom N1 = buildHeavy(residue, "N1", C6, 1386, forceField, bondList);
950     Atom C2 = buildHeavy(residue, "C2", N1, 1385, forceField, bondList);
951     Atom N2 = buildHeavy(residue, "N2", C2, 1389, forceField, bondList);
952     Atom N3 = buildHeavy(residue, "N3", C2, 1384, forceField, bondList);
953     Atom C4 = buildHeavy(residue, "C4", N3, 1380, forceField, bondList);
954     Atom CM1 = buildHeavy(residue, "CM1", N2, 1392, forceField, bondList);
955     Atom CM2 = buildHeavy(residue, "CM2", N2, 1396, forceField, bondList);
956     buildBond(C4, C5, forceField, bondList);
957     buildBond(C4, N9, forceField, bondList);
958     buildH(residue, "H8", C8, 1.08e0, N7, 123.0e0, C5, 180.0e0, 0, 1391, forceField, bondList);
959     buildH(residue, "H1", N1, 1.00e0, C6, 117.4e0, C5, 180.0e0, 0, 1388, forceField, bondList);
960     Atom HM11 = buildH(residue, "HM11", CM1, 1.08e0, N2, 109.5e0, C2, 0.0e0, 0, 1393, forceField,
961         bondList);
962     buildH(residue, "HM12", CM1, 1.08e0, N2, 109.5e0, HM11, 109.5e0, 1, 1394, forceField, bondList);
963     buildH(residue, "HM13", CM1, 1.08e0, N2, 109.5e0, HM11, 109.5e0, -1, 1395, forceField, bondList);
964     Atom HM21 = buildH(residue, "HM21", CM2, 1.08e0, N2, 109.5e0, C2, 0.0e0, 0, 1397, forceField,
965         bondList);
966     buildH(residue, "HM22", CM2, 1.08e0, N2, 109.5e0, HM21, 109.5e0, 1, 1398, forceField, bondList);
967     buildH(residue, "HM23", CM2, 1.08e0, N2, 109.5e0, HM21, 109.5e0, -1, 1399, forceField, bondList);
968     return residue;
969   }
970 
971   /**
972    * buildM2MG.
973    *
974    * @param residue a {@link ffx.potential.bonded.Residue} object.
975    * @param C1s a {@link ffx.potential.bonded.Atom} object.
976    * @param forceField a {@link ForceField} object.
977    * @param bondList a {@link java.util.List} object.
978    * @return a {@link ffx.potential.bonded.Residue} object.
979    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
980    */
981   private static Residue buildM2MG(Residue residue, Atom C1s, ForceField forceField,
982       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
983     Atom N9 = buildHeavy(residue, "N9", C1s, 1316, forceField, bondList);
984     Atom C8 = buildHeavy(residue, "C8", N9, 1320, forceField, bondList);
985     Atom N7 = buildHeavy(residue, "N7", C8, 1319, forceField, bondList);
986     Atom C5 = buildHeavy(residue, "C5", N7, 1318, forceField, bondList);
987     Atom C6 = buildHeavy(residue, "C6", C5, 1324, forceField, bondList);
988     Atom O6 = buildHeavy(residue, "O6", C6, 1328, forceField, bondList);
989     Atom N1 = buildHeavy(residue, "N1", C6, 1323, forceField, bondList);
990     Atom C2 = buildHeavy(residue, "C2", N1, 1322, forceField, bondList);
991     Atom N2 = buildHeavy(residue, "N2", C2, 1326, forceField, bondList);
992     Atom N3 = buildHeavy(residue, "N3", C2, 1321, forceField, bondList);
993     Atom C4 = buildHeavy(residue, "C4", N3, 1317, forceField, bondList);
994     Atom CM2 = buildHeavy(residue, "CM2", N2, 1330, forceField, bondList);
995     buildBond(C4, C5, forceField, bondList);
996     buildBond(C4, N9, forceField, bondList);
997     buildH(residue, "H8", C8, 1.08e0, N7, 123.0e0, C5, 180.0e0, 0, 1329, forceField, bondList);
998     buildH(residue, "H1", N1, 1.00e0, C6, 117.4e0, C5, 180.0e0, 0, 1325, forceField, bondList);
999     buildH(residue, "H2", N2, 1.00e0, C2, 120.0e0, N1, 0.0e0, 0, 1327, forceField, bondList);
1000     Atom HM21 = buildH(residue, "HM21", CM2, 1.08e0, N2, 109.5e0, C2, 0.0e0, 0, 1331, forceField,
1001         bondList);
1002     buildH(residue, "HM22", CM2, 1.08e0, N2, 109.5e0, HM21, 109.5e0, 1, 1332, forceField, bondList);
1003     buildH(residue, "HM23", CM2, 1.08e0, N2, 109.5e0, HM21, 109.5e0, -1, 1333, forceField, bondList);
1004     return residue;
1005   }
1006 
1007   /**
1008    * buildM5MC.
1009    *
1010    * @param residue a {@link ffx.potential.bonded.Residue} object.
1011    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1012    * @param forceField a {@link ForceField} object.
1013    * @param bondList a {@link java.util.List} object.
1014    * @return a {@link ffx.potential.bonded.Residue} object.
1015    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
1016    */
1017   private static Residue buildM5MC(Residue residue, Atom C1s, ForceField forceField,
1018       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
1019     Atom N1 = buildHeavy(residue, "N1", C1s, 1508, forceField, bondList);
1020     Atom C2 = buildHeavy(residue, "C2", N1, 1509, forceField, bondList);
1021     Atom O2 = buildHeavy(residue, "O2", C2, 1514, forceField, bondList);
1022     Atom N3 = buildHeavy(residue, "N3", C2, 1510, forceField, bondList);
1023     Atom C4 = buildHeavy(residue, "C4", N3, 1511, forceField, bondList);
1024     Atom N4 = buildHeavy(residue, "N4", C4, 1515, forceField, bondList);
1025     Atom C5 = buildHeavy(residue, "C5", C4, 1512, forceField, bondList);
1026     Atom C6 = buildHeavy(residue, "C6", C5, 1513, forceField, bondList);
1027     Atom CM5 = buildHeavy(residue, "CM5", C5, 1519, forceField, bondList);
1028     buildBond(C6, N1, forceField, bondList);
1029     buildH(residue, "H41", N4, 1.00e0, C4, 120.0e0, N3, 0.0e0, 0, 1516, forceField, bondList);
1030     buildH(residue, "H42", N4, 1.00e0, C4, 120.0e0, C5, 0.0e0, 0, 1517, forceField, bondList);
1031     buildH(residue, "H6", C6, 1.08e0, C5, 119.4e0, C4, 180.0e0, 0, 1518, forceField, bondList);
1032     Atom HM51 = buildH(residue, "HM51", CM5, 1.08e0, C5, 109.5e0, C4, 0.0e0, 0, 1520, forceField,
1033         bondList);
1034     buildH(residue, "HM52", CM5, 1.08e0, C5, 109.5e0, HM51, 109.5e0, 1, 1521, forceField, bondList);
1035     buildH(residue, "HM53", CM5, 1.08e0, C5, 109.5e0, HM51, 109.5e0, -1, 1522, forceField, bondList);
1036     return residue;
1037   }
1038 
1039   /**
1040    * buildM5MU.
1041    *
1042    * @param residue a {@link ffx.potential.bonded.Residue} object.
1043    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1044    * @param forceField a {@link ForceField} object.
1045    * @param bondList a {@link java.util.List} object.
1046    * @return a {@link ffx.potential.bonded.Residue} object.
1047    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
1048    */
1049   private static Residue buildM5MU(Residue residue, Atom C1s, ForceField forceField,
1050       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
1051 
1052     Atom N1 = buildHeavy(residue, "N1", C1s, 1575, forceField, bondList);
1053     Atom C2 = buildHeavy(residue, "C2", N1, 1576, forceField, bondList);
1054     Atom O2 = buildHeavy(residue, "O2", C2, 1581, forceField, bondList);
1055     Atom N3 = buildHeavy(residue, "N3", C2, 1577, forceField, bondList);
1056     Atom C4 = buildHeavy(residue, "C4", N3, 1578, forceField, bondList);
1057     Atom O4 = buildHeavy(residue, "O4", C4, 1583, forceField, bondList);
1058     Atom C5 = buildHeavy(residue, "C5", C4, 1579, forceField, bondList);
1059     Atom C6 = buildHeavy(residue, "C6", C5, 1580, forceField, bondList);
1060     Atom C5M = buildHeavy(residue, "C5M", C5, 1585, forceField, bondList);
1061     buildBond(C6, N1, forceField, bondList);
1062     buildH(residue, "H3", N3, 1.00e0, C2, 116.5e0, N1, 180.0e0, 0, 1582, forceField, bondList);
1063     buildH(residue, "H6", C6, 1.08e0, C5, 118.6e0, C4, 180.0e0, 0, 1584, forceField, bondList);
1064     Atom H5M1 = buildH(residue, "H5M1", C5M, 1.08e0, C5, 109.5e0, C6, 0.0e0, 0, 1586, forceField,
1065         bondList);
1066     buildH(residue, "H5M2", C5M, 1.08e0, C5, 109.5e0, H5M1, 109.5e0, 1, 1587, forceField, bondList);
1067     buildH(residue, "H5M3", C5M, 1.08e0, C5, 109.5e0, H5M1, 109.5e0, -1, 1588, forceField, bondList);
1068     return residue;
1069   }
1070 
1071   /**
1072    * buildM7MG.
1073    *
1074    * @param residue a {@link ffx.potential.bonded.Residue} object.
1075    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1076    * @param forceField a {@link ForceField} object.
1077    * @param bondList a {@link java.util.List} object.
1078    * @return a {@link ffx.potential.bonded.Residue} object.
1079    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
1080    */
1081   private static Residue buildM7MG(Residue residue, Atom C1s, ForceField forceField,
1082       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
1083     Atom N9 = buildHeavy(residue, "N9", C1s, 1539, forceField, bondList);
1084     Atom C8 = buildHeavy(residue, "C8", N9, 1543, forceField, bondList);
1085     Atom N7 = buildHeavy(residue, "N7", C8, 1542, forceField, bondList);
1086     Atom C5 = buildHeavy(residue, "C5", N7, 1541, forceField, bondList);
1087     Atom C6 = buildHeavy(residue, "C6", C5, 1547, forceField, bondList);
1088     Atom O6 = buildHeavy(residue, "O6", C6, 1552, forceField, bondList);
1089     Atom N1 = buildHeavy(residue, "N1", C6, 1546, forceField, bondList);
1090     Atom C2 = buildHeavy(residue, "C2", N1, 1545, forceField, bondList);
1091     Atom N2 = buildHeavy(residue, "N2", C2, 1549, forceField, bondList);
1092     Atom N3 = buildHeavy(residue, "N3", C2, 1544, forceField, bondList);
1093     Atom C4 = buildHeavy(residue, "C4", N3, 1540, forceField, bondList);
1094     Atom CM7 = buildHeavy(residue, "CM7", N7, 1555, forceField, bondList);
1095     buildBond(C4, C5, forceField, bondList);
1096     buildBond(C4, N9, forceField, bondList);
1097     buildH(residue, "H81", C8, 1.08e0, N7, 109.5e0, N9, 109.5e0, 1, 1553, forceField, bondList);
1098     buildH(residue, "H82", C8, 1.08e0, N7, 109.5e0, N9, 109.5e0, -1, 1554, forceField, bondList);
1099     buildH(residue, "H1", N1, 1.00e0, C6, 117.4e0, C5, 180.0e0, 0, 1548, forceField, bondList);
1100     buildH(residue, "H21", N2, 1.00e0, C2, 120.0e0, N1, 0.0e0, 0, 1550, forceField, bondList);
1101     buildH(residue, "H22", N2, 1.00e0, C2, 120.0e0, N3, 0.0e0, 0, 1551, forceField, bondList);
1102     Atom HM71 = buildH(residue, "HM71", CM7, 1.08e0, N7, 109.5e0, C8, 0.0e0, 0, 1556, forceField,
1103         bondList);
1104     buildH(residue, "HM72", CM7, 1.08e0, N7, 109.5e0, HM71, 109.5e0, 1, 1557, forceField, bondList);
1105     buildH(residue, "HM73", CM7, 1.08e0, N7, 109.5e0, HM71, 109.5e0, -1, 1558, forceField, bondList);
1106     return residue;
1107   }
1108 
1109   /**
1110    * buildOMC.
1111    *
1112    * @param residue a {@link ffx.potential.bonded.Residue} object.
1113    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1114    * @param O4s a {@link ffx.potential.bonded.Atom} object.
1115    * @param C2s a {@link ffx.potential.bonded.Atom} object.
1116    * @param glyco a double.
1117    * @param forceField a {@link ForceField} object.
1118    * @param bondList a {@link java.util.List} object.
1119    * @return a {@link ffx.potential.bonded.Residue} object.
1120    */
1121   private static Residue buildOMC(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
1122       ForceField forceField, List<Bond> bondList) {
1123     return buildCYT(residue, C1s, O4s, C2s, glyco, forceField, bondList);
1124   }
1125 
1126   /**
1127    * buildOMG.
1128    *
1129    * @param residue a {@link ffx.potential.bonded.Residue} object.
1130    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1131    * @param O4s a {@link ffx.potential.bonded.Atom} object.
1132    * @param C2s a {@link ffx.potential.bonded.Atom} object.
1133    * @param glyco a double.
1134    * @param forceField a {@link ForceField} object.
1135    * @param bondList a {@link java.util.List} object.
1136    * @return a {@link ffx.potential.bonded.Residue} object.
1137    */
1138   private static Residue buildOMG(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
1139       ForceField forceField, List<Bond> bondList) {
1140     return buildGUA(residue, C1s, O4s, C2s, glyco, forceField, bondList);
1141   }
1142 
1143   /**
1144    * Builds a missing OP3 atom. Can be applied either to 5'-terminal or 3'-terminal phosphates.
1145    *
1146    * @param residue Residue to build an OP3 for.
1147    * @param phosphate The phosphate atom.
1148    * @param aType The atom type to use.
1149    * @param forceField Force field in use.
1150    * @param bondList List of bonds.
1151    * @param adjacentResidue A Residue either 5' or 3' of residue.
1152    * @param at5prime If this residue is at the 5' terminus (vs. 3').
1153    * @throws MissingHeavyAtomException Thrown if a needed heavy atom is missing.
1154    */
1155   private static void buildOP3(Residue residue, Atom phosphate, int aType, ForceField forceField,
1156       List<Bond> bondList, Residue adjacentResidue, boolean at5prime)
1157       throws MissingHeavyAtomException, MissingAtomTypeException {
1158     Atom P = null;
1159     Atom OP1 = null;
1160     Atom OP2 = null;
1161     Atom riboOxygen = null;
1162     Atom riboCarbon = null;
1163     boolean foundOP3 = false;
1164 
1165     List<Atom> residueAtoms = residue.getAtomList();
1166     // Label for a break statement: OP3 causes a break from a switch to the surrounding for loop.
1167     AtomLoop:
1168     for (Atom at : residueAtoms) {
1169       String atName = at.getName().toUpperCase();
1170       switch (atName) {
1171         case "OP3":
1172           buildHeavy(residue, "OP3", phosphate, aType, forceField, bondList);
1173           foundOP3 = true;
1174           break AtomLoop;
1175         case "OP1":
1176           OP1 = at;
1177           break;
1178         case "OP2":
1179           OP2 = at;
1180           break;
1181         case "P":
1182           P = at;
1183           break;
1184         case "O5'":
1185           riboOxygen = at;
1186           break;
1187         case "C5'":
1188           riboCarbon = at;
1189           break;
1190       }
1191     }
1192 
1193     if (!at5prime) {
1194       riboOxygen = (Atom) adjacentResidue.getAtomNode("O3'");
1195       riboCarbon = (Atom) adjacentResidue.getAtomNode("C3'");
1196       if (riboCarbon == null || riboOxygen == null) {
1197         logger.severe(format(
1198             " Could not find either O3' " + "or C3' in residue %s prior to presumed 3' "
1199                 + "phosphate cap %s", adjacentResidue, residue));
1200       }
1201     }
1202 
1203     if (!foundOP3) {
1204       logger.fine(
1205           format(" EXPERIMENTAL: OP3 of residue %s not found, being rebuilt based on ideal geometry",
1206               residue));
1207       if (P == null || OP1 == null || OP2 == null) {
1208         throw new IllegalArgumentException(format(
1209             " Attempted to build OP3 for residue %s, but one of P, OP1, OP2, O5', or C5' were null",
1210             residue));
1211       }
1212       if (at5prime) {
1213         if (riboOxygen == null) {
1214           logger.fine(" Attempting to find O5' of subsequent residue");
1215           riboOxygen = getNextResAtom(residue, adjacentResidue, "O5'");
1216         }
1217         if (riboCarbon == null) {
1218           logger.fine(" Attempting to find C5' of subsequent residue");
1219           riboCarbon = getNextResAtom(residue, adjacentResidue, "C5'");
1220         }
1221       }
1222 
1223       // Borrow the P-OP1 distance.
1224       double[] xyzOP1 = new double[3];
1225       double[] xyzP = new double[3];
1226       xyzOP1 = OP1.getXYZ(xyzOP1);
1227       xyzP = P.getXYZ(xyzP);
1228       double distance = dist(xyzP, xyzOP1);
1229 
1230       // Borrow the O5'-P-OP1 angle.
1231       double[] xyzRiboO = new double[3];
1232       xyzRiboO = riboOxygen.getXYZ(xyzRiboO);
1233       double angle = toDegrees(DoubleMath.bondAngle(xyzRiboO, xyzP, xyzOP1));
1234 
1235       // Borrow the C5'-O5'-P-OP1 dihedral (which will have +120 or +240 degrees added on).
1236       double[] xyzRiboC = new double[3];
1237       xyzRiboC = riboCarbon.getXYZ(xyzRiboC);
1238       double dihedralOP1 = toDegrees(DoubleMath.dihedralAngle(xyzRiboC, xyzRiboO, xyzP, xyzOP1));
1239 
1240       Atom OP3 = buildH(residue, "OP3", P, distance, riboOxygen, angle, riboCarbon,
1241           dihedralOP1 + 120, 0, aType, forceField, bondList);
1242 
1243       // Measure OP3-OP2 distance for test dihedral + 120 degrees.
1244       double[] xyzOP2 = new double[3];
1245       xyzOP2 = OP2.getXYZ(xyzOP2);
1246       double[] xyzChiral1 = new double[3];
1247       xyzChiral1 = OP3.getXYZ(xyzChiral1);
1248       double distChiral1 = dist(xyzChiral1, xyzOP2);
1249 
1250       // Measure OP3-OP2 distance for test dihedral + 240 degrees.
1251       intxyz(OP3, P, distance, riboOxygen, angle, riboCarbon, dihedralOP1 + 240, 0);
1252       double[] xyzChiral2 = new double[3];
1253       xyzChiral2 = OP3.getXYZ(xyzChiral2);
1254       double distChiral2 = dist(xyzChiral2, xyzOP2);
1255 
1256       if (logger.isLoggable(Level.FINE)) {
1257         logger.fine(
1258             format(" Bond: %10.5f Angle: %10.5f Dihedral: %10.5f", distance, angle, dihedralOP1));
1259         logger.fine(format(" OP2 position: %s", Arrays.toString(xyzOP2)));
1260         logger.fine(format(" Position 1: %10.6g %10.6g %10.6g with distance %10.6g", xyzChiral1[0],
1261             xyzChiral1[1], xyzChiral1[2], distChiral1));
1262         logger.fine(format(" Position 2: %10.6g %10.6g %10.6g with distance %10.6g", xyzChiral2[0],
1263             xyzChiral2[1], xyzChiral2[2], distChiral2));
1264       }
1265       if (distChiral1 > distChiral2) {
1266         logger.fine(" Picked dihedral +120");
1267         OP3.setXYZ(xyzChiral1);
1268       } else {
1269         logger.fine(" Picked dihedral +240");
1270         OP3.setXYZ(xyzChiral2);
1271       }
1272     }
1273   }
1274 
1275   /**
1276    * buildPSU.
1277    *
1278    * @param residue a {@link ffx.potential.bonded.Residue} object.
1279    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1280    * @param forceField a {@link ForceField} object.
1281    * @param bondList a {@link java.util.List} object.
1282    * @return a {@link ffx.potential.bonded.Residue} object.
1283    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
1284    */
1285   private static Residue buildPSU(Residue residue, Atom C1s, ForceField forceField,
1286       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
1287     // C1s bonds to C5 in PsuedoUridine
1288     Atom C5 = buildHeavy(residue, "C5", C1s, 1485, forceField, bondList);
1289     Atom C6 = buildHeavy(residue, "C6", C5, 1486, forceField, bondList);
1290     Atom N1 = buildHeavy(residue, "N1", C6, 1481, forceField, bondList);
1291     Atom C2 = buildHeavy(residue, "C2", N1, 1482, forceField, bondList);
1292     Atom O2 = buildHeavy(residue, "O2", C2, 1487, forceField, bondList);
1293     Atom N3 = buildHeavy(residue, "N3", C2, 1483, forceField, bondList);
1294     Atom C4 = buildHeavy(residue, "C4", N3, 1484, forceField, bondList);
1295     Atom O4 = buildHeavy(residue, "O4", C4, 1489, forceField, bondList);
1296     buildBond(C4, C5, forceField, bondList);
1297     buildH(residue, "H1", N1, 1.00e0, C2, 120.0e0, O2, 0.0e0, 0, 1491, forceField, bondList);
1298     buildH(residue, "H3", N3, 1.00e0, C2, 120.0e0, O2, 0.0e0, 0, 1488, forceField, bondList);
1299     buildH(residue, "H6", C6, 1.08e0, C5, 120.0e0, C1s, 0.0e0, 0, 1490, forceField, bondList);
1300     return residue;
1301   }
1302 
1303   /**
1304    * Builds a sugar O5 atom. No bonds are made to it.
1305    *
1306    * @param residue The residue to operate on.
1307    * @param lookUp Biotype lookup.
1308    * @param forceField The ForceField to use.
1309    * @return The created sugar O5 atoms.
1310    */
1311   private static Atom buildSugarO5(Residue residue, int lookUp, ForceField forceField) {
1312     Atom C5 = residue.getAtomByName("C5'", true);
1313     Atom C4 = residue.getAtomByName("C4'", true);
1314     Atom C3 = residue.getAtomByName("C3'", true);
1315     return buildHeavy(residue, "O5'", C5, 1.43, C4, 109.5, C3, 180.0, 0, 1244, forceField);
1316   }
1317 
1318   /**
1319    * buildURI.
1320    *
1321    * @param residue a {@link ffx.potential.bonded.Residue} object.
1322    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1323    * @param O4s a {@link ffx.potential.bonded.Atom} object.
1324    * @param C2s a {@link ffx.potential.bonded.Atom} object.
1325    * @param glyco a double.
1326    * @param forceField a {@link ForceField} object.
1327    * @param bondList a {@link java.util.List} object.
1328    * @return a {@link ffx.potential.bonded.Residue} object.
1329    */
1330   private static Residue buildURI(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
1331       ForceField forceField, List<Bond> bondList) {
1332     Atom N1 = buildHeavy(residue, "N1", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1106, forceField,
1333         bondList);
1334     Atom C2 = buildHeavy(residue, "C2", N1, 1.38, C1s, 117.1, O4s, glyco, 0, 1107, forceField,
1335         bondList);
1336     Atom O2 = buildHeavy(residue, "O2", C2, 1.22, N1, 123.2, C1s, 0.0, 0, 1112, forceField,
1337         bondList);
1338     Atom N3 = buildHeavy(residue, "N3", C2, 1.37, N1, 114.8, C1s, 180.0, 0, 1108, forceField,
1339         bondList);
1340     Atom C4 = buildHeavy(residue, "C4", N3, 1.38, C2, 127.0, N1, 0.0, 0, 1109, forceField, bondList);
1341     Atom O4 = buildHeavy(residue, "O4", C4, 1.23, N3, 119.8, C2, 180.0, 0, 1114, forceField,
1342         bondList);
1343     Atom C5 = buildHeavy(residue, "C5", C4, 1.44, N3, 114.7, C2, 0.0, 0, 1110, forceField, bondList);
1344     Atom C6 = buildHeavy(residue, "C6", C5, 1.34, O4, 119.2, C4, 0.0, 0, 1111, forceField, bondList);
1345     buildBond(C6, N1, forceField, bondList);
1346     buildH(residue, "H3", N3, 1.00e0, C2, 116.5e0, N1, 180.0e0, 0, 1113, forceField, bondList);
1347     buildH(residue, "H5", C5, 1.08e0, C4, 120.4e0, N3, 180.0e0, 0, 1115, forceField, bondList);
1348     buildH(residue, "H6", C6, 1.08e0, C5, 118.6e0, C4, 180.0e0, 0, 1116, forceField, bondList);
1349 
1350     return residue;
1351   }
1352 
1353   /**
1354    * buildYYG.
1355    *
1356    * @param residue a {@link ffx.potential.bonded.Residue} object.
1357    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1358    * @param forceField a {@link ForceField} object.
1359    * @param bondList a {@link java.util.List} object.
1360    * @return a {@link ffx.potential.bonded.Residue} object.
1361    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
1362    */
1363   private static Residue buildYYG(Residue residue, Atom C1s, ForceField forceField,
1364       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
1365     Atom N9 = buildHeavy(residue, "N9", C1s, 1640, forceField, bondList);
1366     Atom C8 = buildHeavy(residue, "C8", N9, 1644, forceField, bondList);
1367     Atom N7 = buildHeavy(residue, "N7", C8, 1643, forceField, bondList);
1368     Atom C5 = buildHeavy(residue, "C5", N7, 1642, forceField, bondList);
1369     Atom C6 = buildHeavy(residue, "C6", C5, 1648, forceField, bondList);
1370     Atom O6 = buildHeavy(residue, "O6", C6, 1650, forceField, bondList);
1371     Atom N1 = buildHeavy(residue, "N1", C6, 1647, forceField, bondList);
1372     Atom C2 = buildHeavy(residue, "C2", N1, 1646, forceField, bondList);
1373     Atom N2 = buildHeavy(residue, "N2", C2, 1649, forceField, bondList);
1374     Atom N3 = buildHeavy(residue, "N3", C2, 1645, forceField, bondList);
1375     Atom C3 = buildHeavy(residue, "C3", N3, 1652, forceField, bondList);
1376     Atom C4 = buildHeavy(residue, "C4", N3, 1641, forceField, bondList);
1377     Atom C11 = buildHeavy(residue, "C11", N2, 1657, forceField, bondList);
1378     Atom C10 = buildHeavy(residue, "C10", C11, 1658, forceField, bondList);
1379     Atom C12 = buildHeavy(residue, "C12", C11, 1656, forceField, bondList);
1380     Atom C13 = buildHeavy(residue, "C13", C12, 1662, forceField, bondList);
1381     Atom C14 = buildHeavy(residue, "C14", C13, 1665, forceField, bondList);
1382     Atom C15 = buildHeavy(residue, "C15", C14, 1668, forceField, bondList);
1383     Atom C16 = buildHeavy(residue, "C16", C15, 1675, forceField, bondList);
1384     Atom O17 = buildHeavy(residue, "O17", C16, 1676, forceField, bondList);
1385     Atom O18 = buildHeavy(residue, "O18", C16, 1674, forceField, bondList);
1386     Atom C19 = buildHeavy(residue, "C19", O18, 1670, forceField, bondList);
1387     Atom N20 = buildHeavy(residue, "N20", C15, 1677, forceField, bondList);
1388     Atom C21 = buildHeavy(residue, "C21", N20, 1679, forceField, bondList);
1389     Atom O22 = buildHeavy(residue, "O22", C21, 1680, forceField, bondList);
1390     Atom O23 = buildHeavy(residue, "O23", C21, 1681, forceField, bondList);
1391     Atom C24 = buildHeavy(residue, "C24", O23, 1682, forceField, bondList);
1392     buildBond(C4, C5, forceField, bondList);
1393     buildBond(C4, N9, forceField, bondList);
1394     buildBond(N1, C12, forceField, bondList);
1395     buildH(residue, "H8", C8, 1.08e0, N7, 123.0e0, C5, 180.0e0, 0, 1651, forceField, bondList);
1396     Atom H31 = buildH(residue, "H31", C3, 1.08e0, N3, 109.5e0, C4, 0.0e0, 0, 1653, forceField,
1397         bondList);
1398     buildH(residue, "H32", C3, 1.08e0, N3, 109.5e0, H31, 109.5e0, 1, 1654, forceField, bondList);
1399     buildH(residue, "H33", C3, 1.08e0, N3, 109.5e0, H31, 109.5e0, -1, 1655, forceField, bondList);
1400     Atom H101 = buildH(residue, "H101", C10, 1.08e0, C11, 109.5e0, N2, 0.0e0, 0, 1659, forceField,
1401         bondList);
1402     buildH(residue, "H102", C10, 1.08e0, C11, 109.5e0, H101, 109.5e0, 1, 1660, forceField, bondList);
1403     buildH(residue, "H103", C10, 1.08e0, C11, 109.5e0, H101, 109.5e0, -1, 1661, forceField,
1404         bondList);
1405     buildH(residue, "H131", C13, 1.08e0, C12, 109.5e0, C14, 109.5e0, 1, 1663, forceField, bondList);
1406     buildH(residue, "H132", C13, 1.08e0, C12, 109.5e0, C14, 109.5e0, -1, 1664, forceField, bondList);
1407     buildH(residue, "H141", C14, 1.08e0, C13, 109.5e0, C15, 109.5e0, 1, 1666, forceField, bondList);
1408     buildH(residue, "H142", C14, 1.08e0, C13, 109.5e0, C15, 109.5e0, -1, 1667, forceField, bondList);
1409     buildH(residue, "H15", C15, 1.08e0, C14, 109.5e0, O18, 180.e0, 0, 1669, forceField, bondList);
1410     Atom H191 = buildH(residue, "H191", C19, 1.08e0, O18, 109.5e0, C16, 0.0e0, 0, 1671, forceField,
1411         bondList);
1412     buildH(residue, "H192", C19, 1.08e0, O18, 109.5e0, H191, 109.5e0, 1, 1672, forceField, bondList);
1413     buildH(residue, "H193", C19, 1.08e0, O18, 109.5e0, H191, 109.5e0, -1, 1673, forceField,
1414         bondList);
1415     buildH(residue, "HN2", N20, 1.00e0, C15, 109.5e0, O22, 180.0e0, 0, 1678, forceField, bondList);
1416     Atom H241 = buildH(residue, "H241", C24, 1.08e0, O23, 109.5e0, C21, 0.0e0, 0, 1683, forceField,
1417         bondList);
1418     buildH(residue, "H242", C24, 1.08e0, O23, 109.5e0, H241, 109.5e0, 1, 1684, forceField, bondList);
1419     buildH(residue, "H243", C24, 1.08e0, O23, 109.5e0, H241, 109.5e0, -1, 1685, forceField,
1420         bondList);
1421     return residue;
1422   }
1423 
1424   /**
1425    * Intended for 5'-terminal phosphate caps listed as their own residue: find a given atom name in
1426    * the next residue.
1427    *
1428    * @param residue Current residue.
1429    * @param nextResidue Next residue to search in.
1430    * @param atomName Atom name to look for.
1431    * @return The requested atom.
1432    */
1433   private static Atom getNextResAtom(Residue residue, Residue nextResidue, String atomName) {
1434     if (nextResidue == null) {
1435       throw new IllegalArgumentException(
1436           String.format(" Residue %s: No subsequent residue to find atom %s in!", residue,
1437               atomName));
1438     }
1439     List<Atom> nrAtoms = nextResidue.getAtomList();
1440     for (Atom atom : nrAtoms) {
1441       if (atom.getName().equalsIgnoreCase(atomName)) {
1442         return atom;
1443       }
1444     }
1445     throw new IllegalArgumentException(
1446         String.format(" Residue %s: Subsequent residue %s did not contain an atom %s", residue,
1447             nextResidue, atomName));
1448   }
1449 
1450   /**
1451    * getNucleicAcidNumber.
1452    *
1453    * @param residueName a {@link String} object.
1454    * @return The index of the nucleic acid in the nucleicAcidList.
1455    */
1456   public static int getNucleicAcidNumber(String residueName) {
1457     int nucleicAcidNumber = -1;
1458     for (NucleicAcid3 nucleicAcid : nucleicAcidList) {
1459       nucleicAcidNumber++;
1460       if (nucleicAcid.toString().equalsIgnoreCase(residueName)) {
1461         break;
1462       }
1463     }
1464     return nucleicAcidNumber;
1465   }
1466 }