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
11namespace detail {
12template <typename MF>
13void fill_physbc (MF& mf, Geometry const& geom,
14 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> const& bc);
15
16inline Geometry shift_geom (Geometry const& geom)
17{
18 auto const& dom = geom.Domain();
19 auto const& dlo = dom.smallEnd();
20 if (dlo == 0) {
21 return geom;
22 } else {
23 return Geometry(amrex::shift(dom,-dlo), geom.ProbDomain(), geom.CoordInt(),
24 geom.isPeriodic());
25 }
26}
27
28template <typename MF>
29void shift_mfs (IntVect const& domain_lo, MF const& mf1, MF const& mf2,
30 MF& new_mf1, MF& new_mf2)
31{
32 AMREX_ALWAYS_ASSERT(mf1.boxArray() == mf2.boxArray() &&
33 mf1.DistributionMap() == mf2.DistributionMap());
34 BoxArray ba = mf1.boxArray();
35 ba.shift(-domain_lo);
36 new_mf1.define(ba, mf1.DistributionMap(), 1, mf1.nGrowVect(),
37 MFInfo().SetAlloc(false));
38 new_mf2.define(ba, mf2.DistributionMap(), 1, mf2.nGrowVect(),
39 MFInfo().SetAlloc(false));
40 for (MFIter mfi(mf1); mfi.isValid(); ++mfi) {
41 typename MF::fab_type fab1(mf1[mfi], amrex::make_alias, 0, 1);
42 fab1.shift(-domain_lo);
43 new_mf1.setFab(mfi, std::move(fab1));
44
45 typename MF::fab_type fab2(mf2[mfi], amrex::make_alias, 0, 1);
46 fab2.shift(-domain_lo);
47 new_mf2.setFab(mfi, std::move(fab2));
48 }
49}
50
51}
53
59template <typename MF = MultiFab>
61{
62public:
63
64 template <typename FA=MF, std::enable_if_t<IsFabArray_v<FA>,int> = 0>
65 Poisson (Geometry const& geom,
66 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> const& bc)
67 : m_domain_lo(geom.Domain().smallEnd()),
68 m_geom(detail::shift_geom(geom)),
69 m_bc(bc)
70 {
71 bool all_periodic = true;
72 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
73 all_periodic = all_periodic
74 && (bc[idim].first == Boundary::periodic)
75 && (bc[idim].second == Boundary::periodic);
76 }
77 if (all_periodic) {
78 m_r2c = std::make_unique<R2C<typename MF::value_type>>(m_geom.Domain());
79 } else {
80 m_r2x = std::make_unique<R2X<typename MF::value_type>> (m_geom.Domain(), m_bc);
81 }
82 }
83
84 template <typename FA=MF, std::enable_if_t<IsFabArray_v<FA>,int> = 0>
85 explicit Poisson (Geometry const& geom)
86 : m_domain_lo(geom.Domain().smallEnd()),
87 m_geom(detail::shift_geom(geom)),
88 m_bc{AMREX_D_DECL(std::make_pair(Boundary::periodic,Boundary::periodic),
89 std::make_pair(Boundary::periodic,Boundary::periodic),
90 std::make_pair(Boundary::periodic,Boundary::periodic))}
91 {
92 if (m_geom.isAllPeriodic()) {
93 m_r2c = std::make_unique<R2C<typename MF::value_type>>(m_geom.Domain());
94 } else {
95 amrex::Abort("FFT::Poisson: wrong BC");
96 }
97 }
98
99 /*
100 * \brief Solve del dot grad soln = rhs
101 *
102 * If soln has ghost cells, one layer of ghost cells will be filled
103 * except for the corners of the physical domain where they are not used
104 * in the cross stencil of the operator. The two MFs could be the same MF.
105 */
106 void solve (MF& a_soln, MF const& a_rhs);
107
108private:
109 IntVect m_domain_lo;
110 Geometry m_geom;
111 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> m_bc;
112 std::unique_ptr<R2X<typename MF::value_type>> m_r2x;
113 std::unique_ptr<R2C<typename MF::value_type>> m_r2c;
114};
115
116#if (AMREX_SPACEDIM == 3)
121template <typename MF = MultiFab>
123{
124public:
125
126 template <typename FA=MF, std::enable_if_t<IsFabArray_v<FA>,int> = 0>
127 explicit PoissonOpenBC (Geometry const& geom,
129 IntVect const& ngrow = IntVect(0));
130
131 void solve (MF& soln, MF const& rhs);
132
133 void define_doit (); // has to be public for cuda
134
135private:
136 Geometry m_geom;
137 Box m_grown_domain;
138 IntVect m_ngrow;
140};
141#endif
142
149template <typename MF = MultiFab>
151{
152public:
153 using T = typename MF::value_type;
154
155 template <typename FA=MF, std::enable_if_t<IsFabArray_v<FA>,int> = 0>
157 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> const& bc)
158 : m_domain_lo(geom.Domain().smallEnd()),
159 m_geom(detail::shift_geom(geom)),
160 m_bc(bc)
161 {
162#if (AMREX_SPACEDIM < 3)
163 amrex::Abort("FFT::PoissonHybrid: 1D & 2D todo");
164 return;
165#endif
166 bool periodic_xy = true;
167 for (int idim = 0; idim < 2; ++idim) {
168 if (m_geom.Domain().length(idim) > 1) {
169 periodic_xy = periodic_xy && (bc[idim].first == Boundary::periodic);
170 AMREX_ALWAYS_ASSERT((bc[idim].first == Boundary::periodic &&
171 bc[idim].second == Boundary::periodic) ||
172 (bc[idim].first != Boundary::periodic &&
173 bc[idim].second != Boundary::periodic));
174 }
175 }
177 bc[2].second != Boundary::periodic);
178 Info info{};
179 info.setTwoDMode(true);
180 if (m_geom.Domain().length(0) == 1 || m_geom.Domain().length(1) == 1) {
181 info.setOneDMode(true);
182 }
183 if (periodic_xy) {
184 m_r2c = std::make_unique<R2C<typename MF::value_type>>(m_geom.Domain(),
185 info);
186 } else {
187 m_r2x = std::make_unique<R2X<typename MF::value_type>> (m_geom.Domain(),
188 m_bc, info);
189 }
190 build_spmf();
191 }
192
193 /*
194 * \brief Solve del dot grad soln = rhs
195 *
196 * If soln has ghost cells, one layer of ghost cells will be filled
197 * except for the corners of the physical domain where they are not used
198 * in the cross stencil of the operator. The two MFs could be the same MF.
199 */
200 void solve (MF& soln, MF const& rhs);
201 void solve (MF& soln, MF const& rhs, Vector<T> const& dz);
202 void solve (MF& soln, MF const& rhs, Gpu::DeviceVector<T> const& dz);
203
204 void solve_2d (MF& a_soln, MF const& a_rhs);
205
206 template <typename TRIA, typename TRIC>
207 void solve (MF& a_soln, MF const& a_rhs, TRIA const& tria, TRIC const& tric);
208
209 // This is public for cuda
210 template <typename FA, typename TRIA, typename TRIC>
211 void solve_z (FA& spmf, TRIA const& tria, TRIC const& tric);
212
213 [[nodiscard]] std::pair<BoxArray,DistributionMapping> getSpectralDataLayout () const;
214
215private:
216
217 void build_spmf ();
218
219 IntVect m_domain_lo;
220 Geometry m_geom;
221 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> m_bc;
222 std::unique_ptr<R2X<typename MF::value_type>> m_r2x;
223 std::unique_ptr<R2C<typename MF::value_type>> m_r2c;
224 MF m_spmf_r;
225 using cMF = FabArray<BaseFab<GpuComplex<T>>>;
226 cMF m_spmf_c;
227};
228
229template <typename MF>
230void Poisson<MF>::solve (MF& a_soln, MF const& a_rhs)
231{
232 BL_PROFILE("FFT::Poisson::solve");
233
234 MF* soln = &a_soln;
235 MF const* rhs = &a_rhs;
236 MF solntmp, rhstmp;
237 if (m_domain_lo != 0) {
238 detail::shift_mfs(m_domain_lo, a_soln, a_rhs, solntmp, rhstmp);
239 soln = &solntmp;
240 rhs = &rhstmp;
241 }
242
243 AMREX_ASSERT(soln->is_cell_centered() && rhs->is_cell_centered());
244
245 using T = typename MF::value_type;
246
248 {AMREX_D_DECL(Math::pi<T>()/T(m_geom.Domain().length(0)),
249 Math::pi<T>()/T(m_geom.Domain().length(1)),
250 Math::pi<T>()/T(m_geom.Domain().length(2)))};
251 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
252 if (m_bc[idim].first == Boundary::periodic) {
253 fac[idim] *= T(2);
254 }
255 }
257 {AMREX_D_DECL(T(2)/T(m_geom.CellSize(0)*m_geom.CellSize(0)),
258 T(2)/T(m_geom.CellSize(1)*m_geom.CellSize(1)),
259 T(2)/T(m_geom.CellSize(2)*m_geom.CellSize(2)))};
260 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
261 if (m_geom.Domain().length(idim) == 1) {
262 dxfac[idim] = 0;
263 }
264 }
265 auto scale = (m_r2x) ? m_r2x->scalingFactor() : m_r2c->scalingFactor();
266
268 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
269 if (m_bc[idim].first == Boundary::odd &&
270 m_bc[idim].second == Boundary::odd)
271 {
272 offset[idim] = T(1);
273 }
274 else if ((m_bc[idim].first == Boundary::odd &&
275 m_bc[idim].second == Boundary::even) ||
276 (m_bc[idim].first == Boundary::even &&
277 m_bc[idim].second == Boundary::odd))
278 {
279 offset[idim] = T(0.5);
280 }
281 }
282
283 auto f = [=] AMREX_GPU_DEVICE (int i, int j, int k, auto& spectral_data)
284 {
286 AMREX_D_TERM(T a = fac[0]*(i+offset[0]);,
287 T b = fac[1]*(j+offset[1]);,
288 T c = fac[2]*(k+offset[2]));
289 T k2 = AMREX_D_TERM(dxfac[0]*(std::cos(a)-T(1)),
290 +dxfac[1]*(std::cos(b)-T(1)),
291 +dxfac[2]*(std::cos(c)-T(1)));
292 if (k2 != T(0)) {
293 spectral_data /= k2;
294 }
295 spectral_data *= scale;
296 };
297
298 IntVect const& ng = amrex::elemwiseMin(soln->nGrowVect(), IntVect(1));
299
300 if (m_r2x) {
301 m_r2x->forwardThenBackward_doit_0(*rhs, *soln, f, ng, m_geom.periodicity());
302 detail::fill_physbc(*soln, m_geom, m_bc);
303 } else {
304 m_r2c->forward(*rhs);
305 m_r2c->post_forward_doit_0(f);
306 m_r2c->backward_doit(*soln, ng, m_geom.periodicity());
307 }
308}
309
310#if (AMREX_SPACEDIM == 3)
311
312template <typename MF>
313template <typename FA, std::enable_if_t<IsFabArray_v<FA>,int> FOO>
315 IntVect const& ngrow)
316 : m_geom(geom),
317 m_grown_domain(amrex::grow(amrex::convert(geom.Domain(),ixtype),ngrow)),
318 m_ngrow(ngrow),
319 m_solver(m_grown_domain)
320{
321 define_doit();
322}
323
324template <typename MF>
326{
327 using T = typename MF::value_type;
328 auto const& lo = m_grown_domain.smallEnd();
329 auto const dx = T(m_geom.CellSize(0));
330 auto const dy = T(m_geom.CellSize(1));
331 auto const dz = T(m_geom.CellSize(2));
332 auto const gfac = T(1)/T(std::sqrt(T(12)));
333 // 0.125 comes from that there are 8 Gauss quadrature points
334 auto const fac = T(-0.125) * (dx*dy*dz) / (T(4)*Math::pi<T>());
335 m_solver.setGreensFunction([=] AMREX_GPU_DEVICE (int i, int j, int k) -> T
336 {
337 auto x = (T(i-lo[0]) - gfac) * dx; // first Gauss quadrature point
338 auto y = (T(j-lo[1]) - gfac) * dy;
339 auto z = (T(k-lo[2]) - gfac) * dz;
340 T r = 0;
341 for (int gx = 0; gx < 2; ++gx) {
342 for (int gy = 0; gy < 2; ++gy) {
343 for (int gz = 0; gz < 2; ++gz) {
344 auto xg = x + 2*gx*gfac*dx;
345 auto yg = y + 2*gy*gfac*dy;
346 auto zg = z + 2*gz*gfac*dz;
347 r += T(1)/std::sqrt(xg*xg+yg*yg+zg*zg);
348 }}}
349 return fac * r;
350 });
351}
352
353template <typename MF>
354void PoissonOpenBC<MF>::solve (MF& soln, MF const& rhs)
355{
356 AMREX_ASSERT(m_grown_domain.ixType() == soln.ixType() && m_grown_domain.ixType() == rhs.ixType());
357 m_solver.solve(soln, rhs);
358}
359
360#endif /* AMREX_SPACEDIM == 3 */
361
363namespace fft_poisson_detail {
364 template <typename T>
365 struct Tri_Zero {
366 [[nodiscard]] constexpr T operator() (int, int, int) const
367 {
368 return 0;
369 }
370 };
371
372 template <typename T>
373 struct Tri_Uniform {
375 T operator() (int, int, int) const
376 {
377 return m_dz2inv;
378 }
379 T m_dz2inv;
380 };
381
382 template <typename T>
383 struct TriA {
385 T operator() (int, int, int k) const
386 {
387 return (k > 0) ? T(2.0) / (m_dz[k]*(m_dz[k]+m_dz[k-1]))
388 : T(1.0) / (m_dz[k]* m_dz[k]);
389 }
390 T const* m_dz;
391 };
392
393 template <typename T>
394 struct TriC {
396 T operator() (int, int, int k) const
397 {
398 return (k < m_size-1) ? T(2.0) / (m_dz[k]*(m_dz[k]+m_dz[k+1]))
399 : T(1.0) / (m_dz[k]* m_dz[k]);
400 }
401 T const* m_dz;
402 int m_size;
403 };
404}
406
407template <typename MF>
408std::pair<BoxArray,DistributionMapping>
410{
411 if (!m_spmf_r.empty()) {
412 return std::make_pair(m_spmf_r.boxArray(), m_spmf_r.DistributionMap());
413 } else {
414 return std::make_pair(m_spmf_c.boxArray(), m_spmf_c.DistributionMap());
415 }
416}
417
418template <typename MF>
420{
421#if (AMREX_SPACEDIM == 3)
422 AMREX_ALWAYS_ASSERT(m_geom.Domain().length(2) > 1 &&
423 (m_geom.Domain().length(0) > 1 ||
424 m_geom.Domain().length(1) > 1));
425
426 if (m_r2c) {
427 Box cdomain = m_geom.Domain();
428 if (cdomain.length(0) > 1) {
429 cdomain.setBig(0,cdomain.length(0)/2);
430 } else {
431 cdomain.setBig(1,cdomain.length(1)/2);
432 }
433 auto cba = amrex::decompose(cdomain, ParallelContext::NProcsSub(),
434 {AMREX_D_DECL(true,true,false)});
435 DistributionMapping dm = detail::make_iota_distromap(cba.size());
436 m_spmf_c.define(cba, dm, 1, 0);
437 } else if (m_geom.Domain().length(0) > 1 &&
438 m_geom.Domain().length(1) > 1) {
439 if (m_r2x->m_cy.empty()) { // spectral data is real
440 auto sba = amrex::decompose(m_geom.Domain(),ParallelContext::NProcsSub(),
441 {AMREX_D_DECL(true,true,false)});
442 DistributionMapping dm = detail::make_iota_distromap(sba.size());
443 m_spmf_r.define(sba, dm, 1, 0);
444 } else { // spectral data is complex. one of the first two dimensions is periodic.
445 Box cdomain = m_geom.Domain();
446 if (m_bc[0].first == Boundary::periodic) {
447 cdomain.setBig(0,cdomain.length(0)/2);
448 } else {
449 cdomain.setBig(1,cdomain.length(1)/2);
450 }
451 auto cba = amrex::decompose(cdomain, ParallelContext::NProcsSub(),
452 {AMREX_D_DECL(true,true,false)});
453 DistributionMapping dm = detail::make_iota_distromap(cba.size());
454 m_spmf_c.define(cba, dm, 1, 0);
455 }
456 } else {
457 // spectral data is real
458 auto sba = amrex::decompose(m_geom.Domain(),ParallelContext::NProcsSub(),
459 {AMREX_D_DECL(true,true,false)});
460 DistributionMapping dm = detail::make_iota_distromap(sba.size());
461 m_spmf_r.define(sba, dm, 1, 0);
462 }
463#else
465#endif
466}
467
468template <typename MF>
469void PoissonHybrid<MF>::solve (MF& soln, MF const& rhs)
470{
471 auto delz = T(m_geom.CellSize(AMREX_SPACEDIM-1));
472 solve(soln, rhs,
473 fft_poisson_detail::Tri_Uniform<T>{T(1)/(delz*delz)},
474 fft_poisson_detail::Tri_Uniform<T>{T(1)/(delz*delz)});
475}
476
477template <typename MF>
478void PoissonHybrid<MF>::solve (MF& soln, MF const& rhs, Gpu::DeviceVector<T> const& dz)
479{
480 auto const* pdz = dz.dataPtr();
481 solve(soln, rhs,
482 fft_poisson_detail::TriA<T>{pdz},
483 fft_poisson_detail::TriC<T>{pdz,int(dz.size())});
484}
485
486template <typename MF>
487void PoissonHybrid<MF>::solve (MF& soln, MF const& rhs, Vector<T> const& dz)
488{
489 AMREX_ASSERT(soln.is_cell_centered() && rhs.is_cell_centered());
490
491#ifdef AMREX_USE_GPU
492 Gpu::DeviceVector<T> d_dz(dz.size());
493 Gpu::htod_memcpy_async(d_dz.data(), dz.data(), dz.size()*sizeof(T));
494 auto const* pdz = d_dz.data();
495#else
496 auto const* pdz = dz.data();
497#endif
498 solve(soln, rhs,
499 fft_poisson_detail::TriA<T>{pdz},
500 fft_poisson_detail::TriC<T>{pdz,int(dz.size())});
501}
502
503template <typename MF>
504void PoissonHybrid<MF>::solve_2d (MF& soln, MF const& rhs)
505{
506 solve(soln, rhs, fft_poisson_detail::Tri_Zero<T>{}, fft_poisson_detail::Tri_Zero<T>{});
507}
508
509template <typename MF>
510template <typename TRIA, typename TRIC>
511void PoissonHybrid<MF>::solve (MF& a_soln, MF const& a_rhs, TRIA const& tria,
512 TRIC const& tric)
513{
514 BL_PROFILE("FFT::PoissonHybrid::solve");
515
516 AMREX_ASSERT(a_soln.is_cell_centered() && a_rhs.is_cell_centered());
517
518#if (AMREX_SPACEDIM < 3)
519 amrex::ignore_unused(a_soln, a_rhs, tria, tric);
520#else
521
522 MF* soln = &a_soln;
523 MF const* rhs = &a_rhs;
524 MF solntmp, rhstmp;
525 if (m_domain_lo != 0) {
526 detail::shift_mfs(m_domain_lo, a_soln, a_rhs, solntmp, rhstmp);
527 soln = &solntmp;
528 rhs = &rhstmp;
529 }
530
531 IntVect const& ng = amrex::elemwiseMin(soln->nGrowVect(), IntVect(1));
532
533 if (m_r2c)
534 {
535 m_r2c->forward(*rhs, m_spmf_c);
536 solve_z(m_spmf_c, tria, tric);
537 m_r2c->backward_doit(m_spmf_c, *soln, ng, m_geom.periodicity());
538 }
539 else
540 {
541 if (m_r2x->m_cy.empty()) { // spectral data is real
542 m_r2x->forward(*rhs, m_spmf_r);
543 solve_z(m_spmf_r, tria, tric);
544 m_r2x->backward(m_spmf_r, *soln, ng, m_geom.periodicity());
545 } else { // spectral data is complex.
546 m_r2x->forward(*rhs, m_spmf_c);
547 solve_z(m_spmf_c, tria, tric);
548 m_r2x->backward(m_spmf_c, *soln, ng, m_geom.periodicity());
549 }
550 }
551
552 detail::fill_physbc(*soln, m_geom, m_bc);
553#endif
554}
555
556template <typename MF>
557template <typename FA, typename TRIA, typename TRIC>
558void PoissonHybrid<MF>::solve_z (FA& spmf, TRIA const& tria, TRIC const& tric)
559{
560 BL_PROFILE("PoissonHybrid::solve_z");
561
562#if (AMREX_SPACEDIM < 3)
563 amrex::ignore_unused(spmf, tria, tric);
564#else
565 auto facx = Math::pi<T>()/T(m_geom.Domain().length(0));
566 auto facy = Math::pi<T>()/T(m_geom.Domain().length(1));
567 if (m_bc[0].first == Boundary::periodic) { facx *= T(2); }
568 if (m_bc[1].first == Boundary::periodic) { facy *= T(2); }
569 auto dxfac = T(2)/T(m_geom.CellSize(0)*m_geom.CellSize(0));
570 auto dyfac = T(2)/T(m_geom.CellSize(1)*m_geom.CellSize(1));
571 auto scale = (m_r2x) ? m_r2x->scalingFactor() : m_r2c->scalingFactor();
572
573 if (m_geom.Domain().length(0) == 1) { dxfac = 0; }
574 if (m_geom.Domain().length(1) == 1) { dyfac = 0; }
575
576 GpuArray<T,AMREX_SPACEDIM-1> offset{T(0),T(0)};
577 for (int idim = 0; idim < AMREX_SPACEDIM-1; ++idim) {
578 if (m_geom.Domain().length(idim) > 1) {
579 if (m_bc[idim].first == Boundary::odd &&
580 m_bc[idim].second == Boundary::odd)
581 {
582 offset[idim] = T(1);
583 }
584 else if ((m_bc[idim].first == Boundary::odd &&
585 m_bc[idim].second == Boundary::even) ||
586 (m_bc[idim].first == Boundary::even &&
587 m_bc[idim].second == Boundary::odd))
588 {
589 offset[idim] = T(0.5);
590 }
591 }
592 }
593
594 if
595#ifndef _WIN32
596 constexpr
597#endif
598 (std::is_same_v<TRIA,fft_poisson_detail::Tri_Zero<T>> &&
599 std::is_same_v<TRIC,fft_poisson_detail::Tri_Zero<T>>) {
600 amrex::ignore_unused(tria,tric);
601#if defined(AMREX_USE_OMP) && !defined(AMREX_USE_GPU)
602#pragma omp parallel
603#endif
604 for (MFIter mfi(spmf,TilingIfNotGPU()); mfi.isValid(); ++mfi)
605 {
606 auto const& spectral = spmf.array(mfi);
607 auto const& box = mfi.validbox();
608 amrex::ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k)
609 {
610 T a = facx*(i+offset[0]);
611 T b = facy*(j+offset[1]);
612 T k2 = dxfac * (std::cos(a)-T(1))
613 + dyfac * (std::cos(b)-T(1));
614 if (k2 != T(0)) {
615 spectral(i,j,k) /= k2;
616 }
617 spectral(i,j,k) *= scale;
618 });
619 }
620 } else {
621 bool zlo_neumann = m_bc[2].first == Boundary::even;
622 bool zhi_neumann = m_bc[2].second == Boundary::even;
623 bool is_singular = (offset[0] == T(0)) && (offset[1] == T(0))
624 && zlo_neumann && zhi_neumann;
625
626 auto nz = m_geom.Domain().length(2);
627
628#if defined(AMREX_USE_OMP) && !defined(AMREX_USE_GPU)
629#pragma omp parallel
630#endif
631 for (MFIter mfi(spmf); mfi.isValid(); ++mfi)
632 {
633 auto const& spectral = spmf.array(mfi);
634 auto const& box = mfi.validbox();
635 auto const& xybox = amrex::makeSlab(box, 2, 0);
636
637#ifdef AMREX_USE_GPU
638 // xxxxx TODO: We need to explore how to optimize this
639 // function. Maybe we can use cusparse. Maybe we should make
640 // z-direction to be the unit stride direction.
641
642 FArrayBox tridiag_workspace(box,4);
643 auto const& ald = tridiag_workspace.array(0);
644 auto const& bd = tridiag_workspace.array(1);
645 auto const& cud = tridiag_workspace.array(2);
646 auto const& scratch = tridiag_workspace.array(3);
647
648 amrex::ParallelFor(xybox, [=] AMREX_GPU_DEVICE (int i, int j, int)
649 {
650 T a = facx*(i+offset[0]);
651 T b = facy*(j+offset[1]);
652 T k2 = dxfac * (std::cos(a)-T(1))
653 + dyfac * (std::cos(b)-T(1));
654
655 // Tridiagonal solve
656 for(int k=0; k < nz; k++) {
657 if(k==0) {
658 ald(i,j,k) = T(0.);
659 cud(i,j,k) = tric(i,j,k);
660 if (zlo_neumann) {
661 bd(i,j,k) = k2 - cud(i,j,k);
662 } else {
663 bd(i,j,k) = k2 - cud(i,j,k) - T(2.0)*tria(i,j,k);
664 }
665 } else if (k == nz-1) {
666 ald(i,j,k) = tria(i,j,k);
667 cud(i,j,k) = T(0.);
668 if (zhi_neumann) {
669 bd(i,j,k) = k2 - ald(i,j,k);
670 if (i == 0 && j == 0 && is_singular) {
671 bd(i,j,k) *= T(2.0);
672 }
673 } else {
674 bd(i,j,k) = k2 - ald(i,j,k) - T(2.0)*tric(i,j,k);
675 }
676 } else {
677 ald(i,j,k) = tria(i,j,k);
678 cud(i,j,k) = tric(i,j,k);
679 bd(i,j,k) = k2 -ald(i,j,k)-cud(i,j,k);
680 }
681 }
682
683 scratch(i,j,0) = cud(i,j,0)/bd(i,j,0);
684 spectral(i,j,0) = spectral(i,j,0)/bd(i,j,0);
685
686 for (int k = 1; k < nz; k++) {
687 if (k < nz-1) {
688 scratch(i,j,k) = cud(i,j,k) / (bd(i,j,k) - ald(i,j,k) * scratch(i,j,k-1));
689 }
690 spectral(i,j,k) = (spectral(i,j,k) - ald(i,j,k) * spectral(i,j,k - 1))
691 / (bd(i,j,k) - ald(i,j,k) * scratch(i,j,k-1));
692 }
693
694 for (int k = nz - 2; k >= 0; k--) {
695 spectral(i,j,k) -= scratch(i,j,k) * spectral(i,j,k + 1);
696 }
697
698 for (int k = 0; k < nz; ++k) {
699 spectral(i,j,k) *= scale;
700 }
701 });
703
704#else
705
706 Gpu::DeviceVector<T> ald(nz);
708 Gpu::DeviceVector<T> cud(nz);
709 Gpu::DeviceVector<T> scratch(nz);
710
711 amrex::LoopOnCpu(xybox, [&] (int i, int j, int)
712 {
713 T a = facx*(i+offset[0]);
714 T b = facy*(j+offset[1]);
715 T k2 = dxfac * (std::cos(a)-T(1))
716 + dyfac * (std::cos(b)-T(1));
717
718 // Tridiagonal solve
719 for(int k=0; k < nz; k++) {
720 if(k==0) {
721 ald[k] = T(0.);
722 cud[k] = tric(i,j,k);
723 if (zlo_neumann) {
724 bd[k] = k2 - cud[k];
725 } else {
726 bd[k] = k2 - cud[k] - T(2.0)*tria(i,j,k);
727 }
728 } else if (k == nz-1) {
729 ald[k] = tria(i,j,k);
730 cud[k] = T(0.);
731 if (zhi_neumann) {
732 bd[k] = k2 - ald[k];
733 if (i == 0 && j == 0 && is_singular) {
734 bd[k] *= T(2.0);
735 }
736 } else {
737 bd[k] = k2 - ald[k] - T(2.0)*tric(i,j,k);
738 }
739 } else {
740 ald[k] = tria(i,j,k);
741 cud[k] = tric(i,j,k);
742 bd[k] = k2 -ald[k]-cud[k];
743 }
744 }
745
746 scratch[0] = cud[0]/bd[0];
747 spectral(i,j,0) = spectral(i,j,0)/bd[0];
748
749 for (int k = 1; k < nz; k++) {
750 if (k < nz-1) {
751 scratch[k] = cud[k] / (bd[k] - ald[k] * scratch[k-1]);
752 }
753 spectral(i,j,k) = (spectral(i,j,k) - ald[k] * spectral(i,j,k - 1))
754 / (bd[k] - ald[k] * scratch[k-1]);
755 }
756
757 for (int k = nz - 2; k >= 0; k--) {
758 spectral(i,j,k) -= scratch[k] * spectral(i,j,k + 1);
759 }
760
761 for (int k = 0; k < nz; ++k) {
762 spectral(i,j,k) *= scale;
763 }
764 });
765#endif
766 }
767 }
768#endif
769}
770
772namespace detail {
773
774template <class T>
775struct FFTPhysBCTag {
776 Array4<T> dfab;
777 Box dbox;
778 Boundary bc;
779 Orientation face;
780
782 Box const& box () const noexcept { return dbox; }
783};
784
785template <typename MF>
786void fill_physbc (MF& mf, Geometry const& geom,
787 Array<std::pair<Boundary,Boundary>,AMREX_SPACEDIM> const& bc)
788{
789 using T = typename MF::value_type;
790 using Tag = FFTPhysBCTag<T>;
791 Vector<Tag> tags;
792
793 for (MFIter mfi(mf, MFItInfo{}.DisableDeviceSync()); mfi.isValid(); ++mfi)
794 {
795 auto const& box = mfi.fabbox();
796 auto const& arr = mf.array(mfi);
797 for (OrientationIter oit; oit; ++oit) {
798 Orientation face = oit();
799 int idim = face.coordDir();
800 Box b = geom.Domain();
801 Boundary fbc;
802 if (face.isLow()) {
803 b.setRange(idim,geom.Domain().smallEnd(idim)-1);
804 fbc = bc[idim].first;
805 } else {
806 b.setRange(idim,geom.Domain().bigEnd(idim)+1);
807 fbc = bc[idim].second;
808 }
809 b &= box;
810 if (b.ok() && fbc != Boundary::periodic) {
811 tags.push_back({arr, b, fbc, face});
812 }
813 }
814 }
815
816#if defined(AMREX_USE_GPU)
817 amrex::ParallelFor(tags, [=] AMREX_GPU_DEVICE (int i, int j, int k,
818 Tag const& tag) noexcept
819#else
820 auto ntags = int(tags.size());
821#ifdef AMREX_USE_OMP
822#pragma omp parallel for
823#endif
824 for (int itag = 0; itag < ntags; ++itag) {
825 Tag const& tag = tags[itag];
826 amrex::LoopOnCpu(tag.dbox, [&] (int i, int j, int k)
827#endif
828 {
829 int sgn = tag.face.isLow() ? 1 : -1;
830 IntVect siv = IntVect(AMREX_D_DECL(i,j,k))
831 + sgn * IntVect::TheDimensionVector(tag.face.coordDir());
832 if (tag.bc == Boundary::odd) {
833 tag.dfab(i,j,k) = -tag.dfab(siv);
834 } else { // even
835 tag.dfab(i,j,k) = tag.dfab(siv);
836 }
837 });
838#if !defined(AMREX_USE_GPU)
839 }
840#endif
841}
842}
844
845}
846
847#endif
#define BL_PROFILE(a)
Definition AMReX_BLProfiler.H:551
#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:119
#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:1089
#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
Definition AMReX_BaseFab.H:381
__host__ __device__ BoxND & setBig(const IntVectND< dim > &bg) noexcept
Redefine the big end of the BoxND.
Definition AMReX_Box.H:487
__host__ __device__ IntVectND< dim > length() const noexcept
Return the length of the BoxND.
Definition AMReX_Box.H:154
A Fortran Array of REALs.
Definition AMReX_FArrayBox.H:231
Definition AMReX_FFT_OpenBCSolver.H:11
3D Poisson solver for periodic, Dirichlet & Neumann boundaries in the first two dimensions,...
Definition AMReX_FFT_Poisson.H:151
void solve(MF &soln, MF const &rhs)
Definition AMReX_FFT_Poisson.H:469
typename MF::value_type T
Definition AMReX_FFT_Poisson.H:153
void solve_z(FA &spmf, TRIA const &tria, TRIC const &tric)
Definition AMReX_FFT_Poisson.H:558
PoissonHybrid(Geometry const &geom, Array< std::pair< Boundary, Boundary >, 3 > const &bc)
Definition AMReX_FFT_Poisson.H:156
std::pair< BoxArray, DistributionMapping > getSpectralDataLayout() const
Definition AMReX_FFT_Poisson.H:409
void solve_2d(MF &a_soln, MF const &a_rhs)
Definition AMReX_FFT_Poisson.H:504
Poisson solve for Open BC using FFT.
Definition AMReX_FFT_Poisson.H:123
void solve(MF &soln, MF const &rhs)
Definition AMReX_FFT_Poisson.H:354
void define_doit()
Definition AMReX_FFT_Poisson.H:325
PoissonOpenBC(Geometry const &geom, IndexType ixtype=IndexType::TheCellType(), IntVect const &ngrow=IntVect(0))
Definition AMReX_FFT_Poisson.H:314
Poisson solver for periodic, Dirichlet & Neumann boundaries using FFT.
Definition AMReX_FFT_Poisson.H:61
Poisson(Geometry const &geom, Array< std::pair< Boundary, Boundary >, 3 > const &bc)
Definition AMReX_FFT_Poisson.H:65
void solve(MF &a_soln, MF const &a_rhs)
Definition AMReX_FFT_Poisson.H:230
Poisson(Geometry const &geom)
Definition AMReX_FFT_Poisson.H:85
An Array of FortranArrayBox(FAB)-like Objects.
Definition AMReX_FabArray.H:347
Rectangular problem domain geometry.
Definition AMReX_Geometry.H:74
const Box & Domain() const noexcept
Returns our rectangular domain.
Definition AMReX_Geometry.H:211
bool isAllPeriodic() const noexcept
Is domain periodic in all directions?
Definition AMReX_Geometry.H:339
__host__ static __device__ constexpr IndexTypeND< dim > TheCellType() noexcept
This static member function returns an IndexTypeND object of value IndexTypeND::CELL....
Definition AMReX_IndexType.H:152
Iterator for looping ever tiles and boxes of amrex::FabArray based containers.
Definition AMReX_MFIter.H:63
bool isValid() const noexcept
Is the iterator valid i.e. is it associated with a FAB?
Definition AMReX_MFIter.H:147
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:648
T * dataPtr() noexcept
Definition AMReX_PODVector.H:670
T * data() noexcept
Definition AMReX_PODVector.H:666
This class is a thin wrapper around std::vector. Unlike vector, Vector::operator[] provides bound che...
Definition AMReX_Vector.H:28
Long size() const noexcept
Definition AMReX_Vector.H:53
__host__ __device__ BoxND< dim > grow(const BoxND< dim > &b, int i) noexcept
Grow BoxND in all directions by given amount.
Definition AMReX_Box.H:1280
std::array< T, N > Array
Definition AMReX_Array.H:25
Definition AMReX_FFT_Helper.H:46
Boundary
Definition AMReX_FFT_Helper.H:52
void streamSynchronize() noexcept
Definition AMReX_GpuDevice.H:263
void htod_memcpy_async(void *p_d, const void *p_h, const std::size_t sz) noexcept
Definition AMReX_GpuDevice.H:301
int NProcsSub() noexcept
number of ranks in current frame
Definition AMReX_ParallelContext.H:74
Definition AMReX_Amr.cpp:49
@ make_alias
Definition AMReX_MakeType.H:7
__host__ __device__ void ignore_unused(const Ts &...)
This shuts up the compiler about unused variables.
Definition AMReX.H:138
__host__ __device__ BoxND< dim > convert(const BoxND< dim > &b, const IntVectND< dim > &typ) noexcept
Return a BoxND with different type.
Definition AMReX_Box.H:1558
__host__ __device__ BoxND< dim > makeSlab(BoxND< dim > const &b, int direction, int slab_index) noexcept
Definition AMReX_Box.H:2123
std::enable_if_t< std::is_integral_v< T > > ParallelFor(TypeList< CTOs... > ctos, std::array< int, sizeof...(CTOs)> const &runtime_options, T N, F &&f)
Definition AMReX_CTOParallelForImpl.H:193
BoxND< 3 > Box
Box is an alias for amrex::BoxND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:27
double second() noexcept
Definition AMReX_Utility.cpp:940
__host__ __device__ BoxND< dim > shift(const BoxND< dim > &b, int dir, int nzones) noexcept
Return a BoxND with indices shifted by nzones in dir direction.
Definition AMReX_Box.H:1495
BoxArray decompose(Box const &domain, int nboxes, Array< bool, 3 > const &decomp, bool no_overlap)
Decompose domain box into BoxArray.
Definition AMReX_BoxArray.cpp:1940
IntVectND< 3 > IntVect
IntVect is an alias for amrex::IntVectND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:30
bool TilingIfNotGPU() noexcept
Definition AMReX_MFIter.H:12
void Abort(const std::string &msg)
Print out message to cerr and exit via abort().
Definition AMReX.cpp:230
void LoopOnCpu(Dim3 lo, Dim3 hi, F const &f) noexcept
Definition AMReX_Loop.H:365
__host__ __device__ constexpr T elemwiseMin(T const &a, T const &b) noexcept
Definition AMReX_Algorithm.H:49
__host__ __device__ 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:1016
Definition AMReX_Array4.H:61
Definition AMReX_FFT_Helper.H:58
Info & setOneDMode(bool x)
Definition AMReX_FFT_Helper.H:83
Info & setTwoDMode(bool x)
Definition AMReX_FFT_Helper.H:82
Fixed-size array that can be used on GPU.
Definition AMReX_Array.H:40