Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_FFT_Helper.H
Go to the documentation of this file.
1#ifndef AMREX_FFT_HELPER_H_
2#define AMREX_FFT_HELPER_H_
3#include <AMReX_Config.H>
4
5#include <AMReX.H>
6#include <AMReX_BLProfiler.H>
9#include <AMReX_Enum.H>
10#include <AMReX_FabArray.H>
11#include <AMReX_Gpu.H>
12#include <AMReX_GpuComplex.H>
13#include <AMReX_Geometry.H>
14#include <AMReX_Math.H>
15#include <AMReX_NonLocalBC.H>
16#include <AMReX_Periodicity.H>
17
23
24#if defined(AMREX_USE_CUDA)
25# include <cufft.h>
26# include <cuComplex.h>
27#elif defined(AMREX_USE_HIP)
28# if __has_include(<rocfft/rocfft.h>) // ROCm 5.3+
29# include <rocfft/rocfft.h>
30# else
31# include <rocfft.h>
32# endif
33# include <hip/hip_complex.h>
34#elif defined(AMREX_USE_SYCL)
35# if __has_include(<oneapi/mkl/dft.hpp>) // oneAPI 2025.0
36# include <oneapi/mkl/dft.hpp>
37#else
38# define AMREX_USE_MKL_DFTI_2024 1
39# include <oneapi/mkl/dfti.hpp>
40# endif
41#else
42# include <fftw3.h>
43#endif
44
45#include <algorithm>
46#include <complex>
47#include <initializer_list>
48#include <limits>
49#include <memory>
50#include <tuple>
51#include <utility>
52#include <variant>
53
54namespace amrex::FFT
55{
56
57enum struct Direction { forward, backward, both, none };
58
60
63
65
68
77[[nodiscard]] constexpr int
79{
80#if defined(AMREX_USE_CUDA)
81 return 5;
82#else
83 return 6;
84#endif
85}
86
87struct Info
88{
91
95
99 bool twod_mode = false;
100
108 bool oned_mode = false;
109
111 int batch_size = 1;
112
114 int nprocs = std::numeric_limits<int>::max();
115
118
120 bool openbc_padding = true;
121
125
139 Info& setPencilThreshold (int t) { pencil_threshold = t; return *this; }
146 Info& setTwoDMode (bool x) { twod_mode = x; return *this; }
157 Info& setOneDMode (bool x) { oned_mode = x; return *this; }
164 Info& setBatchSize (int bsize) { batch_size = bsize; return *this; }
171 Info& setNumProcs (int n) { nprocs = n; return *this; }
180 Info& setCommStrategy (CommStrategy s) { comm_strategy = s; return *this; }
187 Info& setOpenBCPadding (bool x) { openbc_padding = x; return *this; }
195 {
197 3 <= n && 6 >= n,
198 "amrex::FFT::Info::setOpenBCPaddingNumPrimeFactors: nfactors must be between 3 and 6");
200 return *this;
201 }
202};
203
205namespace detail
206{
207inline Geometry shift_geom (Geometry const& geom)
208{
209 auto const& dom = geom.Domain();
210 auto const& dlo = dom.smallEnd();
211 if (dlo == 0) {
212 return geom;
213 } else {
214 return Geometry(amrex::shift(dom,-dlo), geom.ProbDomain(), geom.CoordInt(),
215 geom.isPeriodic());
216 }
217}
218
219template <typename MF>
220void shift_mf (IntVect const& domain_lo, MF const& mf, MF& new_mf)
221{
222 BoxArray ba = mf.boxArray();
223 ba.shift(-domain_lo);
224 new_mf.define(ba, mf.DistributionMap(), mf.nComp(), mf.nGrowVect(),
225 MFInfo().SetAlloc(false));
226 for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
227 typename MF::fab_type fab(mf[mfi], amrex::make_alias, 0, mf.nComp());
228 fab.shift(-domain_lo);
229 new_mf.setFab(mfi, std::move(fab));
230 }
231}
232
233template <typename MF>
234void shift_mfs (IntVect const& domain_lo, MF const& mf1, MF const& mf2,
235 MF& new_mf1, MF& new_mf2)
236{
237 AMREX_ALWAYS_ASSERT(mf1.boxArray() == mf2.boxArray() &&
238 mf1.DistributionMap() == mf2.DistributionMap());
239 detail::shift_mf(domain_lo, mf1, new_mf1);
240 detail::shift_mf(domain_lo, mf2, new_mf2);
241}
242
243inline void next_fast_len_doit (Long target, Long n, int const* factors,
244 int nfactors, int ifactor, Long& best)
245{
246 if (n >= target) {
247 best = std::min(best, n);
248 return;
249 }
250
251 if (ifactor == nfactors) {
252 // Factors 2 and 3 are handled here: try each n * 3^b, multiply it
253 // by the smallest power of 2 that reaches target, and keep the best.
254 Long const max_m3 = std::numeric_limits<int>::max() / 3;
255 Long const max_m2 = std::numeric_limits<int>::max() / 2;
256 for (Long m3 = n; m3 < best; ) {
257 Long k = m3;
258 bool overflow = false;
259 while (k < target) {
260 if (k > max_m2) {
261 overflow = true;
262 break;
263 }
264 k *= 2;
265 }
266 if (!overflow && k < best) {
267 best = k;
268 }
269 if (m3 > max_m3) {
270 break;
271 }
272 m3 *= 3;
273 }
274 return;
275 }
276
277 int const factor = factors[ifactor];
278 Long const max_len = std::numeric_limits<int>::max();
279 Long const max_m = max_len / factor;
280 for (Long m = n; m < best; ) {
281 next_fast_len_doit(target, m, factors, nfactors, ifactor+1, best);
282 if (m > max_m) {
283 break;
284 }
285 m *= factor;
286 }
287}
288}
290
302[[nodiscard]] inline int
303nextFastLen (int target, int nfactors = FastNumPrimeFactors())
304{
306 "amrex::FFT::nextFastLen: target must be positive");
307 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(3 <= nfactors && 6 >= nfactors,
308 "amrex::FFT::nextFastLen: nfactors must be between 3 and 6");
309
310 int constexpr factors[] = {5, 7, 11, 13, 17}; // The extra '17' is to make the buggy GCC -Warray-bounds quiet.
311
312 Long const max_len = std::numeric_limits<int>::max();
313 Long best = max_len + Long(1);
314
315 // Factors 2 and 3 are handled directly in the base case.
316 detail::next_fast_len_doit(target, Long(1), factors, nfactors-2, 0, best);
317
318 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(best <= max_len,
319 "amrex::FFT::nextFastLen: result exceeds int range");
320 return static_cast<int>(best);
321}
322
323#ifdef AMREX_USE_HIP
325namespace detail { void hip_execute (rocfft_plan plan, void **in, void **out); }
327#endif
328
329#ifdef AMREX_USE_SYCL
331namespace detail
332{
333inline void assert_no_external_stream ()
334{
337 "SYCL FFT does not support external GPU streams.");
338}
339
340template <typename T, Direction direction, typename P, typename TI, typename TO>
341void sycl_execute (P* plan, TI* in, TO* out)
342{
343 assert_no_external_stream();
344#ifndef AMREX_USE_MKL_DFTI_2024
345 std::int64_t workspaceSize = 0;
346#else
347 std::size_t workspaceSize = 0;
348#endif
349 plan->get_value(oneapi::mkl::dft::config_param::WORKSPACE_BYTES,
350 &workspaceSize);
351 auto* buffer = (T*)amrex::The_Arena()->alloc(workspaceSize);
352 plan->set_workspace(buffer);
353 sycl::event r;
354 if (std::is_same_v<TI,TO>) {
356 if constexpr (direction == Direction::forward) {
357 r = oneapi::mkl::dft::compute_forward(*plan, out);
358 } else {
359 r = oneapi::mkl::dft::compute_backward(*plan, out);
360 }
361 } else {
362 if constexpr (direction == Direction::forward) {
363 r = oneapi::mkl::dft::compute_forward(*plan, in, out);
364 } else {
365 r = oneapi::mkl::dft::compute_backward(*plan, in, out);
366 }
367 }
368 r.wait();
369 amrex::The_Arena()->free(buffer);
370}
371}
373#endif
374
375template <typename T>
376struct Plan
377{
378#if defined(AMREX_USE_CUDA)
379 using VendorPlan = cufftHandle;
380 using VendorComplex = std::conditional_t<std::is_same_v<float,T>,
381 cuComplex, cuDoubleComplex>;
382#elif defined(AMREX_USE_HIP)
383 using VendorPlan = rocfft_plan;
384 using VendorComplex = std::conditional_t<std::is_same_v<float,T>,
385 float2, double2>;
386#elif defined(AMREX_USE_SYCL)
387 using mkl_desc_r = oneapi::mkl::dft::descriptor<std::is_same_v<float,T>
388 ? oneapi::mkl::dft::precision::SINGLE
389 : oneapi::mkl::dft::precision::DOUBLE,
390 oneapi::mkl::dft::domain::REAL>;
391 using mkl_desc_c = oneapi::mkl::dft::descriptor<std::is_same_v<float,T>
392 ? oneapi::mkl::dft::precision::SINGLE
393 : oneapi::mkl::dft::precision::DOUBLE,
394 oneapi::mkl::dft::domain::COMPLEX>;
395 using VendorPlan = std::variant<mkl_desc_r*,mkl_desc_c*>;
396 using VendorComplex = std::complex<T>;
397#else
398 using VendorPlan = std::conditional_t<std::is_same_v<float,T>,
399 fftwf_plan, fftw_plan>;
400 using VendorComplex = std::conditional_t<std::is_same_v<float,T>,
401 fftwf_complex, fftw_complex>;
402#endif
403
404 int n = 0;
405 int howmany = 0;
408 bool defined = false;
409 bool defined2 = false;
412 void* pf = nullptr;
413 void* pb = nullptr;
414
415#ifdef AMREX_USE_GPU
422 void set_ptrs (void* p0, void* p1) {
423 pf = p0;
424 pb = p1;
425 }
426#endif
427
431 void destroy ()
432 {
433 if (defined) {
435 defined = false;
436 }
437#if !defined(AMREX_USE_GPU)
438 if (defined2) {
440 defined2 = false;
441 }
442#endif
443 }
444
454 template <Direction D>
455 void init_r2c (Box const& box, T* pr, VendorComplex* pc, bool is_2d_transform = false, int ncomp = 1)
456 {
457 static_assert(D == Direction::forward || D == Direction::backward);
458
459 int rank = is_2d_transform ? 2 : 1;
460
462 defined = true;
463 pf = (void*)pr;
464 pb = (void*)pc;
465
466 int len[2] = {};
467 if (rank == 1) {
468 len[0] = box.length(0);
469 len[1] = box.length(0); // Not used except for HIP. Yes it's `(0)`.
470 } else {
471 len[0] = box.length(1); // Most FFT libraries assume row-major ordering
472 len[1] = box.length(0); // except for rocfft
473 }
474 int nr = (rank == 1) ? len[0] : len[0]*len[1];
475 n = nr;
476 int nc = (rank == 1) ? (len[0]/2+1) : (len[1]/2+1)*len[0];
477#if (AMREX_SPACEDIM == 1)
478 howmany = 1;
479#else
480 howmany = (rank == 1) ? AMREX_D_TERM(1, *box.length(1), *box.length(2))
481 : AMREX_D_TERM(1, *1 , *box.length(2));
482#endif
483 howmany *= ncomp;
484
486
487#if defined(AMREX_USE_CUDA)
488
489 AMREX_CUFFT_SAFE_CALL(cufftCreate(&plan));
490 AMREX_CUFFT_SAFE_CALL(cufftSetAutoAllocation(plan, 0));
491 std::size_t work_size;
492 if constexpr (D == Direction::forward) {
493 cufftType fwd_type = std::is_same_v<float,T> ? CUFFT_R2C : CUFFT_D2Z;
495 (cufftMakePlanMany(plan, rank, len, nullptr, 1, nr, nullptr, 1, nc, fwd_type, howmany, &work_size));
496 } else {
497 cufftType bwd_type = std::is_same_v<float,T> ? CUFFT_C2R : CUFFT_Z2D;
499 (cufftMakePlanMany(plan, rank, len, nullptr, 1, nc, nullptr, 1, nr, bwd_type, howmany, &work_size));
500 }
501
502#elif defined(AMREX_USE_HIP)
503
504 auto prec = std::is_same_v<float,T> ? rocfft_precision_single : rocfft_precision_double;
505 // switch to column-major ordering
506 std::size_t length[2] = {std::size_t(len[1]), std::size_t(len[0])};
507 if constexpr (D == Direction::forward) {
508 AMREX_ROCFFT_SAFE_CALL
509 (rocfft_plan_create(&plan, rocfft_placement_notinplace,
510 rocfft_transform_type_real_forward, prec, rank,
511 length, howmany, nullptr));
512 } else {
513 AMREX_ROCFFT_SAFE_CALL
514 (rocfft_plan_create(&plan, rocfft_placement_notinplace,
515 rocfft_transform_type_real_inverse, prec, rank,
516 length, howmany, nullptr));
517 }
518
519#elif defined(AMREX_USE_SYCL)
520
521 mkl_desc_r* pp;
522 if (rank == 1) {
523 pp = new mkl_desc_r(len[0]);
524 } else {
525 pp = new mkl_desc_r({std::int64_t(len[0]), std::int64_t(len[1])});
526 }
527#ifndef AMREX_USE_MKL_DFTI_2024
528 pp->set_value(oneapi::mkl::dft::config_param::PLACEMENT,
529 oneapi::mkl::dft::config_value::NOT_INPLACE);
530#else
531 pp->set_value(oneapi::mkl::dft::config_param::PLACEMENT, DFTI_NOT_INPLACE);
532#endif
533 pp->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, howmany);
534 pp->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, nr);
535 pp->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, nc);
536 std::vector<std::int64_t> strides;
537 strides.push_back(0);
538 if (rank == 2) { strides.push_back(len[1]); }
539 strides.push_back(1);
540#ifndef AMREX_USE_MKL_DFTI_2024
541 pp->set_value(oneapi::mkl::dft::config_param::FWD_STRIDES, strides);
542 // Do not set BWD_STRIDES
543#else
544 pp->set_value(oneapi::mkl::dft::config_param::FWD_STRIDES, strides.data());
545 // Do not set BWD_STRIDES
546#endif
547 pp->set_value(oneapi::mkl::dft::config_param::WORKSPACE,
548 oneapi::mkl::dft::config_value::WORKSPACE_EXTERNAL);
549 detail::assert_no_external_stream();
550 pp->commit(amrex::Gpu::Device::streamQueue());
551 plan = pp;
552
553#else /* FFTW */
554
555 if constexpr (std::is_same_v<float,T>) {
556 if constexpr (D == Direction::forward) {
557 plan = fftwf_plan_many_dft_r2c
558 (rank, len, howmany, pr, nullptr, 1, nr, pc, nullptr, 1, nc,
559 FFTW_ESTIMATE | FFTW_DESTROY_INPUT);
560 } else {
561 plan = fftwf_plan_many_dft_c2r
562 (rank, len, howmany, pc, nullptr, 1, nc, pr, nullptr, 1, nr,
563 FFTW_ESTIMATE | FFTW_DESTROY_INPUT);
564 }
565 } else {
566 if constexpr (D == Direction::forward) {
567 plan = fftw_plan_many_dft_r2c
568 (rank, len, howmany, pr, nullptr, 1, nr, pc, nullptr, 1, nc,
569 FFTW_ESTIMATE | FFTW_DESTROY_INPUT);
570 } else {
571 plan = fftw_plan_many_dft_c2r
572 (rank, len, howmany, pc, nullptr, 1, nc, pr, nullptr, 1, nr,
573 FFTW_ESTIMATE | FFTW_DESTROY_INPUT);
574 }
575 }
576#endif
577 }
578
588 template <Direction D, int M>
589 void init_r2c (IntVectND<M> const& fft_size, void* pbf, void* pbb, bool cache, int ncomp = 1);
590
606 template <Direction D>
607 void init_c2c (Box const& box, VendorComplex* p, int ncomp = 1, int ndims = 1)
608 {
609 static_assert(D == Direction::forward || D == Direction::backward);
610
612 defined = true;
613 pf = (void*)p;
614 pb = (void*)p;
615
616 int len[3] = {};
617
618 if (ndims == 1) {
619 n = box.length(0);
620 howmany = AMREX_D_TERM(1, *box.length(1), *box.length(2));
621 howmany *= ncomp;
622 len[0] = box.length(0);
623 }
624#if (AMREX_SPACEDIM >= 2)
625 else if (ndims == 2) {
626 n = box.length(0) * box.length(1);
627#if (AMREX_SPACEDIM == 2)
628 howmany = ncomp;
629#else
630 howmany = box.length(2) * ncomp;
631#endif
632 len[0] = box.length(1);
633 len[1] = box.length(0);
634 }
635#if (AMREX_SPACEDIM == 3)
636 else if (ndims == 3) {
637 n = box.length(0) * box.length(1) * box.length(2);
638 howmany = ncomp;
639 len[0] = box.length(2);
640 len[1] = box.length(1);
641 len[2] = box.length(0);
642 }
643#endif
644#endif
645
646#if defined(AMREX_USE_CUDA)
647 AMREX_CUFFT_SAFE_CALL(cufftCreate(&plan));
648 AMREX_CUFFT_SAFE_CALL(cufftSetAutoAllocation(plan, 0));
649
650 cufftType t = std::is_same_v<float,T> ? CUFFT_C2C : CUFFT_Z2Z;
651 std::size_t work_size;
653 (cufftMakePlanMany(plan, ndims, len, nullptr, 1, n, nullptr, 1, n, t, howmany, &work_size));
654
655#elif defined(AMREX_USE_HIP)
656
657 auto prec = std::is_same_v<float,T> ? rocfft_precision_single
658 : rocfft_precision_double;
659 auto dir= (D == Direction::forward) ? rocfft_transform_type_complex_forward
660 : rocfft_transform_type_complex_inverse;
661 std::size_t length[3];
662 if (ndims == 1) {
663 length[0] = len[0];
664 } else if (ndims == 2) {
665 length[0] = len[1];
666 length[1] = len[0];
667 } else {
668 length[0] = len[2];
669 length[1] = len[1];
670 length[2] = len[0];
671 }
672 AMREX_ROCFFT_SAFE_CALL
673 (rocfft_plan_create(&plan, rocfft_placement_inplace, dir, prec, ndims,
674 length, howmany, nullptr));
675
676#elif defined(AMREX_USE_SYCL)
677
678 mkl_desc_c* pp;
679 if (ndims == 1) {
680 pp = new mkl_desc_c(n);
681 } else if (ndims == 2) {
682 pp = new mkl_desc_c({std::int64_t(len[0]), std::int64_t(len[1])});
683 } else {
684 pp = new mkl_desc_c({std::int64_t(len[0]), std::int64_t(len[1]), std::int64_t(len[2])});
685 }
686#ifndef AMREX_USE_MKL_DFTI_2024
687 pp->set_value(oneapi::mkl::dft::config_param::PLACEMENT,
688 oneapi::mkl::dft::config_value::INPLACE);
689#else
690 pp->set_value(oneapi::mkl::dft::config_param::PLACEMENT, DFTI_INPLACE);
691#endif
692 pp->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, howmany);
693 pp->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, n);
694 pp->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, n);
695 std::vector<std::int64_t> strides(ndims+1);
696 strides[0] = 0;
697 strides[ndims] = 1;
698 for (int i = ndims-1; i >= 1; --i) {
699 strides[i] = strides[i+1] * len[i];
700 }
701#ifndef AMREX_USE_MKL_DFTI_2024
702 pp->set_value(oneapi::mkl::dft::config_param::FWD_STRIDES, strides);
703 pp->set_value(oneapi::mkl::dft::config_param::BWD_STRIDES, strides);
704#else
705 pp->set_value(oneapi::mkl::dft::config_param::FWD_STRIDES, strides.data());
706 pp->set_value(oneapi::mkl::dft::config_param::BWD_STRIDES, strides.data());
707#endif
708 pp->set_value(oneapi::mkl::dft::config_param::WORKSPACE,
709 oneapi::mkl::dft::config_value::WORKSPACE_EXTERNAL);
710 detail::assert_no_external_stream();
711 pp->commit(amrex::Gpu::Device::streamQueue());
712 plan = pp;
713
714#else /* FFTW */
715
716 if constexpr (std::is_same_v<float,T>) {
717 if constexpr (D == Direction::forward) {
718 plan = fftwf_plan_many_dft
719 (ndims, len, howmany, p, nullptr, 1, n, p, nullptr, 1, n, -1,
720 FFTW_ESTIMATE);
721 } else {
722 plan = fftwf_plan_many_dft
723 (ndims, len, howmany, p, nullptr, 1, n, p, nullptr, 1, n, +1,
724 FFTW_ESTIMATE);
725 }
726 } else {
727 if constexpr (D == Direction::forward) {
728 plan = fftw_plan_many_dft
729 (ndims, len, howmany, p, nullptr, 1, n, p, nullptr, 1, n, -1,
730 FFTW_ESTIMATE);
731 } else {
732 plan = fftw_plan_many_dft
733 (ndims, len, howmany, p, nullptr, 1, n, p, nullptr, 1, n, +1,
734 FFTW_ESTIMATE);
735 }
736 }
737#endif
738 }
739
740#ifndef AMREX_USE_GPU
741 template <Direction D>
742 fftw_r2r_kind get_fftw_kind (std::pair<Boundary,Boundary> const& bc)
743 {
744 if (bc.first == Boundary::even && bc.second == Boundary::even)
745 {
746 return (D == Direction::forward) ? FFTW_REDFT10 : FFTW_REDFT01;
747 }
748 else if (bc.first == Boundary::even && bc.second == Boundary::odd)
749 {
750 return FFTW_REDFT11;
751 }
752 else if (bc.first == Boundary::odd && bc.second == Boundary::even)
753 {
754 return FFTW_RODFT11;
755 }
756 else if (bc.first == Boundary::odd && bc.second == Boundary::odd)
757 {
758 return (D == Direction::forward) ? FFTW_RODFT10 : FFTW_RODFT01;
759 }
760 else {
761 amrex::Abort("FFT: unsupported BC");
762 return fftw_r2r_kind{};
763 }
764
765 }
766#endif
767
774 template <Direction D>
775 Kind get_r2r_kind (std::pair<Boundary,Boundary> const& bc)
776 {
777 if (bc.first == Boundary::even && bc.second == Boundary::even)
778 {
780 }
781 else if (bc.first == Boundary::even && bc.second == Boundary::odd)
782 {
783 return Kind::r2r_eo;
784 }
785 else if (bc.first == Boundary::odd && bc.second == Boundary::even)
786 {
787 return Kind::r2r_oe;
788 }
789 else if (bc.first == Boundary::odd && bc.second == Boundary::odd)
790 {
792 }
793 else {
794 amrex::Abort("FFT: unsupported BC");
795 return Kind::none;
796 }
797
798 }
799
808 template <Direction D>
809 void init_r2r (Box const& box, T* p, std::pair<Boundary,Boundary> const& bc,
810 int howmany_initval = 1)
811 {
812 static_assert(D == Direction::forward || D == Direction::backward);
813
814 kind = get_r2r_kind<D>(bc);
815 defined = true;
816 pf = (void*)p;
817 pb = (void*)p;
818
819 n = box.length(0);
820 howmany = AMREX_D_TERM(howmany_initval, *box.length(1), *box.length(2));
821
822#if defined(AMREX_USE_GPU)
823 int nex=0;
824 if (bc.first == Boundary::odd && bc.second == Boundary::odd &&
825 Direction::forward == D) {
826 nex = 2*n;
827 } else if (bc.first == Boundary::odd && bc.second == Boundary::odd &&
828 Direction::backward == D) {
829 nex = 4*n;
830 } else if (bc.first == Boundary::even && bc.second == Boundary::even &&
831 Direction::forward == D) {
832 nex = 2*n;
833 } else if (bc.first == Boundary::even && bc.second == Boundary::even &&
834 Direction::backward == D) {
835 nex = 4*n;
836 } else if ((bc.first == Boundary::even && bc.second == Boundary::odd) ||
837 (bc.first == Boundary::odd && bc.second == Boundary::even)) {
838 nex = 4*n;
839 } else {
840 amrex::Abort("FFT: unsupported BC");
841 }
842 int nc = (nex/2) + 1;
843
844#if defined (AMREX_USE_CUDA)
845
846 AMREX_CUFFT_SAFE_CALL(cufftCreate(&plan));
847 AMREX_CUFFT_SAFE_CALL(cufftSetAutoAllocation(plan, 0));
848 cufftType fwd_type = std::is_same_v<float,T> ? CUFFT_R2C : CUFFT_D2Z;
849 std::size_t work_size;
851 (cufftMakePlanMany(plan, 1, &nex, nullptr, 1, nc*2, nullptr, 1, nc, fwd_type, howmany, &work_size));
852
853#elif defined(AMREX_USE_HIP)
854
856 auto prec = std::is_same_v<float,T> ? rocfft_precision_single : rocfft_precision_double;
857 const std::size_t length = nex;
858 AMREX_ROCFFT_SAFE_CALL
859 (rocfft_plan_create(&plan, rocfft_placement_inplace,
860 rocfft_transform_type_real_forward, prec, 1,
861 &length, howmany, nullptr));
862
863#elif defined(AMREX_USE_SYCL)
864
865 auto* pp = new mkl_desc_r(nex);
866#ifndef AMREX_USE_MKL_DFTI_2024
867 pp->set_value(oneapi::mkl::dft::config_param::PLACEMENT,
868 oneapi::mkl::dft::config_value::INPLACE);
869#else
870 pp->set_value(oneapi::mkl::dft::config_param::PLACEMENT, DFTI_INPLACE);
871#endif
872 pp->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, howmany);
873 pp->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, nc*2);
874 pp->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, nc);
875 std::vector<std::int64_t> strides = {0,1};
876#ifndef AMREX_USE_MKL_DFTI_2024
877 pp->set_value(oneapi::mkl::dft::config_param::FWD_STRIDES, strides);
878 pp->set_value(oneapi::mkl::dft::config_param::BWD_STRIDES, strides);
879#else
880 pp->set_value(oneapi::mkl::dft::config_param::FWD_STRIDES, strides.data());
881 pp->set_value(oneapi::mkl::dft::config_param::BWD_STRIDES, strides.data());
882#endif
883 pp->set_value(oneapi::mkl::dft::config_param::WORKSPACE,
884 oneapi::mkl::dft::config_value::WORKSPACE_EXTERNAL);
885 detail::assert_no_external_stream();
886 pp->commit(amrex::Gpu::Device::streamQueue());
887 plan = pp;
888
889#endif
890
891#else /* FFTW */
892 auto fftw_kind = get_fftw_kind<D>(bc);
893 if constexpr (std::is_same_v<float,T>) {
894 plan = fftwf_plan_many_r2r
895 (1, &n, howmany, p, nullptr, 1, n, p, nullptr, 1, n, &fftw_kind,
896 FFTW_ESTIMATE);
897 } else {
898 plan = fftw_plan_many_r2r
899 (1, &n, howmany, p, nullptr, 1, n, p, nullptr, 1, n, &fftw_kind,
900 FFTW_ESTIMATE);
901 }
902#endif
903 }
904
912 template <Direction D>
913 void init_r2r (Box const& box, VendorComplex* pc,
914 std::pair<Boundary,Boundary> const& bc)
915 {
916 static_assert(D == Direction::forward || D == Direction::backward);
917
918 auto* p = (T*)pc;
919
920#if defined(AMREX_USE_GPU)
921
922 init_r2r<D>(box, p, bc, 2);
923 r2r_data_is_complex = true;
924
925#else
926
927 kind = get_r2r_kind<D>(bc);
928 defined = true;
929 pf = (void*)p;
930 pb = (void*)p;
931
932 n = box.length(0);
933 howmany = AMREX_D_TERM(1, *box.length(1), *box.length(2));
934
935 defined2 = true;
936 auto fftw_kind = get_fftw_kind<D>(bc);
937 if constexpr (std::is_same_v<float,T>) {
938 plan = fftwf_plan_many_r2r
939 (1, &n, howmany, p, nullptr, 2, n*2, p, nullptr, 2, n*2, &fftw_kind,
940 FFTW_ESTIMATE);
941 plan2 = fftwf_plan_many_r2r
942 (1, &n, howmany, p+1, nullptr, 2, n*2, p+1, nullptr, 2, n*2, &fftw_kind,
943 FFTW_ESTIMATE);
944 } else {
945 plan = fftw_plan_many_r2r
946 (1, &n, howmany, p, nullptr, 2, n*2, p, nullptr, 2, n*2, &fftw_kind,
947 FFTW_ESTIMATE);
948 plan2 = fftw_plan_many_r2r
949 (1, &n, howmany, p+1, nullptr, 2, n*2, p+1, nullptr, 2, n*2, &fftw_kind,
950 FFTW_ESTIMATE);
951 }
952#endif
953 }
954
960 template <Direction D>
962 {
963 static_assert(D == Direction::forward || D == Direction::backward);
964 if (!defined) { return; }
965
966 using TI = std::conditional_t<(D == Direction::forward), T, VendorComplex>;
967 using TO = std::conditional_t<(D == Direction::backward), T, VendorComplex>;
968 auto* pi = (TI*)((D == Direction::forward) ? pf : pb);
969 auto* po = (TO*)((D == Direction::forward) ? pb : pf);
970
971#if defined(AMREX_USE_CUDA)
972 AMREX_CUFFT_SAFE_CALL(cufftSetStream(plan, Gpu::gpuStream()));
973
974 std::size_t work_size = 0;
975 AMREX_CUFFT_SAFE_CALL(cufftGetSize(plan, &work_size));
976
977 auto* work_area = The_Arena()->alloc(work_size);
978 AMREX_CUFFT_SAFE_CALL(cufftSetWorkArea(plan, work_area));
979
980 if constexpr (D == Direction::forward) {
981 if constexpr (std::is_same_v<float,T>) {
982 AMREX_CUFFT_SAFE_CALL(cufftExecR2C(plan, pi, po));
983 } else {
984 AMREX_CUFFT_SAFE_CALL(cufftExecD2Z(plan, pi, po));
985 }
986 } else {
987 if constexpr (std::is_same_v<float,T>) {
988 AMREX_CUFFT_SAFE_CALL(cufftExecC2R(plan, pi, po));
989 } else {
990 AMREX_CUFFT_SAFE_CALL(cufftExecZ2D(plan, pi, po));
991 }
992 }
994 The_Arena()->free(work_area);
995#elif defined(AMREX_USE_HIP)
996 detail::hip_execute(plan, (void**)&pi, (void**)&po);
997#elif defined(AMREX_USE_SYCL)
998 detail::sycl_execute<T,D>(std::get<0>(plan), pi, po);
999#else
1000 amrex::ignore_unused(pi,po);
1001 if constexpr (std::is_same_v<float,T>) {
1002 fftwf_execute(plan);
1003 } else {
1004 fftw_execute(plan);
1005 }
1006#endif
1007 }
1008
1012 template <Direction D>
1014 {
1015 static_assert(D == Direction::forward || D == Direction::backward);
1016 if (!defined) { return; }
1017
1018 auto* p = (VendorComplex*)pf;
1019
1020#if defined(AMREX_USE_CUDA)
1021 AMREX_CUFFT_SAFE_CALL(cufftSetStream(plan, Gpu::gpuStream()));
1022
1023 std::size_t work_size = 0;
1024 AMREX_CUFFT_SAFE_CALL(cufftGetSize(plan, &work_size));
1025
1026 auto* work_area = The_Arena()->alloc(work_size);
1027 AMREX_CUFFT_SAFE_CALL(cufftSetWorkArea(plan, work_area));
1028
1029 auto dir = (D == Direction::forward) ? CUFFT_FORWARD : CUFFT_INVERSE;
1030 if constexpr (std::is_same_v<float,T>) {
1031 AMREX_CUFFT_SAFE_CALL(cufftExecC2C(plan, p, p, dir));
1032 } else {
1033 AMREX_CUFFT_SAFE_CALL(cufftExecZ2Z(plan, p, p, dir));
1034 }
1036 The_Arena()->free(work_area);
1037#elif defined(AMREX_USE_HIP)
1038 detail::hip_execute(plan, (void**)&p, (void**)&p);
1039#elif defined(AMREX_USE_SYCL)
1040 detail::sycl_execute<T,D>(std::get<1>(plan), p, p);
1041#else
1043 if constexpr (std::is_same_v<float,T>) {
1044 fftwf_execute(plan);
1045 } else {
1046 fftw_execute(plan);
1047 }
1048#endif
1049 }
1050
1051#ifdef AMREX_USE_GPU
1057 [[nodiscard]] void* alloc_scratch_space () const
1058 {
1059 int nc = 0;
1060 if (kind == Kind::r2r_oo_f || kind == Kind::r2r_ee_f) {
1061 nc = n + 1;
1062 } else if (kind == Kind::r2r_oo_b || kind == Kind::r2r_ee_b ||
1064 nc = 2*n+1;
1065 } else {
1066 amrex::Abort("FFT: alloc_scratch_space: unsupported kind");
1067 }
1068 return The_Arena()->alloc(sizeof(GpuComplex<T>)*nc*howmany);
1069 }
1070
1074 static void free_scratch_space (void* p) { The_Arena()->free(p); }
1075
1082 void pack_r2r_buffer (void* pbuf, T const* psrc) const
1083 {
1084 auto* pdst = (T*) pbuf;
1085 if (kind == Kind::r2r_oo_f || kind == Kind::r2r_ee_f) {
1086 T sign = (kind == Kind::r2r_oo_f) ? T(-1) : T(1);
1087 int ostride = (n+1)*2;
1088 int istride = n;
1089 int nex = 2*n;
1090 int norig = n;
1091 Long nelems = Long(nex)*howmany;
1092 if (r2r_data_is_complex) {
1093 ParallelForOMP(nelems/2, [=] AMREX_GPU_DEVICE (Long ielem)
1094 {
1095 auto batch = ielem / Long(nex);
1096 auto i = int(ielem - batch*nex);
1097 for (int ir = 0; ir < 2; ++ir) {
1098 auto* po = pdst + (2*batch+ir)*ostride + i;
1099 auto const* pi = psrc + 2*batch*istride + ir;
1100 if (i < norig) {
1101 *po = pi[i*2];
1102 } else {
1103 *po = sign * pi[(2*norig-1-i)*2];
1104 }
1105 }
1106 });
1107 } else {
1108 ParallelForOMP(nelems, [=] AMREX_GPU_DEVICE (Long ielem)
1109 {
1110 auto batch = ielem / Long(nex);
1111 auto i = int(ielem - batch*nex);
1112 auto* po = pdst + batch*ostride + i;
1113 auto const* pi = psrc + batch*istride;
1114 if (i < norig) {
1115 *po = pi[i];
1116 } else {
1117 *po = sign * pi[2*norig-1-i];
1118 }
1119 });
1120 }
1121 } else if (kind == Kind::r2r_oo_b) {
1122 int ostride = (2*n+1)*2;
1123 int istride = n;
1124 int nex = 4*n;
1125 int norig = n;
1126 Long nelems = Long(nex)*howmany;
1127 if (r2r_data_is_complex) {
1128 ParallelForOMP(nelems/2, [=] AMREX_GPU_DEVICE (Long ielem)
1129 {
1130 auto batch = ielem / Long(nex);
1131 auto i = int(ielem - batch*nex);
1132 for (int ir = 0; ir < 2; ++ir) {
1133 auto* po = pdst + (2*batch+ir)*ostride + i;
1134 auto const* pi = psrc + 2*batch*istride + ir;
1135 if (i < norig) {
1136 *po = pi[i*2];
1137 } else if (i < (2*norig-1)) {
1138 *po = pi[(2*norig-2-i)*2];
1139 } else if (i == (2*norig-1)) {
1140 *po = T(0);
1141 } else if (i < (3*norig)) {
1142 *po = -pi[(i-2*norig)*2];
1143 } else if (i < (4*norig-1)) {
1144 *po = -pi[(4*norig-2-i)*2];
1145 } else {
1146 *po = T(0);
1147 }
1148 }
1149 });
1150 } else {
1151 ParallelForOMP(nelems, [=] AMREX_GPU_DEVICE (Long ielem)
1152 {
1153 auto batch = ielem / Long(nex);
1154 auto i = int(ielem - batch*nex);
1155 auto* po = pdst + batch*ostride + i;
1156 auto const* pi = psrc + batch*istride;
1157 if (i < norig) {
1158 *po = pi[i];
1159 } else if (i < (2*norig-1)) {
1160 *po = pi[2*norig-2-i];
1161 } else if (i == (2*norig-1)) {
1162 *po = T(0);
1163 } else if (i < (3*norig)) {
1164 *po = -pi[i-2*norig];
1165 } else if (i < (4*norig-1)) {
1166 *po = -pi[4*norig-2-i];
1167 } else {
1168 *po = T(0);
1169 }
1170 });
1171 }
1172 } else if (kind == Kind::r2r_ee_b) {
1173 int ostride = (2*n+1)*2;
1174 int istride = n;
1175 int nex = 4*n;
1176 int norig = n;
1177 Long nelems = Long(nex)*howmany;
1178 if (r2r_data_is_complex) {
1179 ParallelForOMP(nelems/2, [=] AMREX_GPU_DEVICE (Long ielem)
1180 {
1181 auto batch = ielem / Long(nex);
1182 auto i = int(ielem - batch*nex);
1183 for (int ir = 0; ir < 2; ++ir) {
1184 auto* po = pdst + (2*batch+ir)*ostride + i;
1185 auto const* pi = psrc + 2*batch*istride + ir;
1186 if (i < norig) {
1187 *po = pi[i*2];
1188 } else if (i == norig) {
1189 *po = T(0);
1190 } else if (i < (2*norig+1)) {
1191 *po = -pi[(2*norig-i)*2];
1192 } else if (i < (3*norig)) {
1193 *po = -pi[(i-2*norig)*2];
1194 } else if (i == 3*norig) {
1195 *po = T(0);
1196 } else {
1197 *po = pi[(4*norig-i)*2];
1198 }
1199 }
1200 });
1201 } else {
1202 ParallelForOMP(nelems, [=] AMREX_GPU_DEVICE (Long ielem)
1203 {
1204 auto batch = ielem / Long(nex);
1205 auto i = int(ielem - batch*nex);
1206 auto* po = pdst + batch*ostride + i;
1207 auto const* pi = psrc + batch*istride;
1208 if (i < norig) {
1209 *po = pi[i];
1210 } else if (i == norig) {
1211 *po = T(0);
1212 } else if (i < (2*norig+1)) {
1213 *po = -pi[2*norig-i];
1214 } else if (i < (3*norig)) {
1215 *po = -pi[i-2*norig];
1216 } else if (i == 3*norig) {
1217 *po = T(0);
1218 } else {
1219 *po = pi[4*norig-i];
1220 }
1221 });
1222 }
1223 } else if (kind == Kind::r2r_eo) {
1224 int ostride = (2*n+1)*2;
1225 int istride = n;
1226 int nex = 4*n;
1227 int norig = n;
1228 Long nelems = Long(nex)*howmany;
1229 if (r2r_data_is_complex) {
1230 ParallelForOMP(nelems/2, [=] AMREX_GPU_DEVICE (Long ielem)
1231 {
1232 auto batch = ielem / Long(nex);
1233 auto i = int(ielem - batch*nex);
1234 for (int ir = 0; ir < 2; ++ir) {
1235 auto* po = pdst + (2*batch+ir)*ostride + i;
1236 auto const* pi = psrc + 2*batch*istride + ir;
1237 if (i < norig) {
1238 *po = pi[i*2];
1239 } else if (i < (2*norig)) {
1240 *po = -pi[(2*norig-1-i)*2];
1241 } else if (i < (3*norig)) {
1242 *po = -pi[(i-2*norig)*2];
1243 } else {
1244 *po = pi[(4*norig-1-i)*2];
1245 }
1246 }
1247 });
1248 } else {
1249 ParallelForOMP(nelems, [=] AMREX_GPU_DEVICE (Long ielem)
1250 {
1251 auto batch = ielem / Long(nex);
1252 auto i = int(ielem - batch*nex);
1253 auto* po = pdst + batch*ostride + i;
1254 auto const* pi = psrc + batch*istride;
1255 if (i < norig) {
1256 *po = pi[i];
1257 } else if (i < (2*norig)) {
1258 *po = -pi[2*norig-1-i];
1259 } else if (i < (3*norig)) {
1260 *po = -pi[i-2*norig];
1261 } else {
1262 *po = pi[4*norig-1-i];
1263 }
1264 });
1265 }
1266 } else if (kind == Kind::r2r_oe) {
1267 int ostride = (2*n+1)*2;
1268 int istride = n;
1269 int nex = 4*n;
1270 int norig = n;
1271 Long nelems = Long(nex)*howmany;
1272 if (r2r_data_is_complex) {
1273 ParallelForOMP(nelems/2, [=] AMREX_GPU_DEVICE (Long ielem)
1274 {
1275 auto batch = ielem / Long(nex);
1276 auto i = int(ielem - batch*nex);
1277 for (int ir = 0; ir < 2; ++ir) {
1278 auto* po = pdst + (2*batch+ir)*ostride + i;
1279 auto const* pi = psrc + 2*batch*istride + ir;
1280 if (i < norig) {
1281 *po = pi[i*2];
1282 } else if (i < (2*norig)) {
1283 *po = pi[(2*norig-1-i)*2];
1284 } else if (i < (3*norig)) {
1285 *po = -pi[(i-2*norig)*2];
1286 } else {
1287 *po = -pi[(4*norig-1-i)*2];
1288 }
1289 }
1290 });
1291 } else {
1292 ParallelForOMP(nelems, [=] AMREX_GPU_DEVICE (Long ielem)
1293 {
1294 auto batch = ielem / Long(nex);
1295 auto i = int(ielem - batch*nex);
1296 auto* po = pdst + batch*ostride + i;
1297 auto const* pi = psrc + batch*istride;
1298 if (i < norig) {
1299 *po = pi[i];
1300 } else if (i < (2*norig)) {
1301 *po = pi[2*norig-1-i];
1302 } else if (i < (3*norig)) {
1303 *po = -pi[i-2*norig];
1304 } else {
1305 *po = -pi[4*norig-1-i];
1306 }
1307 });
1308 }
1309 } else {
1310 amrex::Abort("FFT: pack_r2r_buffer: unsupported kind");
1311 }
1312 }
1313
1320 void unpack_r2r_buffer (T* pdst, void const* pbuf) const
1321 {
1322 auto const* psrc = (GpuComplex<T> const*) pbuf;
1323 int norig = n;
1324 Long nelems = Long(norig)*howmany;
1325 int ostride = n;
1326
1327 if (kind == Kind::r2r_oo_f) {
1328 int istride = n+1;
1329 if (r2r_data_is_complex) {
1330 ParallelForOMP(nelems/2, [=] AMREX_GPU_DEVICE (Long ielem)
1331 {
1332 auto batch = ielem / Long(norig);
1333 auto k = int(ielem - batch*norig);
1334 auto [s, c] = Math::sincospi(T(k+1)/T(2*norig));
1335 for (int ir = 0; ir < 2; ++ir) {
1336 auto const& yk = psrc[(2*batch+ir)*istride+k+1];
1337 pdst[2*batch*ostride+ir+k*2] = s * yk.real() - c * yk.imag();
1338 }
1339 });
1340 } else {
1341 ParallelForOMP(nelems, [=] AMREX_GPU_DEVICE (Long ielem)
1342 {
1343 auto batch = ielem / Long(norig);
1344 auto k = int(ielem - batch*norig);
1345 auto [s, c] = Math::sincospi(T(k+1)/T(2*norig));
1346 auto const& yk = psrc[batch*istride+k+1];
1347 pdst[batch*ostride+k] = s * yk.real() - c * yk.imag();
1348 });
1349 }
1350 } else if (kind == Kind::r2r_oo_b) {
1351 int istride = 2*n+1;
1352 if (r2r_data_is_complex) {
1353 ParallelForOMP(nelems/2, [=] AMREX_GPU_DEVICE (Long ielem)
1354 {
1355 auto batch = ielem / Long(norig);
1356 auto k = int(ielem - batch*norig);
1357 auto [s, c] = Math::sincospi(T(2*k+1)/T(2*norig));
1358 for (int ir = 0; ir < 2; ++ir) {
1359 auto const& yk = psrc[(2*batch+ir)*istride+2*k+1];
1360 pdst[2*batch*ostride+ir+k*2] = T(0.5)*(s * yk.real() - c * yk.imag());
1361 }
1362 });
1363 } else {
1364 ParallelForOMP(nelems, [=] AMREX_GPU_DEVICE (Long ielem)
1365 {
1366 auto batch = ielem / Long(norig);
1367 auto k = int(ielem - batch*norig);
1368 auto [s, c] = Math::sincospi(T(2*k+1)/T(2*norig));
1369 auto const& yk = psrc[batch*istride+2*k+1];
1370 pdst[batch*ostride+k] = T(0.5)*(s * yk.real() - c * yk.imag());
1371 });
1372 }
1373 } else if (kind == Kind::r2r_ee_f) {
1374 int istride = n+1;
1375 if (r2r_data_is_complex) {
1376 ParallelForOMP(nelems/2, [=] AMREX_GPU_DEVICE (Long ielem)
1377 {
1378 auto batch = ielem / Long(norig);
1379 auto k = int(ielem - batch*norig);
1380 auto [s, c] = Math::sincospi(T(k)/T(2*norig));
1381 for (int ir = 0; ir < 2; ++ir) {
1382 auto const& yk = psrc[(2*batch+ir)*istride+k];
1383 pdst[2*batch*ostride+ir+k*2] = c * yk.real() + s * yk.imag();
1384 }
1385 });
1386 } else {
1387 ParallelForOMP(nelems, [=] AMREX_GPU_DEVICE (Long ielem)
1388 {
1389 auto batch = ielem / Long(norig);
1390 auto k = int(ielem - batch*norig);
1391 auto [s, c] = Math::sincospi(T(k)/T(2*norig));
1392 auto const& yk = psrc[batch*istride+k];
1393 pdst[batch*ostride+k] = c * yk.real() + s * yk.imag();
1394 });
1395 }
1396 } else if (kind == Kind::r2r_ee_b) {
1397 int istride = 2*n+1;
1398 if (r2r_data_is_complex) {
1399 ParallelForOMP(nelems/2, [=] AMREX_GPU_DEVICE (Long ielem)
1400 {
1401 auto batch = ielem / Long(norig);
1402 auto k = int(ielem - batch*norig);
1403 for (int ir = 0; ir < 2; ++ir) {
1404 auto const& yk = psrc[(2*batch+ir)*istride+2*k+1];
1405 pdst[2*batch*ostride+ir+k*2] = T(0.5) * yk.real();
1406 }
1407 });
1408 } else {
1409 ParallelForOMP(nelems, [=] AMREX_GPU_DEVICE (Long ielem)
1410 {
1411 auto batch = ielem / Long(norig);
1412 auto k = int(ielem - batch*norig);
1413 auto const& yk = psrc[batch*istride+2*k+1];
1414 pdst[batch*ostride+k] = T(0.5) * yk.real();
1415 });
1416 }
1417 } else if (kind == Kind::r2r_eo) {
1418 int istride = 2*n+1;
1419 if (r2r_data_is_complex) {
1420 ParallelForOMP(nelems/2, [=] AMREX_GPU_DEVICE (Long ielem)
1421 {
1422 auto batch = ielem / Long(norig);
1423 auto k = int(ielem - batch*norig);
1424 auto [s, c] = Math::sincospi((k+T(0.5))/T(2*norig));
1425 for (int ir = 0; ir < 2; ++ir) {
1426 auto const& yk = psrc[(2*batch+ir)*istride+2*k+1];
1427 pdst[2*batch*ostride+ir+k*2] = T(0.5) * (c * yk.real() + s * yk.imag());
1428 }
1429 });
1430 } else {
1431 ParallelForOMP(nelems, [=] AMREX_GPU_DEVICE (Long ielem)
1432 {
1433 auto batch = ielem / Long(norig);
1434 auto k = int(ielem - batch*norig);
1435 auto [s, c] = Math::sincospi((k+T(0.5))/T(2*norig));
1436 auto const& yk = psrc[batch*istride+2*k+1];
1437 pdst[batch*ostride+k] = T(0.5) * (c * yk.real() + s * yk.imag());
1438 });
1439 }
1440 } else if (kind == Kind::r2r_oe) {
1441 int istride = 2*n+1;
1442 if (r2r_data_is_complex) {
1443 ParallelForOMP(nelems/2, [=] AMREX_GPU_DEVICE (Long ielem)
1444 {
1445 auto batch = ielem / Long(norig);
1446 auto k = int(ielem - batch*norig);
1447 auto [s, c] = Math::sincospi((k+T(0.5))/T(2*norig));
1448 for (int ir = 0; ir < 2; ++ir) {
1449 auto const& yk = psrc[(2*batch+ir)*istride+2*k+1];
1450 pdst[2*batch*ostride+ir+k*2] = T(0.5) * (s * yk.real() - c * yk.imag());
1451 }
1452 });
1453 } else {
1454 ParallelForOMP(nelems, [=] AMREX_GPU_DEVICE (Long ielem)
1455 {
1456 auto batch = ielem / Long(norig);
1457 auto k = int(ielem - batch*norig);
1458 auto [s, c] = Math::sincospi((k+T(0.5))/T(2*norig));
1459 auto const& yk = psrc[batch*istride+2*k+1];
1460 pdst[batch*ostride+k] = T(0.5) * (s * yk.real() - c * yk.imag());
1461 });
1462 }
1463 } else {
1464 amrex::Abort("FFT: unpack_r2r_buffer: unsupported kind");
1465 }
1466 }
1467#endif
1468
1472 template <Direction D>
1474 {
1475 static_assert(D == Direction::forward || D == Direction::backward);
1476 if (!defined) { return; }
1477
1478#if defined(AMREX_USE_GPU)
1479
1480 auto* pscratch = alloc_scratch_space();
1481
1482 pack_r2r_buffer(pscratch, (T*)((D == Direction::forward) ? pf : pb));
1483
1484#if defined(AMREX_USE_CUDA)
1485
1486 AMREX_CUFFT_SAFE_CALL(cufftSetStream(plan, Gpu::gpuStream()));
1487
1488 std::size_t work_size = 0;
1489 AMREX_CUFFT_SAFE_CALL(cufftGetSize(plan, &work_size));
1490
1491 auto* work_area = The_Arena()->alloc(work_size);
1492 AMREX_CUFFT_SAFE_CALL(cufftSetWorkArea(plan, work_area));
1493
1494 if constexpr (std::is_same_v<float,T>) {
1495 AMREX_CUFFT_SAFE_CALL(cufftExecR2C(plan, (T*)pscratch, (VendorComplex*)pscratch));
1496 } else {
1497 AMREX_CUFFT_SAFE_CALL(cufftExecD2Z(plan, (T*)pscratch, (VendorComplex*)pscratch));
1498 }
1499
1500#elif defined(AMREX_USE_HIP)
1501 detail::hip_execute(plan, (void**)&pscratch, (void**)&pscratch);
1502#elif defined(AMREX_USE_SYCL)
1503 detail::sycl_execute<T,Direction::forward>(std::get<0>(plan), (T*)pscratch, (VendorComplex*)pscratch);
1504#endif
1505
1506 unpack_r2r_buffer((T*)((D == Direction::forward) ? pb : pf), pscratch);
1507
1509 free_scratch_space(pscratch);
1510#if defined(AMREX_USE_CUDA)
1511 The_Arena()->free(work_area);
1512#endif
1513
1514#else /* FFTW */
1515
1516 if constexpr (std::is_same_v<float,T>) {
1517 fftwf_execute(plan);
1518 if (defined2) { fftwf_execute(plan2); }
1519 } else {
1520 fftw_execute(plan);
1521 if (defined2) { fftw_execute(plan2); }
1522 }
1523
1524#endif
1525 }
1526
1531 {
1532#if defined(AMREX_USE_CUDA)
1533 AMREX_CUFFT_SAFE_CALL(cufftDestroy(plan));
1534#elif defined(AMREX_USE_HIP)
1535 AMREX_ROCFFT_SAFE_CALL(rocfft_plan_destroy(plan));
1536#elif defined(AMREX_USE_SYCL)
1537 std::visit([](auto&& p) { delete p; }, plan);
1538#else
1539 if constexpr (std::is_same_v<float,T>) {
1540 fftwf_destroy_plan(plan);
1541 } else {
1542 fftw_destroy_plan(plan);
1543 }
1544#endif
1545 }
1546};
1547
1549
1550using Key = std::tuple<IntVectND<3>,int,Direction,Kind>;
1551using PlanD = typename Plan<double>::VendorPlan;
1552using PlanF = typename Plan<float>::VendorPlan;
1553
1554namespace detail {
1555 PlanD* get_vendor_plan_d (Key const& key);
1556 PlanF* get_vendor_plan_f (Key const& key);
1557
1558 void add_vendor_plan_d (Key const& key, PlanD plan);
1559 void add_vendor_plan_f (Key const& key, PlanF plan);
1560}
1562
1563template <typename T>
1564template <Direction D, int M>
1565void Plan<T>::init_r2c (IntVectND<M> const& fft_size, void* pbf, void* pbb, bool cache, int ncomp)
1566{
1567 static_assert(D == Direction::forward || D == Direction::backward);
1568
1570 defined = true;
1571 pf = pbf;
1572 pb = pbb;
1573
1574 n = 1;
1575 for (auto s : fft_size) { n *= s; }
1576 howmany = ncomp;
1577
1578#if defined(AMREX_USE_GPU)
1579 Key key = {fft_size.template expand<3>(), ncomp, D, kind};
1580 if (cache) {
1581 VendorPlan* cached_plan = nullptr;
1582 if constexpr (std::is_same_v<float,T>) {
1583 cached_plan = detail::get_vendor_plan_f(key);
1584 } else {
1585 cached_plan = detail::get_vendor_plan_d(key);
1586 }
1587 if (cached_plan) {
1588 plan = *cached_plan;
1589 return;
1590 }
1591 }
1592#else
1593 amrex::ignore_unused(cache);
1594#endif
1595
1596 int len[M];
1597 for (int i = 0; i < M; ++i) {
1598 len[i] = fft_size[M-1-i];
1599 }
1600
1601 int nc = fft_size[0]/2+1;
1602 for (int i = 1; i < M; ++i) {
1603 nc *= fft_size[i];
1604 }
1605
1606#if defined(AMREX_USE_CUDA)
1607
1608 AMREX_CUFFT_SAFE_CALL(cufftCreate(&plan));
1609 AMREX_CUFFT_SAFE_CALL(cufftSetAutoAllocation(plan, 0));
1610 cufftType type;
1611 int n_in, n_out;
1612 if constexpr (D == Direction::forward) {
1613 type = std::is_same_v<float,T> ? CUFFT_R2C : CUFFT_D2Z;
1614 n_in = n;
1615 n_out = nc;
1616 } else {
1617 type = std::is_same_v<float,T> ? CUFFT_C2R : CUFFT_Z2D;
1618 n_in = nc;
1619 n_out = n;
1620 }
1621 std::size_t work_size;
1623 (cufftMakePlanMany(plan, M, len, nullptr, 1, n_in, nullptr, 1, n_out, type, howmany, &work_size));
1624
1625#elif defined(AMREX_USE_HIP)
1626
1627 auto prec = std::is_same_v<float,T> ? rocfft_precision_single : rocfft_precision_double;
1628 std::size_t length[M];
1629 for (int idim = 0; idim < M; ++idim) { length[idim] = fft_size[idim]; }
1630 if constexpr (D == Direction::forward) {
1631 AMREX_ROCFFT_SAFE_CALL
1632 (rocfft_plan_create(&plan, rocfft_placement_notinplace,
1633 rocfft_transform_type_real_forward, prec, M,
1634 length, howmany, nullptr));
1635 } else {
1636 AMREX_ROCFFT_SAFE_CALL
1637 (rocfft_plan_create(&plan, rocfft_placement_notinplace,
1638 rocfft_transform_type_real_inverse, prec, M,
1639 length, howmany, nullptr));
1640 }
1641
1642#elif defined(AMREX_USE_SYCL)
1643
1644 mkl_desc_r* pp;
1645 if (M == 1) {
1646 pp = new mkl_desc_r(fft_size[0]);
1647 } else {
1648 std::vector<std::int64_t> len64(M);
1649 for (int idim = 0; idim < M; ++idim) {
1650 len64[idim] = len[idim];
1651 }
1652 pp = new mkl_desc_r(len64);
1653 }
1654#ifndef AMREX_USE_MKL_DFTI_2024
1655 pp->set_value(oneapi::mkl::dft::config_param::PLACEMENT,
1656 oneapi::mkl::dft::config_value::NOT_INPLACE);
1657#else
1658 pp->set_value(oneapi::mkl::dft::config_param::PLACEMENT, DFTI_NOT_INPLACE);
1659#endif
1660 pp->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, howmany);
1661 pp->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, n);
1662 pp->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, nc);
1663 std::vector<std::int64_t> strides(M+1);
1664 strides[0] = 0;
1665 strides[M] = 1;
1666 for (int i = M-1; i >= 1; --i) {
1667 strides[i] = strides[i+1] * fft_size[M-1-i];
1668 }
1669
1670#ifndef AMREX_USE_MKL_DFTI_2024
1671 pp->set_value(oneapi::mkl::dft::config_param::FWD_STRIDES, strides);
1672 // Do not set BWD_STRIDES
1673#else
1674 pp->set_value(oneapi::mkl::dft::config_param::FWD_STRIDES, strides.data());
1675 // Do not set BWD_STRIDES
1676#endif
1677 pp->set_value(oneapi::mkl::dft::config_param::WORKSPACE,
1678 oneapi::mkl::dft::config_value::WORKSPACE_EXTERNAL);
1679 detail::assert_no_external_stream();
1680 pp->commit(amrex::Gpu::Device::streamQueue());
1681 plan = pp;
1682
1683#else /* FFTW */
1684
1685 if (pf == nullptr || pb == nullptr) {
1686 defined = false;
1687 return;
1688 }
1689
1690 if constexpr (std::is_same_v<float,T>) {
1691 if constexpr (D == Direction::forward) {
1692 plan = fftwf_plan_many_dft_r2c
1693 (M, len, howmany, (float*)pf, nullptr, 1, n, (fftwf_complex*)pb, nullptr, 1, nc,
1694 FFTW_ESTIMATE);
1695 } else {
1696 plan = fftwf_plan_many_dft_c2r
1697 (M, len, howmany, (fftwf_complex*)pb, nullptr, 1, nc, (float*)pf, nullptr, 1, n,
1698 FFTW_ESTIMATE);
1699 }
1700 } else {
1701 if constexpr (D == Direction::forward) {
1702 plan = fftw_plan_many_dft_r2c
1703 (M, len, howmany, (double*)pf, nullptr, 1, n, (fftw_complex*)pb, nullptr, 1, nc,
1704 FFTW_ESTIMATE);
1705 } else {
1706 plan = fftw_plan_many_dft_c2r
1707 (M, len, howmany, (fftw_complex*)pb, nullptr, 1, nc, (double*)pf, nullptr, 1, n,
1708 FFTW_ESTIMATE);
1709 }
1710 }
1711#endif
1712
1713#if defined(AMREX_USE_GPU)
1714 if (cache) {
1715 if constexpr (std::is_same_v<float,T>) {
1716 detail::add_vendor_plan_f(key, plan);
1717 } else {
1718 detail::add_vendor_plan_d(key, plan);
1719 }
1720 }
1721#endif
1722}
1723
1725namespace detail
1726{
1727 DistributionMapping make_iota_distromap (Long n);
1728
1737 CommStrategy resolve_comm_strategy (CommStrategy s, Long max_fab_bytes,
1738 std::size_t max_block_bytes, bool dense);
1739
1740 struct TransposeMetaData;
1741
1744 inline std::size_t max_block_bytes
1745 (std::initializer_list<std::unique_ptr<TransposeMetaData> const*> mds);
1746
1748 inline bool transposes_dense
1749 (std::initializer_list<std::unique_ptr<TransposeMetaData> const*> mds);
1750
1751 template <typename FA>
1752 Long max_fab_bytes (FA const& fa)
1753 {
1754 using T = typename FA::FABType::value_type::value_type;
1755 Long n = 0;
1756 if (!fa.empty()) {
1757 auto const& ba = fa.boxArray();
1758 for (int i = 0, nb = int(ba.size()); i < nb; ++i) {
1759 n = std::max(n, ba[i].numPts());
1760 }
1761 }
1762 return n * fa.nComp() * Long(sizeof(T));
1763 }
1764
1768 struct TransposePattern
1769 {
1770 std::size_t block_bytes = 0;
1771 bool dense = true;
1772 };
1773
1777 struct TransposeMetaData
1778 {
1779 template <typename... Args>
1780 explicit TransposeMetaData (TransposePattern pattern_, Args&&... args)
1781 : cmd(std::forward<Args>(args)...), pattern(pattern_) {}
1782
1783 MultiBlockCommMetaData cmd;
1784 NonLocalBC::CommHandler handler;
1785 TransposePattern pattern;
1786 };
1787
1790 int count_active_procs (std::initializer_list<FabArrayBase const*> fas);
1791
1796 MPI_Comm make_active_comm (int n_active, bool& owned);
1797
1799 void free_active_comm (MPI_Comm comm, bool owned);
1800
1802 inline Vector<std::pair<int,int>> box_intervals (Vector<Box> const& boxes, int dir)
1803 {
1804 Vector<std::pair<int,int>> iv;
1805 iv.reserve(boxes.size());
1806 for (auto const& b : boxes) {
1807 iv.emplace_back(b.smallEnd(dir), b.bigEnd(dir));
1808 }
1809 std::sort(iv.begin(), iv.end());
1810 iv.erase(std::unique(iv.begin(), iv.end()), iv.end());
1811 return iv;
1812 }
1813
1822 template <typename FA, typename DTOS>
1823 TransposePattern transpose_pattern (FA const& dst, Box const& dstbox, FA const& src,
1824 DTOS const& dtos, int n_active)
1825 {
1826 using T = typename FA::FABType::value_type::value_type;
1827 auto const& dstba = dst.boxArray();
1828 auto const& srcba = src.boxArray();
1829
1830 // Destination boxes clipped and mapped into the source index space,
1831 // and their indices.
1832 Vector<Box> dboxes;
1833 Vector<int> dindex;
1834 for (int i = 0, n = int(dstba.size()); i < n; ++i) {
1835 Box const b = dstba[i] & dstbox;
1836 if (b.ok()) {
1837 dboxes.push_back(NonLocalBC::Image(dtos, b).setType(srcba.ixType()));
1838 dindex.push_back(i);
1839 }
1840 }
1841 Vector<Box> sboxes;
1842 for (int k = 0, n = int(srcba.size()); k < n; ++k) {
1843 sboxes.push_back(srcba[k]);
1844 }
1845
1846 // Per direction, count the overlapping (dst,src) interval pairs and
1847 // find the longest overlap. Their products are the number of
1848 // overlapping box pairs and the largest overlap.
1849 Long npairs = 1;
1850 Long maxpts = 1;
1851 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1852 auto const div = box_intervals(dboxes, idim);
1853 auto const siv = box_intervals(sboxes, idim);
1854 Long nover = 0;
1855 int maxlen = 0;
1856 Long j0 = 0;
1857 for (auto const& a : div) {
1858 while (j0 < siv.size() && siv[j0].second < a.first) { ++j0; }
1859 for (auto j = j0; j < siv.size() && siv[j].first <= a.second; ++j) {
1860 ++nover;
1861 maxlen = std::max(maxlen, std::min(a.second, siv[j].second)
1862 - std::max(a.first, siv[j].first) + 1);
1863 }
1864 }
1865 npairs *= nover;
1866 maxpts *= maxlen;
1867 }
1868
1869 // Both layouts put box j on local rank j, so pairs (j,j) are local
1870 // copies, not messages.
1871 for (Long i = 0; i < dboxes.size(); ++i) {
1872 int const j = dindex[i];
1873 if (j < sboxes.size() && dboxes[i].intersects(sboxes[j])) { --npairs; }
1874 }
1875 if (npairs <= 0) { npairs = 0; maxpts = 0; }
1876
1877 TransposePattern p;
1878 p.block_bytes = amrex::aligned_size(alignof(T), std::size_t(maxpts) * dst.nComp() * sizeof(T));
1879 // Dense if at least half of all pairs in the communicator exchange
1880 // data. Alltoall pads every pair to the largest message.
1881 p.dense = (2*npairs >= Long(n_active) * Long(n_active));
1882 return p;
1883 }
1884
1885 inline std::size_t max_block_bytes
1886 (std::initializer_list<std::unique_ptr<TransposeMetaData> const*> mds)
1887 {
1888 std::size_t n = 0;
1889 for (auto const* md : mds) {
1890 if (*md) { n = std::max(n, (*md)->pattern.block_bytes); }
1891 }
1892 return n;
1893 }
1894
1895 inline bool transposes_dense
1896 (std::initializer_list<std::unique_ptr<TransposeMetaData> const*> mds)
1897 {
1898 bool dense = true;
1899 for (auto const* md : mds) {
1900 if (*md) { dense = dense && (*md)->pattern.dense; }
1901 }
1902 return dense;
1903 }
1904
1905 template <typename FA, typename DTOS>
1906 std::unique_ptr<TransposeMetaData>
1907 make_transpose (int n_active, FA& dst, Box const& dstbox, FA const& src,
1908 IntVect const& ngrow, DTOS const& dtos)
1909 {
1910 return std::make_unique<TransposeMetaData>
1911 (transpose_pattern(dst, dstbox, src, dtos, n_active), dst, dstbox, src, ngrow, dtos);
1912 }
1913
1916 template <typename FA, typename DTOS>
1917 void transpose (FA& dst, FA const& src, TransposeMetaData& md, int ncomp,
1918 DTOS const& dtos, CommStrategy strategy, MPI_Comm comm)
1919 {
1920 BL_PROFILE("FFT::transpose");
1921 if (strategy == CommStrategy::pointtopoint) {
1922 NonLocalBC::ParallelCopy(dst, src, md.cmd, 0, 0, ncomp, dtos);
1923 } else if (comm == MPI_COMM_NULL) {
1924 return; // this process owns no data
1925 } else if (strategy == CommStrategy::alltoall && md.pattern.dense) {
1926 // With a sparse pattern the padded buffer would be much larger
1927 // than the data, so alltoallv is used instead.
1928 NonLocalBC::ParallelCopy_alltoall(dst, src, md.cmd, 0, 0, ncomp, dtos,
1929 NonLocalBC::Identity{}, md.pattern.block_bytes,
1930 &md.handler, comm);
1931 } else {
1932 NonLocalBC::ParallelCopy_alltoallv(dst, src, md.cmd, 0, 0, ncomp, dtos,
1933 NonLocalBC::Identity{}, &md.handler, comm);
1934 }
1935 }
1936
1937 template <typename FA>
1938 typename FA::FABType::value_type * get_fab (FA& fa)
1939 {
1940 auto myproc = ParallelContext::MyProcSub();
1941 if (myproc < fa.size()) {
1942 return fa.fabPtr(myproc);
1943 } else {
1944 return nullptr;
1945 }
1946 }
1947
1948 template <typename FA1, typename FA2>
1949 std::unique_ptr<char,DataDeleter> make_mfs_share (FA1& fa1, FA2& fa2)
1950 {
1951 bool not_same_fa = true;
1952 if constexpr (std::is_same_v<FA1,FA2>) {
1953 not_same_fa = (&fa1 != &fa2);
1954 }
1955 using FAB1 = typename FA1::FABType::value_type;
1956 using FAB2 = typename FA2::FABType::value_type;
1957 using T1 = typename FAB1::value_type;
1958 using T2 = typename FAB2::value_type;
1959 auto myproc = ParallelContext::MyProcSub();
1960 bool alloc_1 = (myproc < fa1.size());
1961 bool alloc_2 = (myproc < fa2.size()) && not_same_fa;
1962 void* p = nullptr;
1963 if (alloc_1 && alloc_2) {
1964 Box const& box1 = fa1.fabbox(myproc);
1965 Box const& box2 = fa2.fabbox(myproc);
1966 int ncomp1 = fa1.nComp();
1967 int ncomp2 = fa2.nComp();
1968 p = The_Arena()->alloc(std::max(sizeof(T1)*box1.numPts()*ncomp1,
1969 sizeof(T2)*box2.numPts()*ncomp2));
1970 fa1.setFab(myproc, FAB1(box1, ncomp1, (T1*)p));
1971 fa2.setFab(myproc, FAB2(box2, ncomp2, (T2*)p));
1972 } else if (alloc_1) {
1973 Box const& box1 = fa1.fabbox(myproc);
1974 int ncomp1 = fa1.nComp();
1975 p = The_Arena()->alloc(sizeof(T1)*box1.numPts()*ncomp1);
1976 fa1.setFab(myproc, FAB1(box1, ncomp1, (T1*)p));
1977 } else if (alloc_2) {
1978 Box const& box2 = fa2.fabbox(myproc);
1979 int ncomp2 = fa2.nComp();
1980 p = The_Arena()->alloc(sizeof(T2)*box2.numPts()*ncomp2);
1981 fa2.setFab(myproc, FAB2(box2, ncomp2, (T2*)p));
1982 } else {
1983 return nullptr;
1984 }
1985 return std::unique_ptr<char,DataDeleter>((char*)p, DataDeleter{The_Arena()});
1986 }
1987}
1989
1991
1992struct Swap01
1993{
1994 [[nodiscard]] constexpr Dim3 operator() (Dim3 i) const noexcept
1995 {
1996 return Dim3{.x = i.y, .y = i.x, .z = i.z};
1997 }
1998
1999 static constexpr Dim3 Inverse (Dim3 i)
2000 {
2001 return Dim3{.x = i.y, .y = i.x, .z = i.z};
2002 }
2003
2004 [[nodiscard]] constexpr IndexType operator() (IndexType it) const noexcept
2005 {
2006 return it;
2007 }
2008
2009 static constexpr IndexType Inverse (IndexType it)
2010 {
2011 return it;
2012 }
2013};
2014
2015struct Swap02
2016{
2017 [[nodiscard]] constexpr Dim3 operator() (Dim3 i) const noexcept
2018 {
2019 return Dim3{.x = i.z, .y = i.y, .z = i.x};
2020 }
2021
2022 static constexpr Dim3 Inverse (Dim3 i)
2023 {
2024 return Dim3{.x = i.z, .y = i.y, .z = i.x};
2025 }
2026
2027 [[nodiscard]] constexpr IndexType operator() (IndexType it) const noexcept
2028 {
2029 return it;
2030 }
2031
2032 static constexpr IndexType Inverse (IndexType it)
2033 {
2034 return it;
2035 }
2036};
2037
2038struct RotateFwd
2039{
2040 // dest -> src: (x,y,z) -> (y,z,x)
2041 [[nodiscard]] constexpr Dim3 operator() (Dim3 i) const noexcept
2042 {
2043 return Dim3{.x = i.y, .y = i.z, .z = i.x};
2044 }
2045
2046 // src -> dest: (x,y,z) -> (z,x,y)
2047 static constexpr Dim3 Inverse (Dim3 i)
2048 {
2049 return Dim3{.x = i.z, .y = i.x, .z = i.y};
2050 }
2051
2052 [[nodiscard]] constexpr IndexType operator() (IndexType it) const noexcept
2053 {
2054 return it;
2055 }
2056
2057 static constexpr IndexType Inverse (IndexType it)
2058 {
2059 return it;
2060 }
2061};
2062
2063struct RotateBwd
2064{
2065 // dest -> src: (x,y,z) -> (z,x,y)
2066 [[nodiscard]] constexpr Dim3 operator() (Dim3 i) const noexcept
2067 {
2068 return Dim3{.x = i.z, .y = i.x, .z = i.y};
2069 }
2070
2071 // src -> dest: (x,y,z) -> (y,z,x)
2072 static constexpr Dim3 Inverse (Dim3 i)
2073 {
2074 return Dim3{.x = i.y, .y = i.z, .z = i.x};
2075 }
2076
2077 [[nodiscard]] constexpr IndexType operator() (IndexType it) const noexcept
2078 {
2079 return it;
2080 }
2081
2082 static constexpr IndexType Inverse (IndexType it)
2083 {
2084 return it;
2085 }
2086};
2087
2089
2091namespace detail
2092{
2093 struct SubHelper
2094 {
2095 explicit SubHelper (Box const& domain);
2096
2097 [[nodiscard]] Box make_box (Box const& box) const;
2098
2099 [[nodiscard]] Periodicity make_periodicity (Periodicity const& period) const;
2100
2101 [[nodiscard]] bool ghost_safe (IntVect const& ng) const;
2102
2103 // This rearranges the order.
2104 [[nodiscard]] IntVect make_iv (IntVect const& iv) const;
2105
2106 // This keeps the order, but zero out the values in the hidden dimension.
2107 [[nodiscard]] IntVect make_safe_ghost (IntVect const& ng) const;
2108
2109 [[nodiscard]] BoxArray inverse_boxarray (BoxArray const& ba) const;
2110
2111 [[nodiscard]] IntVect inverse_order (IntVect const& order) const;
2112
2113 template <typename T>
2114 [[nodiscard]] T make_array (T const& a) const
2115 {
2116#if (AMREX_SPACEDIM == 1)
2118 return a;
2119#elif (AMREX_SPACEDIM == 2)
2120 if (m_case == case_1n) {
2121 return T{a[1],a[0]};
2122 } else {
2123 return a;
2124 }
2125#else
2126 if (m_case == case_11n) {
2127 return T{a[2],a[0],a[1]};
2128 } else if (m_case == case_1n1) {
2129 return T{a[1],a[0],a[2]};
2130 } else if (m_case == case_1nn) {
2131 return T{a[1],a[2],a[0]};
2132 } else if (m_case == case_n1n) {
2133 return T{a[0],a[2],a[1]};
2134 } else {
2135 return a;
2136 }
2137#endif
2138 }
2139
2140 [[nodiscard]] GpuArray<int,3> xyz_order () const;
2141
2142 template <typename FA>
2143 FA make_alias_mf (FA const& mf)
2144 {
2145 BoxList bl = mf.boxArray().boxList();
2146 for (auto& b : bl) {
2147 b = make_box(b);
2148 }
2149 auto const& ng = make_iv(mf.nGrowVect());
2150 FA submf(BoxArray(std::move(bl)), mf.DistributionMap(), mf.nComp(), ng, MFInfo{}.SetAlloc(false));
2151 using FAB = typename FA::fab_type;
2152 for (MFIter mfi(submf, MFItInfo().DisableDeviceSync()); mfi.isValid(); ++mfi) {
2153 submf.setFab(mfi, FAB(mfi.fabbox(), mf.nComp(), mf[mfi].dataPtr()));
2154 }
2155 return submf;
2156 }
2157
2158#if (AMREX_SPACEDIM == 2)
2159 enum Case { case_1n, case_other };
2160 int m_case = case_other;
2161#elif (AMREX_SPACEDIM == 3)
2162 enum Case { case_11n, case_1n1, case_1nn, case_n1n, case_other };
2163 int m_case = case_other;
2164#endif
2165 };
2166}
2168
2169}
2170
2171#endif
Runtime initialization/finalization helpers and global diagnostics.
#define BL_PROFILE(a)
Definition AMReX_BLProfiler.H:593
#define AMREX_ALWAYS_ASSERT_WITH_MESSAGE(EX, MSG)
Definition AMReX_BLassert.H:49
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
Lightweight Arena-backed allocator and deleter helpers.
Maps FABs in a FabArray to MPI processes.
Enum reflection utilities and the AMREX_ENUM macro.
Distributed container of FAB objects plus copy and fill utilities.
Problem-domain geometry: maps between index space and physical space.
#define AMREX_CUFFT_SAFE_CALL(call)
Definition AMReX_GpuError.H:92
#define AMREX_GPU_DEVICE
Definition AMReX_GpuQualifiers.H:18
Convenience header for the core AMReX GPU facilities.
while parser_exe_t p
Definition AMReX_Parser_Exe_Body.H:33
#define AMREX_D_TERM(a, b, c)
Definition AMReX_SPACE.H:172
virtual void free(void *pt)=0
Free a previously allocated block pointed to by pt.
virtual void * alloc(std::size_t sz)=0
Allocate sz bytes from this arena.
__host__ __device__ IntVectND< dim > length() const noexcept
Return the length of the BoxND.
Definition AMReX_Box.H:167
__host__ __device__ const IntVectND< dim > & smallEnd() const &noexcept
Return the inclusive lower bound of the box.
Definition AMReX_Box.H:124
int CoordInt() const noexcept
Returns the CoordType as an int.
Definition AMReX_CoordSys.H:52
Calculates the distribution of FABs to MPI processes.
Definition AMReX_DistributionMapping.H:51
Rectangular problem domain geometry.
Definition AMReX_Geometry.H:87
const Box & Domain() const noexcept
Returns our rectangular domain.
Definition AMReX_Geometry.H:246
const RealBox & ProbDomain() const noexcept
Returns the problem domain.
Definition AMReX_Geometry.H:202
bool isPeriodic(int dir) const noexcept
Is the domain periodic in the specified direction?
Definition AMReX_Geometry.H:399
static bool usingExternalStream() noexcept
Definition AMReX_GpuDevice.cpp:973
An Integer Vector in dim-Dimensional Space.
Definition AMReX_IntVect.H:149
amrex_long Long
Definition AMReX_INT.H:30
void ParallelForOMP(T n, L const &f) noexcept
Performance-portable kernel launch function with optional OpenMP threading.
Definition AMReX_GpuLaunch.H:328
__host__ __device__ Dim3 length(Array4< T > const &a) noexcept
Return the spatial extents of an Array4 in Dim3 form.
Definition AMReX_Array4.H:1379
__host__ __device__ BoxND< dim > shift(const BoxND< dim > &b, int dir, int nzones) noexcept
Return a copy of b shifted by nzones cells in direction dir.
Definition AMReX_Box.H:1550
std::size_t aligned_size(std::size_t align_requirement, std::size_t size) noexcept
Return the smallest multiple of align_requirement that is >= size.
Definition AMReX_Arena.H:39
Arena * The_Arena()
Definition AMReX_Arena.cpp:839
#define AMREX_ENUM(CLASS,...)
Declare a scoped enum with reflection support.
Definition AMReX_Enum.H:270
Definition AMReX_FFT_CrossProj.H:8
CommStrategy
How the distributed transposes exchange data between processes.
Definition AMReX_FFT_Helper.H:62
@ alltoall
Definition AMReX_FFT_Helper.H:62
@ automatic
Definition AMReX_FFT_Helper.H:62
@ pointtopoint
Definition AMReX_FFT_Helper.H:62
@ alltoallv
Definition AMReX_FFT_Helper.H:62
constexpr int FastNumPrimeFactors() noexcept
Return the default factor count used for selecting fast FFT lengths.
Definition AMReX_FFT_Helper.H:78
int nextFastLen(int target, int nfactors=FastNumPrimeFactors())
Return the smallest fast FFT length greater than or equal to target.
Definition AMReX_FFT_Helper.H:303
Direction
Definition AMReX_FFT_Helper.H:57
@ backward
Definition AMReX_FFT_Helper.H:57
@ none
Definition AMReX_FFT_Helper.H:57
@ forward
Definition AMReX_FFT_Helper.H:57
@ both
Definition AMReX_FFT_Helper.H:57
Boundary
Definition AMReX_FFT_Helper.H:64
@ odd
Definition AMReX_FFT_Helper.H:64
@ even
Definition AMReX_FFT_Helper.H:64
@ periodic
Definition AMReX_FFT_Helper.H:64
DomainStrategy
Definition AMReX_FFT_Helper.H:59
@ automatic
Definition AMReX_FFT_Helper.H:59
@ pencil
Definition AMReX_FFT_Helper.H:59
@ slab
Definition AMReX_FFT_Helper.H:59
Kind
Definition AMReX_FFT_Helper.H:66
@ r2r_ee_f
Definition AMReX_FFT_Helper.H:66
@ r2r_oo_b
Definition AMReX_FFT_Helper.H:67
@ r2c_b
Definition AMReX_FFT_Helper.H:66
@ none
Definition AMReX_FFT_Helper.H:66
@ r2r_oe
Definition AMReX_FFT_Helper.H:67
@ r2c_f
Definition AMReX_FFT_Helper.H:66
@ r2r_ee_b
Definition AMReX_FFT_Helper.H:66
@ r2r_oo_f
Definition AMReX_FFT_Helper.H:67
@ c2c_f
Definition AMReX_FFT_Helper.H:66
@ r2r_eo
Definition AMReX_FFT_Helper.H:67
@ c2c_b
Definition AMReX_FFT_Helper.H:66
void streamSynchronize() noexcept
Definition AMReX_GpuDevice.H:329
gpuStream_t gpuStream() noexcept
Definition AMReX_GpuDevice.H:310
__host__ __device__ std::pair< double, double > sincospi(double x)
Return sin(pi*x) and cos(pi*x) given x.
Definition AMReX_Math.H:347
int MPI_Comm
Definition AMReX_ccse-mpi.H:51
@ make_alias
Definition AMReX_MakeType.H:7
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:273
@ overflow
Definition AMReX.H:561
BoxND< 3 > Box
Box is an alias for amrex::BoxND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:35
Direction
Definition AMReX_Orientation.H:14
@ x
Definition AMReX_Orientation.H:14
IndexTypeND< 3 > IndexType
IndexType is an alias for amrex::IndexTypeND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:41
IntVectND< 3 > IntVect
IntVect is an alias for amrex::IntVectND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:38
void Abort(const std::string &msg)
Print a fatal-error message to stderr and abort execution.
Definition AMReX.cpp:244
const int[]
Definition AMReX_BLProfiler.cpp:1665
Definition AMReX_FFT_Helper.H:88
bool twod_mode
Definition AMReX_FFT_Helper.H:99
Info & setCommStrategy(CommStrategy s)
Select how the distributed transposes communicate.
Definition AMReX_FFT_Helper.H:180
CommStrategy comm_strategy
Communication strategy for the distributed transposes.
Definition AMReX_FFT_Helper.H:117
Info & setNumProcs(int n)
Cap the number of MPI ranks used by FFT.
Definition AMReX_FFT_Helper.H:171
bool oned_mode
Definition AMReX_FFT_Helper.H:108
int batch_size
Batched FFT size. Only support in R2C, not R2X.
Definition AMReX_FFT_Helper.H:111
Info & setOpenBCPadding(bool x)
Enable or disable OpenBCSolver internal FFT padding.
Definition AMReX_FFT_Helper.H:187
Info & setOpenBCPaddingNumPrimeFactors(int n)
Set the factor count used for OpenBCSolver FFT padding.
Definition AMReX_FFT_Helper.H:194
Info & setDomainStrategy(DomainStrategy s)
Select how the domain is decomposed across MPI ranks.
Definition AMReX_FFT_Helper.H:132
DomainStrategy domain_strategy
Domain composition strategy.
Definition AMReX_FFT_Helper.H:90
int nprocs
Max number of processes to use.
Definition AMReX_FFT_Helper.H:114
int pencil_threshold
Definition AMReX_FFT_Helper.H:94
bool openbc_padding
Whether OpenBCSolver pads internal FFT lengths for better performance.
Definition AMReX_FFT_Helper.H:120
Info & setOneDMode(bool x)
Flag the degenerate 2-D mode (nx==1 or ny==1) that still batches along z.
Definition AMReX_FFT_Helper.H:157
Info & setBatchSize(int bsize)
Specify the batch size for FFT.
Definition AMReX_FFT_Helper.H:164
int openbc_padding_nfactors
Definition AMReX_FFT_Helper.H:124
Info & setPencilThreshold(int t)
Override the slab→pencil break-even threshold for the automatic strategy.
Definition AMReX_FFT_Helper.H:139
Info & setTwoDMode(bool x)
Restrict transforms to the first two dimensions (3-D problems only).
Definition AMReX_FFT_Helper.H:146
Definition AMReX_FFT_Helper.H:377
void * pf
Definition AMReX_FFT_Helper.H:412
void unpack_r2r_buffer(T *pdst, void const *pbuf) const
Collapse the spectral R2R result back into the original real layout.
Definition AMReX_FFT_Helper.H:1320
VendorPlan plan2
Definition AMReX_FFT_Helper.H:411
int n
Definition AMReX_FFT_Helper.H:404
void destroy()
Release any vendor FFT plan objects owned by this Plan.
Definition AMReX_FFT_Helper.H:431
bool defined2
Definition AMReX_FFT_Helper.H:409
void init_r2r(Box const &box, VendorComplex *pc, std::pair< Boundary, Boundary > const &bc)
Initialize a real-to-real plan that reads/writes complex storage.
Definition AMReX_FFT_Helper.H:913
void pack_r2r_buffer(void *pbuf, T const *psrc) const
Expand the real R2R input into the symmetry-extended buffer expected by CUFFT/rocFFT.
Definition AMReX_FFT_Helper.H:1082
static void free_scratch_space(void *p)
Release GPU scratch allocated via alloc_scratch_space().
Definition AMReX_FFT_Helper.H:1074
static void destroy_vendor_plan(VendorPlan plan)
Helper that destroys a vendor plan of the appropriate backend type.
Definition AMReX_FFT_Helper.H:1530
Kind get_r2r_kind(std::pair< Boundary, Boundary > const &bc)
Map boundary conditions to the Plan Kind for real-to-real transforms.
Definition AMReX_FFT_Helper.H:775
cufftHandle VendorPlan
Definition AMReX_FFT_Helper.H:379
Kind kind
Definition AMReX_FFT_Helper.H:406
void init_c2c(Box const &box, VendorComplex *p, int ncomp=1, int ndims=1)
Initialize a complex-to-complex plan across 1/2/3 dimensions.
Definition AMReX_FFT_Helper.H:607
int howmany
Definition AMReX_FFT_Helper.H:405
void * pb
Definition AMReX_FFT_Helper.H:413
void init_r2c(Box const &box, T *pr, VendorComplex *pc, bool is_2d_transform=false, int ncomp=1)
Initialize a 1-D or 2-D real-to-complex plan over the supplied Box.
Definition AMReX_FFT_Helper.H:455
void compute_r2r()
Execute the real-to-real plan, including GPU packing/unpacking.
Definition AMReX_FFT_Helper.H:1473
std::conditional_t< std::is_same_v< float, T >, cuComplex, cuDoubleComplex > VendorComplex
Definition AMReX_FFT_Helper.H:380
void compute_c2c()
Execute the complex-to-complex plan in place.
Definition AMReX_FFT_Helper.H:1013
bool r2r_data_is_complex
Definition AMReX_FFT_Helper.H:407
void * alloc_scratch_space() const
Allocate GPU scratch space large enough to hold packed R2R data.
Definition AMReX_FFT_Helper.H:1057
VendorPlan plan
Definition AMReX_FFT_Helper.H:410
void compute_r2c()
Execute the previously initialized real-to-complex plan.
Definition AMReX_FFT_Helper.H:961
bool defined
Definition AMReX_FFT_Helper.H:408
void set_ptrs(void *p0, void *p1)
Register device pointers used by the forward/backward executions.
Definition AMReX_FFT_Helper.H:422
void init_r2r(Box const &box, T *p, std::pair< Boundary, Boundary > const &bc, int howmany_initval=1)
Initialize a real-to-real (cosine/sine) plan that operates on real buffers.
Definition AMReX_FFT_Helper.H:809
A host / device complex number type, because std::complex doesn't work in device code with Cuda yet.
Definition AMReX_GpuComplex.H:30