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 org.apache.commons.configuration2.CompositeConfiguration;
41  
42  import javax.annotation.Nullable;
43  import java.util.ArrayList;
44  import java.util.HashMap;
45  import java.util.Map.Entry;
46  
47  import static ffx.crystal.SymOp.applySymRot;
48  import static ffx.crystal.SymOp.applyTransSymRot;
49  import static ffx.numerics.math.ScalarMath.mod;
50  import static org.apache.commons.math3.util.FastMath.PI;
51  import static org.apache.commons.math3.util.FastMath.cos;
52  import static org.apache.commons.math3.util.FastMath.floor;
53  import static org.apache.commons.math3.util.FastMath.max;
54  import static org.apache.commons.math3.util.FastMath.min;
55  import static org.apache.commons.math3.util.FastMath.rint;
56  
57  /**
58   * Class to represent a reflection list.
59   *
60   * @author Timothy D. Fenn
61   * @see <a href="http://dx.doi.org/10.1107/S0021889802013420" target="_blank"> Cowtan, K. 2002.
62   * Generic representation and evaluation of properties as a function of position in reciprocal
63   * space. J. Appl. Cryst. 35:655-663. </a>
64   * @since 1.0
65   */
66  public class ReflectionList {
67  
68    /**
69     * The HKL list.
70     */
71    public final ArrayList<HKL> hklList = new ArrayList<>();
72    /**
73     * The Crystal instance.
74     */
75    public final Crystal crystal;
76    /**
77     * The space group.
78     */
79    public final SpaceGroup spaceGroup;
80    /**
81     * Resolution instance.
82     */
83    public final Resolution resolution;
84    /**
85     * String to HKL look-up.
86     */
87    final HashMap<String, HKL> hklMap = new HashMap<>();
88    /**
89     * For binning reflections based on resolution
90     */
91    public int nBins = 10;
92    /**
93     * Histogram.
94     */
95    private final double[] histogram = new double[1001];
96    /**
97     * Minimum resolution.
98     */
99    private double minResolution;
100   /**
101    * Maximum resolution.
102    */
103   private double maxResolution;
104 
105   /**
106    * Constructor for ReflectionList.
107    *
108    * @param crystal    a {@link ffx.crystal.Crystal} object.
109    * @param resolution a {@link ffx.crystal.Resolution} object.
110    */
111   public ReflectionList(Crystal crystal, Resolution resolution) {
112     this(crystal, resolution, null);
113   }
114 
115   /**
116    * Constructor for ReflectionList.
117    *
118    * @param crystal    a {@link ffx.crystal.Crystal} object.
119    * @param resolution a {@link ffx.crystal.Resolution} object.
120    * @param properties a {@link org.apache.commons.configuration2.CompositeConfiguration} object.
121    */
122   public ReflectionList(Crystal crystal, Resolution resolution, CompositeConfiguration properties) {
123     this.crystal = crystal;
124     this.spaceGroup = crystal.spaceGroup;
125     this.resolution = resolution;
126 
127     int hMax = (int) (this.crystal.a / this.resolution.resolutionLimit());
128     int kMax = (int) (this.crystal.b / this.resolution.resolutionLimit());
129     int lMax = (int) (this.crystal.c / this.resolution.resolutionLimit());
130 
131     minResolution = Double.POSITIVE_INFINITY;
132     maxResolution = Double.NEGATIVE_INFINITY;
133     int n = 0;
134 
135     HKL hkl = new HKL();
136     for (int h = -hMax; h <= hMax; h++) {
137       hkl.setH(h);
138       for (int k = -kMax; k <= kMax; k++) {
139         hkl.setK(k);
140         for (int l = -lMax; l <= lMax; l++) {
141           hkl.setL(l);
142 
143           double res = this.crystal.invressq(hkl);
144           getEpsilon(hkl);
145           LaueSystem laueSystem = spaceGroup.laueSystem;
146           if (laueSystem.checkRestrictions(h, k, l)
147               && resolution.inInverseResSqRange(res)
148               && !hkl.sysAbs()) {
149             minResolution = min(res, minResolution);
150             maxResolution = max(res, maxResolution);
151             String s = (h + "_" + k + "_" + l).intern();
152             hklMap.put(s, new HKL(hkl.getH(), hkl.getK(), hkl.getL(), hkl.getEpsilon(), hkl.allowed));
153             n++;
154           }
155         }
156       }
157     }
158 
159     n = 0;
160     for (Entry<String, HKL> entry : hklMap.entrySet()) {
161       HKL ih = entry.getValue();
162       ih.setIndex(n);
163       hklList.add(ih);
164       n++;
165     }
166 
167     // Set up the resolution bins first build a histogram.
168     for (HKL ih : hklList) {
169       double r = (this.crystal.invressq(ih) - minResolution) / (maxResolution - minResolution);
170       int i = (int) (min(r, 0.999) * 1000.0);
171       histogram[i + 1] += 1.0;
172     }
173 
174     // Convert to cumulative histogram
175     for (int i = 1; i < histogram.length; i++) {
176       histogram[i] += histogram[i - 1];
177     }
178     for (int i = 0; i < histogram.length; i++) {
179       histogram[i] /= histogram[histogram.length - 1];
180     }
181 
182     // Assign each reflection to a bin in the range (0-nbins)
183     setResolutionBins(properties);
184   }
185 
186   /**
187    * Constructor for ReflectionList.
188    *
189    * @param a          a double.
190    * @param b          a double.
191    * @param c          a double.
192    * @param alpha      a double.
193    * @param beta       a double.
194    * @param gamma      a double.
195    * @param sg         a {@link java.lang.String} object.
196    * @param resolution a double.
197    */
198   public ReflectionList(double a, double b, double c, double alpha, double beta, double gamma,
199                         String sg, double resolution) {
200     this(new Crystal(a, b, c, alpha, beta, gamma, sg), new Resolution(resolution));
201   }
202 
203   /**
204    * findSymHKL
205    *
206    * @param hkl  a {@link ffx.crystal.HKL} object.
207    * @param mate a {@link ffx.crystal.HKL} object.
208    * @return a boolean.
209    */
210   public boolean findSymHKL(HKL hkl, HKL mate) {
211     return findSymHKL(hkl, mate, false);
212   }
213 
214   /**
215    * findSymHKL
216    *
217    * @param h    an int.
218    * @param k    an int.
219    * @param l    an int.
220    * @param mate a {@link ffx.crystal.HKL} object.
221    * @return a boolean.
222    */
223   public boolean findSymHKL(int h, int k, int l, HKL mate) {
224     return findSymHKL(new HKL(h, k, l), mate, false);
225   }
226 
227   /**
228    * findSymHKL
229    *
230    * @param h         an int.
231    * @param k         an int.
232    * @param l         an int.
233    * @param mate      a {@link ffx.crystal.HKL} object.
234    * @param transpose a boolean.
235    * @return a boolean.
236    */
237   public boolean findSymHKL(int h, int k, int l, HKL mate, boolean transpose) {
238     return findSymHKL(new HKL(h, k, l), mate, transpose);
239   }
240 
241   /**
242    * getHKL
243    *
244    * @param h an int.
245    * @param k an int.
246    * @param l an int.
247    * @return a {@link ffx.crystal.HKL} object.
248    */
249   public HKL getHKL(int h, int k, int l) {
250     String s = (h + "_" + k + "_" + l);
251     return hklMap.get(s);
252   }
253 
254   /**
255    * getHKL
256    *
257    * @param hkl a {@link ffx.crystal.HKL} object.
258    * @return a {@link ffx.crystal.HKL} object.
259    */
260   public HKL getHKL(HKL hkl) {
261     return getHKL(hkl.getH(), hkl.getK(), hkl.getL());
262   }
263 
264   /**
265    * Get the maximum resolution.
266    *
267    * @return Maximum resolution
268    */
269   public double getMaxResolution() {
270     return maxResolution;
271   }
272 
273   /**
274    * Get the minimum resolution.
275    *
276    * @return Minimum resolution
277    */
278   public double getMinResolution() {
279     return minResolution;
280   }
281 
282   /**
283    * hasHKL
284    *
285    * @param hkl a {@link ffx.crystal.HKL} object.
286    * @return a boolean.
287    */
288   public boolean hasHKL(HKL hkl) {
289     return hasHKL(hkl.getH(), hkl.getK(), hkl.getL());
290   }
291 
292   /**
293    * ordinal
294    *
295    * @param s a double.
296    * @return a double.
297    */
298   public final double ordinal(double s) {
299     double r = (s - minResolution) / (maxResolution - minResolution);
300     r = min(r, 0.999) * 1000.0;
301     int i = (int) r;
302     r -= floor(r);
303     return ((1.0 - r) * histogram[i] + r * histogram[i + 1]);
304   }
305 
306   /**
307    * {@inheritDoc}
308    */
309   @Override
310   public String toString() {
311     return " Reflection list with "
312         + this.hklList.size()
313         + " reflections, spacegroup "
314         + this.spaceGroup.shortName
315         + " resolution limit: "
316         + resolution.resolutionLimit();
317   }
318 
319   /**
320    * findSymHKL
321    *
322    * @param hkl       a {@link ffx.crystal.HKL} object.
323    * @param mate      a {@link ffx.crystal.HKL} object.
324    * @param transpose a boolean.
325    * @return a boolean.
326    */
327   private boolean findSymHKL(HKL hkl, HKL mate, boolean transpose) {
328     int nsym = spaceGroup.numPrimitiveSymEquiv;
329 
330     for (int i = 0; i < nsym; i++) {
331       if (transpose) {
332         applyTransSymRot(hkl, mate, spaceGroup.symOps.get(i));
333       } else {
334         applySymRot(hkl, mate, spaceGroup.symOps.get(i));
335       }
336 
337       LaueSystem laueSystem = spaceGroup.laueSystem;
338       if (laueSystem.checkRestrictions(mate.getH(), mate.getK(), mate.getL())) {
339         return false;
340       }
341       if (laueSystem.checkRestrictions(-mate.getH(), -mate.getK(), -mate.getL())) {
342         mate.setH(-mate.getH());
343         mate.setK(-mate.getK());
344         mate.setL(-mate.getL());
345         return true;
346       }
347     }
348 
349     mate.setH(hkl.getH());
350     mate.setK(hkl.getK());
351     mate.setL(hkl.getL());
352     return false;
353   }
354 
355   /**
356    * hasHKL
357    *
358    * @param h an int.
359    * @param k an int.
360    * @param l an int.
361    * @return a boolean.
362    */
363   private boolean hasHKL(int h, int k, int l) {
364     String s = (h + "_" + k + "_" + l);
365     return hklMap.containsKey(s);
366   }
367 
368   private void getEpsilon(HKL hkl) {
369     int epsilon = 1;
370     int allowed = 255;
371 
372     int nSym = spaceGroup.symOps.size();
373     for (int i = 1; i < nSym; i++) {
374       HKL mate = new HKL();
375       SymOp symOp = spaceGroup.symOps.get(i);
376       applySymRot(hkl, mate, symOp);
377       double shift = symOp.symPhaseShift(hkl);
378       if (mate.equals(hkl)) {
379         if (cos(shift) > 0.999) {
380           epsilon++;
381         } else {
382           allowed = 0;
383           epsilon = 0;
384           break;
385         }
386       } else if (mate.equals(hkl.neg())) {
387         // centric reflection
388         allowed = (int) rint(mod(-0.5 * shift, PI) / (PI / HKL.ndiv));
389       }
390     }
391     if (hkl.getH() == 0 && hkl.getK() == 0 && hkl.getL() == 0) {
392       allowed = 0;
393     }
394 
395     hkl.setEpsilon(epsilon);
396     hkl.setAllowed(allowed);
397   }
398 
399   private void setResolutionBins(@Nullable CompositeConfiguration properties) {
400     if (properties != null) {
401       nBins = properties.getInt("reflection-bins", 10);
402     }
403     double nBinsDouble = nBins;
404     for (HKL ih : hklList) {
405       int bin = (int) (nBinsDouble * ordinal(crystal.invressq(ih)));
406       ih.bin = min(bin, nBins - 1);
407     }
408   }
409 }