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