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-2021.
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.cli;
39  
40  import static java.lang.Integer.parseInt;
41  
42  import ffx.algorithms.optimize.RotamerOptimization;
43  import ffx.algorithms.optimize.RotamerOptimization.Algorithm;
44  import ffx.potential.MolecularAssembly;
45  import ffx.potential.bonded.Polymer;
46  import ffx.potential.bonded.Residue;
47  import ffx.potential.bonded.Rotamer;
48  import ffx.potential.bonded.RotamerLibrary;
49  import java.io.File;
50  import java.util.ArrayList;
51  import java.util.Collections;
52  import java.util.List;
53  import java.util.Scanner;
54  import java.util.logging.Logger;
55  import picocli.CommandLine.ArgGroup;
56  import picocli.CommandLine.Option;
57  
58  /**
59   * Represents command line options for scripts that use a many-body expansion for global
60   * optimization.
61   *
62   * @author Michael J. Schnieders
63   * @author Mallory R. Tollefson
64   * @since 1.0
65   */
66  public class ManyBodyOptions {
67  
68    private static final Logger logger = Logger.getLogger(ManyBodyOptions.class.getName());
69  
70    /**
71     * The ArgGroup keeps the ManyBodyOptionGroup together when printing help.
72     */
73    @ArgGroup(heading = "%n Many-Body Optimization Options%n", validate = false)
74    public ManyBodyOptionGroup group = new ManyBodyOptionGroup();
75  
76    /**
77     * The ArgGroup keeps the BoxOptionGroup together when printing help.
78     */
79    @ArgGroup(heading = "%n Many-Body Box Optimization Options%n", validate = false)
80    public BoxOptionGroup boxGroup = new BoxOptionGroup();
81  
82    /**
83     * The ArgGroup keeps the WindowOptionGroup together when printing help.
84     */
85    @ArgGroup(heading = "%n Many-Body Window Optimization Options%n", validate = false)
86    public WindowOptionGroup windowGroup = new WindowOptionGroup();
87  
88    /**
89     * The ArgGroup keeps the WindowOptionGroup together when printing help.
90     */
91    @ArgGroup(heading = "%n Many-Body Energy Expansion and Cut-off Options%n", validate = false)
92    public EnergyOptionGroup energyGroup = new EnergyOptionGroup();
93  
94    /**
95     * The ArgGroup keeps the ResidueOptionGroup together when printing help.
96     */
97    @ArgGroup(heading = "%n Many-Body Residue Selection Options%n", validate = false)
98    public ResidueOptionGroup residueGroup = new ResidueOptionGroup();
99  
100   private RotamerOptimization rotamerOptimization;
101   private RotamerLibrary rotamerLibrary;
102 
103   /**
104    * initRotamerOptimization.
105    *
106    * @param rotamerOptimization a {@link RotamerOptimization} object.
107    * @param activeAssembly a {@link ffx.potential.MolecularAssembly} object.
108    */
109   public void initRotamerOptimization(
110       RotamerOptimization rotamerOptimization, MolecularAssembly activeAssembly) {
111     this.rotamerOptimization = rotamerOptimization;
112 
113     // Collect the residues to optimize.
114     List<Residue> residues = collectResidues(activeAssembly);
115     rotamerOptimization.setResidues(residues);
116     rotamerOptimization.setRotamerLibrary(rotamerLibrary);
117 
118     // If the user has not selected an algorithm, it will be chosen based on the number of residues.
119     Algorithm algorithm = getAlgorithm(residues.size());
120 
121     // Configure general options.
122     rotamerOptimization.setDecomposeOriginal(group.decompose);
123     rotamerOptimization.setUseGoldstein(!group.dee);
124     rotamerOptimization.setRevert(group.revert);
125     boolean monteCarloBool = group.monteCarlo > 1;
126     rotamerOptimization.setMonteCarlo(monteCarloBool, group.monteCarlo);
127     File energyRestartFile;
128     if (!group.energyRestart.equalsIgnoreCase("none")) {
129       energyRestartFile = new File(group.energyRestart);
130       rotamerOptimization.setEnergyRestartFile(energyRestartFile);
131     }
132 
133     // Configure Energy Expansion and Pruning Options.
134     rotamerOptimization.setTwoBodyCutoff(energyGroup.twoBodyCutoff);
135     rotamerOptimization.setThreeBodyEnergy(energyGroup.threeBody);
136     rotamerOptimization.setThreeBodyCutoff(energyGroup.threeBodyCutoff);
137     rotamerOptimization.setDistanceCutoff(energyGroup.cutoff);
138     rotamerOptimization.setPruning(energyGroup.prune);
139     rotamerOptimization.setSingletonClashThreshold(energyGroup.clashThreshold);
140     rotamerOptimization.setPairClashThreshold(energyGroup.pairClashThreshold);
141 
142     // Window
143     if (algorithm == Algorithm.WINDOW) {
144       rotamerOptimization.setWindowSize(windowGroup.window);
145       rotamerOptimization.setIncrement(windowGroup.increment);
146     } else if (algorithm == Algorithm.BOX) {
147       // Box
148       parseBoxSelection();
149       if (boxGroup.approxBoxLength < 0) {
150         logger.info(" Negative box length value changed to -1 * input.");
151         boxGroup.approxBoxLength *= -1;
152       }
153       rotamerOptimization.setBoxBorderSize(boxGroup.boxBorderSize);
154       rotamerOptimization.setApproxBoxLength(boxGroup.approxBoxLength);
155       rotamerOptimization.setNumXYZBoxes(boxGroup.numXYZBoxes);
156       rotamerOptimization.setBoxInclusionCriterion(boxGroup.boxInclusionCriterion);
157       rotamerOptimization.setBoxStart(boxGroup.initialBox);
158       rotamerOptimization.setBoxEnd(boxGroup.finalBox);
159     }
160   }
161 
162   /**
163    * Collect residues based on residue selection flags.
164    *
165    * @param activeAssembly a {@link ffx.potential.MolecularAssembly} object.
166    */
167   public List<Residue> collectResidues(MolecularAssembly activeAssembly) {
168 
169     // Force re-initialization RotamerLibrary prior to collecting residues and rotamers.
170     initRotamerLibrary(true);
171 
172     // First, interpret the residueGroup.listResidues flag if its set.
173     if (!residueGroup.listResidues.equalsIgnoreCase("none")) {
174       List<String> stringList = new ArrayList<>();
175       String[] tok = residueGroup.listResidues.split(",");
176       Collections.addAll(stringList, tok);
177       List<Residue> residueList = new ArrayList<>();
178       Polymer[] polymers = activeAssembly.getChains();
179       for (String s : stringList) {
180         Character chainID = s.charAt(0);
181         int i = parseInt(s.substring(1));
182         for (Polymer polymer : polymers) {
183           if (polymer.getChainID() == chainID) {
184             List<Residue> residues = polymer.getResidues();
185             for (Residue residue : residues) {
186               if (residue.getResidueNumber() == i) {
187                 Rotamer[] rotamers = residue.setRotamers(rotamerLibrary);
188                 if (rotamers != null || rotamers.length > 0) {
189                   residueList.add(residue);
190                 }
191               }
192             }
193           }
194         }
195       }
196       return residueList;
197     }
198 
199     // Check that the finish flag is greater than the start flag.
200     if (residueGroup.finish < residueGroup.start) {
201       residueGroup.finish = Integer.MAX_VALUE;
202     }
203     Character chainID = null;
204     if (!residueGroup.chain.equalsIgnoreCase("-1")) {
205       chainID = residueGroup.chain.charAt(0);
206     }
207 
208     // Otherwise, collect all residues with a rotamer.
209     List<Residue> residueList = new ArrayList<>();
210     Polymer[] polymers = activeAssembly.getChains();
211     for (Polymer polymer : polymers) {
212       // Enforce requested chainID.
213       if (chainID != null && chainID != polymer.getChainID()) {
214         continue;
215       }
216       List<Residue> residues = polymer.getResidues();
217       for (Residue residue : residues) {
218         int resID = residue.getResidueNumber();
219         // Enforce requested residue range.
220         if (resID >= residueGroup.start && resID <= residueGroup.finish) {
221           Rotamer[] rotamers = residue.setRotamers(rotamerLibrary);
222           if (rotamers != null) {
223             residueList.add(residue);
224           }
225         }
226       }
227     }
228 
229     return residueList;
230   }
231 
232   /**
233    * Returns the user selected algorithm or one chosen based on number of residues.
234    *
235    * @return Returns the algorithm choice.
236    */
237   public Algorithm getAlgorithm(int numResidues) {
238     if (group.algorithm == 0) {
239       if (numResidues < 100) {
240         return Algorithm.ALL;
241       } else {
242         return Algorithm.BOX;
243       }
244     }
245     return Algorithm.getAlgorithm(group.algorithm);
246   }
247 
248   public boolean getUsingOriginalCoordinates() {
249     return !group.noOriginal;
250   }
251 
252   public void setOriginalCoordinates(boolean useOrig) {
253     group.noOriginal = !useOrig;
254   }
255 
256   public double getApproximate() {
257     return rotamerOptimization.getApproximate();
258   }
259 
260   /**
261    * Gets the restart file created during rotamer optimization.
262    *
263    * @return The restart file.
264    */
265   public File getRestartFile() {
266     return rotamerOptimization.getRestartFile();
267   }
268 
269   public RotamerLibrary getRotamerLibrary(boolean reinit) {
270     initRotamerLibrary(reinit);
271     return rotamerLibrary;
272   }
273 
274   private void initRotamerLibrary(boolean reinit) {
275     if (rotamerLibrary == null || reinit) {
276       boolean useOrigCoordsRotamer = !group.noOriginal;
277       if (group.decompose) {
278         useOrigCoordsRotamer = true;
279       }
280       rotamerLibrary = new RotamerLibrary(
281           RotamerLibrary.ProteinLibrary.intToProteinLibrary(group.library),
282           useOrigCoordsRotamer);
283     }
284   }
285 
286 
287   /** Set allStartResID, boxStart and boxEnd */
288   private void parseBoxSelection() {
289     // Parse the numBoxes flag.
290     String input = boxGroup.numBoxes;
291     Scanner boxNumInput = new java.util.Scanner(input);
292     boxNumInput.useDelimiter(",");
293     int inputLoopCounter = 0;
294     int[] numXYZBoxes = new int[3];
295     numXYZBoxes[0] = 3; // Default
296     while (inputLoopCounter < 3) {
297       if (boxNumInput.hasNextInt()) {
298         numXYZBoxes[inputLoopCounter] = boxNumInput.nextInt();
299         inputLoopCounter++;
300       } else if (boxNumInput.hasNextDouble()) {
301         numXYZBoxes[inputLoopCounter] = (int) Math.floor(boxNumInput.nextDouble());
302         inputLoopCounter++;
303         logger.info(" Double input to nB truncated to integer.");
304       } else if (boxNumInput.hasNext()) {
305         logger.info(" Non-numeric input to nB discarded");
306         boxNumInput.next();
307       } else {
308         logger.info(
309             " Insufficient input to nB. Non-input values assumed either equal to X or default to 3");
310         break;
311       }
312     }
313     boxNumInput.close();
314 
315     // Initialize dimensions not provided.
316     for (int i = inputLoopCounter; i < 3; i++) {
317       numXYZBoxes[i] = numXYZBoxes[0];
318     }
319 
320     // Correct input errors.
321     int totalCount = 1;
322     for (int i = 0; i < 3; i++) {
323       if (numXYZBoxes[i] == 0) {
324         numXYZBoxes[i] = 3;
325         logger.info(" Input of 0 to nB reset to default of 3.");
326       } else if (numXYZBoxes[i] < 0) {
327         numXYZBoxes[i] = -1 * numXYZBoxes[i];
328         logger.info(" Input of negative number to nB reset to positive number");
329       }
330       totalCount *= numXYZBoxes[i];
331     }
332 
333     boxGroup.numXYZBoxes = numXYZBoxes;
334 
335     if (boxGroup.initialBox < 0) {
336       boxGroup.initialBox = 0;
337     }
338     if (boxGroup.finalBox < 0 || boxGroup.finalBox > totalCount) {
339       boxGroup.finalBox = totalCount;
340     }
341 
342   }
343 
344   /** Sets the standard values for properties in rotamer optimization. */
345   private void setRotOptProperties(Algorithm algorithm) {
346 
347   }
348 
349   public void setAlgorithm(int algorithm) {
350     group.algorithm = algorithm;
351   }
352 
353   /**
354    * Ponder and Richards (1) or Richardson (2) rotamer library.
355    *
356    * @return Returns the Rotamer library.
357    */
358   public int getLibrary() {
359     return group.library;
360   }
361 
362   public void setLibrary(int library) {
363     group.library = library;
364   }
365 
366   /**
367    * Nucleic acid library: currently only Richardson available.
368    *
369    * @return Returns a String for the nucleic acid library.
370    */
371   public String getNaLibraryName() {
372     return group.naLibraryName;
373   }
374 
375   public void setNaLibraryName(String naLibraryName) {
376     group.naLibraryName = naLibraryName;
377   }
378 
379   /**
380    * Use dead-end elimination criteria instead of Goldstein criteria.
381    *
382    * @return Returns true if using DEE instead of Goldstein.
383    */
384   public boolean isDee() {
385     return group.dee;
386   }
387 
388   public void setDee(boolean dee) {
389     group.dee = dee;
390   }
391 
392   /**
393    * Single character chain ID of the residues to optimize.
394    *
395    * @return Returns the Chain name.
396    */
397   public String getChain() {
398     return residueGroup.chain;
399   }
400 
401   public void setChain(String chain) {
402     residueGroup.chain = chain;
403   }
404 
405   /**
406    * Starting residue to perform the optimization on (-1 exits). For box optimization, first box to
407    * optimize.
408    *
409    * @return Returns the starting index.
410    */
411   public int getStart() {
412     return residueGroup.start;
413   }
414 
415   public void setStart(int start) {
416     residueGroup.start = start;
417   }
418 
419   /**
420    * Final residue to perform the optimization on (-1 exits). For box optimization, final box to
421    * optimize.
422    *
423    * @return Returns the finish index.
424    */
425   public int getFinish() {
426     return residueGroup.finish;
427   }
428 
429   public void setFinish(int finish) {
430     residueGroup.finish = finish;
431   }
432 
433   /**
434    * Cutoff distance for two-body interactions.
435    *
436    * @return Returns the 2-body cutoff.
437    */
438   public double getTwoBodyCutoff() {
439     return energyGroup.twoBodyCutoff;
440   }
441 
442   public void setTwoBodyCutoff(double twoBodyCutoff) {
443     energyGroup.twoBodyCutoff = twoBodyCutoff;
444   }
445 
446   /**
447    * -T or --threeBody Include 3-Body interactions in the elimination criteria.
448    *
449    * @return Returns true if 3-body interactions are being used.
450    */
451   public boolean isThreeBody() {
452     return energyGroup.threeBody;
453   }
454 
455   public void setThreeBody(boolean threeBody) {
456     energyGroup.threeBody = threeBody;
457   }
458 
459   /**
460    * Cutoff distance for three-body interactions.
461    *
462    * @return Returns the 3-body cutoff.
463    */
464   public double getThreeBodyCutoff() {
465     return energyGroup.threeBodyCutoff;
466   }
467 
468   public void setThreeBodyCutoff(double threeBodyCutoff) {
469     energyGroup.threeBodyCutoff = threeBodyCutoff;
470   }
471 
472   /**
473    * Prune no clashes (0), only single clashes (1), or all clashes (2).
474    *
475    * @return Returns the pruning condition.
476    */
477   public int getPrune() {
478     return energyGroup.prune;
479   }
480 
481   public void setPrune(int prune) {
482     energyGroup.prune = prune;
483   }
484 
485   /**
486    * Revert unfavorable changes.
487    *
488    * @return Returns true if unfavorable changes are reverted.
489    */
490   public boolean isRevert() {
491     return group.revert;
492   }
493 
494   public void setRevert(boolean revert) {
495     group.revert = revert;
496   }
497 
498   /**
499    * Energy restart file from a previous run (requires that all parameters are the same).
500    *
501    * @return Returns the energy restart file to use.
502    */
503   public String getEnergyRestart() {
504     return group.energyRestart;
505   }
506 
507   public void setEnergyRestart(String energyRestart) {
508     group.energyRestart = energyRestart;
509   }
510 
511   /**
512    * Do not include starting coordinates as their own rotamer.
513    *
514    * @return Returns true if original side-chain coordinates should not be used as a rotamer.
515    */
516   public boolean isNoOriginal() {
517     return group.noOriginal;
518   }
519 
520   public void setNoOriginal(boolean noOriginal) {
521     group.noOriginal = noOriginal;
522   }
523 
524   /**
525    * -E or --decompose Print energy decomposition for the input structure (no optimization).
526    *
527    * @return Returns true if the input structure should undergo an energy decomposition.
528    */
529   public boolean isDecompose() {
530     return group.decompose;
531   }
532 
533   public void setDecompose(boolean decompose) {
534     group.decompose = decompose;
535   }
536 
537   /**
538    * Choose a list of individual residues to optimize (eg. A11,A24,B40).
539    *
540    * @return Returns the list of selected residues.
541    */
542   public String getListResidues() {
543     return residueGroup.listResidues;
544   }
545 
546   public void setListResidues(String listResidues) {
547     residueGroup.listResidues = listResidues;
548   }
549 
550   /**
551    * Follow elimination criteria with 'n' Monte Carlo steps, or enumerate all remaining
552    * conformations, whichever is smaller.
553    *
554    * @return Returns the number of Monte Carlo optimization steps to apply.
555    */
556   public int getMonteCarlo() {
557     return group.monteCarlo;
558   }
559 
560   public void setMonteCarlo(int monteCarlo) {
561     group.monteCarlo = monteCarlo;
562   }
563 
564   /**
565    * Save eliminated singles and eliminated pairs to a text file (global and box optimization).
566    *
567    * @return Returns true to Save eliminated rotamers to a file.
568    */
569   public boolean isSaveOutput() {
570     return group.saveOutput;
571   }
572 
573   public void setSaveOutput(boolean saveOutput) {
574     group.saveOutput = saveOutput;
575   }
576 
577   /**
578    * Size of the sliding window with respect to adjacent residues (default = 7).
579    *
580    * @return Returns the sliding window size.
581    */
582   public int getWindow() {
583     return windowGroup.window;
584   }
585 
586   public void setWindow(int window) {
587     windowGroup.window = window;
588   }
589 
590   /**
591    * Sliding window increment (default = 3).
592    *
593    * @return Returns the sliding window increment.
594    */
595   public int getIncrement() {
596     return windowGroup.increment;
597   }
598 
599   public void setIncrement(int increment) {
600     windowGroup.increment = increment;
601   }
602 
603   /**
604    * The sliding window and box cutoff radius (Angstroms).
605    *
606    * @return Returns the sliding window cutoff radius.
607    */
608   public double getCutoff() {
609     return energyGroup.cutoff;
610   }
611 
612   public void setCutoff(double cutoff) {
613     energyGroup.cutoff = cutoff;
614   }
615 
616   /**
617    * The threshold for pruning clashes. If two self-energies on the same residue have an energy
618    * difference greater than 25 kcal/mol, the high energy rotamers get pruned.
619    *
620    * @return Returns the clash threshold for self energies.
621    */
622   public double getClashThreshold() {
623     return energyGroup.clashThreshold;
624   }
625 
626   public void setClashThreshold(double clashThreshold) {
627     energyGroup.clashThreshold = clashThreshold;
628   }
629 
630   /**
631    * The threshold for pruning clashes. If two pair-energies on the same residues have an energy
632    * difference greater than 25 kcal/mol, the high energy rotamers get pruned.
633    *
634    * @return Returns the clash threshold for pair energies.
635    */
636   public double getPairClashThreshold() {
637     return energyGroup.pairClashThreshold;
638   }
639 
640   public void setPairClashThreshold(double pairClashThreshold) {
641     energyGroup.pairClashThreshold = pairClashThreshold;
642   }
643 
644   /**
645    * The number of boxes along X, Y, and Z (default: '3,3,3').
646    *
647    * @return Returns the number of boxes.
648    */
649   public String getNumBoxes() {
650     return boxGroup.numBoxes;
651   }
652 
653   public void setNumBoxes(String numBoxes) {
654     boxGroup.numBoxes = numBoxes;
655   }
656 
657   /**
658    * Extent of overlap between optimization boxes (default: 0.0 A).
659    *
660    * @return Returns the overlap between optimization boxes.
661    */
662   public double getBoxBorderSize() {
663     return boxGroup.boxBorderSize;
664   }
665 
666   public void setBoxBorderSize(double boxBorderSize) {
667     boxGroup.boxBorderSize = boxBorderSize;
668   }
669 
670   /**
671    * Approximate side lengths of boxes to be constructed (over-rides numXYZBoxes). Box sizes are
672    * rounded up to make a whole number of boxes along each axis (default of 0 disables this
673    * function).
674    *
675    * @return Returns the approximate box length.
676    */
677   public double getApproxBoxLength() {
678     return boxGroup.approxBoxLength;
679   }
680 
681   public void setApproxBoxLength(double approxBoxLength) {
682     boxGroup.approxBoxLength = approxBoxLength;
683   }
684 
685   /**
686    * Criterion to use for adding residues to boxes. (1) uses C alpha only (N1/9 for nucleic acids)
687    * (2) uses any atom. (3) uses any rotamer.
688    *
689    * @return Returns the Box inclusion criteria.
690    */
691   public int getBoxInclusionCriterion() {
692     return boxGroup.boxInclusionCriterion;
693   }
694 
695   public void setBoxInclusionCriterion(int boxInclusionCriterion) {
696     boxGroup.boxInclusionCriterion = boxInclusionCriterion;
697   }
698 
699   public void setTitrationPH(double pH) {
700     group.titrationPH = pH;
701   }
702 
703   public double getTitrationPH() {
704     return group.titrationPH;
705   }
706 
707   public boolean isTitrating() {
708     return group.titrationPH == 0;
709   }
710 
711   /**
712    * Collection of ManyBody Options.
713    */
714   private static class ManyBodyOptionGroup {
715 
716     /**
717      * -a or --algorithm Choices are independent residues (1), all with rotamer elimination (2), all
718      * brute force (3), sliding window (4), or box optimization (5).
719      */
720     @Option(
721         names = {"-a", "--algorithm"},
722         paramLabel = "0",
723         defaultValue = "0",
724         description =
725             "Algorithm: default automatic settings (0), independent residues (1), all with rotamer elimination (2), all brute force (3), sliding window (4), or box optimization (5)")
726     private int algorithm;
727 
728     /** -L or --library Choose either Ponder and Richards (1) or Richardson (2) rotamer library. */
729     @Option(
730         names = {"-L", "--library"},
731         paramLabel = "2",
732         defaultValue = "2",
733         description = "Ponder and Richards (1) or Richardson (2) rotamer library.")
734     private int library;
735 
736     /** -nl or --nucleicLibrary Choose a nucleic acid library: currently only Richardson available. */
737     // @Option(
738     //    names = {"--nl", "--nucleiclibrary"},
739     //    paramLabel = "Richardson",
740     //    defaultValue = "Richardson",
741     //    description = "Nucleic acid library to select: [Richardson]")
742     private String naLibraryName = "Richardson";
743 
744     /** --dee or --deadEnd Use dead-end elimination criteria instead of Goldstein criteria. */
745     @Option(
746         names = {"--dee", "--deadEnd"},
747         paramLabel = "false",
748         defaultValue = "false",
749         description = "Use dead-end elimination criteria instead of Goldstein criteria.")
750     private boolean dee;
751 
752     /** -z or --revert Revert unfavorable changes. */
753     @Option(
754         names = {"-z", "--revert"},
755         defaultValue = "true",
756         description = "Revert unfavorable changes.")
757     private boolean revert;
758 
759     /**
760      * --eR or --energyRestart Load energy restart file from a previous run (requires that all
761      * parameters are the same).
762      */
763     @Option(
764         names = {"--eR", "--energyRestart"},
765         paramLabel = "none",
766         defaultValue = "none",
767         description =
768             "Load energy restart file from a previous run (requires that all parameters are the same).")
769     private String energyRestart;
770 
771     /** -O or --noOriginal Do not include starting coordinates as their own rotamer. */
772     @Option(
773         names = {"-O", "--noOriginal"},
774         defaultValue = "false",
775         description = "Do not include starting coordinates as their own rotamer.")
776     private boolean noOriginal;
777 
778     /** -E or --decompose Print energy decomposition for the input structure (no optimization). */
779     @Option(
780         names = {"-E", "--decompose"},
781         defaultValue = "false",
782         description = "Print energy decomposition for the input structure (no optimization!).")
783     private boolean decompose;
784 
785     /**
786      * --pH or --titrationPH Optimize the titration state of ASP, GLU, HIS and LYS residues.
787      */
788     @Option(
789         names = {"--pH", "--titrationPH"},
790         paramLabel = "0",
791         defaultValue = "0",
792         description = " Optimize the titration state of ASP, GLU, HIS and LYS residues at the given pH (pH = 0 turns off titration")
793     private double titrationPH;
794 
795     /**
796      * --mC or --monteCarlo Follow elimination criteria with 'n' Monte Carlo steps, or enumerate all
797      * remaining conformations, whichever is smaller.
798      */
799     @Option(
800         names = {"--mC", "--monteCarlo"},
801         paramLabel = "-1",
802         defaultValue = "-1",
803         description =
804             "Follow elimination criteria with (n) Monte Carlo steps, or enumerate all remaining conformations, whichever is smaller.")
805     private int monteCarlo;
806 
807     /**
808      * -out or --output Save eliminated singles and eliminated pairs to a text file (global and box
809      * optimization).
810      */
811     //  @Option(
812     //      names = {"--out", "--output"},
813     //      defaultValue = "false",
814     //      description = "Save eliminated singles and eliminated pairs to a text file.")
815     private boolean saveOutput;
816 
817   }
818 
819   /**
820    * Collection of ManyBody Box Optimization Options.
821    */
822   private static class BoxOptionGroup {
823 
824     /** -nB or --numBoxes Specify number of boxes along X, Y, and Z (default: '3,3,3'). */
825     @Option(
826         names = {"--nB", "--numBoxes"},
827         paramLabel = "3,3,3",
828         defaultValue = "3,3,3",
829         description = "Specify number of boxes along X, Y, and Z (default: 3,3,3)")
830     private String numBoxes;
831 
832     /**
833      * Result of parsing numBoxes flag.
834      */
835     private int[] numXYZBoxes;
836 
837     /** -bB or --boxBorderSize Extent of overlap between optimization boxes (default: 0.0 A). */
838     @Option(
839         names = {"--bB", "--boxBorderSize"},
840         paramLabel = "0.0",
841         defaultValue = "0.0",
842         description = "Extent of overlap between optimization boxes in Angstroms.")
843     private double boxBorderSize;
844 
845     /**
846      * -bL or --approxBoxLength Approximate side lengths of boxes to be constructed (over-rides
847      * numXYZBoxes). Box sizes are rounded up to make a whole number of boxes along each axis
848      * (default of 0 disables this function).
849      */
850     @Option(
851         names = {"--bL", "--approxBoxLength"},
852         paramLabel = "20.0",
853         defaultValue = "20.0",
854         description = "Approximate side lengths of boxes to be constructed (over-rides numXYZBoxes).")
855     private double approxBoxLength;
856 
857     /**
858      * -bC or --boxInclusionCriterion Criterion to use for adding residues to boxes. (1) uses C alpha
859      * only (N1/9 for nucleic acids) (2) uses any atom. (3) uses any rotamer
860      */
861     @Option(
862         names = {"--bC", "--boxInclusionCriterion"},
863         paramLabel = "1",
864         defaultValue = "1",
865         description =
866             "Criterion to use for adding a residue to a box: (1) uses C alpha only (N1/9 for nucleic acids), (2) uses any atom, and (3) uses any rotamer")
867     private int boxInclusionCriterion;
868 
869     /**
870      * --iB or --initialBox Initial box to optimize.
871      */
872     @Option(
873         names = {"--iB", "--initialBox"},
874         paramLabel = "",
875         defaultValue = "0",
876         description = "Initial box to optimize.")
877     private int initialBox;
878 
879     /**
880      * --bf or --boxFinal Final box to optimize.
881      */
882     @Option(
883         names = {"--fB", "--finalBox"},
884         paramLabel = "",
885         defaultValue = "2147483647", // Integer.MAX_VALUE
886         description = "Final box to optimize.")
887     private int finalBox;
888 
889   }
890 
891   /**
892    * Collection of ManyBody Window Optimization Options.
893    */
894   private static class WindowOptionGroup {
895 
896     /** --window Size of the sliding window with respect to adjacent residues (default = 7). */
897     @Option(
898         names = {"--window"},
899         paramLabel = "7",
900         defaultValue = "7",
901         description = "Size of the sliding window with respect to adjacent residues.")
902     private int window;
903 
904     /** --increment Sliding window increment (default = 3). */
905     @Option(
906         names = {"--increment"},
907         paramLabel = "3",
908         defaultValue = "3",
909         description = "Sliding window increment.")
910     private int increment;
911 
912   }
913 
914   /**
915    * Collection of ManyBody Energy Optimization Options.
916    */
917   private static class EnergyOptionGroup {
918 
919     /** --radius The sliding window and box cutoff radius (Angstroms). */
920     @Option(
921         names = {"--radius"},
922         paramLabel = "2.0",
923         defaultValue = "2.0",
924         description = "The sliding box and window cutoff radius (Angstroms).")
925     private double cutoff;
926 
927     /** --tC or --twoBodyCutoff Cutoff distance for two-body interactions. */
928     @Option(
929         names = {"--tC", "--twoBodyCutoff"},
930         paramLabel = "3.0",
931         defaultValue = "3.0",
932         description = "Cutoff distance for two body interactions.")
933     private double twoBodyCutoff;
934 
935     /** -T or --threeBody Include 3-Body interactions in the elimination criteria. */
936     @Option(
937         names = {"-T", "--threeBody"},
938         defaultValue = "false",
939         description = "Include 3-Body interactions in the elimination criteria.")
940     private boolean threeBody;
941 
942     /** --thC or --threeBodyCutoff Cutoff distance for three-body interactions. */
943     @Option(
944         names = {"--thC", "--threeBodyCutoff"},
945         paramLabel = "3.0",
946         defaultValue = "3.0",
947         description = "Cutoff distance for three-body interactions.")
948     private double threeBodyCutoff;
949 
950     /** --pr or --prune Prune no clashes (0), only single clashes (1), or all clashes (2). */
951     @Option(
952         names = {"--pr", "--prune"},
953         paramLabel = "1",
954         defaultValue = "1",
955         description = "Prune no clashes (0), only single clashes (1), or all clashes (2)")
956     private int prune;
957 
958     /**
959      * --clashThreshold The threshold for pruning clashes. If two self-energies on the same residue
960      * have an energy difference greater than 25 kcal/mol, the high energy rotamers get pruned.
961      */
962     @Option(
963         names = {"--clashThreshold"},
964         paramLabel = "25.0",
965         defaultValue = "25.0",
966         description = "The threshold for pruning clashes.")
967     private double clashThreshold;
968 
969     /**
970      * --pairClashThreshold The threshold for pruning clashes. If two pair-energies on the same
971      * residues have an energy difference greater than 25 kcal/mol, the high energy rotamers get
972      * pruned.
973      */
974     @Option(
975         names = {"--pairClashThreshold"},
976         paramLabel = "25.0",
977         defaultValue = "25.0",
978         description = "The threshold for pruning pair clashes.")
979     private double pairClashThreshold;
980   }
981 
982   /**
983    * Collection of ManyBody Residue Selection Options.
984    */
985   private static class ResidueOptionGroup {
986 
987     /** --ch or --chain Single character chain ID of the residues to optimize. */
988     @Option(
989         names = {"--ch", "--chain"},
990         paramLabel = "<A>",
991         defaultValue = "-1",
992         description = "Include only specified chain ID (default: all chains).")
993     private String chain;
994 
995     /**
996      * --sR or --start Starting residue to perform the optimization on.
997      */
998     @Option(
999         names = {"--sR", "--start"},
1000         paramLabel = "",
1001         defaultValue = "-2147483648", // Integer.MIN_VALUE
1002         description = "Starting residue to optimize (default: all residues).")
1003     private int start;
1004 
1005     /**
1006      * --fi or --final Final residue to perform the optimization.
1007      */
1008     @Option(
1009         names = {"--fR", "--final"},
1010         paramLabel = "<final>",
1011         defaultValue = "2147483647", // Integer.MAX_VALUE
1012         description = "Final residue to optimize (default: all residues).")
1013     private int finish;
1014 
1015     /** --lR or --listResidues Choose a list of individual residues to optimize (eg. A11,A24,B40). */
1016     @Option(
1017         names = {"--lR", "--listResidues"},
1018         paramLabel = "<list>",
1019         defaultValue = "none",
1020         description = "Select a list of residues to optimize (eg. A11,A24,B40).")
1021     private String listResidues;
1022 
1023   }
1024 
1025   //    /**
1026   //     * Saves all eliminated rotamers to an ouput file called "eliminated.csv"
1027   //     * when the many body command is run with the following syntax and flags:
1028   //     * ffxc ManyBody --out ... file.pdb &gt;&gt; file.log.
1029   //     *
1030   //     * @throws java.io.IOException Throws an exception when output is non piped to a log
1031   //     *                             file. The --out flag relies on the presence of a log file
1032   // where output is
1033   //     *                             piped.
1034   //     */
1035   //    public void saveEliminatedRotamers() throws IOException {
1036   //        if (saveOutput) {
1037   //            rotamerOptimization.outputEliminated();
1038   //        }
1039   //    }
1040 
1041 }