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.crystal;
39  
40  import static ffx.numerics.math.DoubleMath.dot;
41  import static ffx.numerics.math.MatrixMath.mat3Inverse;
42  import static ffx.numerics.math.MatrixMath.mat4Mat4;
43  import static ffx.numerics.math.ScalarMath.mod;
44  import static java.lang.Double.parseDouble;
45  import static java.lang.String.format;
46  import static org.apache.commons.math3.util.FastMath.PI;
47  import static org.apache.commons.math3.util.FastMath.cos;
48  import static org.apache.commons.math3.util.FastMath.random;
49  import static org.apache.commons.math3.util.FastMath.rint;
50  import static org.apache.commons.math3.util.FastMath.sin;
51  import static org.apache.commons.math3.util.FastMath.sqrt;
52  
53  /**
54   * The SymOp class defines the rotation and translation of a single symmetry operator.
55   *
56   * @author Michael J. Schnieders
57   * @see SpaceGroup
58   * @since 1.0
59   */
60  public class SymOp {
61  
62    /**
63     * Constant <code>ZERO = 0.0</code>
64     */
65    private static final double ZERO = 0.0;
66    /**
67     * Constant <code>ZERO_ROTATION = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}}</code>
68     */
69    public static final double[][] ZERO_ROTATION = {
70        {1.0, ZERO, ZERO},
71        {ZERO, 1.0, ZERO},
72        {ZERO, ZERO, 1.0}};
73    /**
74     * Constant <code>Tr_0_0_0={ZERO, ZERO, ZERO}</code>
75     */
76    public static final double[] Tr_0_0_0 = {ZERO, ZERO, ZERO};
77    /**
78     * Constant <code>f12 = 1.0 / 2.0</code>
79     */
80    private static final double f12 = 1.0 / 2.0;
81    /**
82     * Constant <code>Tr_12_0_12={f12, ZERO, f12}</code>
83     */
84    static final double[] Tr_12_0_12 = {f12, ZERO, f12};
85    /**
86     * Constant <code>Tr_0_12_12={ZERO, f12, f12}</code>
87     */
88    static final double[] Tr_0_12_12 = {ZERO, f12, f12};
89    /**
90     * Constant <code>Tr_12_12_0={f12, f12, ZERO}</code>
91     */
92    static final double[] Tr_12_12_0 = {f12, f12, ZERO};
93    /**
94     * Constant <code>Tr_12_12_12={f12, f12, f12}</code>
95     */
96    static final double[] Tr_12_12_12 = {f12, f12, f12};
97    /**
98     * Constant <code>Tr_0_12_0={ZERO, f12, ZERO}</code>
99     */
100   static final double[] Tr_0_12_0 = {ZERO, f12, ZERO};
101   /**
102    * Constant <code>Tr_12_0_0={f12, ZERO, ZERO}</code>
103    */
104   static final double[] Tr_12_0_0 = {f12, ZERO, ZERO};
105   /**
106    * Constant <code>Tr_0_0_12={ZERO, ZERO, f12}</code>
107    */
108   static final double[] Tr_0_0_12 = {ZERO, ZERO, f12};
109   /**
110    * Constant <code>f13 = 1.0 / 3.0</code>
111    */
112   private static final double f13 = 1.0 / 3.0;
113   /**
114    * Constant <code>Tr_0_0_13={ZERO, ZERO, f13}</code>
115    */
116   static final double[] Tr_0_0_13 = {ZERO, ZERO, f13};
117   /**
118    * Constant <code>f23 = 2.0 / 3.0</code>
119    */
120   private static final double f23 = 2.0 / 3.0;
121   /**
122    * Constant <code>Tr_23_13_13={f23, f13, f13}</code>
123    */
124   static final double[] Tr_23_13_13 = {f23, f13, f13};
125   /**
126    * Constant <code>Tr_13_23_23={f13, f23, f23}</code>
127    */
128   static final double[] Tr_13_23_23 = {f13, f23, f23};
129   /**
130    * Constant <code>Tr_0_0_23={ZERO, ZERO, f23}</code>
131    */
132   static final double[] Tr_0_0_23 = {ZERO, ZERO, f23};
133   /**
134    * Constant <code>f14 = 1.0 / 4.0</code>
135    */
136   private static final double f14 = 1.0 / 4.0;
137   /**
138    * Constant <code>Tr_12_0_14={f12, ZERO, f14}</code>
139    */
140   static final double[] Tr_12_0_14 = {f12, ZERO, f14};
141   /**
142    * Constant <code>Tr_0_12_14={ZERO, f12, f14}</code>
143    */
144   static final double[] Tr_0_12_14 = {ZERO, f12, f14};
145   /**
146    * Constant <code>Tr_14_14_14={f14, f14, f14}</code>
147    */
148   static final double[] Tr_14_14_14 = {f14, f14, f14};
149   /**
150    * Constant <code>Tr_12_12_14={f12, f12, f14}</code>
151    */
152   static final double[] Tr_12_12_14 = {f12, f12, f14};
153   /**
154    * Constant <code>Tr_0_0_14={ZERO, ZERO, f14}</code>
155    */
156   static final double[] Tr_0_0_14 = {ZERO, ZERO, f14};
157   /**
158    * Constant <code>f34 = 3.0 / 4.0</code>
159    */
160   private static final double f34 = 3.0 / 4.0;
161   /**
162    * Constant <code>Tr_0_0_34={ZERO, ZERO, f34}</code>
163    */
164   static final double[] Tr_0_0_34 = {ZERO, ZERO, f34};
165   /**
166    * Constant <code>Tr_12_0_34={f12, ZERO, f34}</code>
167    */
168   static final double[] Tr_12_0_34 = {f12, ZERO, f34};
169   /**
170    * Constant <code>Tr_0_12_34={ZERO, f12, f34}</code>
171    */
172   static final double[] Tr_0_12_34 = {ZERO, f12, f34};
173   /**
174    * Constant <code>Tr_34_14_14={f34, f14, f14}</code>
175    */
176   static final double[] Tr_34_14_14 = {f34, f14, f14};
177   /**
178    * Constant <code>Tr_14_14_34={f14, f14, f34}</code>
179    */
180   static final double[] Tr_14_14_34 = {f14, f14, f34};
181   /**
182    * Constant <code>Tr_14_34_14={f14, f34, f14}</code>
183    */
184   static final double[] Tr_14_34_14 = {f14, f34, f14};
185   /**
186    * Constant <code>Tr_12_12_34={f12, f12, f34}</code>
187    */
188   static final double[] Tr_12_12_34 = {f12, f12, f34};
189   /**
190    * Constant <code>Tr_14_34_34={f14, f34, f34}</code>
191    */
192   static final double[] Tr_14_34_34 = {f14, f34, f34};
193   /**
194    * Constant <code>Tr_34_34_14={f34, f34, f14}</code>
195    */
196   static final double[] Tr_34_34_14 = {f34, f34, f14};
197   /**
198    * Constant <code>Tr_34_34_34={f34, f34, f34}</code>
199    */
200   static final double[] Tr_34_34_34 = {f34, f34, f34};
201   /**
202    * Constant <code>Tr_34_14_34={f34, f14, f34}</code>
203    */
204   static final double[] Tr_34_14_34 = {f34, f14, f34};
205   /**
206    * Constant <code>f16 = 1.0 / 6.0</code>
207    */
208   private static final double f16 = 1.0 / 6.0;
209   /**
210    * Constant <code>Tr_13_23_16={f13, f23, f16}</code>
211    */
212   static final double[] Tr_13_23_16 = {f13, f23, f16};
213   /**
214    * Constant <code>Tr_0_0_16={ZERO, ZERO, f16}</code>
215    */
216   static final double[] Tr_0_0_16 = {ZERO, ZERO, f16};
217   /**
218    * Constant <code>f56 = 5.0 / 6.0</code>
219    */
220   private static final double f56 = 5.0 / 6.0;
221   /**
222    * Constant <code>Tr_0_0_56={ZERO, ZERO, f56}</code>
223    */
224   static final double[] Tr_0_0_56 = {ZERO, ZERO, f56};
225   /**
226    * Constant <code>Tr_23_13_56={f23, f13, f56}</code>
227    */
228   static final double[] Tr_23_13_56 = {f23, f13, f56};
229   /**
230    * Constant <code>X = {1.0, ZERO, ZERO}</code>
231    */
232   private static final double[] X = {1.0, ZERO, ZERO};
233   /**
234    * Constant <code>Y = {ZERO, 1.0, ZERO}</code>
235    */
236   private static final double[] Y = {ZERO, 1.0, ZERO};
237   /**
238    * Constant <code>Z = {ZERO, ZERO, 1.0}</code>
239    */
240   private static final double[] Z = {ZERO, ZERO, 1.0};
241   /**
242    * Constant <code>Rot_Y_Z_X={Y, Z, X}</code>
243    */
244   static final double[][] Rot_Y_Z_X = {Y, Z, X};
245   /**
246    * Constant <code>Rot_X_Y_Z={X, Y, Z}</code>
247    */
248   static final double[][] Rot_X_Y_Z = {X, Y, Z};
249   /**
250    * Constant <code>Rot_Z_X_Y={Z, X, Y}</code>
251    */
252   static final double[][] Rot_Z_X_Y = {Z, X, Y};
253   /**
254    * Constant <code>Rot_X_Z_Y={X, Z, Y}</code>
255    */
256   static final double[][] Rot_X_Z_Y = {X, Z, Y};
257   /**
258    * Constant <code>Rot_Z_Y_X={Z, Y, X}</code>
259    */
260   static final double[][] Rot_Z_Y_X = {Z, Y, X};
261   /**
262    * Constant <code>Rot_Y_X_Z={Y, X, Z}</code>
263    */
264   static final double[][] Rot_Y_X_Z = {Y, X, Z};
265   /**
266    * Constant <code>mX = {-1.0, ZERO, ZERO}</code>
267    */
268   private static final double[] mX = {-1.0, ZERO, ZERO};
269   /**
270    * Constant <code>Rot_Y_mX_Z={Y, mX, Z}</code>
271    */
272   static final double[][] Rot_Y_mX_Z = {Y, mX, Z};
273   /**
274    * Constant <code>Rot_mX_Z_Y={mX, Z, Y}</code>
275    */
276   static final double[][] Rot_mX_Z_Y = {mX, Z, Y};
277   /**
278    * Constant <code>Rot_Y_Z_mX={Y, Z, mX}</code>
279    */
280   static final double[][] Rot_Y_Z_mX = {Y, Z, mX};
281   /**
282    * Constant <code>Rot_mX_Y_Z={mX, Y, Z}</code>
283    */
284   static final double[][] Rot_mX_Y_Z = {mX, Y, Z};
285   /**
286    * Constant <code>Rot_Z_Y_mX={Z, Y, mX}</code>
287    */
288   static final double[][] Rot_Z_Y_mX = {Z, Y, mX};
289   /**
290    * Constant <code>Rot_Z_mX_Y={Z, mX, Y}</code>
291    */
292   static final double[][] Rot_Z_mX_Y = {Z, mX, Y};
293   /**
294    * Constant <code>mY = {ZERO, -1.0, ZERO}</code>
295    */
296   private static final double[] mY = {ZERO, -1.0, ZERO};
297   /**
298    * Constant <code>Rot_Z_mY_X={Z, mY, X}</code>
299    */
300   static final double[][] Rot_Z_mY_X = {Z, mY, X};
301   /**
302    * Constant <code>Rot_X_Z_mY={X, Z, mY}</code>
303    */
304   static final double[][] Rot_X_Z_mY = {X, Z, mY};
305   /**
306    * Constant <code>Rot_mY_X_Z={mY, X, Z}</code>
307    */
308   static final double[][] Rot_mY_X_Z = {mY, X, Z};
309   /**
310    * Constant <code>Rot_mY_Z_mX={mY, Z, mX}</code>
311    */
312   static final double[][] Rot_mY_Z_mX = {mY, Z, mX};
313   /**
314    * Constant <code>Rot_mY_Z_X={mY, Z, X}</code>
315    */
316   static final double[][] Rot_mY_Z_X = {mY, Z, X};
317   /**
318    * Constant <code>Rot_Z_X_mY={Z, X, mY}</code>
319    */
320   static final double[][] Rot_Z_X_mY = {Z, X, mY};
321   /**
322    * Constant <code>Rot_Z_mX_mY={Z, mX, mY}</code>
323    */
324   static final double[][] Rot_Z_mX_mY = {Z, mX, mY};
325   /**
326    * Constant <code>Rot_mX_Z_mY={mX, Z, mY}</code>
327    */
328   static final double[][] Rot_mX_Z_mY = {mX, Z, mY};
329   /**
330    * Constant <code>Rot_X_mY_Z={X, mY, Z}</code>
331    */
332   static final double[][] Rot_X_mY_Z = {X, mY, Z};
333   /**
334    * Constant <code>Rot_mY_mX_Z={mY, mX, Z}</code>
335    */
336   static final double[][] Rot_mY_mX_Z = {mY, mX, Z};
337   /**
338    * Constant <code>Rot_Z_mY_mX={Z, mY, mX}</code>
339    */
340   static final double[][] Rot_Z_mY_mX = {Z, mY, mX};
341   /**
342    * Constant <code>Rot_mX_mY_Z={mX, mY, Z}</code>
343    */
344   static final double[][] Rot_mX_mY_Z = {mX, mY, Z};
345   /**
346    * Constant <code>mZ = {ZERO, ZERO, -1.0}</code>
347    */
348   private static final double[] mZ = {ZERO, ZERO, -1.0};
349   /**
350    * Constant <code>Rot_Y_mX_mZ={Y, mX, mZ}</code>
351    */
352   static final double[][] Rot_Y_mX_mZ = {Y, mX, mZ};
353   /**
354    * Constant <code>Rot_mX_Y_mZ={mX, Y, mZ}</code>
355    */
356   static final double[][] Rot_mX_Y_mZ = {mX, Y, mZ};
357   /**
358    * Constant <code>Rot_X_mZ_Y={X, mZ, Y}</code>
359    */
360   static final double[][] Rot_X_mZ_Y = {X, mZ, Y};
361   /**
362    * Constant <code>Rot_mY_mZ_X={mY, mZ, X}</code>
363    */
364   static final double[][] Rot_mY_mZ_X = {mY, mZ, X};
365   /**
366    * Constant <code>Rot_Y_X_mZ={Y, X, mZ}</code>
367    */
368   static final double[][] Rot_Y_X_mZ = {Y, X, mZ};
369   /**
370    * Constant <code>Rot_Y_mZ_X={Y, mZ, X}</code>
371    */
372   static final double[][] Rot_Y_mZ_X = {Y, mZ, X};
373   /**
374    * Constant <code>Rot_mX_mY_mZ={mX, mY, mZ}</code>
375    */
376   static final double[][] Rot_mX_mY_mZ = {mX, mY, mZ};
377   /**
378    * Constant <code>Rot_X_Y_mZ={X, Y, mZ}</code>
379    */
380   static final double[][] Rot_X_Y_mZ = {X, Y, mZ};
381   /**
382    * Constant <code>Rot_mZ_mY_mX={mZ, mY, mX}</code>
383    */
384   static final double[][] Rot_mZ_mY_mX = {mZ, mY, mX};
385   /**
386    * Constant <code>Rot_X_mZ_mY={X, mZ, mY}</code>
387    */
388   static final double[][] Rot_X_mZ_mY = {X, mZ, mY};
389   /**
390    * Constant <code>Rot_mY_mX_mZ={mY, mX, mZ}</code>
391    */
392   static final double[][] Rot_mY_mX_mZ = {mY, mX, mZ};
393   /**
394    * Constant <code>Rot_mY_X_mZ={mY, X, mZ}</code>
395    */
396   static final double[][] Rot_mY_X_mZ = {mY, X, mZ};
397   /**
398    * Constant <code>Rot_mX_mZ_mY={mX, mZ, mY}</code>
399    */
400   static final double[][] Rot_mX_mZ_mY = {mX, mZ, mY};
401   /**
402    * Constant <code>Rot_mZ_mX_mY={mZ, mX, mY}</code>
403    */
404   static final double[][] Rot_mZ_mX_mY = {mZ, mX, mY};
405   /**
406    * Constant <code>Rot_mZ_mY_X={mZ, mY, X}</code>
407    */
408   static final double[][] Rot_mZ_mY_X = {mZ, mY, X};
409   /**
410    * Constant <code>Rot_mZ_Y_mX={mZ, Y, mX}</code>
411    */
412   static final double[][] Rot_mZ_Y_mX = {mZ, Y, mX};
413   /**
414    * Constant <code>Rot_mZ_mX_Y={mZ, mX, Y}</code>
415    */
416   static final double[][] Rot_mZ_mX_Y = {mZ, mX, Y};
417   /**
418    * Constant <code>Rot_mX_mZ_Y={mX, mZ, Y}</code>
419    */
420   static final double[][] Rot_mX_mZ_Y = {mX, mZ, Y};
421   /**
422    * Constant <code>Rot_X_mY_mZ={X, mY, mZ}</code>
423    */
424   static final double[][] Rot_X_mY_mZ = {X, mY, mZ};
425   /**
426    * Constant <code>Rot_mZ_X_Y={mZ, X, Y}</code>
427    */
428   static final double[][] Rot_mZ_X_Y = {mZ, X, Y};
429   /**
430    * Constant <code>Rot_Y_mZ_mX={Y, mZ, mX}</code>
431    */
432   static final double[][] Rot_Y_mZ_mX = {Y, mZ, mX};
433   /**
434    * Constant <code>Rot_mY_mZ_mX={mY, mZ, mX}</code>
435    */
436   static final double[][] Rot_mY_mZ_mX = {mY, mZ, mX};
437   /**
438    * Constant <code>Rot_mZ_Y_X={mZ, Y, X}</code>
439    */
440   static final double[][] Rot_mZ_Y_X = {mZ, Y, X};
441   /**
442    * Constant <code>Rot_mZ_X_mY={mZ, X, mY}</code>
443    */
444   static final double[][] Rot_mZ_X_mY = {mZ, X, mY};
445   /**
446    * Constant <code>XmY = {1.0, -1.0, ZERO}</code>
447    */
448   private static final double[] XmY = {1.0, -1.0, ZERO};
449   /**
450    * Constant <code>Rot_XmY_X_mZ={XmY, X, mZ}</code>
451    */
452   static final double[][] Rot_XmY_X_mZ = {XmY, X, mZ};
453   /**
454    * Constant <code>Rot_XmY_X_Z={XmY, X, Z}</code>
455    */
456   static final double[][] Rot_XmY_X_Z = {XmY, X, Z};
457   /**
458    * Constant <code>Rot_XmY_mY_Z={XmY, mY, Z}</code>
459    */
460   static final double[][] Rot_XmY_mY_Z = {XmY, mY, Z};
461   /**
462    * Constant <code>Rot_X_XmY_Z={X, XmY, Z}</code>
463    */
464   static final double[][] Rot_X_XmY_Z = {X, XmY, Z};
465   /**
466    * Constant <code>Rot_X_XmY_mZ={X, XmY, mZ}</code>
467    */
468   static final double[][] Rot_X_XmY_mZ = {X, XmY, mZ};
469   /**
470    * Constant <code>Rot_mY_XmY_mZ={mY, XmY, mZ}</code>
471    */
472   static final double[][] Rot_mY_XmY_mZ = {mY, XmY, mZ};
473   /**
474    * Constant <code>Rot_mY_XmY_Z={mY, XmY, Z}</code>
475    */
476   static final double[][] Rot_mY_XmY_Z = {mY, XmY, Z};
477   /**
478    * Constant <code>Rot_XmY_mY_mZ={XmY, mY, mZ}</code>
479    */
480   static final double[][] Rot_XmY_mY_mZ = {XmY, mY, mZ};
481   /**
482    * Constant <code>mXY = {-1.0, 1.0, ZERO}</code>
483    */
484   private static final double[] mXY = {-1.0, 1.0, ZERO};
485   /**
486    * Constant <code>Rot_Y_mXY_Z={Y, mXY, Z}</code>
487    */
488   static final double[][] Rot_Y_mXY_Z = {Y, mXY, Z};
489   /**
490    * Constant <code>Rot_mX_mXY_mZ={mX, mXY, mZ}</code>
491    */
492   static final double[][] Rot_mX_mXY_mZ = {mX, mXY, mZ};
493   /**
494    * Constant <code>Rot_mXY_Y_Z={mXY, Y, Z}</code>
495    */
496   static final double[][] Rot_mXY_Y_Z = {mXY, Y, Z};
497   /**
498    * Constant <code>Rot_mXY_mX_Z={mXY, mX, Z}</code>
499    */
500   static final double[][] Rot_mXY_mX_Z = {mXY, mX, Z};
501   /**
502    * Constant <code>Rot_mXY_Y_mZ={mXY, Y, mZ}</code>
503    */
504   static final double[][] Rot_mXY_Y_mZ = {mXY, Y, mZ};
505   /**
506    * Constant <code>Rot_mXY_mX_mZ={mXY, mX, mZ}</code>
507    */
508   static final double[][] Rot_mXY_mX_mZ = {mXY, mX, mZ};
509   /**
510    * Constant <code>Rot_mX_mXY_Z={mX, mXY, Z}</code>
511    */
512   static final double[][] Rot_mX_mXY_Z = {mX, mXY, Z};
513   /**
514    * Constant <code>Rot_Y_mXY_mZ={Y, mXY, mZ}</code>
515    */
516   static final double[][] Rot_Y_mXY_mZ = {Y, mXY, mZ};
517   /**
518    * The rotation matrix in fractional coordinates.
519    */
520   public final double[][] rot;
521   /**
522    * The translation vector in fractional coordinates.
523    */
524   public final double[] tr;
525   /**
526    * A mask equal to 0 for X-coordinates.
527    */
528   private static final int XX = 0;
529   /**
530    * A mask equal to 1 for Y-coordinates.
531    */
532   private static final int YY = 1;
533   /**
534    * A mask equal to 2 for Z-coordinates.
535    */
536   private static final int ZZ = 2;
537   /**
538    * Replicates position for the given symmetry operator (LxMxN).
539    */
540   public final int[] replicatesVector;
541 
542   /**
543    * The SymOp constructor using a rotation matrix and translation vector.
544    *
545    * @param rot The rotation matrix.
546    * @param tr  The translation vector.
547    */
548   public SymOp(double[][] rot, double[] tr) {
549     this.rot = rot;
550     this.tr = tr;
551     replicatesVector = new int[]{0, 0, 0};
552   }
553 
554   /**
555    * The SymOp constructor using a rotation matrix and translation vector.
556    *
557    * @param rot              The rotation matrix.
558    * @param tr               The translation vector.
559    * @param replicatesVector Describes symmetry operators location within replicates crystal.
560    */
561   public SymOp(double[][] rot, double[] tr, int[] replicatesVector) {
562     this.rot = rot;
563     this.tr = tr;
564     this.replicatesVector = replicatesVector;
565   }
566 
567   /**
568    * The SymOp constructor using a 4x4 matrix.
569    *
570    * @param m The rotation matrix and translation vector as a 4x4 matrix.
571    */
572   public SymOp(double[][] m) {
573     this.rot = new double[3][3];
574     rot[0][0] = m[0][0];
575     rot[0][1] = m[0][1];
576     rot[0][2] = m[0][2];
577     rot[1][0] = m[1][0];
578     rot[1][1] = m[1][1];
579     rot[1][2] = m[1][2];
580     rot[2][0] = m[2][0];
581     rot[2][1] = m[2][1];
582     rot[2][2] = m[2][2];
583 
584     this.tr = new double[3];
585     tr[0] = m[0][3] / m[3][3];
586     tr[1] = m[1][3] / m[3][3];
587     tr[2] = m[2][3] / m[3][3];
588 
589     replicatesVector = new int[]{0, 0, 0};
590   }
591 
592   /**
593    * symPhaseShift
594    *
595    * @param hkl an array of double.
596    * @return a double.
597    */
598   public double symPhaseShift(double[] hkl) {
599     // Apply translation
600     return -2.0 * PI * (hkl[0] * tr[0] + hkl[1] * tr[1] + hkl[2] * tr[2]);
601   }
602 
603   /**
604    * symPhaseShift
605    *
606    * @param hkl a {@link HKL} object.
607    * @return a double.
608    */
609   public double symPhaseShift(HKL hkl) {
610     // Apply translation
611     return -2.0 * PI * (hkl.getH() * tr[0] + hkl.getK() * tr[1] + hkl.getL() * tr[2]);
612   }
613 
614   /**
615    * Return the SymOp as a 4x4 matrix.
616    *
617    * @return A 4x4 matrix representation of the SymOp.
618    */
619   public double[][] asMatrix() {
620     return new double[][]{
621         {rot[0][0], rot[0][1], rot[0][2], tr[0]},
622         {rot[1][0], rot[1][1], rot[1][2], tr[1]},
623         {rot[2][0], rot[2][1], rot[2][2], tr[2]},
624         {0.0, 0.0, 0.0, 1.0}};
625   }
626 
627   /**
628    * Return the combined SymOp that is equivalent to first applying <code>this</code> SymOp and then
629    * the argument. Note: Applied as rotation then translation.
630    * <code>X' = S_arg(S_this(X))</code>
631    * <code>X' = S_combined(X)</code>
632    *
633    * @param symOp The SymOp to append to <code>this</code> SymOp.
634    * @return The combined SymOp.
635    */
636   public SymOp append(SymOp symOp) {
637     return new SymOp(mat4Mat4(symOp.asMatrix(), asMatrix()));
638   }
639 
640   /**
641    * Return the combined SymOp that is equivalent to first applying the argument and then
642    * <code>this</code> SymOp. Note: Applied as rotation then translation.
643    * <code>X' = S_this(S_arg(X))</code>
644    * <code>X' = S_combined(X)</code>
645    *
646    * @param symOp The SymOp to prepend to <code>this</code> SymOp.
647    * @return The combined SymOp.
648    */
649   public SymOp prepend(SymOp symOp) {
650     return new SymOp(mat4Mat4(asMatrix(), symOp.asMatrix()));
651   }
652 
653   /**
654    * Return the combined SymOp that is equivalent to first applying symOp1 and then SymOp2. Note:
655    * Applied as rotation then translation.
656    * <p>
657    * <code>X' = S_2(S_1(X))</code>
658    * <code>X' = S_combined(X)</code>
659    *
660    * @param symOp1 The fist SymOp.
661    * @param symOp2 The second SymOp.
662    * @return The combined SymOp.
663    */
664   public static SymOp combineSymOps(SymOp symOp1, SymOp symOp2) {
665     return new SymOp(mat4Mat4(symOp2.asMatrix(), symOp1.asMatrix()));
666   }
667 
668   /**
669    * Apply a Cartesian symmetry operator to an array of Cartesian coordinates. If the arrays x, y or
670    * z are null or not of length n, the method returns immediately. If mateX, mateY or mateZ are null
671    * or not of length n, the method returns immediately.
672    *
673    * @param n     Number of atoms.
674    * @param x     Input cartesian x-coordinates.
675    * @param y     Input cartesian y-coordinates.
676    * @param z     Input cartesian z-coordinates.
677    * @param mateX Output cartesian x-coordinates.
678    * @param mateY Output cartesian y-coordinates.
679    * @param mateZ Output cartesian z-coordinates.
680    * @param symOp The cartesian symmetry operator.
681    */
682   public static void applyCartSymOp(int n, double[] x, double[] y, double[] z,
683                                     double[] mateX, double[] mateY, double[] mateZ, SymOp symOp) {
684     if (x == null || y == null || z == null) {
685       throw new IllegalArgumentException("The input arrays x, y and z must not be null.");
686     }
687     if (x.length < n || y.length < n || z.length < n) {
688       throw new IllegalArgumentException("The input arrays x, y and z must be of length n: " + n);
689     }
690     if (mateX == null || mateY == null || mateZ == null) {
691       throw new IllegalArgumentException("The output arrays mateX, mateY and mateZ must not be null.");
692     }
693     if (mateX.length < n || mateY.length < n || mateZ.length < n) {
694       throw new IllegalArgumentException("The output arrays mateX, mateY and mateZ must be of length n: " + n);
695     }
696 
697     final double[][] rot = symOp.rot;
698     final double[] trans = symOp.tr;
699 
700     final double rot00 = rot[0][0];
701     final double rot10 = rot[1][0];
702     final double rot20 = rot[2][0];
703     final double rot01 = rot[0][1];
704     final double rot11 = rot[1][1];
705     final double rot21 = rot[2][1];
706     final double rot02 = rot[0][2];
707     final double rot12 = rot[1][2];
708     final double rot22 = rot[2][2];
709     final double t0 = trans[0];
710     final double t1 = trans[1];
711     final double t2 = trans[2];
712     for (int i = 0; i < n; i++) {
713       double xc = x[i];
714       double yc = y[i];
715       double zc = z[i];
716       // Apply Symmetry Operator.
717       mateX[i] = rot00 * xc + rot01 * yc + rot02 * zc + t0;
718       mateY[i] = rot10 * xc + rot11 * yc + rot12 * zc + t1;
719       mateZ[i] = rot20 * xc + rot21 * yc + rot22 * zc + t2;
720     }
721   }
722 
723   /**
724    * Apply a cartesian symmetry operator to an array of coordinates.
725    *
726    * @param xyz   Input  cartesian coordinates.
727    * @param mate  Symmetry mate  cartesian coordinates.
728    * @param symOp The cartesian symmetry operator.
729    */
730   public static void applyCartesianSymOp(double[] xyz, double[] mate, SymOp symOp) {
731     applyCartesianSymOp(xyz, mate, symOp, null);
732   }
733 
734   /**
735    * Apply a cartesian symmetry operator to an array of coordinates.
736    *
737    * @param xyz   Input  cartesian coordinates.
738    * @param mate  Symmetry mate  cartesian coordinates.
739    * @param symOp The cartesian symmetry operator.
740    * @param mask  Only apply the SymOp if the per atom mask is true.
741    */
742   public static void applyCartesianSymOp(double[] xyz, double[] mate, SymOp symOp, boolean[] mask) {
743     var rot = symOp.rot;
744     var trans = symOp.tr;
745 
746     assert (xyz.length % 3 == 0);
747     assert (xyz.length == mate.length);
748     int len = xyz.length / 3;
749 
750     // Load the SymOp into local variables.
751     var rot00 = rot[0][0];
752     var rot10 = rot[1][0];
753     var rot20 = rot[2][0];
754     var rot01 = rot[0][1];
755     var rot11 = rot[1][1];
756     var rot21 = rot[2][1];
757     var rot02 = rot[0][2];
758     var rot12 = rot[1][2];
759     var rot22 = rot[2][2];
760     var tx = trans[0];
761     var ty = trans[1];
762     var tz = trans[2];
763 
764     if (mask == null) {
765       for (int i = 0; i < len; i++) {
766         int index = i * 3;
767         var xc = xyz[index + XX];
768         var yc = xyz[index + YY];
769         var zc = xyz[index + ZZ];
770         // Apply Symmetry Operator.
771         mate[index + XX] = rot00 * xc + rot01 * yc + rot02 * zc + tx;
772         mate[index + YY] = rot10 * xc + rot11 * yc + rot12 * zc + ty;
773         mate[index + ZZ] = rot20 * xc + rot21 * yc + rot22 * zc + tz;
774       }
775     } else {
776       for (int i = 0; i < len; i++) {
777         int index = i * 3;
778         var xc = xyz[index + XX];
779         var yc = xyz[index + YY];
780         var zc = xyz[index + ZZ];
781         if (mask[i]) {
782           // Apply Symmetry Operator.
783           mate[index + XX] = rot00 * xc + rot01 * yc + rot02 * zc + tx;
784           mate[index + YY] = rot10 * xc + rot11 * yc + rot12 * zc + ty;
785           mate[index + ZZ] = rot20 * xc + rot21 * yc + rot22 * zc + tz;
786         } else {
787           mate[index + XX] = xc;
788           mate[index + YY] = yc;
789           mate[index + ZZ] = zc;
790         }
791       }
792     }
793   }
794 
795   /**
796    * Apply a fractional symmetry operator to one set of coordinates.
797    *
798    * @param xyz   Input fractional coordinates.
799    * @param mate  Symmetry mate fractional coordinates.
800    * @param symOp The fractional symmetry operator.
801    */
802   public static void applyFracSymOp(double[] xyz, double[] mate, SymOp symOp) {
803     var rot = symOp.rot;
804     var trans = symOp.tr;
805     var xf = xyz[0];
806     var yf = xyz[1];
807     var zf = xyz[2];
808     // Apply Symmetry Operator.
809     mate[0] = rot[0][0] * xf + rot[0][1] * yf + rot[0][2] * zf + trans[0];
810     mate[1] = rot[1][0] * xf + rot[1][1] * yf + rot[1][2] * zf + trans[1];
811     mate[2] = rot[2][0] * xf + rot[2][1] * yf + rot[2][2] * zf + trans[2];
812   }
813 
814   /**
815    * Apply a symmetry operator to one set of coordinates.
816    *
817    * @param h     Input coordinates.
818    * @param k     Input coordinates.
819    * @param l     Input coordinates.
820    * @param mate  Symmetry mate coordinates.
821    * @param symOp The symmetry operator.
822    * @param nx    number of unit cell translations
823    * @param ny    number of unit cell translations
824    * @param nz    number of unit cell translations
825    */
826   public static void applySymOp(int h, int k, int l, int[] mate, SymOp symOp, int nx, int ny, int nz) {
827     var rot = symOp.rot;
828     var trans = symOp.tr;
829     // Apply Symmetry Operator.
830     mate[0] =
831         (int) rot[0][0] * h + (int) rot[0][1] * k + (int) rot[0][2] * l + (int) rint(nx * trans[0]);
832     mate[1] =
833         (int) rot[1][0] * h + (int) rot[1][1] * k + (int) rot[1][2] * l + (int) rint(ny * trans[1]);
834     mate[2] =
835         (int) rot[2][0] * h + (int) rot[2][1] * k + (int) rot[2][2] * l + (int) rint(nz * trans[2]);
836     mate[0] = mod(mate[0], nx);
837     mate[1] = mod(mate[1], ny);
838     mate[2] = mod(mate[2], nz);
839   }
840 
841   /**
842    * Apply a symmetry operator to one HKL.
843    *
844    * @param hkl   Input HKL.
845    * @param mate  Symmetry mate HKL.
846    * @param symOp The symmetry operator.
847    */
848   public static void applySymRot(HKL hkl, HKL mate, SymOp symOp) {
849     var rot = symOp.rot;
850     double h = hkl.getH();
851     double k = hkl.getK();
852     double l = hkl.getL();
853     double hs = rot[0][0] * h + rot[0][1] * k + rot[0][2] * l;
854     double ks = rot[1][0] * h + rot[1][1] * k + rot[1][2] * l;
855     double ls = rot[2][0] * h + rot[2][1] * k + rot[2][2] * l;
856     // Convert back to HKL
857     mate.setH((int) rint(hs));
858     mate.setK((int) rint(ks));
859     mate.setL((int) rint(ls));
860   }
861 
862   /**
863    * Apply a Cartesian symmetry rotation to an array of Cartesian coordinates. The length of xyz must
864    * be divisible by 3 and mate must have the same length.
865    *
866    * @param xyz   Input cartesian x, y, z-coordinates.
867    * @param mate  Output cartesian x, y, z-coordinates.
868    * @param symOp The fractional symmetry operator.
869    */
870   public static void applyCartesianSymRot(double[] xyz, double[] mate, SymOp symOp) {
871     applyCartesianSymRot(xyz, mate, symOp, null);
872   }
873 
874   /**
875    * Apply a Cartesian symmetry rotation to an array of Cartesian coordinates. The length of xyz must
876    * be divisible by 3 and mate must have the same length.
877    *
878    * @param xyz   Input cartesian x, y, z-coordinates.
879    * @param mate  Output cartesian x, y, z-coordinates.
880    * @param symOp The fractional symmetry operator.
881    * @param mask  Only apply the SymOp if the per atom mask is true.
882    */
883   public static void applyCartesianSymRot(double[] xyz, double[] mate, SymOp symOp, boolean[] mask) {
884     int l = xyz.length;
885     int n = l / 3;
886     assert (l % 3 == 0);
887     assert (mate.length == l);
888 
889     // Load the rotation matrix
890     var rot = symOp.rot;
891     var rot00 = rot[0][0];
892     var rot10 = rot[1][0];
893     var rot20 = rot[2][0];
894     var rot01 = rot[0][1];
895     var rot11 = rot[1][1];
896     var rot21 = rot[2][1];
897     var rot02 = rot[0][2];
898     var rot12 = rot[1][2];
899     var rot22 = rot[2][2];
900 
901     if (mask == null) {
902       for (int i = 0; i < n; i++) {
903         int index = i * 3;
904         var xi = xyz[index + XX];
905         var yi = xyz[index + YY];
906         var zi = xyz[index + ZZ];
907         // Apply Symmetry Operator.
908         mate[index + XX] = rot00 * xi + rot01 * yi + rot02 * zi;
909         mate[index + YY] = rot10 * xi + rot11 * yi + rot12 * zi;
910         mate[index + ZZ] = rot20 * xi + rot21 * yi + rot22 * zi;
911       }
912     } else {
913       for (int i = 0; i < n; i++) {
914         int index = i * 3;
915         var xi = xyz[index + XX];
916         var yi = xyz[index + YY];
917         var zi = xyz[index + ZZ];
918         if (mask[i]) {
919           // Apply Symmetry Operator.
920           mate[index + XX] = rot00 * xi + rot01 * yi + rot02 * zi;
921           mate[index + YY] = rot10 * xi + rot11 * yi + rot12 * zi;
922           mate[index + ZZ] = rot20 * xi + rot21 * yi + rot22 * zi;
923         } else {
924           mate[index + XX] = xi;
925           mate[index + YY] = yi;
926           mate[index + ZZ] = zi;
927         }
928       }
929     }
930   }
931 
932   /**
933    * Apply a transpose rotation symmetry operator to one HKL.
934    *
935    * @param hkl   Input HKL.
936    * @param mate  Symmetry mate HKL.
937    * @param symOp The symmetry operator.
938    */
939   public static void applyTransSymRot(HKL hkl, HKL mate, SymOp symOp) {
940     double[][] rot = symOp.rot;
941     double h = hkl.getH();
942     double k = hkl.getK();
943     double l = hkl.getL();
944     // Apply transpose Symmetry Operator.
945     double hs = rot[0][0] * h + rot[1][0] * k + rot[2][0] * l;
946     double ks = rot[0][1] * h + rot[1][1] * k + rot[2][1] * l;
947     double ls = rot[0][2] * h + rot[1][2] * k + rot[2][2] * l;
948     // Convert back to HKL
949     mate.setH((int) rint(hs));
950     mate.setK((int) rint(ks));
951     mate.setL((int) rint(ls));
952   }
953 
954   /**
955    * Print a Sym Op matrix as a continued line string.
956    *
957    * @param symOp Symmetry operation to print.
958    * @return Continued line string.
959    */
960   public static String asMatrixString(SymOp symOp) {
961     double[][] values = symOp.asMatrix();
962     return format("""
963              %14.8f %14.8f %14.8f \\
964                        %14.8f %14.8f %14.8f \\
965                        %14.8f %14.8f %14.8f \\
966                        %14.8f %14.8f %14.8f \
967             """, values[0][0],
968         values[0][1], values[0][2], values[1][0], values[1][1], values[1][2], values[2][0],
969         values[2][1], values[2][2], values[0][3], values[1][3], values[2][3]);
970   }
971 
972   /**
973    * Invert a symmetry operator.
974    *
975    * @param symOp Original symmetry operator of which the inverse is desired.
976    * @return SymOp The inverse symmetry operator of the one supplied.
977    */
978   public static SymOp invertSymOp(SymOp symOp) {
979     var tr = symOp.tr;
980     var rot = symOp.rot;
981     var inv = mat3Inverse(rot);
982     return new SymOp(inv,
983         new double[]{-dot(inv[0], tr), -dot(inv[1], tr), -dot(inv[2], tr)});
984   }
985 
986   /**
987    * Create a SymOp from an input String.
988    *
989    * @param s Input <code>String</code> containing 12 white-space delimited double values.
990    * @return The SymOp.
991    */
992   public static SymOp parse(String s) {
993     String[] tokens = s.split(" +");
994     if (tokens.length < 12) {
995       return null;
996     }
997     return new SymOp(new double[][]{
998         {parseDouble(tokens[0]), parseDouble(tokens[1]), parseDouble(tokens[2])},
999         {parseDouble(tokens[3]), parseDouble(tokens[4]), parseDouble(tokens[5])},
1000         {parseDouble(tokens[6]), parseDouble(tokens[7]), parseDouble(tokens[8])}},
1001         new double[]{parseDouble(tokens[9]), parseDouble(tokens[10]), parseDouble(tokens[11])});
1002   }
1003 
1004   /**
1005    * Generate a random Cartesian Symmetry Operator.
1006    *
1007    * @param scalar The range of translations will be from -scalar/2 .. scalar/2.
1008    * @return A Cartesian SymOp with a random rotation and translation.
1009    */
1010   public static SymOp randomSymOpFactory(double scalar) {
1011     double[] tr = {scalar * (random() - 0.5), scalar * (random() - 0.5), scalar * (random() - 0.5)};
1012     return randomSymOpFactory(tr);
1013   }
1014 
1015   /**
1016    * Generate a random Cartesian Symmetry Operator.
1017    *
1018    * <p>The random rotation matrix is derived from: Arvo, James (1992), "Fast random rotation
1019    * matrices", in David Kirk, Graphics Gems III, San Diego: Academic Press Professional, pp.
1020    * 117–120, ISBN 978-0-12-409671-4
1021    *
1022    * @param tr The translations to apply.
1023    * @return A Cartesian SymOp with a random rotation and translation.
1024    */
1025   public static SymOp randomSymOpFactory(double[] tr) {
1026     double[][] rot = new double[3][3];
1027     double PI2 = 2.0 * PI;
1028     double[] x = new double[3];
1029     x[0] = random();
1030     x[1] = random();
1031     x[2] = random();
1032     /* Rotation about the pole (Z).      */
1033     double theta = x[0] * PI2;
1034     /* For direction of pole deflection. */
1035     double phi = x[1] * PI2;
1036     /* For magnitude of pole deflection. */
1037     double z = x[2] * 2.0;
1038     /*
1039      Compute a vector V used for distributing points over the sphere via
1040      the reflection I - V Transpose(V). This formulation of V will
1041      guarantee that if x[1] and x[2] are uniformly distributed, the
1042      reflected points will be uniform on the sphere. Note that V has
1043      length sqrt(2) to eliminate the 2 in the Householder matrix.
1044     */
1045     double r = sqrt(z);
1046     double Vx = sin(phi) * r;
1047     double Vy = cos(phi) * r;
1048     double Vz = sqrt(2.0 - z);
1049     /*
1050      Compute the row vector S = Transpose(V) * R, where R is a simple
1051      rotation by theta about the z-axis. No need to compute Sz since it's
1052      just Vz.
1053     */
1054     double st = sin(theta);
1055     double ct = cos(theta);
1056     double Sx = Vx * ct - Vy * st;
1057     double Sy = Vx * st + Vy * ct;
1058 
1059     // Construct the rotation matrix ( V Transpose(V) - I ) R, which is equivalent to V S - R.
1060     rot[0][0] = Vx * Sx - ct;
1061     rot[0][1] = Vx * Sy - st;
1062     rot[0][2] = Vx * Vz;
1063     rot[1][0] = Vy * Sx + st;
1064     rot[1][1] = Vy * Sy - ct;
1065     rot[1][2] = Vy * Vz;
1066     rot[2][0] = Vz * Sx;
1067     rot[2][1] = Vz * Sy;
1068     // This equals Vz * Vz - 1.0
1069     rot[2][2] = 1.0 - z;
1070 
1071     return new SymOp(rot, tr);
1072   }
1073 
1074   /**
1075    * trtoString
1076    *
1077    * @param tr a double.
1078    * @return a {@link java.lang.String} object.
1079    */
1080   private static String trtoString(double tr) {
1081     if (tr == f12) {
1082       return "+1/2";
1083     }
1084     if (tr == f13) {
1085       return "+1/3";
1086     }
1087     if (tr == f23) {
1088       return "+2/3";
1089     }
1090     if (tr == f14) {
1091       return "+1/4";
1092     }
1093     if (tr == f34) {
1094       return "+3/4";
1095     }
1096     if (tr == f16) {
1097       return "+1/6";
1098     }
1099     if (tr == f56) {
1100       return "+5/6";
1101     }
1102 
1103     return "";
1104   }
1105 
1106   /**
1107    * {@inheritDoc}
1108    */
1109   @Override
1110   public String toString() {
1111     StringBuilder sb = new StringBuilder(" Rotation operator:\n");
1112     sb.append(format(" [[%4.1f,%4.1f,%4.1f]\n  [%4.1f,%4.1f,%4.1f]\n  [%4.1f,%4.1f,%4.1f]]\n",
1113         rot[0][0], rot[0][1], rot[0][2],
1114         rot[1][0], rot[1][1], rot[1][2],
1115         rot[2][0], rot[2][1], rot[2][2]));
1116     sb.append(" Translation:\n");
1117     sb.append(format(" [%4.2f,%4.2f,%4.2f]", tr[0], tr[1], tr[2]));
1118     return sb.toString();
1119   }
1120 
1121   /**
1122    * Print the symmetry operator with double precision.
1123    *
1124    * @return String of rotation/translation with double precision.
1125    */
1126   public String toStringPrecise() {
1127     StringBuilder sb = new StringBuilder(" Rotation operator:\n");
1128     sb.append(format(
1129         " [[%18.16e,%18.16e,%18.16e]\n  [%18.16e,%18.16e,%18.16e]\n  [%18.16e,%18.16e,%18.16e]]\n",
1130         rot[0][0], rot[0][1], rot[0][2],
1131         rot[1][0], rot[1][1], rot[1][2],
1132         rot[2][0], rot[2][1], rot[2][2]));
1133     sb.append(" Translation:\n");
1134     sb.append(format(" [%18.16e,%18.16e,%18.16e]", tr[0], tr[1], tr[2]));
1135     return sb.toString();
1136   }
1137 
1138   /**
1139    * toXYZString
1140    *
1141    * @return a {@link java.lang.String} object.
1142    */
1143   public String toXYZString() {
1144     StringBuilder sb = new StringBuilder();
1145     for (int i = 0; i < 3; i++) {
1146       boolean s = false;
1147       if (rot[i][0] < 0.0) {
1148         sb.append("-X");
1149         s = true;
1150       } else if (rot[i][0] > 0.0) {
1151         sb.append("X");
1152         s = true;
1153       }
1154       if (rot[i][1] < 0.0) {
1155         sb.append("-Y");
1156         s = true;
1157       } else if (rot[i][1] > 0.0) {
1158         sb.append(s ? "+Y" : "Y");
1159         s = true;
1160       }
1161       if (rot[i][2] < 0.0) {
1162         sb.append("-Z");
1163       } else if (rot[i][2] > 0.0) {
1164         sb.append(s ? "+Z" : "Z");
1165       }
1166 
1167       if (tr[i] > 0.0) {
1168         sb.append(trtoString(tr[i]));
1169       }
1170 
1171       if (i < 2) {
1172         sb.append(", ");
1173       }
1174     }
1175     return sb.toString();
1176   }
1177 }