7#define DOCTEST_CONFIG_IMPLEMENT
17#include <unordered_set>
35 auto c2n = make4QuadCell2Node(g_mpi);
36 c2n.father->createGlobalMapping();
37 auto nodeGM = makeNodeGlobalMapping4Quad(g_mpi);
43 {
return cellLocal2Global4Quad(g_mpi,
i); },
45 {
return nodeLocal2Global4Quad(g_mpi,
i); },
49 auto globalN2C = gatherInverseGlobal(
52 {
return nodeLocal2Global4Quad(g_mpi,
i); },
65 std::vector<std::set<DNDS::index>>
expected = {
84TEST_CASE(
"Inverse: round-trip covers original cell2node")
91 auto c2n = make4QuadCell2Node(g_mpi);
92 c2n.father->createGlobalMapping();
93 auto nodeGM = makeNodeGlobalMapping4Quad(g_mpi);
101 {
return cellLocal2Global4Quad(g_mpi,
i); },
103 {
return nodeLocal2Global4Quad(g_mpi,
i); },
107 auto cellGM = std::make_shared<GlobalOffsetsMapping>();
115 {
return nodeLocal2Global4Quad(g_mpi,
i); },
117 {
return cellLocal2Global4Quad(g_mpi,
i); },
124 std::set<DNDS::index> originalNodes;
125 for (
auto n : c2n.father->operator[](iCell))
126 originalNodes.insert(
n);
128 std::set<DNDS::index> roundTripNodes;
129 for (
auto n : c2n_rt.father->operator[](iCell))
130 roundTripNodes.insert(
n);
132 for (
auto n : originalNodes)
136 CHECK(roundTripNodes.count(
n) == 1);
150TEST_CASE(
"ComposeFiltered: 4-quad cell2cell via node-neighbor")
152 auto c2n = make4QuadCell2Node(g_mpi);
153 c2n.father->createGlobalMapping();
154 auto nodeGM = makeNodeGlobalMapping4Quad(g_mpi);
163 {
return cellLocal2Global4Quad(g_mpi,
i); },
165 {
return nodeLocal2Global4Quad(g_mpi,
i); },
172 for (
auto iNode : c2n.father->operator[](iCell))
174 auto [ret, rank, val] =
nodeGM->search(iNode);
175 if (rank != g_mpi.
rank)
182 n2c.trans.createFatherGlobalMapping();
184 n2c.trans.createMPITypes();
185 n2c.trans.pullOnce();
188 std::unordered_map<DNDS::index, DNDS::index>
nodeG2L;
190 nodeG2L[
n2c.trans.pLGhostMapping->operator()(-1,
i)] =
i;
196 {
return cellLocal2Global4Quad(g_mpi,
i); },
205 auto cellGM = std::make_shared<GlobalOffsetsMapping>();
209 std::vector<std::set<DNDS::index>> globalC2C(4);
213 for (
auto v : c2c.father->operator[](
i))
214 globalC2C[cG].insert(
v);
220 std::vector<DNDS::index> localVec(globalC2C[c].begin(), globalC2C[c].end());
221 int localSize =
static_cast<int>(localVec.size());
222 std::vector<int> sizes(g_mpi.
size);
223 MPI_Allgather(&localSize, 1, MPI_INT, sizes.data(), 1, MPI_INT, g_mpi.
comm);
224 std::vector<int> disps(g_mpi.
size + 1, 0);
225 for (
int r = 0;
r < g_mpi.
size;
r++)
226 disps[
r + 1] = disps[
r] + sizes[
r];
227 std::vector<DNDS::index> allVec(disps[g_mpi.
size]);
231 globalC2C[c].clear();
232 for (
auto v : allVec)
233 globalC2C[c].insert(
v);
240 CHECK(globalC2C[c].size() == 3);
243 CHECK(globalC2C[c].count(other) == 1);
247TEST_CASE(
"ComposeFiltered: face-share filter (minShared=dim)")
256 auto c2n = make4QuadCell2Node(g_mpi);
257 c2n.father->createGlobalMapping();
258 auto nodeGM = makeNodeGlobalMapping4Quad(g_mpi);
266 {
return cellLocal2Global4Quad(g_mpi,
i); },
268 {
return nodeLocal2Global4Quad(g_mpi,
i); },
274 for (
auto iNode : c2n.
father->operator[](iCell))
276 auto [ret, rank, val] =
nodeGM->search(iNode);
277 if (rank != g_mpi.
rank)
284 n2c.trans.createFatherGlobalMapping();
286 n2c.trans.createMPITypes();
289 std::unordered_map<DNDS::index, DNDS::index>
nodeG2L;
291 nodeG2L[
n2c.trans.pLGhostMapping->operator()(-1,
i)] =
i;
297 {
return cellLocal2Global4Quad(g_mpi,
i); },
311 int localSize =
static_cast<int>(localVec.size());
312 std::vector<int> sizes(g_mpi.
size);
313 MPI_Allgather(&localSize, 1, MPI_INT, sizes.data(), 1, MPI_INT, g_mpi.
comm);
314 std::vector<int> disps(g_mpi.
size + 1, 0);
315 for (
int r = 0;
r < g_mpi.
size;
r++)
316 disps[
r + 1] = disps[
r] + sizes[
r];
317 std::vector<DNDS::index> allVec(disps[g_mpi.
size]);
322 for (
auto v : allVec)
356TEST_CASE(
"Regression: Inverse matches RecoverNode2CellAndNode2Bnd on UniformSquare_10")
359 auto mesh = std::make_shared<UnstructuredMesh>(g_mpi, 2);
374 mesh->RecoverNode2CellAndNode2Bnd();
378 std::vector<std::set<DNDS::index>> legacyN2C(
nNodeLocal);
380 for (
auto v :
mesh->node2cell.father->operator[](
i))
381 legacyN2C[
i].insert(
v);
384 if (!
mesh->coords.father->pLGlobalMapping)
385 mesh->coords.father->createGlobalMapping();
392 { return mesh->CellIndexLocal2Global_NoSon(i); },
394 { return mesh->NodeIndexLocal2Global_NoSon(i); },
395 mesh->coords.father->pLGlobalMapping);
400 std::set<DNDS::index> dslSet;
401 for (
auto v : dslN2C.father->operator[](
i))
404 CHECK(dslSet == legacyN2C[
i]);
408TEST_CASE(
"Regression: ComposeFiltered matches RecoverCell2CellAndBnd2Cell on UniformSquare_10")
411 auto mesh = std::make_shared<UnstructuredMesh>(g_mpi, 2);
425 mesh->RecoverNode2CellAndNode2Bnd();
426 mesh->RecoverCell2CellAndBnd2Cell();
430 std::vector<std::set<DNDS::index>> legacyC2C(
nCellLocal);
432 for (
auto v :
mesh->cell2cell.father->operator[](
i))
433 legacyC2C[
i].insert(
v);
437 if (!
mesh->coords.father->pLGlobalMapping)
438 mesh->coords.father->createGlobalMapping();
447 { return mesh->CellIndexLocal2Global_NoSon(i); },
449 { return mesh->NodeIndexLocal2Global_NoSon(i); },
450 mesh->coords.father->pLGlobalMapping);
457 auto [ret, rank, val] =
mesh->coords.father->pLGlobalMapping->search(iNode);
458 if (rank != g_mpi.
rank)
463 dslN2C.son =
make_ssp<
decltype(dslN2C.son)::element_type>(
ObjName{
"dslN2C.son"}, g_mpi);
464 dslN2C.TransAttach();
465 dslN2C.trans.createFatherGlobalMapping();
467 dslN2C.trans.createMPITypes();
468 dslN2C.trans.pullOnce();
471 std::unordered_map<DNDS::index, DNDS::index>
nodeG2L;
473 nodeG2L[dslN2C.trans.pLGhostMapping->operator()(-1,
i)] =
i;
484 { return mesh->CellIndexLocal2Global_NoSon(i); },
490 std::set<DNDS::index> dslSet;
491 for (
auto v : dslC2C.father->operator[](
i))
494 CHECK(dslSet == legacyC2C[
i]);
515 CHECK(c.fromDepth == 2);
516 CHECK(c.toDepth == 0);
536 CHECK(s.fromDepth == 0);
537 CHECK(s.toDepth == 2);
555 cone.
adj = makeAdjVariant<tAdjPair>();
557 CHECK(std::holds_alternative<tAdjPair>(*cone.
adj));
564 cone2.
adj = makeAdjVariant<tAdj2Pair>();
565 CHECK(std::holds_alternative<tAdj2Pair>(*cone2.
adj));
601 f2c.
father->operator()(0, 0) = 0;
604 f2c.
father->operator()(1, 0) = 0;
605 f2c.
father->operator()(1, 1) = 1;
607 f2c.
father->operator()(2, 0) = 0;
609 f2c.
father->operator()(3, 0) = 1;
611 f2c.
father->operator()(4, 0) = 1;
615 auto cellGM = std::make_shared<GlobalOffsetsMapping>();
616 cellGM->setMPIAlignBcast(g_mpi, nCells);
619 auto c2f = MeshConnectivity::Inverse<2>(
624 if (g_mpi.
rank == 0)
return i;
630 if (g_mpi.
rank == 0)
return i;
638 CHECK(c2f.father->Size() == 2);
640 std::set<DNDS::index> c0faces, c1faces;
641 for (
auto f : c2f.father->operator[](0))
643 for (
auto f : c2f.father->operator[](1))
646 CHECK(c0faces == std::set<DNDS::index>{0, 1, 2});
647 CHECK(c1faces == std::set<DNDS::index>{1, 3, 4});
651TEST_CASE(
"narrowAdjToFixed: variable-to-fixed-2 conversion")
657 source.
father->ResizeRow(0, 2);
658 source.
father->operator()(0, 0) = 10;
659 source.
father->operator()(0, 1) = 20;
660 source.
father->ResizeRow(1, 1);
661 source.
father->operator()(1, 0) = 30;
662 source.
father->ResizeRow(2, 2);
663 source.
father->operator()(2, 0) = 40;
664 source.
father->operator()(2, 1) = 50;
667 target.InitPair(
"target", g_mpi);
668 target.father->Resize(3);
670 narrowAdjToFixed<2>(source, target, 3);
672 CHECK(target.father->operator()(0, 0) == 10);
673 CHECK(target.father->operator()(0, 1) == 20);
674 CHECK(target.father->operator()(1, 0) == 30);
676 CHECK(target.father->operator()(2, 0) == 40);
677 CHECK(target.father->operator()(2, 1) == 50);
684TEST_CASE(
"Periodic 2x2x2: ComposeFiltered cell2cellFace is WRONG without pbi filter")
692 c2n.
InitPair(
"p2x2x2_c2n_compose", g_mpi);
693 c2n.
father->Resize(nCells);
697 auto pm = makePeriodic2x2x2Mesh(g_mpi);
707 c2n.
father->createGlobalMapping();
708 auto nodeGM = std::make_shared<GlobalOffsetsMapping>();
709 nodeGM->setMPIAlignBcast(g_mpi, nNodes);
719 n2c.trans.createFatherGlobalMapping();
720 std::vector<DNDS::index> emptyGhost;
721 n2c.trans.createGhostMapping(emptyGhost);
722 n2c.trans.createMPITypes();
723 n2c.trans.pullOnce();
725 std::unordered_map<DNDS::index, DNDS::index>
nodeG2L;
727 nodeG2L[
n2c.trans.pLGhostMapping->operator()(-1,
i)] =
i;
741 CHECK(c2cFace.father->RowSize(
i) == 7);
760makePbiContainmentMatchExtra(
765 const std::unordered_map<DNDS::index, DNDS::index> &cGlobal2Local)
768 const std::vector<DNDS::index> & ) ->
bool
770 auto itC = cGlobal2Local.find(cGlobal);
771 if (itC == cGlobal2Local.end())
780 uint8_t pbiA = uint8_t(a2nodePbi.
father->operator()(aLocal, ia));
782 auto cRow = c2node[cLocal];
785 if (cRow[ic] == nodeA && uint8_t(c2nodePbi(cLocal, ic)) == pbiA)
812 MPI_Init(&argc, &argv);
815 doctest::Context ctx;
816 ctx.applyCommandLine(argc, argv);
Layered DAG of mesh adjacency relations with composable DSL operations.
Shared synthetic mesh builders for MeshConnectivity unit tests.
the host side operators are provided as implemented
const MPI_Datatype DNDS_MPI_INDEX
MPI datatype matching index (= MPI_INT64_T).
DNDS_CONSTANT const index UnInitIndex
Sentinel "not initialised" index value (= INT64_MIN).
int32_t rowsize
Row-width / per-row element-count type (signed 32-bit).
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
ssp< T > make_ssp(Args &&...args)
Type-safe replacement for DNDS_MAKE_SSP. Creates ssp<T> with forwarded args.
DNDS_DEVICE_CALLABLE auto RowSize() const
Uniform row width (delegates to father; father/son share it).
DNDS_DEVICE_CALLABLE index Size() const
Combined father + son row count.
Convenience bundle of a father, son, and attached ArrayTransformer.
ssp< TArray > father
Owned-side array (must be resized before ghost setup).
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).
bool initialized() const
Check if the adjacency pair is initialized (father is non-null).
int toDepth
Target stratum (e.g., 0 for nodes, dim-1 for faces)
int fromDepth
Source stratum (e.g., dim for cells, dim-1 for faces)
ssp< AdjVariant > adj
Shared adjacency pair (typed by row width)
bool hasPbi() const
Check if pbi is attached (only valid for toDepth==0).
ConeAdj & addCone(int fromDepth, int toDepth)
std::vector< SupportAdj > supports
ConeAdj * findCone(int fromDepth, int toDepth)
Find a cone by (fromDepth, toDepth). Returns nullptr if not found.
static tAdjPair Inverse(const ArrayAdjacencyPair< cone_rs > &cone, index nToLocal, const MPIInfo &mpi, const std::function< index(index)> &fromLocal2Global, const std::function< index(index)> &toLocal2Global, const ssp< GlobalOffsetsMapping > &toGlobalMapping)
std::vector< ConeAdj > cones
bool hasSupport(int fromDepth, int toDepth) const
SupportAdj & addSupport(int fromDepth, int toDepth)
static ArrayAdjacencyPair< out_rs > ComposeFiltered(const ArrayAdjacencyPair< rs_AB > &AB, const ArrayAdjacencyPair< rs_BC > &BC, index nALocal, const std::unordered_map< index, index > &bGlobal2Local, const std::function< index(index)> &aLocal2Global, Predicate &&pred, const std::function< bool(index aLocal, index cGlobal, const std::vector< index > &sharedBGlobals)> &matchExtra=nullptr)
bool hasCone(int fromDepth, int toDepth) const
SupportAdj * findSupport(int fromDepth, int toDepth)
Find a support by (fromDepth, toDepth). Returns nullptr if not found.
int toDepth
Target stratum (e.g., dim for cells)
int fromDepth
Source stratum (e.g., 0 for nodes)
void MeshPartitionCell2Cell(const PartitionOptions &options)
void ReadFromCGNSSerial(const std::string &fName, const t_FBCName_2_ID &FBCName_2_ID)
reads a cgns file as serial input
void PartitionReorderToMeshCell2Cell()
void Deduplicate1to1Periodic(real search_eps=1e-8)
void BuildCell2Cell()
build cell2cell topology, with node-neighbors included
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
int size
Number of ranks in comm (-1 until initialised).
int rank
This rank's 0-based index within comm (-1 until initialised).
MPI_Comm comm
The underlying MPI communicator handle.
void setWorld()
Initialise the object to MPI_COMM_WORLD. Requires MPI_Init to have run.
Tag type for naming objects created via make_ssp.
Eigen::Matrix< real, 5, 1 > v
std::vector< std::set< DNDS::index > > expected
std::vector< DNDS::index > ghostNodes(ghostNodeSet.begin(), ghostNodeSet.end())
std::vector< std::set< DNDS::index > > globalC2CFace(4)
std::unordered_map< DNDS::index, DNDS::index > nodeG2L
std::unordered_set< DNDS::index > ghostNodeSet
REQUIRE(bool(result.parent2entityPbi.father))
TEST_CASE("3D: VFV P2 HQM error < P1 on sinCos3D")
Eigen::Vector3d n(1.0, 0.0, 0.0)