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  /**
41   * Compute the 3D FFT of real, double precision input of arbitrary dimensions.
42   *
43   * <p>
44   *
45   * @author Michal J. Schnieders
46   * @see Real
47   * @since 1.0
48   */
49  public class Real3D {
50  
51    private final int nextX, nextY, nextZ;
52    private final int nX, nY, nZ;
53    private final int nX1, nZ2;
54    private final double[] work;
55    private final double[] recip;
56    private final Real fftX;
57    private final Complex fftY, fftZ;
58  
59    /**
60     * Initialize the 3D FFT for complex 3D matrix.
61     *
62     * @param nX X-dimension.
63     * @param nY Y-dimension.
64     * @param nZ Z-dimension.
65     */
66    public Real3D(int nX, int nY, int nZ) {
67      this.nX = nX / 2;
68      this.nY = nY;
69      this.nZ = nZ;
70      nX1 = this.nX + 1;
71      nZ2 = 2 * nZ;
72      nextX = 2;
73      nextY = nX + 2;
74      nextZ = nextY * nY;
75      work = new double[nZ2];
76      recip = new double[nX1 * nY * nZ];
77      fftX = new Real(nX);
78      fftY = new Complex(nY);
79      fftZ = new Complex(nZ);
80    }
81  
82    /**
83     * Determine the index of the real number in the 1D array from the X, Y and Z indices.
84     *
85     * @param i the index along the X-axis.
86     * @param j the index along the Y-axis.
87     * @param k the index along the Z-axis.
88     * @param nX the number of points along the X-axis.
89     * @param nY the number of points along the Y-axis.
90     * @return the index of the real number in the 1D array.
91     */
92    public static int iReal3D(int i, int j, int k, int nX, int nY) {
93      int xSide = nX + 2;
94      int xySlice = xSide * nY;
95      return i + j * xSide + k * xySlice;
96    }
97  
98    /**
99     * convolution
100    *
101    * @param input an array of double.
102    */
103   public void convolution(final double[] input) {
104     for (int z = 0; z < nZ; z++) {
105       for (int offset = z * nextZ, y = 0; y < nY; y++, offset += nextY) {
106         fftX.fft(input, offset);
107       }
108       for (int offset = z * nextZ, x = 0; x < nX1; x++, offset += nextX) {
109         fftY.fft(input, offset, nextY);
110       }
111     }
112     for (int index = 0, offset = 0, y = 0; y < nY; y++) {
113       for (int x = 0; x < nX1; x++, offset += nextX) {
114         for (int i = 0, z = offset; i < nZ2; i += 2, z += nextZ) {
115           work[i] = input[z];
116           work[i + 1] = input[z + 1];
117         }
118         fftZ.fft(work, 0, 2);
119         for (int i = 0; i < nZ2; i += 2) {
120           double r = recip[index++];
121           work[i] *= r;
122           work[i + 1] *= r;
123         }
124         fftZ.ifft(work, 0, 2);
125         for (int i = 0, z = offset; i < nZ2; i += 2, z += nextZ) {
126           input[z] = work[i];
127           input[z + 1] = work[i + 1];
128         }
129       }
130     }
131     for (int z = 0; z < nZ; z++) {
132       for (int offset = z * nextZ, x = 0; x < nX1; x++, offset += nextX) {
133         fftY.ifft(input, offset, nextY);
134       }
135       for (int offset = z * nextZ, y = 0; y < nY; y++, offset += nextY) {
136         fftX.ifft(input, offset);
137       }
138     }
139   }
140 
141   /**
142    * Compute the 3D FFT.
143    *
144    * @param input The input array must be of size (nX + 2) * nY * nZ.
145    */
146   public void fft(final double[] input) {
147     for (int z = 0; z < nZ; z++) {
148       for (int offset = z * nextZ, y = 0; y < nY; y++, offset += nextY) {
149         fftX.fft(input, offset);
150       }
151       for (int offset = z * nextZ, x = 0; x < nX1; x++, offset += nextX) {
152         fftY.fft(input, offset, nextY);
153       }
154     }
155     for (int x = 0; x < nX1; x++) {
156       for (int offset = x * 2, y = 0; y < nY; y++, offset += nextY) {
157         for (int i = 0, z = offset; i < nZ2; i += 2, z += nextZ) {
158           work[i] = input[z];
159           work[i + 1] = input[z + 1];
160         }
161         fftZ.fft(work, 0, 2);
162         for (int i = 0, z = offset; i < nZ2; i += 2, z += nextZ) {
163           input[z] = work[i];
164           input[z + 1] = work[i + 1];
165         }
166       }
167     }
168   }
169 
170   /**
171    * Compute the inverse 3D FFT.
172    *
173    * @param input The input array must be of size (nX + 2) * nY * nZ.
174    */
175   public void ifft(final double[] input) {
176     for (int x = 0; x < nX1; x++) {
177       for (int offset = x * 2, y = 0; y < nY; y++, offset += nextY) {
178         for (int i = 0, z = offset; i < nZ2; i += 2, z += nextZ) {
179           work[i] = input[z];
180           work[i + 1] = input[z + 1];
181         }
182         fftZ.ifft(work, 0, 2);
183         for (int i = 0, z = offset; i < nZ2; i += 2, z += nextZ) {
184           input[z] = work[i];
185           input[z + 1] = work[i + 1];
186         }
187       }
188     }
189     for (int z = 0; z < nZ; z++) {
190       for (int offset = z * nextZ, x = 0; x < nX1; x++, offset += nextX) {
191         fftY.ifft(input, offset, nextY);
192       }
193       for (int offset = z * nextZ, y = 0; y < nY; y++, offset += nextY) {
194         fftX.ifft(input, offset);
195       }
196     }
197   }
198 
199   /**
200    * Setter for the field <code>recip</code>.
201    *
202    * @param recip an array of double.
203    */
204   public void setRecip(double[] recip) {
205     // Reorder the reciprocal space data into the order it is needed by the convolution routine.
206     for (int index = 0, offset = 0, y = 0; y < nY; y++) {
207       for (int x = 0; x < nX1; x++, offset += 1) {
208         for (int i = 0, z = offset; i < nZ; i++, z += nX1 * nY) {
209           this.recip[index++] = recip[z];
210         }
211       }
212     }
213   }
214 }