1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.algorithms.cli;
39
40 import ffx.algorithms.AlgorithmListener;
41 import ffx.algorithms.dynamics.MolecularDynamics;
42 import ffx.crystal.CrystalPotential;
43 import ffx.potential.MolecularAssembly;
44 import ffx.potential.bonded.LambdaInterface;
45 import ffx.potential.cli.WriteoutOptions;
46 import picocli.CommandLine.ArgGroup;
47 import picocli.CommandLine.Option;
48
49 import java.io.File;
50 import java.util.Arrays;
51 import java.util.Collections;
52 import java.util.Set;
53 import java.util.TreeSet;
54 import java.util.logging.Logger;
55
56 import static java.lang.String.format;
57
58
59
60
61
62
63
64
65 public class ThermodynamicsOptions {
66
67 private static final Logger logger = Logger.getLogger(ThermodynamicsOptions.class.getName());
68
69
70
71
72 @ArgGroup(heading = "%n Thermodynamics Options%n", validate = false)
73 private final ThermodynamicsOptionGroup group = new ThermodynamicsOptionGroup();
74
75
76
77
78
79
80 public ThermodynamicsAlgorithm getAlgorithm() {
81 return ThermodynamicsAlgorithm.parse(group.thermoAlgoString);
82 }
83
84
85
86
87
88
89 public long getEquilSteps() {
90 return group.equilibrationSteps;
91 }
92
93
94
95
96
97
98 public boolean getResetNumSteps() {
99 return group.resetNumSteps;
100 }
101
102
103
104
105
106
107
108
109
110
111
112
113 public MolecularDynamics runFixedAlchemy(MolecularAssembly[] molecularAssemblies,
114 CrystalPotential crystalPotential, DynamicsOptions dynamicsOptions,
115 WriteoutOptions writeoutOptions, File dyn, AlgorithmListener algorithmListener) {
116 dynamicsOptions.init();
117
118 MolecularDynamics molDyn = dynamicsOptions.getDynamics(writeoutOptions, crystalPotential,
119 molecularAssemblies[0], algorithmListener);
120 for (int i = 1; i < molecularAssemblies.length; i++) {
121 molDyn.addAssembly(molecularAssemblies[i]);
122 }
123
124 boolean initVelocities = true;
125 long nSteps = dynamicsOptions.getSteps();
126 molDyn.setRestartFrequency(dynamicsOptions.getCheckpoint());
127
128 if (group.equilibrationSteps > 0) {
129 logger.info("\n Beginning Equilibration");
130 runDynamics(molDyn, group.equilibrationSteps, dynamicsOptions, writeoutOptions, true, dyn);
131 logger.info(" Beginning Fixed-Lambda Alchemical Sampling");
132 initVelocities = false;
133 } else {
134 logger.info("\n Beginning Fixed-Lambda Alchemical Sampling Without Equilibration");
135 if (!group.resetNumSteps) {
136
137 initVelocities = true;
138 }
139 }
140
141 if (nSteps > 0) {
142 runDynamics(molDyn, nSteps, dynamicsOptions, writeoutOptions, initVelocities, dyn);
143 } else {
144 logger.info(" No steps remaining for this process!");
145 }
146 return molDyn;
147 }
148
149
150
151
152
153
154
155
156
157
158
159
160 public MolecularDynamics runNEQ(MolecularAssembly[] molecularAssemblies,
161 CrystalPotential crystalPotential, DynamicsOptions dynamicsOptions,
162 WriteoutOptions writeoutOptions, File dyn, AlgorithmListener algorithmListener) {
163 dynamicsOptions.init();
164
165 MolecularDynamics molDyn = dynamicsOptions.getDynamics(writeoutOptions, crystalPotential,
166 molecularAssemblies[0], algorithmListener);
167 for (int i = 1; i < molecularAssemblies.length; i++) {
168 molDyn.addAssembly(molecularAssemblies[i]);
169 }
170
171 boolean initVelocities = true;
172 long nSteps = dynamicsOptions.getSteps();
173 molDyn.setRestartFrequency(dynamicsOptions.getCheckpoint());
174
175 if (group.equilibrationSteps > 0) {
176 double initialLambda = group.reverseNEQ ? 1.0 : 0.0;
177 logger.info(format("\n Beginning Equilibration (at L=%5.3f)", initialLambda));
178 LambdaInterface lambdaInterface = (LambdaInterface) crystalPotential;
179 lambdaInterface.setLambda(initialLambda);
180 runDynamics(molDyn, group.equilibrationSteps, dynamicsOptions, writeoutOptions, true, dyn);
181 if (nSteps > 0) {
182 logger.info(" Beginning Non-Equilibrium Sampling");
183 }
184 initVelocities = false;
185 } else if (nSteps > 0) {
186 logger.info("\n Beginning Non-Equilibrium Sampling Without Equilibration");
187 if (!group.resetNumSteps) {
188
189 initVelocities = true;
190 }
191 }
192
193 if (nSteps > 0) {
194 molDyn.setNonEquilibriumLambda(true, group.nonEquilibriumSteps, group.reverseNEQ);
195 runDynamics(molDyn, nSteps, dynamicsOptions, writeoutOptions, initVelocities, dyn);
196 }
197
198 return molDyn;
199 }
200
201
202
203
204
205
206 public long getEquilibrationSteps() {
207 return group.equilibrationSteps;
208 }
209
210 private void runDynamics(MolecularDynamics molecularDynamics, long nSteps,
211 DynamicsOptions dynamicsOptions, WriteoutOptions writeoutOptions, boolean initVelocities,
212 File dyn) {
213 molecularDynamics.dynamic(nSteps, dynamicsOptions.getDt(), dynamicsOptions.getReport(),
214 dynamicsOptions.getWrite(), dynamicsOptions.getTemperature(), initVelocities,
215 writeoutOptions.getFileType(), dynamicsOptions.getCheckpoint(), dyn);
216 }
217
218 public void setEquilibrationSteps(long equilibrationSteps) {
219 group.equilibrationSteps = equilibrationSteps;
220 }
221
222
223
224
225
226
227 public boolean isResetNumSteps() {
228 return group.resetNumSteps;
229 }
230
231 public void setResetNumSteps(boolean resetNumSteps) {
232 group.resetNumSteps = resetNumSteps;
233 }
234
235
236
237
238
239
240 public String getThermoAlgoString() {
241 return group.thermoAlgoString;
242 }
243
244 public void setThermoAlgoString(String thermoAlgoString) {
245 group.thermoAlgoString = thermoAlgoString;
246 }
247
248
249
250
251 private static class ThermodynamicsOptionGroup {
252
253
254
255
256
257 @Option(names = {"-Q", "--equilibrate"}, paramLabel = "1000", defaultValue = "1000",
258 description = "Number of equilibration steps before evaluation of thermodynamics.")
259 private long equilibrationSteps = 1000;
260
261
262
263
264 @Option(names = {"--nEQ", "--nonEquilibriumSteps"}, paramLabel = "100", defaultValue = "100",
265 description = "Sets the number of non-equilibrium lambda steps.")
266 private int nonEquilibriumSteps = 100;
267
268
269
270
271 @Option(names = {"--rNEQ", "--reverseNonEquilibrium"}, defaultValue = "false",
272 description = "Run non-equilibrium dynamics from L=1 to L=0.")
273 private boolean reverseNEQ = false;
274
275
276
277
278
279 @Option(names = {"--rn", "--resetNumSteps"}, defaultValue = "false",
280 description = "Ignore prior steps logged in .lam or similar files.")
281 private boolean resetNumSteps = false;
282
283
284
285
286
287 @Option(names = {"--tA", "--thermodynamicsAlgorithm"}, paramLabel = "OST", defaultValue = "OST",
288 description = "Choice of thermodynamics algorithm [OST, FIXED, or NEQ].")
289 private String thermoAlgoString = "OST";
290 }
291
292
293
294
295 public enum ThermodynamicsAlgorithm {
296 OST("OST", "MC-OST", "MD-OST", "DEFAULT"),
297 FIXED("FIXED", "BAR", "MBAR", "FEP", "WINDOWED"),
298 NEQ("NEQ", "NON-EQUILIBRIUM");
299
300 private final Set<String> aliases;
301
302 ThermodynamicsAlgorithm(String... aliases) {
303
304 Set<String> names = new TreeSet<>(Arrays.asList(aliases));
305 names.add(this.name());
306 this.aliases = Collections.unmodifiableSet(names);
307 }
308
309
310
311
312
313
314
315
316
317 public static ThermodynamicsAlgorithm parse(String name) throws IllegalArgumentException {
318 String ucName = name.toUpperCase();
319 for (ThermodynamicsAlgorithm thermodynamicsAlgorithm : values()) {
320 if (thermodynamicsAlgorithm.aliases.contains(ucName)) {
321 return thermodynamicsAlgorithm;
322 }
323 }
324 throw new IllegalArgumentException(
325 format(" Could not parse %s as a ThermodynamicsAlgorithm", name));
326 }
327 }
328 }