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
26 #include <mkl_service.h>
28 #include "gromacs/fft/fft.h"
29 #include "gmx_fatal.h"
32 /* For MKL version (<10.0), we should define MKL_LONG. */
34 #define MKL_LONG long int
39 #define GMX_DFTI_PREC DFTI_DOUBLE
41 #define GMX_DFTI_PREC DFTI_SINGLE
46 * Contents of the Intel MKL FFT fft datatype.
48 * Note that this is one of several possible implementations of gmx_fft_t.
50 * The MKL _API_ supports 1D,2D, and 3D transforms, including real-to-complex.
51 * Unfortunately the actual library implementation does not support 3D real
52 * transforms as of version 7.2, and versions before 7.0 don't support 2D real
53 * either. In addition, the multi-dimensional storage format for real data
54 * is not compatible with our padding.
56 * To work around this we roll our own 2D and 3D real-to-complex transforms,
57 * using separate X/Y/Z handles defined to perform (ny*nz), (nx*nz), and
58 * (nx*ny) transforms at once when necessary. To perform strided multiple
59 * transforms out-of-place (i.e., without padding in the last dimension)
60 * on the fly we also need to separate the forward and backward
61 * handles for real-to-complex/complex-to-real data permutation.
63 * This makes it necessary to define 3 handles for in-place FFTs, and 4 for
64 * the out-of-place transforms. Still, whenever possible we try to use
65 * a single 3D-transform handle instead.
67 * So, the handles are enumerated as follows:
69 * 1D FFT (real too): Index 0 is the handle for the entire FFT
70 * 2D complex FFT: Index 0 is the handle for the entire FFT
71 * 3D complex FFT: Index 0 is the handle for the entire FFT
72 * 2D, inplace real FFT: 0=FFTx, 1=FFTy handle
73 * 2D, ooplace real FFT: 0=FFTx, 1=real-to-complex FFTy, 2=complex-to-real FFTy
74 * 3D, inplace real FFT: 0=FFTx, 1=FFTy, 2=FFTz handle
75 * 3D, ooplace real FFT: 0=FFTx, 1=FFTy, 2=r2c FFTz, 3=c2r FFTz
77 * Intel people reading this: Learn from FFTW what a good interface looks like :-)
85 int ndim; /**< Number of dimensions in FFT */
86 int nx; /**< Length of X transform */
87 int ny; /**< Length of Y transform */
88 int nz; /**< Length of Z transform */
89 int real_fft; /**< 1 if real FFT, otherwise 0 */
90 DFTI_DESCRIPTOR * inplace[3]; /**< in-place FFT */
91 DFTI_DESCRIPTOR * ooplace[4]; /**< out-of-place FFT */
92 t_complex * work; /**< Enable out-of-place c2r FFT */
98 gmx_fft_init_1d(gmx_fft_t * pfft,
108 gmx_fatal(FARGS, "Invalid opaque FFT datatype pointer.");
113 if ( (fft = (gmx_fft_t)malloc(sizeof(struct gmx_fft))) == NULL)
118 /* Mark all handles invalid */
119 for (d = 0; d < 3; d++)
121 fft->inplace[d] = fft->ooplace[d] = NULL;
123 fft->ooplace[3] = NULL;
126 status = DftiCreateDescriptor(&fft->inplace[0], GMX_DFTI_PREC, DFTI_COMPLEX, 1, (MKL_LONG)nx);
130 status = DftiSetValue(fft->inplace[0], DFTI_PLACEMENT, DFTI_INPLACE);
135 status = DftiCommitDescriptor(fft->inplace[0]);
141 status = DftiCreateDescriptor(&fft->ooplace[0], GMX_DFTI_PREC, DFTI_COMPLEX, 1, (MKL_LONG)nx);
146 DftiSetValue(fft->ooplace[0], DFTI_PLACEMENT, DFTI_NOT_INPLACE);
151 DftiCommitDescriptor(fft->ooplace[0]);
157 gmx_fatal(FARGS, "Error initializing Intel MKL FFT; status=%d", status);
158 gmx_fft_destroy(fft);
174 gmx_fft_init_1d_real(gmx_fft_t * pfft,
184 gmx_fatal(FARGS, "Invalid opaque FFT datatype pointer.");
189 if ( (fft = (gmx_fft_t)malloc(sizeof(struct gmx_fft))) == NULL)
194 /* Mark all handles invalid */
195 for (d = 0; d < 3; d++)
197 fft->inplace[d] = fft->ooplace[d] = NULL;
199 fft->ooplace[3] = NULL;
201 status = DftiCreateDescriptor(&fft->inplace[0], GMX_DFTI_PREC, DFTI_REAL, 1, (MKL_LONG)nx);
205 status = DftiSetValue(fft->inplace[0], DFTI_PLACEMENT, DFTI_INPLACE);
210 status = DftiCommitDescriptor(fft->inplace[0]);
216 status = DftiCreateDescriptor(&fft->ooplace[0], GMX_DFTI_PREC, DFTI_REAL, 1, (MKL_LONG)nx);
221 status = DftiSetValue(fft->ooplace[0], DFTI_PLACEMENT, DFTI_NOT_INPLACE);
226 status = DftiCommitDescriptor(fft->ooplace[0]);
230 if (status == DFTI_UNIMPLEMENTED)
233 "The linked Intel MKL version (<6.0?) cannot do real FFTs.");
234 gmx_fft_destroy(fft);
241 gmx_fatal(FARGS, "Error initializing Intel MKL FFT; status=%d", status);
242 gmx_fft_destroy(fft);
258 gmx_fft_init_2d_real(gmx_fft_t * pfft,
271 gmx_fatal(FARGS, "Invalid opaque FFT datatype pointer.");
276 if ( (fft = (gmx_fft_t)malloc(sizeof(struct gmx_fft))) == NULL)
283 /* Mark all handles invalid */
284 for (d = 0; d < 3; d++)
286 fft->inplace[d] = fft->ooplace[d] = NULL;
288 fft->ooplace[3] = NULL;
290 /* Roll our own 2D real transform using multiple transforms in MKL,
291 * since the current MKL versions does not support our storage format,
292 * and all but the most recent don't even have 2D real FFTs.
296 status = DftiCreateDescriptor(&fft->inplace[0], GMX_DFTI_PREC, DFTI_COMPLEX, 1, (MKL_LONG)nx);
304 (DftiSetValue(fft->inplace[0], DFTI_PLACEMENT, DFTI_INPLACE) ||
305 DftiSetValue(fft->inplace[0], DFTI_NUMBER_OF_TRANSFORMS, nyc) ||
306 DftiSetValue(fft->inplace[0], DFTI_INPUT_DISTANCE, 1) ||
307 DftiSetValue(fft->inplace[0], DFTI_INPUT_STRIDES, stride) ||
308 DftiSetValue(fft->inplace[0], DFTI_OUTPUT_DISTANCE, 1) ||
309 DftiSetValue(fft->inplace[0], DFTI_OUTPUT_STRIDES, stride));
314 status = DftiCommitDescriptor(fft->inplace[0]);
317 /* Out-of-place X FFT */
320 status = DftiCreateDescriptor(&(fft->ooplace[0]), GMX_DFTI_PREC, DFTI_COMPLEX, 1, (MKL_LONG)nx);
329 (DftiSetValue(fft->ooplace[0], DFTI_PLACEMENT, DFTI_NOT_INPLACE) ||
330 DftiSetValue(fft->ooplace[0], DFTI_NUMBER_OF_TRANSFORMS, nyc) ||
331 DftiSetValue(fft->ooplace[0], DFTI_INPUT_DISTANCE, 1) ||
332 DftiSetValue(fft->ooplace[0], DFTI_INPUT_STRIDES, stride) ||
333 DftiSetValue(fft->ooplace[0], DFTI_OUTPUT_DISTANCE, 1) ||
334 DftiSetValue(fft->ooplace[0], DFTI_OUTPUT_STRIDES, stride));
339 status = DftiCommitDescriptor(fft->ooplace[0]);
346 status = DftiCreateDescriptor(&fft->inplace[1], GMX_DFTI_PREC, DFTI_REAL, 1, (MKL_LONG)ny);
355 (DftiSetValue(fft->inplace[1], DFTI_PLACEMENT, DFTI_INPLACE) ||
356 DftiSetValue(fft->inplace[1], DFTI_NUMBER_OF_TRANSFORMS, (MKL_LONG)nx) ||
357 DftiSetValue(fft->inplace[1], DFTI_INPUT_DISTANCE, 2*nyc) ||
358 DftiSetValue(fft->inplace[1], DFTI_INPUT_STRIDES, stride) ||
359 DftiSetValue(fft->inplace[1], DFTI_OUTPUT_DISTANCE, 2*nyc) ||
360 DftiSetValue(fft->inplace[1], DFTI_OUTPUT_STRIDES, stride) ||
361 DftiCommitDescriptor(fft->inplace[1]));
365 /* Out-of-place real-to-complex (affects output distance) Y FFT */
368 status = DftiCreateDescriptor(&fft->ooplace[1], GMX_DFTI_PREC, DFTI_REAL, 1, (MKL_LONG)ny);
377 (DftiSetValue(fft->ooplace[1], DFTI_PLACEMENT, DFTI_NOT_INPLACE) ||
378 DftiSetValue(fft->ooplace[1], DFTI_NUMBER_OF_TRANSFORMS, (MKL_LONG)nx) ||
379 DftiSetValue(fft->ooplace[1], DFTI_INPUT_DISTANCE, (MKL_LONG)ny) ||
380 DftiSetValue(fft->ooplace[1], DFTI_INPUT_STRIDES, stride) ||
381 DftiSetValue(fft->ooplace[1], DFTI_OUTPUT_DISTANCE, 2*nyc) ||
382 DftiSetValue(fft->ooplace[1], DFTI_OUTPUT_STRIDES, stride) ||
383 DftiCommitDescriptor(fft->ooplace[1]));
387 /* Out-of-place complex-to-real (affects output distance) Y FFT */
390 status = DftiCreateDescriptor(&fft->ooplace[2], GMX_DFTI_PREC, DFTI_REAL, 1, (MKL_LONG)ny);
399 (DftiSetValue(fft->ooplace[2], DFTI_PLACEMENT, DFTI_NOT_INPLACE) ||
400 DftiSetValue(fft->ooplace[2], DFTI_NUMBER_OF_TRANSFORMS, (MKL_LONG)nx) ||
401 DftiSetValue(fft->ooplace[2], DFTI_INPUT_DISTANCE, 2*nyc) ||
402 DftiSetValue(fft->ooplace[2], DFTI_INPUT_STRIDES, stride) ||
403 DftiSetValue(fft->ooplace[2], DFTI_OUTPUT_DISTANCE, (MKL_LONG)ny) ||
404 DftiSetValue(fft->ooplace[2], DFTI_OUTPUT_STRIDES, stride) ||
405 DftiCommitDescriptor(fft->ooplace[2]));
411 if ((fft->work = (t_complex *)malloc(sizeof(t_complex)*(nx*(ny/2+1)))) == NULL)
419 gmx_fatal(FARGS, "Error initializing Intel MKL FFT; status=%d", status);
420 gmx_fft_destroy(fft);
434 gmx_fft_1d(gmx_fft_t fft,
435 enum gmx_fft_direction dir,
439 int inplace = (in_data == out_data);
442 if ( (fft->real_fft == 1) || (fft->ndim != 1) ||
443 ((dir != GMX_FFT_FORWARD) && (dir != GMX_FFT_BACKWARD)) )
445 gmx_fatal(FARGS, "FFT plan mismatch - bad plan or direction.");
449 if (dir == GMX_FFT_FORWARD)
453 status = DftiComputeForward(fft->inplace[0], in_data);
457 status = DftiComputeForward(fft->ooplace[0], in_data, out_data);
464 status = DftiComputeBackward(fft->inplace[0], in_data);
468 status = DftiComputeBackward(fft->ooplace[0], in_data, out_data);
474 gmx_fatal(FARGS, "Error executing Intel MKL FFT.");
484 gmx_fft_1d_real(gmx_fft_t fft,
485 enum gmx_fft_direction dir,
489 int inplace = (in_data == out_data);
492 if ( (fft->real_fft != 1) || (fft->ndim != 1) ||
493 ((dir != GMX_FFT_REAL_TO_COMPLEX) && (dir != GMX_FFT_COMPLEX_TO_REAL)) )
495 gmx_fatal(FARGS, "FFT plan mismatch - bad plan or direction.");
499 if (dir == GMX_FFT_REAL_TO_COMPLEX)
503 status = DftiComputeForward(fft->inplace[0], in_data);
507 status = DftiComputeForward(fft->ooplace[0], in_data, out_data);
514 status = DftiComputeBackward(fft->inplace[0], in_data);
518 status = DftiComputeBackward(fft->ooplace[0], in_data, out_data);
524 gmx_fatal(FARGS, "Error executing Intel MKL FFT.");
533 gmx_fft_2d_real(gmx_fft_t fft,
534 enum gmx_fft_direction dir,
538 int inplace = (in_data == out_data);
541 if ( (fft->real_fft != 1) || (fft->ndim != 2) ||
542 ((dir != GMX_FFT_REAL_TO_COMPLEX) && (dir != GMX_FFT_COMPLEX_TO_REAL)) )
544 gmx_fatal(FARGS, "FFT plan mismatch - bad plan or direction.");
548 if (dir == GMX_FFT_REAL_TO_COMPLEX)
552 /* real-to-complex in Y dimension, in-place */
553 status = DftiComputeForward(fft->inplace[1], in_data);
555 /* complex-to-complex in X dimension, in-place */
558 status = DftiComputeForward(fft->inplace[0], in_data);
563 /* real-to-complex in Y dimension, in_data to out_data */
564 status = DftiComputeForward(fft->ooplace[1], in_data, out_data);
566 /* complex-to-complex in X dimension, in-place to out_data */
569 status = DftiComputeForward(fft->inplace[0], out_data);
575 /* prior implementation was incorrect. See fft.cpp unit test */
576 gmx_incons("Complex -> Real is not supported by MKL.");
581 gmx_fatal(FARGS, "Error executing Intel MKL FFT.");
589 gmx_fft_destroy(gmx_fft_t fft)
595 for (d = 0; d < 3; d++)
597 if (fft->inplace[d] != NULL)
599 DftiFreeDescriptor(&fft->inplace[d]);
601 if (fft->ooplace[d] != NULL)
603 DftiFreeDescriptor(&fft->ooplace[d]);
606 if (fft->ooplace[3] != NULL)
608 DftiFreeDescriptor(&fft->ooplace[3]);
610 if (fft->work != NULL)
618 void gmx_fft_cleanup()
623 const char *gmx_fft_get_version_info()