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.cli;
39  
40  import ffx.algorithms.AlgorithmListener;
41  import ffx.algorithms.dynamics.MDEngine;
42  import ffx.algorithms.dynamics.MolecularDynamics;
43  import ffx.algorithms.thermodynamics.HistogramData;
44  import ffx.algorithms.thermodynamics.LambdaData;
45  import ffx.algorithms.thermodynamics.MonteCarloOST;
46  import ffx.algorithms.thermodynamics.OrthogonalSpaceTempering;
47  import ffx.algorithms.thermodynamics.OrthogonalSpaceTempering.OptimizationParameters;
48  import ffx.crystal.CrystalPotential;
49  import ffx.potential.MolecularAssembly;
50  import ffx.potential.bonded.LambdaInterface;
51  import ffx.potential.cli.WriteoutOptions;
52  import org.apache.commons.configuration2.CompositeConfiguration;
53  import org.apache.commons.configuration2.Configuration;
54  import picocli.CommandLine.ArgGroup;
55  import picocli.CommandLine.Option;
56  
57  import javax.annotation.Nullable;
58  import java.io.File;
59  import java.util.logging.Logger;
60  
61  /**
62   * Represents command line options for scripts that utilize variants of the Orthogonal Space
63   * Tempering (OST) algorithm. Metadynamics will be treated as a special case of OST where there is no
64   * dU/dL axis.
65   *
66   * @author Michael J. Schnieders
67   * @author Jacob M. Litman
68   * @since 1.0
69   */
70  public class OSTOptions {
71  
72    private static final Logger logger = Logger.getLogger(OSTOptions.class.getName());
73  
74    /**
75     * The ArgGroup keeps the OSTOptionGroup together when printing help.
76     */
77    @ArgGroup(heading = "%n Orthogonal Space Tempering Options%n", validate = false)
78    private final OSTOptionGroup group = new OSTOptionGroup();
79  
80    /**
81     * The ArgGroup keeps the OSTOptionGroup together when printing help.
82     */
83    @ArgGroup(heading = "%n Monte Carlo Orthogonal Space Tempering Options%n", validate = false)
84    private final MCOSTOptionGroup mcGroup = new MCOSTOptionGroup();
85  
86    /**
87     * Applies relevant options to an OST, and returns either the OST object or something that wraps
88     * the OST (such as a Barostat).
89     *
90     * @param orthogonalSpaceTempering Orthogonal Space Tempering.
91     * @param firstAssembly            Primary assembly in OST.
92     * @param dynamicsOptions          MD options.
93     * @param barostatOptions          NPT options.
94     * @return a {@link ffx.crystal.CrystalPotential} object.
95     */
96    public CrystalPotential applyAllOSTOptions(OrthogonalSpaceTempering orthogonalSpaceTempering,
97                                               MolecularAssembly firstAssembly, DynamicsOptions dynamicsOptions,
98                                               BarostatOptions barostatOptions) {
99      if (dynamicsOptions.getOptimize()) {
100       OptimizationParameters opt = orthogonalSpaceTempering.getOptimizationParameters();
101       opt.setOptimization(true, firstAssembly);
102     }
103     return barostatOptions.checkNPT(firstAssembly, orthogonalSpaceTempering);
104   }
105 
106   /**
107    * Runs MC-OST.
108    *
109    * @param monteCarloOST         MC-OST to run.
110    * @param dynamicsOptions       Dynamics options.
111    * @param thermodynamicsOptions Thermodynamics options.
112    */
113   public void beginMCOST(MonteCarloOST monteCarloOST, DynamicsOptions dynamicsOptions,
114                          ThermodynamicsOptions thermodynamicsOptions) {
115     long nEquil = thermodynamicsOptions.getEquilSteps();
116 
117     if (nEquil > 0) {
118       logger.info("\n Beginning MC-OST equilibration.");
119       monteCarloOST.setTotalSteps(nEquil);
120       if (mcGroup.twoStep) {
121         monteCarloOST.sampleTwoStep();
122       } else {
123         monteCarloOST.sampleOneStep();
124       }
125       monteCarloOST.setEquilibration(false);
126       logger.info("\n Finished MC-OST equilibration.");
127     }
128 
129     logger.info("\n Beginning MC-OST sampling.");
130     monteCarloOST.setLambdaStdDev(mcGroup.mcLambdaStdDev);
131     monteCarloOST.setTotalSteps(dynamicsOptions.getSteps());
132 
133     if (mcGroup.twoStep) {
134       monteCarloOST.sampleTwoStep();
135     } else {
136       monteCarloOST.sampleOneStep();
137     }
138   }
139 
140   /**
141    * Assembles a MolecularDynamics wrapped around a Potential.
142    *
143    * @param molecularAssemblies MolecularAssembly[]
144    * @param crystalPotential    Potential to run on
145    * @param dynamicsOptions     DynamicsOptions
146    * @param algorithmListener   AlgorithmListener
147    * @return MolecularDynamics
148    */
149   public MolecularDynamics assembleMolecularDynamics(MolecularAssembly[] molecularAssemblies,
150                                                      CrystalPotential crystalPotential, DynamicsOptions dynamicsOptions,
151                                                      AlgorithmListener algorithmListener) {
152     // Create the MolecularDynamics instance.
153     MolecularAssembly firstTop = molecularAssemblies[0];
154 
155     dynamicsOptions.init();
156 
157     MolecularDynamics molDyn = MolecularDynamics.dynamicsFactory(firstTop, crystalPotential,
158         algorithmListener, dynamicsOptions.thermostat, dynamicsOptions.integrator, MDEngine.FFX);
159     for (int i = 1; i < molecularAssemblies.length; i++) {
160       molDyn.addAssembly(molecularAssemblies[i]);
161     }
162     molDyn.setRestartFrequency(dynamicsOptions.getCheckpoint());
163 
164     return molDyn;
165   }
166 
167   /**
168    * constructOST.
169    *
170    * @param crystalPotential      a {@link ffx.crystal.CrystalPotential} to build the OST around.
171    * @param lambdaRestartFile     a {@link java.io.File} lambda restart file.
172    * @param histogramRestartFile  a {@link java.io.File} histogram restart file.
173    * @param firstAssembly         the first {@link ffx.potential.MolecularAssembly} in the OST system.
174    * @param addedProperties       a {@link org.apache.commons.configuration2.Configuration} with additional properties.
175    * @param dynamicsOptions       a {@link ffx.algorithms.cli.DynamicsOptions} with MD-related settings.
176    * @param thermodynamicsOptions a {@link ffx.algorithms.cli.ThermodynamicsOptions} with thermodynamics-related settings.
177    * @param lambdaParticleOptions a {@link ffx.algorithms.cli.LambdaParticleOptions} with lambda particle-related settings.
178    * @param algorithmListener     any {@link ffx.algorithms.AlgorithmListener} that OST should update.
179    * @param async                 If OST should use asynchronous communications.
180    * @return the newly built {@link OrthogonalSpaceTempering} object.
181    */
182   public OrthogonalSpaceTempering constructOST(CrystalPotential crystalPotential,
183                                                @Nullable File lambdaRestartFile,
184                                                File histogramRestartFile, MolecularAssembly firstAssembly,
185                                                @Nullable Configuration addedProperties,
186                                                DynamicsOptions dynamicsOptions, ThermodynamicsOptions thermodynamicsOptions,
187                                                LambdaParticleOptions lambdaParticleOptions,
188                                                @Nullable AlgorithmListener algorithmListener, boolean async) {
189 
190     LambdaInterface lambdaInterface = (LambdaInterface) crystalPotential;
191     CompositeConfiguration compositeConfiguration = new CompositeConfiguration(firstAssembly.getProperties());
192     if (addedProperties != null) {
193       compositeConfiguration.addConfiguration(addedProperties);
194     }
195 
196     // Load HistogramData
197     HistogramData histogramData = HistogramData.readHistogram(histogramRestartFile);
198     // Apply command line settings to the HistogramData if a restart file wasn't read in.
199     int histogramIndex = 0;
200     if (histogramData.wasHistogramRead()) {
201       logger.info("\n Read histogram restart from: " + histogramRestartFile);
202       logger.info(histogramData.toString());
203     } else {
204       // Overwrite defaults with properties.
205       histogramData.applyProperties(compositeConfiguration);
206       // Overwrite defaults & properties with command line options.
207       histogramData.setIndependentWalkers(group.independentWalkers);
208       histogramData.setWriteIndependent(group.independentWalkers);
209       histogramData.setAsynchronous(async);
210       histogramData.setTemperingFactor(getTemperingParameter(histogramIndex));
211       if (thresholdsSet()) {
212         histogramData.setTemperingOffset(getTemperingThreshold(histogramIndex));
213       }
214       histogramData.setMetaDynamics(group.metaDynamics);
215       histogramData.setBiasMag(getBiasMag(histogramIndex));
216       histogramData.setCountInterval(group.countInterval);
217       logger.info("\n OST Histogram Settings");
218       logger.info(histogramData.toString());
219     }
220 
221     // Load LambdaData
222     if (lambdaRestartFile == null) {
223       String filename = histogramRestartFile.toString().replaceFirst("\\.his$", ".lam");
224       lambdaRestartFile = new File(filename);
225     }
226     LambdaData lambdaData = LambdaData.readLambdaData(lambdaRestartFile);
227     // Apply command line settings the LambdaData instances.
228     if (lambdaData.wasLambdaRead()) {
229       logger.info(" Read lambda restart from:    " + lambdaRestartFile);
230     } else {
231       lambdaData.setHistogramIndex(histogramIndex);
232       if (thermodynamicsOptions.getResetNumSteps()) {
233         lambdaData.setStepsTaken(0);
234       }
235     }
236 
237     OrthogonalSpaceTempering orthogonalSpaceTempering = new OrthogonalSpaceTempering(lambdaInterface,
238         crystalPotential, histogramData, lambdaData, compositeConfiguration,
239         dynamicsOptions, lambdaParticleOptions, algorithmListener);
240     orthogonalSpaceTempering.setHardWallConstraint(mcGroup.mcHardWall);
241 
242     // Do NOT run applyOSTOptions here, because that can mutate the OST to a Barostat.
243     return orthogonalSpaceTempering;
244   }
245 
246   /**
247    * Begins MD-OST sampling from an assembled OST.
248    *
249    * @param orthogonalSpaceTempering The OST object.
250    * @param molecularAssemblies      All MolecularAssemblies.
251    * @param crystalPotential         The top-layer CrystalPotential.
252    * @param dynamicsOptions          Dynamics options.
253    * @param writeoutOptions          a {@link WriteoutOptions} object.
254    * @param thermodynamicsOptions    Thermodynamics options.
255    * @param dynFile                  The .dyn dynamics restart file.
256    * @param algorithmListener        AlgorithmListener
257    */
258   public void beginMDOST(OrthogonalSpaceTempering orthogonalSpaceTempering,
259                          MolecularAssembly[] molecularAssemblies, CrystalPotential crystalPotential,
260                          DynamicsOptions dynamicsOptions, WriteoutOptions writeoutOptions,
261                          ThermodynamicsOptions thermodynamicsOptions, File dynFile,
262                          AlgorithmListener algorithmListener) {
263 
264     dynamicsOptions.init();
265 
266     MolecularDynamics molDyn = assembleMolecularDynamics(molecularAssemblies, crystalPotential,
267         dynamicsOptions, algorithmListener);
268 
269     boolean initVelocities = true;
270     long nSteps = dynamicsOptions.getSteps();
271     // Start sampling.
272     long nEquil = thermodynamicsOptions.getEquilSteps();
273     if (nEquil > 0) {
274       logger.info("\n Beginning equilibration");
275       orthogonalSpaceTempering.setPropagateLambda(false);
276       runDynamics(molDyn, nEquil, dynamicsOptions, writeoutOptions, true, dynFile);
277       logger.info(" Beginning OST sampling");
278       orthogonalSpaceTempering.setPropagateLambda(true);
279     } else {
280       logger.info(" Beginning OST sampling without equilibration");
281       if (!thermodynamicsOptions.getResetNumSteps()) {
282         long nEnergyCount = orthogonalSpaceTempering.getEnergyCount();
283         if (nEnergyCount > 0) {
284           nSteps -= nEnergyCount;
285           logger.info(String.format(" Lambda file: %12d steps picked up, now sampling %12d steps",
286               nEnergyCount, nSteps));
287           initVelocities = false;
288         }
289       }
290     }
291     if (nSteps > 0) {
292       runDynamics(molDyn, nSteps, dynamicsOptions, writeoutOptions, initVelocities, dynFile);
293     } else {
294       logger.info(" No steps remaining for this process!");
295     }
296   }
297 
298   /**
299    * Checks if independent walkers has been specified.
300    *
301    * @return Walker independence.
302    */
303   public boolean getIndependentWalkers() {
304     return group.independentWalkers;
305   }
306 
307   /**
308    * Checks if the use of the Monte Carlo algorithm has been specified.
309    *
310    * @return Monte Carlo OST (as opposed to molecular dynamics OST).
311    */
312   public boolean isMonteCarlo() {
313     return mcGroup.monteCarlo;
314   }
315 
316   public void setMonteCarlo(boolean monteCarlo) {
317     mcGroup.monteCarlo = monteCarlo;
318   }
319 
320   /**
321    * Returns true if the 2-step option is enabled (not guaranteed to also mean that MC is enabled!).
322    *
323    * @return If --ts is enabled.
324    */
325   public boolean isTwoStep() {
326     return mcGroup.twoStep;
327   }
328 
329   public void setTwoStep(boolean twoStep) {
330     mcGroup.twoStep = twoStep;
331   }
332 
333   /**
334    * setupMCOST.
335    *
336    * @param orthogonalSpaceTempering a {@link OrthogonalSpaceTempering} object.
337    * @param molecularAssemblies      an array of {@link ffx.potential.MolecularAssembly} objects.
338    * @param dynamicsOptions          a {@link ffx.algorithms.cli.DynamicsOptions} object.
339    * @param thermodynamicsOptions    a {@link ffx.algorithms.cli.ThermodynamicsOptions} object.
340    * @param verbose                  Whether to print out additional information about MC-OST.
341    * @param algorithmListener        An AlgorithmListener
342    * @return An assembled MonteCarloOST ready to run.
343    */
344   public MonteCarloOST setupMCOST(OrthogonalSpaceTempering orthogonalSpaceTempering,
345                                   MolecularAssembly[] molecularAssemblies, DynamicsOptions dynamicsOptions,
346                                   ThermodynamicsOptions thermodynamicsOptions, boolean verbose,
347                                   File dynRestart, AlgorithmListener algorithmListener) {
348     dynamicsOptions.init();
349 
350     MonteCarloOST monteCarloOST = new MonteCarloOST(orthogonalSpaceTempering.getPotentialEnergy(),
351         orthogonalSpaceTempering, molecularAssemblies[0], molecularAssemblies[0].getProperties(),
352         algorithmListener, dynamicsOptions, verbose, mcGroup.mcMDSteps, dynRestart);
353 
354     MolecularDynamics md = monteCarloOST.getMD();
355     for (int i = 1; i < molecularAssemblies.length; i++) {
356       md.addAssembly(molecularAssemblies[i]);
357     }
358 
359     long nEquil = thermodynamicsOptions.getEquilSteps();
360     if (nEquil > 0) {
361       monteCarloOST.setEquilibration(true);
362     }
363     return monteCarloOST;
364   }
365 
366   private boolean thresholdsSet() {
367     return group.temperingThreshold.length != 1 || group.temperingThreshold[0] >= 0;
368   }
369 
370   /**
371    * Returns the initial bias magnitude associated with a walker.
372    *
373    * @param i Index of a walker
374    * @return Its intended initial bias magnitude in kcal/mol.
375    */
376   private double getBiasMag(int i) {
377     return group.biasMag.length > 1 ? group.biasMag[i] : group.biasMag[0];
378   }
379 
380   private void runDynamics(MolecularDynamics molecularDynamics, long numSteps,
381                            DynamicsOptions dynamicsOptions, WriteoutOptions writeoutOptions, boolean initVelocities,
382                            File dyn) {
383     molecularDynamics.dynamic(numSteps, dynamicsOptions.getDt(), dynamicsOptions.getReport(),
384         dynamicsOptions.getWrite(), dynamicsOptions.getTemperature(), initVelocities,
385         writeoutOptions.getFileType(), dynamicsOptions.getCheckpoint(), dyn);
386   }
387 
388   /**
389    * Returns the tempering threshold associated with a walker.
390    *
391    * @param i Index of a walker
392    * @return Its intended tempering threshold in kcal/mol.
393    */
394   private double getTemperingThreshold(int i) {
395     return group.temperingThreshold.length > 1 ? group.temperingThreshold[i]
396         : group.temperingThreshold[0];
397   }
398 
399   /**
400    * Returns the tempering parameter associated with a walker.
401    *
402    * @param i Index of a walker
403    * @return Its intended tempering parameter in kBT.
404    */
405   private double getTemperingParameter(int i) {
406     return group.temperingRate.length > 1 ? group.temperingRate[i] : group.temperingRate[0];
407   }
408 
409   /**
410    * Sets the number of time steps between OST counts.
411    *
412    * @return Returns the interval between OST counts.
413    */
414   public int getCountInterval() {
415     return group.countInterval;
416   }
417 
418   public void setCountInterval(int countInterval) {
419     group.countInterval = countInterval;
420   }
421 
422   /**
423    * Sets the initial Gaussian bias magnitude in kcal/mol.
424    *
425    * @return Returns the Bias magnitude.
426    */
427   public double[] getBiasMag() {
428     return group.biasMag;
429   }
430 
431   public void setBiasMag(double[] biasMag) {
432     group.biasMag = biasMag;
433   }
434 
435   /**
436    * Enforces that each walker maintains their own histogram.
437    *
438    * @return Returns true if each Walker has their own histogram.
439    */
440   public boolean isIndependentWalkers() {
441     return group.independentWalkers;
442   }
443 
444   public void setIndependentWalkers(boolean independentWalkers) {
445     group.independentWalkers = independentWalkers;
446   }
447 
448   /**
449    * Use a MetaDynamics style bias.
450    *
451    * @return Returns true if each Walker has their own histogram.
452    */
453   public boolean isMetaDynamics() {
454     return group.metaDynamics;
455   }
456 
457   public void setMetaDynamics(boolean metaDynamics) {
458     group.metaDynamics = metaDynamics;
459   }
460 
461   /**
462    * The Dama et al. tempering rate parameter, in multiples of kBT.
463    *
464    * @return Returns the tempering rate.
465    */
466   public double[] getTemperingRate() {
467     return group.temperingRate;
468   }
469 
470   public void setTemperingRate(double[] temperingRate) {
471     group.temperingRate = temperingRate;
472   }
473 
474   /**
475    * The tempering threshold/offset in kcal/mol.
476    *
477    * @return Returns the tempering threshold.
478    */
479   public double[] getTemperingThreshold() {
480     return group.temperingThreshold;
481   }
482 
483   public void setTemperingThreshold(double[] temperingThreshold) {
484     group.temperingThreshold = temperingThreshold;
485   }
486 
487   /**
488    * The Monte Carlo scheme can use a hard wall that rejects any sample (Lambda, dU/dL) located in an
489    * empty histogram bin.
490    *
491    * @return Returns true if the MC-OST hard wall constraint is set.
492    */
493   public boolean isMcHardWall() {
494     return mcGroup.mcHardWall;
495   }
496 
497   public void setMcHardWall(boolean mcHardWall) {
498     mcGroup.mcHardWall = mcHardWall;
499   }
500 
501   /**
502    * The number of steps to take for each MD trajectory for MC-OST.
503    *
504    * @return Returns the number of MD steps for each MC-OST round.
505    */
506   public int getMcMDSteps() {
507     return mcGroup.mcMDSteps;
508   }
509 
510   public void setMcMDSteps(int mcMDSteps) {
511     mcGroup.mcMDSteps = mcMDSteps;
512   }
513 
514   /**
515    * The standard deviation for lambda moves.
516    *
517    * @return Returns the MC lambda trial move standard deviations.
518    */
519   public double getMcLambdaStdDev() {
520     return mcGroup.mcLambdaStdDev;
521   }
522 
523   public void setMcLambdaStdDev(double mcLambdaStdDev) {
524     mcGroup.mcLambdaStdDev = mcLambdaStdDev;
525   }
526 
527   /**
528    * Only write out snapshots if lambda is greater than the value specified.
529    *
530    * @return Returns the lambda write-out threshold.
531    */
532   public double getLambdaWriteOut() {
533     return group.lambdaWriteOut;
534   }
535 
536   public void setLambdaWriteOut(double lambdaWriteOut) {
537     group.lambdaWriteOut = lambdaWriteOut;
538   }
539 
540   /**
541    * Collection of Orthogonal Space Tempering Options.
542    */
543   private static class OSTOptionGroup {
544 
545     /**
546      * -c or --count Sets the number of time steps between OST counts.
547      */
548     @Option(names = {"-C", "--count"}, paramLabel = "10", defaultValue = "10",
549         description = "Time steps between MD Orthogonal Space counts.")
550     private int countInterval = 10;
551 
552     /**
553      * --bM or --biasMag sets the initial Gaussian bias magnitude in kcal/mol.
554      */
555     @Option(names = {"--bM", "--biasMag"}, paramLabel = "0.05", defaultValue = "0.05", split = ",",
556         description = "Orthogonal Space Gaussian bias magnitude (kcal/mol); RepEx OST uses a comma-separated list.")
557     private double[] biasMag = {0.05};
558 
559     /**
560      * --iW or --independentWalkers enforces that each walker maintains their own histogram.
561      */
562     @Option(names = {"--iW", "--independentWalkers"}, defaultValue = "false",
563         description = "Enforces that each walker maintains their own histogram.")
564     private boolean independentWalkers = false;
565 
566     /**
567      * --meta or --metaDynamics Use a 1D metadynamics bias.
568      */
569     @Option(names = {"--meta", "--metaDynamics"}, defaultValue = "false",
570         description = "Use a 1D metadynamics style bias.")
571     private boolean metaDynamics = false;
572 
573     /**
574      * --tp or --temperingRate sets the Dama et al. tempering rate parameter (in multiples of kBT).
575      */
576     @Option(names = {"--tp", "--temperingRate"}, paramLabel = "4.0", defaultValue = "4.0", split = ",",
577         description = "Tempering rate parameter in multiples of kBT; RepEx OST uses a comma-separated list.")
578     private double[] temperingRate = {4.0};
579 
580     /**
581      * --tth or --temperingThreshold sets the tempering threshold/offset in kcal/mol.
582      */
583     @Option(names = {"--tth", "--temperingThreshold"}, paramLabel = "20*bias", defaultValue = "-1", split = ",",
584         description = "Tempering threshold in kcal/mol; RepEx OST uses a comma-separated list.")
585     private double[] temperingThreshold = {-1.0};
586 
587     /**
588      * --lw or --lambdaWriteOut Only write out snapshots if lambda is greater than the value
589      * specified.
590      */
591     @Option(names = {"--lw", "--lambdaWriteOut"}, paramLabel = "0.0", defaultValue = "0.0",
592         description = "Only write out snapshots if lambda is greater than the value specified.")
593     private double lambdaWriteOut = 0.0;
594   }
595 
596   /**
597    * Collection of Monte Carlo Orthogonal Space Tempering Options.
598    */
599   private static class MCOSTOptionGroup {
600 
601     /**
602      * --mc or --monteCarlo sets the Monte Carlo scheme for Orthogonal Space Tempering.
603      */
604     @Option(names = {"--mc", "--monteCarlo"}, defaultValue = "false",
605         description = "Specify use of Monte Carlo OST")
606     private boolean monteCarlo = false;
607 
608     /**
609      * --mcHW or --mcHardWall sets the Monte Carlo scheme to use a hard wall that rejects any sample
610      * (Lambda, dU/dL) located in an empty histogram bin.
611      */
612     @Option(names = {"--mcHW", "--mcHardWall"}, defaultValue = "false",
613         description = "Monte Carlo OST hard wall constraint.")
614     private boolean mcHardWall = false;
615 
616     /**
617      * --mcmD or --mcMDSteps Sets the number of steps to take for each MD trajectory for MC-OST.
618      */
619     @Option(names = {"--mcMD", "--mcMDSteps"}, paramLabel = "100", defaultValue = "100",
620         description = "Number of dynamics steps to take for each MD trajectory for Monte Carlo OST")
621     private int mcMDSteps = 100;
622 
623     /**
624      * --mcL or --mcLambdaStdDev Sets the standard deviation for lambda moves.
625      */
626     @Option(names = {"--mcL", "--mcLambdaStdDev"}, paramLabel = "0.01", defaultValue = "0.01",
627         description = "Standard deviation for lambda move.")
628     private double mcLambdaStdDev = 0.01;
629 
630     /**
631      * --ts or --twoStep MC Orthogonal Space sampling using separate lambda and MD moves.
632      */
633     @Option(names = {"--ts", "--twoStep"}, defaultValue = "false",
634         description = "MC Orthogonal Space sampling using separate lambda and MD moves.")
635     private boolean twoStep = false;
636   }
637 
638 }