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 }