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.optimize.anneal;
39
40 import ffx.algorithms.AlgorithmListener;
41 import ffx.algorithms.Terminatable;
42 import ffx.algorithms.dynamics.MolecularDynamics;
43 import ffx.algorithms.dynamics.integrators.IntegratorEnum;
44 import ffx.algorithms.dynamics.thermostats.ThermostatEnum;
45 import ffx.numerics.Potential;
46 import ffx.potential.MolecularAssembly;
47 import java.io.File;
48 import java.util.Set;
49 import java.util.logging.Level;
50 import java.util.logging.Logger;
51
52
53
54
55
56
57
58
59 public class SimulatedAnnealing implements Runnable, Terminatable {
60
61 private static final Logger logger = Logger.getLogger(SimulatedAnnealing.class.getName());
62
63 private final MolecularDynamics molecularDynamics;
64
65 private final AnnealingSchedule schedule;
66
67 private final long mdSteps;
68
69 private final double timeStep;
70
71 private final boolean reInitVelocity;
72
73 private final int trajSteps = 1;
74
75 private double printInterval = 0.01;
76
77 private boolean done = true;
78
79 private boolean terminate;
80
81 private double saveFrequency = 0.1;
82
83 private File dynFile;
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99 public SimulatedAnnealing(MolecularAssembly molecularAssembly, Potential potentialEnergy,
100 AlgorithmListener algorithmListener, ThermostatEnum requestedThermostat,
101 IntegratorEnum requestedIntegrator, AnnealingSchedule annealingSchedule, long mdSteps,
102 double timeStep, boolean reInitVelocity, File dynFile) {
103
104 molecularDynamics = MolecularDynamics.dynamicsFactory(molecularAssembly, potentialEnergy,
105 algorithmListener, requestedThermostat, requestedIntegrator);
106 this.schedule = annealingSchedule;
107 this.mdSteps = mdSteps;
108 this.timeStep = timeStep;
109 this.reInitVelocity = reInitVelocity;
110 this.dynFile = dynFile;
111 }
112
113
114 public void anneal() {
115
116
117 if (!done) {
118 logger.warning(" Programming error - a thread invoked anneal when it was already running.");
119 return;
120 }
121 done = false;
122 logger.info(" Beginning simulated annealing");
123 begin();
124 }
125
126
127
128
129
130
131 public double getKineticEnergy() {
132 return molecularDynamics.getKineticEnergy();
133 }
134
135
136
137
138
139
140 public double getPotentialEnergy() {
141 return molecularDynamics.getPotentialEnergy();
142 }
143
144
145
146
147
148
149 public double getTemperature() {
150 return molecularDynamics.getTemperature();
151 }
152
153
154
155
156
157
158 public double getTotalEnergy() {
159 return molecularDynamics.getTotalEnergy();
160 }
161
162
163
164
165
166
167 @Override
168 public void run() {
169 done = false;
170 terminate = false;
171
172 int minMdSteps = (int) (mdSteps * schedule.minWindowLength());
173 if (minMdSteps < trajSteps) {
174 logger.warning(String.format(
175 " Minimum number of MD steps per annealing cycle %d was less than steps per OpenMM MD cycle %d! Setting steps per MD cycle to %d",
176 minMdSteps, trajSteps, minMdSteps));
177 setTrajectorySteps(minMdSteps);
178 }
179
180 int nWindows = schedule.getNumWindows();
181 boolean forceFirstReinit = (dynFile == null);
182
183 for (int i = 0; i < nWindows; i++) {
184 double temperature = schedule.getTemperature(i);
185 int nSteps = (int) (schedule.windowLength(i) * mdSteps);
186 logger.info(
187 String.format(" Annealing window %d: %d steps at %9.4g K", (i + 1), nSteps, temperature));
188 molecularDynamics.dynamic(nSteps, timeStep, printInterval, saveFrequency, temperature,
189 (reInitVelocity || forceFirstReinit), dynFile);
190 if (dynFile == null) {
191 dynFile = molecularDynamics.getDynFile();
192 }
193 forceFirstReinit = false;
194 if (terminate) {
195 logger.info(String.format("\n Terminating at temperature %8.3f.\n", temperature));
196 break;
197 }
198 }
199 if (!terminate) {
200 logger.info(String.format(" Completed %8d annealing steps\n", nWindows));
201 }
202
203 done = true;
204 terminate = false;
205 }
206
207
208
209
210
211
212 public void setPrintInterval(double printInterval) {
213 this.printInterval = printInterval;
214 }
215
216
217
218
219
220
221
222 public void setRestartFrequency(double restart) throws IllegalArgumentException {
223 if (Double.isFinite(restart) && restart > 0) {
224 molecularDynamics.setRestartFrequency(restart);
225 } else {
226 throw new IllegalArgumentException(
227 String.format(" Restart frequency must be positive finite, was %10.4g", restart));
228 }
229 }
230
231
232
233
234
235
236 public void setSaveFrequency(double save) {
237 this.saveFrequency = save;
238 }
239
240
241
242
243
244
245 public void setTrajectorySteps(int trajectorySteps) {
246 molecularDynamics.setIntervalSteps(trajectorySteps);
247 }
248
249
250 @Override
251 public void terminate() {
252 terminate = true;
253 while (!done) {
254 synchronized (this) {
255 try {
256 wait(1);
257 } catch (Exception e) {
258 logger.log(Level.WARNING, "Exception terminating annealing.\n", e);
259 }
260 }
261 }
262 }
263
264 private void begin() {
265 logger.info(String.format(" Initial temperature: %8.3f (Kelvin)", schedule.getHighTemp()));
266 logger.info(String.format(" Final temperature: %8.3f (Kelvin)", schedule.getLowTemp()));
267 logger.info(String.format(" Annealing steps: %8d", schedule.getNumWindows()));
268 logger.info(String.format(" MD steps/temperature: %8d", mdSteps));
269 logger.info(String.format(" MD time step: %8.3f (fs)", timeStep));
270
271 Thread annealingThread = new Thread(this);
272 annealingThread.start();
273 synchronized (this) {
274 try {
275 while (annealingThread.isAlive()) {
276 wait(100);
277 }
278 } catch (Exception e) {
279 String message = "Simulated annealing interrupted.";
280 logger.log(Level.WARNING, message, e);
281 }
282 }
283 }
284
285
286 public enum Schedules {
287 EXP(ExpAnnealSchedule::new, "EXP", "EXPONENTIAL"), LINEAR(LinearAnnealSchedule::new, "LINEAR");
288
289 private final ScheduleConstructor sc;
290 private final Set<String> aliases;
291
292 Schedules(ScheduleConstructor sc, String... names) {
293 this.sc = sc;
294 aliases = Set.of(names);
295 }
296
297
298
299
300
301
302
303 public static Schedules parse(String name) {
304 name = name.toUpperCase();
305 for (Schedules s : values()) {
306 if (s.aliases.contains(name)) {
307 return s;
308 }
309 }
310 return valueOf(name);
311 }
312
313
314
315
316
317
318
319
320
321 public AnnealingSchedule generate(int nWindows, double tLow, double tHigh) {
322 return sc.asConstruct(nWindows, tLow, tHigh);
323 }
324 }
325
326
327 private interface ScheduleConstructor {
328
329 AnnealingSchedule asConstruct(int nWindows, double tLow, double tHigh);
330 }
331 }