diff --git a/src/utils/checkpointing/ResumingData.cpp b/src/utils/checkpointing/ResumingData.cpp index 561c3747defaed4d9e76da81ce695a25444628b3..2fc7996cc09f1d99d2ec827d5ea66ef2abe8b39a 100644 --- a/src/utils/checkpointing/ResumingData.cpp +++ b/src/utils/checkpointing/ResumingData.cpp @@ -154,9 +154,11 @@ ResumingData::_getConnectivityList(const HighFive::Group& checkpoint) m_id_to_iconnectivity_map.insert({id, Connectivity<3>::build(descriptor)}); break; } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid dimension " + std::to_string(dimension)); } + // LCOV_EXCL_STOP } } else if (type == "dual_connectivity") { const DualMeshType type_of_dual = connectivity_data.getAttribute("type_of_dual").read<DualMeshType>(); @@ -168,7 +170,9 @@ ResumingData::_getConnectivityList(const HighFive::Group& checkpoint) switch (iprimal_connectivity->dimension()) { case 1: { if (type_of_dual != DualMeshType::Dual1D) { + // LCOV_EXCL_START throw UnexpectedError("unexpected dual mesh type"); + // LCOV_EXCL_STOP } const Connectivity<1>& primal_connectivity = dynamic_cast<const Connectivity<1>&>(*iprimal_connectivity); @@ -186,7 +190,9 @@ ResumingData::_getConnectivityList(const HighFive::Group& checkpoint) m_id_to_iconnectivity_map.insert( {id, DualConnectivityManager::instance().getMedianDualConnectivity(primal_connectivity)}); } else { + // LCOV_EXCL_START throw UnexpectedError("unexpected dual mesh type"); + // LCOV_EXCL_STOP } break; } @@ -200,16 +206,22 @@ ResumingData::_getConnectivityList(const HighFive::Group& checkpoint) m_id_to_iconnectivity_map.insert( {id, DualConnectivityManager::instance().getMedianDualConnectivity(primal_connectivity)}); } else { + // LCOV_EXCL_START throw UnexpectedError("unexpected dual mesh type"); + // LCOV_EXCL_STOP } break; } + // LCOV_EXCL_START default: { throw UnexpectedError("unexpected dimension"); } + // LCOV_EXCL_STOP } } else { + // LCOV_EXCL_START throw UnexpectedError("invalid connectivity type: " + type); + // LCOV_EXCL_STOP } } } @@ -229,11 +241,15 @@ ResumingData::_readPolygonalMesh(const HighFive::Group& mesh_data) const uint64_t connectivity_id = mesh_data.getAttribute("connectivity").read<uint64_t>(); auto i_id_to_iconnectivity = m_id_to_iconnectivity_map.find(connectivity_id); if (i_id_to_iconnectivity == m_id_to_iconnectivity_map.end()) { + // LCOV_EXCL_START throw UnexpectedError("cannot find connectivity " + std::to_string(connectivity_id)); + // LCOV_EXCL_STOP } std::shared_ptr<const IConnectivity> i_connectivity = i_id_to_iconnectivity->second; if (i_connectivity->dimension() != Dimension) { + // LCOV_EXCL_START throw UnexpectedError("invalid connectivity dimension " + std::to_string(i_connectivity->dimension())); + // LCOV_EXCL_STOP } std::shared_ptr<const Connectivity<Dimension>> connectivity = @@ -288,9 +304,11 @@ ResumingData::_getMeshVariantList(const HighFive::Group& checkpoint) m_id_to_mesh_variant_map.insert({id, this->_readPolygonalMesh<3>(mesh_data)}); break; } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension " + std::to_string(dimension)); } + // LCOV_EXCL_STOP } } else if (type == "dual_mesh") { const DualMeshType type_of_dual = mesh_data.getAttribute("type_of_dual").read<DualMeshType>(); @@ -305,7 +323,9 @@ ResumingData::_getMeshVariantList(const HighFive::Group& checkpoint) if (type_of_dual == DualMeshType::Dual1D) { m_id_to_mesh_variant_map.insert({id, DualMeshManager::instance().getDual1DMesh(primal_mesh_variant)}); } else { + // LCOV_EXCL_START throw UnexpectedError("unexpected dual mesh type"); + // LCOV_EXCL_STOP } } else if constexpr (std::is_same_v<MeshType, Mesh<2>> or std::is_same_v<MeshType, Mesh<3>>) { if (type_of_dual == DualMeshType::Diamond) { @@ -315,14 +335,18 @@ ResumingData::_getMeshVariantList(const HighFive::Group& checkpoint) m_id_to_mesh_variant_map.insert( {id, DualMeshManager::instance().getMedianDualMesh(primal_mesh_variant)}); } else { + // LCOV_EXCL_START throw UnexpectedError("unexpected dual mesh type"); + // LCOV_EXCL_STOP } } }, primal_mesh_variant->variant()); } else { + // LCOV_EXCL_START throw UnexpectedError("invalid mesh type"); + // LCOV_EXCL_STOP } } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6202c2ad4c3264b2e5f2e1819bd8e2ebc4225e81..d96587863d5ffc6f34a1a715e66b9098aa5db881 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -174,6 +174,7 @@ if(PUGS_HAS_HDF5) test_checkpointing_ItemType.cpp test_checkpointing_IWriter.cpp test_checkpointing_IZoneDescriptor.cpp + test_checkpointing_Mesh.cpp test_checkpointing_Table.cpp ) endif(PUGS_HAS_HDF5) diff --git a/tests/checkpointing_Mesh_utilities.hpp b/tests/checkpointing_Mesh_utilities.hpp new file mode 100644 index 0000000000000000000000000000000000000000..aabfacbbd6ca259de9468fda12ea706c8784005b --- /dev/null +++ b/tests/checkpointing_Mesh_utilities.hpp @@ -0,0 +1,68 @@ +#ifndef CHECKPOINTING_MESH_UTILITIES_HPP +#define CHECKPOINTING_MESH_UTILITIES_HPP + +#include <checkpointing_Connectivity_utilities.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshTraits.hpp> +#include <mesh/MeshVariant.hpp> + +namespace test_only +{ + +PUGS_INLINE +std::shared_ptr<const MeshVariant> +duplicateMesh(std::shared_ptr<const MeshVariant> mesh_v) +{ + std::shared_ptr<const MeshVariant> new_mesh_v; + + std::visit( + [&](auto&& mesh) { + using MeshType = mesh_type_t<decltype(mesh)>; + if constexpr (is_polygonal_mesh_v<MeshType>) { + using Rd = TinyVector<MeshType::Dimension>; + auto new_connectivity = + std::dynamic_pointer_cast<const typename MeshType::Connectivity>(duplicateConnectivity(mesh->connectivity())); + + NodeValue<const Rd> new_xr{*new_connectivity, mesh->xr().arrayView()}; + new_mesh_v = std::make_shared<MeshVariant>(std::make_shared<const MeshType>(new_connectivity, new_xr)); + } else { + throw NotImplementedError("unexpected mesh type"); + } + }, + mesh_v->variant()); + + return new_mesh_v; +} + +PUGS_INLINE bool +isSameMesh(std::shared_ptr<const MeshVariant> mesh_v, std::shared_ptr<const MeshVariant> read_mesh_v) +{ + auto same_value = [](const auto& a, const auto& b) -> bool { + bool same = true; + for (size_t i = 0; i < a.size(); ++i) { + same &= (a[i] == b[i]); + } + return parallel::allReduceAnd(same); + }; + + bool same = true; + + std::visit( + [&](auto&& mesh, auto&& read_mesh) { + using MeshType = mesh_type_t<decltype(mesh)>; + using ReadMeshType = mesh_type_t<decltype(read_mesh)>; + if constexpr (std::is_same_v<MeshType, ReadMeshType>) { + same &= test_only::isSameConnectivity(mesh->connectivity(), read_mesh->connectivity()); + same &= same_value(mesh->xr().arrayView(), read_mesh->xr().arrayView()); + } else { + throw UnexpectedError("mesh of different kind"); + } + }, + mesh_v->variant(), read_mesh_v->variant()); + + return same; +} + +} // namespace test_only + +#endif // CHECKPOINTING_MESH_UTILITIES_HPP diff --git a/tests/test_checkpointing_Mesh.cpp b/tests/test_checkpointing_Mesh.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d9a27ea40585c1b7253881fd93d7a3ba3404b5e --- /dev/null +++ b/tests/test_checkpointing_Mesh.cpp @@ -0,0 +1,159 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <utils/Messenger.hpp> + +#include <language/utils/DataHandler.hpp> +#include <language/utils/EmbeddedData.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshVariant.hpp> +#include <utils/GlobalVariableManager.hpp> +#include <utils/checkpointing/ReadMesh.hpp> +#include <utils/checkpointing/ResumingData.hpp> +#include <utils/checkpointing/WriteMesh.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <checkpointing_Mesh_utilities.hpp> + +#include <filesystem> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("checkpointing_Mesh", "[utils/checkpointing]") +{ + std::string tmp_dirname; + { + { + if (parallel::rank() == 0) { + tmp_dirname = [&]() -> std::string { + std::string temp_filename = std::filesystem::temp_directory_path() / "pugs_checkpointing_XXXXXX"; + return std::string{mkdtemp(&temp_filename[0])}; + }(); + } + parallel::broadcast(tmp_dirname, 0); + } + std::filesystem::path path = tmp_dirname; + const std::string filename = path / "checkpoint.h5"; + + HighFive::FileAccessProps fapl; + fapl.add(HighFive::MPIOFileAccess{MPI_COMM_WORLD, MPI_INFO_NULL}); + fapl.add(HighFive::MPIOCollectiveMetadata{}); + HighFive::File file = HighFive::File(filename, HighFive::File::Truncate, fapl); + + const size_t initial_connectivity_id = GlobalVariableManager::instance().getConnectivityId(); + const size_t initial_mesh_id = GlobalVariableManager::instance().getMeshId(); + + SECTION("Mesh") + { + HighFive::Group checkpoint_group_0 = file.createGroup("checkpoint_0"); + HighFive::Group symbol_table_0 = checkpoint_group_0.createGroup("symbol_table"); + + HighFive::Group checkpoint_group_1 = file.createGroup("checkpoint_1"); + HighFive::Group symbol_table_1 = checkpoint_group_1.createGroup("symbol_table"); + + { // Write + auto mesh_1d = MeshDataBaseForTests::get().unordered1DMesh(); + + // creates artificially holes in numbering + test_only::duplicateMesh(mesh_1d); + + auto new_mesh_1d = test_only::duplicateMesh(mesh_1d); + + using DataHandlerT = DataHandler<const MeshVariant>; + + checkpointing::writeMesh("mesh_1d", EmbeddedData{std::make_shared<DataHandlerT>(new_mesh_1d)}, file, + checkpoint_group_0, symbol_table_0); + checkpointing::writeMesh("mesh_1d", EmbeddedData{std::make_shared<DataHandlerT>(new_mesh_1d)}, file, + checkpoint_group_1, symbol_table_1); + + auto mesh_2d = MeshDataBaseForTests::get().hybrid2DMesh(); + + // creates artificially holes in numbering + test_only::duplicateMesh(mesh_2d); + auto new_mesh_2d = test_only::duplicateMesh(mesh_2d); + + checkpointing::writeMesh("mesh_2d", EmbeddedData{std::make_shared<DataHandlerT>(new_mesh_2d)}, file, + checkpoint_group_0, symbol_table_0); + checkpointing::writeMesh("mesh_2d", EmbeddedData{std::make_shared<DataHandlerT>(new_mesh_2d)}, file, + checkpoint_group_1, symbol_table_1); + + HighFive::Group global_variables_group_0 = checkpoint_group_0.createGroup("singleton/global_variables"); + global_variables_group_0.createAttribute("connectivity_id", + GlobalVariableManager::instance().getConnectivityId()); + global_variables_group_0.createAttribute("mesh_id", GlobalVariableManager::instance().getMeshId()); + + auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh(); + + // creates artificially holes in numbering + test_only::duplicateMesh(mesh_3d); + + auto new_mesh_3d = test_only::duplicateMesh(mesh_3d); + checkpointing::writeMesh("mesh_3d", EmbeddedData{std::make_shared<DataHandlerT>(new_mesh_3d)}, file, + checkpoint_group_1, symbol_table_1); + + // creates artificially holes in numbering + test_only::duplicateMesh(mesh_3d); + test_only::duplicateMesh(mesh_3d); + test_only::duplicateMesh(mesh_3d); + + HighFive::Group global_variables_group_1 = checkpoint_group_1.createGroup("singleton/global_variables"); + global_variables_group_1.createAttribute("connectivity_id", + GlobalVariableManager::instance().getConnectivityId()); + global_variables_group_1.createAttribute("mesh_id", GlobalVariableManager::instance().getMeshId()); + } + + // reset to reuse after resuming + GlobalVariableManager::instance().setConnectivityId(initial_connectivity_id); + GlobalVariableManager::instance().setMeshId(initial_mesh_id); + + file.flush(); + + checkpointing::ResumingData::create(); + checkpointing::ResumingData::instance().readData(checkpoint_group_1, nullptr); + + GlobalVariableManager::instance().setConnectivityId(initial_connectivity_id); + GlobalVariableManager::instance().setMeshId(initial_mesh_id); + { // Read + + auto mesh_1d = MeshDataBaseForTests::get().unordered1DMesh(); + + EmbeddedData e_mesh_1d = checkpointing::readMesh("mesh_1d", symbol_table_1); + + REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const MeshVariant>&>(e_mesh_1d.get())); + + std::shared_ptr<const MeshVariant> read_mesh_1d = + dynamic_cast<const DataHandler<const MeshVariant>&>(e_mesh_1d.get()).data_ptr(); + + REQUIRE(test_only::isSameMesh(mesh_1d, read_mesh_1d)); + + auto mesh_2d = MeshDataBaseForTests::get().hybrid2DMesh(); + + EmbeddedData e_mesh_2d = checkpointing::readMesh("mesh_2d", symbol_table_1); + + REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const MeshVariant>&>(e_mesh_2d.get())); + + std::shared_ptr<const MeshVariant> read_mesh_2d = + dynamic_cast<const DataHandler<const MeshVariant>&>(e_mesh_2d.get()).data_ptr(); + + REQUIRE(test_only::isSameMesh(mesh_2d, read_mesh_2d)); + + auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh(); + + EmbeddedData e_mesh_3d = checkpointing::readMesh("mesh_3d", symbol_table_1); + + REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const MeshVariant>&>(e_mesh_3d.get())); + + std::shared_ptr<const MeshVariant> read_mesh_3d = + dynamic_cast<const DataHandler<const MeshVariant>&>(e_mesh_3d.get()).data_ptr(); + + REQUIRE(test_only::isSameMesh(mesh_3d, read_mesh_3d)); + } + checkpointing::ResumingData::destroy(); + } + } + + parallel::barrier(); + if (parallel::rank() == 0) { + std::filesystem::remove_all(std::filesystem::path{tmp_dirname}); + } +}