mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
fftT.hpp
Go to the documentation of this file.
1/** \file fftT.hpp
2 * \brief The Fast Fourier Transform interface
3 * \ingroup ft_files
4 * \author Jared R. Males (jaredmales@gmail.com)
5 *
6 */
7
8//***********************************************************************//
9// Copyright 2015-2025 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 fftT_hpp
28#define fftT_hpp
29
30#pragma GCC system_header
31#include <Eigen/Dense>
32
33#include <fftw3.h>
34
35#include "fftwTemplates.hpp"
36#include "../../meta/trueFalseT.hpp"
37
38#include "ft.hpp"
39
40namespace mx
41{
42namespace math
43{
44namespace ft
45{
46
47template <typename _inputT, typename _outputT, size_t _rank, int _cudaGPU = 0>
48class fftT;
49
50template <int rank>
51std::vector<int> fftwDimVec( int szX, int szY, int szZ );
52
53/// Fast Fourier Transforms using the \ref fftw "FFTW library"
54/** The \ref fftw_templates "FFTW Templates" type resolution system is used to allow the compiler
55 * to access the right plan and types for the transforms based on inputT and outputT.
56 *
57 * Planning is done with "measure" to achieve optimum performance at the cost of extra time during
58 * the planning step. This can be mitigated using "wisdom", managed by \ref fftwEnvironment.
59 *
60 * Calls to 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. This means that
62 * you do not need to use the `fftw_make_planner_thread_safe` facility in FFTW.
63 *
64 * \note I recommend not using fftw_make_planner_thread_safe or specifying number of threads in
65 * \ref fftwEnvironment.
66 *
67 * \todo add execute interface with fftw like signature
68 * \todo add plan interface where user passes in pointers (to avoid allocations)
69 *
70 * \tparam _inputT is the input type of the transform, can be either real or complex
71 * \tparam _outputT is the output type of the transform, can be either real or complex
72 * \tparam _rank is the rank of the transform. Limited to 1, 2, or 3.
73 *
74 * \ingroup fft
75 */
76template <typename _inputT, typename _outputT, size_t _rank>
77class fftT<_inputT, _outputT, _rank, 0>
78{
79
80 public:
81 typedef _inputT inputT;
82 typedef _outputT outputT;
83
84 static const size_t rank = _rank;
85
86 typedef typename fftwTypeSpec<inputT, outputT>::realT realT;
87
88 typedef typename fftwTypeSpec<inputT, outputT>::complexT complexT;
89
90 typedef Eigen::Array<inputT, -1, -1> eigenArrayInT;
91
92 typedef Eigen::Array<outputT, -1, -1> eigenArrayOutT;
93
94 typedef typename fftwPlanSpec<realT>::planT planT;
95
96 protected:
97 dir m_dir{ dir::forward }; ///< Direction of this FFT, either dir::forward (default) or dir::backward
98
99 int m_szX{ 0 }; ///< Size of the x dimension
100 int m_szY{ 0 }; ///< Size of the y dimension
101 int m_szZ{ 0 }; ///< size of the z dimension
102
103 planT m_plan{ nullptr }; ///< The FFTW plan object. This is a pointer, allocated by FFTW library calls.
104
105 public:
106 /// Default c'tor
108
109 /// Constructor for rank 1 FFT.
110 template <int crank = _rank>
111 fftT( int nx, ///< [in] the desired size of the FFT
112 dir ndir = dir::forward, ///< [in] [optional] direction of this FFT, either dir::forward (default) or
113 ///< dir::backward
114 bool inPlace = false, /**< [in] [optional] whether or not this is an in-place transform.
115 Default is false, out-of-place.*/
116 typename std::enable_if<crank == 1>::type * = 0 );
117
118 /// Constructor for rank 2 FFT.
119 template <int crank = _rank>
120 fftT( int nx, ///< [in] the desired x size of the FFT
121 int ny, ///< [in] the desired y size of the FFT
122 dir ndir = dir::forward, /**< [in] [optional] direction of this FFT, either dir::forward
123 (default) or dir::backward */
124 bool inPlace = false, /**< [in] [optional] whether or not this is an in-place transform.
125 Default is false, out-of-place. */
126 typename std::enable_if<crank == 2>::type * = 0 );
127
128 /// Constructor for rank 3 FFT.
129 template <int crank = _rank>
130 fftT( int nx, ///< [in] the desired x size of the FFT
131 int ny, ///< [in] the desired y size of the FFT
132 int nz, ///< [in] the desired z size of the FFT
133 dir ndir = dir::forward, /**< [in] [optional] direction of this FFT, either dir::forward (default)
134 or dir::backward*/
135 bool inPlace = false, /**< [in] [optional] whether or not this is an in-place
136 transform. Default is false, out-of-place. */
137 typename std::enable_if<crank == 3>::type * = 0 );
138
139 /// Destructor
141
142 /// Destroy (de-allocate) the plan
144
145 /// Get the direction of this FFT
146 /** The direction is either dir::forward or dir::backward.
147 *
148 * \returns the current value of m_dir.
149 */
151
152 /// Call the FFTW planning routine for an out-of-place transform.
154
155 /// Call the FFTW planning routine for an in-place transform.
157
158 /// Planning routine for rank 1 transforms.
159 template <int crank = _rank>
160 void plan( int nx, ///< [in] the desired size of the FFT
161 ft::dir ndir = dir::forward, /**< [in] [optional] direction of this FFT, either dir::forward (default)
162 or dir::backward */
163 bool inPlace = false, /**< [in] [optional] whether or not this is an in-place transform.
164 Default is false, out-of-place. */
165 typename std::enable_if<crank == 1>::type * = 0 );
166
167 /// Planning routine for rank 2 transforms.
168 template <int crank = _rank>
169 void plan( int nx, ///< [in] the desired x size of the FFT
170 int ny, ///< [in] the desired y size of the FFT
171 ft::dir ndir = dir::forward, /**< [in] [optional] direction of this FFT, either dir::forward (default)
172 or dir::backward */
173 bool inPlace = false, /**< [in] [optional] whether or not this is an in-place transform.
174 Default is false, out-of-place. */
175 typename std::enable_if<crank == 2>::type * = 0 );
176
177 /// Planning routine for rank 3 transforms.
178 template <int crank = _rank>
179 void plan( int nx, ///< [in] the desired x size of the FFT
180 int ny, ///< [in] the desired y size of the FFT
181 int nz, ///< [in] the desired z size of the FFT
182 ft::dir ndir = dir::forward, /**< [in] [optional] direction of this FFT, either dir::forward
183 (default) or dir::backward */
184 bool inPlace = false, /**< [in] [optional] whether or not this is an in-place transform.
185 Default is false, out-of-place. */
186 typename std::enable_if<crank == 3>::type * = 0 );
187
188 /// Conduct the FFT
189 void operator()( outputT *out, ///< [out] the output of the FFT, must be pre-allocated
190 inputT *in ///< [in] the input to the FFT
191 ) const;
192
193 /// Conduct the MFT
194 void operator()( eigenArrayOutT &out, /**< [out] the output of the DFT */
195 eigenArrayInT &in /**< [in] the input to the DFT */ ) const;
196};
197
198template <typename inputT, typename outputT, size_t rank>
199fftT<inputT, outputT, rank, 0>::fftT()
200{
201}
202
203template <typename inputT, typename outputT, size_t rank>
204template <int crank>
205fftT<inputT, outputT, rank, 0>::fftT( int nx, ft::dir ndir, bool inPlace, typename std::enable_if<crank == 1>::type * )
206{
207 m_dir = ndir;
208
209 plan( nx, ndir, inPlace );
210}
211
212template <typename inputT, typename outputT, size_t rank>
213template <int crank>
214fftT<inputT, outputT, rank, 0>::fftT(
215 int nx, int ny, ft::dir ndir, bool inPlace, typename std::enable_if<crank == 2>::type * )
216{
217 m_dir = ndir;
218
219 plan( nx, ny, ndir, inPlace );
220}
221
222template <typename inputT, typename outputT, size_t rank>
223template <int crank>
224fftT<inputT, outputT, rank, 0>::fftT(
225 int nx, int ny, int nz, ft::dir ndir, bool inPlace, typename std::enable_if<crank == 3>::type * )
226{
227 m_dir = ndir;
228
229 plan( nx, ny, nz, ndir, inPlace );
230}
231
232template <typename inputT, typename outputT, size_t rank>
233fftT<inputT, outputT, rank, 0>::~fftT()
234{
235 destroyPlan();
236}
237
238template <typename inputT, typename outputT, size_t rank>
239void fftT<inputT, outputT, rank, 0>::destroyPlan()
240{
241 if( m_plan )
242 fftw_destroy_plan<realT>( m_plan );
243
244 m_plan = 0;
245
246 m_szX = 0;
247 m_szY = 0;
248}
249
250template <typename inputT, typename outputT, size_t rank>
251ft::dir fftT<inputT, outputT, rank, 0>::dir()
252{
253 return m_dir;
254}
255
256template <typename inputT, typename outputT, size_t rank>
257void fftT<inputT, outputT, rank, 0>::doPlan( const meta::trueFalseT<false> &inPlace )
258{
259 (void)inPlace;
260
261 inputT *forplan1;
262 outputT *forplan2;
263
264 int sz;
265
266 if( rank == 1 )
267 sz = m_szX;
268 if( rank == 2 )
269 sz = m_szX * m_szY;
270 if( rank == 3 )
271 sz = m_szX * m_szY * m_szZ;
272
275
276 int pdir = FFTW_FORWARD;
277 if( m_dir == dir::backward )
279
280#ifndef MX_FFTW_NOOMP
281 #pragma omp critical
282#endif
283 { // scope for pragma
285 fftwDimVec<rank>( m_szX, m_szY, m_szZ ), forplan1, forplan2, pdir, FFTW_MEASURE );
286 }
287
290}
291
292template <typename inputT, typename outputT, size_t rank>
293void fftT<inputT, outputT, rank, 0>::doPlan( const meta::trueFalseT<true> &inPlace )
294{
295 (void)inPlace;
296
297 complexT *forplan;
298
299 int sz;
300
301 if( rank == 1 )
302 sz = m_szX;
303 if( rank == 2 )
304 sz = m_szX * m_szY;
305 if( rank == 3 )
306 sz = m_szX * m_szY * m_szZ;
307
309
310 int pdir = FFTW_FORWARD;
311 if( m_dir == dir::backward )
313
314#ifndef MX_FFTW_NOOMP
315 #pragma omp critical
316#endif
317 { // scope for pragma
318 m_plan = fftw_plan_dft<inputT, outputT>( fftwDimVec<rank>( m_szX, m_szY, m_szZ ),
319 reinterpret_cast<inputT *>( forplan ),
320 reinterpret_cast<outputT *>( forplan ),
321 pdir,
322 FFTW_MEASURE );
323 }
324
325 fftw_free<inputT>( reinterpret_cast<inputT *>( forplan ) );
326}
327
328template <typename inputT, typename outputT, size_t rank>
329template <int crank>
330void fftT<inputT, outputT, rank, 0>::plan( int nx, ft::dir ndir, bool inPlace, typename std::enable_if<crank == 1>::type * )
331{
332 if( m_szX == nx && m_dir == ndir && m_plan )
333 {
334 return;
335 }
336
337 destroyPlan();
338
339 m_dir = ndir;
340
341 m_szX = nx;
342 m_szY = 0;
343 m_szZ = 0;
344
345 if( inPlace == false )
346 {
347 doPlan( meta::trueFalseT<false>() );
348 }
349 else
350 {
351 doPlan( meta::trueFalseT<true>() );
352 }
353}
354
355template <typename inputT, typename outputT, size_t rank>
356template <int crank>
357void fftT<inputT, outputT, rank, 0>::plan(
358 int nx, int ny, ft::dir ndir, bool inPlace, typename std::enable_if<crank == 2>::type * )
359{
360 if( m_szX == nx && m_szY == ny && m_dir == ndir && m_plan )
361 {
362 return;
363 }
364
365 destroyPlan();
366
367 m_dir = ndir;
368
369 m_szX = nx;
370 m_szY = ny;
371 m_szZ = 0;
372
373 if( inPlace == false )
374 {
375 doPlan( meta::trueFalseT<false>() );
376 }
377 else
378 {
379 doPlan( meta::trueFalseT<true>() );
380 }
381}
382
383template <typename inputT, typename outputT, size_t rank>
384template <int crank>
385void fftT<inputT, outputT, rank, 0>::plan(
386 int nx, int ny, int nz, ft::dir ndir, bool inPlace, typename std::enable_if<crank == 3>::type * )
387{
388 if( m_szX == nx && m_szY == ny && m_szZ == nz && m_dir == ndir && m_plan )
389 {
390 return;
391 }
392
393 destroyPlan();
394
395 m_dir = ndir;
396
397 m_szX = nx;
398 m_szY = ny;
399 m_szZ = nz;
400
401 if( inPlace == false )
402 {
403 doPlan( meta::trueFalseT<false>() );
404 }
405 else
406 {
407 doPlan( meta::trueFalseT<true>() );
408 }
409}
410
411template <typename inputT, typename outputT, size_t rank>
412void fftT<inputT, outputT, rank, 0>::operator()( outputT *out, inputT *in ) const
413{
415}
416
417template <typename inputT, typename outputT, size_t rank>
418void fftT<inputT, outputT, rank, 0>::operator()( eigenArrayOutT &out, eigenArrayInT &in ) const
419{
420 fftw_execute_dft<inputT, outputT>( m_plan, in.data(), out.data() );
421}
422
423template <>
424std::vector<int> fftwDimVec<1>( int szX, int szY, int szZ );
425
426template <>
427std::vector<int> fftwDimVec<2>( int szX, int szY, int szZ );
428
429template <>
430std::vector<int> fftwDimVec<3>( int szX, int szY, int szZ );
431
432#ifdef MX_CUDA
433
434template <>
435class fft<std::complex<float>, std::complex<float>, 2, 1>
436{
437 protected:
438 int m_szX;
439 int m_szY;
440
441 cufftHandle m_plan;
442
443 public:
444 fft()
445 {
446 m_szX = 0;
447 m_szY = 0;
448 }
449
450 fft( int nx, int ny )
451 {
452 m_szX = 0;
453 m_szY = 0;
454
455 plan( nx, ny );
456 }
457
458 void plan( int nx, int ny )
459 {
460 if( m_szX == nx && m_szY == ny )
461 {
462 return;
463 }
464
465 m_szX = nx;
466 m_szY = ny;
467
468 cufftPlan2d( &m_plan, m_szX, m_szY, CUFFT_C2C );
469 }
470
471 void fwdFFT( std::complex<float> *in, std::complex<float> *out )
472 {
473 cufftExecC2C( m_plan, (cufftComplex *)in, (cufftComplex *)out, CUFFT_FORWARD );
474 }
475};
476
477#endif
478
479} // namespace ft
480} // namespace math
481} // namespace mx
482
483#endif // fftT_hpp
void plan(int nx, int ny, ft::dir ndir=dir::forward, bool inPlace=false, typename std::enable_if< crank==2 >::type *=0)
Planning routine for rank 2 transforms.
void operator()(outputT *out, inputT *in) const
Conduct the FFT.
void operator()(eigenArrayOutT &out, eigenArrayInT &in) const
Conduct the MFT.
void plan(int nx, ft::dir ndir=dir::forward, bool inPlace=false, typename std::enable_if< crank==1 >::type *=0)
Planning routine for rank 1 transforms.
fftT(int nx, int ny, int nz, dir ndir=dir::forward, bool inPlace=false, typename std::enable_if< crank==3 >::type *=0)
Constructor for rank 3 FFT.
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.
fftT(int nx, int ny, dir ndir=dir::forward, bool inPlace=false, typename std::enable_if< crank==2 >::type *=0)
Constructor for rank 2 FFT.
ft::dir dir()
Get the direction of this FFT.
void plan(int nx, int ny, int nz, ft::dir ndir=dir::forward, bool inPlace=false, typename std::enable_if< crank==3 >::type *=0)
Planning routine for rank 3 transforms.
fftT(int nx, dir ndir=dir::forward, bool inPlace=false, typename std::enable_if< crank==1 >::type *=0)
Constructor for rank 1 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.
Fourier Transforms.
dir
Directions of the Fourier Transform.
Definition ft.hpp:41
@ backward
Specifies the backward transform.
@ forward
Specifies the forward transform.
constexpr floatT six_fifths()
Return 6/5 in the specified precision.
The mxlib c++ namespace.
Definition mxError.hpp:106
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).
Template declaration of a trueFalseT type.