diff --git a/src/utils/GlobalVariableManager.hpp b/src/utils/GlobalVariableManager.hpp index fa7f52a3db7f2dfedfe797cc81a5b7ee8fd40525..0768a2ab2beb28a9060cadd44ee705f3aba598e2 100644 --- a/src/utils/GlobalVariableManager.hpp +++ b/src/utils/GlobalVariableManager.hpp @@ -32,6 +32,13 @@ class GlobalVariableManager return m_connectivity_id++; } + PUGS_INLINE + void + setConnectivityId(size_t connectivity_id) + { + m_connectivity_id = connectivity_id; + } + PUGS_INLINE size_t getMeshId() const @@ -46,6 +53,13 @@ class GlobalVariableManager return m_mesh_id++; } + PUGS_INLINE + void + setMeshId(size_t mesh_id) + { + m_mesh_id = mesh_id; + } + PUGS_INLINE static GlobalVariableManager& instance() diff --git a/src/utils/checkpointing/ResumingData.cpp b/src/utils/checkpointing/ResumingData.cpp index c90c7af5e53650f2c04a153972561fdae7e9c522..561c3747defaed4d9e76da81ce695a25444628b3 100644 --- a/src/utils/checkpointing/ResumingData.cpp +++ b/src/utils/checkpointing/ResumingData.cpp @@ -37,9 +37,11 @@ ResumingData::_getConnectivityList(const HighFive::Group& checkpoint) GlobalVariableManager::instance().getAndIncrementConnectivityId(); } + // LCOV_EXCL_START if (m_id_to_iconnectivity_map.contains(id)) { throw UnexpectedError("connectivity of id " + std::to_string(id) + " already defined!"); } + // LCOV_EXCL_STOP if (type == "unstructured") { const uint64_t dimension = connectivity_data.getAttribute("dimension").read<uint64_t>(); @@ -105,8 +107,8 @@ ResumingData::_getConnectivityList(const HighFive::Group& checkpoint) for (auto item_type_name : item_group.listObjectNames()) { HighFive::Group item_ref_name_list = item_group.getGroup(item_type_name); - for (auto item_ref_list_name : item_ref_name_list.listObjectNames()) { - HighFive::Group item_ref_list_data = item_ref_name_list.getGroup(item_ref_list_name); + for (auto item_ref_list_id : item_ref_name_list.listObjectNames()) { + HighFive::Group item_ref_list_data = item_ref_name_list.getGroup(item_ref_list_id); RefId ref_id(item_ref_list_data.getAttribute("tag_number").read<uint64_t>(), item_ref_list_data.getAttribute("tag_name").read<std::string>()); @@ -131,7 +133,9 @@ ResumingData::_getConnectivityList(const HighFive::Group& checkpoint) readArray<NodeId>(item_ref_list_data, "list"), ref_item_list_type}); } else { + // LCOV_EXCL_START throw UnexpectedError("invalid item type: " + item_type_name); + // LCOV_EXCL_STOP } } } diff --git a/src/utils/checkpointing/WriteConnectivity.cpp b/src/utils/checkpointing/WriteConnectivity.cpp index 7bed7f83f33b8fbe323a7513bf4455a77b5924c7..d8ee2984182ef790358a99cb06b5ecd97269b9ab 100644 --- a/src/utils/checkpointing/WriteConnectivity.cpp +++ b/src/utils/checkpointing/WriteConnectivity.cpp @@ -5,6 +5,8 @@ #include <utils/checkpointing/WriteArray.hpp> #include <utils/checkpointing/WriteItemValue.hpp> +#include <iomanip> + namespace checkpointing { @@ -16,7 +18,10 @@ writeRefItemList(const Connectivity<Dimension>& connectivity, HighFive::Group& c auto ref_item_list = connectivity.template refItemList<item_type>(i_item_list); std::ostringstream ref_item_list_group_name; - ref_item_list_group_name << "item_ref_list/" << itemName(item_type) << '/' << ref_item_list.refId().tagName(); + ref_item_list_group_name << "item_ref_list/" << itemName(item_type) << '/' << std::setfill('0') + << std::setw( + std::to_string(connectivity.template numberOfRefItemList<item_type>()).length()) + << i_item_list; HighFive::Group ref_item_list_group = connectivity_group.createGroup(ref_item_list_group_name.str()); ref_item_list_group.createAttribute("tag_name", ref_item_list.refId().tagName()); ref_item_list_group.createAttribute("tag_number", ref_item_list.refId().tagNumber()); @@ -145,9 +150,11 @@ writeConnectivity(const IConnectivity& connectivity, HighFive::File& file, HighF writeConnectivity(dynamic_cast<const Connectivity<3>&>(connectivity), file, checkpoint_group); break; } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid connectivity dimension"); } + // LCOV_EXCL_STOP } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d4221e3ad2aa24bc5b0d0a82c389503c663358f1..6202c2ad4c3264b2e5f2e1819bd8e2ebc4225e81 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -161,6 +161,7 @@ set(checkpointing_TESTS) if(PUGS_HAS_HDF5) list(APPEND checkpointing_TESTS test_checkpointing_Array.cpp + test_checkpointing_Connectivity.cpp test_checkpointing_HFTypes.cpp test_checkpointing_ItemArray.cpp test_checkpointing_ItemValue.cpp diff --git a/tests/mpi_test_main.cpp b/tests/mpi_test_main.cpp index 32f646285289ea179e4f384f8b10112dd45dcebc..68b5e0deb4c29f3c8f5ed70bc527ceac927a3d3c 100644 --- a/tests/mpi_test_main.cpp +++ b/tests/mpi_test_main.cpp @@ -47,7 +47,11 @@ main(int argc, char* argv[]) std::filesystem::path gcov_prefix = [&]() -> std::filesystem::path { std::string template_temp_dir = std::filesystem::temp_directory_path() / "pugs_gcov_XXXXXX"; - return std::filesystem::path{mkdtemp(&template_temp_dir[0])}; + if constexpr (std::string_view{PUGS_BUILD_TYPE} == "Coverage") { + return std::filesystem::path{mkdtemp(&template_temp_dir[0])}; + } else { + return ""; + } }(); Catch::Session session; diff --git a/tests/test_checkpointing_Connectivity.cpp b/tests/test_checkpointing_Connectivity.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c65e4a93da30c19a8a308e849ad83c75959ac2a6 --- /dev/null +++ b/tests/test_checkpointing_Connectivity.cpp @@ -0,0 +1,325 @@ +#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/ConnectivityDescriptor.hpp> +#include <mesh/ConnectivityMatrix.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshVariant.hpp> +#include <utils/GlobalVariableManager.hpp> +#include <utils/checkpointing/ResumingData.hpp> +#include <utils/checkpointing/WriteConnectivity.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <filesystem> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("checkpointing_Connectivity", "[utils/checkpointing]") +{ + std::string tmp_dirname; + { + std::map<size_t, size_t> connectivity_id_map; + + { + 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(); + + SECTION("Connectivity") + { + HighFive::Group checkpoint_group_0 = file.createGroup("checkpoint_0"); + HighFive::Group checkpoint_group_1 = file.createGroup("checkpoint_1"); + HighFive::Group useless_group; + + auto duplicate_connectivity = [](const IConnectivity& i_connectivity) -> std::shared_ptr<const IConnectivity> { + auto setRefItemLists = [](const auto& connectivity, ConnectivityDescriptor& descriptor) { + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::cell>(); ++i) { + descriptor.addRefItemList(connectivity.template refItemList<ItemType::cell>(i)); + } + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::face>(); ++i) { + descriptor.addRefItemList(connectivity.template refItemList<ItemType::face>(i)); + } + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::edge>(); ++i) { + descriptor.addRefItemList(connectivity.template refItemList<ItemType::edge>(i)); + } + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::node>(); ++i) { + descriptor.addRefItemList(connectivity.template refItemList<ItemType::node>(i)); + } + }; + + ConnectivityDescriptor descriptor; + switch (i_connectivity.dimension()) { + case 1: { + using ConnectivityType = Connectivity<1>; + + const ConnectivityType& connectivity = dynamic_cast<const ConnectivityType&>(i_connectivity); + descriptor.setCellNumberVector(connectivity.cellNumber().arrayView()); + descriptor.setNodeNumberVector(connectivity.nodeNumber().arrayView()); + + descriptor.setCellTypeVector(connectivity.cellType().arrayView()); + + descriptor.setCellOwnerVector(connectivity.cellOwner().arrayView()); + descriptor.setNodeOwnerVector(connectivity.nodeOwner().arrayView()); + + descriptor.setCellToNodeMatrix(connectivity.getMatrix(ItemType::cell, ItemType::node)); + + setRefItemLists(connectivity, descriptor); + + return ConnectivityType::build(descriptor); + } + case 2: { + using ConnectivityType = Connectivity<2>; + + const ConnectivityType& connectivity = dynamic_cast<const ConnectivityType&>(i_connectivity); + descriptor.setCellNumberVector(connectivity.cellNumber().arrayView()); + descriptor.setFaceNumberVector(connectivity.faceNumber().arrayView()); + descriptor.setNodeNumberVector(connectivity.nodeNumber().arrayView()); + + descriptor.setCellTypeVector(connectivity.cellType().arrayView()); + + descriptor.setCellFaceIsReversed(connectivity.cellFaceIsReversed().arrayView()); + + descriptor.setCellOwnerVector(connectivity.cellOwner().arrayView()); + descriptor.setFaceOwnerVector(connectivity.faceOwner().arrayView()); + descriptor.setNodeOwnerVector(connectivity.nodeOwner().arrayView()); + + descriptor.setCellToFaceMatrix(connectivity.getMatrix(ItemType::cell, ItemType::face)); + descriptor.setCellToNodeMatrix(connectivity.getMatrix(ItemType::cell, ItemType::node)); + descriptor.setFaceToNodeMatrix(connectivity.getMatrix(ItemType::face, ItemType::node)); + + setRefItemLists(connectivity, descriptor); + + return ConnectivityType::build(descriptor); + } + case 3: { + using ConnectivityType = Connectivity<3>; + + const ConnectivityType& connectivity = dynamic_cast<const ConnectivityType&>(i_connectivity); + descriptor.setCellNumberVector(connectivity.cellNumber().arrayView()); + descriptor.setFaceNumberVector(connectivity.faceNumber().arrayView()); + descriptor.setEdgeNumberVector(connectivity.edgeNumber().arrayView()); + descriptor.setNodeNumberVector(connectivity.nodeNumber().arrayView()); + + descriptor.setCellTypeVector(connectivity.cellType().arrayView()); + + descriptor.setCellFaceIsReversed(connectivity.cellFaceIsReversed().arrayView()); + descriptor.setFaceEdgeIsReversed(connectivity.faceEdgeIsReversed().arrayView()); + + descriptor.setCellOwnerVector(connectivity.cellOwner().arrayView()); + descriptor.setFaceOwnerVector(connectivity.faceOwner().arrayView()); + descriptor.setEdgeOwnerVector(connectivity.edgeOwner().arrayView()); + descriptor.setNodeOwnerVector(connectivity.nodeOwner().arrayView()); + + descriptor.setCellToFaceMatrix(connectivity.getMatrix(ItemType::cell, ItemType::face)); + descriptor.setCellToEdgeMatrix(connectivity.getMatrix(ItemType::cell, ItemType::edge)); + descriptor.setCellToNodeMatrix(connectivity.getMatrix(ItemType::cell, ItemType::node)); + descriptor.setFaceToEdgeMatrix(connectivity.getMatrix(ItemType::face, ItemType::edge)); + descriptor.setFaceToNodeMatrix(connectivity.getMatrix(ItemType::face, ItemType::node)); + descriptor.setEdgeToNodeMatrix(connectivity.getMatrix(ItemType::edge, ItemType::node)); + + setRefItemLists(connectivity, descriptor); + + return ConnectivityType::build(descriptor); + } + default: { + throw UnexpectedError("invalid connectivity"); + } + } + }; + + { // Write + auto mesh_1d = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + // creates artificially holes in numbering + duplicate_connectivity(mesh_1d->connectivity()); + + auto new_connectivity_1d = duplicate_connectivity(mesh_1d->connectivity()); + checkpointing::writeConnectivity(*new_connectivity_1d, file, checkpoint_group_0); + checkpointing::writeConnectivity(*new_connectivity_1d, file, checkpoint_group_1); + connectivity_id_map[mesh_1d->id()] = new_connectivity_1d->id(); + + auto mesh_2d = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + // creates artificially holes in numbering + duplicate_connectivity(mesh_2d->connectivity()); + duplicate_connectivity(mesh_2d->connectivity()); + + auto new_connectivity_2d = duplicate_connectivity(mesh_2d->connectivity()); + + checkpointing::writeConnectivity(*new_connectivity_2d, file, checkpoint_group_0); + checkpointing::writeConnectivity(*new_connectivity_2d, file, checkpoint_group_1); + connectivity_id_map[mesh_2d->id()] = new_connectivity_2d->id(); + + 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()->get<Mesh<3>>(); + + // creates artificially holes in numbering + duplicate_connectivity(mesh_3d->connectivity()); + + auto new_connectivity_3d = duplicate_connectivity(mesh_3d->connectivity()); + checkpointing::writeConnectivity(*new_connectivity_3d, file, checkpoint_group_1); + connectivity_id_map[mesh_3d->id()] = new_connectivity_3d->id(); + + 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()); + + // creates artificially holes in numbering + duplicate_connectivity(mesh_3d->connectivity()); + duplicate_connectivity(mesh_3d->connectivity()); + duplicate_connectivity(mesh_3d->connectivity()); + } + + // reset to reuse after resuming + GlobalVariableManager::instance().setConnectivityId(initial_connectivity_id); + + file.flush(); + + checkpointing::ResumingData::create(); + checkpointing::ResumingData::instance().readData(checkpoint_group_1, nullptr); + + GlobalVariableManager::instance().setConnectivityId(initial_connectivity_id); + { // Read + auto is_same = [](const auto& connectivity, const auto& read_connectivity) -> bool { + static_assert(std::is_same_v<decltype(connectivity), decltype(read_connectivity)>); + using ConnectivityType = std::decay_t<decltype(connectivity)>; + + 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); + }; + + auto same_ref_item_list = [&same_value](const auto& a, const auto& b) -> bool { + REQUIRE(a.type() == b.type()); + REQUIRE(a.refId() == b.refId()); + + return same_value(a.list(), b.list()); + }; + + bool same = true; + same &= same_value(connectivity.cellNumber().arrayView(), read_connectivity.cellNumber().arrayView()); + same &= same_value(connectivity.nodeNumber().arrayView(), read_connectivity.nodeNumber().arrayView()); + + same &= same_value(connectivity.cellType().arrayView(), read_connectivity.cellType().arrayView()); + + same &= same_value(connectivity.cellOwner().arrayView(), read_connectivity.cellOwner().arrayView()); + same &= same_value(connectivity.nodeOwner().arrayView(), read_connectivity.nodeOwner().arrayView()); + + same &= same_value(connectivity.cellToNodeMatrix().values(), read_connectivity.cellToNodeMatrix().values()); + + if constexpr (ConnectivityType::Dimension >= 2) { + same &= same_value(connectivity.faceNumber().arrayView(), read_connectivity.faceNumber().arrayView()); + same &= same_value(connectivity.faceOwner().arrayView(), read_connectivity.faceOwner().arrayView()); + same &= same_value(connectivity.cellFaceIsReversed().arrayView(), + read_connectivity.cellFaceIsReversed().arrayView()); + same &= same_value(connectivity.cellToFaceMatrix().values(), read_connectivity.cellToFaceMatrix().values()); + same &= same_value(connectivity.faceToNodeMatrix().values(), read_connectivity.faceToNodeMatrix().values()); + } + + if constexpr (ConnectivityType::Dimension == 3) { + same &= same_value(connectivity.edgeNumber().arrayView(), read_connectivity.edgeNumber().arrayView()); + same &= same_value(connectivity.edgeOwner().arrayView(), read_connectivity.edgeOwner().arrayView()); + same &= same_value(connectivity.faceEdgeIsReversed().arrayView(), + read_connectivity.faceEdgeIsReversed().arrayView()); + same &= same_value(connectivity.cellToEdgeMatrix().values(), read_connectivity.cellToEdgeMatrix().values()); + same &= same_value(connectivity.faceToEdgeMatrix().values(), read_connectivity.faceToEdgeMatrix().values()); + same &= same_value(connectivity.edgeToNodeMatrix().values(), read_connectivity.edgeToNodeMatrix().values()); + } + + REQUIRE(connectivity.template numberOfRefItemList<ItemType::cell>() == + read_connectivity.template numberOfRefItemList<ItemType::cell>()); + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::cell>(); ++i) { + same &= same_ref_item_list(connectivity.template refItemList<ItemType::cell>(i), + read_connectivity.template refItemList<ItemType::cell>(i)); + } + REQUIRE(connectivity.template numberOfRefItemList<ItemType::face>() == + read_connectivity.template numberOfRefItemList<ItemType::face>()); + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::face>(); ++i) { + same &= same_ref_item_list(connectivity.template refItemList<ItemType::face>(i), + read_connectivity.template refItemList<ItemType::face>(i)); + } + REQUIRE(connectivity.template numberOfRefItemList<ItemType::edge>() == + read_connectivity.template numberOfRefItemList<ItemType::edge>()); + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::edge>(); ++i) { + same &= same_ref_item_list(connectivity.template refItemList<ItemType::edge>(i), + read_connectivity.template refItemList<ItemType::edge>(i)); + } + REQUIRE(connectivity.template numberOfRefItemList<ItemType::node>() == + read_connectivity.template numberOfRefItemList<ItemType::node>()); + for (size_t i = 0; i < connectivity.template numberOfRefItemList<ItemType::node>(); ++i) { + same &= same_ref_item_list(connectivity.template refItemList<ItemType::node>(i), + read_connectivity.template refItemList<ItemType::node>(i)); + } + + return same; + }; + + auto mesh_1d = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity_1d = mesh_1d->connectivity(); + + std::shared_ptr<const IConnectivity> p_new_connectivity_1d = + checkpointing::ResumingData::instance().iConnectivity(connectivity_id_map.at(connectivity_1d.id())); + + const Connectivity<1>& read_connectivity_1d = dynamic_cast<const Connectivity<1>&>(*p_new_connectivity_1d); + + REQUIRE(is_same(connectivity_1d, read_connectivity_1d)); + + auto mesh_2d = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity_2d = mesh_2d->connectivity(); + + std::shared_ptr<const IConnectivity> p_new_connectivity_2d = + checkpointing::ResumingData::instance().iConnectivity(connectivity_id_map.at(connectivity_2d.id())); + + const Connectivity<2>& read_connectivity_2d = dynamic_cast<const Connectivity<2>&>(*p_new_connectivity_2d); + + REQUIRE(is_same(connectivity_2d, read_connectivity_2d)); + + auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity_3d = mesh_3d->connectivity(); + + std::shared_ptr<const IConnectivity> p_new_connectivity_3d = + checkpointing::ResumingData::instance().iConnectivity(connectivity_id_map.at(connectivity_3d.id())); + + const Connectivity<3>& read_connectivity_3d = dynamic_cast<const Connectivity<3>&>(*p_new_connectivity_3d); + + REQUIRE(is_same(connectivity_3d, read_connectivity_3d)); + } + checkpointing::ResumingData::destroy(); + } + } + + parallel::barrier(); + if (parallel::rank() == 0) { + std::filesystem::remove_all(std::filesystem::path{tmp_dirname}); + } +}