diff --git a/testing/adios2/engine/bp/CMakeLists.txt b/testing/adios2/engine/bp/CMakeLists.txt index a5d95afecb..5237281f43 100644 --- a/testing/adios2/engine/bp/CMakeLists.txt +++ b/testing/adios2/engine/bp/CMakeLists.txt @@ -130,6 +130,7 @@ bp_gtest_add_tests_helper(WriteReadLocalVariables MPI_ALLOW) bp_gtest_add_tests_helper(WriteReadLocalVariablesSel MPI_ALLOW) bp_gtest_add_tests_helper(WriteReadLocalVariablesSelHighLevel MPI_ALLOW) bp_gtest_add_tests_helper(ChangingShape MPI_ALLOW) +bp_gtest_add_tests_helper(ChangingShapeWithinStep MPI_ALLOW) bp_gtest_add_tests_helper(WriteReadBlockInfo MPI_ALLOW) bp_gtest_add_tests_helper(WriteReadVariableSpan MPI_ALLOW) bp3_bp4_gtest_add_tests_helper(TimeAggregation MPI_ALLOW) diff --git a/testing/adios2/engine/bp/TestBPChangingShape.cpp b/testing/adios2/engine/bp/TestBPChangingShape.cpp index 3942ecd528..9546f094b7 100644 --- a/testing/adios2/engine/bp/TestBPChangingShape.cpp +++ b/testing/adios2/engine/bp/TestBPChangingShape.cpp @@ -18,16 +18,12 @@ #include -#include "../SmallTestData.h" - std::string engineName; // comes from command line class BPChangingShape : public ::testing::Test { public: BPChangingShape() = default; - - SmallTestData m_TestData; }; TEST_F(BPChangingShape, BPWriteReadShape2D) @@ -180,187 +176,6 @@ TEST_F(BPChangingShape, BPWriteReadShape2D) } } -TEST_F(BPChangingShape, MultiBlock) -{ - // Write multiple blocks and change shape in between - // At read, the last shape should be used not the first one - // This test guarantees that one can change the variable shape - // until EndStep() - - const std::string fname("BPChangingShapeMultiblock.bp"); - const int nsteps = 2; - const std::vector nblocks = {2, 3}; - EXPECT_EQ(nsteps, nblocks.size()); - int rank = 0, nproc = 1; - -#if ADIOS2_USE_MPI - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &nproc); - adios2::ADIOS adios(MPI_COMM_WORLD); -#else - adios2::ADIOS adios; -#endif - - // Writer - { - adios2::IO outIO = adios.DeclareIO("Output"); - - if (!engineName.empty()) - { - outIO.SetEngine(engineName); - } - - adios2::Engine writer = outIO.Open(fname, adios2::Mode::Write); - - const size_t dim0 = static_cast(nproc); - const size_t off0 = static_cast(rank); - auto var = - outIO.DefineVariable("v", {dim0, 1}, {off0, 0}, {1, 1}); - - if (!rank) - { - std::cout << "Writing:" << std::endl; - } - for (size_t i = 0; i < nsteps; i++) - { - writer.BeginStep(); - - double value = - static_cast(rank) + static_cast(i + 1) / 10.0; - - for (size_t j = 0; j < static_cast(nblocks[i]); j++) - { - var.SetShape({dim0, j + 1}); - var.SetSelection({{off0, j}, {1, 1}}); - - if (!rank) - { - std::cout << "Step " << i << " block " << j << " shape (" - << var.Shape()[0] << ", " << var.Shape()[1] << ")" - << " value = " << value << std::endl; - } - - writer.Put(var, &value, adios2::Mode::Sync); - value += 0.01; - } - writer.EndStep(); - } - writer.Close(); - } - - // Reader with streaming - { - adios2::IO inIO = adios.DeclareIO("Input"); - - if (!engineName.empty()) - { - inIO.SetEngine(engineName); - } - adios2::Engine reader = inIO.Open(fname, adios2::Mode::Read); - - if (!rank) - { - std::cout << "Reading as stream with BeginStep/EndStep:" - << std::endl; - } - - int step = 0; - while (true) - { - adios2::StepStatus status = - reader.BeginStep(adios2::StepMode::Read); - - if (status != adios2::StepStatus::OK) - { - break; - } - - size_t expected_shape = nblocks[step]; - - auto var = inIO.InquireVariable("v"); - EXPECT_TRUE(var); - - if (!rank) - { - - std::cout << "Step " << step << " shape (" << var.Shape()[0] - << ", " << var.Shape()[1] << ")" << std::endl; - } - - EXPECT_EQ(var.Shape()[0], nproc); - EXPECT_EQ(var.Shape()[1], expected_shape); - - var.SetSelection( - {{0, 0}, {static_cast(nproc), expected_shape}}); - - // Check data on rank 0 - if (!rank) - { - std::vector data(nproc * expected_shape); - reader.Get(var, data.data()); - - reader.PerformGets(); - - for (int i = 0; i < nproc; i++) - { - double value = static_cast(i) + - static_cast(step + 1) / 10.0; - - for (int j = 0; j < nblocks[step]; j++) - { - EXPECT_EQ(data[i * nblocks[step] + j], value); - value += 0.01; - } - } - } - - reader.EndStep(); - ++step; - } - reader.Close(); - } - - // Reader with file reading - { - adios2::IO inIO = adios.DeclareIO("InputFile"); - - if (!engineName.empty()) - { - inIO.SetEngine(engineName); - } - adios2::Engine reader = - inIO.Open(fname, adios2::Mode::ReadRandomAccess); - - if (!rank) - { - std::cout << "Reading as file with SetStepSelection:" << std::endl; - } - - auto var = inIO.InquireVariable("v"); - EXPECT_TRUE(var); - for (int step = 0; step < nsteps; step++) - { - var.SetStepSelection({step, 1}); - if (!rank) - { - std::cout << "Step " << step << " shape (" << var.Shape()[0] - << ", " << var.Shape()[1] << ")" << std::endl; - } - size_t expected_shape = nblocks[step]; - EXPECT_EQ(var.Shape()[0], nproc); - EXPECT_EQ(var.Shape()[1], expected_shape); - - var.SetSelection( - {{0, 0}, {static_cast(nproc), expected_shape}}); - - std::vector data(nproc * expected_shape); - reader.Get(var, data.data()); - - EXPECT_THROW(reader.EndStep(), std::logic_error); - } - } -} - int main(int argc, char **argv) { #if ADIOS2_USE_MPI diff --git a/testing/adios2/engine/bp/TestBPChangingShapeWithinStep.cpp b/testing/adios2/engine/bp/TestBPChangingShapeWithinStep.cpp new file mode 100644 index 0000000000..ba1388fe69 --- /dev/null +++ b/testing/adios2/engine/bp/TestBPChangingShapeWithinStep.cpp @@ -0,0 +1,293 @@ +/* + * Distributed under the OSI-approved Apache License, Version 2.0. See + * accompanying file Copyright.txt for details. + */ +#include +#include + +#include +#include //std::iota +#include +#include + +#include +#include + +#include + +std::string engineName; // comes from command line + +using ParamType = std::tuple; + +class BPChangingShapeWithinStep : public ::testing::TestWithParam +{ +public: + BPChangingShapeWithinStep() = default; + virtual void SetUp(){}; + virtual void TearDown(){}; +}; + +TEST_P(BPChangingShapeWithinStep, MultiBlock) +{ + // Write multiple blocks and change shape in between + // At read, the last shape should be used not the first one + // This test guarantees that one can change the variable shape + // until EndStep() + + auto operatorName = std::get<0>(GetParam()); + auto params = std::get<1>(GetParam()); + double epsilon = std::get<2>(GetParam()); + + const std::string fname("BPChangingShapeMultiblock_" + operatorName + + ".bp"); + const int nsteps = 2; + const std::vector nblocks = {2, 3}; + const int N = 16384; // size of one block (should be big enough to compress) + EXPECT_EQ(nsteps, nblocks.size()); + int rank = 0, nproc = 1; + +#if ADIOS2_USE_MPI + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &nproc); + adios2::ADIOS adios(MPI_COMM_WORLD); +#else + adios2::ADIOS adios; +#endif + + // Writer + { + adios2::IO outIO = adios.DeclareIO("Output"); + + if (!engineName.empty()) + { + outIO.SetEngine(engineName); + } + + adios2::Engine writer = outIO.Open(fname, adios2::Mode::Write); + + const size_t dim0 = static_cast(nproc); + const size_t off0 = static_cast(rank); + auto var = + outIO.DefineVariable("v", {dim0, 1}, {off0, 0}, {1, 1}); + + if (operatorName != "none") + { + auto op = adios.DefineOperator("compressor", operatorName, params); + var.AddOperation(op); + } + + if (!rank) + { + std::cout << "Writing to :" << fname; + if (operatorName != "none") + { + std::cout << " with operator " << operatorName; + } + std::cout << std::endl; + } + for (size_t i = 0; i < nsteps; i++) + { + writer.BeginStep(); + + double value = + static_cast(rank) + static_cast(i + 1) / 10.0; + + for (size_t j = 0; j < static_cast(nblocks[i]); j++) + { + std::vector data(N, value); + var.SetShape({dim0, (j + 1) * N}); + var.SetSelection({{off0, j * N}, {1, N}}); + + if (!rank) + { + std::cout << "Step " << i << " block " << j << " shape (" + << var.Shape()[0] << ", " << var.Shape()[1] << ")" + << " value = " << value << " data[] = " << data[0] + << " .. " << data[N - 1] << std::endl; + } + + writer.Put(var, data.data(), adios2::Mode::Sync); + value += 0.01; + } + writer.EndStep(); + } + writer.Close(); + } + + // Reader with streaming + { + adios2::IO inIO = adios.DeclareIO("Input"); + + if (!engineName.empty()) + { + inIO.SetEngine(engineName); + } + adios2::Engine reader = inIO.Open(fname, adios2::Mode::Read); + + if (!rank) + { + std::cout << "Reading as stream with BeginStep/EndStep:" + << std::endl; + } + + int step = 0; + while (true) + { + adios2::StepStatus status = + reader.BeginStep(adios2::StepMode::Read); + + if (status != adios2::StepStatus::OK) + { + break; + } + + size_t expected_shape = N * nblocks[step]; + + auto var = inIO.InquireVariable("v"); + EXPECT_TRUE(var); + + if (!rank) + { + + std::cout << "Step " << step << " shape (" << var.Shape()[0] + << ", " << var.Shape()[1] << ")" << std::endl; + } + + EXPECT_EQ(var.Shape()[0], nproc); + EXPECT_EQ(var.Shape()[1], expected_shape); + + var.SetSelection( + {{0, 0}, {static_cast(nproc), expected_shape}}); + + // Check data on rank 0 + if (!rank) + { + std::vector data(nproc * expected_shape); + reader.Get(var, data.data()); + + reader.PerformGets(); + + for (int i = 0; i < nproc; i++) + { + double value = static_cast(i) + + static_cast(step + 1) / 10.0; + + for (int j = 0; j < nblocks[step]; j++) + { + EXPECT_LE( + fabs(data[(i * nblocks[step] + j) * N] - value), + epsilon); + value += 0.01; + } + } + } + + reader.EndStep(); + ++step; + } + reader.Close(); + } + + // Reader with file reading + { + adios2::IO inIO = adios.DeclareIO("InputFile"); + + if (!engineName.empty()) + { + inIO.SetEngine(engineName); + } + adios2::Engine reader = + inIO.Open(fname, adios2::Mode::ReadRandomAccess); + + if (!rank) + { + std::cout << "Reading as file with SetStepSelection:" << std::endl; + } + + auto var = inIO.InquireVariable("v"); + EXPECT_TRUE(var); + for (int step = 0; step < nsteps; step++) + { + var.SetStepSelection({step, 1}); + if (!rank) + { + std::cout << "Step " << step << " shape (" << var.Shape()[0] + << ", " << var.Shape()[1] << ")" << std::endl; + } + size_t expected_shape = N * nblocks[step]; + EXPECT_EQ(var.Shape()[0], nproc); + EXPECT_EQ(var.Shape()[1], expected_shape); + + var.SetSelection( + {{0, 0}, {static_cast(nproc), expected_shape}}); + + std::vector data(nproc * expected_shape); + reader.Get(var, data.data()); + reader.PerformGets(); + + for (int i = 0; i < nproc; i++) + { + double value = static_cast(i) + + static_cast(step + 1) / 10.0; + + for (int j = 0; j < nblocks[step]; j++) + { + EXPECT_LE(fabs(data[(i * nblocks[step] + j) * N] - value), + epsilon); + value += 0.01; + } + } + + EXPECT_THROW(reader.EndStep(), std::logic_error); + } + } +} + +adios2::Params paccuracy = {{"accuracy", "0.001"}}; +adios2::Params pblosc = {{"clevel", "9"}}; + +std::vector p = {{"none", paccuracy, 0.0} +#ifdef ADIOS2_HAVE_BLOSC2 + , + {"blosc", pblosc, 0.0} +#endif +#ifdef ADIOS2_HAVE_MGARD + , + {"mgard", paccuracy, 0.001} +#endif +#ifdef ADIOS2_HAVE_ZFP +#ifndef ADIOS2_HAVE_ZFP_CUDA // only test on CPU + , + {"zfp", paccuracy, 0.001} +#endif +#endif + +}; + +INSTANTIATE_TEST_SUITE_P(Multiblock, BPChangingShapeWithinStep, + ::testing::ValuesIn(p)); + +int main(int argc, char **argv) +{ +#if ADIOS2_USE_MPI + int provided; + + // MPI_THREAD_MULTIPLE is only required if you enable the SST MPI_DP + MPI_Init_thread(nullptr, nullptr, MPI_THREAD_MULTIPLE, &provided); +#endif + + int result; + ::testing::InitGoogleTest(&argc, argv); + + if (argc > 1) + { + engineName = std::string(argv[1]); + } + result = RUN_ALL_TESTS(); + +#if ADIOS2_USE_MPI + MPI_Finalize(); +#endif + + return result; +}