Alamo
Operator.cpp
Go to the documentation of this file.
1
2#include <AMReX_MLNodeLinOp.H>
3#include <AMReX_MLCellLinOp.H>
4#include <AMReX_MLNodeLap_K.H>
5#include <AMReX_MultiFabUtil.H>
6#include "Util/Color.H"
7#include "Set/Set.H"
8#include "Operator.H"
9
10using namespace amrex;
11namespace Operator {
12
13// constexpr amrex::IntVect AMREX_D_DECL(Operator<Grid::Node>::dx,Operator<Grid::Node>::dy,Operator<Grid::Node>::dz);
15
17{
18 BL_PROFILE(Color::FG::Yellow + "Operator::Diagonal()" + Color::Reset);
19 if (!recompute && m_diagonal_computed) return;
20 m_diagonal_computed = true;
21
22 for (int amrlev = 0; amrlev < m_num_amr_levels; ++amrlev)
23 {
24 for (int mglev = 0; mglev < m_num_mg_levels[amrlev]; ++mglev)
25 {
26 Diagonal(amrlev, mglev, *m_diag[amrlev][mglev]);
27 }
28 }
29}
30
31void Operator<Grid::Node>::Diagonal(int amrlev, int mglev, amrex::MultiFab& diag)
32{
33 BL_PROFILE("Operator::Diagonal()");
34 //Util::Message(INFO);
35
36 int ncomp = diag.nComp();
37 int nghost = 0;
38
39 int sep = 2;
40 int num = AMREX_D_TERM(sep, *sep, *sep);
41 int cntr = 0;
42
43 amrex::MultiFab x(m_diag[amrlev][mglev]->boxArray(), m_diag[amrlev][mglev]->DistributionMap(), ncomp, nghost);
44 amrex::MultiFab Ax(m_diag[amrlev][mglev]->boxArray(), m_diag[amrlev][mglev]->DistributionMap(), ncomp, nghost);
45
46 for (MFIter mfi(x, false); mfi.isValid(); ++mfi)
47 {
48 const Box& bx = mfi.validbox();
49 amrex::FArrayBox& diagfab = diag[mfi];
50 amrex::FArrayBox& xfab = x[mfi];
51 amrex::FArrayBox& Axfab = Ax[mfi];
52
53 diagfab.setVal<amrex::RunOn::Device>(0.0);
54
55 for (int i = 0; i < num; i++)
56 {
57 for (int n = 0; n < ncomp; n++)
58 {
59 xfab.setVal<amrex::RunOn::Device>(0.0);
60 Axfab.setVal<amrex::RunOn::Device>(0.0);
61
62 //BL_PROFILE_VAR("Operator::Part1", part1);
63 AMREX_D_TERM(for (int m1 = bx.loVect()[0]; m1 <= bx.hiVect()[0]; m1++),
64 for (int m2 = bx.loVect()[1]; m2 <= bx.hiVect()[1]; m2++),
65 for (int m3 = bx.loVect()[2]; m3 <= bx.hiVect()[2]; m3++))
66 {
67 amrex::IntVect m(AMREX_D_DECL(m1, m2, m3));
68
69 if (m1 % sep == i / sep && m2 % sep == i % sep) xfab(m, n) = 1.0;
70 else xfab(m, n) = 0.0;
71 }
72 //BL_PROFILE_VAR_STOP(part1);
73
74 BL_PROFILE_VAR("Operator::Part2", part2);
75 Util::Message(INFO, "Calling fapply...", cntr++);
76 Fapply(amrlev, mglev, Ax, x);
77 BL_PROFILE_VAR_STOP(part2);
78
79 //BL_PROFILE_VAR("Operator::Part3", part3);
80 Axfab.mult<amrex::RunOn::Device>(xfab, n, n, 1);
81 diagfab.plus<amrex::RunOn::Device>(Axfab, n, n, 1);
82 //BL_PROFILE_VAR_STOP(part3);
83 }
84 }
85 }
86}
87
88void Operator<Grid::Node>::Fsmooth(int amrlev, int mglev, amrex::MultiFab& x, const amrex::MultiFab& b) const
89{
90 BL_PROFILE("Operator::Fsmooth()");
91
92 amrex::Box domain(m_geom[amrlev][mglev].growPeriodicDomain(1));
93 domain.convert(amrex::IntVect::TheNodeVector());
94
95 int ncomp = b.nComp();
96 const bool relax_ghost_rows = relaxCoarseFineGhostRows();
97 int nghost = relax_ghost_rows ? 2 : 0;
98
99
100 amrex::MultiFab Ax(x.boxArray(), x.DistributionMap(), ncomp, nghost);
101 amrex::MultiFab Dx(x.boxArray(), x.DistributionMap(), ncomp, nghost);
102 amrex::MultiFab Rx(x.boxArray(), x.DistributionMap(), ncomp, nghost);
103
104 if (!m_diagonal_computed) Util::Abort(INFO, "Operator::Diagonal() must be called before using Fsmooth");
105
106 // This is a JACOBI iteration, not Gauss-Seidel.
107 // So we need to do twice the number of iterations to get the same behavior as GS.
108 for (int ctr = 0; ctr < 2; ctr++)
109 {
110 Fapply(amrlev, mglev, Ax, x); // find Ax
111
112 amrex::MultiFab::Copy(Dx, x, 0, 0, ncomp, nghost); // Dx = x
113 amrex::MultiFab::Multiply(Dx, *m_diag[amrlev][mglev], 0, 0, ncomp, nghost); // Dx *= diag (Dx = x*diag)
114
115 amrex::MultiFab::Copy(Rx, Ax, 0, 0, ncomp, nghost); // Rx = Ax
116 amrex::MultiFab::Subtract(Rx, Dx, 0, 0, ncomp, nghost); // Rx -= Dx (Rx = Ax - Dx)
117
118 for (MFIter mfi(x, false); mfi.isValid(); ++mfi)
119 {
120 Box bx = mfi.grownnodaltilebox();
121
122 const auto xfab = x.array(mfi);
123 const auto bfab = b.const_array(mfi);
124 const auto Rxfab = Rx.const_array(mfi);
125 const auto diagfab = (*m_diag[amrlev][mglev]).const_array(mfi);
126
127 if (!relax_ghost_rows)
128 {
129 const Box cbx = mfi.nodaltilebox() & domain;
130 for (int n = 0; n < ncomp; ++n)
131 {
132 amrex::LoopConcurrentOnCpu(cbx, [&] (int i, int j, int k)
133 {
134 xfab(i,j,k,n) = (1. - m_omega) * xfab(i,j,k, n)
135 + m_omega * (bfab(i,j,k, n) - Rxfab(i,j,k, n))
136 / diagfab(i,j,k,n);
137 });
138 }
139 continue;
140 }
141
142
143 for (int n = 0; n < ncomp; n++)
144 {
145 const Set::Scalar omega = m_omega;
146 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
147 {
148
149 // Skip ghost cells outside problem domain
150 if (!domain.contains(i,j,k))
151 {
152 //continue;
153 }
154 else if ( !bx.strictly_contains(i,j,k))
155 {
156 xfab(i, j, k, n) = 0.0;
157 //continue;
158 }
159 else
160 {
161 xfab(i,j,k,n) = (1. - omega) * xfab(i,j,k, n) + omega * (bfab(i,j,k, n) - Rxfab(i,j,k, n)) / diagfab(i,j,k,n);
162 }
163 });
164 }
165 }
166 amrex::Geometry geom = m_geom[amrlev][mglev];
167 x.setMultiGhost(true);
168 x.FillBoundary(geom.periodicity());
169 nodalSync(amrlev, mglev, x);
170 }
171}
172
173void Operator<Grid::Node>::normalize(int amrlev, int mglev, MultiFab& a_x) const
174{
175 if (!m_diagonal_computed)
176 Util::Abort(INFO, "Operator::Diagonal() must be called before using normalize");
177
178 a_x.divide(*m_diag[amrlev][mglev],0,getNComp(),relaxCoarseFineGhostRows() ? 2 : 0);
179
180 a_x.setMultiGhost(true);
181 a_x.FillBoundaryAndSync(Geom(amrlev,mglev).periodicity());
182}
183
184Operator<Grid::Node>::Operator(const Vector<Geometry>& a_geom,
185 const Vector<BoxArray>& a_grids,
186 const Vector<DistributionMapping>& a_dmap,
187 const LPInfo& a_info,
188 const Vector<FabFactory<FArrayBox> const*>& a_factory)
189{
190 BL_PROFILE("Operator::Operator()");
192
193 if (!(a_grids[0].ixType() == amrex::IndexType::TheNodeType()))
194 Util::Abort(INFO, "Operator must be defined using CELL CENTERED boxarrays.");
195
196 define(a_geom, a_grids, a_dmap, a_info, a_factory);
197}
198
201
202void Operator<Grid::Node>::define(const Vector<Geometry>& a_geom,
203 const Vector<BoxArray>& a_grids,
204 const Vector<DistributionMapping>& a_dmap,
205 const LPInfo& a_info,
206 const Vector<FabFactory<FArrayBox> const*>& a_factory)
207{
208 BL_PROFILE("Operator::~Operator()");
209
210 // Make sure we're not trying to parallelize in vain.
211 if (amrex::ParallelDescriptor::NProcs() > a_grids[0].size())
212 {
213 Util::Warning(INFO, "There are more processors than there are boxes in the amrlev=0 boxarray!!\n",
214 "(NProcs = ", amrex::ParallelDescriptor::NProcs(), ", a_grids[0].size() = ", a_grids[0].size(), ")\n",
215 "You should decrease max_grid_size or you will not get proper scaling!");
216 }
217
218 // This makes sure grids are node-centered;
219 Vector<BoxArray> cc_grids = a_grids;
220 for (auto& ba : cc_grids) {
221 ba.enclosedCells();
222 }
223
224 MLNodeLinOp::define(a_geom, a_grids, a_dmap, a_info, a_factory);
225
226 int nghost = 2;
227 // Resize the multifab containing the operator diagonal
228 m_diag.resize(m_num_amr_levels);
229 for (int amrlev = 0; amrlev < m_num_amr_levels; ++amrlev)
230 {
231 m_diag[amrlev].resize(m_num_mg_levels[amrlev]);
232
233 for (int mglev = 0; mglev < m_num_mg_levels[amrlev]; ++mglev)
234 {
235 m_diag[amrlev][mglev].reset(new MultiFab(amrex::convert(m_grids[amrlev][mglev], amrex::IntVect::TheNodeVector()),
236 m_dmap[amrlev][mglev], getNComp(), nghost));
237 }
238 }
239
240 // We need to instantiate the m_lobc objects.
241 // WE DO NOT USE THEM - our BCs are implemented differently.
242 // But they need to be the right size or the code will segfault.
243 m_lobc.resize(getNComp(), { {AMREX_D_DECL(BCType::bogus,BCType::bogus,BCType::bogus)} });
244 m_hibc.resize(getNComp(), { {AMREX_D_DECL(BCType::bogus,BCType::bogus,BCType::bogus)} });
245}
246
247void Operator<Grid::Node>::fixUpResidualMask(int amrlev, iMultiFab& resmsk)
248{
249 BL_PROFILE("Operator::fixUpResidualMask()");
250
251 if (!m_masks_built) buildMasks();
252
253 const iMultiFab& cfmask = *m_nd_fine_mask[amrlev];
254
255#ifdef _OPENMP
256#pragma omp parallel if (Gpu::notInLaunchRegion())
257#endif
258 for (MFIter mfi(resmsk, TilingIfNotGPU()); mfi.isValid(); ++mfi)
259 {
260 const Box& bx = mfi.tilebox();
261 Array4<int> const& rmsk = resmsk.array(mfi);
262 Array4<int const> const& fmsk = cfmask.const_array(mfi);
263 AMREX_HOST_DEVICE_PARALLEL_FOR_3D(bx, i, j, k,
264 {
265 if (fmsk(i,j,k) == amrex::nodelap_detail::crse_fine_node) rmsk(i,j,k) = 1;
266 });
267 }
268}
269
271{
272 BL_PROFILE("Operator::prepareForSolve()");
273 MLNodeLinOp::prepareForSolve();
274 buildMasks();
275 averageDownCoeffs();
276 Diagonal(true);
277}
278
279void Operator<Grid::Node>::restriction(int amrlev, int cmglev, MultiFab& crse, MultiFab& fine) const
280{
281 BL_PROFILE("Operator::restriction()");
282
283 applyBC(amrlev, cmglev - 1, fine, BCMode::Homogeneous, StateMode::Solution);
284
285 amrex::Box cdomain = m_geom[amrlev][cmglev].growPeriodicDomain(1);
286 cdomain = cdomain.convert(amrex::IntVect::TheNodeVector());
287
288 bool need_parallel_copy = !amrex::isMFIterSafe(crse, fine);
289 MultiFab cfine;
290 if (need_parallel_copy) {
291 const BoxArray& ba = amrex::coarsen(fine.boxArray(), 2);
292 cfine.define(ba, fine.DistributionMap(), fine.nComp(), fine.nGrow());
293 }
294
295 MultiFab* pcrse = (need_parallel_copy) ? &cfine : &crse;
296
297 for (MFIter mfi(*pcrse, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi)
298 {
299 const Box& bx = mfi.grownnodaltilebox(-1,1) & cdomain;
300
301 amrex::Array4<const amrex::Real> const& fdata = fine.array(mfi);
302 amrex::Array4<amrex::Real> const& cdata = pcrse->array(mfi);
303
304 const Dim3 lo = amrex::lbound(bx), hi = amrex::ubound(bx);
305
306
307 for (int n = 0; n < crse.nComp(); n++)
308 {
309 // I,J,K == coarse coordinates
310 // i,j,k == fine coordinates
311 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int I, int J, int K) {
312 int i = 2 * I, j = 2 * J, k = 2 * K;
313
314 if ((I == lo.x || I == hi.x) &&
315 (J == lo.y || J == hi.y) &&
316 (K == lo.z || K == hi.z)) // Corner
317 {
318 cdata(I, J, K, n) = fdata(i, j, k, n);
319 }
320 else if ((J == lo.y || J == hi.y) &&
321 (K == lo.z || K == hi.z)) // X edge
322 {
323 cdata(I, J, K, n) = 0.25 * fdata(i - 1, j, k, n) + 0.5 * fdata(i, j, k, n) + 0.25 * fdata(i + 1, j, k, n);
324 }
325 else if ((K == lo.z || K == hi.z) &&
326 (I == lo.x || I == hi.x)) // Y edge
327 {
328 cdata(I, J, K, n) = 0.25 * fdata(i, j - 1, k, n) + 0.5 * fdata(i, j, k, n) + 0.25 * fdata(i, j + 1, k, n);
329 }
330 else if ((I == lo.x || I == hi.x) &&
331 (J == lo.y || J == hi.y)) // Z edge
332 {
333 cdata(I, J, K, n) = 0.25 * fdata(i, j, k - 1, n) + 0.5 * fdata(i, j, k, n) + 0.25 * fdata(i, j, k + 1, n);
334 }
335 else if (I == lo.x || I == hi.x) // X face
336 {
337 cdata(I, J, K, n) =
338 (+fdata(i, j - 1, k - 1, n) + 2.0 * fdata(i, j, k - 1, n) + fdata(i, j + 1, k - 1, n)
339 + 2.0 * fdata(i, j - 1, k, n) + 4.0 * fdata(i, j, k, n) + 2.0 * fdata(i, j + 1, k, n)
340 + fdata(i, j - 1, k + 1, n) + 2.0 * fdata(i, j, k + 1, n) + fdata(i, j + 1, k + 1, n)) / 16.0;
341 }
342 else if (J == lo.y || J == hi.y) // Y face
343 {
344 cdata(I, J, K, n) =
345 (+fdata(i - 1, j, k - 1, n) + 2.0 * fdata(i - 1, j, k, n) + fdata(i - 1, j, k + 1, n)
346 + 2.0 * fdata(i, j, k - 1, n) + 4.0 * fdata(i, j, k, n) + 2.0 * fdata(i, j, k + 1, n)
347 + fdata(i + 1, j, k - 1, n) + 2.0 * fdata(i + 1, j, k, n) + fdata(i + 1, j, k + 1, n)) / 16.0;
348 }
349 else if (K == lo.z || K == hi.z) // Z face
350 {
351 cdata(I, J, K, n) =
352 (+fdata(i - 1, j - 1, k, n) + 2.0 * fdata(i, j - 1, k, n) + fdata(i + 1, j - 1, k, n)
353 + 2.0 * fdata(i - 1, j, k, n) + 4.0 * fdata(i, j, k, n) + 2.0 * fdata(i + 1, j, k, n)
354 + fdata(i - 1, j + 1, k, n) + 2.0 * fdata(i, j + 1, k, n) + fdata(i + 1, j + 1, k, n)) / 16.0;
355 }
356 else // Interior
357 cdata(I, J, K, n) =
358 (fdata(i - 1, j - 1, k - 1, n) + fdata(i - 1, j - 1, k + 1, n) + fdata(i - 1, j + 1, k - 1, n) + fdata(i - 1, j + 1, k + 1, n) +
359 fdata(i + 1, j - 1, k - 1, n) + fdata(i + 1, j - 1, k + 1, n) + fdata(i + 1, j + 1, k - 1, n) + fdata(i + 1, j + 1, k + 1, n)) / 64.0
360 +
361 (fdata(i, j - 1, k - 1, n) + fdata(i, j - 1, k + 1, n) + fdata(i, j + 1, k - 1, n) + fdata(i, j + 1, k + 1, n) +
362 fdata(i - 1, j, k - 1, n) + fdata(i + 1, j, k - 1, n) + fdata(i - 1, j, k + 1, n) + fdata(i + 1, j, k + 1, n) +
363 fdata(i - 1, j - 1, k, n) + fdata(i - 1, j + 1, k, n) + fdata(i + 1, j - 1, k, n) + fdata(i + 1, j + 1, k, n)) / 32.0
364 +
365 (fdata(i - 1, j, k, n) + fdata(i, j - 1, k, n) + fdata(i, j, k - 1, n) +
366 fdata(i + 1, j, k, n) + fdata(i, j + 1, k, n) + fdata(i, j, k + 1, n)) / 16.0
367 +
368 fdata(i, j, k, n) / 8.0;
369 });
370 }
371 }
372
373 if (need_parallel_copy) {
374 crse.ParallelCopy(cfine);
375 }
376
377 crse.setMultiGhost(true);
378 crse.FillBoundary(Geom(amrlev,cmglev).periodicity());
379 nodalSync(amrlev, cmglev, crse);
380}
381
382void Operator<Grid::Node>::interpolation(int amrlev, int fmglev, MultiFab& fine, const MultiFab& crse) const
383{
384 BL_PROFILE("Operator::interpolation()");
385 amrex::Box fdomain = m_geom[amrlev][fmglev].growPeriodicDomain(2);
386 fdomain.convert(amrex::IntVect::TheNodeVector());
387
388 bool need_parallel_copy = !amrex::isMFIterSafe(crse, fine);
389 MultiFab cfine;
390 const MultiFab* cmf = &crse;
391 if (need_parallel_copy) {
392 const BoxArray& ba = amrex::coarsen(fine.boxArray(), 2);
393 cfine.define(ba, fine.DistributionMap(), crse.nComp(), crse.nGrow());
394 cfine.ParallelCopy(crse);
395 cmf = &cfine;
396 }
397
398 for (MFIter mfi(fine, false); mfi.isValid(); ++mfi)
399 {
400 Box fine_bx = mfi.validbox() & fdomain;
401
402 const Box& course_bx = amrex::coarsen(fine_bx, 2);
403 const Box& tmpbx = amrex::refine(course_bx, 2);
404 FArrayBox tmpfab;
405 tmpfab.resize(tmpbx, fine.nComp());
406 tmpfab.setVal<amrex::RunOn::Device>(0.0);
407 const amrex::FArrayBox& crsefab = (*cmf)[mfi];
408
409 amrex::Array4<const amrex::Real> const& cdata = crsefab.const_array();
410 amrex::Array4<amrex::Real> const& fdata = tmpfab.array();
411
412 for (int n = 0; n < crse.nComp(); n++)
413 {
414 // I,J,K == coarse coordinates
415 // i,j,k == fine coordinates
416 amrex::ParallelFor(fine_bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
417
418 int I = i / 2, J = j / 2, K = k / 2;
419
420 if (i % 2 == 0 && j % 2 == 0 && k % 2 == 0) // Coincident
421 fdata(i, j, k, n) = cdata(I, J, K, n);
422 else if (j % 2 == 0 && k % 2 == 0) // X Edge
423 fdata(i, j, k, n) = 0.5 * (cdata(I, J, K, n) + cdata(I + 1, J, K, n));
424 else if (k % 2 == 0 && i % 2 == 0) // Y Edge
425 fdata(i, j, k, n) = 0.5 * (cdata(I, J, K, n) + cdata(I, J + 1, K, n));
426 else if (i % 2 == 0 && j % 2 == 0) // Z Edge
427 fdata(i, j, k, n) = 0.5 * (cdata(I, J, K, n) + cdata(I, J, K + 1, n));
428 else if (i % 2 == 0) // X Face
429 fdata(i, j, k, n) = 0.25 * (cdata(I, J, K, n) + cdata(I, J + 1, K, n) +
430 cdata(I, J, K + 1, n) + cdata(I, J + 1, K + 1, n));
431 else if (j % 2 == 0) // Y Face
432 fdata(i, j, k, n) = 0.25 * (cdata(I, J, K, n) + cdata(I, J, K + 1, n) +
433 cdata(I + 1, J, K, n) + cdata(I + 1, J, K + 1, n));
434 else if (k % 2 == 0) // Z Face
435 fdata(i, j, k, n) = 0.25 * (cdata(I, J, K, n) + cdata(I + 1, J, K, n) +
436 cdata(I, J + 1, K, n) + cdata(I + 1, J + 1, K, n));
437 else // Center
438 fdata(i, j, k, n) = 0.125 * (cdata(I, J, K, n) +
439 cdata(I + 1, J, K, n) + cdata(I, J + 1, K, n) + cdata(I, J, K + 1, n) +
440 cdata(I, J + 1, K + 1, n) + cdata(I + 1, J, K + 1, n) + cdata(I + 1, J + 1, K, n) +
441 cdata(I + 1, J + 1, K + 1, n));
442
443 });
444 }
445 fine[mfi].plus<amrex::RunOn::Device>(tmpfab, fine_bx, fine_bx, 0, 0, fine.nComp());
446 }
447
448 fine.setMultiGhost(true);
449 fine.FillBoundary(Geom(amrlev,fmglev).periodicity());
450 nodalSync(amrlev, fmglev, fine);
451}
452
453void Operator<Grid::Node>::averageDownSolutionRHS(int camrlev, MultiFab& crse_sol, MultiFab& /*crse_rhs*/,
454 const MultiFab& fine_sol, const MultiFab& /*fine_rhs*/)
455{
456 BL_PROFILE("Operator::averageDownSolutionRHS()");
457 const auto& amrrr = AMRRefRatio(camrlev);
458 amrex::average_down(fine_sol, crse_sol, 0, crse_sol.nComp(), amrrr);
459
460 if (isSingular(0))
461 {
462 Util::Abort(INFO, "Singular operators not supported!");
463 }
464
465}
466
467void Operator<Grid::Node>::interpolationAmr(int famrlev, MultiFab& fine,
468 const MultiFab& crse, IntVect const& nghost) const
469{
470 BL_PROFILE("Operator::interpolationAmr()");
471 if (!useQuadraticAmrInterpolation())
472 {
473 amrex::MLNodeLinOp::interpolationAmr(famrlev, fine, crse, nghost);
474 return;
475 }
476 Util::Assert(INFO, TEST(AMRRefRatio(famrlev - 1) == 2));
477 const int ncomp = getNComp();
478
479 for (MFIter mfi(fine, false); mfi.isValid(); ++mfi)
480 {
481 Box fbx = mfi.tilebox();
482 const Box valid = mfi.validbox();
483 fbx.grow(nghost);
484 const Dim3 vlo = amrex::lbound(valid), vhi = amrex::ubound(valid);
485 Array4<Real> const& ffab = fine.array(mfi);
486 Array4<Real const> const& cfab = crse.const_array(mfi);
487
488 amrex::LoopConcurrentOnCpu(fbx, ncomp,
489 [=] (int i, int j, int k, int n)
490 {
491 int ci[3][3] = {};
492 Real cw[3][3] = {};
493 int nc[3] = {1, 1, 1};
494 cw[0][0] = cw[1][0] = cw[2][0] = 1.0;
495
496 const int fi[3] = {i, j, k};
497 const int flo[3] = {vlo.x, vlo.y, vlo.z};
498 const int fhi[3] = {vhi.x, vhi.y, vhi.z};
499 for (int d = 0; d < AMREX_SPACEDIM; ++d)
500 {
501 const int q = fi[d] >= 0 ? fi[d] / 2 : (fi[d] - 1) / 2;
502 if (fi[d] % 2 == 0)
503 {
504 ci[d][0] = q;
505 }
506 else if (fi[d] < flo[d])
507 {
508 nc[d] = 3;
509 ci[d][0] = q; cw[d][0] = 3.0 / 8.0;
510 ci[d][1] = q + 1; cw[d][1] = 3.0 / 4.0;
511 ci[d][2] = q + 2; cw[d][2] = -1.0 / 8.0;
512 }
513 else if (fi[d] > fhi[d])
514 {
515 nc[d] = 3;
516 ci[d][0] = q - 1; cw[d][0] = -1.0 / 8.0;
517 ci[d][1] = q; cw[d][1] = 3.0 / 4.0;
518 ci[d][2] = q + 1; cw[d][2] = 3.0 / 8.0;
519 }
520 else
521 {
522 nc[d] = 2;
523 ci[d][0] = q; cw[d][0] = 0.5;
524 ci[d][1] = q + 1; cw[d][1] = 0.5;
525 }
526 }
527
528 Real value = 0.0;
529 for (int a = 0; a < nc[0]; ++a)
530 for (int b = 0; b < nc[1]; ++b)
531 for (int c = 0; c < nc[2]; ++c)
532 value += cw[0][a] * cw[1][b] * cw[2][c]
533 * cfab(ci[0][a], ci[1][b], ci[2][c], n);
534 ffab(i, j, k, n) = value;
535 });
536 }
537}
538
539void Operator<Grid::Node>::realFillBoundary(MultiFab& phi, const Geometry& geom)
540{
541 Util::RealFillBoundary(phi, geom);
542}
543
544void Operator<Grid::Node>::applyBC(int amrlev, int mglev, MultiFab& phi, BCMode/* bc_mode*/,
545 amrex::MLLinOp::StateMode /**/, bool skip_fillboundary) const
546{
547 BL_PROFILE("Operator::applyBC()");
548
549 const Geometry& geom = m_geom[amrlev][mglev];
550
551 if (!skip_fillboundary) {
552 //phi.FillBoundary(geom.periodicity());
553 //phi.setMultiGhost(true);
554 phi.FillBoundaryAndSync(geom.periodicity());
555 }
556}
557
558const amrex::FArrayBox&
559Operator<Grid::Node>::GetFab(const int num, const int amrlev, const int mglev, const amrex::MFIter& mfi) const
560{
561 BL_PROFILE("Operator::GetFab()");
563 return m_a_coeffs[num][amrlev][mglev][mfi];
564}
565
566void Operator<Grid::Node>::RegisterNewFab(amrex::Vector<amrex::MultiFab>& input)
567{
568 BL_PROFILE("Operator::RegisterNewFab()");
570 /// \todo assertions here
571 m_a_coeffs.resize(m_a_coeffs.size() + 1);
572 m_a_coeffs[m_num_a_fabs].resize(m_num_amr_levels);
573 for (int amrlev = 0; amrlev < m_num_amr_levels; ++amrlev)
574 {
575 m_a_coeffs[m_num_a_fabs][amrlev].resize(m_num_mg_levels[amrlev]);
576 for (int mglev = 0; mglev < m_num_mg_levels[amrlev]; ++mglev)
577 m_a_coeffs[m_num_a_fabs][amrlev][mglev].define(m_grids[amrlev][mglev],
578 m_dmap[amrlev][mglev],
579 input[amrlev].nComp(),
580 input[amrlev].nGrow());
581
582 amrex::MultiFab::Copy(m_a_coeffs[m_num_a_fabs][amrlev][0],
583 input[amrlev], 0, 0,
584 input[amrlev].nComp(),
585 input[amrlev].nGrow());
586 }
587 m_num_a_fabs++;
588}
589
590
591void Operator<Grid::Node>::RegisterNewFab(amrex::Vector<std::unique_ptr<amrex::MultiFab> >& input)
592{
593 BL_PROFILE("Operator::RegisterNewFab()");
595 /// \todo assertions here
596 m_a_coeffs.resize(m_a_coeffs.size() + 1);
597 m_a_coeffs[m_num_a_fabs].resize(m_num_amr_levels);
598 for (int amrlev = 0; amrlev < m_num_amr_levels; ++amrlev)
599 {
600 m_a_coeffs[m_num_a_fabs][amrlev].resize(m_num_mg_levels[amrlev]);
601 for (int mglev = 0; mglev < m_num_mg_levels[amrlev]; ++mglev)
602 m_a_coeffs[m_num_a_fabs][amrlev][mglev].define(m_grids[amrlev][mglev],
603 m_dmap[amrlev][mglev],
604 input[amrlev]->nComp(),
605 input[amrlev]->nGrow());
606
607 amrex::MultiFab::Copy(m_a_coeffs[m_num_a_fabs][amrlev][0],
608 *input[amrlev], 0, 0,
609 input[amrlev]->nComp(),
610 input[amrlev]->nGrow());
611 }
612 m_num_a_fabs++;
613}
614
615void Operator<Grid::Node>::reflux(int crse_amrlev,
616 MultiFab& res, const MultiFab& /*crse_sol*/, const MultiFab& /*crse_rhs*/,
617 MultiFab& fine_res, MultiFab& /*fine_sol*/, const MultiFab& /*fine_rhs*/) const
618{
619 BL_PROFILE("Operator::Elastic::reflux()");
620
621 int ncomp = AMREX_SPACEDIM;
622
623 amrex::Box cdomain(m_geom[crse_amrlev][0].growPeriodicDomain(2));
624 cdomain.convert(amrex::IntVect::TheNodeVector());
625
626 const Geometry& cgeom = m_geom[crse_amrlev][0];
627
628 const BoxArray& fba = fine_res.boxArray();
629 const DistributionMapping& fdm = fine_res.DistributionMap();
630
631 MultiFab fine_res_for_coarse(amrex::coarsen(fba, 2), fdm, ncomp, 2);
632 fine_res_for_coarse.ParallelCopy(res, 0, 0, ncomp, 0, 0, cgeom.periodicity());
633
634 applyBC(crse_amrlev + 1, 0, fine_res, BCMode::Inhomogeneous, StateMode::Solution);
635
636 /// \todo Replace with Enum
637 // const int coarse_coarse_node = 0;
638 const int coarse_fine_node = 1;
639 const int fine_fine_node = 2;
640
641 amrex::iMultiFab nodemask(amrex::coarsen(fba, 2), fdm, 1, 2);
642 nodemask.ParallelCopy(*m_nd_fine_mask[crse_amrlev], 0, 0, 1, 0, 0, cgeom.periodicity());
643
644 for (MFIter mfi(fine_res_for_coarse, false); mfi.isValid(); ++mfi)
645 {
646 const Box& bx = mfi.grownnodaltilebox(-1,1) & cdomain;
647
648 amrex::Array4<const int> const& nmask = nodemask.array(mfi);
649 //amrex::Array4<const int> const& cmask = cellmask.array(mfi);
650
651 amrex::Array4<amrex::Real> const& cdata = fine_res_for_coarse.array(mfi);
652 amrex::Array4<const amrex::Real> const& fdata = fine_res.array(mfi);
653
654 const Dim3 lo = amrex::lbound(bx), hi = amrex::ubound(bx);
655
656 for (int n = 0; n < fine_res.nComp(); n++)
657 {
658 // I,J,K == coarse coordinates
659 // i,j,k == fine coordinates
660 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int I, int J, int K) {
661 int i = I * 2, j = J * 2, k = K * 2;
662
663 if (nmask(I, J, K) == fine_fine_node || nmask(I, J, K) == coarse_fine_node)
664 {
665 if ((I == lo.x || I == hi.x) &&
666 (J == lo.y || J == hi.y) &&
667 (K == lo.z || K == hi.z)) // Corner
668 cdata(I, J, K, n) = fdata(i, j, k, n);
669 else if ((J == lo.y || J == hi.y) &&
670 (K == lo.z || K == hi.z)) // X edge
671 cdata(I, J, K, n) = 0.25 * fdata(i - 1, j, k, n) + 0.5 * fdata(i, j, k, n) + 0.25 * fdata(i + 1, j, k, n);
672 else if ((K == lo.z || K == hi.z) &&
673 (I == lo.x || I == hi.x)) // Y edge
674 cdata(I, J, K, n) = 0.25 * fdata(i, j - 1, k, n) + 0.5 * fdata(i, j, k, n) + 0.25 * fdata(i, j + 1, k, n);
675 else if ((I == lo.x || I == hi.x) &&
676 (J == lo.y || J == hi.y)) // Z edge
677 cdata(I, J, K, n) = 0.25 * fdata(i, j, k - 1, n) + 0.5 * fdata(i, j, k, n) + 0.25 * fdata(i, j, k + 1, n);
678 else if (I == lo.x || I == hi.x) // X face
679 cdata(I, J, K, n) =
680 (+fdata(i, j - 1, k - 1, n) + 2.0 * fdata(i, j, k - 1, n) + fdata(i, j + 1, k - 1, n)
681 + 2.0 * fdata(i, j - 1, k, n) + 4.0 * fdata(i, j, k, n) + 2.0 * fdata(i, j + 1, k, n)
682 + fdata(i, j - 1, k + 1, n) + 2.0 * fdata(i, j, k + 1, n) + fdata(i, j + 1, k + 1, n)) / 16.0;
683 else if (J == lo.y || J == hi.y) // Y face
684 cdata(I, J, K, n) =
685 (+fdata(i - 1, j, k - 1, n) + 2.0 * fdata(i - 1, j, k, n) + fdata(i - 1, j, k + 1, n)
686 + 2.0 * fdata(i, j, k - 1, n) + 4.0 * fdata(i, j, k, n) + 2.0 * fdata(i, j, k + 1, n)
687 + fdata(i + 1, j, k - 1, n) + 2.0 * fdata(i + 1, j, k, n) + fdata(i + 1, j, k + 1, n)) / 16.0;
688 else if (K == lo.z || K == hi.z) // Z face
689 cdata(I, J, K, n) =
690 (+fdata(i - 1, j - 1, k, n) + 2.0 * fdata(i, j - 1, k, n) + fdata(i + 1, j - 1, k, n)
691 + 2.0 * fdata(i - 1, j, k, n) + 4.0 * fdata(i, j, k, n) + 2.0 * fdata(i + 1, j, k, n)
692 + fdata(i - 1, j + 1, k, n) + 2.0 * fdata(i, j + 1, k, n) + fdata(i + 1, j + 1, k, n)) / 16.0;
693 else // Interior
694 cdata(I, J, K, n) =
695 (fdata(i - 1, j - 1, k - 1, n) + fdata(i - 1, j - 1, k + 1, n) + fdata(i - 1, j + 1, k - 1, n) + fdata(i - 1, j + 1, k + 1, n) +
696 fdata(i + 1, j - 1, k - 1, n) + fdata(i + 1, j - 1, k + 1, n) + fdata(i + 1, j + 1, k - 1, n) + fdata(i + 1, j + 1, k + 1, n)) / 64.0
697 +
698 (fdata(i, j - 1, k - 1, n) + fdata(i, j - 1, k + 1, n) + fdata(i, j + 1, k - 1, n) + fdata(i, j + 1, k + 1, n) +
699 fdata(i - 1, j, k - 1, n) + fdata(i + 1, j, k - 1, n) + fdata(i - 1, j, k + 1, n) + fdata(i + 1, j, k + 1, n) +
700 fdata(i - 1, j - 1, k, n) + fdata(i - 1, j + 1, k, n) + fdata(i + 1, j - 1, k, n) + fdata(i + 1, j + 1, k, n)) / 32.0
701 +
702 (fdata(i - 1, j, k, n) + fdata(i, j - 1, k, n) + fdata(i, j, k - 1, n) +
703 fdata(i + 1, j, k, n) + fdata(i, j + 1, k, n) + fdata(i, j, k + 1, n)) / 16.0
704 +
705 fdata(i, j, k, n) / 8.0;
706 }
707
708 });
709 }
710 }
711
712 // Copy the fine residual restricted onto the coarse grid
713 // into the final residual.
714 res.ParallelCopy(fine_res_for_coarse, 0, 0, ncomp, 0, 0, cgeom.periodicity());
715
716 // Sync up ghost nodes
717 res.setMultiGhost(true);
718 res.FillBoundaryAndSync(Geom(crse_amrlev).periodicity());
719 nodalSync(crse_amrlev, 0, res);
720
721 return;
722}
723
724void
725Operator<Grid::Node>::solutionResidual(int amrlev, MultiFab& resid, MultiFab& x, const MultiFab& b,
726 const MultiFab* /*crse_bcdata*/)
727{
728 const int mglev = 0;
729 const int ncomp = b.nComp();
730 apply(amrlev, mglev, resid, x, BCMode::Inhomogeneous, StateMode::Solution);
731 MultiFab::Xpay(resid, -1.0, b, 0, 0, ncomp, 2);
732 resid.setMultiGhost(true);
733 resid.FillBoundaryAndSync(Geom(amrlev).periodicity());
734}
735
736void
737Operator<Grid::Node>::correctionResidual(int amrlev, int mglev, MultiFab& resid, MultiFab& x, const MultiFab& b,
738 BCMode /*bc_mode*/, const MultiFab* /*crse_bcdata*/)
739{
740 resid.setVal(0.0);
741 apply(amrlev, mglev, resid, x, BCMode::Homogeneous, StateMode::Correction);
742 int ncomp = b.nComp();
743 MultiFab::Xpay(resid, -1.0, b, 0, 0, ncomp, resid.nGrow());
744 resid.setMultiGhost(true);
745 resid.FillBoundaryAndSync(Geom(amrlev).periodicity());
746}
747
748
749
750
752{
753 m_ixtype = amrex::IntVect::TheCellVector();
754}
755
756void
757Operator<Grid::Cell>::define(amrex::Vector<amrex::Geometry> a_geom,
758 const amrex::Vector<amrex::BoxArray>& a_grids,
759 const amrex::Vector<amrex::DistributionMapping>& a_dmap,
761 const amrex::LPInfo& a_info,
762 const amrex::Vector<amrex::FabFactory<amrex::FArrayBox> const*>& a_factory)
763{
764 m_bc = &a_bc;
765
766 std::array<int, AMREX_SPACEDIM> is_periodic = m_bc->IsPeriodic();
767
768 MLCellLinOp::define(a_geom, a_grids, a_dmap, a_info, a_factory);
769
770 Util::Warning(INFO, "This section of code has not been tested.");
771 for (int n = 0; n < getNComp(); n++)
772 {
773 m_lobc.push_back({ AMREX_D_DECL(is_periodic[0] ? amrex::LinOpBCType::Periodic : amrex::LinOpBCType::Dirichlet,
774 is_periodic[1] ? amrex::LinOpBCType::Periodic : amrex::LinOpBCType::Dirichlet,
775 is_periodic[2] ? amrex::LinOpBCType::Periodic : amrex::LinOpBCType::Dirichlet) });
776 m_hibc.push_back({ AMREX_D_DECL(is_periodic[0] ? amrex::LinOpBCType::Periodic : amrex::LinOpBCType::Dirichlet,
777 is_periodic[1] ? amrex::LinOpBCType::Periodic : amrex::LinOpBCType::Dirichlet,
778 is_periodic[2] ? amrex::LinOpBCType::Periodic : amrex::LinOpBCType::Dirichlet) });
779 }
780
781 for (int ilev = 0; ilev < a_geom.size(); ++ilev)
782 setLevelBC(ilev, nullptr);
783
784}
785
786
787void
789{
790 MLCellLinOp::prepareForSolve();
791}
792
793Operator<Grid::Cell>::BndryCondLoc::BndryCondLoc(const amrex::BoxArray& ba, const amrex::DistributionMapping& dm)
794 : bcond(ba, dm),
795 bcloc(ba, dm)
796{
797}
798
799void
800Operator<Grid::Cell>::BndryCondLoc::setLOBndryConds(const amrex::Geometry& /*geom*/, const amrex::Real* /*dx*/,
801 const amrex::Array<BCType, AMREX_SPACEDIM>& /*lobc*/,
802 const amrex::Array<BCType, AMREX_SPACEDIM>& /*hibc*/,
803 int /*ratio*/, const amrex::RealVect& /*a_loc*/)
804{
805 Util::Warning(INFO, "This code has not been properlyt tested");
806}
807
808
809void
811{
812 for (int i = 0; i < m_num_a_fabs; i++)
813 {
814 for (int amrlev = m_num_amr_levels - 1; amrlev > 0; --amrlev)
815 {
816 auto& fine_a_coeffs = m_a_coeffs[i][amrlev];
817 averageDownCoeffsSameAmrLevel(fine_a_coeffs);
818 }
819 averageDownCoeffsSameAmrLevel(m_a_coeffs[i][0]);
820 }
821}
822
823void
825{
826 int nmglevs = a.size();
827 for (int mglev = 1; mglev < nmglevs; ++mglev)
828 {
829 amrex::average_down(a[mglev - 1], a[mglev], 0, a[0].nComp(), mg_coarsen_ratio);
830 }
831}
832
833
834
835const amrex::FArrayBox&
836Operator<Grid::Cell>::GetFab(const int num, const int amrlev, const int mglev, const amrex::MFIter& mfi) const
837{
838 return m_a_coeffs[num][amrlev][mglev][mfi];
839}
840
841
842void
843Operator<Grid::Cell>::RegisterNewFab(amrex::Vector<amrex::MultiFab>& input)
844{
845 /// \todo assertions here
846 m_a_coeffs.resize(m_a_coeffs.size() + 1);
847 m_a_coeffs[m_num_a_fabs].resize(m_num_amr_levels);
848 for (int amrlev = 0; amrlev < m_num_amr_levels; ++amrlev)
849 {
850 m_a_coeffs[m_num_a_fabs][amrlev].resize(m_num_mg_levels[amrlev]);
851 for (int mglev = 0; mglev < m_num_mg_levels[amrlev]; ++mglev)
852 m_a_coeffs[m_num_a_fabs][amrlev][mglev].define(m_grids[amrlev][mglev],
853 m_dmap[amrlev][mglev],
854 input[amrlev].nComp(),
855 input[amrlev].nGrow());
856
857 amrex::MultiFab::Copy(m_a_coeffs[m_num_a_fabs][amrlev][0],
858 input[amrlev], 0, 0,
859 input[amrlev].nComp(),
860 input[amrlev].nGrow());
861 }
862 m_num_a_fabs++;
863}
864
865
866void
867Operator<Grid::Cell>::RegisterNewFab(amrex::Vector<std::unique_ptr<amrex::MultiFab> >& input)
868{
869 /// \todo assertions here
870 m_a_coeffs.resize(m_a_coeffs.size() + 1);
871 m_a_coeffs[m_num_a_fabs].resize(m_num_amr_levels);
872 for (int amrlev = 0; amrlev < m_num_amr_levels; ++amrlev)
873 {
874 m_a_coeffs[m_num_a_fabs][amrlev].resize(m_num_mg_levels[amrlev]);
875 for (int mglev = 0; mglev < m_num_mg_levels[amrlev]; ++mglev)
876 m_a_coeffs[m_num_a_fabs][amrlev][mglev].define(m_grids[amrlev][mglev],
877 m_dmap[amrlev][mglev],
878 input[amrlev]->nComp(),
879 input[amrlev]->nGrow());
880
881 amrex::MultiFab::Copy(m_a_coeffs[m_num_a_fabs][amrlev][0],
882 *input[amrlev], 0, 0,
883 input[amrlev]->nComp(),
884 input[amrlev]->nGrow());
885 }
886 m_num_a_fabs++;
887}
888
889
890}
#define TEST(x)
Definition Util.H:25
#define INFO
Definition Util.H:24
Definition BC.H:43
virtual amrex::Array< int, AMREX_SPACEDIM > IsPeriodic()
Definition BC.H:114
static std::string Yellow
Definition Color.H:22
static std::string Reset
Definition Color.H:8
Documentation for operator namespace.
Definition Diagonal.cpp:14
constexpr amrex::IntVect AMREX_D_DECL(Operator< Grid::Cell >::dx, Operator< Grid::Cell >::dy, Operator< Grid::Cell >::dz)
amrex::Real Scalar
Definition Base.H:19
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE void Assert(const char *file, const char *func, int line, const char *smt, bool pass, Args const &... args)
Definition Util.H:60
void Warning(std::string file, std::string func, int line, Args const &... args)
Definition Util.H:213
void Message(std::string file, std::string func, int line, Args const &... args)
Definition Util.H:140
AMREX_GPU_HOST_DEVICE void Abort(const char *msg)
Definition Util.cpp:406
AMREX_FORCE_INLINE void RealFillBoundary(amrex::FabArray< amrex::BaseFab< T > > &a_mf, const amrex::Geometry &, const int nghost=2)
Definition Util.H:350