From 01ab08d739f649cd6a745b271369473f349a876c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20P=C3=B6schel?= Date: Sun, 30 Sep 2018 14:56:10 +0200 Subject: [PATCH 1/7] ADIOS2 backend Undebugged and erroneous writing and reading version of ADIOS2 backend Add InvalidatableFile class Implement (parts of) ADIOS2IOHandlerImpl Create AbstractIOHandlerImplCommon Large parts of the concrete implementation is the same across backends, try to capture that. Frontend part of JSON backend implementation Implement JSON backend to dummy state Misses the actual implementation of AbstractIOHandlerImpl Declare IOHandlerImpl for JSON and integrate it with other places Misses the implementation. Undebugged minimum implementation for JSON writing First basically runnable version of JSON writing To address: No reading or deleting yet. Datatypes are currently ignored and the data is assumed to be int64_t. Attribute values are ignored and replaced with a dummy value. If a subgroup name can be parsed as a nonnegative string, the JSON API will create a JSON array rather than a JSON object (associative array) as intended. Correctly handle groups that can be parsed as int See last commit's description. Fix index calculation with offsets in WriteData Fix some mistakes in JSON writing Correctly handle overwriting files: -> overwritten files should not be possible to access any longer and be clearly distinguished from the newly-created file Make some verifications execute independent of compiler options. Full implementation of JSON writing Respects all datatypes now. Format code according to Clion Stylesheet https://github.com/ComputationalRadiationPhysics/contributing/blob/master/IDESettings/CLion/CRP_CLion2016_1.xml Add generic branching over an openPMD datatype First runnable version of JSON Reading Cleanup and implementation of dataset extension Undebugged version of JSON deletion Properly (de)serialize datatypes Instead of casting the Datatype enum to and from int (which is likely to break when altering the enum), serialize to and from String values. Fix a number of mistakes in JSON reading and writing Cleanup Add JSON tests and fix bugs found thusly Add further tests and fix a further bug The JSON library does not understand long double values (i.e. 128bit floats), represent them as a char array. Handle floating point special values JSON represents +/-Infinity and NaN values as null. The JSON library will correctly serialize those values *to* JSON, implement (semi)-correct handly for deserialization. As it is unclear which exact value a null represents, deserialize it to NaN. Take notice that large floating point values (128 bit) might be serialized to null as well. Use std::is_floating_point to distinguish them from other types Additionally write the byte width of the underlying type Not yet used in reading Mark the writable written after successfully extending a dataset Remove support for absolute paths from openPath Fix some rough edges from rebasing Add documentation for the JSON backend Integrate the JSON backend with the build system Further implement ADIOS2 backend First basically writing commit Further implement ADIOS2 backend Open an IO for each file to write to Move switchType structs to detail namespace Further implement ADIOS2 backend Mainly: handling non-trivial datatypes in writing operations Replace MPI::Init and MPI::Finalize with MPI_Init and MPI_Finalize Fix destruction order Implement readAttribute Refactor helper structs less boilerplate Implement readDataset WIP: refactor Engine opening and closing Execute buffered actions in original order Bugfixing First successful read Before cleanup Does still not write the correct values as datasets. Hoping to find the reason during cleanup. Cleanup, Clang compiler support Add support for non-MPI ADIOS2 Also enable building without ADIOS2. Overwrite existing Attributes Bugfixes Adapt to accessType -> accessTypeBackend refactoring Various datatype fixes Fix listPaths bug Fix openPath Bug Paths are opened relative to the Writable's parent. Passes all serial test By implementing a workaround for ADIOS2 currently not yet supporting Append mode. Use m_dirty to postpone Engine opening Delay attribute reading, allowing us to open Engines later Add validity checks for returned ADIOS2 objects Switch to clang-format Clion formatter does not understand all templates Activate test cases for ADIOS2 backend Cleanup: Imports and Formatting Update documentation Concerns documentation for building the openPMD API. Documentation on the usage of the ADIOS2 backend is not yet present. Fix parallel test Throw exception upon deletion instead of silently passing on Add configuration options via environment variables OPENPMD_ADIOS_HAVE_PROFILING, OPENPMD_ADIOS_HAVE_METADATA_FILE and OPENPMD_ADIOS_NUM_SUBSTREAMS Add documentation for ADIOS2 backend Add support for compression Zfp and Sz Remove debug output from Datatype.cpp Simplify the handling of attributes So far, the ADIOS2 backend distinguished attributes of datasets and of groups. Unify this. Add ADIOS2 to continuous integration - WIP experiencing compiler version woes with travis atm For each build that uses ADIOS1, add an equivalent build that replaces ADIOS1 with ADIOS2 Notice that a single build cannot support both versions of ADIOS, i.e. when building with both versions enabled, the runtime will always decide in favor of ADIOS2 Hence, do not add ADIOS2 to code coverage (for now) Deactivate ADIOS2 debug mode Revert .travis.yml to dev version Fix constructors of ADIOS2 classes Previous versions did not work correctly with MPI enabled. Also, not all constructors took compression into consideration. Fix pointer slicing Deactivate non-MPI versions of ADIOS2 backend when building with MPI Since ADIOS2 cannot be used without MPI if it has been built with support for MPI, we do it the same way (for now?). Deactivate tests accordingly. Add new ADIOS2 type names Activate serial ADIOS2 when building with MPI Due to restrictions in ADIOS2, this was hitherto impossible. Fix documentation and rename environment variables rename OPENPMD_ADIOS_* to OPENPMD_ADIOS2_* Fix picking of backends in tests Add '.bp' file ending at most once and enable serial ADIOS2 tests also when compiling with support for MPI. Remember whether an attribute is boolean Since we store boolean attributes as uchar, their type cannot be retrieved later properly. Explicitly store the type. Refactor compression to support any techniques that ADIOS2 supports Fix preprocessor macros in case ADIOS2 is not available --- CMakeLists.txt | 7 +- README.md | 13 +- docs/source/backends/adios2.rst | 77 + docs/source/dev/buildoptions.rst | 12 +- docs/source/dev/dependencies.rst | 2 +- docs/source/index.rst | 2 +- include/openPMD/Datatype.hpp | 289 ++-- include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp | 98 ++ .../openPMD/IO/ADIOS/ADIOS2FilePosition.hpp | 47 +- include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp | 624 +++++++- .../IO/AbstractIOHandlerImplCommon.hpp | 259 ++++ include/openPMD/IO/AccessType.hpp | 2 +- include/openPMD/IO/IOTask.hpp | 27 +- include/openPMD/IO/InvalidatableFile.hpp | 96 ++ include/openPMD/auxiliary/Environment.hpp | 31 + include/openPMD/auxiliary/StringManip.hpp | 51 +- .../openPMD/backend/BaseRecordComponent.hpp | 8 + include/openPMD/backend/Writable.hpp | 5 + src/Datatype.cpp | 86 ++ src/IO/ADIOS/ADIOS2Auxiliary.cpp | 172 +++ src/IO/ADIOS/ADIOS2IOHandler.cpp | 1265 ++++++++++++++++- src/IO/AbstractIOHandlerHelper.cpp | 9 +- src/IO/InvalidatableFile.cpp | 70 + src/Series.cpp | 5 +- test/SerialIOTest.cpp | 8 +- 25 files changed, 3018 insertions(+), 247 deletions(-) create mode 100644 docs/source/backends/adios2.rst create mode 100644 include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp create mode 100644 include/openPMD/IO/AbstractIOHandlerImplCommon.hpp create mode 100644 include/openPMD/IO/InvalidatableFile.hpp create mode 100644 include/openPMD/auxiliary/Environment.hpp create mode 100644 src/IO/ADIOS/ADIOS2Auxiliary.cpp create mode 100644 src/IO/InvalidatableFile.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 93f8f1a694..ba9a620d40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,7 +59,7 @@ openpmd_option(MPI "Parallel, Multi-Node I/O for clusters" AUTO) openpmd_option(JSON "JSON backend (.json files)" AUTO) openpmd_option(HDF5 "HDF5 backend (.h5 files)" AUTO) openpmd_option(ADIOS1 "ADIOS1 backend (.bp files)" AUTO) -openpmd_option(ADIOS2 "ADIOS2 backend (.bp files)" OFF) +openpmd_option(ADIOS2 "ADIOS2 backend (.bp files)" AUTO) openpmd_option(PYTHON "Enable Python bindings" AUTO) option(openPMD_HAVE_PKGCONFIG "Generate a .pc file for pkg-config" ON) @@ -341,7 +341,10 @@ set(IO_SOURCE src/IO/HDF5/ParallelHDF5IOHandler.cpp src/IO/JSON/JSONIOHandler.cpp src/IO/JSON/JSONIOHandlerImpl.cpp - src/IO/JSON/JSONFilePosition.cpp) + src/IO/JSON/JSONFilePosition.cpp + src/IO/ADIOS/ADIOS2IOHandler.cpp + src/IO/ADIOS/ADIOS2Auxiliary.cpp + src/IO/InvalidatableFile.cpp) set(IO_ADIOS1_SEQUENTIAL_SOURCE src/auxiliary/Filesystem.cpp src/IO/ADIOS/ADIOS1IOHandler.cpp) diff --git a/README.md b/README.md index 0adb9aa08b..783a7b7cc7 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ Optional I/O backends: * [JSON](https://en.wikipedia.org/wiki/JSON) * [HDF5](https://support.hdfgroup.org/HDF5) 1.8.13+ * [ADIOS1](https://www.olcf.ornl.gov/center-projects/adios) 1.13.1+ -* [ADIOS2](https://github.com/ornladios/ADIOS2) 2.4.0+ (*not yet implemented*) +* [ADIOS2](https://github.com/ornladios/ADIOS2) 2.4.0+ while those can be built either with or without: * MPI 2.1+, e.g. OpenMPI 1.6.5+ or MPICH2 @@ -212,15 +212,14 @@ CMake controls options with prefixed `-D`, e.g. `-DopenPMD_USE_MPI=OFF`: | `openPMD_USE_JSON` | **AUTO**/ON/OFF | JSON backend (`.json` files) | | `openPMD_USE_HDF5` | **AUTO**/ON/OFF | HDF5 backend (`.h5` files) | | `openPMD_USE_ADIOS1` | **AUTO**/ON/OFF | ADIOS1 backend (`.bp` files) | -| `openPMD_USE_ADIOS2` | AUTO/ON/**OFF** | ADIOS2 backend (`.bp` files) 1 | +| `openPMD_USE_ADIOS2` | **AUTO**/ON/OFF | ADIOS2 backend (`.bp` files) | | `openPMD_USE_PYTHON` | **AUTO**/ON/OFF | Enable Python bindings | -| `openPMD_USE_INVASIVE_TESTS` | ON/**OFF** | Enable unit tests that modify source code 2 | -| `openPMD_USE_VERIFY` | **ON**/OFF | Enable internal VERIFY (assert) macro independent of build type 3 | +| `openPMD_USE_INVASIVE_TESTS` | ON/**OFF** | Enable unit tests that modify source code 1 | +| `openPMD_USE_VERIFY` | **ON**/OFF | Enable internal VERIFY (assert) macro independent of build type 2 | | `PYTHON_EXECUTABLE` | (first found) | Path to Python executable | -1 *not yet implemented* -2 *e.g. changes C++ visibility keywords, breaks MSVC* -3 *this includes most pre-/post-condition checks, disabling without specific cause is highly discouraged* +1 *e.g. changes C++ visibility keywords, breaks MSVC* +2 *this includes most pre-/post-condition checks, disabling without specific cause is highly discouraged* Additionally, the following libraries are shipped internally. The following options allow to switch to external installs: diff --git a/docs/source/backends/adios2.rst b/docs/source/backends/adios2.rst new file mode 100644 index 0000000000..28d2c3a221 --- /dev/null +++ b/docs/source/backends/adios2.rst @@ -0,0 +1,77 @@ +.. _backends-adios2: + +ADIOS2 Backend +============== + +openPMD supports writing to and reading from ADIOS2 ``.bp`` files. +For this, the installed copy of openPMD must have been built with support for the ADIOS2 backend. +To build openPMD with support for ADIOS2, use the CMake option ``-DopenPMD_USE_ADIOS2=ON``. +For further information, check out the :ref:`installation guide `, +:ref:`build dependencies ` and the :ref:`build options `. + + +I/O Method +---------- + +ADIOS2 has several engines for alternative file formats and other kinds of backends, yet natively writes to ``.bp`` files. At the moment, the openPMD API exclusively uses the BPFile engine. +We currently leverage the default ADIOS2 transport parameters, i.e. ``POSIX`` on Unix systems and ``FStream`` on Windows. + + +Backend-Specific Controls +------------------------- + +The following environment variables control ADIOS2 I/O behavior at runtime. +Fine-tuning these is especially useful when running at large scale. + +===================================== ======= =================================================================== +environment variable default description +===================================== ======= =================================================================== +``OPENPMD_ADIOS2_HAVE_PROFILING`` ``1`` Turns on/off profiling information right after a run. +``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. +===================================== ======= =================================================================== + +Please refer to the `ADIOS2 manual, section 5.1 `_ for details. + + +Best Practice at Large Scale +---------------------------- + +A good practice at scale is to disable the online creation of the metadata file. +After writing the data, run ``bpmeta`` on the (to-be-created) filename to generate the metadata file offline (repeat per iteration for file-based encoding). +This metadata file is needed for reading, while the actual heavy data resides in ``.dir/`` directories. +Note that such a tool is not yet available for ADIOS2, but the ``bpmeta`` utility provided by ADIOS1 is capable of processing files written by ADIOS2. + +Further options depend heavily on filesystem type, specific file striping, network infrastructure and available RAM on the aggregator nodes. +A good number for substreams is usually the number of contributing nodes divided by four. + +For fine-tuning at extreme scale or for exotic systems, please refer to the ADIOS2 manual and talk to your filesystem admins and the ADIOS2 authors. +Be aware that extreme-scale I/O is a research topic after all. + +Selected References +------------------- + +* Hasan Abbasi, Matthew Wolf, Greg Eisenhauer, Scott Klasky, Karsten Schwan, and Fang Zheng. + *Datastager: scalable data staging services for petascale applications,* + Cluster Computing, 13(3):277–290, 2010. + `DOI:10.1007/s10586-010-0135-6 `_ + +* Ciprian Docan, Manish Parashar, and Scott Klasky. + *DataSpaces: An interaction and coordination framework or coupled simulation workflows,* + In Proc. of 19th International Symposium on High Performance and Distributed Computing (HPDC’10), June 2010. + `DOI:10.1007/s10586-011-0162-y `_ + +* Qing Liu, Jeremy Logan, Yuan Tian, Hasan Abbasi, Norbert Podhorszki, Jong Youl Choi, Scott Klasky, Roselyne Tchoua, Jay Lofstead, Ron Oldfield, Manish Parashar, Nagiza Samatova, Karsten Schwan, Arie Shoshani, Matthew Wolf, Kesheng Wu, and Weikuan Yu. + *Hello ADIOS: the challenges and lessons of developing leadership class I/O frameworks,* + Concurrency and Computation: Practice and Experience, 26(7):1453–1473, 2014. + `DOI:10.1002/cpe.3125 `_ + +* Robert McLay, Doug James, Si Liu, John Cazes, and William Barth. + *A user-friendly approach for tuning parallel file operations,* + In Proceedings of the International Conference for High Performance Computing, Networking, Storage and Analysis, SC'14, pages 229–236, IEEE Press, 2014. + `DOI:10.1109/SC.2014.24 `_ + +* Axel Huebl, Rene Widera, Felix Schmitt, Alexander Matthes, Norbert Podhorszki, Jong Youl Choi, Scott Klasky, and Michael Bussmann. + *On the Scalability of Data Reduction Techniques in Current and Upcoming HPC Systems from an Application Perspective,* + ISC High Performance 2017: High Performance Computing, pp. 15-29, 2017. + `arXiv:1706.00522 `_, `DOI:10.1007/978-3-319-67630-2_2 `_ diff --git a/docs/source/dev/buildoptions.rst b/docs/source/dev/buildoptions.rst index 6fdd4cc90f..11160fe3f7 100644 --- a/docs/source/dev/buildoptions.rst +++ b/docs/source/dev/buildoptions.rst @@ -18,18 +18,16 @@ CMake Option Values Description ``openPMD_USE_JSON`` **AUTO**/ON/OFF JSON backend (``.json`` files) ``openPMD_USE_HDF5`` **AUTO**/ON/OFF HDF5 backend (``.h5`` files) ``openPMD_USE_ADIOS1`` **AUTO**/ON/OFF ADIOS1 backend (``.bp`` files) -``openPMD_USE_ADIOS2`` AUTO/ON/**OFF** ADIOS2 backend (``.bp`` files) :sup:`1` +``openPMD_USE_ADIOS2`` **AUTO**/ON/OFF ADIOS2 backend (``.bp`` files) ``openPMD_USE_PYTHON`` **AUTO**/ON/OFF Enable Python bindings -``openPMD_USE_INVASIVE_TESTS`` ON/**OFF** Enable unit tests that modify source code :sup:`2` -``openPMD_USE_VERIFY`` **ON**/OFF Enable internal VERIFY (assert) macro independent of build type :sup:`3` +``openPMD_USE_INVASIVE_TESTS`` ON/**OFF** Enable unit tests that modify source code :sup:`1` +``openPMD_USE_VERIFY`` **ON**/OFF Enable internal VERIFY (assert) macro independent of build type :sup:`2` ``PYTHON_EXECUTABLE`` (first found) Path to Python executable ============================== =============== ======================================================================== -:sup:`1` *not yet implemented* +:sup:`1` e.g. changes C++ visibility keywords, breaks MSVC -:sup:`2` e.g. changes C++ visibility keywords, breaks MSVC - -:sup:`3` this includes most pre-/post-condition checks, disabling without specific cause is highly discouraged +:sup:`2` this includes most pre-/post-condition checks, disabling without specific cause is highly discouraged Shared or Static diff --git a/docs/source/dev/dependencies.rst b/docs/source/dev/dependencies.rst index 2147a3ef7d..5808fdd946 100644 --- a/docs/source/dev/dependencies.rst +++ b/docs/source/dev/dependencies.rst @@ -30,7 +30,7 @@ Optional: I/O backends * `JSON `_ * `HDF5 `_ 1.8.13+ * `ADIOS1 `_ 1.13.1+ -* `ADIOS2 `_ 2.4.0+ (*not yet implemented*) +* `ADIOS2 `_ 2.4.0+ while those can be build either with or without: diff --git a/docs/source/index.rst b/docs/source/index.rst index 0698c77346..5117f13c67 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -95,6 +95,7 @@ Backends backends/json backends/adios1 + backends/adios2 Utilities --------- @@ -104,4 +105,3 @@ Utilities :hidden: utilities/benchmark.rst - diff --git a/include/openPMD/Datatype.hpp b/include/openPMD/Datatype.hpp index 22d982114b..44f84d0555 100644 --- a/include/openPMD/Datatype.hpp +++ b/include/openPMD/Datatype.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Fabian Koller +/* Copyright 2017-2019 Fabian Koller and Franz Pöschel * * This file is part of openPMD-api. * @@ -524,6 +524,7 @@ isSame( openPMD::Datatype const d, openPMD::Datatype const e ) #else #define OPENPMD_TEMPLATE_OPERATOR template operator #endif + /** * Generalizes switching over an openPMD datatype. * @@ -541,151 +542,163 @@ isSame( openPMD::Datatype const d, openPMD::Datatype const e ) * the passed arguments and the template parameter type corresponding to the * openPMD type. */ -template< - typename ReturnType = void, - typename Action, - typename ...Args -> -ReturnType switchType( - Datatype dt, - Action action, - Args && ...args -) { -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) - auto f = &Action::OPENPMD_TEMPLATE_OPERATOR() < int >; - using fun = decltype(f); -#else - using fun = decltype( &Action::OPENPMD_TEMPLATE_OPERATOR() < int > ); -#endif - static std::map< - Datatype, - fun - > funs { - { - Datatype::CHAR , - &Action::OPENPMD_TEMPLATE_OPERATOR() < char > }, - { - Datatype::UCHAR , - &Action::OPENPMD_TEMPLATE_OPERATOR() < unsigned char > }, - { - Datatype::SHORT , - &Action::OPENPMD_TEMPLATE_OPERATOR() < short > }, - { - Datatype::INT , - &Action::OPENPMD_TEMPLATE_OPERATOR() < int > }, - { - Datatype::LONG , - &Action::OPENPMD_TEMPLATE_OPERATOR() < long > }, - { - Datatype::LONGLONG , - &Action::OPENPMD_TEMPLATE_OPERATOR() < long long > }, - { - Datatype::USHORT , - &Action::OPENPMD_TEMPLATE_OPERATOR() < unsigned short > }, - { - Datatype::UINT , - &Action::OPENPMD_TEMPLATE_OPERATOR() < unsigned int > }, - { - Datatype::ULONG , - &Action::OPENPMD_TEMPLATE_OPERATOR() < unsigned long > }, - { - Datatype::ULONGLONG , - &Action::OPENPMD_TEMPLATE_OPERATOR() < unsigned long long > }, - { - Datatype::FLOAT , - &Action::OPENPMD_TEMPLATE_OPERATOR() < float > }, - { - Datatype::DOUBLE , - &Action::OPENPMD_TEMPLATE_OPERATOR() < double > }, - { - Datatype::LONG_DOUBLE , - &Action::OPENPMD_TEMPLATE_OPERATOR() < long double > }, - { - Datatype::STRING , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::string > }, - { - Datatype::VEC_CHAR , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< char>> - }, - { - Datatype::VEC_SHORT , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< short>> - }, - { - Datatype::VEC_INT , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< int>> - }, - { - Datatype::VEC_LONG , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< long>> - }, - { - Datatype::VEC_LONGLONG , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< long long>> - }, - { - Datatype::VEC_UCHAR , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< unsigned char>> - }, - { - Datatype::VEC_USHORT , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< unsigned short>> - }, - { - Datatype::VEC_UINT , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< unsigned int>> - }, - { - Datatype::VEC_ULONG , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< unsigned long>> - }, - { - Datatype::VEC_ULONGLONG , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< unsigned long long>> - }, - { - Datatype::VEC_FLOAT , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< float>> - }, - { - Datatype::VEC_DOUBLE , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< double>> - }, - { - Datatype::VEC_LONG_DOUBLE , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< long double>> - }, - { - Datatype::VEC_STRING , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::vector< std::string>> - }, - { - Datatype::ARR_DBL_7 , - &Action::OPENPMD_TEMPLATE_OPERATOR() < std::array< - double, - 7>> - }, - { - Datatype::BOOL , - &Action::OPENPMD_TEMPLATE_OPERATOR() < bool > } - }; - auto it = funs.find( dt ); - if( it != funs.end( ) ) - { - return ( ( action ).* - ( it->second ) )( std::forward< Args >( args )... ); - } - else +template < typename ReturnType = void, typename Action, typename... Args > +ReturnType switchType( Datatype dt, Action action, Args &&... args ) +{ + switch ( dt ) { + case Datatype::CHAR: + return action.OPENPMD_TEMPLATE_OPERATOR( )< char >( + std::forward< Args >( args )... ); + case Datatype::UCHAR: + return action.OPENPMD_TEMPLATE_OPERATOR( )< unsigned char >( + std::forward< Args >( args )... ); + case Datatype::SHORT: + return action.OPENPMD_TEMPLATE_OPERATOR( )< short >( + std::forward< Args >( args )... ); + case Datatype::INT: + return action.OPENPMD_TEMPLATE_OPERATOR( )< int >( + std::forward< Args >( args )... ); + case Datatype::LONG: + return action.OPENPMD_TEMPLATE_OPERATOR( )< long >( + std::forward< Args >( args )... ); + case Datatype::LONGLONG: + return action.OPENPMD_TEMPLATE_OPERATOR( )< long long >( + std::forward< Args >( args )... ); + case Datatype::USHORT: + return action.OPENPMD_TEMPLATE_OPERATOR( )< unsigned short >( + std::forward< Args >( args )... ); + case Datatype::UINT: + return action.OPENPMD_TEMPLATE_OPERATOR( )< unsigned int >( + std::forward< Args >( args )... ); + case Datatype::ULONG: + return action.OPENPMD_TEMPLATE_OPERATOR( )< unsigned long >( + std::forward< Args >( args )... ); + case Datatype::ULONGLONG: + return action.OPENPMD_TEMPLATE_OPERATOR( )< unsigned long long >( + std::forward< Args >( args )... ); + case Datatype::FLOAT: + return action.OPENPMD_TEMPLATE_OPERATOR( )< float >( + std::forward< Args >( args )... ); + case Datatype::DOUBLE: + return action.OPENPMD_TEMPLATE_OPERATOR( )< double >( + std::forward< Args >( args )... ); + case Datatype::LONG_DOUBLE: + return action.OPENPMD_TEMPLATE_OPERATOR( )< long double >( + std::forward< Args >( args )... ); + case Datatype::STRING: + return action.OPENPMD_TEMPLATE_OPERATOR( )< std::string >( + std::forward< Args >( args )... ); + case Datatype::VEC_CHAR: + return action.OPENPMD_TEMPLATE_OPERATOR( )< std::vector< char > >( + std::forward< Args >( args )... ); + case Datatype::VEC_SHORT: + return action.OPENPMD_TEMPLATE_OPERATOR( )< std::vector< short > >( + std::forward< Args >( args )... ); + case Datatype::VEC_INT: + return action.OPENPMD_TEMPLATE_OPERATOR( )< std::vector< int > >( + std::forward< Args >( args )... ); + case Datatype::VEC_LONG: + return action.OPENPMD_TEMPLATE_OPERATOR( )< std::vector< long > >( + std::forward< Args >( args )... ); + case Datatype::VEC_LONGLONG: + return action.OPENPMD_TEMPLATE_OPERATOR( )< std::vector< long long > >( + std::forward< Args >( args )... ); + case Datatype::VEC_UCHAR: + return action + .OPENPMD_TEMPLATE_OPERATOR( )< std::vector< unsigned char > >( + std::forward< Args >( args )... ); + case Datatype::VEC_USHORT: + return action + .OPENPMD_TEMPLATE_OPERATOR( )< std::vector< unsigned short > >( + std::forward< Args >( args )... ); + case Datatype::VEC_UINT: + return action + .OPENPMD_TEMPLATE_OPERATOR( )< std::vector< unsigned int > >( + std::forward< Args >( args )... ); + case Datatype::VEC_ULONG: + return action + .OPENPMD_TEMPLATE_OPERATOR( )< std::vector< unsigned long > >( + std::forward< Args >( args )... ); + case Datatype::VEC_ULONGLONG: + return action + .OPENPMD_TEMPLATE_OPERATOR( )< std::vector< unsigned long long > >( + std::forward< Args >( args )... ); + case Datatype::VEC_FLOAT: + return action.OPENPMD_TEMPLATE_OPERATOR( )< std::vector< float > >( + std::forward< Args >( args )... ); + case Datatype::VEC_DOUBLE: + return action.OPENPMD_TEMPLATE_OPERATOR( )< std::vector< double > >( + std::forward< Args >( args )... ); + case Datatype::VEC_LONG_DOUBLE: + return action + .OPENPMD_TEMPLATE_OPERATOR( )< std::vector< long double > >( + std::forward< Args >( args )... ); + case Datatype::VEC_STRING: + return action + .OPENPMD_TEMPLATE_OPERATOR( )< std::vector< std::string > >( + std::forward< Args >( args )... ); + case Datatype::ARR_DBL_7: + return action.OPENPMD_TEMPLATE_OPERATOR( )< std::array< double, 7 > >( + std::forward< Args >( args )... ); + case Datatype::BOOL: + return action.OPENPMD_TEMPLATE_OPERATOR( )< bool >( + std::forward< Args >( args )... ); + case Datatype::DATATYPE: + return action.OPENPMD_TEMPLATE_OPERATOR( )< 1000 >( + std::forward< Args >( args )... ); + case Datatype::UNDEFINED: + return action.OPENPMD_TEMPLATE_OPERATOR( )< 0 >( + std::forward< Args >( args )... ); + default: throw std::runtime_error( "Internal error: Encountered unknown datatype (switchType) ->" + - std::to_string( static_cast(dt) ) - ); + std::to_string( static_cast< int >( dt ) ) ); } } #undef OPENPMD_TEMPLATE_OPERATOR +namespace detail { + template + struct BasicDatatypeHelper { + Datatype m_dt = determineDatatype(); + }; + + template + struct BasicDatatypeHelper> { + Datatype m_dt = BasicDatatypeHelper{}.m_dt; + }; + + template + struct BasicDatatypeHelper> { + Datatype m_dt = BasicDatatypeHelper{}.m_dt; + }; + + struct BasicDatatype { + template + Datatype operator()(); + + template + Datatype operator()(); + }; +} + +/** + * @brief basicDatatype Strip openPMD Datatype of std::vector, std::array et. al. + * @param dt The "full" Datatype. + * @return The "inner" Datatype. + */ +Datatype basicDatatype(Datatype dt); + +Datatype toVectorType(Datatype dt); + +std::string datatypeToString( Datatype dt ); + +Datatype stringToDatatype( std::string s ); + +extern std::vector< Datatype > openPMD_Datatypes; + std::string datatypeToString( Datatype dt ); Datatype stringToDatatype( std::string s ); diff --git a/include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp b/include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp new file mode 100644 index 0000000000..bfd035ec5a --- /dev/null +++ b/include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include "openPMD/config.hpp" +#if openPMD_HAVE_ADIOS2 +#include "openPMD/Datatype.hpp" +#include +#include +#include + +namespace openPMD +{ +namespace detail +{ + // ADIOS2 does not natively support boolean values + // Since we need them for attributes, + // we represent booleans as unsigned chars + using bool_representation = unsigned char; + + template < typename T > struct ToDatatypeHelper + { + static std::string type( ); + }; + + template < typename T > struct ToDatatypeHelper< std::vector< T > > + { + static std::string type( ); + }; + + template < typename T, size_t n > + struct ToDatatypeHelper< std::array< T, n > > + { + static std::string type( ); + }; + + template <> struct ToDatatypeHelper< bool > + { + static std::string type( ); + }; + + struct ToDatatype + { + template < typename T > std::string operator( )( ); + + + template < int n > std::string operator( )( ); + }; + + /** + * @brief Convert ADIOS2 datatype to openPMD type. + * @param dt + * @return + */ + Datatype fromADIOS2Type( std::string const & dt ); + + template < typename T > struct AttributeInfoHelper + { + static typename std::vector< T >::size_type + getSize( adios2::IO &, std::string const & attributeName ); + }; + + template < typename T > struct AttributeInfoHelper< std::vector< T > > + { + static typename std::vector< T >::size_type + getSize( adios2::IO &, std::string const & attributeName ); + }; + + template < typename T, std::size_t n > + struct AttributeInfoHelper< std::array< T, n > > + { + static typename std::vector< T >::size_type + getSize( adios2::IO & IO, std::string const & attributeName ) + { + return AttributeInfoHelper< T >::getSize( IO, attributeName ); + } + }; + + template <> struct AttributeInfoHelper< bool > + { + static typename std::vector< bool_representation >::size_type + getSize( adios2::IO &, std::string const & attributeName ); + }; + + struct AttributeInfo + { + template < typename T > + typename std::vector< T >::size_type + operator( )( adios2::IO &, std::string const & attributeName ); + + template < int n, typename... Params > + size_t operator( )( Params &&... ); + }; + + Datatype attributeInfo( adios2::IO &, std::string const & attributeName ); +} // namespace detail + +} // namespace openPMD + +#endif \ No newline at end of file diff --git a/include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp b/include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp index 012e7fc05e..9283bbdffe 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Fabian Koller +/* Copyright 2017-2019 Fabian Koller and Franz Pöschel * * This file is part of openPMD-api. * @@ -20,14 +20,49 @@ */ #pragma once + #include "openPMD/IO/AbstractFilePosition.hpp" +#include namespace openPMD { -struct ADIOS2FilePosition : public AbstractFilePosition -{ - ADIOS2FilePosition() - { } -}; // ADIOS2FilePosition + struct ADIOS2FilePosition : + public AbstractFilePosition + { + enum class GD + { + GROUP, + DATASET + }; + + + ADIOS2FilePosition( + std::string s, + GD groupOrDataset + ) : + location { std::move( s ) }, + gd { groupOrDataset } + {} + + + explicit ADIOS2FilePosition( GD groupOrDataset ) : + ADIOS2FilePosition { + "/", + groupOrDataset + } + {} + + + ADIOS2FilePosition( ) : + ADIOS2FilePosition{ GD::GROUP } + {} + + + /** + * Convention: Starts with slash '/', ends without. + */ + std::string location; + GD gd; + }; // ADIOS2FilePosition } // openPMD diff --git a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp index 696809ba03..fa750f28f6 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Fabian Koller +/* Copyright 2017-2019 Fabian Koller and Franz Pöschel * * This file is part of openPMD-api. * @@ -21,64 +21,606 @@ #pragma once #include "openPMD/config.hpp" + +#include "ADIOS2FilePosition.hpp" #include "openPMD/IO/AbstractIOHandler.hpp" +#include "openPMD/IO/AbstractIOHandlerImpl.hpp" +#include "openPMD/IO/AbstractIOHandlerImplCommon.hpp" +#include "openPMD/IO/IOTask.hpp" +#include "openPMD/IO/InvalidatableFile.hpp" +#include "openPMD/backend/Writable.hpp" +#include #include -#include +#include // shared_ptr #include +#include +#include // pair +#include + + +#if openPMD_HAVE_ADIOS2 +#include +#include "openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp" +#endif + +#if openPMD_HAVE_MPI +#include +#endif namespace openPMD { #if openPMD_HAVE_ADIOS2 + class ADIOS2IOHandler; +namespace detail +{ + template < typename, typename > struct DatasetHelper; + struct DatasetReader; + struct AttributeReader; + struct AttributeWriter; + template < typename > struct AttributeTypes; + struct DatasetOpener; + template < typename > struct DatasetTypes; + struct WriteDataset; + struct BufferedActions; + struct BufferedPut; + struct BufferedGet; + struct BufferedAttributeRead; +} // namespace detail + + class ADIOS2IOHandlerImpl +: public AbstractIOHandlerImplCommon< ADIOS2FilePosition > { + template < typename, typename > friend struct detail::DatasetHelper; + friend struct detail::DatasetReader; + friend struct detail::AttributeReader; + friend struct detail::AttributeWriter; + template < typename > friend struct detail::AttributeTypes; + friend struct detail::DatasetOpener; + template < typename > friend struct detail::DatasetTypes; + friend struct detail::WriteDataset; + friend struct detail::BufferedActions; + friend struct detail::BufferedAttributeRead; + + static constexpr bool ADIOS2_DEBUG_MODE = false; + + public: - ADIOS2IOHandlerImpl(AbstractIOHandler*); - virtual ~ADIOS2IOHandlerImpl(); - - virtual std::future< void > flush(); - - using ArgumentMap = std::map< std::string, ParameterArgument >; - virtual void createFile(Writable*, ArgumentMap const&); - virtual void createPath(Writable*, ArgumentMap const&); - virtual void createDataset(Writable*, ArgumentMap const&); - virtual void extendDataset(Writable*, ArgumentMap const&); - virtual void openFile(Writable*, ArgumentMap const&); - virtual void openPath(Writable*, ArgumentMap const&); - virtual void openDataset(Writable*, ArgumentMap &); - virtual void deleteFile(Writable*, ArgumentMap const&); - virtual void deletePath(Writable*, ArgumentMap const&); - virtual void deleteDataset(Writable*, ArgumentMap const&); - virtual void deleteAttribute(Writable*, ArgumentMap const&); - virtual void writeDataset(Writable*, ArgumentMap const&); - virtual void writeAttribute(Writable*, ArgumentMap const&); - virtual void readDataset(Writable*, ArgumentMap &); - virtual void readAttribute(Writable*, ArgumentMap &); - virtual void listPaths(Writable*, ArgumentMap &); - virtual void listDatasets(Writable*, ArgumentMap &); - virtual void listAttributes(Writable*, ArgumentMap &); - - AbstractIOHandler* m_handler; -}; //ADIOS2IOHandlerImpl -#else -class ADIOS2IOHandlerImpl -{ }; -#endif + static_assert( + sizeof( bool ) == 1, + "ADIOS2 backend needs a platform with boolean size equals one byte." ); + + +#if openPMD_HAVE_MPI + + ADIOS2IOHandlerImpl( AbstractIOHandler *, MPI_Comm ); + + MPI_Comm m_comm; + +#endif // openPMD_HAVE_MPI + + explicit ADIOS2IOHandlerImpl( AbstractIOHandler * ); + + + ~ADIOS2IOHandlerImpl( ) override; + + std::future< void > flush( ) override; + + void createFile( Writable *, + Parameter< Operation::CREATE_FILE > const & ) override; + + void createPath( Writable *, + Parameter< Operation::CREATE_PATH > const & ) override; + + void + createDataset( Writable *, + Parameter< Operation::CREATE_DATASET > const & ) override; + + void + extendDataset( Writable *, + Parameter< Operation::EXTEND_DATASET > const & ) override; + + void openFile( Writable *, + Parameter< Operation::OPEN_FILE > const & ) override; + + void openPath( Writable *, + Parameter< Operation::OPEN_PATH > const & ) override; + + void openDataset( Writable *, + Parameter< Operation::OPEN_DATASET > & ) override; + + void deleteFile( Writable *, + Parameter< Operation::DELETE_FILE > const & ) override; + + void deletePath( Writable *, + Parameter< Operation::DELETE_PATH > const & ) override; + + void + deleteDataset( Writable *, + Parameter< Operation::DELETE_DATASET > const & ) override; + + void deleteAttribute( Writable *, + Parameter< Operation::DELETE_ATT > const & ) override; + + void writeDataset( Writable *, + Parameter< Operation::WRITE_DATASET > const & ) override; + + void writeAttribute( Writable *, + Parameter< Operation::WRITE_ATT > const & ) override; + + void readDataset( Writable *, + Parameter< Operation::READ_DATASET > & ) override; + + void readAttribute( Writable *, + Parameter< Operation::READ_ATT > & ) override; + + void listPaths( Writable *, Parameter< Operation::LIST_PATHS > & ) override; + + void listDatasets( Writable *, + Parameter< Operation::LIST_DATASETS > & ) override; + + void + listAttributes( Writable *, + Parameter< Operation::LIST_ATTS > & parameters ) override; + + + + /** + * @brief The ADIOS2 access type to chose for Engines opened + * within this instance. + */ + adios2::Mode adios2Accesstype( ); + + +private: + adios2::ADIOS m_ADIOS; + + /* + * We need to give names to IO objects. These names are irrelevant + * within this application, since: + * 1) The name of the file written to is decided by the opened Engine's + * name. + * 2) The IOs are managed by the unordered_map m_fileData, so we do not + * need the ADIOS2 internal management. + * Since within one m_ADIOS object, the same IO name cannot be used more + * than once, we ensure different names by using the name counter. + * This allows to overwrite a file later without error. + */ + int nameCounter{0}; + + /* + * IO-heavy actions are deferred to a later point. This map stores for + * each open file (identified by an InvalidatableFile object) an object + * that manages IO-heavy actions, as well as its ADIOS2 objects, i.e. + * IO and Engine object. + * Not to be accessed directly, use getFileData(). + */ + std::unordered_map< InvalidatableFile, + std::unique_ptr< detail::BufferedActions > > + m_fileData; + + std::map< std::string, adios2::Operator > m_operators; + + // Overrides from AbstractIOHandlerImplCommon. + + std::string + filePositionToString( std::shared_ptr< ADIOS2FilePosition > ) override; + + std::shared_ptr< ADIOS2FilePosition > + extendFilePosition( std::shared_ptr< ADIOS2FilePosition > const & pos, + std::string extend ) override; + + // Helper methods. + + std::unique_ptr< adios2::Operator > + getCompressionOperator( std::string const & compression ); + + /* + * The name of the ADIOS2 variable associated with this Writable. + * To be used for Writables that represent a dataset. + */ + std::string nameOfVariable( Writable * writable ); + + /** + * @brief nameOfAttribute + * @param The Writable at whose level the attribute lies. + * @param The openPMD name of the attribute. + * @return The ADIOS2 name of the attribute, consisting of + * the variable that the attribute is associated with + * (possibly the empty string, representing no variable) + * and the actual name. + */ + std::string nameOfAttribute( Writable * writable, std::string attribute ); + + /* + * Figure out whether the Writable corresponds with a + * group or a dataset. + */ + ADIOS2FilePosition::GD groupOrDataset( Writable * ); + + detail::BufferedActions & getFileData( InvalidatableFile file ); + + void dropFileData( InvalidatableFile file ); + + /* + * Prepare a variable that already exists for an IO + * operation, including: + * (1) checking that its datatype matches T. + * (2) the offset and extent match the variable's shape + * (3) setting the offset and extent (ADIOS lingo: start + * and count) + */ + template < typename T > + adios2::Variable< T > verifyDataset( Offset const & offset, + Extent const & extent, adios2::IO & IO, + std::string const & var ); + + + /* + * At the time of writing this, the ADIOS2 API supports creating + * attributes in a variable's scope, but only retrieving the type + * of an attribute defined in global (i.e. the IO's) scope + */ + std::unique_ptr< std::pair< std::string, int > > + workaroundDatatypeOfAttribute( adios2::IO & IO, std::string variable, + std::string attribute ); + +}; // ADIOS2IOHandlerImpl + +namespace detail +{ + // Helper structs for calls to the switchType function + + struct DatasetReader + { + openPMD::ADIOS2IOHandlerImpl * m_impl; + + + explicit DatasetReader( openPMD::ADIOS2IOHandlerImpl * impl ); + + + template < typename T > + void operator( )( BufferedGet & bp, adios2::IO & IO, + adios2::Engine & engine, + std::string const & fileName ); + + template < int T, typename... Params > void operator( )( Params &&... ); + }; + + struct AttributeReader + { + template < typename T > + Datatype operator( )( adios2::IO & IO, std::string name, + std::shared_ptr< Attribute::resource > resource ); + + template < int n, typename... Params > + Datatype operator( )( Params &&... ); + }; + + struct AttributeWriter + { + template < typename T > + void + operator( )( ADIOS2IOHandlerImpl * impl, Writable * writable, + const Parameter< Operation::WRITE_ATT > & parameters ); + + + template < int n, typename... Params > void operator( )( Params &&... ); + }; + + struct DatasetOpener + { + ADIOS2IOHandlerImpl * m_impl; + + + explicit DatasetOpener( ADIOS2IOHandlerImpl * impl ); + + + template < typename T > + void operator( )( InvalidatableFile, const std::string & varName, + Parameter< Operation::OPEN_DATASET > & parameters ); + + + template < int n, typename... Params > void operator( )( Params &&... ); + }; + + struct WriteDataset + { + ADIOS2IOHandlerImpl * m_handlerImpl; + + + WriteDataset( ADIOS2IOHandlerImpl * handlerImpl ); + + + template < typename T > + void operator( )( BufferedPut & bp, adios2::IO & IO, + adios2::Engine & engine ); + + template < int n, typename... Params > void operator( )( Params &&... ); + }; + + struct VariableDefiner + { + + template < typename T > + void operator( )( adios2::IO & IO, const std::string & name, + std::unique_ptr< adios2::Operator > compression, + const adios2::Dims & shape = adios2::Dims( ), + const adios2::Dims & start = adios2::Dims( ), + const adios2::Dims & count = adios2::Dims( ), + bool constantDims = false ); + + template < int n, typename... Params > + void operator( )( adios2::IO & IO, Params &&... ); + }; + + + + // Helper structs to help distinguish valid attribute/variable + // datatypes from invalid ones + + + /* + * This struct's purpose is to have specialisations + * for vector and array types, as well as the boolean + * type (which is not natively supported by ADIOS). + */ + template < typename T > struct AttributeTypes + { + using Attr = adios2::Attribute< T >; + using BasicType = T; + + static Attr createAttribute( adios2::IO & IO, std::string name, + BasicType value ); + + static void + readAttribute( adios2::IO & IO, std::string name, + std::shared_ptr< Attribute::resource > resource ); + }; + + template < typename T > struct AttributeTypes< std::vector< T > > + { + using Attr = adios2::Attribute< T >; + using BasicType = T; + + static Attr createAttribute( adios2::IO & IO, std::string name, + const std::vector< T > & value ); + + static void + readAttribute( adios2::IO & IO, std::string name, + std::shared_ptr< Attribute::resource > resource ); + }; + + template < typename T, size_t n > + struct AttributeTypes< std::array< T, n > > + { + using Attr = adios2::Attribute< T >; + using BasicType = T; + + static Attr createAttribute( adios2::IO & IO, std::string name, + const std::array< T, n > & value ); + + static void + readAttribute( adios2::IO & IO, std::string name, + std::shared_ptr< Attribute::resource > resource ); + }; + + template <> struct AttributeTypes< bool > + { + using rep = detail::bool_representation; + using Attr = adios2::Attribute< rep >; + using BasicType = rep; + + static Attr createAttribute( adios2::IO & IO, std::string name, + bool value ); + + static void + readAttribute( adios2::IO & IO, std::string name, + std::shared_ptr< Attribute::resource > resource ); + + + static constexpr rep toRep( bool b ) + { + return b ? 1U : 0U; + } + + + static constexpr bool fromRep( rep r ) + { + return r != 0; + } + }; + + + /** + * This struct's only field indicates whether the template + * parameter is a valid datatype to use for a dataset + * (ADIOS2 variable). + */ + template < typename T > struct DatasetTypes + { + static constexpr bool validType = true; + }; + + template < typename T > struct DatasetTypes< std::vector< T > > + { + static constexpr bool validType = false; + }; + + template <> struct DatasetTypes< bool > + { + static constexpr bool validType = false; + }; + + + template < typename T, size_t n > struct DatasetTypes< std::array< T, n > > + { + static constexpr bool validType = false; + }; + + /* + * This struct's purpose is to have exactly two specialisations: + * (1) for types that are legal to use in a dataset + * (2) for types that are not legal to use in a dataset + * The methods in the latter specialisation will fail at runtime. + */ + template < typename, typename = void > struct DatasetHelper; + + template < typename T > + struct DatasetHelper< + T, typename std::enable_if< DatasetTypes< T >::validType >::type > + { + openPMD::ADIOS2IOHandlerImpl * m_impl; + + + explicit DatasetHelper( openPMD::ADIOS2IOHandlerImpl * impl ); + + + void openDataset( InvalidatableFile, const std::string & varName, + Parameter< Operation::OPEN_DATASET > & parameters ); + + void readDataset( BufferedGet &, adios2::IO &, adios2::Engine &, + std::string const & fileName ); + + static void + defineVariable( adios2::IO & IO, const std::string & name, + std::unique_ptr< adios2::Operator > compression, + const adios2::Dims & shape, const adios2::Dims & start, + const adios2::Dims & count, bool constantDims ); + + void writeDataset( BufferedPut &, adios2::IO &, adios2::Engine & ); + }; + + template < typename T > + struct DatasetHelper< + T, typename std::enable_if< !DatasetTypes< T >::validType >::type > + { + explicit DatasetHelper( openPMD::ADIOS2IOHandlerImpl * impl ); + + + static void throwErr( ); + + template < typename... Params > void openDataset( Params &&... ); + + template < typename... Params > void readDataset( Params &&... ); + + template < typename... Params > + static void defineVariable( Params &&... ); + + template < typename... Params > void writeDataset( Params &&... ); + }; + + // Other datatypes used in the ADIOS2IOHandler implementation + + + struct BufferedActions; + + /* + * IO-heavy action to be executed upon flushing. + */ + struct BufferedAction + { + virtual ~BufferedAction( ) = default; + + virtual void run( BufferedActions & ) = 0; + }; + + struct BufferedGet : BufferedAction + { + std::string name; + Parameter< Operation::READ_DATASET > param; + + void run( BufferedActions & ) override; + }; + + struct BufferedPut : BufferedAction + { + std::string name; + Parameter< Operation::WRITE_DATASET > param; + + void run( BufferedActions & ) override; + }; + + struct BufferedAttributeRead : BufferedAction + { + Parameter< Operation::READ_ATT > param; + std::string name; + + void run( BufferedActions & ) override; + }; + + /* + * Manages per-file information about + * (1) the file's IO and Engine objects + * (2) the file's deferred IO-heavy actions + */ + struct BufferedActions + { + BufferedActions( BufferedActions const & ) = delete; + + std::string m_file; + adios2::IO m_IO; + std::vector< std::unique_ptr< BufferedAction > > m_buffer; + // std::optional would be more idiomatic, but it's not in + // the C++11 standard + std::unique_ptr< adios2::Engine > m_engine; + adios2::Mode m_mode; + detail::WriteDataset m_writeDataset; + detail::DatasetReader m_readDataset; + detail::AttributeReader m_attributeReader; + ADIOS2IOHandlerImpl & m_impl; + + + BufferedActions( ADIOS2IOHandlerImpl & impl, InvalidatableFile file ); + + ~BufferedActions( ); + + adios2::Engine & getEngine( ); + + template < typename BA > void enqueue( BA && ba ); + + + void flush( ); + + /* + * Delete all buffered actions without running them. + */ + void drop( ); + }; + + +} // namespace detail +#endif // openPMD_HAVE_ADIOS2 + class ADIOS2IOHandler : public AbstractIOHandler { - friend class ADIOS2IOHandlerImpl; +#if openPMD_HAVE_ADIOS2 + +friend class ADIOS2IOHandlerImpl; + +private: + ADIOS2IOHandlerImpl m_impl; public: - ADIOS2IOHandler(std::string path, AccessType); - ~ADIOS2IOHandler() override; + ~ADIOS2IOHandler( ) override; - std::future< void > flush() override; +#else +public: +#endif -private: - std::unique_ptr< ADIOS2IOHandlerImpl > m_impl; -}; //ADIOS2IOHandler -} // openPMD +#if openPMD_HAVE_MPI + + ADIOS2IOHandler( std::string path, AccessType, MPI_Comm ); + +#endif + + ADIOS2IOHandler( std::string path, AccessType ); + + std::future< void > flush( ) override; +}; // ADIOS2IOHandler +} // namespace openPMD diff --git a/include/openPMD/IO/AbstractIOHandlerImplCommon.hpp b/include/openPMD/IO/AbstractIOHandlerImplCommon.hpp new file mode 100644 index 0000000000..ea2857e7e1 --- /dev/null +++ b/include/openPMD/IO/AbstractIOHandlerImplCommon.hpp @@ -0,0 +1,259 @@ +/* Copyright 2018-2019 Franz Pöschel + * + * This file is part of openPMD-api. + * + * openPMD-api is free software: you can redistribute it and/or modify + * it under the terms of of either the GNU General Public License or + * the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * openPMD-api is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with openPMD-api. + * If not, see . + */ + +#pragma once + + +#include "openPMD/IO/AbstractFilePosition.hpp" +#include "openPMD/IO/AbstractIOHandler.hpp" +#include "openPMD/IO/AbstractIOHandlerImpl.hpp" +#include "openPMD/IO/InvalidatableFile.hpp" +#include "openPMD/auxiliary/StringManip.hpp" +#include "openPMD/backend/Writable.hpp" + +#include +#include + + + +namespace openPMD +{ +template < typename FilePositionType = AbstractFilePosition > +class AbstractIOHandlerImplCommon : public AbstractIOHandlerImpl +{ + // friend struct detail::BufferedActions; +public: + explicit AbstractIOHandlerImplCommon( AbstractIOHandler * handler ); + + ~AbstractIOHandlerImplCommon( ) override; + +protected: + /** + * map each Writable to its associated file contains only the filename, + * without the OS path + */ + std::unordered_map< Writable *, InvalidatableFile > m_files; + std::unordered_set< InvalidatableFile > m_dirty; + + enum PossiblyExisting + { + PE_InvalidatableFile = 0, + PE_Iterator, + PE_NewlyCreated, + }; + + std::tuple< InvalidatableFile, + std::unordered_map< Writable *, InvalidatableFile >::iterator, + bool > + getPossiblyExisting( std::string file ); + + void associateWithFile( Writable * writable, InvalidatableFile file ); + + /** + * + * @return Full OS path of the file. + */ + std::string fullPath( InvalidatableFile ); + + std::string fullPath( std::string ); + + /** + * Get the writable's containing file. + * @param writable The writable whose containing file to figure out. + * @return The containing file of the writable. If its parent is associated + * with another file, update the writable to match its parent and return + * the refreshed file. + */ + InvalidatableFile refreshFileFromParent( Writable * writable ); + + /** + * Figure out the file position of the writable. + * Only modify the writable's fileposition when specified. + * @param writable The writable. + * @param write Whether to refresh the writable's file position. + * @return The current file position. + */ + std::shared_ptr< FilePositionType > + setAndGetFilePosition( Writable * writable, bool write = true ); + + /** + * Figure out the file position of the writable and extend it. + * @param writable The writable. + * @param write The extension string. + * @return The current file position. + */ + virtual std::shared_ptr< FilePositionType > + setAndGetFilePosition( Writable * writable, std::string extend ); + + /** + * @return A string representation of the file position. + */ + virtual std::string + filePositionToString( std::shared_ptr< FilePositionType > ) = 0; + + /** + * @return A new file position that is extended with the given string. + */ + virtual std::shared_ptr< FilePositionType > + extendFilePosition( std::shared_ptr< FilePositionType > const &, + std::string ) = 0; +}; + +template < typename FilePositionType > +AbstractIOHandlerImplCommon< FilePositionType >::AbstractIOHandlerImplCommon( + AbstractIOHandler * handler ) +: AbstractIOHandlerImpl{handler} +{ +} + + +template < typename FilePositionType > +AbstractIOHandlerImplCommon< + FilePositionType >::~AbstractIOHandlerImplCommon( ) = default; + + +template < typename FilePositionType > +std::tuple< InvalidatableFile, + std::unordered_map< Writable *, InvalidatableFile >::iterator, + bool > +AbstractIOHandlerImplCommon< FilePositionType >::getPossiblyExisting( + std::string file ) +{ + + auto it = std::find_if( + m_files.begin( ), m_files.end( ), + [file]( std::unordered_map< + Writable *, InvalidatableFile >::value_type const & entry ) { + return *entry.second == file && entry.second.valid( ); + } ); + + bool newlyCreated; + InvalidatableFile name; + if ( it == m_files.end( ) ) + { + name = file; + newlyCreated = true; + } + else + { + name = it->second; + newlyCreated = false; + } + return std::tuple< + InvalidatableFile, + std::unordered_map< Writable *, InvalidatableFile >::iterator, bool >( + std::move( name ), it, newlyCreated ); +} + + +template < typename FilePositionType > +void AbstractIOHandlerImplCommon< FilePositionType >::associateWithFile( + Writable * writable, InvalidatableFile file ) +{ + // make sure to overwrite + m_files[writable] = std::move( file ); +} + + +template < typename FilePositionType > +std::string AbstractIOHandlerImplCommon< FilePositionType >::fullPath( + InvalidatableFile fileName ) +{ + return fullPath( *fileName ); +} + + +template < typename FilePositionType > +std::string AbstractIOHandlerImplCommon< FilePositionType >::fullPath( + std::string fileName ) +{ + if ( auxiliary::ends_with( m_handler->directory, "/" ) ) + { + return m_handler->directory + fileName; + } + else + { + return m_handler->directory + "/" + fileName; + } +} + + +template < typename FilePositionType > +InvalidatableFile +AbstractIOHandlerImplCommon< FilePositionType >::refreshFileFromParent( + Writable * writable ) +{ + if ( writable->parent ) + { + auto file = m_files.find( writable->parent )->second; + associateWithFile( writable, file ); + return file; + } + else + { + return m_files.find( writable )->second; + } +} + + +template < typename FilePositionType > +std::shared_ptr< FilePositionType > +AbstractIOHandlerImplCommon< FilePositionType >::setAndGetFilePosition( + Writable * writable, bool write ) +{ + std::shared_ptr< AbstractFilePosition > res; + + if ( writable->abstractFilePosition ) + { + res = writable->abstractFilePosition; + } + else if ( writable->parent ) + { + res = writable->parent->abstractFilePosition; + } + else + { // we are root + res = std::make_shared< FilePositionType >( ); + } + if ( write ) + { + writable->abstractFilePosition = res; + } + return std::dynamic_pointer_cast< FilePositionType >( res ); +} + + +template < typename FilePositionType > +std::shared_ptr< FilePositionType > +AbstractIOHandlerImplCommon< FilePositionType >::setAndGetFilePosition( + Writable * writable, std::string extend ) +{ + if ( !auxiliary::starts_with( extend, '/' ) ) + { + extend = "/" + extend; + } + auto oldPos = setAndGetFilePosition( writable, false ); + auto res = extendFilePosition( oldPos, extend ); + + writable->abstractFilePosition = res; + return res; +} +} // namespace openPMD diff --git a/include/openPMD/IO/AccessType.hpp b/include/openPMD/IO/AccessType.hpp index ab8b43533a..1d982ac724 100644 --- a/include/openPMD/IO/AccessType.hpp +++ b/include/openPMD/IO/AccessType.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Fabian Koller +/* Copyright 2017-2019 Fabian Koller and Franz Pöschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/IO/IOTask.hpp b/include/openPMD/IO/IOTask.hpp index 22bf2ae16d..0e2ba0ce19 100644 --- a/include/openPMD/IO/IOTask.hpp +++ b/include/openPMD/IO/IOTask.hpp @@ -301,10 +301,18 @@ template<> struct EXPORT Parameter< Operation::WRITE_DATASET > : public AbstractParameter { Parameter() = default; - Parameter(Parameter const & p) : AbstractParameter(), + Parameter(Parameter const & p) : AbstractParameter(), extent(p.extent), offset(p.offset), dtype(p.dtype), data(p.data) {}; + Parameter& operator=(const Parameter& p) { + this->extent = p.extent; + this->offset = p.offset; + this->dtype = p.dtype; + this->data = p.data; + return *this; + } + std::unique_ptr< AbstractParameter > clone() const override { @@ -322,10 +330,18 @@ template<> struct EXPORT Parameter< Operation::READ_DATASET > : public AbstractParameter { Parameter() = default; - Parameter(Parameter const & p) : AbstractParameter(), + Parameter(Parameter const & p) : AbstractParameter(), extent(p.extent), offset(p.offset), dtype(p.dtype), data(p.data) {}; + Parameter& operator=(const Parameter &p) { + this->extent = p.extent; + this->offset = p.offset; + this->dtype = p.dtype; + this->data = p.data; + return *this; + } + std::unique_ptr< AbstractParameter > clone() const override { @@ -399,6 +415,13 @@ struct EXPORT Parameter< Operation::READ_ATT > : public AbstractParameter Parameter(Parameter const & p) : AbstractParameter(), name(p.name), dtype(p.dtype), resource(p.resource) {}; + Parameter& operator=(const Parameter &p) { + this->name = p.name; + this->dtype = p.dtype; + this->resource = p.resource; + return *this; + } + std::unique_ptr< AbstractParameter > clone() const override { diff --git a/include/openPMD/IO/InvalidatableFile.hpp b/include/openPMD/IO/InvalidatableFile.hpp new file mode 100644 index 0000000000..094dd2e1cc --- /dev/null +++ b/include/openPMD/IO/InvalidatableFile.hpp @@ -0,0 +1,96 @@ +/* Copyright 2018-2019 Franz Pöschel + * + * This file is part of openPMD-api. + * + * openPMD-api is free software: you can redistribute it and/or modify + * it under the terms of of either the GNU General Public License or + * the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * openPMD-api is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with openPMD-api. + * If not, see . + */ +#pragma once + + +#include +#include + + +namespace openPMD +{ + /** + * Wrapper around a shared pointer to: + * * a filename + * * and a boolean indicating whether the file still exists + * The wrapper adds no extra information, but some commodity functions. + * Invariant for any context within which this class shall be used: + * For any valid filename, there is at any time at most one + * such shared pointer (wrapper) known in said context's data structures + * (counting by pointer equality) + * This means, that a file can be invalidated (i.e. deleted or overwritten) + * by simply searching for one instance of the file among all known files and + * invalidating this instance + * A new instance may hence only be created after making sure that there are + * no valid instances in the data structures. + */ + struct InvalidatableFile + { + explicit InvalidatableFile( std::string s ); + + + InvalidatableFile( ) = default; + + + struct FileState + { + explicit FileState( std::string s ); + + std::string name; + bool valid = true; + }; + + std::shared_ptr< FileState > fileState; + + + void invalidate( ); + + + bool valid( ) const; + + + InvalidatableFile & operator=( std::string s ); + + + bool operator==( InvalidatableFile const & f ) const; + + + std::string & operator*( ) const; + + + std::string * operator->( ) const; + + + explicit operator bool( ) const; + }; +} + +namespace std +{ + template< > + struct hash< openPMD::InvalidatableFile > + { + using argument_type = openPMD::InvalidatableFile; + using result_type = std::size_t; + + result_type operator()( argument_type const & s ) const noexcept; + }; +} diff --git a/include/openPMD/auxiliary/Environment.hpp b/include/openPMD/auxiliary/Environment.hpp new file mode 100644 index 0000000000..326dd204fa --- /dev/null +++ b/include/openPMD/auxiliary/Environment.hpp @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include +#include + +namespace openPMD +{ +namespace auxiliary +{ + + inline int getEnvNum( std::string const & key, int defaultValue ) + { + char const * env = std::getenv( key.c_str( ) ); + if ( env != nullptr ) + { + std::string env_string{env}; + try + { + return std::stoi( env_string ); + } + catch ( std::invalid_argument const & ) + { + return defaultValue; + } + } + else + return defaultValue; + } +} // namespace auxiliary +} // namespace openPMD diff --git a/include/openPMD/auxiliary/StringManip.hpp b/include/openPMD/auxiliary/StringManip.hpp index fdbd32df05..4f1241a542 100644 --- a/include/openPMD/auxiliary/StringManip.hpp +++ b/include/openPMD/auxiliary/StringManip.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Fabian Koller +/* Copyright 2017-2019 Fabian Koller, Franz Pöschel * * This file is part of openPMD-api. * @@ -105,6 +105,26 @@ replace_last(std::string s, return s; } +inline std::string +replace_all_nonrecursively(std::string s, + std::string const& target, + std::string const& replacement) +{ + std::string::size_type pos = 0; + auto tsize = target.size(); + auto rsize = replacement.size(); + while (true) + { + pos = s.find(target, pos); + if (pos == std::string::npos) + break; + s.replace(pos, tsize, replacement); + pos += rsize; + } + s.shrink_to_fit(); + return s; +} + inline std::string replace_all(std::string s, std::string const& target, @@ -183,5 +203,34 @@ join(std::vector< std::string > const& vs, std::string const& delimiter) return ss.str(); } } + +inline std::string +removeSlashes( std::string s ) +{ + if( auxiliary::starts_with( + s, + '/' + ) ) + { + s = auxiliary::replace_first( + s, + "/", + "" + ); + } + if( auxiliary::ends_with( + s, + '/' + ) ) + { + s = auxiliary::replace_last( + s, + "/", + "" + ); + } + return s; +} + } // auxiliary } // openPMD diff --git a/include/openPMD/backend/BaseRecordComponent.hpp b/include/openPMD/backend/BaseRecordComponent.hpp index 818f6d0a26..2c7777284c 100644 --- a/include/openPMD/backend/BaseRecordComponent.hpp +++ b/include/openPMD/backend/BaseRecordComponent.hpp @@ -81,6 +81,14 @@ struct DefaultValue { rc.makeConstant( T() ); } + + template< unsigned n, typename... Args > + void + operator()( Args &&... ) + { + throw std::runtime_error( + "makeEmpty: Datatype not supported by openPMD." ); + } }; } // namespace detail } // namespace openPMD diff --git a/include/openPMD/backend/Writable.hpp b/include/openPMD/backend/Writable.hpp index 6f6b574381..ed881d4ac0 100644 --- a/include/openPMD/backend/Writable.hpp +++ b/include/openPMD/backend/Writable.hpp @@ -38,6 +38,10 @@ struct TestHelper; class AbstractFilePosition; class AbstractIOHandler; class Attributable; +struct ADIOS2FilePosition; +template +class AbstractIOHandlerImplCommon; + /** @brief Layer to mirror structure of logical data and persistent data in file. * @@ -69,6 +73,7 @@ class Writable friend class ADIOS2IOHandlerImpl; friend class HDF5IOHandlerImpl; friend class ParallelHDF5IOHandlerImpl; + friend class AbstractIOHandlerImplCommon; friend class JSONIOHandlerImpl; friend struct test::TestHelper; friend std::string concrete_h5_file_position(Writable*); diff --git a/src/Datatype.cpp b/src/Datatype.cpp index 2969bb0650..3b94f2fc25 100644 --- a/src/Datatype.cpp +++ b/src/Datatype.cpp @@ -303,4 +303,90 @@ namespace openPMD os << dt; return buf.str(); } + + std::vector openPMD_Datatypes{ + Datatype::CHAR , + Datatype::UCHAR, + Datatype::SHORT, + Datatype::INT, + Datatype::LONG, + Datatype::LONGLONG, + Datatype::USHORT, + Datatype::UINT, + Datatype::ULONG, + Datatype::ULONGLONG, + Datatype::FLOAT, + Datatype::DOUBLE, + Datatype::LONG_DOUBLE, + Datatype::STRING, + Datatype::VEC_CHAR, + Datatype::VEC_SHORT, + Datatype::VEC_INT, + Datatype::VEC_LONG, + Datatype::VEC_LONGLONG, + Datatype::VEC_UCHAR, + Datatype::VEC_USHORT, + Datatype::VEC_UINT, + Datatype::VEC_ULONG, + Datatype::VEC_ULONGLONG, + Datatype::VEC_FLOAT, + Datatype::VEC_DOUBLE, + Datatype::VEC_LONG_DOUBLE, + Datatype::VEC_STRING, + Datatype::ARR_DBL_7, + Datatype::BOOL, + Datatype::DATATYPE, + Datatype::UNDEFINED + }; + + + Datatype basicDatatype( Datatype dt ) + { + return switchType(dt, detail::BasicDatatype{}); + } + + + Datatype toVectorType( Datatype dt ) + { + auto initializer = []() { + std::map res; + for (Datatype d: openPMD_Datatypes) { + if (d == Datatype::ARR_DBL_7 + || d == Datatype::UNDEFINED + || d == Datatype::DATATYPE) + continue; + Datatype basic = basicDatatype(d); + if (basic == d) + continue; + res[basic] = d; + } + return res; + }; + static auto map (initializer()); + auto it = map.find(dt); + if (it != map.end()) { + return it->second; + } else { + std::cerr << "Encountered non-basice type " << dt << ", aborting." + << std::endl; + throw std::runtime_error("toVectorType: passed non-basic type."); + } + } + + + namespace detail { + template< typename T > + Datatype BasicDatatype::operator()() + { + static auto res = BasicDatatypeHelper{}.m_dt; + return res; + } + + + template< int n > + Datatype BasicDatatype::operator()() + { + throw std::runtime_error( "basicDatatype: received unknown datatype." ); + } + } } diff --git a/src/IO/ADIOS/ADIOS2Auxiliary.cpp b/src/IO/ADIOS/ADIOS2Auxiliary.cpp new file mode 100644 index 0000000000..68339377d0 --- /dev/null +++ b/src/IO/ADIOS/ADIOS2Auxiliary.cpp @@ -0,0 +1,172 @@ +#include "openPMD/config.hpp" +#if openPMD_HAVE_ADIOS2 +#include "openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp" +#include "openPMD/Datatype.hpp" +#include + +namespace openPMD +{ +namespace detail +{ + template< typename T > + std::string + ToDatatypeHelper< T >::type() + { + return adios2::GetType< T >(); + } + + template< typename T > + std::string + ToDatatypeHelper< std::vector< T > >::type() + { + return + + adios2::GetType< T >(); + } + + template< typename T, size_t n > + std::string + ToDatatypeHelper< std::array< T, n > >::type() + { + return + + adios2::GetType< T >(); + } + + std::string + ToDatatypeHelper< bool >::type() + { + return ToDatatypeHelper< bool_representation >::type(); + } + + template< typename T > + std::string + ToDatatype::operator()() + { + return ToDatatypeHelper< T >::type(); + } + + template< int n > + std::string + ToDatatype::operator()() + { + return ""; + } + + Datatype + fromADIOS2Type( std::string const & dt ) + { + static std::map< std::string, Datatype > map{ + { "string", Datatype::STRING }, + { "char", Datatype::CHAR }, + { "signed char", Datatype::CHAR }, + { "unsigned char", Datatype::UCHAR }, + { "short", Datatype::SHORT }, + { "unsigned short", Datatype::USHORT }, + { "int", Datatype::INT }, + { "unsigned int", Datatype::UINT }, + { "long int", Datatype::LONG }, + { "unsigned long int", Datatype::ULONG }, + { "long long int", Datatype::LONGLONG }, + { "unsigned long long int", Datatype::ULONGLONG }, + { "float", Datatype::FLOAT }, + { "double", Datatype::DOUBLE }, + { "long double", Datatype::LONG_DOUBLE }, + { "uint8_t", Datatype::UCHAR }, + { "int8_t", Datatype::CHAR }, + { "uint16_t", determineDatatype< uint16_t >() }, + { "int16_t", determineDatatype< int16_t >() }, + { "uint32_t", determineDatatype< uint32_t >() }, + { "int32_t", determineDatatype< int32_t >() }, + { "uint64_t", determineDatatype< uint64_t >() }, + { "int64_t", determineDatatype< int64_t >() } + }; + auto it = map.find( dt ); + if( it != map.end() ) + { + return it->second; + } + else + { + std::cerr << "Warning: Encountered unknown ADIOS2 datatype," + " defaulting to UNDEFINED." << std::endl; + return Datatype::UNDEFINED; + } + } + + template< typename T > + typename std::vector< T >::size_type + AttributeInfoHelper< T >::getSize( + adios2::IO & IO, + std::string const & attributeName ) + { + auto attribute = IO.InquireAttribute< T >( attributeName ); + if( !attribute ) + { + throw std::runtime_error( + "Internal error: Attribute not present." ); + } + return attribute.Data().size(); + } + + template< typename T > + typename std::vector< T >::size_type + AttributeInfoHelper< std::vector< T > >::getSize( + adios2::IO & IO, + std::string const & attributeName ) + { + return AttributeInfoHelper< T >::getSize( IO, attributeName ); + } + + typename std::vector< bool_representation >::size_type + AttributeInfoHelper< bool >::getSize( + adios2::IO & IO, + std::string const & attributeName ) + { + return AttributeInfoHelper< bool_representation >::getSize( + IO, attributeName ); + } + + template< typename T > + typename std::vector< T >::size_type + AttributeInfo::operator()( + adios2::IO & IO, + std::string const & attributeName ) + { + return AttributeInfoHelper< T >::getSize( IO, attributeName ); + } + + template< int n, typename... Params > + size_t + AttributeInfo::operator()( Params &&... ) + { + return 0; + } + + Datatype + attributeInfo( adios2::IO & IO, std::string const & attributeName ) + { + std::string type = IO.AttributeType( attributeName ); + if( type.empty() ) + { + std::cerr << "Warning: Attribute with name " << attributeName + << " has no type in backend." << std::endl; + return Datatype::UNDEFINED; + } + else + { + static AttributeInfo ai; + Datatype basicType = fromADIOS2Type( type ); + auto size = + switchType< size_t >( basicType, ai, IO, attributeName ); + Datatype openPmdType = size == 1 + ? basicType + : size == 7 && basicType == Datatype::DOUBLE + ? Datatype::ARR_DBL_7 + : toVectorType( basicType ); + return openPmdType; + } + } +} // namespace detail +} // namespace openPMD +#endif diff --git a/src/IO/ADIOS/ADIOS2IOHandler.cpp b/src/IO/ADIOS/ADIOS2IOHandler.cpp index 55a727252e..5bb4e49569 100644 --- a/src/IO/ADIOS/ADIOS2IOHandler.cpp +++ b/src/IO/ADIOS/ADIOS2IOHandler.cpp @@ -20,58 +20,1259 @@ */ #include "openPMD/IO/ADIOS/ADIOS2IOHandler.hpp" -#include - +#include "openPMD/Datatype.hpp" +#include "openPMD/IO/ADIOS/ADIOS2FilePosition.hpp" +#include "openPMD/IO/ADIOS/ADIOS2IOHandler.hpp" +#include "openPMD/auxiliary/Environment.hpp" +#include "openPMD/auxiliary/Filesystem.hpp" +#include "openPMD/auxiliary/StringManip.hpp" +#include +#include +#include namespace openPMD { +#if openPMD_USE_VERIFY +#define VERIFY( CONDITION, TEXT ) \ + { \ + if ( !( CONDITION ) ) \ + throw std::runtime_error( ( TEXT ) ); \ + } +#else +#define VERIFY( CONDITION, TEXT ) \ + do \ + { \ + (void)sizeof( CONDITION ); \ + } while ( 0 ); +#endif + +#define VERIFY_ALWAYS( CONDITION, TEXT ) \ + { \ + if ( !( CONDITION ) ) \ + throw std::runtime_error( ( TEXT ) ); \ + } + #if openPMD_HAVE_ADIOS2 -ADIOS2IOHandler::ADIOS2IOHandler(std::string path, AccessType at) - : AbstractIOHandler(std::move(path), at), - m_impl{new ADIOS2IOHandlerImpl(this)} -{ } -ADIOS2IOHandler::~ADIOS2IOHandler() -{ } +#if openPMD_HAVE_MPI -std::future< void > -ADIOS2IOHandler::flush() +ADIOS2IOHandlerImpl::ADIOS2IOHandlerImpl( + AbstractIOHandler * handler, + MPI_Comm communicator ) + : AbstractIOHandlerImplCommon( handler ) + , m_comm{ communicator } + , m_ADIOS{ communicator, ADIOS2_DEBUG_MODE } { - return m_impl->flush(); } -#endif +#endif // openPMD_HAVE_MPI -#if openPMD_HAVE_ADIOS2 && !openPMD_HAVE_MPI -ADIOS2IOHandler::ADIOS2IOHandler(std::string path, AccessType at) - : AbstractIOHandler(std::move(path), at), - m_impl{new ADIOS2IOHandlerImpl(this)} -{ } +ADIOS2IOHandlerImpl::ADIOS2IOHandlerImpl( AbstractIOHandler * handler ) + : AbstractIOHandlerImplCommon( handler ), m_ADIOS{ ADIOS2_DEBUG_MODE } +{ +} -ADIOS2IOHandler::~ADIOS2IOHandler() = default; +ADIOS2IOHandlerImpl::~ADIOS2IOHandlerImpl( ) +{ + this->flush( ); +} -std::future< void > -ADIOS2IOHandler::flush() +std::future< void > ADIOS2IOHandlerImpl::flush( ) { - return m_impl->flush(); + auto res = AbstractIOHandlerImpl::flush( ); + for ( auto & p : m_fileData ) + { + if ( m_dirty.find( p.first ) != m_dirty.end( ) ) + { + p.second->flush( ); + } + else + { + p.second->drop( ); + } + } + return res; } -#else -ADIOS2IOHandler::ADIOS2IOHandler(std::string path, AccessType at) + +void ADIOS2IOHandlerImpl::createFile( + Writable * writable, + Parameter< Operation::CREATE_FILE > const & parameters ) +{ + VERIFY_ALWAYS( m_handler->accessTypeBackend != AccessType::READ_ONLY, + "Creating a file in read-only mode is not possible." ); + + if ( !writable->written ) + { + std::string name = parameters.name; + if ( !auxiliary::ends_with( name, ".bp" ) ) + { + name += ".bp"; + } + + auto res_pair = getPossiblyExisting( name ); + InvalidatableFile shared_name = InvalidatableFile( name ); + VERIFY_ALWAYS( + !( m_handler->accessTypeBackend == AccessType::READ_WRITE && + ( !std::get< PE_NewlyCreated >( res_pair ) || + auxiliary::file_exists( fullPath( + std::get< PE_InvalidatableFile >( res_pair ) ) ) ) ), + "Can only overwrite existing file in CREATE mode." ); + + if ( !std::get< PE_NewlyCreated >( res_pair ) ) + { + auto file = std::get< PE_InvalidatableFile >( res_pair ); + m_dirty.erase( file ); + dropFileData( file ); + file.invalidate( ); + } + + std::string const dir( m_handler->directory ); + if ( !auxiliary::directory_exists( dir ) ) + { + auto success = auxiliary::create_directories( dir ); + VERIFY( success, "Could not create directory." ); + } + + associateWithFile( writable, shared_name ); + this->m_dirty.emplace( shared_name ); + getFileData( shared_name ).m_mode = adios2::Mode::Write; // WORKAROUND + // ↑ ADIOS2 does not yet implement ReadWrite Mode + + writable->written = true; + writable->abstractFilePosition = + std::make_shared< ADIOS2FilePosition >( ); + } +} + +void ADIOS2IOHandlerImpl::createPath( + Writable * writable, + const Parameter< Operation::CREATE_PATH > & parameters ) +{ + std::string path; + refreshFileFromParent( writable ); + + /* Sanitize path */ + if ( !auxiliary::starts_with( parameters.path, '/' ) ) + { + path = filePositionToString( setAndGetFilePosition( writable ) ) + "/" + + auxiliary::removeSlashes( parameters.path ); + } + else + { + path = "/" + auxiliary::removeSlashes( parameters.path ); + } + + /* ADIOS has no concept for explicitly creating paths. + * They are implicitly created with the paths of variables/attributes. */ + + writable->written = true; + writable->abstractFilePosition = std::make_shared< ADIOS2FilePosition >( + path, ADIOS2FilePosition::GD::GROUP ); + + auto varName = nameOfVariable( writable ); + // m_IO.DefineVariable< void >( varName ); +} + +void ADIOS2IOHandlerImpl::createDataset( + Writable * writable, + const Parameter< Operation::CREATE_DATASET > & parameters ) +{ + if ( m_handler->accessTypeBackend == AccessType::READ_ONLY ) + { + throw std::runtime_error( "Creating a dataset in a file opened as read " + "only is not possible." ); + } + if ( !writable->written ) + { + /* Sanitize name */ + std::string name = auxiliary::removeSlashes( parameters.name ); + + auto file = refreshFileFromParent( writable ); + auto filePos = setAndGetFilePosition( writable, name ); + filePos->gd = ADIOS2FilePosition::GD::DATASET; + auto varName = filePositionToString( filePos ); + // we use a unique_ptr to circumvent the fact that std::optional + // is only available beginning with c++17 + std::unique_ptr< adios2::Operator > compression; + if ( !parameters.compression.empty( ) ) + { + compression = getCompressionOperator( parameters.compression ); + } + switchType( parameters.dtype, detail::VariableDefiner( ), + getFileData( file ).m_IO, varName, + std::move( compression ), parameters.extent ); + writable->written = true; + m_dirty.emplace( file ); + } +} + +void ADIOS2IOHandlerImpl::extendDataset( + Writable *, const Parameter< Operation::EXTEND_DATASET > & ) +{ + throw std::runtime_error( + "Dataset extension not implemented in ADIOS backend" ); +} + +void ADIOS2IOHandlerImpl::openFile( + Writable * writable, const Parameter< Operation::OPEN_FILE > & parameters ) +{ + if ( !auxiliary::directory_exists( m_handler->directory ) ) + { + throw no_such_file_error( "Supplied directory is not valid: " + + m_handler->directory ); + } + + std::string name = parameters.name; + if ( !auxiliary::ends_with( name, ".bp" ) ) + { + name += ".bp"; + } + + auto file = std::get< PE_InvalidatableFile >( getPossiblyExisting( name ) ); + + associateWithFile( writable, file ); + + writable->written = true; + writable->abstractFilePosition = std::make_shared< ADIOS2FilePosition >( ); +} + +void ADIOS2IOHandlerImpl::openPath( + Writable * writable, const Parameter< Operation::OPEN_PATH > & parameters ) +{ + /* Sanitize path */ + refreshFileFromParent( writable ); + std::string prefix = + filePositionToString( setAndGetFilePosition( writable->parent ) ); + std::string suffix = auxiliary::removeSlashes( parameters.path ); + std::string infix = auxiliary::ends_with( prefix, '/' ) ? "" : "/"; + + /* ADIOS has no concept for explicitly creating paths. + * They are implicitly created with the paths of variables/attributes. */ + + writable->abstractFilePosition = std::make_shared< ADIOS2FilePosition >( + prefix + infix + suffix, ADIOS2FilePosition::GD::GROUP ); + writable->written = true; +} + +void ADIOS2IOHandlerImpl::openDataset( + Writable * writable, Parameter< Operation::OPEN_DATASET > & parameters ) +{ + auto name = auxiliary::removeSlashes( parameters.name ); + writable->abstractFilePosition.reset( ); + auto pos = setAndGetFilePosition( writable, name ); + pos->gd = ADIOS2FilePosition::GD::DATASET; + auto file = refreshFileFromParent( writable ); + auto varName = filePositionToString( pos ); + *parameters.dtype = detail::fromADIOS2Type( + getFileData( file ).m_IO.VariableType( varName ) ); + switchType( *parameters.dtype, detail::DatasetOpener( this ), file, varName, + parameters ); + writable->written = true; +} + +void ADIOS2IOHandlerImpl::deleteFile( + Writable *, const Parameter< Operation::DELETE_FILE > & ) +{ + throw std::runtime_error( "ADIOS2 backend does not support deletion." ); +} + +void ADIOS2IOHandlerImpl::deletePath( + Writable *, const Parameter< Operation::DELETE_PATH > & ) +{ + throw std::runtime_error( "ADIOS2 backend does not support deletion." ); +} + +void ADIOS2IOHandlerImpl::deleteDataset( + Writable *, const Parameter< Operation::DELETE_DATASET > & ) +{ + throw std::runtime_error( "ADIOS2 backend does not support deletion." ); +} + +void ADIOS2IOHandlerImpl::deleteAttribute( + Writable *, const Parameter< Operation::DELETE_ATT > & ) +{ + throw std::runtime_error( "ADIOS2 backend does not support deletion." ); +} + +void ADIOS2IOHandlerImpl::writeDataset( + Writable * writable, + const Parameter< Operation::WRITE_DATASET > & parameters ) +{ + VERIFY_ALWAYS( m_handler->accessTypeBackend != AccessType::READ_ONLY, + "Cannot write data in read-only mode." ); + setAndGetFilePosition( writable ); + auto file = refreshFileFromParent( writable ); + detail::BufferedActions & ba = getFileData( file ); + detail::BufferedPut bp; + bp.name = nameOfVariable( writable ); + bp.param = parameters; + ba.enqueue( std::move( bp ) ); + m_dirty.emplace( std::move( file ) ); + writable->written = true; // TODO erst nach dem Schreiben? +} + +void ADIOS2IOHandlerImpl::writeAttribute( + Writable * writable, const Parameter< Operation::WRITE_ATT > & parameters ) +{ + switchType( parameters.dtype, detail::AttributeWriter( ), this, writable, + parameters ); +} + +void ADIOS2IOHandlerImpl::readDataset( + Writable * writable, Parameter< Operation::READ_DATASET > & parameters ) +{ + setAndGetFilePosition( writable ); + auto file = refreshFileFromParent( writable ); + detail::BufferedActions & ba = getFileData( file ); + detail::BufferedGet bg; + bg.name = nameOfVariable( writable ); + bg.param = parameters; + ba.enqueue( std::move( bg ) ); + m_dirty.emplace( std::move( file ) ); +} + +void ADIOS2IOHandlerImpl::readAttribute( + Writable * writable, Parameter< Operation::READ_ATT > & parameters ) +{ + auto file = refreshFileFromParent( writable ); + auto pos = setAndGetFilePosition( writable ); + detail::BufferedActions & ba = getFileData( file ); + detail::BufferedAttributeRead bar; + bar.name = nameOfAttribute( writable, parameters.name ); + bar.param = parameters; + ba.enqueue( std::move( bar ) ); + m_dirty.emplace( std::move( file ) ); +} + +void ADIOS2IOHandlerImpl::listPaths( + Writable * writable, Parameter< Operation::LIST_PATHS > & parameters ) +{ + VERIFY_ALWAYS( + writable->written, + "Internal error: Writable not marked written during path listing" ); + auto file = refreshFileFromParent( writable ); + auto pos = setAndGetFilePosition( writable ); + // adios2::Engine & engine = getEngine( file ); + std::string myName = filePositionToString( pos ); + if ( !auxiliary::ends_with( myName, '/' ) ) + { + myName = myName + '/'; + } + + /* + * since ADIOS does not have a concept of paths, restore them + * from variables and attributes. + * yes. + */ + + auto & IO = getFileData( file ).m_IO; + + std::unordered_set< std::string > subdirs; + /* + * When reading an attribute, we cannot distinguish + * whether its containing "folder" is a group or a + * dataset. If we stumble upon a dataset at the current + * level (which can be distinguished via variables), + * we put in in the list 'delete_me' to remove them + * again later on. + * Note that the method 'listDatasets' does not have + * this problem since datasets can be restored solely + * from variables – attributes don't even need to be + * inspected. + */ + std::vector< std::string > delete_me; + auto f = [myName, &subdirs, &delete_me]( + std::vector< std::string > & varsOrAttrs, bool variables ) { + for ( auto var : varsOrAttrs ) + { + if ( auxiliary::starts_with( var, myName ) ) + { + var = auxiliary::replace_first( var, myName, "" ); + auto firstSlash = var.find_first_of( '/' ); + if ( firstSlash != std::string::npos ) + { + var = var.substr( 0, firstSlash ); + subdirs.emplace( std::move( var ) ); + } + else if ( variables ) + { // var is a dataset at the current level + delete_me.push_back( std::move( var ) ); + } + } + } + }; + std::vector< std::string > vars; + for ( auto const & p : IO.AvailableVariables( ) ) + { + vars.emplace_back( p.first ); + } + + std::vector< std::string > attrs; + for ( auto const & p : IO.AvailableAttributes( ) ) + { + attrs.emplace_back( p.first ); + } + f( vars, true ); + f( attrs, false ); + for ( auto & d : delete_me ) + { + subdirs.erase( d ); + } + for ( auto & path : subdirs ) + { + parameters.paths->emplace_back( std::move( path ) ); + } +} + +void ADIOS2IOHandlerImpl::listDatasets( + Writable * writable, Parameter< Operation::LIST_DATASETS > & parameters ) +{ + VERIFY_ALWAYS( + writable->written, + "Internal error: Writable not marked written during path listing" ); + auto file = refreshFileFromParent( writable ); + auto pos = setAndGetFilePosition( writable ); + // adios2::Engine & engine = getEngine( file ); + std::string myName = filePositionToString( pos ); + if ( !auxiliary::ends_with( myName, '/' ) ) + { + myName = myName + '/'; + } + + /* + * since ADIOS does not have a concept of paths, restore them + * from variables and attributes. + * yes. + */ + + std::map< std::string, adios2::Params > vars = + getFileData( file ).m_IO.AvailableVariables( ); + + std::unordered_set< std::string > subdirs; + for ( auto & pair : vars ) + { + std::string var = pair.first; + if ( auxiliary::starts_with( var, myName ) ) + { + var = auxiliary::replace_first( var, myName, "" ); + auto firstSlash = var.find_first_of( '/' ); + if ( firstSlash == std::string::npos ) + { + subdirs.emplace( std::move( var ) ); + } // else: var is a path or a dataset in a group below the current + // group + } + } + for ( auto & dataset : subdirs ) + { + parameters.datasets->emplace_back( std::move( dataset ) ); + } +} + +void ADIOS2IOHandlerImpl::listAttributes( + Writable * writable, Parameter< Operation::LIST_ATTS > & parameters ) +{ + VERIFY_ALWAYS( writable->written, + "Internal error: Writable not marked " + "written during attribute writing" ); + auto file = refreshFileFromParent( writable ); + auto pos = setAndGetFilePosition( writable ); + auto attributePrefix = filePositionToString( pos ); + if ( attributePrefix == "/" ) + { + attributePrefix = ""; + } + auto & ba = getFileData( file ); + ba.getEngine( ); // make sure that the attributes are present + auto attrs = ba.m_IO.AvailableAttributes( attributePrefix ); + for ( auto & pair : attrs ) + { + auto attr = auxiliary::removeSlashes( pair.first ); + if ( attr.find_last_of( '/' ) == std::string::npos ) + { + parameters.attributes->push_back( std::move( attr ) ); + } + } +} + + + +adios2::Mode ADIOS2IOHandlerImpl::adios2Accesstype( ) +{ + switch ( m_handler->accessTypeBackend ) + { + case AccessType::CREATE: + return adios2::Mode::Write; + case AccessType::READ_ONLY: + return adios2::Mode::Read; + case AccessType::READ_WRITE: + std::cerr << "ADIOS2 does currently not yet implement ReadWrite " + "(Append) mode." + << "Replacing with Read mode." << std::endl; + return adios2::Mode::Read; + default: + return adios2::Mode::Undefined; + } +} + +std::string ADIOS2IOHandlerImpl::filePositionToString( + std::shared_ptr< ADIOS2FilePosition > filepos ) +{ + return filepos->location; +} + +std::shared_ptr< ADIOS2FilePosition > ADIOS2IOHandlerImpl::extendFilePosition( + std::shared_ptr< ADIOS2FilePosition > const & oldPos, std::string s ) +{ + auto path = filePositionToString( oldPos ); + if ( !auxiliary::ends_with( path, '/' ) && + !auxiliary::starts_with( s, '/' ) ) + { + path = path + "/"; + } + else if ( auxiliary::ends_with( path, '/' ) && + auxiliary::starts_with( s, '/' ) ) + { + path = auxiliary::replace_last( path, "/", "" ); + } + return std::make_shared< ADIOS2FilePosition >( path + std::move( s ), + oldPos->gd ); +} + +std::unique_ptr< adios2::Operator > +ADIOS2IOHandlerImpl::getCompressionOperator( std::string const & compression ) +{ + adios2::Operator res; + auto it = m_operators.find( compression ); + if ( it == m_operators.end( ) ) + { + try { + res = m_ADIOS.DefineOperator( compression, compression ); + } + catch ( std::invalid_argument const & ) + { + std::cerr << "Warning: ADIOS2 backend does not support compression " + "method " << compression << ". Continuing without compression." + << std::endl; + return std::unique_ptr< adios2::Operator >( ); + } + m_operators.emplace( compression, res ); + + } + else + { + res = it->second; + } + return std::unique_ptr< adios2::Operator >( + new adios2::Operator( res ) ); +} + +std::string ADIOS2IOHandlerImpl::nameOfVariable( Writable * writable ) +{ + return filePositionToString( setAndGetFilePosition( writable ) ); +} + +std::string ADIOS2IOHandlerImpl::nameOfAttribute( Writable * writable, + std::string attribute ) +{ + auto pos = setAndGetFilePosition( writable ); + return filePositionToString( + extendFilePosition( pos, auxiliary::removeSlashes( attribute ) ) ); +} + +ADIOS2FilePosition::GD +ADIOS2IOHandlerImpl::groupOrDataset( Writable * writable ) +{ + return setAndGetFilePosition( writable )->gd; +} + +detail::BufferedActions & +ADIOS2IOHandlerImpl::getFileData( InvalidatableFile file ) +{ + VERIFY_ALWAYS( file.valid( ), + "Cannot retrieve file data for a file that has " + "been overwritten or deleted." ) + auto it = m_fileData.find( file ); + if ( it == m_fileData.end( ) ) + { + return *m_fileData + .emplace( std::move( file ), + std::unique_ptr< detail::BufferedActions >{ + new detail::BufferedActions{*this, file}} ) + .first->second; + } + else + { + return *it->second; + } +} + +void ADIOS2IOHandlerImpl::dropFileData( InvalidatableFile file ) +{ + auto it = m_fileData.find( file ); + if ( it != m_fileData.end( ) ) + { + it->second->drop( ); + m_fileData.erase( it ); + } +} + +template < typename T > +adios2::Variable< T > +ADIOS2IOHandlerImpl::verifyDataset( Offset const & offset, + Extent const & extent, adios2::IO & IO, + std::string const & varName ) +{ + { + auto requiredType = adios2::GetType< T >( ); + auto actualType = IO.VariableType( varName ); + VERIFY_ALWAYS( requiredType == actualType, + "Trying to access a dataset with wrong type (trying to " + "access dataset with type " + + requiredType + ", but has type " + actualType + ")" ) + } + adios2::Variable< T > var = IO.InquireVariable< T >( varName ); + VERIFY_ALWAYS( var.operator bool( ), + "Internal error: Failed opening ADIOS2 variable." ) + // TODO leave this check to ADIOS? + adios2::Dims shape = var.Shape( ); + auto actualDim = shape.size( ); + { + auto requiredDim = extent.size( ); + VERIFY_ALWAYS( requiredDim == actualDim, + "Trying to access a dataset with wrong dimensionality " + "(trying to access dataset with dimensionality " + + std::to_string( requiredDim ) + + ", but has dimensionality " + + std::to_string( actualDim ) + ")" ) + } + for ( unsigned int i = 0; i < actualDim; i++ ) + { + VERIFY_ALWAYS( offset[i] + extent[i] <= shape[i], + "Dataset access out of bounds." ) + } + var.SetSelection( {offset, extent} ); + return var; +} + +std::unique_ptr< std::pair< std::string, int > > +ADIOS2IOHandlerImpl::workaroundDatatypeOfAttribute( adios2::IO & IO, + std::string variable, + std::string attribute ) +{ + std::map< std::string, adios2::Params > attrs = + IO.AvailableAttributes( variable ); + auto it = attrs.find( attribute ); + if ( it == attrs.end( ) ) + { + return std::unique_ptr< std::pair< std::string, int > >( ); + } + else + { + return std::unique_ptr< std::pair< std::string, int > >( + new std::pair< std::string, int >( + it->second["Type"], std::stoi( it->second["Elements"] ) ) ); + } +} + +namespace detail +{ + DatasetReader::DatasetReader( openPMD::ADIOS2IOHandlerImpl * impl ) + : m_impl{impl} + { + } + + template < typename T > + void DatasetReader::operator( )( detail::BufferedGet & bp, adios2::IO & IO, + adios2::Engine & engine, + std::string const & fileName ) + { + DatasetHelper< T >{m_impl}.readDataset( bp, IO, engine, fileName ); + } + + template < int n, typename... Params > + void DatasetReader::operator( )( Params &&... ) + { + throw std::runtime_error( + "Internal error: Unknown datatype trying to read a dataset." ); + } + + template < typename T > + Datatype AttributeReader:: + operator( )( adios2::IO & IO, std::string name, + std::shared_ptr< Attribute::resource > resource ) + { + /* + * If we store an attribute of boolean type, we store an additional + * attribute prefixed with '__is_boolean__' to indicate this information + * that would otherwise be lost. Check whether this has been done. + */ + using rep = AttributeTypes::rep; +#if __cplusplus > 201402L + constexpr +#endif + if( std::is_same< T, rep >::value ) + { + std::string metaAttr = "__is_boolean__" + name; + auto type = attributeInfo( IO, "__is_boolean__" + name ); + if ( type == determineDatatype() ) + { + auto attr = IO.InquireAttribute< rep >( metaAttr ); + if (attr.Data().size() == 1 && attr.Data()[0] == 1) + { + AttributeTypes< bool >::readAttribute( IO, name, resource ); + return determineDatatype< bool >(); + } + } + } + AttributeTypes< T >::readAttribute( IO, name, resource ); + return determineDatatype< T >(); + } + + template < int n, typename... Params > + Datatype AttributeReader::operator( )( Params &&... ) + { + throw std::runtime_error( "Internal error: Unknown datatype while " + "trying to read an attribute." ); + } + + template < typename T > + void AttributeWriter:: + operator( )( ADIOS2IOHandlerImpl * impl, Writable * writable, + const Parameter< Operation::WRITE_ATT > & parameters ) + { + + VERIFY_ALWAYS( impl->m_handler->accessTypeBackend != + AccessType::READ_ONLY, + "Cannot write attribute in read-only mode." ); + auto pos = impl->setAndGetFilePosition( writable ); + auto file = impl->refreshFileFromParent( writable ); + auto fullName = impl->nameOfAttribute( writable, parameters.name ); + auto prefix = impl->filePositionToString( pos ); + + adios2::IO IO = impl->getFileData( file ).m_IO; + impl->m_dirty.emplace( std::move( file ) ); + + std::string t = IO.AttributeType( fullName ); + if ( !t.empty( ) ) // an attribute is present <=> it has a type + { + IO.RemoveAttribute( fullName ); + } + typename AttributeTypes< T >::Attr attr = + AttributeTypes< T >::createAttribute( + IO, fullName, variantSrc::get< T >( parameters.resource ) ); + VERIFY_ALWAYS( attr, "Failed creating attribute." ) + } + + template < int n, typename... Params > + void AttributeWriter::operator( )( Params &&... ) + { + throw std::runtime_error( "Internal error: Unknown datatype while " + "trying to write an attribute." ); + } + + DatasetOpener::DatasetOpener( ADIOS2IOHandlerImpl * impl ) : m_impl{impl} + { + } + + template < typename T > + void DatasetOpener:: + operator( )( InvalidatableFile file, const std::string & varName, + Parameter< Operation::OPEN_DATASET > & parameters ) + { + DatasetHelper< T >{m_impl}.openDataset( file, varName, parameters ); + } + + template < int n, typename... Params > + void DatasetOpener::operator( )( Params &&... ) + { + throw std::runtime_error( + "Unknown datatype while trying to open dataset." ); + } + + WriteDataset::WriteDataset( ADIOS2IOHandlerImpl * handlerImpl ) + : m_handlerImpl{handlerImpl} + { + } + + template < typename T > + void WriteDataset::operator( )( detail::BufferedPut & bp, adios2::IO & IO, + adios2::Engine & engine ) + { + DatasetHelper< T > dh{m_handlerImpl}; + dh.writeDataset( bp, IO, engine ); + } + + template < int n, typename... Params > + void WriteDataset::operator( )( Params &&... ) + { + throw std::runtime_error( "WRITE_DATASET: Invalid datatype." ); + } + + template < typename T > + void VariableDefiner:: + operator( )( adios2::IO & IO, const std::string & name, + std::unique_ptr< adios2::Operator > compression, + const adios2::Dims & shape, const adios2::Dims & start, + const adios2::Dims & count, const bool constantDims ) + { + DatasetHelper< T >::defineVariable( IO, name, std::move( compression ), + shape, start, count, constantDims ); + } + + template < int n, typename... Params > + void VariableDefiner::operator( )( adios2::IO &, Params &&... ) + { + throw std::runtime_error( "Defining a variable with undefined type." ); + } + + + + template < typename T > + typename AttributeTypes< T >::Attr + AttributeTypes< T >::createAttribute( adios2::IO & IO, std::string name, + const T value ) + { + auto attr = IO.DefineAttribute( name, value ); + if ( !attr ) + { + throw std::runtime_error( + "Internal error: Failed defining attribute '" + name + "'." ); + } + return attr; + } + + template < typename T > + void AttributeTypes< T >::readAttribute( + adios2::IO & IO, std::string name, + std::shared_ptr< Attribute::resource > resource ) + { + auto attr = IO.InquireAttribute< BasicType >( name ); + if ( !attr ) + { + throw std::runtime_error( + "Internal error: Failed reading attribute '" + name + "'." ); + } + *resource = attr.Data( )[0]; + } + + template < typename T > + typename AttributeTypes< std::vector< T > >::Attr + AttributeTypes< std::vector< T > >::createAttribute( + adios2::IO & IO, std::string name, const std::vector< T > & value ) + { + auto attr = IO.DefineAttribute( name, value.data( ), value.size( ) ); + if ( !attr ) + { + throw std::runtime_error( + "Internal error: Failed defining attribute '" + name + "'." ); + } + return attr; + } + + template < typename T > + void AttributeTypes< std::vector< T > >::readAttribute( + adios2::IO & IO, std::string name, + std::shared_ptr< Attribute::resource > resource ) + { + auto attr = IO.InquireAttribute< BasicType >( name ); + if ( !attr ) + { + throw std::runtime_error( + "Internal error: Failed reading attribute '" + name + "'." ); + } + *resource = attr.Data( ); + } + + template < typename T, size_t n > + typename AttributeTypes< std::array< T, n > >::Attr + AttributeTypes< std::array< T, n > >::createAttribute( + adios2::IO & IO, std::string name, const std::array< T, n > & value ) + { + auto attr = IO.DefineAttribute( name, value.data( ), n ); + if ( !attr ) + { + throw std::runtime_error( + "Internal error: Failed defining attribute '" + name + "'." ); + } + return attr; + } + + template < typename T, size_t n > + void AttributeTypes< std::array< T, n > >::readAttribute( + adios2::IO & IO, std::string name, + std::shared_ptr< Attribute::resource > resource ) + { + auto attr = IO.InquireAttribute< BasicType >( name ); + if ( !attr ) + { + throw std::runtime_error( + "Internal error: Failed reading attribute '" + name + "'." ); + } + auto data = attr.Data( ); + std::array< T, n > res; + for ( size_t i = 0; i < n; i++ ) + { + res[i] = data[i]; + } + *resource = res; + } + + typename AttributeTypes< bool >::Attr + AttributeTypes< bool >::createAttribute( adios2::IO & IO, std::string name, + const bool value ) + { + IO.DefineAttribute< bool_representation >( "__is_boolean__" + name, 1 ); + return AttributeTypes< bool_representation >::createAttribute( + IO, name, toRep( value ) ); + } + + void AttributeTypes< bool >::readAttribute( + adios2::IO & IO, std::string name, + std::shared_ptr< Attribute::resource > resource ) + { + auto attr = IO.InquireAttribute< BasicType >( name ); + if ( !attr ) + { + throw std::runtime_error( + "Internal error: Failed reading attribute '" + name + "'." ); + } + *resource = fromRep( attr.Data( )[0] ); + } + + template < typename T > + DatasetHelper< + T, typename std::enable_if< DatasetTypes< T >::validType >::type >:: + DatasetHelper( openPMD::ADIOS2IOHandlerImpl * impl ) + : m_impl{impl} + { + } + + template < typename T > + void DatasetHelper< + T, typename std::enable_if< DatasetTypes< T >::validType >::type >:: + openDataset( InvalidatableFile file, const std::string & varName, + Parameter< Operation::OPEN_DATASET > & parameters ) + { + auto & IO = m_impl->getFileData( file ).m_IO; + adios2::Variable< T > var = IO.InquireVariable< T >( varName ); + if ( !var ) + { + throw std::runtime_error( + "Failed retrieving ADIOS2 Variable with name '" + varName + + "' from file " + *file + "." ); + } + *parameters.extent = var.Shape( ); + } + + template < typename T > + void DatasetHelper< + T, typename std::enable_if< DatasetTypes< T >::validType >::type >:: + readDataset( detail::BufferedGet & bp, adios2::IO & IO, + adios2::Engine & engine, std::string const & fileName ) + { + adios2::Variable< T > var = m_impl->verifyDataset< T >( + bp.param.offset, bp.param.extent, IO, bp.name ); + if ( !var ) + { + throw std::runtime_error( + "Failed retrieving ADIOS2 Variable with name '" + bp.name + + "' from file " + fileName + "." ); + } + auto ptr = std::static_pointer_cast< T >( bp.param.data ).get( ); + engine.Get( var, ptr ); + } + + template < typename T > + void DatasetHelper< + T, typename std::enable_if< DatasetTypes< T >::validType >::type >:: + defineVariable( adios2::IO & IO, const std::string & name, + std::unique_ptr< adios2::Operator > compression, + const adios2::Dims & shape, const adios2::Dims & start, + const adios2::Dims & count, const bool constantDims ) + { + adios2::Variable< T > var = + IO.DefineVariable< T >( name, shape, start, count, constantDims ); + if ( !var ) + { + throw std::runtime_error( + "Internal error: Could not create Variable '" + name + "'." ); + } + // check whether the unique_ptr has an element + // and whether the held operator is valid + if ( compression && *compression ) + { + var.AddOperation( *compression ); + } + } + + template < typename T > + void DatasetHelper< + T, typename std::enable_if< DatasetTypes< T >::validType >::type >:: + writeDataset( detail::BufferedPut & bp, adios2::IO & IO, + adios2::Engine & engine ) + { + VERIFY_ALWAYS( m_impl->m_handler->accessTypeBackend != + AccessType::READ_ONLY, + "Cannot write data in read-only mode." ); + + auto ptr = std::static_pointer_cast< const T >( bp.param.data ).get( ); + + adios2::Variable< T > var = m_impl->verifyDataset< T >( + bp.param.offset, bp.param.extent, IO, bp.name ); + + engine.Put( var, ptr ); + } + + template < typename T > + DatasetHelper< + T, typename std::enable_if< !DatasetTypes< T >::validType >::type >:: + DatasetHelper( openPMD::ADIOS2IOHandlerImpl * ) + { + } + + template < typename T > + void DatasetHelper< T, + typename std::enable_if< + !DatasetTypes< T >::validType >::type >::throwErr( ) + { + throw std::runtime_error( + "Trying to access dataset with unallowed datatype: " + + datatypeToString( determineDatatype< T >( ) ) ); + } + + template < typename T > + template < typename... Params > + void DatasetHelper< + T, typename std::enable_if< !DatasetTypes< T >::validType >::type >:: + openDataset( Params &&... ) + { + throwErr( ); + } + + template < typename T > + template < typename... Params > + void DatasetHelper< + T, typename std::enable_if< !DatasetTypes< T >::validType >::type >:: + readDataset( Params &&... ) + { + throwErr( ); + } + + template < typename T > + template < typename... Params > + void DatasetHelper< + T, typename std::enable_if< !DatasetTypes< T >::validType >::type >:: + defineVariable( Params &&... ) + { + throwErr( ); + } + + template < typename T > + template < typename... Params > + void DatasetHelper< + T, typename std::enable_if< !DatasetTypes< T >::validType >::type >:: + writeDataset( Params &&... ) + { + throwErr( ); + } + + void BufferedGet::run( BufferedActions & ba ) + { + switchType( param.dtype, ba.m_readDataset, *this, ba.m_IO, + ba.getEngine( ), ba.m_file ); + } + + void BufferedPut::run( BufferedActions & ba ) + { + switchType( param.dtype, ba.m_writeDataset, *this, ba.m_IO, + ba.getEngine( ) ); + } + + void BufferedAttributeRead::run( BufferedActions & ba ) + { + auto type = attributeInfo( ba.m_IO, name ); + + if ( type == Datatype::UNDEFINED ) + { + throw std::runtime_error( "Requested attribute (" + name + + ") not found in backend." ); + } + + Datatype ret = + switchType< Datatype >( + type, + detail::AttributeReader{}, + ba.m_IO, + name, + param.resource ); + *param.dtype = ret; + } + + BufferedActions::BufferedActions( ADIOS2IOHandlerImpl & impl, + InvalidatableFile file ) + : m_file( impl.fullPath( std::move( file ) ) ), + m_IO( impl.m_ADIOS.DeclareIO( std::to_string( impl.nameCounter++ ) ) ), + m_mode( impl.adios2Accesstype( ) ), m_writeDataset( &impl ), + m_readDataset( &impl ), m_attributeReader( ), m_impl( impl ) + { + if ( !m_IO ) + { + throw std::runtime_error( + "Internal error: Failed declaring ADIOS2 IO object for file " + + m_file ); + } + else + { + // read parameters from environment + if ( 1 == + auxiliary::getEnvNum( + "OPENPMD_ADIOS2_HAVE_METADATA_FILE", 1 ) ) + { + m_IO.SetParameter( "CollectiveMetadata", "On" ); + } + else + { + m_IO.SetParameter( "CollectiveMetadata", "Off" ); + } + if ( 1 == + auxiliary::getEnvNum( "OPENPMD_ADIOS2_HAVE_PROFILING", 1 ) ) + { + m_IO.SetParameter( "Profile", "On" ); + } + else + { + m_IO.SetParameter( "Profile", "Off" ); + } #if openPMD_HAVE_MPI - : AbstractIOHandler(std::move(path), at, MPI_COMM_NULL) -#else -: AbstractIOHandler(std::move(path), at) + { + auto num_substreams = + auxiliary::getEnvNum( "OPENPMD_ADIOS2_NUM_SUBSTREAMS", 0 ); + if ( 0 != num_substreams ) + { + m_IO.SetParameter( "SubStreams", + std::to_string( num_substreams ) ); + } + } #endif + } + } + + BufferedActions::~BufferedActions( ) + { + if ( m_engine ) + { + m_engine->Close( ); + } + } + + adios2::Engine & BufferedActions::getEngine( ) + { + if ( !m_engine ) + { + m_engine = std::unique_ptr< adios2::Engine >( + new adios2::Engine( m_IO.Open( m_file, m_mode ) ) ); + if ( !m_engine ) + { + throw std::runtime_error( "Failed opening ADIOS2 Engine." ); + } + } + return *m_engine; + } + + template < typename BA > void BufferedActions::enqueue( BA && ba ) + { + using _BA = typename std::remove_reference< BA >::type; + m_buffer.emplace_back( std::unique_ptr< BufferedAction >( + new _BA( std::forward< BA >( ba ) ) ) ); + } + + void BufferedActions::flush( ) + { + auto & eng = getEngine( ); + { + for ( auto const & ba : m_buffer ) + { + ba->run( *this ); + } + // Flush() does not necessarily perform + // deferred actions.... + switch ( m_mode ) + { + case adios2::Mode::Write: + eng.PerformPuts( ); + break; + case adios2::Mode::Read: + eng.PerformGets( ); + break; + case adios2::Mode::Append: + // TODO order? + eng.PerformGets( ); + eng.PerformPuts( ); + break; + default: + break; + } + } + m_buffer.clear( ); + } + + void BufferedActions::drop( ) + { + m_buffer.clear( ); + } + +} // namespace detail + +#if openPMD_HAVE_MPI + +ADIOS2IOHandler::ADIOS2IOHandler( std::string path, openPMD::AccessType at, + MPI_Comm comm ) +: AbstractIOHandler( std::move( path ), at, comm ), m_impl{this, comm + + } +{ +} + +#endif + +ADIOS2IOHandler::ADIOS2IOHandler( std::string path, AccessType at ) +: AbstractIOHandler( std::move( path ), at ), m_impl{this} +{ +} + + +ADIOS2IOHandler::~ADIOS2IOHandler( ) +{ + this->flush( ); +} + +std::future< void > ADIOS2IOHandler::flush( ) { - throw std::runtime_error("openPMD-api built without parallel ADIOS2 support"); + return m_impl.flush( ); } -ADIOS2IOHandler::~ADIOS2IOHandler() = default; +#else // openPMD_HAVE_ADIOS2 -std::future< void > -ADIOS2IOHandler::flush() +#if openPMD_HAVE_MPI +ADIOS2IOHandler::ADIOS2IOHandler( std::string path, AccessType at, + MPI_Comm comm ) +: AbstractIOHandler( std::move( path ), at, comm ) { - return std::future< void >(); } + #endif -} // openPMD + +ADIOS2IOHandler::ADIOS2IOHandler( std::string path, AccessType at ) +: AbstractIOHandler( std::move( path ), at ) +{ +} + +std::future< void > ADIOS2IOHandler::flush( ) +{ + return std::future< void >( ); +} + +#endif + +} // namespace openPMD diff --git a/src/IO/AbstractIOHandlerHelper.cpp b/src/IO/AbstractIOHandlerHelper.cpp index 856f4771e4..c1e31fad6b 100644 --- a/src/IO/AbstractIOHandlerHelper.cpp +++ b/src/IO/AbstractIOHandlerHelper.cpp @@ -17,7 +17,8 @@ * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with openPMD-api. * If not, see . - */ + */ +#include #include "openPMD/IO/AbstractIOHandlerHelper.hpp" #include "openPMD/IO/DummyIOHandler.hpp" #include "openPMD/IO/ADIOS/ADIOS1IOHandler.hpp" @@ -50,7 +51,7 @@ namespace openPMD return std::make_shared< DummyIOHandler >(path, accessTypeBackend); # endif case Format::ADIOS2: - throw std::runtime_error("ADIOS2 backend not yet implemented"); + return std::make_shared(path, accessTypeBackend, comm); default: return std::make_shared< DummyIOHandler >(path, accessTypeBackend); } @@ -74,8 +75,10 @@ namespace openPMD throw std::runtime_error("openPMD-api built without ADIOS1 support"); return std::make_shared< DummyIOHandler >(path, accessType); # endif +#if openPMD_HAVE_ADIOS2 case Format::ADIOS2: - throw std::runtime_error("ADIOS2 backend not yet implemented"); + return std::make_shared(path, accessType); +#endif case Format::JSON: return std::make_shared< JSONIOHandler >(path, accessType); default: diff --git a/src/IO/InvalidatableFile.cpp b/src/IO/InvalidatableFile.cpp new file mode 100644 index 0000000000..ae5aa074a9 --- /dev/null +++ b/src/IO/InvalidatableFile.cpp @@ -0,0 +1,70 @@ + +#include "openPMD/IO/InvalidatableFile.hpp" + + +openPMD::InvalidatableFile::InvalidatableFile( std::string s ) : + fileState { std::make_shared< FileState >( s ) } +{} + + +void openPMD::InvalidatableFile::invalidate( ) +{ + fileState->valid = false; +} + + +bool openPMD::InvalidatableFile::valid( ) const +{ + return fileState->valid; +} + + +openPMD::InvalidatableFile & +openPMD::InvalidatableFile::operator=( std::string s ) +{ + if( fileState ) + { + fileState->name = s; + } + else + { + fileState = std::make_shared< FileState >( s ); + } + return *this; +} + + +bool +openPMD::InvalidatableFile::operator==( const openPMD::InvalidatableFile & f ) const +{ + return this->fileState == f.fileState; +} + + +std::string & openPMD::InvalidatableFile::operator*( ) const +{ + return fileState->name; +} + + +std::string * openPMD::InvalidatableFile::operator->( ) const +{ + return &fileState->name; +} + + +openPMD::InvalidatableFile::operator bool( ) const +{ + return fileState.operator bool( ); +} + + +openPMD::InvalidatableFile::FileState::FileState( std::string s ) : + name { std::move( s ) } +{} + +std::hash< openPMD::InvalidatableFile >::result_type +std::hash< openPMD::InvalidatableFile >::operator()( const openPMD::InvalidatableFile & s ) const noexcept +{ + return std::hash< shared_ptr< openPMD::InvalidatableFile::FileState>> {}( s.fileState ); +} diff --git a/src/Series.cpp b/src/Series.cpp index eb91ed5a50..de6d8d0c2d 100644 --- a/src/Series.cpp +++ b/src/Series.cpp @@ -865,10 +865,13 @@ determineFormat(std::string const& filename) if( auxiliary::ends_with(filename, ".h5") ) return Format::HDF5; if( auxiliary::ends_with(filename, ".bp") ) +#if openPMD_HAVE_ADIOS2 + return Format::ADIOS2; +#else return Format::ADIOS1; +#endif if( auxiliary::ends_with(filename, ".json") ) return Format::JSON; - if( std::string::npos != filename.find('.') /* extension is provided */ ) throw std::runtime_error("Unknown file format. Did you append a valid filename extension?"); diff --git a/test/SerialIOTest.cpp b/test/SerialIOTest.cpp index ee29e5ab74..1b64b8803f 100644 --- a/test/SerialIOTest.cpp +++ b/test/SerialIOTest.cpp @@ -23,14 +23,14 @@ std::vector> getBackends() { // first component: backend file ending // second component: whether to test 128 bit values std::vector> res; -#if openPMD_HAVE_ADIOS1 +#if openPMD_HAVE_JSON + res.emplace_back("json", false); +#endif +#if openPMD_HAVE_ADIOS1 || openPMD_HAVE_ADIOS2 res.emplace_back("bp", true); #endif #if openPMD_HAVE_HDF5 res.emplace_back("h5", true); -#endif -#if openPMD_HAVE_JSON - res.emplace_back("json", false); #endif return res; } From a16b78c591a9baefe00c59119cbff6793190c674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20P=C3=B6schel?= Date: Wed, 24 Jul 2019 16:24:44 +0200 Subject: [PATCH 2/7] Remove workaroundAttributeType --- include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp | 11 ----------- src/IO/ADIOS/ADIOS2IOHandler.cpp | 20 -------------------- 2 files changed, 31 deletions(-) diff --git a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp index fa750f28f6..f9962d71ed 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp @@ -258,17 +258,6 @@ class ADIOS2IOHandlerImpl adios2::Variable< T > verifyDataset( Offset const & offset, Extent const & extent, adios2::IO & IO, std::string const & var ); - - - /* - * At the time of writing this, the ADIOS2 API supports creating - * attributes in a variable's scope, but only retrieving the type - * of an attribute defined in global (i.e. the IO's) scope - */ - std::unique_ptr< std::pair< std::string, int > > - workaroundDatatypeOfAttribute( adios2::IO & IO, std::string variable, - std::string attribute ); - }; // ADIOS2IOHandlerImpl namespace detail diff --git a/src/IO/ADIOS/ADIOS2IOHandler.cpp b/src/IO/ADIOS/ADIOS2IOHandler.cpp index 5bb4e49569..fdd6ed5d03 100644 --- a/src/IO/ADIOS/ADIOS2IOHandler.cpp +++ b/src/IO/ADIOS/ADIOS2IOHandler.cpp @@ -655,26 +655,6 @@ ADIOS2IOHandlerImpl::verifyDataset( Offset const & offset, return var; } -std::unique_ptr< std::pair< std::string, int > > -ADIOS2IOHandlerImpl::workaroundDatatypeOfAttribute( adios2::IO & IO, - std::string variable, - std::string attribute ) -{ - std::map< std::string, adios2::Params > attrs = - IO.AvailableAttributes( variable ); - auto it = attrs.find( attribute ); - if ( it == attrs.end( ) ) - { - return std::unique_ptr< std::pair< std::string, int > >( ); - } - else - { - return std::unique_ptr< std::pair< std::string, int > >( - new std::pair< std::string, int >( - it->second["Type"], std::stoi( it->second["Elements"] ) ) ); - } -} - namespace detail { DatasetReader::DatasetReader( openPMD::ADIOS2IOHandlerImpl * impl ) From 909fa5efcd6a692535aa1cee4f905b6d439b7b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20P=C3=B6schel?= Date: Thu, 5 Sep 2019 11:54:42 +0200 Subject: [PATCH 3/7] Ensure that ADIOS2 writes a file even if no dataset is written --- src/IO/ADIOS/ADIOS2IOHandler.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/IO/ADIOS/ADIOS2IOHandler.cpp b/src/IO/ADIOS/ADIOS2IOHandler.cpp index fdd6ed5d03..689178c11b 100644 --- a/src/IO/ADIOS/ADIOS2IOHandler.cpp +++ b/src/IO/ADIOS/ADIOS2IOHandler.cpp @@ -1140,6 +1140,11 @@ namespace detail BufferedActions::~BufferedActions( ) { + // if write accessing, ensure that the engine is opened + if ( !m_engine && m_mode != adios2::Mode::Read ) + { + getEngine( ); + } if ( m_engine ) { m_engine->Close( ); From 7def78717237cea23222cc8bc508716bce7d2056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20P=C3=B6schel?= Date: Fri, 4 Oct 2019 15:56:56 +0200 Subject: [PATCH 4/7] Extend tests for ADIOS 2.5 BP4 engine does not write files, but directories which becomes relevant in tests for filebased layout. --- test/SerialIOTest.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/SerialIOTest.cpp b/test/SerialIOTest.cpp index 1b64b8803f..971816bbb0 100644 --- a/test/SerialIOTest.cpp +++ b/test/SerialIOTest.cpp @@ -718,9 +718,12 @@ void fileBased_write_test(const std::string & backend) o.setOpenPMDextension(1); o.iterations[3].setTime(static_cast< double >(3)); } - REQUIRE(auxiliary::file_exists("../samples/subdir/serial_fileBased_write00000001." + backend)); - REQUIRE(auxiliary::file_exists("../samples/subdir/serial_fileBased_write00000002." + backend)); - REQUIRE(auxiliary::file_exists("../samples/subdir/serial_fileBased_write00000003." + backend)); + REQUIRE((auxiliary::file_exists("../samples/subdir/serial_fileBased_write00000001." + backend) + || auxiliary::directory_exists("../samples/subdir/serial_fileBased_write00000001." + backend))); + REQUIRE((auxiliary::file_exists("../samples/subdir/serial_fileBased_write00000002." + backend) + || auxiliary::directory_exists("../samples/subdir/serial_fileBased_write00000002." + backend))); + REQUIRE((auxiliary::file_exists("../samples/subdir/serial_fileBased_write00000003." + backend) + || auxiliary::directory_exists("../samples/subdir/serial_fileBased_write00000003." + backend))); { Series o = Series("../samples/subdir/serial_fileBased_write%T." + backend, AccessType::READ_ONLY); @@ -789,7 +792,8 @@ void fileBased_write_test(const std::string & backend) o.iterations[4]; REQUIRE(o.iterations.size() == 4); } - REQUIRE(auxiliary::file_exists("../samples/subdir/serial_fileBased_write00000004." + backend)); + REQUIRE((auxiliary::file_exists("../samples/subdir/serial_fileBased_write00000004." + backend) + || auxiliary::directory_exists("../samples/subdir/serial_fileBased_write00000004." + backend))); // additional iteration with different iteration padding but similar content { @@ -806,7 +810,8 @@ void fileBased_write_test(const std::string & backend) REQUIRE(o.iterations.size() == 1); } - REQUIRE(auxiliary::file_exists("../samples/subdir/serial_fileBased_write10." + backend)); + REQUIRE((auxiliary::file_exists("../samples/subdir/serial_fileBased_write10." + backend) + || auxiliary::directory_exists("../samples/subdir/serial_fileBased_write10." + backend))); // read back with auto-detection and non-fixed padding { From c40108c7efe8c4b5a0d34d518b5b901d782a7e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20P=C3=B6schel?= Date: Fri, 4 Oct 2019 15:58:47 +0200 Subject: [PATCH 5/7] Implement reviewers notes Without formatting and licensing headers. --- include/openPMD/Datatype.hpp | 9 +++++++-- include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp | 1 + include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp | 4 ---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/openPMD/Datatype.hpp b/include/openPMD/Datatype.hpp index 44f84d0555..e09d04056c 100644 --- a/include/openPMD/Datatype.hpp +++ b/include/openPMD/Datatype.hpp @@ -65,6 +65,13 @@ enum class Datatype : int UNDEFINED }; // Datatype +/** + * @brief All openPMD datatypes defined in Datatype, + * listed in order in a vector. + * + */ +extern std::vector< Datatype > openPMD_Datatypes; + /** @brief Fundamental equivalence check for two given types T and U. * * This checks whether the fundamental datatype (i.e. that of a single value @@ -697,8 +704,6 @@ std::string datatypeToString( Datatype dt ); Datatype stringToDatatype( std::string s ); -extern std::vector< Datatype > openPMD_Datatypes; - std::string datatypeToString( Datatype dt ); Datatype stringToDatatype( std::string s ); diff --git a/include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp b/include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp index 9283bbdffe..94446f5142 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp @@ -23,6 +23,7 @@ #include "openPMD/IO/AbstractFilePosition.hpp" #include +#include namespace openPMD diff --git a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp index f9962d71ed..04931c3f20 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp @@ -90,10 +90,6 @@ class ADIOS2IOHandlerImpl public: - static_assert( - sizeof( bool ) == 1, - "ADIOS2 backend needs a platform with boolean size equals one byte." ); - #if openPMD_HAVE_MPI From 5ba1cec23d7792fb3d4e71994fd6da2503ddefe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20P=C3=B6schel?= Date: Fri, 4 Oct 2019 16:27:24 +0200 Subject: [PATCH 6/7] Formatting, licensing headers and some comments --- include/openPMD/Datatype.hpp | 2 +- include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp | 23 ++++++++++++++++++- include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp | 17 ++++++++------ .../IO/AbstractIOHandlerImplCommon.hpp | 6 ++--- include/openPMD/IO/InvalidatableFile.hpp | 2 +- include/openPMD/auxiliary/Environment.hpp | 22 ++++++++++++++++++ include/openPMD/auxiliary/StringManip.hpp | 6 +++++ src/IO/ADIOS/ADIOS2Auxiliary.cpp | 21 +++++++++++++++++ src/IO/ADIOS/ADIOS2IOHandler.cpp | 16 ++++++------- src/IO/InvalidatableFile.cpp | 20 ++++++++++++++++ 10 files changed, 114 insertions(+), 21 deletions(-) diff --git a/include/openPMD/Datatype.hpp b/include/openPMD/Datatype.hpp index e09d04056c..f89d25c12b 100644 --- a/include/openPMD/Datatype.hpp +++ b/include/openPMD/Datatype.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Fabian Koller and Franz Pöschel +/* Copyright 2017-2019 Fabian Koller and Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp b/include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp index bfd035ec5a..57f7728adf 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp @@ -1,3 +1,24 @@ +/* Copyright 2017-2019 Franz Poeschel. + * + * This file is part of openPMD-api. + * + * openPMD-api is free software: you can redistribute it and/or modify + * it under the terms of of either the GNU General Public License or + * the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * openPMD-api is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with openPMD-api. + * If not, see . + */ + #pragma once #include "openPMD/config.hpp" @@ -95,4 +116,4 @@ namespace detail } // namespace openPMD -#endif \ No newline at end of file +#endif // openPMD_HAVE_ADIOS2 diff --git a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp index 04931c3f20..dd23822e5d 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp @@ -40,12 +40,12 @@ #if openPMD_HAVE_ADIOS2 -#include -#include "openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp" +# include +# include "openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp" #endif #if openPMD_HAVE_MPI -#include +# include #endif @@ -196,8 +196,8 @@ class ADIOS2IOHandlerImpl * Not to be accessed directly, use getFileData(). */ std::unordered_map< InvalidatableFile, - std::unique_ptr< detail::BufferedActions > > - m_fileData; + std::unique_ptr< detail::BufferedActions > + > m_fileData; std::map< std::string, adios2::Operator > m_operators; @@ -550,8 +550,11 @@ namespace detail std::string m_file; adios2::IO m_IO; std::vector< std::unique_ptr< BufferedAction > > m_buffer; - // std::optional would be more idiomatic, but it's not in - // the C++11 standard + /** + * @brief std::optional would be more idiomatic, but it's not in + * the C++11 standard + * @todo replace with std::optional upon switching to C++17 + */ std::unique_ptr< adios2::Engine > m_engine; adios2::Mode m_mode; detail::WriteDataset m_writeDataset; diff --git a/include/openPMD/IO/AbstractIOHandlerImplCommon.hpp b/include/openPMD/IO/AbstractIOHandlerImplCommon.hpp index ea2857e7e1..4a9a2ea4d5 100644 --- a/include/openPMD/IO/AbstractIOHandlerImplCommon.hpp +++ b/include/openPMD/IO/AbstractIOHandlerImplCommon.hpp @@ -1,4 +1,4 @@ -/* Copyright 2018-2019 Franz Pöschel +/* Copyright 2018-2019 Franz Poeschel * * This file is part of openPMD-api. * @@ -62,8 +62,8 @@ class AbstractIOHandlerImplCommon : public AbstractIOHandlerImpl std::tuple< InvalidatableFile, std::unordered_map< Writable *, InvalidatableFile >::iterator, - bool > - getPossiblyExisting( std::string file ); + bool + > getPossiblyExisting( std::string file ); void associateWithFile( Writable * writable, InvalidatableFile file ); diff --git a/include/openPMD/IO/InvalidatableFile.hpp b/include/openPMD/IO/InvalidatableFile.hpp index 094dd2e1cc..ea824a4276 100644 --- a/include/openPMD/IO/InvalidatableFile.hpp +++ b/include/openPMD/IO/InvalidatableFile.hpp @@ -1,4 +1,4 @@ -/* Copyright 2018-2019 Franz Pöschel +/* Copyright 2018-2019 Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/auxiliary/Environment.hpp b/include/openPMD/auxiliary/Environment.hpp index 326dd204fa..b0a3bdd01b 100644 --- a/include/openPMD/auxiliary/Environment.hpp +++ b/include/openPMD/auxiliary/Environment.hpp @@ -1,3 +1,25 @@ +/* Copyright 2018-2019 Franz Poeschel + * + * This file is part of openPMD-api. + * + * openPMD-api is free software: you can redistribute it and/or modify + * it under the terms of of either the GNU General Public License or + * the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * openPMD-api is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with openPMD-api. + * If not, see . + */ + + #pragma once #include #include diff --git a/include/openPMD/auxiliary/StringManip.hpp b/include/openPMD/auxiliary/StringManip.hpp index 4f1241a542..6fb2465d3a 100644 --- a/include/openPMD/auxiliary/StringManip.hpp +++ b/include/openPMD/auxiliary/StringManip.hpp @@ -204,6 +204,12 @@ join(std::vector< std::string > const& vs, std::string const& delimiter) } } +/** + * @brief Remove surrounding slashes from a string. + * + * @param s A string, possibly with a slash as first and/or last letter. + * @return std::string The same string without those slashes. + */ inline std::string removeSlashes( std::string s ) { diff --git a/src/IO/ADIOS/ADIOS2Auxiliary.cpp b/src/IO/ADIOS/ADIOS2Auxiliary.cpp index 68339377d0..46cdca5614 100644 --- a/src/IO/ADIOS/ADIOS2Auxiliary.cpp +++ b/src/IO/ADIOS/ADIOS2Auxiliary.cpp @@ -1,3 +1,24 @@ +/* Copyright 2017-2019 Franz Poeschel. + * + * This file is part of openPMD-api. + * + * openPMD-api is free software: you can redistribute it and/or modify + * it under the terms of of either the GNU General Public License or + * the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * openPMD-api is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with openPMD-api. + * If not, see . + */ + #include "openPMD/config.hpp" #if openPMD_HAVE_ADIOS2 #include "openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp" diff --git a/src/IO/ADIOS/ADIOS2IOHandler.cpp b/src/IO/ADIOS/ADIOS2IOHandler.cpp index 689178c11b..24024876ed 100644 --- a/src/IO/ADIOS/ADIOS2IOHandler.cpp +++ b/src/IO/ADIOS/ADIOS2IOHandler.cpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Fabian Koller +/* Copyright 2017-2019 Fabian Koller and Franz Poeschel. * * This file is part of openPMD-api. * @@ -19,13 +19,13 @@ * If not, see . */ #include "openPMD/IO/ADIOS/ADIOS2IOHandler.hpp" - #include "openPMD/Datatype.hpp" #include "openPMD/IO/ADIOS/ADIOS2FilePosition.hpp" #include "openPMD/IO/ADIOS/ADIOS2IOHandler.hpp" #include "openPMD/auxiliary/Environment.hpp" #include "openPMD/auxiliary/Filesystem.hpp" #include "openPMD/auxiliary/StringManip.hpp" + #include #include #include @@ -136,7 +136,7 @@ void ADIOS2IOHandlerImpl::createFile( associateWithFile( writable, shared_name ); this->m_dirty.emplace( shared_name ); getFileData( shared_name ).m_mode = adios2::Mode::Write; // WORKAROUND - // ↑ ADIOS2 does not yet implement ReadWrite Mode + // ADIOS2 does not yet implement ReadWrite Mode writable->written = true; writable->abstractFilePosition = @@ -168,9 +168,6 @@ void ADIOS2IOHandlerImpl::createPath( writable->written = true; writable->abstractFilePosition = std::make_shared< ADIOS2FilePosition >( path, ADIOS2FilePosition::GD::GROUP ); - - auto varName = nameOfVariable( writable ); - // m_IO.DefineVariable< void >( varName ); } void ADIOS2IOHandlerImpl::createDataset( @@ -191,8 +188,11 @@ void ADIOS2IOHandlerImpl::createDataset( auto filePos = setAndGetFilePosition( writable, name ); filePos->gd = ADIOS2FilePosition::GD::DATASET; auto varName = filePositionToString( filePos ); - // we use a unique_ptr to circumvent the fact that std::optional - // is only available beginning with c++17 + /* + * @brief std::optional would be more idiomatic, but it's not in + * the C++11 standard + * @todo replace with std::optional upon switching to C++17 + */ std::unique_ptr< adios2::Operator > compression; if ( !parameters.compression.empty( ) ) { diff --git a/src/IO/InvalidatableFile.cpp b/src/IO/InvalidatableFile.cpp index ae5aa074a9..c6ffe525a8 100644 --- a/src/IO/InvalidatableFile.cpp +++ b/src/IO/InvalidatableFile.cpp @@ -1,3 +1,23 @@ +/* Copyright 2017-2019 Franz Poeschel. + * + * This file is part of openPMD-api. + * + * openPMD-api is free software: you can redistribute it and/or modify + * it under the terms of of either the GNU General Public License or + * the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * openPMD-api is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with openPMD-api. + * If not, see . + */ #include "openPMD/IO/InvalidatableFile.hpp" From 38e7c4fbdc2663703fd1f6d13cd3231032d6bd96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20P=C3=B6schel?= Date: Fri, 4 Oct 2019 16:36:46 +0200 Subject: [PATCH 7/7] Replace the umlaut in my name with oe --- include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp | 2 +- include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp | 2 +- include/openPMD/IO/AccessType.hpp | 2 +- include/openPMD/IO/JSON/JSONFilePosition.hpp | 2 +- include/openPMD/IO/JSON/JSONIOHandler.hpp | 2 +- include/openPMD/IO/JSON/JSONIOHandlerImpl.hpp | 2 +- include/openPMD/auxiliary/StringManip.hpp | 2 +- include/openPMD/benchmark/mpi/BlockSlicer.hpp | 2 +- include/openPMD/benchmark/mpi/DatasetFiller.hpp | 2 +- include/openPMD/benchmark/mpi/MPIBenchmark.hpp | 2 +- include/openPMD/benchmark/mpi/MPIBenchmarkReport.hpp | 2 +- include/openPMD/benchmark/mpi/OneDimensionalBlockSlicer.hpp | 2 +- include/openPMD/benchmark/mpi/RandomDatasetFiller.hpp | 2 +- src/IO/JSON/JSONIOHandler.cpp | 2 +- src/IO/JSON/JSONIOHandlerImpl.cpp | 2 +- src/benchmark/mpi/OneDimensionalBlockSlicer.cpp | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp b/include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp index 94446f5142..c47315d910 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2FilePosition.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Fabian Koller and Franz Pöschel +/* Copyright 2017-2019 Fabian Koller and Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp index dd23822e5d..54bc13fcd1 100644 --- a/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp +++ b/include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Fabian Koller and Franz Pöschel +/* Copyright 2017-2019 Fabian Koller and Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/IO/AccessType.hpp b/include/openPMD/IO/AccessType.hpp index 1d982ac724..38e903a414 100644 --- a/include/openPMD/IO/AccessType.hpp +++ b/include/openPMD/IO/AccessType.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Fabian Koller and Franz Pöschel +/* Copyright 2017-2019 Fabian Koller and Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/IO/JSON/JSONFilePosition.hpp b/include/openPMD/IO/JSON/JSONFilePosition.hpp index 70748b094a..41c6e5f97c 100644 --- a/include/openPMD/IO/JSON/JSONFilePosition.hpp +++ b/include/openPMD/IO/JSON/JSONFilePosition.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Franz Pöschel +/* Copyright 2017-2019 Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/IO/JSON/JSONIOHandler.hpp b/include/openPMD/IO/JSON/JSONIOHandler.hpp index b657b9b534..b65b674ce8 100644 --- a/include/openPMD/IO/JSON/JSONIOHandler.hpp +++ b/include/openPMD/IO/JSON/JSONIOHandler.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Franz Pöschel +/* Copyright 2017-2019 Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/IO/JSON/JSONIOHandlerImpl.hpp b/include/openPMD/IO/JSON/JSONIOHandlerImpl.hpp index 9038ccd0d8..86415787a5 100644 --- a/include/openPMD/IO/JSON/JSONIOHandlerImpl.hpp +++ b/include/openPMD/IO/JSON/JSONIOHandlerImpl.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Franz Pöschel +/* Copyright 2017-2019 Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/auxiliary/StringManip.hpp b/include/openPMD/auxiliary/StringManip.hpp index 6fb2465d3a..c290a94740 100644 --- a/include/openPMD/auxiliary/StringManip.hpp +++ b/include/openPMD/auxiliary/StringManip.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Fabian Koller, Franz Pöschel +/* Copyright 2017-2019 Fabian Koller, Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/benchmark/mpi/BlockSlicer.hpp b/include/openPMD/benchmark/mpi/BlockSlicer.hpp index de70dc4eef..30cd63b6ce 100644 --- a/include/openPMD/benchmark/mpi/BlockSlicer.hpp +++ b/include/openPMD/benchmark/mpi/BlockSlicer.hpp @@ -1,4 +1,4 @@ -/* Copyright 2018-2019 Franz Pöschel +/* Copyright 2018-2019 Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/benchmark/mpi/DatasetFiller.hpp b/include/openPMD/benchmark/mpi/DatasetFiller.hpp index c1fb5f6b3b..731ff00621 100644 --- a/include/openPMD/benchmark/mpi/DatasetFiller.hpp +++ b/include/openPMD/benchmark/mpi/DatasetFiller.hpp @@ -1,4 +1,4 @@ -/* Copyright 2018-2019 Franz Pöschel +/* Copyright 2018-2019 Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/benchmark/mpi/MPIBenchmark.hpp b/include/openPMD/benchmark/mpi/MPIBenchmark.hpp index d9d517346d..c0214b6e43 100644 --- a/include/openPMD/benchmark/mpi/MPIBenchmark.hpp +++ b/include/openPMD/benchmark/mpi/MPIBenchmark.hpp @@ -1,4 +1,4 @@ -/* Copyright 2018-2019 Franz Pöschel +/* Copyright 2018-2019 Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/benchmark/mpi/MPIBenchmarkReport.hpp b/include/openPMD/benchmark/mpi/MPIBenchmarkReport.hpp index cea79f16f7..33fa4dc1cf 100644 --- a/include/openPMD/benchmark/mpi/MPIBenchmarkReport.hpp +++ b/include/openPMD/benchmark/mpi/MPIBenchmarkReport.hpp @@ -1,4 +1,4 @@ -/* Copyright 2018-2019 Franz Pöschel +/* Copyright 2018-2019 Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/benchmark/mpi/OneDimensionalBlockSlicer.hpp b/include/openPMD/benchmark/mpi/OneDimensionalBlockSlicer.hpp index 3067a48684..fde70c7ca3 100644 --- a/include/openPMD/benchmark/mpi/OneDimensionalBlockSlicer.hpp +++ b/include/openPMD/benchmark/mpi/OneDimensionalBlockSlicer.hpp @@ -1,4 +1,4 @@ -/* Copyright 2018-2019 Franz Pöschel +/* Copyright 2018-2019 Franz Poeschel * * This file is part of openPMD-api. * diff --git a/include/openPMD/benchmark/mpi/RandomDatasetFiller.hpp b/include/openPMD/benchmark/mpi/RandomDatasetFiller.hpp index 7648c4781e..edb959b24b 100644 --- a/include/openPMD/benchmark/mpi/RandomDatasetFiller.hpp +++ b/include/openPMD/benchmark/mpi/RandomDatasetFiller.hpp @@ -1,4 +1,4 @@ -/* Copyright 2018-2019 Franz Pöschel +/* Copyright 2018-2019 Franz Poeschel * * This file is part of openPMD-api. * diff --git a/src/IO/JSON/JSONIOHandler.cpp b/src/IO/JSON/JSONIOHandler.cpp index d945e41ae9..797156e85f 100644 --- a/src/IO/JSON/JSONIOHandler.cpp +++ b/src/IO/JSON/JSONIOHandler.cpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Franz Pöschel +/* Copyright 2017-2019 Franz Poeschel * * This file is part of openPMD-api. * diff --git a/src/IO/JSON/JSONIOHandlerImpl.cpp b/src/IO/JSON/JSONIOHandlerImpl.cpp index 25e6b7a271..901f76f917 100644 --- a/src/IO/JSON/JSONIOHandlerImpl.cpp +++ b/src/IO/JSON/JSONIOHandlerImpl.cpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2019 Franz Pöschel +/* Copyright 2017-2019 Franz Poeschel * * This file is part of openPMD-api. * diff --git a/src/benchmark/mpi/OneDimensionalBlockSlicer.cpp b/src/benchmark/mpi/OneDimensionalBlockSlicer.cpp index bd0b391e3e..39a3675bb0 100644 --- a/src/benchmark/mpi/OneDimensionalBlockSlicer.cpp +++ b/src/benchmark/mpi/OneDimensionalBlockSlicer.cpp @@ -1,4 +1,4 @@ -/* Copyright 2018-2019 Franz Pöschel +/* Copyright 2018-2019 Franz Poeschel * * This file is part of openPMD-api. *