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.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] = (int) rot[0][0] * h + (int) rot[0][1] * k + (int) rot[0][2] * l + (int) rint(nx * trans[0]);
831     mate[1] = (int) rot[1][0] * h + (int) rot[1][1] * k + (int) rot[1][2] * l + (int) rint(ny * trans[1]);
832     mate[2] = (int) rot[2][0] * h + (int) rot[2][1] * k + (int) rot[2][2] * l + (int) rint(nz * trans[2]);
833     mate[0] = mod(mate[0], nx);
834     mate[1] = mod(mate[1], ny);
835     mate[2] = mod(mate[2], nz);
836   }
837 
838   /**
839    * Apply a symmetry operator to one HKL.
840    *
841    * @param hkl   Input HKL.
842    * @param mate  Symmetry mate HKL.
843    * @param symOp The symmetry operator.
844    */
845   public static void applySymRot(HKL hkl, HKL mate, SymOp symOp) {
846     var rot = symOp.rot;
847     double h = hkl.getH();
848     double k = hkl.getK();
849     double l = hkl.getL();
850     double hs = rot[0][0] * h + rot[0][1] * k + rot[0][2] * l;
851     double ks = rot[1][0] * h + rot[1][1] * k + rot[1][2] * l;
852     double ls = rot[2][0] * h + rot[2][1] * k + rot[2][2] * l;
853     // Convert back to HKL
854     mate.setH((int) rint(hs));
855     mate.setK((int) rint(ks));
856     mate.setL((int) rint(ls));
857   }
858 
859   /**
860    * Apply a Cartesian symmetry rotation to an array of Cartesian coordinates. The length of xyz must
861    * be divisible by 3 and mate must have the same length.
862    *
863    * @param xyz   Input cartesian x, y, z-coordinates.
864    * @param mate  Output cartesian x, y, z-coordinates.
865    * @param symOp The fractional symmetry operator.
866    */
867   public static void applyCartesianSymRot(double[] xyz, double[] mate, SymOp symOp) {
868     applyCartesianSymRot(xyz, mate, symOp, null);
869   }
870 
871   /**
872    * Apply a Cartesian symmetry rotation to an array of Cartesian coordinates. The length of xyz must
873    * be divisible by 3 and mate must have the same length.
874    *
875    * @param xyz   Input cartesian x, y, z-coordinates.
876    * @param mate  Output cartesian x, y, z-coordinates.
877    * @param symOp The fractional symmetry operator.
878    * @param mask  Only apply the SymOp if the per atom mask is true.
879    */
880   public static void applyCartesianSymRot(double[] xyz, double[] mate, SymOp symOp, boolean[] mask) {
881     int l = xyz.length;
882     int n = l / 3;
883     assert (l % 3 == 0);
884     assert (mate.length == l);
885 
886     // Load the rotation matrix
887     var rot = symOp.rot;
888     var rot00 = rot[0][0];
889     var rot10 = rot[1][0];
890     var rot20 = rot[2][0];
891     var rot01 = rot[0][1];
892     var rot11 = rot[1][1];
893     var rot21 = rot[2][1];
894     var rot02 = rot[0][2];
895     var rot12 = rot[1][2];
896     var rot22 = rot[2][2];
897 
898     if (mask == null) {
899       for (int i = 0; i < n; i++) {
900         int index = i * 3;
901         var xi = xyz[index + XX];
902         var yi = xyz[index + YY];
903         var zi = xyz[index + ZZ];
904         // Apply Symmetry Operator.
905         mate[index + XX] = rot00 * xi + rot01 * yi + rot02 * zi;
906         mate[index + YY] = rot10 * xi + rot11 * yi + rot12 * zi;
907         mate[index + ZZ] = rot20 * xi + rot21 * yi + rot22 * zi;
908       }
909     } else {
910       for (int i = 0; i < n; i++) {
911         int index = i * 3;
912         var xi = xyz[index + XX];
913         var yi = xyz[index + YY];
914         var zi = xyz[index + ZZ];
915         if (mask[i]) {
916           // Apply Symmetry Operator.
917           mate[index + XX] = rot00 * xi + rot01 * yi + rot02 * zi;
918           mate[index + YY] = rot10 * xi + rot11 * yi + rot12 * zi;
919           mate[index + ZZ] = rot20 * xi + rot21 * yi + rot22 * zi;
920         } else {
921           mate[index + XX] = xi;
922           mate[index + YY] = yi;
923           mate[index + ZZ] = zi;
924         }
925       }
926     }
927   }
928 
929   /**
930    * Apply a transpose rotation symmetry operator to one HKL.
931    *
932    * @param hkl   Input HKL.
933    * @param mate  Symmetry mate HKL.
934    * @param symOp The symmetry operator.
935    */
936   public static void applyTransSymRot(HKL hkl, HKL mate, SymOp symOp) {
937     double[][] rot = symOp.rot;
938     double h = hkl.getH();
939     double k = hkl.getK();
940     double l = hkl.getL();
941     // Apply transpose Symmetry Operator.
942     double hs = rot[0][0] * h + rot[1][0] * k + rot[2][0] * l;
943     double ks = rot[0][1] * h + rot[1][1] * k + rot[2][1] * l;
944     double ls = rot[0][2] * h + rot[1][2] * k + rot[2][2] * l;
945     // Convert back to HKL
946     mate.setH((int) rint(hs));
947     mate.setK((int) rint(ks));
948     mate.setL((int) rint(ls));
949   }
950 
951   /**
952    * Print a Sym Op matrix as a continued line string.
953    *
954    * @param symOp Symmetry operation to print.
955    * @return Continued line string.
956    */
957   public static String asMatrixString(SymOp symOp) {
958     double[][] values = symOp.asMatrix();
959     return format("""
960              %14.8f %14.8f %14.8f \\
961                        %14.8f %14.8f %14.8f \\
962                        %14.8f %14.8f %14.8f \\
963                        %14.8f %14.8f %14.8f \
964             """, values[0][0],
965         values[0][1], values[0][2], values[1][0], values[1][1], values[1][2], values[2][0],
966         values[2][1], values[2][2], values[0][3], values[1][3], values[2][3]);
967   }
968 
969   /**
970    * Invert a symmetry operator.
971    *
972    * @param symOp Original symmetry operator of which the inverse is desired.
973    * @return SymOp The inverse symmetry operator of the one supplied.
974    */
975   public static SymOp invertSymOp(SymOp symOp) {
976     var tr = symOp.tr;
977     var rot = symOp.rot;
978     var inv = mat3Inverse(rot);
979     return new SymOp(inv,
980         new double[]{-dot(inv[0], tr), -dot(inv[1], tr), -dot(inv[2], tr)});
981   }
982 
983   /**
984    * Create a SymOp from an input String.
985    *
986    * @param s Input <code>String</code> containing 12 white-space delimited double values.
987    * @return The SymOp.
988    */
989   public static SymOp parse(String s) {
990     String[] tokens = s.split(" +");
991     if (tokens.length < 12) {
992       return null;
993     }
994     return new SymOp(new double[][]{
995         {parseDouble(tokens[0]), parseDouble(tokens[1]), parseDouble(tokens[2])},
996         {parseDouble(tokens[3]), parseDouble(tokens[4]), parseDouble(tokens[5])},
997         {parseDouble(tokens[6]), parseDouble(tokens[7]), parseDouble(tokens[8])}},
998         new double[]{parseDouble(tokens[9]), parseDouble(tokens[10]), parseDouble(tokens[11])});
999   }
1000 
1001   /**
1002    * Generate a random Cartesian Symmetry Operator.
1003    *
1004    * @param scalar The range of translations will be from -scalar/2 .. scalar/2.
1005    * @return A Cartesian SymOp with a random rotation and translation.
1006    */
1007   public static SymOp randomSymOpFactory(double scalar) {
1008     double[] tr = {scalar * (random() - 0.5), scalar * (random() - 0.5), scalar * (random() - 0.5)};
1009     return randomSymOpFactory(tr);
1010   }
1011 
1012   /**
1013    * Generate a random Cartesian Symmetry Operator.
1014    *
1015    * <p>The random rotation matrix is derived from: Arvo, James (1992), "Fast random rotation
1016    * matrices", in David Kirk, Graphics Gems III, San Diego: Academic Press Professional, pp.
1017    * 117–120, ISBN 978-0-12-409671-4
1018    *
1019    * @param tr The translations to apply.
1020    * @return A Cartesian SymOp with a random rotation and translation.
1021    */
1022   public static SymOp randomSymOpFactory(double[] tr) {
1023     double[][] rot = new double[3][3];
1024     double PI2 = 2.0 * PI;
1025     double[] x = new double[3];
1026     x[0] = random();
1027     x[1] = random();
1028     x[2] = random();
1029     /* Rotation about the pole (Z).      */
1030     double theta = x[0] * PI2;
1031     /* For direction of pole deflection. */
1032     double phi = x[1] * PI2;
1033     /* For magnitude of pole deflection. */
1034     double z = x[2] * 2.0;
1035     /*
1036      Compute a vector V used for distributing points over the sphere via
1037      the reflection I - V Transpose(V). This formulation of V will
1038      guarantee that if x[1] and x[2] are uniformly distributed, the
1039      reflected points will be uniform on the sphere. Note that V has
1040      length sqrt(2) to eliminate the 2 in the Householder matrix.
1041     */
1042     double r = sqrt(z);
1043     double Vx = sin(phi) * r;
1044     double Vy = cos(phi) * r;
1045     double Vz = sqrt(2.0 - z);
1046     /*
1047      Compute the row vector S = Transpose(V) * R, where R is a simple
1048      rotation by theta about the z-axis. No need to compute Sz since it's
1049      just Vz.
1050     */
1051     double st = sin(theta);
1052     double ct = cos(theta);
1053     double Sx = Vx * ct - Vy * st;
1054     double Sy = Vx * st + Vy * ct;
1055 
1056     // Construct the rotation matrix ( V Transpose(V) - I ) R, which is equivalent to V S - R.
1057     rot[0][0] = Vx * Sx - ct;
1058     rot[0][1] = Vx * Sy - st;
1059     rot[0][2] = Vx * Vz;
1060     rot[1][0] = Vy * Sx + st;
1061     rot[1][1] = Vy * Sy - ct;
1062     rot[1][2] = Vy * Vz;
1063     rot[2][0] = Vz * Sx;
1064     rot[2][1] = Vz * Sy;
1065     // This equals Vz * Vz - 1.0
1066     rot[2][2] = 1.0 - z;
1067 
1068     return new SymOp(rot, tr);
1069   }
1070 
1071   /**
1072    * trtoString
1073    *
1074    * @param tr a double.
1075    * @return a {@link java.lang.String} object.
1076    */
1077   private static String trtoString(double tr) {
1078     if (tr == f12) {
1079       return "+1/2";
1080     }
1081     if (tr == f13) {
1082       return "+1/3";
1083     }
1084     if (tr == f23) {
1085       return "+2/3";
1086     }
1087     if (tr == f14) {
1088       return "+1/4";
1089     }
1090     if (tr == f34) {
1091       return "+3/4";
1092     }
1093     if (tr == f16) {
1094       return "+1/6";
1095     }
1096     if (tr == f56) {
1097       return "+5/6";
1098     }
1099 
1100     return "";
1101   }
1102 
1103   /**
1104    * {@inheritDoc}
1105    */
1106   @Override
1107   public String toString() {
1108     StringBuilder sb = new StringBuilder(" Rotation operator:\n");
1109     sb.append(format(" [[%4.1f,%4.1f,%4.1f]\n  [%4.1f,%4.1f,%4.1f]\n  [%4.1f,%4.1f,%4.1f]]\n",
1110         rot[0][0], rot[0][1], rot[0][2],
1111         rot[1][0], rot[1][1], rot[1][2],
1112         rot[2][0], rot[2][1], rot[2][2]));
1113     sb.append(" Translation:\n");
1114     sb.append(format(" [%4.2f,%4.2f,%4.2f]", tr[0], tr[1], tr[2]));
1115     return sb.toString();
1116   }
1117 
1118   /**
1119    * Print the symmetry operator with double precision.
1120    *
1121    * @return String of rotation/translation with double precision.
1122    */
1123   public String toStringPrecise() {
1124     StringBuilder sb = new StringBuilder(" Rotation operator:\n");
1125     sb.append(format(
1126         " [[%18.16e,%18.16e,%18.16e]\n  [%18.16e,%18.16e,%18.16e]\n  [%18.16e,%18.16e,%18.16e]]\n",
1127         rot[0][0], rot[0][1], rot[0][2],
1128         rot[1][0], rot[1][1], rot[1][2],
1129         rot[2][0], rot[2][1], rot[2][2]));
1130     sb.append(" Translation:\n");
1131     sb.append(format(" [%18.16e,%18.16e,%18.16e]", tr[0], tr[1], tr[2]));
1132     return sb.toString();
1133   }
1134 
1135   /**
1136    * toXYZString
1137    *
1138    * @return a {@link java.lang.String} object.
1139    */
1140   public String toXYZString() {
1141     StringBuilder sb = new StringBuilder();
1142     for (int i = 0; i < 3; i++) {
1143       boolean s = false;
1144       if (rot[i][0] < 0.0) {
1145         sb.append("-X");
1146         s = true;
1147       } else if (rot[i][0] > 0.0) {
1148         sb.append("X");
1149         s = true;
1150       }
1151       if (rot[i][1] < 0.0) {
1152         sb.append("-Y");
1153         s = true;
1154       } else if (rot[i][1] > 0.0) {
1155         sb.append(s ? "+Y" : "Y");
1156         s = true;
1157       }
1158       if (rot[i][2] < 0.0) {
1159         sb.append("-Z");
1160       } else if (rot[i][2] > 0.0) {
1161         sb.append(s ? "+Z" : "Z");
1162       }
1163 
1164       if (tr[i] > 0.0) {
1165         sb.append(trtoString(tr[i]));
1166       }
1167 
1168       if (i < 2) {
1169         sb.append(", ");
1170       }
1171     }
1172     return sb.toString();
1173   }
1174 }