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.xray;
39
40 import static java.lang.String.format;
41
42 import ffx.algorithms.AlgorithmListener;
43 import ffx.algorithms.Terminatable;
44 import ffx.numerics.optimization.LBFGS;
45 import ffx.numerics.optimization.LineSearch.LineSearchResult;
46 import ffx.numerics.optimization.OptimizationListener;
47 import ffx.potential.MolecularAssembly;
48 import ffx.potential.bonded.Atom;
49 import ffx.potential.bonded.Molecule;
50 import ffx.potential.bonded.Residue;
51 import ffx.realspace.RealSpaceData;
52 import java.util.List;
53 import java.util.logging.Level;
54 import java.util.logging.Logger;
55
56
57
58
59
60
61
62
63 public class RefinementMinimize implements OptimizationListener, Terminatable {
64
65 private static final Logger logger = Logger.getLogger(RefinementMinimize.class.getName());
66 private static final double toSeconds = 1.0e-9;
67 public final RefinementEnergy refinementEnergy;
68 private final AlgorithmListener listener;
69 private final DataContainer dataContainer;
70 private final Atom[] activeAtomArray;
71 private final int nXYZ;
72 private final int nB;
73 private final int nOcc;
74 private final int n;
75 private final double[] x;
76 private final double[] grad;
77 private final double[] scaling;
78 private final RefinementMode refinementMode;
79 private boolean done = false;
80 private boolean terminate = false;
81 private long time;
82 private double grms;
83 private int nSteps;
84 private double eps = 0.1;
85
86
87
88
89
90
91
92 public RefinementMinimize(DataContainer data) {
93 this(data, RefinementMode.COORDINATES_AND_BFACTORS, null);
94 }
95
96
97
98
99
100
101
102
103
104 public RefinementMinimize(DataContainer data, RefinementMode refinementmode) {
105 this(data, refinementmode, null);
106 }
107
108
109
110
111
112
113
114
115
116
117 public RefinementMinimize(
118 DataContainer data, RefinementMode refinementMode, AlgorithmListener listener) {
119 dataContainer = data;
120 this.listener = listener;
121 this.refinementMode = refinementMode;
122 refinementEnergy = new RefinementEnergy(data, refinementMode, null);
123 nXYZ = refinementEnergy.nXYZ;
124 nB = refinementEnergy.nBFactor;
125 nOcc = refinementEnergy.nOccupancy;
126 n = refinementEnergy.getNumberOfVariables();
127
128 Atom[] atomArray = data.getAtomArray();
129 RefinementModel refinementModel = data.getRefinementModel();
130 int nAtoms = atomArray.length;
131
132
133 int count = 0;
134 for (Atom a : atomArray) {
135 if (a.isActive()) {
136 count++;
137 }
138 }
139 int nActive = count;
140 activeAtomArray = new Atom[count];
141 count = 0;
142 for (Atom a : atomArray) {
143 if (a.isActive()) {
144 activeAtomArray[count++] = a;
145 }
146 }
147
148 x = new double[n];
149 grad = new double[n];
150 scaling = new double[n];
151
152 refinementEnergy.getCoordinates(x);
153 refinementEnergy.setScaling(scaling);
154
155 double xyzscale = 1.0;
156 double bisoscale = 1.0;
157 double anisouscale = 50.0;
158 double occscale = 15.0;
159
160 if (refinementMode == RefinementMode.COORDINATES_AND_BFACTORS
161 || refinementMode == RefinementMode.COORDINATES_AND_BFACTORS_AND_OCCUPANCIES) {
162 bisoscale = 0.4;
163 anisouscale = 40.0;
164 }
165
166 if (refinementMode == RefinementMode.COORDINATES_AND_OCCUPANCIES
167 || refinementMode == RefinementMode.COORDINATES_AND_BFACTORS_AND_OCCUPANCIES) {
168 occscale = 10.0;
169 }
170
171
172 if (refinementMode == RefinementMode.COORDINATES
173 || refinementMode == RefinementMode.COORDINATES_AND_BFACTORS
174 || refinementMode == RefinementMode.COORDINATES_AND_OCCUPANCIES
175 || refinementMode == RefinementMode.COORDINATES_AND_BFACTORS_AND_OCCUPANCIES) {
176 for (int i = 0; i < n; i++) {
177 scaling[i] = xyzscale;
178 }
179 }
180
181 if (refinementMode == RefinementMode.BFACTORS
182 || refinementMode == RefinementMode.BFACTORS_AND_OCCUPANCIES
183 || refinementMode == RefinementMode.COORDINATES_AND_BFACTORS
184 || refinementMode == RefinementMode.COORDINATES_AND_BFACTORS_AND_OCCUPANCIES) {
185 int i = nXYZ;
186 int resnum = -1;
187 if (data instanceof DiffractionData) {
188 DiffractionData diffractionData = (DiffractionData) data;
189 int nres = diffractionData.getnResidueBFactor() + 1;
190 for (Atom a : activeAtomArray) {
191
192 if (a.getAtomicNumber() == 1) {
193 continue;
194 }
195 if (a.getAnisou(null) != null) {
196 for (int j = 0; j < 6; j++) {
197 scaling[i + j] = anisouscale;
198 }
199 i += 6;
200 } else if (diffractionData.isResidueBFactor()) {
201 if (resnum != a.getResidueNumber()) {
202 if (nres >= diffractionData.getnResidueBFactor()) {
203 if (resnum > -1 && i < nXYZ + nB - 1) {
204 i++;
205 }
206 if (i < nXYZ + nB) {
207 scaling[i] = bisoscale;
208 }
209 nres = 1;
210 } else {
211 nres++;
212 }
213 resnum = a.getResidueNumber();
214 }
215 } else {
216 scaling[i] = bisoscale;
217 i++;
218 }
219 }
220 } else {
221 logger.severe(" B refinement not supported for this data type!");
222 }
223 }
224
225 if (refinementMode == RefinementMode.OCCUPANCIES
226 || refinementMode == RefinementMode.BFACTORS_AND_OCCUPANCIES
227 || refinementMode == RefinementMode.COORDINATES_AND_OCCUPANCIES
228 || refinementMode == RefinementMode.COORDINATES_AND_BFACTORS_AND_OCCUPANCIES) {
229 if (nAtoms != nActive) {
230 logger.severe(" Occupancy refinement is not supported for inactive atoms.");
231 }
232 if (data instanceof DiffractionData) {
233
234 int i = nXYZ + nB;
235 for (List<Residue> list : refinementModel.getAltResidues()) {
236 for (int j = 0; j < list.size(); j++) {
237 scaling[i] = occscale;
238 i++;
239 }
240 }
241 for (List<Molecule> list : refinementModel.getAltMolecules()) {
242 for (int j = 0; j < list.size(); j++) {
243 scaling[i] = occscale;
244 i++;
245 }
246 }
247 } else {
248 logger.severe(" Occupancy refinement not supported for this data type!");
249 }
250 }
251 }
252
253
254
255
256
257
258
259 public static RefinementMode parseMode(String str) {
260 try {
261 return RefinementMode.valueOf(str.toUpperCase());
262 } catch (Exception e) {
263 logger.info(
264 format(
265 " Could not parse %s as a refinement mode; defaulting to coordinates.", str));
266 return RefinementMode.COORDINATES;
267 }
268 }
269
270
271
272
273
274
275 public Double getEps() {
276 boolean hasaniso = false;
277
278 for (Atom a : activeAtomArray) {
279
280 if (a.getAtomicNumber() == 1) {
281 continue;
282 }
283 if (a.getAnisou(null) != null) {
284 hasaniso = true;
285 break;
286 }
287 }
288
289 switch (refinementMode) {
290 case COORDINATES:
291 eps = 0.4;
292 break;
293 case BFACTORS, BFACTORS_AND_OCCUPANCIES:
294 if (hasaniso) {
295 eps = 20.0;
296 } else {
297 eps = 0.01;
298 }
299 break;
300 case COORDINATES_AND_BFACTORS, COORDINATES_AND_BFACTORS_AND_OCCUPANCIES:
301 if (hasaniso) {
302 eps = 20.0;
303 } else {
304 eps = 0.2;
305 }
306 break;
307 case OCCUPANCIES:
308 eps = 0.1;
309 break;
310 case COORDINATES_AND_OCCUPANCIES:
311 eps = 0.2;
312 break;
313 }
314
315 return eps;
316 }
317
318
319
320
321
322
323 public int getNB() {
324 return nB;
325 }
326
327
328
329
330
331
332 public int getNOcc() {
333 return nOcc;
334 }
335
336
337
338
339
340
341 public int getNXYZ() {
342 return nXYZ;
343 }
344
345
346
347
348
349
350 public RefinementEnergy minimize() {
351 return minimize(1.0);
352 }
353
354
355
356
357
358
359
360 public RefinementEnergy minimize(double eps) {
361 return minimize(7, eps, Integer.MAX_VALUE - 2);
362 }
363
364
365
366
367
368
369
370 public RefinementEnergy minimize(int maxiter) {
371 return minimize(7, 1.0, maxiter);
372 }
373
374
375
376
377
378
379
380
381 public RefinementEnergy minimize(double eps, int maxiter) {
382 return minimize(7, eps, maxiter);
383 }
384
385
386
387
388
389
390
391
392
393 public RefinementEnergy minimize(int m, double eps, int maxiter) {
394 String typestring;
395 if (dataContainer instanceof DiffractionData) {
396 typestring = "X-ray";
397 } else if (dataContainer instanceof RealSpaceData) {
398 typestring = "Real Space";
399 } else {
400 typestring = "null";
401 }
402
403 logger.info(" Beginning " + typestring + " Refinement");
404 switch (refinementMode) {
405 case COORDINATES:
406 logger.info(" Mode: Coordinates");
407 break;
408 case BFACTORS:
409 logger.info(" Mode: B-Factors");
410 break;
411 case COORDINATES_AND_BFACTORS:
412 logger.info(" Mode: Coordinates and B-Factors");
413 break;
414 case OCCUPANCIES:
415 logger.info(" Mode: Occupancies");
416 break;
417 case COORDINATES_AND_OCCUPANCIES:
418 logger.info(" Mode: Coordinates and Occupancies");
419 break;
420 case BFACTORS_AND_OCCUPANCIES:
421 logger.info(" Mode: B-Factors and Occupancies");
422 break;
423 case COORDINATES_AND_BFACTORS_AND_OCCUPANCIES:
424 logger.info(" Mode: Coordinates, B-Factors and Occupancies");
425 break;
426 }
427 logger.info(" Number of Parameters: " + n);
428
429 refinementEnergy.getCoordinates(x);
430
431
432 for (int i = 0; i < n; i++) {
433 x[i] *= scaling[i];
434 }
435
436 long mtime = -System.nanoTime();
437 time = -System.nanoTime();
438 done = false;
439 int status;
440 double e = refinementEnergy.energyAndGradient(x, grad);
441 status = LBFGS.minimize(n, m, x, e, grad, eps, maxiter, refinementEnergy, this);
442 done = true;
443 switch (status) {
444 case 0:
445 logger.info(format("\n Optimization achieved convergence criteria: %8.5f", grms));
446 break;
447 case 1:
448 logger.info(format("\n Optimization terminated at step %d.", nSteps));
449 break;
450 default:
451 logger.warning("\n Optimization failed.");
452 }
453
454 if (logger.isLoggable(Level.INFO)) {
455 StringBuilder sb = new StringBuilder();
456 mtime += System.nanoTime();
457 sb.append(format(" Optimization time: %g (sec)", mtime * toSeconds));
458 logger.info(sb.toString());
459 }
460
461 return refinementEnergy;
462 }
463
464
465 @Override
466 public boolean optimizationUpdate(
467 int iter,
468 int nBFGS,
469 int nfun,
470 double grms,
471 double xrms,
472 double f,
473 double df,
474 double angle,
475 LineSearchResult info) {
476 long currentTime = System.nanoTime();
477 Double seconds = (currentTime - time) * 1.0e-9;
478 time = currentTime;
479 this.grms = grms;
480 this.nSteps = iter;
481
482
483 if (listener != null) {
484 MolecularAssembly[] molecularAssembly = dataContainer.getMolecularAssemblies();
485 for (MolecularAssembly ma : molecularAssembly) {
486 listener.algorithmUpdate(ma);
487 }
488 }
489
490 if (iter == 0) {
491 if (nBFGS > 0) {
492 logger.info("\n Limited Memory BFGS Quasi-Newton Optimization: \n");
493 } else {
494 logger.info("\n Steepest Decent Optimization: \n");
495 }
496 logger.info(
497 " Cycle Energy G RMS Delta E Delta X Angle Evals Time "
498 + dataContainer.printOptimizationHeader());
499 }
500 if (info == null) {
501 logger.info(format("%6d %12.3f %10.3f", iter, f, grms));
502 } else if (info == LineSearchResult.Success) {
503 StringBuilder sb = new StringBuilder();
504 sb.append(format("%6d %12.3f %10.3f %10.3f %9.4f %8.2f %6d %8.3f ",
505 iter, f, grms, df, xrms, angle, nfun, seconds));
506 sb.append(dataContainer.printOptimizationUpdate());
507 logger.info(sb.toString());
508 } else {
509 logger.info(
510 format("%6d %12.3f %10.3f %10.3f %9.4f %8.2f %6d %8s",
511 iter, f, grms, df, xrms, angle, nfun, info));
512 }
513 if (terminate) {
514 logger.info(" The optimization recieved a termination request.");
515
516 return false;
517 }
518 return true;
519 }
520
521
522 @Override
523 public void terminate() {
524 terminate = true;
525 while (!done) {
526 synchronized (this) {
527 try {
528 wait(1);
529 } catch (Exception e) {
530 logger.log(Level.WARNING, " Exception terminating minimization.\n", e);
531 }
532 }
533 }
534 }
535
536
537 public enum RefinementMode {
538
539
540 COORDINATES,
541
542 BFACTORS,
543
544 COORDINATES_AND_BFACTORS,
545
546 OCCUPANCIES,
547
548 BFACTORS_AND_OCCUPANCIES,
549
550 COORDINATES_AND_OCCUPANCIES,
551
552 COORDINATES_AND_BFACTORS_AND_OCCUPANCIES
553 }
554 }