JayBeams  0.1
Another project to have fun coding.
plan.hpp
Go to the documentation of this file.
1 #ifndef jb_fftw_plan_hpp
2 #define jb_fftw_plan_hpp
3 
5 #include <jb/fftw/cast.hpp>
6 #include <jb/complex_traits.hpp>
7 
8 #include <memory>
9 
10 namespace jb {
11 namespace fftw {
12 
13 int constexpr default_plan_flags =
14  (FFTW_ESTIMATE | FFTW_PRESERVE_INPUT | FFTW_UNALIGNED);
15 
16 namespace detail {
17 /**
18  * Helper function to check the inputs to a create_*_plan_*()
19  *
20  * @param in_elements the number of elements in the input vector or array
21  * @param on_elements the number of elements in the output vector or array
22  * @param in_nsamples the number of samples per-timeseries in the input array
23  * @param on_nsamples the number of samples per timeseries in the output array
24  * @param function_name the name of the function to include in the
25  * exception message if a problem is detected
26  * @throws std::invalid_argument if the sizes do not match, with a
27  * descriptive message.
28  */
30  std::size_t in_elements, std::size_t on_elements, std::size_t in_nsamples,
31  std::size_t on_nsamples, char const* function_name);
32 } // namespace detail
33 
34 /**
35  * Wrap FFTW3 plan objects.
36  *
37  * The FFTW3 optimizes execution by pre-computing coefficients and
38  * execution plans for a DFT based on the original types, size and
39  * alingment of the data. In C++, we prefer the type system to
40  * remember details like this instead of getting an error message when
41  * we use the wrong types (or a crash).
42  *
43  * In addition, the FFTW3 library uses different names for the types
44  * that have single (fftwf_*), double (fftw_*) or quad-precision
45  * (fftwl_*). In C++ we prefer to hide such details in generics.
46  *
47  * Finally, these plans must be destroyed to release resources.
48  * FFTW3, being a C library, requires wrappers to automate the
49  * destruction of these objects.
50  *
51  * @tparam in_timeseries_type the type of the input timeseries
52  * @tparam out_timeseries_type the type of the output timeseries
53  */
54 template <typename in_timeseries_type, typename out_timeseries_type>
55 class plan {
56 public:
57  //@{
58  /**
59  * type traits
60  */
61  using in_value_type =
63  using precision_type =
66  using std_complex_type = typename traits::std_complex_type;
67  using fftw_complex_type = typename traits::fftw_complex_type;
68  using fftw_plan_type = typename traits::fftw_plan_type;
69  //@}
70 
71  /// Create unusable, empty, or null plan
72  plan()
73  : p_(nullptr) {
74  }
75 
76  /// Basic move constructor.
77  plan(plan&& rhs)
78  : p_(rhs.p_) {
79  rhs.p_ = nullptr;
80  }
81 
82  /// Move assignment
83  plan& operator=(plan&& rhs) {
84  plan tmp(std::move(rhs));
85  std::swap(p_, tmp.p_);
86  return *this;
87  }
88 
89  /// Destructor, cleanup the plan.
90  ~plan() {
91  check_constraints checker;
92  if (p_ != nullptr) {
93  traits::destroy_plan(p_);
94  }
95  }
96 
97  //@{
98  /**
99  * @name Prevent copy construction and assignment
100  */
101  plan(plan const&) = delete;
102  plan& operator=(plan const&) = delete;
103  //@}
104 
105  /// Execute the plan for vectors
106  void execute(in_timeseries_type const& in, out_timeseries_type& out) const {
109  jb::detail::nsamples(in), jb::detail::nsamples(out), __func__);
110  execute_impl(fftw_cast(in), fftw_cast(out));
111  }
112 
113 private:
114  // forward declare a helper type to check compile-time constraints.
115  struct check_constraints;
116 
117  // grant access to create_*_impl functions
118  template <typename itype, typename otype>
119  friend plan<itype, otype> create_forward_plan(itype const&, otype&, int);
120  template <typename itype, typename otype>
121  friend plan<itype, otype> create_backward_plan(itype const&, otype&, int);
122  template <typename itype, typename otype>
123  friend plan<itype, otype> create_forward_plan_1d(itype const&, otype&, int);
124  template <typename itype, typename otype>
125  friend plan<itype, otype> create_backward_plan_1d(itype const&, otype&, int);
126 
127 private:
128  /// Execute the plan in the c2c case
129  void execute_impl(fftw_complex_type const* in, fftw_complex_type* out) const {
130  traits::execute_plan(p_, in, out);
131  }
132 
133  /// Execute the plan for arrays of r2c case
134  void execute_impl(precision_type const* in, fftw_complex_type* out) const {
135  traits::execute_plan(p_, in, out);
136  }
137 
138  /// Execute the plan for arrays of r2c case
139  void execute_impl(fftw_complex_type const* in, precision_type* out) const {
140  traits::execute_plan(p_, in, out);
141  }
142 
143  /// Create the direct plan for vectors in the c2c case.
145  std::size_t nsamples, fftw_complex_type const* in, fftw_complex_type* out,
146  int flags) {
147  return plan(traits::create_forward_plan(nsamples, in, out, flags));
148  }
149 
150  /// Create the inverse plan for vectors in the c2c case
152  std::size_t nsamples, fftw_complex_type const* in, fftw_complex_type* out,
153  int flags) {
154  return plan(traits::create_backward_plan(nsamples, in, out, flags));
155  }
156 
157  /// Create the plan for vectors in the r2c case
159  std::size_t nsamples, precision_type const* in, fftw_complex_type* out,
160  int flags) {
161  return plan(traits::create_plan(nsamples, in, out, flags));
162  }
163 
164  /// Create the plan for vectors in the c2r case
166  std::size_t nsamples, fftw_complex_type const* in, precision_type* out,
167  int flags) {
168  return plan(traits::create_plan(nsamples, in, out, flags));
169  }
170 
171  /// Create the direct plan for arrays in the c2c case.
173  int howmany, std::size_t nsamples, fftw_complex_type const* in,
174  fftw_complex_type* out, int flags) {
175  return plan(
176  traits::create_forward_plan_many(howmany, nsamples, in, out, flags));
177  }
178 
179  /// Create the inverse plan for arrays in the c2c case
181  int howmany, std::size_t nsamples, fftw_complex_type const* in,
182  fftw_complex_type* out, int flags) {
183  return plan(
184  traits::create_backward_plan_many(howmany, nsamples, in, out, flags));
185  }
186 
187  /// Create the batched plan for arrays in the r2c case
189  int howmany, std::size_t nsamples, precision_type const* in,
190  fftw_complex_type* out, int flags) {
191  return plan(traits::create_plan_many(howmany, nsamples, in, out, flags));
192  }
193 
194  /// Create the batched plan for arrays in the c2r case
196  int howmany, std::size_t nsamples, fftw_complex_type const* in,
197  precision_type* out, int flags) {
198  return plan(traits::create_plan_many(howmany, nsamples, in, out, flags));
199  }
200 
201 private:
202  /**
203  * Constructor from a raw FFTW plan.
204  *
205  * @param p the raw FFTW plan
206  */
207  explicit plan(fftw_plan_type p)
208  : p_(p) {
209  }
210 
211 private:
212  /// The raw FFTW plan
214 };
215 
216 /**
217  * Create a plan to compute many DFTs given the input and output
218  * arrays.
219  *
220  * @param in the input array of timeseries
221  * @param out the output array of timeseries
222  * @param flags the FFTW flags for the plan
223  * @throws std::invalid_argument if the input and output vectors are
224  * not compatible
225  * @returns a jb::fftw::plan<> of the right type.
226  *
227  * @tparam in_array_type the type of the input array of timeseries
228  * @tparam out_array_type the type of the output array of timeseries
229  */
230 template <typename in_array_type, typename out_array_type>
232  in_array_type const& in, out_array_type& out,
233  int flags = default_plan_flags) {
236  jb::detail::nsamples(in), jb::detail::nsamples(out), __func__);
237  auto howmany = jb::detail::element_count(in) / jb::detail::nsamples(in);
238  if (howmany == 1) {
240  jb::detail::nsamples(in), fftw_cast(in), fftw_cast(out), flags);
241  }
243  howmany, jb::detail::nsamples(in), fftw_cast(in), fftw_cast(out), flags);
244 }
245 
246 /**
247  * Create a plan to compute many inverse DFT given the input and output
248  * arrays.
249  *
250  * @param in the input timeseries
251  * @param out the output timeseries
252  * @param flags the FFTW flags for the plan
253  * @throws std::invalid_argument if the input and output vectors are
254  * not compatible
255  * @returns a jb::fftw::plan<> of the right type.
256  *
257  * @tparam in_array_type the type of the input timeseries
258  * @tparam out_array_type the type of the output timeseries
259  */
260 template <typename in_array_type, typename out_array_type>
262  in_array_type const& in, out_array_type& out,
263  int flags = default_plan_flags) {
266  jb::detail::nsamples(in), jb::detail::nsamples(out), __func__);
267  auto howmany = jb::detail::element_count(in) / jb::detail::nsamples(in);
268  if (howmany == 1) {
270  jb::detail::nsamples(in), fftw_cast(in), fftw_cast(out), flags);
271  }
273  howmany, jb::detail::nsamples(in), fftw_cast(in), fftw_cast(out), flags);
274 }
275 
276 /**
277  * Check the compile-time constraints for a jb::fftw::plan<>
278  */
279 template <typename in_timeseries_type, typename out_timeseries_type>
280 struct plan<in_timeseries_type, out_timeseries_type>::check_constraints {
282  using in_value_type =
284  using out_value_type =
286  using in_precision_type =
288  using out_precision_type =
290  static_assert(
291  std::is_same<in_precision_type, out_precision_type>::value,
292  "Mismatched precision_type, both timeseries must have the same"
293  " precision");
294  }
295 };
296 
297 } // namespace fftw
298 } // namespace jb
299 
300 #endif // jb_fftw_plan_hpp
plan & operator=(plan &&rhs)
Move assignment.
Definition: plan.hpp:83
~plan()
Destructor, cleanup the plan.
Definition: plan.hpp:90
fftw_plan_type p_
The raw FFTW plan.
Definition: plan.hpp:213
Wrap fftw_* types and operations to treat floating point values generically.
Definition: traits.hpp:22
static plan create_forward_impl(std::size_t nsamples, precision_type const *in, fftw_complex_type *out, int flags)
Create the plan for vectors in the r2c case.
Definition: plan.hpp:158
void execute_impl(fftw_complex_type const *in, precision_type *out) const
Execute the plan for arrays of r2c case.
Definition: plan.hpp:139
plan< invector, outvector > create_forward_plan_1d(outvector const &out, invector const &in, boost::compute::context &ct, boost::compute::command_queue &q, int batch_size=1)
Create a forward DFT plan.
Definition: plan.hpp:255
typename container_type::value_type element_type
Define the type of the elements in the container.
plan< in_array_type, out_array_type > create_forward_plan(in_array_type const &in, out_array_type &out, int flags=default_plan_flags)
Create a plan to compute many DFTs given the input and output arrays.
Definition: plan.hpp:231
Wrap FFTW3 plan objects.
Definition: plan.hpp:55
auto fftw_cast(vector &in) -> decltype(fftw_cast_array(&in[0]))
Definition: cast.hpp:33
typename traits::fftw_complex_type fftw_complex_type
Definition: plan.hpp:67
void execute_impl(precision_type const *in, fftw_complex_type *out) const
Execute the plan for arrays of r2c case.
Definition: plan.hpp:134
void execute_impl(fftw_complex_type const *in, fftw_complex_type *out) const
Execute the plan in the c2c case.
Definition: plan.hpp:129
static plan create_forward_many_impl(int howmany, std::size_t nsamples, precision_type const *in, fftw_complex_type *out, int flags)
Create the batched plan for arrays in the r2c case.
Definition: plan.hpp:188
plan()
Create unusable, empty, or null plan.
Definition: plan.hpp:72
void execute(in_timeseries_type const &in, out_timeseries_type &out) const
Execute the plan for vectors.
Definition: plan.hpp:106
static plan create_forward_impl(std::size_t nsamples, fftw_complex_type const *in, fftw_complex_type *out, int flags)
Create the direct plan for vectors in the c2c case.
Definition: plan.hpp:144
plan(fftw_plan_type p)
Constructor from a raw FFTW plan.
Definition: plan.hpp:207
static plan create_backward_many_impl(int howmany, std::size_t nsamples, fftw_complex_type const *in, precision_type *out, int flags)
Create the batched plan for arrays in the c2r case.
Definition: plan.hpp:195
Check the compile-time constraints for a jb::fftw::plan<>
Definition: plan.hpp:280
plan(plan &&rhs)
Basic move constructor.
Definition: plan.hpp:77
plan< in_array_type, out_array_type > create_backward_plan(in_array_type const &in, out_array_type &out, int flags=default_plan_flags)
Create a plan to compute many inverse DFT given the input and output arrays.
Definition: plan.hpp:261
static plan create_backward_impl(std::size_t nsamples, fftw_complex_type const *in, fftw_complex_type *out, int flags)
Create the inverse plan for vectors in the c2c case.
Definition: plan.hpp:151
typename jb::detail::array_traits< in_timeseries_type >::element_type in_value_type
type traits
Definition: plan.hpp:62
typename jb::extract_value_type< in_value_type >::precision precision_type
Definition: plan.hpp:64
static plan create_backward_impl(std::size_t nsamples, fftw_complex_type const *in, precision_type *out, int flags)
Create the plan for vectors in the c2r case.
Definition: plan.hpp:165
std::size_t nsamples(container_type const &a)
Count the elements in the last dimension of a vector-like container.
int constexpr default_plan_flags
Definition: plan.hpp:13
static plan create_backward_many_impl(int howmany, std::size_t nsamples, fftw_complex_type const *in, fftw_complex_type *out, int flags)
Create the inverse plan for arrays in the c2c case.
Definition: plan.hpp:180
static plan create_forward_many_impl(int howmany, std::size_t nsamples, fftw_complex_type const *in, fftw_complex_type *out, int flags)
Create the direct plan for arrays in the c2c case.
Definition: plan.hpp:172
std::size_t element_count(container_type const &a)
Count the number of elements for a vector-like container.
void check_plan_inputs(std::size_t in_elements, std::size_t on_elements, std::size_t in_nsamples, std::size_t on_nsamples, char const *function_name)
Helper function to check the inputs to a create_*_plan_*()
Definition: plan.cpp:10
typename traits::fftw_plan_type fftw_plan_type
Definition: plan.hpp:68
typename traits::std_complex_type std_complex_type
Definition: plan.hpp:66
The top-level namespace for the JayBeams library.
Definition: as_hhmmss.hpp:7