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