2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2003 David van der Spoel, Erik Lindahl, University of Groningen.
5 * Copyright (c) 2013,2014, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
43 #include "external/fftpack/fftpack.h"
45 #include "gromacs/fft/fft.h"
46 #include "gromacs/utility/fatalerror.h"
47 #include "gromacs/utility/real.h"
51 * Contents of the FFTPACK fft datatype.
53 * Note that this is one of several possible implementations of gmx_fft_t.
55 * FFTPACK only does 1d transforms, so we use a pointers to another fft for
56 * the transform in the next dimension.
57 * Thus, a 3d-structure contains a pointer to a 2d one, which in turns contains
58 * a pointer to a 1d. The 1d structure has next==NULL.
61 struct gmx_fft_fftpack
66 int ndim; /**< Dimensions, including our subdimensions. */
67 int n; /**< Number of points in this dimension. */
68 int ifac[15]; /**< 15 bytes needed for cfft and rfft */
69 struct gmx_fft *next; /**< Pointer to next dimension, or NULL. */
70 real * work; /**< 1st 4n reserved for cfft, 1st 2n for rfft */
78 gmx_fft_init_1d(gmx_fft_t * pfft,
86 gmx_fatal(FARGS, "Invalid FFT opaque type pointer.");
91 if ( (fft = (struct gmx_fft *)malloc(sizeof(struct gmx_fft))) == NULL)
99 /* Need 4*n storage for 1D complex FFT */
100 if ( (fft->work = (real *)malloc(sizeof(real)*(4*nx))) == NULL)
108 fftpack_cffti1(nx, fft->work, fft->ifac);
118 gmx_fft_init_1d_real(gmx_fft_t * pfft,
120 int gmx_unused flags)
126 gmx_fatal(FARGS, "Invalid FFT opaque type pointer.");
131 if ( (fft = (struct gmx_fft *)malloc(sizeof(struct gmx_fft))) == NULL)
139 /* Need 2*n storage for 1D real FFT */
140 if ((fft->work = (real *)malloc(sizeof(real)*(2*nx))) == NULL)
148 fftpack_rffti1(nx, fft->work, fft->ifac);
156 gmx_fft_init_2d_real(gmx_fft_t * pfft,
162 int nyc = (ny/2 + 1);
167 gmx_fatal(FARGS, "Invalid FFT opaque type pointer.");
172 /* Create the X transform */
173 if ( (fft = (struct gmx_fft *)malloc(sizeof(struct gmx_fft))) == NULL)
180 /* Need 4*nx storage for 1D complex FFT, and another
181 * 2*nx*nyc elements for complex-to-real storage in our high-level routine.
183 if ( (fft->work = (real *)malloc(sizeof(real)*(4*nx+2*nx*nyc))) == NULL)
188 fftpack_cffti1(nx, fft->work, fft->ifac);
190 /* Create real Y transform as a link from X */
191 if ( (rc = gmx_fft_init_1d_real(&(fft->next), ny, flags)) != 0)
203 gmx_fft_1d (gmx_fft_t fft,
204 enum gmx_fft_direction dir,
216 p1 = (real *)in_data;
217 p2 = (real *)out_data;
222 /* FFTPACK only does in-place transforms, so emulate out-of-place
223 * by copying data to the output array first.
225 if (in_data != out_data)
227 p1 = (real *)in_data;
228 p2 = (real *)out_data;
230 /* n complex = 2*n real elements */
231 for (i = 0; i < 2*n; i++)
237 /* Elements 0 .. 2*n-1 in work are used for ffac values,
238 * Elements 2*n .. 4*n-1 are internal FFTPACK work space.
241 if (dir == GMX_FFT_FORWARD)
243 fftpack_cfftf1(n, (real *)out_data, fft->work+2*n, fft->work, fft->ifac, -1);
245 else if (dir == GMX_FFT_BACKWARD)
247 fftpack_cfftf1(n, (real *)out_data, fft->work+2*n, fft->work, fft->ifac, 1);
251 gmx_fatal(FARGS, "FFT plan mismatch - bad plan or direction.");
261 gmx_fft_1d_real (gmx_fft_t fft,
262 enum gmx_fft_direction dir,
274 p1 = (real *)in_data;
275 p2 = (real *)out_data;
277 if (dir == GMX_FFT_REAL_TO_COMPLEX)
283 if (dir == GMX_FFT_REAL_TO_COMPLEX)
285 /* FFTPACK only does in-place transforms, so emulate out-of-place
286 * by copying data to the output array first. This works fine, since
287 * the complex array must be larger than the real.
289 if (in_data != out_data)
291 p1 = (real *)in_data;
292 p2 = (real *)out_data;
294 for (i = 0; i < 2*(n/2+1); i++)
300 /* Elements 0 .. n-1 in work are used for ffac values,
301 * Elements n .. 2*n-1 are internal FFTPACK work space.
303 fftpack_rfftf1(n, (real *)out_data, fft->work+n, fft->work, fft->ifac);
306 * FFTPACK has a slightly more compact storage than we, time to
307 * convert it: ove most of the array one step up to make room for
308 * zero imaginary parts.
310 p2 = (real *)out_data;
311 for (i = n-1; i > 0; i--)
315 /* imaginary zero freq. */
325 else if (dir == GMX_FFT_COMPLEX_TO_REAL)
327 /* FFTPACK only does in-place transforms, and we cannot just copy
328 * input to output first here since our real array is smaller than
329 * the complex one. However, since the FFTPACK complex storage format
330 * is more compact than ours (2 reals) it will fit, so compact it
331 * and copy on-the-fly to the output array.
333 p1 = (real *) in_data;
334 p2 = (real *)out_data;
337 for (i = 1; i < n; i++)
341 fftpack_rfftb1(n, (real *)out_data, fft->work+n, fft->work, fft->ifac);
345 gmx_fatal(FARGS, "FFT plan mismatch - bad plan or direction.");
354 gmx_fft_2d_real (gmx_fft_t fft,
355 enum gmx_fft_direction dir,
359 int i, j, nx, ny, nyc;
367 /* Number of complex elements in y direction */
370 work = fft->work+4*nx;
372 if (dir == GMX_FFT_REAL_TO_COMPLEX)
374 /* If we are doing an in-place transform the 2D array is already
375 * properly padded by the user, and we are all set.
377 * For out-of-place there is no array padding, but FFTPACK only
378 * does in-place FFTs internally, so we need to start by copying
379 * data from the input to the padded (larger) output array.
381 if (in_data != out_data)
383 p1 = (real *)in_data;
384 p2 = (real *)out_data;
386 for (i = 0; i < nx; i++)
388 for (j = 0; j < ny; j++)
390 p2[i*nyc*2+j] = p1[i*ny+j];
394 data = (t_complex *)out_data;
396 /* y real-to-complex FFTs */
397 for (i = 0; i < nx; i++)
399 gmx_fft_1d_real(fft->next, GMX_FFT_REAL_TO_COMPLEX, data+i*nyc, data+i*nyc);
402 /* Transform to get X data in place */
403 gmx_fft_transpose_2d(data, data, nx, nyc);
405 /* Complex-to-complex X FFTs */
406 for (i = 0; i < nyc; i++)
408 gmx_fft_1d(fft, GMX_FFT_FORWARD, data+i*nx, data+i*nx);
412 gmx_fft_transpose_2d(data, data, nyc, nx);
415 else if (dir == GMX_FFT_COMPLEX_TO_REAL)
417 /* An in-place complex-to-real transform is straightforward,
418 * since the output array must be large enough for the padding to fit.
420 * For out-of-place complex-to-real transforms we cannot just copy
421 * data to the output array, since it is smaller than the input.
422 * In this case there's nothing to do but employing temporary work data,
423 * starting at work+4*nx and using nx*nyc*2 elements.
425 if (in_data != out_data)
427 memcpy(work, in_data, sizeof(t_complex)*nx*nyc);
428 data = (t_complex *)work;
433 data = (t_complex *)out_data;
436 /* Transpose to get X arrays */
437 gmx_fft_transpose_2d(data, data, nx, nyc);
440 for (i = 0; i < nyc; i++)
442 gmx_fft_1d(fft, GMX_FFT_BACKWARD, data+i*nx, data+i*nx);
445 /* Transpose to get Y arrays */
446 gmx_fft_transpose_2d(data, data, nyc, nx);
449 for (i = 0; i < nx; i++)
451 gmx_fft_1d_real(fft->next, GMX_FFT_COMPLEX_TO_REAL, data+i*nyc, data+i*nyc);
454 if (in_data != out_data)
456 /* Output (pointed to by data) is now in padded format.
457 * Pack it into out_data if we were doing an out-of-place transform.
460 p2 = (real *)out_data;
462 for (i = 0; i < nx; i++)
464 for (j = 0; j < ny; j++)
466 p2[i*ny+j] = p1[i*nyc*2+j];
473 gmx_fatal(FARGS, "FFT plan mismatch - bad plan or direction.");
481 gmx_fft_destroy(gmx_fft_t fft)
486 if (fft->next != NULL)
488 gmx_fft_destroy(fft->next);
494 void gmx_fft_cleanup()
498 const char *gmx_fft_get_version_info()
500 return "fftpack (built-in)";