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();
247 g_refCounts[ic].
nCellGlobal = mesh->NumCellGlobal();
248 g_refCounts[ic].
nNodeGlobal = mesh->NumNodeGlobal();
249 g_refCounts[ic].
nBndGlobal = mesh->NumBndGlobal();
250 g_refCounts[ic].
nFaceGlobal = mesh->NumFaceGlobal();
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());
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];
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).
auto RowSize() const
Uniform row width (delegates to father).
index NumCellProc() const
tAdjPair node2cell
inverse relations
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)
#define FOR_EACH_CONFIG(body)
#define FOR_EACH_CROSS_CONFIG(body)
TEST_CASE("3D: VFV P2 HQM error < P1 on sinCos3D")