diff --git a/src/utils/checkpointing/ReadSubItemValuePerItemVariant.cpp b/src/utils/checkpointing/ReadSubItemValuePerItemVariant.cpp index 0b3dd2b0baf4c537cf3e22fd545fd3df33d94091..509b2e29ebeedb4d6a88b732462ab30f4a18310c 100644 --- a/src/utils/checkpointing/ReadSubItemValuePerItemVariant.cpp +++ b/src/utils/checkpointing/ReadSubItemValuePerItemVariant.cpp @@ -72,11 +72,15 @@ readSubItemValuePerItemVariant(const HighFive::Group& sub_item_value_per_item_va readSubItemValuePerItem<TinyMatrix<3>, item_type, sub_item_type>(sub_item_value_per_item_variant_group, "values", connectivity)); } else { + // LCOV_EXCL_START throw UnexpectedError("unexpected discrete function data type: " + data_type); + // LCOV_EXCL_STOP } return p_sub_item_value_per_item_variant; } else { + // LCOV_EXCL_START throw UnexpectedError("item_type and sub_item_type must be different"); + // LCOV_EXCL_STOP } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7af1682aeb37ea1de1c3c449e39deb64b01d3dac..cff9f33d2f34532db3acb03e8f91187a2a3296c7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -177,6 +177,7 @@ if(PUGS_HAS_HDF5) test_checkpointing_IWriter.cpp test_checkpointing_IZoneDescriptor.cpp test_checkpointing_Mesh.cpp + test_checkpointing_SubItemValuePerItemVariant.cpp test_checkpointing_Table.cpp ) endif(PUGS_HAS_HDF5) diff --git a/tests/test_checkpointing_SubItemValuePerItemVariant.cpp b/tests/test_checkpointing_SubItemValuePerItemVariant.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc1553757677c8e32a25fe1393a807d7f1024f9b --- /dev/null +++ b/tests/test_checkpointing_SubItemValuePerItemVariant.cpp @@ -0,0 +1,369 @@ +#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/SubItemValuePerItemVariant.hpp> +#include <utils/GlobalVariableManager.hpp> +#include <utils/checkpointing/ReadSubItemValuePerItemVariant.hpp> +#include <utils/checkpointing/ResumingData.hpp> +#include <utils/checkpointing/WriteSubItemValuePerItemVariant.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <checkpointing_Connectivity_utilities.hpp> + +#include <filesystem> + +// clazy:excludeall=non-pod-global-static + +namespace test_only +{ + +template <typename DataType, typename ItemOfItemTypeT> +PUGS_INLINE void +check_is_same(const SubItemValuePerItem<DataType, ItemOfItemTypeT>& reference, const EmbeddedData& e_read_data) +{ + 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); + }; + + REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const SubItemValuePerItemVariant>&>(e_read_data.get())); + + std::shared_ptr<const SubItemValuePerItemVariant> p_new_data_v = + dynamic_cast<const DataHandler<const SubItemValuePerItemVariant>&>(e_read_data.get()).data_ptr(); + + using SubItemValuePerItemT = SubItemValuePerItem<const DataType, ItemOfItemTypeT>; + + SubItemValuePerItemT read_data = p_new_data_v->get<SubItemValuePerItemT>(); + + switch (reference.connectivity_ptr()->dimension()) { + case 1: { + REQUIRE(test_only::isSameConnectivity(dynamic_cast<const Connectivity<1>&>(*reference.connectivity_ptr()), + dynamic_cast<const Connectivity<1>&>(*read_data.connectivity_ptr()))); + break; + } + case 2: { + REQUIRE(test_only::isSameConnectivity(dynamic_cast<const Connectivity<2>&>(*reference.connectivity_ptr()), + dynamic_cast<const Connectivity<2>&>(*read_data.connectivity_ptr()))); + break; + } + case 3: { + REQUIRE(test_only::isSameConnectivity(dynamic_cast<const Connectivity<3>&>(*reference.connectivity_ptr()), + dynamic_cast<const Connectivity<3>&>(*read_data.connectivity_ptr()))); + break; + } + default: { + throw UnexpectedError("invalid connectivity dimension"); + } + } + + REQUIRE(same_value(reference.arrayView(), read_data.arrayView())); +} + +} // namespace test_only + +TEST_CASE("checkpointing_SubItemValuePerItemVariant", "[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(); + + SECTION("Connectivity") + { + using R1 = TinyVector<1>; + using R2 = TinyVector<2>; + using R3 = TinyVector<3>; + using R1x1 = TinyMatrix<1>; + using R2x2 = TinyMatrix<2>; + using R3x3 = TinyMatrix<3>; + + HighFive::Group checkpoint_group = file.createGroup("checkpoint"); + HighFive::Group symbol_table_group = checkpoint_group.createGroup("symbol_table"); + + auto mesh_1d = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + NodeValuePerCell<bool> node_B_per_cell_1d{mesh_1d->connectivity()}; + for (CellId cell_id = 0; cell_id < mesh_1d->numberOfCells(); ++cell_id) { + auto node_list = mesh_1d->connectivity().cellToNodeMatrix()[cell_id]; + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + node_B_per_cell_1d[cell_id][i_node] = (std::rand() / (RAND_MAX / mesh_1d->numberOfCells())) % 2; + } + } + + NodeValuePerCell<uint64_t> node_N_per_cell_1d{mesh_1d->connectivity()}; + for (CellId cell_id = 0; cell_id < mesh_1d->numberOfCells(); ++cell_id) { + auto node_list = mesh_1d->connectivity().cellToNodeMatrix()[cell_id]; + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + node_N_per_cell_1d[cell_id][i_node] = (std::rand() / (RAND_MAX / mesh_1d->numberOfCells())); + } + } + + CellValuePerNode<int64_t> cell_Z_per_node_1d{mesh_1d->connectivity()}; + for (NodeId node_id = 0; node_id < mesh_1d->numberOfNodes(); ++node_id) { + auto cell_list = mesh_1d->connectivity().nodeToCellMatrix()[node_id]; + for (size_t i_cell = 0; i_cell < cell_list.size(); ++i_cell) { + cell_Z_per_node_1d[node_id][i_cell] = + 100 * (std::rand() - RAND_MAX / 2.) / (RAND_MAX / mesh_1d->numberOfNodes()); + } + } + + CellValuePerNode<double> cell_R_per_node_1d{mesh_1d->connectivity()}; + for (NodeId node_id = 0; node_id < mesh_1d->numberOfNodes(); ++node_id) { + auto cell_list = mesh_1d->connectivity().nodeToCellMatrix()[node_id]; + for (size_t i_cell = 0; i_cell < cell_list.size(); ++i_cell) { + cell_R_per_node_1d[node_id][i_cell] = std::rand() / (1. * RAND_MAX / mesh_1d->numberOfNodes()); + } + } + + NodeValuePerCell<R1> node_R1_per_cell_1d{mesh_1d->connectivity()}; + for (CellId cell_id = 0; cell_id < mesh_1d->numberOfCells(); ++cell_id) { + auto node_list = mesh_1d->connectivity().cellToNodeMatrix()[cell_id]; + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + node_R1_per_cell_1d[cell_id][i_node] = R1{std::rand() / (1. * RAND_MAX / mesh_1d->numberOfCells())}; + } + } + + CellValuePerNode<R2> cell_R2_per_node_1d{mesh_1d->connectivity()}; + for (NodeId node_id = 0; node_id < mesh_1d->numberOfNodes(); ++node_id) { + auto cell_list = mesh_1d->connectivity().nodeToCellMatrix()[node_id]; + for (size_t i_cell = 0; i_cell < cell_list.size(); ++i_cell) { + cell_R2_per_node_1d[node_id][i_cell] = R2{std::rand() / (1. * RAND_MAX / mesh_1d->numberOfNodes()), + std::rand() / (1. * RAND_MAX / mesh_1d->numberOfNodes())}; + } + } + + auto mesh_2d = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + CellValuePerFace<R3> cell_R3_per_face_2d{mesh_2d->connectivity()}; + for (FaceId face_id = 0; face_id < mesh_2d->numberOfFaces(); ++face_id) { + auto cell_list = mesh_2d->connectivity().faceToCellMatrix()[face_id]; + for (size_t i_cell = 0; i_cell < cell_list.size(); ++i_cell) { + cell_R3_per_face_2d[face_id][i_cell] = R3{std::rand() / (1. * RAND_MAX / mesh_2d->numberOfFaces()), + std::rand() / (1. * RAND_MAX / mesh_2d->numberOfFaces()), + std::rand() / (1. * RAND_MAX / mesh_2d->numberOfFaces())}; + } + } + + FaceValuePerNode<R2x2> face_R2x2_per_node_2d{mesh_2d->connectivity()}; + for (NodeId node_id = 0; node_id < mesh_2d->numberOfNodes(); ++node_id) { + auto face_list = mesh_2d->connectivity().nodeToFaceMatrix()[node_id]; + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + face_R2x2_per_node_2d[node_id][i_face] = R2x2{std::rand() / (1. * RAND_MAX / mesh_2d->numberOfNodes()), + std::rand() / (1. * RAND_MAX / mesh_2d->numberOfNodes()), + std::rand() / (1. * RAND_MAX / mesh_2d->numberOfNodes()), + std::rand() / (1. * RAND_MAX / mesh_2d->numberOfNodes())}; + } + } + + auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + FaceValuePerEdge<R3> face_R3_per_edge_3d{mesh_3d->connectivity()}; + for (EdgeId edge_id = 0; edge_id < mesh_3d->numberOfEdges(); ++edge_id) { + auto face_list = mesh_3d->connectivity().edgeToFaceMatrix()[edge_id]; + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + face_R3_per_edge_3d[edge_id][i_face] = R3{std::rand() / (1. * RAND_MAX / mesh_3d->numberOfEdges()), + std::rand() / (1. * RAND_MAX / mesh_3d->numberOfEdges()), + std::rand() / (1. * RAND_MAX / mesh_3d->numberOfEdges())}; + } + } + + EdgeValuePerFace<R1x1> edge_R1x1_per_face_3d{mesh_3d->connectivity()}; + for (FaceId face_id = 0; face_id < mesh_3d->numberOfFaces(); ++face_id) { + auto edge_list = mesh_3d->connectivity().faceToEdgeMatrix()[face_id]; + for (size_t i_edge = 0; i_edge < edge_list.size(); ++i_edge) { + edge_R1x1_per_face_3d[face_id][i_edge] = R1x1{std::rand() / (1. * RAND_MAX / mesh_3d->numberOfFaces())}; + } + } + + EdgeValuePerNode<R3x3> edge_R3x3_per_node_3d{mesh_3d->connectivity()}; + for (NodeId node_id = 0; node_id < mesh_3d->numberOfNodes(); ++node_id) { + auto edge_list = mesh_3d->connectivity().nodeToEdgeMatrix()[node_id]; + for (size_t i_edge = 0; i_edge < edge_list.size(); ++i_edge) { + edge_R3x3_per_node_3d[node_id][i_edge] = R3x3{std::rand() / (1. * RAND_MAX / mesh_3d->numberOfNodes()), + std::rand() / (1. * RAND_MAX / mesh_3d->numberOfNodes()), + std::rand() / (1. * RAND_MAX / mesh_3d->numberOfNodes()), + std::rand() / (1. * RAND_MAX / mesh_3d->numberOfNodes()), + std::rand() / (1. * RAND_MAX / mesh_3d->numberOfNodes()), + std::rand() / (1. * RAND_MAX / mesh_3d->numberOfNodes()), + std::rand() / (1. * RAND_MAX / mesh_3d->numberOfNodes()), + std::rand() / (1. * RAND_MAX / mesh_3d->numberOfNodes()), + std::rand() / (1. * RAND_MAX / mesh_3d->numberOfNodes())}; + } + } + + { // Write + using DataHandlerT = DataHandler<const SubItemValuePerItemVariant>; + + auto new_connectivity_1d = test_only::duplicateConnectivity(mesh_1d->connectivity()); + + NodeValuePerCell<const bool> node_B_per_cell_1d_new{*new_connectivity_1d, node_B_per_cell_1d.arrayView()}; + CellValuePerNode<const int64_t> cell_Z_per_node_1d_new{*new_connectivity_1d, cell_Z_per_node_1d.arrayView()}; + NodeValuePerCell<const uint64_t> node_N_per_cell_1d_new{*new_connectivity_1d, node_N_per_cell_1d.arrayView()}; + CellValuePerNode<const double> cell_R_per_node_1d_new{*new_connectivity_1d, cell_R_per_node_1d.arrayView()}; + NodeValuePerCell<const R1> node_R1_per_cell_1d_new{*new_connectivity_1d, node_R1_per_cell_1d.arrayView()}; + CellValuePerNode<const R2> cell_R2_per_node_1d_new{*new_connectivity_1d, cell_R2_per_node_1d.arrayView()}; + + checkpointing::writeSubItemValuePerItemVariant("node_B_per_cell_1d", + EmbeddedData{std::make_shared<DataHandlerT>( + std::make_shared<const SubItemValuePerItemVariant>( + node_B_per_cell_1d_new))}, + file, checkpoint_group, symbol_table_group); + + checkpointing::writeSubItemValuePerItemVariant("node_N_per_cell_1d", + EmbeddedData{std::make_shared<DataHandlerT>( + std::make_shared<const SubItemValuePerItemVariant>( + node_N_per_cell_1d_new))}, + file, checkpoint_group, symbol_table_group); + + checkpointing::writeSubItemValuePerItemVariant("cell_Z_per_node_1d", + EmbeddedData{std::make_shared<DataHandlerT>( + std::make_shared<const SubItemValuePerItemVariant>( + cell_Z_per_node_1d_new))}, + file, checkpoint_group, symbol_table_group); + + checkpointing::writeSubItemValuePerItemVariant("cell_R_per_node_1d", + EmbeddedData{std::make_shared<DataHandlerT>( + std::make_shared<const SubItemValuePerItemVariant>( + cell_R_per_node_1d_new))}, + file, checkpoint_group, symbol_table_group); + + checkpointing::writeSubItemValuePerItemVariant("node_R1_per_cell_1d", + EmbeddedData{std::make_shared<DataHandlerT>( + std::make_shared<const SubItemValuePerItemVariant>( + node_R1_per_cell_1d_new))}, + file, checkpoint_group, symbol_table_group); + + checkpointing::writeSubItemValuePerItemVariant("cell_R2_per_node_1d", + EmbeddedData{std::make_shared<DataHandlerT>( + std::make_shared<const SubItemValuePerItemVariant>( + cell_R2_per_node_1d_new))}, + file, checkpoint_group, symbol_table_group); + + auto new_connectivity_2d = test_only::duplicateConnectivity(mesh_2d->connectivity()); + + CellValuePerFace<const R3> cell_R3_per_face_2d_new{*new_connectivity_2d, cell_R3_per_face_2d.arrayView()}; + FaceValuePerNode<const R2x2> face_R2x2_per_node_2d_new{*new_connectivity_2d, face_R2x2_per_node_2d.arrayView()}; + + checkpointing::writeSubItemValuePerItemVariant("cell_R3_per_face_2d", + EmbeddedData{std::make_shared<DataHandlerT>( + std::make_shared<const SubItemValuePerItemVariant>( + cell_R3_per_face_2d_new))}, + file, checkpoint_group, symbol_table_group); + + checkpointing::writeSubItemValuePerItemVariant("face_R2x2_per_node_2d", + EmbeddedData{std::make_shared<DataHandlerT>( + std::make_shared<const SubItemValuePerItemVariant>( + face_R2x2_per_node_2d_new))}, + file, checkpoint_group, symbol_table_group); + + auto new_connectivity_3d = test_only::duplicateConnectivity(mesh_3d->connectivity()); + + FaceValuePerEdge<const R3> face_R3_per_edge_3d_new{*new_connectivity_3d, face_R3_per_edge_3d.arrayView()}; + EdgeValuePerFace<const R1x1> edge_R1x1_per_face_3d_new{*new_connectivity_3d, edge_R1x1_per_face_3d.arrayView()}; + EdgeValuePerNode<const R3x3> edge_R3x3_per_node_3d_new{*new_connectivity_3d, edge_R3x3_per_node_3d.arrayView()}; + + checkpointing::writeSubItemValuePerItemVariant("face_R3_per_edge_3d", + EmbeddedData{std::make_shared<DataHandlerT>( + std::make_shared<const SubItemValuePerItemVariant>( + face_R3_per_edge_3d_new))}, + file, checkpoint_group, symbol_table_group); + + checkpointing::writeSubItemValuePerItemVariant("edge_R1x1_per_face_3d", + EmbeddedData{std::make_shared<DataHandlerT>( + std::make_shared<const SubItemValuePerItemVariant>( + edge_R1x1_per_face_3d_new))}, + file, checkpoint_group, symbol_table_group); + + checkpointing::writeSubItemValuePerItemVariant("edge_R3x3_per_node_3d", + EmbeddedData{std::make_shared<DataHandlerT>( + std::make_shared<const SubItemValuePerItemVariant>( + edge_R3x3_per_node_3d_new))}, + file, checkpoint_group, symbol_table_group); + + HighFive::Group global_variables_group = checkpoint_group.createGroup("singleton/global_variables"); + global_variables_group.createAttribute("connectivity_id", + GlobalVariableManager::instance().getConnectivityId()); + global_variables_group.createAttribute("mesh_id", GlobalVariableManager::instance().getMeshId()); + } + + // reset to reuse after resuming + GlobalVariableManager::instance().setConnectivityId(initial_connectivity_id); + + file.flush(); + + checkpointing::ResumingData::create(); + checkpointing::ResumingData::instance().readData(checkpoint_group, nullptr); + + GlobalVariableManager::instance().setConnectivityId(initial_connectivity_id); + { // Read + auto e_cell_B_per_node_1d = + checkpointing::readSubItemValuePerItemVariant("node_B_per_cell_1d", symbol_table_group); + test_only::check_is_same(node_B_per_cell_1d, e_cell_B_per_node_1d); + + auto e_cell_N_per_node_1d = + checkpointing::readSubItemValuePerItemVariant("node_N_per_cell_1d", symbol_table_group); + test_only::check_is_same(node_N_per_cell_1d, e_cell_N_per_node_1d); + + auto e_node_Z_1d = checkpointing::readSubItemValuePerItemVariant("cell_Z_per_node_1d", symbol_table_group); + test_only::check_is_same(cell_Z_per_node_1d, e_node_Z_1d); + + auto e_node_R_1d = checkpointing::readSubItemValuePerItemVariant("cell_R_per_node_1d", symbol_table_group); + test_only::check_is_same(cell_R_per_node_1d, e_node_R_1d); + + auto e_cell_R1_1d = checkpointing::readSubItemValuePerItemVariant("node_R1_per_cell_1d", symbol_table_group); + test_only::check_is_same(node_R1_per_cell_1d, e_cell_R1_1d); + + auto e_node_R2_1d = checkpointing::readSubItemValuePerItemVariant("cell_R2_per_node_1d", symbol_table_group); + test_only::check_is_same(cell_R2_per_node_1d, e_node_R2_1d); + + auto e_face_R3_2d = checkpointing::readSubItemValuePerItemVariant("cell_R3_per_face_2d", symbol_table_group); + test_only::check_is_same(cell_R3_per_face_2d, e_face_R3_2d); + + auto e_node_R2x2_2d = + checkpointing::readSubItemValuePerItemVariant("face_R2x2_per_node_2d", symbol_table_group); + test_only::check_is_same(face_R2x2_per_node_2d, e_node_R2x2_2d); + + auto e_edge_R3_3d = checkpointing::readSubItemValuePerItemVariant("face_R3_per_edge_3d", symbol_table_group); + test_only::check_is_same(face_R3_per_edge_3d, e_edge_R3_3d); + + auto e_face_R1x1_3d = + checkpointing::readSubItemValuePerItemVariant("edge_R1x1_per_face_3d", symbol_table_group); + test_only::check_is_same(edge_R1x1_per_face_3d, e_face_R1x1_3d); + + auto e_node_R3x3_3d = + checkpointing::readSubItemValuePerItemVariant("edge_R3x3_per_node_3d", symbol_table_group); + test_only::check_is_same(edge_R3x3_per_node_3d, e_node_R3x3_3d); + } + checkpointing::ResumingData::destroy(); + } + } + + parallel::barrier(); + if (parallel::rank() == 0) { + std::filesystem::remove_all(std::filesystem::path{tmp_dirname}); + } +}