diff --git a/fft_8hpp_source.html b/fft_8hpp_source.html index 2b7da7366..f5001fb38 100644 --- a/fft_8hpp_source.html +++ b/fft_8hpp_source.html @@ -133,832 +133,494 @@
4
5#pragma once
6
-
7#include <array>
-
8#include <cstddef>
-
9#include <stdexcept>
-
10#include <type_traits>
-
11#include <utility>
+
7#include <stdexcept>
+
8#include <type_traits>
+
9#include <utility>
+
10
+
11#include <ddc/ddc.hpp>
12
-
13#include <ddc/ddc.hpp>
-
14
-
15#include <Kokkos_Core.hpp>
-
16
-
17#if fftw_serial_AVAIL || fftw_omp_AVAIL
-
18#include <fftw3.h>
-
19#endif
-
20
-
21#if cufft_AVAIL
-
22#include <functional>
-
23#include <memory>
-
24
-
25#include <cuda_runtime_api.h>
-
26#include <cufft.h>
-
27#endif
-
28
-
29#if hipfft_AVAIL
-
30#include <functional>
-
31#include <memory>
-
32
-
33#include <hip/hip_runtime_api.h>
-
34#include <hipfft/hipfft.h>
-
35#endif
-
36
-
37#if fftw_serial_AVAIL || fftw_omp_AVAIL
-
38static_assert(sizeof(fftwf_complex) == sizeof(Kokkos::complex<float>));
-
39static_assert(alignof(fftwf_complex) <= alignof(Kokkos::complex<float>));
-
40
-
41static_assert(sizeof(fftw_complex) == sizeof(Kokkos::complex<double>));
-
42static_assert(alignof(fftw_complex) <= alignof(Kokkos::complex<double>));
-
43
-
44static_assert(sizeof(fftwl_complex) == sizeof(Kokkos::complex<long double>));
-
45static_assert(alignof(fftwl_complex) <= alignof(Kokkos::complex<long double>));
-
46#endif
-
47
-
48#if cufft_AVAIL
-
49static_assert(sizeof(cufftComplex) == sizeof(Kokkos::complex<float>));
-
50static_assert(alignof(cufftComplex) <= alignof(Kokkos::complex<float>));
-
51
-
52static_assert(sizeof(cufftDoubleComplex) == sizeof(Kokkos::complex<double>));
-
53static_assert(alignof(cufftDoubleComplex) <= alignof(Kokkos::complex<double>));
-
54#endif
-
55
-
56#if hipfft_AVAIL
-
57static_assert(sizeof(hipfftComplex) == sizeof(Kokkos::complex<float>));
-
58static_assert(alignof(hipfftComplex) <= alignof(Kokkos::complex<float>));
-
59
-
60static_assert(sizeof(hipfftDoubleComplex) == sizeof(Kokkos::complex<double>));
-
61static_assert(alignof(hipfftDoubleComplex) <= alignof(Kokkos::complex<double>));
-
62#endif
-
63
-
64namespace ddc {
-
65
-
66/**
-
67 * @brief A templated tag representing a continuous dimension in the Fourier space associated to the original continuous dimension.
-
68 *
-
69 * @tparam The tag representing the original dimension.
-
70 */
-
71template <typename Dim>
-
72struct Fourier;
-
73
-
74/**
-
75 * @brief A named argument to choose the direction of the FFT.
-
76 *
-
77 * @see kwArgs_impl, kwArgs_fft
-
78 */
-
79enum class FFT_Direction {
-
80 FORWARD, ///< Forward, corresponds to direct FFT up to normalization
-
81 BACKWARD ///< Backward, corresponds to inverse FFT up to normalization
-
82};
-
83
-
84/**
-
85 * @brief A named argument to choose the type of normalization of the FFT.
-
86 *
-
87 * @see kwArgs_impl, kwArgs_fft
-
88 */
-
89enum class FFT_Normalization {
-
90 OFF, ///< No normalization. Un-normalized FFT is sum_j f(x_j)*e^-ikx_j
-
91 FORWARD, ///< Multiply by 1/N for forward FFT, no normalization for backward FFT
-
92 BACKWARD, ///< No normalization for forward FFT, multiply by 1/N for backward FFT
-
93 ORTHO, ///< Multiply by 1/sqrt(N)
-
94 FULL /**<
-
95 * Multiply by dx/sqrt(2*pi) for forward FFT and dk/sqrt(2*pi) for backward
-
96 * FFT. It is aligned with the usual definition of the (continuous) Fourier transform
-
97 * 1/sqrt(2*pi)*int f(x)*e^-ikx*dx, and thus may be relevant for spectral analysis applications.
-
98 */
-
99};
-
100
-
101} // namespace ddc
-
102
-
103namespace ddc::detail::fft {
-
104
-
105template <typename T>
-
106struct real_type
-
107{
-
108 using type = T;
+
13#include <KokkosFFT.hpp>
+
14#include <Kokkos_Core.hpp>
+
15
+
16namespace ddc {
+
17
+
18/**
+
19 * @brief A templated tag representing a continuous dimension in the Fourier space associated to the original continuous dimension.
+
20 *
+
21 * @tparam The tag representing the original dimension.
+
22 */
+
23template <typename Dim>
+
24struct Fourier;
+
25
+
26/**
+
27 * @brief A named argument to choose the direction of the FFT.
+
28 *
+
29 * @see kwArgs_impl, kwArgs_fft
+
30 */
+
31enum class FFT_Direction {
+
32 FORWARD, ///< Forward, corresponds to direct FFT up to normalization
+
33 BACKWARD ///< Backward, corresponds to inverse FFT up to normalization
+
34};
+
35
+
36/**
+
37 * @brief A named argument to choose the type of normalization of the FFT.
+
38 *
+
39 * @see kwArgs_impl, kwArgs_fft
+
40 */
+
41enum class FFT_Normalization {
+
42 OFF, ///< No normalization. Un-normalized FFT is sum_j f(x_j)*e^-ikx_j
+
43 FORWARD, ///< Multiply by 1/N for forward FFT, no normalization for backward FFT
+
44 BACKWARD, ///< No normalization for forward FFT, multiply by 1/N for backward FFT
+
45 ORTHO, ///< Multiply by 1/sqrt(N)
+
46 FULL /**<
+
47 * Multiply by dx/sqrt(2*pi) for forward FFT and dk/sqrt(2*pi) for backward
+
48 * FFT. It is aligned with the usual definition of the (continuous) Fourier transform
+
49 * 1/sqrt(2*pi)*int f(x)*e^-ikx*dx, and thus may be relevant for spectral analysis applications.
+
50 */
+
51};
+
52
+
53} // namespace ddc
+
54
+
55namespace ddc::detail::fft {
+
56
+
57template <typename T>
+
58struct real_type
+
59{
+
60 using type = T;
+
61};
+
62
+
63template <typename T>
+
64struct real_type<Kokkos::complex<T>>
+
65{
+
66 using type = T;
+
67};
+
68
+
69template <typename T>
+
70using real_type_t = typename real_type<T>::type;
+
71
+
72// is_complex : trait to determine if type is Kokkos::complex<something>
+
73template <typename T>
+
74struct is_complex : std::false_type
+
75{
+
76};
+
77
+
78template <typename T>
+
79struct is_complex<Kokkos::complex<T>> : std::true_type
+
80{
+
81};
+
82
+
83template <typename T>
+
84constexpr bool is_complex_v = is_complex<T>::value;
+
85
+
86// LastSelector: returns a if Dim==Last, else b
+
87template <typename T, typename Dim, typename Last>
+
88KOKKOS_FUNCTION constexpr T LastSelector(const T a, const T b)
+
89{
+
90 return std::is_same_v<Dim, Last> ? a : b;
+
91}
+
92
+
93template <typename T, typename Dim, typename First, typename Second, typename... Tail>
+
94KOKKOS_FUNCTION constexpr T LastSelector(const T a, const T b)
+
95{
+
96 return LastSelector<T, Dim, Second, Tail...>(a, b);
+
97}
+
98
+
99/*
+
100 * @brief A structure embedding the configuration of the impl FFT function: direction and type of normalization.
+
101 *
+
102 * @see FFT_impl
+
103 */
+
104struct kwArgs_impl
+
105{
+
106 ddc::FFT_Direction
+
107 direction; // Only effective for C2C transform and for normalization BACKWARD and FORWARD
+
108 ddc::FFT_Normalization normalization;
109};
110
-
111template <typename T>
-
112struct real_type<Kokkos::complex<T>>
-
113{
-
114 using type = T;
-
115};
-
116
-
117template <typename T>
-
118using real_type_t = typename real_type<T>::type;
-
119
-
120// is_complex : trait to determine if type is Kokkos::complex<something>
-
121template <typename T>
-
122struct is_complex : std::false_type
-
123{
-
124};
-
125
-
126template <typename T>
-
127struct is_complex<Kokkos::complex<T>> : std::true_type
-
128{
-
129};
-
130
-
131template <typename T>
-
132constexpr bool is_complex_v = is_complex<T>::value;
-
133
-
134// LastSelector: returns a if Dim==Last, else b
-
135template <typename T, typename Dim, typename Last>
-
136KOKKOS_FUNCTION constexpr T LastSelector(const T a, const T b)
+
111/**
+
112 * @brief Get the mesh size along a given dimension.
+
113 *
+
114 * @tparam DDim The dimension along which the mesh size is returned.
+
115 * @param x_mesh The mesh.
+
116 *
+
117 * @return The mesh size along the required dimension.
+
118 */
+
119template <typename DDim, typename... DDimX>
+
120int N(ddc::DiscreteDomain<DDimX...> x_mesh)
+
121{
+
122 static_assert(
+
123 (is_uniform_point_sampling_v<DDimX> && ...),
+
124 "DDimX dimensions should derive from UniformPointSampling");
+
125 return static_cast<int>(x_mesh.template extent<DDim>());
+
126}
+
127
+
128template <typename... DDimX>
+
129KokkosFFT::axis_type<sizeof...(DDimX)> axes()
+
130{
+
131 return KokkosFFT::axis_type<sizeof...(DDimX)> {
+
132 static_cast<int>(ddc::type_seq_rank_v<DDimX, ddc::detail::TypeSeq<DDimX...>>)...};
+
133}
+
134
+
135inline KokkosFFT::Normalization ddc_fft_normalization_to_kokkos_fft(
+
136 FFT_Normalization const ddc_fft_normalization)
137{
-
138 return std::is_same_v<Dim, Last> ? a : b;
-
139}
-
140
-
141template <typename T, typename Dim, typename First, typename Second, typename... Tail>
-
142KOKKOS_FUNCTION constexpr T LastSelector(const T a, const T b)
-
143{
-
144 return LastSelector<T, Dim, Second, Tail...>(a, b);
-
145}
+
138 if (ddc_fft_normalization == ddc::FFT_Normalization::OFF
+
139 || ddc_fft_normalization == ddc::FFT_Normalization::FULL) {
+
140 return KokkosFFT::Normalization::none;
+
141 }
+
142
+
143 if (ddc_fft_normalization == ddc::FFT_Normalization::FORWARD) {
+
144 return KokkosFFT::Normalization::forward;
+
145 }
146
-
147/**
-
148 * @brief A trait to identify the type of transformation (R2C, C2R, C2C...).
-
149 *
-
150 * It does not contain the information about the base type (float or double).
-
151 */
-
152enum class TransformType { R2R, R2C, C2R, C2C };
-
153
-
154template <typename T1, typename T2>
-
155struct transform_type
-
156{
-
157 static constexpr TransformType value = TransformType::R2R;
-
158};
-
159
-
160template <typename T1, typename T2>
-
161struct transform_type<T1, Kokkos::complex<T2>>
-
162{
-
163 static constexpr TransformType value = TransformType::R2C;
-
164};
-
165
-
166template <typename T1, typename T2>
-
167struct transform_type<Kokkos::complex<T1>, T2>
-
168{
-
169 static constexpr TransformType value = TransformType::C2R;
-
170};
-
171
-
172template <typename T1, typename T2>
-
173struct transform_type<Kokkos::complex<T1>, Kokkos::complex<T2>>
-
174{
-
175 static constexpr TransformType value = TransformType::C2C;
-
176};
-
177
-
178/**
-
179 * @brief A trait to get the TransformType for the input and output types.
-
180 *
-
181 * Internally check if T1 and T2 are Kokkos::complex<something> or not.
-
182 *
-
183 * @tparam T1 The input type.
-
184 * @tparam T2 The output type.
-
185 */
-
186template <typename T1, typename T2>
-
187constexpr TransformType transform_type_v = transform_type<T1, T2>::value;
-
188
-
189#if fftw_serial_AVAIL || fftw_omp_AVAIL
-
190// _fftw_type : compatible with both single and double precision
-
191template <typename T>
-
192struct _fftw_type
-
193{
-
194 using type = T;
-
195};
-
196
-
197template <typename T>
-
198struct _fftw_type<Kokkos::complex<T>>
-
199{
-
200 using type = std::
-
201 conditional_t<std::is_same_v<real_type_t<T>, float>, fftwf_complex, fftw_complex>;
-
202};
-
203
-
204// _fftw_plan : compatible with both single and double precision
-
205template <typename T>
-
206using _fftw_plan = std::conditional_t<std::is_same_v<real_type_t<T>, float>, fftwf_plan, fftw_plan>;
-
207
-
208// _fftw_plan_many_dft : templated function working for all types of transformation
-
209template <typename Tin, typename Tout, typename... Args, typename PenultArg, typename LastArg>
-
210_fftw_plan<Tin> _fftw_plan_many_dft(
-
211 [[maybe_unused]] PenultArg penultArg,
-
212 LastArg lastArg,
-
213 Args... args)
-
214{ // Ugly, penultArg and lastArg are passed before the rest because of a limitation of C++ (parameter packs must be last arguments)
-
215 const TransformType transformType = transform_type_v<Tin, Tout>;
-
216 if constexpr (transformType == TransformType::R2C && std::is_same_v<Tin, float>) {
-
217 return fftwf_plan_many_dft_r2c(args..., lastArg);
-
218 } else if constexpr (transformType == TransformType::R2C && std::is_same_v<Tin, double>) {
-
219 return fftw_plan_many_dft_r2c(args..., lastArg);
-
220 } else if constexpr (transformType == TransformType::C2R && std::is_same_v<Tout, float>) {
-
221 return fftwf_plan_many_dft_c2r(args..., lastArg);
-
222 } else if constexpr (transformType == TransformType::C2R && std::is_same_v<Tout, double>) {
-
223 return fftw_plan_many_dft_c2r(args..., lastArg);
-
224 } else if constexpr (
-
225 transformType == TransformType::C2C && std::is_same_v<Tin, Kokkos::complex<float>>) {
-
226 return fftwf_plan_many_dft(args..., penultArg, lastArg);
-
227 } else if constexpr (
-
228 transformType == TransformType::C2C && std::is_same_v<Tin, Kokkos::complex<double>>) {
-
229 return fftw_plan_many_dft(args..., penultArg, lastArg);
-
230 }
-
231 // else constexpr
-
232 // static_assert(false, "Transform type not supported");
-
233}
-
234
-
235#endif
-
236#if cufft_AVAIL
-
237// _cufft_type : compatible with both single and double precision
-
238template <typename T>
-
239struct _cufft_type
-
240{
-
241 using type = std::conditional_t<std::is_same_v<T, float>, cufftReal, cufftDoubleReal>;
-
242};
-
243
-
244template <typename T>
-
245struct _cufft_type<Kokkos::complex<T>>
-
246{
-
247 using type = std::
-
248 conditional_t<std::is_same_v<real_type_t<T>, float>, cufftComplex, cufftDoubleComplex>;
-
249};
-
250
-
251// cufft_transform_type : argument passed in the cufftMakePlan function
-
252template <typename Tin, typename Tout>
-
253constexpr auto cufft_transform_type()
-
254{
-
255 const TransformType transformType = transform_type_v<Tin, Tout>;
-
256 if constexpr (transformType == TransformType::R2C && std::is_same_v<Tin, float>)
-
257 return CUFFT_R2C;
-
258 else if constexpr (transformType == TransformType::R2C && std::is_same_v<Tin, double>)
-
259 return CUFFT_D2Z;
-
260 else if constexpr (transformType == TransformType::C2R && std::is_same_v<Tout, float>)
-
261 return CUFFT_C2R;
-
262 else if constexpr (transformType == TransformType::C2R && std::is_same_v<Tout, double>)
-
263 return CUFFT_Z2D;
-
264 else if constexpr (
-
265 transformType == TransformType::C2C && std::is_same_v<Tin, Kokkos::complex<float>>)
-
266 return CUFFT_C2C;
-
267 else if constexpr (
-
268 transformType == TransformType::C2C && std::is_same_v<Tin, Kokkos::complex<double>>)
-
269 return CUFFT_Z2Z;
-
270 // else constexpr
-
271 // static_assert(false, "Transform type not supported");
-
272}
-
273
-
274// cufftExec : argument passed in the cufftMakePlan function
-
275// _fftw_plan_many_dft : templated function working for all types of transformation
-
276template <typename Tin, typename Tout, typename... Args, typename LastArg>
-
277cufftResult _cufftExec([[maybe_unused]] LastArg lastArg, Args... args)
-
278{ // Ugly for same reason as fftw
-
279 const TransformType transformType = transform_type_v<Tin, Tout>;
-
280 if constexpr (transformType == TransformType::R2C && std::is_same_v<Tin, float>)
-
281 return cufftExecR2C(args...);
-
282 else if constexpr (transformType == TransformType::R2C && std::is_same_v<Tin, double>)
-
283 return cufftExecD2Z(args...);
-
284 else if constexpr (transformType == TransformType::C2R && std::is_same_v<Tout, float>)
-
285 return cufftExecC2R(args...);
-
286 else if constexpr (transformType == TransformType::C2R && std::is_same_v<Tout, double>)
-
287 return cufftExecZ2D(args...);
-
288 else if constexpr (
-
289 transformType == TransformType::C2C && std::is_same_v<Tin, Kokkos::complex<float>>)
-
290 return cufftExecC2C(args..., lastArg);
-
291 else if constexpr (
-
292 transformType == TransformType::C2C && std::is_same_v<Tin, Kokkos::complex<double>>)
-
293 return cufftExecZ2Z(args..., lastArg);
-
294 // else constexpr
-
295 // static_assert(false, "Transform type not supported");
-
296}
-
297#endif
-
298#if hipfft_AVAIL
-
299// _hipfft_type : compatible with both single and double precision
-
300template <typename T>
-
301struct _hipfft_type
-
302{
-
303 using type = std::conditional_t<std::is_same_v<T, float>, hipfftReal, hipfftDoubleReal>;
-
304};
-
305
-
306template <typename T>
-
307struct _hipfft_type<Kokkos::complex<T>>
-
308{
-
309 using type = std::conditional_t<
-
310 std::is_same_v<real_type_t<T>, float>,
-
311 hipfftComplex,
-
312 hipfftDoubleComplex>;
-
313};
-
314
-
315// hipfft_transform_type : argument passed in the hipfftMakePlan function
-
316template <typename Tin, typename Tout>
-
317constexpr auto hipfft_transform_type()
-
318{
-
319 const TransformType transformType = transform_type_v<Tin, Tout>;
-
320 if constexpr (transformType == TransformType::R2C && std::is_same_v<Tin, float>)
-
321 return HIPFFT_R2C;
-
322 else if constexpr (transformType == TransformType::R2C && std::is_same_v<Tin, double>)
-
323 return HIPFFT_D2Z;
-
324 else if constexpr (transformType == TransformType::C2R && std::is_same_v<Tout, float>)
-
325 return HIPFFT_C2R;
-
326 else if constexpr (transformType == TransformType::C2R && std::is_same_v<Tout, double>)
-
327 return HIPFFT_Z2D;
-
328 else if constexpr (
-
329 transformType == TransformType::C2C && std::is_same_v<Tin, Kokkos::complex<float>>)
-
330 return HIPFFT_C2C;
-
331 else if constexpr (
-
332 transformType == TransformType::C2C && std::is_same_v<Tin, Kokkos::complex<double>>)
-
333 return HIPFFT_Z2Z;
-
334 // else constexpr
-
335 // static_assert(false, "Transform type not supported");
-
336}
-
337
-
338// hipfftExec : argument passed in the hipfftMakePlan function
-
339// _fftw_plan_many_dft : templated function working for all types of transformation
-
340template <typename Tin, typename Tout, typename... Args, typename LastArg>
-
341hipfftResult _hipfftExec([[maybe_unused]] LastArg lastArg, Args... args)
-
342{
-
343 const TransformType transformType = transform_type_v<Tin, Tout>;
-
344 if constexpr (transformType == TransformType::R2C && std::is_same_v<Tin, float>)
-
345 return hipfftExecR2C(args...);
-
346 else if constexpr (transformType == TransformType::R2C && std::is_same_v<Tin, double>)
-
347 return hipfftExecD2Z(args...);
-
348 else if constexpr (transformType == TransformType::C2R && std::is_same_v<Tout, float>)
-
349 return hipfftExecC2R(args...);
-
350 else if constexpr (transformType == TransformType::C2R && std::is_same_v<Tout, double>)
-
351 return hipfftExecZ2D(args...);
-
352 else if constexpr (
-
353 transformType == TransformType::C2C && std::is_same_v<Tin, Kokkos::complex<float>>)
-
354 return hipfftExecC2C(args..., lastArg);
-
355 else if constexpr (
-
356 transformType == TransformType::C2C && std::is_same_v<Tin, Kokkos::complex<double>>)
-
357 return hipfftExecZ2Z(args..., lastArg);
-
358 // else constexpr
-
359 // static_assert(false, "Transform type not supported");
-
360}
-
361#endif
-
362
-
363/*
-
364 * @brief A structure embedding the configuration of the impl FFT function: direction and type of normalization.
-
365 *
-
366 * @see FFT_impl
-
367 */
-
368struct kwArgs_impl
-
369{
-
370 ddc::FFT_Direction
-
371 direction; // Only effective for C2C transform and for normalization BACKWARD and FORWARD
-
372 ddc::FFT_Normalization normalization;
-
373};
-
374
-
375/**
-
376 * @brief Get the mesh size along a given dimension.
-
377 *
-
378 * @tparam DDim The dimension along which the mesh size is returned.
-
379 * @param x_mesh The mesh.
-
380 *
-
381 * @return The mesh size along the required dimension.
-
382 */
-
383template <typename DDim, typename... DDimX>
-
384int N(ddc::DiscreteDomain<DDimX...> x_mesh)
-
385{
-
386 static_assert(
-
387 (is_uniform_point_sampling_v<DDimX> && ...),
-
388 "DDimX dimensions should derive from UniformPointSampling");
-
389 return static_cast<int>(x_mesh.template extent<DDim>());
-
390}
-
391
-
392/// @brief Core internal function to perform the FFT.
-
393template <typename Tin, typename Tout, typename ExecSpace, typename MemorySpace, typename... DDimX>
-
394void impl(
-
395 ExecSpace const& exec_space,
-
396 Tout* out_data,
-
397 Tin* in_data,
-
398 ddc::DiscreteDomain<DDimX...> mesh,
-
399 const kwArgs_impl& kwargs)
-
400{
-
401 static_assert(
-
402 std::is_same_v<real_type_t<Tin>, float> || std::is_same_v<real_type_t<Tin>, double>,
-
403 "Base type of Tin (and Tout) must be float or double.");
-
404 static_assert(
-
405 std::is_same_v<real_type_t<Tin>, real_type_t<Tout>>,
-
406 "Types Tin and Tout must be based on same type (float or double)");
+
147 if (ddc_fft_normalization == ddc::FFT_Normalization::BACKWARD) {
+
148 return KokkosFFT::Normalization::backward;
+
149 }
+
150
+
151 if (ddc_fft_normalization == ddc::FFT_Normalization::ORTHO) {
+
152 return KokkosFFT::Normalization::ortho;
+
153 }
+
154
+
155 throw std::runtime_error("ddc::FFT_Normalization not handled");
+
156}
+
157
+
158template <
+
159 typename ExecSpace,
+
160 typename ElementType,
+
161 typename DDom,
+
162 typename Layout,
+
163 typename MemorySpace,
+
164 typename T>
+
165void rescale(
+
166 ExecSpace const& exec_space,
+
167 ddc::ChunkSpan<ElementType, DDom, Layout, MemorySpace> const& chunk_span,
+
168 T const& value)
+
169{
+
170 ddc::parallel_for_each(
+
171 "ddc_fft_normalization",
+
172 exec_space,
+
173 chunk_span.domain(),
+
174 KOKKOS_LAMBDA(typename DDom::discrete_element_type const i) {
+
175 chunk_span(i) *= value;
+
176 });
+
177}
+
178
+
179/// @brief Core internal function to perform the FFT.
+
180template <
+
181 typename Tin,
+
182 typename Tout,
+
183 typename ExecSpace,
+
184 typename MemorySpace,
+
185 typename LayoutIn,
+
186 typename LayoutOut,
+
187 typename... DDimIn,
+
188 typename... DDimOut>
+
189void impl(
+
190 ExecSpace const& exec_space,
+
191 ddc::ChunkSpan<Tin, ddc::DiscreteDomain<DDimIn...>, LayoutIn, MemorySpace> const& in,
+
192 ddc::ChunkSpan<Tout, ddc::DiscreteDomain<DDimOut...>, LayoutOut, MemorySpace> const& out,
+
193 kwArgs_impl const& kwargs)
+
194{
+
195 static_assert(
+
196 std::is_same_v<real_type_t<Tin>, float> || std::is_same_v<real_type_t<Tin>, double>,
+
197 "Base type of Tin (and Tout) must be float or double.");
+
198 static_assert(
+
199 std::is_same_v<real_type_t<Tin>, real_type_t<Tout>>,
+
200 "Types Tin and Tout must be based on same type (float or double)");
+
201 static_assert(
+
202 Kokkos::SpaceAccessibility<ExecSpace, MemorySpace>::accessible,
+
203 "MemorySpace has to be accessible for ExecutionSpace.");
+
204
+
205 Kokkos::View<
+
206 ddc::detail::mdspan_to_kokkos_element_t<Tin, sizeof...(DDimIn)>,
+
207 ddc::detail::mdspan_to_kokkos_layout_t<LayoutIn>,
+
208 MemorySpace> const in_view
+
209 = in.allocation_kokkos_view();
+
210 Kokkos::View<
+
211 ddc::detail::mdspan_to_kokkos_element_t<Tout, sizeof...(DDimIn)>,
+
212 ddc::detail::mdspan_to_kokkos_layout_t<LayoutOut>,
+
213 MemorySpace> const out_view
+
214 = out.allocation_kokkos_view();
+
215 KokkosFFT::Normalization const kokkos_fft_normalization
+
216 = ddc_fft_normalization_to_kokkos_fft(kwargs.normalization);
+
217
+
218 // C2C
+
219 if constexpr (std::is_same_v<Tin, Tout>) {
+
220 if (kwargs.direction == ddc::FFT_Direction::FORWARD) {
+
221 KokkosFFT::
+
222 fftn(exec_space,
+
223 in_view,
+
224 out_view,
+
225 axes<DDimIn...>(),
+
226 kokkos_fft_normalization);
+
227 } else {
+
228 KokkosFFT::
+
229 ifftn(exec_space,
+
230 in_view,
+
231 out_view,
+
232 axes<DDimIn...>(),
+
233 kokkos_fft_normalization);
+
234 }
+
235 // R2C & C2R
+
236 } else {
+
237 if constexpr (is_complex_v<Tout>) {
+
238 assert(kwargs.direction == ddc::FFT_Direction::FORWARD);
+
239 KokkosFFT::
+
240 rfftn(exec_space,
+
241 in_view,
+
242 out_view,
+
243 axes<DDimIn...>(),
+
244 kokkos_fft_normalization);
+
245 } else {
+
246 assert(kwargs.direction == ddc::FFT_Direction::BACKWARD);
+
247 KokkosFFT::
+
248 irfftn(exec_space,
+
249 in_view,
+
250 out_view,
+
251 axes<DDimIn...>(),
+
252 kokkos_fft_normalization);
+
253 }
+
254 }
+
255
+
256 // The FULL normalization is mesh-dependant and thus handled by DDC
+
257 if (kwargs.normalization == ddc::FFT_Normalization::FULL) {
+
258 real_type_t<Tout> norm_coef;
+
259 if (kwargs.direction == ddc::FFT_Direction::FORWARD) {
+
260 norm_coef
+
261 = (((coordinate(ddc::select<DDimIn>(in.domain()).back())
+
262 - coordinate(ddc::select<DDimIn>(in.domain()).front()))
+
263 / (ddc::get<DDimIn>(in.domain().extents()) - 1)
+
264 / Kokkos::sqrt(2 * Kokkos::numbers::pi))
+
265 * ...);
+
266 } else {
+
267 norm_coef
+
268 = ((Kokkos::sqrt(2 * Kokkos::numbers::pi)
+
269 / (coordinate(ddc::select<DDimOut>(out.domain()).back())
+
270 - coordinate(ddc::select<DDimOut>(out.domain()).front()))
+
271 * (ddc::get<DDimOut>(out.domain().extents()) - 1)
+
272 / ddc::get<DDimOut>(out.domain().extents()))
+
273 * ...);
+
274 }
+
275
+
276 rescale(exec_space, out, norm_coef);
+
277 }
+
278}
+
279
+
280} // namespace ddc::detail::fft
+
281
+
282namespace ddc {
+
283
+
284/**
+
285 * @brief Initialize a Fourier discrete dimension.
+
286 *
+
287 * Initialize the (1D) discrete space representing the Fourier discrete dimension associated
+
288 * to the (1D) mesh passed as argument. It is a N-periodic PeriodicSampling with a periodic window of width 2*pi/dx.
+
289 *
+
290 * This value comes from the Nyquist-Shannon theorem: the period of the spectral domain is N*dk = 2*pi/dx.
+
291 * Adding to this the relations dx = (xmax-xmin)/(N-1), and dk = (kmax-kmin)/(N-1), we get kmax-kmin = 2*pi*(N-1)^2/N/(xmax-xmin),
+
292 * which is used in the implementation (xmax, xmin, kmin and kmax are the centers of lower and upper cells inside a single period of the meshes).
+
293 *
+
294 * @tparam DDimFx A PeriodicSampling representing the Fourier discrete dimension.
+
295 * @tparam DDimX The type of the original discrete dimension.
+
296 *
+
297 * @param x_mesh The DiscreteDomain representing the (1D) original mesh.
+
298 *
+
299 * @return The initialized Impl representing the discrete Fourier space.
+
300 *
+
301 * @see PeriodicSampling
+
302 */
+
303template <typename DDimFx, typename DDimX>
+
304typename DDimFx::template Impl<DDimFx, Kokkos::HostSpace> init_fourier_space(
+
305 ddc::DiscreteDomain<DDimX> x_mesh)
+
306{
+
307 static_assert(
+
308 is_uniform_point_sampling_v<DDimX>,
+
309 "DDimX dimensions should derive from UniformPointSampling");
+
310 static_assert(
+
311 is_periodic_sampling_v<DDimFx>,
+
312 "DDimFx dimensions should derive from PeriodicSampling");
+
313 auto [impl, ddom] = DDimFx::template init<DDimFx>(
+
314 ddc::Coordinate<typename DDimFx::continuous_dimension_type>(0),
+
315 ddc::Coordinate<typename DDimFx::continuous_dimension_type>(
+
316 2 * (ddc::detail::fft::N<DDimX>(x_mesh) - 1)
+
317 * (ddc::detail::fft::N<DDimX>(x_mesh) - 1)
+
318 / static_cast<double>(
+
319 ddc::detail::fft::N<DDimX>(x_mesh)
+
320 * (ddc::coordinate(x_mesh.back()) - ddc::coordinate(x_mesh.front())))
+
321 * Kokkos::numbers::pi),
+
322 ddc::DiscreteVector<DDimFx>(ddc::detail::fft::N<DDimX>(x_mesh)),
+
323 ddc::DiscreteVector<DDimFx>(ddc::detail::fft::N<DDimX>(x_mesh)));
+
324 return std::move(impl);
+
325}
+
326
+
327/**
+
328 * @brief Get the Fourier mesh.
+
329 *
+
330 * Compute the Fourier (or spectral) mesh on which the Discrete Fourier Transform of a
+
331 * discrete function is defined.
+
332 *
+
333 * @param x_mesh The DiscreteDomain representing the original mesh.
+
334 * @param C2C A flag indicating if a complex-to-complex DFT is going to be performed. Indeed,
+
335 * in this case the two meshes have same number of points, whereas for real-to-complex
+
336 * or complex-to-real DFT, each complex value of the Fourier-transformed function contains twice more
+
337 * information, and thus only half (actually Nx*Ny*(Nz/2+1) for 3D R2C FFT to take in account mode 0)
+
338 * values are needed (cf. DFT conjugate symmetry property for more information about this).
+
339 *
+
340 * @return The domain representing the Fourier mesh.
+
341 */
+
342template <typename... DDimFx, typename... DDimX>
+
343ddc::DiscreteDomain<DDimFx...> FourierMesh(ddc::DiscreteDomain<DDimX...> x_mesh, bool C2C)
+
344{
+
345 static_assert(
+
346 (is_uniform_point_sampling_v<DDimX> && ...),
+
347 "DDimX dimensions should derive from UniformPointSampling");
+
348 static_assert(
+
349 (is_periodic_sampling_v<DDimFx> && ...),
+
350 "DDimFx dimensions should derive from PeriodicPointSampling");
+
351 return ddc::DiscreteDomain<DDimFx...>(ddc::DiscreteDomain<DDimFx>(
+
352 ddc::DiscreteElement<DDimFx>(0),
+
353 ddc::DiscreteVector<DDimFx>(
+
354 (C2C ? ddc::detail::fft::N<DDimX>(x_mesh)
+
355 : ddc::detail::fft::LastSelector<double, DDimX, DDimX...>(
+
356 ddc::detail::fft::N<DDimX>(x_mesh) / 2 + 1,
+
357 ddc::detail::fft::N<DDimX>(x_mesh)))))...);
+
358}
+
359
+
360/**
+
361 * @brief A structure embedding the configuration of the exposed FFT function with the type of normalization.
+
362 *
+
363 * @see fft, ifft
+
364 */
+
365struct kwArgs_fft
+
366{
+
367 ddc::FFT_Normalization
+
368 normalization; ///< Enum member to identify the type of normalization performed
+
369};
+
370
+
371/**
+
372 * @brief Perform a direct Fast Fourier Transform.
+
373 *
+
374 * Compute the discrete Fourier transform of a function using the specialized implementation for the Kokkos::ExecutionSpace
+
375 * of the FFT algorithm.
+
376 *
+
377 * @tparam Tin The type of the input elements (float, Kokkos::complex<float>, double or Kokkos::complex<double>).
+
378 * @tparam Tout The type of the output elements (Kokkos::complex<float> or Kokkos::complex<double>).
+
379 * @tparam DDimFx... The parameter pack of the Fourier discrete dimensions.
+
380 * @tparam DDimX... The parameter pack of the original discrete dimensions.
+
381 * @tparam ExecSpace The type of the Kokkos::ExecutionSpace on which the FFT is performed. It determines which specialized
+
382 * backend is used (ie. fftw, cuFFT...).
+
383 * @tparam MemorySpace The type of the Kokkos::MemorySpace on which are stored the input and output discrete functions.
+
384 * @tparam LayoutIn The layout of the Chunkspan representing the input discrete function.
+
385 * @tparam LayoutOut The layout of the Chunkspan representing the output discrete function.
+
386 *
+
387 * @param exec_space The Kokkos::ExecutionSpace on which the FFT is performed.
+
388 * @param out The output discrete function, represented as a ChunkSpan storing values on a spectral mesh.
+
389 * @param in The input discrete function, represented as a ChunkSpan storing values on a mesh.
+
390 * @param kwargs The kwArgs_fft configuring the FFT.
+
391 */
+
392template <
+
393 typename Tin,
+
394 typename Tout,
+
395 typename... DDimFx,
+
396 typename... DDimX,
+
397 typename ExecSpace,
+
398 typename MemorySpace,
+
399 typename LayoutIn,
+
400 typename LayoutOut>
+
401void fft(
+
402 ExecSpace const& exec_space,
+
403 ddc::ChunkSpan<Tout, ddc::DiscreteDomain<DDimFx...>, LayoutOut, MemorySpace> out,
+
404 ddc::ChunkSpan<Tin, ddc::DiscreteDomain<DDimX...>, LayoutIn, MemorySpace> in,
+
405 ddc::kwArgs_fft kwargs = {ddc::FFT_Normalization::OFF})
+
406{
407 static_assert(
-
408 Kokkos::SpaceAccessibility<ExecSpace, MemorySpace>::accessible,
-
409 "MemorySpace has to be accessible for ExecutionSpace.");
-
410 static_assert(
-
411 (is_uniform_point_sampling_v<DDimX> && ...),
-
412 "DDimX dimensions should derive from UniformPointSampling");
-
413
-
414 std::array<int, sizeof...(DDimX)> n = {static_cast<int>(ddc::get<DDimX>(mesh.extents()))...};
-
415 int idist = 1;
-
416 int odist = 1;
-
417 for (std::size_t i = 0; i < sizeof...(DDimX); ++i) {
-
418 idist = transform_type_v<Tin, Tout> == TransformType::C2R && i == sizeof...(DDimX) - 1
-
419 ? idist * (n[i] / 2 + 1)
-
420 : idist * n[i];
-
421 odist = transform_type_v<Tin, Tout> == TransformType::R2C && i == sizeof...(DDimX) - 1
-
422 ? odist * (n[i] / 2 + 1)
-
423 : odist * n[i];
-
424 }
-
425
-
426#if fftw_serial_AVAIL
-
427 if constexpr (std::is_same_v<ExecSpace, Kokkos::Serial>) {
-
428 _fftw_plan<Tin> plan = _fftw_plan_many_dft<Tin, Tout>(
-
429 kwargs.direction == ddc::FFT_Direction::FORWARD ? FFTW_FORWARD : FFTW_BACKWARD,
-
430 FFTW_ESTIMATE,
-
431 static_cast<int>(sizeof...(DDimX)),
-
432 n.data(),
-
433 1,
-
434 reinterpret_cast<typename _fftw_type<Tin>::type*>(in_data),
-
435 static_cast<int*>(nullptr),
-
436 1,
-
437 idist,
-
438 reinterpret_cast<typename _fftw_type<Tout>::type*>(out_data),
-
439 static_cast<int*>(nullptr),
-
440 1,
-
441 odist);
-
442 if constexpr (std::is_same_v<real_type_t<Tin>, float>) {
-
443 fftwf_execute(plan);
-
444 fftwf_destroy_plan(plan);
-
445 } else {
-
446 fftw_execute(plan);
-
447 fftw_destroy_plan(plan);
-
448 }
-
449 }
-
450#endif
-
451#if fftw_omp_AVAIL
-
452 if constexpr (std::is_same_v<ExecSpace, Kokkos::OpenMP>) {
-
453 if constexpr (std::is_same_v<real_type_t<Tin>, float>) {
-
454 fftwf_init_threads();
-
455 fftwf_plan_with_nthreads(exec_space.concurrency());
-
456 } else {
-
457 fftw_init_threads();
-
458 fftw_plan_with_nthreads(exec_space.concurrency());
-
459 }
-
460 _fftw_plan<Tin> plan = _fftw_plan_many_dft<Tin, Tout>(
-
461 kwargs.direction == ddc::FFT_Direction::FORWARD ? FFTW_FORWARD : FFTW_BACKWARD,
-
462 FFTW_ESTIMATE,
-
463 static_cast<int>(sizeof...(DDimX)),
-
464 n.data(),
-
465 1,
-
466 reinterpret_cast<typename _fftw_type<Tin>::type*>(in_data),
-
467 static_cast<int*>(nullptr),
-
468 1,
-
469 idist,
-
470 reinterpret_cast<typename _fftw_type<Tout>::type*>(out_data),
-
471 static_cast<int*>(nullptr),
-
472 1,
-
473 odist);
-
474 if constexpr (std::is_same_v<real_type_t<Tin>, float>) {
-
475 fftwf_execute(plan);
-
476 fftwf_destroy_plan(plan);
-
477 } else {
-
478 fftw_execute(plan);
-
479 fftw_destroy_plan(plan);
-
480 }
-
481 }
-
482#endif
-
483#if cufft_AVAIL
-
484 if constexpr (std::is_same_v<ExecSpace, Kokkos::Cuda>) {
-
485 cudaStream_t stream = exec_space.cuda_stream();
-
486
-
487 cufftHandle unmanaged_plan = -1;
-
488 cufftResult cufft_rt = cufftCreate(&unmanaged_plan);
-
489
-
490 if (cufft_rt != CUFFT_SUCCESS)
-
491 throw std::runtime_error("cufftCreate failed");
-
492
-
493 std::unique_ptr<cufftHandle, std::function<void(cufftHandle*)>> const
-
494 managed_plan(&unmanaged_plan, [](cufftHandle* handle) { cufftDestroy(*handle); });
-
495
-
496 cufftSetStream(unmanaged_plan, stream);
-
497 cufft_rt = cufftPlanMany(
-
498 &unmanaged_plan, // plan handle
-
499 sizeof...(DDimX),
-
500 n.data(), // Nx, Ny...
-
501 nullptr,
-
502 1,
-
503 idist,
-
504 nullptr,
-
505 1,
-
506 odist,
-
507 cufft_transform_type<Tin, Tout>(),
-
508 1);
-
509
-
510 if (cufft_rt != CUFFT_SUCCESS)
-
511 throw std::runtime_error("cufftPlan failed");
-
512
-
513 cufft_rt = _cufftExec<Tin, Tout>(
-
514 kwargs.direction == ddc::FFT_Direction::FORWARD ? CUFFT_FORWARD : CUFFT_INVERSE,
-
515 unmanaged_plan,
-
516 reinterpret_cast<typename _cufft_type<Tin>::type*>(in_data),
-
517 reinterpret_cast<typename _cufft_type<Tout>::type*>(out_data));
-
518 if (cufft_rt != CUFFT_SUCCESS)
-
519 throw std::runtime_error("cufftExec failed");
-
520 }
-
521#endif
-
522#if hipfft_AVAIL
-
523 if constexpr (std::is_same_v<ExecSpace, Kokkos::HIP>) {
-
524 hipStream_t stream = exec_space.hip_stream();
-
525
-
526 hipfftHandle unmanaged_plan;
-
527 hipfftResult hipfft_rt = hipfftCreate(&unmanaged_plan);
-
528
-
529 if (hipfft_rt != HIPFFT_SUCCESS)
-
530 throw std::runtime_error("hipfftCreate failed");
-
531
-
532 std::unique_ptr<hipfftHandle, std::function<void(hipfftHandle*)>> const
-
533 managed_plan(&unmanaged_plan, [](hipfftHandle* handle) { hipfftDestroy(*handle); });
-
534
-
535 hipfftSetStream(unmanaged_plan, stream);
-
536 hipfft_rt = hipfftPlanMany(
-
537 &unmanaged_plan, // plan handle
-
538 sizeof...(DDimX),
-
539 n.data(), // Nx, Ny...
-
540 nullptr,
-
541 1,
-
542 idist,
-
543 nullptr,
-
544 1,
-
545 odist,
-
546 hipfft_transform_type<Tin, Tout>(),
-
547 1);
-
548
-
549 if (hipfft_rt != HIPFFT_SUCCESS)
-
550 throw std::runtime_error("hipfftPlan failed");
-
551
-
552 hipfft_rt = _hipfftExec<Tin, Tout>(
-
553 kwargs.direction == ddc::FFT_Direction::FORWARD ? HIPFFT_FORWARD : HIPFFT_BACKWARD,
-
554 unmanaged_plan,
-
555 reinterpret_cast<typename _hipfft_type<Tin>::type*>(in_data),
-
556 reinterpret_cast<typename _hipfft_type<Tout>::type*>(out_data));
-
557 if (hipfft_rt != HIPFFT_SUCCESS)
-
558 throw std::runtime_error("hipfftExec failed");
-
559 }
-
560#endif
-
561
-
562 if (kwargs.normalization != ddc::FFT_Normalization::OFF) {
-
563 real_type_t<Tout> norm_coef = 1;
-
564 if (kwargs.normalization == ddc::FFT_Normalization::FORWARD) {
-
565 if (kwargs.direction == ddc::FFT_Direction::FORWARD) {
-
566 norm_coef = 1. / (ddc::get<DDimX>(mesh.extents()) * ...);
-
567 }
-
568 } else if (kwargs.normalization == ddc::FFT_Normalization::BACKWARD) {
-
569 if (kwargs.direction == ddc::FFT_Direction::BACKWARD) {
-
570 norm_coef = 1. / (ddc::get<DDimX>(mesh.extents()) * ...);
-
571 }
-
572 } else if (kwargs.normalization == ddc::FFT_Normalization::ORTHO) {
-
573 norm_coef = 1. / Kokkos::sqrt((ddc::get<DDimX>(mesh.extents()) * ...));
-
574 } else if (kwargs.normalization == ddc::FFT_Normalization::FULL) {
-
575 if (kwargs.direction == ddc::FFT_Direction::FORWARD) {
-
576 norm_coef
-
577 = (((coordinate(ddc::select<DDimX>(mesh).back())
-
578 - coordinate(ddc::select<DDimX>(mesh).front()))
-
579 / (ddc::get<DDimX>(mesh.extents()) - 1)
-
580 / Kokkos::sqrt(2 * Kokkos::numbers::pi))
-
581 * ...);
-
582 } else {
-
583 norm_coef
-
584 = ((Kokkos::sqrt(2 * Kokkos::numbers::pi)
-
585 / (coordinate(ddc::select<DDimX>(mesh).back())
-
586 - coordinate(ddc::select<DDimX>(mesh).front()))
-
587 * (ddc::get<DDimX>(mesh.extents()) - 1)
-
588 / ddc::get<DDimX>(mesh.extents()))
-
589 * ...);
-
590 }
-
591 } else {
-
592 throw std::runtime_error("ddc::FFT_Normalization not handled");
-
593 }
-
594
-
595 Kokkos::parallel_for(
-
596 "ddc_fft_normalization",
-
597 Kokkos::RangePolicy<ExecSpace>(
-
598 exec_space,
-
599 0,
-
600 is_complex_v<Tout> && transform_type_v<Tin, Tout> != TransformType::C2C
-
601 ? (LastSelector<double, DDimX, DDimX...>(
-
602 ddc::get<DDimX>(mesh.extents()) / 2 + 1,
-
603 ddc::get<DDimX>(mesh.extents()))
-
604 * ...)
-
605 : (ddc::get<DDimX>(mesh.extents()) * ...)),
-
606 KOKKOS_LAMBDA(const int& i) { out_data[i] = out_data[i] * norm_coef; });
-
607 }
-
608}
-
609
-
610} // namespace ddc::detail::fft
-
611
-
612namespace ddc {
-
613
-
614/**
-
615 * @brief Initialize a Fourier discrete dimension.
-
616 *
-
617 * Initialize the (1D) discrete space representing the Fourier discrete dimension associated
-
618 * to the (1D) mesh passed as argument. It is a N-periodic PeriodicSampling with a periodic window of width 2*pi/dx.
-
619 *
-
620 * This value comes from the Nyquist-Shannon theorem: the period of the spectral domain is N*dk = 2*pi/dx.
-
621 * Adding to this the relations dx = (xmax-xmin)/(N-1), and dk = (kmax-kmin)/(N-1), we get kmax-kmin = 2*pi*(N-1)^2/N/(xmax-xmin),
-
622 * which is used in the implementation (xmax, xmin, kmin and kmax are the centers of lower and upper cells inside a single period of the meshes).
-
623 *
-
624 * @tparam DDimFx A PeriodicSampling representing the Fourier discrete dimension.
-
625 * @tparam DDimX The type of the original discrete dimension.
-
626 *
-
627 * @param x_mesh The DiscreteDomain representing the (1D) original mesh.
-
628 *
-
629 * @return The initialized Impl representing the discrete Fourier space.
-
630 *
-
631 * @see PeriodicSampling
-
632 */
-
633template <typename DDimFx, typename DDimX>
-
634typename DDimFx::template Impl<DDimFx, Kokkos::HostSpace> init_fourier_space(
-
635 ddc::DiscreteDomain<DDimX> x_mesh)
-
636{
-
637 static_assert(
-
638 is_uniform_point_sampling_v<DDimX>,
-
639 "DDimX dimensions should derive from UniformPointSampling");
-
640 static_assert(
-
641 is_periodic_sampling_v<DDimFx>,
-
642 "DDimFx dimensions should derive from PeriodicSampling");
-
643 auto [impl, ddom] = DDimFx::template init<DDimFx>(
-
644 ddc::Coordinate<typename DDimFx::continuous_dimension_type>(0),
-
645 ddc::Coordinate<typename DDimFx::continuous_dimension_type>(
-
646 2 * (ddc::detail::fft::N<DDimX>(x_mesh) - 1)
-
647 * (ddc::detail::fft::N<DDimX>(x_mesh) - 1)
-
648 / static_cast<double>(
-
649 ddc::detail::fft::N<DDimX>(x_mesh)
-
650 * (ddc::coordinate(x_mesh.back()) - ddc::coordinate(x_mesh.front())))
-
651 * Kokkos::numbers::pi),
-
652 ddc::DiscreteVector<DDimFx>(ddc::detail::fft::N<DDimX>(x_mesh)),
-
653 ddc::DiscreteVector<DDimFx>(ddc::detail::fft::N<DDimX>(x_mesh)));
-
654 return std::move(impl);
-
655}
-
656
-
657/**
-
658 * @brief Get the Fourier mesh.
-
659 *
-
660 * Compute the Fourier (or spectral) mesh on which the Discrete Fourier Transform of a
-
661 * discrete function is defined.
-
662 *
-
663 * @param x_mesh The DiscreteDomain representing the original mesh.
-
664 * @param C2C A flag indicating if a complex-to-complex DFT is going to be performed. Indeed,
-
665 * in this case the two meshes have same number of points, whereas for real-to-complex
-
666 * or complex-to-real DFT, each complex value of the Fourier-transformed function contains twice more
-
667 * information, and thus only half (actually Nx*Ny*(Nz/2+1) for 3D R2C FFT to take in account mode 0)
-
668 * values are needed (cf. DFT conjugate symmetry property for more information about this).
-
669 *
-
670 * @return The domain representing the Fourier mesh.
-
671 */
-
672template <typename... DDimFx, typename... DDimX>
-
673ddc::DiscreteDomain<DDimFx...> FourierMesh(ddc::DiscreteDomain<DDimX...> x_mesh, bool C2C)
-
674{
-
675 static_assert(
-
676 (is_uniform_point_sampling_v<DDimX> && ...),
-
677 "DDimX dimensions should derive from UniformPointSampling");
-
678 static_assert(
-
679 (is_periodic_sampling_v<DDimFx> && ...),
-
680 "DDimFx dimensions should derive from PeriodicPointSampling");
-
681 return ddc::DiscreteDomain<DDimFx...>(ddc::DiscreteDomain<DDimFx>(
-
682 ddc::DiscreteElement<DDimFx>(0),
-
683 ddc::DiscreteVector<DDimFx>(
-
684 (C2C ? ddc::detail::fft::N<DDimX>(x_mesh)
-
685 : ddc::detail::fft::LastSelector<double, DDimX, DDimX...>(
-
686 ddc::detail::fft::N<DDimX>(x_mesh) / 2 + 1,
-
687 ddc::detail::fft::N<DDimX>(x_mesh)))))...);
-
688}
-
689
-
690/**
-
691 * @brief A structure embedding the configuration of the exposed FFT function with the type of normalization.
-
692 *
-
693 * @see fft, ifft
-
694 */
-
695struct kwArgs_fft
-
696{
-
697 ddc::FFT_Normalization
-
698 normalization; ///< Enum member to identify the type of normalization performed
-
699};
-
700
-
701/**
-
702 * @brief Perform a direct Fast Fourier Transform.
-
703 *
-
704 * Compute the discrete Fourier transform of a function using the specialized implementation for the Kokkos::ExecutionSpace
-
705 * of the FFT algorithm.
-
706 *
-
707 * @tparam Tin The type of the input elements (float, Kokkos::complex<float>, double or Kokkos::complex<double>).
-
708 * @tparam Tout The type of the output elements (Kokkos::complex<float> or Kokkos::complex<double>).
-
709 * @tparam DDimFx... The parameter pack of the Fourier discrete dimensions.
-
710 * @tparam DDimX... The parameter pack of the original discrete dimensions.
-
711 * @tparam ExecSpace The type of the Kokkos::ExecutionSpace on which the FFT is performed. It determines which specialized
-
712 * backend is used (ie. fftw, cuFFT...).
-
713 * @tparam MemorySpace The type of the Kokkos::MemorySpace on which are stored the input and output discrete functions.
-
714 * @tparam LayoutIn The layout of the Chunkspan representing the input discrete function.
-
715 * @tparam LayoutOut The layout of the Chunkspan representing the output discrete function.
-
716 *
-
717 * @param exec_space The Kokkos::ExecutionSpace on which the FFT is performed.
-
718 * @param out The output discrete function, represented as a ChunkSpan storing values on a spectral mesh.
-
719 * @param in The input discrete function, represented as a ChunkSpan storing values on a mesh.
-
720 * @param kwargs The kwArgs_fft configuring the FFT.
-
721 */
-
722template <
-
723 typename Tin,
-
724 typename Tout,
-
725 typename... DDimFx,
-
726 typename... DDimX,
-
727 typename ExecSpace,
-
728 typename MemorySpace,
-
729 typename LayoutIn,
-
730 typename LayoutOut>
-
731void fft(
-
732 ExecSpace const& exec_space,
-
733 ddc::ChunkSpan<Tout, ddc::DiscreteDomain<DDimFx...>, LayoutOut, MemorySpace> out,
-
734 ddc::ChunkSpan<Tin, ddc::DiscreteDomain<DDimX...>, LayoutIn, MemorySpace> in,
-
735 ddc::kwArgs_fft kwargs = {ddc::FFT_Normalization::OFF})
-
736{
-
737 static_assert(
-
738 std::is_same_v<LayoutIn, Kokkos::layout_right>
-
739 && std::is_same_v<LayoutOut, Kokkos::layout_right>,
-
740 "Layouts must be right-handed");
-
741 static_assert(
-
742 (is_uniform_point_sampling_v<DDimX> && ...),
-
743 "DDimX dimensions should derive from UniformPointSampling");
-
744 static_assert(
-
745 (is_periodic_sampling_v<DDimFx> && ...),
-
746 "DDimFx dimensions should derive from PeriodicPointSampling");
-
747
-
748 ddc::detail::fft::impl<Tin, Tout, ExecSpace, MemorySpace, DDimX...>(
-
749 exec_space,
-
750 out.data_handle(),
-
751 in.data_handle(),
-
752 in.domain(),
-
753 {ddc::FFT_Direction::FORWARD, kwargs.normalization});
-
754}
-
755
-
756/**
-
757 * @brief Perform an inverse Fast Fourier Transform.
-
758 *
-
759 * Compute the inverse discrete Fourier transform of a spectral function using the specialized implementation for the Kokkos::ExecutionSpace
-
760 * of the iFFT algorithm.
-
761 *
-
762 * @warning C2R iFFT does NOT preserve input.
-
763 *
-
764 * @tparam Tin The type of the input elements (Kokkos::complex<float> or Kokkos::complex<double>).
-
765 * @tparam Tout The type of the output elements (float, Kokkos::complex<float>, double or Kokkos::complex<double>).
-
766 * @tparam DDimX... The parameter pack of the original discrete dimensions.
-
767 * @tparam DDimFx... The parameter pack of the Fourier discrete dimensions.
-
768 * @tparam ExecSpace The type of the Kokkos::ExecutionSpace on which the iFFT is performed. It determines which specialized
-
769 * backend is used (ie. fftw, cuFFT...).
-
770 * @tparam MemorySpace The type of the Kokkos::MemorySpace on which are stored the input and output discrete functions.
-
771 * @tparam LayoutIn The layout of the Chunkspan representing the input discrete function.
-
772 * @tparam LayoutOut The layout of the Chunkspan representing the output discrete function.
-
773 *
-
774 * @param exec_space The Kokkos::ExecutionSpace on which the iFFT is performed.
-
775 * @param out The output discrete function, represented as a ChunkSpan storing values on a mesh.
-
776 * @param in The input discrete function, represented as a ChunkSpan storing values on a spectral mesh.
-
777 * @param kwargs The kwArgs_fft configuring the iFFT.
-
778 */
-
779template <
-
780 typename Tin,
-
781 typename Tout,
-
782 typename... DDimX,
-
783 typename... DDimFx,
-
784 typename ExecSpace,
-
785 typename MemorySpace,
-
786 typename LayoutIn,
-
787 typename LayoutOut>
-
788void ifft(
-
789 ExecSpace const& exec_space,
-
790 ddc::ChunkSpan<Tout, ddc::DiscreteDomain<DDimX...>, LayoutOut, MemorySpace> out,
-
791 ddc::ChunkSpan<Tin, ddc::DiscreteDomain<DDimFx...>, LayoutIn, MemorySpace> in,
-
792 ddc::kwArgs_fft kwargs = {ddc::FFT_Normalization::OFF})
-
793{
-
794 static_assert(
-
795 std::is_same_v<LayoutIn, Kokkos::layout_right>
-
796 && std::is_same_v<LayoutOut, Kokkos::layout_right>,
-
797 "Layouts must be right-handed");
-
798 static_assert(
-
799 (is_uniform_point_sampling_v<DDimX> && ...),
-
800 "DDimX dimensions should derive from UniformPointSampling");
-
801 static_assert(
-
802 (is_periodic_sampling_v<DDimFx> && ...),
-
803 "DDimFx dimensions should derive from PeriodicPointSampling");
-
804
-
805 ddc::detail::fft::impl<Tin, Tout, ExecSpace, MemorySpace, DDimX...>(
-
806 exec_space,
-
807 out.data_handle(),
-
808 in.data_handle(),
-
809 out.domain(),
-
810 {ddc::FFT_Direction::BACKWARD, kwargs.normalization});
-
811}
-
812
-
813} // namespace ddc
+
408 std::is_same_v<LayoutIn, Kokkos::layout_right>
+
409 && std::is_same_v<LayoutOut, Kokkos::layout_right>,
+
410 "Layouts must be right-handed");
+
411 static_assert(
+
412 (is_uniform_point_sampling_v<DDimX> && ...),
+
413 "DDimX dimensions should derive from UniformPointSampling");
+
414 static_assert(
+
415 (is_periodic_sampling_v<DDimFx> && ...),
+
416 "DDimFx dimensions should derive from PeriodicPointSampling");
+
417
+
418 ddc::detail::fft::
+
419 impl(exec_space, in, out, {ddc::FFT_Direction::FORWARD, kwargs.normalization});
+
420}
+
421
+
422/**
+
423 * @brief Perform an inverse Fast Fourier Transform.
+
424 *
+
425 * Compute the inverse discrete Fourier transform of a spectral function using the specialized implementation for the Kokkos::ExecutionSpace
+
426 * of the iFFT algorithm.
+
427 *
+
428 * @warning C2R iFFT does NOT preserve input.
+
429 *
+
430 * @tparam Tin The type of the input elements (Kokkos::complex<float> or Kokkos::complex<double>).
+
431 * @tparam Tout The type of the output elements (float, Kokkos::complex<float>, double or Kokkos::complex<double>).
+
432 * @tparam DDimX... The parameter pack of the original discrete dimensions.
+
433 * @tparam DDimFx... The parameter pack of the Fourier discrete dimensions.
+
434 * @tparam ExecSpace The type of the Kokkos::ExecutionSpace on which the iFFT is performed. It determines which specialized
+
435 * backend is used (ie. fftw, cuFFT...).
+
436 * @tparam MemorySpace The type of the Kokkos::MemorySpace on which are stored the input and output discrete functions.
+
437 * @tparam LayoutIn The layout of the Chunkspan representing the input discrete function.
+
438 * @tparam LayoutOut The layout of the Chunkspan representing the output discrete function.
+
439 *
+
440 * @param exec_space The Kokkos::ExecutionSpace on which the iFFT is performed.
+
441 * @param out The output discrete function, represented as a ChunkSpan storing values on a mesh.
+
442 * @param in The input discrete function, represented as a ChunkSpan storing values on a spectral mesh.
+
443 * @param kwargs The kwArgs_fft configuring the iFFT.
+
444 */
+
445template <
+
446 typename Tin,
+
447 typename Tout,
+
448 typename... DDimX,
+
449 typename... DDimFx,
+
450 typename ExecSpace,
+
451 typename MemorySpace,
+
452 typename LayoutIn,
+
453 typename LayoutOut>
+
454void ifft(
+
455 ExecSpace const& exec_space,
+
456 ddc::ChunkSpan<Tout, ddc::DiscreteDomain<DDimX...>, LayoutOut, MemorySpace> out,
+
457 ddc::ChunkSpan<Tin, ddc::DiscreteDomain<DDimFx...>, LayoutIn, MemorySpace> in,
+
458 ddc::kwArgs_fft kwargs = {ddc::FFT_Normalization::OFF})
+
459{
+
460 static_assert(
+
461 std::is_same_v<LayoutIn, Kokkos::layout_right>
+
462 && std::is_same_v<LayoutOut, Kokkos::layout_right>,
+
463 "Layouts must be right-handed");
+
464 static_assert(
+
465 (is_uniform_point_sampling_v<DDimX> && ...),
+
466 "DDimX dimensions should derive from UniformPointSampling");
+
467 static_assert(
+
468 (is_periodic_sampling_v<DDimFx> && ...),
+
469 "DDimFx dimensions should derive from PeriodicPointSampling");
+
470
+
471 ddc::detail::fft::
+
472 impl(exec_space, in, out, {ddc::FFT_Direction::BACKWARD, kwargs.normalization});
+
473}
+
474
+
475} // namespace ddc
ddc::ChunkCommon< ElementType, DiscreteDomain< DDims... >, LayoutStridedPolicy >::ChunkSpan
friend class ChunkSpan
Definition chunk_common.hpp:87
ddc::DiscreteDomain::DiscreteDomain
friend class DiscreteDomain
Definition discrete_domain.hpp:55
ddc::DiscreteVector::operator!=
KOKKOS_FUNCTION constexpr bool operator!=(DiscreteVector< OTags... > const &rhs) const noexcept
Definition discrete_vector.hpp:313
ddc
The top-level namespace of DDC.
Definition aligned_allocator.hpp:11
-
ddc::kwArgs_fft::normalization
ddc::FFT_Normalization normalization
Enum member to identify the type of normalization performed.
Definition fft.hpp:698
-
ddc::ifft
void ifft(ExecSpace const &exec_space, ddc::ChunkSpan< Tout, ddc::DiscreteDomain< DDimX... >, LayoutOut, MemorySpace > out, ddc::ChunkSpan< Tin, ddc::DiscreteDomain< DDimFx... >, LayoutIn, MemorySpace > in, ddc::kwArgs_fft kwargs={ddc::FFT_Normalization::OFF})
Perform an inverse Fast Fourier Transform.
Definition fft.hpp:788
-
ddc::fft
void fft(ExecSpace const &exec_space, ddc::ChunkSpan< Tout, ddc::DiscreteDomain< DDimFx... >, LayoutOut, MemorySpace > out, ddc::ChunkSpan< Tin, ddc::DiscreteDomain< DDimX... >, LayoutIn, MemorySpace > in, ddc::kwArgs_fft kwargs={ddc::FFT_Normalization::OFF})
Perform a direct Fast Fourier Transform.
Definition fft.hpp:731
-
ddc::FourierMesh
ddc::DiscreteDomain< DDimFx... > FourierMesh(ddc::DiscreteDomain< DDimX... > x_mesh, bool C2C)
Get the Fourier mesh.
Definition fft.hpp:673
-
ddc::FFT_Normalization
FFT_Normalization
A named argument to choose the type of normalization of the FFT.
Definition fft.hpp:89
+
ddc::kwArgs_fft::normalization
ddc::FFT_Normalization normalization
Enum member to identify the type of normalization performed.
Definition fft.hpp:368
+
ddc::ifft
void ifft(ExecSpace const &exec_space, ddc::ChunkSpan< Tout, ddc::DiscreteDomain< DDimX... >, LayoutOut, MemorySpace > out, ddc::ChunkSpan< Tin, ddc::DiscreteDomain< DDimFx... >, LayoutIn, MemorySpace > in, ddc::kwArgs_fft kwargs={ddc::FFT_Normalization::OFF})
Perform an inverse Fast Fourier Transform.
Definition fft.hpp:454
+
ddc::fft
void fft(ExecSpace const &exec_space, ddc::ChunkSpan< Tout, ddc::DiscreteDomain< DDimFx... >, LayoutOut, MemorySpace > out, ddc::ChunkSpan< Tin, ddc::DiscreteDomain< DDimX... >, LayoutIn, MemorySpace > in, ddc::kwArgs_fft kwargs={ddc::FFT_Normalization::OFF})
Perform a direct Fast Fourier Transform.
Definition fft.hpp:401
+
ddc::FourierMesh
ddc::DiscreteDomain< DDimFx... > FourierMesh(ddc::DiscreteDomain< DDimX... > x_mesh, bool C2C)
Get the Fourier mesh.
Definition fft.hpp:343
+
ddc::FFT_Normalization
FFT_Normalization
A named argument to choose the type of normalization of the FFT.
Definition fft.hpp:41
ddc::FFT_Normalization::BACKWARD
@ BACKWARD
No normalization for forward FFT, multiply by 1/N for backward FFT.
ddc::FFT_Normalization::OFF
@ OFF
No normalization. Un-normalized FFT is sum_j f(x_j)*e^-ikx_j.
ddc::FFT_Normalization::ORTHO
@ ORTHO
Multiply by 1/sqrt(N)
ddc::FFT_Normalization::FULL
@ FULL
Multiply by dx/sqrt(2*pi) for forward FFT and dk/sqrt(2*pi) for backward FFT.
ddc::FFT_Normalization::FORWARD
@ FORWARD
Multiply by 1/N for forward FFT, no normalization for backward FFT.
-
ddc::init_fourier_space
DDimFx::template Impl< DDimFx, Kokkos::HostSpace > init_fourier_space(ddc::DiscreteDomain< DDimX > x_mesh)
Initialize a Fourier discrete dimension.
Definition fft.hpp:634
-
ddc::FFT_Direction
FFT_Direction
A named argument to choose the direction of the FFT.
Definition fft.hpp:79
+
ddc::init_fourier_space
DDimFx::template Impl< DDimFx, Kokkos::HostSpace > init_fourier_space(ddc::DiscreteDomain< DDimX > x_mesh)
Initialize a Fourier discrete dimension.
Definition fft.hpp:304
+
ddc::FFT_Direction
FFT_Direction
A named argument to choose the direction of the FFT.
Definition fft.hpp:31
ddc::FFT_Direction::BACKWARD
@ BACKWARD
Backward, corresponds to inverse FFT up to normalization.
ddc::FFT_Direction::FORWARD
@ FORWARD
Forward, corresponds to direct FFT up to normalization.
-
ddc::kwArgs_fft
A structure embedding the configuration of the exposed FFT function with the type of normalization.
Definition fft.hpp:696
+
ddc::kwArgs_fft
A structure embedding the configuration of the exposed FFT function with the type of normalization.
Definition fft.hpp:366
diff --git a/namespaceddc.html b/namespaceddc.html index e4117355f..8a8bc7d14 100644 --- a/namespaceddc.html +++ b/namespaceddc.html @@ -624,7 +624,7 @@  Initialize a Fourier discrete dimension.
  template<typename... DDimFx, typename... DDimX> -ddc::DiscreteDomain< DDimFx... > FourierMesh (ddc::DiscreteDomain< DDimX... > x_mesh, bool C2C) +ddc::DiscreteDomain< DDimFx... > FourierMesh (ddc::DiscreteDomain< DDimX... > x_mesh, bool C2C)  Get the Fourier mesh.
  template<typename Tin , typename Tout , typename... DDimFx, typename... DDimX, typename ExecSpace , typename MemorySpace , typename LayoutIn , typename LayoutOut > @@ -1165,7 +1165,7 @@

-

Definition at line 72 of file fft.hpp.

+

Definition at line 24 of file fft.hpp.

Collaboration diagram for ddc::Fourier< Dim >:
Collaboration diagram for ddc::kwArgs_fft:
@@ -1775,7 +1775,7 @@

Definition at line 79 of file fft.hpp.

+

Definition at line 31 of file fft.hpp.

@@ -1815,7 +1815,7 @@

Definition at line 89 of file fft.hpp.

+

Definition at line 41 of file fft.hpp.

@@ -4382,7 +4382,7 @@

Returns
The initialized Impl representing the discrete Fourier space.
See also
PeriodicSampling
-

Definition at line 634 of file fft.hpp.

+

Definition at line 304 of file fft.hpp.

@@ -4425,7 +4425,7 @@

Returns
The domain representing the Fourier mesh.
-

Definition at line 673 of file fft.hpp.

+

Definition at line 343 of file fft.hpp.

@@ -4494,7 +4494,7 @@

Definition at line 731 of file fft.hpp.

+

Definition at line 401 of file fft.hpp.

@@ -4564,7 +4564,7 @@

Definition at line 788 of file fft.hpp.

+

Definition at line 454 of file fft.hpp.

diff --git a/namespacemembers.html b/namespacemembers.html index 975b1948b..979395732 100644 --- a/namespacemembers.html +++ b/namespacemembers.html @@ -137,9 +137,8 @@

- c -

  • chunk_value_t : ddc
  • ChunkSpan() : ddc
  • ChunkView : ddc
  • -
  • coordinate() : ddc
  • -
  • Coordinate : ddc
  • coordinate() : ddc
  • +
  • Coordinate : ddc
  • coordinate_of_t : ddc
  • CoordinateElement : ddc
  • create_mirror() : ddc
  • diff --git a/search/all_2.js b/search/all_2.js index 4bb5d8eab..82ff72b29 100644 --- a/search/all_2.js +++ b/search/all_2.js @@ -33,7 +33,7 @@ var searchData= ['continuous_5fdimension_5ftype1_30',['continuous_dimension_type1',['../classddc_1_1SplineBuilder2D.html#a0eac449778659b7134074de3d3975f66',1,'ddc::SplineBuilder2D::continuous_dimension_type1'],['../classddc_1_1SplineEvaluator2D.html#aa55c51cd13ac919507cb6a2f540e348a',1,'ddc::SplineEvaluator2D::continuous_dimension_type1']]], ['continuous_5fdimension_5ftype2_31',['continuous_dimension_type2',['../classddc_1_1SplineEvaluator2D.html#a2a6f822ff56ab3715c0620b456e6b2be',1,'ddc::SplineEvaluator2D::continuous_dimension_type2'],['../classddc_1_1SplineBuilder2D.html#a350330978d3d5293bb8c7b6be10decae',1,'ddc::SplineBuilder2D::continuous_dimension_type2']]], ['continuous_5felement_5ftype_32',['continuous_element_type',['../classddc_1_1UniformPointSampling.html#a161040fcb8674519b2cf03d510883fa5',1,'ddc::UniformPointSampling::continuous_element_type'],['../classddc_1_1PeriodicSampling.html#ab204e83a5070c16fded936a42de5997c',1,'ddc::PeriodicSampling::continuous_element_type'],['../classddc_1_1NonUniformPointSampling.html#a6f8ca7023571e80f0a8fa2305cffb28c',1,'ddc::NonUniformPointSampling::continuous_element_type']]], - ['coordinate_33',['coordinate',['../classddc_1_1PeriodicSampling_1_1Impl.html#ada7b882455239f8328afd790eafc9d57',1,'ddc::PeriodicSampling::Impl::coordinate()'],['../namespaceddc.html#ac18286f7d289865ee020284651f29bb1',1,'ddc::Coordinate'],['../namespaceddc.html#ad1dd2f87c3672f42f1adbddcdaa78b97',1,'ddc::coordinate(DiscreteElement< DDim > const &c)'],['../classddc_1_1UniformPointSampling_1_1Impl.html#a7b8224f1299ed3091c4de59199e3bb74',1,'ddc::UniformPointSampling::Impl::coordinate()'],['../classddc_1_1NonUniformPointSampling_1_1Impl.html#a12850761ffb35df7c319119dac8dd4e4',1,'ddc::NonUniformPointSampling::Impl::coordinate()'],['../namespaceddc.html#a6b058df8c02517deafeeb424e2f8803a',1,'ddc::coordinate(DiscreteElement< DDim... > const &c)'],['../namespaceddc.html#acb92141804530afd55d2747b2f0e7398',1,'ddc::coordinate(DiscreteElement< DDim > const &c)']]], + ['coordinate_33',['coordinate',['../classddc_1_1PeriodicSampling_1_1Impl.html#ada7b882455239f8328afd790eafc9d57',1,'ddc::PeriodicSampling::Impl::coordinate()'],['../namespaceddc.html#ad1dd2f87c3672f42f1adbddcdaa78b97',1,'ddc::coordinate(DiscreteElement< DDim > const &c)'],['../namespaceddc.html#acb92141804530afd55d2747b2f0e7398',1,'ddc::coordinate(DiscreteElement< DDim > const &c)'],['../classddc_1_1UniformPointSampling_1_1Impl.html#a7b8224f1299ed3091c4de59199e3bb74',1,'ddc::UniformPointSampling::Impl::coordinate()'],['../classddc_1_1NonUniformPointSampling_1_1Impl.html#a12850761ffb35df7c319119dac8dd4e4',1,'ddc::NonUniformPointSampling::Impl::coordinate()'],['../namespaceddc.html#ac18286f7d289865ee020284651f29bb1',1,'ddc::Coordinate'],['../namespaceddc.html#a6b058df8c02517deafeeb424e2f8803a',1,'ddc::coordinate(DiscreteElement< DDim... > const &c)']]], ['coordinate_5fof_34',['coordinate_of',['../namespaceddc.html#structddc_1_1coordinate__of',1,'ddc']]], ['coordinate_5fof_5ft_35',['coordinate_of_t',['../namespaceddc.html#a8c3f382fc669c87da3d57d3113b35e74',1,'ddc']]], ['coordinateelement_36',['CoordinateElement',['../namespaceddc.html#a287fd69405cf97cad795bf3d70139928',1,'ddc']]],