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.optimize.anneal.AnnealingSchedule;
42  import ffx.algorithms.optimize.anneal.FlatEndAnnealSchedule;
43  import ffx.algorithms.optimize.anneal.SimulatedAnnealing;
44  import ffx.numerics.Potential;
45  import ffx.potential.MolecularAssembly;
46  import picocli.CommandLine.ArgGroup;
47  import picocli.CommandLine.Option;
48  
49  import java.io.File;
50  import java.util.logging.Logger;
51  
52  import static java.lang.String.format;
53  
54  /**
55   * Represents command line options for scripts that utilize simulated annealing.
56   *
57   * @author Michael J. Schnieders
58   * @author Jacob M. Litman
59   * @since 1.0
60   */
61  public class AnnealOptions {
62  
63    private static final Logger logger = Logger.getLogger(AnnealOptions.class.getName());
64  
65    /**
66     * The ArgGroup keeps the AnnealOptionGroup together when printing help.
67     */
68    @ArgGroup(heading = "%n Simulated Annealing Options%n", validate = false)
69    private final AnnealOptionGroup group = new AnnealOptionGroup();
70  
71    /**
72     * Number of annealing windows.
73     *
74     * @return Returns the number of windows.
75     */
76    public int getWindows() {
77      return group.windows;
78    }
79  
80    /**
81     * Creates a SimulatedAnnealing object.
82     *
83     * @param dynamicsOptions   Dynamics options to use.
84     * @param molecularAssembly MolecularAssembly
85     * @param potential         Potential
86     * @param algorithmListener AlgorithmListener
87     * @return SimulatedAnnealing
88     */
89    public SimulatedAnnealing createAnnealer(DynamicsOptions dynamicsOptions,
90                                             MolecularAssembly molecularAssembly, Potential potential,
91                                             AlgorithmListener algorithmListener) {
92      return createAnnealer(dynamicsOptions, molecularAssembly, potential, algorithmListener, null);
93    }
94  
95    /**
96     * Creates a SimulatedAnnealing object.
97     *
98     * @param dynamicsOptions   Dynamics options to use.
99     * @param molecularAssembly MolecularAssembly
100    * @param potential         Potential
101    * @param algorithmListener AlgorithmListener
102    * @param dynFile           Dynamics restart file.
103    * @return SimulatedAnnealing
104    */
105   public SimulatedAnnealing createAnnealer(DynamicsOptions dynamicsOptions, MolecularAssembly molecularAssembly,
106                                            Potential potential, AlgorithmListener algorithmListener,
107                                            File dynFile) {
108     AnnealingSchedule schedule = getSchedule();
109     double totNormLen = schedule.totalWindowLength();
110     long totSteps = dynamicsOptions.getSteps();
111     long perWindowSteps = (long) (totSteps / totNormLen);
112 
113     if (totSteps > perWindowSteps * totNormLen) {
114       ++perWindowSteps;
115     }
116 
117     long minWindowSteps = (long) (perWindowSteps * schedule.minWindowLength());
118     long maxWindowSteps = (long) (perWindowSteps * schedule.maxWindowLength());
119     int nWindows = schedule.getNumWindows();
120 
121     if (minWindowSteps == maxWindowSteps && minWindowSteps == perWindowSteps) {
122       logger.info(format(" Each of %d simulated annealing windows will have %d steps each, for a "
123           + "total duration of %d time-steps", nWindows, perWindowSteps, perWindowSteps * nWindows));
124     } else {
125       logger.info(format(" Each of %d simulated annealing windows will have %d-%d steps each, "
126               + "with a \"normal\" length of %d steps, for a total duration of %d time-steps", nWindows,
127           minWindowSteps, maxWindowSteps, perWindowSteps,
128           (int) (perWindowSteps * schedule.totalWindowLength())));
129     }
130 
131     if (nWindows < 201) {
132       StringBuilder sb = new StringBuilder(
133           "\n Simulated annealing windows [index,MD steps, temperature (K)]:\n [");
134       for (int i = 0; i < nWindows; i++) {
135         double len = schedule.windowLength(i);
136         double temp = schedule.getTemperature(i);
137         sb.append(format("[%d,%d,%10.4g]", (i + 1), (int) (len * perWindowSteps), temp));
138         if (i == nWindows - 1) {
139           sb.append("]\n");
140         } else if (i % 10 == 9) {
141           sb.append("\n");
142         } else {
143           sb.append(",");
144         }
145       }
146       logger.info(sb.toString());
147     } else {
148       logger.info(" Skipping printout of window lengths/temperatures (max printout at 200 windows)");
149     }
150 
151     return new SimulatedAnnealing(molecularAssembly, potential, algorithmListener,
152         dynamicsOptions.thermostat, dynamicsOptions.integrator, schedule, perWindowSteps,
153         dynamicsOptions.getDt(), isReinitVelocities(), dynFile);
154   }
155 
156   /**
157    * Constructs an AnnealingSchedule.
158    *
159    * @return An AnnealingSchedule.
160    */
161   public AnnealingSchedule getSchedule() {
162     SimulatedAnnealing.Schedules schedules = SimulatedAnnealing.Schedules.parse(getTemperString());
163     AnnealingSchedule annealingSchedule = schedules.generate(getWindows(), getLow(), getUpper());
164     if (getTemperBefore() > 0 || getTemperAfter() > 0) {
165       annealingSchedule = new FlatEndAnnealSchedule(annealingSchedule, getLow(), getUpper(),
166           getTemperBefore(), getTemperAfter());
167     }
168     return annealingSchedule;
169   }
170 
171   public void setWindows(int windows) {
172     group.windows = windows;
173   }
174 
175   /**
176    * Low temperature limit in degrees Kelvin.
177    *
178    * @return Returns the low temperature limit.
179    */
180   public double getLow() {
181     return group.low;
182   }
183 
184   public void setLow(double low) {
185     group.low = low;
186   }
187 
188   /**
189    * Upper temperature limit in degrees Kelvin.
190    *
191    * @return Returns the upper temperature limit.
192    */
193   public double getUpper() {
194     return group.upper;
195   }
196 
197   public void setUpper(double upper) {
198     group.upper = upper;
199   }
200 
201   /**
202    * Forces simulated annealing to re-initialize velocities to the new temperature at each annealing
203    * step, rather than letting the thermostat shift temperature downwards.
204    *
205    * @return Returns true for re-initialization of velocities.
206    */
207   public boolean isReinitVelocities() {
208     return group.reinitVelocities;
209   }
210 
211   public void setReinitVelocities(boolean reinitVelocities) {
212     group.reinitVelocities = reinitVelocities;
213   }
214 
215   /**
216    * Sets the schedule to be used.
217    *
218    * @return Returns a String representation of the tempering schedule.
219    */
220   public String getTemperString() {
221     return group.temperString;
222   }
223 
224   public void setTemperString(String temperString) {
225     group.temperString = temperString;
226   }
227 
228   /**
229    * Sets the number of annealing windows to hold flat at the high temperature (in addition to normal
230    * windows).
231    *
232    * @return Returns the number of annealing windows to hold flat at the high temperature.
233    */
234   public int getTemperBefore() {
235     return group.temperBefore;
236   }
237 
238   public void setTemperBefore(int temperBefore) {
239     group.temperBefore = temperBefore;
240   }
241 
242   /**
243    * Sets the number of annealing windows to hold flat at the low temperature (in addition to normal
244    * windows).
245    *
246    * @return Returns the number of annealing windows to hold flat at the low temperature.
247    */
248   public int getTemperAfter() {
249     return group.temperAfter;
250   }
251 
252   public void setTemperAfter(int temperAfter) {
253     group.temperAfter = temperAfter;
254   }
255 
256   /**
257    * Collection of Simulated Annealing Options.
258    */
259   private static class AnnealOptionGroup {
260 
261     /**
262      * -w or --windows Number of annealing windows (10).
263      */
264     @Option(names = {"-W", "--windows"}, paramLabel = "10", defaultValue = "10",
265         description = "Number of annealing windows.")
266     private int windows = 10;
267 
268     /**
269      * -l or --low Low temperature limit in degrees Kelvin (10.0).
270      */
271     @Option(names = {"--tl", "--temperatureLow"}, paramLabel = "10.0", defaultValue = "10.0",
272         description = "Low temperature limit (Kelvin).")
273     private double low = 10.0;
274 
275     /**
276      * -u or --upper Upper temperature limit in degrees Kelvin (1000.0).
277      */
278     @Option(names = {"--tu", "--temperatureUpper"}, paramLabel = "1000.0", defaultValue = "1000.0",
279         description = "High temperature limit (Kelvin).")
280     private double upper = 1000.0;
281 
282     /**
283      * --rv or --reinitVelocities forces simulated annealing to re-initialize velocities to the new
284      * temperature at each annealing step, rather than letting the thermostat shift temperature
285      * downwards.
286      */
287     @Option(names = {"--rv", "--reinitVelocities"}, paramLabel = "false", defaultValue = "false",
288         description = "Re-initialize velocities before each round of annealing.")
289     private boolean reinitVelocities = false;
290 
291     /**
292      * --tmS or --temperingSchedule sets the schedule to be used.
293      */
294     @Option(names = {"--tmS", "--temperingSchedule"}, paramLabel = "EXP", defaultValue = "EXP",
295         description = "Tempering schedule: choose between EXP (exponential) or LINEAR")
296     private String temperString = "EXP";
297 
298     /**
299      * --tmB or --temperingBefore sets the number of annealing windows to hold flat at the high
300      * temperature (in addition to normal windows).
301      */
302     @Option(names = {"--tmB", "--temperingBefore"}, paramLabel = "0", defaultValue = "0",
303         description = "Number of (annealing, not MD/MC) steps to remain at the high temperature")
304     private int temperBefore = 0;
305 
306     /**
307      * --tmA or --temperingAfter sets the number of annealing windows to hold flat at the low
308      * temperature (in addition to normal windows).
309      */
310     @Option(names = {"--tmA", "--temperingAfter"}, paramLabel = "0", defaultValue = "0",
311         description = "Number of (annealing, not MD/MC) steps to remain at the low temperature")
312     private int temperAfter = 0;
313   }
314 }