DNDSR 0.2.1
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
AdjIndexInfo.hpp
Go to the documentation of this file.
1#pragma once
2#include "Mesh_DeviceView.hpp"
4
5namespace DNDS::Geom
6{
7 /**
8 * \brief Per-adjacency index state tracking.
9 *
10 * Records whether an adjacency array's entries are global or local
11 * indices, and holds a shared reference to the ghost mapping of the
12 * **target** entity kind (the entity that the indices refer to).
13 *
14 * For example, `cell2node` points to nodes, so its `AdjIndexInfo`
15 * holds a reference to the node ghost mapping
16 * (`coords.trans.pLGhostMapping`).
17 *
18 * All fields are private. State transitions are enforced by methods:
19 *
20 * - markGlobal(): Adj_Unknown -> Adj_PointToGlobal
21 * - wireTargetMapping(m): requires !isLocal(); stores ghost mapping
22 * - toLocal(adj, n): Adj_PointToGlobal -> Adj_PointToLocal (requires wired)
23 * - toGlobal(adj, n): Adj_PointToLocal -> Adj_PointToGlobal (requires wired)
24 * - bootstrapToLocal(m,adj,n): markGlobal + wire + toLocal in one call
25 * (accepts Adj_Unknown or Adj_PointToGlobal)
26 */
28 {
29 private:
30 /// Current global/local state of the entries in this adjacency.
32
33 /// Ghost mapping of the **target** entity kind.
34 /// Used for global<->local conversion of entries.
35 /// nullptr until the target entity's ghost layer has been built.
36 t_pLGhostMapping _targetMapping;
37
38 public:
39 // ============================================================
40 // Queries
41 // ============================================================
42
43 [[nodiscard]] MeshAdjState state() const { return _state; }
44 [[nodiscard]] bool isLocal() const { return _state == Adj_PointToLocal; }
45 [[nodiscard]] bool isGlobal() const { return _state == Adj_PointToGlobal; }
46 [[nodiscard]] bool isBuilt() const { return _state != Adj_Unknown; }
47 [[nodiscard]] bool isWired() const { return _targetMapping != nullptr; }
48
49 /// Read-only access to the stored mapping (for assertions/comparisons).
50 [[nodiscard]] const t_pLGhostMapping &mapping() const { return _targetMapping; }
51
52 // ============================================================
53 // State transitions
54 // ============================================================
55
56 /// \brief Mark this adjacency as containing global indices.
57 ///
58 /// Valid from Adj_Unknown or Adj_PointToGlobal (idempotent).
59 /// Use after building or receiving an adjacency whose entries
60 /// are global entity indices.
62 {
64 _state == Adj_Unknown || _state == Adj_PointToGlobal,
65 "markGlobal: expected Adj_Unknown or Adj_PointToGlobal state");
66 _state = Adj_PointToGlobal;
67 }
68
69 /// \brief Mark this adjacency as containing local indices.
70 ///
71 /// Valid from Adj_Unknown only. Requires target mapping to be wired
72 /// (needed for future toGlobal calls).
73 /// Use when an adjacency is populated directly with local indices,
74 /// bypassing the normal global->local pipeline.
75 void markLocal()
76 {
78 _state == Adj_Unknown,
79 "markLocal: expected Adj_Unknown state");
81 _targetMapping,
82 "markLocal: target mapping must be wired before marking local");
83 _state = Adj_PointToLocal;
84 }
85
86 /// \brief Attach the target entity's ghost mapping.
87 ///
88 /// Callable when state is Adj_Unknown or Adj_PointToGlobal.
89 /// Calling while Adj_PointToLocal is a logic error: indices are
90 /// already local w.r.t. some (possibly different) mapping, and
91 /// replacing the mapping silently would make toGlobal() produce
92 /// garbage.
93 ///
94 /// \param mapping Must be non-null.
96 {
98 _state != Adj_PointToLocal,
99 "wireTargetMapping called while indices are local — "
100 "convert to global first, or the stored mapping will "
101 "be inconsistent with the index values");
103 mapping != nullptr,
104 "wireTargetMapping: mapping must be non-null");
105 _targetMapping = mapping;
106 }
107
108 // ============================================================
109 // Factory: father-only mapping (empty ghost set)
110 // ============================================================
111
112 /// \brief Create a ghost mapping with no ghost entries (father-only).
113 ///
114 /// The resulting OffsetAscendIndexMapping maps owned globals to
115 /// [0, fatherSize) via search_indexAppend, and returns false for
116 /// anything off-rank (encoded as -1 - globalIndex by toLocal).
117 ///
118 /// Useful for wiring adjacencies that will never have ghost data
119 /// (e.g., boundary mesh cell2node).
120 ///
121 /// \warning Collective — calls MPI_Alltoall internally.
123 const ssp<GlobalOffsetsMapping> &globalMapping,
124 index fatherSize,
125 const MPIInfo &mpi)
126 {
127 DNDS_assert_info(globalMapping, "makeFatherOnlyMapping: globalMapping must be non-null");
128 std::vector<index> emptyGhosts;
129 return std::make_shared<OffsetAscendIndexMapping>(
130 (*globalMapping)(mpi.rank, 0), // mainOffset
131 fatherSize, // mainSize
132 emptyGhosts, // empty pull set
133 *globalMapping,
134 mpi);
135 }
136
137 // ============================================================
138 // Conversion: toLocal / toGlobal (mapping must be wired)
139 // ============================================================
140
141 /// \brief Bulk-convert all entries in [0, nRows) from global to local.
142 ///
143 /// Entries that cannot be found in the ghost mapping are encoded as
144 /// `(-1 - globalIndex)`, matching the convention of
145 /// UnstructuredMesh::IndexGlobal2Local.
146 template <class TAdj>
147 void toLocal(TAdj &adj, index nRows)
148 {
150 "toLocal: expected Adj_PointToGlobal state");
151 DNDS_assert_info(_targetMapping,
152 "toLocal: target mapping not wired");
153 for (index i = 0; i < nRows; i++)
154 for (rowsize j = 0; j < adj.RowSize(i); j++)
155 {
156 index &v = adj(i, j);
157 if (v == UnInitIndex)
158 continue;
159 MPI_int rank = UnInitMPIInt;
160 index val = UnInitIndex;
161 if (_targetMapping->search_indexAppend(v, rank, val))
162 v = val;
163 else
164 v = -1 - v; // not-found encoding
165 }
166 _state = Adj_PointToLocal;
167 }
168
169 /// \brief Bulk-convert all entries in [0, nRows) from local to global.
170 template <class TAdj>
171 void toGlobal(TAdj &adj, index nRows)
172 {
174 "toGlobal: expected Adj_PointToLocal state");
175 DNDS_assert_info(_targetMapping,
176 "toGlobal: target mapping not wired");
177 for (index i = 0; i < nRows; i++)
178 for (rowsize j = 0; j < adj.RowSize(i); j++)
179 {
180 index &v = adj(i, j);
181 if (v == UnInitIndex)
182 continue;
183 if (v < 0) // "not-found" encoding from prior G2L
184 v = -1 - v;
185 else
186 v = _targetMapping->operator()(-1, v);
187 }
188 _state = Adj_PointToGlobal;
189 }
190
191 /// \brief OMP-parallelized variant of toLocal.
192 template <class TAdj>
193 void toLocalOMP(TAdj &adj, index nRows)
194 {
196 "toLocalOMP: expected Adj_PointToGlobal state");
197 DNDS_assert_info(_targetMapping,
198 "toLocalOMP: target mapping not wired");
199#ifdef DNDS_USE_OMP
200# pragma omp parallel for
201#endif
202 for (index i = 0; i < nRows; i++)
203 for (rowsize j = 0; j < adj.RowSize(i); j++)
204 {
205 index &v = adj(i, j);
206 if (v == UnInitIndex)
207 continue;
208 MPI_int rank = UnInitMPIInt;
209 index val = UnInitIndex;
210 if (_targetMapping->search_indexAppend(v, rank, val))
211 v = val;
212 else
213 v = -1 - v;
214 }
215 _state = Adj_PointToLocal;
216 }
217
218 /// \brief OMP-parallelized variant of toGlobal.
219 template <class TAdj>
220 void toGlobalOMP(TAdj &adj, index nRows)
221 {
223 "toGlobalOMP: expected Adj_PointToLocal state");
224 DNDS_assert_info(_targetMapping,
225 "toGlobalOMP: target mapping not wired");
226#ifdef DNDS_USE_OMP
227# pragma omp parallel for
228#endif
229 for (index i = 0; i < nRows; i++)
230 for (rowsize j = 0; j < adj.RowSize(i); j++)
231 {
232 index &v = adj(i, j);
233 if (v == UnInitIndex)
234 continue;
235 if (v < 0)
236 v = -1 - v;
237 else
238 v = _targetMapping->operator()(-1, v);
239 }
240 _state = Adj_PointToGlobal;
241 }
242
243 // ============================================================
244 // Bootstrap: wire mapping + convert in one atomic operation
245 // ============================================================
246
247 /// \brief Wire target mapping and convert to local in one step.
248 ///
249 /// Solves the chicken-and-egg problem where the mapping becomes
250 /// available at the same time the adjacency needs converting.
251 /// Accepts Adj_Unknown or Adj_PointToGlobal. Sets state to
252 /// Adj_PointToLocal on completion.
253 template <class TAdj>
255 {
257 _state == Adj_Unknown || _state == Adj_PointToGlobal,
258 "bootstrapToLocal: expected Adj_Unknown or Adj_PointToGlobal state");
259 _targetMapping = mapping;
260 _state = Adj_PointToGlobal; // ensure toLocal precondition
261 toLocal(adj, nRows);
262 }
263
264 /// \brief OMP variant of bootstrapToLocal.
265 template <class TAdj>
267 {
269 _state == Adj_Unknown || _state == Adj_PointToGlobal,
270 "bootstrapToLocalOMP: expected Adj_Unknown or Adj_PointToGlobal state");
271 _targetMapping = mapping;
272 _state = Adj_PointToGlobal;
273 toLocalOMP(adj, nRows);
274 }
275 };
276
277 /**
278 * \brief Flattened wrapper: inherits from TPair and adds AdjIndexInfo.
279 *
280 * Because AdjPairTracked inherits publicly from TPair, all existing
281 * code that accesses `.father`, `.son`, `.trans`, `operator[]`,
282 * `BorrowAndPull()`, etc. works unchanged. The only addition is
283 * the `idx` member for per-adjacency state tracking.
284 *
285 * \tparam TPair The ArrayPair type (e.g. tAdjPair, tAdj2Pair).
286 */
287 template <class TPair>
288 struct AdjPairTracked : public TPair
289 {
291
292 // Convenience: convert this adjacency in-place
293 void toLocal() { idx.toLocal(*this, this->Size()); }
294 void toGlobal() { idx.toGlobal(*this, this->Size()); }
295 void toLocalOMP() { idx.toLocalOMP(*this, this->Size()); }
296 void toGlobalOMP() { idx.toGlobalOMP(*this, this->Size()); }
297
298 /// Bootstrap: wire mapping + convert to local (solves chicken-and-egg).
300 {
301 idx.bootstrapToLocal(mapping, *this, this->Size());
302 }
304 {
305 idx.bootstrapToLocalOMP(mapping, *this, this->Size());
306 }
307
308 // State queries (delegate to idx, with data-presence checks)
309 [[nodiscard]] MeshAdjState state() const { return idx.state(); }
310 [[nodiscard]] bool isLocal() const { return idx.isLocal(); }
311 [[nodiscard]] bool isGlobal() const { return idx.isGlobal(); }
312 /// Whether this adjacency is built and has live data.
313 /// Unlike AdjIndexInfo::isBuilt(), this also checks that the
314 /// father array is non-null, catching stale states after reset.
315 [[nodiscard]] bool isBuilt() const { return this->father && idx.isBuilt(); }
316 [[nodiscard]] bool isWired() const { return idx.isWired(); }
317 [[nodiscard]] const t_pLGhostMapping &mapping() const { return idx.mapping(); }
318
319 /// Tear down both the array data and the per-adj state atomically.
320 /// After this call, isBuilt() returns false.
321 void reset()
322 {
323 this->father.reset();
324 this->son.reset();
325 idx = AdjIndexInfo{};
326 }
327
328 // =============================================================
329 // Device views
330 // =============================================================
331
332 template <DeviceBackend B>
334
335 /// Create a device view that carries the per-adj state alongside
336 /// the father/son array views.
337 template <DeviceBackend B>
339 {
340 auto base = TPair::template deviceView<B>();
341 return t_deviceView<B>{base.father, base.son,
343 }
344
345 template <DeviceBackend B>
346 [[nodiscard]] auto deviceView() const
347 {
348 auto base = TPair::template deviceView<B>();
350 base.father, base.son,
352 }
353 };
354
355} // namespace DNDS::Geom
ParArray (MPI-aware array) and ArrayTransformer (ghost/halo communication).
#define DNDS_assert_info(expr, info)
Debug-only assertion with an extra std::string info message.
Definition Errors.hpp:117
ssp< OffsetAscendIndexMapping > t_pLGhostMapping
Shared pointer to an OffsetAscendIndexMapping (per-rank ghost layout).
DNDS_CONSTANT const index UnInitIndex
Sentinel "not initialised" index value (= INT64_MIN).
Definition Defines.hpp:181
int32_t rowsize
Row-width / per-row element-count type (signed 32-bit).
Definition Defines.hpp:114
constexpr MPI_int UnInitMPIInt
Sentinel "not initialised" MPI_int value (= -1, invalid rank).
Definition MPI.hpp:66
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
Definition Defines.hpp:112
std::shared_ptr< T > ssp
Shortened alias for std::shared_ptr used pervasively in DNDSR.
Definition Defines.hpp:143
int MPI_int
MPI counterpart type for MPI_int (= C int). Used for counts and ranks in MPI calls.
Definition MPI.hpp:54
Device-side state for an adjacency (trivially copyable).
Per-adjacency index state tracking.
void toGlobalOMP(TAdj &adj, index nRows)
OMP-parallelized variant of toGlobal.
void bootstrapToLocalOMP(const t_pLGhostMapping &mapping, TAdj &adj, index nRows)
OMP variant of bootstrapToLocal.
void toLocal(TAdj &adj, index nRows)
Bulk-convert all entries in [0, nRows) from global to local.
void markLocal()
Mark this adjacency as containing local indices.
void bootstrapToLocal(const t_pLGhostMapping &mapping, TAdj &adj, index nRows)
Wire target mapping and convert to local in one step.
static t_pLGhostMapping makeFatherOnlyMapping(const ssp< GlobalOffsetsMapping > &globalMapping, index fatherSize, const MPIInfo &mpi)
Create a ghost mapping with no ghost entries (father-only).
MeshAdjState state() const
void toGlobal(TAdj &adj, index nRows)
Bulk-convert all entries in [0, nRows) from local to global.
void toLocalOMP(TAdj &adj, index nRows)
OMP-parallelized variant of toLocal.
void wireTargetMapping(const t_pLGhostMapping &mapping)
Attach the target entity's ghost mapping.
const t_pLGhostMapping & mapping() const
Read-only access to the stored mapping (for assertions/comparisons).
void markGlobal()
Mark this adjacency as containing global indices.
Flattened wrapper: inherits from TPair and adds AdjIndexInfo.
AdjPairTrackedDeviceView< B, typename TPair::t_arr > t_deviceView
const t_pLGhostMapping & mapping() const
MeshAdjState state() const
void bootstrapToLocal(const t_pLGhostMapping &mapping)
Bootstrap: wire mapping + convert to local (solves chicken-and-egg).
void bootstrapToLocalOMP(const t_pLGhostMapping &mapping)
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
Definition MPI.hpp:231
Eigen::Matrix< real, 5, 1 > v
auto adj
DNDS::index idx