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 ffx.crystal.Crystal;
41 import ffx.crystal.HKL;
42 import ffx.crystal.ReflectionList;
43 import ffx.crystal.ReflectionSpline;
44 import ffx.numerics.OptimizationInterface;
45 import ffx.numerics.math.ComplexNumber;
46
47 import java.util.logging.Logger;
48
49 import static java.lang.Double.isNaN;
50 import static java.lang.String.format;
51 import static java.util.Arrays.fill;
52 import static org.apache.commons.math3.util.FastMath.abs;
53 import static org.apache.commons.math3.util.FastMath.pow;
54 import static org.apache.commons.math3.util.FastMath.sqrt;
55
56
57
58
59
60
61
62
63
64 public class SplineEnergy implements OptimizationInterface {
65
66 private static final Logger logger = Logger.getLogger(SplineEnergy.class.getName());
67 private final ReflectionList reflectionList;
68 private final ReflectionSpline spline;
69 private final int nParams;
70 private final SplineType type;
71 private final Crystal crystal;
72 private final DiffractionRefinementData refinementData;
73 private final double[][] fc;
74 private final double[][] fo;
75 private final ComplexNumber fct = new ComplexNumber();
76 private double[] optimizationScaling = null;
77 private double totalEnergy;
78
79
80
81
82
83
84
85
86
87 SplineEnergy(
88 ReflectionList reflectionList,
89 DiffractionRefinementData refinementData,
90 int nParams,
91 SplineType type) {
92 this.reflectionList = reflectionList;
93 this.crystal = reflectionList.crystal;
94 this.refinementData = refinementData;
95 this.type = type;
96 this.fc = refinementData.fc;
97 this.fo = refinementData.fSigF;
98
99
100 this.spline = new ReflectionSpline(reflectionList, nParams);
101 this.nParams = nParams;
102 }
103
104
105
106
107 @Override
108 public boolean destroy() {
109
110 return true;
111 }
112
113
114
115
116 @Override
117 public double energy(double[] x) {
118 unscaleCoordinates(x);
119 double sum = target(x, null, false, false);
120 scaleCoordinates(x);
121 return sum;
122 }
123
124
125
126
127 @Override
128 public double energyAndGradient(double[] x, double[] g) {
129 unscaleCoordinates(x);
130 double sum = target(x, g, true, false);
131 scaleCoordinatesAndGradient(x, g);
132 return sum;
133 }
134
135
136
137
138 @Override
139 public double[] getCoordinates(double[] parameters) {
140 throw new UnsupportedOperationException("Not supported yet.");
141 }
142
143
144
145
146 @Override
147 public void setCoordinates(double[] parameters) {
148 throw new UnsupportedOperationException("Not supported yet.");
149 }
150
151
152
153
154 @Override
155 public int getNumberOfVariables() {
156 throw new UnsupportedOperationException("Not supported yet.");
157 }
158
159
160
161
162 @Override
163 public double[] getScaling() {
164 return optimizationScaling;
165 }
166
167
168
169
170 @Override
171 public void setScaling(double[] scaling) {
172 if (scaling != null && scaling.length == nParams) {
173 optimizationScaling = scaling;
174 } else {
175 optimizationScaling = null;
176 }
177 }
178
179
180
181
182 @Override
183 public double getTotalEnergy() {
184 return totalEnergy;
185 }
186
187
188
189
190
191
192
193
194
195
196 public double target(double[] x, double[] g, boolean gradient, boolean print) {
197
198 double r = 0.0;
199 double rf = 0.0;
200 double rfree = 0.0;
201 double rfreef = 0.0;
202 double sum = 0.0;
203 double sumfo = 1.0;
204
205
206 if (gradient) {
207 fill(g, 0.0);
208 }
209
210 for (HKL ih : reflectionList.hklList) {
211 int i = ih.getIndex();
212 if (isNaN(fc[i][0]) || isNaN(fo[i][0]) || fo[i][1] <= 0.0) {
213 continue;
214 }
215
216 if (type == SplineType.FOTOESQ && fo[i][0] <= 0.0) {
217 continue;
218 }
219
220 double eps = ih.getEpsilon();
221 double s = crystal.invressq(ih);
222
223 double fh = spline.f(s, x);
224 refinementData.getFcTotIP(i, fct);
225
226 double d2 = 0.0;
227 double dr = 0.0;
228 double w = 0.0;
229 switch (type) {
230 case SplineType.FOFC:
231 w = 1.0;
232 double f1 = refinementData.getF(i);
233 double f2 = fct.abs();
234 double d = f1 - fh * f2;
235 d2 = d * d;
236 dr = -2.0 * f2 * d;
237 sumfo += f1 * f1;
238 break;
239 case SplineType.F1F2:
240 w = 2.0 / ih.epsilonc();
241 double ieps = 1.0 / eps;
242 f1 = pow(fct.abs(), 2.0) * ieps;
243 f2 = pow(refinementData.getF(i), 2) * ieps;
244 d = fh * f1 - f2;
245 d2 = d * d / f1;
246 dr = 2.0 * d;
247 break;
248 case SplineType.FCTOESQ:
249 w = 2.0 / ih.epsilonc();
250 f1 = pow(fct.abs() / sqrt(eps), 2);
251 d = f1 * fh - 1.0;
252 d2 = d * d / f1;
253 dr = 2.0 * d;
254 break;
255 case SplineType.FOTOESQ:
256 w = 2.0 / ih.epsilonc();
257 f1 = pow(refinementData.getF(i) / sqrt(eps), 2);
258 d = f1 * fh - 1.0;
259 d2 = d * d / f1;
260 dr = 2.0 * d;
261 break;
262 }
263
264 sum += w * d2;
265
266 double afo = abs(fo[i][0]);
267 double afh = abs(fh * fct.abs());
268 if (refinementData.isFreeR(i)) {
269 rfree += abs(afo - afh);
270 rfreef += afo;
271 } else {
272 r += abs(afo - afh);
273 rf += afo;
274 }
275
276 if (gradient) {
277 int i0 = spline.i0();
278 int i1 = spline.i1();
279 int i2 = spline.i2();
280 double g0 = spline.dfi0();
281 double g1 = spline.dfi1();
282 double g2 = spline.dfi2();
283
284 g[i0] += w * dr * g0;
285 g[i1] += w * dr * g1;
286 g[i2] += w * dr * g2;
287 }
288 }
289
290 if (gradient && type == SplineType.FOFC) {
291 double isumfo = 1.0 / sumfo;
292 for (int i = 0; i < g.length; i++) {
293 g[i] *= isumfo;
294 }
295 }
296
297 if (print) {
298 StringBuilder sb = new StringBuilder("\n");
299 sb.append(" Computed Potential Energy\n");
300 sb.append(format(" residual: %8.3f\n", sum / sumfo));
301 if (type == SplineType.FOFC || type == SplineType.F1F2) {
302 sb.append(
303 format(
304 " R: %8.3f Rfree: %8.3f\n", (r / rf) * 100.0, (rfree / rfreef) * 100.0));
305 }
306 sb.append("x: ");
307 for (double x1 : x) {
308 sb.append(format("%8g ", x1));
309 }
310 sb.append("\ng: ");
311 for (double v : g) {
312 sb.append(format("%8g ", v));
313 }
314 sb.append("\n");
315 logger.info(sb.toString());
316 }
317
318 totalEnergy = sum / sumfo;
319 return sum / sumfo;
320 }
321
322
323
324
325
326
327
328
329
330
331
332
333 public enum SplineType {
334 FOFC,
335 F1F2,
336 FCTOESQ,
337 FOTOESQ;
338
339 public String toString() {
340 return switch (this) {
341 case FOFC -> "Fo to Fc";
342 case F1F2 -> "F1 to F2";
343 case FCTOESQ -> "Fc to Esq";
344 case FOTOESQ -> "Fo to Esq";
345 };
346 }
347 }
348 }