diff --git a/docs/source/backends/adios2.rst b/docs/source/backends/adios2.rst index a6161ed9dc..55f080494c 100644 --- a/docs/source/backends/adios2.rst +++ b/docs/source/backends/adios2.rst @@ -48,6 +48,10 @@ Exceptions to this are the BP3 and SST engines which require their endings ``.bp For file engines, we currently leverage the default ADIOS2 transport parameters, i.e. ``POSIX`` on Unix systems and ``FStream`` on Windows. +.. tip:: + + Use the ``adios2.engine.treat_unsupported_engine_as`` :ref:`JSON/TOML parameter ` for experimentally interacting with an unsupported ADIOS2 engine. + Steps ----- @@ -81,6 +85,7 @@ environment variable default description ``OPENPMD_ADIOS2_HAVE_METADATA_FILE`` ``1`` Online creation of the adios journal file (``1``: yes, ``0``: no). ``OPENPMD_ADIOS2_NUM_SUBSTREAMS`` ``0`` Number of files to be created, 0 indicates maximum number possible. ``OPENPMD_ADIOS2_ENGINE`` ``File`` `ADIOS2 engine `_ +``OPENPMD_ADIOS2_PRETEND_ENGINE`` *empty* Pretend that an (unknown) ADIOS2 engine is in fact another one (also see the ``adios2.pretend_engine`` :ref:`parameter `). ``OPENPMD2_ADIOS2_USE_GROUP_TABLE`` ``0`` Use group table (see below) ``OPENPMD_ADIOS2_STATS_LEVEL`` ``0`` whether to generate statistics for variables in ADIOS2. (``1``: yes, ``0``: no). ``OPENPMD_ADIOS2_ASYNC_WRITE`` ``0`` ADIOS2 BP5 engine: 1 means setting "AsyncWrite" in ADIOS2 to "on". Flushes will go to the buffer by default (see ``preferred_flush_target``). diff --git a/docs/source/details/backendconfig.rst b/docs/source/details/backendconfig.rst index 49cccebe69..fdfa3d68e3 100644 --- a/docs/source/details/backendconfig.rst +++ b/docs/source/details/backendconfig.rst @@ -122,6 +122,14 @@ Explanation of the single keys: * ``adios2.engine.type``: A string that is passed directly to ``adios2::IO:::SetEngine`` for choosing the ADIOS2 engine to be used. Please refer to the `official ADIOS2 documentation `_ for a list of available engines. +* ``adios2.engine.pretend_engine``: May be used for experimentally testing an ADIOS2 engine that is not explicitly supported by the openPMD-api. + Specify the actual engine via ``adios2.engine.type`` and use ``adios2.engine.pretend_engine`` to make the ADIOS2 backend pretend that it is in fact using another engine that it knows. + Some advanced engine-specific features will be turned off indiscriminately: + + * The Span API will use a fallback implementation + * ``PerformDataWrite()`` will not be used, even when specifying ``adios2.engine.preferred_flush_target = "disk"``. + * Engine-specific parameters such as ``QueueLimit`` will not be set by default. + * No engine-specific filename extension handling will be executed, the extension specified by the user is taken "as is". * ``adios2.engine.access_mode``: One of ``"Write", "Read", "Append", "ReadRandomAccess"``. Only needed in specific use cases, the access mode is usually determined from the specified ``openPMD::Access``. Useful for finetuning the backend-specific behavior of ADIOS2 when overwriting existing Iterations in file-based Append mode. diff --git a/include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp b/include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp index ff14f2d69a..9cb275d339 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp @@ -77,6 +77,7 @@ namespace adios_defaults using const_str = char const *const; constexpr const_str str_engine = "engine"; constexpr const_str str_type = "type"; + constexpr const_str str_treat_unsupported_engine_like = "pretend_engine"; constexpr const_str str_params = "parameters"; constexpr const_str str_usesteps = "usesteps"; constexpr const_str str_flushtarget = "preferred_flush_target"; diff --git a/include/openPMD/IO/ADIOS/ADIOS2File.hpp b/include/openPMD/IO/ADIOS/ADIOS2File.hpp index 6a15a45b90..0bcdaa6131 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2File.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2File.hpp @@ -413,10 +413,6 @@ class ADIOS2File private: ADIOS2IOHandlerImpl *m_impl; std::optional m_engine; //! ADIOS engine - /** - * The ADIOS2 engine type, to be passed to adios2::IO::SetEngine - */ - std::string m_engineType; /* * Not all engines support the CurrentStep() call, so we have to diff --git a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp index bd7c698f37..db3162a2da 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp @@ -225,6 +225,32 @@ class ADIOS2IOHandlerImpl * The ADIOS2 engine type, to be passed to adios2::IO::SetEngine */ std::string m_engineType; + std::optional m_realEngineType; + + inline std::string const &realEngineType() const + { + if (m_realEngineType.has_value()) + { + return *m_realEngineType; + } + else + { + return m_engineType; + } + } + inline std::string &realEngineType() + { + return const_cast( + static_cast(this)->realEngineType()); + } + inline void pretendEngine(std::string facade_engine) + { + if (!m_realEngineType.has_value()) + { + m_realEngineType = std::move(m_engineType); + } + m_engineType = std::move(facade_engine); + } /* * The filename extension specified by the user. */ diff --git a/src/IO/ADIOS/ADIOS2File.cpp b/src/IO/ADIOS/ADIOS2File.cpp index 8dbb9d17e9..ee0c1a9062 100644 --- a/src/IO/ADIOS/ADIOS2File.cpp +++ b/src/IO/ADIOS/ADIOS2File.cpp @@ -163,7 +163,6 @@ ADIOS2File::ADIOS2File(ADIOS2IOHandlerImpl &impl, InvalidatableFile file) : m_file(impl.fullPath(std::move(file))) , m_ADIOS(impl.m_ADIOS) , m_impl(&impl) - , m_engineType(impl.m_engineType) { // Declaring these members in the constructor body to avoid // initialization order hazards. Need the IO_ prefix since in some @@ -324,7 +323,7 @@ namespace size_t ADIOS2File::currentStep() { - if (nonpersistentEngine(m_engineType)) + if (nonpersistentEngine(m_impl->m_engineType)) { return m_currentStep; } @@ -337,9 +336,9 @@ size_t ADIOS2File::currentStep() void ADIOS2File::configure_IO_Read() { bool upfrontParsing = supportsUpfrontParsing( - m_impl->m_handler->m_backendAccess, m_engineType); + m_impl->m_handler->m_backendAccess, m_impl->m_engineType); PerstepParsing perstepParsing = supportsPerstepParsing( - m_impl->m_handler->m_backendAccess, m_engineType); + m_impl->m_handler->m_backendAccess, m_impl->m_engineType); switch (m_impl->m_handler->m_backendAccess) { @@ -355,7 +354,7 @@ void ADIOS2File::configure_IO_Read() * In non-persistent (streaming) engines, per-step parsing is * always fine and always required. */ - streamStatus = nonpersistentEngine(m_engineType) + streamStatus = nonpersistentEngine(m_impl->m_engineType) ? StreamStatus::OutsideOfStep : StreamStatus::Undecided; parsePreference = ParsePreference::PerStep; @@ -374,7 +373,7 @@ void ADIOS2File::configure_IO_Read() * Prefer up-front parsing, but try to fallback to per-step parsing * if possible. */ - if (upfrontParsing == nonpersistentEngine(m_engineType)) + if (upfrontParsing == nonpersistentEngine(m_impl->m_engineType)) { throw error::Internal( "Internal control flow error: With access types " @@ -412,7 +411,7 @@ void ADIOS2File::configure_IO_Write() // Also, it should only be done when truly streaming, not // when using a disk-based engine that behaves like a // streaming engine (otherwise attributes might vanish) - nonpersistentEngine(m_engineType); + nonpersistentEngine(m_impl->m_engineType); streamStatus = StreamStatus::OutsideOfStep; } @@ -466,13 +465,13 @@ void ADIOS2File::configure_IO() // set engine type { - m_IO.SetEngine(m_engineType); + m_IO.SetEngine(m_impl->realEngineType()); } - if (!supportedEngine(m_engineType)) + if (!supportedEngine(m_impl->m_engineType)) { std::stringstream sstream; - sstream << "User-selected ADIOS2 engine '" << m_engineType + sstream << "User-selected ADIOS2 engine '" << m_impl->m_engineType << "' is not recognized by the openPMD-api. Select one of: '"; bool first_entry = true; auto add_entries = [&first_entry, &sstream](auto &list) { @@ -687,7 +686,7 @@ void ADIOS2File::configure_IO() auxiliary::getEnvNum("OPENPMD_ADIOS2_STATS_LEVEL", 0); m_IO.SetParameter("StatsLevel", std::to_string(stats_level)); } - if (m_engineType == "sst" && notYetConfigured("QueueLimit")) + if (m_impl->realEngineType() == "sst" && notYetConfigured("QueueLimit")) { /* * By default, the SST engine of ADIOS2 does not set a limit on its @@ -773,7 +772,8 @@ adios2::Engine &ADIOS2File::getEngine() bool openedANewStep = false; { if (!supportsUpfrontParsing( - m_impl->m_handler->m_backendAccess, m_engineType)) + m_impl->m_handler->m_backendAccess, + m_impl->m_engineType)) { /* * In BP5 with Linear read mode, we now need to @@ -1048,7 +1048,7 @@ void ADIOS2File::flush_impl(ADIOS2FlushParams flushParams, bool writeLatePuts) { case FlushTarget::Disk: case FlushTarget::Disk_Override: - if (m_engineType == "bp5" || + if (m_impl->realEngineType() == "bp5" || /* this second check should be sufficient, but we leave the first check in as a safeguard against renamings in ADIOS2. Also do a lowerCase transform since the docstring diff --git a/src/IO/ADIOS/ADIOS2IOHandler.cpp b/src/IO/ADIOS/ADIOS2IOHandler.cpp index d97769d19b..bdbd43325a 100644 --- a/src/IO/ADIOS/ADIOS2IOHandler.cpp +++ b/src/IO/ADIOS/ADIOS2IOHandler.cpp @@ -192,14 +192,17 @@ template void ADIOS2IOHandlerImpl::init( json::TracingJSON cfg, Callback &&callbackWriteAttributesFromRank) { + if (auto unsupported_engine_cfg = + auxiliary::getEnvString("OPENPMD_ADIOS2_PRETEND_ENGINE", ""); + !unsupported_engine_cfg.empty()) + { + auxiliary::lowerCase(unsupported_engine_cfg); + pretendEngine(std::move(unsupported_engine_cfg)); + } // allow overriding through environment variable - m_engineType = + realEngineType() = auxiliary::getEnvString("OPENPMD_ADIOS2_ENGINE", m_engineType); - std::transform( - m_engineType.begin(), - m_engineType.end(), - m_engineType.begin(), - [](unsigned char c) { return std::tolower(c); }); + auxiliary::lowerCase(realEngineType()); // environment-variable based configuration if (int groupTableViaEnv = @@ -255,7 +258,7 @@ void ADIOS2IOHandlerImpl::init( if (maybeEngine.has_value()) { // override engine type by JSON/TOML configuration - m_engineType = std::move(maybeEngine.value()); + realEngineType() = std::move(maybeEngine.value()); } else { @@ -264,6 +267,24 @@ void ADIOS2IOHandlerImpl::init( "Must be convertible to string type."); } } + + if (engineConfig.json().contains( + adios_defaults::str_treat_unsupported_engine_like)) + { + auto maybeEngine = json::asLowerCaseStringDynamic( + engineConfig + [adios_defaults::str_treat_unsupported_engine_like] + .json()); + if (!maybeEngine.has_value()) + { + throw error::BackendConfigSchema( + {"adios2", + adios_defaults::str_engine, + adios_defaults::str_treat_unsupported_engine_like}, + "Must be convertible to string type."); + } + pretendEngine(std::move(*maybeEngine)); + } } auto operators = getOperators(); if (operators) @@ -362,6 +383,12 @@ std::string ADIOS2IOHandlerImpl::fileSuffix(bool verbose) const constexpr char const *const default_file_ending = ".bp4"; #endif + if (m_realEngineType.has_value()) + { + // unknown engine type, use whatever ending the user specified + return m_userSpecifiedExtension; + } + static std::map const endings{ {"sst", {{"", ""}, {".sst", ""}, {".%E", ""}}}, {"staging", {{"", ""}, {".sst", ""}, {".%E", ""}}}, @@ -656,7 +683,7 @@ void ADIOS2IOHandlerImpl::checkFile( bool ADIOS2IOHandlerImpl::checkFile(std::string fullFilePath) const { - if (m_engineType == "bp3") + if (realEngineType() == "bp3") { if (!auxiliary::ends_with(fullFilePath, ".bp")) { @@ -666,7 +693,7 @@ bool ADIOS2IOHandlerImpl::checkFile(std::string fullFilePath) const fullFilePath += ".bp"; } } - else if (m_engineType == "sst") + else if (realEngineType() == "sst") { /* * SST will add this ending indiscriminately @@ -1144,7 +1171,7 @@ void ADIOS2IOHandlerImpl::getBufferView( begin(optInEngines), end(optInEngines), [this](std::string const &engine) { - return engine == this->m_engineType; + return engine == this->realEngineType(); })) { parameters.out->backendManagedBuffer = false;