Branch data Line data Source code
1 : : //
2 : : // Class FFT
3 : : // The FFT class performs complex-to-complex,
4 : : // real-to-complex on IPPL Fields.
5 : : // FFT is templated on the type of transform to be performed,
6 : : // the dimensionality of the Field to transform, and the
7 : : // floating-point precision type of the Field (float or double).
8 : : // Currently, we use heffte for taking the transforms and the class FFT
9 : : // serves as an interface between IPPL and heffte. In making this interface,
10 : : // we have referred Cabana library.
11 : : // https://github.com/ECP-copa/Cabana.
12 : : //
13 : : // Copyright (c) 2021, Sriramkrishnan Muralikrishnan,
14 : : // Paul Scherrer Institut, Villigen PSI, Switzerland
15 : : // All rights reserved
16 : : //
17 : : // This file is part of IPPL.
18 : : //
19 : : /**
20 : : Implementations for FFT constructor/destructor and transforms
21 : : */
22 : :
23 : : #include "Utility/IpplTimings.h"
24 : :
25 : : #include "Field/BareField.h"
26 : :
27 : : #include "FieldLayout/FieldLayout.h"
28 : :
29 : : namespace ippl {
30 : :
31 : : template <typename Field, template <typename...> class FFT, typename Backend, typename T>
32 [ + - ]: 4 : FFTBase<Field, FFT, Backend, T>::FFTBase(const Layout_t& layout, const ParameterList& params) {
33 : : std::array<long long, 3> low;
34 : : std::array<long long, 3> high;
35 : :
36 [ + - ]: 4 : const NDIndex<Dim> lDom = layout.getLocalNDIndex();
37 [ + - ]: 4 : domainToBounds(lDom, low, high);
38 : :
39 : 4 : heffte::box3d<long long> inbox = {low, high};
40 : 4 : heffte::box3d<long long> outbox = {low, high};
41 : :
42 [ + - ]: 4 : setup(inbox, outbox, params);
43 : 4 : }
44 : :
45 : : template <typename Field, template <typename...> class FFT, typename Backend, typename T>
46 : 12 : void FFTBase<Field, FFT, Backend, T>::domainToBounds(const NDIndex<Dim>& domain,
47 : : std::array<long long, 3>& low,
48 : : std::array<long long, 3>& high) {
49 [ + - ]: 12 : low.fill(0);
50 [ + - ]: 12 : high.fill(0);
51 : :
52 : : /**
53 : : * Static cast to detail::long long (uint64_t) is necessary, as heffte::box3d requires it
54 : : * like that.
55 : : */
56 [ + + ]: 42 : for (size_t d = 0; d < Dim; ++d) {
57 : 30 : low[d] = static_cast<long long>(domain[d].first());
58 : 30 : high[d] = static_cast<long long>(domain[d].length() + domain[d].first() - 1);
59 : : }
60 : 12 : }
61 : :
62 : : /**
63 : : setup performs the initialization necessary.
64 : : */
65 : : template <typename Field, template <typename...> class FFT, typename Backend, typename T>
66 : 8 : void FFTBase<Field, FFT, Backend, T>::setup(const heffte::box3d<long long>& inbox,
67 : : const heffte::box3d<long long>& outbox,
68 : : const ParameterList& params) {
69 [ + - ]: 8 : heffte::plan_options heffteOptions = heffte::default_options<heffteBackend>();
70 : :
71 [ + - + - : 16 : if (!params.get<bool>("use_heffte_defaults")) {
- + ]
72 [ # # # # ]: 0 : heffteOptions.use_pencils = params.get<bool>("use_pencils");
73 [ # # # # ]: 0 : heffteOptions.use_reorder = params.get<bool>("use_reorder");
74 : : #ifdef Heffte_ENABLE_GPU
75 : : heffteOptions.use_gpu_aware = params.get<bool>("use_gpu_aware");
76 : : #endif
77 : :
78 [ # # # # : 0 : switch (params.get<int>("comm")) {
# # # #
# ]
79 : 0 : case a2a:
80 : 0 : heffteOptions.algorithm = heffte::reshape_algorithm::alltoall;
81 : 0 : break;
82 : 0 : case a2av:
83 : 0 : heffteOptions.algorithm = heffte::reshape_algorithm::alltoallv;
84 : 0 : break;
85 : 0 : case p2p:
86 : 0 : heffteOptions.algorithm = heffte::reshape_algorithm::p2p;
87 : 0 : break;
88 : 0 : case p2p_pl:
89 : 0 : heffteOptions.algorithm = heffte::reshape_algorithm::p2p_plined;
90 : 0 : break;
91 : 0 : default:
92 [ # # # # : 0 : throw IpplException("FFT::setup", "Unrecognized heffte communication type");
# # ]
93 : : }
94 : : }
95 : :
96 : : if constexpr (std::is_same_v<FFT<heffteBackend>, heffte::fft3d<heffteBackend>>) {
97 [ + - ]: 4 : heffte_m = std::make_shared<FFT<heffteBackend, long long>>(
98 : 4 : inbox, outbox, Comm->getCommunicator(), heffteOptions);
99 : : } else {
100 : 4 : heffte_m = std::make_shared<FFT<heffteBackend, long long>>(
101 [ + - + - : 12 : inbox, outbox, params.get<int>("r2c_direction"), Comm->getCommunicator(),
+ - ]
102 : : heffteOptions);
103 : : }
104 : :
105 : : // heffte::gpu::device_set(Comm->rank() % heffte::gpu::device_count());
106 [ + - ]: 8 : if (workspace_m.size() < heffte_m->size_workspace()) {
107 [ + - ]: 16 : workspace_m = workspace_t(heffte_m->size_workspace());
108 : : }
109 : 8 : }
110 : :
111 : : template <typename ComplexField>
112 : 0 : void FFT<CCTransform, ComplexField>::warmup(ComplexField& f) {
113 : 0 : this->transform(FORWARD, f);
114 : 0 : this->transform(BACKWARD, f);
115 : 0 : }
116 : :
117 : : template <typename ComplexField>
118 : 8 : void FFT<CCTransform, ComplexField>::transform(TransformDirection direction, ComplexField& f) {
119 : : static_assert(Dim == 2 || Dim == 3, "heFFTe only supports 2D and 3D");
120 : :
121 [ + - ]: 8 : auto fview = f.getView();
122 : 8 : const int nghost = f.getNghost();
123 : :
124 : : /**
125 : : *This copy to a temporary Kokkos view is needed because of following
126 : : *reasons:
127 : : *1) heffte wants the input and output fields without ghost layers
128 : : *2) heffte accepts data in layout left (by default) even though this
129 : : *can be changed during heffte box creation
130 : : */
131 : 8 : auto& tempField = this->tempField;
132 [ + - + + ]: 8 : if (tempField.size() != f.getOwned().size()) {
133 [ + - + - : 8 : tempField = detail::shrinkView("tempField", fview, nghost);
+ - ]
134 : : }
135 : :
136 : : using index_array_type = typename RangePolicy<Dim>::index_array_type;
137 [ + - + - ]: 8 : ippl::parallel_for(
138 : 8 : "copy from Kokkos FFT", getRangePolicy(fview, nghost),
139 [ + - + - : 36880 : KOKKOS_LAMBDA(const index_array_type& args) {
+ - - - ]
140 [ + - + - : 18432 : apply(tempField, args - nghost).real(apply(fview, args).real());
+ - + - +
- + - + -
+ - + - +
- + - +
- ][ # # #
# # # ][ #
# # # # #
# # # # #
# ]
141 [ + - + - : 18432 : apply(tempField, args - nghost).imag(apply(fview, args).imag());
+ - + - +
- + - + -
+ - + - +
- + - +
- ][ # # #
# # # ][ #
# # # # #
# # # # #
# ]
142 : : });
143 : :
144 [ + + ]: 8 : if (direction == FORWARD) {
145 [ + - + - : 4 : this->heffte_m->forward(tempField.data(), tempField.data(), this->workspace_m.data(),
+ - ]
146 : : heffte::scale::full);
147 [ + - ]: 4 : } else if (direction == BACKWARD) {
148 [ + - + - : 4 : this->heffte_m->backward(tempField.data(), tempField.data(), this->workspace_m.data(),
+ - ]
149 : : heffte::scale::none);
150 : : } else {
151 [ # # ]: 0 : throw std::logic_error("Only 1:forward and -1:backward are allowed as directions");
152 : : }
153 : :
154 [ + - + - ]: 8 : ippl::parallel_for(
155 : 8 : "copy to Kokkos FFT", getRangePolicy(fview, nghost),
156 [ + - + - : 36880 : KOKKOS_LAMBDA(const index_array_type& args) {
+ - - - -
- ]
157 [ + - + - : 18432 : apply(fview, args).real() = apply(tempField, args - nghost).real();
+ - + - +
- + - + -
+ - + - +
- + - +
- ][ # # #
# # # ][ #
# # # # #
# # # # #
# ]
158 [ + - + - : 18432 : apply(fview, args).imag() = apply(tempField, args - nghost).imag();
+ - + - +
- + - + -
+ - + - +
- + - +
- ][ # # #
# # # ][ #
# # # # #
# # # # #
# ]
159 : : });
160 : 8 : }
161 : :
162 : : //========================================================================
163 : : // FFT RCTransform Constructors
164 : : //========================================================================
165 : :
166 : : /**
167 : : *Create a new FFT object of type RCTransform, with given input and output
168 : : *layouts and heffte parameters.
169 : : */
170 : :
171 : : template <typename RealField>
172 : 4 : FFT<RCTransform, RealField>::FFT(const Layout_t& layoutInput, const Layout_t& layoutOutput,
173 [ + - ]: 4 : const ParameterList& params) {
174 : : /**
175 : : * Heffte requires to pass a 3D array even for 2D and
176 : : * 1D FFTs we just have to make the length in other
177 : : * dimensions to be 1.
178 : : */
179 : : std::array<long long, 3> lowInput;
180 : : std::array<long long, 3> highInput;
181 : : std::array<long long, 3> lowOutput;
182 : : std::array<long long, 3> highOutput;
183 : :
184 [ + - ]: 4 : const NDIndex<Dim>& lDomInput = layoutInput.getLocalNDIndex();
185 [ + - ]: 4 : const NDIndex<Dim>& lDomOutput = layoutOutput.getLocalNDIndex();
186 : :
187 [ + - ]: 4 : this->domainToBounds(lDomInput, lowInput, highInput);
188 [ + - ]: 4 : this->domainToBounds(lDomOutput, lowOutput, highOutput);
189 : :
190 : 4 : heffte::box3d<long long> inbox = {lowInput, highInput};
191 : 4 : heffte::box3d<long long> outbox = {lowOutput, highOutput};
192 : :
193 [ + - ]: 4 : this->setup(inbox, outbox, params);
194 : 4 : }
195 : :
196 : : template <typename RealField>
197 : 0 : void FFT<RCTransform, RealField>::warmup(RealField& f, ComplexField& g) {
198 : 0 : this->transform(FORWARD, f, g);
199 : 0 : this->transform(BACKWARD, f, g);
200 : 0 : }
201 : :
202 : : template <typename RealField>
203 : 8 : void FFT<RCTransform, RealField>::transform(TransformDirection direction, RealField& f,
204 : : ComplexField& g) {
205 : : static_assert(Dim == 2 || Dim == 3, "heFFTe only supports 2D and 3D");
206 : :
207 [ + - ]: 8 : auto fview = f.getView();
208 [ + - ]: 8 : auto gview = g.getView();
209 : 8 : const int nghostf = f.getNghost();
210 : 8 : const int nghostg = g.getNghost();
211 : :
212 : : /**
213 : : *This copy to a temporary Kokkos view is needed because of following
214 : : *reasons:
215 : : *1) heffte wants the input and output fields without ghost layers
216 : : *2) heffte accepts data in layout left (by default) eventhough this
217 : : *can be changed during heffte box creation
218 : : */
219 : 8 : auto& tempFieldf = this->tempField;
220 : 8 : auto& tempFieldg = this->tempFieldComplex;
221 [ + - + + ]: 8 : if (tempFieldf.size() != f.getOwned().size()) {
222 [ + - + - : 8 : tempFieldf = detail::shrinkView("tempFieldf", fview, nghostf);
+ - ]
223 : : }
224 [ + - + + ]: 8 : if (tempFieldg.size() != g.getOwned().size()) {
225 [ + - + - : 8 : tempFieldg = detail::shrinkView("tempFieldg", gview, nghostg);
+ - ]
226 : : }
227 : :
228 : : using index_array_type = typename RangePolicy<Dim>::index_array_type;
229 [ + - + - ]: 8 : ippl::parallel_for(
230 : 8 : "copy from Kokkos f field in FFT", getRangePolicy(fview, nghostf),
231 [ + - + - : 36880 : KOKKOS_LAMBDA(const index_array_type& args) {
+ - - - ]
232 [ + - + - : 18432 : apply(tempFieldf, args - nghostf) = apply(fview, args);
+ - + - +
- + - + -
+ - ]
[ # # # # ]
[ # # # #
# # # # ]
233 : : });
234 [ + - + - ]: 8 : ippl::parallel_for(
235 : 8 : "copy from Kokkos g field in FFT", getRangePolicy(gview, nghostg),
236 [ + - + - : 19600 : KOKKOS_LAMBDA(const index_array_type& args) {
+ - - - ]
237 [ + - + - : 9792 : apply(tempFieldg, args - nghostg).real(apply(gview, args).real());
+ - + - +
- + - + -
+ - + - +
- + - +
- ][ # # #
# # # ][ #
# # # # #
# # # # #
# ]
238 [ + - + - : 9792 : apply(tempFieldg, args - nghostg).imag(apply(gview, args).imag());
+ - + - +
- + - + -
+ - + - +
- + - +
- ][ # # #
# # # ][ #
# # # # #
# # # # #
# ]
239 : : });
240 : :
241 [ + + ]: 8 : if (direction == FORWARD) {
242 [ + - + - : 4 : this->heffte_m->forward(tempFieldf.data(), tempFieldg.data(), this->workspace_m.data(),
+ - ]
243 : : heffte::scale::full);
244 [ + - ]: 4 : } else if (direction == BACKWARD) {
245 [ + - + - : 4 : this->heffte_m->backward(tempFieldg.data(), tempFieldf.data(), this->workspace_m.data(),
+ - ]
246 : : heffte::scale::none);
247 : : } else {
248 [ # # ]: 0 : throw std::logic_error("Only 1:forward and -1:backward are allowed as directions");
249 : : }
250 : :
251 [ + - + - ]: 8 : ippl::parallel_for(
252 : 8 : "copy to Kokkos f field FFT", getRangePolicy(fview, nghostf),
253 [ + - + - : 36880 : KOKKOS_LAMBDA(const index_array_type& args) {
+ - - - -
- ]
254 [ + - + - : 18432 : apply(fview, args) = apply(tempFieldf, args - nghostf);
+ - + - +
- + - + -
+ - + - +
- + - +
- ][ # # #
# # # ][ #
# # # # #
# # # # #
# ]
255 : : });
256 : :
257 [ + - + - ]: 8 : ippl::parallel_for(
258 : 8 : "copy to Kokkos g field FFT", getRangePolicy(gview, nghostg),
259 [ + - + - : 19600 : KOKKOS_LAMBDA(const index_array_type& args) {
+ - - - -
- ]
260 [ + - + - : 9792 : apply(gview, args).real() = apply(tempFieldg, args - nghostg).real();
+ - + - +
- + - + -
+ - + - +
- + - +
- ][ # # #
# # # ][ #
# # # # #
# # # # #
# ]
261 [ + - + - : 9792 : apply(gview, args).imag() = apply(tempFieldg, args - nghostg).imag();
+ - + - +
- + - + -
+ - + - +
- + - +
- ][ # # #
# # # ][ #
# # # # #
# # # # #
# ]
262 : : });
263 : 8 : }
264 : :
265 : : template <typename Field>
266 : : void FFT<SineTransform, Field>::warmup(Field& f) {
267 : : this->transform(FORWARD, f);
268 : : this->transform(BACKWARD, f);
269 : : }
270 : :
271 : : template <typename Field>
272 : 0 : void FFT<SineTransform, Field>::transform(TransformDirection direction, Field& f) {
273 : : static_assert(Dim == 2 || Dim == 3, "heFFTe only supports 2D and 3D");
274 : : #ifdef Heffte_ENABLE_FFTW
275 : : if (direction == FORWARD) {
276 : : f = f / 8.0;
277 : : }
278 : : #endif
279 : :
280 [ # # ]: 0 : auto fview = f.getView();
281 : 0 : const int nghost = f.getNghost();
282 : :
283 : : /**
284 : : *This copy to a temporary Kokkos view is needed because of following
285 : : *reasons:
286 : : *1) heffte wants the input and output fields without ghost layers
287 : : *2) heffte accepts data in layout left (by default) eventhough this
288 : : *can be changed during heffte box creation
289 : : */
290 : 0 : auto& tempField = this->tempField;
291 [ # # # # ]: 0 : if (tempField.size() != f.getOwned().size()) {
292 [ # # # # : 0 : tempField = detail::shrinkView("tempField", fview, nghost);
# # ]
293 : : }
294 : :
295 : : using index_array_type = typename RangePolicy<Dim>::index_array_type;
296 [ # # # # ]: 0 : ippl::parallel_for(
297 : 0 : "copy from Kokkos FFT", getRangePolicy(fview, nghost),
298 [ # # # # : 0 : KOKKOS_LAMBDA(const index_array_type& args) {
# # # # ]
299 [ # # # # ]: 0 : apply(tempField, args - nghost) = apply(fview, args);
300 : : });
301 : :
302 [ # # ]: 0 : if (direction == FORWARD) {
303 [ # # # # : 0 : this->heffte_m->forward(tempField.data(), tempField.data(), this->workspace_m.data(),
# # ]
304 : : heffte::scale::full);
305 [ # # ]: 0 : } else if (direction == BACKWARD) {
306 [ # # # # : 0 : this->heffte_m->backward(tempField.data(), tempField.data(), this->workspace_m.data(),
# # ]
307 : : heffte::scale::none);
308 : : } else {
309 [ # # ]: 0 : throw std::logic_error("Only 1:forward and -1:backward are allowed as directions");
310 : : }
311 : :
312 [ # # # # ]: 0 : ippl::parallel_for(
313 : 0 : "copy to Kokkos FFT", getRangePolicy(fview, nghost),
314 [ # # # # : 0 : KOKKOS_LAMBDA(const index_array_type& args) {
# # # # #
# ]
315 [ # # # # : 0 : apply(fview, args) = apply(tempField, args - nghost);
# # ]
316 : : });
317 : : #ifdef Heffte_ENABLE_FFTW
318 : : if (direction == BACKWARD) {
319 : : f = f * 8.0;
320 : : }
321 : : #endif
322 : 0 : }
323 : :
324 : : template <typename Field>
325 : : void FFT<CosTransform, Field>::warmup(Field& f) {
326 : : this->transform(FORWARD, f);
327 : : this->transform(BACKWARD, f);
328 : : }
329 : :
330 : : template <typename Field>
331 : 0 : void FFT<CosTransform, Field>::transform(TransformDirection direction, Field& f) {
332 : : static_assert(Dim == 2 || Dim == 3, "heFFTe only supports 2D and 3D");
333 : : #ifdef Heffte_ENABLE_FFTW
334 : : if (direction == FORWARD) {
335 : : f = f / 8.0;
336 : : }
337 : : #endif
338 : :
339 [ # # ]: 0 : auto fview = f.getView();
340 : 0 : const int nghost = f.getNghost();
341 : :
342 : : /**
343 : : *This copy to a temporary Kokkos view is needed because of following
344 : : *reasons:
345 : : *1) heffte wants the input and output fields without ghost layers
346 : : *2) heffte accepts data in layout left (by default) eventhough this
347 : : *can be changed during heffte box creation
348 : : */
349 : 0 : auto& tempField = this->tempField;
350 [ # # # # ]: 0 : if (tempField.size() != f.getOwned().size()) {
351 [ # # # # : 0 : tempField = detail::shrinkView("tempField", fview, nghost);
# # ]
352 : : }
353 : :
354 : : using index_array_type = typename RangePolicy<Dim>::index_array_type;
355 [ # # # # ]: 0 : ippl::parallel_for(
356 : 0 : "copy from Kokkos FFT", getRangePolicy(fview, nghost),
357 [ # # # # : 0 : KOKKOS_LAMBDA(const index_array_type& args) {
# # # # ]
358 [ # # # # ]: 0 : apply(tempField, args - nghost) = apply(fview, args);
359 : : });
360 : :
361 [ # # ]: 0 : if (direction == FORWARD) {
362 [ # # # # : 0 : this->heffte_m->forward(tempField.data(), tempField.data(), this->workspace_m.data(),
# # ]
363 : : heffte::scale::full);
364 [ # # ]: 0 : } else if (direction == BACKWARD) {
365 [ # # # # : 0 : this->heffte_m->backward(tempField.data(), tempField.data(), this->workspace_m.data(),
# # ]
366 : : heffte::scale::none);
367 : : } else {
368 [ # # ]: 0 : throw std::logic_error("Only 1:forward and -1:backward are allowed as directions");
369 : : }
370 : :
371 [ # # # # ]: 0 : ippl::parallel_for(
372 : 0 : "copy to Kokkos FFT", getRangePolicy(fview, nghost),
373 [ # # # # : 0 : KOKKOS_LAMBDA(const index_array_type& args) {
# # # # #
# ]
374 [ # # # # : 0 : apply(fview, args) = apply(tempField, args - nghost);
# # ]
375 : : });
376 : : #ifdef Heffte_ENABLE_FFTW
377 : : if (direction == BACKWARD) {
378 : : f = f * 8.0;
379 : : }
380 : : #endif
381 : 0 : }
382 : :
383 : : template <typename Field>
384 : 0 : void FFT<Cos1Transform, Field>::warmup(Field& f) {
385 : 0 : this->transform(FORWARD, f);
386 : 0 : this->transform(BACKWARD, f);
387 : 0 : }
388 : :
389 : : template <typename Field>
390 : 0 : void FFT<Cos1Transform, Field>::transform(TransformDirection direction, Field& f) {
391 : : static_assert(Dim == 2 || Dim == 3, "heFFTe only supports 2D and 3D");
392 : :
393 : : /**
394 : : * This rescaling is needed to match the normalization constant
395 : : * between fftw and the other gpu interfaces. fftw rescales with an extra factor of 8.
396 : : */
397 : : #ifdef Heffte_ENABLE_FFTW
398 : : if (direction == FORWARD) {
399 : : f = f / 8.0;
400 : : }
401 : : #endif
402 : :
403 [ # # ]: 0 : auto fview = f.getView();
404 : 0 : const int nghost = f.getNghost();
405 : :
406 : : /**
407 : : *This copy to a temporary Kokkos view is needed because of following
408 : : *reasons:
409 : : *1) heffte wants the input and output fields without ghost layers
410 : : *2) heffte accepts data in layout left (by default) eventhough this
411 : : *can be changed during heffte box creation
412 : : */
413 : 0 : auto& tempField = this->tempField;
414 [ # # # # ]: 0 : if (tempField.size() != f.getOwned().size()) {
415 [ # # # # : 0 : tempField = detail::shrinkView("tempField", fview, nghost);
# # ]
416 : : }
417 : :
418 : : using index_array_type = typename RangePolicy<Dim>::index_array_type;
419 [ # # # # ]: 0 : ippl::parallel_for(
420 : 0 : "copy from Kokkos FFT", getRangePolicy(fview, nghost),
421 [ # # # # : 0 : KOKKOS_LAMBDA(const index_array_type& args) {
# # # # ]
422 [ # # # # ]: 0 : apply(tempField, args - nghost) = apply(fview, args);
[ # # # #
# # # # ]
423 : : });
424 : :
425 [ # # ]: 0 : if (direction == FORWARD) {
426 [ # # # # : 0 : this->heffte_m->forward(tempField.data(), tempField.data(), this->workspace_m.data(),
# # ]
427 : : heffte::scale::full);
428 [ # # ]: 0 : } else if (direction == BACKWARD) {
429 [ # # # # : 0 : this->heffte_m->backward(tempField.data(), tempField.data(), this->workspace_m.data(),
# # ]
430 : : heffte::scale::none);
431 : : } else {
432 [ # # ]: 0 : throw std::logic_error("Only 1:forward and -1:backward are allowed as directions");
433 : : }
434 : :
435 [ # # # # ]: 0 : ippl::parallel_for(
436 : 0 : "copy to Kokkos FFT", getRangePolicy(fview, nghost),
437 [ # # # # : 0 : KOKKOS_LAMBDA(const index_array_type& args) {
# # # # #
# ]
438 [ # # # # : 0 : apply(fview, args) = apply(tempField, args - nghost);
# # ][ # #
# # # # #
# # # #
# ]
439 : : });
440 : :
441 : : /**
442 : : * This rescaling is needed to match the normalization constant
443 : : * between fftw and the other gpu interfaces. fftw rescales with an extra factor of 8.
444 : : */
445 : : #ifdef Heffte_ENABLE_FFTW
446 : : if (direction == BACKWARD) {
447 : : f = f * 8.0;
448 : : }
449 : : #endif
450 : 0 : }
451 : :
452 : : } // namespace ippl
453 : :
454 : : // vi: set et ts=4 sw=4 sts=4:
455 : : // Local Variables:
456 : : // mode:c
457 : : // c-basic-offset: 4
458 : : // indent-tabs-mode: nil
459 : : // require-final-newline: nil
460 : : // End:
|