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;
39
40 import ffx.algorithms.AlgorithmListener;
41 import ffx.algorithms.Terminatable;
42 import ffx.numerics.Potential;
43 import ffx.numerics.optimization.LBFGS;
44 import ffx.numerics.optimization.LineSearch;
45 import ffx.numerics.optimization.OptimizationListener;
46 import ffx.potential.ForceFieldEnergy;
47 import ffx.potential.MolecularAssembly;
48 import ffx.potential.bonded.Atom;
49 import ffx.potential.extended.ExtendedSystem;
50
51 import java.util.logging.Level;
52 import java.util.logging.Logger;
53
54 import static java.lang.String.format;
55 import static java.util.Arrays.fill;
56
57
58
59
60
61
62
63 public class PhMinimize implements OptimizationListener, Terminatable {
64
65 private static final Logger logger = Logger.getLogger(PhMinimize.class.getName());
66
67
68 protected final MolecularAssembly molecularAssembly;
69
70 protected final Potential potential;
71
72 protected final ExtendedSystem esvSystem;
73
74 protected final AlgorithmListener algorithmListener;
75
76 protected final int n;
77
78 protected final int nESV;
79
80 protected final double[] x;
81
82 protected double[] theta;
83
84 protected final double[] grad;
85
86 protected double[] gradESV;
87
88 protected final double[] scaling;
89
90 protected boolean done = false;
91
92 protected boolean terminate = false;
93
94 protected long time;
95
96 protected double energy;
97
98 protected int status;
99
100 protected int nSteps;
101
102 double rmsGradient;
103
104
105
106
107
108
109
110
111 public PhMinimize(MolecularAssembly molecularAssembly, Potential potential,
112 AlgorithmListener algorithmListener, ExtendedSystem esvSystem) {
113 this.molecularAssembly = molecularAssembly;
114 this.algorithmListener = algorithmListener;
115 this.potential = potential;
116 this.esvSystem = esvSystem;
117 n = potential.getNumberOfVariables();
118 nESV = esvSystem.getNumberOfVariables();
119 x = new double[n];
120 theta = new double[nESV];
121 grad = new double[n];
122 gradESV = new double[nESV];
123 scaling = new double[n];
124 fill(scaling, 12.0);
125 }
126
127
128
129
130
131
132
133 public PhMinimize(MolecularAssembly molecularAssembly, AlgorithmListener algorithmListener,
134 ExtendedSystem esvSystem) {
135 this.molecularAssembly = molecularAssembly;
136 this.algorithmListener = algorithmListener;
137 this.esvSystem = esvSystem;
138 if (molecularAssembly.getPotentialEnergy() == null) {
139 molecularAssembly.setPotential(ForceFieldEnergy.energyFactory(molecularAssembly));
140 }
141 potential = molecularAssembly.getPotentialEnergy();
142 n = potential.getNumberOfVariables();
143 nESV = esvSystem.getNumberOfVariables();
144 x = new double[n];
145 theta = new double[nESV];
146 grad = new double[n];
147 gradESV = new double[nESV];
148 scaling = new double[n];
149 fill(scaling, 12.0);
150 }
151
152
153
154
155
156
157 public double getEnergy() {
158 return energy;
159 }
160
161
162
163
164
165
166 public double getRMSGradient() {
167 return rmsGradient;
168 }
169
170
171
172
173
174
175 public int getStatus() {
176 return status;
177 }
178
179
180
181
182
183
184 public Potential minimizeCoordinates() {
185 return minimizeCoordinates(7, 1.0, Integer.MAX_VALUE);
186 }
187
188
189
190
191
192
193
194 public Potential minimizeCoordinates(double eps) {
195 return minimizeCoordinates(7, eps, Integer.MAX_VALUE);
196 }
197
198
199
200
201
202
203
204
205 public Potential minimizeCoordinates(double eps, int maxIterations) {
206 return minimizeCoordinates(7, eps, maxIterations);
207 }
208
209
210
211
212
213
214
215
216
217 public Potential minimizeCoordinates(int m, double eps, int maxIterations) {
218 time = System.nanoTime();
219 potential.getCoordinates(x);
220 potential.setScaling(scaling);
221
222
223 for (int i = 0; i < n; i++) {
224 x[i] *= scaling[i];
225 }
226
227 done = false;
228 energy = potential.energyAndGradient(x, grad);
229
230 if (logger.isLoggable(Level.FINE)) {
231 logger.fine(format(" Minimize initial energy: %16.8f", energy));
232 }
233
234 status = LBFGS.minimize(n, m, x, energy, grad, eps, maxIterations, potential, this);
235 done = true;
236
237 switch (status) {
238 case 0 -> logger.info(format("\n Optimization achieved convergence criteria: %8.5f", rmsGradient));
239 case 1 -> logger.info(format("\n Optimization terminated at step %d.", nSteps));
240 default -> logger.warning("\n Optimization failed.");
241 }
242
243 potential.setScaling(null);
244 return potential;
245 }
246
247
248
249
250
251
252
253
254 public Potential minimizeTitration(double eps, int maxIterations) {
255 return minimizeTitration(7, eps, maxIterations);
256 }
257
258
259
260
261
262
263
264
265
266 public Potential minimizeTitration(int m, double eps, int maxIterations) {
267 time = System.nanoTime();
268 theta = esvSystem.getThetaPosition();
269
270 done = false;
271 potential.getCoordinates(x);
272 theta = esvSystem.getThetaPosition();
273 energy = esvSystem.energyAndGradient(theta, gradESV);
274
275 if (logger.isLoggable(Level.FINE)) {
276 logger.fine(format(" Minimize initial energy: %16.8f", energy));
277 }
278
279 status = LBFGS.minimize(nESV, m, theta, energy, gradESV, eps, maxIterations, esvSystem, this);
280 done = true;
281
282 switch (status) {
283 case 0 -> {
284 logger.info(format("\n Optimization achieved convergence criteria: %8.5f", rmsGradient));
285 for (Atom atom : molecularAssembly.getAtomList()) {
286 int atomIndex = atom.getIndex() - 1;
287 atom.setOccupancy(esvSystem.getTitrationLambda(atomIndex));
288 atom.setTempFactor(esvSystem.getTautomerLambda(atomIndex));
289 }
290 }
291 case 1 -> logger.info(format("\n Optimization terminated at step %d.", nSteps));
292 default -> logger.warning("\n Optimization failed.");
293 }
294
295 potential.setScaling(null);
296 return potential;
297 }
298
299
300
301
302
303
304
305
306 @Override
307 public boolean optimizationUpdate(int iteration, int nBFGS, int functionEvaluations,
308 double rmsGradient, double rmsCoordinateChange, double energy, double energyChange,
309 double angle, LineSearch.LineSearchResult lineSearchResult) {
310 long currentTime = System.nanoTime();
311 Double seconds = (currentTime - time) * 1.0e-9;
312 time = currentTime;
313 this.rmsGradient = rmsGradient;
314 this.nSteps = iteration;
315 this.energy = energy;
316
317 if (iteration == 0) {
318 if (nBFGS > 0) {
319 logger.info("\n Limited Memory BFGS Quasi-Newton Optimization: \n");
320 } else {
321 logger.info("\n Steepest Decent Optimization: \n");
322 }
323 logger.info(" Cycle Energy G RMS Delta E Delta X Angle Evals Time\n");
324 }
325 if (lineSearchResult == null) {
326 logger.info(format("%6d%13.4f%11.4f", iteration, energy, rmsGradient));
327 } else {
328 if (lineSearchResult == LineSearch.LineSearchResult.Success) {
329 logger.info(
330 format("%6d%13.4f%11.4f%11.4f%10.4f%9.2f%7d %8.3f", iteration, energy, rmsGradient,
331 energyChange, rmsCoordinateChange, angle, functionEvaluations, seconds));
332 } else {
333 logger.info(format("%6d%13.4f%11.4f%11.4f%10.4f%9.2f%7d %8s", iteration, energy, rmsGradient,
334 energyChange, rmsCoordinateChange, angle, functionEvaluations, lineSearchResult));
335 }
336 }
337
338 if (algorithmListener != null) {
339 algorithmListener.algorithmUpdate(molecularAssembly);
340 }
341
342 if (terminate) {
343 logger.info(" The optimization recieved a termination request.");
344
345 return false;
346 }
347 return true;
348 }
349
350
351 @Override
352 public void terminate() {
353 terminate = true;
354 while (!done) {
355 synchronized (this) {
356 try {
357 wait(1);
358 } catch (Exception e) {
359 logger.log(Level.WARNING, "Exception terminating minimization.\n", e);
360 }
361 }
362 }
363 }
364 }