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 org.apache.commons.math3.util.FastMath.abs;
41  import static org.apache.commons.math3.util.FastMath.random;
42  
43  /**
44   * Enumeration of the 7 lattice systems.
45   * <p>
46   * Currently, the SpaceGroup class uses the HEXAGONAL_LATTICE in all cases where its also possible to
47   * use a RHOMBOHEDRAL_LATTICE.
48   * <p>
49   * This includes space groups 146, 148, 155, 160, 161, 166 and 167.
50   *
51   * @author Michael J. Schnieders
52   * @since 1.0
53   */
54  public enum LatticeSystem {
55    /**
56     * Triclinic lattice system.
57     */
58    TRICLINIC_LATTICE,
59    /**
60     * Monoclinic lattice system.
61     */
62    MONOCLINIC_LATTICE,
63    /**
64     * Orthorhombic lattice system.
65     */
66    ORTHORHOMBIC_LATTICE,
67    /**
68     * Tetragonal lattice system.
69     */
70    TETRAGONAL_LATTICE,
71    /**
72     * Rhombohedral lattice system.
73     */
74    RHOMBOHEDRAL_LATTICE,
75    /**
76     * Hexagonal lattice system.
77     */
78    HEXAGONAL_LATTICE,
79    /**
80     * Cubic lattice system.
81     */
82    CUBIC_LATTICE;
83  
84    /**
85     * Tolerance for checking if the lattice system restrictions are satisfied.
86     * <p>
87     * Set this to 0.0 for strict checking of lattice parameters.
88     * <p>
89     * For an acetamide crystal minimization, 1.0e-16 was too small a tolerance for equivalent lattice
90     * parameters to equate as equal.
91     */
92    private static final double tolerance = 1.0e-15;
93  
94    /**
95     * If the two passed values are the same, within the tolerance, return true.
96     *
97     * @param x1 First value.
98     * @param x2 Second value.
99     * @return Return true if the two values are the same within specified tolerance.
100    */
101   public static boolean check(double x1, double x2) {
102     return abs(x1 - x2) < tolerance;
103   }
104 
105   /**
106    * Reset lattice parameters for the given lattice systems.
107    *
108    * @return New unit cell parameters.
109    */
110   public double[] resetUnitCellParams() {
111     double alpha = 60.0 + random() * 60.0;
112     double beta = 60.0 + random() * 60.0;
113     double gamma = 60.0 + random() * 60.0;
114     double[] params = {0.25 + random(), 0.25 + random(), 0.25 + random(), alpha, beta, gamma};
115     double ab = 0.5 * (params[0] + params[1]);
116     double abc = (params[0] + params[1] + params[2]) / 3.0;
117     switch (this) {
118       default -> {
119         // TRICLINIC -- No restrictions.
120       }
121       case MONOCLINIC_LATTICE -> {
122         // alpha = gamma = 90
123         params[3] = 90.0;
124         params[5] = 90.0;
125       }
126       case ORTHORHOMBIC_LATTICE -> {
127         // alpha = beta = gamma = 90
128         params[3] = 90.0;
129         params[4] = 90.0;
130         params[5] = 90.0;
131       }
132       case TETRAGONAL_LATTICE -> {
133         // a = b, alpha = beta = gamma = 90
134         params[0] = ab;
135         params[1] = ab;
136         params[3] = 90.0;
137         params[4] = 90.0;
138         params[5] = 90.0;
139       }
140       case RHOMBOHEDRAL_LATTICE -> {
141         // a = b = c, alpha = beta = gamma.
142         double angles = (params[3] + params[4] + params[5]) / 3.0;
143         params[0] = abc;
144         params[1] = abc;
145         params[2] = abc;
146         params[3] = angles;
147         params[4] = angles;
148         params[5] = angles;
149       }
150       case HEXAGONAL_LATTICE -> {
151         // a = b, alpha = beta = 90, gamma = 120
152         params[0] = ab;
153         params[1] = ab;
154         params[3] = 90.0;
155         params[4] = 90.0;
156         params[5] = 120.0;
157       }
158       case CUBIC_LATTICE -> {
159         // a = b = c, alpha = beta = gamma = 90
160         params[0] = abc;
161         params[1] = abc;
162         params[2] = abc;
163         params[3] = 90.0;
164         params[4] = 90.0;
165         params[5] = 90.0;
166       }
167     }
168     return params;
169   }
170 
171   /**
172    * Check that the lattice parameters satisfy the restrictions of the lattice systems.
173    *
174    * @param a     the a-axis length.
175    * @param b     the b-axis length.
176    * @param c     the c-axis length.
177    * @param alpha the alpha angle.
178    * @param beta  the beta angle.
179    * @param gamma the gamma angle.
180    * @return True if the restrictions are satisfied, false otherwise.
181    */
182   public boolean validParameters(double a, double b, double c, double alpha, double beta,
183                                  double gamma) {
184     switch (this) {
185       default -> {
186         // TRICLINIC -- No restrictions.
187         return true;
188       }
189       case MONOCLINIC_LATTICE -> {
190         // alpha = gamma = 90
191         return check(alpha, 90.0) && check(gamma, 90.0);
192       }
193       case ORTHORHOMBIC_LATTICE -> {
194         // alpha = beta = gamma = 90
195         return check(alpha, 90.0) && check(beta, 90.0) && check(gamma, 90.0);
196       }
197       case TETRAGONAL_LATTICE -> {
198         // a = b, alpha = beta = gamma = 90
199         return check(a, b) && check(alpha, 90.0) && check(beta, 90.0) && check(gamma, 90.0);
200       }
201       case RHOMBOHEDRAL_LATTICE -> {
202         // a = b = c, alpha = beta = gamma.
203         return check(a, b) && check(b, c) && check(alpha, beta) && check(beta, gamma);
204       }
205       case HEXAGONAL_LATTICE -> {
206         // a = b, alpha = beta = 90, gamma = 120
207         return check(a, b) && check(alpha, 90.0) && check(beta, 90.0) && check(gamma, 120.0);
208       }
209       case CUBIC_LATTICE -> {
210         // a = b = c; alpha = beta = gamma = 90
211         return check(a, b) && check(b, c) && check(alpha, 90.0) && check(beta, 90.0) && check(gamma,
212             90.0);
213       }
214     }
215   }
216 
217   /**
218    * Change the lattice parameters to satisfy the restrictions of the lattice system.
219    *
220    * @param a     the proposed a-axis length.
221    * @param b     the proposed b-axis length.
222    * @param c     the proposed c-axis length.
223    * @param alpha the proposed alpha angle.
224    * @param beta  the proposed beta angle.
225    * @param gamma the proposed gamma angle.
226    * @return Adjusted parameters if the restrictions are satisfied, original parameters otherwise.
227    */
228   public double[] fixParameters(double a, double b, double c, double alpha, double beta,
229                                 double gamma) {
230     double[] parameters = {a, b, c, alpha, beta, gamma};
231     double ab = (parameters[0] + parameters[1]) / 2;
232     double abc = (parameters[0] + parameters[1] + parameters[2]) / 3;
233     switch (this) {
234       default -> {
235         // TRICLINIC -- No restrictions.
236         return parameters;
237       }
238       case MONOCLINIC_LATTICE -> {
239         // alpha = gamma = 90
240         parameters[3] = 90.0;
241         parameters[5] = 90.0;
242         return parameters;
243       }
244       case ORTHORHOMBIC_LATTICE -> {
245         // alpha = beta = gamma = 90
246         parameters[3] = 90.0;
247         parameters[4] = 90.0;
248         parameters[5] = 90.0;
249         return parameters;
250       }
251       case TETRAGONAL_LATTICE -> {
252         // a = b, alpha = beta = gamma = 90
253         parameters[0] = ab;
254         parameters[1] = ab;
255         parameters[3] = 90.0;
256         parameters[4] = 90.0;
257         parameters[5] = 90.0;
258         return parameters;
259       }
260       case RHOMBOHEDRAL_LATTICE -> {
261         // a = b = c, alpha = beta = gamma.
262         double angles = (parameters[3] + parameters[4] + parameters[5]) / 3;
263         parameters[0] = abc;
264         parameters[1] = abc;
265         parameters[2] = abc;
266         parameters[3] = angles;
267         parameters[4] = angles;
268         parameters[5] = angles;
269         return parameters;
270       }
271       case HEXAGONAL_LATTICE -> {
272         // a = b, alpha = beta = 90, gamma = 120
273         parameters[0] = ab;
274         parameters[1] = ab;
275         parameters[3] = 90.0;
276         parameters[4] = 90.0;
277         parameters[5] = 120.0;
278         return parameters;
279       }
280       case CUBIC_LATTICE -> {
281         // a = b = c; alpha = beta = gamma = 90
282         parameters[0] = abc;
283         parameters[1] = abc;
284         parameters[2] = abc;
285         parameters[3] = 90.0;
286         parameters[4] = 90.0;
287         parameters[5] = 90.0;
288         return parameters;
289       }
290     }
291   }
292 
293   /**
294    * Returns the default b-axis for the lattice system.
295    *
296    * @param aaxis the a-axis length is the best guess for b-axis.
297    * @return default b-axis value
298    */
299   public double getDefaultBAxis(double aaxis) {
300     return aaxis;
301   }
302 
303   /**
304    * Returns the default c-axis for the lattice system.
305    *
306    * @param aaxis the a-axis length.
307    * @param baxis the b-axis length.
308    * @return default c-axis value
309    */
310   public double getDefaultCAxis(double aaxis, double baxis) {
311     return (aaxis + baxis) / 2;
312   }
313 
314   /**
315    * Returns the default alpha for the lattice system.
316    *
317    * @return default alpha value
318    */
319   public double getDefaultAlpha() {
320     return 90.0;
321   }
322 
323   /**
324    * Returns the default beta for the lattice system.
325    *
326    * @return default beta value
327    */
328   public double getDefaultBeta() {
329     return 90.0;
330   }
331 
332   /**
333    * Returns the default gamma for the lattice system.
334    *
335    * @return default gamma value
336    */
337   public double getDefaultGamma() {
338     double gamma = 90.0;
339     if (this == HEXAGONAL_LATTICE) {
340       gamma = 120.0;
341     }
342     return gamma;
343   }
344 }