HOPS
HOPS class reference
MHO_MultidimensionalFastFourierTransformFFTW.hh
Go to the documentation of this file.
1 #ifndef MHO_MultidimensionalFastFourierTransformFFTW_HH__
2 #define MHO_MultidimensionalFastFourierTransformFFTW_HH__
3 
4 #include <cstring>
5 
6 #include "MHO_FFTWTypes.hh"
7 
8 #include "MHO_Message.hh"
9 #include "MHO_Meta.hh"
10 #include "MHO_NDArrayWrapper.hh"
11 #include "MHO_UnaryOperator.hh"
12 
14 #include "MHO_TableContainer.hh"
15 
16 #define HOPS_FFTW_PLAN_ALGO FFTW_ESTIMATE
17 
18 //could use FFTW_MEASURE instead
19 
20 namespace hops
21 {
22 
34 template< typename XArgType >
37 {
38  public:
40  "Array element type must be a complex floating point type.");
41 
42  using complex_value_type = typename XArgType::value_type;
43  using floating_point_value_type = typename XArgType::value_type::value_type;
44 
46  {
47  fTotalArraySize = 0;
48  fInPtr = NULL;
49  fOutPtr = NULL;
50  fCurrentPlan = NULL;
51  fInPlacePtr = NULL;
52  fPlanForward = NULL;
53  fPlanBackward = NULL;
54  fPlanForwardInPlace = NULL;
55  fPlanBackwardInPlace = NULL;
56  AllocateWorkspace(16); //pre-allocate a bit of space, so we can test for memory alignment
57  fHaveAlignmentFuncs = false;
58 
59  //determine what version of FFTW3 we have
60  //(fftw versions < 3.3.4 do not have functions to determine memory alignment)
61  int fftw3_major = MHO_FFTWTypeInfo::get_fftw_version_major();
62  int fftw3_minor = MHO_FFTWTypeInfo::get_fftw_version_minor();
63  int fftw3_patch = MHO_FFTWTypeInfo::get_fftw_version_patch();
64  if(fftw3_minor > 3)
65  {
66  fHaveAlignmentFuncs = true;
67  }
68  else if(fftw3_minor == 3)
69  {
70  if(fftw3_patch >= 4)
71  {
72  fHaveAlignmentFuncs = true;
73  }
74  }
75  };
76 
78  {
79  DeallocateWorkspace();
80  DestructPlan();
81  };
82 
83  protected:
91  virtual bool InitializeInPlace(XArgType* in) override
92  {
93  if(in != nullptr)
94  {
95  this->fIsValid = true;
96  }
97  else
98  {
99  this->fIsValid = false;
100  }
101 
102  if(this->fIsValid)
103  {
104  //check if the current transform sizes are the same as input
105  bool need_to_resize = false;
106  for(std::size_t i = 0; i < XArgType::rank::value; i++)
107  {
108  if(this->fDimensionSize[i] != in->GetDimension(i))
109  {
110  need_to_resize = true;
111  break;
112  }
113  }
114  if(need_to_resize || !this->fInitialized)
115  {
116  in->GetDimensions(this->fDimensionSize);
117  fTotalArraySize = MHO_NDArrayMath::TotalArraySize< XArgType::rank::value >(this->fDimensionSize);
118  bool aligned = HaveSameAlignment(in->GetData(), fInPtr);
119  if(!aligned)
120  {
121  DeallocateWorkspace();
122  AllocateWorkspace(fTotalArraySize);
123  }
124  DestructPlan();
125  this->fInitialized = ConstructPlan();
126 #ifdef HOPS_ENABLE_DEBUG_MSG
127  msg_debug("operators", "initialized an in-place FFTW3 FFT plan." << eom);
128  for(std::size_t i = 0; i < XArgType::rank::value; i++)
129  {
130  msg_debug("operators", "fft plan dimension: " << i << " has size: " << this->fDimensionSize[i]
131  << ", enabled? " << this->fAxesToXForm[i] << "." << eom);
132  }
133 #endif
134  }
135  else
136  {
137  this->fInitialized = true;
138  }
139  }
140  return (this->fInitialized && this->fIsValid);
141  }
142 
151  virtual bool InitializeOutOfPlace(const XArgType* in, XArgType* out) override
152  {
153  if(in != nullptr && out != nullptr)
154  {
155  this->fIsValid = true;
156  }
157  else
158  {
159  this->fIsValid = false;
160  }
161 
162  if(this->fIsValid)
163  {
164  //check if the arrays have the same dimensions
165  if(!HaveSameDimensions(in, out))
166  {
167  //resize the output array to match input
168  out->Resize(in->GetDimensions());
169  }
170  //check if the current transform sizes are the same as input
171  bool need_to_resize = false;
172  for(std::size_t i = 0; i < XArgType::rank::value; i++)
173  {
174  if(this->fDimensionSize[i] != in->GetDimension(i))
175  {
176  need_to_resize = true;
177  break;
178  }
179  }
180  if(need_to_resize || !this->fInitialized)
181  {
182  in->GetDimensions(this->fDimensionSize);
183  fTotalArraySize = MHO_NDArrayMath::TotalArraySize< XArgType::rank::value >(this->fDimensionSize);
184  DeallocateWorkspace();
185  AllocateWorkspace(fTotalArraySize);
186  DestructPlan();
187  this->fInitialized = ConstructPlan();
188 #ifdef HOPS_ENABLE_DEBUG_MSG
189  msg_debug("operators", "initialized an out-of-place FFTW3 FFT plan." << eom);
190  for(std::size_t i = 0; i < XArgType::rank::value; i++)
191  {
192  msg_debug("operators", "fft plan dimension: " << i << " has size: " << this->fDimensionSize[i]
193  << ", enabled? " << this->fAxesToXForm[i] << "." << eom);
194  }
195 #endif
196  }
197  else
198  {
199  this->fInitialized = true;
200  }
201  }
202  return (this->fInitialized && this->fIsValid);
203  }
204 
212  virtual bool ExecuteInPlace(XArgType* in) override
213  {
214  if(!this->fIsValid || !this->fInitialized)
215  {
216  //error
217  if(!this->fIsValid)
218  {
219  msg_error("operators",
220  "FFT input/output array dimensions/pointers are not valid. Aborting transform." << eom);
221  }
222  if(!this->fInitialized)
223  {
224  msg_error("operators", "FFT intialization failed. Aborting transform." << eom);
225  }
226  return false;
227  }
228 
229  //check memory alignment to determine if we can avoid copying the data around
230  if(HaveSameAlignment(in->GetData(), fInPtr))
231  {
232  //msg_info("operators", "FFT input/workspace data have the same memory alignment, no copy needed" << eom);
233  if(this->fForward)
234  {
235  fCurrentPlan = &fPlanForwardInPlace;
236  }
237  else
238  {
239  fCurrentPlan = &fPlanBackwardInPlace;
240  }
242  *fCurrentPlan,
244  in->GetData()),
246  in->GetData()));
247  }
248  else
249  {
250  if(this->fForward)
251  {
252  fCurrentPlan = &fPlanForward;
253  }
254  else
255  {
256  fCurrentPlan = &fPlanBackward;
257  }
258  //msg_info("operators", "FFT input/workspace do not have the same memory alignment, copy needed" << eom);
259  //alignment doesn't match so we need to use memcpy
260  std::memcpy(fInPtr, in->GetData(),
261  fTotalArraySize * sizeof(typename MHO_FFTWTypes< floating_point_value_type >::fftw_complex_type));
263  std::memcpy(in->GetData(), fOutPtr,
264  fTotalArraySize * sizeof(typename MHO_FFTWTypes< floating_point_value_type >::fftw_complex_type));
265  }
266 
267  for(size_t d = 0; d < XArgType::rank::value; d++)
268  {
269  if(this->fAxesToXForm[d])
270  {
271  this->IfTableTransformAxis(in, d);
272  }
273  };
274 
275  return true;
276  }
277 
286  virtual bool ExecuteOutOfPlace(const XArgType* in, XArgType* out) override
287  {
288  //if input and output point to the same array, don't bother copying data over
289  if(in && out && in != out)
290  {
291  out->Copy(*in);
292  }
293  return ExecuteInPlace(out);
294  }
295 
296  private:
303  virtual void AllocateWorkspace(std::size_t total_array_size)
304  {
306  fOutPtr = MHO_FFTWTypes< floating_point_value_type >::alloc_func(total_array_size);
307  fInPlacePtr = MHO_FFTWTypes< floating_point_value_type >::alloc_func(total_array_size);
308  }
309 
314  virtual void DeallocateWorkspace()
315  {
316  MHO_FFTWTypes< floating_point_value_type >::free_func(fInPtr);
317  MHO_FFTWTypes< floating_point_value_type >::free_func(fOutPtr);
318  MHO_FFTWTypes< floating_point_value_type >::free_func(fInPlacePtr);
319  }
320 
324  void DestructPlan()
325  {
326  if(this->fInitialized)
327  {
328  MHO_FFTWTypes< floating_point_value_type >::destroy_plan_func(fPlanForward);
329  fPlanForward = NULL;
330  MHO_FFTWTypes< floating_point_value_type >::destroy_plan_func(fPlanBackward);
331  fPlanBackward = NULL;
332  MHO_FFTWTypes< floating_point_value_type >::destroy_plan_func(fPlanForwardInPlace);
333  fPlanForwardInPlace = NULL;
334  MHO_FFTWTypes< floating_point_value_type >::destroy_plan_func(fPlanBackwardInPlace);
335  fPlanBackwardInPlace = NULL;
336  }
337  }
338 
344  bool ConstructPlan()
345  {
346  if(fInPtr == NULL || fOutPtr == NULL || fInPlacePtr == NULL)
347  {
348  return false;
349  }
350 
351  //then compute the rank of the transform (the number of axes selected)
352  int rank = 0;
353  for(size_t d = 0; d < XArgType::rank::value; d++)
354  {
355  if(this->fAxesToXForm[d])
356  {
357  rank++;
358  }
359  }
360 
361  //then compute the howmany_rank of the transform
362  //number of dimensions need to describe location of the starting point
363  int howmany_rank = XArgType::rank::value - rank;
364 
365  //now figure out the dimensions of the transforms
366  //note: unless we are transforming every dimension of the input,
367  //we may not fill up this array completely (hence the count variable)
368  int count = 0;
369  for(size_t d = 0; d < XArgType::rank::value; d++)
370  {
371  if(this->fAxesToXForm[d])
372  {
373  //figure out the dims parameter (length of the FFT and stride between elements)
374  fDims[count].n = this->fDimensionSize[d];
375  fDims[count].is =
376  MHO_NDArrayMath::StrideFromRowMajorIndex< XArgType::rank::value >(d, this->fDimensionSize);
377  fDims[count].os =
378  MHO_NDArrayMath::StrideFromRowMajorIndex< XArgType::rank::value >(d, this->fDimensionSize);
379  count++;
380  }
381  }
382 
383  count = 0;
384  for(size_t d = 0; d < XArgType::rank::value; d++)
385  {
386  if(!this->fAxesToXForm[d])
387  {
388  //figure out the dims parameter (length of the FFT and stride between elements)
389  fHowManyDims[count].n = this->fDimensionSize[d];
390  fHowManyDims[count].is =
391  MHO_NDArrayMath::StrideFromRowMajorIndex< XArgType::rank::value >(d, this->fDimensionSize);
392  fHowManyDims[count].os =
393  MHO_NDArrayMath::StrideFromRowMajorIndex< XArgType::rank::value >(d, this->fDimensionSize);
394  count++;
395  }
396  }
397 
398  fPlanForward = MHO_FFTWTypes< floating_point_value_type >::plan_guru_func(
399  rank, fDims, howmany_rank, fHowManyDims, fInPtr, fOutPtr, FFTW_FORWARD, HOPS_FFTW_PLAN_ALGO);
400 
401  fPlanBackward = MHO_FFTWTypes< floating_point_value_type >::plan_guru_func(
402  rank, fDims, howmany_rank, fHowManyDims, fInPtr, fOutPtr, FFTW_BACKWARD, HOPS_FFTW_PLAN_ALGO);
403 
404  fPlanForwardInPlace = MHO_FFTWTypes< floating_point_value_type >::plan_guru_func(
405  rank, fDims, howmany_rank, fHowManyDims, fInPlacePtr, fInPlacePtr, FFTW_FORWARD, HOPS_FFTW_PLAN_ALGO);
406 
407  fPlanBackwardInPlace = MHO_FFTWTypes< floating_point_value_type >::plan_guru_func(
408  rank, fDims, howmany_rank, fHowManyDims, fInPlacePtr, fInPlacePtr, FFTW_BACKWARD, HOPS_FFTW_PLAN_ALGO);
409 
410  if(fPlanForward != NULL && fPlanBackward != NULL && fPlanBackwardInPlace != NULL && fPlanForwardInPlace != NULL)
411  {
412  return true;
413  }
414  else
415  {
416  msg_warn("operators", "could not construct FFTW transform plan." << eom);
417  return false;
418  }
419  }
420 
428  template< typename XPtrType1, typename XPtrType2 > bool HaveSameAlignment(XPtrType1 ptr1, XPtrType2 ptr2)
429  {
430  if(!fHaveAlignmentFuncs){return false;}
431  return (MHO_FFTWTypes< floating_point_value_type >::alignment_of_func(
432  reinterpret_cast< floating_point_value_type* >(ptr1)) ==
433  MHO_FFTWTypes< floating_point_value_type >::alignment_of_func(
434  reinterpret_cast< floating_point_value_type* >(ptr2)));
435  }
436 
437  //data
438  size_t fTotalArraySize;
439  typename MHO_FFTWTypes< floating_point_value_type >::plan_type* fCurrentPlan;
440  typename MHO_FFTWTypes< floating_point_value_type >::iodim_type fDims[XArgType::rank::value];
441  typename MHO_FFTWTypes< floating_point_value_type >::iodim_type fHowManyDims[XArgType::rank::value];
442  typename MHO_FFTWTypes< floating_point_value_type >::plan_type fPlanForward;
443  typename MHO_FFTWTypes< floating_point_value_type >::plan_type fPlanBackward;
444  typename MHO_FFTWTypes< floating_point_value_type >::plan_type fPlanForwardInPlace;
445  typename MHO_FFTWTypes< floating_point_value_type >::plan_type fPlanBackwardInPlace;
446  typename MHO_FFTWTypes< floating_point_value_type >::fftw_complex_type_ptr fInPtr;
447  typename MHO_FFTWTypes< floating_point_value_type >::fftw_complex_type_ptr fOutPtr;
448  typename MHO_FFTWTypes< floating_point_value_type >::fftw_complex_type_ptr fInPlacePtr;
449 
450  //detect if we have mem alignment functions
451  bool fHaveAlignmentFuncs;
452 };
453 
454 } // namespace hops
455 
456 #endif
#define msg_debug(xKEY, xCONTENT)
Definition: MHO_Message.hh:297
#define msg_warn(xKEY, xCONTENT)
Definition: MHO_Message.hh:254
#define msg_error(xKEY, xCONTENT)
Definition: MHO_Message.hh:244
template meta-programming helper functions, mostly tuple access/modification
#define HOPS_FFTW_PLAN_ALGO
Definition: MHO_MultidimensionalFastFourierTransformFFTW.hh:16
static int get_fftw_version_major()
Retrieves the major version number of FFTW3.
Definition: MHO_FFTWTypes.hh:228
static int get_fftw_version_minor()
Retrieves the minor version number of FFTW3.
Definition: MHO_FFTWTypes.hh:246
static int get_fftw_version_patch()
Retrieves the patch version number from FFTW library.
Definition: MHO_FFTWTypes.hh:264
Class MHO_MultidimensionalFastFourierTransformFFTW.
Definition: MHO_MultidimensionalFastFourierTransformFFTW.hh:37
virtual bool ExecuteInPlace(XArgType *in) override
Function ExecuteInPlace.
Definition: MHO_MultidimensionalFastFourierTransformFFTW.hh:212
typename XArgType::value_type::value_type floating_point_value_type
Definition: MHO_MultidimensionalFastFourierTransformFFTW.hh:43
virtual bool InitializeInPlace(XArgType *in) override
Function InitializeInPlace.
Definition: MHO_MultidimensionalFastFourierTransformFFTW.hh:91
typename XArgType::value_type complex_value_type
Definition: MHO_MultidimensionalFastFourierTransformFFTW.hh:42
virtual bool InitializeOutOfPlace(const XArgType *in, XArgType *out) override
Function InitializeOutOfPlace.
Definition: MHO_MultidimensionalFastFourierTransformFFTW.hh:151
virtual ~MHO_MultidimensionalFastFourierTransformFFTW()
Definition: MHO_MultidimensionalFastFourierTransformFFTW.hh:77
MHO_MultidimensionalFastFourierTransformFFTW()
Definition: MHO_MultidimensionalFastFourierTransformFFTW.hh:45
virtual bool ExecuteOutOfPlace(const XArgType *in, XArgType *out) override
Copies input data to output and executes in-place if input/output are different.
Definition: MHO_MultidimensionalFastFourierTransformFFTW.hh:286
Class MHO_MultidimensionalFastFourierTransformInterface.
Definition: MHO_MultidimensionalFastFourierTransformInterface.hh:25
size_t fDimensionSize[XArgType::rank::value]
Definition: MHO_MultidimensionalFastFourierTransformInterface.hh:236
std::enable_if< !std::is_base_of< MHO_TableContainerBase, XCheckType >::value, void >::type IfTableTransformAxis(XArgType *, std::size_t)
Transforms axis of input data if transformation is enabled and axis was transformed.
Definition: MHO_MultidimensionalFastFourierTransformInterface.hh:129
bool fForward
Definition: MHO_MultidimensionalFastFourierTransformInterface.hh:232
bool fIsValid
Definition: MHO_MultidimensionalFastFourierTransformInterface.hh:231
bool fAxesToXForm[XArgType::rank::value]
Definition: MHO_MultidimensionalFastFourierTransformInterface.hh:237
bool fInitialized
Definition: MHO_MultidimensionalFastFourierTransformInterface.hh:233
Class MHO_UnaryOperator.
Definition: MHO_UnaryOperator.hh:24
Definition: MHO_ChannelLabeler.hh:17
Class MHO_FFTWTypes.
Definition: MHO_FFTWTypes.hh:59
Definition: MHO_Meta.hh:341