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.Double.isNaN;
41 import static java.lang.String.format;
42 import static java.lang.System.arraycopy;
43 import static java.util.Arrays.fill;
44 import static org.apache.commons.math3.util.FastMath.log;
45
46 import edu.rit.pj.ParallelTeam;
47 import ffx.algorithms.Terminatable;
48 import ffx.crystal.Crystal;
49 import ffx.crystal.HKL;
50 import ffx.crystal.ReflectionList;
51 import ffx.numerics.optimization.LBFGS;
52 import ffx.numerics.optimization.LineSearch.LineSearchResult;
53 import ffx.numerics.optimization.OptimizationListener;
54 import ffx.xray.CrystalReciprocalSpace.SolventModel;
55 import java.util.logging.Level;
56 import java.util.logging.Logger;
57
58
59
60
61
62
63
64 public class ScaleBulkMinimize implements OptimizationListener, Terminatable {
65
66 private static final Logger logger = Logger.getLogger(ScaleBulkMinimize.class.getName());
67 private static final double toSeconds = 1.0e-9;
68 private final ReflectionList reflectionlist;
69 private final DiffractionRefinementData refinementData;
70 private final Crystal crystal;
71 private final CrystalReciprocalSpace crystalReciprocalSpace;
72 private final ScaleBulkEnergy bulkSolventEnergy;
73 private final int solventN;
74 private final int n;
75 private final double[] x;
76 private final double[] grad;
77 private final double[] scaling;
78 private boolean done = false;
79 private boolean terminate = false;
80 private long time;
81 private double grms;
82 private int nSteps;
83
84
85
86
87
88
89
90
91
92 public ScaleBulkMinimize(
93 ReflectionList reflectionList,
94 DiffractionRefinementData refinementData,
95 CrystalReciprocalSpace crystalReciprocalSpace,
96 ParallelTeam parallelTeam) {
97 this.reflectionlist = reflectionList;
98 this.refinementData = refinementData;
99 this.crystal = reflectionList.crystal;
100 this.crystalReciprocalSpace = crystalReciprocalSpace;
101
102 if (crystalReciprocalSpace.solventModel == SolventModel.NONE) {
103 solventN = 1;
104 } else {
105 solventN = 3;
106 }
107 n = solventN + refinementData.nScale;
108 bulkSolventEnergy = new ScaleBulkEnergy(reflectionList, refinementData, n, parallelTeam);
109
110 x = new double[n];
111 grad = new double[n];
112 scaling = new double[n];
113 fill(scaling, 1.0);
114
115 x[0] = refinementData.modelScaleK;
116 if (solventN > 1) {
117 x[1] = refinementData.bulkSolventK;
118 x[2] = refinementData.bulkSolventUeq;
119 }
120 for (int i = 0; i < 6; i++) {
121 if (crystal.scaleB[i] >= 0) {
122 x[solventN + crystal.scaleB[i]] = refinementData.modelAnisoB[i];
123 }
124 }
125
126 setInitialScale();
127 }
128
129
130
131
132
133
134
135 public double[] getCoordinates(double[] x) {
136 if (x == null) {
137 x = new double[this.x.length];
138 }
139 arraycopy(this.x, 0, x, 0, this.x.length);
140 return x;
141 }
142
143
144
145
146
147
148 public int getNumberOfVariables() {
149 return x.length;
150 }
151
152
153 public void ksbsGridOptimize() {
154 if (solventN < 3) {
155 return;
156 }
157
158 bulkSolventEnergy.setScaling(scaling);
159
160 double min = Double.POSITIVE_INFINITY;
161 double bulkSolventK = refinementData.bulkSolventK;
162 double bulkSolventUeq = refinementData.bulkSolventUeq;
163 double kmin = 0.05;
164 double kmax = 0.9;
165 double kstep = 0.05;
166 double bmin = 10.0;
167 double bmax = 200.0;
168 double bstep = 10.0;
169 for (double i = kmin; i <= kmax; i += kstep) {
170 for (double j = bmin; j <= bmax; j += bstep) {
171 x[1] = i;
172 x[2] = j;
173 double sum = bulkSolventEnergy.energyAndGradient(x, grad);
174 logger.info(" ks: " + i + " bs: " + j + " sum: " + sum);
175 if (sum < min) {
176 min = sum;
177 bulkSolventK = i;
178 bulkSolventUeq = j;
179 }
180 }
181 }
182 logger.info(" minks: " + bulkSolventK + " minbs: " + bulkSolventUeq + " min: " + min);
183 refinementData.bulkSolventK = bulkSolventK;
184 refinementData.bulkSolventUeq = bulkSolventUeq;
185 bulkSolventEnergy.setScaling(null);
186 }
187
188
189
190
191
192
193 public ScaleBulkEnergy minimize() {
194 return minimize(0.5);
195 }
196
197
198
199
200
201
202
203 public ScaleBulkEnergy minimize(double eps) {
204 return minimize(5, eps);
205 }
206
207
208
209
210
211
212
213
214 public ScaleBulkEnergy minimize(int m, double eps) {
215 bulkSolventEnergy.setScaling(scaling);
216 double e = bulkSolventEnergy.energyAndGradient(x, grad);
217
218 long mtime = -System.nanoTime();
219 time = -System.nanoTime();
220 done = false;
221 int status = LBFGS.minimize(n, m, x, e, grad, eps, bulkSolventEnergy, this);
222 done = true;
223 switch (status) {
224 case 0:
225 logger.info(format("\n Optimization achieved convergence criteria: %10.5f\n", grms));
226 break;
227 case 1:
228 logger.info(format("\n Optimization terminated at step %d.\n", nSteps));
229 break;
230 default:
231 logger.warning("\n Optimization failed.\n");
232 }
233 refinementData.modelScaleK = x[0] / scaling[0];
234 if (solventN > 1) {
235 refinementData.bulkSolventK = x[1] / scaling[1];
236 refinementData.bulkSolventUeq = x[2] / scaling[2];
237 if (crystalReciprocalSpace != null) {
238 refinementData.solventA = crystalReciprocalSpace.solventA;
239 refinementData.solventB = crystalReciprocalSpace.solventB;
240 }
241 }
242 for (int i = 0; i < 6; i++) {
243 int offset = crystal.scaleB[i];
244 if (offset >= 0) {
245 int index = solventN + offset;
246 refinementData.modelAnisoB[i] = x[index] / scaling[index];
247 }
248 }
249
250 if (logger.isLoggable(Level.INFO)) {
251 mtime += System.nanoTime();
252 logger.info(format(" Optimization time: %8.3f (sec)\n", mtime * toSeconds));
253 }
254
255 bulkSolventEnergy.setScaling(null);
256
257 return bulkSolventEnergy;
258 }
259
260
261 @Override
262 public boolean optimizationUpdate(
263 int iter,
264 int nBFGS,
265 int nfun,
266 double grms,
267 double xrms,
268 double f,
269 double df,
270 double angle,
271 LineSearchResult info) {
272 long currentTime = System.nanoTime();
273 Double seconds = (currentTime - time) * 1.0e-9;
274 time = currentTime;
275 this.grms = grms;
276 this.nSteps = iter;
277
278 if (iter == 0) {
279 if (nBFGS > 0) {
280 logger.info("\n Limited Memory BFGS Quasi-Newton Optimization of Fc to Fo Scaling Parameters\n");
281 } else {
282 logger.info("\n Steepest Decent Optimization of Fc to Fo Scaling Parameters\n");
283 }
284 logger.info(" Cycle Energy G RMS Delta E Delta X Angle Evals Time");
285 }
286 if (info == null) {
287 logger.info(format("%6d %12.5f %10.6f", iter, f, grms));
288 } else {
289 if (info == LineSearchResult.Success) {
290 logger.info(format("%6d %12.5f %10.6f %10.6f %9.5f %8.2f %6d %8.3f",
291 iter, f, grms, df, xrms, angle, nfun, seconds));
292 } else {
293 logger.info(format("%6d %12.5f %10.6f %10.6f %9.5f %8.2f %6d %8s",
294 iter, f, grms, df, xrms, angle, nfun, info));
295 }
296 }
297 if (terminate) {
298 logger.info(" The optimization recieved a termination request.");
299
300 return false;
301 }
302 return true;
303 }
304
305
306 @Override
307 public void terminate() {
308 terminate = true;
309 while (!done) {
310 synchronized (this) {
311 try {
312 wait(1);
313 } catch (Exception e) {
314 logger.log(Level.WARNING, "Exception terminating minimization.\n", e);
315 }
316 }
317 }
318 }
319
320
321
322
323
324
325 ScaleBulkEnergy getScaleBulkEnergy() {
326 return bulkSolventEnergy;
327 }
328
329 private void setInitialScale() {
330 double[][] fc = refinementData.fc;
331 double[][] fSigf = refinementData.fSigF;
332
333 bulkSolventEnergy.setScaling(scaling);
334 double e = bulkSolventEnergy.energyAndGradient(x, grad);
335 bulkSolventEnergy.setScaling(null);
336
337 double sumfofc = 0.0;
338 double sumfc = 0.0;
339 for (HKL ih : reflectionlist.hklList) {
340 int i = ih.getIndex();
341 if (isNaN(fc[i][0]) || isNaN(fSigf[i][0]) || fSigf[i][1] <= 0.0) {
342 continue;
343 }
344
345 double fcTotF = refinementData.fcTotF(i);
346 sumfofc += fSigf[i][0] * fcTotF;
347 sumfc += fcTotF * fcTotF;
348 }
349
350 x[0] = log(4.0 * sumfofc / sumfc);
351 }
352
353
354 void gridOptimize() {
355 if (crystalReciprocalSpace == null) {
356 return;
357 }
358
359 bulkSolventEnergy.setScaling(scaling);
360
361 double min = Double.POSITIVE_INFINITY;
362 double solventA = crystalReciprocalSpace.solventA;
363 double solventB = crystalReciprocalSpace.solventB;
364 double amin = solventA - 1.0;
365 double amax = (solventA + 1.0) / 0.9999;
366 double astep = 0.25;
367 double bmin = solventB - 0.2;
368 double bmax = (solventB + 0.2) / 0.9999;
369 double bstep = 0.05;
370 if (crystalReciprocalSpace.solventModel == SolventModel.BINARY) {
371 amin = solventA - 0.2;
372 amax = (solventA + 0.2) / 0.9999;
373 astep = 0.05;
374 }
375
376 logger.info(" Bulk Solvent Grid Search");
377 for (double i = amin; i <= amax; i += astep) {
378 for (double j = bmin; j <= bmax; j += bstep) {
379 crystalReciprocalSpace.setSolventAB(i, j);
380 crystalReciprocalSpace.computeDensity(refinementData.fs);
381 double sum = bulkSolventEnergy.energy(x);
382 logger.info(format(" A: %6.3f B: %6.3f Sum: %12.8f", i, j, sum));
383 if (sum < min) {
384 min = sum;
385 solventA = i;
386 solventB = j;
387 }
388 }
389 }
390
391 logger.info(format("\n Minimum at\n A: %6.3f B: %6.3f Sum: %12.8f", solventA, solventB, min));
392 crystalReciprocalSpace.setSolventAB(solventA, solventB);
393 refinementData.solventA = solventA;
394 refinementData.solventB = solventB;
395 crystalReciprocalSpace.computeDensity(refinementData.fs);
396 bulkSolventEnergy.setScaling(null);
397 }
398 }