86#define DOCTEST_CONFIG_IMPLEMENT
114 {
"UniformSquare_10",
"UniformSquare_10.cgns", 2,
false,
115 {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, 100, 40},
116 {
"IV10_10",
"IV10_10.cgns", 2,
true,
117 {10, 0, 0}, {0, 10, 0}, {0, 0, 10}, 100, -1},
118 {
"IV10U_10",
"IV10U_10.cgns", 2,
true,
119 {10, 0, 0}, {0, 10, 0}, {0, 0, 10}, 322, -1},
120 {
"NACA0012_H2",
"NACA0012_H2.cgns", 2,
false,
121 {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, 20816, 484},
123static constexpr int N_CONFIGS =
sizeof(g_configs) /
sizeof(g_configs[0]);
141static bool g_crossNpAvailable =
false;
144static std::string g_h5Same[N_CONFIGS];
145static std::string g_h5Cross[N_CONFIGS];
150static std::string meshPath(
const std::string &name)
152 std::string f(__FILE__);
153 for (
int i = 0;
i < 4;
i++)
155 auto pos = f.rfind(
'/');
156 if (pos == std::string::npos)
158 if (pos != std::string::npos)
159 f = f.substr(0, pos);
161 return f +
"/data/mesh/" + name;
164static std::string tmpDir()
166 std::string f(__FILE__);
167 for (
int i = 0;
i < 4;
i++)
169 auto pos = f.rfind(
'/');
170 if (pos == std::string::npos)
172 if (pos != std::string::npos)
173 f = f.substr(0, pos);
177 return f +
"/data/tmp_test_distributed_read_np" + std::to_string(g_mpi.
size);
185 mesh->SetPeriodicGeometry(
195 mesh->RecoverNode2CellAndNode2Bnd();
196 mesh->RecoverCell2CellAndBnd2Cell();
197 mesh->BuildGhostPrimary();
198 mesh->AdjGlobal2LocalPrimary();
199 mesh->AdjGlobal2LocalN2CB();
201 mesh->InterpolateFace();
202 mesh->AssertOnFaces();
204 mesh->AdjLocal2GlobalN2CB();
205 mesh->BuildGhostN2CB();
206 mesh->AdjGlobal2LocalN2CB();
209 mesh->RecreatePeriodicNodes();
210 mesh->BuildVTKConnectivity();
214static void buildAndWriteRef(
217 auto mesh = std::make_shared<UnstructuredMesh>(mpi, cfg.
dim);
219 setPeriodicIfNeeded(
mesh, cfg);
221 reader.ReadFromCGNSSerial(meshPath(cfg.
file));
222 reader.Deduplicate1to1Periodic(1e-8);
223 reader.BuildCell2Cell();
230 reader.MeshPartitionCell2Cell(pOpt);
231 reader.PartitionReorderToMeshCell2Cell();
233 mesh->RecoverNode2CellAndNode2Bnd();
234 mesh->RecoverCell2CellAndBnd2Cell();
235 mesh->BuildGhostPrimary();
236 mesh->AdjGlobal2LocalPrimary();
237 mesh->AdjGlobal2LocalN2CB();
239 mesh->InterpolateFace();
240 mesh->AssertOnFaces();
242 mesh->AdjLocal2GlobalN2CB();
243 mesh->BuildGhostN2CB();
244 mesh->AdjGlobal2LocalN2CB();
253 mesh->AdjLocal2GlobalPrimary();
254 auto ser = std::make_shared<Serializer::SerializerH5>(mpi);
255 ser->OpenFile(h5Path,
false);
256 mesh->WriteSerialize(ser,
"meshPart");
262static void buildAndWriteRefSubComm(
263 int ic,
const MeshConfig &cfg,
int npWrite,
const std::string &h5Path)
265 int color = (g_mpi.
rank < npWrite) ? 0 : MPI_UNDEFINED;
266 MPI_Comm writeComm = MPI_COMM_NULL;
267 MPI_Comm_split(MPI_COMM_WORLD, color, g_mpi.
rank, &writeComm);
269 if (writeComm != MPI_COMM_NULL)
272 buildAndWriteRef(ic, cfg, writeMpi, h5Path);
273 MPI_Comm_free(&writeComm);
282 const MeshConfig &cfg,
const std::string &h5Path)
284 auto mesh = std::make_shared<UnstructuredMesh>(g_mpi, cfg.
dim);
285 setPeriodicIfNeeded(
mesh, cfg);
293 auto ser = std::make_shared<Serializer::SerializerH5>(g_mpi);
294 ser->OpenFile(h5Path,
true);
295 mesh->ReadSerializeAndDistribute(ser,
"meshPart", pOpt);
298 rebuildAfterDistributedRead(
mesh, cfg);
309 std::vector<DNDS::index> localOrig(
mesh.NumCell());
311 localOrig[iC] =
mesh.cell2cellOrig(iC, 0);
313 int localCount =
static_cast<int>(localOrig.size());
314 std::vector<int> allCounts(
mpi.size);
315 MPI_Allgather(&localCount, 1, MPI_INT, allCounts.data(), 1, MPI_INT,
mpi.comm);
317 std::vector<int> displs(
mpi.size + 1, 0);
318 for (
int r = 0;
r <
mpi.size;
r++)
319 displs[
r + 1] = displs[
r] + allCounts[
r];
320 int totalCount = displs[
mpi.size];
322 std::vector<DNDS::index> allOrig(totalCount);
326 std::sort(allOrig.begin(), allOrig.end());
327 auto last = std::unique(allOrig.begin(), allOrig.end());
328 CHECK(last == allOrig.end());
329 CHECK(totalCount == expectedGlobal);
333static std::pair<Eigen::Vector3d, Eigen::Vector3d>
336 Eigen::Vector3d localMin = Eigen::Vector3d::Constant(1e100);
337 Eigen::Vector3d localMax = Eigen::Vector3d::Constant(-1e100);
339 for (
int d = 0; d < 3; d++)
341 localMin(d) = std::min(localMin(d),
mesh.coords[iN](d));
342 localMax(d) = std::max(localMax(d),
mesh.coords[iN](d));
344 Eigen::Vector3d globalMin, globalMax;
345 MPI_Allreduce(localMin.data(), globalMin.data(), 3, MPI_DOUBLE, MPI_MIN,
mpi.comm);
346 MPI_Allreduce(localMax.data(), globalMax.data(), 3, MPI_DOUBLE, MPI_MAX,
mpi.comm);
347 return {globalMin, globalMax};
353 MPI_Init(&argc, &argv);
358 std::filesystem::create_directories(tmpDir());
359 MPI::Barrier(g_mpi.
comm);
362 for (
int ic = 0; ic < N_CONFIGS; ic++)
364 g_h5Same[ic] = tmpDir() +
"/" + g_configs[ic].
name +
"_same.dnds.h5";
366 log() <<
"[setup] same-np: building + writing " << g_configs[ic].
name << std::endl;
367 buildAndWriteRef(ic, g_configs[ic], g_mpi, g_h5Same[ic]);
370 log() <<
"[setup] same-np: distributed read " << g_configs[ic].
name << std::endl;
371 g_sameNpMesh[ic] = distributedRead(g_configs[ic], g_h5Same[ic]);
375 g_crossNpAvailable = (g_mpi.
size >= 2);
376 if (g_crossNpAvailable)
378 int npWrite = std::max(1, g_mpi.
size / 2);
379 for (
int ic = 0; ic < N_CONFIGS; ic++)
381 g_h5Cross[ic] = tmpDir() +
"/" + g_configs[ic].
name +
"_cross.dnds.h5";
383 log() <<
"[setup] cross-np: building + writing " << g_configs[ic].
name
384 <<
" with npWrite=" << npWrite << std::endl;
385 buildAndWriteRefSubComm(ic, g_configs[ic], npWrite, g_h5Cross[ic]);
387 MPI::Barrier(g_mpi.
comm);
389 log() <<
"[setup] cross-np: distributed read " << g_configs[ic].
name << std::endl;
390 g_crossNpMesh[ic] = distributedRead(g_configs[ic], g_h5Cross[ic]);
395 doctest::Context ctx;
396 ctx.applyCommandLine(argc, argv);
400 for (
auto &
m : g_sameNpMesh)
402 for (
auto &
m : g_crossNpMesh)
404 MPI::Barrier(g_mpi.
comm);
406 std::filesystem::remove_all(tmpDir());
416#define FOR_EACH_CONFIG(body) \
417 for (int ic = 0; ic < N_CONFIGS; ic++) \
420 CAPTURE(g_configs[ic].name); \
427 CHECK(g_sameNpMesh[ic]->NumCellGlobal() == g_refCounts[ic].nCellGlobal);
434 CHECK(g_sameNpMesh[ic]->NumNodeGlobal() == g_refCounts[ic].nNodeGlobal);
441 CHECK(g_sameNpMesh[ic]->NumBndGlobal() == g_refCounts[ic].nBndGlobal);
448 CHECK(g_sameNpMesh[ic]->NumFaceGlobal() == g_refCounts[ic].nFaceGlobal);
455 if (g_configs[ic].expectedCells >= 0)
456 CHECK(g_refCounts[ic].nCellGlobal == g_configs[ic].expectedCells);
457 if (g_configs[ic].expectedBnds >= 0)
458 CHECK(g_refCounts[ic].nBndGlobal == g_configs[ic].expectedBnds);
465 CHECK(g_sameNpMesh[ic]->NumCell() > 0);
467 CHECK(g_sameNpMesh[ic]->NumNode() >= 0);
474 auto &
mesh = *g_sameNpMesh[ic];
493 auto &
mesh = *g_sameNpMesh[ic];
502 checkOrigUnique(*g_sameNpMesh[ic], g_refCounts[ic].nCellGlobal, g_mpi);
509 auto bbox = computeBBox(*g_sameNpMesh[ic], g_mpi);
510 for (
int d = 0; d < g_configs[ic].
dim; d++)
511 CHECK(bbox.second(d) > bbox.first(d));
519#define FOR_EACH_CROSS_CONFIG(body) \
520 if (!g_crossNpAvailable) \
522 for (int ic = 0; ic < N_CONFIGS; ic++) \
525 CAPTURE(g_configs[ic].name); \
532 CHECK(g_crossNpMesh[ic]->NumCellGlobal() == g_refCounts[ic].nCellGlobal);
539 CHECK(g_crossNpMesh[ic]->NumNodeGlobal() == g_refCounts[ic].nNodeGlobal);
546 CHECK(g_crossNpMesh[ic]->NumBndGlobal() == g_refCounts[ic].nBndGlobal);
553 CHECK(g_crossNpMesh[ic]->NumFaceGlobal() == g_refCounts[ic].nFaceGlobal);
560 CHECK(g_crossNpMesh[ic]->NumCell() > 0);
561 CHECK(g_crossNpMesh[ic]->NumNode() >= 0);
568 auto &
mesh = *g_crossNpMesh[ic];
587 checkOrigUnique(*g_crossNpMesh[ic], g_refCounts[ic].nCellGlobal, g_mpi);
594 auto &
mesh = *g_crossNpMesh[ic];
Eigen::Matrix< real, 3, 3 > m
MPI-parallel HDF5 serializer implementing the SerializerBase interface.
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).
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
std::shared_ptr< T > ssp
Shortened alias for std::shared_ptr used pervasively in DNDSR.
std::ostream & log()
Return the current DNDSR log stream (either std::cout or the installed file).
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.
DNDS::index expectedCells
Expected global cell count (-1 = skip check)
tPoint translation1
Periodic translation axis 1.
bool periodic
Has periodic boundaries.
int dim
Spatial dimension.
tPoint translation3
Periodic translation axis 3.
const char * file
Filename relative to data/mesh/.
tPoint translation2
Periodic translation axis 2.
const char * name
Human-readable label.
DNDS::index expectedBnds
Expected global bnd count (-1 = skip check)
REQUIRE(bool(result.parent2entityPbi.father))
#define FOR_EACH_CONFIG(body)
#define FOR_EACH_CROSS_CONFIG(body)
TEST_CASE("3D: VFV P2 HQM error < P1 on sinCos3D")