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
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
41 Poisson (Geometry const& geom,
42 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> const& bc)
43 requires (IsFabArray_v<MF>)
44 : m_domain_lo(geom.Domain().smallEnd()),
45 m_geom(detail::shift_geom(geom)),
46 m_bc(bc)
47 {
48 bool all_periodic = true;
49 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
50 all_periodic = all_periodic
51 && (bc[idim].first == Boundary::periodic)
52 && (bc[idim].second == Boundary::periodic);
53 }
54 if (all_periodic) {
55 m_r2c = std::make_unique<R2C<typename MF::value_type>>(m_geom.Domain());
56 } else {
57 m_r2x = std::make_unique<R2X<typename MF::value_type>> (m_geom.Domain(), m_bc);
58 }
59 }
60
66 explicit Poisson (Geometry const& geom)
67 requires (IsFabArray_v<MF>)
68 : m_domain_lo(geom.Domain().smallEnd()),
69 m_geom(detail::shift_geom(geom)),
70 m_bc{AMREX_D_DECL(std::make_pair(Boundary::periodic,Boundary::periodic),
71 std::make_pair(Boundary::periodic,Boundary::periodic),
72 std::make_pair(Boundary::periodic,Boundary::periodic))}
73 {
74 if (m_geom.isAllPeriodic()) {
75 m_r2c = std::make_unique<R2C<typename MF::value_type>>(m_geom.Domain());
76 } else {
77 amrex::Abort("FFT::Poisson: wrong BC");
78 }
79 }
80
91 void solve (MF& a_soln, MF const& a_rhs);
92
93private:
94 IntVect m_domain_lo;
95 Geometry m_geom;
96 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> m_bc;
97 std::unique_ptr<R2X<typename MF::value_type>> m_r2x;
98 std::unique_ptr<R2C<typename MF::value_type>> m_r2c;
99};
100
101#if (AMREX_SPACEDIM == 3)
106template <typename MF = MultiFab>
108{
109public:
110
119 template <typename FA=MF>
120 requires (IsFabArray_v<FA>)
121 explicit PoissonOpenBC (Geometry const& geom,
123 IntVect const& ngrow = IntVect(0),
124 Info const& info = Info{});
125
132 void solve (MF& soln, MF const& rhs);
133
137 void define_doit (); // has to be public for cuda
138
144 [[nodiscard]] IntVect const& PaddedLength () const { return m_solver.PaddedLength(); }
145
146private:
147 static Info make_solver_info (Info const& info);
148
149 Geometry m_geom;
150 Box m_grown_domain;
151 IntVect m_ngrow;
153};
154#endif
155
162template <typename MF = MultiFab>
164{
165public:
166 using T = typename MF::value_type;
167
175 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> const& bc)
176 requires (IsFabArray_v<MF>)
177 : m_domain_lo(geom.Domain().smallEnd()),
178 m_geom(detail::shift_geom(geom)),
179 m_bc(bc)
180 {
181#if (AMREX_SPACEDIM < 3)
182 amrex::Abort("FFT::PoissonHybrid: 1D & 2D todo");
183 return;
184#endif
185 bool periodic_xy = true;
186 for (int idim = 0; idim < 2; ++idim) {
187 if (m_geom.Domain().length(idim) > 1) {
188 periodic_xy = periodic_xy && (bc[idim].first == Boundary::periodic);
189 AMREX_ALWAYS_ASSERT((bc[idim].first == Boundary::periodic &&
190 bc[idim].second == Boundary::periodic) ||
191 (bc[idim].first != Boundary::periodic &&
192 bc[idim].second != Boundary::periodic));
193 }
194 }
196 bc[2].second != Boundary::periodic);
197 Info info{};
198 info.setTwoDMode(true);
199 if (m_geom.Domain().length(0) == 1 || m_geom.Domain().length(1) == 1) {
200 info.setOneDMode(true);
201 }
202 if (periodic_xy) {
203 m_r2c = std::make_unique<R2C<typename MF::value_type>>(m_geom.Domain(),
204 info);
205 } else {
206 m_r2x = std::make_unique<R2X<typename MF::value_type>> (m_geom.Domain(),
207 m_bc, info);
208 }
209 build_spmf();
210 }
211
221 void solve (MF& soln, MF const& rhs);
229 void solve (MF& soln, MF const& rhs, Vector<T> const& dz);
237 void solve (MF& soln, MF const& rhs, Gpu::DeviceVector<T> const& dz);
238
245 void solve_2d (MF& a_soln, MF const& a_rhs);
246
255 template <typename TRIA, typename TRIC>
256 void solve (MF& a_soln, MF const& a_rhs, TRIA const& tria, TRIC const& tric);
257
258 // This is public for cuda
266 template <typename FA, typename TRIA, typename TRIC>
267 void solve_z (FA& spmf, TRIA const& tria, TRIC const& tric);
268
274 [[nodiscard]] std::pair<BoxArray,DistributionMapping> getSpectralDataLayout () const;
275
276private:
277
278 void build_spmf ();
279
280 IntVect m_domain_lo;
281 Geometry m_geom;
282 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> m_bc;
283 std::unique_ptr<R2X<typename MF::value_type>> m_r2x;
284 std::unique_ptr<R2C<typename MF::value_type>> m_r2c;
285 MF m_spmf_r;
286 using cMF = FabArray<BaseFab<GpuComplex<T>>>;
287 cMF m_spmf_c;
288};
289
290template <typename MF>
291void Poisson<MF>::solve (MF& a_soln, MF const& a_rhs)
292{
293 BL_PROFILE("FFT::Poisson::solve");
294
295 MF* soln = &a_soln;
296 MF const* rhs = &a_rhs;
297 MF solntmp, rhstmp;
298 if (m_domain_lo != 0) {
299 detail::shift_mfs(m_domain_lo, a_soln, a_rhs, solntmp, rhstmp);
300 soln = &solntmp;
301 rhs = &rhstmp;
302 }
303
304 AMREX_ASSERT(soln->is_cell_centered() && rhs->is_cell_centered());
305
306 using T = typename MF::value_type;
307
309 {AMREX_D_DECL(Math::pi<T>()/T(m_geom.Domain().length(0)),
310 Math::pi<T>()/T(m_geom.Domain().length(1)),
311 Math::pi<T>()/T(m_geom.Domain().length(2)))};
312 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
313 if (m_bc[idim].first == Boundary::periodic) {
314 fac[idim] *= T(2);
315 }
316 }
318 {AMREX_D_DECL(T(2)/T(m_geom.CellSize(0)*m_geom.CellSize(0)),
319 T(2)/T(m_geom.CellSize(1)*m_geom.CellSize(1)),
320 T(2)/T(m_geom.CellSize(2)*m_geom.CellSize(2)))};
321 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
322 if (m_geom.Domain().length(idim) == 1) {
323 dxfac[idim] = 0;
324 }
325 }
326 auto scale = (m_r2x) ? m_r2x->scalingFactor() : m_r2c->scalingFactor();
327
329 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
330 if (m_bc[idim].first == Boundary::odd &&
331 m_bc[idim].second == Boundary::odd)
332 {
333 offset[idim] = T(1);
334 }
335 else if ((m_bc[idim].first == Boundary::odd &&
336 m_bc[idim].second == Boundary::even) ||
337 (m_bc[idim].first == Boundary::even &&
338 m_bc[idim].second == Boundary::odd))
339 {
340 offset[idim] = T(0.5);
341 }
342 }
343
344 auto f = [=] AMREX_GPU_DEVICE (int i, int j, int k, auto& spectral_data)
345 {
347 AMREX_D_TERM(T a = fac[0]*(T(i)+offset[0]);,
348 T b = fac[1]*(T(j)+offset[1]);,
349 T c = fac[2]*(T(k)+offset[2]));
350 T k2 = AMREX_D_TERM(dxfac[0]*(std::cos(a)-T(1)),
351 +dxfac[1]*(std::cos(b)-T(1)),
352 +dxfac[2]*(std::cos(c)-T(1)));
353 // Select the denominator instead of guarding the division. An
354 // optimizing compiler may speculate a guarded division, and 0/0
355 // for the zero mode then trips amrex.fpe_trap_invalid.
356 T const k2d = (k2 != T(0)) ? k2 : T(1);
357 spectral_data /= k2d;
358 spectral_data *= scale;
359 };
360
361 IntVect const& ng = amrex::elemwiseMin(soln->nGrowVect(), IntVect(1));
362
363 if (m_r2x) {
364 m_r2x->forwardThenBackward_doit_0(*rhs, *soln, f, ng, m_geom.periodicity());
365 detail::fill_physbc(*soln, m_geom, m_bc);
366 } else {
367 m_r2c->forward(*rhs);
368 m_r2c->post_forward_doit_0(f);
369 m_r2c->backward_doit(*soln, ng, m_geom.periodicity());
370 }
371}
372
373#if (AMREX_SPACEDIM == 3)
374
375template <typename MF>
376template <typename FA>
377requires (IsFabArray_v<FA>)
379 IntVect const& ngrow, Info const& info)
380 : m_geom(geom),
381 m_grown_domain(amrex::grow(amrex::convert(geom.Domain(),ixtype),ngrow)),
382 m_ngrow(ngrow),
383 m_solver(m_grown_domain, make_solver_info(info))
384{
385 define_doit();
386}
387
388template <typename MF>
390{
391 Info solver_info;
392 solver_info.openbc_padding = info.openbc_padding;
394 return solver_info;
395}
396
397template <typename MF>
399{
400 using T = typename MF::value_type;
401 auto const& lo = m_grown_domain.smallEnd();
402 auto const dx = T(m_geom.CellSize(0));
403 auto const dy = T(m_geom.CellSize(1));
404 auto const dz = T(m_geom.CellSize(2));
405 auto const gfac = T(1)/T(std::sqrt(T(12)));
406 // 0.125 comes from that there are 8 Gauss quadrature points
407 auto const fac = T(-0.125) * (dx*dy*dz) / (T(4)*Math::pi<T>());
408 m_solver.setGreensFunction([=] AMREX_GPU_DEVICE (int i, int j, int k) -> T
409 {
410 auto x = (T(i-lo[0]) - gfac) * dx; // first Gauss quadrature point
411 auto y = (T(j-lo[1]) - gfac) * dy;
412 auto z = (T(k-lo[2]) - gfac) * dz;
413 T r = 0;
414 for (int gx = 0; gx < 2; ++gx) {
415 for (int gy = 0; gy < 2; ++gy) {
416 for (int gz = 0; gz < 2; ++gz) {
417 auto xg = x + T(2*gx)*gfac*dx;
418 auto yg = y + T(2*gy)*gfac*dy;
419 auto zg = z + T(2*gz)*gfac*dz;
420 r += T(1)/std::sqrt(xg*xg+yg*yg+zg*zg);
421 }}}
422 return fac * r;
423 });
424}
425
426template <typename MF>
427void PoissonOpenBC<MF>::solve (MF& soln, MF const& rhs)
428{
429 AMREX_ASSERT(m_grown_domain.ixType() == soln.ixType() && m_grown_domain.ixType() == rhs.ixType());
430 m_solver.solve(soln, rhs);
431}
432
433#endif /* AMREX_SPACEDIM == 3 */
434
436namespace fft_poisson_detail {
437 template <typename T>
438 struct Tri_Zero {
439 [[nodiscard]] constexpr T operator() (int, int, int) const
440 {
441 return 0;
442 }
443 };
444
445 template <typename T>
446 struct Tri_Uniform {
448 T operator() (int, int, int) const
449 {
450 return m_dz2inv;
451 }
452 T m_dz2inv;
453 };
454
455 template <typename T>
456 struct TriA {
458 T operator() (int, int, int k) const
459 {
460 return (k > 0) ? T(2.0) / (m_dz[k]*(m_dz[k]+m_dz[k-1]))
461 : T(1.0) / (m_dz[k]* m_dz[k]);
462 }
463 T const* m_dz;
464 };
465
466 template <typename T>
467 struct TriC {
469 T operator() (int, int, int k) const
470 {
471 return (k < m_size-1) ? T(2.0) / (m_dz[k]*(m_dz[k]+m_dz[k+1]))
472 : T(1.0) / (m_dz[k]* m_dz[k]);
473 }
474 T const* m_dz;
475 int m_size;
476 };
477}
479
480template <typename MF>
481std::pair<BoxArray,DistributionMapping>
483{
484 if (!m_spmf_r.empty()) {
485 return std::make_pair(m_spmf_r.boxArray(), m_spmf_r.DistributionMap());
486 } else {
487 return std::make_pair(m_spmf_c.boxArray(), m_spmf_c.DistributionMap());
488 }
489}
490
491template <typename MF>
493{
494#if (AMREX_SPACEDIM == 3)
495 AMREX_ALWAYS_ASSERT(m_geom.Domain().length(2) > 1 &&
496 (m_geom.Domain().length(0) > 1 ||
497 m_geom.Domain().length(1) > 1));
498
499 if (m_r2c) {
500 Box cdomain = m_geom.Domain();
501 if (cdomain.length(0) > 1) {
502 cdomain.setBig(0,cdomain.length(0)/2);
503 } else {
504 cdomain.setBig(1,cdomain.length(1)/2);
505 }
506 auto cba = amrex::decompose(cdomain, ParallelContext::NProcsSub(),
507 {AMREX_D_DECL(true,true,false)});
508 DistributionMapping dm = detail::make_iota_distromap(cba.size());
509 m_spmf_c.define(cba, dm, 1, 0);
510 } else if (m_geom.Domain().length(0) > 1 &&
511 m_geom.Domain().length(1) > 1) {
512 if (m_r2x->m_cy.empty()) { // spectral data is real
514 {AMREX_D_DECL(true,true,false)});
515 DistributionMapping dm = detail::make_iota_distromap(sba.size());
516 m_spmf_r.define(sba, dm, 1, 0);
517 } else { // spectral data is complex. one of the first two dimensions is periodic.
518 Box cdomain = m_geom.Domain();
519 if (m_bc[0].first == Boundary::periodic) {
520 cdomain.setBig(0,cdomain.length(0)/2);
521 } else {
522 cdomain.setBig(1,cdomain.length(1)/2);
523 }
524 auto cba = amrex::decompose(cdomain, ParallelContext::NProcsSub(),
525 {AMREX_D_DECL(true,true,false)});
526 DistributionMapping dm = detail::make_iota_distromap(cba.size());
527 m_spmf_c.define(cba, dm, 1, 0);
528 }
529 } else {
530 // spectral data is real
532 {AMREX_D_DECL(true,true,false)});
533 DistributionMapping dm = detail::make_iota_distromap(sba.size());
534 m_spmf_r.define(sba, dm, 1, 0);
535 }
536#else
538#endif
539}
540
541template <typename MF>
542void PoissonHybrid<MF>::solve (MF& soln, MF const& rhs)
543{
544 auto delz = T(m_geom.CellSize(AMREX_SPACEDIM-1));
545 solve(soln, rhs,
546 fft_poisson_detail::Tri_Uniform<T>{T(1)/(delz*delz)},
547 fft_poisson_detail::Tri_Uniform<T>{T(1)/(delz*delz)});
548}
549
550template <typename MF>
551void PoissonHybrid<MF>::solve (MF& soln, MF const& rhs, Gpu::DeviceVector<T> const& dz)
552{
554 int(dz.size()) == m_geom.Domain().length(2),
555 "FFT::PoissonHybrid: dz.size() must equal domain length in z");
556
557 auto const* pdz = dz.dataPtr();
558 solve(soln, rhs,
559 fft_poisson_detail::TriA<T>{pdz},
560 fft_poisson_detail::TriC<T>{pdz,int(dz.size())});
561}
562
563template <typename MF>
564void PoissonHybrid<MF>::solve (MF& soln, MF const& rhs, Vector<T> const& dz)
565{
566 AMREX_ASSERT(soln.is_cell_centered() && rhs.is_cell_centered());
568 int(dz.size()) == m_geom.Domain().length(2),
569 "FFT::PoissonHybrid: dz.size() must equal domain length in z");
570
571#ifdef AMREX_USE_GPU
572 Gpu::DeviceVector<T> d_dz(dz.size());
573 Gpu::htod_memcpy_async(d_dz.data(), dz.data(), dz.size()*sizeof(T));
574 auto const* pdz = d_dz.data();
575#else
576 auto const* pdz = dz.data();
577#endif
578 solve(soln, rhs,
579 fft_poisson_detail::TriA<T>{pdz},
580 fft_poisson_detail::TriC<T>{pdz,int(dz.size())});
581}
582
583template <typename MF>
584void PoissonHybrid<MF>::solve_2d (MF& soln, MF const& rhs)
585{
586 solve(soln, rhs, fft_poisson_detail::Tri_Zero<T>{}, fft_poisson_detail::Tri_Zero<T>{});
587}
588
589template <typename MF>
590template <typename TRIA, typename TRIC>
591void PoissonHybrid<MF>::solve (MF& a_soln, MF const& a_rhs, TRIA const& tria,
592 TRIC const& tric)
593{
594 BL_PROFILE("FFT::PoissonHybrid::solve");
595
596 AMREX_ASSERT(a_soln.is_cell_centered() && a_rhs.is_cell_centered());
597
598#if (AMREX_SPACEDIM < 3)
599 amrex::ignore_unused(a_soln, a_rhs, tria, tric);
600#else
601
602 MF* soln = &a_soln;
603 MF const* rhs = &a_rhs;
604 MF solntmp, rhstmp;
605 if (m_domain_lo != 0) {
606 detail::shift_mfs(m_domain_lo, a_soln, a_rhs, solntmp, rhstmp);
607 soln = &solntmp;
608 rhs = &rhstmp;
609 }
610
611 IntVect const& ng = amrex::elemwiseMin(soln->nGrowVect(), IntVect(1));
612
613 if (m_r2c)
614 {
615 m_r2c->forward(*rhs, m_spmf_c);
616 solve_z(m_spmf_c, tria, tric);
617 m_r2c->backward_doit(m_spmf_c, *soln, ng, m_geom.periodicity());
618 }
619 else
620 {
621 if (m_r2x->m_cy.empty()) { // spectral data is real
622 m_r2x->forward(*rhs, m_spmf_r);
623 solve_z(m_spmf_r, tria, tric);
624 m_r2x->backward(m_spmf_r, *soln, ng, m_geom.periodicity());
625 } else { // spectral data is complex.
626 m_r2x->forward(*rhs, m_spmf_c);
627 solve_z(m_spmf_c, tria, tric);
628 m_r2x->backward(m_spmf_c, *soln, ng, m_geom.periodicity());
629 }
630 }
631
632 detail::fill_physbc(*soln, m_geom, m_bc);
633#endif
634}
635
636template <typename MF>
637template <typename FA, typename TRIA, typename TRIC>
638void PoissonHybrid<MF>::solve_z (FA& spmf, TRIA const& tria, TRIC const& tric)
639{
640 BL_PROFILE("PoissonHybrid::solve_z");
641
642#if (AMREX_SPACEDIM < 3)
643 amrex::ignore_unused(spmf, tria, tric);
644#else
645 auto facx = Math::pi<T>()/T(m_geom.Domain().length(0));
646 auto facy = Math::pi<T>()/T(m_geom.Domain().length(1));
647 if (m_bc[0].first == Boundary::periodic) { facx *= T(2); }
648 if (m_bc[1].first == Boundary::periodic) { facy *= T(2); }
649 auto dxfac = T(2)/T(m_geom.CellSize(0)*m_geom.CellSize(0));
650 auto dyfac = T(2)/T(m_geom.CellSize(1)*m_geom.CellSize(1));
651 auto scale = (m_r2x) ? m_r2x->scalingFactor() : m_r2c->scalingFactor();
652
653 if (m_geom.Domain().length(0) == 1) { dxfac = 0; }
654 if (m_geom.Domain().length(1) == 1) { dyfac = 0; }
655
656 GpuArray<T,AMREX_SPACEDIM-1> offset{T(0),T(0)};
657 for (int idim = 0; idim < AMREX_SPACEDIM-1; ++idim) {
658 if (m_geom.Domain().length(idim) > 1) {
659 if (m_bc[idim].first == Boundary::odd &&
660 m_bc[idim].second == Boundary::odd)
661 {
662 offset[idim] = T(1);
663 }
664 else if ((m_bc[idim].first == Boundary::odd &&
665 m_bc[idim].second == Boundary::even) ||
666 (m_bc[idim].first == Boundary::even &&
667 m_bc[idim].second == Boundary::odd))
668 {
669 offset[idim] = T(0.5);
670 }
671 }
672 }
673
674 if
675#ifndef _WIN32
676 constexpr
677#endif
678 (std::is_same_v<TRIA,fft_poisson_detail::Tri_Zero<T>> &&
679 std::is_same_v<TRIC,fft_poisson_detail::Tri_Zero<T>>) {
680 amrex::ignore_unused(tria,tric);
681#if defined(AMREX_USE_OMP) && !defined(AMREX_USE_GPU)
682#pragma omp parallel
683#endif
684 for (MFIter mfi(spmf,TilingIfNotGPU()); mfi.isValid(); ++mfi)
685 {
686 auto const& spectral = spmf.array(mfi);
687 auto const& box = mfi.tilebox();
688 amrex::ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k)
689 {
690 T a = facx*(T(i)+offset[0]);
691 T b = facy*(T(j)+offset[1]);
692 T k2 = dxfac * (std::cos(a)-T(1))
693 + dyfac * (std::cos(b)-T(1));
694 // Select the denominator instead of guarding the division
695 // (see Poisson::solve): avoids a speculated 0/0.
696 T const k2d = (k2 != T(0)) ? k2 : T(1);
697 spectral(i,j,k) /= k2d;
698 spectral(i,j,k) *= scale;
699 });
700 }
701 } else {
702 bool zlo_neumann = m_bc[2].first == Boundary::even;
703 bool zhi_neumann = m_bc[2].second == Boundary::even;
704 bool is_singular = (offset[0] == T(0)) && (offset[1] == T(0))
705 && zlo_neumann && zhi_neumann;
706
707 auto nz = m_geom.Domain().length(2);
708
709#if defined(AMREX_USE_OMP) && !defined(AMREX_USE_GPU)
710#pragma omp parallel
711#endif
712 for (MFIter mfi(spmf); mfi.isValid(); ++mfi)
713 {
714 auto const& spectral = spmf.array(mfi);
715 auto const& box = mfi.validbox();
716 auto const& xybox = amrex::makeSlab(box, 2, 0);
717
718#ifdef AMREX_USE_GPU
719 // xxxxx TODO: We need to explore how to optimize this
720 // function. Maybe we can use cusparse. Maybe we should make
721 // z-direction to be the unit stride direction.
722
723 FArrayBox tridiag_workspace(box,4);
724 auto const& ald = tridiag_workspace.array(0);
725 auto const& bd = tridiag_workspace.array(1);
726 auto const& cud = tridiag_workspace.array(2);
727 auto const& scratch = tridiag_workspace.array(3);
728
729 amrex::ParallelFor(xybox, [=] AMREX_GPU_DEVICE (int i, int j, int)
730 {
731 T a = facx*(T(i)+offset[0]);
732 T b = facy*(T(j)+offset[1]);
733 T k2 = dxfac * (std::cos(a)-T(1))
734 + dyfac * (std::cos(b)-T(1));
735
736 // Tridiagonal solve
737 for(int k=0; k < nz; k++) {
738 if(k==0) {
739 ald(i,j,k) = T(0.);
740 cud(i,j,k) = tric(i,j,k);
741 if (zlo_neumann) {
742 bd(i,j,k) = k2 - cud(i,j,k);
743 } else {
744 bd(i,j,k) = k2 - cud(i,j,k) - T(2.0)*tria(i,j,k);
745 }
746 } else if (k == nz-1) {
747 ald(i,j,k) = tria(i,j,k);
748 cud(i,j,k) = T(0.);
749 if (zhi_neumann) {
750 bd(i,j,k) = k2 - ald(i,j,k);
751 if (i == 0 && j == 0 && is_singular) {
752 bd(i,j,k) *= T(2.0);
753 }
754 } else {
755 bd(i,j,k) = k2 - ald(i,j,k) - T(2.0)*tric(i,j,k);
756 }
757 } else {
758 ald(i,j,k) = tria(i,j,k);
759 cud(i,j,k) = tric(i,j,k);
760 bd(i,j,k) = k2 -ald(i,j,k)-cud(i,j,k);
761 }
762 }
763
764 scratch(i,j,0) = cud(i,j,0)/bd(i,j,0);
765 spectral(i,j,0) = spectral(i,j,0)/bd(i,j,0);
766
767 for (int k = 1; k < nz; k++) {
768 if (k < nz-1) {
769 scratch(i,j,k) = cud(i,j,k) / (bd(i,j,k) - ald(i,j,k) * scratch(i,j,k-1));
770 }
771 spectral(i,j,k) = (spectral(i,j,k) - ald(i,j,k) * spectral(i,j,k - 1))
772 / (bd(i,j,k) - ald(i,j,k) * scratch(i,j,k-1));
773 }
774
775 for (int k = nz - 2; k >= 0; k--) {
776 spectral(i,j,k) -= scratch(i,j,k) * spectral(i,j,k + 1);
777 }
778
779 for (int k = 0; k < nz; ++k) {
780 spectral(i,j,k) *= scale;
781 }
782 });
784
785#else
786
787 Gpu::DeviceVector<T> ald(nz);
789 Gpu::DeviceVector<T> cud(nz);
790 Gpu::DeviceVector<T> scratch(nz);
791
792 amrex::LoopOnCpu(xybox, [&] (int i, int j, int)
793 {
794 T a = facx*(T(i)+offset[0]);
795 T b = facy*(T(j)+offset[1]);
796 T k2 = dxfac * (std::cos(a)-T(1))
797 + dyfac * (std::cos(b)-T(1));
798
799 // Tridiagonal solve
800 for(int k=0; k < nz; k++) {
801 if(k==0) {
802 ald[k] = T(0.);
803 cud[k] = tric(i,j,k);
804 if (zlo_neumann) {
805 bd[k] = k2 - cud[k];
806 } else {
807 bd[k] = k2 - cud[k] - T(2.0)*tria(i,j,k);
808 }
809 } else if (k == nz-1) {
810 ald[k] = tria(i,j,k);
811 cud[k] = T(0.);
812 if (zhi_neumann) {
813 bd[k] = k2 - ald[k];
814 if (i == 0 && j == 0 && is_singular) {
815 bd[k] *= T(2.0);
816 }
817 } else {
818 bd[k] = k2 - ald[k] - T(2.0)*tric(i,j,k);
819 }
820 } else {
821 ald[k] = tria(i,j,k);
822 cud[k] = tric(i,j,k);
823 bd[k] = k2 -ald[k]-cud[k];
824 }
825 }
826
827 scratch[0] = cud[0]/bd[0];
828 spectral(i,j,0) = spectral(i,j,0)/bd[0];
829
830 for (int k = 1; k < nz; k++) {
831 if (k < nz-1) {
832 scratch[k] = cud[k] / (bd[k] - ald[k] * scratch[k-1]);
833 }
834 spectral(i,j,k) = (spectral(i,j,k) - ald[k] * spectral(i,j,k - 1))
835 / (bd[k] - ald[k] * scratch[k-1]);
836 }
837
838 for (int k = nz - 2; k >= 0; k--) {
839 spectral(i,j,k) -= scratch[k] * spectral(i,j,k + 1);
840 }
841
842 for (int k = 0; k < nz; ++k) {
843 spectral(i,j,k) *= scale;
844 }
845 });
846#endif
847 }
848 }
849#endif
850}
851
853namespace detail {
854
855template <class T>
856struct FFTPhysBCTag {
857 Array4<T> dfab;
858 Box dbox;
859 Boundary bc;
860 Orientation face;
861
863 Box const& box () const noexcept { return dbox; }
864};
865
866template <typename MF>
867void fill_physbc (MF& mf, Geometry const& geom,
868 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> const& bc)
869{
870 using T = typename MF::value_type;
871 using Tag = FFTPhysBCTag<T>;
872 Vector<Tag> tags;
873
874 for (MFIter mfi(mf, MFItInfo{}.DisableDeviceSync()); mfi.isValid(); ++mfi)
875 {
876 auto const& box = mfi.fabbox();
877 auto const& arr = mf.array(mfi);
878 for (OrientationIter oit; oit; ++oit) {
879 Orientation face = oit();
880 int idim = face.coordDir();
881 Box b = geom.Domain();
882 Boundary fbc;
883 if (face.isLow()) {
884 b.setRange(idim,geom.Domain().smallEnd(idim)-1);
885 fbc = bc[idim].first;
886 } else {
887 b.setRange(idim,geom.Domain().bigEnd(idim)+1);
888 fbc = bc[idim].second;
889 }
890 b &= box;
891 if (b.ok() && fbc != Boundary::periodic) {
892 tags.push_back(Tag{.dfab = arr, .dbox = b, .bc = fbc, .face = face});
893 }
894 }
895 }
896
897#if defined(AMREX_USE_GPU)
898 amrex::ParallelFor(tags, [=] AMREX_GPU_DEVICE (int i, int j, int k,
899 Tag const& tag) noexcept
900#else
901 auto ntags = int(tags.size());
902#ifdef AMREX_USE_OMP
903#pragma omp parallel for
904#endif
905 for (int itag = 0; itag < ntags; ++itag) {
906 Tag const& tag = tags[itag];
907 amrex::LoopOnCpu(tag.dbox, [&] (int i, int j, int k)
908#endif
909 {
910 int sgn = tag.face.isLow() ? 1 : -1;
911 IntVect siv = IntVect(AMREX_D_DECL(i,j,k))
912 + sgn * IntVect::TheDimensionVector(tag.face.coordDir());
913 if (tag.bc == Boundary::odd) {
914 tag.dfab(i,j,k) = -tag.dfab(siv);
915 } else { // even
916 tag.dfab(i,j,k) = tag.dfab(siv);
917 }
918 });
919#if !defined(AMREX_USE_GPU)
920 }
921#endif
922}
923}
925
926}
927
928#endif
#define BL_PROFILE(a)
Definition AMReX_BLProfiler.H:562
#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
Array4< int const > offset
Definition AMReX_HypreMLABecLap.cpp:1139
#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
const Real * CellSize() const noexcept
Returns the cellsize for each coordinate direction.
Definition AMReX_CoordSys.H:79
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
IntVect const & PaddedLength() const
Access the one-sided padded length used to build the internal FFT domain.
Definition AMReX_FFT_OpenBCSolver.H:68
3D Poisson solver for periodic, Dirichlet & Neumann boundaries in the first two dimensions,...
Definition AMReX_FFT_Poisson.H:164
void solve(MF &soln, MF const &rhs)
Solve del dot grad soln = rhs for uniform spacing in all directions.
Definition AMReX_FFT_Poisson.H:542
typename MF::value_type T
Definition AMReX_FFT_Poisson.H:166
PoissonHybrid(Geometry const &geom, Array< std::pair< Boundary, Boundary >, 3 > const &bc)
Construct the hybrid Poisson solver (mixed BCs in z with optional nonuniform spacing).
Definition AMReX_FFT_Poisson.H:174
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:638
std::pair< BoxArray, DistributionMapping > getSpectralDataLayout() const
Layout information for spectral storage used by the hybrid solver.
Definition AMReX_FFT_Poisson.H:482
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:584
Poisson solve for Open BC using FFT.
Definition AMReX_FFT_Poisson.H:108
void solve(MF &soln, MF const &rhs)
Solve the open-boundary Poisson problem.
Definition AMReX_FFT_Poisson.H:427
IntVect const & PaddedLength() const
Access the one-sided padded length used by the internal OpenBC solver.
Definition AMReX_FFT_Poisson.H:144
void define_doit()
Initialize the discretized Green's function cache (public for CUDA kernels).
Definition AMReX_FFT_Poisson.H:398
Poisson solver for periodic, Dirichlet & Neumann boundaries using FFT.
Definition AMReX_FFT_Poisson.H:32
Poisson(Geometry const &geom, Array< std::pair< Boundary, Boundary >, 3 > const &bc)
Construct a Poisson solver with explicit boundary types.
Definition AMReX_FFT_Poisson.H:41
Poisson(Geometry const &geom)
Construct a purely periodic Poisson solver.
Definition AMReX_FFT_Poisson.H:66
void solve(MF &a_soln, MF const &a_rhs)
Solve del dot grad soln = rhs.
Definition AMReX_FFT_Poisson.H:291
An Array of FortranArrayBox(FAB)-like Objects.
Definition AMReX_FabArray.H:356
Rectangular problem domain geometry.
Definition AMReX_Geometry.H:85
const Box & Domain() const noexcept
Returns our rectangular domain.
Definition AMReX_Geometry.H:244
Periodicity periodicity() const noexcept
Return the Periodicity based on the length of the domain.
Definition AMReX_Geometry.H:424
bool isAllPeriodic() const noexcept
Is domain periodic in all directions?
Definition AMReX_Geometry.H:404
__host__ static __device__ constexpr IndexTypeND< dim > TheCellType() noexcept
This static member function returns an IndexTypeND object of value IndexTypeND::CELL....
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
Dynamically allocated vector for trivially copyable data.
Definition AMReX_PODVector.H:308
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
__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:1630
__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:1326
std::array< T, N > Array
Definition AMReX_Array.H:31
__host__ __device__ constexpr T elemwiseMin(T const &a, T const &b) noexcept
Definition AMReX_Algorithm.H:73
Definition AMReX_FFT_Helper.H:53
Boundary
Definition AMReX_FFT_Helper.H:59
void streamSynchronize() noexcept
Definition AMReX_GpuDevice.H:310
void htod_memcpy_async(void *p_d, const void *p_h, const std::size_t sz) noexcept
Definition AMReX_GpuDevice.H:421
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:2436
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
IntVectND< 3 > IntVect
IntVect is an alias for amrex::IntVectND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:38
__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
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
A multidimensional array accessor.
Definition AMReX_Array4.H:289
Definition AMReX_FFT_Helper.H:83
bool openbc_padding
Whether OpenBCSolver pads internal FFT lengths for better performance.
Definition AMReX_FFT_Helper.H:112
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:149
int openbc_padding_nfactors
Definition AMReX_FFT_Helper.H:116
Info & setTwoDMode(bool x)
Restrict transforms to the first two dimensions (3-D problems only).
Definition AMReX_FFT_Helper.H:138
Fixed-size array that can be used on GPU.
Definition AMReX_Array.H:52