include/pops/numerics/spatial/primitives/face_flux.hpp Source FileΒΆ

adc_cpp: include/pops/numerics/spatial/primitives/face_flux.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
face_flux.hpp
Go to the documentation of this file.
1
15
16#pragma once
17
18#include <pops/core/model/physical_model.hpp> // HasPrimitiveVars: optional primitive reconstruction
27
28#include <stdexcept> // require_reconstruction_ghosts: state without the stencil width -> clear error
29
30namespace pops {
31
36template <class Model>
37POPS_HD inline typename Model::State rusanov_flux(const Model& m, const typename Model::State& UL,
38 const Aux& AL, const typename Model::State& UR,
39 const Aux& AR, int dir) {
40 return RusanovFlux{}(m, UL, AL, UR, AR, dir);
41}
42
50template <class Model, class Limiter>
51POPS_HD inline typename Model::State reconstruct(const Model& model, const ConstArray4& u, int i,
52 int j, int dir, Real sgn, const Limiter& lim,
53 bool prim) {
54 if constexpr (HasPrimitiveVars<Model> && Limiter::n_ghost >= 2) {
55 if (prim) { // convert the stencil U->P, limit on P, convert back P->U
56 using Prim = typename Model::Prim;
57 const Prim P0 = model.to_primitive(load_state<Model>(u, i, j));
58 Prim Pf{};
59 if constexpr (Limiter::n_ghost == 2) {
60 const Prim Pm =
61 model.to_primitive(load_state<Model>(u, dir == 0 ? i - 1 : i, dir == 0 ? j : j - 1));
62 const Prim Pp =
63 model.to_primitive(load_state<Model>(u, dir == 0 ? i + 1 : i, dir == 0 ? j : j + 1));
64 for (int c = 0; c < Model::n_vars; ++c)
65 Pf[c] = P0[c] + sgn * Real(0.5) * lim(P0[c] - Pm[c], Pp[c] - P0[c]);
66 } else { // WENO5 on the 5-point stencil in primitive variables
67 const int d = (sgn > Real(0)) ? 1 : -1;
68 const Prim Pm2 = model.to_primitive(
69 load_state<Model>(u, dir == 0 ? i - 2 * d : i, dir == 0 ? j : j - 2 * d));
70 const Prim Pm1 =
71 model.to_primitive(load_state<Model>(u, dir == 0 ? i - d : i, dir == 0 ? j : j - d));
72 const Prim Pp1 =
73 model.to_primitive(load_state<Model>(u, dir == 0 ? i + d : i, dir == 0 ? j : j + d));
74 const Prim Pp2 = model.to_primitive(
75 load_state<Model>(u, dir == 0 ? i + 2 * d : i, dir == 0 ? j : j + 2 * d));
76 for (int c = 0; c < Model::n_vars; ++c)
77 Pf[c] = weno5z(Pm2[c], Pm1[c], P0[c], Pp1[c], Pp2[c]);
78 }
79 return model.to_conservative(Pf);
80 }
81 }
82 (void)model;
83 (void)prim;
84 typename Model::State s = load_state<Model>(u, i, j);
85 if constexpr (Limiter::n_ghost == 2) {
86 // MUSCL: per-component limited slope (order 2).
87 for (int c = 0; c < Model::n_vars; ++c) {
88 const Real am = (dir == 0) ? u(i, j, c) - u(i - 1, j, c) : u(i, j, c) - u(i, j - 1, c);
89 const Real ap = (dir == 0) ? u(i + 1, j, c) - u(i, j, c) : u(i, j + 1, c) - u(i, j, c);
90 s[c] += sgn * Real(0.5) * lim(am, ap);
91 }
92 } else if constexpr (Limiter::n_ghost >= 3) {
93 // WENO5 (order 5): face value from a 5-point stencil oriented by sgn
94 // (sgn>0 -> +dir face; sgn<0 -> -dir face, reversed stencil). lim unused.
95 (void)lim;
96 const int d = (sgn > Real(0)) ? 1 : -1;
97 for (int c = 0; c < Model::n_vars; ++c) {
98 if (dir == 0)
99 s[c] = weno5z(u(i - 2 * d, j, c), u(i - d, j, c), u(i, j, c), u(i + d, j, c),
100 u(i + 2 * d, j, c));
101 else
102 s[c] = weno5z(u(i, j - 2 * d, c), u(i, j - d, c), u(i, j, c), u(i, j + d, c),
103 u(i, j + 2 * d, c));
104 }
105 }
106 return s;
107}
108
113template <class Model, class Limiter>
114POPS_HD inline typename Model::State reconstruct_pp(const Model& model, const ConstArray4& u, int i,
115 int j, int dir, Real sgn, const Limiter& lim,
116 bool prim, Real pos_floor, int pos_comp) {
117 typename Model::State s = reconstruct<Model>(model, u, i, j, dir, sgn, lim, prim);
118 zhang_shu_scale<Model>(s, u, i, j, pos_floor, pos_comp);
119 return s;
120}
121
122namespace detail {
132template <class Limiter>
134 if (U.n_grow() < Limiter::n_ghost)
135 throw std::runtime_error(
136 "spatial operator: the state must carry at least Limiter::n_ghost ghost layers "
137 "(the reconstruction stencil reads i+-Limiter::n_ghost at the edge of the valid box); "
138 "allocate the state MultiFab with this number of ghosts.");
139}
140} // namespace detail
141
147inline Box2D xface_box(const Box2D& v) {
148 return Box2D{{v.lo[0], v.lo[1]}, {v.hi[0] + 1, v.hi[1]}};
149}
150inline Box2D yface_box(const Box2D& v) {
151 return Box2D{{v.lo[0], v.lo[1]}, {v.hi[0], v.hi[1] + 1}};
152}
153
154namespace detail {
160template <class Limiter, class NumericalFlux, class Model>
162 Model model;
166 Limiter lim;
167 NumericalFlux nflux;
170 int pos_comp = 0;
171 POPS_HD void operator()(int i, int j) const {
172 const auto L =
173 reconstruct_pp<Model>(model, u, i - 1, j, 0, +1, lim, recon_prim, pos_floor, pos_comp);
174 const auto Rr =
175 reconstruct_pp<Model>(model, u, i, j, 0, -1, lim, recon_prim, pos_floor, pos_comp);
176 const auto F = nflux(model, L, load_aux<aux_comps<Model>()>(ax, i - 1, j), Rr,
177 load_aux<aux_comps<Model>()>(ax, i, j), 0);
178 for (int c = 0; c < Model::n_vars; ++c)
179 fx(i, j, c) = F[c];
180 if constexpr (DiffusiveModel<Model>) {
181 const Real nu = model.diffusivity();
182 for (int c = 0; c < Model::n_vars; ++c)
183 fx(i, j, c) += -nu * (u(i, j, c) - u(i - 1, j, c)) / dx;
184 }
185 }
186};
190template <class Limiter, class NumericalFlux, class Model>
192 Model model;
196 Limiter lim;
197 NumericalFlux nflux;
200 int pos_comp = 0;
201 POPS_HD void operator()(int i, int j) const {
202 const auto L =
203 reconstruct_pp<Model>(model, u, i, j - 1, 1, +1, lim, recon_prim, pos_floor, pos_comp);
204 const auto Rr =
205 reconstruct_pp<Model>(model, u, i, j, 1, -1, lim, recon_prim, pos_floor, pos_comp);
206 const auto F = nflux(model, L, load_aux<aux_comps<Model>()>(ax, i, j - 1), Rr,
207 load_aux<aux_comps<Model>()>(ax, i, j), 1);
208 for (int c = 0; c < Model::n_vars; ++c)
209 fy(i, j, c) = F[c];
210 if constexpr (DiffusiveModel<Model>) {
211 const Real nu = model.diffusivity();
212 for (int c = 0; c < Model::n_vars; ++c)
213 fy(i, j, c) += -nu * (u(i, j, c) - u(i, j - 1, c)) / dy;
214 }
215 }
216};
217} // namespace detail
218
228//
229// compute_face_fluxes: writes the numerical fluxes at the FACES (Fx at faces normal to x,
230// Fy at y), BEFORE divergence. This is the brick the AMR reflux needs (it accumulates the
231// fine fluxes and subtracts the coarse flux at the coarse-fine interfaces; assemble_rhs
232// itself computes -div F directly and discards the face fluxes).
233//
234// Conventions: Fx(i,j) = flux at the face between cells (i-1,j) and (i,j), i in [lo..hi+1].
235// Fy(i,j) = flux between (i,j-1) and (i,j), j in [lo..hi+1]. Same reconstruction (Limiter)
236// and numerical flux (NumericalFlux) as assemble_rhs, so
237// r(i,j) = S - (Fx(i+1,j)-Fx(i,j))/dx - (Fy(i,j+1)-Fy(i,j))/dy
238// gives back EXACTLY the assemble_rhs residual. Fx, Fy sized by the caller (xface_box/yface_box
239// boxes, ncomp = Model::n_vars, 0 ghost). Device-callable.
240//
241// DIFFUSION on AMR (milestone 4): for a DiffusiveModel, we add the FACE Fickian flux
242// F_diff = -nu (u_R - u_L)/h (centered gradient at the face, cell values). Its divergence
243// -(Fx(i+1)-Fx(i))/dx gives back EXACTLY +nu Lap(u) of assemble_rhs, but treated as a FLUX:
244// the AMR reflux therefore sees it, and the diffusion stays conservative at the coarse-fine
245// interfaces (otherwise a direct Laplacian would be ignored by the reflux). dx/dy = step of
246// the LEVEL (passed by the caller; 0 by default, not read for a non-diffusive model -> the
247// hyperbolic path is strictly bit-identical).
248template <class Limiter = NoSlope, class NumericalFlux = RusanovFlux, class Model>
249void compute_face_fluxes(const Model& model, const MultiFab& U, const MultiFab& aux, MultiFab& Fx,
250 MultiFab& Fy, Real dx = 0, Real dy = 0, bool recon_prim = false,
251 Real pos_floor = Real(0)) {
252 detail::require_reconstruction_ghosts<Limiter>(U); // state ghosts >= stencil (otherwise OOB)
253 const Limiter lim{};
254 const NumericalFlux nflux{};
255 const int pos_comp = detail::positivity_comp<Model>(pos_floor);
256 for (int li = 0; li < U.local_size(); ++li) {
257 const ConstArray4 u = U.fab(li).const_array();
258 const ConstArray4 ax = aux.fab(li).const_array();
259 Array4 fx = Fx.fab(li).array();
260 Array4 fy = Fy.fab(li).array();
261 const Box2D v = U.box(li);
264 model, u, ax, fx, dx, lim, nflux, recon_prim, pos_floor, pos_comp});
267 model, u, ax, fy, dy, lim, nflux, recon_prim, pos_floor, pos_comp});
268 }
269}
270
271} // namespace pops
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
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
int local_size() const
Number of fabs OWNED by this rank (bound on local indices).
Definition multifab.hpp:65
DiffusiveModel: optional concept for models with isotropic scalar diffusion.
Definition state_access.hpp:38
OPTIONAL extension of a PhysicalModel: primitive variables + cons<->prim conversions.
Definition physical_model.hpp:167
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...
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).
void require_reconstruction_ghosts(const MultiFab &U)
require_reconstruction_ghosts<Limiter>: STRUCTURAL ENTRY GUARD of the FV spatial operators.
Definition face_flux.hpp:133
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 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
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 compute_face_fluxes(const Model &model, const MultiFab &U, const MultiFab &aux, MultiFab &Fx, MultiFab &Fy, Real dx=0, Real dy=0, bool recon_prim=false, Real pos_floor=Real(0))
compute_face_fluxes<Limiter,NumericalFlux>: writes the face fluxes BEFORE divergence.
Definition face_flux.hpp:249
POPS_HD Model::State reconstruct_pp(const Model &model, const ConstArray4 &u, int i, int j, int dir, Real sgn, const Limiter &lim, bool prim, Real pos_floor, int pos_comp)
reconstruct_pp: reconstruct + zhang_shu_scale positivity limiter on the returned state.
Definition face_flux.hpp:114
Box2D xface_box(const Box2D &v)
xface_box / yface_box: face boxes normal to x (resp.
Definition face_flux.hpp:147
POPS_HD Real weno5z(Real vm2, Real vm1, Real v0, Real vp1, Real vp2)
weno5z: WENO5-Z reconstruction (Borges 2008) at one interface, on a 5-point stencil.
Definition reconstruction.hpp:70
POPS_HD Model::State reconstruct(const Model &model, const ConstArray4 &u, int i, int j, int dir, Real sgn, const Limiter &lim, bool prim)
reconstruct<Model,Limiter>: face value at (i,j) extrapolated in direction dir.
Definition face_flux.hpp:51
Box2D yface_box(const Box2D &v)
Definition face_flux.hpp:150
Single-interface numerical flux policies: Rusanov, HLL, HLLC, Roe.
C++20 concepts defining the contract of the physics layer.
Zhang-Shu positivity limiter and Density-role resolution.
Interface reconstruction policies: MUSCL limiters and WENO5-Z.
Model/state/aux access layer of the Cartesian spatial operator.
WRITE POD handle (raw pointer + strides) over a Fab2D buffer, indexed by (i, j, c) IN GLOBAL INDICES ...
Definition fab2d.hpp:29
POINTWISE auxiliary fields shared with the physics: single coupling channel.
Definition state.hpp:123
2D integer index space, cell-centered.
Definition box2d.hpp:37
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
RusanovFlux (local Lax-Friedrichs): robust flux, compatible with any minimal PhysicalModel.
Definition numerical_flux.hpp:55
FaceFluxXKernel: device kernel for the flux at the radial x face (between i-1 and i).
Definition face_flux.hpp:161
bool recon_prim
Definition face_flux.hpp:168
Real pos_floor
Zhang-Shu positivity limiter (<= 0: inactive, bit-identical)
Definition face_flux.hpp:169
ConstArray4 u
Definition face_flux.hpp:163
ConstArray4 ax
Definition face_flux.hpp:163
Model model
Definition face_flux.hpp:162
Limiter lim
Definition face_flux.hpp:166
Array4 fx
Definition face_flux.hpp:164
POPS_HD void operator()(int i, int j) const
Definition face_flux.hpp:171
Real dx
Definition face_flux.hpp:165
int pos_comp
component of the Density role (resolved by the host caller)
Definition face_flux.hpp:170
NumericalFlux nflux
Definition face_flux.hpp:167
FaceFluxYKernel: device kernel for the flux at the y face (between j-1 and j).
Definition face_flux.hpp:191
POPS_HD void operator()(int i, int j) const
Definition face_flux.hpp:201
Limiter lim
Definition face_flux.hpp:196
int pos_comp
component of the Density role (resolved by the host caller)
Definition face_flux.hpp:200
bool recon_prim
Definition face_flux.hpp:198
Model model
Definition face_flux.hpp:192
ConstArray4 ax
Definition face_flux.hpp:193
ConstArray4 u
Definition face_flux.hpp:193
Array4 fy
Definition face_flux.hpp:194
Real pos_floor
Zhang-Shu positivity limiter (<= 0: inactive, bit-identical)
Definition face_flux.hpp:199
NumericalFlux nflux
Definition face_flux.hpp:197
Real dy
Definition face_flux.hpp:195
#define POPS_HD
Definition types.hpp:25