DNDSR 0.2.1
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
Mesh_Reorder.cpp
Go to the documentation of this file.
1/// @file Mesh_Reorder.cpp
2/// @brief Implementation of ReorderPlan::build, ReorderPlan::apply, and
3/// UnstructuredMesh::buildReorderRegistry / ReorderEntities.
4
5#include "ReorderPlan.hpp"
6#include "Mesh.hpp"
8
9#include <algorithm>
10#include <set>
11
12namespace DNDS::Geom
13{
14 // =================================================================
15 // ComputeFollowMapFromAdj: derive follower placement from leader
16 // =================================================================
17
18 /// Compute a follow map for `follower` based on `leader`'s explicit map.
19 ///
20 /// Uses the support adjacency follower->leader (e.g., node2cell) to
21 /// determine: for each follower entity, go to min(leader.targetRank)
22 /// over all leaders referencing it.
23 ///
24 /// @param follower2leader Raw follower->leader adj (father-only, global entries).
25 /// @param nFollower Number of follower entities (father size).
26 /// @param leaderGM Global mapping for the leader entity kind.
27 /// @param leaderTargetRanks Per-leader-slot target rank (the explicit map).
28 /// @param mpi MPI communicator.
29 /// @return Per-follower-slot target rank.
30 /// @warning Collective.
31 template <rowsize f2l_rs>
32 static std::vector<MPI_int> ComputeFollowMapFromAdj(
33 const ArrayAdjacencyPair<f2l_rs> &follower2leader,
34 index nFollower,
35 const ssp<GlobalOffsetsMapping> &leaderGM,
36 const std::vector<MPI_int> &leaderTargetRanks,
37 const MPIInfo &mpi)
38 {
39 DNDS_assert(leaderGM);
40 DNDS_assert(follower2leader.father);
41
42 // Step 1: Build a ghost-pullable lookup of leader's targetRanks.
43 ArrayAdjacencyPair<1> leaderLookup;
44 leaderLookup.InitPair("followMap_leaderLookup", mpi);
45 auto nLeader = static_cast<index>(leaderTargetRanks.size());
46 leaderLookup.father->Resize(nLeader);
47 for (index i = 0; i < nLeader; i++)
48 leaderLookup(i, 0) = static_cast<index>(leaderTargetRanks[i]);
49 leaderLookup.TransAttach();
50 leaderLookup.trans.createFatherGlobalMapping();
51
52 // Step 2: Collect off-rank leader globals from follower2leader entries.
53 std::set<index> offRankLeaderGlobals;
54 for (index i = 0; i < nFollower; i++)
55 for (rowsize j = 0; j < follower2leader.RowSize(i); j++)
56 {
57 index leaderGlobal = follower2leader(i, j);
58 if (leaderGlobal == UnInitIndex)
59 continue;
60 auto [found, rank, val] = leaderGM->search(leaderGlobal);
61 if (found && rank != mpi.rank)
62 offRankLeaderGlobals.insert(leaderGlobal);
63 }
64
65 // Step 3: Ghost-pull leaderLookup for off-rank leaders.
66 std::vector<index> pullSet(offRankLeaderGlobals.begin(), offRankLeaderGlobals.end());
67 leaderLookup.trans.createGhostMapping(pullSet);
68 leaderLookup.trans.createMPITypes();
69 leaderLookup.trans.pullOnce();
70
71 // Step 4: For each follower, find min leader targetRank.
72 std::vector<MPI_int> followMap(nFollower, mpi.size); // init to max
73 for (index i = 0; i < nFollower; i++)
74 {
75 MPI_int minRank = mpi.size;
76 for (rowsize j = 0; j < follower2leader.RowSize(i); j++)
77 {
78 index leaderGlobal = follower2leader(i, j);
79 if (leaderGlobal == UnInitIndex)
80 continue;
81 // Resolve to local-appended index in leaderLookup
82 MPI_int rank;
83 index val;
84 bool found = leaderLookup.trans.pLGhostMapping->search_indexAppend(
85 leaderGlobal, rank, val);
86 DNDS_assert(found);
87 auto leaderTarget = static_cast<MPI_int>(leaderLookup(val, 0));
88 minRank = std::min(minRank, leaderTarget);
89 }
90 // If no leaders found (should not happen for valid mesh), stay put
91 followMap[i] = (minRank < mpi.size) ? minRank : mpi.rank;
92 }
93
94 return followMap;
95 }
96
97 // =================================================================
98 // ReorderPlan::build
99 // =================================================================
100
102 const ReorderInput &input,
103 const ReorderRegistry &registry,
104 const MPIInfo &mpi)
105 {
107
108 // --- Step 1: Merge explicit maps into allMaps ---
109 std::unordered_map<EntityKind, std::vector<MPI_int>> allMaps;
110 for (const auto &em : input.explicitMaps)
111 allMaps[em.kind] = em.targetRanks;
112
113 // --- Step 2: Compute follow maps ---
114 // Follow computation is handled by the mesh wrapper (resolveFollows)
115 // before calling build. By the time we reach here, all entity kinds
116 // should be in explicitMaps. The input.follows field is not processed
117 // here — it is a mesh-wrapper-level concept.
118 DNDS_assert_info(input.follows.empty(),
119 "ReorderPlan::build: input.follows must be empty; "
120 "use UnstructuredMesh::resolveFollows to compute follow maps "
121 "before calling build");
122
123 // --- Step 3: Build PermutationTransfer per entity kind ---
124 plan.reorderedKinds.clear();
125 for (auto &[kind, ranks] : allMaps)
126 {
127 plan.reorderedKinds.insert(kind);
128 auto gm = registry.getGlobalMapping(kind);
129 DNDS_assert_info(gm, fmt::format("ReorderPlan::build: no global mapping for kind {}",
130 entityKindName(kind)));
131 plan.transfers[kind] = PermutationTransfer::fromPartition(ranks, gm, mpi);
132 }
133
134 // --- Step 4: Detect global local-only ---
135 plan.isLocalOnly = true;
136 for (auto &[kind, transfer] : plan.transfers)
137 if (!transfer.isLocalOnly)
138 {
139 plan.isLocalOnly = false;
140 break;
141 }
142 int globalFlag;
143 int localFlag = plan.isLocalOnly ? 1 : 0;
144 MPI_Allreduce(&localFlag, &globalFlag, 1, MPI_INT, MPI_LAND, mpi.comm);
145 plan.isLocalOnly = (globalFlag != 0);
146
147 // --- Step 5: Collect pull sets and build lookups ---
148 for (auto kind : plan.reorderedKinds)
149 {
150 std::set<index> pullSetCollector;
151 auto gm = registry.getGlobalMapping(kind);
152
153 // Use pre-collected pull sets from the registry
154 std::vector<index> pullSet;
155 auto psIt = registry.pullSets.find(kind);
156 if (psIt != registry.pullSets.end())
157 pullSet = psIt->second;
158
159 plan.lookups[kind] = plan.transfers.at(kind).buildLookup(pullSet, mpi);
160 }
161
162 return plan;
163 }
164
165 // =================================================================
166 // ReorderPlan::apply
167 // =================================================================
168
169 void ReorderPlan::apply(ReorderRegistry &registry, const MPIInfo &mpi) const
170 {
171 // Phase 1: REMAP all adj entries
172 for (auto &adj : registry.adjs)
173 {
174 auto action = classifyAdj(adj.kind, reorderedKinds);
175 if (action == AdjAction::REMAP ||
176 action == AdjAction::RELOCATE_REMAP ||
177 action == AdjAction::SELF)
178 {
179 EntityKind targetKind = adj.kind.isIntraLevel()
180 ? adj.kind.from
181 : adj.kind.to;
182 auto it = lookups.find(targetKind);
183 DNDS_assert_info(it != lookups.end(),
184 fmt::format("ReorderPlan::apply REMAP: no lookup for target kind {}",
185 entityKindName(targetKind)));
186 if (adj.remapFn)
187 adj.remapFn(it->second);
188 }
189 }
190
191 // Phase 2: RELOCATE all adj rows
192 for (auto &adj : registry.adjs)
193 {
194 auto action = classifyAdj(adj.kind, reorderedKinds);
195 if (action == AdjAction::RELOCATE ||
196 action == AdjAction::RELOCATE_REMAP ||
197 action == AdjAction::SELF)
198 {
199 EntityKind sourceKind = adj.kind.from;
200 auto it = transfers.find(sourceKind);
201 DNDS_assert_info(it != transfers.end(),
202 fmt::format("ReorderPlan::apply RELOCATE: no transfer for source kind {}",
203 entityKindName(sourceKind)));
204 if (adj.relocateFn)
205 adj.relocateFn(it->second, mpi);
206 }
207 }
208
209 // Phase 3: RELOCATE all companions of reordered kinds
210 for (auto &comp : registry.companions)
211 {
212 if (reorderedKinds.count(comp.kind))
213 {
214 auto it = transfers.find(comp.kind);
215 DNDS_assert_info(it != transfers.end(),
216 fmt::format("ReorderPlan::apply COMPANION: no transfer for kind {}",
217 entityKindName(comp.kind)));
218 comp.fn(it->second, mpi);
219 }
220 }
221 }
222
223 // =================================================================
224 // UnstructuredMesh::buildReorderRegistry
225 // =================================================================
226
228 const std::unordered_set<EntityKind> &destroyKinds)
229 {
231
232 auto shouldSkip = [&](AdjKind kind)
233 {
234 return destroyKinds.count(kind.from) || destroyKinds.count(kind.to);
235 };
236
237 // --- Helper: register a tracked adj member ---
238 auto regAdj = [&](AdjKind kind, auto &trackedPair)
239 {
240 if (!trackedPair.father || shouldSkip(kind))
241 return;
242
244 {
245 index nRows = trackedPair.father->Size();
246 for (index i = 0; i < nRows; i++)
247 for (rowsize j = 0; j < trackedPair.RowSize(i); j++)
248 {
249 index &v = trackedPair(i, j);
250 if (v != UnInitIndex)
251 v = lookup.resolve(v);
252 }
253 };
254
256 const PermutationTransfer &t, const MPIInfo &m)
257 {
258 t.transferRows(trackedPair, m);
259 };
260
261 reg.registerAdj(kind, std::move(remap), std::move(relocate),
262 adjKindName(kind));
263 };
264
265 // Register tracked adj members
278
279 // --- Helper: register a companion ---
280 auto regComp = [&](EntityKind kind, auto &pair, const char *name)
281 {
282 if (!pair.father || destroyKinds.count(kind))
283 return;
284 reg.registerCompanion(kind, [&pair](const PermutationTransfer &t, const MPIInfo &m)
285 { t.transferRows(pair, m); }, name);
286 };
287
288 // Register companions
289 regComp(EntityKind::Cell, cellElemInfo, "cellElemInfo");
290 regComp(EntityKind::Cell, cell2cellOrig, "cell2cellOrig");
291 regComp(EntityKind::Node, coords, "coords");
292 regComp(EntityKind::Node, node2nodeOrig, "node2nodeOrig");
293 regComp(EntityKind::Bnd, bndElemInfo, "bndElemInfo");
294 regComp(EntityKind::Bnd, bnd2bndOrig, "bnd2bndOrig");
295
296 if (isPeriodic)
297 {
298 regComp(EntityKind::Cell, cell2nodePbi, "cell2nodePbi");
299 regComp(EntityKind::Bnd, bnd2nodePbi, "bnd2nodePbi");
300 if (!destroyKinds.count(EntityKind::Face))
301 regComp(EntityKind::Face, face2nodePbi, "face2nodePbi");
302 }
303 if (!destroyKinds.count(EntityKind::Face))
304 regComp(EntityKind::Face, faceElemInfo, "faceElemInfo");
306 regComp(EntityKind::Node, coordsElevDisp, "coordsElevDisp");
308 regComp(EntityKind::Node, nodeWallDist, "nodeWallDist");
309
310 // --- Register global mappings ---
311 auto getGM = [](const auto &pair) -> ssp<GlobalOffsetsMapping>
312 {
313 if (pair.father && pair.father->pLGlobalMapping)
314 return pair.father->pLGlobalMapping;
315 return nullptr;
316 };
317
318 auto firstValid = [](std::initializer_list<ssp<GlobalOffsetsMapping>> candidates)
320 {
321 for (const auto &gm : candidates)
322 if (gm)
323 return gm;
324 return nullptr;
325 };
326
328 reg.registerGlobalMapping(EntityKind::Cell, gm);
329 if (auto gm = coords.father ? coords.father->pLGlobalMapping : nullptr)
330 reg.registerGlobalMapping(EntityKind::Node, gm);
332 reg.registerGlobalMapping(EntityKind::Bnd, gm);
334 reg.registerGlobalMapping(EntityKind::Face, gm);
335
336 // --- Pre-collect pull sets ---
337 // For each entity kind, collect off-rank globals from adj entries targeting it.
338 auto collectPS = [&](EntityKind targetKind, const auto &adjPair, auto targetGM)
339 {
340 if (!adjPair.father || !targetGM)
341 return;
342 auto &ps = reg.pullSets[targetKind];
343 DNDS::index nRows = adjPair.father->Size();
344 for (DNDS::index i = 0; i < nRows; i++)
345 for (rowsize j = 0; j < adjPair.RowSize(i); j++)
346 {
347 DNDS::index v = adjPair(i, j);
348 if (v == UnInitIndex)
349 continue;
350 auto [found, rank, val] = targetGM->search(v);
351 if (found && rank != mpi.rank)
352 ps.push_back(v);
353 }
354 };
355
356 // Cell as target (from: bnd2cell, face2cell, node2cell, cell2cell)
357 auto cellGM = reg.getGlobalMapping(EntityKind::Cell);
363
364 // Node as target (from: cell2node, bnd2node, face2node)
365 auto nodeGM = reg.getGlobalMapping(EntityKind::Node);
370
371 // Bnd as target (from: node2bnd, face2bnd)
372 auto bndGM = reg.getGlobalMapping(EntityKind::Bnd);
376
377 // Deduplicate and sort pull sets
378 for (auto &[kind, ps] : reg.pullSets)
379 {
380 std::sort(ps.begin(), ps.end());
381 ps.erase(std::unique(ps.begin(), ps.end()), ps.end());
382 }
383
384 return reg;
385 }
386
387 // =================================================================
388 // UnstructuredMesh::resolveFollows
389 // =================================================================
390
392 const ReorderInput &input,
393 const ReorderRegistry &reg)
394 {
396 std::unordered_set<EntityKind> explicitKinds;
397 for (auto &em : augmented.explicitMaps)
398 explicitKinds.insert(em.kind);
399
400 // When follows is empty, apply default policy: all non-explicit
401 // entity kinds with a support adjacency to an explicit kind follow it.
402 // Currently: Node and Bnd follow Cell if Cell is explicit.
403 if (augmented.follows.empty())
404 {
406 {
407 if (!explicitKinds.count(EntityKind::Node) && node2cell.father)
408 augmented.follows.push_back(
410 if (!explicitKinds.count(EntityKind::Bnd) && bnd2cell.father)
411 augmented.follows.push_back(
413 }
414 }
415
416 // Compute follow maps and merge into explicit maps
417 std::unordered_map<EntityKind, std::vector<MPI_int>> allMaps;
418 for (auto &em : augmented.explicitMaps)
419 allMaps[em.kind] = em.targetRanks;
420
421 for (auto &spec : augmented.follows)
422 {
423 if (allMaps.count(spec.follower))
424 continue; // explicit takes precedence
425
426 auto leaderIt = allMaps.find(spec.leader);
427 DNDS_assert_info(leaderIt != allMaps.end(),
428 fmt::format("resolveFollows: follow spec references leader {} "
429 "which has no map",
430 entityKindName(spec.leader)));
431
432 auto followerGM = reg.getGlobalMapping(spec.follower);
433 DNDS_assert_info(followerGM,
434 fmt::format("resolveFollows: no global mapping for follower {}",
435 entityKindName(spec.follower)));
436 index nFollower = followerGM->RLengths()[mpi.rank];
437
438 std::vector<MPI_int> followMap;
439 if (spec.follower2leader == Adj::Node2Cell && node2cell.father)
440 {
441 followMap = ComputeFollowMapFromAdj(
442 static_cast<const tAdjPair &>(node2cell),
443 nFollower, reg.getGlobalMapping(spec.leader),
444 leaderIt->second, mpi);
445 }
446 else if (spec.follower2leader == Adj::Bnd2Cell && bnd2cell.father)
447 {
448 followMap = ComputeFollowMapFromAdj(
449 static_cast<const tAdj2Pair &>(bnd2cell),
450 nFollower, reg.getGlobalMapping(spec.leader),
451 leaderIt->second, mpi);
452 }
453 else if (spec.follower2leader == Adj::Node2Bnd && node2bnd.father)
454 {
455 followMap = ComputeFollowMapFromAdj(
456 static_cast<const tAdjPair &>(node2bnd),
457 nFollower, reg.getGlobalMapping(spec.leader),
458 leaderIt->second, mpi);
459 }
460 else
461 {
462 DNDS_assert_info(false,
463 fmt::format("resolveFollows: unsupported follow adj {}",
464 adjKindName(spec.follower2leader)));
465 }
466
467 allMaps[spec.follower] = std::move(followMap);
468 }
469
470 // Build finalised input with all maps explicit
471 ReorderInput finalInput;
472 for (auto &[kind, ranks] : allMaps)
473 finalInput.explicitMaps.push_back(EntityReorderMap{kind, ranks});
474 finalInput.destroyKinds = input.destroyKinds;
475 return finalInput;
476 }
477
478 // =================================================================
479 // UnstructuredMesh::buildReorderPlan
480 // =================================================================
481
482 ReorderPlan UnstructuredMesh::buildReorderPlan(const ReorderInput &input)
483 {
484 auto reg = buildReorderRegistry(input.destroyKinds);
485 auto finalInput = resolveFollows(input, reg);
486 return ReorderPlan::build(finalInput, reg, mpi);
487 }
488
489 // =================================================================
490 // UnstructuredMesh::ReorderEntities
491 // =================================================================
492
493 void UnstructuredMesh::ReorderEntities(const ReorderInput &input)
494 {
495 // Step 0: Validate precondition
496 DNDS_assert_info(adjPrimaryState == Adj_PointToGlobal,
497 "ReorderEntities: adjPrimaryState must be Adj_PointToGlobal");
498
499 // Step 1: Build registry (with destroy skip)
500 auto reg = buildReorderRegistry(input.destroyKinds);
501
502 // Step 2: Resolve follows and build plan
503 auto finalInput = resolveFollows(input, reg);
504 auto plan = ReorderPlan::build(finalInput, reg, mpi);
505
506 // Step 3: Destroy adjacencies for destroyKinds
507 auto destroyAdj = [&](auto &trackedPair)
508 {
509 trackedPair.father.reset();
510 trackedPair.son.reset();
512 };
513 for (auto kind : input.destroyKinds)
514 {
515 if (kind == EntityKind::Face)
516 {
517 destroyAdj(cell2face);
518 destroyAdj(face2node);
519 destroyAdj(face2cell);
520 destroyAdj(face2bnd);
521 destroyAdj(bnd2face);
522 destroyAdj(cell2cellFace);
523 faceElemInfo.father.reset();
524 faceElemInfo.son.reset();
525 if (isPeriodic)
526 {
527 face2nodePbi.father.reset();
528 face2nodePbi.son.reset();
529 }
530 adjFacialState = Adj_Unknown;
531 adjC2FState = Adj_Unknown;
532 adjC2CFaceState = Adj_Unknown;
533 }
534 }
535
536 // Step 4: Apply plan (REMAP entries, RELOCATE rows + companions)
537 plan.apply(reg, mpi);
538
539 // Step 5: Rebuild global mappings for reordered entities
540 for (auto kind : plan.reorderedKinds)
541 {
542 if (kind == EntityKind::Cell && cell2node.father)
543 {
544 cell2node.father->createGlobalMapping();
545 // Borrow to other cell-parallel arrays
546 if (cell2cell.father)
547 cell2cell.father->pLGlobalMapping = cell2node.father->pLGlobalMapping;
548 if (cellElemInfo.father)
549 cellElemInfo.father->pLGlobalMapping = cell2node.father->pLGlobalMapping;
550 if (cell2cellOrig.father)
551 cell2cellOrig.father->pLGlobalMapping = cell2node.father->pLGlobalMapping;
552 }
553 else if (kind == EntityKind::Node && coords.father)
554 {
555 coords.father->createGlobalMapping();
556 if (node2nodeOrig.father)
557 node2nodeOrig.father->pLGlobalMapping = coords.father->pLGlobalMapping;
558 }
559 else if (kind == EntityKind::Bnd && bnd2node.father)
560 {
561 bnd2node.father->createGlobalMapping();
562 if (bndElemInfo.father)
563 bndElemInfo.father->pLGlobalMapping = bnd2node.father->pLGlobalMapping;
564 if (bnd2bndOrig.father)
565 bnd2bndOrig.father->pLGlobalMapping = bnd2node.father->pLGlobalMapping;
566 }
567 else if (kind == EntityKind::Face && face2node.father)
568 {
569 face2node.father->createGlobalMapping();
570 if (faceElemInfo.father)
571 faceElemInfo.father->pLGlobalMapping = face2node.father->pLGlobalMapping;
572 }
573 }
574
575 // Step 5b: Re-attach transformers with empty sons (prepare for ghost rebuild)
576 // After transferRows, sons are null. The rebuild pipeline
577 // (RecoverNode2CellAndNode2Bnd, etc.) needs TransAttach-ready pairs.
578 auto reattach = [&](auto &pair)
579 {
580 if (!pair.father)
581 return;
582 using TArr = typename std::remove_reference_t<decltype(pair)>::t_arr;
583 if (!pair.son)
584 pair.son = make_ssp<TArr>(ObjName{"reorder.son"}, mpi);
585 pair.TransAttach();
586 };
587
588 for (auto kind : plan.reorderedKinds)
589 {
590 if (kind == EntityKind::Cell)
591 {
593 reattach(cell2cell);
595 reattach(cell2cellOrig);
596 if (isPeriodic)
598 }
599 else if (kind == EntityKind::Node)
600 {
601 reattach(coords);
602 reattach(node2nodeOrig);
603 }
604 else if (kind == EntityKind::Bnd)
605 {
606 reattach(bnd2node);
607 reattach(bnd2cell);
608 reattach(bndElemInfo);
609 reattach(bnd2bndOrig);
610 if (isPeriodic)
611 reattach(bnd2nodePbi);
612 }
613 }
614 // Also reattach inverse adjacencies if they exist
615 if (node2cell.father)
616 reattach(node2cell);
617 if (node2bnd.father)
618 reattach(node2bnd);
619
620 // Step 6: Update idx states
621 auto markGlobalIfBuilt = [](auto &trackedPair)
622 {
623 if (trackedPair.father && trackedPair.idx.state() != Adj_Unknown)
624 trackedPair.idx.markGlobal();
625 };
626 // For all tracked adj that were affected (non-SKIP), mark global.
627 // Simplification: mark all existing adj as global since we're in
628 // Adj_PointToGlobal state overall.
629 if (cell2node.father)
630 cell2node.idx.markGlobal();
631 if (cell2cell.father)
632 cell2cell.idx.markGlobal();
633 if (bnd2node.father)
634 bnd2node.idx.markGlobal();
635 if (bnd2cell.father)
636 bnd2cell.idx.markGlobal();
637 if (node2cell.father)
638 node2cell.idx.markGlobal();
639 if (node2bnd.father)
640 node2bnd.idx.markGlobal();
641
642 // Step 7: Update mesh-level state
643 adjPrimaryState = Adj_PointToGlobal;
644 if (node2cell.father)
645 adjN2CBState = Adj_PointToGlobal;
646
647 // Invalidate local vectors
648 cell2parentCell.clear();
649 node2parentNode.clear();
650 node2bndNode.clear();
651 vtkCell2nodeOffsets.clear();
652 vtkCellType.clear();
653 vtkCell2node.clear();
654 nodeRecreated2nodeLocal.clear();
655 localPartitionStarts.clear();
656 }
657
658 // =================================================================
659 // UnstructuredMesh::ReorderLocalCells (new, using ReorderEntities)
660 // =================================================================
661
662 void UnstructuredMesh::ReorderLocalCells(int nParts, int nPartsInner)
663 {
664 DNDS_assert(this->adjPrimaryState == Adj_PointToLocal);
665 DNDS_assert(cell2node.isLocal() && bnd2node.isLocal() &&
666 cell2cell.isLocal() && bnd2cell.isLocal());
667 nParts = std::max(nParts, 1);
668 nPartsInner = std::max(nPartsInner, 1);
669
670 // --- Section A: Compute cell permutation (same as legacy) ---
671 // We need the local face-adjacency graph and cell2cell.
672 // Convert to global first to get the permutation computation right,
673 // then convert back and redo with the framework.
674 // Actually: ComputeCellPermutation works on LOCAL indices (it uses
675 // cell2cell in local state). So compute the permutation first.
676 auto cell2cellFaceV = this->GetCell2CellFaceVLocal();
677
678 auto perm = detail::ComputeCellPermutation(
679 cell2cellFaceV, cell2cell, NumCell(), nParts, nPartsInner);
680 this->localPartitionStarts = std::move(perm.localPartitionStarts);
681
684 if (mpi.rank == mRank)
685 log() << fmt::format("UnstructuredMesh === ReorderLocalCells, nPart0 [{}], "
686 "got reordering, bw [{}] to [{}]",
687 nParts, perm.bwOld, perm.bwNew)
688 << std::endl;
689
690 // --- Convert to global ---
691 if (this->adjFacialState == Adj_PointToLocal && face2cell.isBuilt())
692 this->AdjLocal2GlobalFacial();
693 if (this->adjC2FState == Adj_PointToLocal && cell2face.isBuilt())
694 this->AdjLocal2GlobalC2F();
695 if (this->adjC2CFaceState == Adj_PointToLocal && cell2cellFace.isBuilt())
696 this->AdjLocal2GlobalC2CFace();
697 if (this->adjN2CBState == Adj_PointToLocal && node2cell.isBuilt())
698 this->AdjLocal2GlobalN2CB();
700
701 // --- Build cell partition map (all local, use permutation) ---
702 // fromLocalPermutation expects old2new. perm.cellOld2New is that.
703 std::vector<MPI_int> cellPartition(NumCell(), mpi.rank);
704
705 // We need to communicate the permutation to ReorderEntities.
706 // The framework's fromPartition computes new globals automatically,
707 // but for a local permutation we want a specific ordering.
708 // Use fromLocalPermutation by overriding the transfer after build.
709 //
710 // Actually, the simplest approach: call ReorderEntities with the
711 // all-same-rank partition (which is an identity from the framework's
712 // perspective), then separately apply the local permutation.
713 //
714 // Better: don't use ReorderEntities here. Instead, use the framework
715 // components directly with fromLocalPermutation.
716
717 // Build a PermutationTransfer from the computed permutation
718 auto cellGM = cell2node.father->pLGlobalMapping;
721 perm.cellOld2New, cellGM, mpi);
722 DNDS_assert(cellTransfer.isLocalOnly);
723
724 // Build lookup for cell entry remapping
725 // Collect off-rank cell globals from adj entries targeting cells
726 std::set<index> cellPullSet;
727 auto addCellRefs = [&](const auto &adj, index nRows)
728 {
729 for (index i = 0; i < nRows; i++)
730 for (rowsize j = 0; j < adj.RowSize(i); j++)
731 {
732 index v = adj(i, j);
733 if (v == UnInitIndex)
734 continue;
735 auto [found, rank, val] = cellGM->search(v);
736 if (found && rank != mpi.rank)
737 cellPullSet.insert(v);
738 }
739 };
740 addCellRefs(cell2cell, NumCell());
741 addCellRefs(bnd2cell, NumBnd());
742 if (node2cell.father)
743 addCellRefs(node2cell, NumNode());
744 if (face2cell.father)
745 addCellRefs(face2cell, NumFace());
746 if (cell2cellFace.father)
747 addCellRefs(cell2cellFace, NumCell());
748
749 std::vector<index> pullVec(cellPullSet.begin(), cellPullSet.end());
750 auto cellLookup = cellTransfer.buildLookup(pullVec, mpi);
751
752 // --- REMAP: update cell indices in xxx2cell adjacencies ---
753 auto remapCellEntries = [&](auto &adj, index nRows)
754 {
755 for (index i = 0; i < nRows; i++)
756 for (rowsize j = 0; j < adj.RowSize(i); j++)
757 {
758 index &v = adj(i, j);
759 if (v != UnInitIndex)
760 v = cellLookup.resolve(v);
761 }
762 };
763
764 remapCellEntries(cell2cell, NumCell());
765 remapCellEntries(bnd2cell, NumBnd());
766 if (node2cell.father)
767 remapCellEntries(node2cell, NumNode());
768 if (face2cell.father)
769 remapCellEntries(face2cell, NumFace());
770 if (cell2cellFace.father)
771 remapCellEntries(cell2cellFace, NumCell());
772
773 // --- RELOCATE: permute cell2xxx rows ---
774 cellTransfer.transferRows(cell2node, mpi);
775 cellTransfer.transferRows(cell2cell, mpi);
776 cellTransfer.transferRows(cellElemInfo, mpi);
777 cellTransfer.transferRows(cell2cellOrig, mpi);
778 if (cell2face.father)
779 cellTransfer.transferRows(cell2face, mpi);
780 if (cell2cellFace.father)
781 cellTransfer.transferRows(cell2cellFace, mpi);
782 if (isPeriodic && cell2nodePbi.father)
783 cellTransfer.transferRows(cell2nodePbi, mpi);
784
785 // --- Rebuild global mapping ---
786 cell2node.father->createGlobalMapping();
787 if (cell2cell.father)
788 cell2cell.father->pLGlobalMapping = cell2node.father->pLGlobalMapping;
789 if (cellElemInfo.father)
790 cellElemInfo.father->pLGlobalMapping = cell2node.father->pLGlobalMapping;
791 if (cell2cellOrig.father)
792 cell2cellOrig.father->pLGlobalMapping = cell2node.father->pLGlobalMapping;
793
794 // --- Rebuild ghost mappings (local-only optimization) ---
795 // Permute the ghost index list to match new cell globals
796 {
797 std::vector<index> ghostCellGlobalsNew;
798 if (cell2node.trans.pLGhostMapping)
799 {
800 ghostCellGlobalsNew = cell2node.trans.pLGhostMapping->ghostIndex;
802 g = cellLookup.resolve(g);
803 }
804 // Reattach son and create ghost mapping
805 if (!cell2node.son)
806 cell2node.son = make_ssp<tAdjPair::t_arr>(ObjName{"reorder.son"}, mpi);
807 cell2node.TransAttach();
808 cell2node.trans.createFatherGlobalMapping();
809 cell2node.trans.createGhostMapping(ghostCellGlobalsNew);
810 cell2node.trans.createMPITypes();
811 cell2node.trans.pullOnce();
812 }
813 // Borrow ghost indexing for other cell arrays
814 {
815 auto borrowAndPull = [&](auto &pair)
816 {
817 if (!pair.father)
818 return;
819 if (!pair.son)
820 {
821 using TArr = typename std::remove_reference_t<decltype(pair)>::t_arr;
822 pair.son = make_ssp<TArr>(ObjName{"reorder.son"}, mpi);
823 }
824 pair.TransAttach();
825 pair.trans.BorrowGGIndexing(cell2node.trans);
826 pair.trans.createMPITypes();
827 pair.trans.pullOnce();
828 };
829 borrowAndPull(cell2cell);
830 borrowAndPull(cell2cellOrig);
832 if (cell2face.father)
833 borrowAndPull(cell2face);
834 if (cell2cellFace.father)
835 borrowAndPull(cell2cellFace);
836 if (isPeriodic && cell2nodePbi.father)
838 }
839
840 // --- Re-wire target mappings ---
841 {
842 auto cellGhostMap = cell2node.trans.pLGhostMapping;
843 cell2cell.idx.wireTargetMapping(cellGhostMap);
844 bnd2cell.idx.wireTargetMapping(cellGhostMap);
845 if (cell2cellFace.father && cell2cellFace.idx.isWired())
846 cell2cellFace.idx.wireTargetMapping(cellGhostMap);
847 if (node2cell.father && node2cell.idx.isWired())
848 node2cell.idx.wireTargetMapping(cellGhostMap);
849 if (face2cell.father && face2cell.idx.isWired())
850 face2cell.idx.wireTargetMapping(cellGhostMap);
851 }
852
853 // --- Pull ghost data for non-cell adjacencies (face2cell, node2cell) ---
854 if (face2cell.father && face2cell.trans.pLGhostMapping)
855 face2cell.trans.pullOnce();
856 if (node2cell.father && node2cell.trans.pLGhostMapping)
857 node2cell.trans.pullOnce();
858 DNDS_check_throw_info(bnd2cell.father && bnd2cell.trans.pLGhostMapping,
859 "ReorderLocalCells: bnd2cell must have ghost mapping for pull");
860 bnd2cell.trans.pullOnce();
861
862 // --- Convert back to local ---
863 if (this->adjFacialState == Adj_PointToGlobal && face2cell.isBuilt())
864 this->AdjGlobal2LocalFacial();
865 if (this->adjC2FState == Adj_PointToGlobal && cell2face.isBuilt())
866 this->AdjGlobal2LocalC2F();
867 if (this->adjC2CFaceState == Adj_PointToGlobal && cell2cellFace.isBuilt())
868 this->AdjGlobal2LocalC2CFace();
869 if (this->adjN2CBState == Adj_PointToGlobal && node2cell.isBuilt())
870 this->AdjGlobal2LocalN2CB();
871 this->AdjGlobal2LocalPrimary();
872
873 if (mpi.rank == mRank)
874 log() << fmt::format("UnstructuredMesh === ReorderLocalCells finished") << std::endl;
875 }
876
877} // namespace DNDS::Geom
#define DNDS_assert_info(expr, info)
Debug-only assertion with an extra std::string info message.
Definition Errors.hpp:117
#define DNDS_assert(expr)
Debug-only assertion (compiled out when DNDS_NDEBUG is defined). Prints the expression + file/line + ...
Definition Errors.hpp:112
#define DNDS_check_throw_info(expr, info)
Same as DNDS_check_throw but attaches a user-supplied info message to the thrown std::runtime_error.
Definition Errors.hpp:100
Helper for computing cell reordering permutations via Metis.
Eigen::Matrix< real, 3, 3 > m
Distributed entity reordering framework: ReorderRegistry, ReorderPlan, ReorderInput.
constexpr AdjKind Bnd2Node
constexpr AdjKind Cell2Node
constexpr AdjKind Node2Cell
constexpr AdjKind Face2Cell
constexpr AdjKind Cell2Face
constexpr AdjKind Cell2CellFace
constexpr AdjKind Node2Bnd
constexpr AdjKind Bnd2Face
constexpr AdjKind Cell2Cell
constexpr AdjKind Face2Node
constexpr AdjKind Face2Bnd
constexpr AdjKind Bnd2Cell
AdjAction classifyAdj(AdjKind adj, const std::unordered_set< EntityKind > &reorderedKinds)
Classify an adjacency given the set of reordered entity kinds.
std::function< void(const PermutationTransfer::LookupResult &lookup)> AdjRemapFn
Callback invoked during REMAP phase for an adjacency array.
@ RELOCATE
Source reordered, target not: move rows.
@ REMAP
Target reordered, source not: update entries.
@ RELOCATE_REMAP
Both reordered: update entries then move rows.
@ SELF
Intra-level (A==A): update entries then move rows.
std::function< void(const PermutationTransfer &transfer, const MPIInfo &mpi)> AdjRelocateFn
Callback invoked during RELOCATE phase for an adjacency or companion array.
const char * entityKindName(EntityKind kind)
String name for an EntityKind (for diagnostics).
DNDS::ArrayAdjacencyPair< DNDS::NonUniformSize > tAdjPair
std::string adjKindName(const AdjKind &kind)
Format an AdjKind as a diagnostic string, e.g. "Cell2Node", "Cell2Cell(Node)".
void AllreduceOneIndex(index &v, MPI_Op op, const MPIInfo &mpi)
Single-scalar Allreduce helper for indices (in-place, count = 1).
Definition MPI.hpp:727
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
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
Definition Defines.hpp:112
std::ostream & log()
Return the current DNDSR log stream (either std::cout or the installed file).
Definition Defines.cpp:50
int MPI_int
MPI counterpart type for MPI_int (= C int). Used for counts and ranks in MPI calls.
Definition MPI.hpp:54
ssp< TArray > father
Owned-side array (must be resized before ghost setup).
Per-adjacency index state tracking.
Input to the reorder framework.
void apply(ReorderRegistry &registry, const MPIInfo &mpi) const
std::unordered_map< EntityKind, PermutationTransfer > transfers
Per reordered entity kind: the computed transfer.
std::unordered_map< EntityKind, PermutationTransfer::LookupResult > lookups
Per reordered entity kind: the old->new global lookup.
static ReorderPlan build(const ReorderInput &input, const ReorderRegistry &registry, const MPIInfo &mpi)
std::unordered_set< EntityKind > reorderedKinds
Set of entity kinds being reordered.
ssp< GlobalOffsetsMapping > getGlobalMapping(EntityKind kind) const
Get a registered global mapping (nullptr if not registered).
std::unordered_map< EntityKind, std::vector< index > > pullSets
std::vector< AdjEntry > adjs
All adjacency entries (mesh members + external).
std::vector< CompanionEntry > companions
All companion entries (mesh members + external).
tCoordPair nodeWallDist
wall dist:
Definition Mesh.hpp:177
UnstructuredMeshDeviceView< B > t_deviceView
Definition Mesh.hpp:1036
tPbiPair cell2nodePbi
periodic only, after reader
Definition Mesh.hpp:68
AdjPairTracked< tAdjPair > cell2cellFace
constructed on demand
Definition Mesh.hpp:127
tCoordPair coordsElevDisp
only elevation
Definition Mesh.hpp:156
tPbiPair face2nodePbi
periodic only, after interpolated
Definition Mesh.hpp:107
AdjPairTracked< tAdj1Pair > face2bnd
Definition Mesh.hpp:102
AdjPairTracked< tAdj2Pair > face2cell
Definition Mesh.hpp:99
ReorderInput resolveFollows(const ReorderInput &input, const ReorderRegistry &reg)
Augment a ReorderInput with default follows and compute follow maps.
tCoordPair coords
reader
Definition Mesh.hpp:57
AdjPairTracked< tAdjPair > cell2face
interpolated
Definition Mesh.hpp:97
ReorderRegistry buildReorderRegistry(const std::unordered_set< EntityKind > &destroyKinds={})
Build a ReorderRegistry containing all mesh members.
AdjPairTracked< tAdjPair > cell2cell
Definition Mesh.hpp:61
AdjPairTracked< tAdjPair > face2node
Definition Mesh.hpp:98
AdjPairTracked< tAdj1Pair > bnd2face
Definition Mesh.hpp:101
tElemInfoArrayPair faceElemInfo
Definition Mesh.hpp:100
AdjPairTracked< tAdjPair > cell2node
Definition Mesh.hpp:58
tElemInfoArrayPair cellElemInfo
Definition Mesh.hpp:62
AdjPairTracked< tAdjPair > node2bnd
Definition Mesh.hpp:87
AdjPairTracked< tAdjPair > bnd2node
Definition Mesh.hpp:59
AdjPairTracked< tAdjPair > node2cell
inverse relations
Definition Mesh.hpp:86
tElemInfoArrayPair bndElemInfo
Definition Mesh.hpp:63
AdjPairTracked< tAdj2Pair > bnd2cell
Definition Mesh.hpp:60
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
Definition MPI.hpp:231
int rank
This rank's 0-based index within comm (-1 until initialised).
Definition MPI.hpp:235
Tag type for naming objects created via make_ssp.
Definition Defines.hpp:254
Result of buildLookup: ghost-pullable old-global -> new-global map.
static PermutationTransfer fromPartition(const std::vector< MPI_int > &partition, const ssp< GlobalOffsetsMapping > &oldGlobalMapping, const MPIInfo &mpi)
static PermutationTransfer fromLocalPermutation(const std::vector< index > &old2new, const ssp< GlobalOffsetsMapping > &oldGlobalMapping, const MPIInfo &mpi)
Eigen::Matrix< real, 5, 1 > v
auto adj
tElemInfoArrayPair cellElemInfo
auto reg
auto plan
std::vector< MPI_int > cellPartition(nCellBefore)
mesh AdjLocal2GlobalPrimary()
input explicitMaps push_back(EntityReorderMap{EntityKind::Cell, cellPartition})
const auto & transfer
ReorderInput input