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-2026.
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 crystalPotential         a {@link ffx.crystal.CrystalPotential} object.
339    * @param dynamicsOptions          a {@link ffx.algorithms.cli.DynamicsOptions} object.
340    * @param thermodynamicsOptions    a {@link ffx.algorithms.cli.ThermodynamicsOptions} object.
341    * @param verbose                  Whether to print out additional information about MC-OST.
342    * @param algorithmListener        An AlgorithmListener
343    * @return An assembled MonteCarloOST ready to run.
344    */
345   public MonteCarloOST setupMCOST(OrthogonalSpaceTempering orthogonalSpaceTempering,
346                                   MolecularAssembly[] molecularAssemblies, CrystalPotential crystalPotential,
347                                   DynamicsOptions dynamicsOptions,
348                                   ThermodynamicsOptions thermodynamicsOptions, boolean verbose,
349                                   File dynRestart, AlgorithmListener algorithmListener) {
350     dynamicsOptions.init();
351 
352     MonteCarloOST monteCarloOST = new MonteCarloOST(crystalPotential, orthogonalSpaceTempering,
353         molecularAssemblies[0], molecularAssemblies[0].getProperties(),
354         algorithmListener, dynamicsOptions, verbose, mcGroup.mcMDSteps, dynRestart);
355 
356     MolecularDynamics md = monteCarloOST.getMD();
357     for (int i = 1; i < molecularAssemblies.length; i++) {
358       md.addAssembly(molecularAssemblies[i]);
359     }
360 
361     long nEquil = thermodynamicsOptions.getEquilSteps();
362     if (nEquil > 0) {
363       monteCarloOST.setEquilibration(true);
364     }
365     return monteCarloOST;
366   }
367 
368   private boolean thresholdsSet() {
369     return group.temperingThreshold.length != 1 || group.temperingThreshold[0] >= 0;
370   }
371 
372   /**
373    * Returns the initial bias magnitude associated with a walker.
374    *
375    * @param i Index of a walker
376    * @return Its intended initial bias magnitude in kcal/mol.
377    */
378   private double getBiasMag(int i) {
379     return group.biasMag.length > 1 ? group.biasMag[i] : group.biasMag[0];
380   }
381 
382   private void runDynamics(MolecularDynamics molecularDynamics, long numSteps,
383                            DynamicsOptions dynamicsOptions, WriteoutOptions writeoutOptions, boolean initVelocities,
384                            File dyn) {
385     molecularDynamics.dynamic(numSteps, dynamicsOptions.getDt(), dynamicsOptions.getReport(),
386         dynamicsOptions.getWrite(), dynamicsOptions.getTemperature(), initVelocities,
387         writeoutOptions.getFileType(), dynamicsOptions.getCheckpoint(), dyn);
388   }
389 
390   /**
391    * Returns the tempering threshold associated with a walker.
392    *
393    * @param i Index of a walker
394    * @return Its intended tempering threshold in kcal/mol.
395    */
396   private double getTemperingThreshold(int i) {
397     return group.temperingThreshold.length > 1 ? group.temperingThreshold[i]
398         : group.temperingThreshold[0];
399   }
400 
401   /**
402    * Returns the tempering parameter associated with a walker.
403    *
404    * @param i Index of a walker
405    * @return Its intended tempering parameter in kBT.
406    */
407   private double getTemperingParameter(int i) {
408     return group.temperingRate.length > 1 ? group.temperingRate[i] : group.temperingRate[0];
409   }
410 
411   /**
412    * Sets the number of time steps between OST counts.
413    *
414    * @return Returns the interval between OST counts.
415    */
416   public int getCountInterval() {
417     return group.countInterval;
418   }
419 
420   public void setCountInterval(int countInterval) {
421     group.countInterval = countInterval;
422   }
423 
424   /**
425    * Sets the initial Gaussian bias magnitude in kcal/mol.
426    *
427    * @return Returns the Bias magnitude.
428    */
429   public double[] getBiasMag() {
430     return group.biasMag;
431   }
432 
433   public void setBiasMag(double[] biasMag) {
434     group.biasMag = biasMag;
435   }
436 
437   /**
438    * Enforces that each walker maintains their own histogram.
439    *
440    * @return Returns true if each Walker has their own histogram.
441    */
442   public boolean isIndependentWalkers() {
443     return group.independentWalkers;
444   }
445 
446   public void setIndependentWalkers(boolean independentWalkers) {
447     group.independentWalkers = independentWalkers;
448   }
449 
450   /**
451    * Use a MetaDynamics style bias.
452    *
453    * @return Returns true if each Walker has their own histogram.
454    */
455   public boolean isMetaDynamics() {
456     return group.metaDynamics;
457   }
458 
459   public void setMetaDynamics(boolean metaDynamics) {
460     group.metaDynamics = metaDynamics;
461   }
462 
463   /**
464    * The Dama et al. tempering rate parameter, in multiples of kBT.
465    *
466    * @return Returns the tempering rate.
467    */
468   public double[] getTemperingRate() {
469     return group.temperingRate;
470   }
471 
472   public void setTemperingRate(double[] temperingRate) {
473     group.temperingRate = temperingRate;
474   }
475 
476   /**
477    * The tempering threshold/offset in kcal/mol.
478    *
479    * @return Returns the tempering threshold.
480    */
481   public double[] getTemperingThreshold() {
482     return group.temperingThreshold;
483   }
484 
485   public void setTemperingThreshold(double[] temperingThreshold) {
486     group.temperingThreshold = temperingThreshold;
487   }
488 
489   /**
490    * The Monte Carlo scheme can use a hard wall that rejects any sample (Lambda, dU/dL) located in an
491    * empty histogram bin.
492    *
493    * @return Returns true if the MC-OST hard wall constraint is set.
494    */
495   public boolean isMcHardWall() {
496     return mcGroup.mcHardWall;
497   }
498 
499   public void setMcHardWall(boolean mcHardWall) {
500     mcGroup.mcHardWall = mcHardWall;
501   }
502 
503   /**
504    * The number of steps to take for each MD trajectory for MC-OST.
505    *
506    * @return Returns the number of MD steps for each MC-OST round.
507    */
508   public int getMcMDSteps() {
509     return mcGroup.mcMDSteps;
510   }
511 
512   public void setMcMDSteps(int mcMDSteps) {
513     mcGroup.mcMDSteps = mcMDSteps;
514   }
515 
516   /**
517    * The standard deviation for lambda moves.
518    *
519    * @return Returns the MC lambda trial move standard deviations.
520    */
521   public double getMcLambdaStdDev() {
522     return mcGroup.mcLambdaStdDev;
523   }
524 
525   public void setMcLambdaStdDev(double mcLambdaStdDev) {
526     mcGroup.mcLambdaStdDev = mcLambdaStdDev;
527   }
528 
529   /**
530    * Only write out snapshots if lambda is greater than the value specified.
531    *
532    * @return Returns the lambda write-out threshold.
533    */
534   public double getLambdaWriteOut() {
535     return group.lambdaWriteOut;
536   }
537 
538   public void setLambdaWriteOut(double lambdaWriteOut) {
539     group.lambdaWriteOut = lambdaWriteOut;
540   }
541 
542   /**
543    * Collection of Orthogonal Space Tempering Options.
544    */
545   private static class OSTOptionGroup {
546 
547     /**
548      * -c or --count Sets the number of time steps between OST counts.
549      */
550     @Option(names = {"-C", "--count"}, paramLabel = "10", defaultValue = "10",
551         description = "Time steps between MD Orthogonal Space counts.")
552     private int countInterval = 10;
553 
554     /**
555      * --bM or --biasMag sets the initial Gaussian bias magnitude in kcal/mol.
556      */
557     @Option(names = {"--bM", "--biasMag"}, paramLabel = "0.05", defaultValue = "0.05", split = ",",
558         description = "Orthogonal Space Gaussian bias magnitude (kcal/mol); RepEx OST uses a comma-separated list.")
559     private double[] biasMag = {0.05};
560 
561     /**
562      * --iW or --independentWalkers enforces that each walker maintains their own histogram.
563      */
564     @Option(names = {"--iW", "--independentWalkers"}, defaultValue = "false",
565         description = "Enforces that each walker maintains their own histogram.")
566     private boolean independentWalkers = false;
567 
568     /**
569      * --meta or --metaDynamics Use a 1D metadynamics bias.
570      */
571     @Option(names = {"--meta", "--metaDynamics"}, defaultValue = "false",
572         description = "Use a 1D metadynamics style bias.")
573     private boolean metaDynamics = false;
574 
575     /**
576      * --tp or --temperingRate sets the Dama et al. tempering rate parameter (in multiples of kBT).
577      */
578     @Option(names = {"--tp", "--temperingRate"}, paramLabel = "4.0", defaultValue = "4.0", split = ",",
579         description = "Tempering rate parameter in multiples of kBT; RepEx OST uses a comma-separated list.")
580     private double[] temperingRate = {4.0};
581 
582     /**
583      * --tth or --temperingThreshold sets the tempering threshold/offset in kcal/mol.
584      */
585     @Option(names = {"--tth", "--temperingThreshold"}, paramLabel = "20*bias", defaultValue = "-1", split = ",",
586         description = "Tempering threshold in kcal/mol; RepEx OST uses a comma-separated list.")
587     private double[] temperingThreshold = {-1.0};
588 
589     /**
590      * --lw or --lambdaWriteOut Only write out snapshots if lambda is greater than the value
591      * specified.
592      */
593     @Option(names = {"--lw", "--lambdaWriteOut"}, paramLabel = "0.0", defaultValue = "0.0",
594         description = "Only write out snapshots if lambda is greater than the value specified.")
595     private double lambdaWriteOut = 0.0;
596   }
597 
598   /**
599    * Collection of Monte Carlo Orthogonal Space Tempering Options.
600    */
601   private static class MCOSTOptionGroup {
602 
603     /**
604      * --mc or --monteCarlo sets the Monte Carlo scheme for Orthogonal Space Tempering.
605      */
606     @Option(names = {"--mc", "--monteCarlo"}, defaultValue = "false",
607         description = "Specify use of Monte Carlo OST")
608     private boolean monteCarlo = false;
609 
610     /**
611      * --mcHW or --mcHardWall sets the Monte Carlo scheme to use a hard wall that rejects any sample
612      * (Lambda, dU/dL) located in an empty histogram bin.
613      */
614     @Option(names = {"--mcHW", "--mcHardWall"}, defaultValue = "false",
615         description = "Monte Carlo OST hard wall constraint.")
616     private boolean mcHardWall = false;
617 
618     /**
619      * --mcmD or --mcMDSteps Sets the number of steps to take for each MD trajectory for MC-OST.
620      */
621     @Option(names = {"--mcMD", "--mcMDSteps"}, paramLabel = "100", defaultValue = "100",
622         description = "Number of dynamics steps to take for each MD trajectory for Monte Carlo OST")
623     private int mcMDSteps = 100;
624 
625     /**
626      * --mcL or --mcLambdaStdDev Sets the standard deviation for lambda moves.
627      */
628     @Option(names = {"--mcL", "--mcLambdaStdDev"}, paramLabel = "0.01", defaultValue = "0.01",
629         description = "Standard deviation for lambda move.")
630     private double mcLambdaStdDev = 0.01;
631 
632     /**
633      * --ts or --twoStep MC Orthogonal Space sampling using separate lambda and MD moves.
634      */
635     @Option(names = {"--ts", "--twoStep"}, defaultValue = "false",
636         description = "MC Orthogonal Space sampling using separate lambda and MD moves.")
637     private boolean twoStep = false;
638   }
639 
640 }