diff --git a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp index e091c02e07..ae39d96944 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp @@ -259,6 +259,16 @@ class ADIOS2IOHandlerImpl UseSpan m_useSpanBasedPutByDefault = UseSpan::Auto; + enum class ModifiableAttributes : char + { + Yes, + No, + Unspecified + }; + + ModifiableAttributes m_modifiableAttributes = + ModifiableAttributes::Unspecified; + inline SupportedSchema schema() const { switch (m_schema) diff --git a/src/IO/ADIOS/ADIOS2IOHandler.cpp b/src/IO/ADIOS/ADIOS2IOHandler.cpp index 78b08b99ac..34dcb9d5e7 100644 --- a/src/IO/ADIOS/ADIOS2IOHandler.cpp +++ b/src/IO/ADIOS/ADIOS2IOHandler.cpp @@ -156,6 +156,14 @@ void ADIOS2IOHandlerImpl::init(json::TracingJSON cfg) : UseSpan::No; } + if (m_config.json().contains("modifiable_attributes")) + { + m_modifiableAttributes = + m_config["modifiable_attributes"].json().get() + ? ModifiableAttributes::Yes + : ModifiableAttributes::No; + } + auto engineConfig = config(ADIOS2Defaults::str_engine); if (!engineConfig.json().is_null()) { @@ -1638,8 +1646,10 @@ namespace detail adios2::IO IO = filedata.m_IO; impl->m_dirty.emplace(std::move(file)); - if (parameters.changesOverSteps == - Parameter::ChangesOverSteps::No) + if (impl->m_modifiableAttributes == + ADIOS2IOHandlerImpl::ModifiableAttributes::No && + parameters.changesOverSteps == + Parameter::ChangesOverSteps::No) { std::string t = IO.AttributeType(fullName); if (!t.empty()) // an attribute is present <=> it has a type @@ -1676,6 +1686,10 @@ namespace detail } auto &value = std::get(parameters.resource); + bool modifiable = impl->m_modifiableAttributes == + ADIOS2IOHandlerImpl::ModifiableAttributes::Yes || + parameters.changesOverSteps != + Parameter::ChangesOverSteps::No; if constexpr (IsUnsupportedComplex_v) { @@ -1691,8 +1705,7 @@ namespace detail value.size(), /* variableName = */ "", /* separator = */ "/", - /* allowModification = */ parameters.changesOverSteps != - Parameter::ChangesOverSteps::No); + /* allowModification = */ modifiable); if (!attr) { throw std::runtime_error( @@ -1708,8 +1721,7 @@ namespace detail value.size(), /* variableName = */ "", /* separator = */ "/", - /* allowModification = */ parameters.changesOverSteps != - Parameter::ChangesOverSteps::No); + /* allowModification = */ modifiable); if (!attr) { throw std::runtime_error( @@ -1727,8 +1739,7 @@ namespace detail representation, /* variableName = */ "", /* separator = */ "/", - /* allowModification = */ parameters.changesOverSteps != - Parameter::ChangesOverSteps::No); + /* allowModification = */ modifiable); if (!attr) { throw std::runtime_error( @@ -1743,8 +1754,7 @@ namespace detail value, /* variableName = */ "", /* separator = */ "/", - /* allowModification = */ parameters.changesOverSteps != - Parameter::ChangesOverSteps::No); + /* allowModification = */ modifiable); if (!attr) { throw std::runtime_error( @@ -2276,6 +2286,15 @@ namespace detail m_impl->m_schema = ADIOS2Schema::schema_2022_07_26; } + if (m_impl->m_modifiableAttributes == + ADIOS2IOHandlerImpl::ModifiableAttributes::Unspecified) + { + m_impl->m_modifiableAttributes = + m_impl->m_iterationEncoding == IterationEncoding::variableBased + ? ADIOS2IOHandlerImpl::ModifiableAttributes::Yes + : ADIOS2IOHandlerImpl::ModifiableAttributes::No; + } + // set engine type { m_IO.SetEngine(m_engineType); diff --git a/test/SerialIOTest.cpp b/test/SerialIOTest.cpp index a1b08d1df6..d288986164 100644 --- a/test/SerialIOTest.cpp +++ b/test/SerialIOTest.cpp @@ -5237,10 +5237,9 @@ TEST_CASE("git_adios2_sample_test", "[serial][adios2]") void variableBasedSeries(std::string const &file) { - std::string selectADIOS2 = R"({"backend": "adios2"})"; constexpr Extent::value_type extent = 1000; - { - Series writeSeries(file, Access::CREATE, selectADIOS2); + auto testWrite = [&file](std::string const &jsonConfig) { + Series writeSeries(file, Access::CREATE, jsonConfig); writeSeries.setIterationEncoding(IterationEncoding::variableBased); REQUIRE( writeSeries.iterationEncoding() == @@ -5260,6 +5259,8 @@ void variableBasedSeries(std::string const &file) "iteration_is_larger_than_two", "it truly is"); } + iteration.setAttribute("changing_value", i); + // this tests changing extents and dimensionalities // across iterations auto E_y = iteration.meshes["E"]["y"]; @@ -5294,14 +5295,16 @@ void variableBasedSeries(std::string const &file) iteration.close(); } - } - - REQUIRE(auxiliary::directory_exists(file)); + REQUIRE(auxiliary::directory_exists(file)); + }; - auto testRead = [&file, &extent, &selectADIOS2]( - std::string const &jsonConfig) { + auto testRead = [&file, &extent]( + std::string const &parseMode, + bool supportsModifiableAttributes) { Series readSeries( - file, Access::READ_LINEAR, json::merge(selectADIOS2, jsonConfig)); + file, + Access::READ_LINEAR, + json::merge(R"(backend = "adios2")", parseMode)); size_t last_iteration_index = 0; for (auto iteration : readSeries.readIterations()) @@ -5318,6 +5321,13 @@ void variableBasedSeries(std::string const &file) "iteration_is_larger_than_two")); } + // If modifiable attributes are unsupported, the attribute is + // written once in step 0 and then never changed + // A warning is printed upon trying to write + REQUIRE( + iteration.getAttribute("changing_value").get() == + (supportsModifiableAttributes ? iteration.iterationIndex : 0)); + auto E_x = iteration.meshes["E"]["x"]; REQUIRE(E_x.getDimensionality() == 1); REQUIRE(E_x.getExtent()[0] == extent); @@ -5383,8 +5393,47 @@ void variableBasedSeries(std::string const &file) REQUIRE(last_iteration_index == 9); }; - testRead("{\"defer_iteration_parsing\": true}"); - testRead("{\"defer_iteration_parsing\": false}"); + std::string jsonConfig = R"( +{ + "backend": "adios2", + "adios2": { + "modifiable_attributes": true + } +})"; + testWrite(jsonConfig); + testRead( + "{\"defer_iteration_parsing\": true}", + /*supportsModifiableAttributes = */ true); + testRead( + "{\"defer_iteration_parsing\": false}", + /*supportsModifiableAttributes = */ true); + + jsonConfig = R"( +{ + "backend": "adios2" +})"; + testWrite(jsonConfig); + testRead( + "{\"defer_iteration_parsing\": true}", + /*supportsModifiableAttributes = */ true); + testRead( + "{\"defer_iteration_parsing\": false}", + /*supportsModifiableAttributes = */ true); + + jsonConfig = R"( +{ + "backend": "adios2", + "adios2": { + "modifiable_attributes": false + } +})"; + testWrite(jsonConfig); + testRead( + "{\"defer_iteration_parsing\": true}", + /*supportsModifiableAttributes = */ false); + testRead( + "{\"defer_iteration_parsing\": false}", + /*supportsModifiableAttributes = */ false); } #if openPMD_HAVE_ADIOS2