peclet-amr 0.2.0
Block-local-Morton AMR octree + collocated cut-cell Navier–Stokes solver (header-only C++20, Kokkos + MPI)
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// KOKKOS_FUNCTION).
18#ifndef PECLET_AMR_ASSEMBLY_HPP
19#define PECLET_AMR_ASSEMBLY_HPP
20
21#include <array>
22
24#include "peclet/amr/common.hpp"
25#include "peclet/amr/csr.hpp"
26#include "peclet/amr/fv_op.hpp"
28#include "peclet/core/common/view.hpp"
29
30namespace peclet::amr {
31
35template <int Dim, unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
36struct FvFaceEmit {
38 using M = typename View_t::M;
39 using Code = typename View_t::Code;
40 using Coord = typename View_t::Coord;
41
44 Coord fineExt[Dim] = {};
45 Real h0[Dim] = {};
46 bool hasOpen = false;
47 bool periodic = true;
48 bool immersedWall = false;
49
50 static constexpr int kFaces = 2 * Dim;
51 KOKKOS_INLINE_FUNCTION static int faceIndex(int axis, int dir) {
52 return 2 * axis + (dir > 0 ? 0 : 1);
53 }
54 KOKKOS_INLINE_FUNCTION double openness(Index leaf, int axis, int dir) const {
55 if (!hasOpen)
56 return 1.0;
57 return alpha(static_cast<std::size_t>(leaf) * kFaces + faceIndex(axis, dir));
58 }
61 Real a = 1;
62 for (int d = 0; d < Dim; ++d)
63 if (d != axis)
64 a *= static_cast<Real>(s) * h0[d];
65 return a;
66 }
68 const Real dist = 0.5 * (static_cast<Real>(si) + static_cast<Real>(sj)) * h0[axis];
69 return areaOf(si < sj ? si : sj, axis) / 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 }
76 return h0[axis] * static_cast<Real>(Index(1) << ov.levels(i));
77 }
80 const Real f = static_cast<Real>(Index(1) << ov.levels(i));
81 Real v = 1;
82 for (int d = 0; d < Dim; ++d)
83 v *= h0[d] * f;
84 return v;
85 }
86
89 if (periodic && !immersedWall)
90 return 0.0;
91 std::array<Coord, Dim> lo = M::from_code(ov.codes(i)).decode();
92 const Coord si = Coord(Coord(1) << ov.levels(i));
93 double s = 0.0;
94 for (int axis = 0; axis < Dim; ++axis) {
95 const double wall = areaOf(si, axis) / (0.5 * static_cast<Real>(si) * h0[axis]);
96 for (int dir = -1; dir <= 1; dir += 2) {
97 const long pc = (dir > 0) ? static_cast<long>(lo[axis]) + static_cast<long>(si)
98 : static_cast<long>(lo[axis]) - 1;
99 const bool domainBoundary = !periodic && (pc < 0 || pc >= static_cast<long>(fineExt[axis]));
100 if (domainBoundary)
101 s += openness(i, axis, dir) * wall;
102 else if (immersedWall)
103 s += (1.0 - openness(i, axis, dir)) * wall;
104 }
105 }
106 return s;
107 }
108
111 template <class Sink>
113 std::array<Coord, Dim> lo = M::from_code(ov.codes(i)).decode();
114 const unsigned Li = ov.levels(i);
115 const Coord si = Coord(Coord(1) << Li);
116 for (int axis = 0; axis < Dim; ++axis)
117 for (int dir = -1; dir <= 1; dir += 2) {
118 const long pc = (dir > 0) ? static_cast<long>(lo[axis]) + static_cast<long>(si)
119 : static_cast<long>(lo[axis]) - 1;
120 if (!periodic && (pc < 0 || pc >= static_cast<long>(fineExt[axis])))
121 continue;
122 std::array<Coord, Dim> p = lo;
123 p[axis] = wrap(pc, axis);
124 const Index j = ov.locate(M::encode(p).code());
125 const unsigned Lj = ov.levels(j);
126 if (Lj >= Li) {
127 sink(j, openness(i, axis, dir) * coeff(si, Coord(Coord(1) << Lj), axis));
128 } else {
129 const Coord sj = Coord(si >> 1);
130 const int nsub = 1 << (Dim - 1);
131 for (int k = 0; k < nsub; ++k) {
132 std::array<Coord, Dim> q = lo;
133 q[axis] = wrap(pc, axis);
134 int bit = 0;
135 for (int t = 0; t < Dim; ++t) {
136 if (t == axis)
137 continue;
138 const Coord off = ((k >> bit) & 1) ? sj : Coord(0);
139 q[t] = wrap(static_cast<long>(lo[t]) + static_cast<long>(off), t);
140 ++bit;
141 }
142 const Index jj = ov.locate(M::encode(q).code());
143 sink(jj, openness(jj, axis, -dir) * coeff(si, sj, axis));
144 }
145 }
146 }
147 }
148};
149
154template <int Dim, unsigned Bits>
157 emit.ov = ov;
158 for (int d = 0; d < Dim; ++d)
159 emit.h0[d] = ap.h0()[d];
160 emit.periodic = ap.periodic();
161 emit.immersedWall = ap.immersedWall();
162 emit.hasOpen = ap.hasOpenness();
163 for (int d = 0; d < Dim; ++d)
164 emit.fineExt[d] = ap.fineExt()[d];
165 if (ap.hasOpenness())
166 emit.alpha = toDevice(ap.opennessRaw(), "amr::alpha");
167
169
170 FvOp op;
171 op.n = ov.numLeaves();
172 op.faceStart = csr.start;
173 op.faceNbr = csr.nbr;
174 op.faceW = csr.coef;
175 op.invVol = View<double>(Kokkos::view_alloc("amr::invVol", Kokkos::WithoutInitializing),
176 static_cast<std::size_t>(op.n));
177 op.bcDiag = View<double>(Kokkos::view_alloc("amr::bcDiag", Kokkos::WithoutInitializing),
178 static_cast<std::size_t>(op.n));
179 View<double> invVol = op.invVol;
180 View<double> bcDiag = op.bcDiag;
182 Kokkos::parallel_for(
183 "amr::fv_diag", op.n, KOKKOS_LAMBDA(const Index i) {
184 invVol(i) = 1.0 / e.cellVolume(i);
185 bcDiag(i) = e.bcDiag(i);
186 });
187 return op;
188}
189
190} // namespace peclet::amr
191
192#endif // PECLET_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:592
const Vec< Dim > & h0() const
Definition poisson.hpp:587
bool immersedWall() const
Definition poisson.hpp:222
bool hasOpenness() const
Definition poisson.hpp:156
bool periodic() const
Definition poisson.hpp:212
const std::vector< double > & opennessRaw() const
Definition poisson.hpp:157
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:155
std::vector< std::array< double, Dim > > transferFieldGradients(const BlockOctree< Dim, Bits > &oldT, const std::vector< double > &oldF)
Per-old-leaf minmod prolongation gradients (per fine-coordinate unit) — transferField's stencil,...
Definition adapt.hpp:51
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
Device-callable reproduction of AmrPoisson's per-cell face walk + geometry.
Definition assembly.hpp:36
static constexpr int kFaces
Definition assembly.hpp:50
typename View_t::Code Code
Definition assembly.hpp:39
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:112
double bcDiag(Index i) const
Σ over leaf i's Dirichlet-wall faces of A_f/(½·cellWidth) — mirrors AmrPoisson::boundaryDiag.
Definition assembly.hpp:88
Real areaOf(Coord s, int axis) const
Area of a face NORMAL TO axis (Phase 3: the product over the OTHER axes).
Definition assembly.hpp:60
Real cellWidth(Index i, int axis) const
Definition assembly.hpp:75
typename View_t::Coord Coord
Definition assembly.hpp:40
Real cellVolume(Index i) const
Definition assembly.hpp:79
Real coeff(Coord si, Coord sj, int axis) const
Definition assembly.hpp:67
Real cellWidth(Index i) const
Definition assembly.hpp:78
Coord fineExt[Dim]
periodic-wrap modulus per axis
Definition assembly.hpp:44
View_t ov
device octree (codes/levels/locate)
Definition assembly.hpp:42
typename View_t::M M
Definition assembly.hpp:38
View< const double > alpha
per-leaf·(2·Dim) openness, or empty when !hasOpen
Definition assembly.hpp:43
double openness(Index leaf, int axis, int dir) const
Definition assembly.hpp:54
Coord wrap(long c, int axis) const
Definition assembly.hpp:71
Real h0[Dim]
finest cell width PER AXIS (Phase 3); set by the emitters
Definition assembly.hpp:45
static int faceIndex(int axis, int dir)
Definition assembly.hpp:51
View< Index > faceStart
CSR row offsets, size n+1.
Definition fv_op.hpp:80
View< double > bcDiag
Dirichlet boundary diagonal per cell (0 if periodic), size n.
Definition fv_op.hpp:83
View< double > invVol
1/V_i, size n
Definition fv_op.hpp:79
View< Index > faceNbr
neighbour leaf per face, size nFaces
Definition fv_op.hpp:81
View< double > faceW
w_f = openness·A_f/d_f per face, size nFaces
Definition fv_op.hpp:82