9#define DOCTEST_CONFIG_IMPLEMENT
27TEST_CASE(
"Interpolate: 4-quad faces (2D)")
30 if (g_mpi.
rank != 0 && g_mpi.
size > 1)
45 auto m = makeHandCraftedMesh(g_mpi,
53 auto query = makeFaceQuery(
m.cellElemInfo);
64 CHECK(
res.parent2entity.father->RowSize(
i) == 4);
77 if (
res.entity2parent.father->RowSize(
i) >= 2)
88 auto vs = getEntityVertexSet(
res.entity2node,
res.entityElemInfo,
i);
97 CHECK(
res.entity2node.father->RowSize(
i) == 2);
111 if (
res.entity2parent.father->operator()(iEnt,
p) == iCell)
122TEST_CASE(
"Interpolate: 2-tri faces (2D)")
124 if (g_mpi.
rank != 0 && g_mpi.
size > 1)
139 auto m = makeHandCraftedMesh(g_mpi,
145 auto query = makeFaceQuery(
m.cellElemInfo);
153 if (
res.entity2parent.father->RowSize(
i) >= 2)
160 if (
res.entity2parent.father->RowSize(
i) >= 2)
162 auto vs = getEntityVertexSet(
res.entity2node,
res.entityElemInfo,
i);
163 CHECK(vs == std::set<DNDS::index>{1, 2});
172TEST_CASE(
"Interpolate: single tet faces (3D)")
174 if (g_mpi.
rank != 0 && g_mpi.
size > 1)
179 auto m = makeHandCraftedMesh(g_mpi,
184 auto query = makeFaceQuery(
m.cellElemInfo);
193 CHECK(
res.entity2node.father->RowSize(
i) == 3);
198 CHECK(
res.entity2parent.father->RowSize(
i) < 2);
202 {0, 1, 2}, {0, 1, 3}, {1, 2, 3}, {0, 2, 3}};
213TEST_CASE(
"Interpolate: two tets sharing a face (3D)")
215 if (g_mpi.
rank != 0 && g_mpi.
size > 1)
221 auto m = makeHandCraftedMesh(g_mpi,
227 auto query = makeFaceQuery(
m.cellElemInfo);
234 if (
res.entity2parent.father->RowSize(
i) >= 2)
241 if (
res.entity2parent.father->RowSize(
i) >= 2)
243 auto vs = getEntityVertexSet(
res.entity2node,
res.entityElemInfo,
i);
244 CHECK(vs == std::set<DNDS::index>{1, 2, 3});
253TEST_CASE(
"Interpolate: single hex faces (3D)")
255 if (g_mpi.
rank != 0 && g_mpi.
size > 1)
259 auto m = makeHandCraftedMesh(g_mpi,
264 auto query = makeFaceQuery(
m.cellElemInfo);
272 CHECK(
res.entity2node.father->RowSize(
i) == 4);
273 CHECK(
res.entity2parent.father->RowSize(
i) < 2);
278 {0, 1, 2, 3}, {0, 1, 4, 5}, {1, 2, 5, 6},
279 {2, 3, 6, 7}, {0, 3, 4, 7}, {4, 5, 6, 7}};
290TEST_CASE(
"Interpolate: single tet edges (3D)")
292 if (g_mpi.
rank != 0 && g_mpi.
size > 1)
296 auto m = makeHandCraftedMesh(g_mpi,
301 auto query = makeEdgeQuery(
m.cellElemInfo);
309 CHECK(
res.entity2node.father->RowSize(
i) == 2);
314 {0, 1}, {1, 2}, {0, 2}, {0, 3}, {1, 3}, {2, 3}};
318 std::set<DNDS::index> vs;
320 vs.insert(
res.entity2node.father->operator()(
i,
j));
330TEST_CASE(
"Interpolate: two-tet shared edges (3D)")
332 if (g_mpi.
rank != 0 && g_mpi.
size > 1)
340 auto m = makeHandCraftedMesh(g_mpi,
346 auto query = makeEdgeQuery(
m.cellElemInfo);
353 if (
res.entity2parent.father->RowSize(
i) >= 2)
362 if (
res.entity2parent.father->RowSize(
i) >= 2)
364 std::set<DNDS::index> vs;
366 vs.insert(
res.entity2node.father->operator()(
i,
j));
377TEST_CASE(
"Interpolate: mixed tri+quad faces (2D)")
379 if (g_mpi.
rank != 0 && g_mpi.
size > 1)
398 auto m = makeHandCraftedMesh(g_mpi,
405 auto query = makeFaceQuery(
m.cellElemInfo);
412 if (
res.entity2parent.father->RowSize(
i) >= 2)
425TEST_CASE(
"Regression: Interpolate matches InterpolateFace on UniformSquare_10")
428 auto mesh = std::make_shared<UnstructuredMesh>(g_mpi, 2);
442 mesh->RecoverNode2CellAndNode2Bnd();
443 mesh->RecoverCell2CellAndBnd2Cell();
444 mesh->BuildGhostPrimary();
446 mesh->AdjGlobal2LocalPrimary();
447 mesh->AdjGlobal2LocalN2CB();
450 mesh->InterpolateFace();
457 (
mesh->coords.son ?
mesh->coords.son->Size() : 0);
474 int nFaces = eCell.GetNumFaces();
477 std::set<std::set<DNDS::index>> legacyFaceSets;
481 auto eFace = eCell.ObtainFace(
j);
482 int nVerts = eFace.GetNumVertices();
483 std::set<DNDS::index> vs;
484 for (
int k = 0; k < nVerts; k++)
485 vs.insert(
mesh->face2node[iFace][k]);
486 legacyFaceSets.insert(vs);
490 std::set<std::set<DNDS::index>> dslFaceSets;
496 std::set<DNDS::index> vs;
497 for (
int k = 0; k < nVerts; k++)
498 vs.insert(
res.entity2node.father->operator()(iEnt, k));
499 dslFaceSets.insert(vs);
502 CHECK(dslFaceSets == legacyFaceSets);
528TEST_CASE(
"Interpolate: 2x2 periodic quad mesh — collaborating check required")
530 if (g_mpi.
rank != 0 && g_mpi.
size > 1)
533 auto pm = makePeriodic2x2Mesh(g_mpi);
537 auto faceQueryNoPbi = makeFaceQuery(
pm.cellElemInfo);
539 pm.cell2node, faceQueryNoPbi, 4, 4, g_mpi);
548 if (resNoPbi.entity2parent.father->RowSize(
i) > 2)
559 DNDS::index candidateParent,
int candidateSub) ->
bool
561 auto eParentA =
Elem::Element{
pm.cellElemInfo[iParent]->getElemType()};
565 std::vector<DNDS::index> nodesA(nFN), nodesB(nFN);
566 eParentA.ExtractFaceNodes(iSub,
pm.cell2node[iParent], nodesA);
567 auto eParentB =
Elem::Element{
pm.cellElemInfo[candidateParent]->getElemType()};
570 std::vector<NodePeriodicBits> pbiA(nFN), pbiB(nFN);
571 eParentA.ExtractFaceNodes(iSub,
pm.cell2nodePbi[iParent], pbiA);
572 eParentB.ExtractFaceNodes(candidateSub,
pm.cell2nodePbi[candidateParent], pbiB);
574 using P = std::pair<DNDS::index, NodePeriodicBits>;
575 auto cmp = [](
const P &L,
const P &R)
576 {
return L.first == R.first ? uint8_t(L.second) < uint8_t(R.second)
577 : L.first < R.first; };
579 std::vector<P> pA(nFN), pB(nFN);
580 for (
int i = 0;
i < nFN;
i++)
582 pA[
i] = {nodesA[
i], pbiA[
i]};
583 pB[
i] = {nodesB[
i], pbiB[
i]};
585 std::sort(pA.begin(), pA.end(), cmp);
586 std::sort(pB.begin(), pB.end(), cmp);
588 auto v0 = pA[0].second ^ pB[0].second;
589 for (
int i = 1;
i < nFN;
i++)
590 if ((pA[
i].second ^ pB[
i].second) != v0)
596 pm.cell2node, faceQueryPbi, 4, 4, g_mpi);
599 CHECK(resPbi.nEntities == 8);
604 if (resPbi.entity2parent.father->RowSize(
i) >= 2)
612 CHECK(resPbi.parent2entity.father->RowSize(
i) == 4);
618 for (
DNDS::rowsize j = 0;
j < resPbi.parent2entity.father->RowSize(iCell);
j++)
620 DNDS::index iEnt = resPbi.parent2entity.father->operator()(iCell,
j);
621 CAPTURE(iCell); CAPTURE(
j); CAPTURE(iEnt);
623 for (
DNDS::rowsize p = 0;
p < resPbi.entity2parent.father->RowSize(iEnt);
p++)
624 if (resPbi.entity2parent.father->operator()(iEnt,
p) == iCell)
635 for (
DNDS::rowsize q =
p + 1; q < resPbi.entity2parent.father->RowSize(
i); q++)
636 CHECK(resPbi.entity2parent.father->operator()(
i,
p) !=
637 resPbi.entity2parent.father->operator()(
i, q));
643 std::map<std::set<DNDS::index>,
int> pairCount;
646 REQUIRE(resPbi.entity2parent.father->RowSize(
i) >= 2);
647 auto p0 = resPbi.entity2parent.father->operator()(
i, 0);
648 auto p1 = resPbi.entity2parent.father->operator()(
i, 1);
649 pairCount[{p0, p1}]++;
651 CHECK(pairCount[{0, 1}] == 2);
652 CHECK(pairCount[{0, 2}] == 2);
653 CHECK(pairCount[{1, 3}] == 2);
654 CHECK(pairCount[{2, 3}] == 2);
655 CHECK(pairCount.count({0, 3}) == 0);
656 CHECK(pairCount.count({1, 2}) == 0);
668TEST_CASE(
"Periodic 2x2x2: face interpolation (3D, collaborating check)")
670 if (g_mpi.
rank != 0 && g_mpi.
size > 1)
673 auto pm = makePeriodic2x2x2Mesh(g_mpi);
677 auto q = makeFaceQuery(
pm.cellElemInfo);
683 if (
res.entity2parent.father->RowSize(
i) > 2)
690 auto q = makeFaceQuery(
pm.cellElemInfo);
691 q.matchExtra = makePeriodicMatchExtra(
pm.cellElemInfo,
pm.cell2node,
pm.cell2nodePbi,
false);
699 if (
res.entity2parent.father->RowSize(
i) >= 2)
705 CHECK(
res.entity2parent.father->RowSize(
i) <= 2);
711 CHECK(
res.parent2entity.father->RowSize(
i) == 6);
721 REQUIRE(
res.entity2parent.father->RowSize(
i) >= 2);
722 auto p0 =
res.entity2parent.father->operator()(
i, 0);
723 auto p1 =
res.entity2parent.father->operator()(
i, 1);
737 std::map<DNDS::index, std::set<DNDS::index>>
cellFN;
740 REQUIRE(
res.entity2parent.father->RowSize(
i) >= 2);
741 auto p0 =
res.entity2parent.father->operator()(
i, 0);
742 auto p1 =
res.entity2parent.father->operator()(
i, 1);
754TEST_CASE(
"Periodic 2x2x2: edge interpolation (3D, collaborating check)")
756 if (g_mpi.
rank != 0 && g_mpi.
size > 1)
759 auto pm = makePeriodic2x2x2Mesh(g_mpi);
763 auto q = makeEdgeQuery(
pm.cellElemInfo);
769 if (
res.entity2parent.father->RowSize(
i) > 2)
776 auto q = makeEdgeQuery(
pm.cellElemInfo);
777 q.matchExtra = makePeriodicMatchExtra(
pm.cellElemInfo,
pm.cell2node,
pm.cell2nodePbi,
true);
790 CHECK(
res.parent2entity.father->RowSize(
i) == 12);
798 CHECK(
res.entity2parent.father->RowSize(
i) == 4);
823TEST_CASE(
"InterpolateGlobal: 4x4x4 distributed hex faces (non-periodic)")
836 [&](
const std::vector<DNDS::index> &parents,
837 const std::vector<DNDS::MPI_int> &parentRanks,
840 DNDS::MPI_int minRank = *std::min_element(parentRanks.begin(), parentRanks.end());
841 bool anyLocal =
false;
842 for (
auto p : parents)
847 if (minRank != g_mpi.
rank)
849 std::vector<DNDS::MPI_int> peers;
850 for (
size_t i = 0;
i < parents.size();
i++)
851 if (parents[
i] >=
nLocal && parentRanks[
i] != g_mpi.
rank)
852 peers.push_back(parentRanks[
i]);
853 std::sort(peers.begin(), peers.end());
854 peers.erase(std::unique(peers.begin(), peers.end()), peers.end());
855 return {
true, std::move(peers)};
875 np *
N * (
N + 1) *
N +
876 np *
N *
N * (
N + 1);
890 auto pRow =
result.entity2parent.father->operator[](
i);
891 if (pRow.size() == 1)
893 else if (pRow.size() == 2)
896 FAIL(
"face has " << pRow.size() <<
" parents");
911 CHECK(
result.parent2entity.father->RowSize(iCell) == 6);
924TEST_CASE(
"InterpolateGlobal: 4x4x4 distributed hex faces (X-periodic)")
942 [&](
const std::vector<DNDS::index> &parents,
943 const std::vector<DNDS::MPI_int> &parentRanks,
946 DNDS::MPI_int minRank = *std::min_element(parentRanks.begin(), parentRanks.end());
947 bool anyLocal =
false;
948 for (
auto p : parents)
953 if (minRank != g_mpi.
rank)
955 std::vector<DNDS::MPI_int> peers;
956 for (
size_t i = 0;
i < parents.size();
i++)
957 if (parents[
i] >=
nLocal && parentRanks[
i] != g_mpi.
rank)
958 peers.push_back(parentRanks[
i]);
959 std::sort(peers.begin(), peers.end());
960 peers.erase(std::unique(peers.begin(), peers.end()), peers.end());
961 return {
true, std::move(peers)};
981 np *
N * (
N + 1) *
N +
982 np *
N *
N * (
N + 1);
990 auto pRow =
result.entity2parent.father->operator[](
i);
991 if (pRow.size() == 1)
1004 CHECK(
result.parent2entity.father->RowSize(iCell) == 6);
1013TEST_CASE(
"InterpolateGlobal: 4x4x4 distributed hex edges (non-periodic)")
1025 [&](
const std::vector<DNDS::index> &parents,
1026 const std::vector<DNDS::MPI_int> &parentRanks,
1029 DNDS::MPI_int minRank = *std::min_element(parentRanks.begin(), parentRanks.end());
1030 bool anyLocal =
false;
1031 for (
auto p : parents)
1036 if (minRank != g_mpi.
rank)
1038 std::vector<DNDS::MPI_int> peers;
1039 for (
size_t i = 0;
i < parents.size();
i++)
1040 if (parents[
i] >=
nLocal && parentRanks[
i] != g_mpi.
rank)
1041 peers.push_back(parentRanks[
i]);
1042 std::sort(peers.begin(), peers.end());
1043 peers.erase(std::unique(peers.begin(), peers.end()), peers.end());
1044 return {
true, std::move(peers)};
1064 (
np *
N + 1) *
N * (
N + 1) +
1065 (
np *
N + 1) * (
N + 1) *
N;
1079 auto pRow =
result.entity2parent.father->operator[](
i);
1081 CHECK(pRow.size() >= 1);
1082 CHECK(pRow.size() <= 4);
1089 CHECK(
result.parent2entity.father->RowSize(iCell) == 12);
1101TEST_CASE(
"InterpolateGlobal: 4x4x4 distributed hex edges (X-periodic)")
1116 [&](
const std::vector<DNDS::index> &parents,
1117 const std::vector<DNDS::MPI_int> &parentRanks,
1120 DNDS::MPI_int minRank = *std::min_element(parentRanks.begin(), parentRanks.end());
1121 bool anyLocal =
false;
1122 for (
auto p : parents)
1127 if (minRank != g_mpi.
rank)
1129 std::vector<DNDS::MPI_int> peers;
1130 for (
size_t i = 0;
i < parents.size();
i++)
1131 if (parents[
i] >=
nLocal && parentRanks[
i] != g_mpi.
rank)
1132 peers.push_back(parentRanks[
i]);
1133 std::sort(peers.begin(), peers.end());
1134 peers.erase(std::unique(peers.begin(), peers.end()), peers.end());
1135 return {
true, std::move(peers)};
1157 np *
N *
N * (
N + 1) +
1158 np *
N * (
N + 1) *
N;
1169 auto pRow =
result.entity2parent.father->operator[](
i);
1170 CHECK(pRow.size() >= 1);
1171 CHECK(pRow.size() <= 4);
1178 CHECK(
result.parent2entity.father->RowSize(iCell) == 12);
1187TEST_CASE(
"InterpolateGlobal: 4x4x4 distributed hex faces (triply-periodic)")
1205 auto cellGM = std::make_shared<GlobalOffsetsMapping>();
1207 auto nodeGM = std::make_shared<GlobalOffsetsMapping>();
1217 gx = ((gx % totalNx) + totalNx) % totalNx;
1218 gy = ((gy %
N) +
N) %
N;
1219 gz = ((gz %
N) +
N) %
N;
1222 return (*
nodeGM)(ownerRank, 0) + localIx *
N *
N + gy *
N + gz;
1231 if ((gx + di) >= totalNx || (gx + di) < 0)
1233 if ((gy + dj) >=
N || (gy + dj) < 0)
1235 if ((gz + dk) >=
N || (gz + dk) < 0)
1252 const int di8[8] = {0, 1, 1, 0, 0, 1, 1, 0};
1253 const int dj8[8] = {0, 0, 1, 1, 0, 0, 1, 1};
1254 const int dk8[8] = {0, 0, 0, 0, 1, 1, 1, 1};
1262 for (
int k = 0; k < 8; k++)
1286 gc.
global = (*cellGM)(srcRank, 0) + srcIx *
N *
N + iy *
N + iz;
1287 for (
int k = 0; k < 8; k++)
1305 for (
int k = 0; k < 8; k++)
1312 std::vector<DNDS::index> ghostNodeGlobals;
1317 ghostNodeGlobals.push_back(
ng);
1323 dummyNode.
trans.createFatherGlobalMapping();
1324 dummyNode.
trans.createGhostMapping(ghostNodeGlobals);
1356 for (
int k = 0; k < 8; k++)
1370 for (
rowsize k = 0; k < 8; k++)
1385 auto fA = eA.ObtainFace(iSub);
1386 int nFN = fA.GetNumNodes();
1387 std::vector<DNDS::index> nodesA(nFN), nodesB(nFN);
1388 eA.ExtractFaceNodes(iSub,
cell2node[iParent], nodesA);
1389 eB.ExtractFaceNodes(candidateSub,
cell2node[candidateParent], nodesB);
1390 std::vector<NodePeriodicBits> pbiA(nFN), pbiB(nFN);
1391 eA.ExtractFaceNodes(iSub,
cell2nodePbi[iParent], pbiA);
1392 eB.ExtractFaceNodes(candidateSub,
cell2nodePbi[candidateParent], pbiB);
1393 using P = std::pair<DNDS::index, uint8_t>;
1394 auto cmp = [](
const P &l,
const P &
r)
1395 {
return l.first ==
r.first ? l.second <
r.second : l.first <
r.first; };
1396 std::vector<P> pa(nFN), pb(nFN);
1397 for (
int i = 0;
i < nFN;
i++) { pa[
i] = {nodesA[
i], uint8_t(pbiA[
i])}; pb[
i] = {nodesB[
i], uint8_t(pbiB[
i])}; }
1398 std::sort(pa.begin(), pa.end(), cmp);
1399 std::sort(pb.begin(), pb.end(), cmp);
1400 uint8_t v0 = pa[0].second ^ pb[0].second;
1401 for (
int i = 1;
i < nFN;
i++)
1402 if ((pa[
i].second ^ pb[
i].second) != v0)
1411 auto f = e.ObtainFace(iSub);
1412 std::vector<NodePeriodicBits> pp(e.GetNumNodes());
1413 for (
int i = 0;
i < e.GetNumNodes();
i++) pp[
i] = pPbi(
i);
1414 std::vector<NodePeriodicBits> fp(f.GetNumNodes());
1415 e.ExtractFaceNodes(iSub, pp, fp);
1416 for (
int i = 0;
i < f.GetNumNodes();
i++) out[
i] = fp[
i];
1420 [&](
const std::vector<DNDS::index> &parents,
1421 const std::vector<DNDS::MPI_int> &parentRanks,
1424 DNDS::MPI_int minRank = *std::min_element(parentRanks.begin(), parentRanks.end());
1425 bool anyLocal =
false;
1426 for (
auto p : parents)
1428 if (!anyLocal)
return {
false, {}};
1429 if (minRank != g_mpi.
rank)
return {
false, {}};
1430 std::vector<DNDS::MPI_int> peers;
1431 for (
size_t i = 0;
i < parents.size();
i++)
1432 if (parents[
i] >=
nLocal && parentRanks[
i] != g_mpi.
rank)
1433 peers.push_back(parentRanks[
i]);
1434 std::sort(peers.begin(), peers.end());
1435 peers.erase(std::unique(peers.begin(), peers.end()), peers.end());
1436 return {
true, std::move(peers)};
1458 if (
result.entity2parent.father->RowSize(
i) != 2)
1468 CHECK(
result.parent2entity.father->RowSize(iCell) == 6);
1480 auto [pFound, pRank, parentLocal] =
cellGhostMapping->search_indexAppend(parentGlobal);
1489 auto faceNodeRow =
result.entity2node.father->operator[](iFace);
1490 std::set<DNDS::index> faceNodeGlobals;
1491 for (
auto gn : faceNodeRow)
1492 faceNodeGlobals.insert(gn);
1495 int nFaces = eParent.GetNumFaces();
1496 bool matched =
false;
1497 for (
int iSub = 0; iSub <
nFaces && !matched; iSub++)
1499 auto eFace = eParent.ObtainFace(iSub);
1500 int nFN = eFace.GetNumNodes();
1503 std::vector<DNDS::index> subNodes(nFN);
1504 std::vector<DNDS::index> parentNodes(8);
1505 for (
int k = 0; k < 8; k++)
1506 parentNodes[k] =
cell2node(parentLocal, k);
1507 eParent.ExtractFaceNodes(iSub, parentNodes, subNodes);
1509 std::set<DNDS::index> subNodeGlobals;
1510 for (
auto la : subNodes)
1513 if (subNodeGlobals != faceNodeGlobals)
1517 std::vector<NodePeriodicBits> parentPbiVec(8);
1518 for (
int k = 0; k < 8; k++)
1520 std::vector<NodePeriodicBits> expectedPbi(nFN);
1521 eParent.ExtractFaceNodes(iSub, parentPbiVec, expectedPbi);
1524 REQUIRE(
result.entity2nodePbi.father->RowSize(iFace) == nFN);
1525 for (
int j = 0;
j < nFN;
j++)
1527 auto stored =
result.entity2nodePbi.father->operator()(iFace,
j);
1528 if (!(stored == expectedPbi[
j]))
1561 auto faceFatherGM =
result.entity2node.trans.pLGlobalMapping;
1564 if (gFace < myFaceStart || gFace >= myFaceEnd)
1570 auto eFace = eCell.ObtainFace(
j);
1571 int nFN = eFace.GetNumNodes();
1572 std::vector<NodePeriodicBits> cellFacePbi(nFN);
1573 std::vector<DNDS::index> cellFaceNodes(nFN);
1575 std::vector<NodePeriodicBits> parentPbiVec(8);
1576 std::vector<DNDS::index> parentNodes(8);
1577 for (
int k = 0; k < 8; k++)
1582 eCell.ExtractFaceNodes(
j, parentPbiVec, cellFacePbi);
1583 eCell.ExtractFaceNodes(
j, parentNodes, cellFaceNodes);
1587 auto faceNodeRow =
result.entity2node.father->operator[](iFaceLocal);
1590 using NP = std::pair<DNDS::index, uint8_t>;
1591 auto cmp = [](
const NP &a,
const NP &
b)
1592 {
return a.first ==
b.first ? a.second <
b.second : a.first <
b.first; };
1593 std::vector<NP> cellNP(nFN), faceNP(nFN);
1594 for (
int k = 0; k < nFN; k++)
1597 uint8_t(cellFacePbi[k])};
1598 faceNP[k] = {faceNodeRow[k],
1599 uint8_t(
result.entity2nodePbi.father->operator()(iFaceLocal, k))};
1601 std::sort(cellNP.begin(), cellNP.end(), cmp);
1602 std::sort(faceNP.begin(), faceNP.end(), cmp);
1605 uint8_t expectedXor = cellNP[0].second ^ faceNP[0].second;
1606 bool uniform =
true;
1607 for (
int k = 1; k < nFN; k++)
1608 if ((cellNP[k].second ^ faceNP[k].second) != expectedXor)
1627 auto edA = eA.ObtainEdge(iSub);
1628 int nEN = edA.GetNumNodes();
1629 std::vector<DNDS::index> nodesA(nEN), nodesB(nEN);
1630 eA.ExtractEdgeNodes(iSub,
cell2node[iParent], nodesA);
1631 eB.ExtractEdgeNodes(candidateSub,
cell2node[candidateParent], nodesB);
1632 std::vector<NodePeriodicBits> pbiA(nEN), pbiB(nEN);
1633 eA.ExtractEdgeNodes(iSub,
cell2nodePbi[iParent], pbiA);
1634 eB.ExtractEdgeNodes(candidateSub,
cell2nodePbi[candidateParent], pbiB);
1635 using P = std::pair<DNDS::index, uint8_t>;
1636 auto cmp = [](
const P &l,
const P &
r)
1637 {
return l.first ==
r.first ? l.second <
r.second : l.first <
r.first; };
1638 std::vector<P> pa(nEN), pb(nEN);
1639 for (
int i = 0;
i < nEN;
i++) { pa[
i] = {nodesA[
i], uint8_t(pbiA[
i])}; pb[
i] = {nodesB[
i], uint8_t(pbiB[
i])}; }
1640 std::sort(pa.begin(), pa.end(), cmp);
1641 std::sort(pb.begin(), pb.end(), cmp);
1642 uint8_t v0 = pa[0].second ^ pb[0].second;
1643 for (
int i = 1;
i < nEN;
i++)
1644 if ((pa[
i].second ^ pb[
i].second) != v0)
1653 auto ed = e.ObtainEdge(iSub);
1654 std::vector<NodePeriodicBits> pp(e.GetNumNodes());
1655 for (
int i = 0;
i < e.GetNumNodes();
i++) pp[
i] = pPbi(
i);
1656 std::vector<NodePeriodicBits> ep(ed.GetNumNodes());
1657 e.ExtractEdgeNodes(iSub, pp, ep);
1658 for (
int i = 0;
i < ed.GetNumNodes();
i++) out[
i] = ep[
i];
1698 auto [pFound, pRank, parentLocal] =
cellGhostMapping->search_indexAppend(parentGlobal);
1701 auto edgeNodeRow =
edgeResult.entity2node.father->operator[](iEdge);
1702 std::set<DNDS::index> edgeNodeGlobals;
1703 for (
auto gn : edgeNodeRow)
1704 edgeNodeGlobals.insert(gn);
1707 int nEdges = eParent.GetNumEdges();
1708 bool matched =
false;
1709 for (
int iSub = 0; iSub < nEdges && !matched; iSub++)
1711 auto eEdge = eParent.ObtainEdge(iSub);
1712 int nEN = eEdge.GetNumNodes();
1714 std::vector<DNDS::index> subNodes(nEN);
1715 std::vector<DNDS::index> parentNodes(8);
1716 for (
int k = 0; k < 8; k++)
1717 parentNodes[k] =
cell2node(parentLocal, k);
1718 eParent.ExtractEdgeNodes(iSub, parentNodes, subNodes);
1720 std::set<DNDS::index> subNodeGlobals;
1721 for (
auto la : subNodes)
1724 if (subNodeGlobals != edgeNodeGlobals)
1727 std::vector<NodePeriodicBits> parentPbiVec(8);
1728 for (
int k = 0; k < 8; k++)
1730 std::vector<NodePeriodicBits> expectedPbi(nEN);
1731 eParent.ExtractEdgeNodes(iSub, parentPbiVec, expectedPbi);
1734 for (
int j = 0;
j < nEN;
j++)
1736 auto stored =
edgeResult.entity2nodePbi.father->operator()(iEdge,
j);
1737 if (!(stored == expectedPbi[
j]))
1757 uint8_t
v = uint8_t(
result.parent2entityPbi.father->operator()(iCell,
j));
1776 nodeGM->search(nodeGlobalIdx,
r,
v);
1777 DNDS::index localIdx = nodeGlobalIdx - (*nodeGM)(
r, 0);
1781 return tPoint(
double(
r *
N + ix),
double(iy),
double(iz));
1786 if (pbi.getP1()) ret(0) += double(
np *
N);
1787 if (pbi.getP2()) ret(1) += double(
N);
1788 if (pbi.getP3()) ret(2) += double(
N);
1802 auto faceFatherGM =
result.entity2node.trans.pLGlobalMapping;
1805 if (gFace < myFaceStart || gFace >= myFaceEnd)
continue;
1811 auto eFace = eCell.ObtainFace(
j);
1812 int nFN = eFace.GetNumNodes();
1813 std::vector<DNDS::index> cellFaceNodes(nFN);
1814 std::vector<NodePeriodicBits> cellFacePbiVec(nFN);
1816 std::vector<DNDS::index> pN(8);
1817 std::vector<NodePeriodicBits> pP(8);
1818 for (
int k = 0; k < 8; k++)
1823 eCell.ExtractFaceNodes(
j, pN, cellFaceNodes);
1824 eCell.ExtractFaceNodes(
j, pP, cellFacePbiVec);
1826 tPoint centerCell = tPoint::Zero();
1827 for (
int k = 0; k < nFN; k++)
1832 centerCell /= double(nFN);
1835 auto faceNodeRow =
result.entity2node.father->operator[](iFaceLocal);
1836 tPoint centerEntity = tPoint::Zero();
1837 for (
rowsize k = 0; k < static_cast<rowsize>(faceNodeRow.size()); k++)
1842 centerEntity /= double(faceNodeRow.size());
1846 if ((centerCell - centerConverted).norm() > 1e-12)
1865 auto edgeFatherGM =
edgeResult.entity2node.trans.pLGlobalMapping;
1868 if (gEdge < myEdgeStart || gEdge >= myEdgeEnd)
continue;
1874 auto eEdge = eCell.ObtainEdge(
j);
1875 int nEN = eEdge.GetNumNodes();
1876 std::vector<DNDS::index> cellEdgeNodes(nEN);
1877 std::vector<NodePeriodicBits> cellEdgePbiVec(nEN);
1879 std::vector<DNDS::index> pN(8);
1880 std::vector<NodePeriodicBits> pP(8);
1881 for (
int k = 0; k < 8; k++)
1886 eCell.ExtractEdgeNodes(
j, pN, cellEdgeNodes);
1887 eCell.ExtractEdgeNodes(
j, pP, cellEdgePbiVec);
1889 tPoint centerCell = tPoint::Zero();
1890 for (
int k = 0; k < nEN; k++)
1895 centerCell /= double(nEN);
1898 auto edgeNodeRow =
edgeResult.entity2node.father->operator[](iEdgeLocal);
1899 tPoint centerEntity = tPoint::Zero();
1900 for (
rowsize k = 0; k < static_cast<rowsize>(edgeNodeRow.size()); k++)
1905 centerEntity /= double(edgeNodeRow.size());
1908 if ((centerCell - centerConverted).norm() > 1e-12)
1922 MPI_Init(&argc, &argv);
1925 doctest::Context ctx;
1926 ctx.applyCommandLine(argc, argv);
1927 int res = ctx.run();
#define DNDS_assert(expr)
Debug-only assertion (compiled out when DNDS_NDEBUG is defined). Prints the expression + file/line + ...
Layered DAG of mesh adjacency relations with composable DSL operations.
Shared synthetic mesh builders for MeshConnectivity unit tests.
std::function< OwnershipDecision(const std::vector< index > &parents, const std::vector< MPI_int > &parentRanks, index nLocalParents)> OwnershipResolverMulti
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).
int MPI_int
MPI counterpart type for MPI_int (= C int). Used for counts and ranks in MPI calls.
DNDS_DEVICE_CALLABLE index Size() const
Combined father + son row count.
void TransAttach()
Bind the transformer to the current father / son pointers.
ssp< TArray > father
Owned-side array (must be resized before ghost setup).
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).
TTrans trans
Ghost-communication engine bound to father and son.
DNDS_DEVICE_CALLABLE constexpr Element ObtainFace(t_index iFace) const
DNDS_DEVICE_CALLABLE constexpr t_index GetNumVertices() const
void ExtractFaceNodes(t_index iFace, const TIn &nodes, TOut &faceNodesOut)
DNDS_DEVICE_CALLABLE constexpr t_index GetNumNodes() const
static InterpolateResult Interpolate(const ArrayAdjacencyPair< p2n_rs > &parent2node, const SubEntityQuery &query, index nParent, index nNode, const MPIInfo &mpi)
static InterpolateGlobalResultT< e2p_rs > InterpolateGlobal(const ArrayAdjacencyPair< p2n_rs > &parent2node, const tPbiPair &parent2nodePbi, const OffsetAscendIndexMapping &parentGhostMapping, const GlobalOffsetsMapping &parentGlobalMapping, const OffsetAscendIndexMapping &nodeGhostMapping, const SubEntityQueryPbi &query, index nLocalParents, index nTotalParents, index nNode, const OwnershipResolverMulti &resolver, const MPIInfo &mpi)
static MPI_Datatype CommType()
std::function< bool(index iParent, int iSub, index iCandEntity, index candidateParent, int candidateSub)> matchExtra
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.
void build(DNDS::index tileN, bool isPeriodic, const MPIInfo &mpi)
tElemInfoArrayPair cellElemInfo
ssp< OffsetAscendIndexMapping > nodeGhostMapping
ssp< GlobalOffsetsMapping > cellGM
ssp< OffsetAscendIndexMapping > cellGhostMapping
std::array< NodePeriodicBits, 8 > pbi
std::array< DNDS::index, 8 > nodes
Eigen::Matrix< real, 5, 1 > v
if(g_mpi.rank==0) std auto[rhsDensity, rhsEnergy, incL2]
std::set< std::set< DNDS::index > > expectedEdges
std::vector< DNDS::index > ghostGlobals
std::set< std::set< DNDS::index > > actualShared
j edgeRefCount[res.parent2entity.father->operator()(iCell, j)]
std::set< std::set< DNDS::index > > expectedShared
std::vector< GhostCell > ghostCells
MPI_Allreduce & nMultiBit
const DNDS::index nCellLocal
REQUIRE(bool(result.parent2entityPbi.father))
DNDS::index edgeGlobalOwned
std::set< std::set< DNDS::index > > actualFaces
DNDS::index globalMultiBit
std::set< DNDS::index > allNodeGlobals
DNDS::index edgeLocalOwned
tElemInfoArrayPair cellElemInfo
std::map< DNDS::index, std::set< DNDS::index > > cellFN
for(DNDS::index i=0;i< 4;i++)
const DNDS::index nNodeLocal
std::set< std::set< DNDS::index > > allVertSets
std::set< std::set< DNDS::index > > actualEdges
std::set< std::set< DNDS::index > > expectedFaces
OwnershipResolverMulti resolver
input explicitMaps push_back(EntityReorderMap{EntityKind::Cell, cellPartition})
TEST_CASE("3D: VFV P2 HQM error < P1 on sinCos3D")
const tPoint const tPoint const tPoint & p