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 ffx.numerics.math.DoubleMath.dot;
41 import static ffx.numerics.math.DoubleMath.length;
42 import static ffx.numerics.math.DoubleMath.sub;
43 import static ffx.numerics.math.MatrixMath.determinant3;
44 import static ffx.numerics.math.MatrixMath.mat3Inverse;
45 import static ffx.numerics.math.MatrixMath.mat3Mat3;
46 import static ffx.numerics.math.MatrixMath.scalarMat3Mat3;
47 import static ffx.numerics.math.MatrixMath.vec3Mat3;
48 import static ffx.numerics.math.ScalarMath.b2u;
49 import static ffx.numerics.math.ScalarMath.quadForm;
50 import static ffx.numerics.math.ScalarMath.u2b;
51 import static java.lang.System.arraycopy;
52 import static java.util.Arrays.fill;
53 import static org.apache.commons.math3.util.FastMath.PI;
54 import static org.apache.commons.math3.util.FastMath.exp;
55 import static org.apache.commons.math3.util.FastMath.pow;
56 import static org.apache.commons.math3.util.FastMath.sqrt;
57
58 import ffx.crystal.HKL;
59 import ffx.potential.bonded.Atom;
60 import ffx.xray.RefinementMinimize.RefinementMode;
61 import java.util.HashMap;
62 import java.util.logging.Logger;
63
64
65
66
67
68
69
70
71
72
73
74
75
76 public final class NeutronFormFactor implements FormFactor {
77
78 private static final Logger logger = Logger.getLogger(ffx.xray.NeutronFormFactor.class.getName());
79 private static final double twopi2 = 2.0 * PI * PI;
80 private static final double twopi32 = pow(2.0 * PI, -1.5);
81 private static final double[] vx = {1.0, 0.0, 0.0};
82 private static final double[] vy = {0.0, 1.0, 0.0};
83 private static final double[] vz = {0.0, 0.0, 1.0};
84 private static final double[][] u11 = {{1.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}};
85 private static final double[][] u22 = {{0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 0.0}};
86 private static final double[][] u33 = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 1.0}};
87 private static final double[][] u12 = {{0.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, 0.0}};
88 private static final double[][] u13 = {{0.0, 0.0, 1.0}, {0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}};
89 private static final double[][] u23 = {{0.0, 0.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 1.0, 0.0}};
90 private static final HashMap<String, double[][]> formfactors = new HashMap<>();
91 private static final String[] atoms = {
92 "H", "D", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S",
93 "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge",
94 "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Ru", "Rh", "Pd", "Ag", "Cd", "In",
95 "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Sm", "Eu", "Gd", "Tb", "Dy",
96 "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb",
97 "Bi", "Th", "U"
98 };
99 private static final String[] atomsi = {
100 "1_1", "1_2", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
101 "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32",
102 "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "44", "45", "46", "47", "48", "49",
103 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "62", "63", "64", "65", "66",
104 "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82",
105 "83", "90", "92"
106 };
107 private static final double[][][] ffactors = {
108 {{0}, {-3.7390, 0.0}},
109 {{1}, {6.671, 0.0}},
110 {{2}, {3.26, 0.0}},
111 {{3}, {-1.90, 0.0}},
112 {{4}, {7.79, 0.0}},
113 {{5}, {5.30, 0.213}},
114 {{6}, {6.6460, 0.0}},
115 {{7}, {9.36, 0.0}},
116 {{8}, {5.803, 0.0}},
117 {{9}, {5.654, 0.0}},
118 {{10}, {4.566, 0.0}},
119 {{11}, {3.63, 0.0}},
120 {{12}, {5.375, 0.0}},
121 {{13}, {3.449, 0.0}},
122 {{14}, {4.1491, 0.0}},
123 {{15}, {5.13, 0.0}},
124 {{16}, {2.847, 0.0}},
125 {{17}, {9.5770, 0.0}},
126 {{18}, {1.909, 0.0}},
127 {{19}, {3.67, 0.0}},
128 {{20}, {4.70, 0.0}},
129 {{21}, {12.29, 0.0}},
130 {{22}, {-3.370, 0.0}},
131 {{23}, {-0.3824, 0.0}},
132 {{24}, {3.635, 0.0}},
133 {{25}, {-3.750, 0.0}},
134 {{26}, {9.45, 0.0}},
135 {{27}, {2.49, 0.0}},
136 {{28}, {10.3, 0.0}},
137 {{29}, {7.718, 0.0}},
138 {{30}, {5.60, 0.0}},
139 {{31}, {7.288, 0.0}},
140 {{32}, {8.185, 0.0}},
141 {{33}, {6.58, 0.0}},
142 {{34}, {7.970, 0.0}},
143 {{35}, {6.795, 0.0}},
144 {{36}, {7.81, 0.0}},
145 {{37}, {7.09, 0.0}},
146 {{38}, {7.02, 0.0}},
147 {{39}, {7.75, 0.0}},
148 {{40}, {7.16, 0.0}},
149 {{41}, {7.054, 0.0}},
150 {{42}, {6.715, 0.0}},
151 {{43}, {7.03, 0.0}},
152 {{44}, {5.88, 0.0}},
153 {{45}, {5.91, 0.0}},
154 {{46}, {5.922, 0.0}},
155 {{47}, {4.87, -0.70}},
156 {{48}, {2.08, -0.0539}},
157 {{49}, {6.225, 0.0}},
158 {{50}, {5.57, 0.0}},
159 {{51}, {5.80, 0.0}},
160 {{52}, {5.28, 0.0}},
161 {{53}, {4.92, 0.0}},
162 {{54}, {5.42, 0.0}},
163 {{55}, {5.07, 0.0}},
164 {{56}, {8.24, 0.0}},
165 {{57}, {4.84, 0.0}},
166 {{58}, {4.58, 0.0}},
167 {{59}, {7.69, 0.0}},
168 {{60}, {0.80, -1.65}},
169 {{61}, {7.22, -1.26}},
170 {{62}, {6.5, -13.82}},
171 {{63}, {7.38, 0.0}},
172 {{64}, {16.9, -0.276}},
173 {{65}, {8.01, 0.0}},
174 {{66}, {7.79, 0.0}},
175 {{67}, {7.07, 0.0}},
176 {{68}, {12.43, 0.0}},
177 {{69}, {7.21, 0.0}},
178 {{70}, {7.77, 0.0}},
179 {{71}, {6.91, 0.0}},
180 {{72}, {4.86, 0.0}},
181 {{73}, {9.2, 0.0}},
182 {{74}, {10.7, 0.0}},
183 {{75}, {10.6, 0.0}},
184 {{76}, {9.60, 0.0}},
185 {{77}, {7.63, 0.0}},
186 {{78}, {12.692, 0.0}},
187 {{79}, {8.776, 0.0}},
188 {{80}, {9.405, 0.0}},
189 {{81}, {8.532, 0.0}},
190 {{82}, {10.31, 0.0}},
191 {{83}, {8.417, 0.0}}
192 };
193
194 static {
195 for (int i = 0; i < atoms.length; i++) {
196 formfactors.put(atomsi[i], ffactors[i]);
197 }
198 }
199
200 private final Atom atom;
201 private final double[] xyz = new double[3];
202 private final double[] dxyz = new double[3];
203 private final double[] a = new double[2];
204 private final double[] ainv = new double[1];
205 private final double[] binv = new double[1];
206 private final double[][][] u = new double[1][3][3];
207 private final double[][][] uinv = new double[1][3][3];
208 private final double[][][] jmat = new double[6][3][3];
209 private final double[] gradp = new double[6];
210 private final double[] gradu = new double[6];
211 private final double[] resv = new double[3];
212 private final double[][] resm = new double[3][3];
213 private double uadd;
214 private double occ;
215 private boolean hasAnisou;
216 private double[] uaniso = null;
217
218
219
220
221
222
223 public NeutronFormFactor(Atom atom) {
224 this(atom, 0.0, atom.getXYZ(null));
225 }
226
227
228
229
230
231
232
233 public NeutronFormFactor(Atom atom, double badd) {
234 this(atom, badd, atom.getXYZ(null));
235 }
236
237
238
239
240
241
242
243
244 public NeutronFormFactor(Atom atom, double badd, double[] xyz) {
245 this.atom = atom;
246 this.uadd = b2u(badd);
247 double[][] ffactor;
248
249 String key;
250 if (atom.getAtomicNumber() == 1) {
251
252
253
254 if (atom.isDeuterium()) {
255 key = atom.getAtomicNumber() + "_2";
256 } else {
257 key = atom.getAtomicNumber() + "_1";
258 }
259 } else {
260 key = "" + atom.getAtomicNumber();
261 }
262
263 ffactor = getFormFactor(key);
264 arraycopy(ffactor[1], 0, a, 0, ffactor[1].length);
265 if (a[1] != 0.0) {
266 logger.severe(" Complex neutron form factor method not supported");
267 }
268 occ = atom.getOccupancy();
269
270 if (occ <= 0.0) {
271 StringBuilder sb = new StringBuilder();
272 sb.append(" Zero occupancy: ").append(atom);
273 logger.fine(sb.toString());
274 }
275
276 update(xyz, uadd);
277 }
278
279
280
281
282
283
284
285 public static double[] getFormFactorA(String atom) {
286 double[][] ffactor;
287 ffactor = getFormFactor(atom);
288 if (ffactor != null) {
289 return ffactor[1];
290 } else {
291 return null;
292 }
293 }
294
295
296
297
298
299
300
301 public static int getFormFactorIndex(String atom) {
302 double[][] ffactor;
303 ffactor = getFormFactor(atom);
304 if (ffactor != null) {
305 return (int) ffactor[0][0];
306 } else {
307 return -1;
308 }
309 }
310
311
312
313
314
315
316
317 private static double[][] getFormFactor(String atom) {
318 double[][] ffactor = null;
319
320 if (formfactors.containsKey(atom)) {
321 ffactor = formfactors.get(atom);
322 } else {
323 String message = "Form factor for atom: " + atom + " not found!";
324 logger.severe(message);
325 }
326
327 return ffactor;
328 }
329
330
331
332
333
334
335
336 public double f(HKL hkl) {
337 double sum = a[0] * exp(-twopi2 * hkl.quadForm(u[0]));
338 return occ * sum;
339 }
340
341
342 @Override
343 public double rho(double f, double lambda, double[] xyz) {
344 sub(this.xyz, xyz, xyz);
345 double r = length(xyz);
346 if (r > atom.getFormFactorWidth()) {
347 return f;
348 }
349 double sum = ainv[0] * exp(-0.5 * quadForm(xyz, uinv[0]));
350 return f + (lambda * occ * twopi32 * sum);
351 }
352
353
354 @Override
355 public void rhoGrad(double[] xyz, double dfc, RefinementMode refinementmode) {
356 sub(this.xyz, xyz, dxyz);
357 double r = length(dxyz);
358 double r2 = r * r;
359 fill(gradp, 0.0);
360 fill(gradu, 0.0);
361
362 if (r > atom.getFormFactorWidth()) {
363 return;
364 }
365
366 boolean refinexyz = false;
367 boolean refineb = false;
368 boolean refineanisou = false;
369 boolean refineocc = false;
370 if (refinementmode == RefinementMode.COORDINATES
371 || refinementmode == RefinementMode.COORDINATES_AND_BFACTORS
372 || refinementmode == RefinementMode.COORDINATES_AND_OCCUPANCIES
373 || refinementmode == RefinementMode.COORDINATES_AND_BFACTORS_AND_OCCUPANCIES) {
374 refinexyz = true;
375 }
376 if (refinementmode == RefinementMode.BFACTORS
377 || refinementmode == RefinementMode.BFACTORS_AND_OCCUPANCIES
378 || refinementmode == RefinementMode.COORDINATES_AND_BFACTORS
379 || refinementmode == RefinementMode.COORDINATES_AND_BFACTORS_AND_OCCUPANCIES) {
380 refineb = true;
381 if (hasAnisou) {
382 refineanisou = true;
383 }
384 }
385 if (refinementmode == RefinementMode.OCCUPANCIES
386 || refinementmode == RefinementMode.BFACTORS_AND_OCCUPANCIES
387 || refinementmode == RefinementMode.COORDINATES_AND_OCCUPANCIES
388 || refinementmode == RefinementMode.COORDINATES_AND_BFACTORS_AND_OCCUPANCIES) {
389 refineocc = true;
390 }
391
392 double aex = ainv[0] * exp(-0.5 * quadForm(dxyz, uinv[0]));
393
394 if (refinexyz) {
395 vec3Mat3(dxyz, uinv[0], resv);
396 gradp[0] += aex * dot(resv, vx);
397 gradp[1] += aex * dot(resv, vy);
398 gradp[2] += aex * dot(resv, vz);
399 }
400
401 if (refineocc) {
402 gradp[3] += aex;
403 }
404
405 if (refineb) {
406 gradp[4] += aex * 0.5 * (r2 * binv[0] * binv[0] - 3.0 * binv[0]);
407
408 if (refineanisou) {
409 scalarMat3Mat3(-1.0, uinv[0], u11, resm);
410 mat3Mat3(resm, uinv[0], jmat[0]);
411 scalarMat3Mat3(-1.0, uinv[0], u22, resm);
412 mat3Mat3(resm, uinv[0], jmat[1]);
413 scalarMat3Mat3(-1.0, uinv[0], u33, resm);
414 mat3Mat3(resm, uinv[0], jmat[2]);
415 scalarMat3Mat3(-1.0, uinv[0], u12, resm);
416 mat3Mat3(resm, uinv[0], jmat[3]);
417 scalarMat3Mat3(-1.0, uinv[0], u13, resm);
418 mat3Mat3(resm, uinv[0], jmat[4]);
419 scalarMat3Mat3(-1.0, uinv[0], u23, resm);
420 mat3Mat3(resm, uinv[0], jmat[5]);
421
422 gradu[0] += aex * 0.5 * (-quadForm(dxyz, jmat[0]) - uinv[0][0][0]);
423 gradu[1] += aex * 0.5 * (-quadForm(dxyz, jmat[1]) - uinv[0][1][1]);
424 gradu[2] += aex * 0.5 * (-quadForm(dxyz, jmat[2]) - uinv[0][2][2]);
425 gradu[3] += aex * 0.5 * (-quadForm(dxyz, jmat[3]) - uinv[0][0][1] * 2.0);
426 gradu[4] += aex * 0.5 * (-quadForm(dxyz, jmat[4]) - uinv[0][0][2] * 2.0);
427 gradu[5] += aex * 0.5 * (-quadForm(dxyz, jmat[5]) - uinv[0][1][2] * 2.0);
428 }
429 }
430
431
432
433 if (refinexyz) {
434 atom.addToXYZGradient(
435 dfc * occ * -twopi32 * gradp[0],
436 dfc * occ * -twopi32 * gradp[1],
437 dfc * occ * -twopi32 * gradp[2]);
438 }
439
440
441 if (refineocc) {
442 atom.addToOccupancyGradient(dfc * twopi32 * gradp[3]);
443 }
444
445
446 if (refineb) {
447 atom.addToTempFactorGradient(dfc * b2u(occ * twopi32 * gradp[4]));
448
449 if (hasAnisou) {
450 for (int i = 0; i < 6; i++) {
451 gradu[i] = dfc * occ * twopi32 * gradu[i];
452 }
453 atom.addToAnisouGradient(gradu);
454 }
455 }
456 }
457
458
459 @Override
460 public void update(double[] xyz) {
461 update(xyz, u2b(uadd));
462 }
463
464
465 @Override
466 public void update(double[] xyz, double badd) {
467 this.xyz[0] = xyz[0];
468 this.xyz[1] = xyz[1];
469 this.xyz[2] = xyz[2];
470 double biso = atom.getTempFactor();
471 uadd = b2u(badd);
472 occ = atom.getOccupancy();
473
474
475 if (occ < 0.0) {
476 StringBuilder sb = new StringBuilder();
477 sb.append("negative occupancy for atom: ").append(atom).append("\n");
478 sb.append("resetting to 0.0\n");
479 logger.warning(sb.toString());
480 occ = 0.0;
481 atom.setOccupancy(0.0);
482 }
483
484
485 if (atom.getAnisou(null) == null) {
486 if (uaniso == null) {
487 uaniso = new double[6];
488 }
489 hasAnisou = false;
490 } else {
491 hasAnisou = true;
492 }
493
494 if (hasAnisou) {
495
496 uaniso = atom.getAnisou(null);
497 double det = determinant3(uaniso);
498
499 if (det <= 1e-14) {
500 StringBuilder sb = new StringBuilder();
501 sb.append("non-positive definite ANISOU for atom: ").append(atom).append("\n");
502 sb.append("resetting ANISOU based on isotropic B: (").append(biso).append(")\n");
503 logger.warning(sb.toString());
504
505 uaniso[0] = uaniso[1] = uaniso[2] = b2u(biso);
506 uaniso[3] = uaniso[4] = uaniso[5] = 0.0;
507 atom.setAnisou(uaniso);
508 }
509 } else {
510 if (biso < 0.0) {
511 StringBuilder sb = new StringBuilder();
512 sb.append("negative B factor for atom: ").append(atom).append("\n");
513 sb.append("resetting B to 0.01\n");
514 logger.warning(sb.toString());
515 atom.setTempFactor(0.01);
516 uaniso[0] = uaniso[1] = uaniso[2] = b2u(0.01);
517 } else {
518 uaniso[0] = uaniso[1] = uaniso[2] = b2u(biso);
519 }
520 uaniso[3] = uaniso[4] = uaniso[5] = 0.0;
521 }
522
523 u[0][0][0] = uaniso[0] + uadd;
524 u[0][1][1] = uaniso[1] + uadd;
525 u[0][2][2] = uaniso[2] + uadd;
526 u[0][0][1] = u[0][1][0] = uaniso[3];
527 u[0][0][2] = u[0][2][0] = uaniso[4];
528 u[0][1][2] = u[0][2][1] = uaniso[5];
529
530 mat3Inverse(u[0], uinv[0]);
531
532 double det = determinant3(u[0]);
533 ainv[0] = a[0] / sqrt(det);
534 det = determinant3(uinv[0]);
535 binv[0] = pow(det, 0.33333333333);
536 }
537 }