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-2025.
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.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   * This implementation uses the coefficients from International Tables, Vol. C, chapter 4.4.4.
68   *
69   * @author Timothy D. Fenn
70   * @see <a href="http://dx.doi.org/10.1107/97809553602060000594" target="_blank"> V. F. Sears, Int.
71   *     Tables Vol. C (2006). Table 4.4.4.1</a>
72   * @see <a href="http://dx.doi.org/10.1107/97809553602060000600" target="_blank"> B. T. M. Willis,
73   *     Int. Tables Vol. C (2006). Chapter 6.1.3</a>
74   * @see <a href="http://dx.doi.org/10.1107/S0907444909022707" target="_blank"> M. J. Schnieders, T.
75   *     D. Fenn, V. S. Pande and A. T. Brunger, Acta Cryst. (2009). D65 952-965.</a>
76   * @since 1.0
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}},  // H
113       {{1}, {6.671, 0.0}},    // D
114       {{2}, {3.26, 0.0}},     // He
115       {{3}, {-1.90, 0.0}},    // Li
116       {{4}, {7.79, 0.0}},     // Be
117       {{5}, {5.30, 0.213}},   // B
118       {{6}, {6.6460, 0.0}},   // C
119       {{7}, {9.36, 0.0}},     // N
120       {{8}, {5.803, 0.0}},    // O
121       {{9}, {5.654, 0.0}},    // F
122       {{10}, {4.566, 0.0}},   // Ne
123       {{11}, {3.63, 0.0}},    // Na
124       {{12}, {5.375, 0.0}},   // Mg
125       {{13}, {3.449, 0.0}},   // Al
126       {{14}, {4.1491, 0.0}},  // Si
127       {{15}, {5.13, 0.0}},    // P
128       {{16}, {2.847, 0.0}},   // S
129       {{17}, {9.5770, 0.0}},  // Cl
130       {{18}, {1.909, 0.0}},   // Ar
131       {{19}, {3.67, 0.0}},    // K
132       {{20}, {4.70, 0.0}},    // Ca
133       {{21}, {12.29, 0.0}},   // Sc
134       {{22}, {-3.370, 0.0}},  // Ti
135       {{23}, {-0.3824, 0.0}}, // V
136       {{24}, {3.635, 0.0}},   // Cr
137       {{25}, {-3.750, 0.0}},  // Mn
138       {{26}, {9.45, 0.0}},    // Fe
139       {{27}, {2.49, 0.0}},    // Co
140       {{28}, {10.3, 0.0}},    // Ni
141       {{29}, {7.718, 0.0}},   // Cu
142       {{30}, {5.60, 0.0}},    // Zn
143       {{31}, {7.288, 0.0}},   // Ga
144       {{32}, {8.185, 0.0}},   // Ge
145       {{33}, {6.58, 0.0}},    // As
146       {{34}, {7.970, 0.0}},   // Se
147       {{35}, {6.795, 0.0}},   // Br
148       {{36}, {7.81, 0.0}},    // Kr
149       {{37}, {7.09, 0.0}},    // Rb
150       {{38}, {7.02, 0.0}},    // Sr
151       {{39}, {7.75, 0.0}},    // Y
152       {{40}, {7.16, 0.0}},    // Zr
153       {{41}, {7.054, 0.0}},   // Nb
154       {{42}, {6.715, 0.0}},   // Mo
155       {{43}, {6.80, 0.0}},    // Tc
156       {{44}, {7.03, 0.0}},    // Ru
157       {{45}, {5.88, 0.0}},    // Rh
158       {{46}, {5.91, 0.0}},    // Pd
159       {{47}, {5.922, 0.0}},   // Ag
160       {{48}, {4.87, -0.70}},  // Cd
161       {{49}, {2.08, -0.0539}},// In; nCNS 4.065
162       {{50}, {6.225, 0.0}},   // Sn
163       {{51}, {5.57, 0.0}},    // Sb
164       {{52}, {5.80, 0.0}},    // Te; nCNS 5.68
165       {{53}, {5.28, 0.0}},    // I
166       {{54}, {4.92, 0.0}},    // Xe
167       {{55}, {5.42, 0.0}},    // Cs
168       {{56}, {5.07, 0.0}},    // Ba
169       {{57}, {8.24, 0.0}},    // La
170       {{58}, {4.84, 0.0}},    // Ce
171       {{59}, {4.58, 0.0}},    // Pr
172       {{60}, {7.69, 0.0}},    // Nd
173                               // No PM
174       {{61}, {0.80, -1.65}},  // Sm cCNS 0.0
175       {{62}, {7.22, -1.26}},  // Eu nCNS 5.3
176       {{63}, {6.5, -13.82}},  // Gd nCNS 9.5
177       {{64}, {7.38, 0.0}},    // Tb
178       {{65}, {16.9, -0.276}}, // Dy
179       {{66}, {8.01, 0.0}},    // Ho
180       {{67}, {7.79, 0.0}},    // Er
181       {{68}, {7.07, 0.0}},    // Tm
182       {{69}, {12.43, 0.0}},   // Yb
183       {{70}, {7.21, 0.0}},    // Lu
184       {{71}, {7.77, 0.0}},    // Hf
185       {{72}, {6.91, 0.0}},    // Ta
186       {{73}, {4.86, 0.0}},    // W
187       {{74}, {9.2, 0.0}},     // Re
188       {{75}, {10.7, 0.0}},    // Os
189       {{76}, {10.6, 0.0}},    // Ir
190       {{77}, {9.60, 0.0}},    // Pt
191       {{78}, {7.63, 0.0}},    // Au
192       {{79}, {12.692, 0.0}},  // Hg
193       {{80}, {8.776, 0.0}},   // Tl
194       {{81}, {9.405, 0.0}},   // Pb
195       {{82}, {8.532, 0.0}},   // Bi
196       {{83}, {10.31, 0.0}},   // Th
197       {{84}, {8.417, 0.0}}    // U
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    * Constructor for NeutronFormFactor.
226    *
227    * @param atom a {@link ffx.potential.bonded.Atom} object.
228    */
229   public NeutronFormFactor(Atom atom) {
230     this(atom, 0.0, atom.getXYZ(null));
231   }
232 
233   /**
234    * Constructor for NeutronFormFactor.
235    *
236    * @param atom a {@link ffx.potential.bonded.Atom} object.
237    * @param badd a double.
238    */
239   public NeutronFormFactor(Atom atom, double badd) {
240     this(atom, badd, atom.getXYZ(null));
241   }
242 
243   /**
244    * Constructor for NeutronFormFactor.
245    *
246    * @param atom a {@link ffx.potential.bonded.Atom} object.
247    * @param badd a double.
248    * @param xyz an array of double.
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    * Returns the string representation of the NeutronFormFactor object.
285    * The string includes details about the associated atom and its neutron
286    * scattering magnitude.
287    *
288    * @return A formatted string containing the atom's information and its
289    * neutron scattering magnitude.
290    */
291   @Override
292   public String toString() {
293     return atom.toString() + format(" Neutron mag: %9.6f", a[0]);
294   }
295 
296   /**
297    * getFormFactorA
298    *
299    * @param atom a {@link java.lang.String} object.
300    * @return an array of double.
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    * getFormFactorIndex
313    *
314    * @param atom a {@link java.lang.String} object.
315    * @return a int.
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    * getFormFactor
328    *
329    * @param atom a {@link java.lang.String} object.
330    * @return an array of double.
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    * f
347    *
348    * @param hkl a {@link ffx.crystal.HKL} object.
349    * @return a double.
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   /** {@inheritDoc} */
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   /** {@inheritDoc} */
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     // double rho = occ * inverseTwoPI32 * gradp[3];
447     // x, y, z
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     // occ
456     if (refineocc) {
457       atom.addToOccupancyGradient(dfc * inverseTwoPI32 * gradp[3]);
458     }
459 
460     // Biso
461     if (refineb) {
462       atom.addToTempFactorGradient(dfc * b2u(occ * inverseTwoPI32 * gradp[4]));
463       // Uaniso
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   /** {@inheritDoc} */
474   @Override
475   public void update(double[] xyz) {
476     update(xyz, u2b(uadd));
477   }
478 
479   /** {@inheritDoc} */
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     // Check occ is valid.
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     // Check if anisou changed.
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       // first check the ANISOU is valid
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 }