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.algorithms.optimize;
39  
40  import static java.lang.String.format;
41  import static org.junit.Assert.assertEquals;
42  
43  import ffx.algorithms.misc.AlgorithmsTest;
44  import ffx.algorithms.optimize.manybody.EliminatedRotamers;
45  import ffx.algorithms.optimize.manybody.EnergyExpansion;
46  import ffx.potential.ForceFieldEnergy;
47  import ffx.potential.MolecularAssembly;
48  import ffx.potential.Utilities;
49  import ffx.potential.bonded.Polymer;
50  import ffx.potential.bonded.Residue;
51  import ffx.potential.bonded.Rotamer;
52  import ffx.potential.bonded.RotamerLibrary;
53  import ffx.potential.utils.PotentialsUtils;
54  import java.io.File;
55  import java.util.ArrayList;
56  import java.util.Arrays;
57  import java.util.Collection;
58  import java.util.List;
59  import org.junit.Test;
60  import org.junit.runner.RunWith;
61  import org.junit.runners.Parameterized;
62  
63  /**
64   * Test the Goldstein elimination criteria for both self and pair eliminations.
65   *
66   * @author Mallory R. Tollefson
67   * @author Claire E. OConnell
68   */
69  @RunWith(Parameterized.class)
70  public class RotamerOptimizationTest extends AlgorithmsTest {
71  
72    private final String info;
73    private final String filename;
74    private final String restartName;
75    private final int startResID;
76    private final int endResID;
77    private final int pruningLevel;
78    private final boolean useGoldstein;
79    private final boolean useThreeBody;
80    private final boolean useOriginalRotamers;
81    private final boolean doOverallOpt;
82    private final double expectedEnergy;
83    private final boolean doSelfOpt;
84    private final double expectedSelfEnergy;
85    private final boolean doPairOpt;
86    private final int pairResidue;
87    private final double expectedPairEnergy;
88    private final boolean doTripleOpt;
89    private final int tripleResidue1;
90    private final int tripleResidue2;
91    private final double expectedTripleEnergy;
92    private final double tolerance;
93    private File restartFile;
94    private MolecularAssembly molecularAssembly;
95    private ForceFieldEnergy forceFieldEnergy;
96  
97    public RotamerOptimizationTest(
98        String info,
99        String filename,
100       String restartName,
101       int startResID,
102       int endResID,
103       int pruningLevel,
104       boolean useGoldstein,
105       boolean useThreeBody,
106       boolean useOriginalRotamers,
107       boolean doOverallOpt,
108       double expectedEnergy,
109       boolean doSelfOpt,
110       double expectedSelfEnergy,
111       boolean doPairOpt,
112       int pairResidue,
113       double expectedPairEnergy,
114       boolean doTripleOpt,
115       int tripleResidue1,
116       int tripleResidue2,
117       double expectedTripleEnergy,
118       double tolerance) {
119     this.info = info;
120     this.filename = filename;
121     this.restartName = restartName;
122     this.startResID = startResID;
123     this.endResID = endResID;
124     this.pruningLevel = pruningLevel;
125     this.useGoldstein = useGoldstein;
126     this.useThreeBody = useThreeBody;
127     this.useOriginalRotamers = useOriginalRotamers;
128     this.doOverallOpt = doOverallOpt;
129     this.expectedEnergy = expectedEnergy;
130     this.doSelfOpt = doSelfOpt;
131     this.expectedSelfEnergy = expectedSelfEnergy;
132     this.doPairOpt = doPairOpt;
133     this.pairResidue = pairResidue;
134     this.expectedPairEnergy = expectedPairEnergy;
135     this.doTripleOpt = doTripleOpt;
136     this.tripleResidue1 = tripleResidue1;
137     this.tripleResidue2 = tripleResidue2;
138     this.expectedTripleEnergy = expectedTripleEnergy;
139     this.tolerance = tolerance;
140   }
141 
142   @Parameterized.Parameters
143   public static Collection<Object[]> data() {
144     return Arrays.asList(
145         new Object[][] {
146             {
147                 "Chignolin Direct with Orig Rot - No Pruning (Goldstein)",
148                 "5awl.pdb",
149                 "5awl.direct.orig.prun0.residues1-4.restart",
150                 1, // Start Residue.
151                 4, // End Residue.
152                 0, // Pruning Level.
153                 true, // Goldstein Elimination.
154                 false, // Use 3-body Energies.
155                 true, // Use Original Rotamers.
156                 true, // Do Overall Opt.
157                 -212.8853397349646, // Expected Energy.
158                 true, // Do Self-Energy Opt.
159                 -212.8853397349646, // Expected Self-Energy.
160                 true, // Do Pair-Energy Opt.
161                 1, // Pair residue
162                 992.7753296883213, // Expected Pair-Energy.
163                 false, // Do Trimer-Energy Opt.
164                 1, // Trimer residue 1.
165                 2, // Trimer residue 2.
166                 0.0, // Expected trimer energy.
167                 1.0e-3 // Energy Tolerance.
168             },
169             {
170                 "Chignolin Direct with Orig Rot - Singles Pruning (Goldstein)",
171                 "5awl.pdb",
172                 "5awl.direct.orig.prun1.residues1-4.restart",
173                 1, // Start Residue.
174                 4, // End Residue.
175                 1, // Pruning Level.
176                 true, // Goldstein Elimination.
177                 false, // Use 3-body Energies.
178                 true, // Use Original Rotamers.
179                 true, // Do Overall Opt.
180                 -212.8853397349646, // Expected Energy.
181                 true, // Do Self-Energy Opt.
182                 -212.8853397349646, // Expected Self-Energy.
183                 true, // Do Pair-Energy Opt.
184                 1, // Pair residue
185                 -197.82425073284452, // Expected Pair-Energy.
186                 false, // Do Trimer-Energy Opt.
187                 1, // Trimer residue 1.
188                 2, // Trimer residue 2.
189                 0.0, // Expected trimer energy.
190                 1.0e-3 // Energy Tolerance.
191             },
192             {
193                 "Chignolin Direct with Orig Rot - Pairs Pruning (Goldstein)",
194                 "5awl.pdb",
195                 "5awl.direct.orig.prun2.residues1-4.restart",
196                 1, // Start Residue.
197                 4, // End Residue.
198                 2, // Pruning Level.
199                 true, // Goldstein Elimination.
200                 false, // Use 3-body Energies.
201                 true, // Use Original Rotamers.
202                 true, // Do Overall Opt.
203                 -212.8853397349646, // Expected Energy.
204                 true, // Do Self-Energy Opt.
205                 -212.8853397349646, // Expected Self-Energy.
206                 true, // Do Pair-Energy Opt.
207                 1, // Pair residue
208                 -197.82425073284452, // Expected Pair-Energy.
209                 false, // Do Trimer-Energy Opt.
210                 1, // Trimer residue 1.
211                 2, // Trimer residue 2.
212                 0.0, // Expected trimer energy.
213                 1.0e-3 // Energy Tolerance.
214             },
215             {
216                 "Chignolin Direct with Orig Rot - 3-body (Goldstein)",
217                 "5awl.pdb",
218                 "5awl.direct.orig.prun1.3body.residues1-4.restart",
219                 1, // Start Residue.
220                 4, // End Residue.
221                 1, // Pruning Level.
222                 true, // Goldstein Elimination.
223                 true, // Use 3-body Energies.
224                 true, // Use Original Rotamers.
225                 true, // Do Overall Opt.
226                 -212.8853397349646, // Expected Energy.
227                 true, // Do Self-Energy Opt.
228                 -212.8853397349646, // Expected Self-Energy.
229                 true, // Do Pair-Energy Opt.
230                 1, // Pair residue
231                 -197.82425073284452, // Expected Pair-Energy.
232                 true, // Do Trimer-Energy Opt.
233                 1, // Trimer residue 1.
234                 2, // Trimer residue 2.
235                 -189.1182888424594, // Expected trimer energy.
236                 1.0e-3 // Energy Tolerance.
237             },
238             {
239                 "Chignolin Direct with Orig Rot - No Pruning (DEE)",
240                 "5awl.pdb",
241                 "5awl.direct.orig.prun0.residues1-4.restart",
242                 1, // Start Residue.
243                 4, // End Residue.
244                 0, // Pruning Level.
245                 false, // Goldstein Elimination.
246                 false, // Use 3-body Energies.
247                 true, // Use Original Rotamers.
248                 true, // Do Overall Opt.
249                 -212.8853397349646, // Expected Energy.
250                 true, // Do Self-Energy Opt.
251                 -212.8853397349646, // Expected Self-Energy.
252                 true, // Do Pair-Energy Opt.
253                 1, // Pair residue
254                 992.7753296883207, // Expected Pair-Energy.
255                 false, // Do Trimer-Energy Opt.
256                 1, // Trimer residue 1.
257                 2, // Trimer residue 2.
258                 0.0, // Expected trimer energy.
259                 1.0e-3 // Energy Tolerance.
260             },
261             {
262                 "Chignolin Direct with Orig Rot - Singles Pruning (DEE)",
263                 "5awl.pdb",
264                 "5awl.direct.orig.prun1.residues1-4.restart",
265                 1, // Start Residue.
266                 4, // End Residue.
267                 1, // Pruning Level.
268                 false, // Goldstein Elimination.
269                 false, // Use 3-body Energies.
270                 true, // Use Original Rotamers.
271                 true, // Do Overall Opt.
272                 -212.8853397349646, // Expected Energy.
273                 true, // Do Self-Energy Opt.
274                 -212.8853397349646, // Expected Self-Energy.
275                 true, // Do Pair-Energy Opt.
276                 1, // Pair residue
277                 -197.82425073284452, // Expected Pair-Energy.
278                 false, // Do Trimer-Energy Opt.
279                 1, // Trimer residue 1.
280                 2, // Trimer residue 2.
281                 0.0, // Expected trimer energy.
282                 1.0e-3 // Energy Tolerance.
283             },
284             {
285                 "Chignolin Direct with Orig Rot - Pairs Pruning (DEE)",
286                 "5awl.pdb",
287                 "5awl.direct.orig.prun2.residues1-4.restart",
288                 1, // Start Residue.
289                 4, // End Residue.
290                 2, // Pruning Level.
291                 false, // Goldstein Elimination.
292                 false, // Use 3-body Energies.
293                 true, // Use Original Rotamers.
294                 true, // Do Overall Opt.
295                 -212.8853397349646, // Expected Energy.
296                 true, // Do Self-Energy Opt.
297                 -212.8853397349646, // Expected Self-Energy.
298                 true, // Do Pair-Energy Opt.
299                 1, // Pair residue
300                 -197.82425073284452, // Expected Pair-Energy.
301                 false, // Do Trimer-Energy Opt.
302                 1, // Trimer residue 1.
303                 2, // Trimer residue 2.
304                 0.0, // Expected trimer energy.
305                 1.0e-3 // Energy Tolerance.
306             },
307             {
308                 "Chignolin Direct with Orig Rot - 3-body (DEE)",
309                 "5awl.pdb",
310                 "5awl.direct.orig.prun1.3body.residues1-4.restart",
311                 1, // Start Residue.
312                 4, // End Residue.
313                 1, // Pruning Level.
314                 false, // Goldstein Elimination.
315                 true, // Use 3-body Energies.
316                 true, // Use Original Rotamers.
317                 true, // Do Overall Opt.
318                 -212.8853397349646, // Expected Energy.
319                 true, // Do Self-Energy Opt.
320                 -212.8853397349646, // Expected Self-Energy.
321                 true, // Do Pair-Energy Opt.
322                 1, // Pair residue
323                 -197.82425073284452, // Expected Pair-Energy.
324                 true, // Do Trimer-Energy Opt.
325                 1, // Trimer residue 1.
326                 2, // Trimer residue 2.
327                 -189.1182888424594, // Expected trimer energy.
328                 1.0e-3 // Energy Tolerance.
329             }
330         });
331   }
332 
333   @Test
334   public void testPairEnergyElimination() {
335     // Load the test system.
336     load();
337 
338     // Run the optimization.
339     RotamerLibrary rLib = new RotamerLibrary(useOriginalRotamers);
340 
341     int counter = 1;
342     List<Residue> residueList = new ArrayList<>();
343     Polymer[] polymers = molecularAssembly.getChains();
344     for (Polymer polymer : polymers) {
345       List<Residue> residues = polymer.getResidues();
346       for (int i = 0; i < endResID; i++) {
347         Residue residue = residues.get(i);
348         Rotamer[] rotamers = residue.setRotamers(rLib);
349         if (rotamers != null) {
350           int nrot = rotamers.length;
351           if (nrot == 1) {
352             RotamerLibrary.applyRotamer(residue, rotamers[0]);
353           }
354           if (counter >= startResID) {
355             residueList.add(residue);
356           }
357         }
358         counter++;
359       }
360     }
361     Residue[] residues = residueList.toArray(new Residue[0]);
362 
363     RotamerOptimization rotamerOptimization =
364         new RotamerOptimization(molecularAssembly, forceFieldEnergy, null);
365     rotamerOptimization.setRotamerLibrary(rLib);
366     rotamerOptimization.setThreeBodyEnergy(useThreeBody);
367     rotamerOptimization.setUseGoldstein(useGoldstein);
368     rotamerOptimization.setPruning(pruningLevel);
369     rotamerOptimization.setEnergyRestartFile(restartFile);
370 
371     rotamerOptimization.setResidues(residueList);
372     rotamerOptimization.setSingletonClashThreshold(20.0);
373     rotamerOptimization.setPairClashThreshold(20.0);
374 
375     double energy;
376     int nRes = residueList.size();
377     if (doOverallOpt) {
378       rotamerOptimization.turnRotamerSingleEliminationOff();
379       try {
380         energy = rotamerOptimization.optimize(RotamerOptimization.Algorithm.ALL);
381       } catch (Exception e) {
382         logger.warning(Utilities.stackTraceToString(e));
383         throw e;
384       }
385       // System.out.println("The expected overall energy is: " + energy);
386       assertEquals(info + " Total Energy", expectedEnergy, energy, tolerance);
387     }
388 
389     // ToDo: Test self-energy use for rotamer 2-body eliminations.
390     if (doSelfOpt) {
391       rotamerOptimization.turnRotamerSingleEliminationOff();
392       rotamerOptimization.setTestSelfEnergyEliminations(true);
393       energy = rotamerOptimization.optimize(RotamerOptimization.Algorithm.ALL);
394       // System.out.println("The expected self energy is: " + energy);
395       assertEquals(info + " Self-Energy", expectedSelfEnergy, energy, tolerance);
396 
397       // Check that optimized rotamers are equivalent to the lowest self-energy of each residue.
398       int[] optimum = rotamerOptimization.getOptimumRotamers();
399 
400       EliminatedRotamers eR = rotamerOptimization.getEliminatedRotamers();
401 
402       // Loop over all residues
403       for (int i = 0; i < nRes; i++) {
404         Residue res = residueList.get(i);
405         Rotamer[] rotI = res.getRotamers();
406         int nRot = rotI.length;
407 
408         int rotCounter = 0;
409         while (rotCounter < nRot && eR.checkPrunedSingles(i, rotCounter)) {
410           rotCounter++;
411         }
412 
413         EnergyExpansion eE = rotamerOptimization.getEnergyExpansion();
414         double lowEnergy = eE.getSelf(i, rotCounter);
415         int bestRot = rotCounter;
416         for (int ri = 1; ri < nRot; ri++) {
417           if (!eR.checkPrunedSingles(i, ri)) {
418             double selfEnergy = eE.getSelf(i, ri);
419             if (selfEnergy < lowEnergy) {
420               lowEnergy = selfEnergy;
421               bestRot = ri;
422             }
423           }
424         }
425         assertEquals(format(" %s Self-Energy of residue %d", info, i), optimum[i], bestRot);
426       }
427     }
428 
429     // ToDo: Test 2-body energy use for rotamer pair eliminations.
430     if (doPairOpt) {
431       rotamerOptimization.turnRotamerSingleEliminationOff();
432       rotamerOptimization.setTestPairEnergyEliminations(pairResidue);
433       energy = rotamerOptimization.optimize(RotamerOptimization.Algorithm.ALL);
434       // System.out.println("The expected 2-body energy is: " + energy);
435       assertEquals(info + " Pair-Energy", expectedPairEnergy, energy, tolerance);
436 
437       // Check that optimized rotamers are equivalent to the lowest 2-body energy sum for the
438       // "pairResidue".
439       int[] optimum = rotamerOptimization.getOptimumRotamers();
440 
441       Residue resI = residueList.get(pairResidue);
442       Rotamer[] rotI = resI.getRotamers();
443       int ni = rotI.length;
444 
445       double minEnergy = Double.POSITIVE_INFINITY;
446       int bestRotI = -1;
447 
448       EliminatedRotamers eR = rotamerOptimization.getEliminatedRotamers();
449 
450       // Loop over the pairResidue rotamers to find its lowest energy rotamer.
451       for (int ri = 0; ri < ni; ri++) {
452         double energyForRi = 0.0;
453         if (eR.checkPrunedSingles(pairResidue, ri)) {
454           continue;
455         }
456         // Loop over residue J
457         for (int j = 0; j < nRes; j++) {
458           if (j == pairResidue) {
459             continue;
460           }
461           Residue resJ = residueList.get(j);
462           Rotamer[] rotJ = resJ.getRotamers();
463           int nRot = rotJ.length;
464 
465           int rj = 0;
466           while (eR.checkPrunedSingles(j, rj) || eR.checkPrunedPairs(pairResidue, ri, j, rj)) {
467             if (++rj >= nRot) {
468               logger.warning("RJ is too large.");
469             }
470           }
471 
472           EnergyExpansion eE = rotamerOptimization.getEnergyExpansion();
473           double lowEnergy = eE.get2Body(pairResidue, ri, j, rj);
474 
475           for (rj = 1; rj < nRot; rj++) {
476             if (!eR.checkPrunedSingles(j, rj) && !eR.checkPrunedPairs(pairResidue, ri, j, rj)) {
477               double pairEnergy = eE.get2Body(pairResidue, ri, j, rj);
478               if (pairEnergy < lowEnergy) {
479                 lowEnergy = pairEnergy;
480               }
481             }
482           }
483           energyForRi += lowEnergy;
484         }
485         if (energyForRi < minEnergy) {
486           minEnergy = energyForRi;
487           bestRotI = ri;
488         }
489       }
490 
491       assertEquals(
492           format(
493               " %s Best 2-body energy sum for residue %d is with rotamer %d at %10.4f.",
494               info, pairResidue, bestRotI, minEnergy),
495           optimum[pairResidue],
496           bestRotI);
497 
498       // Given the minimum energy rotamer for "pairResidue" is "bestRotI", we can check selected
499       // rotamers for all other residues.
500       for (int j = 0; j < nRes; j++) {
501         if (j == pairResidue) {
502           continue;
503         }
504         Residue resJ = residueList.get(j);
505         Rotamer[] rotJ = resJ.getRotamers();
506         int nRotJ = rotJ.length;
507 
508         int rotCounter = 0;
509         while (eR.checkPrunedPairs(pairResidue, bestRotI, j, rotCounter) && rotCounter < nRotJ) {
510           rotCounter++;
511         }
512 
513         EnergyExpansion eE = rotamerOptimization.getEnergyExpansion();
514         double lowEnergy = eE.get2Body(pairResidue, bestRotI, j, rotCounter);
515         int bestRotJ = rotCounter;
516         for (int rj = 1; rj < nRotJ; rj++) {
517           if (!eR.checkPrunedSingles(j, rj) && !eR.checkPrunedPairs(pairResidue, bestRotI, j, rj)) {
518             double pairEnergy = eE.get2Body(pairResidue, bestRotI, j, rj);
519             if (pairEnergy < lowEnergy) {
520               lowEnergy = pairEnergy;
521               bestRotJ = rj;
522             }
523           }
524         }
525         if (bestRotJ != optimum[j]) {
526           // Check if 2-body energies are equal.
527           if (lowEnergy == eE.get2Body(pairResidue, bestRotI, j, optimum[j])) {
528             logger.warning(
529                 format(
530                     " Identical 2-body energies for %s: resi %d-%d, resj %d, best rotamer J %d, optimum J %d, 2-body energy (both) %10.6f",
531                     info, pairResidue, bestRotI, j, bestRotJ, optimum[j], lowEnergy));
532           } else {
533             assertEquals(
534                 format(
535                     " %s Pair-Energy of residue (%d,%d) with residue %d",
536                     info, pairResidue, bestRotI, j),
537                 optimum[j],
538                 bestRotJ);
539           }
540         }
541       }
542     }
543 
544     // ToDo: Test 3-Body use for rotamer pair eliminations.
545     if (doTripleOpt) {
546       rotamerOptimization.turnRotamerSingleEliminationOff();
547       rotamerOptimization.setTestTripleEnergyEliminations(tripleResidue1, tripleResidue2);
548       try {
549         energy = rotamerOptimization.optimize(RotamerOptimization.Algorithm.ALL);
550         assertEquals(info + " Triple-Energy", expectedTripleEnergy, energy, tolerance);
551       } catch (Exception e) {
552         e.fillInStackTrace();
553         e.printStackTrace();
554         logger.log(java.util.logging.Level.INFO, "Error in doTripleOpt", e);
555       }
556 
557       // Check that optimized rotamers are equivalent to the lowest 3-body energy of each residue
558       // with the tripleResidue1 and 2.
559       int[] optimum = rotamerOptimization.getOptimumRotamers();
560 
561       // fix residue 1 and gets its rotamers
562       Residue resI = residueList.get(tripleResidue1);
563       Rotamer[] rotI = resI.getRotamers();
564       int ni = rotI.length;
565 
566       // fix residue 2 and get its rotamers
567       Residue resJ = residueList.get(tripleResidue2);
568       Rotamer[] rotJ = resJ.getRotamers();
569       int nj = rotJ.length;
570 
571       double minEnergyIJ = Double.POSITIVE_INFINITY;
572       int bestRotI = -1;
573       int bestRotJ = -1;
574 
575       EliminatedRotamers eR = rotamerOptimization.getEliminatedRotamers();
576 
577       for (int ri = 0; ri < ni; ri++) { // loop through rot I
578         if (eR.check(tripleResidue1, ri)) {
579           continue;
580         }
581         for (int rj = 0; rj < nj; rj++) { // loop through rot J
582           if (eR.checkPrunedSingles(tripleResidue2, rj)
583               || eR.checkPrunedPairs(tripleResidue1, ri, tripleResidue2, rj)) {
584             continue;
585           }
586           double currentEnergy = 0.0;
587           for (int k = 0; k < nRes; k++) { // loop through all other residues
588             if (k == tripleResidue1 || k == tripleResidue2) {
589               continue;
590             }
591             Residue resK = residueList.get(k);
592             Rotamer[] rotK = resK.getRotamers();
593             int nk = rotK.length;
594 
595             int rkStart = 0;
596             while (eR.checkPrunedSingles(k, rkStart)
597                 || eR.checkPrunedPairs(tripleResidue1, ri, k, rkStart)
598                 || eR.checkPrunedPairs(tripleResidue2, rj, k, rkStart)) {
599               if (++rkStart >= nk) {
600                 logger.warning("RJ is too large.");
601               }
602             }
603 
604             EnergyExpansion eE = rotamerOptimization.getEnergyExpansion();
605             double lowEnergy =
606                 eE.get3Body(residues, tripleResidue1, ri, tripleResidue2, rj, k, rkStart);
607             for (int rk = rkStart; rk < nk; rk++) {
608               if (!eR.checkPrunedSingles(k, rk)
609                   && !eR.checkPrunedPairs(tripleResidue1, ri, k, rk)
610                   && !eR.checkPrunedPairs(tripleResidue2, rj, k, rk)) {
611                 double tripleEnergy =
612                     eE.get3Body(residues, tripleResidue1, ri, tripleResidue2, rj, k, rk);
613                 if (tripleEnergy < lowEnergy) {
614                   lowEnergy = tripleEnergy;
615                 }
616               }
617             }
618             currentEnergy +=
619                 lowEnergy; // adds lowest energy conformation of residue k to that of the rotamer I
620           }
621           if (currentEnergy < minEnergyIJ) {
622             minEnergyIJ = currentEnergy;
623             bestRotI = ri;
624             bestRotJ = rj;
625           }
626         }
627       }
628 
629       assertEquals(
630           format(
631               " %s Best three-body energy sum for residue %d is with rotamer %d at %10.4f.",
632               info, tripleResidue1, bestRotI, minEnergyIJ),
633           optimum[tripleResidue1],
634           bestRotI);
635 
636       assertEquals(
637           format(
638               " %s Best three-body energy sum for residue %d is with rotamer %d at %10.4f.",
639               info, tripleResidue2, bestRotJ, minEnergyIJ),
640           optimum[tripleResidue2],
641           bestRotJ);
642 
643       // loop over the residues to find the best rotamer per residue given bestRotI and bestRotJ
644       for (int k = 0; k < nRes; k++) {
645         if (k == tripleResidue1 || k == tripleResidue2) {
646           continue;
647         }
648         Residue resK = residueList.get(k);
649         Rotamer[] rotK = resK.getRotamers();
650         int nk = rotK.length;
651 
652         int rotCounter = 0;
653         while (eR.checkPrunedPairs(tripleResidue1, bestRotI, k, rotCounter)
654             && eR.checkPrunedPairs(tripleResidue2, bestRotJ, k, rotCounter)
655             && rotCounter < nk) {
656           rotCounter++;
657         }
658         EnergyExpansion eE = rotamerOptimization.getEnergyExpansion();
659         double lowEnergy =
660             eE.get3Body(
661                 residues, tripleResidue1, bestRotI, tripleResidue2, bestRotJ, k, rotCounter);
662         int bestRotK = rotCounter;
663         for (int rk = 1; rk < nk; rk++) {
664           if (!eR.checkPrunedSingles(k, rk)
665               && !eR.checkPrunedPairs(tripleResidue1, bestRotI, k, rk)
666               && !eR.checkPrunedPairs(tripleResidue2, bestRotJ, k, rk)) {
667             double tripleEnergy =
668                 eE.get3Body(residues, tripleResidue1, bestRotI, tripleResidue2, bestRotJ, k, rk);
669             if (tripleEnergy < lowEnergy) {
670               lowEnergy = tripleEnergy;
671               bestRotK = rk;
672             }
673           }
674         }
675         assertEquals(
676             format(
677                 " %s Triple-Energy of residue (%d,%d) and residue (%d,%d) with residue %d",
678                 info, tripleResidue1, bestRotI, tripleResidue2, bestRotJ, k),
679             optimum[k],
680             bestRotK);
681       }
682     }
683   }
684 
685   @Test
686   public void testSelfEnergyElimination() {
687     // Load the test system.
688     load();
689 
690     // Run the optimization.
691     RotamerLibrary rLib = new RotamerLibrary(useOriginalRotamers);
692 
693     int counter = 1;
694     List<Residue> residueList = new ArrayList<>();
695     Polymer[] polymers = molecularAssembly.getChains();
696     for (Polymer polymer : polymers) {
697       List<Residue> residues = polymer.getResidues();
698       for (int i = 0; i < endResID; i++) {
699         Residue residue = residues.get(i);
700         Rotamer[] rotamers = residue.setRotamers(rLib);
701         if (rotamers != null) {
702           int nrot = rotamers.length;
703           if (nrot == 1) {
704             RotamerLibrary.applyRotamer(residue, rotamers[0]);
705           }
706           if (counter >= startResID) {
707             residueList.add(residue);
708           }
709         }
710         counter++;
711       }
712     }
713     Residue[] residues = residueList.toArray(new Residue[0]);
714 
715     RotamerOptimization rotamerOptimization =
716         new RotamerOptimization(molecularAssembly, forceFieldEnergy, null);
717     rotamerOptimization.setRotamerLibrary(rLib);
718     rotamerOptimization.setThreeBodyEnergy(useThreeBody);
719     rotamerOptimization.setUseGoldstein(useGoldstein);
720     rotamerOptimization.setPruning(pruningLevel);
721     rotamerOptimization.setEnergyRestartFile(restartFile);
722     rotamerOptimization.setResidues(residueList);
723     rotamerOptimization.setSingletonClashThreshold(20.0);
724     rotamerOptimization.setPairClashThreshold(20.0);
725 
726     double energy;
727     int nRes = residueList.size();
728     if (doOverallOpt) {
729       rotamerOptimization.turnRotamerPairEliminationOff();
730       rotamerOptimization.setTestOverallOpt(true);
731       try {
732         energy = rotamerOptimization.optimize(RotamerOptimization.Algorithm.ALL);
733       } catch (Exception e) {
734         logger.warning(Utilities.stackTraceToString(e));
735         throw e;
736       }
737       // System.out.println("The expected overall energy is: " + energy);
738       assertEquals(info + " Total Energy", expectedEnergy, energy, tolerance);
739     }
740 
741     if (doSelfOpt) {
742       rotamerOptimization.turnRotamerPairEliminationOff();
743       rotamerOptimization.setTestSelfEnergyEliminations(true);
744       energy = rotamerOptimization.optimize(RotamerOptimization.Algorithm.ALL);
745       // System.out.println("The expected self is: " + energy);
746       assertEquals(info + " Self-Energy", expectedSelfEnergy, energy, tolerance);
747 
748       // Check that optimized rotamers are equivalent to the lowest self-energy of each residue.
749       int[] optimum = rotamerOptimization.getOptimumRotamers();
750 
751       EliminatedRotamers eR = rotamerOptimization.getEliminatedRotamers();
752 
753       // Loop over all residues
754       for (int i = 0; i < nRes; i++) {
755         Residue res = residueList.get(i);
756         Rotamer[] rotI = res.getRotamers();
757         int nRot = rotI.length;
758 
759         int rotCounter = 0;
760         while (rotCounter < nRot && eR.checkPrunedSingles(i, rotCounter)) {
761           rotCounter++;
762         }
763 
764         EnergyExpansion eE = rotamerOptimization.getEnergyExpansion();
765         double lowEnergy = eE.getSelf(i, rotCounter);
766         int bestRot = rotCounter;
767         for (int ri = 1; ri < nRot; ri++) {
768           if (!eR.checkPrunedSingles(i, ri)) {
769             double selfEnergy = eE.getSelf(i, ri);
770             if (selfEnergy < lowEnergy) {
771               lowEnergy = selfEnergy;
772               bestRot = ri;
773             }
774           }
775         }
776         assertEquals(format(" %s Self-Energy of residue %d", info, i), optimum[i], bestRot);
777       }
778     }
779 
780     if (doPairOpt) {
781       rotamerOptimization.turnRotamerPairEliminationOff();
782       rotamerOptimization.setTestPairEnergyEliminations(pairResidue);
783       energy = rotamerOptimization.optimize(RotamerOptimization.Algorithm.ALL);
784       assertEquals(info + " Pair-Energy", expectedPairEnergy, energy, tolerance);
785 
786       // Check that optimized rotamers are equivalent to the lowest 2-Body energy sum for the
787       // "pairResidue".
788       int[] optimum = rotamerOptimization.getOptimumRotamers();
789 
790       Residue resI = residueList.get(pairResidue);
791       Rotamer[] rotI = resI.getRotamers();
792       int ni = rotI.length;
793 
794       double minEnergy = Double.POSITIVE_INFINITY;
795       int bestRotI = -1;
796 
797       EliminatedRotamers eR = rotamerOptimization.getEliminatedRotamers();
798 
799       // Loop over the pairResidue rotamers to find its lowest energy rotamer.
800       for (int ri = 0; ri < ni; ri++) {
801         double energyForRi = 0.0;
802         if (eR.checkPrunedSingles(pairResidue, ri)) {
803           continue;
804         }
805         // Loop over residue J
806         for (int j = 0; j < nRes; j++) {
807           if (j == pairResidue) {
808             continue;
809           }
810           Residue resJ = residueList.get(j);
811           Rotamer[] rotJ = resJ.getRotamers();
812           int nRot = rotJ.length;
813 
814           int rj = 0;
815           while (eR.checkPrunedSingles(j, rj) || eR.checkPrunedPairs(pairResidue, ri, j, rj)) {
816             if (++rj >= nRot) {
817               logger.warning("RJ is too large.");
818             }
819           }
820 
821           EnergyExpansion eE = rotamerOptimization.getEnergyExpansion();
822           double lowEnergy = eE.get2Body(pairResidue, ri, j, rj);
823 
824           for (rj = 1; rj < nRot; rj++) {
825             if (!eR.checkPrunedSingles(j, rj) && !eR.checkPrunedPairs(pairResidue, ri, j, rj)) {
826               double pairEnergy = eE.get2Body(pairResidue, ri, j, rj);
827               if (pairEnergy < lowEnergy) {
828                 lowEnergy = pairEnergy;
829               }
830             }
831           }
832           energyForRi += lowEnergy;
833         }
834         if (energyForRi < minEnergy) {
835           minEnergy = energyForRi;
836           bestRotI = ri;
837         }
838       }
839 
840       assertEquals(
841           format(
842               " %s Best 2-body energy sum for residue %d is with rotamer %d at %10.4f.",
843               info, pairResidue, bestRotI, minEnergy),
844           optimum[pairResidue],
845           bestRotI);
846 
847       // Given the minimum energy rotamer for "pairResidue" is "bestRotI", we can check selected
848       // rotamers for all other residues.
849       for (int j = 0; j < nRes; j++) {
850         if (j == pairResidue) {
851           continue;
852         }
853         Residue resJ = residueList.get(j);
854         Rotamer[] rotJ = resJ.getRotamers();
855         int nRotJ = rotJ.length;
856 
857         int rotCounter = 0;
858         while (eR.checkPrunedPairs(pairResidue, bestRotI, j, rotCounter) && rotCounter < nRotJ) {
859           rotCounter++;
860         }
861 
862         EnergyExpansion eE = rotamerOptimization.getEnergyExpansion();
863         double lowEnergy = eE.get2Body(pairResidue, bestRotI, j, rotCounter);
864         int bestRotJ = rotCounter;
865         for (int rj = 1; rj < nRotJ; rj++) {
866           if (!eR.checkPrunedSingles(j, rj) && !eR.checkPrunedPairs(pairResidue, bestRotI, j, rj)) {
867             double pairEnergy = eE.get2Body(pairResidue, bestRotI, j, rj);
868             if (pairEnergy < lowEnergy) {
869               lowEnergy = pairEnergy;
870               bestRotJ = rj;
871             }
872           }
873         }
874         assertEquals(
875             format(
876                 " %s Pair-Energy of residue (%d,%d) with residue %d",
877                 info, pairResidue, bestRotI, j),
878             optimum[j],
879             bestRotJ);
880       }
881     }
882 
883     // Test 3-Body Energy Eliminations.
884     if (doTripleOpt) {
885       rotamerOptimization.turnRotamerPairEliminationOff();
886       rotamerOptimization.setTestTripleEnergyEliminations(tripleResidue1, tripleResidue2);
887       try {
888         energy = rotamerOptimization.optimize(RotamerOptimization.Algorithm.ALL);
889         assertEquals(info + " Triple-Energy", expectedTripleEnergy, energy, tolerance);
890       } catch (Exception e) {
891         e.fillInStackTrace();
892         e.printStackTrace();
893         logger.log(java.util.logging.Level.INFO, "Error in doTripleOpt", e);
894       }
895 
896       // Check that optimized rotamers are equivalent to the lowest 3-body energy of each residue
897       // with the tripleResidue1 and 2.
898       int[] optimum = rotamerOptimization.getOptimumRotamers();
899 
900       // fix residue 1 and gets its rotamers
901       Residue resI = residueList.get(tripleResidue1);
902       Rotamer[] rotI = resI.getRotamers();
903       int ni = rotI.length;
904 
905       // fix residue 2 and get its rotamers
906       Residue resJ = residueList.get(tripleResidue2);
907       Rotamer[] rotJ = resJ.getRotamers();
908       int nj = rotJ.length;
909 
910       double minEnergyIJ = Double.POSITIVE_INFINITY;
911       int bestRotI = -1;
912       int bestRotJ = -1;
913 
914       EliminatedRotamers eR = rotamerOptimization.getEliminatedRotamers();
915 
916       for (int ri = 0; ri < ni; ri++) { // loop through rot I
917         if (eR.check(tripleResidue1, ri)) {
918           continue;
919         }
920         for (int rj = 0; rj < nj; rj++) { // loop through rot J
921           if (eR.checkPrunedSingles(tripleResidue2, rj)
922               || eR.checkPrunedPairs(tripleResidue1, ri, tripleResidue2, rj)) {
923             continue;
924           }
925           double currentEnergy = 0.0;
926           for (int k = 0; k < nRes; k++) { // loop through all other residues
927             if (k == tripleResidue1 || k == tripleResidue2) {
928               continue;
929             }
930             Residue resK = residueList.get(k);
931             Rotamer[] rotK = resK.getRotamers();
932             int nk = rotK.length;
933 
934             int rkStart = 0;
935             while (eR.checkPrunedSingles(k, rkStart)
936                 || eR.checkPrunedPairs(tripleResidue1, ri, k, rkStart)
937                 || eR.checkPrunedPairs(tripleResidue2, rj, k, rkStart)) {
938               if (++rkStart >= nk) {
939                 logger.warning("RJ is too large.");
940               }
941             }
942 
943             EnergyExpansion eE = rotamerOptimization.getEnergyExpansion();
944             double lowEnergy =
945                 eE.get3Body(residues, tripleResidue1, ri, tripleResidue2, rj, k, rkStart);
946             for (int rk = rkStart; rk < nk; rk++) {
947               if (!eR.checkPrunedSingles(k, rk)
948                   && !eR.checkPrunedPairs(tripleResidue1, ri, k, rk)
949                   && !eR.checkPrunedPairs(tripleResidue2, rj, k, rk)) {
950                 double tripleEnergy =
951                     eE.get3Body(residues, tripleResidue1, ri, tripleResidue2, rj, k, rk);
952                 if (tripleEnergy < lowEnergy) {
953                   lowEnergy = tripleEnergy;
954                 }
955               }
956             }
957             currentEnergy +=
958                 lowEnergy; // adds lowest energy conformation of residue k to that of the rotamer I
959           }
960           if (currentEnergy < minEnergyIJ) {
961             minEnergyIJ = currentEnergy;
962             bestRotI = ri;
963             bestRotJ = rj;
964           }
965         }
966       }
967 
968       assertEquals(
969           format(
970               " %s Best three-body energy sum for residue %d is with rotamer %d at %10.4f.",
971               info, tripleResidue1, bestRotI, minEnergyIJ),
972           optimum[tripleResidue1],
973           bestRotI);
974 
975       assertEquals(
976           format(
977               " %s Best three-body energy sum for residue %d is with rotamer %d at %10.4f.",
978               info, tripleResidue2, bestRotJ, minEnergyIJ),
979           optimum[tripleResidue2],
980           bestRotJ);
981 
982       // loop over the residues to find the best rotamer per residue given bestRotI and bestRotJ
983       for (int k = 0; k < nRes; k++) {
984         if (k == tripleResidue1 || k == tripleResidue2) {
985           continue;
986         }
987         Residue resK = residueList.get(k);
988         Rotamer[] rotK = resK.getRotamers();
989         int nk = rotK.length;
990 
991         int rotCounter = 0;
992         while (eR.checkPrunedPairs(tripleResidue1, bestRotI, k, rotCounter)
993             && eR.checkPrunedPairs(tripleResidue2, bestRotJ, k, rotCounter)
994             && rotCounter < nk) {
995           rotCounter++;
996         }
997 
998         EnergyExpansion eE = rotamerOptimization.getEnergyExpansion();
999         double lowEnergy =
1000             eE.get3Body(
1001                 residues, tripleResidue1, bestRotI, tripleResidue2, bestRotJ, k, rotCounter);
1002         int bestRotK = rotCounter;
1003         for (int rk = 1; rk < nk; rk++) {
1004           if (!eR.checkPrunedSingles(k, rk)
1005               && !eR.checkPrunedPairs(tripleResidue1, bestRotI, k, rk)
1006               && !eR.checkPrunedPairs(tripleResidue2, bestRotJ, k, rk)) {
1007             double tripleEnergy =
1008                 eE.get3Body(residues, tripleResidue1, bestRotI, tripleResidue2, bestRotJ, k, rk);
1009             if (tripleEnergy < lowEnergy) {
1010               lowEnergy = tripleEnergy;
1011               bestRotK = rk;
1012             }
1013           }
1014         }
1015         assertEquals(
1016             format(
1017                 " %s Triple-Energy of residue (%d,%d) and residue (%d,%d) with residue %d",
1018                 info, tripleResidue1, bestRotI, tripleResidue2, bestRotJ, k),
1019             optimum[k],
1020             bestRotK);
1021       }
1022     }
1023   }
1024 
1025   /** Load the test system. */
1026   private void load() {
1027     String structure = getResourcePath(filename);
1028     restartFile = getResourceFile(restartName);
1029     PotentialsUtils potentialUtils = new PotentialsUtils();
1030     molecularAssembly = potentialUtils.openQuietly(structure);
1031     forceFieldEnergy = molecularAssembly.getPotentialEnergy();
1032   }
1033 }