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  //profiler_scope();
215  if(!this->fIsValid || !this->fInitialized)
216  {
217  //error
218  if(!this->fIsValid)
219  {
220  msg_error("operators",
221  "FFT input/output array dimensions/pointers are not valid. Aborting transform." << eom);
222  }
223  if(!this->fInitialized)
224  {
225  msg_error("operators", "FFT intialization failed. Aborting transform." << eom);
226  }
227  return false;
228  }
229 
230  //check memory alignment to determine if we can avoid copying the data around
231  if(HaveSameAlignment(in->GetData(), fInPtr))
232  {
233  //msg_info("operators", "FFT input/workspace data have the same memory alignment, no copy needed" << eom);
234  if(this->fForward)
235  {
236  fCurrentPlan = &fPlanForwardInPlace;
237  }
238  else
239  {
240  fCurrentPlan = &fPlanBackwardInPlace;
241  }
243  *fCurrentPlan,
245  in->GetData()),
247  in->GetData()));
248  }
249  else
250  {
251  if(this->fForward)
252  {
253  fCurrentPlan = &fPlanForward;
254  }
255  else
256  {
257  fCurrentPlan = &fPlanBackward;
258  }
259  //msg_info("operators", "FFT input/workspace do not have the same memory alignment, copy needed" << eom);
260  //alignment doesn't match so we need to use memcpy
261  std::memcpy(fInPtr, in->GetData(),
262  fTotalArraySize * sizeof(typename MHO_FFTWTypes< floating_point_value_type >::fftw_complex_type));
264  std::memcpy(in->GetData(), fOutPtr,
265  fTotalArraySize * sizeof(typename MHO_FFTWTypes< floating_point_value_type >::fftw_complex_type));
266  }
267 
268  for(size_t d = 0; d < XArgType::rank::value; d++)
269  {
270  if(this->fAxesToXForm[d])
271  {
272  this->IfTableTransformAxis(in, d);
273  }
274  };
275 
276  return true;
277  }
278 
287  virtual bool ExecuteOutOfPlace(const XArgType* in, XArgType* out) override
288  {
289  //if input and output point to the same array, don't bother copying data over
290  if(in && out && in != out)
291  {
292  out->Copy(*in);
293  }
294  return ExecuteInPlace(out);
295  }
296 
297  private:
304  virtual void AllocateWorkspace(std::size_t total_array_size)
305  {
307  fOutPtr = MHO_FFTWTypes< floating_point_value_type >::alloc_func(total_array_size);
308  fInPlacePtr = MHO_FFTWTypes< floating_point_value_type >::alloc_func(total_array_size);
309  }
310 
315  virtual void DeallocateWorkspace()
316  {
317  MHO_FFTWTypes< floating_point_value_type >::free_func(fInPtr);
318  MHO_FFTWTypes< floating_point_value_type >::free_func(fOutPtr);
319  MHO_FFTWTypes< floating_point_value_type >::free_func(fInPlacePtr);
320  }
321 
325  void DestructPlan()
326  {
327  if(this->fInitialized)
328  {
329  MHO_FFTWTypes< floating_point_value_type >::destroy_plan_func(fPlanForward);
330  fPlanForward = NULL;
331  MHO_FFTWTypes< floating_point_value_type >::destroy_plan_func(fPlanBackward);
332  fPlanBackward = NULL;
333  MHO_FFTWTypes< floating_point_value_type >::destroy_plan_func(fPlanForwardInPlace);
334  fPlanForwardInPlace = NULL;
335  MHO_FFTWTypes< floating_point_value_type >::destroy_plan_func(fPlanBackwardInPlace);
336  fPlanBackwardInPlace = NULL;
337  }
338  }
339 
345  bool ConstructPlan()
346  {
347  if(fInPtr == NULL || fOutPtr == NULL || fInPlacePtr == NULL)
348  {
349  return false;
350  }
351 
352  //then compute the rank of the transform (the number of axes selected)
353  int rank = 0;
354  for(size_t d = 0; d < XArgType::rank::value; d++)
355  {
356  if(this->fAxesToXForm[d])
357  {
358  rank++;
359  }
360  }
361 
362  //then compute the howmany_rank of the transform
363  //number of dimensions need to describe location of the starting point
364  int howmany_rank = XArgType::rank::value - rank;
365 
366  //now figure out the dimensions of the transforms
367  //note: unless we are transforming every dimension of the input,
368  //we may not fill up this array completely (hence the count variable)
369  int count = 0;
370  for(size_t d = 0; d < XArgType::rank::value; d++)
371  {
372  if(this->fAxesToXForm[d])
373  {
374  //figure out the dims parameter (length of the FFT and stride between elements)
375  fDims[count].n = this->fDimensionSize[d];
376  fDims[count].is =
377  MHO_NDArrayMath::StrideFromRowMajorIndex< XArgType::rank::value >(d, this->fDimensionSize);
378  fDims[count].os =
379  MHO_NDArrayMath::StrideFromRowMajorIndex< XArgType::rank::value >(d, this->fDimensionSize);
380  count++;
381  }
382  }
383 
384  count = 0;
385  for(size_t d = 0; d < XArgType::rank::value; d++)
386  {
387  if(!this->fAxesToXForm[d])
388  {
389  //figure out the dims parameter (length of the FFT and stride between elements)
390  fHowManyDims[count].n = this->fDimensionSize[d];
391  fHowManyDims[count].is =
392  MHO_NDArrayMath::StrideFromRowMajorIndex< XArgType::rank::value >(d, this->fDimensionSize);
393  fHowManyDims[count].os =
394  MHO_NDArrayMath::StrideFromRowMajorIndex< XArgType::rank::value >(d, this->fDimensionSize);
395  count++;
396  }
397  }
398 
399  fPlanForward = MHO_FFTWTypes< floating_point_value_type >::plan_guru_func(
400  rank, fDims, howmany_rank, fHowManyDims, fInPtr, fOutPtr, FFTW_FORWARD, HOPS_FFTW_PLAN_ALGO);
401 
402  fPlanBackward = MHO_FFTWTypes< floating_point_value_type >::plan_guru_func(
403  rank, fDims, howmany_rank, fHowManyDims, fInPtr, fOutPtr, FFTW_BACKWARD, HOPS_FFTW_PLAN_ALGO);
404 
405  fPlanForwardInPlace = MHO_FFTWTypes< floating_point_value_type >::plan_guru_func(
406  rank, fDims, howmany_rank, fHowManyDims, fInPlacePtr, fInPlacePtr, FFTW_FORWARD, HOPS_FFTW_PLAN_ALGO);
407 
408  fPlanBackwardInPlace = MHO_FFTWTypes< floating_point_value_type >::plan_guru_func(
409  rank, fDims, howmany_rank, fHowManyDims, fInPlacePtr, fInPlacePtr, FFTW_BACKWARD, HOPS_FFTW_PLAN_ALGO);
410 
411  if(fPlanForward != NULL && fPlanBackward != NULL && fPlanBackwardInPlace != NULL && fPlanForwardInPlace != NULL)
412  {
413  return true;
414  }
415  else
416  {
417  msg_warn("operators", "could not construct FFTW transform plan." << eom);
418  return false;
419  }
420  }
421 
429  template< typename XPtrType1, typename XPtrType2 > bool HaveSameAlignment(XPtrType1 ptr1, XPtrType2 ptr2)
430  {
431  if(!fHaveAlignmentFuncs)
432  {
433  return false;
434  }
435  return (MHO_FFTWTypes< floating_point_value_type >::alignment_of_func(
436  reinterpret_cast< floating_point_value_type* >(ptr1)) ==
437  MHO_FFTWTypes< floating_point_value_type >::alignment_of_func(
438  reinterpret_cast< floating_point_value_type* >(ptr2)));
439  }
440 
441  //data
442  size_t fTotalArraySize;
443  typename MHO_FFTWTypes< floating_point_value_type >::plan_type* fCurrentPlan;
444  typename MHO_FFTWTypes< floating_point_value_type >::iodim_type fDims[XArgType::rank::value];
445  typename MHO_FFTWTypes< floating_point_value_type >::iodim_type fHowManyDims[XArgType::rank::value];
446  typename MHO_FFTWTypes< floating_point_value_type >::plan_type fPlanForward;
447  typename MHO_FFTWTypes< floating_point_value_type >::plan_type fPlanBackward;
448  typename MHO_FFTWTypes< floating_point_value_type >::plan_type fPlanForwardInPlace;
449  typename MHO_FFTWTypes< floating_point_value_type >::plan_type fPlanBackwardInPlace;
450  typename MHO_FFTWTypes< floating_point_value_type >::fftw_complex_type_ptr fInPtr;
451  typename MHO_FFTWTypes< floating_point_value_type >::fftw_complex_type_ptr fOutPtr;
452  typename MHO_FFTWTypes< floating_point_value_type >::fftw_complex_type_ptr fInPlacePtr;
453 
454  //detect if we have mem alignment functions
455  bool fHaveAlignmentFuncs;
456 };
457 
458 } // namespace hops
459 
460 #endif
#define msg_debug(xKEY, xCONTENT)
Definition: MHO_Message.hh:291
#define msg_warn(xKEY, xCONTENT)
Definition: MHO_Message.hh:248
#define msg_error(xKEY, xCONTENT)
Definition: MHO_Message.hh:238
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:225
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:267
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:287
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_AdhocFlagging.hh:18
Class MHO_FFTWTypes.
Definition: MHO_FFTWTypes.hh:58
Definition: MHO_Meta.hh:372