Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_FFT_Poisson.H
Go to the documentation of this file.
1#ifndef AMREX_FFT_POISSON_H_
2#define AMREX_FFT_POISSON_H_
3
4#include <AMReX_FFT.H>
5#include <AMReX_Geometry.H>
6
7namespace amrex::FFT
8{
9
15
17namespace detail {
18template <typename MF>
19void fill_physbc (MF& mf, Geometry const& geom,
20 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> const& bc);
21
22}
24
30template <typename MF = MultiFab>
32{
33public:
34
42 Poisson (Geometry const& geom,
43 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> const& bc,
44 Info const& info = Info{})
45 requires (IsFabArray_v<MF>)
46 : m_domain_lo(geom.Domain().smallEnd()),
47 m_geom(detail::shift_geom(geom)),
48 m_bc(bc)
49 {
50 bool all_periodic = true;
51 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
52 all_periodic = all_periodic
53 && (bc[idim].first == Boundary::periodic)
54 && (bc[idim].second == Boundary::periodic);
55 }
56 if (all_periodic) {
57 m_r2c = std::make_unique<R2C<typename MF::value_type>>
58 (m_geom.Domain(), make_solver_info(info));
59 } else {
60 m_r2x = std::make_unique<R2X<typename MF::value_type>>
61 (m_geom.Domain(), m_bc, make_solver_info(info));
62 }
63 }
64
71 explicit Poisson (Geometry const& geom, Info const& info = Info{})
72 requires (IsFabArray_v<MF>)
73 : m_domain_lo(geom.Domain().smallEnd()),
74 m_geom(detail::shift_geom(geom)),
75 m_bc{AMREX_D_DECL(std::make_pair(Boundary::periodic,Boundary::periodic),
76 std::make_pair(Boundary::periodic,Boundary::periodic),
77 std::make_pair(Boundary::periodic,Boundary::periodic))}
78 {
79 if (m_geom.isAllPeriodic()) {
80 m_r2c = std::make_unique<R2C<typename MF::value_type>>
81 (m_geom.Domain(), make_solver_info(info));
82 } else {
83 amrex::Abort("FFT::Poisson: wrong BC");
84 }
85 }
86
97 void solve (MF& a_soln, MF const& a_rhs);
98
99private:
100 static Info make_solver_info (Info const& info) {
101 return Info{}.setCommStrategy(info.comm_strategy);
102 }
103
104 IntVect m_domain_lo;
105 Geometry m_geom;
106 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> m_bc;
107 std::unique_ptr<R2X<typename MF::value_type>> m_r2x;
108 std::unique_ptr<R2C<typename MF::value_type>> m_r2c;
109};
110
111#if (AMREX_SPACEDIM == 3)
116template <typename MF = MultiFab>
118{
119public:
120
129 template <typename FA=MF>
130 requires (IsFabArray_v<FA>)
131 explicit PoissonOpenBC (Geometry const& geom,
133 IntVect const& ngrow = IntVect(0),
134 Info const& info = Info{});
135
142 void solve (MF& soln, MF const& rhs);
143
147 void define_doit (); // has to be public for cuda
148
154 [[nodiscard]] IntVect const& PaddedLength () const { return m_solver.PaddedLength(); }
155
156private:
157 static Info make_solver_info (Info const& info);
158
159 Geometry m_geom;
160 Box m_grown_domain;
161 IntVect m_ngrow;
163};
164#endif
165
172template <typename MF = MultiFab>
174{
175public:
176 using T = typename MF::value_type;
177
186 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> const& bc,
187 Info const& a_info = Info{})
188 requires (IsFabArray_v<MF>)
189 : m_domain_lo(geom.Domain().smallEnd()),
190 m_geom(detail::shift_geom(geom)),
191 m_bc(bc)
192 {
193#if (AMREX_SPACEDIM < 3)
194 amrex::Abort("FFT::PoissonHybrid: 1D & 2D todo");
195 return;
196#endif
197 bool periodic_xy = true;
198 for (int idim = 0; idim < 2; ++idim) {
199 if (m_geom.Domain().length(idim) > 1) {
200 periodic_xy = periodic_xy && (bc[idim].first == Boundary::periodic);
201 AMREX_ALWAYS_ASSERT((bc[idim].first == Boundary::periodic &&
202 bc[idim].second == Boundary::periodic) ||
203 (bc[idim].first != Boundary::periodic &&
204 bc[idim].second != Boundary::periodic));
205 }
206 }
208 bc[2].second != Boundary::periodic);
209 Info info{};
210 info.setTwoDMode(true);
211 info.setCommStrategy(a_info.comm_strategy);
212 if (m_geom.Domain().length(0) == 1 || m_geom.Domain().length(1) == 1) {
213 info.setOneDMode(true);
214 }
215 if (periodic_xy) {
216 m_r2c = std::make_unique<R2C<typename MF::value_type>>(m_geom.Domain(),
217 info);
218 } else {
219 m_r2x = std::make_unique<R2X<typename MF::value_type>> (m_geom.Domain(),
220 m_bc, info);
221 }
222 build_spmf();
223 }
224
234 void solve (MF& soln, MF const& rhs);
242 void solve (MF& soln, MF const& rhs, Vector<T> const& dz);
250 void solve (MF& soln, MF const& rhs, Gpu::DeviceVector<T> const& dz);
251
258 void solve_2d (MF& a_soln, MF const& a_rhs);
259
268 template <typename TRIA, typename TRIC>
269 void solve (MF& a_soln, MF const& a_rhs, TRIA const& tria, TRIC const& tric);
270
271 // This is public for cuda
279 template <typename FA, typename TRIA, typename TRIC>
280 void solve_z (FA& spmf, TRIA const& tria, TRIC const& tric);
281
287 [[nodiscard]] std::pair<BoxArray,DistributionMapping> getSpectralDataLayout () const;
288
289private:
290
291 void build_spmf ();
292
293 IntVect m_domain_lo;
294 Geometry m_geom;
295 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> m_bc;
296 std::unique_ptr<R2X<typename MF::value_type>> m_r2x;
297 std::unique_ptr<R2C<typename MF::value_type>> m_r2c;
298 MF m_spmf_r;
299 using cMF = FabArray<BaseFab<GpuComplex<T>>>;
300 cMF m_spmf_c;
301};
302
303template <typename MF>
304void Poisson<MF>::solve (MF& a_soln, MF const& a_rhs)
305{
306 BL_PROFILE("FFT::Poisson::solve");
307
308 MF* soln = &a_soln;
309 MF const* rhs = &a_rhs;
310 MF solntmp, rhstmp;
311 if (m_domain_lo != 0) {
312 detail::shift_mfs(m_domain_lo, a_soln, a_rhs, solntmp, rhstmp);
313 soln = &solntmp;
314 rhs = &rhstmp;
315 }
316
317 AMREX_ASSERT(soln->is_cell_centered() && rhs->is_cell_centered());
318
319 using T = typename MF::value_type;
320
322 {AMREX_D_DECL(Math::pi<T>()/T(m_geom.Domain().length(0)),
323 Math::pi<T>()/T(m_geom.Domain().length(1)),
324 Math::pi<T>()/T(m_geom.Domain().length(2)))};
325 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
326 if (m_bc[idim].first == Boundary::periodic) {
327 fac[idim] *= T(2);
328 }
329 }
331 {AMREX_D_DECL(T(2)/T(m_geom.CellSize(0)*m_geom.CellSize(0)),
332 T(2)/T(m_geom.CellSize(1)*m_geom.CellSize(1)),
333 T(2)/T(m_geom.CellSize(2)*m_geom.CellSize(2)))};
334 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
335 if (m_geom.Domain().length(idim) == 1) {
336 dxfac[idim] = 0;
337 }
338 }
339 auto scale = (m_r2x) ? m_r2x->scalingFactor() : m_r2c->scalingFactor();
340
341 GpuArray<T,AMREX_SPACEDIM> offset{AMREX_D_DECL(T(0),T(0),T(0))};
342 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
343 if (m_bc[idim].first == Boundary::odd &&
344 m_bc[idim].second == Boundary::odd)
345 {
346 offset[idim] = T(1);
347 }
348 else if ((m_bc[idim].first == Boundary::odd &&
349 m_bc[idim].second == Boundary::even) ||
350 (m_bc[idim].first == Boundary::even &&
351 m_bc[idim].second == Boundary::odd))
352 {
353 offset[idim] = T(0.5);
354 }
355 }
356
357 auto f = [=] AMREX_GPU_DEVICE (int i, int j, int k, auto& spectral_data)
358 {
360 AMREX_D_TERM(T a = fac[0]*(T(i)+offset[0]);,
361 T b = fac[1]*(T(j)+offset[1]);,
362 T c = fac[2]*(T(k)+offset[2]));
363 T k2 = AMREX_D_TERM(dxfac[0]*(std::cos(a)-T(1)),
364 +dxfac[1]*(std::cos(b)-T(1)),
365 +dxfac[2]*(std::cos(c)-T(1)));
366 // Select the denominator instead of guarding the division. An
367 // optimizing compiler may speculate a guarded division, and 0/0
368 // for the zero mode then trips amrex.fpe_trap_invalid.
369 T const k2d = (k2 != T(0)) ? k2 : T(1);
370 spectral_data /= k2d;
371 spectral_data *= scale;
372 };
373
374 IntVect const& ng = amrex::elemwiseMin(soln->nGrowVect(), IntVect(1));
375
376 if (m_r2x) {
377 m_r2x->forwardThenBackward_doit_0(*rhs, *soln, f, ng, m_geom.periodicity());
378 detail::fill_physbc(*soln, m_geom, m_bc);
379 } else {
380 m_r2c->forward(*rhs);
381 m_r2c->post_forward_doit_0(f);
382 m_r2c->backward_doit(*soln, ng, m_geom.periodicity());
383 }
384}
385
386#if (AMREX_SPACEDIM == 3)
387
388template <typename MF>
389template <typename FA>
390requires (IsFabArray_v<FA>)
392 IntVect const& ngrow, Info const& info)
393 : m_geom(geom),
394 m_grown_domain(amrex::grow(amrex::convert(geom.Domain(),ixtype),ngrow)),
395 m_ngrow(ngrow),
396 m_solver(m_grown_domain, make_solver_info(info))
397{
398 define_doit();
399}
400
401template <typename MF>
402Info PoissonOpenBC<MF>::make_solver_info (Info const& info)
403{
404 Info solver_info;
405 solver_info.openbc_padding = info.openbc_padding;
407 solver_info.comm_strategy = info.comm_strategy;
408 return solver_info;
409}
410
411template <typename MF>
413{
414 using T = typename MF::value_type;
415 auto const& lo = m_grown_domain.smallEnd();
416 auto const dx = T(m_geom.CellSize(0));
417 auto const dy = T(m_geom.CellSize(1));
418 auto const dz = T(m_geom.CellSize(2));
419 auto const gfac = T(1)/T(std::sqrt(T(12)));
420 // 0.125 comes from that there are 8 Gauss quadrature points
421 auto const fac = T(-0.125) * (dx*dy*dz) / (T(4)*Math::pi<T>());
422 m_solver.setGreensFunction([=] AMREX_GPU_DEVICE (int i, int j, int k) -> T
423 {
424 auto x = (T(i-lo[0]) - gfac) * dx; // first Gauss quadrature point
425 auto y = (T(j-lo[1]) - gfac) * dy;
426 auto z = (T(k-lo[2]) - gfac) * dz;
427 T r = 0;
428 for (int gx = 0; gx < 2; ++gx) {
429 for (int gy = 0; gy < 2; ++gy) {
430 for (int gz = 0; gz < 2; ++gz) {
431 auto xg = x + T(2*gx)*gfac*dx;
432 auto yg = y + T(2*gy)*gfac*dy;
433 auto zg = z + T(2*gz)*gfac*dz;
434 r += T(1)/std::sqrt(xg*xg+yg*yg+zg*zg);
435 }}}
436 return fac * r;
437 });
438}
439
440template <typename MF>
441void PoissonOpenBC<MF>::solve (MF& soln, MF const& rhs)
442{
443 AMREX_ASSERT(m_grown_domain.ixType() == soln.ixType() && m_grown_domain.ixType() == rhs.ixType());
444 m_solver.solve(soln, rhs);
445}
446
447#endif /* AMREX_SPACEDIM == 3 */
448
450namespace fft_poisson_detail {
451 template <typename T>
452 struct Tri_Zero {
453 [[nodiscard]] constexpr T operator() (int, int, int) const
454 {
455 return 0;
456 }
457 };
458
459 template <typename T>
460 struct Tri_Uniform {
462 T operator() (int, int, int) const
463 {
464 return m_dz2inv;
465 }
466 T m_dz2inv;
467 };
468
469 template <typename T>
470 struct TriA {
472 T operator() (int, int, int k) const
473 {
474 return (k > 0) ? T(2.0) / (m_dz[k]*(m_dz[k]+m_dz[k-1]))
475 : T(1.0) / (m_dz[k]* m_dz[k]);
476 }
477 T const* m_dz;
478 };
479
480 template <typename T>
481 struct TriC {
483 T operator() (int, int, int k) const
484 {
485 return (k < m_size-1) ? T(2.0) / (m_dz[k]*(m_dz[k]+m_dz[k+1]))
486 : T(1.0) / (m_dz[k]* m_dz[k]);
487 }
488 T const* m_dz;
489 int m_size;
490 };
491}
493
494template <typename MF>
495std::pair<BoxArray,DistributionMapping>
497{
498 if (!m_spmf_r.empty()) {
499 return std::make_pair(m_spmf_r.boxArray(), m_spmf_r.DistributionMap());
500 } else {
501 return std::make_pair(m_spmf_c.boxArray(), m_spmf_c.DistributionMap());
502 }
503}
504
505template <typename MF>
506void PoissonHybrid<MF>::build_spmf ()
507{
508#if (AMREX_SPACEDIM == 3)
509 AMREX_ALWAYS_ASSERT(m_geom.Domain().length(2) > 1 &&
510 (m_geom.Domain().length(0) > 1 ||
511 m_geom.Domain().length(1) > 1));
512
513 if (m_r2c) {
514 Box cdomain = m_geom.Domain();
515 if (cdomain.length(0) > 1) {
516 cdomain.setBig(0,cdomain.length(0)/2);
517 } else {
518 cdomain.setBig(1,cdomain.length(1)/2);
519 }
520 auto cba = amrex::decompose(cdomain, ParallelContext::NProcsSub(),
521 {AMREX_D_DECL(true,true,false)});
522 DistributionMapping dm = detail::make_iota_distromap(cba.size());
523 m_spmf_c.define(cba, dm, 1, 0);
524 } else if (m_geom.Domain().length(0) > 1 &&
525 m_geom.Domain().length(1) > 1) {
526 if (m_r2x->m_cy.empty()) { // spectral data is real
527 auto sba = amrex::decompose(m_geom.Domain(),ParallelContext::NProcsSub(),
528 {AMREX_D_DECL(true,true,false)});
529 DistributionMapping dm = detail::make_iota_distromap(sba.size());
530 m_spmf_r.define(sba, dm, 1, 0);
531 } else { // spectral data is complex. one of the first two dimensions is periodic.
532 Box cdomain = m_geom.Domain();
533 if (m_bc[0].first == Boundary::periodic) {
534 cdomain.setBig(0,cdomain.length(0)/2);
535 } else {
536 cdomain.setBig(1,cdomain.length(1)/2);
537 }
538 auto cba = amrex::decompose(cdomain, ParallelContext::NProcsSub(),
539 {AMREX_D_DECL(true,true,false)});
540 DistributionMapping dm = detail::make_iota_distromap(cba.size());
541 m_spmf_c.define(cba, dm, 1, 0);
542 }
543 } else {
544 // spectral data is real
545 auto sba = amrex::decompose(m_geom.Domain(),ParallelContext::NProcsSub(),
546 {AMREX_D_DECL(true,true,false)});
547 DistributionMapping dm = detail::make_iota_distromap(sba.size());
548 m_spmf_r.define(sba, dm, 1, 0);
549 }
550#else
552#endif
553}
554
555template <typename MF>
556void PoissonHybrid<MF>::solve (MF& soln, MF const& rhs)
557{
558 auto delz = T(m_geom.CellSize(AMREX_SPACEDIM-1));
559 solve(soln, rhs,
560 fft_poisson_detail::Tri_Uniform<T>{T(1)/(delz*delz)},
561 fft_poisson_detail::Tri_Uniform<T>{T(1)/(delz*delz)});
562}
563
564template <typename MF>
565void PoissonHybrid<MF>::solve (MF& soln, MF const& rhs, Gpu::DeviceVector<T> const& dz)
566{
568 int(dz.size()) == m_geom.Domain().length(2),
569 "FFT::PoissonHybrid: dz.size() must equal domain length in z");
570
571 auto const* pdz = dz.dataPtr();
572 solve(soln, rhs,
573 fft_poisson_detail::TriA<T>{pdz},
574 fft_poisson_detail::TriC<T>{pdz,int(dz.size())});
575}
576
577template <typename MF>
578void PoissonHybrid<MF>::solve (MF& soln, MF const& rhs, Vector<T> const& dz)
579{
580 AMREX_ASSERT(soln.is_cell_centered() && rhs.is_cell_centered());
582 int(dz.size()) == m_geom.Domain().length(2),
583 "FFT::PoissonHybrid: dz.size() must equal domain length in z");
584
585#ifdef AMREX_USE_GPU
586 Gpu::DeviceVector<T> d_dz(dz.size());
587 Gpu::htod_memcpy_async(d_dz.data(), dz.data(), dz.size()*sizeof(T));
588 auto const* pdz = d_dz.data();
589#else
590 auto const* pdz = dz.data();
591#endif
592 solve(soln, rhs,
593 fft_poisson_detail::TriA<T>{pdz},
594 fft_poisson_detail::TriC<T>{pdz,int(dz.size())});
595}
596
597template <typename MF>
598void PoissonHybrid<MF>::solve_2d (MF& soln, MF const& rhs)
599{
600 solve(soln, rhs, fft_poisson_detail::Tri_Zero<T>{}, fft_poisson_detail::Tri_Zero<T>{});
601}
602
603template <typename MF>
604template <typename TRIA, typename TRIC>
605void PoissonHybrid<MF>::solve (MF& a_soln, MF const& a_rhs, TRIA const& tria,
606 TRIC const& tric)
607{
608 BL_PROFILE("FFT::PoissonHybrid::solve");
609
610 AMREX_ASSERT(a_soln.is_cell_centered() && a_rhs.is_cell_centered());
611
612#if (AMREX_SPACEDIM < 3)
613 amrex::ignore_unused(a_soln, a_rhs, tria, tric);
614#else
615
616 MF* soln = &a_soln;
617 MF const* rhs = &a_rhs;
618 MF solntmp, rhstmp;
619 if (m_domain_lo != 0) {
620 detail::shift_mfs(m_domain_lo, a_soln, a_rhs, solntmp, rhstmp);
621 soln = &solntmp;
622 rhs = &rhstmp;
623 }
624
625 IntVect const& ng = amrex::elemwiseMin(soln->nGrowVect(), IntVect(1));
626
627 if (m_r2c)
628 {
629 m_r2c->forward(*rhs, m_spmf_c);
630 solve_z(m_spmf_c, tria, tric);
631 m_r2c->backward_doit(m_spmf_c, *soln, ng, m_geom.periodicity());
632 }
633 else
634 {
635 if (m_r2x->m_cy.empty()) { // spectral data is real
636 m_r2x->forward(*rhs, m_spmf_r);
637 solve_z(m_spmf_r, tria, tric);
638 m_r2x->backward(m_spmf_r, *soln, ng, m_geom.periodicity());
639 } else { // spectral data is complex.
640 m_r2x->forward(*rhs, m_spmf_c);
641 solve_z(m_spmf_c, tria, tric);
642 m_r2x->backward(m_spmf_c, *soln, ng, m_geom.periodicity());
643 }
644 }
645
646 detail::fill_physbc(*soln, m_geom, m_bc);
647#endif
648}
649
650template <typename MF>
651template <typename FA, typename TRIA, typename TRIC>
652void PoissonHybrid<MF>::solve_z (FA& spmf, TRIA const& tria, TRIC const& tric)
653{
654 BL_PROFILE("PoissonHybrid::solve_z");
655
656#if (AMREX_SPACEDIM < 3)
657 amrex::ignore_unused(spmf, tria, tric);
658#else
659 auto facx = Math::pi<T>()/T(m_geom.Domain().length(0));
660 auto facy = Math::pi<T>()/T(m_geom.Domain().length(1));
661 if (m_bc[0].first == Boundary::periodic) { facx *= T(2); }
662 if (m_bc[1].first == Boundary::periodic) { facy *= T(2); }
663 auto dxfac = T(2)/T(m_geom.CellSize(0)*m_geom.CellSize(0));
664 auto dyfac = T(2)/T(m_geom.CellSize(1)*m_geom.CellSize(1));
665 auto scale = (m_r2x) ? m_r2x->scalingFactor() : m_r2c->scalingFactor();
666
667 if (m_geom.Domain().length(0) == 1) { dxfac = 0; }
668 if (m_geom.Domain().length(1) == 1) { dyfac = 0; }
669
670 GpuArray<T,AMREX_SPACEDIM-1> offset{T(0),T(0)};
671 for (int idim = 0; idim < AMREX_SPACEDIM-1; ++idim) {
672 if (m_geom.Domain().length(idim) > 1) {
673 if (m_bc[idim].first == Boundary::odd &&
674 m_bc[idim].second == Boundary::odd)
675 {
676 offset[idim] = T(1);
677 }
678 else if ((m_bc[idim].first == Boundary::odd &&
679 m_bc[idim].second == Boundary::even) ||
680 (m_bc[idim].first == Boundary::even &&
681 m_bc[idim].second == Boundary::odd))
682 {
683 offset[idim] = T(0.5);
684 }
685 }
686 }
687
688 if
689#ifndef _WIN32
690 constexpr
691#endif
692 (std::is_same_v<TRIA,fft_poisson_detail::Tri_Zero<T>> &&
693 std::is_same_v<TRIC,fft_poisson_detail::Tri_Zero<T>>) {
694 amrex::ignore_unused(tria,tric);
695#if defined(AMREX_USE_OMP) && !defined(AMREX_USE_GPU)
696#pragma omp parallel
697#endif
698 for (MFIter mfi(spmf,TilingIfNotGPU()); mfi.isValid(); ++mfi)
699 {
700 auto const& spectral = spmf.array(mfi);
701 auto const& box = mfi.tilebox();
702 amrex::ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k)
703 {
704 T a = facx*(T(i)+offset[0]);
705 T b = facy*(T(j)+offset[1]);
706 T k2 = dxfac * (std::cos(a)-T(1))
707 + dyfac * (std::cos(b)-T(1));
708 // Select the denominator instead of guarding the division
709 // (see Poisson::solve): avoids a speculated 0/0.
710 T const k2d = (k2 != T(0)) ? k2 : T(1);
711 spectral(i,j,k) /= k2d;
712 spectral(i,j,k) *= scale;
713 });
714 }
715 } else {
716 bool zlo_neumann = m_bc[2].first == Boundary::even;
717 bool zhi_neumann = m_bc[2].second == Boundary::even;
718 bool is_singular = (offset[0] == T(0)) && (offset[1] == T(0))
719 && zlo_neumann && zhi_neumann;
720
721 auto nz = m_geom.Domain().length(2);
722
723#if defined(AMREX_USE_OMP) && !defined(AMREX_USE_GPU)
724#pragma omp parallel
725#endif
726 for (MFIter mfi(spmf); mfi.isValid(); ++mfi)
727 {
728 auto const& spectral = spmf.array(mfi);
729 auto const& box = mfi.validbox();
730 auto const& xybox = amrex::makeSlab(box, 2, 0);
731
732#ifdef AMREX_USE_GPU
733 // xxxxx TODO: We need to explore how to optimize this
734 // function. Maybe we can use cusparse. Maybe we should make
735 // z-direction to be the unit stride direction.
736
737 FArrayBox tridiag_workspace(box,4);
738 auto const& ald = tridiag_workspace.array(0);
739 auto const& bd = tridiag_workspace.array(1);
740 auto const& cud = tridiag_workspace.array(2);
741 auto const& scratch = tridiag_workspace.array(3);
742
743 amrex::ParallelFor(xybox, [=] AMREX_GPU_DEVICE (int i, int j, int)
744 {
745 T a = facx*(T(i)+offset[0]);
746 T b = facy*(T(j)+offset[1]);
747 T k2 = dxfac * (std::cos(a)-T(1))
748 + dyfac * (std::cos(b)-T(1));
749
750 // Tridiagonal solve
751 for(int k=0; k < nz; k++) {
752 if(k==0) {
753 ald(i,j,k) = T(0.);
754 cud(i,j,k) = tric(i,j,k);
755 if (zlo_neumann) {
756 bd(i,j,k) = k2 - cud(i,j,k);
757 } else {
758 bd(i,j,k) = k2 - cud(i,j,k) - T(2.0)*tria(i,j,k);
759 }
760 } else if (k == nz-1) {
761 ald(i,j,k) = tria(i,j,k);
762 cud(i,j,k) = T(0.);
763 if (zhi_neumann) {
764 bd(i,j,k) = k2 - ald(i,j,k);
765 if (i == 0 && j == 0 && is_singular) {
766 bd(i,j,k) *= T(2.0);
767 }
768 } else {
769 bd(i,j,k) = k2 - ald(i,j,k) - T(2.0)*tric(i,j,k);
770 }
771 } else {
772 ald(i,j,k) = tria(i,j,k);
773 cud(i,j,k) = tric(i,j,k);
774 bd(i,j,k) = k2 -ald(i,j,k)-cud(i,j,k);
775 }
776 }
777
778 scratch(i,j,0) = cud(i,j,0)/bd(i,j,0);
779 spectral(i,j,0) = spectral(i,j,0)/bd(i,j,0);
780
781 for (int k = 1; k < nz; k++) {
782 if (k < nz-1) {
783 scratch(i,j,k) = cud(i,j,k) / (bd(i,j,k) - ald(i,j,k) * scratch(i,j,k-1));
784 }
785 spectral(i,j,k) = (spectral(i,j,k) - ald(i,j,k) * spectral(i,j,k - 1))
786 / (bd(i,j,k) - ald(i,j,k) * scratch(i,j,k-1));
787 }
788
789 for (int k = nz - 2; k >= 0; k--) {
790 spectral(i,j,k) -= scratch(i,j,k) * spectral(i,j,k + 1);
791 }
792
793 for (int k = 0; k < nz; ++k) {
794 spectral(i,j,k) *= scale;
795 }
796 });
798
799#else
800
801 Gpu::DeviceVector<T> ald(nz);
803 Gpu::DeviceVector<T> cud(nz);
804 Gpu::DeviceVector<T> scratch(nz);
805
806 amrex::LoopOnCpu(xybox, [&] (int i, int j, int)
807 {
808 T a = facx*(T(i)+offset[0]);
809 T b = facy*(T(j)+offset[1]);
810 T k2 = dxfac * (std::cos(a)-T(1))
811 + dyfac * (std::cos(b)-T(1));
812
813 // Tridiagonal solve
814 for(int k=0; k < nz; k++) {
815 if(k==0) {
816 ald[k] = T(0.);
817 cud[k] = tric(i,j,k);
818 if (zlo_neumann) {
819 bd[k] = k2 - cud[k];
820 } else {
821 bd[k] = k2 - cud[k] - T(2.0)*tria(i,j,k);
822 }
823 } else if (k == nz-1) {
824 ald[k] = tria(i,j,k);
825 cud[k] = T(0.);
826 if (zhi_neumann) {
827 bd[k] = k2 - ald[k];
828 if (i == 0 && j == 0 && is_singular) {
829 bd[k] *= T(2.0);
830 }
831 } else {
832 bd[k] = k2 - ald[k] - T(2.0)*tric(i,j,k);
833 }
834 } else {
835 ald[k] = tria(i,j,k);
836 cud[k] = tric(i,j,k);
837 bd[k] = k2 -ald[k]-cud[k];
838 }
839 }
840
841 scratch[0] = cud[0]/bd[0];
842 spectral(i,j,0) = spectral(i,j,0)/bd[0];
843
844 for (int k = 1; k < nz; k++) {
845 if (k < nz-1) {
846 scratch[k] = cud[k] / (bd[k] - ald[k] * scratch[k-1]);
847 }
848 spectral(i,j,k) = (spectral(i,j,k) - ald[k] * spectral(i,j,k - 1))
849 / (bd[k] - ald[k] * scratch[k-1]);
850 }
851
852 for (int k = nz - 2; k >= 0; k--) {
853 spectral(i,j,k) -= scratch[k] * spectral(i,j,k + 1);
854 }
855
856 for (int k = 0; k < nz; ++k) {
857 spectral(i,j,k) *= scale;
858 }
859 });
860#endif
861 }
862 }
863#endif
864}
865
867namespace detail {
868
869template <class T>
870struct FFTPhysBCTag {
871 Array4<T> dfab;
872 Box dbox;
873 Boundary bc;
874 Orientation face;
875
877 Box const& box () const noexcept { return dbox; }
878};
879
880template <typename MF>
881void fill_physbc (MF& mf, Geometry const& geom,
882 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> const& bc)
883{
884 using T = typename MF::value_type;
885 using Tag = FFTPhysBCTag<T>;
886 Vector<Tag> tags;
887
888 for (MFIter mfi(mf, MFItInfo{}.DisableDeviceSync()); mfi.isValid(); ++mfi)
889 {
890 auto const& box = mfi.fabbox();
891 auto const& arr = mf.array(mfi);
892 for (OrientationIter oit; oit; ++oit) {
893 Orientation face = oit();
894 int idim = face.coordDir();
895 Box b = geom.Domain();
896 Boundary fbc;
897 if (face.isLow()) {
898 b.setRange(idim,geom.Domain().smallEnd(idim)-1);
899 fbc = bc[idim].first;
900 } else {
901 b.setRange(idim,geom.Domain().bigEnd(idim)+1);
902 fbc = bc[idim].second;
903 }
904 b &= box;
905 if (b.ok() && fbc != Boundary::periodic) {
906 tags.push_back(Tag{.dfab = arr, .dbox = b, .bc = fbc, .face = face});
907 }
908 }
909 }
910
911#if defined(AMREX_USE_GPU)
912 amrex::ParallelFor(tags, [=] AMREX_GPU_DEVICE (int i, int j, int k,
913 Tag const& tag) noexcept
914#else
915 auto ntags = int(tags.size());
916#ifdef AMREX_USE_OMP
917#pragma omp parallel for
918#endif
919 for (int itag = 0; itag < ntags; ++itag) {
920 Tag const& tag = tags[itag];
921 amrex::LoopOnCpu(tag.dbox, [&] (int i, int j, int k)
922#endif
923 {
924 int sgn = tag.face.isLow() ? 1 : -1;
925 IntVect siv = IntVect(AMREX_D_DECL(i,j,k))
926 + sgn * IntVect::TheDimensionVector(tag.face.coordDir());
927 if (tag.bc == Boundary::odd) {
928 tag.dfab(i,j,k) = -tag.dfab(siv);
929 } else { // even
930 tag.dfab(i,j,k) = tag.dfab(siv);
931 }
932 });
933#if !defined(AMREX_USE_GPU)
934 }
935#endif
936}
937}
939
940}
941
942#endif
#define BL_PROFILE(a)
Definition AMReX_BLProfiler.H:593
#define AMREX_ALWAYS_ASSERT_WITH_MESSAGE(EX, MSG)
Definition AMReX_BLassert.H:49
#define AMREX_ASSERT(EX)
Definition AMReX_BLassert.H:38
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
#define AMREX_FORCE_INLINE
Definition AMReX_Extension.H:124
Problem-domain geometry: maps between index space and physical space.
#define AMREX_GPU_DEVICE
Definition AMReX_GpuQualifiers.H:18
#define AMREX_GPU_HOST_DEVICE
Definition AMReX_GpuQualifiers.H:20
#define AMREX_D_TERM(a, b, c)
Definition AMReX_SPACE.H:172
#define AMREX_D_DECL(a, b, c)
Definition AMReX_SPACE.H:171
Array4< T const > array() const noexcept
Create an Array4 view over all components.
Definition AMReX_BaseFab.H:475
__host__ __device__ BoxND & setBig(const IntVectND< dim > &bg) noexcept
Redefine the big end of the BoxND.
Definition AMReX_Box.H:516
__host__ __device__ IntVectND< dim > length() const noexcept
Return the length of the BoxND.
Definition AMReX_Box.H:167
A Fortran Array of REALs.
Definition AMReX_FArrayBox.H:237
Convolution-based solver for open boundary conditions using Green's functions.
Definition AMReX_FFT_OpenBCSolver.H:26
void solve(MF &soln, MF const &rhs)
Solve del dot grad soln = rhs for uniform spacing in all directions.
Definition AMReX_FFT_Poisson.H:556
typename MF::value_type T
Definition AMReX_FFT_Poisson.H:176
void solve_z(FA &spmf, TRIA const &tria, TRIC const &tric)
CUDA helper that applies the supplied tridiagonal operator along z.
Definition AMReX_FFT_Poisson.H:652
PoissonHybrid(Geometry const &geom, Array< std::pair< Boundary, Boundary >, 3 > const &bc, Info const &a_info=Info{})
Construct the hybrid Poisson solver (mixed BCs in z with optional nonuniform spacing).
Definition AMReX_FFT_Poisson.H:185
std::pair< BoxArray, DistributionMapping > getSpectralDataLayout() const
Layout information for spectral storage used by the hybrid solver.
Definition AMReX_FFT_Poisson.H:496
void solve_2d(MF &a_soln, MF const &a_rhs)
Solve an independent 2-D Poisson problem on every z-plane.
Definition AMReX_FFT_Poisson.H:598
PoissonOpenBC(Geometry const &geom, IndexType ixtype=IndexType::TheCellType(), IntVect const &ngrow=IntVect(0), Info const &info=Info{})
Build an open-boundary FFT solver over the grown domain.
Definition AMReX_FFT_Poisson.H:391
void solve(MF &soln, MF const &rhs)
Solve the open-boundary Poisson problem.
Definition AMReX_FFT_Poisson.H:441
IntVect const & PaddedLength() const
Access the one-sided padded length used by the internal OpenBC solver.
Definition AMReX_FFT_Poisson.H:154
void define_doit()
Initialize the discretized Green's function cache (public for CUDA kernels).
Definition AMReX_FFT_Poisson.H:412
Poisson(Geometry const &geom, Info const &info=Info{})
Construct a purely periodic Poisson solver.
Definition AMReX_FFT_Poisson.H:71
void solve(MF &a_soln, MF const &a_rhs)
Solve del dot grad soln = rhs.
Definition AMReX_FFT_Poisson.H:304
Poisson(Geometry const &geom, Array< std::pair< Boundary, Boundary >, 3 > const &bc, Info const &info=Info{})
Construct a Poisson solver with explicit boundary types.
Definition AMReX_FFT_Poisson.H:42
Rectangular problem domain geometry.
Definition AMReX_Geometry.H:87
const Box & Domain() const noexcept
Returns our rectangular domain.
Definition AMReX_Geometry.H:246
bool isAllPeriodic() const noexcept
Is domain periodic in all directions?
Definition AMReX_Geometry.H:406
__host__ static __device__ constexpr IndexTypeND< dim > TheCellType() noexcept
Definition AMReX_IndexType.H:150
Iterator for looping ever tiles and boxes of amrex::FabArray based containers.
Definition AMReX_MFIter.H:88
bool isValid() const noexcept
Is the iterator valid i.e. is it associated with a FAB?
Definition AMReX_MFIter.H:176
Encapsulation of the Orientation of the Faces of a Box.
Definition AMReX_Orientation.H:29
size_type size() const noexcept
Definition AMReX_PODVector.H:654
T * dataPtr() noexcept
Definition AMReX_PODVector.H:676
T * data() noexcept
Definition AMReX_PODVector.H:672
This class is a thin wrapper around std::vector. Unlike vector, Vector::operator[] provides bound che...
Definition AMReX_Vector.H:29
Long size() const noexcept
Definition AMReX_Vector.H:54
ArrayND< T, 4, true > Array4
Convenient alias for a 4D ArrayND with three spatial dimensions plus a component dimension.
Definition AMReX_Array4.H:1340
__host__ __device__ BoxND< dim > convert(const BoxND< dim > &b, const IntVectND< dim > &typ) noexcept
Return a copy of b converted to the nodal flags typ.
Definition AMReX_Box.H:1632
__host__ __device__ BoxND< dim > grow(const BoxND< dim > &b, int i) noexcept
Return a copy of b grown uniformly by i cells in every direction.
Definition AMReX_Box.H:1328
std::array< T, N > Array
Definition AMReX_Array.H:31
PODVector< T, ArenaAllocator< T > > DeviceVector
A PODVector that uses the standard memory Arena. Note that the memory might or might not be managed d...
Definition AMReX_GpuContainers.H:24
__host__ __device__ constexpr T elemwiseMin(T const &a, T const &b) noexcept
Definition AMReX_Algorithm.H:75
Definition AMReX_FFT_CrossProj.H:8
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
void streamSynchronize() noexcept
Definition AMReX_GpuDevice.H:329
void htod_memcpy_async(void *p_d, const void *p_h, const std::size_t sz) noexcept
Definition AMReX_GpuDevice.H:440
constexpr T pi()
Definition AMReX_Math.H:211
int NProcsSub() noexcept
number of ranks in current frame
Definition AMReX_ParallelContext.H:74
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:273
__host__ __device__ BoxND< dim > makeSlab(BoxND< dim > const &b, int direction, int slab_index) noexcept
Return a copy of b collapsed to a slab perpendicular to direction.
Definition AMReX_Box.H:2438
constexpr bool IsFabArray_v
Definition AMReX_TypeTraits.H:34
void ParallelFor(TypeList< CTOs... > ctos, std::array< int, sizeof...(CTOs)> const &runtime_options, T N, F &&f)
Definition AMReX_CTOParallelForImpl.H:202
BoxND< 3 > Box
Box is an alias for amrex::BoxND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:35
double second() noexcept
Definition AMReX_Utility.cpp:919
BoxArray decompose(Box const &domain, int nboxes, Array< bool, 3 > const &decomp, bool no_overlap)
Decompose domain box into BoxArray.
Definition AMReX_BoxArray.cpp:1961
@ y
Definition AMReX_Orientation.H:14
@ x
Definition AMReX_Orientation.H:14
@ z
Definition AMReX_Orientation.H:14
IndexTypeND< 3 > IndexType
IndexType is an alias for amrex::IndexTypeND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:41
__host__ __device__ constexpr IntVectND< dim > scale(const IntVectND< dim > &p, int s) noexcept
Returns a IntVectND obtained by multiplying each of the components of this IntVectND by s.
Definition AMReX_IntVect.H:1128
bool TilingIfNotGPU() noexcept
Definition AMReX_MFIter.H:12
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
void LoopOnCpu(Dim3 lo, Dim3 hi, F const &f) noexcept
Definition AMReX_Loop.H:365
Definition AMReX_FFT_Helper.H:88
CommStrategy comm_strategy
Communication strategy for the distributed transposes.
Definition AMReX_FFT_Helper.H:117
bool openbc_padding
Whether OpenBCSolver pads internal FFT lengths for better performance.
Definition AMReX_FFT_Helper.H:120
int openbc_padding_nfactors
Definition AMReX_FFT_Helper.H:124
Fixed-size array that can be used on GPU.
Definition AMReX_Array.H:52