mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
fft.hpp
Go to the documentation of this file.
1 /** \file fft.hpp
2  * \brief The fast Fourier transform interface
3  * \ingroup fft_files
4  * \author Jared R. Males (jaredmales@gmail.com)
5  *
6  */
7 
8 //***********************************************************************//
9 // Copyright 2015-2020 Jared R. Males (jaredmales@gmail.com)
10 //
11 // This file is part of mxlib.
12 //
13 // mxlib is free software: you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation, either version 3 of the License, or
16 // (at your option) any later version.
17 //
18 // mxlib is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with mxlib. If not, see <http://www.gnu.org/licenses/>.
25 //***********************************************************************//
26 
27 #ifndef fft_hpp
28 #define fft_hpp
29 
30 
31 #include <fftw3.h>
32 
33 #include <complex>
34 
35 #include "fftwTemplates.hpp"
36 #include "../../meta/trueFalseT.hpp"
37 
38 namespace mx
39 {
40 namespace math
41 {
42 namespace fft
43 {
44 
45 #define MXFFT_FORWARD (FFTW_FORWARD)
46 #define MXFFT_BACKWARD (FFTW_BACKWARD)
47 
48 
49 
50 template<typename _inputT, typename _outputT, size_t _rank, int _cudaGPU=0>
51 class fftT;
52 
53 template<int rank>
54 std::vector<int> fftwDimVec( int szX, int szY, int szZ);
55 
56 /// Fast Fourier Transforms using the FFTW library interface
57 /** The fftwTemplates type resolution system is used to allow the compiler
58  * to access the right plan and types for the transforms based on inputT and outputT.
59  *
60  * Calls the FFTW plan functions are protected by '\#pragma omp critical' directives
61  * unless MX_FFTW_NOOMP is define prior to the first inclusion of this file.
62  *
63  * \todo add execute interface with fftw like signature
64  * \todo add plan interface where user passes in pointers (to avoid allocations)
65  *
66  * \tparam inputT is the input type of the transform, can be either real or complex
67  * \tparam outputT is the output type of the transform, can be either real or complex
68  *
69  * \ingroup fft
70  */
71 template<typename _inputT, typename _outputT, size_t _rank>
72 class fftT< _inputT, _outputT, _rank, 0>
73 {
74  typedef _inputT inputT;
75  typedef _outputT outputT;
76 
77  typedef typename fftwTypeSpec<inputT, outputT>::complexT complexT;
78 
79  static const size_t rank = _rank;
80 
81  typedef typename fftwTypeSpec<inputT, outputT>::realT realT;
82 
83  typedef typename fftwPlanSpec<realT>::planT planT;
84 
85 
86 protected:
87  int m_dir {MXFFT_FORWARD}; ///< Direction of this FFT, either MXFFT_FORWARD (default) or MXFFT_BACKWARD
88 
89  int m_szX {0}; ///< Size of the x dimension
90  int m_szY {0}; ///< Size of the y dimension
91  int m_szZ {0}; ///< size of the z dimension
92 
93  planT m_plan {nullptr}; ///< The FFTW plan object. This is a pointer, allocated by FFTW library calls.
94 
95 public:
96 
97  /// Default c'tor
98  fftT();
99 
100  ///Constructor for rank 1 FFT.
101  template<int crank = _rank>
102  fftT( int nx, ///< [in] the desired size of the FFT
103  int ndir = MXFFT_FORWARD, ///< [in] [optional] direction of this FFT, either MXFFT_FORWARD (default) or MXFFT_BACKWARD
104  bool inPlace = false, ///< [in] [optional] whether or not this is an in-place transform. Default is false, out-of-place.
105  typename std::enable_if<crank==1>::type* = 0
106  );
107 
108  ///Constructor for rank 2 FFT.
109  template<int crank = _rank>
110  fftT( int nx, ///< [in] the desired x size of the FFT
111  int ny, ///< [in] the desired y size of the FFT
112  int ndir = MXFFT_FORWARD, ///< [in] [optional] direction of this FFT, either MXFFT_FORWARD (default) or MXFFT_BACKWARD
113  bool inPlace = false, ///< [in] [optional] whether or not this is an in-place transform. Default is false, out-of-place.
114  typename std::enable_if<crank==2>::type* = 0 );
115 
116  ///Constructor for rank 3 FFT.
117  template<int crank = _rank>
118  fftT( int nx, ///< [in] the desired x size of the FFT
119  int ny, ///< [in] the desired y size of the FFT
120  int nz, ///< [in] the desired z size of the FFT
121  int ndir = MXFFT_FORWARD, ///< [in] [optional] direction of this FFT, either MXFFT_FORWARD (default) or MXFFT_BACKWARD
122  bool inPlace = false, ///< [in] [optional] whether or not this is an in-place transform. Default is false, out-of-place.
123  typename std::enable_if<crank==3>::type* = 0 );
124 
125  ///Destructor
126  ~fftT();
127 
128  ///Destroy (de-allocate) the plan
129  void destroyPlan();
130 
131  ///Get the direction of this FFT
132  /** The direction is either MXFFT_FORWARD or MXFFT_BACKWARD.
133  *
134  * \returns the current value of m_dir.
135  */
136  int dir();
137 
138  /// Call the FFTW planning routine for an out-of-place transform.
139  void doPlan(const meta::trueFalseT<false> & inPlace);
140 
141  /// Call the FFTW planning routine for an in-place transform.
142  void doPlan(const meta::trueFalseT<true> & inPlace);
143 
144  /// Planning routine for rank 1 transforms.
145  template<int crank = _rank>
146  void plan( int nx, ///< [in] the desired size of the FFT
147  int ndir=MXFFT_FORWARD, ///< [in] [optional] direction of this FFT, either MXFFT_FORWARD (default) or MXFFT_BACKWARD
148  bool inPlace=false, ///< [in] [optional] whether or not this is an in-place transform. Default is false, out-of-place.
149  typename std::enable_if<crank==1>::type* = 0
150  );
151 
152  /// Planning routine for rank 2 transforms.
153  template<int crank = _rank>
154  void plan( int nx, ///< [in] the desired x size of the FFT
155  int ny, ///< [in] the desired y size of the FFT
156  int ndir=MXFFT_FORWARD, ///< [in] [optional] direction of this FFT, either MXFFT_FORWARD (default) or MXFFT_BACKWARD
157  bool inPlace=false, ///< [in] [optional] whether or not this is an in-place transform. Default is false, out-of-place.
158  typename std::enable_if<crank==2>::type* = 0
159  );
160 
161  /// Planning routine for rank 2 transforms.
162  template<int crank = _rank>
163  void plan( int nx, ///< [in] the desired x size of the FFT
164  int ny, ///< [in] the desired y size of the FFT
165  int nz, ///< [in] the desired z size of the FFT
166  int ndir=MXFFT_FORWARD, ///< [in] [optional] direction of this FFT, either MXFFT_FORWARD (default) or MXFFT_BACKWARD
167  bool inPlace=false, ///< [in] [optional] whether or not this is an in-place transform. Default is false, out-of-place.
168  typename std::enable_if<crank==3>::type* = 0
169  );
170 
171  /// Conduct the FFT
172  void operator()( outputT *out, ///< [out] the output of the FFT, must be pre-allocated
173  inputT * in ///< [in] the input to the FFT
174  ) const;
175 
176 };
177 
178 template<typename inputT, typename outputT, size_t rank>
179 fftT<inputT,outputT,rank,0>::fftT()
180 {
181 }
182 
183 template<typename inputT, typename outputT, size_t rank>
184 template<int crank>
185 fftT<inputT,outputT,rank,0>::fftT( int nx,
186  int ndir,
187  bool inPlace,
188  typename std::enable_if<crank==1>::type*
189  )
190 {
191  m_dir = ndir;
192 
193  plan(nx, ndir, inPlace);
194 }
195 
196 template<typename inputT, typename outputT, size_t rank>
197 template<int crank>
198 fftT<inputT,outputT,rank,0>::fftT( int nx,
199  int ny,
200  int ndir,
201  bool inPlace,
202  typename std::enable_if<crank==2>::type*
203  )
204 {
205  m_dir = ndir;
206 
207  plan(nx, ny, ndir, inPlace);
208 }
209 
210 template<typename inputT, typename outputT, size_t rank>
211 template<int crank>
212 fftT<inputT,outputT,rank,0>::fftT( int nx,
213  int ny,
214  int nz,
215  int ndir,
216  bool inPlace,
217  typename std::enable_if<crank==3>::type*
218  )
219 {
220  m_dir = ndir;
221 
222  plan(nx, ny, nz, ndir, inPlace);
223 }
224 
225 template<typename inputT, typename outputT, size_t rank>
226 fftT<inputT,outputT,rank,0>::~fftT()
227 {
228  destroyPlan();
229 }
230 
231 template<typename inputT, typename outputT, size_t rank>
232 void fftT<inputT,outputT,rank,0>::destroyPlan()
233 {
234  if(m_plan) fftw_destroy_plan<realT>(m_plan);
235 
236  m_plan = 0;
237 
238  m_szX = 0;
239  m_szY = 0;
240 
241 }
242 
243 template<typename inputT, typename outputT, size_t rank>
244 int fftT<inputT,outputT,rank,0>::dir()
245 {
246  return m_dir;
247 }
248 
249 template<typename inputT, typename outputT, size_t rank>
250 void fftT<inputT,outputT,rank,0>::doPlan(const meta::trueFalseT<false> & inPlace)
251 {
252  (void) inPlace;
253 
254  inputT * forplan1;
255  outputT * forplan2;
256 
257  int sz;
258 
259  if(rank == 1) sz = m_szX;
260  if(rank == 2) sz = m_szX*m_szY;
261  if(rank == 3) sz = m_szX*m_szY*m_szZ;
262 
263  forplan1 = fftw_malloc<inputT>(sz);
264  forplan2 = fftw_malloc<outputT>(sz);
265 
266  int pdir = FFTW_FORWARD;
267  if(m_dir == MXFFT_BACKWARD) pdir = FFTW_BACKWARD;
268 
269  #ifndef MX_FFTW_NOOMP
270  #pragma omp critical
271  #endif
272  {//scope for pragma
273  m_plan = fftw_plan_dft<inputT, outputT>( fftwDimVec<rank>(m_szX, m_szY, m_szZ), forplan1, forplan2, pdir, FFTW_MEASURE);
274  }
275 
276  fftw_free<inputT>(forplan1);
277  fftw_free<outputT>(forplan2);
278 
279 }
280 
281 template<typename inputT, typename outputT, size_t rank>
282 void fftT<inputT,outputT,rank,0>::doPlan(const meta::trueFalseT<true> & inPlace)
283 {
284  (void) inPlace;
285 
286  complexT * forplan;
287 
288  int sz;
289 
290  if(rank == 1) sz = m_szX;
291  if(rank == 2) sz = m_szX*m_szY;
292  if(rank == 3) sz = m_szX*m_szY*m_szZ;
293 
294  forplan = fftw_malloc<complexT>(sz);
295 
296  int pdir = FFTW_FORWARD;
297  if(m_dir == MXFFT_BACKWARD) pdir = FFTW_BACKWARD;
298 
299  #ifndef MX_FFTW_NOOMP
300  #pragma omp critical
301  #endif
302  {//scope for pragma
303  m_plan = fftw_plan_dft<inputT, outputT>( fftwDimVec<rank>(m_szX, m_szY, m_szZ), reinterpret_cast<inputT*>(forplan), reinterpret_cast<outputT*>(forplan), pdir, FFTW_MEASURE);
304  }
305 
306  fftw_free<inputT>(reinterpret_cast<inputT*>(forplan));
307 }
308 
309 template<typename inputT, typename outputT, size_t rank>
310 template<int crank>
311 void fftT<inputT,outputT,rank,0>::plan( int nx,
312  int ndir,
313  bool inPlace,
314  typename std::enable_if<crank==1>::type*
315  )
316 {
317  if(m_szX == nx && m_dir == ndir && m_plan)
318  {
319  return;
320  }
321 
322  destroyPlan();
323 
324  m_dir = ndir;
325 
326  m_szX = nx;
327  m_szY = 0;
328  m_szZ = 0;
329 
330  if(inPlace == false)
331  {
332  doPlan(meta::trueFalseT<false>());
333  }
334  else
335  {
336  doPlan(meta::trueFalseT<true>());
337  }
338 }
339 
340 
341 template<typename inputT, typename outputT, size_t rank>
342 template<int crank>
343 void fftT<inputT,outputT,rank,0>::plan( int nx,
344  int ny,
345  int ndir,
346  bool inPlace,
347  typename std::enable_if<crank==2>::type*
348  )
349 {
350  if(m_szX == nx && m_szY == ny && m_dir == ndir && m_plan)
351  {
352  return;
353  }
354 
355  destroyPlan();
356 
357  m_dir = ndir;
358 
359  m_szX = nx;
360  m_szY = ny;
361  m_szZ = 0;
362 
363  if(inPlace == false)
364  {
365  doPlan(meta::trueFalseT<false>());
366  }
367  else
368  {
369  doPlan(meta::trueFalseT<true>());
370  }
371 }
372 
373 template<typename inputT, typename outputT, size_t rank>
374 template<int crank>
375 void fftT<inputT,outputT,rank,0>::plan( int nx,
376  int ny,
377  int nz,
378  int ndir,
379  bool inPlace,
380  typename std::enable_if<crank==3>::type*
381  )
382 {
383  if(m_szX == nx && m_szY == ny && m_szZ == nz && m_dir == ndir && m_plan)
384  {
385  return;
386  }
387 
388  destroyPlan();
389 
390  m_dir = ndir;
391 
392  m_szX = nx;
393  m_szY = ny;
394  m_szZ = nz;
395 
396  if(inPlace == false)
397  {
398  doPlan(meta::trueFalseT<false>());
399  }
400  else
401  {
402  doPlan(meta::trueFalseT<true>());
403  }
404 }
405 
406 template<typename inputT, typename outputT, size_t rank>
407 void fftT<inputT,outputT,rank,0>::operator()( outputT *out,
408  inputT * in
409  ) const
410 {
411  fftw_execute_dft<inputT,outputT>(m_plan, in, out);
412 }
413 
414 
415 
416 
417 
418 template<>
419 std::vector<int> fftwDimVec<1>( int szX,
420  int szY,
421  int szZ
422  );
423 
424 template<>
425 std::vector<int> fftwDimVec<2>( int szX,
426  int szY,
427  int szZ
428  );
429 
430 template<>
431 std::vector<int> fftwDimVec<3>( int szX,
432  int szY,
433  int szZ
434  );
435 
436 
437 
438 
439 #ifdef MX_CUDA
440 
441 template<>
442 class fft<std::complex<float>, std::complex<float>, 2, 1>
443 {
444 protected:
445  int m_szX;
446  int m_szY;
447 
448  cufftHandle m_plan;
449 
450 public:
451 
452  fft()
453  {
454  m_szX = 0;
455  m_szY = 0;
456  }
457 
458  fft(int nx, int ny)
459  {
460  m_szX = 0;
461  m_szY = 0;
462 
463  plan(nx, ny);
464  }
465 
466  void plan(int nx, int ny)
467  {
468  if(m_szX == nx && m_szY == ny)
469  {
470  return;
471  }
472 
473  m_szX = nx;
474  m_szY = ny;
475 
476  cufftPlan2d(&m_plan, m_szX, m_szY, CUFFT_C2C);
477  }
478 
479  void fwdFFT(std::complex<float> * in, std::complex<float> * out)
480  {
481  cufftExecC2C(m_plan, (cufftComplex *) in, (cufftComplex *) out, CUFFT_FORWARD);
482  }
483 };
484 
485 #endif
486 
487 }//namespace ffit
488 }//namespace math
489 }//namespace mx
490 
491 #endif // fft_hpp
492 
void doPlan(const meta::trueFalseT< false > &inPlace)
Call the FFTW planning routine for an out-of-place transform.
void destroyPlan()
Destroy (de-allocate) the plan.
void operator()(outputT *out, inputT *in) const
Conduct the FFT.
fftT(int nx, int ny, int nz, int ndir=MXFFT_FORWARD, bool inPlace=false, typename std::enable_if< crank==3 >::type *=0)
Constructor for rank 3 FFT.
fftT(int nx, int ndir=MXFFT_FORWARD, bool inPlace=false, typename std::enable_if< crank==1 >::type *=0)
Constructor for rank 1 FFT.
void plan(int nx, int ny, int nz, int ndir=MXFFT_FORWARD, bool inPlace=false, typename std::enable_if< crank==3 >::type *=0)
Planning routine for rank 2 transforms.
fftT(int nx, int ny, int ndir=MXFFT_FORWARD, bool inPlace=false, typename std::enable_if< crank==2 >::type *=0)
Constructor for rank 2 FFT.
void plan(int nx, int ny, int ndir=MXFFT_FORWARD, bool inPlace=false, typename std::enable_if< crank==2 >::type *=0)
Planning routine for rank 2 transforms.
void plan(int nx, int ndir=MXFFT_FORWARD, bool inPlace=false, typename std::enable_if< crank==1 >::type *=0)
Planning routine for rank 1 transforms.
int dir()
Get the direction of this FFT.
void doPlan(const meta::trueFalseT< true > &inPlace)
Call the FFTW planning routine for an in-place transform.
Declares and defines templatized wrappers for the fftw library.
The mxlib c++ namespace.
Definition: mxError.hpp:107
fftwX_plan planT
Specializations typedef planT as fftwf_plan, fftw_plan, fftwl_plan, or fftwq_plan.
std::complex< realT > complexT
The complex data type.
_realT realT
The real data type (_realT is actually defined in specializations).
The false specialization of trueFalseT.
Definition: trueFalseT.hpp:68
The true specialization of trueFalseT.
Definition: trueFalseT.hpp:58