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.HKL;
41 import ffx.potential.bonded.Atom;
42 import ffx.xray.RefinementMinimize.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(ffx.xray.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
258
259
260 if (atom.isDeuterium()) {
261 key = atom.getAtomicNumber() + "_2";
262 } else {
263 key = atom.getAtomicNumber() + "_1";
264 }
265 } else {
266 key = "" + atom.getAtomicNumber();
267 }
268
269 ffactor = getFormFactor(key);
270 arraycopy(ffactor[1], 0, a, 0, ffactor[1].length);
271 if (a[1] != 0.0) {
272 logger.severe(" Complex neutron form factor method not supported");
273 }
274
275 occ = atom.getOccupancy();
276
277 if (occ <= 0.0) {
278 StringBuilder sb = new StringBuilder();
279 sb.append(" Zero occupancy: ").append(atom);
280 logger.fine(sb.toString());
281 }
282
283 update(xyz, uadd);
284 }
285
286
287
288
289
290
291
292
293
294 @Override
295 public String toString() {
296 return atom.toString() + format(" Neutron mag: %9.6f", a[0]);
297 }
298
299
300
301
302
303
304
305 public static double[] getFormFactorA(String atom) {
306 double[][] ffactor = getFormFactor(atom);
307 if (ffactor != null) {
308 return ffactor[1];
309 } else {
310 return null;
311 }
312 }
313
314
315
316
317
318
319
320 public static int getFormFactorIndex(String atom) {
321 double[][] ffactor = getFormFactor(atom);
322 if (ffactor != null) {
323 return (int) ffactor[0][0];
324 } else {
325 return -1;
326 }
327 }
328
329
330
331
332
333
334
335 private static double[][] getFormFactor(String atom) {
336 double[][] ffactor = null;
337
338 if (formfactors.containsKey(atom)) {
339 ffactor = formfactors.get(atom);
340 } else {
341 String message = " Form factor for atom: " + atom + " not found!";
342 logger.severe(message);
343 }
344
345 return ffactor;
346 }
347
348
349
350
351
352
353
354 public double f(HKL hkl) {
355 double sum = a[0] * exp(-twoPI2 * hkl.quadForm(u[0]));
356 return occ * sum;
357 }
358
359
360 @Override
361 public double rho(double f, double lambda, double[] xyz) {
362 sub(this.xyz, xyz, xyz);
363 double r = length(xyz);
364 if (r > atom.getFormFactorWidth()) {
365 return f;
366 }
367 double sum = ainv[0] * exp(-0.5 * quadForm(xyz, uinv[0]));
368 return f + (lambda * occ * inverseTwoPI32 * sum);
369 }
370
371
372 @Override
373 public void rhoGrad(double[] xyz, double dfc, RefinementMode refinementmode) {
374 sub(this.xyz, xyz, dxyz);
375 double r = length(dxyz);
376 double r2 = r * r;
377 fill(gradp, 0.0);
378 fill(gradu, 0.0);
379
380 if (r > atom.getFormFactorWidth()) {
381 return;
382 }
383
384 boolean refinexyz = false;
385 boolean refineb = false;
386 boolean refineanisou = false;
387 boolean refineocc = false;
388 if (refinementmode == RefinementMode.COORDINATES
389 || refinementmode == RefinementMode.COORDINATES_AND_BFACTORS
390 || refinementmode == RefinementMode.COORDINATES_AND_OCCUPANCIES
391 || refinementmode == RefinementMode.COORDINATES_AND_BFACTORS_AND_OCCUPANCIES) {
392 refinexyz = true;
393 }
394 if (refinementmode == RefinementMode.BFACTORS
395 || refinementmode == RefinementMode.BFACTORS_AND_OCCUPANCIES
396 || refinementmode == RefinementMode.COORDINATES_AND_BFACTORS
397 || refinementmode == RefinementMode.COORDINATES_AND_BFACTORS_AND_OCCUPANCIES) {
398 refineb = true;
399 if (hasAnisou) {
400 refineanisou = true;
401 }
402 }
403 if (refinementmode == RefinementMode.OCCUPANCIES
404 || refinementmode == RefinementMode.BFACTORS_AND_OCCUPANCIES
405 || refinementmode == RefinementMode.COORDINATES_AND_OCCUPANCIES
406 || refinementmode == RefinementMode.COORDINATES_AND_BFACTORS_AND_OCCUPANCIES) {
407 refineocc = true;
408 }
409
410 double aex = ainv[0] * exp(-0.5 * quadForm(dxyz, uinv[0]));
411
412 if (refinexyz) {
413 vec3Mat3(dxyz, uinv[0], resv);
414 gradp[0] += aex * dot(resv, vx);
415 gradp[1] += aex * dot(resv, vy);
416 gradp[2] += aex * dot(resv, vz);
417 }
418
419 if (refineocc) {
420 gradp[3] += aex;
421 }
422
423 if (refineb) {
424 gradp[4] += aex * 0.5 * (r2 * binv[0] * binv[0] - 3.0 * binv[0]);
425
426 if (refineanisou) {
427 scalarMat3Mat3(-1.0, uinv[0], u11, resm);
428 mat3Mat3(resm, uinv[0], jmat[0]);
429 scalarMat3Mat3(-1.0, uinv[0], u22, resm);
430 mat3Mat3(resm, uinv[0], jmat[1]);
431 scalarMat3Mat3(-1.0, uinv[0], u33, resm);
432 mat3Mat3(resm, uinv[0], jmat[2]);
433 scalarMat3Mat3(-1.0, uinv[0], u12, resm);
434 mat3Mat3(resm, uinv[0], jmat[3]);
435 scalarMat3Mat3(-1.0, uinv[0], u13, resm);
436 mat3Mat3(resm, uinv[0], jmat[4]);
437 scalarMat3Mat3(-1.0, uinv[0], u23, resm);
438 mat3Mat3(resm, uinv[0], jmat[5]);
439
440 gradu[0] += aex * 0.5 * (-quadForm(dxyz, jmat[0]) - uinv[0][0][0]);
441 gradu[1] += aex * 0.5 * (-quadForm(dxyz, jmat[1]) - uinv[0][1][1]);
442 gradu[2] += aex * 0.5 * (-quadForm(dxyz, jmat[2]) - uinv[0][2][2]);
443 gradu[3] += aex * 0.5 * (-quadForm(dxyz, jmat[3]) - uinv[0][0][1] * 2.0);
444 gradu[4] += aex * 0.5 * (-quadForm(dxyz, jmat[4]) - uinv[0][0][2] * 2.0);
445 gradu[5] += aex * 0.5 * (-quadForm(dxyz, jmat[5]) - uinv[0][1][2] * 2.0);
446 }
447 }
448
449
450
451 if (refinexyz) {
452 atom.addToXYZGradient(
453 dfc * occ * -inverseTwoPI32 * gradp[0],
454 dfc * occ * -inverseTwoPI32 * gradp[1],
455 dfc * occ * -inverseTwoPI32 * gradp[2]);
456 }
457
458
459 if (refineocc) {
460 atom.addToOccupancyGradient(dfc * inverseTwoPI32 * gradp[3]);
461 }
462
463
464 if (refineb) {
465 atom.addToTempFactorGradient(dfc * b2u(occ * inverseTwoPI32 * gradp[4]));
466
467 if (hasAnisou) {
468 for (int i = 0; i < 6; i++) {
469 gradu[i] = dfc * occ * inverseTwoPI32 * gradu[i];
470 }
471 atom.addToAnisouGradient(gradu);
472 }
473 }
474 }
475
476
477 @Override
478 public void update(double[] xyz) {
479 update(xyz, u2b(uadd));
480 }
481
482
483 @Override
484 public void update(double[] xyz, double badd) {
485 this.xyz[0] = xyz[0];
486 this.xyz[1] = xyz[1];
487 this.xyz[2] = xyz[2];
488 double biso = atom.getTempFactor();
489 uadd = b2u(badd);
490 occ = atom.getOccupancy();
491
492
493 if (occ < 0.0) {
494 StringBuilder sb = new StringBuilder();
495 sb.append(" Negative occupancy for atom: ").append(atom).append("\n");
496 sb.append(" Resetting to 0.0\n");
497 logger.warning(sb.toString());
498 occ = 0.0;
499 atom.setOccupancy(0.0);
500 }
501
502
503 if (atom.getAnisou(null) == null) {
504 if (uaniso == null) {
505 uaniso = new double[6];
506 }
507 hasAnisou = false;
508 } else {
509 hasAnisou = true;
510 }
511
512 if (hasAnisou) {
513
514 uaniso = atom.getAnisou(null);
515 double det = determinant3(uaniso);
516 if (det <= 1e-14) {
517 StringBuilder sb = new StringBuilder();
518 sb.append(" Non-positive definite ANISOU for atom: ").append(atom).append("\n");
519 sb.append(" Resetting ANISOU based on isotropic B: (").append(biso).append(")\n");
520 logger.warning(sb.toString());
521 double val = b2u(biso);
522 uaniso[0] = val;
523 uaniso[1] = val;
524 uaniso[2] = val;
525 uaniso[3] = 0.0;
526 uaniso[4] = 0.0;
527 uaniso[5] = 0.0;
528 atom.setAnisou(uaniso);
529 }
530 } else {
531 if (biso < 0.0) {
532 StringBuilder sb = new StringBuilder();
533 sb.append("negative B factor for atom: ").append(atom).append("\n");
534 sb.append("resetting B to 0.01\n");
535 logger.warning(sb.toString());
536 atom.setTempFactor(0.01);
537 double val = b2u(0.01);
538 uaniso[0] = val;
539 uaniso[1] = val;
540 uaniso[2] = val;
541 } else {
542 double val = b2u(biso);
543 uaniso[0] = val;
544 uaniso[1] = val;
545 uaniso[2] = val;
546 }
547 uaniso[3] = 0.0;
548 uaniso[4] = 0.0;
549 uaniso[5] = 0.0;
550 }
551
552 u[0][0][0] = uaniso[0] + uadd;
553 u[0][1][1] = uaniso[1] + uadd;
554 u[0][2][2] = uaniso[2] + uadd;
555 u[0][0][1] = uaniso[3];
556 u[0][1][0] = uaniso[3];
557 u[0][0][2] = uaniso[4];
558 u[0][2][0] = uaniso[4];
559 u[0][1][2] = uaniso[5];
560 u[0][2][1] = uaniso[5];
561 mat3Inverse(u[0], uinv[0]);
562
563 double det = determinant3(u[0]);
564 ainv[0] = a[0] / sqrt(det);
565 det = determinant3(uinv[0]);
566 binv[0] = pow(det, oneThird);
567 }
568 }