DNDSR 0.2.1
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
Mesh.cpp
Go to the documentation of this file.
1#include "Mesh.hpp"
6#include "Solver/Direct.hpp"
7
8#include <cstdlib>
9#include <string>
10#include <map>
11#include <set>
12#include <unordered_map>
13#include <unordered_set>
14#include <filesystem>
15#include <fmt/core.h>
16#include "DNDS/EigenPCH.hpp"
19
21
22namespace DNDS
23{
24 // DNDS_DEVICE_STORAGE_BASE_DELETER_INST(Geom::ElemInfo, )
25 // DNDS_DEVICE_STORAGE_INST(Geom::ElemInfo, DeviceBackend::Host, )
26}
27
28namespace DNDS::Geom
29{
30
31 /**
32 * \brief Reserved skeleton for parallel topology interpolation.
33 *
34 * This commented-out method is preserved as a placeholder for a future generic
35 * topology management API. Currently, mesh topology (cell2face, face2cell, etc.)
36 * is constructed serially and distributed. However, a parallel interpolation
37 * capability may be needed for:
38 * - Dynamic mesh adaptation (refinement/coarsening)
39 * - Load balancing with topology migration
40 * - Multi-physics coupling with different mesh partitions
41 *
42 * If implementing parallel topology construction, this skeleton provides the
43 * basic structure for building face-based adjacencies from cell2node without
44 * requiring a serial global mesh on rank 0.
45 *
46 * \todo Evaluate need for parallel topology API in Phase 5 refactoring.
47 */
48
49 /*******************************************************************************************************************/
50 /*******************************************************************************************************************/
51 /*******************************************************************************************************************/
52
53 // Partition2LocalIdx, Partition2Serial2Global, ConvertAdjSerial2Global,
54 // TransferDataSerial2Global are now in Mesh_PartitionHelpers.hpp
55 // so they can be shared with Mesh_ReadSerializeDistributed.cpp.
56
57 //! inefficient, use Partition2Serial2Global ! only used for convenient comparison
58 void PushInfo2Serial2Global(std::vector<DNDS::index> &serial2Global,
59 DNDS::index localSize,
60 const std::vector<DNDS::index> &pushIndex,
61 const std::vector<DNDS::index> &pushIndexStart,
62 const DNDS::MPIInfo &mpi)
63 {
64 tIndPair Serial2Global;
65 Serial2Global.InitPair("Serial2Global", mpi);
66 Serial2Global.father->Resize(localSize);
67 Serial2Global.TransAttach();
68 Serial2Global.trans.createFatherGlobalMapping();
69 Serial2Global.trans.createGhostMapping(pushIndex, pushIndexStart);
70 Serial2Global.trans.createMPITypes();
71 Serial2Global.son->createGlobalMapping();
72 // Set son to son's global
73 for (DNDS::index iSon = 0; iSon < Serial2Global.son->Size(); iSon++)
74 (*Serial2Global.son)[iSon] = Serial2Global.son->pLGlobalMapping->operator()(mpi.rank, iSon);
75 Serial2Global.trans.pushOnce();
76 serial2Global.resize(localSize);
77 for (DNDS::index iFat = 0; iFat < Serial2Global.father->Size(); iFat++)
78 serial2Global[iFat] = Serial2Global.father->operator[](iFat);
79 }
80
81 // template <class TAdj = tAdj1>
82 // void ConvertAdjSerial2Global(TAdj &arraySerialAdj,
83 // const std::vector<DNDS::index> &partitionJSerial2Global,
84 // const DNDS::MPIInfo &mpi)
85 // {
86 // }
87 /*******************************************************************************************************************/
88 /*******************************************************************************************************************/
89 /*******************************************************************************************************************/
90
93 {
94 if (mesh->getMPI().rank == mRank)
95 DNDS::log() << "UnstructuredMeshSerialRW === Doing PartitionReorderToMeshCell2Cell" << std::endl;
96 DNDS_assert(cnPart == mesh->getMPI().size);
97 // * 1: get the nodal partition
98 nodePartition.resize(coordSerial->Size(), static_cast<DNDS::MPI_int>(INT32_MAX));
99 for (DNDS::index iCell = 0; iCell < cell2nodeSerial->Size(); iCell++)
100 for (DNDS::rowsize ic2n = 0; ic2n < (*cell2nodeSerial).RowSize(iCell); ic2n++)
101 nodePartition[(*cell2nodeSerial)(iCell, ic2n)] = std::min(nodePartition[(*cell2nodeSerial)(iCell, ic2n)], cellPartition.at(iCell));
102 // * 1: get the bnd partition
103 bndPartition.resize(bnd2cellSerial->Size());
104 for (DNDS::index iBnd = 0; iBnd < bnd2cellSerial->Size(); iBnd++)
105 bndPartition[iBnd] = cellPartition[(*bnd2cellSerial)(iBnd, 0)];
106
107 std::vector<DNDS::index> cell_push, cell_pushStart, node_push, node_pushStart, bnd_push, bnd_pushStart;
108 Partition2LocalIdx(cellPartition, cell_push, cell_pushStart, mesh->getMPI());
109 Partition2LocalIdx(nodePartition, node_push, node_pushStart, mesh->getMPI());
110 Partition2LocalIdx(bndPartition, bnd_push, bnd_pushStart, mesh->getMPI());
111 std::vector<DNDS::index> cell_Serial2Global, node_Serial2Global, bnd_Serial2Global;
112 Partition2Serial2Global(cellPartition, cell_Serial2Global, mesh->getMPI(), mesh->getMPI().size);
113 Partition2Serial2Global(nodePartition, node_Serial2Global, mesh->getMPI(), mesh->getMPI().size);
114 // Partition2Serial2Global(bndPartition, bnd_Serial2Global, mesh->getMPI(), mesh->getMPI().size);//seems not needed for now
115 // PushInfo2Serial2Global(cell_Serial2Global, cellPartition.size(), cell_push, cell_pushStart, mesh->getMPI());//*safe validation version
116 // PushInfo2Serial2Global(node_Serial2Global, nodePartition.size(), node_push, node_pushStart, mesh->getMPI());//*safe validation version
117 // PushInfo2Serial2Global(bnd_Serial2Global, bndPartition.size(), bnd_push, bnd_pushStart, mesh->getMPI()); //*safe validation version
118 if (mesh->getMPI().rank == mRank)
119 DNDS::log() << "UnstructuredMeshSerialRW === Doing PartitionReorderToMeshCell2Cell ConvertAdjSerial2Global" << std::endl;
120 ConvertAdjSerial2Global(cell2nodeSerial, node_Serial2Global, mesh->getMPI());
121 // !cell2cell discarded
122 // ConvertAdjSerial2Global(cell2cellSerial, cell_Serial2Global, mesh->getMPI());
123 ConvertAdjSerial2Global(bnd2nodeSerial, node_Serial2Global, mesh->getMPI());
124 ConvertAdjSerial2Global(bnd2cellSerial, cell_Serial2Global, mesh->getMPI());
125
126 mesh->coords.InitPair("coords", mesh->getMPI());
127 mesh->cellElemInfo.InitPair("cellElemInfo", ElemInfo::CommType(), ElemInfo::CommMult(), mesh->getMPI());
128 mesh->bndElemInfo.InitPair("bndElemInfo", ElemInfo::CommType(), ElemInfo::CommMult(), mesh->getMPI());
129 mesh->cell2node.InitPair("cell2node", mesh->getMPI());
130 mesh->cell2cellOrig.InitPair("cell2cellOrig", mesh->getMPI());
131 mesh->node2nodeOrig.InitPair("node2nodeOrig", mesh->getMPI());
132 mesh->bnd2bndOrig.InitPair("bnd2bndOrig", mesh->getMPI());
133 if (mesh->isPeriodic)
134 mesh->cell2nodePbi.InitPair("cell2nodePbi", mesh->getMPI());
135 // !cell2cell discarded
136 mesh->bnd2node.InitPair("bnd2node", mesh->getMPI());
137 mesh->bnd2cell.InitPair("bnd2cell", mesh->getMPI());
138 if (mesh->isPeriodic)
139 mesh->bnd2nodePbi.InitPair("bnd2nodePbi", mesh->getMPI());
140
141 // coord transferring
142 if (mesh->getMPI().rank == mRank)
143 DNDS::log() << "UnstructuredMeshSerialRW === Doing PartitionReorderToMeshCell2Cell Trasfer Data Coord" << std::endl;
144 TransferDataSerial2Global(coordSerial, mesh->coords.father, node_push, node_pushStart, mesh->getMPI());
145 TransferDataSerial2Global(node2nodeOrigSerial, mesh->node2nodeOrig.father, node_push, node_pushStart, mesh->getMPI());
146
147 if (mesh->getMPI().rank == mRank)
148 DNDS::log() << "UnstructuredMeshSerialRW === Doing PartitionReorderToMeshCell2Cell Trasfer Data Cell" << std::endl;
149 // cells transferring
150 // !cell2cell discarded
151 // TransferDataSerial2Global(cell2cellSerial, mesh->cell2cell.father, cell_push, cell_pushStart, mesh->getMPI());
152 TransferDataSerial2Global(cell2nodeSerial, mesh->cell2node.father, cell_push, cell_pushStart, mesh->getMPI());
153 TransferDataSerial2Global(cell2cellOrigSerial, mesh->cell2cellOrig.father, cell_push, cell_pushStart, mesh->getMPI());
154 if (mesh->isPeriodic)
155 TransferDataSerial2Global(cell2nodePbiSerial, mesh->cell2nodePbi.father, cell_push, cell_pushStart, mesh->getMPI());
156 TransferDataSerial2Global(cellElemInfoSerial, mesh->cellElemInfo.father, cell_push, cell_pushStart, mesh->getMPI());
157 if (mesh->getMPI().rank == mRank)
158 DNDS::log() << "UnstructuredMeshSerialRW === Doing PartitionReorderToMeshCell2Cell Trasfer Data Bnd" << std::endl;
159 // bnds transferring
160 TransferDataSerial2Global(bnd2cellSerial, mesh->bnd2cell.father, bnd_push, bnd_pushStart, mesh->getMPI());
161 TransferDataSerial2Global(bnd2nodeSerial, mesh->bnd2node.father, bnd_push, bnd_pushStart, mesh->getMPI());
162 TransferDataSerial2Global(bndElemInfoSerial, mesh->bndElemInfo.father, bnd_push, bnd_pushStart, mesh->getMPI());
163 TransferDataSerial2Global(bnd2bndOrigSerial, mesh->bnd2bndOrig.father, bnd_push, bnd_pushStart, mesh->getMPI());
164 if (mesh->isPeriodic)
165 TransferDataSerial2Global(bnd2nodePbiSerial, mesh->bnd2nodePbi.father, bnd_push, bnd_pushStart, mesh->getMPI());
166
167 {
168 DNDS::MPISerialDo(mesh->getMPI(), [&]()
169 { log() << "[" << mesh->getMPI().rank << ": nCell " << mesh->cell2node.father->Size() << "] " << (((mesh->getMPI().rank + 1) % 10) ? "" : "\n") << std::flush; });
170 MPI::Barrier(mesh->getMPI().comm);
171 if (mesh->getMPI().rank == 0)
172 log() << std::endl;
173 DNDS::MPISerialDo(mesh->getMPI(), [&]()
174 { log() << "[" << mesh->getMPI().rank << ": nNode " << mesh->coords.father->Size() << "] " << (((mesh->getMPI().rank + 1) % 10) ? "" : "\n") << std::flush; });
175 MPI::Barrier(mesh->getMPI().comm);
176 if (mesh->getMPI().rank == 0)
177 log() << std::endl;
178 DNDS::MPISerialDo(mesh->getMPI(), [&]()
179 { log() << "[" << mesh->getMPI().rank << ": nBnd " << mesh->bnd2node.father->Size() << "] " << (((mesh->getMPI().rank + 1) % 10) ? "" : "\n") << std::flush; });
180 MPI::Barrier(mesh->getMPI().comm);
181 if (mesh->getMPI().rank == 0)
182 log() << std::endl;
183 }
184 mesh->adjPrimaryState = Adj_PointToGlobal;
185 mesh->cell2node.idx.markGlobal();
186 mesh->bnd2node.idx.markGlobal();
187 mesh->bnd2cell.idx.markGlobal();
188 if (mesh->getMPI().rank == mRank)
189 DNDS::log() << "UnstructuredMeshSerialRW === Done PartitionReorderToMeshCell2Cell" << std::endl;
190 }
191
194 {
195 DNDS_assert(mesh->adjPrimaryState == Adj_PointToGlobal);
196 DNDS_assert(mesh->cell2node.isGlobal());
198 dataIsSerialIn = false;
199 dataIsSerialOut = true;
200
201 std::vector<DNDS::index> serialPullCell;
202 std::vector<DNDS::index> serialPullNode;
203 // std::vector<DNDS::index> serialPullBnd;
204
205 DNDS::index numCellGlobal = mesh->cellElemInfo.father->globalSize();
206 // DNDS::index numBndGlobal = mesh->bndElemInfo.father->globalSize();
207 DNDS::index numNodeGlobal = mesh->coords.father->globalSize();
208
209 if (mesh->getMPI().rank == mRank)
210 {
211 serialPullCell.resize(numCellGlobal);
212 serialPullNode.resize(numNodeGlobal);
213 // serialPullBnd.reserve(numBndGlobal);
214 for (DNDS::index i = 0; i < numCellGlobal; i++)
215 serialPullCell[i] = i;
216 for (DNDS::index i = 0; i < numNodeGlobal; i++)
217 serialPullNode[i] = i;
218 // for (DNDS::index i = 0; i < numBndGlobal; i++)
219 // serialPullBnd[i] = i;
220 }
221 cell2nodeSerial = make_ssp<decltype(cell2nodeSerial)::element_type>(ObjName{"cell2nodeSerial"}, mesh->getMPI());
222 if (mesh->isPeriodic)
223 cell2nodePbiSerial = make_ssp<decltype(cell2nodePbiSerial)::element_type>(ObjName{"cell2nodePbiSerial"}, NodePeriodicBits::CommType(), NodePeriodicBits::CommMult(), mesh->getMPI());
224 coordSerial = make_ssp<decltype(coordSerial)::element_type>(ObjName{"coordSerial"}, mesh->getMPI());
225 cellElemInfoSerial = make_ssp<decltype(cellElemInfoSerial)::element_type>(ObjName{"cellElemInfoSerial"}, ElemInfo::CommType(), ElemInfo::CommMult(), mesh->getMPI());
226
227 coordSerialOutTrans.setFatherSon(mesh->coords.father, coordSerial);
228 cell2nodeSerialOutTrans.setFatherSon(mesh->cell2node.father, cell2nodeSerial);
229 if (mesh->isPeriodic)
230 cell2nodePbiSerialOutTrans.setFatherSon(mesh->cell2nodePbi.father, cell2nodePbiSerial);
231 // bnd2nodeSerialOutTrans.setFatherSon(mesh->bnd2node.father, bnd2nodeSerial);
232 cellElemInfoSerialOutTrans.setFatherSon(mesh->cellElemInfo.father, cellElemInfoSerial);
233 // bndElemInfoSerialOutTrans.setFatherSon(mesh->bndElemInfo.father, bndElemInfoSerial);
234
235 // Reuse existing father global mappings to avoid side-effects on
236 // the mesh's own pLGlobalMapping pointers. createFatherGlobalMapping()
237 // would call father->createGlobalMapping() which replaces the shared_ptr
238 // on the father array, affecting the mesh's own transformer.
239 auto reuseOrCreateFatherGlobalMapping = [](auto &trans)
240 {
241 if (trans.father->pLGlobalMapping)
242 trans.pLGlobalMapping = trans.father->pLGlobalMapping;
243 else
244 trans.createFatherGlobalMapping();
245 };
246 reuseOrCreateFatherGlobalMapping(coordSerialOutTrans);
247 reuseOrCreateFatherGlobalMapping(cell2nodeSerialOutTrans);
248 if (mesh->isPeriodic)
249 reuseOrCreateFatherGlobalMapping(cell2nodePbiSerialOutTrans);
250 // reuseOrCreateFatherGlobalMapping(bnd2nodeSerialOutTrans);
251 reuseOrCreateFatherGlobalMapping(cellElemInfoSerialOutTrans);
252 // reuseOrCreateFatherGlobalMapping(bndElemInfoSerialOutTrans);
253
254 coordSerialOutTrans.createGhostMapping(serialPullNode);
255 cell2nodeSerialOutTrans.createGhostMapping(serialPullCell);
256 if (mesh->isPeriodic)
257 cell2nodePbiSerialOutTrans.createGhostMapping(serialPullCell);
258 // bnd2nodeSerialOutTrans.createGhostMapping(serialPullBnd);
259 // cellElemInfo shares the same cell indexing as cell2node: copy
260 // ghost + global mappings without overwriting the father's pointer.
261 cellElemInfoSerialOutTrans.pLGhostMapping = cell2nodeSerialOutTrans.pLGhostMapping;
262 cellElemInfoSerialOutTrans.pLGlobalMapping = cell2nodeSerialOutTrans.pLGlobalMapping;
263 // bndElemInfoSerialOutTrans.BorrowGGIndexing(bnd2nodeSerialOutTrans);
264
265 coordSerialOutTrans.createMPITypes();
266 cell2nodeSerialOutTrans.createMPITypes();
267 if (mesh->isPeriodic)
268 cell2nodePbiSerialOutTrans.createMPITypes();
269 // bnd2nodeSerialOutTrans.createMPITypes();
270 cellElemInfoSerialOutTrans.createMPITypes();
271 // bndElemInfoSerialOutTrans.createMPITypes();
272
273 coordSerialOutTrans.pullOnce();
274 cell2nodeSerialOutTrans.pullOnce();
275 if (mesh->isPeriodic)
277 // bnd2nodeSerialOutTrans.pullOnce();
279 // bndElemInfoSerialOutTrans.pullOnce();
280 if (mesh->getMPI().rank == mRank)
281 {
282 DNDS::log() << "UnstructuredMeshSerialRW === BuildSerialOut Done " << std::endl;
283 }
284 }
285
286}
287
288namespace DNDS::Geom
289{
290
292 {
293 using namespace Elem;
294 int hasBad = 0;
295 for (index iCell = 0; iCell < cellElemInfo.Size(); iCell++)
296 {
297 auto eType = cellElemInfo(iCell, 0).getElemType();
298 if (eType == ElemType::Line2 ||
299 eType == ElemType::Tri3 ||
300 eType == ElemType::Quad4 ||
301 eType == ElemType::Tet4 ||
302 eType == ElemType::Hex8 ||
303 eType == ElemType::Prism6 ||
304 eType == ElemType::Pyramid5)
305 {
306 continue;
307 }
308 else
309 {
310 hasBad = 1;
311 break;
312 }
313 }
314 int hasBadAll = 0;
316 return hasBad == 0;
317 }
318
320 {
321 using namespace Elem;
322 int hasBad = 0;
323 for (index iCell = 0; iCell < cellElemInfo.Size(); iCell++)
324 {
325 auto eType = cellElemInfo(iCell, 0).getElemType();
326 if (eType == ElemType::Line3 ||
327 eType == ElemType::Tri6 ||
328 eType == ElemType::Quad9 ||
329 eType == ElemType::Tet10 ||
330 eType == ElemType::Hex27 ||
331 eType == ElemType::Prism18 ||
332 eType == ElemType::Pyramid14)
333 {
334 continue;
335 }
336 else
337 {
338 hasBad = 1;
339 break;
340 }
341 }
342 int hasBadAll = 0;
344 return hasBad == 0;
345 }
346
348 const tPoint &translation1,
349 const tPoint &rotationCenter1,
350 const tPoint &eulerAngles1,
351 const tPoint &translation2,
352 const tPoint &rotationCenter2,
353 const tPoint &eulerAngles2,
354 const tPoint &translation3,
355 const tPoint &rotationCenter3,
356 const tPoint &eulerAngles3)
357 {
358 periodicInfo.translation[1].map() = translation1;
359 periodicInfo.translation[2].map() = translation2;
360 periodicInfo.translation[3].map() = translation3;
364 periodicInfo.rotation[1].map() =
368 periodicInfo.rotation[2].map() =
372 periodicInfo.rotation[3].map() =
376 }
377
380 {
382 DNDS_assert(cell2node.isGlobal() && bnd2node.isGlobal());
384 DNDS_assert(cell2node.father);
385 DNDS_assert(bnd2node.father);
386
387 if (!coords.father->pLGlobalMapping)
388 coords.father->createGlobalMapping();
389 if (!cell2node.father->pLGlobalMapping)
390 cell2node.father->createGlobalMapping();
391
392 // Ensure ghost mappings exist so IndexLocal2Global / IndexGlobal2Local work
393 // (father-only at this stage — no ghost cells/nodes yet).
395 coords.trans.createFatherGlobalMapping();
397 cell2node.TransAttach();
398 cell2node.trans.createFatherGlobalMapping();
400
401 // node2cell via DSL Inverse (cell2node must be in global state)
402 // Result is already AdjPairTracked with father adopted, state = Global.
405 coords.father->Size(), mpi);
406
407 // node2bnd via DSL Inverse (bnd2node must be in global state)
408 if (!bnd2node.father->pLGlobalMapping)
409 bnd2node.father->createGlobalMapping();
410 bnd2node.TransAttach();
411 bnd2node.trans.createFatherGlobalMapping();
413
416 coords.father->Size(), mpi);
417
419 // markGlobal already done by CheckedInverse
420 }
421
423 {
425 DNDS_assert(cell2node.isGlobal() && bnd2node.isGlobal());
427 DNDS_assert(node2cell.isGlobal() && node2bnd.isGlobal());
429 DNDS_assert(cell2node.father);
430 DNDS_assert(bnd2node.father);
431 DNDS_assert(node2cell.father);
432
434 coords.trans.createFatherGlobalMapping();
436 cell2node.TransAttach();
437 cell2node.trans.createFatherGlobalMapping();
439 bnd2node.TransAttach();
440 bnd2node.trans.createFatherGlobalMapping();
442
443 // Ghost-pull node2cell for off-rank nodes referenced by local cells and bnds.
444 // Use evaluateGhostTree: Cell → Cell2Node → Node ∪ Bnd → Bnd2Node → Node.
445 {
448
452 }};
453 auto n2cbResult = dagN2CB.evaluateGhostTree(
455
456 auto itN = n2cbResult.ghostIndices.find(EntityKind::Node);
457 std::vector<index> ghostNodes;
458 if (itN != n2cbResult.ghostIndices.end())
459 ghostNodes = std::move(itN->second);
460
461 node2cell.son = make_ssp<decltype(node2cell.son)::element_type>(ObjName{"node2cell.son"}, mpi);
462 node2cell.TransAttach();
463 node2cell.trans.createFatherGlobalMapping();
464 node2cell.trans.createGhostMapping(ghostNodes);
465 node2cell.trans.createMPITypes();
466 node2cell.trans.pullOnce();
467 }
468
469 // Build global→local-appended map for node2cell
470 std::unordered_map<index, index> nodeG2L;
471 for (index i = 0; i < node2cell.Size(); i++)
472 nodeG2L[node2cell.trans.pLGhostMapping->operator()(-1, i)] = i;
473
474 // cell2cell via DSL ComposeFiltered (inputs must be in global state)
475 // Result is AdjPairTracked with father adopted, state = Global.
478 nodeG2L,
479 SharedCountPredicate{.minShared = 1, .removeSelf = true});
480
481 // bnd2cell via ComposeFiltered with per-bnd node-count predicate.
482 // For periodic meshes, additionally uses pbi containment matchExtra.
483 bnd2cell.InitPair("bnd2cell", mpi);
484 bnd2cell.father->Resize(bnd2node.father->Size());
485
486 // For periodic: ghost-pull cell2node and cell2nodePbi for all cells
487 // reachable through node2cell, so the pbi matchExtra can access them.
488 std::unordered_map<index, index> cellG2LAppended; // cell global → local-appended in cell2node/cell2nodePbi
489 if (isPeriodic)
490 {
491 // Collect all unique cell globals from node2cell (father+son)
492 std::unordered_set<index> allCellGlobals;
493 for (index iNode = 0; iNode < node2cell.Size(); iNode++)
494 for (auto ic : node2cell[iNode])
495 allCellGlobals.insert(ic);
496 std::vector<index> neededCells;
497 neededCells.reserve(allCellGlobals.size());
498 for (auto ic : allCellGlobals)
499 neededCells.push_back(ic);
500
504 cell2nodePbi.trans.createFatherGlobalMapping();
505 cell2nodePbi.trans.createGhostMapping(neededCells);
506 cell2nodePbi.trans.createMPITypes();
507 cell2nodePbi.trans.pullOnce();
508 cell2node.son = make_ssp<decltype(cell2node.son)::element_type>(ObjName{"cell2node.son"}, mpi);
509 cell2node.BorrowAndPull(cell2nodePbi);
510
511 // Build global→local-appended map
512 for (index i = 0; i < cell2node.Size(); i++)
513 cellG2LAppended[cell2nodePbi.trans.pLGhostMapping->operator()(-1, i)] = i;
514 }
515
516 // Per-bnd predicate: keep cells sharing ALL bnd nodes (set intersection)
517 auto bndAllNodesPred = [this](index aBndGlobal, index cCellGlobal, int nShared) -> bool
518 {
519 // Map aBndGlobal to local bnd index to get row size
520 index aBndLocal = this->BndIndexGlobal2Local(aBndGlobal);
522 return nShared >= bnd2node.father->RowSize(aBndLocal);
523 };
524
525 // matchExtra for periodic: pbi containment check
526 std::function<bool(index, index, const std::vector<index> &)> bndMatchExtra = nullptr;
527 if (isPeriodic)
528 {
531 const std::vector<index> & /*sharedNodes*/) -> bool
532 {
533 auto itC = cellG2LAppended.find(cCellGlobal);
534 if (itC == cellG2LAppended.end())
535 return false;
536 index cLocal = itC->second;
537
538 // Every (node, pbi) in the bnd must appear in the cell
539 for (rowsize ib2n = 0; ib2n < bnd2node.father->RowSize(aBndLocal); ib2n++)
540 {
541 index iNode = bnd2node.father->operator()(aBndLocal, ib2n);
542 auto iNodePbi = bnd2nodePbi.father->operator()(aBndLocal, ib2n);
543 bool found = false;
544 for (rowsize ic2n = 0; ic2n < cell2node[cLocal].size(); ic2n++)
545 {
546 if (cell2node(cLocal, ic2n) == iNode &&
548 {
549 found = true;
550 break;
551 }
552 }
553 if (!found)
554 return false;
555 }
556 return true;
557 };
558 }
559
560 // bnd2cell via ComposeFiltered (inputs must be in global state)
561 // Result is AdjPairTracked<tAdj2Pair> with father adopted, state = Global.
564 nodeG2L,
567
568 // Periodic fixup: when a periodic bnd has only 1 matching cell (slot 1 == UnInitIndex),
569 // fill slot 1 with slot 0 (self-reference) to match legacy behavior.
570 if (isPeriodic)
571 {
572 for (index i = 0; i < bnd2cell.father->Size(); i++)
573 {
574 if (Geom::FaceIDIsPeriodic(bndElemInfo.father->operator()(i, 0).zone) &&
575 bnd2cell.father->operator()(i, 1) == UnInitIndex)
576 {
577 bnd2cell.father->operator()(i, 1) = bnd2cell.father->operator()(i, 0);
578 }
579 }
580 }
581
582 // Periodic: donor/main swap check — ensure bnd2cell(i, 0) is the donor-side cell.
583 // (This domain-specific logic is kept as-is.)
584 if (isPeriodic)
585 {
586 for (index i = 0; i < bnd2cell.father->Size(); i++)
587 if (bnd2cell(i, 1) != UnInitIndex && bnd2cell(i, 0) != bnd2cell(i, 1))
588 {
589 index ic0 = bnd2cell(i, 0);
590 index ic1 = bnd2cell(i, 1);
591 auto [ret0, rank0, ic0L] = cell2nodePbi.trans.pLGhostMapping->search_indexAppend(ic0);
592 auto [ret1, rank1, ic1L] = cell2nodePbi.trans.pLGhostMapping->search_indexAppend(ic1);
593 DNDS_assert_info(ret0 && ret1, "search failed");
594 std::vector<Geom::NodePeriodicBits> pbi0, pbi1;
595
596 for (auto in : bnd2node[i])
597 {
598 bool found0{false}, found1{false};
599 for (rowsize ic2n = 0; ic2n < cell2node[ic0L].size(); ic2n++)
600 if (cell2node[ic0L][ic2n] == in)
601 found0 = true, pbi0.push_back(cell2nodePbi(ic0L, ic2n));
602 for (rowsize ic2n = 0; ic2n < cell2node[ic1L].size(); ic2n++)
603 if (cell2node[ic1L][ic2n] == in)
604 found1 = true, pbi1.push_back(cell2nodePbi(ic1L, ic2n));
606 }
608 {
610 b = b & nodePB1;
611 else if (Geom::FaceIDIsPeriodic2(bndElemInfo(i, 0).zone))
612 b = b & nodePB2;
613 else if (Geom::FaceIDIsPeriodic3(bndElemInfo(i, 0).zone))
614 b = b & nodePB3;
615 };
616 for (auto &b : pbi0)
618 for (auto &b : pbi1)
620
622 if (bndElemInfo(i, 0).zone == Geom::BC_ID_PERIODIC_1_DONOR)
624 else if (bndElemInfo(i, 0).zone == Geom::BC_ID_PERIODIC_2_DONOR)
625 bndBit.setP2True();
626 else if (bndElemInfo(i, 0).zone == Geom::BC_ID_PERIODIC_3_DONOR)
627 bndBit.setP3True();
628 else if (!Geom::FaceIDIsPeriodic(bndElemInfo(i, 0).zone))
629 DNDS_assert_info(false, "this bnd with both sides has to be periodic");
630
631 bool match0{true}, match1{true};
632 for (auto b : pbi0)
633 if (b ^ bndBit)
634 match0 = false;
635 for (auto b : pbi1)
636 if (b ^ bndBit)
637 match1 = false;
638 if (match0 && !match1)
639 {
640 } // keep current ordering — match0 alone wins
641 else if (match1 && !match0)
642 std::swap(bnd2cell(i, 0), bnd2cell(i, 1));
643 else
644 {
645 if (mpi.rank >= 0)
646 {
647 for (auto b : pbi0)
648 DNDS::log() << b << ", ";
649 DNDS::log() << " ---- ";
650 for (auto b : pbi1)
651 DNDS::log() << b << ", ";
652 DNDS::log() << " ---- " << bndBit;
653 DNDS::log() << std::endl;
654 }
655 DNDS_assert_info(false,
656 match0
657 ? "this periodic bnd matches both sides"
658 : "this periodic bnd matches no sides");
659 }
660 }
661 }
662
663 // markGlobal already done by CheckedComposeFiltered
664 }
665
668 {
670 DNDS_assert(cell2node.isGlobal() && bnd2node.isGlobal());
672 DNDS_assert(cell2cell.idx.state() == Adj_PointToGlobal || cell2cell.idx.state() == Adj_Unknown);
673 DNDS_assert(bnd2cell.idx.state() == Adj_PointToGlobal || bnd2cell.idx.state() == Adj_Unknown);
674 DNDS_assert(cell2cell.father && cell2cell.father->Size() == this->NumCell());
675 DNDS_assert(bnd2cell.father && bnd2cell.father->Size() == this->NumBnd());
676
677 /********************************/
678 // Attach transformers and create father global mappings.
679 cell2cell.TransAttach();
680 cell2node.TransAttach();
682 if (isPeriodic)
687 bnd2cell.TransAttach();
688 bnd2node.TransAttach();
689 if (isPeriodic)
693 node2bnd.TransAttach();
694
695 cell2cell.trans.createFatherGlobalMapping();
696 coords.trans.createFatherGlobalMapping();
697 bnd2cell.trans.createFatherGlobalMapping();
698 node2bnd.trans.createFatherGlobalMapping();
699
700 /********************************/
701 // Unified ghost evaluation: single DAG, all inputs father-only.
702 // The evaluator handles scratch pulls internally.
705
708 auto ghostResult = dag.evaluateGhostTree(tree, mpi);
709
710 /********************************/
711 // Apply ghost sets to real arrays.
712
713 // --- Cells ---
714 {
715 auto it = ghostResult.ghostIndices.find(EntityKind::Cell);
716 std::vector<DNDS::index> ghostCells;
717 if (it != ghostResult.ghostIndices.end())
718 ghostCells = std::move(it->second);
719 cell2cell.trans.createGhostMapping(ghostCells);
720 cell2cell.trans.createMPITypes();
721 cell2cell.trans.pullOnce();
722 cell2node.BorrowAndPull(cell2cell);
724 if (isPeriodic)
727 }
728
729 // --- Nodes ---
730 {
731 auto itN = ghostResult.ghostIndices.find(EntityKind::Node);
732 std::vector<DNDS::index> ghostNodes;
733 if (itN != ghostResult.ghostIndices.end())
734 ghostNodes = std::move(itN->second);
735 coords.trans.createGhostMapping(ghostNodes);
736 coords.trans.createMPITypes();
737 coords.trans.pullOnce();
739 }
740
741 // --- Bnds ---
742 {
743 DNDS_assert(node2bnd.father);
745 DNDS_assert(node2bnd.isGlobal());
746
747 auto itB = ghostResult.ghostIndices.find(EntityKind::Bnd);
748 std::vector<DNDS::index> ghostBnds;
749 if (itB != ghostResult.ghostIndices.end())
750 ghostBnds = std::move(itB->second);
751 bnd2cell.trans.createGhostMapping(ghostBnds);
752 bnd2cell.trans.createMPITypes();
753 bnd2cell.trans.pullOnce();
754 bnd2node.BorrowAndPull(bnd2cell);
755 if (isPeriodic)
759
760 // Ghost bnds may reference nodes not yet in the coord ghost layer.
761 {
762 std::vector<DNDS::index> extraGhostNodes;
763 for (DNDS::index iBnd = bnd2node.father->Size(); iBnd < bnd2node.Size(); iBnd++)
764 for (DNDS::rowsize j = 0; j < bnd2node.RowSize(iBnd); j++)
765 {
766 auto iNode = bnd2node(iBnd, j);
769 if (!coords.trans.pLGhostMapping->search_indexAppend(iNode, rank, val))
770 extraGhostNodes.push_back(iNode);
771 }
775 if (nExtraGlobal > 0)
776 {
777 auto &existingGhost = coords.trans.pLGhostMapping->ghostIndex;
778 std::vector<DNDS::index> allGhostNodes(existingGhost.begin(), existingGhost.end());
779 allGhostNodes.insert(allGhostNodes.end(), extraGhostNodes.begin(), extraGhostNodes.end());
780 coords.trans.createGhostMapping(allGhostNodes);
781 node2nodeOrig.trans.BorrowGGIndexing(coords.trans);
782 coords.trans.createMPITypes();
783 node2nodeOrig.trans.createMPITypes();
784 coords.trans.pullOnce();
785 node2nodeOrig.trans.pullOnce();
786 }
787 }
788 }
789
790 // === Wire per-adjacency target mappings ===
791 {
792 auto cellGhostMap = cellElemInfo.trans.pLGhostMapping;
793 auto nodeGhostMap = coords.trans.pLGhostMapping;
794 auto bndGhostMap = bndElemInfo.trans.pLGhostMapping;
795
796 cell2node.idx.wireTargetMapping(nodeGhostMap);
797 bnd2node.idx.wireTargetMapping(nodeGhostMap);
798 cell2cell.idx.wireTargetMapping(cellGhostMap);
799 bnd2cell.idx.wireTargetMapping(cellGhostMap);
800 node2cell.idx.wireTargetMapping(cellGhostMap);
801 node2bnd.idx.wireTargetMapping(bndGhostMap);
802 }
803 }
804
807 {
808 // needs results of BuildGhostPrimary()
810 DNDS_assert(cell2node.isGlobal() && bnd2node.isGlobal() && cell2cell.isGlobal() && bnd2cell.isGlobal());
811 DNDS_assert_info(cell2node.idx.isWired(), "cell2node target mapping not wired");
812 DNDS_assert_info(cell2cell.idx.isWired(), "cell2cell target mapping not wired");
813 DNDS_assert_info(bnd2node.idx.isWired(), "bnd2node target mapping not wired");
814 DNDS_assert_info(bnd2cell.idx.isWired(), "bnd2cell target mapping not wired");
815
816 /**********************************/
817 cell2cell.toLocal();
818 cell2node.toLocal();
819 bnd2node.toLocal();
820
821 // bnd2cell: use toLocal() then assert father bnds' slot-0 cell is inside
822 bnd2cell.toLocal();
823 for (DNDS::index iBnd = 0; iBnd < bnd2cell.father->Size(); iBnd++)
824 DNDS_assert(bnd2cell(iBnd, 0) >= 0); // father bnd's owner cell must be found
825 /**********************************/
827 }
828
831 {
833 DNDS_assert(cell2node.isLocal() && bnd2node.isLocal() && cell2cell.isLocal() && bnd2cell.isLocal());
834 DNDS_assert_info(cell2node.idx.isWired(), "cell2node target mapping not wired");
835 DNDS_assert_info(cell2cell.idx.isWired(), "cell2cell target mapping not wired");
836 DNDS_assert_info(bnd2node.idx.isWired(), "bnd2node target mapping not wired");
837 DNDS_assert_info(bnd2cell.idx.isWired(), "bnd2cell target mapping not wired");
838
839 /**********************************/
840 cell2cell.toGlobal();
841 bnd2cell.toGlobal();
842 cell2node.toGlobal();
843 bnd2node.toGlobal();
844 /**********************************/
846 }
847
849 AdjGlobal2LocalPrimaryForBnd() // a reduction of primary version
850 {
851 // needs results of BuildGhostPrimary()
853 DNDS_assert(cell2node.isGlobal());
854 DNDS_assert_info(cell2node.idx.isWired(), "cell2node target mapping not wired");
855 /**********************************/
856 cell2node.toLocal();
857 /**********************************/
859 }
860
862 AdjLocal2GlobalPrimaryForBnd() // a reduction of primary version
863 {
865 DNDS_assert(cell2node.isLocal());
866 DNDS_assert_info(cell2node.idx.isWired(), "cell2node target mapping not wired");
867 /**********************************/
868 cell2node.toGlobal();
869 /**********************************/
871 }
872
875 {
877 DNDS_assert(face2node.isGlobal() && face2cell.isGlobal());
878 DNDS_assert_info(face2node.idx.isWired(), "face2node target mapping not wired");
879 DNDS_assert_info(face2cell.idx.isWired(), "face2cell target mapping not wired");
880 /**********************************/
881 face2node.toLocalOMP();
882 face2cell.toLocalOMP();
883 // face2bnd is not wired until MatchFaceBoundary runs; skip if not yet built.
884 if (face2bnd.idx.isWired())
885 {
886 DNDS_assert(face2bnd.isGlobal());
887 face2bnd.toLocal();
888 }
889 /**********************************/
891 }
892
895 {
897 DNDS_assert(face2node.isLocal() && face2cell.isLocal() && face2bnd.isLocal());
898 DNDS_assert_info(face2node.idx.isWired(), "face2node target mapping not wired");
899 DNDS_assert_info(face2cell.idx.isWired(), "face2cell target mapping not wired");
900 DNDS_assert_info(face2bnd.idx.isWired(), "face2bnd target mapping not wired");
901 /**********************************/
902 face2node.toGlobalOMP();
903 face2cell.toGlobalOMP();
904 face2bnd.toGlobal();
905 // MPI::Barrier(mpi.comm);
906 /**********************************/
908 }
909
912 {
914 DNDS_assert(cell2face.isLocal() && bnd2face.isLocal());
915 DNDS_assert_info(cell2face.idx.isWired(), "cell2face target mapping not wired");
916 DNDS_assert_info(bnd2face.idx.isWired(), "bnd2face target mapping not wired");
917 /**********************************/
918 cell2face.toGlobalOMP();
919 bnd2face.toGlobal();
920 /**********************************/
922 }
923
926 {
928 DNDS_assert(cell2face.isGlobal() && bnd2face.isGlobal());
929 DNDS_assert_info(cell2face.idx.isWired(), "cell2face target mapping not wired");
930 DNDS_assert_info(bnd2face.idx.isWired(), "bnd2face target mapping not wired");
931 /**********************************/
932 cell2face.toLocalOMP();
933 bnd2face.toLocal();
934 /**********************************/
936 }
937
940 {
942 DNDS_assert(node2cell.isGlobal() && node2bnd.isGlobal());
943
944 DNDS_assert(coords.trans.father && coords.trans.pLGhostMapping);
945
946 node2cell.TransAttach();
947 node2cell.trans.BorrowGGIndexing(coords.trans);
948 node2cell.trans.createMPITypes();
949 node2cell.trans.pullOnce();
950
951 node2bnd.TransAttach();
952 node2bnd.trans.BorrowGGIndexing(coords.trans);
953 node2bnd.trans.createMPITypes();
954 node2bnd.trans.pullOnce();
955
956 node2cell.idx.markGlobal();
957 node2bnd.idx.markGlobal();
958 }
959
962 {
964 DNDS_assert(node2cell.isLocal() && node2bnd.isLocal());
965 DNDS_assert_info(node2cell.idx.isWired(), "node2cell target mapping not wired");
966 DNDS_assert_info(node2bnd.idx.isWired(), "node2bnd target mapping not wired");
967 /**********************************/
968 node2cell.toGlobalOMP();
969 node2bnd.toGlobalOMP();
970 /**********************************/
972 }
973
976 {
978 DNDS_assert(node2cell.isGlobal() && node2bnd.isGlobal());
979 DNDS_assert_info(node2cell.idx.isWired(), "node2cell target mapping not wired");
980 DNDS_assert_info(node2bnd.idx.isWired(), "node2bnd target mapping not wired");
981 /**********************************/
982 node2cell.toLocalOMP();
983 node2bnd.toLocalOMP();
984 /**********************************/
986 }
987
989 {
990 for (index iNode = NumNode(); iNode < NumNodeProc(); iNode++)
991 {
992 int nCellAdjIn = 0;
993 for (auto iCell : node2cell[iNode])
994 if (iCell >= 0)
995 {
997 nCellAdjIn++;
998 for (auto iNodeOther : cell2node[iCell])
999 {
1001 }
1002 }
1004 }
1005
1006 std::set<index> bnd_main_nodes;
1007 for (index iNode = 0; iNode < NumNode(); iNode++)
1008 {
1009 for (auto iCell : node2cell[iNode])
1010 {
1011 DNDS_assert(iCell < NumCellProc() && iCell >= 0);
1012 for (auto iNodeOther : cell2node[iCell])
1013 {
1015 if (iNodeOther >= NumNode())
1016 bnd_main_nodes.insert(iNode);
1017 }
1018 }
1019 }
1020 std::map<index, int> bnd_main_nodes_adj_ghost_num;
1021 for (index iNode : bnd_main_nodes)
1023 for (index iNode = NumNode(); iNode < NumNodeProc(); iNode++)
1024 for (auto iCell : node2cell[iNode])
1025 if (iCell >= 0)
1026 for (auto iNodeOther : cell2node[iCell])
1027 if (iNodeOther >= 0 && iNodeOther < NumNode())
1031 }
1032
1034 {
1036 DNDS_assert(cell2node.isLocal() && bnd2node.isLocal());
1038 DNDS_assert(face2cell.isLocal() && face2node.isLocal());
1039 DNDS_assert_info(cellElemInfo.trans.pLGhostMapping, "trans of cellElemInfo needed but not built");
1040
1041 cell2cellFace.InitPair("cell2cellFace", mpi);
1042 cell2cellFace.father->Resize(this->NumCell());
1043 for (index iCell = 0; iCell < this->NumCell(); iCell++)
1044 {
1045 cell2cellFace.ResizeRow(iCell, cell2face[iCell].size());
1046 for (rowsize ic2f = 0; ic2f < cell2face[iCell].size(); ic2f++)
1047 {
1051 cell2cellFace[iCell][ic2f] = this->CellIndexLocal2Global(iCellOther);
1052 }
1053 }
1054 cell2cellFace.father->Compress();
1055 cell2cellFace.TransAttach();
1056 cell2cellFace.trans.BorrowGGIndexing(cell2node.trans);
1057 cell2cellFace.trans.createMPITypes();
1058 cell2cellFace.trans.pullOnce(); // warning! to pull the adj, must be in global state!
1060 cell2cellFace.idx.markGlobal();
1061
1062 // Wire cell2cellFace target mapping (points to cells)
1063 cell2cellFace.idx.wireTargetMapping(cellElemInfo.trans.pLGhostMapping);
1064 }
1065
1067 {
1068 // needs results of BuildGhostPrimary()
1070 DNDS_assert(cell2cellFace.isLocal());
1071 DNDS_assert_info(cell2cellFace.idx.isWired(), "cell2cellFace target mapping not wired");
1072
1073 /**********************************/
1074 cell2cellFace.toGlobal();
1075 /**********************************/
1076
1078 }
1079
1081 {
1082 // needs results of BuildGhostPrimary()
1084 DNDS_assert(cell2cellFace.isGlobal());
1085 DNDS_assert_info(cell2cellFace.idx.isWired(), "cell2cellFace target mapping not wired");
1086
1087 /**********************************/
1088 cell2cellFace.toLocal();
1089 /**********************************/
1090
1092 }
1093
1096 {
1098 DNDS_assert(cell2node.isLocal() && cell2cell.isLocal() && bnd2node.isLocal());
1099
1100 // === Section A: Allocate face-related array pairs ===
1101 cell2face.InitPair("cell2face", mpi);
1102 face2cell.InitPair("face2cell", mpi);
1103 face2node.InitPair("face2node", mpi);
1104 if (isPeriodic)
1105 face2nodePbi.InitPair("face2nodePbi", mpi);
1106 faceElemInfo.InitPair("faceElemInfo", mpi);
1107 face2bnd.InitPair("face2bnd", mpi);
1108 bnd2face.InitPair("bnd2face", mpi);
1109
1110 cell2face.father->Resize(cell2cell.father->Size());
1111 cell2face.son->Resize(cell2cell.son->Size());
1112
1113 index nCellAll = cell2cell.Size();
1115 index nLocalCells = cell2cell.father->Size();
1116
1117 // === Section B: Build SubEntityQueryPbi ===
1119 faceQuery.numSubEntities = [this](index iParent) -> int
1120 {
1121 return Elem::Element{cellElemInfo[iParent]->getElemType()}.GetNumFaces();
1122 };
1123 faceQuery.describe = [this](index iParent, int iSub) -> SubEntityDesc
1124 {
1125 auto eParent = Elem::Element{cellElemInfo[iParent]->getElemType()};
1126 auto eFace = eParent.ObtainFace(iSub);
1127 return SubEntityDesc{eFace.GetNumVertices(), eFace.GetNumNodes(),
1128 static_cast<t_index>(eFace.type)};
1129 };
1130 faceQuery.extractNodes = [this](index iParent, int iSub,
1131 const std::function<index(int)> &parentNodes,
1132 index *out)
1133 {
1134 auto eParent = Elem::Element{cellElemInfo[iParent]->getElemType()};
1135 auto eFace = eParent.ObtainFace(iSub);
1136 std::vector<index> pNodes(eParent.GetNumNodes());
1137 for (int i = 0; i < eParent.GetNumNodes(); i++)
1138 pNodes[i] = parentNodes(i);
1139 std::vector<index> fNodes(eFace.GetNumNodes());
1140 eParent.ExtractFaceNodes(iSub, pNodes, fNodes);
1141 for (int i = 0; i < eFace.GetNumNodes(); i++)
1142 out[i] = fNodes[i];
1143 };
1144 if (isPeriodic)
1145 {
1146 faceQuery.matchExtra = [this](index iParent, int iSub,
1147 index /*iCandEntity*/,
1148 index candidateParent, int candidateSub) -> bool
1149 {
1150 auto eParentA = Elem::Element{cellElemInfo[iParent]->getElemType()};
1151 auto eFaceA = eParentA.ObtainFace(iSub);
1152 int nFaceNodes = eFaceA.GetNumNodes();
1153 std::vector<index> nodesA(nFaceNodes);
1154 eParentA.ExtractFaceNodes(iSub, cell2node[iParent], nodesA);
1155 std::vector<NodePeriodicBits> pbiA(nFaceNodes);
1156 eParentA.ExtractFaceNodes(iSub, cell2nodePbi[iParent], pbiA);
1157 auto eParentB = Elem::Element{cellElemInfo[candidateParent]->getElemType()};
1158 std::vector<index> nodesB(nFaceNodes);
1160 std::vector<NodePeriodicBits> pbiB(nFaceNodes);
1162 using idx_pbi = std::pair<index, NodePeriodicBits>;
1163 auto cmp = [](const idx_pbi &L, const idx_pbi &R)
1164 { return L.first == R.first ? uint8_t(L.second) < uint8_t(R.second)
1165 : L.first < R.first; };
1166 std::vector<idx_pbi> pairsA(nFaceNodes), pairsB(nFaceNodes);
1167 for (int i = 0; i < nFaceNodes; i++)
1168 {
1169 pairsA[i] = {nodesA[i], pbiA[i]};
1170 pairsB[i] = {nodesB[i], pbiB[i]};
1171 }
1172 std::sort(pairsA.begin(), pairsA.end(), cmp);
1173 std::sort(pairsB.begin(), pairsB.end(), cmp);
1174 auto v0 = pairsA[0].second ^ pairsB[0].second;
1175 for (int i = 1; i < nFaceNodes; i++)
1176 if ((pairsA[i].second ^ pairsB[i].second) != v0)
1177 return false;
1178 return true;
1179 };
1180 faceQuery.extractPbi = [this](index iParent, int iSub,
1181 const std::function<NodePeriodicBits(int)> &parentPbi,
1183 {
1184 auto eParent = Elem::Element{cellElemInfo[iParent]->getElemType()};
1185 auto eFace = eParent.ObtainFace(iSub);
1186 std::vector<NodePeriodicBits> pPbi(eParent.GetNumNodes());
1187 for (int i = 0; i < eParent.GetNumNodes(); i++)
1188 pPbi[i] = parentPbi(i);
1189 std::vector<NodePeriodicBits> fPbi(eFace.GetNumNodes());
1190 eParent.ExtractFaceNodes(iSub, pPbi, fPbi);
1191 for (int i = 0; i < eFace.GetNumNodes(); i++)
1192 out[i] = fPbi[i];
1193 };
1194 }
1195
1196 // === Section C: Ownership resolver ===
1198 [this](const std::vector<index> &parents,
1199 const std::vector<MPI_int> &parentRanks,
1201 {
1203 for (size_t i = 1; i < parentRanks.size(); i++)
1204 if (parentRanks[i] < minRank)
1206 bool anyLocal = false;
1207 for (auto p : parents)
1208 if (p < nLocal)
1209 anyLocal = true;
1210 if (!anyLocal)
1211 return {false, {}};
1212 if (minRank != mpi.rank)
1213 return {false, {}};
1214 std::vector<MPI_int> peers;
1215 for (size_t i = 0; i < parents.size(); i++)
1216 if (parents[i] >= nLocal && parentRanks[i] != mpi.rank)
1217 peers.push_back(parentRanks[i]);
1218 std::sort(peers.begin(), peers.end());
1219 peers.erase(std::unique(peers.begin(), peers.end()), peers.end());
1220 return {true, std::move(peers)};
1221 };
1222
1223 // === Section D: InterpolateGlobal (e2p_rs=2: face2cell is fixed-width) ===
1226 *cell2node.trans.pLGhostMapping,
1227 *cell2node.father->pLGlobalMapping,
1228 *coords.trans.pLGhostMapping,
1231
1232 index nOwnedFaces = globalResult.nOwnedEntities;
1233
1234 // === Section E: Adopt owned face data directly into mesh arrays ===
1235 face2node.father = globalResult.entity2node.father;
1236 face2node.TransAttach();
1237 face2node.trans.createFatherGlobalMapping();
1238
1239 // entity2parent is already ArrayAdjacencyPair<2> — adopt directly, no copy.
1240 face2cell.father = globalResult.entity2parent.father;
1241
1242 faceElemInfo.father = globalResult.entityElemInfo.father;
1243 if (isPeriodic)
1244 face2nodePbi.father = globalResult.entity2nodePbi.father;
1245
1246 // Populate cell2face from parent2entity (global face IDs).
1247 for (index iCell = 0; iCell < nCellAll; iCell++)
1248 {
1249 rowsize nFaces = globalResult.parent2entity.RowSize(iCell);
1250 cell2face.ResizeRow(iCell, nFaces);
1251 for (rowsize j = 0; j < nFaces; j++)
1252 cell2face(iCell, j) = globalResult.parent2entity(iCell, j);
1253 }
1254 cell2face.father->Compress();
1255 cell2face.son->Compress();
1256
1257 auto gSize = face2node.father->globalSize();
1258 if (mpi.rank == 0)
1259 log() << "UnstructuredMesh === InterpolateFace: total faces " << gSize << std::endl;
1260
1263 }
1264
1267 {
1268 // Determine ghost faces via evaluateGhostTree(Cell → Cell2Face → Face).
1269 // cell2face is cell-indexed but has no own pLGlobalMapping — borrow from cell2node.
1270 if (!cell2face.father->pLGlobalMapping)
1271 cell2face.father->pLGlobalMapping = cell2node.father->pLGlobalMapping;
1272 {
1275
1277 ghostSpec.chains.push_back(GhostChain{
1281 });
1283 auto ghostResult = dag.evaluateGhostTree(ghostTree, mpi);
1284
1285 auto &ghostFaces = ghostResult.ghostIndices[EntityKind::Face];
1286
1287 face2cell.TransAttach();
1288 face2cell.trans.createFatherGlobalMapping();
1289 face2cell.trans.createGhostMapping(ghostFaces);
1290 face2cell.trans.createMPITypes();
1291 face2cell.trans.pullOnce();
1292 face2node.trans.BorrowGGIndexing(face2cell.trans);
1293 face2node.trans.createMPITypes();
1294 face2node.trans.pullOnce();
1296 faceElemInfo.trans.BorrowGGIndexing(face2cell.trans);
1297 faceElemInfo.trans.createMPITypes();
1298 faceElemInfo.trans.pullOnce();
1299 if (isPeriodic)
1300 {
1302 face2nodePbi.trans.BorrowGGIndexing(face2cell.trans);
1303 face2nodePbi.trans.createMPITypes();
1304 face2nodePbi.trans.pullOnce();
1305 }
1306 }
1307
1308 // Wire per-adjacency target mappings for facial adjacencies.
1309 // Ghost mappings are available after the ghost-tree pull above.
1310 {
1311 auto cellGhostMap = cellElemInfo.trans.pLGhostMapping;
1312 auto nodeGhostMap = coords.trans.pLGhostMapping;
1313 face2node.idx.wireTargetMapping(nodeGhostMap);
1314 face2cell.idx.wireTargetMapping(cellGhostMap);
1315 }
1316
1317 // Convert face arrays to local indices.
1319 face2node.idx.markGlobal();
1320 face2cell.idx.markGlobal();
1322
1323 // Convert cell2face to local face indices (father + son).
1324 // Wire target mapping first (face ghost mapping available from BuildGhostFace).
1325 {
1326 auto faceGhostMap = face2cell.trans.pLGhostMapping;
1327 cell2face.idx.wireTargetMapping(faceGhostMap);
1328 bnd2face.idx.wireTargetMapping(faceGhostMap);
1329 }
1331 cell2face.idx.markGlobal();
1332 bnd2face.idx.markGlobal();
1333 cell2face.toLocalOMP();
1335 // bnd2face has no entries yet (populated in MatchFaceBoundary),
1336 // but its state must track cell2face for the group invariant.
1337 bnd2face.toLocal();
1338 }
1339
1342 {
1346
1347 // Re-pull faceElemInfo so ghost faces inherit the owning rank's zone.
1348 // MatchBoundariesToFaces assigns periodic zones (main vs donor) based
1349 // on local boundary elements, which can disagree across ranks for the
1350 // same physical face. The owning rank's assignment is authoritative.
1351 faceElemInfo.trans.pullOnce();
1352
1353 // face2bnd.father now contains local bnd indices (from MatchBoundariesToFaces).
1354 // Wire + markLocal, then convert to global before the ghost pull so that
1355 // remote ranks receive rank-independent global bnd indices.
1356 face2bnd.idx.wireTargetMapping(bndElemInfo.trans.pLGhostMapping);
1357 face2bnd.idx.markLocal();
1358 face2bnd.toGlobal();
1359 face2bnd.BorrowAndPull(face2cell);
1360 face2bnd.toLocal(); // converts both father and son: global → local
1361
1362 // Communicate cell2face and bnd2face ghost data.
1363 // cell2face/bnd2face are already wired (BuildGhostFace).
1364 this->AdjLocal2GlobalC2F();
1365 cell2face.TransAttach();
1366 cell2face.trans.BorrowGGIndexing(cell2node.trans);
1367 cell2face.trans.createMPITypes();
1368 cell2face.trans.pullOnce();
1369 bnd2face.TransAttach();
1370 bnd2face.trans.BorrowGGIndexing(bnd2node.trans);
1371 bnd2face.trans.createMPITypes();
1372 bnd2face.trans.pullOnce();
1373
1374 this->AdjGlobal2LocalC2F();
1375 }
1376
1379 {
1380
1381 //* some assertions on faces
1382 std::vector<uint16_t> cCont(cell2cell.Size(), 0); // simulate flux
1383 for (DNDS::index iFace = 0; iFace < faceElemInfo.Size(); iFace++)
1384 {
1385 auto faceID = faceElemInfo(iFace, 0).zone;
1386 // NOLINTBEGIN(bugprone-branch-clone) — branches share asserts but are semantically distinct
1388 {
1389 // if (FaceIDIsPeriodic(faceID))
1390 // {
1391 // // TODO: tend to the case of face is PeriodicDonor with Main in same proc
1392 // continue;
1393 // }
1394 // if (face2cell[iFace][0] < cell2cell.father->Size()) // other side prime cell, periodic also
1396 fmt::format(
1397 "Face {} is internal, but f2c[1] is null, at {},{},{} - {},{},{}", iFace,
1398 coords[face2node[iFace][0]](0),
1399 coords[face2node[iFace][0]](1),
1400 coords[face2node[iFace][0]](2),
1401 face2node[iFace].size() > 1 ? coords[face2node[iFace][1]](0) : 0.,
1402 face2node[iFace].size() > 1 ? coords[face2node[iFace][1]](1) : 0.,
1403 face2node[iFace].size() > 1 ? coords[face2node[iFace][1]](2) : 0.)); // Assert has enough cell donors
1404 DNDS_assert(face2cell[iFace][0] >= 0 && face2cell[iFace][0] < cell2cell.Size());
1405 DNDS_assert(face2cell[iFace][1] >= 0 && face2cell[iFace][1] < cell2cell.Size());
1406 cCont[face2cell[iFace][0]]++;
1407 cCont[face2cell[iFace][1]]++;
1408 }
1409 else // a external BC
1410 {
1412 DNDS_assert(face2cell[iFace][0] >= 0 && face2cell[iFace][0] < cell2cell.father->Size());
1413 cCont[face2cell[iFace][0]]++;
1414 }
1415 // NOLINTEND(bugprone-branch-clone)
1416 }
1417 for (DNDS::index iCell = 0; iCell < cellElemInfo.father->Size(); iCell++) // for every non-ghost
1418 {
1419 for (auto iFace : cell2face[iCell])
1420 {
1421 DNDS_assert(iFace >= 0 && iFace < face2cell.Size());
1423 }
1424 DNDS_assert(cCont[iCell] == cell2face.RowSize(iCell));
1425 }
1426 }
1427
1430 {
1431 // TODO: Make these read/write of mesh independent of ghost part!
1433 DNDS_assert(cell2node.isGlobal() && bnd2node.isGlobal() && cell2cell.isGlobal() && bnd2cell.isGlobal());
1434
1435 auto cwd = serializerP->GetCurrentPath();
1436 serializerP->CreatePath(name);
1437 serializerP->GoToPath(name);
1438
1439 serializerP->WriteString("mesh", "UnstructuredMesh");
1440 serializerP->WriteIndex("dim", dim);
1441 if (serializerP->IsPerRank())
1442 serializerP->WriteIndex("MPIRank", mpi.rank);
1443 serializerP->WriteIndex("MPISize", mpi.size);
1444 serializerP->WriteInt("isPeriodic", isPeriodic);
1445
1447 cell2node.WriteSerialize(serializerP, "cell2node");
1448 // cell2cell.WriteSerialize(serializerP, "cell2cell");
1449 cellElemInfo.WriteSerialize(serializerP, "cellElemInfo");
1450 bnd2node.WriteSerialize(serializerP, "bnd2node");
1451 // bnd2cell.WriteSerialize(serializerP, "bnd2cell");
1452 bndElemInfo.WriteSerialize(serializerP, "bndElemInfo");
1453 if (isPeriodic)
1454 {
1455 cell2nodePbi.WriteSerialize(serializerP, "cell2nodePbi");
1456 bnd2nodePbi.WriteSerialize(serializerP, "bnd2nodePbi");
1457 periodicInfo.WriteSerializer(serializerP, "periodicInfo");
1458 }
1459 bnd2bndOrig.WriteSerialize(serializerP, "bnd2bndOrig");
1460 cell2cellOrig.WriteSerialize(serializerP, "cell2cellOrig");
1461 node2nodeOrig.WriteSerialize(serializerP, "node2nodeOrig");
1462
1463 serializerP->GoToPath(cwd);
1464 }
1465
1468 {
1469 auto cwd = serializerP->GetCurrentPath();
1470 // serializerP->CreatePath(name);//! remember no create!
1471 serializerP->GoToPath(name);
1472
1473 std::string meshRead;
1474 index dimRead{0}, rankRead{0}, sizeRead{0};
1475 int isPeriodicRead = 0;
1476 serializerP->ReadString("mesh", meshRead);
1477 serializerP->ReadIndex("dim", dimRead);
1478 if (serializerP->IsPerRank())
1479 serializerP->ReadIndex("MPIRank", rankRead);
1480 serializerP->ReadIndex("MPISize", sizeRead);
1481 serializerP->ReadInt("isPeriodic", isPeriodicRead);
1483 DNDS_assert(meshRead == "UnstructuredMesh");
1485 DNDS_assert((!serializerP->IsPerRank() || rankRead == mpi.rank) && sizeRead == mpi.size);
1486
1487 // make the empty arrays
1488 coords.InitPair("coords", getMPI());
1489 cellElemInfo.InitPair("cellElemInfo", getMPI());
1490 bndElemInfo.InitPair("bndElemInfo", getMPI());
1491 cell2node.InitPair("cell2node", getMPI());
1492 if (isPeriodic)
1493 {
1494 cell2nodePbi.InitPair("cell2nodePbi", getMPI());
1495 bnd2nodePbi.InitPair("bnd2nodePbi", getMPI());
1496 }
1497 bnd2node.InitPair("bnd2node", getMPI());
1498
1499 bnd2bndOrig.InitPair("bnd2bndOrig", getMPI());
1500 cell2cellOrig.InitPair("cell2cellOrig", getMPI());
1501 node2nodeOrig.InitPair("node2nodeOrig", getMPI());
1502
1503 coords.ReadSerialize(serializerP, "coords");
1504 cell2node.ReadSerialize(serializerP, "cell2node");
1505 // cell2cell.ReadSerialize(serializerP, "cell2cell");
1506 cellElemInfo.ReadSerialize(serializerP, "cellElemInfo");
1507 bnd2node.ReadSerialize(serializerP, "bnd2node");
1508 // bnd2cell.ReadSerialize(serializerP, "bnd2cell");
1509 bndElemInfo.ReadSerialize(serializerP, "bndElemInfo");
1510 if (isPeriodic)
1511 {
1512 cell2nodePbi.ReadSerialize(serializerP, "cell2nodePbi");
1513 bnd2nodePbi.ReadSerialize(serializerP, "bnd2nodePbi");
1514 periodicInfo.ReadSerializer(serializerP, "periodicInfo");
1515 }
1516 bnd2bndOrig.ReadSerialize(serializerP, "bnd2bndOrig");
1517 cell2cellOrig.ReadSerialize(serializerP, "cell2cellOrig");
1518 node2nodeOrig.ReadSerialize(serializerP, "node2nodeOrig");
1519
1520 // after matters:
1521 coords.trans.createMPITypes();
1522 cell2node.trans.createMPITypes();
1523 // cell2cell.trans.createMPITypes();
1524 cellElemInfo.trans.createMPITypes();
1525 bnd2node.trans.createMPITypes();
1526 // bnd2cell.trans.createMPITypes();
1527 bndElemInfo.trans.createMPITypes();
1528 if (isPeriodic)
1529 {
1530 cell2nodePbi.trans.createMPITypes();
1531 bnd2nodePbi.trans.createMPITypes();
1532 }
1533 bnd2bndOrig.trans.createMPITypes();
1534 cell2cellOrig.trans.createMPITypes();
1535 node2nodeOrig.trans.createMPITypes();
1536 adjPrimaryState = Adj_PointToGlobal; // the file is pointing to local
1537 cell2node.idx.markGlobal();
1538 bnd2node.idx.markGlobal();
1539
1540 index nCellG = this->NumCellGlobal(); // collective call!
1541 index nNodeG = this->NumNodeGlobal(); // collective call!
1542 index nNodeB = this->NumBndGlobal(); // collective call!
1543 if (mpi.rank == mRank)
1544 {
1545 log() << "UnstructuredMesh === ReadSerialize "
1546 << "Global NumCell [ " << nCellG << " ]" << std::endl;
1547 log() << "UnstructuredMesh === ReadSerialize "
1548 << "Global NumNode [ " << nNodeG << " ]" << std::endl;
1549 log() << "UnstructuredMesh === ReadSerialize "
1550 << "Global NumBnd [ " << nNodeB << " ]" << std::endl;
1551 }
1552 MPISerialDo(mpi, [&]()
1553 { log() << " Rank: " << mpi.rank << " nCell " << this->NumCell() << " nCellGhost " << this->NumCellGhost() << std::endl; });
1554 MPISerialDo(mpi, [&]()
1555 { log() << " Rank: " << mpi.rank << " nNode " << this->NumNode() << " nNodeGhost " << this->NumNodeGhost() << std::endl; });
1556
1557 serializerP->GoToPath(cwd);
1558 }
1559
1561 {
1562 DNDS_assert(bMesh.dim == dim - 1 && bMesh.mpi == mpi);
1563 bMesh.cell2node.InitPair("bMesh.cell2node", mpi);
1564 bMesh.coords.InitPair("bMesh.coords", mpi);
1565 if (isPeriodic)
1566 {
1567 bMesh.isPeriodic = true;
1569 bMesh.cell2nodePbi.InitPair("bMesh.cell2nodePbi", mpi);
1570 }
1571 bMesh.cellElemInfo.InitPair("bMesh.cellElemInfo", mpi);
1572 bMesh.cell2cellOrig.InitPair("bMesh.cell2cellOrig", mpi);
1573 bMesh.node2nodeOrig.InitPair("bMesh.node2nodeOrig", mpi);
1574
1575 bMesh.bnd2cell.InitPair("bMesh.bnd2cell", mpi); // which will remain 0 sized
1576 bMesh.bnd2node.InitPair("bMesh.bnd2node", mpi); // which will remain 0 sized
1577 bMesh.bndElemInfo.InitPair("bMesh.bndElemInfo", mpi); // which will remain 0 sized
1578 bMesh.bnd2bndOrig.InitPair("bMesh.bnd2bndOrig", mpi); // which will remain 0 sized
1579 if (isPeriodic)
1580 bMesh.bnd2nodePbi.InitPair("bMesh.bnd2nodePbi", mpi); // which will remain 0 sized
1581
1583 node2bndNodeGlobal.InitPair("node2bndNodeGlobal", mpi);
1584
1585 node2bndNodeGlobal.father->Resize(this->NumNode());
1586 node2bndNodeGlobal.TransAttach();
1587 node2bndNodeGlobal.trans.BorrowGGIndexing(coords.trans);
1588 node2bndNodeGlobal.trans.createMPITypes();
1590 {
1591 for (index i = 0; i < node2bndNodeGlobal.Size(); i++)
1592 node2bndNodeGlobal[i] = 0;
1593 for (index iBnd = 0; iBnd < this->NumBnd(); iBnd++)
1594 for (auto iNode : bnd2node.father->operator[](iBnd))
1595 node2bndNodeGlobal[iNode]++; // now stores num-reference
1596
1597 {
1598 auto node2bndNodeGlobalPast = make_ssp<tInd::element_type>(ObjName{"node2bndNodeGlobalPast"}, mpi);
1601 node2bndNodeGlobalPastTrans.createFatherGlobalMapping();
1602 std::vector<index> pushSonSeries(node2bndNodeGlobal.son->Size());
1603 for (size_t i = 0; i < pushSonSeries.size(); i++)
1605 node2bndNodeGlobalPastTrans.createGhostMapping(pushSonSeries, node2bndNodeGlobal.trans.pLGhostMapping->ghostStart);
1606 node2bndNodeGlobalPastTrans.createMPITypes();
1607 node2bndNodeGlobalPastTrans.pullOnce();
1608 DNDS_assert(DNDS::size_to_index(node2bndNodeGlobal.trans.pLGhostMapping->ghostIndex.size()) == node2bndNodeGlobal.son->Size());
1609 DNDS_assert(DNDS::size_to_index(node2bndNodeGlobal.trans.pLGhostMapping->pushingIndexGlobal.size()) == node2bndNodeGlobalPast->Size());
1610 for (index i = 0; i < node2bndNodeGlobalPast->Size(); i++)
1611 {
1612 index iNodeG = node2bndNodeGlobal.trans.pLGhostMapping->pushingIndexGlobal[i]; //?should be right
1613 auto [ret, rank, iNodeL] = node2bndNodeGlobal.trans.pLGlobalMapping->search(iNodeG);
1614 DNDS_assert(ret && rank == node2bndNodeGlobal.father->getMPI().rank); // has to be local main
1615 node2bndNodeGlobal[iNodeL] += (*node2bndNodeGlobalPast)[i];
1616 }
1617 }
1618 {
1619 for (index i = 0; i < node2bndNodeGlobal.father->Size(); i++)
1620 if (node2bndNodeGlobal[i] == 0)
1622 else
1624 }
1628 for (index i = 0; i < node2bndNodeGlobal.Size(); i++)
1629 if (node2bndNodeGlobal[i] >= 0)
1631 }
1632 node2bndNodeGlobal.trans.pullOnce();
1634 bMesh.coords.TransAttach();
1635 bMesh.coords.trans.createFatherGlobalMapping();
1636 std::vector<index> bndNodePullingSet;
1637 for (index iNode = 0; iNode < this->NumNodeProc(); iNode++)
1638 {
1639 if (node2bndNodeGlobal[iNode] < 0)
1640 continue;
1641 // then node2bndNodeGlobal[iNode] >= 0, which covers all bnd2node entries
1642 auto [ret, rank, iNodeL_in_bnd] = bMesh.coords.trans.pLGlobalMapping->search(node2bndNodeGlobal[iNode]);
1644 if (rank != node2bndNodeGlobal.father->getMPI().rank)
1645 bndNodePullingSet.push_back(node2bndNodeGlobal[iNode]); // bndNodePullingSet now also covers bnd2node needs
1646 }
1647 bMesh.coords.trans.createGhostMapping(bndNodePullingSet);
1648 bMesh.coords.trans.createMPITypes();
1649 bMesh.node2nodeOrig.father->Resize(bndNodeCount);
1650 bMesh.node2nodeOrig.TransAttach();
1651 bMesh.node2nodeOrig.trans.BorrowGGIndexing(bMesh.coords.trans);
1652 bMesh.node2nodeOrig.trans.createMPITypes();
1653
1654 // std::cout << mpi.rank << ", " << bndNodePullingSet.size() << ", " << bndNodeCount << std::endl;
1655 node2bndNode.resize(this->NumNodeProc(), -1);
1656 bMesh.node2parentNode.resize(bMesh.coords.Size());
1657 for (index iN = 0; iN < node2bndNodeGlobal.Size(); iN++)
1658 node2bndNode.at(iN) = bMesh.NodeIndexGlobal2Local(node2bndNodeGlobal[iN]),
1660 for (size_t iNode = 0; iNode < node2bndNode.size(); iNode++)
1661 if (node2bndNode[iNode] >= 0)
1662 bMesh.node2parentNode.at(node2bndNode[iNode]) = iNode;
1663
1664 // std::cout << mpi.rank << ", " << bndNodeCount << std::endl;
1665 for (index iBNode = 0; iBNode < bMesh.coords.Size(); iBNode++)
1666 bMesh.coords[iBNode] = coords[bMesh.node2parentNode[iBNode]];
1667 bMesh.coords.trans.pullOnce(); // excessive, should be identical
1668 for (index iBNode = 0; iBNode < bMesh.node2nodeOrig.Size(); iBNode++)
1669 bMesh.node2nodeOrig[iBNode] = node2nodeOrig[bMesh.node2parentNode[iBNode]];
1670
1671 index nBndCellUse{0};
1672 for (index iB = 0; iB < this->NumBnd(); iB++)
1673 if (!FaceIDIsPeriodic(this->bndElemInfo(iB, 0).zone))
1674 nBndCellUse++;
1676 if (isPeriodic)
1678 bMesh.cell2cellOrig.father->Resize(nBndCellUse);
1680 bMesh.cell2parentCell.resize(nBndCellUse, -1);
1681 nBndCellUse = 0;
1682 for (index iB = 0; iB < this->NumBnd(); iB++)
1683 {
1684 if (FaceIDIsPeriodic(this->bndElemInfo(iB, 0).zone))
1685 continue;
1686 bMesh.cell2parentCell.at(nBndCellUse) = iB;
1687 bMesh.cell2node.ResizeRow(nBndCellUse, bnd2node.RowSize(iB));
1688 if (isPeriodic)
1689 bMesh.cell2nodePbi.ResizeRow(nBndCellUse, bnd2node.RowSize(iB));
1691 bMesh.cell2cellOrig(nBndCellUse, 0) = bnd2bndOrig(iB, 0);
1692
1693 for (rowsize ib2n = 0; ib2n < bnd2node.RowSize(iB); ib2n++)
1694 {
1695 if (bnd2faceV.at(iB) < 0) // where bnd has not a face!
1697 else
1698 bMesh.cell2node[nBndCellUse][ib2n] = node2bndNode.at(face2node[bnd2faceV.at(iB)][ib2n]); //* respect the face ordering if possible // this can be omitted if all bnds used are not periodic
1700 if (isPeriodic)
1701 {
1702 if (bnd2faceV.at(iB) < 0) // where bnd has not a face!
1703 bMesh.cell2nodePbi[nBndCellUse][ib2n] = Geom::NodePeriodicBits{}; // a invalid value
1704 else
1705 {
1707 }
1708 }
1709 }
1710 nBndCellUse++;
1711 }
1712
1713 bMesh.cell2node.father->Compress();
1714 bMesh.cell2node.father->createGlobalMapping();
1715 bMesh.cell2node.TransAttach();
1716 bMesh.cell2node.trans.createGhostMapping(std::vector<int>{}); // now bnd mesh has no valid cell2cell and ghost cells
1717
1718 // Ensure global mappings exist on all arrays that BuildSerialOut
1719 // (and other consumers) may query via globalSize(). The ghost
1720 // mappings were already set up for coords/node2nodeOrig above;
1721 // cellElemInfo and cell2cellOrig only need the global mapping.
1722 bMesh.cellElemInfo.father->createGlobalMapping();
1723 bMesh.cell2cellOrig.father->createGlobalMapping();
1724
1726 // bMesh.cell2node was populated directly with local indices.
1727 // Wire the target mapping (bMesh's node ghost mapping), then mark local.
1728 bMesh.cell2node.idx.wireTargetMapping(bMesh.coords.trans.pLGhostMapping);
1729 bMesh.cell2node.idx.markLocal();
1730 if (mpi.rank == mRank)
1731 log() << "UnstructuredMesh === ConstructBndMesh Done" << std::endl;
1732 }
1733
1742
1744 MeshConnectivity &dag) const
1745 {
1746 fillRegistry(dag, {});
1747 }
1748
1751 const std::unordered_set<AdjKind, AdjKindHash> &skip) const
1752 {
1753 dag.meshDim = dim;
1754
1755 // --- Register adjacency arrays (father-only shallow copy) ---
1756 auto tryAdj = [&](AdjKind kind, const auto &pair)
1757 {
1758 if (pair.father && skip.find(kind) == skip.end())
1759 dag.registerAdj(kind, pair);
1760 };
1761
1762 // Primary
1767 // N2CB
1770 // Facial
1776 // C2CFace
1778
1779 // --- Register global mappings ---
1780 // For each entity kind, find the first adj array whose father
1781 // carries a valid pLGlobalMapping. All adj arrays for the same
1782 // entity kind share equivalent offsets (same father Size), so
1783 // any one suffices. check_throw if a mapping is needed (i.e.,
1784 // at least one adj was registered whose .from is this kind) but
1785 // none is found.
1786
1787 // Candidate sources per entity kind (ordered by typical availability).
1788 // All adj arrays for the same entity kind have equivalent offsets
1789 // (same father Size), so any one suffices.
1790 auto firstValid = [](std::initializer_list<ssp<GlobalOffsetsMapping>> candidates)
1792 {
1793 for (const auto &gm : candidates)
1794 if (gm)
1795 return gm;
1796 return nullptr;
1797 };
1798
1799 auto getGM = [](const auto &pair) -> ssp<GlobalOffsetsMapping>
1800 {
1801 if (pair.father && pair.father->pLGlobalMapping)
1802 return pair.father->pLGlobalMapping;
1803 return nullptr;
1804 };
1805
1807 auto nodeMap = coords.father ? coords.father->pLGlobalMapping : nullptr;
1810
1811 // Register if found; check_throw if any registered adj needs it
1812 auto regMap = [&](EntityKind kind, const ssp<GlobalOffsetsMapping> &gm)
1813 {
1814 if (gm)
1815 {
1816 dag.registerGlobalMapping(kind, gm);
1817 }
1818 else
1819 {
1820 // Verify no registered adj has this kind as its from-entity
1821 for (auto &[adjKind, _] : dag.adjRegistry)
1823 adjKind.from != kind,
1824 fmt::format("fillRegistry: no pLGlobalMapping found for EntityKind {} "
1825 "but adj {} requires it",
1826 static_cast<int>(kind), adjKindName(adjKind)));
1827 }
1828 };
1829
1834 }
1835
1836 // =================================================================
1837 // File-local helpers for ReorderLocalCells
1838 // =================================================================
1839 // Cell permutation helper: extracted to shared header.
1840 // See Mesh_CellPermutation.hpp for CellPermutationResult + ComputeCellPermutation.
1841} // namespace DNDS::Geom
1842#include "Mesh_CellPermutation.hpp"
1843namespace DNDS::Geom
1844{
1845
1847 {
1849 DNDS_assert(cell2node.isLocal() && bnd2node.isLocal() && cell2cell.isLocal() && bnd2cell.isLocal());
1850 DNDS_check_throw(int64_t(nParts) * int64_t(nPartsInner) < std::numeric_limits<int>::max());
1851 nParts = std::max(nParts, 1);
1852 nPartsInner = std::max(nPartsInner, 1);
1853
1854 // Section A: Compute cell permutation (Metis partition + contiguous sort)
1858 this->localPartitionStarts = std::move(perm.localPartitionStarts);
1859
1862 if (mpi.rank == mRank)
1863 log()
1864 << fmt::format("UnstructuredMesh === ReorderLocalCells, nPart0 [{}], got reordering, bw [{}] to [{}]", nParts, perm.bwOld, perm.bwNew) << std::endl;
1865
1866 // Section B: Build cellOld2NewArr for MPI communication of new cell indices
1868 cellOld2NewArr.InitPair("cellOld2NewArr", mpi);
1869 cellOld2NewArr.father->Resize(this->NumCell());
1870 for (index iCell = 0; iCell < this->NumCell(); iCell++)
1871 (*cellOld2NewArr.father)[iCell][0] = this->CellIndexLocal2Global_NoSon(perm.cellOld2New.at(iCell)); // this is right but bad syntax
1872 cellOld2NewArr.TransAttach();
1873
1874 std::set<index> cellsNonLocalFull;
1875
1876 auto record_iCellNonLocal = [&](index iCell)
1877 {
1878 if (iCell >= 0 && iCell < cell2node.father->pLGlobalMapping->globalSize())
1879 {
1880 if (std::get<1>(cell2node.father->pLGlobalMapping->search(iCell)) != mpi.rank)
1881 cellsNonLocalFull.insert(iCell);
1882 }
1883 else
1885 };
1886
1887 if (this->adjFacialState != Adj_Unknown && this->face2cell.isBuilt())
1888 {
1890 DNDS_assert(face2cell.isLocal() && face2node.isLocal());
1891 this->AdjLocal2GlobalFacial();
1892 // see face2cell
1893 for (index iF = 0; iF < this->NumFace(); iF++)
1894 for (rowsize if2c = 0; if2c < 2; if2c++)
1896 }
1897 if (this->adjC2FState != Adj_Unknown && this->cell2face.isBuilt())
1898 {
1900 DNDS_assert(cell2face.isLocal() && bnd2face.isLocal());
1901 this->AdjLocal2GlobalC2F();
1902 }
1903 if (this->adjN2CBState != Adj_Unknown && this->node2cell.isBuilt())
1904 {
1906 DNDS_assert(node2cell.isLocal() && node2bnd.isLocal());
1907 this->AdjLocal2GlobalN2CB();
1908 // see node2cell
1909 for (index iN = 0; iN < this->NumNode(); iN++)
1910 for (rowsize in2c = 0; in2c < node2cell[iN].size(); in2c++)
1912 }
1913 this->AdjLocal2GlobalPrimary();
1914 // see cell2cell
1915 for (index iC = 0; iC < this->NumCell(); iC++)
1916 for (rowsize ic2c = 0; ic2c < cell2cell[iC].size(); ic2c++)
1918 // see bnd2cell
1919 for (index iB = 0; iB < this->NumBnd(); iB++)
1920 for (rowsize ib2c = 0; ib2c < 2; ib2c++)
1922
1923 for (auto iCellGhost : cell2node.trans.pLGhostMapping->ghostIndex)
1924 cellsNonLocalFull.insert(iCellGhost); // ! should have no effect normally
1925
1926 cellOld2NewArr.trans.createFatherGlobalMapping();
1927 cellOld2NewArr.trans.createGhostMapping(std::vector<index>(cellsNonLocalFull.begin(), cellsNonLocalFull.end()));
1928 cellOld2NewArr.trans.createMPITypes();
1929 cellOld2NewArr.trans.pullOnce();
1930
1931 // Section C: Replace cell indices in RHS of xxx2cell arrays
1933 {
1934 if (iCellOld == UnInitIndex)
1935 return UnInitIndex;
1936 auto [ret, rank, val] = cellOld2NewArr.trans.pLGhostMapping->search_indexAppend(iCellOld);
1938 return cellOld2NewArr(val, 0);
1939 };
1940
1941 if (this->adjFacialState != Adj_Unknown && this->face2cell.isBuilt())
1942 for (index iFace = 0; iFace < this->NumFace(); iFace++)
1943 for (index &iCell : face2cell[iFace])
1945 if (this->adjN2CBState != Adj_Unknown && this->node2cell.isBuilt())
1946 for (index iNode = 0; iNode < this->NumNode(); iNode++)
1947 for (index &iCell : node2cell[iNode])
1949 for (index iCell = 0; iCell < this->NumCell(); iCell++)
1952 for (index iBnd = 0; iBnd < this->NumBnd(); iBnd++)
1953 for (index &iCell : bnd2cell[iBnd])
1955
1956 // Section D: Pull ghost data for xxx2cell
1957 if (this->adjFacialState != Adj_Unknown && this->face2cell.isBuilt())
1958 face2cell.trans.pullOnce();
1959 if (this->adjN2CBState != Adj_Unknown && this->node2cell.isBuilt())
1960 node2cell.trans.pullOnce();
1961 cell2cell.trans.pullOnce(); // should be unnecessary
1962 bnd2cell.trans.pullOnce();
1963
1964 // Section E: Permute LHS of cell2xxx arrays
1965 auto cellOld2NewLocal = [&](index iCell) -> index
1966 { return this->CellIndexGlobal2Local_NoSon(cellOld2NewArr(iCell, 0)); };
1967
1971 if (this->adjC2FState != Adj_Unknown && this->cell2face.isBuilt())
1973 if (this->isPeriodic)
1976
1977 // Section F: Rebuild ghost mappings with new cell indices
1978 { // cell2node
1979 std::vector<index> ghostCellGlobalsNew = cell2node.trans.pLGhostMapping->ghostIndex;
1982 cell2node.trans.createGhostMapping(ghostCellGlobalsNew);
1983 cell2node.trans.createMPITypes();
1984 cell2node.trans.pullOnce();
1985 }
1986 { // cell2cell
1987 cell2cell.trans.BorrowGGIndexing(cell2node.trans);
1988 cell2cell.trans.createMPITypes();
1989 cell2cell.trans.pullOnce();
1990 }
1991 { // cell2cellOrig
1992 cell2cellOrig.trans.BorrowGGIndexing(cell2node.trans);
1993 cell2cellOrig.trans.createMPITypes();
1994 cell2cellOrig.trans.pullOnce();
1995 }
1996 if (this->adjC2FState != Adj_Unknown && this->cell2face.isBuilt())
1997 { // cell2face
1998 cell2face.trans.BorrowGGIndexing(cell2node.trans);
1999 cell2face.trans.createMPITypes();
2000 cell2face.trans.pullOnce();
2001 }
2002 if (this->isPeriodic)
2003 { // cell2nodePbi
2004 cell2nodePbi.trans.BorrowGGIndexing(cell2node.trans);
2005 cell2nodePbi.trans.createMPITypes();
2006 cell2nodePbi.trans.pullOnce();
2007 }
2008 { // cellElemInfo
2009 cellElemInfo.trans.BorrowGGIndexing(cell2node.trans);
2010 cellElemInfo.trans.createMPITypes();
2011 cellElemInfo.trans.pullOnce();
2012 }
2013
2014 // Re-wire per-adjacency target mappings: ghost mappings were rebuilt
2015 // above with new cell indices, so the previously captured pointers are
2016 // stale. All xxx2cell adjacencies use cellGhostMap; xxx2bnd use
2017 // bndGhostMap; xxx2node use nodeGhostMap.
2018 {
2019 auto cellGhostMap = cellElemInfo.trans.pLGhostMapping;
2020 auto bndGhostMap = bndElemInfo.trans.pLGhostMapping;
2021
2022 cell2cell.idx.wireTargetMapping(cellGhostMap);
2023 bnd2cell.idx.wireTargetMapping(cellGhostMap);
2024 node2cell.idx.wireTargetMapping(cellGhostMap);
2025 face2cell.idx.wireTargetMapping(cellGhostMap);
2026 node2bnd.idx.wireTargetMapping(bndGhostMap);
2027 }
2028
2029 // Section G: Restore all adjacencies to local indices
2030 if (this->adjFacialState != Adj_Unknown && this->face2cell.isBuilt())
2031 {
2032 this->AdjGlobal2LocalFacial();
2033 }
2034 if (this->adjC2FState != Adj_Unknown && this->cell2face.isBuilt())
2035 {
2036 this->AdjGlobal2LocalC2F();
2037 }
2038 if (this->adjN2CBState != Adj_Unknown && this->node2cell.isBuilt())
2039 {
2040 this->AdjGlobal2LocalN2CB();
2041 }
2042 this->AdjGlobal2LocalPrimary();
2043 if (mpi.rank == mRank)
2044 log() << fmt::format("UnstructuredMesh === ReorderLocalCells finished") << std::endl;
2045 }
2046}
Reserved for template implementations of #DeviceStorage primitives.
Pre-compiled-header style shim that includes the heavy Eigen headers under DNDSR's warning suppressio...
#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
#define DNDS_check_throw(expr)
Runtime check active in both debug and release builds. Throws std::runtime_error if expr evaluates to...
Definition Errors.hpp:93
Layered DAG of mesh adjacency relations with composable DSL operations.
Thin free-function wrappers over MeshConnectivity DSL methods that assert per-adjacency state (AdjPai...
Helper for computing cell reordering permutations via Metis.
DNDS::index nFaces
Internal helpers for mesh partition redistribution.
Ghost-communication engine for a father / son ParArray pair.
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
CellPermutationResult ComputeCellPermutation(tLocalMatStruct &cell2cellFaceV, const tAdjPair &cell2cell, index nCell, int nParts, int nPartsInner)
void PushInfo2Serial2Global(std::vector< DNDS::index > &serial2Global, DNDS::index localSize, const std::vector< DNDS::index > &pushIndex, const std::vector< DNDS::index > &pushIndexStart, const DNDS::MPIInfo &mpi)
Reserved skeleton for parallel topology interpolation.
Definition Mesh.cpp:58
int32_t t_index
Definition Geometric.hpp:6
@ SerialOutput
Definition Mesh.hpp:1081
void Partition2LocalIdx(const std::vector< TPartitionIdx > &partition, std::vector< DNDS::index > &localPush, std::vector< DNDS::index > &localPushStart, const DNDS::MPIInfo &mpi)
void Partition2Serial2Global(const std::vector< TPartitionIdx > &partition, std::vector< DNDS::index > &serial2Global, const DNDS::MPIInfo &mpi, DNDS::MPI_int nPart)
DNDS_DEVICE_CALLABLE bool FaceIDIsInternal(t_index id)
Eigen::Vector3d tPoint
Definition Geometric.hpp:9
AdjPairTracked< ArrayAdjacencyPair< out_rs > > CheckedComposeFiltered(const AdjPairTracked< ArrayAdjacencyPair< rs_AB > > &AB, const AdjPairTracked< ArrayAdjacencyPair< rs_BC > > &BC, const std::unordered_map< index, index > &bGlobal2Local, Predicate &&pred, TArgs &&...args)
std::function< OwnershipDecision(const std::vector< index > &parents, const std::vector< MPI_int > &parentRanks, index nLocalParents)> OwnershipResolverMulti
DNDS_DEVICE_CALLABLE bool FaceIDIsPeriodic(t_index id)
tGPoint RotX(real theta)
AdjPairTracked< tAdjPair > CheckedInverse(const AdjPairTracked< ArrayAdjacencyPair< cone_rs > > &cone, const ToPair &toPair, index nToLocal, const MPIInfo &mpi)
tGPoint RotZ(real theta)
void TransferDataSerial2Global(TArr &arraySerial, TArr &arrayDist, const std::vector< DNDS::index > &pushIndex, const std::vector< DNDS::index > &pushIndexStart, const DNDS::MPIInfo &mpi)
void ConvertAdjSerial2Global(TAdj &arraySerialAdj, const std::vector< DNDS::index > &partitionJSerial2Global, const DNDS::MPIInfo &mpi)
DNDS_DEVICE_CALLABLE bool FaceIDIsPeriodic3(t_index id)
DNDS_DEVICE_CALLABLE bool FaceIDIsPeriodic2(t_index id)
std::string adjKindName(const AdjKind &kind)
Format an AdjKind as a diagnostic string, e.g. "Cell2Node", "Cell2Cell(Node)".
DNDS_DEVICE_CALLABLE bool FaceIDIsPeriodic1(t_index id)
tGPoint RotY(real theta)
MPI_int Scan(const void *sendbuf, void *recvbuf, MPI_int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
Wrapper over MPI_Scan (inclusive prefix reduction).
Definition MPI.cpp:219
MPI_int Allreduce(const void *sendbuf, void *recvbuf, MPI_int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
Wrapper over MPI_Allreduce.
Definition MPI.cpp:202
MPI_int Barrier(MPI_Comm comm)
Wrapper over MPI_Barrier.
Definition MPI.cpp:247
void AllreduceOneIndex(index &v, MPI_Op op, const MPIInfo &mpi)
Single-scalar Allreduce helper for indices (in-place, count = 1).
Definition MPI.hpp:727
ssp< SerializerBase > SerializerBaseSSP
the host side operators are provided as implemented
const MPI_Datatype DNDS_MPI_INDEX
MPI datatype matching index (= MPI_INT64_T).
Definition MPI.hpp:106
DNDS_CONSTANT const index UnInitIndex
Sentinel "not initialised" index value (= INT64_MIN).
Definition Defines.hpp:181
void MPISerialDo(const MPIInfo &mpi, F f)
Execute f on each rank serially, in rank order.
Definition MPI.hpp:749
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
index size_to_index(size_t v)
Range-checked conversion from size_t to DNDS::index.
Definition Defines.hpp:431
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
Definition Defines.hpp:112
ssp< T > make_ssp(Args &&...args)
Type-safe replacement for DNDS_MAKE_SSP. Creates ssp<T> with forwarded args.
Definition Defines.hpp:260
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
DNDS_DEVICE_CALLABLE index Size() const
Combined father + son row count.
Definition ArrayPair.hpp:33
t_arrayDeviceView father
Convenience bundle of a father, son, and attached ArrayTransformer.
void TransAttach()
Bind the transformer to the current father / son pointers.
ssp< TArray > father
Owned-side array (must be resized before ghost setup).
void WriteSerialize(Serializer::SerializerBaseSSP serializerP, const std::string &name, bool includePIG=true, bool includeSon=true)
Writes the ArrayPair (father, optional son, optional ghost mapping).
index Size() const
Combined row count (father->Size() + son->Size()).
void InitPair(const std::string &name, Args &&...args)
Allocate both father and son arrays, forwarding all args to TArray constructor.
ssp< TArray > son
Ghost-side array (sized automatically by createMPITypes / BorrowAndPull).
void BorrowAndPull(TPrimaryPair &primary)
Attach, borrow ghost indexing from a primary pair, create MPI types, and pull once.
void ReadSerialize(Serializer::SerializerBaseSSP serializerP, const std::string &name, bool includePIG=true, bool includeSon=true)
Reads an ArrayPair written by WriteSerialize (same partition count).
TTrans trans
Ghost-communication engine bound to father and son.
void ObtainSymmetricSymbolicFactorization(const TAdj &cell2cellFaceV, const std::vector< index > localPartStarts_in, int iluCode)
get symmetric symbolic matrix factorization over cell2cellFaceV
Definition Direct.hpp:83
static CompiledGhostTree compile(const GhostSpec &spec)
static MPI_Datatype CommType()
static GhostSpec defaultPrimary()
static MPI_Datatype CommType()
DNDS_HOST void WriteSerializer(Serializer::SerializerBaseSSP serializerP, const std::string &name)
std::array< tPointPortable, 4 > rotationCenter
std::array< tPointPortable, 4 > translation
std::array< tGPointPortable, 4 > rotation
DNDS_HOST void ReadSerializer(Serializer::SerializerBaseSSP serializerP, const std::string &name)
std::function< int(index iParent)> numSubEntities
Number of sub-entities for parent iParent.
AdjPairTrackedDeviceView< B, tAdjPair::t_arr > bnd2node
tCoordPair::t_deviceView< B > coords
reader
tElemInfoArrayPair::t_deviceView< B > cellElemInfo
tPbiPair::t_deviceView< B > cell2nodePbi
periodic only, after reader
AdjPairTrackedDeviceView< B, tAdj2Pair::t_arr > bnd2cell
tPbiPair::t_deviceView< B > bnd2nodePbi
tElemInfoArrayPair::t_deviceView< B > bndElemInfo
AdjPairTrackedDeviceView< B, tAdjPair::t_arr > cell2node
DNDS::ArrayTransformerType< tCoord::element_type >::Type coordSerialOutTrans
Definition Mesh.hpp:1168
DNDS::ArrayTransformerType< tElemInfoArray::element_type >::Type cellElemInfoSerialOutTrans
Definition Mesh.hpp:1172
void BuildSerialOut()
should be called to build data for serial out
Definition Mesh.cpp:193
DNDS::ArrayTransformerType< tAdj::element_type >::Type cell2nodeSerialOutTrans
Definition Mesh.hpp:1169
DNDS::ssp< UnstructuredMesh > mesh
Definition Mesh.hpp:1117
DNDS::ArrayTransformerType< tPbi::element_type >::Type cell2nodePbiSerialOutTrans
Definition Mesh.hpp:1170
std::vector< DNDS::MPI_int > cellPartition
Definition Mesh.hpp:1175
std::vector< DNDS::MPI_int > bndPartition
Definition Mesh.hpp:1177
std::vector< DNDS::MPI_int > nodePartition
Definition Mesh.hpp:1176
MeshAdjState adjC2FState
Definition Mesh.hpp:48
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
void fillRegistry(MeshConnectivity &dag) const
Populate a MeshConnectivity registry from this mesh's currently-built adjacencies.
Definition Mesh.cpp:1743
std::unordered_map< index, index > face2bndM
Definition Mesh.hpp:105
void ReorderLocalCellsLegacy(int nParts=1, int nPartsInner=1)
Legacy implementation preserved for reference/fallback.
Definition Mesh.cpp:1846
index NumNodeProc() const
Definition Mesh.hpp:643
index CellIndexLocal2Global_NoSon(index i)
Definition Mesh.hpp:330
tPbiPair face2nodePbi
periodic only, after interpolated
Definition Mesh.hpp:107
AdjPairTracked< tAdj1Pair > face2bnd
Definition Mesh.hpp:102
std::vector< index > node2bndNode
Definition Mesh.hpp:131
index NumNodeGhost() const
Definition Mesh.hpp:622
MeshAdjState adjC2CFaceState
Definition Mesh.hpp:52
std::vector< index > node2parentNode
parent built
Definition Mesh.hpp:130
AdjPairTracked< tAdj2Pair > face2cell
Definition Mesh.hpp:99
index NumCellProc() const
Definition Mesh.hpp:648
tLocalMatStruct cell2cellFaceVLocalParts
for cell local factorization
Definition Mesh.hpp:172
tCoordPair coords
reader
Definition Mesh.hpp:57
tLocalMatStruct GetCell2CellFaceVLocal(bool onLocalPartition=false)
void RecoverNode2CellAndNode2Bnd()
only requires father part of cell2node, bnd2node and coords generates node2cell and node2bnd (father ...
Definition Mesh.cpp:379
MeshAdjState adjN2CBState
Definition Mesh.hpp:50
AdjPairTracked< tAdjPair > cell2face
interpolated
Definition Mesh.hpp:97
void BuildGhostPrimary(int nGhostLayers=1)
building ghost (son) from primary (currently only cell2cell)
Definition Mesh.cpp:667
void ReadSerialize(Serializer::SerializerBaseSSP serializerP, const std::string &name)
Definition Mesh.cpp:1467
index CellIndexGlobal2Local_NoSon(index i)
Definition Mesh.hpp:331
AdjPairTracked< tAdjPair > cell2cell
Definition Mesh.hpp:61
std::vector< index > bnd2faceV
Definition Mesh.hpp:104
index CellIndexLocal2Global(DNDS::index i)
Definition Mesh.hpp:329
AdjPairTracked< tAdjPair > face2node
Definition Mesh.hpp:98
MeshAdjState adjPrimaryState
Definition Mesh.hpp:44
void ObtainSymmetricSymbolicFactorization(Direct::SerialSymLUStructure &symLU, Direct::DirectPrecControl control) const
Definition Mesh.cpp:1734
AdjPairTracked< tAdj1Pair > bnd2face
Definition Mesh.hpp:101
index CellFaceOther(index iCell, index iFace) const
Definition Mesh.hpp:845
tElemInfoArrayPair faceElemInfo
Definition Mesh.hpp:100
void SetPeriodicGeometry(const tPoint &translation1, const tPoint &rotationCenter1, const tPoint &eulerAngles1, const tPoint &translation2, const tPoint &rotationCenter2, const tPoint &eulerAngles2, const tPoint &translation3, const tPoint &rotationCenter3, const tPoint &eulerAngles3)
Definition Mesh.cpp:347
std::vector< index > localPartitionStarts
Definition Mesh.hpp:174
MeshAdjState adjFacialState
Definition Mesh.hpp:46
index BndIndexGlobal2Local(DNDS::index i)
Definition Mesh.hpp:336
AdjPairTracked< tAdjPair > cell2node
Definition Mesh.hpp:58
void RecoverCell2CellAndBnd2Cell()
needs to use RecoverNode2CellAndNode2Bnd before doing this. Requires node2cell.father and builds a ve...
Definition Mesh.cpp:422
void WriteSerialize(Serializer::SerializerBaseSSP serializerP, const std::string &name)
Definition Mesh.cpp:1429
tElemInfoArrayPair cellElemInfo
Definition Mesh.hpp:62
AdjPairTracked< tAdjPair > node2bnd
Definition Mesh.hpp:87
index NumCellGhost() const
Definition Mesh.hpp:627
static void PermuteRows(TPair &pair, index nRows, TFn &&old2new)
Permute the father rows of an ArrayPair according to a mapping function.
Definition Mesh.hpp:409
AdjPairTracked< tAdjPair > bnd2node
Definition Mesh.hpp:59
AdjPairTracked< tAdjPair > node2cell
inverse relations
Definition Mesh.hpp:86
void ConstructBndMesh(UnstructuredMesh &bMesh)
Definition Mesh.cpp:1560
tElemInfoArrayPair bndElemInfo
Definition Mesh.hpp:63
void EnsureGhostMapping(TPair &pair)
Ensure a pair has a ghost mapping on its transformer.
Definition Mesh.hpp:230
AdjPairTracked< tAdj2Pair > bnd2cell
Definition Mesh.hpp:60
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
Definition MPI.hpp:231
int size
Number of ranks in comm (-1 until initialised).
Definition MPI.hpp:237
int rank
This rank's 0-based index within comm (-1 until initialised).
Definition MPI.hpp:235
MPI_Comm comm
The underlying MPI communicator handle.
Definition MPI.hpp:233
Tag type for naming objects created via make_ssp.
Definition Defines.hpp:254
ArrayTransformer< DNDS::real, DynamicSize > trans
constexpr DNDS::index nLocal
tVec b(NCells)
std::vector< DNDS::index > ghostNodes(ghostNodeSet.begin(), ghostNodeSet.end())
std::unordered_map< DNDS::index, DNDS::index > nodeG2L
std::vector< GhostCell > ghostCells
solverDOF father pLGlobalMapping
const tPoint const tPoint const tPoint & p