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;
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   * 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(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}},  // 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.getResidueName().equalsIgnoreCase("DOD")) {
258       //  logger.info(" Atom:" + atom + " " + atom.isDeuterium());
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    * Returns the string representation of the NeutronFormFactor object.
288    * The string includes details about the associated atom and its neutron
289    * scattering magnitude.
290    *
291    * @return A formatted string containing the atom's information and its
292    * neutron scattering magnitude.
293    */
294   @Override
295   public String toString() {
296     return atom.toString() + format(" Neutron mag: %9.6f", a[0]);
297   }
298 
299   /**
300    * getFormFactorA
301    *
302    * @param atom a {@link java.lang.String} object.
303    * @return an array of double.
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    * getFormFactorIndex
316    *
317    * @param atom a {@link java.lang.String} object.
318    * @return a int.
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    * getFormFactor
331    *
332    * @param atom a {@link java.lang.String} object.
333    * @return an array of double.
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    * f
350    *
351    * @param hkl a {@link ffx.crystal.HKL} object.
352    * @return a double.
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   /** {@inheritDoc} */
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   /** {@inheritDoc} */
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     // double rho = occ * inverseTwoPI32 * gradp[3];
450     // x, y, z
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     // occ
459     if (refineocc) {
460       atom.addToOccupancyGradient(dfc * inverseTwoPI32 * gradp[3]);
461     }
462 
463     // Biso
464     if (refineb) {
465       atom.addToTempFactorGradient(dfc * b2u(occ * inverseTwoPI32 * gradp[4]));
466       // Uaniso
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   /** {@inheritDoc} */
477   @Override
478   public void update(double[] xyz) {
479     update(xyz, u2b(uadd));
480   }
481 
482   /** {@inheritDoc} */
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     // Check occ is valid.
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     // Check if anisou changed.
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       // first check the ANISOU is valid
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 }