1 /* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
4 * Gromacs 4.0 Copyright (c) 1991-2003
5 * David van der Spoel, Erik Lindahl, University of Groningen.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * To help us fund GROMACS development, we humbly ask that you cite
13 * the research papers on the package. Check out http://www.gromacs.org
16 * Gnomes, ROck Monsters And Chili Sauce
22 #ifdef GMX_FFT_FFTPACK
31 #include "gmx_fatal.h"
32 #include "external/fftpack/fftpack.h"
34 /** Contents of the FFTPACK fft datatype.
36 * FFTPACK only does 1d transforms, so we use a pointers to another fft for
37 * the transform in the next dimension.
38 * Thus, a 3d-structure contains a pointer to a 2d one, which in turns contains
39 * a pointer to a 1d. The 1d structure has next==NULL.
43 int ndim; /**< Dimensions, including our subdimensions. */
44 int n; /**< Number of points in this dimension. */
45 int ifac[15]; /**< 15 bytes needed for cfft and rfft */
46 struct gmx_fft *next; /**< Pointer to next dimension, or NULL. */
47 real * work; /**< 1st 4n reserved for cfft, 1st 2n for rfft */
55 gmx_fft_init_1d(gmx_fft_t * pfft,
63 gmx_fatal(FARGS,"Invalid FFT opaque type pointer.");
68 if( (fft = (struct gmx_fft *)malloc(sizeof(struct gmx_fft))) == NULL)
76 /* Need 4*n storage for 1D complex FFT */
77 if( (fft->work = (real *)malloc(sizeof(real)*(4*nx))) == NULL)
84 fftpack_cffti1(nx,fft->work,fft->ifac);
93 gmx_fft_init_1d_real(gmx_fft_t * pfft,
101 gmx_fatal(FARGS,"Invalid FFT opaque type pointer.");
106 if( (fft = (struct gmx_fft *)malloc(sizeof(struct gmx_fft))) == NULL)
114 /* Need 2*n storage for 1D real FFT */
115 if((fft->work = (real *)malloc(sizeof(real)*(2*nx)))==NULL)
122 fftpack_rffti1(nx,fft->work,fft->ifac);
131 gmx_fft_init_2d(gmx_fft_t * pfft,
141 gmx_fatal(FARGS,"Invalid FFT opaque type pointer.");
146 /* Create the X transform */
147 if( (rc = gmx_fft_init_1d(&fft,nx,flags)) != 0)
152 /* Create Y transform as a link from X */
153 if( (rc=gmx_fft_init_1d(&(fft->next),ny,flags)) != 0)
165 gmx_fft_init_2d_real(gmx_fft_t * pfft,
171 int nyc = (ny/2 + 1);
176 gmx_fatal(FARGS,"Invalid FFT opaque type pointer.");
181 /* Create the X transform */
182 if( (fft = (struct gmx_fft *)malloc(sizeof(struct gmx_fft))) == NULL)
189 /* Need 4*nx storage for 1D complex FFT, and another
190 * 2*nx*nyc elements for complex-to-real storage in our high-level routine.
192 if( (fft->work = (real *)malloc(sizeof(real)*(4*nx+2*nx*nyc))) == NULL)
197 fftpack_cffti1(nx,fft->work,fft->ifac);
199 /* Create real Y transform as a link from X */
200 if( (rc=gmx_fft_init_1d_real(&(fft->next),ny,flags)) != 0)
212 gmx_fft_init_3d(gmx_fft_t * pfft,
223 gmx_fatal(FARGS,"Invalid FFT opaque type pointer.");
228 /* Create the X transform */
230 if( (fft = (struct gmx_fft *)malloc(sizeof(struct gmx_fft))) == NULL)
237 /* Need 4*nx storage for 1D complex FFT, and another
238 * 2*nz elements for gmx_fft_transpose_2d_nelem() storage.
240 if( (fft->work = (real *)malloc(sizeof(real)*(4*nx+2*nz))) == NULL)
246 fftpack_cffti1(nx,fft->work,fft->ifac);
249 /* Create 2D Y/Z transforms as a link from X */
250 if( (rc=gmx_fft_init_2d(&(fft->next),ny,nz,flags)) != 0)
262 gmx_fft_init_3d_real(gmx_fft_t * pfft,
269 int nzc = (nz/2 + 1);
274 gmx_fatal(FARGS,"Invalid FFT opaque type pointer.");
279 /* Create the X transform */
280 if( (fft = (struct gmx_fft *)malloc(sizeof(struct gmx_fft))) == NULL)
287 /* Need 4*nx storage for 1D complex FFT, another
288 * 2*nx*ny*nzc elements to copy the entire 3D matrix when
289 * doing out-of-place complex-to-real FFTs, and finally
290 * 2*nzc elements for transpose work space.
292 if( (fft->work = (real *)malloc(sizeof(real)*(4*nx+2*nx*ny*nzc+2*nzc))) == NULL)
297 fftpack_cffti1(nx,fft->work,fft->ifac);
299 /* Create 2D real Y/Z transform as a link from X */
300 if( (rc=gmx_fft_init_2d_real(&(fft->next),ny,nz,flags)) != 0)
312 gmx_fft_1d (gmx_fft_t fft,
313 enum gmx_fft_direction dir,
325 p1 = (real *)in_data;
326 p2 = (real *)out_data;
331 /* FFTPACK only does in-place transforms, so emulate out-of-place
332 * by copying data to the output array first.
334 if( in_data != out_data )
336 p1 = (real *)in_data;
337 p2 = (real *)out_data;
339 /* n complex = 2*n real elements */
346 /* Elements 0 .. 2*n-1 in work are used for ffac values,
347 * Elements 2*n .. 4*n-1 are internal FFTPACK work space.
350 if(dir == GMX_FFT_FORWARD)
352 fftpack_cfftf1(n,(real *)out_data,fft->work+2*n,fft->work,fft->ifac, -1);
354 else if(dir == GMX_FFT_BACKWARD)
356 fftpack_cfftf1(n,(real *)out_data,fft->work+2*n,fft->work,fft->ifac, 1);
360 gmx_fatal(FARGS,"FFT plan mismatch - bad plan or direction.");
370 gmx_fft_1d_real (gmx_fft_t fft,
371 enum gmx_fft_direction dir,
383 p1 = (real *)in_data;
384 p2 = (real *)out_data;
386 if(dir == GMX_FFT_REAL_TO_COMPLEX)
390 if(dir == GMX_FFT_REAL_TO_COMPLEX)
392 /* FFTPACK only does in-place transforms, so emulate out-of-place
393 * by copying data to the output array first. This works fine, since
394 * the complex array must be larger than the real.
396 if( in_data != out_data )
398 p1 = (real *)in_data;
399 p2 = (real *)out_data;
401 for(i=0;i<2*(n/2+1);i++)
407 /* Elements 0 .. n-1 in work are used for ffac values,
408 * Elements n .. 2*n-1 are internal FFTPACK work space.
410 fftpack_rfftf1(n,(real *)out_data,fft->work+n,fft->work,fft->ifac);
413 * FFTPACK has a slightly more compact storage than we, time to
414 * convert it: ove most of the array one step up to make room for
415 * zero imaginary parts.
417 p2 = (real *)out_data;
422 /* imaginary zero freq. */
432 else if(dir == GMX_FFT_COMPLEX_TO_REAL)
434 /* FFTPACK only does in-place transforms, and we cannot just copy
435 * input to output first here since our real array is smaller than
436 * the complex one. However, since the FFTPACK complex storage format
437 * is more compact than ours (2 reals) it will fit, so compact it
438 * and copy on-the-fly to the output array.
440 p1 = (real *) in_data;
441 p2 = (real *)out_data;
448 fftpack_rfftb1(n,(real *)out_data,fft->work+n,fft->work,fft->ifac);
452 gmx_fatal(FARGS,"FFT plan mismatch - bad plan or direction.");
461 gmx_fft_2d (gmx_fft_t fft,
462 enum gmx_fft_direction dir,
472 /* FFTPACK only does in-place transforms, so emulate out-of-place
473 * by copying data to the output array first.
474 * For 2D there is likely enough data to benefit from memcpy().
476 if( in_data != out_data )
478 memcpy(out_data,in_data,sizeof(t_complex)*nx*ny);
481 /* Much easier to do pointer arithmetic when base has the correct type */
482 data = (t_complex *)out_data;
487 gmx_fft_1d(fft->next,dir,data+i*ny,data+i*ny);
490 /* Transpose in-place to get data in place for x transform now */
491 gmx_fft_transpose_2d(data,data,nx,ny);
496 gmx_fft_1d(fft,dir,data+i*nx,data+i*nx);
499 /* Transpose in-place to get data back in original order */
500 gmx_fft_transpose_2d(data,data,ny,nx);
508 gmx_fft_2d_real (gmx_fft_t fft,
509 enum gmx_fft_direction dir,
521 /* Number of complex elements in y direction */
524 work = fft->work+4*nx;
526 if(dir==GMX_FFT_REAL_TO_COMPLEX)
528 /* If we are doing an in-place transform the 2D array is already
529 * properly padded by the user, and we are all set.
531 * For out-of-place there is no array padding, but FFTPACK only
532 * does in-place FFTs internally, so we need to start by copying
533 * data from the input to the padded (larger) output array.
535 if( in_data != out_data )
537 p1 = (real *)in_data;
538 p2 = (real *)out_data;
544 p2[i*nyc*2+j] = p1[i*ny+j];
548 data = (t_complex *)out_data;
550 /* y real-to-complex FFTs */
553 gmx_fft_1d_real(fft->next,GMX_FFT_REAL_TO_COMPLEX,data+i*nyc,data+i*nyc);
556 /* Transform to get X data in place */
557 gmx_fft_transpose_2d(data,data,nx,nyc);
559 /* Complex-to-complex X FFTs */
562 gmx_fft_1d(fft,GMX_FFT_FORWARD,data+i*nx,data+i*nx);
566 gmx_fft_transpose_2d(data,data,nyc,nx);
569 else if(dir==GMX_FFT_COMPLEX_TO_REAL)
571 /* An in-place complex-to-real transform is straightforward,
572 * since the output array must be large enough for the padding to fit.
574 * For out-of-place complex-to-real transforms we cannot just copy
575 * data to the output array, since it is smaller than the input.
576 * In this case there's nothing to do but employing temporary work data,
577 * starting at work+4*nx and using nx*nyc*2 elements.
579 if(in_data != out_data)
581 memcpy(work,in_data,sizeof(t_complex)*nx*nyc);
582 data = (t_complex *)work;
587 data = (t_complex *)out_data;
590 /* Transpose to get X arrays */
591 gmx_fft_transpose_2d(data,data,nx,nyc);
596 gmx_fft_1d(fft,GMX_FFT_BACKWARD,data+i*nx,data+i*nx);
599 /* Transpose to get Y arrays */
600 gmx_fft_transpose_2d(data,data,nyc,nx);
605 gmx_fft_1d_real(fft->next,GMX_FFT_COMPLEX_TO_REAL,data+i*nyc,data+i*nyc);
608 if( in_data != out_data )
610 /* Output (pointed to by data) is now in padded format.
611 * Pack it into out_data if we were doing an out-of-place transform.
614 p2 = (real *)out_data;
620 p2[i*ny+j] = p1[i*nyc*2+j];
627 gmx_fatal(FARGS,"FFT plan mismatch - bad plan or direction.");
637 gmx_fft_3d (gmx_fft_t fft,
638 enum gmx_fft_direction dir,
647 nz=fft->next->next->n;
649 /* First 4*nx positions are FFTPACK workspace, then ours starts */
650 work = (t_complex *)(fft->work+4*nx);
652 /* FFTPACK only does in-place transforms, so emulate out-of-place
653 * by copying data to the output array first.
654 * For 3D there is likely enough data to benefit from memcpy().
656 if( in_data != out_data )
658 memcpy(out_data,in_data,sizeof(t_complex)*nx*ny*nz);
661 /* Much easier to do pointer arithmetic when base has the correct type */
662 data = (t_complex *)out_data;
664 /* Perform z transforms */
666 gmx_fft_1d(fft->next->next,dir,data+i*nz,data+i*nz);
668 /* For each X slice, transpose the y & z dimensions inside the slice */
671 gmx_fft_transpose_2d(data+i*ny*nz,data+i*ny*nz,ny,nz);
674 /* Array is now (nx,nz,ny) - perform y transforms */
677 gmx_fft_1d(fft->next,dir,data+i*ny,data+i*ny);
680 /* Transpose back to (nx,ny,nz) */
683 gmx_fft_transpose_2d(data+i*ny*nz,data+i*ny*nz,nz,ny);
686 /* Transpose entire x & y slices to go from
687 * (nx,ny,nz) to (ny,nx,nz).
688 * Use work data elements 4*n .. 4*n+2*nz-1.
690 rc=gmx_fft_transpose_2d_nelem(data,data,nx,ny,nz,work);
693 gmx_fatal(FARGS,"Cannot transpose X & Y/Z in gmx_fft_3d().");
697 /* Then go from (ny,nx,nz) to (ny,nz,nx) */
700 gmx_fft_transpose_2d(data+i*nx*nz,data+i*nx*nz,nx,nz);
703 /* Perform x transforms */
706 gmx_fft_1d(fft,dir,data+i*nx,data+i*nx);
709 /* Transpose back from (ny,nz,nx) to (ny,nx,nz) */
712 gmx_fft_transpose_2d(data+i*nz*nx,data+i*nz*nx,nz,nx);
715 /* Transpose from (ny,nx,nz) to (nx,ny,nz)
716 * Use work data elements 4*n .. 4*n+2*nz-1.
718 rc = gmx_fft_transpose_2d_nelem(data,data,ny,nx,nz,work);
721 gmx_fatal(FARGS,"Cannot transpose Y/Z & X in gmx_fft_3d().");
730 gmx_fft_3d_real (gmx_fft_t fft,
731 enum gmx_fft_direction dir,
738 t_complex * work_transp;
739 t_complex * work_c2r;
745 nz=fft->next->next->n;
749 /* First 4*nx positions are FFTPACK workspace, then ours starts.
750 * We have 2*nx*ny*nzc elements for temp complex-to-real storage when
751 * doing out-of-place transforms, and another 2*nzc for transpose data.
753 work_c2r = (t_complex *)(fft->work+4*nx);
754 work_transp = (t_complex *)(fft->work+4*nx+2*nx*ny*nzc);
756 /* Much easier to do pointer arithmetic when base has the correct type */
757 data = (t_complex *)out_data;
759 if(dir==GMX_FFT_REAL_TO_COMPLEX)
761 /* FFTPACK only does in-place transforms, so emulate out-of-place
762 * by copying data to the output array first. This is guaranteed to
763 * work for real-to-complex since complex data is larger than the real.
764 * For 3D there is likely enough data to benefit from memcpy().
766 if( in_data != out_data )
768 p1 = (real *)in_data;
769 p2 = (real *)out_data;
777 p2[(i*ny+j)*2*nzc+k] = p1[(i*ny+j)*nz+k];
782 data = (t_complex *)out_data;
784 /* Transform the Y/Z slices real-to-complex */
787 gmx_fft_2d_real(fft->next,dir,data+i*ny*nzc,data+i*ny*nzc);
790 /* Transpose x & y slices to go from
791 * (nx,ny,nzc) to (ny,nx,nzc).
793 rc=gmx_fft_transpose_2d_nelem(data,data,nx,ny,nzc,work_transp);
796 gmx_fatal(FARGS,"Cannot transpose X & Y/Z gmx_fft_3d_real().");
800 /* Then transpose from (ny,nx,nzc) to (ny,nzc,nx) */
803 gmx_fft_transpose_2d(data+i*nx*nzc,data+i*nx*nzc,nx,nzc);
806 /* Perform x transforms */
807 for(i=0;i<ny*nzc;i++)
809 gmx_fft_1d(fft,GMX_FFT_FORWARD,data+i*nx,data+i*nx);
812 /* Transpose from (ny,nzc,nx) back to (ny,nx,nzc) */
815 gmx_fft_transpose_2d(data+i*nzc*nx,data+i*nzc*nx,nzc,nx);
818 /* Transpose back from (ny,nx,nzc) to (nx,ny,nz) */
819 rc=gmx_fft_transpose_2d_nelem(data,data,ny,nx,nzc,work_transp);
822 gmx_fatal(FARGS,"Cannot transpose Y/Z & X in gmx_fft_3d_real().");
827 else if(dir==GMX_FFT_COMPLEX_TO_REAL)
829 /* An in-place complex-to-real transform is straightforward,
830 * since the output array must be large enough for the padding to fit.
832 * For out-of-place complex-to-real transforms we cannot just copy
833 * data to the output array, since it is smaller than the input.
834 * In this case there's nothing to do but employing temporary work data.
836 if(in_data != out_data)
838 memcpy(work_c2r,in_data,sizeof(t_complex)*nx*ny*nzc);
839 data = (t_complex *)work_c2r;
844 data = (t_complex *)out_data;
847 /* Transpose x & y slices to go from
848 * (nx,ny,nz) to (ny,nx,nz).
850 gmx_fft_transpose_2d_nelem(data,data,nx,ny,nzc,work_transp);
852 /* Then go from (ny,nx,nzc) to (ny,nzc,nx) */
855 gmx_fft_transpose_2d(data+i*nx*nzc,data+i*nx*nzc,nx,nzc);
859 /* Perform x transforms */
860 for(i=0;i<ny*nzc;i++)
862 gmx_fft_1d(fft,GMX_FFT_BACKWARD,data+i*nx,data+i*nx);
865 /* Transpose back from (ny,nzc,nx) to (ny,nx,nzc) */
868 gmx_fft_transpose_2d(data+i*nzc*nx,data+i*nzc*nx,nzc,nx);
871 /* Transpose back from (ny,nx,nzc) to (nx,ny,nz) */
872 gmx_fft_transpose_2d_nelem(data,data,ny,nx,nzc,work_transp);
875 /* Do 2D complex-to-real */
878 gmx_fft_2d_real(fft->next,dir,data+i*ny*nzc,data+i*ny*nzc);
881 if( in_data != out_data )
883 /* Output (pointed to by data) is now in padded format.
884 * Pack it into out_data if we were doing an out-of-place transform.
887 p2 = (real *)out_data;
895 p2[(i*ny+j)*nz+k] = p1[(i*ny+j)*nzc*2+k];
904 gmx_fatal(FARGS,"FFT plan mismatch - bad plan or direction.");
915 gmx_fft_destroy(gmx_fft_t fft)
920 if(fft->next != NULL)
921 gmx_fft_destroy(fft->next);
925 #endif /* GMX_FFT_FFTPACK */