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,2015,2016,2017, 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.
45 #include "external/fftpack/fftpack.h"
47 #include "gromacs/fft/fft.h"
48 #include "gromacs/utility/fatalerror.h"
49 #include "gromacs/utility/real.h"
53 * Contents of the FFTPACK fft datatype.
55 * Note that this is one of several possible implementations of gmx_fft_t.
57 * FFTPACK only does 1d transforms, so we use a pointers to another fft for
58 * the transform in the next dimension.
59 * Thus, a 3d-structure contains a pointer to a 2d one, which in turns contains
60 * a pointer to a 1d. The 1d structure has next==NULL.
63 struct gmx_fft_fftpack
68 int ndim
; /**< Dimensions, including our subdimensions. */
69 int n
; /**< Number of points in this dimension. */
70 int ifac
[15]; /**< 15 bytes needed for cfft and rfft */
71 struct gmx_fft
*next
; /**< Pointer to next dimension, or NULL. */
72 real
* work
; /**< 1st 4n reserved for cfft, 1st 2n for rfft */
76 gmx_fft_init_1d(gmx_fft_t
* pfft
,
84 gmx_fatal(FARGS
, "Invalid FFT opaque type pointer.");
89 if ( (fft
= (struct gmx_fft
*)malloc(sizeof(struct gmx_fft
))) == nullptr)
97 /* Need 4*n storage for 1D complex FFT */
98 if ( (fft
->work
= (real
*)malloc(sizeof(real
)*(4*nx
))) == nullptr)
106 fftpack_cffti1(nx
, fft
->work
, fft
->ifac
);
116 gmx_fft_init_1d_real(gmx_fft_t
* pfft
,
118 int gmx_unused flags
)
124 gmx_fatal(FARGS
, "Invalid FFT opaque type pointer.");
129 if ( (fft
= (struct gmx_fft
*)malloc(sizeof(struct gmx_fft
))) == nullptr)
137 /* Need 2*n storage for 1D real FFT */
138 if ((fft
->work
= (real
*)malloc(sizeof(real
)*(2*nx
))) == nullptr)
146 fftpack_rffti1(nx
, fft
->work
, fft
->ifac
);
154 gmx_fft_init_2d_real(gmx_fft_t
* pfft
,
160 int nyc
= (ny
/2 + 1);
165 gmx_fatal(FARGS
, "Invalid FFT opaque type pointer.");
170 /* Create the X transform */
171 if ( (fft
= (struct gmx_fft
*)malloc(sizeof(struct gmx_fft
))) == nullptr)
178 /* Need 4*nx storage for 1D complex FFT, and another
179 * 2*nx*nyc elements for complex-to-real storage in our high-level routine.
181 if ( (fft
->work
= (real
*)malloc(sizeof(real
)*(4*nx
+2*nx
*nyc
))) == nullptr)
186 fftpack_cffti1(nx
, fft
->work
, fft
->ifac
);
188 /* Create real Y transform as a link from X */
189 if ( (rc
= gmx_fft_init_1d_real(&(fft
->next
), ny
, flags
)) != 0)
201 gmx_fft_1d (gmx_fft_t fft
,
202 enum gmx_fft_direction dir
,
214 p1
= (real
*)in_data
;
215 p2
= (real
*)out_data
;
220 /* FFTPACK only does in-place transforms, so emulate out-of-place
221 * by copying data to the output array first.
223 if (in_data
!= out_data
)
225 p1
= (real
*)in_data
;
226 p2
= (real
*)out_data
;
228 /* n complex = 2*n real elements */
229 for (i
= 0; i
< 2*n
; i
++)
235 /* Elements 0 .. 2*n-1 in work are used for ffac values,
236 * Elements 2*n .. 4*n-1 are internal FFTPACK work space.
239 if (dir
== GMX_FFT_FORWARD
)
241 fftpack_cfftf1(n
, (real
*)out_data
, fft
->work
+2*n
, fft
->work
, fft
->ifac
, -1);
243 else if (dir
== GMX_FFT_BACKWARD
)
245 fftpack_cfftf1(n
, (real
*)out_data
, fft
->work
+2*n
, fft
->work
, fft
->ifac
, 1);
249 gmx_fatal(FARGS
, "FFT plan mismatch - bad plan or direction.");
259 gmx_fft_1d_real (gmx_fft_t fft
,
260 enum gmx_fft_direction dir
,
272 p1
= (real
*)in_data
;
273 p2
= (real
*)out_data
;
275 if (dir
== GMX_FFT_REAL_TO_COMPLEX
)
281 if (dir
== GMX_FFT_REAL_TO_COMPLEX
)
283 /* FFTPACK only does in-place transforms, so emulate out-of-place
284 * by copying data to the output array first. This works fine, since
285 * the complex array must be larger than the real.
287 if (in_data
!= out_data
)
289 p1
= (real
*)in_data
;
290 p2
= (real
*)out_data
;
292 for (i
= 0; i
< 2*(n
/2+1); i
++)
298 /* Elements 0 .. n-1 in work are used for ffac values,
299 * Elements n .. 2*n-1 are internal FFTPACK work space.
301 fftpack_rfftf1(n
, (real
*)out_data
, fft
->work
+n
, fft
->work
, fft
->ifac
);
304 * FFTPACK has a slightly more compact storage than we, time to
305 * convert it: ove most of the array one step up to make room for
306 * zero imaginary parts.
308 p2
= (real
*)out_data
;
309 for (i
= n
-1; i
> 0; i
--)
313 /* imaginary zero freq. */
323 else if (dir
== GMX_FFT_COMPLEX_TO_REAL
)
325 /* FFTPACK only does in-place transforms, and we cannot just copy
326 * input to output first here since our real array is smaller than
327 * the complex one. However, since the FFTPACK complex storage format
328 * is more compact than ours (2 reals) it will fit, so compact it
329 * and copy on-the-fly to the output array.
331 p1
= (real
*) in_data
;
332 p2
= (real
*)out_data
;
335 for (i
= 1; i
< n
; i
++)
339 fftpack_rfftb1(n
, (real
*)out_data
, fft
->work
+n
, fft
->work
, fft
->ifac
);
343 gmx_fatal(FARGS
, "FFT plan mismatch - bad plan or direction.");
352 gmx_fft_2d_real (gmx_fft_t fft
,
353 enum gmx_fft_direction dir
,
357 int i
, j
, nx
, ny
, nyc
;
365 /* Number of complex elements in y direction */
368 work
= fft
->work
+4*nx
;
370 if (dir
== GMX_FFT_REAL_TO_COMPLEX
)
372 /* If we are doing an in-place transform the 2D array is already
373 * properly padded by the user, and we are all set.
375 * For out-of-place there is no array padding, but FFTPACK only
376 * does in-place FFTs internally, so we need to start by copying
377 * data from the input to the padded (larger) output array.
379 if (in_data
!= out_data
)
381 p1
= (real
*)in_data
;
382 p2
= (real
*)out_data
;
384 for (i
= 0; i
< nx
; i
++)
386 for (j
= 0; j
< ny
; j
++)
388 p2
[i
*nyc
*2+j
] = p1
[i
*ny
+j
];
392 data
= static_cast<t_complex
*>(out_data
);
394 /* y real-to-complex FFTs */
395 for (i
= 0; i
< nx
; i
++)
397 gmx_fft_1d_real(fft
->next
, GMX_FFT_REAL_TO_COMPLEX
, data
+i
*nyc
, data
+i
*nyc
);
400 /* Transform to get X data in place */
401 gmx_fft_transpose_2d(data
, data
, nx
, nyc
);
403 /* Complex-to-complex X FFTs */
404 for (i
= 0; i
< nyc
; i
++)
406 gmx_fft_1d(fft
, GMX_FFT_FORWARD
, data
+i
*nx
, data
+i
*nx
);
410 gmx_fft_transpose_2d(data
, data
, nyc
, nx
);
413 else if (dir
== GMX_FFT_COMPLEX_TO_REAL
)
415 /* An in-place complex-to-real transform is straightforward,
416 * since the output array must be large enough for the padding to fit.
418 * For out-of-place complex-to-real transforms we cannot just copy
419 * data to the output array, since it is smaller than the input.
420 * In this case there's nothing to do but employing temporary work data,
421 * starting at work+4*nx and using nx*nyc*2 elements.
423 if (in_data
!= out_data
)
425 memcpy(work
, in_data
, sizeof(t_complex
)*nx
*nyc
);
426 data
= reinterpret_cast<t_complex
*>(work
);
431 data
= reinterpret_cast<t_complex
*>(out_data
);
434 /* Transpose to get X arrays */
435 gmx_fft_transpose_2d(data
, data
, nx
, nyc
);
438 for (i
= 0; i
< nyc
; i
++)
440 gmx_fft_1d(fft
, GMX_FFT_BACKWARD
, data
+i
*nx
, data
+i
*nx
);
443 /* Transpose to get Y arrays */
444 gmx_fft_transpose_2d(data
, data
, nyc
, nx
);
447 for (i
= 0; i
< nx
; i
++)
449 gmx_fft_1d_real(fft
->next
, GMX_FFT_COMPLEX_TO_REAL
, data
+i
*nyc
, data
+i
*nyc
);
452 if (in_data
!= out_data
)
454 /* Output (pointed to by data) is now in padded format.
455 * Pack it into out_data if we were doing an out-of-place transform.
458 p2
= (real
*)out_data
;
460 for (i
= 0; i
< nx
; i
++)
462 for (j
= 0; j
< ny
; j
++)
464 p2
[i
*ny
+j
] = p1
[i
*nyc
*2+j
];
471 gmx_fatal(FARGS
, "FFT plan mismatch - bad plan or direction.");
479 gmx_fft_destroy(gmx_fft_t fft
)
484 if (fft
->next
!= nullptr)
486 gmx_fft_destroy(fft
->next
);
492 void gmx_fft_cleanup()