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