include/pops/numerics/elliptic/mg/composite_fac_poisson.hpp Source FileΒΆ

adc_cpp: include/pops/numerics/elliptic/mg/composite_fac_poisson.hpp Source File
adc_cpp 0.3.0
Model-free C++23 core for coupled hyperbolic-elliptic systems on adaptive (AMR) meshes, with MPI and GPU (Kokkos) backends
composite_fac_poisson.hpp
Go to the documentation of this file.
1#pragma once
2
11#include <pops/mesh/layout/refinement.hpp> // average_down, coarsen_index
12#include <pops/numerics/elliptic/mg/geometric_mg.hpp> // coarse solver (geometric multigrid)
13#include <pops/numerics/elliptic/poisson/poisson_operator.hpp> // apply_laplacian (residual, reads the already-filled ghosts)
14#include <pops/numerics/time/amr/levels/amr_patch_range.hpp> // PatchRange, CoverageMask (coarse footprint of a patch)
15
16#include <algorithm>
17#include <cmath>
18#include <cstdio>
19#include <stdexcept>
20#include <vector>
21
61
62namespace pops {
63
64namespace detail {
65
70POPS_HD inline Real fac_bilerp_coarse(const ConstArray4& C, int i, int j, int r) {
71 const Real fx = (Real(i) + Real(0.5)) / Real(r) - Real(0.5);
72 const Real fy = (Real(j) + Real(0.5)) / Real(r) - Real(0.5);
73 const int Ic = static_cast<int>(std::floor(fx));
74 const int Jc = static_cast<int>(std::floor(fy));
75 const Real tx = fx - Real(Ic), ty = fy - Real(Jc);
76 const Real c00 = C(Ic, Jc, 0), c10 = C(Ic + 1, Jc, 0);
77 const Real c01 = C(Ic, Jc + 1, 0), c11 = C(Ic + 1, Jc + 1, 0);
78 return (Real(1) - tx) * (Real(1) - ty) * c00 + tx * (Real(1) - ty) * c10 +
79 (Real(1) - tx) * ty * c01 + tx * ty * c11;
80}
81
82} // namespace detail
83
88 public:
95 CompositeFacPoisson(const Geometry& geom_c, const BoxArray& ba_c, const BCRec& bc,
96 const Box2D& fine_box, int ratio = 2)
97 : CompositeFacPoisson(geom_c, ba_c, bc, BoxArray(std::vector<Box2D>{fine_box}), ratio) {}
98
102 CompositeFacPoisson(const Geometry& geom_c, const BoxArray& ba_c, const BCRec& bc,
103 const BoxArray& fine_boxes, int ratio = 2)
104 : geom_c_(geom_c),
105 geom_f_(geom_c.refine(ratio)),
106 ba_c_(ba_c),
107 dm_c_(ba_c.size(), n_ranks()),
108 bc_(bc),
109 ratio_(ratio),
110 ba_f_(fine_boxes),
111 dm_f_(fine_boxes.size(), n_ranks()),
112 mg_(geom_c, ba_c, bc, {}, /*replicated=*/true),
113 phi_c_(ba_c, dm_c_, 1, 1),
114 phi_f_(ba_f_, dm_f_, 1, 1),
115 f_c_(ba_c, dm_c_, 1, 0),
116 f_f_(ba_f_, dm_f_, 1, 0),
117 res_c_(ba_c, dm_c_, 1, 0),
118 eps_c_(ba_c, dm_c_, 1, 1),
119 eps_f_(ba_f_, dm_f_, 1, 1),
120 axy_c_(ba_c, dm_c_, 1, 1),
121 ayx_c_(ba_c, dm_c_, 1, 1),
122 axy_f_(ba_f_, dm_f_, 1, 1),
123 ayx_f_(ba_f_, dm_f_, 1, 1),
124 cov_(Box2D::from_extents(geom_c.domain.nx(), geom_c.domain.ny())) {
125 require_separated_patches(
126 fine_boxes); // guard: NON adjacent patches (fine-fine join = Phase 4b)
127 // coarse footprints (covered cells) PER PATCH: PatchRange (lo/2 .. (hi-1)/2). The global coarse
128 // coverage = UNION of the footprints (any gap between disjoint patches stays NON covered).
129 for (int g = 0; g < fine_boxes.size(); ++g)
130 patch_coarse_.push_back(PatchRange(fine_boxes[g]).box());
131 for (const Box2D& pc : patch_coarse_)
132 cov_.mark(pc);
133 phi_c_.set_val(Real(0));
134 phi_f_.set_val(Real(0));
135 eps_c_.set_val(Real(1)); // default permittivity 1 -> operator = Laplacian (scalar)
136 eps_f_.set_val(Real(1));
137 axy_c_.set_val(Real(0)); // default cross terms 0 -> diagonal block only
138 ayx_c_.set_val(Real(0));
139 axy_f_.set_val(Real(0));
140 ayx_f_.set_val(Real(0));
141 }
142
144 return f_c_;
145 }
146 MultiFab& rhs_fine() { return f_f_; }
147 MultiFab& phi_coarse() { return phi_c_; }
148 MultiFab& phi_fine() { return phi_f_; }
152 MultiFab& eps_coarse() { return eps_c_; }
153 MultiFab& eps_fine() { return eps_f_; }
154 void use_variable_coefficient(bool v) { has_eps_ = v; }
160 MultiFab& a_xy_coarse() { return axy_c_; }
161 MultiFab& a_yx_coarse() { return ayx_c_; }
162 MultiFab& a_xy_fine() { return axy_f_; }
163 MultiFab& a_yx_fine() { return ayx_f_; }
164 void use_cross_terms(bool v) { has_cross_ = v; }
166 const Box2D& patch_coarse() const { return patch_coarse_[0]; }
168 const Box2D& patch_coarse(int g) const { return patch_coarse_[g]; }
170 int n_fine_patches() const { return ba_f_.size(); }
171
172 void set_verbose(bool v) { verbose_ = v; }
175 void set_two_way(bool v) { two_way_ = v; }
176
179 Real solve(int max_iters = 30, int fine_sweeps = 400, Real tol = 1e-9) {
180 // VARIABLE COEFFICIENT (condensed Schur operator B_z=0): sets eps on the coarse solver and
181 // fills the eps ghosts PER LEVEL. eps_c ghosts = zero-gradient (coeff_bc Foextrap, like the
182 // Schur builder); eps_f C-F ghosts = bilerp of eps_c (consistency of the coefficient flux across
183 // the interface). Without variable coefficient -> scalar Laplacian operator (Phase 1, bit-identical).
184 if (has_eps_) {
185 device_fence();
186 fill_ghosts(eps_c_, geom_c_.domain, coeff_bc(bc_));
187 fill_cf_coarse_to_fine(eps_c_, eps_f_);
188 mg_.set_epsilon(eps_c_);
189 }
190 if (has_cross_) { // FULL tensor (Schur B_z != 0): cross terms on both solvers + ghosts.
191 device_fence();
192 fill_ghosts(axy_c_, geom_c_.domain, coeff_bc(bc_));
193 fill_ghosts(ayx_c_, geom_c_.domain, coeff_bc(bc_));
194 fill_cf_coarse_to_fine(axy_c_, axy_f_);
195 fill_cf_coarse_to_fine(ayx_c_, ayx_f_);
196 mg_.set_cross_terms(axy_c_, ayx_c_);
197 }
198 // 0) initial coarse solve (gives a phi_c for the 1st C-F ghost).
199 copy0(mg_.rhs(), f_c_);
200 mg_.phi().set_val(Real(0));
201 mg_.solve(Real(1e-12), 100);
202 copy0(phi_c_, mg_.phi());
203
204 // 1) bilinear C-F ghosts + fine solve (base ONE-WAY).
205 refresh_fine(fine_sweeps);
206
207 Real rnorm = composite_coarse_residual();
208 if (verbose_ && my_rank() == 0)
209 std::fprintf(stderr, "[FAC] init r_c=%.4e\n", rnorm);
210 if (!two_way_) {
211 last_residual_ = rnorm;
212 return rnorm;
213 }
214
215 // 2) FAC two-way iterations: coarse correction (C-F flux) then re-solve fine.
216 for (int it = 0; it < max_iters; ++it) {
217 if (rnorm < tol)
218 break;
219 // coarse correction: Lap e_c = r_c (homogeneous Dirichlet), phi_c += e_c (non covered).
220 copy0(mg_.rhs(), res_c_);
221 mg_.phi().set_val(Real(0));
222 mg_.solve(Real(1e-12), 100);
223 add_uncovered(phi_c_, mg_.phi());
224 // re-ghost + re-solve fine on the corrected phi_c.
225 refresh_fine(fine_sweeps);
226 rnorm = composite_coarse_residual();
227 if (verbose_ && my_rank() == 0)
228 std::fprintf(stderr, "[FAC] it=%d r_c=%.4e\n", it, rnorm);
229 }
230 last_residual_ = rnorm;
231 return rnorm;
232 }
233
234 Real last_residual() const { return last_residual_; }
235
236 private:
238 void copy0(MultiFab& dst, const MultiFab& src) {
239 device_fence();
240 for (int li = 0; li < dst.local_size(); ++li) {
241 Array4 d = dst.fab(li).array();
242 const ConstArray4 s = src.fab(li).const_array();
243 const Box2D b = dst.box(li);
244 for (int j = b.lo[1]; j <= b.hi[1]; ++j)
245 for (int i = b.lo[0]; i <= b.hi[0]; ++i)
246 d(i, j, 0) = s(i, j, 0);
247 }
248 }
249
251 void add_uncovered(MultiFab& phi, const MultiFab& e) {
252 device_fence();
253 for (int li = 0; li < phi.local_size(); ++li) {
254 Array4 p = phi.fab(li).array();
255 const ConstArray4 ec = e.fab(li).const_array();
256 const Box2D b = phi.box(li);
257 for (int j = b.lo[1]; j <= b.hi[1]; ++j)
258 for (int i = b.lo[0]; i <= b.hi[0]; ++i)
259 if (!cov_.covered(i, j))
260 p(i, j, 0) += ec(i, j, 0);
261 }
262 }
263
269 static void require_separated_patches(const BoxArray& fine_boxes) {
270 const int N = fine_boxes.size();
271 for (int g = 0; g < N; ++g) {
272 const Box2D ag = PatchRange(fine_boxes[g]).box();
273 for (int h = g + 1; h < N; ++h) {
274 const Box2D bh = PatchRange(fine_boxes[h]).box();
275 if (!ag.grow(1).intersect(bh).empty())
276 throw std::runtime_error(
277 "CompositeFacPoisson: adjacent or overlapping fine patches (coarse footprints "
278 "separated by less than one cell); the multi-patch fine-fine join is Phase 4b -- "
279 "require disjoint patches separated by at least one coarse cell.");
280 }
281 }
282 }
283
287 void fill_cf_ghosts() {
288 const ConstArray4 C = phi_c_.fab(0).const_array(); // replicated mono-box coarse
289 const int ng = phi_f_.n_grow();
290 for (int li = 0; li < phi_f_.local_size(); ++li) {
291 Array4 F = phi_f_.fab(li).array();
292 const Box2D vb = phi_f_.box(li);
293 for (int j = vb.lo[1] - ng; j <= vb.hi[1] + ng; ++j)
294 for (int i = vb.lo[0] - ng; i <= vb.hi[0] + ng; ++i) {
295 const bool inside = (i >= vb.lo[0] && i <= vb.hi[0] && j >= vb.lo[1] && j <= vb.hi[1]);
296 if (inside)
297 continue; // ghosts only
298 F(i, j, 0) = detail::fac_bilerp_coarse(C, i, j, ratio_);
299 }
300 }
301 }
302
306 void fill_cf_coarse_to_fine(const MultiFab& coarse, MultiFab& fine) {
307 const ConstArray4 C = coarse.fab(0).const_array(); // replicated mono-box coarse
308 const int ng = fine.n_grow();
309 for (int li = 0; li < fine.local_size(); ++li) {
310 Array4 F = fine.fab(li).array();
311 const Box2D vb = fine.box(li);
312 for (int j = vb.lo[1] - ng; j <= vb.hi[1] + ng; ++j)
313 for (int i = vb.lo[0] - ng; i <= vb.hi[0] + ng; ++i) {
314 const bool inside = (i >= vb.lo[0] && i <= vb.hi[0] && j >= vb.lo[1] && j <= vb.hi[1]);
315 if (inside)
316 continue;
317 F(i, j, 0) = detail::fac_bilerp_coarse(C, i, j, ratio_);
318 }
319 }
320 }
321
324 static BCRec coeff_bc(const BCRec& b) {
325 auto fo = [](BCType t) { return t == BCType::Periodic ? t : BCType::Foextrap; };
326 BCRec c;
327 c.xlo = fo(b.xlo);
328 c.xhi = fo(b.xhi);
329 c.ylo = fo(b.ylo);
330 c.yhi = fo(b.yhi);
331 return c;
332 }
333
336 Real sor_omega(const Box2D& b) const {
337 const int N = std::max(b.nx(), b.ny());
338 return Real(2) / (Real(1) + std::sin(Real(kPi_) / Real(N)));
339 }
340
342 void refresh_fine(int sweeps) {
343 device_fence();
344 fill_ghosts(phi_c_, geom_c_.domain,
345 bc_); // phi_c physical ghosts (the bilerp reads up to the border)
346 fill_cf_ghosts();
347 fine_sor(sweeps);
348 average_down(phi_f_, phi_c_,
349 ratio_); // consistency: coarse covered = fine average (multi-box OK)
350 }
351
356 void fine_sor(int sweeps) {
357 const Real idx2 = Real(1) / (geom_f_.dx() * geom_f_.dx());
358 const Real idy2 = Real(1) / (geom_f_.dy() * geom_f_.dy());
359 const bool he = has_eps_;
360 const bool hc = has_cross_;
361 const Real idx = Real(1) / geom_f_.dx(), idy = Real(1) / geom_f_.dy(); // cross_div: 1/dx, 1/dy
362 for (int li = 0; li < phi_f_.local_size(); ++li) {
363 const Box2D vb = phi_f_.box(li);
364 const Real omega = sor_omega(vb);
365 Array4 P = phi_f_.fab(li).array();
366 const ConstArray4 Pc =
367 phi_f_.fab(li).const_array(); // const view (same memory) for cross stencil
368 const ConstArray4 F = f_f_.fab(li).const_array();
369 const ConstArray4 E = eps_f_.fab(li).const_array();
370 const ConstArray4 AXY = axy_f_.fab(li).const_array();
371 const ConstArray4 AYX = ayx_f_.fab(li).const_array();
372 for (int s = 0; s < sweeps; ++s)
373 for (int color = 0; color < 2; ++color)
374 for (int j = vb.lo[1]; j <= vb.hi[1]; ++j)
375 for (int i = vb.lo[0]; i <= vb.hi[0]; ++i) {
376 if (((i + j) & 1) != color)
377 continue;
378 // FACE permittivities (harmonic mean of the 2 centers); eps==1 -> faces == 1.
379 const Real exm = he ? eps_harmonic(E(i, j, 0), E(i - 1, j, 0)) : Real(1);
380 const Real exp = he ? eps_harmonic(E(i, j, 0), E(i + 1, j, 0)) : Real(1);
381 const Real eym = he ? eps_harmonic(E(i, j, 0), E(i, j - 1, 0)) : Real(1);
382 const Real eyp = he ? eps_harmonic(E(i, j, 0), E(i, j + 1, 0)) : Real(1);
383 const Real diag = (exm + exp) * idx2 + (eym + eyp) * idy2;
384 const Real nb = (exm * P(i - 1, j, 0) + exp * P(i + 1, j, 0)) * idx2 +
385 (eym * P(i, j - 1, 0) + eyp * P(i, j + 1, 0)) * idy2;
386 // EXPLICIT cross terms (9 points, read from the current P): div(A grad phi) =
387 // diag_block + cross. We solve diag_block(P) + cross(P) = f -> P = (nb + cross - f)/diag.
388 const Real cross =
389 hc ? detail::cross_div(Pc, true, AXY, true, AYX, i, j, idx, idy) : Real(0);
390 const Real pgs = (nb + cross - F(i, j, 0)) / diag;
391 P(i, j, 0) = (Real(1) - omega) * P(i, j, 0) +
392 omega * pgs; // over-relax (under-relax if strong)
393 }
394 }
395 }
396
399 Real composite_coarse_residual() {
400 device_fence();
401 fill_ghosts(phi_c_, geom_c_.domain, bc_);
402 // r_c = f_c - div(A grad phi_c) (apply_laplacian reads the already-filled ghosts; eps + cross if active).
403 // The cross terms are read also on the COVERED cells (= fine average after average_down) -> the
404 // 9-point stencil stays consistent at the interface; only the NORMAL flux is explicitly joined C-F
405 // (the cross flux, tangential and small for the Schur step, is carried by the volume stencil).
406 MultiFab lap(ba_c_, dm_c_, 1, 0);
407 apply_laplacian(phi_c_, geom_c_, lap, /*coef=*/nullptr, has_eps_ ? &eps_c_ : nullptr,
408 /*kappa=*/nullptr, /*eps_y=*/nullptr, has_cross_ ? &axy_c_ : nullptr,
409 has_cross_ ? &ayx_c_ : nullptr);
410 device_fence();
411 Array4 R = res_c_.fab(0).array();
412 const ConstArray4 LAP = lap.fab(0).const_array();
413 const ConstArray4 FC = f_c_.fab(0).const_array();
414 const Box2D b = res_c_.box(0);
415 for (int j = b.lo[1]; j <= b.hi[1]; ++j)
416 for (int i = b.lo[0]; i <= b.hi[0]; ++i)
417 R(i, j, 0) = cov_.covered(i, j) ? Real(0) : (FC(i, j, 0) - LAP(i, j, 0));
418
419 // C-F FLUX CORRECTION, PER FINE PATCH. On each coarse cell BORDERING a patch (non covered,
420 // covered neighbor), we REPLACE the contribution of the C-F face in div(eps grad phi_c) by the
421 // FINE contribution (conservative sum of the r fine faces, harmonic face eps): r_c += (coarse
422 // - fine). Since the patches are separated by at least one coarse cell, each border is a TRUE
423 // coarse-fine join; the test !cov_.covered(I, J) defensively skips a bordering cell that would be
424 // covered by ANOTHER patch (impossible under the guard, but robust: a covered bordering
425 // cell is already interior to another patch, its residual stays 0). A cell SEPARATING two
426 // patches (right border of one, left border of the other) gets TWO corrections, one per face: correct.
427 const ConstArray4 PC = phi_c_.fab(0).const_array();
428 const ConstArray4 EC = eps_c_.fab(0).const_array();
429 const bool he = has_eps_;
430 const Real idx2 = Real(1) / (geom_c_.dx() * geom_c_.dx());
431 const Real idy2 = Real(1) / (geom_c_.dy() * geom_c_.dy());
432 const int r = ratio_;
433 for (int g = 0; g < phi_f_.local_size(); ++g) {
434 const ConstArray4 PF = phi_f_.fab(g).const_array();
435 const ConstArray4 EF = eps_f_.fab(g).const_array();
436 const int Ic0 = patch_coarse_[g].lo[0], Ic1 = patch_coarse_[g].hi[0];
437 const int Jc0 = patch_coarse_[g].lo[1], Jc1 = patch_coarse_[g].hi[1];
438 // Faces NORMAL TO X: bordering columns I = Ic0-1 (covered +x face) and I = Ic1+1 (-x face).
439 for (int J = Jc0; J <= Jc1; ++J) {
440 if (!cov_.covered(Ic0 - 1, J)) { // left: cell (Ic0-1, J), fine face at i = r*Ic0.
441 const int I = Ic0 - 1;
442 const Real efc = he ? eps_harmonic(EC(I, J, 0), EC(I + 1, J, 0)) : Real(1);
443 const Real coarse_c = efc * (PC(I + 1, J, 0) - PC(I, J, 0)) * idx2;
444 Real fine_sum = Real(0);
445 for (int t = 0; t < r; ++t) {
446 const int jf = r * J + t;
447 const Real eff =
448 he ? eps_harmonic(EF(r * Ic0 - 1, jf, 0), EF(r * Ic0, jf, 0)) : Real(1);
449 fine_sum += eff * (PF(r * Ic0, jf, 0) - PF(r * Ic0 - 1, jf, 0)); // interior - ghost
450 }
451 R(I, J, 0) += coarse_c - fine_sum * idx2;
452 }
453 if (!cov_.covered(Ic1 + 1, J)) { // right: cell (Ic1+1, J), fine faces at i = r*Ic1+r.
454 const int I = Ic1 + 1;
455 const Real efc = he ? eps_harmonic(EC(I, J, 0), EC(I - 1, J, 0)) : Real(1);
456 const Real coarse_c = efc * (PC(I - 1, J, 0) - PC(I, J, 0)) * idx2;
457 Real fine_sum = Real(0);
458 for (int t = 0; t < r; ++t) {
459 const int jf = r * J + t;
460 const Real eff =
461 he ? eps_harmonic(EF(r * Ic1 + r - 1, jf, 0), EF(r * Ic1 + r, jf, 0)) : Real(1);
462 fine_sum += eff * (PF(r * Ic1 + r - 1, jf, 0) - PF(r * Ic1 + r, jf, 0));
463 }
464 R(I, J, 0) += coarse_c - fine_sum * idx2;
465 }
466 }
467 // Faces NORMAL TO Y: bordering rows J = Jc0-1 (+y face) and J = Jc1+1 (-y face).
468 for (int I = Ic0; I <= Ic1; ++I) {
469 if (!cov_.covered(I, Jc0 - 1)) {
470 const int J = Jc0 - 1;
471 const Real efc = he ? eps_harmonic(EC(I, J, 0), EC(I, J + 1, 0)) : Real(1);
472 const Real coarse_c = efc * (PC(I, J + 1, 0) - PC(I, J, 0)) * idy2;
473 Real fine_sum = Real(0);
474 for (int t = 0; t < r; ++t) {
475 const int iff = r * I + t;
476 const Real eff =
477 he ? eps_harmonic(EF(iff, r * Jc0 - 1, 0), EF(iff, r * Jc0, 0)) : Real(1);
478 fine_sum += eff * (PF(iff, r * Jc0, 0) - PF(iff, r * Jc0 - 1, 0));
479 }
480 R(I, J, 0) += coarse_c - fine_sum * idy2;
481 }
482 if (!cov_.covered(I, Jc1 + 1)) {
483 const int J = Jc1 + 1;
484 const Real efc = he ? eps_harmonic(EC(I, J, 0), EC(I, J - 1, 0)) : Real(1);
485 const Real coarse_c = efc * (PC(I, J - 1, 0) - PC(I, J, 0)) * idy2;
486 Real fine_sum = Real(0);
487 for (int t = 0; t < r; ++t) {
488 const int iff = r * I + t;
489 const Real eff =
490 he ? eps_harmonic(EF(iff, r * Jc1 + r - 1, 0), EF(iff, r * Jc1 + r, 0)) : Real(1);
491 fine_sum += eff * (PF(iff, r * Jc1 + r - 1, 0) - PF(iff, r * Jc1 + r, 0));
492 }
493 R(I, J, 0) += coarse_c - fine_sum * idy2;
494 }
495 }
496 }
497
498 // inf norm of the residual over the NON covered cells.
499 Real nrm = Real(0);
500 for (int j = b.lo[1]; j <= b.hi[1]; ++j)
501 for (int i = b.lo[0]; i <= b.hi[0]; ++i)
502 if (!cov_.covered(i, j))
503 nrm = std::fmax(nrm, std::fabs(R(i, j, 0)));
504 return nrm;
505 }
506
507 Geometry geom_c_, geom_f_;
508 BoxArray ba_c_;
509 DistributionMapping dm_c_;
510 BCRec bc_;
511 int ratio_;
512 BoxArray ba_f_;
513 DistributionMapping dm_f_;
514 GeometricMG mg_;
515 MultiFab phi_c_, phi_f_, f_c_, f_f_, res_c_;
516 MultiFab eps_c_, eps_f_;
517 MultiFab axy_c_, ayx_c_, axy_f_, ayx_f_;
518 std::vector<Box2D> patch_coarse_;
519 CoverageMask cov_;
520 Real last_residual_ = 0;
521 bool has_eps_ = false;
522 bool has_cross_ = false;
523 bool verbose_ = false;
524 bool two_way_ = true;
525 static constexpr Real kPi_ = Real(3.14159265358979323846);
526};
527
528} // namespace pops
Named types of the multi-patch coarse-fine interface: PatchRange (coarse footprint of a fine patch),...
Box2D: the integer index space of a 2D cell-centered Cartesian grid.
BoxArray: the set of boxes tiling a level (disjoint, covering).
Ordered list of boxes tiling a level.
Definition box_array.hpp:22
int size() const
Number of boxes in the tiling.
Definition box_array.hpp:42
2-level COMPOSITE FAC Poisson solver (scalar).
Definition composite_fac_poisson.hpp:87
MultiFab & a_yx_fine()
Definition composite_fac_poisson.hpp:163
void use_variable_coefficient(bool v)
Definition composite_fac_poisson.hpp:154
void set_two_way(bool v)
true: iterate the FAC two-way coupling (C-F flux correction + coarse correction).
Definition composite_fac_poisson.hpp:175
int n_fine_patches() const
Number of fine patches (size of the fine BoxArray).
Definition composite_fac_poisson.hpp:170
CompositeFacPoisson(const Geometry &geom_c, const BoxArray &ba_c, const BCRec &bc, const Box2D &fine_box, int ratio=2)
MONO-PATCH CTOR (Phase 1): DELEGATES to the multi-patch ctor with a fine BoxArray of a single box,...
Definition composite_fac_poisson.hpp:95
MultiFab & rhs_fine()
fine right-hand side f_f (div(eps grad phi_f) = f_f)
Definition composite_fac_poisson.hpp:146
void set_verbose(bool v)
Definition composite_fac_poisson.hpp:172
void use_cross_terms(bool v)
Definition composite_fac_poisson.hpp:164
MultiFab & a_xy_coarse()
Cross terms a_xy / a_yx (at cell centers) PER LEVEL: FULL tensor A = diag(eps,eps) + [[0,...
Definition composite_fac_poisson.hpp:160
MultiFab & phi_fine()
Definition composite_fac_poisson.hpp:148
CompositeFacPoisson(const Geometry &geom_c, const BoxArray &ba_c, const BCRec &bc, const BoxArray &fine_boxes, int ratio=2)
MULTI-PATCH CTOR (Phase 4a).
Definition composite_fac_poisson.hpp:102
MultiFab & a_yx_coarse()
Definition composite_fac_poisson.hpp:161
Real solve(int max_iters=30, int fine_sweeps=400, Real tol=1e-9)
Solves the composite system.
Definition composite_fac_poisson.hpp:179
MultiFab & phi_coarse()
Definition composite_fac_poisson.hpp:147
MultiFab & rhs_coarse()
coarse right-hand side f_c (div(eps grad phi_c) = f_c)
Definition composite_fac_poisson.hpp:143
MultiFab & a_xy_fine()
Definition composite_fac_poisson.hpp:162
Real last_residual() const
Definition composite_fac_poisson.hpp:234
const Box2D & patch_coarse(int g) const
Coarse footprint of fine patch g (0 <= g < n_fine_patches()).
Definition composite_fac_poisson.hpp:168
const Box2D & patch_coarse() const
Coarse footprint of the FIRST fine patch (mono-patch compat). Multi-patch: see patch_coarse(g).
Definition composite_fac_poisson.hpp:166
MultiFab & eps_fine()
Definition composite_fac_poisson.hpp:153
MultiFab & eps_coarse()
VARIABLE permittivity eps (at cell centers) PER LEVEL.
Definition composite_fac_poisson.hpp:152
ConstArray4 const_array() const
READ handle (POD device-copyable) over this Fab. Valid as long as the Fab lives.
Definition fab2d.hpp:96
Array4 array()
WRITE handle (POD device-copyable) over this Fab. Valid as long as the Fab lives.
Definition fab2d.hpp:91
int solve(Real rel_tol, int max_cycles, Real abs_tol=Real(0))
Definition geometric_mg.hpp:407
void set_epsilon(std::function< Real(Real, Real)> eps_fn)
Definition geometric_mg.hpp:249
MultiFab & rhs()
Definition geometric_mg.hpp:222
MultiFab & phi()
Definition geometric_mg.hpp:221
void set_cross_terms(std::function< Real(Real, Real)> a_xy_fn, std::function< Real(Real, Real)> a_yx_fn)
Definition geometric_mg.hpp:329
Field distributed over a level: decomposition (BoxArray) + distribution (DistributionMapping) + ncomp...
Definition multifab.hpp:33
Fab2D & fab(int li)
Local fab at index li (0 <= li < local_size()), for writing.
Definition multifab.hpp:67
int n_grow() const
Number of ghost layers.
Definition multifab.hpp:62
const Box2D & box(int li) const
VALID box of local fab li.
Definition multifab.hpp:71
void set_val(Real v)
Fills all cells (valid + ghosts) of every local fab with v.
Definition multifab.hpp:85
int local_size() const
Number of fabs OWNED by this rank (bound on local indices).
Definition multifab.hpp:65
DistributionMapping: maps each box (by global index) to its owning MPI rank.
for_each_cell and reductions: the parallelism SEAM over the cells of a Box2D; sync_host / sync_device...
GeometricMG: in-house geometric multigrid (V-cycle) for the elliptic operator, Gauss-Seidel smoother ...
Geometry: index-space (Box2D) <-> Cartesian physical-space mapping; PolarGeometry: SIBLING for a glob...
MultiFab: a field DISTRIBUTED over a level (equivalent of AMReX's MultiFab).
POPS_HD Real fac_bilerp_coarse(const ConstArray4 &C, int i, int j, int r)
BILINEAR interpolation of the coarse potential (cell-centered, C with ghosts) at the CENTER of the fi...
Definition composite_fac_poisson.hpp:70
POPS_HD Real cross_div(const ConstArray4 &p, bool hxy, const ConstArray4 &axy, bool hyx, const ConstArray4 &ayx, int i, int j, Real idx, Real idy)
Definition poisson_operator.hpp:79
Definition amr_hierarchy.hpp:29
void average_down(const MultiFab &fine, MultiFab &coarse, int r, MultiFab &cfine)
CONSERVATIVE average fine -> coarse (ratio r): coarse(I, J) = average of the r^2 fine cells of the bl...
Definition refinement.hpp:181
double Real
Definition types.hpp:30
int n_ranks()
Definition comm.hpp:139
void apply_laplacian(const MultiFab &phi, const Geometry &geom, MultiFab &lap, const MultiFab *coef=nullptr, const MultiFab *eps=nullptr, const MultiFab *kappa=nullptr, const MultiFab *eps_y=nullptr, const MultiFab *a_xy=nullptr, const MultiFab *a_yx=nullptr)
Definition poisson_operator.hpp:191
void device_fence()
Device barrier: waits for in-flight kernels to finish before a HOST access to unified memory.
Definition kokkos_env.hpp:43
int my_rank()
Definition comm.hpp:136
POPS_HD Real eps_harmonic(Real ec, Real ev)
Definition poisson_operator.hpp:40
BCType
Boundary condition type for a face: Periodic (handled by fill_boundary), Foextrap (zero gradient,...
Definition physical_bc.hpp:25
void fill_ghosts(MultiFab &mf, const Box2D &domain, const BCRec &bc)
COMPLETE ghost filling: fill_boundary (interior + periodic, periodicity deduced from bc) THEN fill_ph...
Definition physical_bc.hpp:227
PHYSICAL boundary conditions at the domain edge (BCType, BCRec, fill_physical_bc, fill_ghosts).
Free functions of the elliptic operator: apply_laplacian (matvec), poisson_residual (residual),...
AMR inter-level transfer operators (integer ratio r) + parallel_copy.
WRITE POD handle (raw pointer + strides) over a Fab2D buffer, indexed by (i, j, c) IN GLOBAL INDICES ...
Definition fab2d.hpp:29
Boundary conditions for the FOUR faces of the domain (type + associated Dirichlet value).
Definition physical_bc.hpp:29
2D integer index space, cell-centered.
Definition box2d.hpp:37
static Box2D from_extents(int nx, int ny)
Box [0, nx-1] x [0, ny-1] covering nx*ny cells from the index origin.
Definition box2d.hpp:42
int hi[2]
Definition box2d.hpp:39
int lo[2]
Definition box2d.hpp:38
READ-only handle (const counterpart of Array4): same layout and same contract (POD device-copyable,...
Definition fab2d.hpp:44
bool covered(int I, int J) const
Definition amr_patch_range.hpp:168
Cartesian geometry of a level: index domain + physical bounds [xlo, xhi] x [ylo, yhi].
Definition geometry.hpp:20
POPS_HD Real dy() const
Grid spacing in y (= (yhi - ylo) / domain.ny()). POPS_HD.
Definition geometry.hpp:33
POPS_HD Real dx() const
Grid spacing in x (= (xhi - xlo) / domain.nx()). POPS_HD.
Definition geometry.hpp:31
Box2D domain
Definition geometry.hpp:21
Definition amr_patch_range.hpp:42
Base scalar types and the POPS_HD macro (host+device portability).
#define POPS_HD
Definition types.hpp:25