View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2024.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.potential.bonded;
39  
40  import static ffx.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;
554     switch (nucleicAcid) {
555       case ADE -> buildADE(residue, C1s, O4s, C2s, glyco, forceField, bondList);
556       case M1MA -> buildM1MA(residue, C1s, forceField, bondList);
557       case CYT -> buildCYT(residue, C1s, O4s, C2s, glyco, forceField, bondList);
558       case OMC -> buildOMC(residue, C1s, O4s, C2s, glyco, forceField, bondList);
559       case M5MC -> buildM5MC(residue, C1s, forceField, bondList);
560       case GUA -> buildGUA(residue, C1s, O4s, C2s, glyco, forceField, bondList);
561       case OMG -> buildOMG(residue, C1s, O4s, C2s, glyco, forceField, bondList);
562       case YYG -> buildYYG(residue, C1s, forceField, bondList);
563       case M2MG -> buildM2MG(residue, C1s, forceField, bondList);
564       case M2G -> buildM2G(residue, C1s, forceField, bondList);
565       case M7MG -> buildM7MG(residue, C1s, forceField, bondList);
566       case URI -> buildURI(residue, C1s, O4s, C2s, glyco, forceField, bondList);
567       case PSU -> buildPSU(residue, C1s, forceField, bondList);
568       case H2U -> buildH2U(residue, C1s, forceField, bondList);
569       case M5MU -> buildM5MU(residue, C1s, forceField, bondList);
570       case DAD -> buildDAD(residue, C1s, O4s, C2s, glyco, forceField, bondList);
571       case DCY -> buildDCY(residue, C1s, O4s, C2s, glyco, forceField, bondList);
572       case DGU -> buildDGU(residue, C1s, O4s, C2s, glyco, forceField, bondList);
573       case DTY -> buildDTY(residue, C1s, O4s, C2s, glyco, forceField, bondList);
574     }
575   }
576 
577   /**
578    * buildADE.
579    *
580    * @param residue a {@link ffx.potential.bonded.Residue} object.
581    * @param C1s a {@link ffx.potential.bonded.Atom} object.
582    * @param O4s a {@link ffx.potential.bonded.Atom} object.
583    * @param C2s a {@link ffx.potential.bonded.Atom} object.
584    * @param glyco a double.
585    * @param forceField a {@link ForceField} object.
586    * @param bondList a {@link java.util.List} object.
587    * @return a {@link ffx.potential.bonded.Residue} object.
588    */
589   private static Residue buildADE(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
590       ForceField forceField, List<Bond> bondList) {
591     Atom N9 = buildHeavy(residue, "N9", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1017, forceField,
592         bondList);
593     Atom C8 = buildHeavy(residue, "C8", N9, 1.37, C1s, 128.4, O4s, glyco + 180.0, 0, 1021,
594         forceField, bondList);
595     Atom N7 = buildHeavy(residue, "N7", C8, 1.30, N9, 113.8, C1s, 180.0, 0, 1020, forceField,
596         bondList);
597     Atom C5 = buildHeavy(residue, "C5", N7, 1.39, C8, 104.0, N9, 0.0, 0, 1019, forceField, bondList);
598     Atom C6 = buildHeavy(residue, "C6", C5, 1.40, N7, 132.4, C8, 180.0, 0, 1025, forceField,
599         bondList);
600     Atom N6 = buildHeavy(residue, "N6", C6, 1.34, C5, 123.5, N7, 0.0, 0, 1027, forceField, bondList);
601     Atom N1 = buildHeavy(residue, "N1", C6, 1.35, C5, 117.4, N7, 180.0, 0, 1024, forceField,
602         bondList);
603     Atom C2 = buildHeavy(residue, "C2", N1, 1.33, C6, 118.8, C5, 0.0, 0, 1023, forceField, bondList);
604     Atom N3 = buildHeavy(residue, "N3", C2, 1.32, N1, 129.2, C6, 0.0, 0, 1022, forceField, bondList);
605     Atom C4 = buildHeavy(residue, "C4", N3, 1.35, C2, 110.9, N1, 0.0, 0, 1018, forceField, bondList);
606     buildBond(C4, C5, forceField, bondList);
607     buildBond(C4, N9, forceField, bondList);
608     buildH(residue, "H8", C8, 1.08e0, N7, 123.1e0, C5, 180.0e0, 0, 1030, forceField, bondList);
609     buildH(residue, "H61", N6, 1.00e0, C6, 120.0e0, N7, 180.0e0, 0, 1028, forceField, bondList);
610     buildH(residue, "H62", N6, 1.00e0, C6, 120.0e0, N7, 0.0e0, 0, 1029, forceField, bondList);
611     buildH(residue, "H2", C2, 1.08e0, N3, 115.4e0, C4, 180.0e0, 0, 1026, forceField, bondList);
612     return residue;
613   }
614 
615   /**
616    * buildCYT.
617    *
618    * @param residue a {@link ffx.potential.bonded.Residue} object.
619    * @param C1s a {@link ffx.potential.bonded.Atom} object.
620    * @param O4s a {@link ffx.potential.bonded.Atom} object.
621    * @param C2s a {@link ffx.potential.bonded.Atom} object.
622    * @param glyco a double.
623    * @param forceField a {@link ForceField} object.
624    * @param bondList a {@link java.util.List} object.
625    * @return a {@link ffx.potential.bonded.Residue} object.
626    */
627   private static Residue buildCYT(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
628       ForceField forceField, List<Bond> bondList) {
629     Atom N1 = buildHeavy(residue, "N1", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1078, forceField,
630         bondList);
631     Atom C2 = buildHeavy(residue, "C2", N1, 1.37, C1s, 117.8, O4s, glyco + 180, 0, 1079, forceField,
632         bondList);
633     Atom O2 = buildHeavy(residue, "O2", C2, 1.24, N1, 118.9, C1s, 0.0, 0, 1084, forceField,
634         bondList);
635     Atom N3 = buildHeavy(residue, "N3", C2, 1.38, N1, 118.7, C1s, 180.0, 0, 1080, forceField,
636         bondList);
637     Atom C4 = buildHeavy(residue, "C4", N3, 1.34, C2, 120.6, N1, 0.0, 0, 1081, forceField, bondList);
638     Atom N4 = buildHeavy(residue, "N4", C4, 1.32, N3, 118.3, O2, 180.0, 0, 1085, forceField,
639         bondList);
640     Atom C5 = buildHeavy(residue, "C5", C4, 1.43, N3, 121.6, C2, 0.0, 0, 1082, forceField, bondList);
641     Atom C6 = buildHeavy(residue, "C6", C5, 1.36, C4, 116.9, N3, 0.0, 0, 1083, forceField, bondList);
642     buildBond(C6, N1, forceField, bondList);
643     buildH(residue, "H41", N4, 1.00e0, C4, 120.0e0, N3, 0.0e0, 0, 1086, forceField, bondList);
644     buildH(residue, "H42", N4, 1.00e0, C4, 120.0e0, N3, 180.0e0, 0, 1087, forceField, bondList);
645     buildH(residue, "H5", C5, 1.08e0, C4, 121.6e0, N3, 180.0e0, 0, 1088, forceField, bondList);
646     buildH(residue, "H6", C6, 1.08e0, C5, 119.4e0, C4, 180.0e0, 0, 1089, forceField, bondList);
647     return residue;
648   }
649 
650   /**
651    * buildDAD.
652    *
653    * @param residue a {@link ffx.potential.bonded.Residue} object.
654    * @param C1s a {@link ffx.potential.bonded.Atom} object.
655    * @param O4s a {@link ffx.potential.bonded.Atom} object.
656    * @param C2s a {@link ffx.potential.bonded.Atom} object.
657    * @param glyco a double.
658    * @param forceField a {@link ForceField} object.
659    * @param bondList a {@link java.util.List} object.
660    * @return a {@link ffx.potential.bonded.Residue} object.
661    */
662   private static Residue buildDAD(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
663       ForceField forceField, List<Bond> bondList) {
664 
665     Atom N9 = buildHeavy(residue, "N9", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1132, forceField,
666         bondList);
667     Atom C8 = buildHeavy(residue, "C8", N9, 1.37, C1s, 128.4, O4s, glyco + 180, 0, 1136, forceField,
668         bondList);
669     Atom N7 = buildHeavy(residue, "N7", C8, 1.30, N9, 113.8, C1s, 180.0, 0, 1135, forceField,
670         bondList);
671     Atom C5 = buildHeavy(residue, "C5", N7, 1.39, C8, 104.0, N9, 0.0, 0, 1134, forceField, bondList);
672     Atom C6 = buildHeavy(residue, "C6", C5, 1.40, N7, 132.4, C8, 180.0, 0, 1140, forceField,
673         bondList);
674     Atom N6 = buildHeavy(residue, "N6", C6, 1.34, C5, 123.5, N7, 0.0, 0, 1142, forceField, bondList);
675     Atom N1 = buildHeavy(residue, "N1", C6, 1.35, C5, 117.4, N7, 180.0, 0, 1139, forceField,
676         bondList);
677     Atom C2 = buildHeavy(residue, "C2", N1, 1.33, C6, 118.8, C5, 0.0, 0, 1138, forceField, bondList);
678     Atom N3 = buildHeavy(residue, "N3", C2, 1.32, N1, 129.2, C6, 0.0, 0, 1137, forceField, bondList);
679     Atom C4 = buildHeavy(residue, "C4", N3, 1.35, C2, 110.9, N1, 0.0, 0, 1133, forceField, bondList);
680     buildBond(C4, C5, forceField, bondList);
681     buildBond(C4, N9, forceField, bondList);
682     buildH(residue, "H8", C8, 1.08e0, N7, 123.1e0, C5, 180.0e0, 0, 1145, forceField, bondList);
683     buildH(residue, "H61", N6, 1.00e0, C6, 120.0e0, N7, 180.0e0, 0, 1143, forceField, bondList);
684     buildH(residue, "H62", N6, 1.00e0, C6, 120.0e0, N7, 0.0e0, 0, 1144, forceField, bondList);
685     buildH(residue, "H2", C2, 1.08e0, N3, 115.4e0, C4, 180.0e0, 0, 1141, forceField, bondList);
686     return residue;
687   }
688 
689   /**
690    * buildDCY.
691    *
692    * @param residue a {@link ffx.potential.bonded.Residue} object.
693    * @param C1s a {@link ffx.potential.bonded.Atom} object.
694    * @param O4s a {@link ffx.potential.bonded.Atom} object.
695    * @param C2s a {@link ffx.potential.bonded.Atom} object.
696    * @param glyco a double.
697    * @param forceField a {@link ForceField} object.
698    * @param bondList a {@link java.util.List} object.
699    * @return a {@link ffx.potential.bonded.Residue} object.
700    */
701   private static Residue buildDCY(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
702       ForceField forceField, List<Bond> bondList) {
703 
704     Atom N1 = buildHeavy(residue, "N1", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1191, forceField,
705         bondList);
706     Atom C2 = buildHeavy(residue, "C2", N1, 1.37, C1s, 117.8, O4s, glyco, 0, 1192, forceField,
707         bondList);
708     Atom O2 = buildHeavy(residue, "O2", C2, 1.24, N1, 118.9, C1s, 0.0, 0, 1197, forceField,
709         bondList);
710     Atom N3 = buildHeavy(residue, "N3", C2, 1.38, N1, 118.7, C1s, 180, 0, 1193, forceField,
711         bondList);
712     Atom C4 = buildHeavy(residue, "C4", N3, 1.34, C2, 120.6, N1, 0.0, 0, 1194, forceField, bondList);
713     Atom N4 = buildHeavy(residue, "N4", C4, 1.32, N3, 118.3, C2, 180.0, 0, 1198, forceField,
714         bondList);
715     Atom C5 = buildHeavy(residue, "C5", C4, 1.43, N3, 121.6, C2, 0.0, 0, 1195, forceField, bondList);
716     Atom C6 = buildHeavy(residue, "C6", C5, 1.36, C4, 116.9, N3, 0.0, 0, 1196, forceField, bondList);
717     buildBond(C6, N1, forceField, bondList);
718     buildH(residue, "H41", N4, 1.00e0, C4, 120.0e0, N3, 0.0e0, 0, 1199, forceField, bondList);
719     buildH(residue, "H42", N4, 1.00e0, C4, 120.0e0, N3, 180.0e0, 0, 1200, forceField, bondList);
720     buildH(residue, "H5", C5, 1.08e0, C4, 121.6e0, N3, 180.0e0, 0, 1201, forceField, bondList);
721     buildH(residue, "H6", C6, 1.08e0, C5, 119.4e0, C4, 180.0e0, 0, 1202, forceField, bondList);
722     return residue;
723   }
724 
725   /**
726    * buildDGU.
727    *
728    * @param residue a {@link ffx.potential.bonded.Residue} object.
729    * @param C1s a {@link ffx.potential.bonded.Atom} object.
730    * @param O4s a {@link ffx.potential.bonded.Atom} object.
731    * @param C2s a {@link ffx.potential.bonded.Atom} object.
732    * @param glyco a double.
733    * @param forceField a {@link ForceField} object.
734    * @param bondList a {@link java.util.List} object.
735    * @return a {@link ffx.potential.bonded.Residue} object.
736    */
737   private static Residue buildDGU(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
738       ForceField forceField, List<Bond> bondList) {
739 
740     Atom N9 = buildHeavy(residue, "N9", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1161, forceField,
741         bondList);
742     Atom C8 = buildHeavy(residue, "C8", N9, 1.38, C1s, 128.4, O4s, glyco + 180, 0, 1165, forceField,
743         bondList);
744     Atom N7 = buildHeavy(residue, "N7", C8, 1.31, N9, 114.0, C1s, 180.0, 0, 1164, forceField,
745         bondList);
746     Atom C5 = buildHeavy(residue, "C5", N7, 1.39, C8, 103.8, N9, 0.0, 0, 1163, forceField, bondList);
747     Atom C6 = buildHeavy(residue, "C6", C5, 1.40, N7, 130.1, C8, 180.0, 0, 1169, forceField,
748         bondList);
749     Atom O6 = buildHeavy(residue, "O6", C6, 1.23, C5, 128.8, N7, 0.0, 0, 1174, forceField, bondList);
750     Atom N1 = buildHeavy(residue, "N1", C6, 1.40, C5, 111.4, N7, 180.0, 0, 1168, forceField,
751         bondList);
752     Atom C2 = buildHeavy(residue, "C2", N1, 1.38, C6, 125.2, C5, 0.0, 0, 1167, forceField, bondList);
753     Atom N2 = buildHeavy(residue, "N2", C2, 1.34, N1, 116.1, C6, 180.0, 0, 1171, forceField,
754         bondList);
755     Atom N3 = buildHeavy(residue, "N3", C2, 1.33, N1, 123.3, O6, 0.0, 0, 1166, forceField, bondList);
756     Atom C4 = buildHeavy(residue, "C4", N3, 1.36, C2, 112.3, N1, 0.0, 0, 1162, forceField, bondList);
757     buildBond(C4, C5, forceField, bondList);
758     buildBond(C4, N9, forceField, bondList);
759     buildH(residue, "H8", C8, 1.08e0, N7, 123.0e0, C5, 180.0e0, 0, 1175, forceField, bondList);
760     buildH(residue, "H1", N1, 1.00e0, C6, 117.4e0, C5, 180.0e0, 0, 1170, forceField, bondList);
761     buildH(residue, "H21", N2, 1.00e0, C2, 120.0e0, N1, 0.0e0, 0, 1172, forceField, bondList);
762     buildH(residue, "H22", N2, 1.00e0, C2, 120.0e0, N1, 180.0e0, 0, 1173, forceField, bondList);
763     return residue;
764   }
765 
766   /**
767    * buildDTY.
768    *
769    * @param residue a {@link ffx.potential.bonded.Residue} object.
770    * @param C1s a {@link ffx.potential.bonded.Atom} object.
771    * @param O4s a {@link ffx.potential.bonded.Atom} object.
772    * @param C2s a {@link ffx.potential.bonded.Atom} object.
773    * @param glyco a double.
774    * @param forceField a {@link ForceField} object.
775    * @param bondList a {@link java.util.List} object.
776    * @return a {@link ffx.potential.bonded.Residue} object.
777    */
778   private static Residue buildDTY(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
779       ForceField forceField, List<Bond> bondList) {
780     Atom N1 = buildHeavy(residue, "N1", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1218, forceField,
781         bondList);
782     Atom C2 = buildHeavy(residue, "C2", N1, 1.37, C1s, 117.1, O4s, glyco, 0, 1219, forceField,
783         bondList);
784     Atom O2 = buildHeavy(residue, "O2", C2, 1.22, N1, 122.9, C1s, 0.0, 0, 1224, forceField,
785         bondList);
786     Atom N3 = buildHeavy(residue, "N3", C2, 1.38, N1, 115.4, C1s, 180.0, 0, 1220, forceField,
787         bondList);
788     Atom C4 = buildHeavy(residue, "C4", N3, 1.38, C2, 126.4, N1, 0.0, 0, 1221, forceField, bondList);
789     Atom O4 = buildHeavy(residue, "O4", C4, 1.23, N3, 120.5, C2, 180.0, 0, 1226, forceField,
790         bondList);
791     Atom C5 = buildHeavy(residue, "C5", C4, 1.44, N3, 114.1, C2, 0.0, 0, 1222, forceField, bondList);
792     Atom C7 = buildHeavy(residue, "C7", C5, 1.50, C4, 117.5, N3, 180.0, 0, 1227, forceField,
793         bondList);
794     Atom C6 = buildHeavy(residue, "C6", C5, 1.34, C4, 120.8, N3, 0.0, 0, 1223, forceField, bondList);
795     buildBond(C6, N1, forceField, bondList);
796     buildH(residue, "H3", N3, 1.00e0, C2, 116.8e0, N1, 180.0e0, 0, 1225, forceField, bondList);
797     Atom H = buildH(residue, "H71", C7, 1.09e0, C5, 109.5e0, C4, 0.0e0, 0, 1228, forceField,
798         bondList);
799     buildH(residue, "H72", C7, 1.09e0, C5, 109.5e0, H, 109.5e0, 1, 1228, forceField, bondList);
800     buildH(residue, "H73", C7, 1.09e0, C5, 109.5e0, H, 109.5e0, -1, 1228, forceField, bondList);
801     buildH(residue, "H6", C6, 1.08e0, C5, 119.4e0, C4, 180.0e0, 0, 1229, forceField, bondList);
802     return residue;
803   }
804 
805   /**
806    * buildGUA.
807    *
808    * @param residue a {@link ffx.potential.bonded.Residue} object.
809    * @param C1s a {@link ffx.potential.bonded.Atom} object.
810    * @param O4s a {@link ffx.potential.bonded.Atom} object.
811    * @param C2s a {@link ffx.potential.bonded.Atom} object.
812    * @param glyco a double.
813    * @param forceField a {@link ForceField} object.
814    * @param bondList a {@link java.util.List} object.
815    * @return a {@link ffx.potential.bonded.Residue} object.
816    */
817   private static Residue buildGUA(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
818       ForceField forceField, List<Bond> bondList) {
819     Atom N9 = buildHeavy(residue, "N9", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1047, forceField,
820         bondList);
821     Atom C8 = buildHeavy(residue, "C8", N9, 1.38, C1s, 128.4, O4s, glyco + 180, 0, 1051, forceField,
822         bondList);
823     Atom N7 = buildHeavy(residue, "N7", C8, 1.31, N9, 114.0, C1s, 180.0, 0, 1050, forceField,
824         bondList);
825     Atom C5 = buildHeavy(residue, "C5", N7, 1.39, C8, 103.8, N9, 0.0, 0, 1049, forceField, bondList);
826     Atom C6 = buildHeavy(residue, "C6", C5, 1.40, N7, 130.1, C8, 180.0, 0, 1055, forceField,
827         bondList);
828     Atom O6 = buildHeavy(residue, "O6", C6, 1.23, C5, 128.8, N7, 0.0, 0, 1060, forceField, bondList);
829     Atom N1 = buildHeavy(residue, "N1", C6, 1.40, C5, 111.4, N7, 180.0, 0, 1054, forceField,
830         bondList);
831     Atom C2 = buildHeavy(residue, "C2", N1, 1.38, C6, 125.2, C5, 0.0, 0, 1053, forceField, bondList);
832     Atom N2 = buildHeavy(residue, "N2", C2, 1.34, N1, 116.1, C6, 180.0, 0, 1057, forceField,
833         bondList);
834     Atom N3 = buildHeavy(residue, "N3", C2, 1.33, N1, 123.3, O6, 0.0, 0, 1052, forceField, bondList);
835     Atom C4 = buildHeavy(residue, "C4", N3, 1.36, C2, 112.3, N1, 0.0, 0, 1048, forceField, bondList);
836     buildBond(C4, C5, forceField, bondList);
837     buildBond(C4, N9, forceField, bondList);
838     buildH(residue, "H8", C8, 1.08e0, N7, 123.0e0, C5, 180.0e0, 0, 1061, forceField, bondList);
839     buildH(residue, "H1", N1, 1.00e0, C6, 117.4e0, C5, 180.0e0, 0, 1056, forceField, bondList);
840     buildH(residue, "H21", N2, 1.00e0, C2, 120.0e0, N1, 0.0e0, 0, 1058, forceField, bondList);
841     buildH(residue, "H22", N2, 1.00e0, C2, 120.0e0, N1, 180.0e0, 0, 1059, forceField, bondList);
842     return residue;
843   }
844 
845   /**
846    * buildH2U.
847    *
848    * @param residue a {@link ffx.potential.bonded.Residue} object.
849    * @param C1s a {@link ffx.potential.bonded.Atom} object.
850    * @param forceField a {@link ForceField} object.
851    * @param bondList a {@link java.util.List} object.
852    * @return a {@link ffx.potential.bonded.Residue} object.
853    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
854    */
855   private static Residue buildH2U(Residue residue, Atom C1s, ForceField forceField,
856       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
857 
858     Atom N1 = buildHeavy(residue, "N1", C1s, 1350, forceField, bondList);
859     Atom C2 = buildHeavy(residue, "C2", N1, 1351, forceField, bondList);
860     Atom O2 = buildHeavy(residue, "O2", C2, 1356, forceField, bondList);
861     Atom N3 = buildHeavy(residue, "N3", C2, 1352, forceField, bondList);
862     Atom C4 = buildHeavy(residue, "C4", N3, 1353, forceField, bondList);
863     Atom O4 = buildHeavy(residue, "O4", C4, 1358, forceField, bondList);
864     Atom C5 = buildHeavy(residue, "C5", C4, 1354, forceField, bondList);
865     Atom C6 = buildHeavy(residue, "C6", C5, 1355, forceField, bondList);
866     buildBond(C6, N1, forceField, bondList);
867     buildH(residue, "H3", N3, 1.00e0, C2, 116.5e0, N1, 180.0e0, 0, 1357, forceField, bondList);
868     buildH(residue, "H51", C5, 1.08e0, C4, 109.5e0, C6, 109.5e0, 1, 1359, forceField, bondList);
869     buildH(residue, "H52", C5, 1.08e0, C4, 109.5e0, C6, 109.5e0, -1, 1360, forceField, bondList);
870     buildH(residue, "H61", C6, 1.08e0, C5, 109.5e0, N1, 109.5e0, 1, 1361, forceField, bondList);
871     buildH(residue, "H62", C6, 1.08e0, C5, 109.5e0, N1, 109.5e0, -1, 1362, forceField, bondList);
872     return residue;
873   }
874 
875   /**
876    * buildM1MA.
877    *
878    * @param residue a {@link ffx.potential.bonded.Residue} object.
879    * @param C1s a {@link ffx.potential.bonded.Atom} object.
880    * @param forceField a {@link ForceField} object.
881    * @param bondList a {@link java.util.List} object.
882    * @return a {@link ffx.potential.bonded.Residue} object.
883    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
884    */
885   private static Residue buildM1MA(Residue residue, Atom C1s, ForceField forceField,
886       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
887     Atom N9 = buildHeavy(residue, "N9", C1s, 1605, forceField, bondList);
888     Atom C8 = buildHeavy(residue, "C8", N9, 1609, forceField, bondList);
889     Atom N7 = buildHeavy(residue, "N7", C8, 1608, forceField, bondList);
890     Atom C5 = buildHeavy(residue, "C5", N7, 1607, forceField, bondList);
891     Atom C6 = buildHeavy(residue, "C6", C5, 1613, forceField, bondList);
892     Atom N6 = buildHeavy(residue, "N6", C6, 1615, forceField, bondList);
893     Atom N1 = buildHeavy(residue, "N1", C6, 1612, forceField, bondList);
894     Atom C2 = buildHeavy(residue, "C2", N1, 1611, forceField, bondList);
895     Atom N3 = buildHeavy(residue, "N3", C2, 1610, forceField, bondList);
896     Atom C4 = buildHeavy(residue, "C4", N3, 1606, forceField, bondList);
897     Atom CM1 = buildHeavy(residue, "CM1", N1, 1619, forceField, bondList);
898     buildBond(C4, C5, forceField, bondList);
899     buildBond(C4, N9, forceField, bondList);
900     buildH(residue, "H2", C2, 1.08e0, N3, 115.4e0, C4, 180.0e0, 0, 1614, forceField, bondList);
901     buildH(residue, "H6", C6, 1.08e0, C5, 109.5e0, C4, 180.0e0, 0, 1623, forceField, bondList);
902     buildH(residue, "H8", C8, 1.08e0, N7, 123.1e0, C5, 180.0e0, 0, 1618, forceField, bondList);
903     buildH(residue, "HN61", N6, 1.00e0, C6, 109.5e0, C5, 0.0e0, 0, 1616, forceField, bondList);
904     buildH(residue, "HN62", N6, 1.00e0, C6, 109.5e0, C5, 109.5e0, 0, 1617, forceField, bondList);
905     Atom HM11 = buildH(residue, "HM11", CM1, 1.08e0, N1, 109.5e0, C2, 0.0e0, 0, 1620, forceField,
906         bondList);
907     buildH(residue, "HM12", CM1, 1.08e0, N1, 109.5e0, HM11, 109.5e0, 1, 1621, forceField, bondList);
908     buildH(residue, "HM13", CM1, 1.08e0, N1, 109.5e0, HM11, 109.5e0, -1, 1622, forceField, bondList);
909     return residue;
910   }
911 
912   /**
913    * buildM2G.
914    *
915    * @param residue a {@link ffx.potential.bonded.Residue} object.
916    * @param C1s a {@link ffx.potential.bonded.Atom} object.
917    * @param forceField a {@link ForceField} object.
918    * @param bondList a {@link java.util.List} object.
919    * @return a {@link ffx.potential.bonded.Residue} object.
920    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
921    */
922   private static Residue buildM2G(Residue residue, Atom C1s, ForceField forceField,
923       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
924     Atom N9 = buildHeavy(residue, "N9", C1s, 1379, forceField, bondList);
925     Atom C8 = buildHeavy(residue, "C8", N9, 1383, forceField, bondList);
926     Atom N7 = buildHeavy(residue, "N7", C8, 1382, forceField, bondList);
927     Atom C5 = buildHeavy(residue, "C5", N7, 1381, forceField, bondList);
928     Atom C6 = buildHeavy(residue, "C6", C5, 1387, forceField, bondList);
929     Atom O6 = buildHeavy(residue, "O6", C6, 1390, forceField, bondList);
930     Atom N1 = buildHeavy(residue, "N1", C6, 1386, forceField, bondList);
931     Atom C2 = buildHeavy(residue, "C2", N1, 1385, forceField, bondList);
932     Atom N2 = buildHeavy(residue, "N2", C2, 1389, forceField, bondList);
933     Atom N3 = buildHeavy(residue, "N3", C2, 1384, forceField, bondList);
934     Atom C4 = buildHeavy(residue, "C4", N3, 1380, forceField, bondList);
935     Atom CM1 = buildHeavy(residue, "CM1", N2, 1392, forceField, bondList);
936     Atom CM2 = buildHeavy(residue, "CM2", N2, 1396, forceField, bondList);
937     buildBond(C4, C5, forceField, bondList);
938     buildBond(C4, N9, forceField, bondList);
939     buildH(residue, "H8", C8, 1.08e0, N7, 123.0e0, C5, 180.0e0, 0, 1391, forceField, bondList);
940     buildH(residue, "H1", N1, 1.00e0, C6, 117.4e0, C5, 180.0e0, 0, 1388, forceField, bondList);
941     Atom HM11 = buildH(residue, "HM11", CM1, 1.08e0, N2, 109.5e0, C2, 0.0e0, 0, 1393, forceField,
942         bondList);
943     buildH(residue, "HM12", CM1, 1.08e0, N2, 109.5e0, HM11, 109.5e0, 1, 1394, forceField, bondList);
944     buildH(residue, "HM13", CM1, 1.08e0, N2, 109.5e0, HM11, 109.5e0, -1, 1395, forceField, bondList);
945     Atom HM21 = buildH(residue, "HM21", CM2, 1.08e0, N2, 109.5e0, C2, 0.0e0, 0, 1397, forceField,
946         bondList);
947     buildH(residue, "HM22", CM2, 1.08e0, N2, 109.5e0, HM21, 109.5e0, 1, 1398, forceField, bondList);
948     buildH(residue, "HM23", CM2, 1.08e0, N2, 109.5e0, HM21, 109.5e0, -1, 1399, forceField, bondList);
949     return residue;
950   }
951 
952   /**
953    * buildM2MG.
954    *
955    * @param residue a {@link ffx.potential.bonded.Residue} object.
956    * @param C1s a {@link ffx.potential.bonded.Atom} object.
957    * @param forceField a {@link ForceField} object.
958    * @param bondList a {@link java.util.List} object.
959    * @return a {@link ffx.potential.bonded.Residue} object.
960    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
961    */
962   private static Residue buildM2MG(Residue residue, Atom C1s, ForceField forceField,
963       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
964     Atom N9 = buildHeavy(residue, "N9", C1s, 1316, forceField, bondList);
965     Atom C8 = buildHeavy(residue, "C8", N9, 1320, forceField, bondList);
966     Atom N7 = buildHeavy(residue, "N7", C8, 1319, forceField, bondList);
967     Atom C5 = buildHeavy(residue, "C5", N7, 1318, forceField, bondList);
968     Atom C6 = buildHeavy(residue, "C6", C5, 1324, forceField, bondList);
969     Atom O6 = buildHeavy(residue, "O6", C6, 1328, forceField, bondList);
970     Atom N1 = buildHeavy(residue, "N1", C6, 1323, forceField, bondList);
971     Atom C2 = buildHeavy(residue, "C2", N1, 1322, forceField, bondList);
972     Atom N2 = buildHeavy(residue, "N2", C2, 1326, forceField, bondList);
973     Atom N3 = buildHeavy(residue, "N3", C2, 1321, forceField, bondList);
974     Atom C4 = buildHeavy(residue, "C4", N3, 1317, forceField, bondList);
975     Atom CM2 = buildHeavy(residue, "CM2", N2, 1330, forceField, bondList);
976     buildBond(C4, C5, forceField, bondList);
977     buildBond(C4, N9, forceField, bondList);
978     buildH(residue, "H8", C8, 1.08e0, N7, 123.0e0, C5, 180.0e0, 0, 1329, forceField, bondList);
979     buildH(residue, "H1", N1, 1.00e0, C6, 117.4e0, C5, 180.0e0, 0, 1325, forceField, bondList);
980     buildH(residue, "H2", N2, 1.00e0, C2, 120.0e0, N1, 0.0e0, 0, 1327, forceField, bondList);
981     Atom HM21 = buildH(residue, "HM21", CM2, 1.08e0, N2, 109.5e0, C2, 0.0e0, 0, 1331, forceField,
982         bondList);
983     buildH(residue, "HM22", CM2, 1.08e0, N2, 109.5e0, HM21, 109.5e0, 1, 1332, forceField, bondList);
984     buildH(residue, "HM23", CM2, 1.08e0, N2, 109.5e0, HM21, 109.5e0, -1, 1333, forceField, bondList);
985     return residue;
986   }
987 
988   /**
989    * buildM5MC.
990    *
991    * @param residue a {@link ffx.potential.bonded.Residue} object.
992    * @param C1s a {@link ffx.potential.bonded.Atom} object.
993    * @param forceField a {@link ForceField} object.
994    * @param bondList a {@link java.util.List} object.
995    * @return a {@link ffx.potential.bonded.Residue} object.
996    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
997    */
998   private static Residue buildM5MC(Residue residue, Atom C1s, ForceField forceField,
999       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
1000     Atom N1 = buildHeavy(residue, "N1", C1s, 1508, forceField, bondList);
1001     Atom C2 = buildHeavy(residue, "C2", N1, 1509, forceField, bondList);
1002     Atom O2 = buildHeavy(residue, "O2", C2, 1514, forceField, bondList);
1003     Atom N3 = buildHeavy(residue, "N3", C2, 1510, forceField, bondList);
1004     Atom C4 = buildHeavy(residue, "C4", N3, 1511, forceField, bondList);
1005     Atom N4 = buildHeavy(residue, "N4", C4, 1515, forceField, bondList);
1006     Atom C5 = buildHeavy(residue, "C5", C4, 1512, forceField, bondList);
1007     Atom C6 = buildHeavy(residue, "C6", C5, 1513, forceField, bondList);
1008     Atom CM5 = buildHeavy(residue, "CM5", C5, 1519, forceField, bondList);
1009     buildBond(C6, N1, forceField, bondList);
1010     buildH(residue, "H41", N4, 1.00e0, C4, 120.0e0, N3, 0.0e0, 0, 1516, forceField, bondList);
1011     buildH(residue, "H42", N4, 1.00e0, C4, 120.0e0, C5, 0.0e0, 0, 1517, forceField, bondList);
1012     buildH(residue, "H6", C6, 1.08e0, C5, 119.4e0, C4, 180.0e0, 0, 1518, forceField, bondList);
1013     Atom HM51 = buildH(residue, "HM51", CM5, 1.08e0, C5, 109.5e0, C4, 0.0e0, 0, 1520, forceField,
1014         bondList);
1015     buildH(residue, "HM52", CM5, 1.08e0, C5, 109.5e0, HM51, 109.5e0, 1, 1521, forceField, bondList);
1016     buildH(residue, "HM53", CM5, 1.08e0, C5, 109.5e0, HM51, 109.5e0, -1, 1522, forceField, bondList);
1017     return residue;
1018   }
1019 
1020   /**
1021    * buildM5MU.
1022    *
1023    * @param residue a {@link ffx.potential.bonded.Residue} object.
1024    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1025    * @param forceField a {@link ForceField} object.
1026    * @param bondList a {@link java.util.List} object.
1027    * @return a {@link ffx.potential.bonded.Residue} object.
1028    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
1029    */
1030   private static Residue buildM5MU(Residue residue, Atom C1s, ForceField forceField,
1031       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
1032 
1033     Atom N1 = buildHeavy(residue, "N1", C1s, 1575, forceField, bondList);
1034     Atom C2 = buildHeavy(residue, "C2", N1, 1576, forceField, bondList);
1035     Atom O2 = buildHeavy(residue, "O2", C2, 1581, forceField, bondList);
1036     Atom N3 = buildHeavy(residue, "N3", C2, 1577, forceField, bondList);
1037     Atom C4 = buildHeavy(residue, "C4", N3, 1578, forceField, bondList);
1038     Atom O4 = buildHeavy(residue, "O4", C4, 1583, forceField, bondList);
1039     Atom C5 = buildHeavy(residue, "C5", C4, 1579, forceField, bondList);
1040     Atom C6 = buildHeavy(residue, "C6", C5, 1580, forceField, bondList);
1041     Atom C5M = buildHeavy(residue, "C5M", C5, 1585, forceField, bondList);
1042     buildBond(C6, N1, forceField, bondList);
1043     buildH(residue, "H3", N3, 1.00e0, C2, 116.5e0, N1, 180.0e0, 0, 1582, forceField, bondList);
1044     buildH(residue, "H6", C6, 1.08e0, C5, 118.6e0, C4, 180.0e0, 0, 1584, forceField, bondList);
1045     Atom H5M1 = buildH(residue, "H5M1", C5M, 1.08e0, C5, 109.5e0, C6, 0.0e0, 0, 1586, forceField,
1046         bondList);
1047     buildH(residue, "H5M2", C5M, 1.08e0, C5, 109.5e0, H5M1, 109.5e0, 1, 1587, forceField, bondList);
1048     buildH(residue, "H5M3", C5M, 1.08e0, C5, 109.5e0, H5M1, 109.5e0, -1, 1588, forceField, bondList);
1049     return residue;
1050   }
1051 
1052   /**
1053    * buildM7MG.
1054    *
1055    * @param residue a {@link ffx.potential.bonded.Residue} object.
1056    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1057    * @param forceField a {@link ForceField} object.
1058    * @param bondList a {@link java.util.List} object.
1059    * @return a {@link ffx.potential.bonded.Residue} object.
1060    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
1061    */
1062   private static Residue buildM7MG(Residue residue, Atom C1s, ForceField forceField,
1063       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
1064     Atom N9 = buildHeavy(residue, "N9", C1s, 1539, forceField, bondList);
1065     Atom C8 = buildHeavy(residue, "C8", N9, 1543, forceField, bondList);
1066     Atom N7 = buildHeavy(residue, "N7", C8, 1542, forceField, bondList);
1067     Atom C5 = buildHeavy(residue, "C5", N7, 1541, forceField, bondList);
1068     Atom C6 = buildHeavy(residue, "C6", C5, 1547, forceField, bondList);
1069     Atom O6 = buildHeavy(residue, "O6", C6, 1552, forceField, bondList);
1070     Atom N1 = buildHeavy(residue, "N1", C6, 1546, forceField, bondList);
1071     Atom C2 = buildHeavy(residue, "C2", N1, 1545, forceField, bondList);
1072     Atom N2 = buildHeavy(residue, "N2", C2, 1549, forceField, bondList);
1073     Atom N3 = buildHeavy(residue, "N3", C2, 1544, forceField, bondList);
1074     Atom C4 = buildHeavy(residue, "C4", N3, 1540, forceField, bondList);
1075     Atom CM7 = buildHeavy(residue, "CM7", N7, 1555, forceField, bondList);
1076     buildBond(C4, C5, forceField, bondList);
1077     buildBond(C4, N9, forceField, bondList);
1078     buildH(residue, "H81", C8, 1.08e0, N7, 109.5e0, N9, 109.5e0, 1, 1553, forceField, bondList);
1079     buildH(residue, "H82", C8, 1.08e0, N7, 109.5e0, N9, 109.5e0, -1, 1554, forceField, bondList);
1080     buildH(residue, "H1", N1, 1.00e0, C6, 117.4e0, C5, 180.0e0, 0, 1548, forceField, bondList);
1081     buildH(residue, "H21", N2, 1.00e0, C2, 120.0e0, N1, 0.0e0, 0, 1550, forceField, bondList);
1082     buildH(residue, "H22", N2, 1.00e0, C2, 120.0e0, N3, 0.0e0, 0, 1551, forceField, bondList);
1083     Atom HM71 = buildH(residue, "HM71", CM7, 1.08e0, N7, 109.5e0, C8, 0.0e0, 0, 1556, forceField,
1084         bondList);
1085     buildH(residue, "HM72", CM7, 1.08e0, N7, 109.5e0, HM71, 109.5e0, 1, 1557, forceField, bondList);
1086     buildH(residue, "HM73", CM7, 1.08e0, N7, 109.5e0, HM71, 109.5e0, -1, 1558, forceField, bondList);
1087     return residue;
1088   }
1089 
1090   /**
1091    * buildOMC.
1092    *
1093    * @param residue a {@link ffx.potential.bonded.Residue} object.
1094    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1095    * @param O4s a {@link ffx.potential.bonded.Atom} object.
1096    * @param C2s a {@link ffx.potential.bonded.Atom} object.
1097    * @param glyco a double.
1098    * @param forceField a {@link ForceField} object.
1099    * @param bondList a {@link java.util.List} object.
1100    * @return a {@link ffx.potential.bonded.Residue} object.
1101    */
1102   private static Residue buildOMC(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
1103       ForceField forceField, List<Bond> bondList) {
1104     return buildCYT(residue, C1s, O4s, C2s, glyco, forceField, bondList);
1105   }
1106 
1107   /**
1108    * buildOMG.
1109    *
1110    * @param residue a {@link ffx.potential.bonded.Residue} object.
1111    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1112    * @param O4s a {@link ffx.potential.bonded.Atom} object.
1113    * @param C2s a {@link ffx.potential.bonded.Atom} object.
1114    * @param glyco a double.
1115    * @param forceField a {@link ForceField} object.
1116    * @param bondList a {@link java.util.List} object.
1117    * @return a {@link ffx.potential.bonded.Residue} object.
1118    */
1119   private static Residue buildOMG(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
1120       ForceField forceField, List<Bond> bondList) {
1121     return buildGUA(residue, C1s, O4s, C2s, glyco, forceField, bondList);
1122   }
1123 
1124   /**
1125    * Builds a missing OP3 atom. Can be applied either to 5'-terminal or 3'-terminal phosphates.
1126    *
1127    * @param residue Residue to build an OP3 for.
1128    * @param phosphate The phosphate atom.
1129    * @param aType The atom type to use.
1130    * @param forceField Force field in use.
1131    * @param bondList List of bonds.
1132    * @param adjacentResidue A Residue either 5' or 3' of residue.
1133    * @param at5prime If this residue is at the 5' terminus (vs. 3').
1134    * @throws MissingHeavyAtomException Thrown if a needed heavy atom is missing.
1135    */
1136   private static void buildOP3(Residue residue, Atom phosphate, int aType, ForceField forceField,
1137       List<Bond> bondList, Residue adjacentResidue, boolean at5prime)
1138       throws MissingHeavyAtomException, MissingAtomTypeException {
1139     Atom P = null;
1140     Atom OP1 = null;
1141     Atom OP2 = null;
1142     Atom riboOxygen = null;
1143     Atom riboCarbon = null;
1144     boolean foundOP3 = false;
1145 
1146     List<Atom> residueAtoms = residue.getAtomList();
1147     // Label for a break statement: OP3 causes a break from a switch to the surrounding for loop.
1148     AtomLoop:
1149     for (Atom at : residueAtoms) {
1150       String atName = at.getName().toUpperCase();
1151       switch (atName) {
1152         case "OP3":
1153           buildHeavy(residue, "OP3", phosphate, aType, forceField, bondList);
1154           foundOP3 = true;
1155           break AtomLoop;
1156         case "OP1":
1157           OP1 = at;
1158           break;
1159         case "OP2":
1160           OP2 = at;
1161           break;
1162         case "P":
1163           P = at;
1164           break;
1165         case "O5'":
1166           riboOxygen = at;
1167           break;
1168         case "C5'":
1169           riboCarbon = at;
1170           break;
1171       }
1172     }
1173 
1174     if (!at5prime) {
1175       riboOxygen = (Atom) adjacentResidue.getAtomNode("O3'");
1176       riboCarbon = (Atom) adjacentResidue.getAtomNode("C3'");
1177       if (riboCarbon == null || riboOxygen == null) {
1178         logger.severe(format(
1179             " Could not find either O3' " + "or C3' in residue %s prior to presumed 3' "
1180                 + "phosphate cap %s", adjacentResidue, residue));
1181       }
1182     }
1183 
1184     if (!foundOP3) {
1185       logger.fine(
1186           format(" EXPERIMENTAL: OP3 of residue %s not found, being rebuilt based on ideal geometry",
1187               residue));
1188       if (P == null || OP1 == null || OP2 == null) {
1189         throw new IllegalArgumentException(format(
1190             " Attempted to build OP3 for residue %s, but one of P, OP1, OP2, O5', or C5' were null",
1191             residue));
1192       }
1193       if (at5prime) {
1194         if (riboOxygen == null) {
1195           logger.fine(" Attempting to find O5' of subsequent residue");
1196           riboOxygen = getNextResAtom(residue, adjacentResidue, "O5'");
1197         }
1198         if (riboCarbon == null) {
1199           logger.fine(" Attempting to find C5' of subsequent residue");
1200           riboCarbon = getNextResAtom(residue, adjacentResidue, "C5'");
1201         }
1202       }
1203 
1204       // Borrow the P-OP1 distance.
1205       double[] xyzOP1 = new double[3];
1206       double[] xyzP = new double[3];
1207       xyzOP1 = OP1.getXYZ(xyzOP1);
1208       xyzP = P.getXYZ(xyzP);
1209       double distance = dist(xyzP, xyzOP1);
1210 
1211       // Borrow the O5'-P-OP1 angle.
1212       double[] xyzRiboO = new double[3];
1213       xyzRiboO = riboOxygen.getXYZ(xyzRiboO);
1214       double angle = toDegrees(DoubleMath.bondAngle(xyzRiboO, xyzP, xyzOP1));
1215 
1216       // Borrow the C5'-O5'-P-OP1 dihedral (which will have +120 or +240 degrees added on).
1217       double[] xyzRiboC = new double[3];
1218       xyzRiboC = riboCarbon.getXYZ(xyzRiboC);
1219       double dihedralOP1 = toDegrees(DoubleMath.dihedralAngle(xyzRiboC, xyzRiboO, xyzP, xyzOP1));
1220 
1221       Atom OP3 = buildH(residue, "OP3", P, distance, riboOxygen, angle, riboCarbon,
1222           dihedralOP1 + 120, 0, aType, forceField, bondList);
1223 
1224       // Measure OP3-OP2 distance for test dihedral + 120 degrees.
1225       double[] xyzOP2 = new double[3];
1226       xyzOP2 = OP2.getXYZ(xyzOP2);
1227       double[] xyzChiral1 = new double[3];
1228       xyzChiral1 = OP3.getXYZ(xyzChiral1);
1229       double distChiral1 = dist(xyzChiral1, xyzOP2);
1230 
1231       // Measure OP3-OP2 distance for test dihedral + 240 degrees.
1232       intxyz(OP3, P, distance, riboOxygen, angle, riboCarbon, dihedralOP1 + 240, 0);
1233       double[] xyzChiral2 = new double[3];
1234       xyzChiral2 = OP3.getXYZ(xyzChiral2);
1235       double distChiral2 = dist(xyzChiral2, xyzOP2);
1236 
1237       if (logger.isLoggable(Level.FINE)) {
1238         logger.fine(
1239             format(" Bond: %10.5f Angle: %10.5f Dihedral: %10.5f", distance, angle, dihedralOP1));
1240         logger.fine(format(" OP2 position: %s", Arrays.toString(xyzOP2)));
1241         logger.fine(format(" Position 1: %10.6g %10.6g %10.6g with distance %10.6g", xyzChiral1[0],
1242             xyzChiral1[1], xyzChiral1[2], distChiral1));
1243         logger.fine(format(" Position 2: %10.6g %10.6g %10.6g with distance %10.6g", xyzChiral2[0],
1244             xyzChiral2[1], xyzChiral2[2], distChiral2));
1245       }
1246       if (distChiral1 > distChiral2) {
1247         logger.fine(" Picked dihedral +120");
1248         OP3.setXYZ(xyzChiral1);
1249       } else {
1250         logger.fine(" Picked dihedral +240");
1251         OP3.setXYZ(xyzChiral2);
1252       }
1253     }
1254   }
1255 
1256   /**
1257    * buildPSU.
1258    *
1259    * @param residue a {@link ffx.potential.bonded.Residue} object.
1260    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1261    * @param forceField a {@link ForceField} object.
1262    * @param bondList a {@link java.util.List} object.
1263    * @return a {@link ffx.potential.bonded.Residue} object.
1264    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
1265    */
1266   private static Residue buildPSU(Residue residue, Atom C1s, ForceField forceField,
1267       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
1268     // C1s bonds to C5 in PsuedoUridine
1269     Atom C5 = buildHeavy(residue, "C5", C1s, 1485, forceField, bondList);
1270     Atom C6 = buildHeavy(residue, "C6", C5, 1486, forceField, bondList);
1271     Atom N1 = buildHeavy(residue, "N1", C6, 1481, forceField, bondList);
1272     Atom C2 = buildHeavy(residue, "C2", N1, 1482, forceField, bondList);
1273     Atom O2 = buildHeavy(residue, "O2", C2, 1487, forceField, bondList);
1274     Atom N3 = buildHeavy(residue, "N3", C2, 1483, forceField, bondList);
1275     Atom C4 = buildHeavy(residue, "C4", N3, 1484, forceField, bondList);
1276     Atom O4 = buildHeavy(residue, "O4", C4, 1489, forceField, bondList);
1277     buildBond(C4, C5, forceField, bondList);
1278     buildH(residue, "H1", N1, 1.00e0, C2, 120.0e0, O2, 0.0e0, 0, 1491, forceField, bondList);
1279     buildH(residue, "H3", N3, 1.00e0, C2, 120.0e0, O2, 0.0e0, 0, 1488, forceField, bondList);
1280     buildH(residue, "H6", C6, 1.08e0, C5, 120.0e0, C1s, 0.0e0, 0, 1490, forceField, bondList);
1281     return residue;
1282   }
1283 
1284   /**
1285    * Builds a sugar O5 atom. No bonds are made to it.
1286    *
1287    * @param residue The residue to operate on.
1288    * @param lookUp Biotype lookup.
1289    * @param forceField The ForceField to use.
1290    * @return The created sugar O5 atoms.
1291    */
1292   private static Atom buildSugarO5(Residue residue, int lookUp, ForceField forceField) {
1293     Atom C5 = residue.getAtomByName("C5'", true);
1294     Atom C4 = residue.getAtomByName("C4'", true);
1295     Atom C3 = residue.getAtomByName("C3'", true);
1296     return buildHeavy(residue, "O5'", C5, 1.43, C4, 109.5, C3, 180.0, 0, 1244, forceField);
1297   }
1298 
1299   /**
1300    * buildURI.
1301    *
1302    * @param residue a {@link ffx.potential.bonded.Residue} object.
1303    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1304    * @param O4s a {@link ffx.potential.bonded.Atom} object.
1305    * @param C2s a {@link ffx.potential.bonded.Atom} object.
1306    * @param glyco a double.
1307    * @param forceField a {@link ForceField} object.
1308    * @param bondList a {@link java.util.List} object.
1309    * @return a {@link ffx.potential.bonded.Residue} object.
1310    */
1311   private static Residue buildURI(Residue residue, Atom C1s, Atom O4s, Atom C2s, double glyco,
1312       ForceField forceField, List<Bond> bondList) {
1313     Atom N1 = buildHeavy(residue, "N1", C1s, 1.48, O4s, 108.1, C2s, 113.7, 1, 1106, forceField,
1314         bondList);
1315     Atom C2 = buildHeavy(residue, "C2", N1, 1.38, C1s, 117.1, O4s, glyco, 0, 1107, forceField,
1316         bondList);
1317     Atom O2 = buildHeavy(residue, "O2", C2, 1.22, N1, 123.2, C1s, 0.0, 0, 1112, forceField,
1318         bondList);
1319     Atom N3 = buildHeavy(residue, "N3", C2, 1.37, N1, 114.8, C1s, 180.0, 0, 1108, forceField,
1320         bondList);
1321     Atom C4 = buildHeavy(residue, "C4", N3, 1.38, C2, 127.0, N1, 0.0, 0, 1109, forceField, bondList);
1322     Atom O4 = buildHeavy(residue, "O4", C4, 1.23, N3, 119.8, C2, 180.0, 0, 1114, forceField,
1323         bondList);
1324     Atom C5 = buildHeavy(residue, "C5", C4, 1.44, N3, 114.7, C2, 0.0, 0, 1110, forceField, bondList);
1325     Atom C6 = buildHeavy(residue, "C6", C5, 1.34, O4, 119.2, C4, 0.0, 0, 1111, forceField, bondList);
1326     buildBond(C6, N1, forceField, bondList);
1327     buildH(residue, "H3", N3, 1.00e0, C2, 116.5e0, N1, 180.0e0, 0, 1113, forceField, bondList);
1328     buildH(residue, "H5", C5, 1.08e0, C4, 120.4e0, N3, 180.0e0, 0, 1115, forceField, bondList);
1329     buildH(residue, "H6", C6, 1.08e0, C5, 118.6e0, C4, 180.0e0, 0, 1116, forceField, bondList);
1330 
1331     return residue;
1332   }
1333 
1334   /**
1335    * buildYYG.
1336    *
1337    * @param residue a {@link ffx.potential.bonded.Residue} object.
1338    * @param C1s a {@link ffx.potential.bonded.Atom} object.
1339    * @param forceField a {@link ForceField} object.
1340    * @param bondList a {@link java.util.List} object.
1341    * @return a {@link ffx.potential.bonded.Residue} object.
1342    * @throws ffx.potential.bonded.BondedUtils.MissingHeavyAtomException if any.
1343    */
1344   private static Residue buildYYG(Residue residue, Atom C1s, ForceField forceField,
1345       List<Bond> bondList) throws MissingHeavyAtomException, MissingAtomTypeException {
1346     Atom N9 = buildHeavy(residue, "N9", C1s, 1640, forceField, bondList);
1347     Atom C8 = buildHeavy(residue, "C8", N9, 1644, forceField, bondList);
1348     Atom N7 = buildHeavy(residue, "N7", C8, 1643, forceField, bondList);
1349     Atom C5 = buildHeavy(residue, "C5", N7, 1642, forceField, bondList);
1350     Atom C6 = buildHeavy(residue, "C6", C5, 1648, forceField, bondList);
1351     Atom O6 = buildHeavy(residue, "O6", C6, 1650, forceField, bondList);
1352     Atom N1 = buildHeavy(residue, "N1", C6, 1647, forceField, bondList);
1353     Atom C2 = buildHeavy(residue, "C2", N1, 1646, forceField, bondList);
1354     Atom N2 = buildHeavy(residue, "N2", C2, 1649, forceField, bondList);
1355     Atom N3 = buildHeavy(residue, "N3", C2, 1645, forceField, bondList);
1356     Atom C3 = buildHeavy(residue, "C3", N3, 1652, forceField, bondList);
1357     Atom C4 = buildHeavy(residue, "C4", N3, 1641, forceField, bondList);
1358     Atom C11 = buildHeavy(residue, "C11", N2, 1657, forceField, bondList);
1359     Atom C10 = buildHeavy(residue, "C10", C11, 1658, forceField, bondList);
1360     Atom C12 = buildHeavy(residue, "C12", C11, 1656, forceField, bondList);
1361     Atom C13 = buildHeavy(residue, "C13", C12, 1662, forceField, bondList);
1362     Atom C14 = buildHeavy(residue, "C14", C13, 1665, forceField, bondList);
1363     Atom C15 = buildHeavy(residue, "C15", C14, 1668, forceField, bondList);
1364     Atom C16 = buildHeavy(residue, "C16", C15, 1675, forceField, bondList);
1365     Atom O17 = buildHeavy(residue, "O17", C16, 1676, forceField, bondList);
1366     Atom O18 = buildHeavy(residue, "O18", C16, 1674, forceField, bondList);
1367     Atom C19 = buildHeavy(residue, "C19", O18, 1670, forceField, bondList);
1368     Atom N20 = buildHeavy(residue, "N20", C15, 1677, forceField, bondList);
1369     Atom C21 = buildHeavy(residue, "C21", N20, 1679, forceField, bondList);
1370     Atom O22 = buildHeavy(residue, "O22", C21, 1680, forceField, bondList);
1371     Atom O23 = buildHeavy(residue, "O23", C21, 1681, forceField, bondList);
1372     Atom C24 = buildHeavy(residue, "C24", O23, 1682, forceField, bondList);
1373     buildBond(C4, C5, forceField, bondList);
1374     buildBond(C4, N9, forceField, bondList);
1375     buildBond(N1, C12, forceField, bondList);
1376     buildH(residue, "H8", C8, 1.08e0, N7, 123.0e0, C5, 180.0e0, 0, 1651, forceField, bondList);
1377     Atom H31 = buildH(residue, "H31", C3, 1.08e0, N3, 109.5e0, C4, 0.0e0, 0, 1653, forceField,
1378         bondList);
1379     buildH(residue, "H32", C3, 1.08e0, N3, 109.5e0, H31, 109.5e0, 1, 1654, forceField, bondList);
1380     buildH(residue, "H33", C3, 1.08e0, N3, 109.5e0, H31, 109.5e0, -1, 1655, forceField, bondList);
1381     Atom H101 = buildH(residue, "H101", C10, 1.08e0, C11, 109.5e0, N2, 0.0e0, 0, 1659, forceField,
1382         bondList);
1383     buildH(residue, "H102", C10, 1.08e0, C11, 109.5e0, H101, 109.5e0, 1, 1660, forceField, bondList);
1384     buildH(residue, "H103", C10, 1.08e0, C11, 109.5e0, H101, 109.5e0, -1, 1661, forceField,
1385         bondList);
1386     buildH(residue, "H131", C13, 1.08e0, C12, 109.5e0, C14, 109.5e0, 1, 1663, forceField, bondList);
1387     buildH(residue, "H132", C13, 1.08e0, C12, 109.5e0, C14, 109.5e0, -1, 1664, forceField, bondList);
1388     buildH(residue, "H141", C14, 1.08e0, C13, 109.5e0, C15, 109.5e0, 1, 1666, forceField, bondList);
1389     buildH(residue, "H142", C14, 1.08e0, C13, 109.5e0, C15, 109.5e0, -1, 1667, forceField, bondList);
1390     buildH(residue, "H15", C15, 1.08e0, C14, 109.5e0, O18, 180.e0, 0, 1669, forceField, bondList);
1391     Atom H191 = buildH(residue, "H191", C19, 1.08e0, O18, 109.5e0, C16, 0.0e0, 0, 1671, forceField,
1392         bondList);
1393     buildH(residue, "H192", C19, 1.08e0, O18, 109.5e0, H191, 109.5e0, 1, 1672, forceField, bondList);
1394     buildH(residue, "H193", C19, 1.08e0, O18, 109.5e0, H191, 109.5e0, -1, 1673, forceField,
1395         bondList);
1396     buildH(residue, "HN2", N20, 1.00e0, C15, 109.5e0, O22, 180.0e0, 0, 1678, forceField, bondList);
1397     Atom H241 = buildH(residue, "H241", C24, 1.08e0, O23, 109.5e0, C21, 0.0e0, 0, 1683, forceField,
1398         bondList);
1399     buildH(residue, "H242", C24, 1.08e0, O23, 109.5e0, H241, 109.5e0, 1, 1684, forceField, bondList);
1400     buildH(residue, "H243", C24, 1.08e0, O23, 109.5e0, H241, 109.5e0, -1, 1685, forceField,
1401         bondList);
1402     return residue;
1403   }
1404 
1405   /**
1406    * Intended for 5'-terminal phosphate caps listed as their own residue: find a given atom name in
1407    * the next residue.
1408    *
1409    * @param residue Current residue.
1410    * @param nextResidue Next residue to search in.
1411    * @param atomName Atom name to look for.
1412    * @return The requested atom.
1413    */
1414   private static Atom getNextResAtom(Residue residue, Residue nextResidue, String atomName) {
1415     if (nextResidue == null) {
1416       throw new IllegalArgumentException(
1417           String.format(" Residue %s: No subsequent residue to find atom %s in!", residue,
1418               atomName));
1419     }
1420     List<Atom> nrAtoms = nextResidue.getAtomList();
1421     for (Atom atom : nrAtoms) {
1422       if (atom.getName().equalsIgnoreCase(atomName)) {
1423         return atom;
1424       }
1425     }
1426     throw new IllegalArgumentException(
1427         String.format(" Residue %s: Subsequent residue %s did not contain an atom %s", residue,
1428             nextResidue, atomName));
1429   }
1430 
1431 }