diff --git a/examples/basics/joinedArray/joinedArray_write.cpp b/examples/basics/joinedArray/joinedArray_write.cpp index 73aa4fc6b251665d8c1f4f205a127c58755dc77f..d22438dda7dfe985b869d9abc8c9911efc186ad0 100644 --- a/examples/basics/joinedArray/joinedArray_write.cpp +++ b/examples/basics/joinedArray/joinedArray_write.cpp @@ -70,6 +70,8 @@ int main(int argc, char *argv[]) // Get io settings from the config file or // create one with default settings here adios2::IO &io = adios.DeclareIO("Output"); + // io.SetEngine("ADIOS1Writer"); + // io.AddTransport("File", {{"library", "MPI"}}); /* * Define joinable local array: type, name, global and local size diff --git a/source/adios2/core/VariableBase.cpp b/source/adios2/core/VariableBase.cpp index 07759db41f82a76badab8a28635159d417befadf..713485cb904fe5920266488acc8e147123d4bdef 100644 --- a/source/adios2/core/VariableBase.cpp +++ b/source/adios2/core/VariableBase.cpp @@ -60,8 +60,7 @@ void VariableBase::SetSelection(const Dims start, const Dims count) } if (m_ShapeID == ShapeID::GlobalArray && - (m_Shape.size() != m_Count.size() || - m_Shape.size() != m_Start.size())) + (m_Shape.size() != count.size() || m_Shape.size() != start.size())) { throw std::invalid_argument("ERROR: count and start must be the " "same size as shape for variable " + @@ -154,85 +153,133 @@ void VariableBase::ClearTransforms() noexcept { m_TransformsInfo.clear(); } // PRIVATE void VariableBase::InitShapeType() { - if (!m_Shape.empty() && m_Start.empty() && m_Count.empty()) + if (!m_Shape.empty()) { - if (m_DebugMode) + if (std::count(m_Shape.begin(), m_Shape.end(), JoinedDim) == 1) { - if (m_ConstantDims) + if (!m_Start.empty() && + std::count(m_Start.begin(), m_Start.end(), 0) != m_Start.size()) { - throw std::invalid_argument( - "ERROR: isConstantShape (true) argument is invalid " - "with empty start and count " - "arguments\n"); + throw std::invalid_argument("ERROR: The Start array must be " + "empty or full-zero when defining " + "a Joined Array in call to " + "DefineVariable " + + m_Name + "\n"); } + m_ShapeID = ShapeID::JoinedArray; } - - m_ShapeID = ShapeID::GlobalArray; - } - else if (!m_Shape.empty() && m_Shape.size() == m_Start.size() && - m_Shape.size() == m_Count.size()) - { - if (m_DebugMode) + else if (std::count(m_Shape.begin(), m_Shape.end(), JoinedDim) > 1) { - auto lf_LargerThanError = [&](const unsigned int i, - const std::string dims1, - const std::string dims2) { - - const std::string iString(std::to_string(i)); - throw std::invalid_argument( - "ERROR: " + dims1 + "[" + iString + "] > " + dims2 + "[" + - iString + "], in DefineVariable " + m_Name + "\n"); - }; - - for (unsigned int i = 0; i < m_Shape.size(); ++i) + throw std::invalid_argument( + "ERROR: variable can't have more than one " + "JoinedDim in shape argument, in call to " + "DefineVariable " + + m_Name + "\n"); + } + else if (m_Start.empty() && m_Count.empty()) + { + if (m_Shape.size() == 1 && m_Shape.front() == LocalValueDim) { - if (m_Count[i] > m_Shape[i]) + m_ShapeID = ShapeID::LocalValue; + m_SingleValue = true; + } + else + { + if (m_DebugMode) { - lf_LargerThanError(i, "count", "shape"); + if (m_ConstantDims) + { + throw std::invalid_argument( + "ERROR: isConstantShape (true) argument is invalid " + "with empty start and count " + "arguments in call to " + "DefineVariable " + + m_Name + "\n"); + } } - if (m_Start[i] > m_Shape[i]) + + m_ShapeID = ShapeID::GlobalArray; + } + } + else if (m_Shape.size() == m_Start.size() && + m_Shape.size() == m_Count.size()) + { + if (m_DebugMode) + { + auto lf_LargerThanError = [&](const unsigned int i, + const std::string dims1, + const std::string dims2) { + + const std::string iString(std::to_string(i)); + throw std::invalid_argument( + "ERROR: " + dims1 + "[" + iString + "] > " + dims2 + + "[" + iString + "], in DefineVariable " + m_Name + + "\n"); + }; + + for (unsigned int i = 0; i < m_Shape.size(); ++i) { - lf_LargerThanError(i, "start", "shape"); + if (m_Count[i] > m_Shape[i]) + { + lf_LargerThanError(i, "count", "shape"); + } + if (m_Start[i] > m_Shape[i]) + { + lf_LargerThanError(i, "start", "shape"); + } } } + m_ShapeID = ShapeID::GlobalArray; + } + else + { + throw std::invalid_argument("ERROR: the " + "combination of shape, start and count " + "arguments is inconsistent, in call to " + "DefineVariable " + + m_Name + "\n"); } - - m_ShapeID = ShapeID::GlobalArray; - } - else if (m_Shape.empty() && m_Start.empty() && m_Count.empty()) - { - m_ShapeID = ShapeID::GlobalValue; - m_SingleValue = true; - } - else if (m_Shape.empty() && m_Start.empty() && !m_Count.empty()) - { - m_ShapeID = ShapeID::LocalArray; - } - else if (m_Shape.size() == 1 && m_Shape.front() == LocalValueDim) - { - m_ShapeID = ShapeID::LocalValue; - m_SingleValue = true; - } - else if (!m_Shape.empty() && - std::count(m_Shape.begin(), m_Shape.end(), JoinedDim) == 1) - { - m_ShapeID = ShapeID::JoinedArray; } - else if (!m_Shape.empty() && - std::count(m_Shape.begin(), m_Shape.end(), JoinedDim) > 1) + else //(m_Shape.empty()) { - throw std::invalid_argument("ERROR: variable can't have more than one " - "JoinedDim in shape argument, in call to " - "DefineVariable " + - m_Name + "\n"); + if (m_Start.empty()) + { + if (m_Count.empty()) + { + m_ShapeID = ShapeID::GlobalValue; + m_SingleValue = true; + } + else if (m_Start.empty() && !m_Count.empty()) + { + m_ShapeID = ShapeID::LocalArray; + } + } + else + { + throw std::invalid_argument( + "ERROR: if the " + "shape is empty, start must be empty as well, in call to " + "DefineVariable " + + m_Name + "\n"); + } } - else + + /* Extra checks for invalid settings */ + if (m_ShapeID != ShapeID::LocalValue) { - throw std::invalid_argument("ERROR: the " - "combination of shape, start and count " - "arguments is inconsistent, in call to " - "DefineVariable " + - m_Name + "\n"); + if ((!m_Shape.empty() && + std::count(m_Shape.begin(), m_Shape.end(), LocalValueDim) > 0) || + (!m_Start.empty() && + std::count(m_Start.begin(), m_Start.end(), LocalValueDim) > 0) || + (!m_Count.empty() && + std::count(m_Count.begin(), m_Count.end(), LocalValueDim) > 0)) + { + throw std::invalid_argument("ERROR: LocalValueDim is only " + "allowed in a {LocalValueDim} " + "shape in call to " + "DefineVariable " + + m_Name + "\n"); + } } } diff --git a/testing/adios2/interface/CMakeLists.txt b/testing/adios2/interface/CMakeLists.txt index e229416de28347dbe46931805724e7d05bbdfdb2..e68ccfa6187ab17710e4bca93579ede6ded95079 100644 --- a/testing/adios2/interface/CMakeLists.txt +++ b/testing/adios2/interface/CMakeLists.txt @@ -6,4 +6,8 @@ add_executable(TestADIOSInterfaceWrite TestADIOSInterfaceWrite.cpp) target_link_libraries(TestADIOSInterfaceWrite adios2 gtest gtest_main) +add_executable(TestADIOSDefineVariable TestADIOSDefineVariable.cpp) +target_link_libraries(TestADIOSDefineVariable adios2 gtest gtest_main) + gtest_add_tests(TARGET TestADIOSInterfaceWrite) +gtest_add_tests(TARGET TestADIOSDefineVariable) \ No newline at end of file diff --git a/testing/adios2/interface/TestADIOSDefineVariable.cpp b/testing/adios2/interface/TestADIOSDefineVariable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..100f05fc4e7df69a6512a749cf7b3cf1eb705260 --- /dev/null +++ b/testing/adios2/interface/TestADIOSDefineVariable.cpp @@ -0,0 +1,355 @@ +#include <cstdint> + +#include <iostream> +#include <stdexcept> + +#include <adios2.h> + +#include <gtest/gtest.h> + +class ADIOSDefineVariableTest : public ::testing::Test +{ +public: + ADIOSDefineVariableTest() : adios(true), io(adios.DeclareIO("TestIO")) {} + +protected: + // virtual void SetUp() { } + + // virtual void TearDown() { } + + adios2::ADIOS adios; + adios2::IO &io; +}; + +TEST_F(ADIOSDefineVariableTest, DefineGlobalValue) +{ + // Define ADIOS global value + auto &globalvalue = io.DefineVariable<int>("globalvalue"); + + // Verify the return type is as expected + ::testing::StaticAssertTypeEq<decltype(globalvalue), + adios2::Variable<int> &>(); + + // Verify the dimensions, name, and type are correct + ASSERT_EQ(globalvalue.m_Shape.size(), 0); + EXPECT_EQ(globalvalue.m_Start.size(), 0); + EXPECT_EQ(globalvalue.m_Count.size(), 0); + EXPECT_EQ(globalvalue.m_Name, "globalvalue"); + EXPECT_EQ(globalvalue.m_Type, "int"); +} + +TEST_F(ADIOSDefineVariableTest, DefineLocalValue) +{ + // Define ADIOS local value (a value changing across processes) + auto &localvalue = + io.DefineVariable<int>("localvalue", {adios2::LocalValueDim}); + + // Verify the return type is as expected + ::testing::StaticAssertTypeEq<decltype(localvalue), + adios2::Variable<int> &>(); + + // Verify the dimensions, name, and type are correct + ASSERT_EQ(localvalue.m_Shape.size(), 1); + EXPECT_EQ(localvalue.m_Shape[0], adios2::LocalValueDim); + EXPECT_EQ(localvalue.m_Start.size(), 0); + EXPECT_EQ(localvalue.m_Count.size(), 0); + EXPECT_EQ(localvalue.m_Name, "localvalue"); + EXPECT_EQ(localvalue.m_Type, "int"); +} + +TEST_F(ADIOSDefineVariableTest, DefineGlobalArray) +{ + // Define ADIOS global array + std::size_t n = 50; + auto &globalarray = io.DefineVariable<int>("globalarray", {100, n, 30}, + {50, n / 2, 0}, {10, n / 2, 30}); + + // Verify the return type is as expected + ::testing::StaticAssertTypeEq<decltype(globalarray), + adios2::Variable<int> &>(); + + // Verify the dimensions, name, and type are correct + ASSERT_EQ(globalarray.m_Shape.size(), 3); + EXPECT_EQ(globalarray.m_Shape[0], 100); + EXPECT_EQ(globalarray.m_Shape[1], n); + EXPECT_EQ(globalarray.m_Shape[2], 30); + EXPECT_EQ(globalarray.m_Start.size(), 3); + EXPECT_EQ(globalarray.m_Start[0], 50); + EXPECT_EQ(globalarray.m_Start[1], n / 2); + EXPECT_EQ(globalarray.m_Start[2], 0); + EXPECT_EQ(globalarray.m_Count.size(), 3); + EXPECT_EQ(globalarray.m_Count[0], 10); + EXPECT_EQ(globalarray.m_Count[1], n / 2); + EXPECT_EQ(globalarray.m_Count[2], 30); + EXPECT_EQ(globalarray.m_Name, "globalarray"); + EXPECT_EQ(globalarray.m_Type, "int"); +} + +TEST_F(ADIOSDefineVariableTest, DefineGlobalArrayWithSelections) +{ + // Define ADIOS global array with postponed size definition in SetSelection + std::size_t n = 50; + auto &globalarray = io.DefineVariable<int>("globalarray", {100, n, 30}); + + // Verify the return type is as expected + ::testing::StaticAssertTypeEq<decltype(globalarray), + adios2::Variable<int> &>(); + + // Make a 3D selection to describe the local dimensions of the + // variable we write and its offsets in the global spaces + adios2::SelectionBoundingBox sel({50, n / 2, 0}, {10, n / 2, 30}); + globalarray.SetSelection(sel); + + // Verify the dimensions, name, and type are correct + ASSERT_EQ(globalarray.m_Shape.size(), 3); + EXPECT_EQ(globalarray.m_Shape[0], 100); + EXPECT_EQ(globalarray.m_Shape[1], n); + EXPECT_EQ(globalarray.m_Shape[2], 30); + EXPECT_EQ(globalarray.m_Start.size(), 3); + EXPECT_EQ(globalarray.m_Start[0], 50); + EXPECT_EQ(globalarray.m_Start[1], n / 2); + EXPECT_EQ(globalarray.m_Start[2], 0); + EXPECT_EQ(globalarray.m_Count.size(), 3); + EXPECT_EQ(globalarray.m_Count[0], 10); + EXPECT_EQ(globalarray.m_Count[1], n / 2); + EXPECT_EQ(globalarray.m_Count[2], 30); + EXPECT_EQ(globalarray.m_Name, "globalarray"); + EXPECT_EQ(globalarray.m_Type, "int"); +} + +TEST_F(ADIOSDefineVariableTest, DefineGlobalArrayConstantDims) +{ + // Define ADIOS global array with locked-down dimensions + std::size_t n = 50; + auto &globalarray = io.DefineVariable<int>( + "globalarray", {100, n, 30}, {50, n / 2, 0}, {10, n / 2, 30}, true); + + // Verify the return type is as expected + ::testing::StaticAssertTypeEq<decltype(globalarray), + adios2::Variable<int> &>(); + + adios2::SelectionBoundingBox sel({50, n / 2, 0}, {10, n / 2, 30}); + EXPECT_THROW(globalarray.SetSelection(sel), std::invalid_argument); + + // Verify the dimensions, name, and type are correct + ASSERT_EQ(globalarray.m_Shape.size(), 3); + EXPECT_EQ(globalarray.m_Shape[0], 100); + EXPECT_EQ(globalarray.m_Shape[1], n); + EXPECT_EQ(globalarray.m_Shape[2], 30); + EXPECT_EQ(globalarray.m_Start.size(), 3); + EXPECT_EQ(globalarray.m_Start[0], 50); + EXPECT_EQ(globalarray.m_Start[1], n / 2); + EXPECT_EQ(globalarray.m_Start[2], 0); + EXPECT_EQ(globalarray.m_Count.size(), 3); + EXPECT_EQ(globalarray.m_Count[0], 10); + EXPECT_EQ(globalarray.m_Count[1], n / 2); + EXPECT_EQ(globalarray.m_Count[2], 30); + EXPECT_EQ(globalarray.m_Name, "globalarray"); + EXPECT_EQ(globalarray.m_Type, "int"); +} + +TEST_F(ADIOSDefineVariableTest, DefineGlobalArrayInvalidLocalValueDim) +{ + // Define ADIOS global array + std::size_t n = 50; + adios2::Variable<int> *globalarray; + EXPECT_THROW(globalarray = &io.DefineVariable<int>( + "globalarray", {100, adios2::LocalValueDim, 30}, + {50, n / 2, 0}, {10, n / 2, 30}), + std::invalid_argument); +} + +TEST_F(ADIOSDefineVariableTest, DefineLocalArray) +{ + // Define ADIOS local array (no global dimensions, no offsets) + std::size_t n = 50; + auto &localarray = + io.DefineVariable<int>("localarray", {}, {}, {10, n / 2, 30}); + + // Verify the return type is as expected + ::testing::StaticAssertTypeEq<decltype(localarray), + adios2::Variable<int> &>(); + + // Verify the dimensions, name, and type are correct + ASSERT_EQ(localarray.m_Shape.size(), 0); + EXPECT_EQ(localarray.m_Start.size(), 0); + EXPECT_EQ(localarray.m_Count.size(), 3); + EXPECT_EQ(localarray.m_Count[0], 10); + EXPECT_EQ(localarray.m_Count[1], n / 2); + EXPECT_EQ(localarray.m_Count[2], 30); + EXPECT_EQ(localarray.m_Name, "localarray"); + EXPECT_EQ(localarray.m_Type, "int"); + EXPECT_EQ(localarray.m_ShapeID, adios2::ShapeID::LocalArray); +} + +TEST_F(ADIOSDefineVariableTest, DefineLocalArrayWithSelection) +{ + // Define ADIOS local array with postponed size definition in SetSelection + std::size_t n = 50; + auto &localarray = io.DefineVariable<int>( + "localarray", {}, {}, + {adios2::UnknownDim, adios2::UnknownDim, adios2::UnknownDim}); + + // Verify the return type is as expected + ::testing::StaticAssertTypeEq<decltype(localarray), + adios2::Variable<int> &>(); + + ASSERT_EQ(localarray.m_Shape.size(), 0); + EXPECT_EQ(localarray.m_Start.size(), 0); + EXPECT_EQ(localarray.m_Count.size(), 3); + EXPECT_EQ(localarray.m_Count[0], 0); + EXPECT_EQ(localarray.m_Count[1], 0); + EXPECT_EQ(localarray.m_Count[2], 0); + EXPECT_EQ(localarray.m_Name, "localarray"); + EXPECT_EQ(localarray.m_Type, "int"); + EXPECT_EQ(localarray.m_ShapeID, adios2::ShapeID::LocalArray); + + // Make a 3D selection to describe the local dimensions of the + // variable we write + adios2::SelectionBoundingBox sel({}, {10, n / 2, 30}); + localarray.SetSelection(sel); + + adios2::SelectionBoundingBox selbad({50, n / 2, 0}, {10, n / 2, 30}); + EXPECT_THROW(localarray.SetSelection(selbad), std::invalid_argument); + + // Verify the dimensions, name, and type are correct + ASSERT_EQ(localarray.m_Shape.size(), 0); + EXPECT_EQ(localarray.m_Start.size(), 0); + EXPECT_EQ(localarray.m_Count.size(), 3); + EXPECT_EQ(localarray.m_Count[0], 10); + EXPECT_EQ(localarray.m_Count[1], n / 2); + EXPECT_EQ(localarray.m_Count[2], 30); + EXPECT_EQ(localarray.m_Name, "localarray"); + EXPECT_EQ(localarray.m_Type, "int"); + EXPECT_EQ(localarray.m_ShapeID, adios2::ShapeID::LocalArray); +} + +TEST_F(ADIOSDefineVariableTest, DefineLocalArrayConstantDims) +{ + // Define ADIOS local array with locked down dimensions + std::size_t n = 50; + auto &localarray = + io.DefineVariable<int>("localarray", {}, {}, {10, n / 2, 30}, true); + + // Verify the return type is as expected + ::testing::StaticAssertTypeEq<decltype(localarray), + adios2::Variable<int> &>(); + + adios2::SelectionBoundingBox sel({}, {10, n / 2, 30}); + EXPECT_THROW(localarray.SetSelection(sel), std::invalid_argument); + + // Verify the dimensions, name, and type are correct + ASSERT_EQ(localarray.m_Shape.size(), 0); + EXPECT_EQ(localarray.m_Start.size(), 0); + EXPECT_EQ(localarray.m_Count.size(), 3); + EXPECT_EQ(localarray.m_Count[0], 10); + EXPECT_EQ(localarray.m_Count[1], n / 2); + EXPECT_EQ(localarray.m_Count[2], 30); + EXPECT_EQ(localarray.m_Name, "localarray"); + EXPECT_EQ(localarray.m_Type, "int"); + EXPECT_EQ(localarray.m_ShapeID, adios2::ShapeID::LocalArray); +} + +TEST_F(ADIOSDefineVariableTest, DefineLocalArrayInvalidOffsets) +{ + // Define ADIOS local array but try to add offsets + std::size_t n = 50; + + adios2::Variable<int> *localarray; + EXPECT_THROW(localarray = &io.DefineVariable<int>( + "localarray", {}, {50, n / 2, 0}, {10, n / 2, 30}), + std::invalid_argument); +} + +TEST_F(ADIOSDefineVariableTest, DefineJoinedArrayFirstDim) +{ + // Define ADIOS joined array + std::size_t n = 50; + auto &joinedarray = io.DefineVariable<int>( + "joinedarray", {adios2::JoinedDim, n, 30}, {}, {10, n, 30}); + + // Verify the return type is as expected + ::testing::StaticAssertTypeEq<decltype(joinedarray), + adios2::Variable<int> &>(); + + // Verify the dimensions, name, and type are correct + ASSERT_EQ(joinedarray.m_Shape.size(), 3); + EXPECT_EQ(joinedarray.m_Shape[0], adios2::JoinedDim); + EXPECT_EQ(joinedarray.m_Shape[1], n); + EXPECT_EQ(joinedarray.m_Shape[2], 30); + EXPECT_EQ(joinedarray.m_Start.size(), 0); + EXPECT_EQ(joinedarray.m_Count.size(), 3); + EXPECT_EQ(joinedarray.m_Count[0], 10); + EXPECT_EQ(joinedarray.m_Count[1], n); + EXPECT_EQ(joinedarray.m_Count[2], 30); + EXPECT_EQ(joinedarray.m_Name, "joinedarray"); + EXPECT_EQ(joinedarray.m_Type, "int"); +} + +TEST_F(ADIOSDefineVariableTest, DefineJoinedArraySecondDim) +{ + // Define ADIOS joined array + std::size_t n = 50; + auto &joinedarray = io.DefineVariable<int>( + "joinedarray", {n, adios2::JoinedDim, 30}, {0, 0, 0}, {n, 10, 30}); + + // Verify the return type is as expected + ::testing::StaticAssertTypeEq<decltype(joinedarray), + adios2::Variable<int> &>(); + + // Verify the dimensions, name, and type are correct + ASSERT_EQ(joinedarray.m_Shape.size(), 3); + EXPECT_EQ(joinedarray.m_Shape[0], n); + EXPECT_EQ(joinedarray.m_Shape[1], adios2::JoinedDim); + EXPECT_EQ(joinedarray.m_Shape[2], 30); + EXPECT_EQ(joinedarray.m_Start.size(), 3); + EXPECT_EQ(joinedarray.m_Start[0], 0); + EXPECT_EQ(joinedarray.m_Start[1], 0); + EXPECT_EQ(joinedarray.m_Start[2], 0); + EXPECT_EQ(joinedarray.m_Count.size(), 3); + EXPECT_EQ(joinedarray.m_Count[0], n); + EXPECT_EQ(joinedarray.m_Count[1], 10); + EXPECT_EQ(joinedarray.m_Count[2], 30); + EXPECT_EQ(joinedarray.m_Name, "joinedarray"); + EXPECT_EQ(joinedarray.m_Type, "int"); +} + +TEST_F(ADIOSDefineVariableTest, DefineJoinedArrayTooManyJoinedDims) +{ + // Define ADIOS joined array + std::size_t n = 50; + adios2::Variable<int> *joinedarray; + EXPECT_THROW(joinedarray = &io.DefineVariable<int>( + "joinedarray", {n, adios2::JoinedDim, adios2::JoinedDim}, + {}, {n, 50, 30}), + std::invalid_argument); +} + +TEST_F(ADIOSDefineVariableTest, DefineJoinedArrayInvalidStart) +{ + // Define ADIOS joined array + std::size_t n = 10; + std::size_t WrongValue = 1; + adios2::Variable<int> *joinedarray; + // Start must be empty or full zero array + EXPECT_THROW( + joinedarray = &io.DefineVariable<int>( + "joinedarray", {adios2::JoinedDim, 50}, {0, WrongValue}, {n, 50}), + std::invalid_argument); +} + +int main(int argc, char **argv) +{ +#ifdef ADIOS2_HAVE_MPI + MPI_Init(nullptr, nullptr); +#endif + + ::testing::InitGoogleTest(&argc, argv); + int result = RUN_ALL_TESTS(); + +#ifdef ADIOS2_HAVE_MPI + MPI_Finalize(); +#endif + + return result; +}