32 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
35 if (dir == 0)
return mode * kscale[0];
36 return (mode < length[dir] / 2 ? mode : mode - length[dir]) * kscale[dir];
39 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
43 amrex::ignore_unused(p);
45 return AMREX_D_TERM(K(0,m,length,kscale) * K(0,m,length,kscale),
46 + K(1,n,length,kscale) * K(1,n,length,kscale),
47 + K(2,p,length,kscale) * K(2,p,length,kscale));
50 FFT (
const amrex::Geometry& a_geom)
52 m_fft(a_geom.Domain())
54 auto const& layout = m_fft.getSpectralDataLayout();
56 m_cdm = layout.second;
58 m_domain = a_geom.Domain();
59 m_scaling = 1.0 / a_geom.Domain().d_numPts();
60 for (
int dir = 0; dir < AMREX_SPACEDIM; ++dir)
62 m_length[dir] = a_geom.Domain().length(dir);
68 const amrex::Vector<amrex::Geometry>& a_geom,
69 const amrex::Vector<amrex::IntVect>& a_ref_ratio,
71 :
FFT(a_geom[a_finest_level])
75 Util::Assert(
INFO,
TEST(a_finest_level == 0 ||
static_cast<int>(a_ref_ratio.size()) >= a_finest_level));
76 m_geom_hierarchy = a_geom;
77 m_ref_ratio = a_ref_ratio;
82 FFT& operator= (
const FFT&) =
delete;
83 FFT& operator= (
FFT&&) =
delete;
85 const amrex::Geometry& Geom ()
const {
return m_geom; }
86 const amrex::Box& Domain ()
const {
return m_domain; }
87 const amrex::BoxArray& SpectralBoxArray ()
const {
return m_cba; }
88 const amrex::DistributionMapping& SpectralDistributionMap ()
const {
return m_cdm; }
89 int Length (
int dir)
const {
return m_length[dir]; }
90 Set::Scalar KScale (
int dir)
const {
return m_kscale[dir]; }
93 return K(dir, mode, m_length, m_kscale);
98 return Omega2(m, n, p, m_length, m_kscale);
101 amrex::FabArray<amrex::BaseFab<Set::Complex> > MakeSpectralFab (
int ncomp = 1,
int ngrow = 0)
const
103 return amrex::FabArray<amrex::BaseFab<Set::Complex> >(m_cba, m_cdm, ncomp, ngrow);
107 void ParallelFor (
const amrex::Box& bx, F&& f)
const
109 int length[AMREX_SPACEDIM];
111 for (
int dir = 0; dir < AMREX_SPACEDIM; ++dir)
113 length[dir] = m_length[dir];
114 kscale[dir] = m_kscale[dir];
117 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(
int m,
int n,
int p) {
118 f(m, n, p, Omega2(m, n, p, length, kscale));
122 void Forward (
const amrex::MultiFab& in, amrex::FabArray<amrex::BaseFab<Set::Complex> >& out,
int scomp = 0,
int dcomp = 0)
124 m_fft.forward(in, out, scomp, dcomp);
127 void Backward (
const amrex::FabArray<amrex::BaseFab<Set::Complex> >& in, amrex::MultiFab& out,
int scomp = 0,
int dcomp = 0)
129 m_fft.backward(in, out, scomp, dcomp);
130 out.mult(m_scaling, dcomp, 1, 0);
133 bool IsFullDomain (
const amrex::MultiFab& mf)
const
135 return mf.boxArray().complementIn(m_domain).isEmpty();
139 const amrex::Vector<std::unique_ptr<amrex::MultiFab> >& hierarchy,
141 amrex::FabArray<amrex::BaseFab<Set::Complex> >& out,
144 amrex::Real time = 0.0)
148 if (IsFullDomain(*hierarchy[lev]))
150 Forward(*hierarchy[lev], out, scomp, dcomp);
154 auto full = CompositeToUniform(hierarchy, lev, time, hierarchy[lev]->nComp(), hierarchy[lev]->nGrow());
155 Forward(*full, out, scomp, dcomp);
159 const amrex::FabArray<amrex::BaseFab<Set::Complex> >& in,
160 amrex::Vector<std::unique_ptr<amrex::MultiFab> >& hierarchy,
167 if (IsFullDomain(*hierarchy[lev]))
169 Backward(in, *hierarchy[lev], scomp, dcomp);
173 EnsureRealTemporary(hierarchy[lev]->nComp(), hierarchy[lev]->nGrow());
174 Backward(in, *m_real_tmp, scomp, dcomp);
175 CopyFromFull(*m_real_tmp, *hierarchy[lev], m_geom, dcomp, dcomp, 1);
178 static amrex::BoxArray FullBoxArray (
const amrex::Geometry& geom,
int max_grid_size = 0)
180 amrex::BoxArray ba(geom.Domain());
181 if (max_grid_size > 0) ba.maxSize(max_grid_size);
185 static std::unique_ptr<amrex::MultiFab> MakeFullMultiFab (
186 const amrex::Geometry& geom,
189 int max_grid_size = 0)
191 amrex::BoxArray ba = FullBoxArray(geom, max_grid_size);
192 amrex::DistributionMapping dm(ba);
193 return std::make_unique<amrex::MultiFab>(ba, dm, ncomp, ngrow);
196 static void CopyToFull (
197 amrex::MultiFab& full,
198 const amrex::MultiFab& src,
199 const amrex::Geometry& geom,
204 if (ncomp < 0) ncomp = std::min(src.nComp() - scomp, full.nComp() - dcomp);
205 full.ParallelCopy(src, scomp, dcomp, ncomp, 0, 0, geom.periodicity());
208 static void CopyFromFull (
209 const amrex::MultiFab& full,
210 amrex::MultiFab& dst,
211 const amrex::Geometry& geom,
216 if (ncomp < 0) ncomp = std::min(full.nComp() - scomp, dst.nComp() - dcomp);
217 dst.ParallelCopy(full, scomp, dcomp, ncomp, 0, 0, geom.periodicity());
220 template <
class PhysBC>
221 static void ProlongateFromCoarse (
222 amrex::MultiFab& fine,
224 const amrex::MultiFab& coarse,
225 const amrex::Geometry& crse_geom,
226 const amrex::Geometry& fine_geom,
227 const amrex::IntVect& ref_ratio,
233 if (ncomp < 0) ncomp = std::min(coarse.nComp() - scomp, fine.nComp() - dcomp);
235 amrex::Interpolater* mapper = &amrex::cell_cons_interp;
236 if (fine.boxArray().ixType() == amrex::IndexType::TheNodeType())
237 mapper = &amrex::node_bilinear_interp;
239 physbc.define(fine_geom);
240 amrex::Vector<amrex::BCRec> bcs(ncomp, physbc.GetBCRec());
241 amrex::InterpFromCoarseLevel(
242 fine, time, coarse, scomp, dcomp, ncomp,
243 crse_geom, fine_geom,
250 template <
class PhysBC>
251 static std::unique_ptr<amrex::MultiFab> CompositeToUniform (
252 const amrex::Vector<amrex::MultiFab*>& hierarchy,
253 const amrex::Vector<amrex::Geometry>& geom,
254 const amrex::Vector<amrex::IntVect>& ref_ratio,
260 int max_grid_size = 0)
266 amrex::Vector<std::unique_ptr<amrex::MultiFab> > full(finest_level + 1);
267 for (
int lev = 0; lev <= finest_level; ++lev)
269 full[lev] = MakeFullMultiFab(geom[lev], ncomp, ngrow, max_grid_size);
270 full[lev]->setVal(0.0);
275 ProlongateFromCoarse(*full[lev], time, *full[lev - 1], geom[lev - 1], geom[lev],
276 ref_ratio[lev - 1], physbc, 0, 0, ncomp);
279 CopyToFull(*full[lev], *hierarchy[lev], geom[lev], 0, 0, ncomp);
282 return std::move(full[finest_level]);
285 static void UniformToComposite (
286 const amrex::MultiFab& full_fine,
287 amrex::Vector<amrex::MultiFab*>& hierarchy,
288 const amrex::Vector<amrex::Geometry>& geom,
289 const amrex::Vector<amrex::IntVect>& ref_ratio,
292 int max_grid_size = 0)
298 amrex::Vector<std::unique_ptr<amrex::MultiFab> > full(finest_level + 1);
299 full[finest_level] = MakeFullMultiFab(geom[finest_level], ncomp, 0, max_grid_size);
300 CopyToFull(*full[finest_level], full_fine, geom[finest_level], 0, 0, ncomp);
302 for (
int lev = finest_level - 1; lev >= 0; --lev)
305 full[lev] = MakeFullMultiFab(geom[lev], ncomp, 0, max_grid_size);
306 amrex::average_down(*full[lev + 1], *full[lev], 0, ncomp, ref_ratio[lev]);
309 for (
int lev = 0; lev <= finest_level; ++lev)
310 CopyFromFull(*full[lev], *hierarchy[lev], geom[lev], 0, 0, ncomp);
314 void EnsureRealTemporary (
int ncomp,
int ngrow)
316 if (!m_real_tmp || m_real_tmp->nComp() != ncomp || m_real_tmp->nGrow() != ngrow)
317 m_real_tmp = MakeFullMultiFab(m_geom, ncomp, ngrow);
320 std::unique_ptr<amrex::MultiFab> CompositeToUniform (
321 const amrex::Vector<std::unique_ptr<amrex::MultiFab> >& hierarchy,
330 "Patchy spectral FFT requires hierarchy geometry context");
331 Util::Assert(
INFO,
TEST(finest_lev == 0 ||
static_cast<int>(m_ref_ratio.size()) >= finest_lev));
333 amrex::Vector<std::unique_ptr<amrex::MultiFab> > full(finest_lev + 1);
334 for (
int ilev = 0; ilev <= finest_lev; ++ilev)
336 const amrex::Geometry& level_geom = m_geom_hierarchy[ilev];
337 full[ilev] = MakeFullMultiFab(level_geom, ncomp, ngrow);
338 full[ilev]->setVal(0.0);
343 ProlongateFromCoarse(*full[ilev], time, *full[ilev - 1],
344 m_geom_hierarchy[ilev - 1], level_geom, m_ref_ratio[ilev - 1], physbc, 0, 0, ncomp);
347 CopyToFull(*full[ilev], *hierarchy[ilev], level_geom, 0, 0, ncomp);
350 return std::move(full[finest_lev]);
353 amrex::Geometry m_geom;
354 amrex::FFT::R2C<Set::Scalar> m_fft;
355 amrex::BoxArray m_cba;
356 amrex::DistributionMapping m_cdm;
357 std::unique_ptr<amrex::MultiFab> m_real_tmp;
358 amrex::Vector<amrex::Geometry> m_geom_hierarchy;
359 amrex::Vector<amrex::IntVect> m_ref_ratio;
361 int m_length[AMREX_SPACEDIM];