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
30 #include "gmx_fatal.h"
34 #ifdef FFTW2_NAME_FFTW
37 #elif defined FFTW2_NAME_SFFTW
40 #elif defined FFTW2_NAME_DFFTW
44 #error No FFTW2 name defined - you must define one of:
45 #error FFTW2_NAME_FFTW, FFTW2_NAME_SFFTW, FFTW2_NAME_DFFTW
49 /* Contents of the FFTW2 setup */
52 int ndim
; /**< Number of dimensions in transform. */
53 int nx
; /**< Data X dimension */
54 int ny
; /**< Data Y dimension */
55 int nz
; /**< Data Z dimension */
56 /* Arrays with fftw2 plans.
57 * First index is 0 for out-of-place, 1 for in-place transform.
58 * Second index is 0 for backward, 1 for forward.
60 fftw_plan single
[2][2]; /**< Plans for 1d transforms. */
61 fftwnd_plan multi
[2][2]; /**< Plans for n-d transforms. */
62 real
* work
; /**< Avoid overwriting input for c2r ffts */
68 gmx_fft_init_1d(gmx_fft_t
* pfft
,
70 enum gmx_fft_flag flags
)
76 /* FFTW2 is slow to measure, so we do not use it */
78 /* If you change this, add an #ifndef for GMX_DISABLE_FFTW_MEASURE around it! */
79 fftw_flags
= FFTW_ESTIMATE
;
83 gmx_fatal(FARGS
,"Invalid opaque FFT datatype pointer.");
88 if( (fft
= (gmx_fft_t
)malloc(sizeof(struct gmx_fft
))) == NULL
)
94 fft
->single
[0][0] = fftw_create_plan(nx
,FFTW_BACKWARD
,FFTW_OUT_OF_PLACE
|fftw_flags
);
95 fft
->single
[0][1] = fftw_create_plan(nx
,FFTW_FORWARD
,FFTW_OUT_OF_PLACE
|fftw_flags
);
96 fft
->single
[1][0] = fftw_create_plan(nx
,FFTW_BACKWARD
,FFTW_IN_PLACE
|fftw_flags
);
97 fft
->single
[1][1] = fftw_create_plan(nx
,FFTW_FORWARD
,FFTW_IN_PLACE
|fftw_flags
);
100 fft
->multi
[0][0] = NULL
;
101 fft
->multi
[0][1] = NULL
;
102 fft
->multi
[1][0] = NULL
;
103 fft
->multi
[1][1] = NULL
;
109 if(fft
->single
[i
][j
] == NULL
)
111 gmx_fatal(FARGS
,"Error initializing FFTW2 plan.");
112 gmx_fft_destroy(fft
);
118 /* No workspace needed for complex-to-complex FFTs */
131 gmx_fft_init_1d_real(gmx_fft_t
* pfft
,
133 enum gmx_fft_flag flags
)
139 /* FFTW2 is slow to measure, so we do not use it */
140 /* If you change this, add an #ifndef for GMX_DISABLE_FFTW_MEASURE around it! */
141 fftw_flags
= FFTW_ESTIMATE
;
145 gmx_fatal(FARGS
,"Invalid opaque FFT datatype pointer.");
150 if( (fft
= (gmx_fft_t
)malloc(sizeof(struct gmx_fft
))) == NULL
)
156 fft
->single
[0][0] = rfftw_create_plan(nx
,FFTW_COMPLEX_TO_REAL
,FFTW_OUT_OF_PLACE
|fftw_flags
);
157 fft
->single
[0][1] = rfftw_create_plan(nx
,FFTW_REAL_TO_COMPLEX
,FFTW_OUT_OF_PLACE
|fftw_flags
);
158 fft
->single
[1][0] = rfftw_create_plan(nx
,FFTW_COMPLEX_TO_REAL
,FFTW_IN_PLACE
|fftw_flags
);
159 fft
->single
[1][1] = rfftw_create_plan(nx
,FFTW_REAL_TO_COMPLEX
,FFTW_IN_PLACE
|fftw_flags
);
162 fft
->multi
[0][0] = NULL
;
163 fft
->multi
[0][1] = NULL
;
164 fft
->multi
[1][0] = NULL
;
165 fft
->multi
[1][1] = NULL
;
171 if(fft
->single
[i
][j
] == NULL
)
173 gmx_fatal(FARGS
,"Error initializing FFTW2 plan.");
174 gmx_fft_destroy(fft
);
180 /* FFTW2 overwrites the input when doing out-of-place complex-to-real FFTs.
181 * This is not acceptable for the Gromacs interface, so we define a
182 * work array and copy the data there before doing complex-to-real FFTs.
184 fft
->work
= (real
*)malloc(sizeof(real
)*( (nx
/2 + 1)*2) );
185 if(fft
->work
== NULL
)
187 gmx_fatal(FARGS
,"Cannot allocate complex-to-real FFT workspace.");
188 gmx_fft_destroy(fft
);
202 gmx_fft_init_2d(gmx_fft_t
* pfft
,
205 enum gmx_fft_flag flags
)
212 /* FFTW2 is slow to measure, so we do not use it */
213 /* If you change this, add an #ifndef for GMX_DISABLE_FFTW_MEASURE around it! */
214 fftw_flags
= FFTW_ESTIMATE
;
218 gmx_fatal(FARGS
,"Invalid opaque FFT datatype pointer.");
223 if( (fft
= (gmx_fft_t
)malloc(sizeof(struct gmx_fft
))) == NULL
)
228 fft
->single
[0][0] = NULL
;
229 fft
->single
[0][1] = NULL
;
230 fft
->single
[1][0] = NULL
;
231 fft
->single
[1][1] = NULL
;
233 fft
->multi
[0][0] = fftw2d_create_plan(nx
,ny
,FFTW_BACKWARD
,FFTW_OUT_OF_PLACE
|fftw_flags
);
234 fft
->multi
[0][1] = fftw2d_create_plan(nx
,ny
,FFTW_FORWARD
,FFTW_OUT_OF_PLACE
|fftw_flags
);
235 fft
->multi
[1][0] = fftw2d_create_plan(nx
,ny
,FFTW_BACKWARD
,FFTW_IN_PLACE
|fftw_flags
);
236 fft
->multi
[1][1] = fftw2d_create_plan(nx
,ny
,FFTW_FORWARD
,FFTW_IN_PLACE
|fftw_flags
);
242 if(fft
->multi
[i
][j
] == NULL
)
244 gmx_fatal(FARGS
,"Error initializing FFTW2 plan.");
245 gmx_fft_destroy(fft
);
251 /* No workspace needed for complex-to-complex FFTs */
266 gmx_fft_init_2d_real(gmx_fft_t
* pfft
,
269 enum gmx_fft_flag flags
)
276 /* FFTW2 is slow to measure, so we do not use it */
277 /* If you change this, add an #ifndef for GMX_DISABLE_FFTW_MEASURE around it! */
278 fftw_flags
= FFTW_ESTIMATE
;
282 gmx_fatal(FARGS
,"Invalid opaque FFT datatype pointer.");
287 if( (fft
= (gmx_fft_t
)malloc(sizeof(struct gmx_fft
))) == NULL
)
292 fft
->single
[0][0] = NULL
;
293 fft
->single
[0][1] = NULL
;
294 fft
->single
[1][0] = NULL
;
295 fft
->single
[1][1] = NULL
;
298 fft
->multi
[0][0] = rfftw2d_create_plan(nx
,ny
,FFTW_COMPLEX_TO_REAL
,FFTW_OUT_OF_PLACE
|fftw_flags
);
299 fft
->multi
[0][1] = rfftw2d_create_plan(nx
,ny
,FFTW_REAL_TO_COMPLEX
,FFTW_OUT_OF_PLACE
|fftw_flags
);
300 fft
->multi
[1][0] = rfftw2d_create_plan(nx
,ny
,FFTW_COMPLEX_TO_REAL
,FFTW_IN_PLACE
|fftw_flags
);
301 fft
->multi
[1][1] = rfftw2d_create_plan(nx
,ny
,FFTW_REAL_TO_COMPLEX
,FFTW_IN_PLACE
|fftw_flags
);
308 if(fft
->multi
[i
][j
] == NULL
)
310 gmx_fatal(FARGS
,"Error initializing FFTW2 plan.");
311 gmx_fft_destroy(fft
);
317 /* FFTW2 overwrites the input when doing out-of-place complex-to-real FFTs.
318 * This is not acceptable for the Gromacs interface, so we define a
319 * work array and copy the data there before doing complex-to-real FFTs.
321 fft
->work
= (real
*)malloc(sizeof(real
)*( nx
*(ny
/2 + 1)*2) );
322 if(fft
->work
== NULL
)
324 gmx_fatal(FARGS
,"Cannot allocate complex-to-real FFT workspace.");
325 gmx_fft_destroy(fft
);
340 gmx_fft_init_3d(gmx_fft_t
* pfft
,
344 enum gmx_fft_flag flags
)
351 /* FFTW2 is slow to measure, so we do not use it */
352 /* If you change this, add an #ifndef for GMX_DISABLE_FFTW_MEASURE around it! */
353 fftw_flags
= FFTW_ESTIMATE
;
357 gmx_fatal(FARGS
,"Invalid opaque FFT datatype pointer.");
362 if( (fft
= (gmx_fft_t
)malloc(sizeof(struct gmx_fft
))) == NULL
)
367 fft
->single
[0][0] = NULL
;
368 fft
->single
[0][1] = NULL
;
369 fft
->single
[1][0] = NULL
;
370 fft
->single
[1][1] = NULL
;
373 fft
->multi
[0][0] = fftw3d_create_plan(nx
,ny
,nz
,FFTW_BACKWARD
,FFTW_OUT_OF_PLACE
|fftw_flags
);
374 fft
->multi
[0][1] = fftw3d_create_plan(nx
,ny
,nz
,FFTW_FORWARD
,FFTW_OUT_OF_PLACE
|fftw_flags
);
375 fft
->multi
[1][0] = fftw3d_create_plan(nx
,ny
,nz
,FFTW_BACKWARD
,FFTW_IN_PLACE
|fftw_flags
);
376 fft
->multi
[1][1] = fftw3d_create_plan(nx
,ny
,nz
,FFTW_FORWARD
,FFTW_IN_PLACE
|fftw_flags
);
383 if(fft
->multi
[i
][j
] == NULL
)
385 gmx_fatal(FARGS
,"Error initializing FFTW2 plan.");
386 gmx_fft_destroy(fft
);
392 /* No workspace needed for complex-to-complex FFTs */
408 gmx_fft_init_3d_real(gmx_fft_t
* pfft
,
412 enum gmx_fft_flag flags
)
419 /* FFTW2 is slow to measure, so we do not use it */
420 /* If you change this, add an #ifndef for GMX_DISABLE_FFTW_MEASURE around it! */
421 fftw_flags
= FFTW_ESTIMATE
;
425 gmx_fatal(FARGS
,"Invalid opaque FFT datatype pointer.");
430 if( (fft
= (gmx_fft_t
)malloc(sizeof(struct gmx_fft
))) == NULL
)
435 fft
->single
[0][0] = NULL
;
436 fft
->single
[0][1] = NULL
;
437 fft
->single
[1][0] = NULL
;
438 fft
->single
[1][1] = NULL
;
441 fft
->multi
[0][0] = rfftw3d_create_plan(nx
,ny
,nz
,FFTW_COMPLEX_TO_REAL
,FFTW_OUT_OF_PLACE
|fftw_flags
);
442 fft
->multi
[0][1] = rfftw3d_create_plan(nx
,ny
,nz
,FFTW_REAL_TO_COMPLEX
,FFTW_OUT_OF_PLACE
|fftw_flags
);
443 fft
->multi
[1][0] = rfftw3d_create_plan(nx
,ny
,nz
,FFTW_COMPLEX_TO_REAL
,FFTW_IN_PLACE
|fftw_flags
);
444 fft
->multi
[1][1] = rfftw3d_create_plan(nx
,ny
,nz
,FFTW_REAL_TO_COMPLEX
,FFTW_IN_PLACE
|fftw_flags
);
451 if(fft
->multi
[i
][j
] == NULL
)
453 gmx_fatal(FARGS
,"Error initializing FFTW2 plan.");
454 gmx_fft_destroy(fft
);
460 /* FFTW2 overwrites the input when doing out-of-place complex-to-real FFTs.
461 * This is not acceptable for the Gromacs interface, so we define a
462 * work array and copy the data there before doing complex-to-real FFTs.
464 fft
->work
= (real
*)malloc(sizeof(real
)*( nx
*ny
*(nz
/2 + 1)*2) );
465 if(fft
->work
== NULL
)
467 gmx_fatal(FARGS
,"Cannot allocate complex-to-real FFT workspace.");
468 gmx_fft_destroy(fft
);
483 gmx_fft_1d(gmx_fft_t fft
,
484 enum gmx_fft_direction dir
,
488 int inplace
= (in_data
== out_data
);
489 int isforward
= (dir
== GMX_FFT_FORWARD
);
491 if((fft
->ndim
!= 1) ||
492 ((dir
!= GMX_FFT_FORWARD
) && (dir
!= GMX_FFT_BACKWARD
)))
494 gmx_fatal(FARGS
,"FFT plan mismatch - bad plan or direction.");
498 fftw_one(fft
->single
[inplace
][isforward
],(fftw_complex
*)in_data
,(fftw_complex
*)out_data
);
504 gmx_fft_1d_real(gmx_fft_t fft
,
505 enum gmx_fft_direction dir
,
509 /* FFTW2 1-dimensional real transforms are special.
511 * First, the complex data is stored in a special packed half-complex
512 * fashion. To enable a standard common Gromacs interface this forces us
513 * to always use out-of-place FFTs, and permute the data after
514 * real-to-complex FFTs or before complex-to-real FFTs.
516 * The input is also destroyed for out-of-place complex-to-real FFTs, but
517 * this doesn't matter since we need to permute and copy the data into
518 * the work array first anyway.
520 real
* work
= fft
->work
;
525 if((fft
->ndim
!= 1) ||
526 ((dir
!= GMX_FFT_REAL_TO_COMPLEX
) && (dir
!= GMX_FFT_COMPLEX_TO_REAL
)))
528 gmx_fatal(FARGS
,"FFT plan mismatch - bad plan or direction.");
532 if(dir
==GMX_FFT_REAL_TO_COMPLEX
)
534 rfftw_one(fft
->single
[0][1],(fftw_real
*)in_data
,(fftw_real
*)work
);
535 /* permute it back into data, in standard complex format
536 * instead of halfcomplex...
538 data
= (t_complex
*)out_data
;
540 data
[0].re
= work
[0];
545 data
[i
].re
= work
[i
];
546 data
[i
].im
= work
[n
-i
];
557 data
[i
].im
=work
[n
-i
];
562 /* Complex-to-real. First permute standard format into halfcomplex */
563 data
= (t_complex
*)in_data
;
570 work
[n
-i
]=data
[i
].im
;
575 work
[n
-i
]=data
[i
].im
;
578 rfftw_one(fft
->single
[0][0],(fftw_real
*)work
,(fftw_real
*)out_data
);
586 gmx_fft_2d(gmx_fft_t fft
,
587 enum gmx_fft_direction dir
,
591 int inplace
= (in_data
== out_data
);
592 int isforward
= (dir
== GMX_FFT_FORWARD
);
594 if((fft
->ndim
!= 2) ||
595 ((dir
!= GMX_FFT_FORWARD
) && (dir
!= GMX_FFT_BACKWARD
)))
597 gmx_fatal(FARGS
,"FFT plan mismatch - bad plan or direction.");
601 fftwnd_one(fft
->multi
[inplace
][isforward
],(fftw_complex
*)in_data
,(fftw_complex
*)out_data
);
608 gmx_fft_2d_real(gmx_fft_t fft
,
609 enum gmx_fft_direction dir
,
613 int inplace
= (in_data
== out_data
);
614 int isforward
= (dir
== GMX_FFT_REAL_TO_COMPLEX
);
617 if((fft
->ndim
!= 2) ||
618 ((dir
!= GMX_FFT_REAL_TO_COMPLEX
) && (dir
!= GMX_FFT_COMPLEX_TO_REAL
)))
620 gmx_fatal(FARGS
,"FFT plan mismatch - bad plan or direction.");
626 /* Copy data to avoid overwriting input, and redirect input ptr to work array */
627 sz
= fft
->nx
*(fft
->ny
/2 + 1)*2;
628 memcpy(fft
->work
,in_data
,sz
*sizeof(real
));
634 rfftwnd_one_real_to_complex(fft
->multi
[inplace
][isforward
],(fftw_real
*)in_data
,(fftw_complex
*)out_data
);
638 rfftwnd_one_complex_to_real(fft
->multi
[inplace
][isforward
],(fftw_complex
*)in_data
,(fftw_real
*)out_data
);
646 gmx_fft_3d(gmx_fft_t fft
,
647 enum gmx_fft_direction dir
,
651 int inplace
= (in_data
== out_data
);
652 int isforward
= (dir
== GMX_FFT_FORWARD
);
654 if((fft
->ndim
!= 3) ||
655 ((dir
!= GMX_FFT_FORWARD
) && (dir
!= GMX_FFT_BACKWARD
)))
657 gmx_fatal(FARGS
,"FFT plan mismatch - bad plan or direction.");
661 fftwnd_one(fft
->multi
[inplace
][isforward
],(fftw_complex
*)in_data
,(fftw_complex
*)out_data
);
668 gmx_fft_3d_real(gmx_fft_t fft
,
669 enum gmx_fft_direction dir
,
673 int inplace
= (in_data
== out_data
);
674 int isforward
= (dir
== GMX_FFT_REAL_TO_COMPLEX
);
677 if((fft
->ndim
!= 3) ||
678 ((dir
!= GMX_FFT_REAL_TO_COMPLEX
) && (dir
!= GMX_FFT_COMPLEX_TO_REAL
)))
680 gmx_fatal(FARGS
,"FFT plan mismatch - bad plan or direction.");
686 /* Copy data to avoid overwriting input, and redirect input ptr to work array */
687 sz
= fft
->nx
*fft
->ny
*(fft
->nz
/2 + 1)*2;
688 memcpy(fft
->work
,in_data
,sz
*sizeof(real
));
694 rfftwnd_one_real_to_complex(fft
->multi
[inplace
][isforward
],(fftw_real
*)in_data
,(fftw_complex
*)out_data
);
698 rfftwnd_one_complex_to_real(fft
->multi
[inplace
][isforward
],(fftw_complex
*)in_data
,(fftw_real
*)out_data
);
708 gmx_fft_destroy(gmx_fft_t fft
)
718 if(fft
->single
[i
][j
] != NULL
)
720 rfftw_destroy_plan(fft
->single
[i
][j
]);
721 fft
->single
[i
][j
] = NULL
;
723 if(fft
->multi
[i
][j
] != NULL
)
725 rfftwnd_destroy_plan(fft
->multi
[i
][j
]);
726 fft
->multi
[i
][j
] = NULL
;
738 #endif /* GMX_FFT_FFTW2 */