24 std::vector<AdjKind> cellHops(nLayers, Cell2Cell);
27 std::vector<AdjKind> nodeHops(nLayers, Cell2Cell);
28 nodeHops.push_back(Cell2Node);
43 static void insertChainIntoTrie(
44 std::vector<GhostTreeNode> &roots,
48 if (chain.
hops.empty())
49 throw std::runtime_error(
"GhostChain: empty hop list");
51 throw std::runtime_error(fmt::format(
52 "GhostChain: anchor {} != first hop from {}",
56 throw std::runtime_error(fmt::format(
57 "GhostChain: target {} != last hop to {}",
60 for (
size_t i = 0;
i + 1 < chain.
hops.size();
i++)
62 if (chain.
hops[
i].to != chain.
hops[
i + 1].from)
63 throw std::runtime_error(fmt::format(
64 "GhostChain: hop[{}].to ({}) != hop[{}].from ({})",
70 GhostTreeNode *current =
nullptr;
71 for (
auto &root : roots)
73 if (root.kind == chain.
anchor)
81 roots.push_back(GhostTreeNode{chain.
anchor, {},
false, 0, {}});
82 current = &roots.back();
86 for (
size_t hi = 0; hi < chain.
hops.size(); hi++)
88 const AdjKind &hop = chain.
hops[hi];
89 int childLevel = current->level + 1;
93 GhostTreeNode *child =
nullptr;
94 for (
auto &c : current->children)
96 if (c.hop == hop && c.kind == childKind)
104 current->children.push_back(
105 GhostTreeNode{childKind, hop,
false, childLevel, {}});
106 child = ¤t->children.back();
113 if (hi + 1 == chain.
hops.size() || childKind == chain.
target)
114 child->collect =
true;
123 for (
const auto &chain : spec.
chains)
124 insertChainIntoTrie(tree.
roots, chain);
137 std::queue<QEntry> q;
138 for (
auto &root : tree.
roots)
146 auto [node, pid] = q.front();
150 node->parentId = pid;
155 for (
auto &child : node->children)
156 q.push({&child, node->id});
159 tree.totalNodes = nextId;
162 tree.levels.resize(tree.maxLevel + 1);
174 for (
const auto &child :
n.children)
177 for (
const auto &root : tree.roots)
187 static void collectRequiredAdjsHelper(
188 const GhostTreeNode &node,
189 std::unordered_set<AdjKind, AdjKindHash> &out)
191 for (
const auto &child : node.children)
193 out.insert(child.hop);
194 collectRequiredAdjsHelper(child, out);
198 std::unordered_set<AdjKind, AdjKindHash> CompiledGhostTree::requiredAdjs()
const
200 std::unordered_set<AdjKind, AdjKindHash>
result;
201 for (
const auto &root : roots)
202 collectRequiredAdjsHelper(root,
result);
206 std::vector<AdjKind> CompiledGhostTree::checkAvailable(
209 auto required = requiredAdjs();
210 std::vector<AdjKind> missing;
211 for (
const auto &
adj : required)
214 missing.push_back(
adj);
219 static void collectCollectedKindsHelper(
221 std::unordered_set<EntityKind> &out)
224 out.insert(node.
kind);
225 for (
const auto &child : node.children)
226 collectCollectedKindsHelper(child, out);
229 std::unordered_set<EntityKind> CompiledGhostTree::collectedKinds()
const
231 std::unordered_set<EntityKind>
result;
232 for (
const auto &root : roots)
233 collectCollectedKindsHelper(root,
result);
241 static void dumpNode(
243 std::ostringstream &oss,
246 for (
int i = 0;
i < indent;
i++)
251 <<
" (level=" << node.
level;
255 for (
const auto &child : node.children)
256 dumpNode(child, oss, indent + 1);
259 std::string CompiledGhostTree::dump()
const
261 std::ostringstream oss;
262 for (
const auto &root : roots)
263 dumpNode(root, oss, 0);
278 static void sortedMergeInto(std::vector<index> &dst,
const std::vector<index> &src)
287 std::vector<index> merged;
288 merged.reserve(dst.size() + src.size());
289 std::set_union(dst.begin(), dst.end(), src.begin(), src.end(),
290 std::back_inserter(merged));
291 dst = std::move(merged);
297 template <
class TAdjPair>
298 static std::vector<index> traverseHopImpl(
299 const std::vector<index> &parentSet,
303 if (parentSet.empty())
306 auto *gm =
adj.father->pLGlobalMapping.get();
309 index fatherSize =
adj.father->Size();
311 index totalSize = fatherSize + sonSize;
312 index myOffset = gm->operator()(
adj.father->getMPI().rank, 0);
313 auto *ghostMapping =
adj.trans.pLGhostMapping.get();
316 std::unordered_set<index> seen;
317 seen.reserve(parentSet.size() * 2);
319 for (
auto parentGlobal : parentSet)
323 if (parentGlobal >= myOffset && parentGlobal < myOffset + fatherSize)
325 localIdx = parentGlobal - myOffset;
327 else if (ghostMapping)
329 auto [found, rank, ghostIdx] = ghostMapping->search_indexAppend(parentGlobal);
330 if (found && ghostIdx >= 0 && ghostIdx < totalSize)
337 fmt::format(
"traverseHop: global index {} not found locally "
338 "(father=[{}, {}), son={}). Scratch pull incomplete?",
339 parentGlobal, myOffset, myOffset + fatherSize, sonSize));
343 for (
auto entry :
adj[localIdx])
348 std::vector<index>
result(seen.begin(), seen.end());
354 static std::vector<index> traverseHop(
355 const std::vector<index> &parentSet,
356 const AdjVariant &adjVar,
359 return std::visit([&](
const auto &
adj) -> std::vector<index>
360 {
return traverseHopImpl(parentSet,
adj, assertFound); },
365 static std::vector<index> filterNonOwned(
366 const std::vector<index> &
set,
369 std::vector<index> ghosts;
372 if (gi < ownedStart || gi >= ownedEnd)
373 ghosts.push_back(gi);
384 if (!missing.empty())
387 for (
auto &
m : missing)
390 fmt::format(
"evaluateGhostTree: missing adjacencies:{}", names));
394 std::vector<std::vector<index>> nodeSets(tree.
totalNodes);
400 std::unordered_map<AdjKind, ssp<AdjVariant>,
AdjKindHash> scratchAdjs;
405 auto sit = scratchAdjs.find(kind);
406 if (sit != scratchAdjs.end())
408 return resolveAdj(kind);
412 for (
const auto &entry : tree.
levels[0])
414 auto gm = getGlobalMapping(entry.kind);
416 fmt::format(
"evaluateGhostTree: no GlobalOffsetsMapping for root kind {}",
418 index myOffset = gm->operator()(mpi.rank, 0);
419 index mySize = gm->RLengths()[mpi.rank];
420 auto &
set = nodeSets[entry.nodeId];
423 set[
i] = myOffset +
i;
433 std::unordered_map<EntityKind, std::vector<index>> intermediateGhosts;
436 std::unordered_map<AdjKind, size_t, AdjKindHash> lastScratchSize;
439 for (
int level = 0; level <= tree.
maxLevel; level++)
441 const auto &levelEntries = tree.
levels[level];
445 for (
const auto &entry : levelEntries)
447 auto gm = getGlobalMapping(entry.kind);
449 fmt::format(
"evaluateGhostTree: no GlobalOffsetsMapping "
452 index myOffset = gm->operator()(mpi.rank, 0);
453 index myEnd = myOffset + gm->RLengths()[mpi.rank];
455 auto ghosts = filterNonOwned(nodeSets[entry.nodeId], myOffset, myEnd);
456 sortedMergeInto(intermediateGhosts[entry.kind], ghosts);
459 sortedMergeInto(
result.ghostIndices[entry.kind], ghosts);
468 std::vector<AdjKind> hopsNeedingScratch;
469 for (
const auto &childEntry : tree.
levels[level + 1])
474 bool alreadyHandled =
false;
475 for (
auto &h : hopsNeedingScratch)
478 alreadyHandled =
true;
485 auto git = intermediateGhosts.find(parentKind);
486 if (git != intermediateGhosts.end())
487 curSize = git->second.size();
489 bool needsPull = (curSize > 0 &&
490 curSize > lastScratchSize[hop]);
492 int localNeedsIt = needsPull ? 1 : 0;
493 int globalNeedsIt = 0;
494 MPI_Allreduce(&localNeedsIt, &globalNeedsIt, 1, MPI_INT, MPI_MAX, mpi.comm);
498 hopsNeedingScratch.push_back(hop);
499 lastScratchSize[hop] = curSize;
503 for (
auto &hop : hopsNeedingScratch)
506 auto origAdj = resolveAdj(hop);
510 std::vector<index> ghostSet;
511 auto git = intermediateGhosts.find(parentKind);
512 if (git != intermediateGhosts.end())
513 ghostSet = git->second;
515 std::visit([&](
const auto &
adj)
517 using TPair = std::decay_t<
decltype(
adj)>;
518 using TArray =
typename TPair::t_arr;
520 auto tempSon = make_ssp<TArray>(
521 ObjName{
"scratch_son"},
adj.father->getMPI());
523 auto livePair = makeAdjVariant<TPair>();
524 auto &live = std::get<TPair>(*livePair);
525 live.father =
adj.father;
527 live.trans.setFatherSon(
adj.father, tempSon);
530 "scratch pull: father must have pLGlobalMapping");
531 live.trans.pLGlobalMapping =
adj.father->pLGlobalMapping;
533 live.trans.createGhostMapping(ghostSet);
534 live.trans.createMPITypes();
535 live.trans.pullOnce();
537 scratchAdjs[hop] = std::move(livePair); }, *origAdj);
542 const auto &nextLevelEntries = tree.
levels[level + 1];
543 for (
const auto &childEntry : nextLevelEntries)
545 auto &parentSet = nodeSets[childEntry.parentId];
547 auto adjVar = resolveAdjLive(childEntry.hop);
549 fmt::format(
"evaluateGhostTree: adjacency {} not resolved",
552 nodeSets[childEntry.nodeId] = traverseHop(parentSet, *adjVar,
false);
558 for (
auto it =
result.ghostIndices.begin(); it !=
result.ghostIndices.end();)
560 if (it->second.empty())
561 it =
result.ghostIndices.erase(it);
568 for (
auto &[kind, indices] :
result.ghostIndices)
569 localMask |= (1 <<
static_cast<int>(kind));
572 MPI_Allreduce(&localMask, &globalMask, 1, MPI_INT, MPI_BOR, mpi.comm);
574 for (
int i = 0; i < static_cast<int>(EntityKind::NUM_KINDS);
i++)
576 if (globalMask & (1 <<
i))
#define DNDS_assert_info(expr, info)
Debug-only assertion with an extra std::string info message.
#define DNDS_assert(expr)
Debug-only assertion (compiled out when DNDS_NDEBUG is defined). Prints the expression + file/line + ...
Layered DAG of mesh adjacency relations with composable DSL operations.
Eigen::Matrix< real, 3, 3 > m
const char * entityKindName(EntityKind kind)
String name for an EntityKind (for diagnostics).
std::string adjKindName(const AdjKind &kind)
Format an AdjKind as a diagnostic string, e.g. "Cell2Node", "Cell2Cell(Node)".
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
std::shared_ptr< T > ssp
Shortened alias for std::shared_ptr used pervasively in DNDSR.
set(LIBNAME cfv) set(LINKS) set(LINKS_SHARED geom_shared dnds_shared $
Hash for AdjKind (for use in unordered containers).
int maxLevel
Maximum BFS depth across all nodes.
int totalNodes
Total number of nodes (for flat array sizing).
std::vector< AdjKind > checkAvailable(const MeshConnectivity &dag) const
static CompiledGhostTree compile(const GhostSpec &spec)
std::vector< std::vector< LevelEntry > > levels
std::vector< GhostTreeNode > roots
EntityKind target
Entity kind to ghost (must == hops.back().to).
EntityKind anchor
Owned entities to start from.
std::vector< AdjKind > hops
Sequence of adjacency lookups.
static GhostSpec defaultPrimary()
std::vector< GhostChain > chains
One node in the compiled ghost tree.
int level
BFS depth (root = 0).
int parentId
Parent node ID (-1 for roots).
EntityKind kind
Entity kind at this node.
bool collect
If true, non-owned entities here become ghosts.
int parentId
ID of the parent node (-1 for roots).
bool hasAdj(AdjKind kind) const
Check whether an AdjKind is registered.
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
Tag type for naming objects created via make_ssp.
Eigen::Vector3d n(1.0, 0.0, 0.0)