include/pops/coupling/schur/amr/amr_condensed_schur_source_stepper.hpp Source FileΒΆ

adc_cpp: include/pops/coupling/schur/amr/amr_condensed_schur_source_stepper.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
amr_condensed_schur_source_stepper.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <pops/coupling/schur/source/condensed_schur_source_stepper.hpp> // CondensedSchurSourceStepper (#126) + detail kernels
5#include <pops/coupling/schur/core/schur_condensation.hpp> // ElectrostaticLorentzCondensation (assemble per level)
6#include <pops/numerics/elliptic/mg/composite_fac_poisson.hpp> // CompositeFacPoisson (composite FAC elliptic solve)
7#include <pops/numerics/time/amr/reflux/amr_reflux_mf.hpp> // mf_average_down_mb (fine -> coarse cascade)
8#include <pops/numerics/time/amr/levels/amr_subcycling.hpp> // AmrLevelMP (multi-patch hierarchy)
9
10#include <memory>
11#include <stdexcept>
12#include <string>
13#include <vector>
14
49
50namespace pops {
51
55 public:
63 AmrCondensedSchurSourceStepper(const VariableSet& vars, const Geometry& coarse_geom,
64 const BoxArray& coarse_ba, const BCRec& bcPhi, Real alpha,
65 int n_precond_vcycles = 1)
67 vars, vars.index_of(VariableRole::Density), vars.index_of(VariableRole::MomentumX),
68 vars.index_of(VariableRole::MomentumY), vars.index_of(VariableRole::Energy),
69 coarse_geom, coarse_ba, bcPhi, alpha, n_precond_vcycles) {}
70
73 AmrCondensedSchurSourceStepper(const VariableSet& vars, int c_rho, int c_mx, int c_my, int c_E,
74 const Geometry& coarse_geom, const BoxArray& coarse_ba,
75 const BCRec& bcPhi, Real alpha, int n_precond_vcycles = 1)
76 : vars_(vars),
77 coarse_geom_(coarse_geom),
78 coarse_ba_(coarse_ba),
79 bcPhi_(bcPhi),
80 alpha_(alpha),
81 c_rho_(c_rho),
82 c_mx_(c_mx),
83 c_my_(c_my),
84 c_E_(c_E),
85 coarse_(vars, c_rho, c_mx, c_my, c_E, coarse_geom, coarse_ba, bcPhi, alpha,
86 n_precond_vcycles) {}
87
91 void set_krylov(Real tol, int max_iters) { coarse_.set_krylov(tol, max_iters); }
92
94 bool has_energy() const { return coarse_.energy_comp() >= 0; }
95
105 void step(std::vector<AmrLevelMP>& levels, MultiFab& coarse_phi, const MultiFab& coarse_bz,
106 int c_bz, Real theta, Real dt) {
107 if (levels.empty())
108 return;
109 // A fine level EFFECTIVELY POPULATED (>= one patch) signals a multi-level hierarchy. NB: the
110 // compiled path (build_amr_compiled) ALWAYS allocates a seed fine level, EMPTY after regrid when
111 // no refinement is requested (refine_threshold disabled) -> levels.size() is 2 but the
112 // hierarchy is EFFECTIVELY mono-level. So we gate on the NUMBER OF fine PATCHES, not on
113 // levels.size(), to avoid refusing the mono-level case with an allocated but empty fine level.
114 int n_fine_patches = 0;
115 for (std::size_t k = 1; k < levels.size(); ++k)
116 n_fine_patches += static_cast<int>(levels[k].U.box_array().size());
117 if (n_fine_patches == 0) {
118 // MONO-LEVEL (no fine patch): COMPLETE uniform stage on the coarse level (assemble + solve +
119 // reconstruction + extrapolation + energy + ghosts), bit-for-bit identical to #126.
120 coarse_.step(levels[0].U, coarse_phi, coarse_bz, c_bz, theta, dt);
121 return;
122 }
123 // MULTI-LEVEL (Phase 4a): COMPOSITE condensed source stage -- the fine patches REALLY refine
124 // the elliptic (tensor Schur operator solved by FAC on coarse + fine), then velocity
125 // reconstruction PER LEVEL and average_down cascade. Phase 4a frame: 2 levels, 1..N fine patches
126 // disjoint NON ADJACENT (separated by at least one coarse cell -- guard imposed by the FAC),
127 // coarse replicated mono-block, MONO-RANK. Beyond that -> clear error (> 2 levels / MPI / multi-block
128 // = Phase 4b). The fine-fine join between adjacent patches is rejected at the FAC ctor (Phase 4b).
129 if (levels.size() != 2 || n_ranks() != 1)
130 throw std::runtime_error(
131 "AmrCondensedSchurSourceStepper: COMPOSITE condensed source stage wired for 2 levels + "
132 "NON ADJACENT multi-box fine patches, mono-rank ; > 2 levels / MPI / multi-block = Phase "
133 "4b.");
134 step_multilevel(levels, coarse_phi, coarse_bz, c_bz, theta, dt);
135 }
136
138 const KrylovResult& last_solve() const { return coarse_.last_solve(); }
139
140 int density_comp() const { return coarse_.density_comp(); }
141 int momentum_x_comp() const { return coarse_.momentum_x_comp(); }
142 int momentum_y_comp() const { return coarse_.momentum_y_comp(); }
143 int energy_comp() const { return coarse_.energy_comp(); }
144
145 private:
151 void step_multilevel(std::vector<AmrLevelMP>& levels, MultiFab& coarse_phi,
152 const MultiFab& coarse_bz, int c_bz, Real theta, Real dt) {
153 // COMPLETE fine BoxArray (1..N patches): the FAC is built on this tiling; the patches being separated
154 // by at least one coarse cell (FAC ctor guard), each edge is a true C-F join.
155 const BoxArray& fine_ba = levels[1].U.box_array();
156 ensure_fac(fine_ba);
157 const Geometry geom_c = coarse_geom_;
158 const Geometry geom_f = coarse_geom_.refine(kAmrRefRatio);
159 ElectrostaticLorentzCondensation builder(vars_, alpha_, theta, dt);
160
161 MultiFab& Uc = levels[0].U;
162 MultiFab& Uf = levels[1].U;
163 const BoxArray bac = Uc.box_array();
164 const DistributionMapping dmc = Uc.dmap();
165 const BoxArray baf = Uf.box_array();
166 const DistributionMapping dmf = Uf.dmap();
167
168 // --- B_z 1-component per level (coarse: extracted from coarse_bz; fine: bilerp of the coarse) ---
169 MultiFab bz_c(bac, dmc, 1, 1), bz_f(baf, dmf, 1, 1);
170 copy_comp(bz_c, coarse_bz, c_bz);
171 device_fence();
172 fill_ghosts(bz_c, geom_c.domain, coeff_bc(bcPhi_));
173 bilerp_coarse_to_fine(bz_f, bz_c); // fine B_z from the coarse (B0 uniform -> exact)
174
175 // --- phi^n per level (coarse = coarse_phi; fine = injected aux, levels[1].aux comp 0) ---
176 MultiFab phi_n_c(bac, dmc, 1, 1), phi_n_f(baf, dmf, 1, 1);
177 copy0(phi_n_c, coarse_phi);
178 copy0(phi_n_f, *levels[1].aux);
179
180 // --- v^n per level (before the solve: the reconstruction overwrites mom) ---
181 MultiFab vx_n_c(bac, dmc, 1, 0), vy_n_c(bac, dmc, 1, 0);
182 MultiFab vx_n_f(baf, dmf, 1, 0), vy_n_f(baf, dmf, 1, 0);
183 extract_v(Uc, vx_n_c, vy_n_c);
184 extract_v(Uf, vx_n_f, vy_n_f);
185
186 // --- operator + condensed RHS assembly PER LEVEL, into the composite solver fields ---
187 // eps_x == eps_y for the Schur (A_xx = A_yy = 1 + c rho/det): we write eps_x into the single eps of the
188 // composite and eps_y into a discarded scratch. f_composite = -rhs_schur (sign convention #126).
189 MultiFab eps_y_c(bac, dmc, 1, 1), eps_y_f(baf, dmf, 1, 1);
190 MultiFab rhs_c(bac, dmc, 1, 0), rhs_f(baf, dmf, 1, 0);
191 builder.assemble_operator(Uc, bz_c, geom_c, bcPhi_, fac_->eps_coarse(), eps_y_c,
192 fac_->a_xy_coarse(), fac_->a_yx_coarse());
193 builder.assemble_operator(Uf, bz_f, geom_f, bcPhi_, fac_->eps_fine(), eps_y_f,
194 fac_->a_xy_fine(), fac_->a_yx_fine());
195 {
196 MultiFab pn(bac, dmc, 1, 1);
197 copy0(pn, phi_n_c);
198 builder.assemble_rhs(pn, Uc, bz_c, geom_c, bcPhi_, rhs_c);
199 negate_into(fac_->rhs_coarse(), rhs_c);
200 }
201 {
202 MultiFab pn(baf, dmf, 1, 1);
203 copy0(pn, phi_n_f);
204 builder.assemble_rhs(pn, Uf, bz_f, geom_f, bcPhi_, rhs_f);
205 negate_into(fac_->rhs_fine(), rhs_f);
206 }
207
208 // --- COMPOSITE SOLVE: phi^{n+theta} per level (the fine patch refines the elliptic) ---
209 fac_->use_variable_coefficient(true);
210 fac_->use_cross_terms(true);
211 fac_->solve();
212
213 // --- velocity reconstruction + phi/v extrapolation + energy, PER LEVEL ---
214 reconstruct_level(Uc, fac_->phi_coarse(), phi_n_c, bz_c, vx_n_c, vy_n_c, geom_c, theta, dt,
215 /*fill_phi_ghosts=*/true);
216 reconstruct_level(Uf, fac_->phi_fine(), phi_n_f, bz_f, vx_n_f, vy_n_f, geom_f, theta, dt,
217 /*fill_phi_ghosts=*/false); // C-F ghosts already set by the composite solve
218
219 // coarse phi^{n+1} (extrapolated in place into fac_->phi_coarse()) -> published into coarse_phi.
220 copy0(coarse_phi, fac_->phi_coarse());
221
222 // --- fine -> coarse cascade: the COVERED coarse cells = 2x2 average of the fine cells (#169) ---
223 device_fence();
224 mf_average_down_mb(Uf, Uc);
225 device_fence();
226 fill_ghosts(coarse_phi, geom_c.domain, bcPhi_);
227 }
228
233 void reconstruct_level(MultiFab& state, MultiFab& phi_nt, const MultiFab& phi_n,
234 const MultiFab& bz, const MultiFab& vx_n, const MultiFab& vy_n,
235 const Geometry& geom, Real theta, Real dt, bool fill_phi_ghosts) {
236 const Real th_dt = theta * dt, inv_theta = Real(1) / theta;
237 const Real half_idx = Real(1) / (Real(2) * geom.dx());
238 const Real half_idy = Real(1) / (Real(2) * geom.dy());
239 device_fence();
240 if (fill_phi_ghosts)
241 fill_ghosts(phi_nt, geom.domain, bcPhi_);
242 device_fence();
243 MultiFab vx_t(state.box_array(), state.dmap(), 1, 0),
244 vy_t(state.box_array(), state.dmap(), 1, 0);
245 for (int li = 0; li < state.local_size(); ++li)
247 state.box(li),
248 detail::SchurReconstructKernel{
249 phi_nt.fab(li).const_array(), vx_n.fab(li).const_array(), vy_n.fab(li).const_array(),
250 bz.fab(li).const_array(), state.fab(li).array(), vx_t.fab(li).array(),
251 vy_t.fab(li).array(), th_dt, half_idx, half_idy, c_rho_, c_mx_, c_my_});
252 for (int li = 0; li < phi_nt.local_size(); ++li)
253 for_each_cell(phi_nt.box(li),
254 detail::SchurExtrapolateScalarKernel{phi_n.fab(li).const_array(),
255 phi_nt.fab(li).array(), inv_theta});
256 for (int li = 0; li < state.local_size(); ++li)
257 for_each_cell(state.box(li), detail::SchurExtrapolateVelocityKernel{
258 vx_n.fab(li).const_array(), vy_n.fab(li).const_array(),
259 vx_t.fab(li).array(), vy_t.fab(li).array(),
260 state.fab(li).array(), inv_theta, c_rho_, c_mx_, c_my_});
261 if (c_E_ >= 0)
262 for (int li = 0; li < state.local_size(); ++li)
263 for_each_cell(state.box(li), detail::SchurEnergyKernel{
264 vx_n.fab(li).const_array(), vy_n.fab(li).const_array(),
265 vx_t.fab(li).const_array(), vy_t.fab(li).const_array(),
266 state.fab(li).array(), c_rho_, c_E_});
267 device_fence();
268 fill_ghosts(state, geom.domain, coeff_bc(bcPhi_));
269 }
270
274 void ensure_fac(const BoxArray& fine_ba) {
275 if (fac_ && fac_fine_boxes_ == fine_ba.boxes())
276 return;
277 fac_ = std::make_unique<CompositeFacPoisson>(coarse_geom_, coarse_ba_, bcPhi_, fine_ba,
279 fac_fine_boxes_ = fine_ba.boxes();
280 }
281
283 static BCRec coeff_bc(const BCRec& b) {
284 auto fo = [](BCType t) { return t == BCType::Periodic ? t : BCType::Foextrap; };
285 BCRec c;
286 c.xlo = fo(b.xlo);
287 c.xhi = fo(b.xhi);
288 c.ylo = fo(b.ylo);
289 c.yhi = fo(b.yhi);
290 return c;
291 }
292
293 void copy0(MultiFab& dst, const MultiFab& src) {
294 device_fence();
295 for (int li = 0; li < dst.local_size(); ++li)
296 for_each_cell(dst.box(li),
297 detail::CopyComp0Kernel{dst.fab(li).array(), src.fab(li).const_array()});
298 }
299 void copy_comp(MultiFab& dst, const MultiFab& src, int c) { // dst comp0 <- src comp c
300 device_fence();
301 for (int li = 0; li < dst.local_size(); ++li)
302 for_each_cell(dst.box(li),
303 detail::CopyBzKernel{src.fab(li).const_array(), dst.fab(li).array(), c});
304 }
305 void negate_into(MultiFab& dst, const MultiFab& src) {
306 device_fence();
307 for (int li = 0; li < dst.local_size(); ++li)
308 for_each_cell(dst.box(li),
309 detail::NegateKernel{src.fab(li).const_array(), dst.fab(li).array()});
310 }
311 void extract_v(const MultiFab& state, MultiFab& vx, MultiFab& vy) {
312 device_fence();
313 for (int li = 0; li < state.local_size(); ++li)
314 for_each_cell(state.box(li),
315 detail::ExtractVelocityKernel{state.fab(li).const_array(), vx.fab(li).array(),
316 vy.fab(li).array(), c_rho_, c_mx_, c_my_});
317 }
320 void bilerp_coarse_to_fine(MultiFab& fine, const MultiFab& coarse) {
321 device_fence();
322 const ConstArray4 C = coarse.fab(0).const_array();
323 const int ng = fine.n_grow();
324 for (int li = 0; li < fine.local_size(); ++li) {
325 Array4 F = fine.fab(li).array();
326 const Box2D vb = fine.box(li);
327 for (int j = vb.lo[1] - ng; j <= vb.hi[1] + ng; ++j)
328 for (int i = vb.lo[0] - ng; i <= vb.hi[0] + ng; ++i)
329 F(i, j, 0) = detail::fac_bilerp_coarse(C, i, j, kAmrRefRatio);
330 }
331 }
332
333 VariableSet vars_;
334 Geometry coarse_geom_;
335 BoxArray coarse_ba_;
336 BCRec bcPhi_;
337 Real alpha_;
338 int c_rho_, c_mx_, c_my_, c_E_;
340 CondensedSchurSourceStepper coarse_;
342 std::unique_ptr<CompositeFacPoisson> fac_;
344 std::vector<Box2D> fac_fine_boxes_;
345};
346
347} // namespace pops
Umbrella for the AMR MultiFab stack: includes the numerics/time sub-headers in dependency order (flux...
AMR multi-patch subcycling engine (several fine boxes per level): 2-level step (amr_step_2level_multi...
Schur-condensed SOURCE stage over an AMR hierarchy.
Definition amr_condensed_schur_source_stepper.hpp:54
void step(std::vector< AmrLevelMP > &levels, MultiFab &coarse_phi, const MultiFab &coarse_bz, int c_bz, Real theta, Real dt)
Condensed SOURCE stage, IN-PLACE on the hierarchy levels and the coarse potential coarse_phi.
Definition amr_condensed_schur_source_stepper.hpp:105
int momentum_y_comp() const
Definition amr_condensed_schur_source_stepper.hpp:142
int momentum_x_comp() const
Definition amr_condensed_schur_source_stepper.hpp:141
AmrCondensedSchurSourceStepper(const VariableSet &vars, int c_rho, int c_mx, int c_my, int c_E, const Geometry &coarse_geom, const BoxArray &coarse_ba, const BCRec &bcPhi, Real alpha, int n_precond_vcycles=1)
EXPLICIT-COMPONENT variant (audit wave 3, parity with the System steppers): roles carried by the ABI ...
Definition amr_condensed_schur_source_stepper.hpp:73
int density_comp() const
Definition amr_condensed_schur_source_stepper.hpp:140
const KrylovResult & last_solve() const
Diagnostic of the last coarse stage solve (BiCGStab iterations, relative residual,...
Definition amr_condensed_schur_source_stepper.hpp:138
AmrCondensedSchurSourceStepper(const VariableSet &vars, const Geometry &coarse_geom, const BoxArray &coarse_ba, const BCRec &bcPhi, Real alpha, int n_precond_vcycles=1)
vars: descriptor of the fluid block (MUST expose Density / MomentumX / MomentumY; Energy optional).
Definition amr_condensed_schur_source_stepper.hpp:63
void set_krylov(Real tol, int max_iters)
Tolerance / budget of the COARSE stage Krylov solve (delegated to the uniform stage #126; historical ...
Definition amr_condensed_schur_source_stepper.hpp:91
int energy_comp() const
Definition amr_condensed_schur_source_stepper.hpp:143
bool has_energy() const
true if the model carries an Energy role (energy update active in the coarse stage).
Definition amr_condensed_schur_source_stepper.hpp:94
Ordered list of boxes tiling a level.
Definition box_array.hpp:22
void set_krylov(Real tol, int max_iters)
Tolerance / iteration budget of the stage Krylov solve (BiCGStab).
Definition condensed_schur_source_stepper.hpp:277
int density_comp() const
Definition condensed_schur_source_stepper.hpp:283
int momentum_x_comp() const
Definition condensed_schur_source_stepper.hpp:284
int energy_comp() const
Definition condensed_schur_source_stepper.hpp:286
const KrylovResult & last_solve() const
Diagnostic of the last solve (BiCGStab iterations, relative residual, convergence).
Definition condensed_schur_source_stepper.hpp:272
void step(MultiFab &state, MultiFab &phi, const MultiFab &bz_field, int c_bz, Real theta, Real dt)
Condensed SOURCE STAGE, IN-PLACE on state and phi.
Definition condensed_schur_source_stepper.hpp:190
int momentum_y_comp() const
Definition condensed_schur_source_stepper.hpp:285
Owning MPI rank of each box, indexed by GLOBAL box index (parallel to a BoxArray).
Definition distribution_mapping.hpp:19
GENERIC builder of the condensed source stage for the electrostatic + Lorentz source (kind="electrost...
Definition schur_condensation.hpp:153
Field distributed over a level: decomposition (BoxArray) + distribution (DistributionMapping) + ncomp...
Definition multifab.hpp:33
const DistributionMapping & dmap() const
GLOBAL distribution (owner rank per box).
Definition multifab.hpp:58
const BoxArray & box_array() const
GLOBAL decomposition of the level (all boxes, all ranks).
Definition multifab.hpp:56
CompositeFacPoisson: 2-level AMR COMPOSITE elliptic solver (Fast Adaptive Composite,...
CondensedSchurSourceStepper: Schur-condensed SOURCE STAGE (level 4 of docs/SCHUR_CONDENSATION_DESIGN....
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
Definition amr_hierarchy.hpp:29
void for_each_cell(const Box2D &b, F f)
Applies f to EACH cell (i, j) of box b (bounds inclusive), via Kokkos::parallel_for (Serial / OpenMP ...
Definition for_each.hpp:138
double Real
Definition types.hpp:30
void mf_average_down_mb(const MultiFab &Uf, MultiFab &Uc)
Definition amr_subcycling.hpp:305
int n_ranks()
Definition comm.hpp:139
void device_fence()
Device barrier: waits for in-flight kernels to finish before a HOST access to unified memory.
Definition kokkos_env.hpp:43
constexpr int kAmrRefRatio
The native AMR refinement ratio between two consecutive levels.
Definition refinement_ratio.hpp:30
VariableRole
PHYSICAL role of a component.
Definition variables.hpp:27
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
Single source of truth for the native AMR refinement ratio.
BUILDER (NOT solver) of the Schur-condensed source stage of the implicit source coupling potential / ...
Boundary conditions for the FOUR faces of the domain (type + associated Dirichlet value).
Definition physical_bc.hpp:29
Cartesian geometry of a level: index domain + physical bounds [xlo, xhi] x [ylo, yhi].
Definition geometry.hpp:20
Geometry refine(int r) const
Geometry refined by ratio r: SAME physical extent, refined index domain (dx -> dx/r).
Definition geometry.hpp:40
Box2D domain
Definition geometry.hpp:21
Outcome of a Krylov solve: iterations performed, final relative residual, convergence flag.
Definition krylov_result.hpp:16
A model's variable set: kind (cons/prim), names, size, canonical roles (optional, parallel to names; ...
Definition variables.hpp:58