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.numerics.fft;
39  
40  import static java.lang.System.arraycopy;
41  import static org.apache.commons.math3.util.FastMath.PI;
42  import static org.apache.commons.math3.util.FastMath.cos;
43  import static org.apache.commons.math3.util.FastMath.sin;
44  
45  /**
46   * Compute the FFT of real, double precision data of arbitrary length n using a Complex transform.
47   *
48   * @author Michal J. Schnieders<br> Derived from: <br> Bruce R. Miller bruce.miller@nist.gov <br>
49   * Contribution of the National Institute of Standards and Technology, not subject to copyright.
50   * <br>
51   * Derived from:<br> GSL (Gnu Scientific Library) FFT Code by Brian Gough bjg@vvv.lanl.gov
52   * @see <ul>
53   * <li><a href="http://dx.doi.org/10.1109/TASSP.1987.1165220" target="_blank"> Henrik V.
54   * Sorenson, Douglas L. Jones, Michael T. Heideman, and C. Sidney Burrus. Real-valued fast
55   * fourier fft algorithms. IEEE Transactions on Acoustics, Speech, and Signal Processing,
56   * ASSP-35(6):849–863, 1987. </a>
57   * <li><a href="http://www.jstor.org/stable/2003354" target="_blank">J. W. Cooley and J. W.
58   * Tukey, Mathematics of Computation 19 (90), 297 (1965) </a>
59   * <li><a href="http://en.wikipedia.org/wiki/Fast_Fourier_transform" target="_blank">FFT at
60   * Wikipedia </a>
61   * </ul>
62   * @since 1.0
63   */
64  public class Real {
65  
66    private final Complex complexFFT;
67    private final int n;
68    private final int halfN;
69    private final double cosTheta;
70    private final double sinTheta;
71    private final double[] work;
72  
73    /**
74     * Constructs a Complex FFT of length (n / 2) for real data of length n.
75     *
76     * @param n the length of the real data.
77     */
78    public Real(int n) {
79      this.n = n;
80      halfN = n / 2;
81      double theta = PI / halfN;
82      cosTheta = cos(theta);
83      sinTheta = sin(theta);
84      complexFFT = new Complex(halfN);
85      work = new double[n + 2];
86    }
87  
88    /**
89     * fft
90     *
91     * @param data   Input data.
92     * @param offset Offset to the beginning of the data.
93     */
94    public void fft(double[] data, int offset) {
95      complexFFT.fft(data, offset, 2);
96      unpack(data, offset);
97    }
98  
99    /**
100    * ifft
101    *
102    * @param data   Input data.
103    * @param offset Offset to the beginning of the data.
104    */
105   public void ifft(double[] data, int offset) {
106     pack(data, offset);
107     complexFFT.ifft(data, offset, 2);
108     // Renormalize the 1/2 length fft.
109     int ii = offset;
110     for (int i = 0; i < n; i++) {
111       data[ii++] *= 2.0;
112     }
113   }
114 
115   /**
116    * inverse
117    *
118    * @param data   Input data.
119    * @param offset Offset to the beginning of the data.
120    */
121   public void inverse(double[] data, int offset) {
122     ifft(data, offset);
123 
124     // normalize inverse FFT with 1/n.
125     double norm = normalization();
126     for (int i = 0; i < n; i++) {
127       final int index = offset + i;
128       data[index] *= norm;
129     }
130   }
131 
132   /**
133    * Return the normalization factor. Multiply the elements of the back-transformed data to get the
134    * normalized inverse.
135    *
136    * @return a double.
137    */
138   private double normalization() {
139     return 1.0 / n;
140   }
141 
142   /**
143    * Unpack following the forward Complex FFT.
144    *
145    * @param data   Input data.
146    * @param offset Offset to the beginning of the data.
147    */
148   private void unpack(double[] data, int offset) {
149     arraycopy(data, offset, work, 0, n);
150     double wrs = cosTheta;
151     double wis = -sinTheta;
152     final double d0 = work[0];
153     final double d1 = work[1];
154     final int on = offset + n;
155     final int o1 = offset + 1;
156     data[offset] = d0 + d1;
157     data[o1] = 0.0;
158     data[on] = d0 - d1;
159     data[on + 1] = 0.0;
160     double wr = wrs;
161     double wi = wis;
162     for (int i = 1; i < halfN; i++) {
163       int i1 = 2 * i;
164       double rk = work[i1];
165       double ik = work[i1 + 1];
166       int i2 = n - 2 * i;
167       double rn = work[i2];
168       double in = work[i2 + 1];
169       double s1r = 0.5 * (rk + rn);
170       double s1i = 0.5 * (ik - in);
171       double s2r = 0.5 * (ik + in);
172       double s2i = 0.5 * (rn - rk);
173       double fr = s1r + wr * s2r - wi * s2i;
174       double fi = s1i + wi * s2r + wr * s2i;
175       data[i1 + offset] = fr;
176       data[i1 + 1 + offset] = fi;
177       double wrp = wr;
178       wr = wr * wrs - wi * wis;
179       wi = wrp * wis + wi * wrs;
180     }
181   }
182 
183   /**
184    * Pack prior to inverse Complex FFT.
185    *
186    * @param data   Input data.
187    * @param offset Offset to the beginning of the data.
188    */
189   private void pack(double[] data, int offset) {
190     arraycopy(data, offset, work, 0, n + 2);
191     double wrs = cosTheta;
192     double wis = sinTheta;
193     double wr = 1.0;
194     double wi = 0.0;
195     for (int i = 0; i < halfN; i++) {
196       int i1 = 2 * i;
197       double fkr = work[i1];
198       double fki = work[i1 + 1];
199       int i2 = n - 2 * i;
200       double fnr = work[i2];
201       double fni = -work[i2 + 1];
202       double s1r = fkr + fnr;
203       double s1i = fki + fni;
204       double s2r = fkr - fnr;
205       double s2i = fki - fni;
206       double fr = s2r * wr - s2i * wi;
207       double fi = s2r * wi + s2i * wr;
208       data[i1 + offset] = 0.5 * (s1r - fi);
209       data[i1 + 1 + offset] = 0.5 * (s1i + fr);
210       double wrp = wr;
211       wr = wr * wrs - wi * wis;
212       wi = wrp * wis + wi * wrs;
213     }
214   }
215 }