core
Shared MPI block decomposition + asynchronous ghost-layer exchange (header-only C++20)
Loading...
Searching...
No Matches
assembly.hpp
Go to the documentation of this file.
1// core — device assembly of the FV (pressure) operator (D2), built on the S1 CSR primitive.
2//
3// AmrPoisson::assembleFv walks `forEachFaceNeighbor` on the host (serial) to produce the weight-CSR
4// (invVol / start / nbr / coef=openness·A_f/d_f / bcDiag) that the device MG then applies. For a
5// STATIC geometry that host walk runs once; for a DYNAMIC geometry (moving SDF / adapt) it would
6// run every step and force a host round-trip. This header reproduces that walk as a device functor
7// + a per-cell diagonal kernel, so the FvOp is assembled entirely on the device — no host
8// round-trip — feeding the same shared face_csr.hpp apply kernels.
9//
10// Bit-exactness: the device functor emits faces in the exact same axis/dir/sub-face order as the
11// host `forEachFaceNeighbor`, each cell fills its own CSR slice (S1, atomic-free), and every
12// coefficient uses the identical double arithmetic. So on the OpenMP backend the device-assembled
13// FvOp is bit-for-bit equal to the host `assembleFv` / `buildFaceCsr` (the cross-backend anti-drift
14// lock in tests/test_amr_assembly_kokkos). GPU is tolerance-not-bit-exact (FMA), per the
15// convention.
16//
17// Requires a Kokkos build + the morton checkout (PECLET_CORE_HAVE_MORTON ⇒ MORTON_HD ==
18// KOKKOS_FUNCTION).
19#ifndef PECLET_CORE_AMR_ASSEMBLY_HPP
20#define PECLET_CORE_AMR_ASSEMBLY_HPP
21
22#ifdef PECLET_CORE_HAVE_MORTON
23
24#include <array>
25
31
32namespace peclet::core::amr {
33
37template <int Dim, unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
38struct FvFaceEmit {
40 using M = typename View_t::M;
41 using Code = typename View_t::Code;
42 using Coord = typename View_t::Coord;
43
46 Coord fineExt[Dim] = {};
47 Real h0 = 1.0;
48 bool hasOpen = false;
49 bool periodic = true;
50 bool immersedWall = false;
51
52 static constexpr int kFaces = 2 * Dim;
53 KOKKOS_INLINE_FUNCTION static int faceIndex(int axis, int dir) {
54 return 2 * axis + (dir > 0 ? 0 : 1);
55 }
56 KOKKOS_INLINE_FUNCTION double openness(Index leaf, int axis, int dir) const {
57 if (!hasOpen)
58 return 1.0;
59 return alpha(static_cast<std::size_t>(leaf) * kFaces + faceIndex(axis, dir));
60 }
61 KOKKOS_INLINE_FUNCTION Real areaOf(Coord s) const {
62 Real a = 1;
63 for (int d = 0; d < Dim - 1; ++d)
64 a *= static_cast<Real>(s) * h0;
65 return a;
66 }
67 KOKKOS_INLINE_FUNCTION Real coeff(Coord si, Coord sj) const {
68 const Real dist = 0.5 * (static_cast<Real>(si) + static_cast<Real>(sj)) * h0;
69 return areaOf(si < sj ? si : sj) / dist;
70 }
71 KOKKOS_INLINE_FUNCTION Coord wrap(long c, int axis) const {
72 const long e = static_cast<long>(fineExt[axis]);
73 return static_cast<Coord>(((c % e) + e) % e);
74 }
75 KOKKOS_INLINE_FUNCTION Real cellWidth(Index i) const {
76 return h0 * static_cast<Real>(Index(1) << ov.levels(i));
77 }
78 KOKKOS_INLINE_FUNCTION Real cellVolume(Index i) const {
79 Real w = cellWidth(i), v = 1;
80 for (int d = 0; d < Dim; ++d)
81 v *= w;
82 return v;
83 }
84
86 KOKKOS_INLINE_FUNCTION double bcDiag(Index i) const {
87 if (periodic && !immersedWall)
88 return 0.0;
89 std::array<Coord, Dim> lo = M::from_code(ov.codes(i)).decode();
90 const Coord si = Coord(Coord(1) << ov.levels(i));
91 const double wall = areaOf(si) / (0.5 * static_cast<Real>(si) * h0);
92 double s = 0.0;
93 for (int axis = 0; axis < Dim; ++axis)
94 for (int dir = -1; dir <= 1; dir += 2) {
95 const long pc = (dir > 0) ? static_cast<long>(lo[axis]) + static_cast<long>(si)
96 : static_cast<long>(lo[axis]) - 1;
97 const bool domainBoundary = !periodic && (pc < 0 || pc >= static_cast<long>(fineExt[axis]));
98 if (domainBoundary)
99 s += openness(i, axis, dir) * wall;
100 else if (immersedWall)
101 s += (1.0 - openness(i, axis, dir)) * wall;
102 }
103 return s;
104 }
105
108 template <class Sink>
109 KOKKOS_INLINE_FUNCTION void operator()(Index i, Sink& sink) const {
110 std::array<Coord, Dim> lo = M::from_code(ov.codes(i)).decode();
111 const unsigned Li = ov.levels(i);
112 const Coord si = Coord(Coord(1) << Li);
113 for (int axis = 0; axis < Dim; ++axis)
114 for (int dir = -1; dir <= 1; dir += 2) {
115 const long pc = (dir > 0) ? static_cast<long>(lo[axis]) + static_cast<long>(si)
116 : static_cast<long>(lo[axis]) - 1;
117 if (!periodic && (pc < 0 || pc >= static_cast<long>(fineExt[axis])))
118 continue;
119 std::array<Coord, Dim> p = lo;
120 p[axis] = wrap(pc, axis);
121 const Index j = ov.locate(M::encode(p).code());
122 const unsigned Lj = ov.levels(j);
123 if (Lj >= Li) {
124 sink(j, openness(i, axis, dir) * coeff(si, Coord(Coord(1) << Lj)));
125 } else {
126 const Coord sj = Coord(si >> 1);
127 const int nsub = 1 << (Dim - 1);
128 for (int k = 0; k < nsub; ++k) {
129 std::array<Coord, Dim> q = lo;
130 q[axis] = wrap(pc, axis);
131 int bit = 0;
132 for (int t = 0; t < Dim; ++t) {
133 if (t == axis)
134 continue;
135 const Coord off = ((k >> bit) & 1) ? sj : Coord(0);
136 q[t] = wrap(static_cast<long>(lo[t]) + static_cast<long>(off), t);
137 ++bit;
138 }
139 const Index jj = ov.locate(M::encode(q).code());
140 sink(jj, openness(jj, axis, -dir) * coeff(si, sj));
141 }
142 }
143 }
144 }
145};
146
151template <int Dim, unsigned Bits>
154 emit.ov = ov;
155 emit.h0 = ap.h0();
156 emit.periodic = ap.periodic();
157 emit.immersedWall = ap.immersedWall();
158 emit.hasOpen = ap.hasOpenness();
159 for (int d = 0; d < Dim; ++d)
160 emit.fineExt[d] = ap.fineExt()[d];
161 if (ap.hasOpenness())
162 emit.alpha = toDevice(ap.opennessRaw(), "amr::alpha");
163
164 Csr csr = buildFaceCsr(ov.numLeaves(), emit);
165
166 FvOp op;
167 op.n = ov.numLeaves();
168 op.faceStart = csr.start;
169 op.faceNbr = csr.nbr;
170 op.faceW = csr.coef;
171 op.invVol = View<double>(Kokkos::view_alloc("amr::invVol", Kokkos::WithoutInitializing),
172 static_cast<std::size_t>(op.n));
173 op.bcDiag = View<double>(Kokkos::view_alloc("amr::bcDiag", Kokkos::WithoutInitializing),
174 static_cast<std::size_t>(op.n));
175 View<double> invVol = op.invVol;
176 View<double> bcDiag = op.bcDiag;
178 Kokkos::parallel_for(
179 "amr::fv_diag", op.n, KOKKOS_LAMBDA(const Index i) {
180 invVol(i) = 1.0 / e.cellVolume(i);
181 bcDiag(i) = e.bcDiag(i);
182 });
183 return op;
184}
185
186} // namespace peclet::core::amr
187
188#endif // PECLET_CORE_HAVE_MORTON
189#endif // PECLET_CORE_AMR_ASSEMBLY_HPP
Cell-centered FV Poisson operator on one (periodic) block octree.
Definition poisson.hpp:43
const std::array< Coord, Dim > & fineExt() const
Per-axis fine-grid extent (brick·2^lmax) — the periodic wrap modulus.
Definition poisson.hpp:478
const std::vector< double > & opennessRaw() const
Definition poisson.hpp:76
Csr buildFaceCsr(Index n, const Emit &emit)
Build a face-CSR on device from a per-cell emit functor.
Definition csr.hpp:89
FvOp assembleFv(const AmrPoisson< Dim, Bits > &ap, const BlockOctreeView< Dim, Bits > &ov)
Assemble the FvOp entirely on the device from a host AmrPoisson's geometry (h0, periodicity,...
Definition assembly.hpp:152
View< T > toDevice(const std::vector< T > &h, const std::string &label)
Upload a host std::vector into a freshly-sized device View (empty vector => empty view).
Definition view.hpp:44
Kokkos::View< T *, MemSpace > View
1D device array.
Definition view.hpp:26
double Real
Default host floating type. Device kernels may use float; conversions happen at the boundary.
Definition types.hpp:18
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
Definition types.hpp:15
Index locate(Code p) const
Leaf containing Morton code p, or -1. Callable in a device kernel.
An assembled face-CSR: row offsets (size n+1), neighbour index + coefficient per face (size nFaces).
Definition csr.hpp:72
View< double > coef
coefficient per face, size nFaces
Definition csr.hpp:75
View< Index > nbr
neighbour leaf per face, size nFaces
Definition csr.hpp:74
View< Index > start
CSR row offsets, size n+1; start(n) == nFaces.
Definition csr.hpp:73
Device-callable reproduction of AmrPoisson's per-cell face walk + geometry.
Definition assembly.hpp:38
Real areaOf(Coord s) const
Definition assembly.hpp:61
Coord fineExt[Dim]
periodic-wrap modulus per axis
Definition assembly.hpp:46
static int faceIndex(int axis, int dir)
Definition assembly.hpp:53
static constexpr int kFaces
Definition assembly.hpp:52
double openness(Index leaf, int axis, int dir) const
Definition assembly.hpp:56
Real cellVolume(Index i) const
Definition assembly.hpp:78
typename View_t::Coord Coord
Definition assembly.hpp:42
View< const double > alpha
per-leaf·(2·Dim) openness, or empty when !hasOpen
Definition assembly.hpp:45
Coord wrap(long c, int axis) const
Definition assembly.hpp:71
void operator()(Index i, Sink &sink) const
Emit each face of leaf i as sink(neighbourLeaf, coef=openness·A_f/d_f) — the exact enumeration (axis-...
Definition assembly.hpp:109
double bcDiag(Index i) const
Σ over leaf i's Dirichlet-wall faces of A_f/(½·cellWidth) — mirrors AmrPoisson::boundaryDiag.
Definition assembly.hpp:86
View_t ov
device octree (codes/levels/locate)
Definition assembly.hpp:44
Real cellWidth(Index i) const
Definition assembly.hpp:75
Real h0
finest cell width
Definition assembly.hpp:47
typename View_t::Code Code
Definition assembly.hpp:41
Real coeff(Coord si, Coord sj) const
Definition assembly.hpp:67
View< double > invVol
1/V_i, size n
Definition fv_op.hpp:81
View< double > faceW
w_f = openness·A_f/d_f per face, size nFaces
Definition fv_op.hpp:84
View< Index > faceStart
CSR row offsets, size n+1.
Definition fv_op.hpp:82
View< double > bcDiag
Dirichlet boundary diagonal per cell (0 if periodic), size n.
Definition fv_op.hpp:85
View< Index > faceNbr
neighbour leaf per face, size nFaces
Definition fv_op.hpp:83