diff --git a/CHANGELOG.md b/CHANGELOG.md index 06ebee3cf..0582d098c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to ### For users #### Added +* Add tests for HDF5 precision conversion * Improved messages for specification tree errors, with file & line numbers and support for file names from Paraconf 1.1, [#657](https://github.com/pdidev/pdi/issues/657) diff --git a/plugins/decl_hdf5/tests/CMakeLists.txt b/plugins/decl_hdf5/tests/CMakeLists.txt index db7a8f259..e1f247d80 100644 --- a/plugins/decl_hdf5/tests/CMakeLists.txt +++ b/plugins/decl_hdf5/tests/CMakeLists.txt @@ -52,7 +52,7 @@ endif() ) add_executable(decl_hdf5_tests decl_hdf5_tests.cxx) -target_link_libraries(decl_hdf5_tests PDI::PDI_C GTest::gmock GTest::gmock_main GTest::gtest GTest::gtest_main) +target_link_libraries(decl_hdf5_tests PDI::PDI_C GTest::gmock GTest::gmock_main GTest::gtest GTest::gtest_main ${HDF5_DEPS}) gtest_discover_tests(decl_hdf5_tests) # compression test diff --git a/plugins/decl_hdf5/tests/decl_hdf5_tests.cxx b/plugins/decl_hdf5/tests/decl_hdf5_tests.cxx index d9abb9c90..5314f5c02 100644 --- a/plugins/decl_hdf5/tests/decl_hdf5_tests.cxx +++ b/plugins/decl_hdf5/tests/decl_hdf5_tests.cxx @@ -28,6 +28,8 @@ #include #include +#include + #include using PDI::make_random; @@ -845,3 +847,126 @@ logging: trace ); PDI_expose("array_data", array_data.data(), PDI_OUT); } + +/* Precision conversion with decl_hdf5 + * data in double precision + * file dataset in double, float, and int + */ +TEST_F(DeclHdf5, PrecisionConversion) +{ + InitPdi(PC_parse_string(R"==( +logging: trace +metadata: + N: int +data: + array: {type: array, size: [$N, $N], subtype: double} +plugins: + decl_hdf5: + - file: d2d_test.h5 + datasets: + double_ds: {type: array, size: [$N, $N], subtype: double} + write: + array: + dataset: double_ds + - file: d2f_test.h5 + datasets: + float_ds: {type: array, size: [$N, $N], subtype: float} + write: + array: + dataset: float_ds + - file: d2i_test.h5 + datasets: + int_ds: {type: array, size: [$N, $N], subtype: int} + write: + array: + dataset: int_ds +)==")); + + EXPECT_FALSE(std::filesystem::exists("d2d_test.h5")); + EXPECT_FALSE(std::filesystem::exists("d2f_test.h5")); + EXPECT_FALSE(std::filesystem::exists("d2i_test.h5")); + + static constexpr size_t N = 100; + PDI_expose("N", &N, PDI_OUT); + + auto const test_array = make_a, N>>(); + PDI_expose("array", test_array.data(), PDI_OUT); + + ASSERT_TRUE(std::filesystem::exists("d2d_test.h5")); + ASSERT_TRUE(std::filesystem::exists("d2f_test.h5")); + ASSERT_TRUE(std::filesystem::exists("d2i_test.h5")); + + // read double precision dataset and compare + hid_t file_id = H5Fopen("d2d_test.h5", H5F_ACC_RDONLY, H5P_DEFAULT); + hid_t dataset_id = H5Dopen2(file_id, "/double_ds", H5P_DEFAULT); + hid_t type_id = H5Dget_type(dataset_id); + ASSERT_GE(file_id, 0); + ASSERT_GE(dataset_id, 0); + ASSERT_GE(type_id, 0); + + EXPECT_GT(H5Tequal(type_id, H5T_IEEE_F64LE), 0); + std::array, N> read_double_array; + + herr_t status = H5Dread(dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, read_double_array.data()->data()); + ASSERT_GE(status, 0); + + EXPECT_EQ(test_array, read_double_array); + + H5Tclose(type_id); + H5Dclose(dataset_id); + H5Fclose(file_id); + + // read simple precision dataset and compare + file_id = H5Fopen("d2f_test.h5", H5F_ACC_RDONLY, H5P_DEFAULT); + dataset_id = H5Dopen2(file_id, "/float_ds", H5P_DEFAULT); + type_id = H5Dget_type(dataset_id); + ASSERT_GE(file_id, 0); + ASSERT_GE(dataset_id, 0); + ASSERT_GE(type_id, 0); + + EXPECT_GT(H5Tequal(type_id, H5T_IEEE_F32LE), 0); + std::array< std::array, N > read_float_array; + + status = H5Dread(dataset_id, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, read_float_array.data()->data()); + ASSERT_GE(status, 0); + +#if defined(__clang__) && (__clang_major__ == 15) +// Skipping the numerical comparaison because Clang 15 has known issues with ranges. +#else + EXPECT_THAT(read_float_array, testing::ElementsAreArray(test_array | std::views::transform([](std::array const & aref) { + return testing::Pointwise(testing::FloatEq(), aref); + }))); +#endif + + H5Tclose(type_id); + H5Dclose(dataset_id); + H5Fclose(file_id); + + // read integer dataset and compare + file_id = H5Fopen("d2i_test.h5", H5F_ACC_RDONLY, H5P_DEFAULT); + dataset_id = H5Dopen2(file_id, "/int_ds", H5P_DEFAULT); + type_id = H5Dget_type(dataset_id); + ASSERT_GE(file_id, 0); + ASSERT_GE(dataset_id, 0); + ASSERT_GE(type_id, 0); + + EXPECT_GT(H5Tequal(type_id, H5T_STD_I32LE), 0); + std::array< std::array, N > read_int_array; + + status = H5Dread(dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, read_int_array.data()->data()); + ASSERT_GE(status, 0); + +#if defined(__clang__) && (__clang_major__ == 15) +// Skipping the numerical comparaison because Clang 15 has known issues with ranges. +#else + EXPECT_THAT(read_int_array, testing::ElementsAreArray(test_array | std::views::transform([](std::array const & aref) { + return testing::ElementsAreArray(aref | std::views::transform([](double const & ref) { + return static_cast(ref); + })); + }))); +#endif + + H5Tclose(type_id); + H5Dclose(dataset_id); + H5Fclose(file_id); +}