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.scatter;
39
40 import ffx.crystal.HKL;
41 import ffx.potential.bonded.Atom;
42 import ffx.xray.refine.RefinementMode;
43
44 import java.util.logging.Level;
45 import java.util.logging.Logger;
46
47 import static ffx.numerics.math.DoubleMath.length2;
48 import static ffx.numerics.math.DoubleMath.sub;
49 import static ffx.numerics.math.MatrixMath.mat3Determinant;
50 import static ffx.numerics.math.MatrixMath.mat3Inverse;
51 import static ffx.numerics.math.MatrixMath.mat3Mat3Multiply;
52 import static ffx.numerics.math.MatrixMath.vec3Mat3;
53 import static ffx.numerics.math.ScalarMath.b2u;
54 import static ffx.numerics.math.ScalarMath.quadForm;
55 import static ffx.numerics.math.ScalarMath.u2b;
56 import static ffx.xray.scatter.XRayScatteringParameters.getFormFactor;
57 import static java.lang.String.format;
58 import static org.apache.commons.math3.util.FastMath.exp;
59 import static org.apache.commons.math3.util.FastMath.pow;
60 import static org.apache.commons.math3.util.FastMath.sqrt;
61
62
63
64
65
66
67
68
69
70
71
72
73
74 public final class XRayFormFactor implements FormFactor {
75
76 private static final Logger logger = Logger.getLogger(XRayFormFactor.class.getName());
77
78 private final Atom atom;
79 private final double[] xyz = new double[3];
80 private final double[] dxyz = new double[3];
81 private final double[] resv = new double[3];
82 private final double[] a = new double[6];
83 private final double[] b = new double[6];
84 private final double[] ainv = new double[6];
85 private final double[] binv = new double[6];
86 private final double[] gradu = new double[6];
87 private final double[][][] u = new double[6][3][3];
88 private final double[][][] uInv = new double[6][3][3];
89 private final double[][] jmat = new double[3][3];
90 private final int nGaussians;
91 private boolean hasAnisou;
92 private double[] anisou = null;
93 private double uAdd;
94 private double occupancy;
95
96
97
98
99
100
101
102
103 public XRayFormFactor(Atom atom, boolean use3G, double badd) {
104 this(atom, use3G, badd, atom.getXYZ(null));
105 }
106
107
108
109
110
111
112
113
114
115 public XRayFormFactor(Atom atom, boolean use3G, double badd, double[] xyz) {
116 this.atom = atom;
117 this.uAdd = b2u(badd);
118
119
120 int charge = 0;
121 if (atom.getMultipoleType() != null) {
122 charge = (int) atom.getMultipoleType().getCharge();
123 }
124
125 XRayScatteringParameters parameters = getFormFactor(atom.getAtomicNumber(), charge, use3G);
126 double[][] formFactor = parameters.formFactor();
127
128 int i;
129 for (i = 0; i < formFactor[1].length; i++) {
130 if (formFactor[1][i] < 0.01) {
131 break;
132 }
133 a[i] = formFactor[1][i];
134 b[i] = formFactor[2][i];
135 }
136 nGaussians = i;
137 assert (nGaussians > 0);
138 occupancy = atom.getOccupancy();
139
140 if (occupancy <= 0.0 && logger.isLoggable(Level.FINE)) {
141 logger.log(Level.FINE, " Zero occupancy for atom: {0}", atom.toString());
142 }
143
144 update(xyz, uAdd);
145 }
146
147
148
149
150
151
152
153 public double f(HKL hkl) {
154 return fN(hkl, nGaussians);
155 }
156
157
158
159
160
161
162
163
164 public double fN(HKL hkl, int nGaussians) {
165 double sum = 0.0;
166
167 for (int i = 0; i < nGaussians; i++) {
168 sum += a[i] * exp(-twoPI2 * hkl.quadForm(u[i]));
169 }
170 return occupancy * sum;
171 }
172
173
174
175
176 @Override
177 public double rho(double f, double lambda, double[] xyz) {
178 return rhoN(f, lambda, xyz, nGaussians);
179 }
180
181
182
183
184 @Override
185 public void rhoGrad(double[] xyz, double dfc, RefinementMode refinementMode) {
186 rhoGradN(xyz, nGaussians, dfc, refinementMode);
187 }
188
189
190
191
192 @Override
193 public void update(double[] xyz) {
194 update(xyz, u2b(uAdd));
195 }
196
197
198
199
200 @Override
201 public void update(double[] xyz, double bAdd) {
202 this.xyz[0] = xyz[0];
203 this.xyz[1] = xyz[1];
204 this.xyz[2] = xyz[2];
205 uAdd = b2u(bAdd);
206 occupancy = atom.getOccupancy();
207 double bIso = atom.getTempFactor();
208
209
210 if (occupancy < 0.0) {
211 logger.warning(format(" %s negative occupancy reset to 0.0.", atom));
212 occupancy = 0.0;
213 atom.setOccupancy(0.0);
214 }
215
216
217 if (atom.getAnisou(null) == null) {
218 if (anisou == null) {
219 anisou = new double[6];
220 }
221 hasAnisou = false;
222 } else {
223 hasAnisou = true;
224 }
225
226 if (hasAnisou) {
227
228 anisou = atom.getAnisou(null);
229 double det = mat3Determinant(anisou);
230
231 if (det <= 1e-14) {
232 String message = format(" %s non-positive definite ANISOU\n Reset to isotropic B: %6.2f", atom, bIso);
233 logger.warning(message);
234
235 double uIso = b2u(bIso);
236 anisou[0] = uIso;
237 anisou[1] = uIso;
238 anisou[2] = uIso;
239 anisou[3] = 0.0;
240 anisou[4] = 0.0;
241 anisou[5] = 0.0;
242 atom.setAnisou(anisou);
243 }
244 } else {
245 if (bIso < 0.0) {
246 StringBuilder sb = new StringBuilder();
247 sb.append(" Negative B factor for atom: ").append(atom);
248 sb.append("\n Resetting B to 5.0\n");
249 logger.warning(sb.toString());
250 bIso = 5.0;
251 atom.setTempFactor(5.0);
252 }
253 double uIso = b2u(bIso);
254 anisou[0] = uIso;
255 anisou[1] = uIso;
256 anisou[2] = uIso;
257 anisou[3] = 0.0;
258 anisou[4] = 0.0;
259 anisou[5] = 0.0;
260 }
261
262 for (int i = 0; i < nGaussians; i++) {
263 double uIso = b2u(b[i]);
264 u[i][0][0] = anisou[0] + uIso + uAdd;
265 u[i][1][1] = anisou[1] + uIso + uAdd;
266 u[i][2][2] = anisou[2] + uIso + uAdd;
267 u[i][0][1] = anisou[3];
268 u[i][1][0] = anisou[3];
269 u[i][0][2] = anisou[4];
270 u[i][2][0] = anisou[4];
271 u[i][1][2] = anisou[5];
272 u[i][2][1] = anisou[5];
273 mat3Inverse(u[i], uInv[i]);
274 double det = mat3Determinant(u[i]);
275 ainv[i] = a[i] / sqrt(det);
276 det = mat3Determinant(uInv[i]);
277 binv[i] = pow(det, oneThird);
278 }
279 }
280
281
282
283
284
285
286
287
288
289
290 private double rhoN(double f, double lambda, double[] xyz, int nGaussians) {
291 assert (nGaussians > 0 && nGaussians <= this.nGaussians);
292 sub(this.xyz, xyz, xyz);
293
294
295 if (length2(xyz) > atom.getFormFactorWidth2()) {
296 return f;
297 }
298
299 double sum = 0.0;
300 for (int i = 0; i < nGaussians; i++) {
301 sum += ainv[i] * exp(-0.5 * quadForm(xyz, uInv[i]));
302 }
303 return f + (lambda * occupancy * inverseTwoPI32 * sum);
304 }
305
306
307
308
309
310
311
312
313
314 private void rhoGradN(double[] xyz, int nGaussians, double dfc, RefinementMode refinementMode) {
315 assert (nGaussians > 0 && nGaussians <= this.nGaussians);
316 sub(this.xyz, xyz, dxyz);
317 double r2 = length2(dxyz);
318
319
320 if (r2 > atom.getFormFactorWidth2()) {
321 return;
322 }
323
324 boolean refineXYZ = refinementMode.includesCoordinates();
325 boolean refineBFactor = refinementMode.includesBFactors();
326 boolean refineOccupancy = refinementMode.includesOccupancies();
327
328 for (int i = 0; i < nGaussians; i++) {
329 double aex = ainv[i] * exp(-0.5 * quadForm(dxyz, uInv[i]));
330
331 if (refineXYZ) {
332 vec3Mat3(dxyz, uInv[i], resv);
333 double scale = aex * dfc * occupancy * inverseTwoPI32;
334 double dex = -scale * resv[0];
335 double dey = -scale * resv[1];
336 double dez = -scale * resv[2];
337 atom.addToXYZGradient(dex, dey, dez);
338 }
339
340 if (refineOccupancy) {
341 atom.addToOccupancyGradient(dfc * inverseTwoPI32 * aex);
342 }
343
344 if (refineBFactor) {
345 double scale = 0.5 * aex * dfc * occupancy * inverseTwoPI32;
346 double deb = scale * (r2 * binv[i] * binv[i] - 3.0 * binv[i]);
347 atom.addToTempFactorGradient(b2u(deb));
348 if (hasAnisou) {
349
350 mat3Mat3Multiply(uInv[i], dUdU11, jmat);
351 mat3Mat3Multiply(jmat, uInv[i], jmat);
352 gradu[0] = scale * (quadForm(dxyz, jmat) - uInv[i][0][0]);
353
354 mat3Mat3Multiply(uInv[i], dUdU22, jmat);
355 mat3Mat3Multiply(jmat, uInv[i], jmat);
356 gradu[1] = scale * (quadForm(dxyz, jmat) - uInv[i][1][1]);
357
358 mat3Mat3Multiply(uInv[i], dUdU33, jmat);
359 mat3Mat3Multiply(jmat, uInv[i], jmat);
360 gradu[2] = scale * (quadForm(dxyz, jmat) - uInv[i][2][2]);
361
362 mat3Mat3Multiply(uInv[i], dUdU12, jmat);
363 mat3Mat3Multiply(jmat, uInv[i], jmat);
364 gradu[3] = scale * (quadForm(dxyz, jmat) - uInv[i][0][1] * 2.0);
365
366 mat3Mat3Multiply(uInv[i], dUdU13, jmat);
367 mat3Mat3Multiply(jmat, uInv[i], jmat);
368 gradu[4] = scale * (quadForm(dxyz, jmat) - uInv[i][0][2] * 2.0);
369
370 mat3Mat3Multiply(uInv[i], dUdU23, jmat);
371 mat3Mat3Multiply(jmat, uInv[i], jmat);
372 gradu[5] = scale * (quadForm(dxyz, jmat) - uInv[i][1][2] * 2.0);
373
374 atom.addToAnisouGradient(gradu);
375 }
376 }
377 }
378 }
379 }