include/pops/numerics/time/reference/amr_reflux.hpp Source FileΒΆ

adc_cpp: include/pops/numerics/time/reference/amr_reflux.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_reflux.hpp
Go to the documentation of this file.
1#pragma once
2
10
11#include <vector>
12
33
34namespace pops {
35
36static_assert(kAmrRefRatio == 2, "ratio-2-structural kernels below assume kAmrRefRatio == 2");
37
38// xface_box / yface_box: provided by numerics/spatial_operator.hpp (included above),
39// same face-box conventions. We do not redefine them here.
40
41namespace detail {
42
43// NAMED FUNCTORS (and not POPS_HD lambdas) for the AMR reflux device kernels. Same reasons as the
44// rest of the mesh/time path (AmrSspRhsKernel, fill_boundary recipe): these kernels are first-
45// instantiated from an external loader TU, where an extended lambda trips the device kernel emission
46// under nvcc. Body strictly identical to the previous lambdas -> bit-identical CPU and device.
47
49template <class Model>
51 Model m;
54 POPS_HD void operator()(int i, int j) const {
55 typename Model::State UL{}, UR{};
56 UL[0] = u(i - 1, j);
57 UR[0] = u(i, j);
58 F(i, j) = rusanov_flux(m, UL, load_aux<aux_comps<Model>()>(ax, i - 1, j), UR,
59 load_aux<aux_comps<Model>()>(ax, i, j), 0)[0];
60 }
61};
62
64template <class Model>
66 Model m;
69 POPS_HD void operator()(int i, int j) const {
70 typename Model::State UL{}, UR{};
71 UL[0] = u(i, j - 1);
72 UR[0] = u(i, j);
73 F(i, j) = rusanov_flux(m, UL, load_aux<aux_comps<Model>()>(ax, i, j - 1), UR,
74 load_aux<aux_comps<Model>()>(ax, i, j), 1)[0];
75 }
76};
77
82 double dx, dy, dt;
83 POPS_HD void operator()(int i, int j) const {
84 uu(i, j) -= dt * ((FX(i + 1, j) - FX(i, j)) / dx + (FY(i, j + 1) - FY(i, j)) / dy);
85 }
86};
87
88} // namespace detail
89
90// First-order Rusanov flux, 1 component, aux variable in space (Fab2D with
91// 3 components [phi, gx, gy], ghosts filled), on a Fab2D.
92// fx(i,j) = flux at the left face of cell i; fy(i,j) = bottom face of j.
93template <class Model>
94void compute_fluxes_1c(const Model& m, const Fab2D& U, const Fab2D& aux, Fab2D& fx, Fab2D& fy) {
95 const ConstArray4 u = U.const_array();
96 const ConstArray4 ax = aux.const_array();
97 {
98 Array4 F = fx.array();
100 }
101 {
102 Array4 F = fy.array();
104 }
105}
106
107// Explicit Euler: U -= dt div(F). The ghosts of U must be filled.
108template <class Model>
109void advance_fab_1c(const Model& m, Fab2D& U, const Fab2D& aux, double dx, double dy, double dt,
110 Fab2D& fx, Fab2D& fy) {
111 compute_fluxes_1c(m, U, aux, fx, fy);
112 Array4 uu = U.array();
113 const ConstArray4 FX = fx.const_array();
114 const ConstArray4 FY = fy.const_array();
115 for_each_cell(U.box(), detail::AdvanceFab1cKernel{uu, FX, FY, dx, dy, dt});
116}
117
118// Periodic ghosts for a single Fab2D covering the domain.
119inline void fill_periodic_fab(Fab2D& U, const Box2D& dom) {
120 const int ng = U.n_ghost();
121 const int nx = dom.nx(), ny = dom.ny();
122 Array4 a = U.array();
123 for (int j = dom.lo[1]; j <= dom.hi[1]; ++j)
124 for (int g = 1; g <= ng; ++g) {
125 a(dom.lo[0] - g, j) = a(dom.hi[0] - g + 1, j);
126 a(dom.hi[0] + g, j) = a(dom.lo[0] + g - 1, j);
127 }
128 for (int i = dom.lo[0] - ng; i <= dom.hi[0] + ng; ++i)
129 for (int g = 1; g <= ng; ++g) {
130 a(i, dom.lo[1] - g) = a(i, dom.hi[1] - g + 1);
131 a(i, dom.hi[1] + g) = a(i, dom.lo[1] + g - 1);
132 }
133}
134
135// Fine ghosts by injection from the coarse (ratio 2), interpolated in time
136// between the old coarse state (frac=0) and the new one (frac=1): space-time
137// FillPatch for the Berger-Oliger subcycling.
138inline void fill_fine_ghosts_t(Fab2D& Uf, const Fab2D& Uco, const Fab2D& Ucn, double frac) {
139 const ConstArray4 co = Uco.const_array();
140 const ConstArray4 cn = Ucn.const_array();
141 Array4 f = Uf.array();
142 const Box2D g = Uf.grown_box();
143 const Box2D v = Uf.box();
144 auto coarsen = [](int x) { return (x >= 0) ? x / 2 : -((-x + 1) / 2); };
145 for (int j = g.lo[1]; j <= g.hi[1]; ++j)
146 for (int i = g.lo[0]; i <= g.hi[0]; ++i)
147 if (!v.contains(i, j)) {
148 const int ci = coarsen(i), cj = coarsen(j);
149 f(i, j) = (1 - frac) * co(ci, cj) + frac * cn(ci, cj);
150 }
151}
152
153// Fine -> coarse average over the covered region (ratio 2).
154inline void average_down_fab(const Fab2D& Uf, Fab2D& Uc, int CI0, int CI1, int CJ0, int CJ1) {
155 const ConstArray4 f = Uf.const_array();
156 Array4 c = Uc.array();
157 for (int J = CJ0; J <= CJ1; ++J)
158 for (int I = CI0; I <= CI1; ++I)
159 c(I, J) = 0.25 * (f(2 * I, 2 * J) + f(2 * I + 1, 2 * J) + f(2 * I, 2 * J + 1) +
160 f(2 * I + 1, 2 * J + 1));
161}
162
163// One conservative 2-level step with Berger-Oliger subcycling (the fine does
164// r=2 substeps of dt/2) and reflux. Fine region = coarse cells
165// [CI0..CI1] x [CJ0..CJ1] (strictly interior), refined into
166// [2CI0..2CI1+1] x [...]. dom = periodic coarse domain.
167//
168// Flux register: we accumulate the coarse flux (x dt) and the sum of the fine
169// fluxes (x dt/2 per substep, spatially averaged) at the 4 faces of the fine
170// region, then we correct the adjacent coarse cells by their difference.
171template <class Model>
172void amr_step_2level(const Model& m, Fab2D& Uc, const Box2D& dom, double dxc, double dyc, Fab2D& Uf,
173 int CI0, int CI1, int CJ0, int CJ1, const Fab2D& auxc, const Fab2D& auxf,
174 double dt) {
175 const int r = kAmrRefRatio;
176 const double dxf = dxc / kAmrRefRatio, dyf = dyc / kAmrRefRatio, dtf = dt / r;
177 const int nJ = CJ1 - CJ0 + 1, nI = CI1 - CI0 + 1;
178
179 const Fab2D Uc_old = Uc; // coarse state at time t (for temporal interp)
180
181 // --- coarse fluxes (before update) at the 4 faces of the fine region ---
182 fill_periodic_fab(Uc, dom);
183 Fab2D fxc(xface_box(Uc.box()), 1, 0), fyc(yface_box(Uc.box()), 1, 0);
184 compute_fluxes_1c(m, Uc, auxc, fxc, fyc);
185 const ConstArray4 FXc = fxc.const_array();
186 const ConstArray4 FYc = fyc.const_array();
187 std::vector<double> cL(nJ), cR(nJ), cB(nI), cT(nI);
188 for (int J = CJ0; J <= CJ1; ++J) {
189 cL[J - CJ0] = FXc(CI0, J);
190 cR[J - CJ0] = FXc(CI1 + 1, J);
191 }
192 for (int I = CI0; I <= CI1; ++I) {
193 cB[I - CI0] = FYc(I, CJ0);
194 cT[I - CI0] = FYc(I, CJ1 + 1);
195 }
196
197 advance_fab_1c(m, Uc, auxc, dxc, dyc, dt, fxc, fyc); // Uc becomes the "t+dt" state
198
199 // --- fine subcycling: r substeps, accumulation of fine fluxes (x dtf) ---
200 std::vector<double> fL(nJ, 0), fR(nJ, 0), fB(nI, 0), fT(nI, 0);
201 Fab2D fxf(xface_box(Uf.box()), 1, 0), fyf(yface_box(Uf.box()), 1, 0);
202 for (int s = 0; s < r; ++s) {
203 fill_fine_ghosts_t(Uf, Uc_old, Uc, double(s) / r); // time-interpolated BC
204 compute_fluxes_1c(m, Uf, auxf, fxf, fyf);
205 const ConstArray4 FXf = fxf.const_array();
206 const ConstArray4 FYf = fyf.const_array();
207 for (int J = CJ0; J <= CJ1; ++J) {
208 fL[J - CJ0] += 0.5 * (FXf(2 * CI0, 2 * J) + FXf(2 * CI0, 2 * J + 1)) * dtf;
209 fR[J - CJ0] += 0.5 * (FXf(2 * CI1 + 2, 2 * J) + FXf(2 * CI1 + 2, 2 * J + 1)) * dtf;
210 }
211 for (int I = CI0; I <= CI1; ++I) {
212 fB[I - CI0] += 0.5 * (FYf(2 * I, 2 * CJ0) + FYf(2 * I + 1, 2 * CJ0)) * dtf;
213 fT[I - CI0] += 0.5 * (FYf(2 * I, 2 * CJ1 + 2) + FYf(2 * I + 1, 2 * CJ1 + 2)) * dtf;
214 }
215 advance_fab_1c(m, Uf, auxf, dxf, dyf, dtf, fxf, fyf);
216 }
217
218 average_down_fab(Uf, Uc, CI0, CI1, CJ0, CJ1); // sync of the covered cells
219
220 // --- reflux: coarse flux (x dt) replaced by sum of fine fluxes (x dtf) ---
221 Array4 c = Uc.array();
222 for (int J = CJ0; J <= CJ1; ++J) {
223 c(CI0 - 1, J) -= (fL[J - CJ0] - cL[J - CJ0] * dt) / dxc;
224 c(CI1 + 1, J) += (fR[J - CJ0] - cR[J - CJ0] * dt) / dxc;
225 }
226 for (int I = CI0; I <= CI1; ++I) {
227 c(I, CJ0 - 1) -= (fB[I - CI0] - cB[I - CI0] * dt) / dyc;
228 c(I, CJ1 + 1) += (fT[I - CI0] - cT[I - CI0] * dt) / dyc;
229 }
230}
231
232} // namespace pops
Box2D: the integer index space of a 2D cell-centered Cartesian grid.
Single-grid data on a Box2D: VALID box + ng ghost layers, ncomp components, component-slow layout.
Definition fab2d.hpp:59
const Box2D & grown_box() const
Grown box (valid + ng ghosts) = actual memory footprint.
Definition fab2d.hpp:76
const Box2D & box() const
VALID box (without ghosts).
Definition fab2d.hpp:74
int n_ghost() const
Number of ghost layers.
Definition fab2d.hpp:80
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
Fab2D: single-grid data on a Box2D (in-house equivalent of AMReX's FArrayBox); Array4 / ConstArray4: ...
for_each_cell and reductions: the parallelism SEAM over the cells of a Box2D; sync_host / sync_device...
Definition amr_hierarchy.hpp:29
POPS_HD Model::State rusanov_flux(const Model &m, const typename Model::State &UL, const Aux &AL, const typename Model::State &UR, const Aux &AR, int dir)
rusanov_flux: free compat, delegates to RusanovFlux{} (policy of numerical_flux.hpp).
Definition face_flux.hpp:37
void fill_periodic_fab(Fab2D &U, const Box2D &dom)
Definition amr_reflux.hpp:119
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
void compute_fluxes_1c(const Model &m, const Fab2D &U, const Fab2D &aux, Fab2D &fx, Fab2D &fy)
Definition amr_reflux.hpp:94
POPS_HD Aux load_aux(const ConstArray4 &a, int i, int j)
load_aux<NComp>: reads NComp components of the auxiliary from an Array4 at (i,j).
Definition state_access.hpp:141
void average_down_fab(const Fab2D &Uf, Fab2D &Uc, int CI0, int CI1, int CJ0, int CJ1)
Definition amr_reflux.hpp:154
BoxArray coarsen(const BoxArray &ba, int r)
Coarsens each box of the BoxArray by a ratio r (coarsen box by box, order preserved).
Definition refinement.hpp:39
void amr_step_2level(const Model &m, Fab2D &Uc, const Box2D &dom, double dxc, double dyc, Fab2D &Uf, int CI0, int CI1, int CJ0, int CJ1, const Fab2D &auxc, const Fab2D &auxf, double dt)
Definition amr_reflux.hpp:172
Box2D xface_box(const Box2D &v)
xface_box / yface_box: face boxes normal to x (resp.
Definition face_flux.hpp:147
Box2D yface_box(const Box2D &v)
Definition face_flux.hpp:150
constexpr int kAmrRefRatio
The native AMR refinement ratio between two consecutive levels.
Definition refinement_ratio.hpp:30
void advance_fab_1c(const Model &m, Fab2D &U, const Fab2D &aux, double dx, double dy, double dt, Fab2D &fx, Fab2D &fy)
Definition amr_reflux.hpp:109
void fill_fine_ghosts_t(Fab2D &Uf, const Fab2D &Uco, const Fab2D &Ucn, double frac)
Definition amr_reflux.hpp:138
Single source of truth for the native AMR refinement ratio.
Cartesian spatial operator: assembles R(U, aux) = -div F + S over the cells of a level.
Pointwise types of the physics layer: StateVec<N> (conserved state) and Aux (auxiliary fields from th...
WRITE POD handle (raw pointer + strides) over a Fab2D buffer, indexed by (i, j, c) IN GLOBAL INDICES ...
Definition fab2d.hpp:29
2D integer index space, cell-centered.
Definition box2d.hpp:37
POPS_HD int nx() const
Width (direction 0). POPS_HD (called from Geometry::dx() in a device kernel).
Definition box2d.hpp:50
int hi[2]
Definition box2d.hpp:39
bool contains(int i, int j) const
true if cell (i, j) is inside the box (lo/hi bounds inclusive).
Definition box2d.hpp:61
POPS_HD int ny() const
Height (direction 1). POPS_HD (called from Geometry::dy() in a device kernel).
Definition box2d.hpp:52
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
Explicit Euler, 1 component: U -= dt div(F) on cell (i,j).
Definition amr_reflux.hpp:79
ConstArray4 FY
Definition amr_reflux.hpp:81
double dx
Definition amr_reflux.hpp:82
Array4 uu
Definition amr_reflux.hpp:80
double dy
Definition amr_reflux.hpp:82
double dt
Definition amr_reflux.hpp:82
POPS_HD void operator()(int i, int j) const
Definition amr_reflux.hpp:83
ConstArray4 FX
Definition amr_reflux.hpp:81
Rusanov flux at the left face (x axis) of cell (i,j).
Definition amr_reflux.hpp:50
Model m
Definition amr_reflux.hpp:51
Array4 F
Definition amr_reflux.hpp:53
ConstArray4 ax
Definition amr_reflux.hpp:52
ConstArray4 u
Definition amr_reflux.hpp:52
POPS_HD void operator()(int i, int j) const
Definition amr_reflux.hpp:54
Rusanov flux at the bottom face (y axis) of cell (i,j).
Definition amr_reflux.hpp:65
ConstArray4 u
Definition amr_reflux.hpp:67
POPS_HD void operator()(int i, int j) const
Definition amr_reflux.hpp:69
ConstArray4 ax
Definition amr_reflux.hpp:67
Model m
Definition amr_reflux.hpp:66
Array4 F
Definition amr_reflux.hpp:68
Base scalar types and the POPS_HD macro (host+device portability).
#define POPS_HD
Definition types.hpp:25