View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2024.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
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   * This implementation uses the coefficients from International Tables, Vol. C, chapter 4.4.4.
66   *
67   * @author Timothy D. Fenn
68   * @see <a href="http://dx.doi.org/10.1107/97809553602060000594" target="_blank"> V. F. Sears, Int.
69   *     Tables Vol. C (2006). Table 4.4.4.1</a>
70   * @see <a href="http://dx.doi.org/10.1107/97809553602060000600" target="_blank"> B. T. M. Willis,
71   *     Int. Tables Vol. C (2006). Chapter 6.1.3</a>
72   * @see <a href="http://dx.doi.org/10.1107/S0907444909022707" target="_blank"> M. J. Schnieders, T.
73   *     D. Fenn, V. S. Pande and A. T. Brunger, Acta Cryst. (2009). D65 952-965.</a>
74   * @since 1.0
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    * Constructor for NeutronFormFactor.
220    *
221    * @param atom a {@link ffx.potential.bonded.Atom} object.
222    */
223   public NeutronFormFactor(Atom atom) {
224     this(atom, 0.0, atom.getXYZ(null));
225   }
226 
227   /**
228    * Constructor for NeutronFormFactor.
229    *
230    * @param atom a {@link ffx.potential.bonded.Atom} object.
231    * @param badd a double.
232    */
233   public NeutronFormFactor(Atom atom, double badd) {
234     this(atom, badd, atom.getXYZ(null));
235   }
236 
237   /**
238    * Constructor for NeutronFormFactor.
239    *
240    * @param atom a {@link ffx.potential.bonded.Atom} object.
241    * @param badd a double.
242    * @param xyz an array of double.
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       // if (!atom.getResidueName().equalsIgnoreCase("DOD")) {
252       //  logger.info(" Atom:" + atom + " " + atom.isDeuterium());
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    * getFormFactorA
281    *
282    * @param atom a {@link java.lang.String} object.
283    * @return an array of double.
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    * getFormFactorIndex
297    *
298    * @param atom a {@link java.lang.String} object.
299    * @return a int.
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    * getFormFactor
313    *
314    * @param atom a {@link java.lang.String} object.
315    * @return an array of double.
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    * f
332    *
333    * @param hkl a {@link ffx.crystal.HKL} object.
334    * @return a double.
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   /** {@inheritDoc} */
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   /** {@inheritDoc} */
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     // double rho = occ * twopi32 * gradp[3];
432     // x, y, z
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     // occ
441     if (refineocc) {
442       atom.addToOccupancyGradient(dfc * twopi32 * gradp[3]);
443     }
444 
445     // Biso
446     if (refineb) {
447       atom.addToTempFactorGradient(dfc * b2u(occ * twopi32 * gradp[4]));
448       // Uaniso
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   /** {@inheritDoc} */
459   @Override
460   public void update(double[] xyz) {
461     update(xyz, u2b(uadd));
462   }
463 
464   /** {@inheritDoc} */
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     // Check occ is valid.
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     // Check if anisou changed.
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       // first check the ANISOU is valid
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 }