openPMD-api
ADIOS2IOHandler.hpp
1 /* Copyright 2017-2020 Fabian Koller and Franz Poeschel
2  *
3  * This file is part of openPMD-api.
4  *
5  * openPMD-api is free software: you can redistribute it and/or modify
6  * it under the terms of of either the GNU General Public License or
7  * the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * openPMD-api is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License and the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * and the GNU Lesser General Public License along with openPMD-api.
19  * If not, see <http://www.gnu.org/licenses/>.
20  */
21 #pragma once
22 
23 #include "openPMD/config.hpp"
24 
25 #include "ADIOS2FilePosition.hpp"
26 #include "openPMD/IO/AbstractIOHandler.hpp"
27 #include "openPMD/IO/AbstractIOHandlerImpl.hpp"
28 #include "openPMD/IO/AbstractIOHandlerImplCommon.hpp"
29 #include "openPMD/IO/IOTask.hpp"
30 #include "openPMD/IO/InvalidatableFile.hpp"
31 #include "openPMD/backend/Writable.hpp"
32 
33 #include <array>
34 #include <future>
35 #include <memory> // shared_ptr
36 #include <string>
37 #include <unordered_map>
38 #include <utility> // pair
39 #include <vector>
40 
41 
42 #if openPMD_HAVE_ADIOS2
43 # include <adios2.h>
44 # include "openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp"
45 #endif
46 
47 #if openPMD_HAVE_MPI
48 # include <mpi.h>
49 #endif
50 
51 
52 namespace openPMD
53 {
54 #if openPMD_HAVE_ADIOS2
55 
56 class ADIOS2IOHandler;
57 
58 namespace detail
59 {
60  template < typename, typename > struct DatasetHelper;
61  struct DatasetReader;
62  struct AttributeReader;
63  struct AttributeWriter;
64  template < typename > struct AttributeTypes;
65  struct DatasetOpener;
66  template < typename > struct DatasetTypes;
67  struct WriteDataset;
68  struct BufferedActions;
69  struct BufferedPut;
70  struct BufferedGet;
71  struct BufferedAttributeRead;
72 } // namespace detail
73 
74 
76 : public AbstractIOHandlerImplCommon< ADIOS2FilePosition >
77 {
78  template < typename, typename > friend struct detail::DatasetHelper;
79  friend struct detail::DatasetReader;
80  friend struct detail::AttributeReader;
81  friend struct detail::AttributeWriter;
82  template < typename > friend struct detail::AttributeTypes;
83  friend struct detail::DatasetOpener;
84  template < typename > friend struct detail::DatasetTypes;
85  friend struct detail::WriteDataset;
86  friend struct detail::BufferedActions;
87  friend struct detail::BufferedAttributeRead;
88 
89  static constexpr bool ADIOS2_DEBUG_MODE = false;
90 
91 
92 public:
93 
94 #if openPMD_HAVE_MPI
95 
97 
98  MPI_Comm m_comm;
99 
100 #endif // openPMD_HAVE_MPI
101 
103 
104 
105  ~ADIOS2IOHandlerImpl( ) override = default;
106 
107  std::future< void > flush( ) override;
108 
109  void createFile( Writable *,
110  Parameter< Operation::CREATE_FILE > const & ) override;
111 
112  void createPath( Writable *,
113  Parameter< Operation::CREATE_PATH > const & ) override;
114 
115  void
116  createDataset( Writable *,
117  Parameter< Operation::CREATE_DATASET > const & ) override;
118 
119  void
120  extendDataset( Writable *,
121  Parameter< Operation::EXTEND_DATASET > const & ) override;
122 
123  void openFile( Writable *,
124  Parameter< Operation::OPEN_FILE > const & ) override;
125 
126  void openPath( Writable *,
127  Parameter< Operation::OPEN_PATH > const & ) override;
128 
129  void openDataset( Writable *,
131 
132  void deleteFile( Writable *,
133  Parameter< Operation::DELETE_FILE > const & ) override;
134 
135  void deletePath( Writable *,
136  Parameter< Operation::DELETE_PATH > const & ) override;
137 
138  void
139  deleteDataset( Writable *,
140  Parameter< Operation::DELETE_DATASET > const & ) override;
141 
142  void deleteAttribute( Writable *,
143  Parameter< Operation::DELETE_ATT > const & ) override;
144 
145  void writeDataset( Writable *,
146  Parameter< Operation::WRITE_DATASET > const & ) override;
147 
148  void writeAttribute( Writable *,
149  Parameter< Operation::WRITE_ATT > const & ) override;
150 
151  void readDataset( Writable *,
153 
154  void readAttribute( Writable *,
156 
157  void listPaths( Writable *, Parameter< Operation::LIST_PATHS > & ) override;
158 
159  void listDatasets( Writable *,
161 
162  void
163  listAttributes( Writable *,
164  Parameter< Operation::LIST_ATTS > & parameters ) override;
165 
166 
167 
172  adios2::Mode adios2Accesstype( );
173 
174 
175 private:
176  adios2::ADIOS m_ADIOS;
177 
178  /*
179  * We need to give names to IO objects. These names are irrelevant
180  * within this application, since:
181  * 1) The name of the file written to is decided by the opened Engine's
182  * name.
183  * 2) The IOs are managed by the unordered_map m_fileData, so we do not
184  * need the ADIOS2 internal management.
185  * Since within one m_ADIOS object, the same IO name cannot be used more
186  * than once, we ensure different names by using the name counter.
187  * This allows to overwrite a file later without error.
188  */
189  int nameCounter{0};
190 
191  /*
192  * IO-heavy actions are deferred to a later point. This map stores for
193  * each open file (identified by an InvalidatableFile object) an object
194  * that manages IO-heavy actions, as well as its ADIOS2 objects, i.e.
195  * IO and Engine object.
196  * Not to be accessed directly, use getFileData().
197  */
198  std::unordered_map< InvalidatableFile,
199  std::unique_ptr< detail::BufferedActions >
200  > m_fileData;
201 
202  std::map< std::string, adios2::Operator > m_operators;
203 
204  // Overrides from AbstractIOHandlerImplCommon.
205 
206  std::string
207  filePositionToString( std::shared_ptr< ADIOS2FilePosition > ) override;
208 
209  std::shared_ptr< ADIOS2FilePosition >
210  extendFilePosition( std::shared_ptr< ADIOS2FilePosition > const & pos,
211  std::string extend ) override;
212 
213  // Helper methods.
214 
215  std::unique_ptr< adios2::Operator >
216  getCompressionOperator( std::string const & compression );
217 
218  /*
219  * The name of the ADIOS2 variable associated with this Writable.
220  * To be used for Writables that represent a dataset.
221  */
222  std::string nameOfVariable( Writable * writable );
223 
233  std::string nameOfAttribute( Writable * writable, std::string attribute );
234 
235  /*
236  * Figure out whether the Writable corresponds with a
237  * group or a dataset.
238  */
239  ADIOS2FilePosition::GD groupOrDataset( Writable * );
240 
241  detail::BufferedActions & getFileData( InvalidatableFile file );
242 
243  void dropFileData( InvalidatableFile file );
244 
245  /*
246  * Prepare a variable that already exists for an IO
247  * operation, including:
248  * (1) checking that its datatype matches T.
249  * (2) the offset and extent match the variable's shape
250  * (3) setting the offset and extent (ADIOS lingo: start
251  * and count)
252  */
253  template < typename T >
254  adios2::Variable< T > verifyDataset( Offset const & offset,
255  Extent const & extent, adios2::IO & IO,
256  std::string const & var );
257 }; // ADIOS2IOHandlerImpl
258 
259 namespace detail
260 {
261  // Helper structs for calls to the switchType function
262 
264  {
266 
267 
268  explicit DatasetReader( openPMD::ADIOS2IOHandlerImpl * impl );
269 
270 
271  template < typename T >
272  void operator( )( BufferedGet & bp, adios2::IO & IO,
273  adios2::Engine & engine,
274  std::string const & fileName );
275 
276  template < int T, typename... Params > void operator( )( Params &&... );
277  };
278 
280  {
281  template < typename T >
282  Datatype operator( )( adios2::IO & IO, std::string name,
283  std::shared_ptr< Attribute::resource > resource );
284 
285  template < int n, typename... Params >
286  Datatype operator( )( Params &&... );
287  };
288 
290  {
291  template < typename T >
292  void
293  operator( )( ADIOS2IOHandlerImpl * impl, Writable * writable,
294  const Parameter< Operation::WRITE_ATT > & parameters );
295 
296 
297  template < int n, typename... Params > void operator( )( Params &&... );
298  };
299 
301  {
302  ADIOS2IOHandlerImpl * m_impl;
303 
304 
305  explicit DatasetOpener( ADIOS2IOHandlerImpl * impl );
306 
307 
308  template < typename T >
309  void operator( )( InvalidatableFile, const std::string & varName,
311 
312 
313  template < int n, typename... Params > void operator( )( Params &&... );
314  };
315 
317  {
318  ADIOS2IOHandlerImpl * m_handlerImpl;
319 
320 
321  WriteDataset( ADIOS2IOHandlerImpl * handlerImpl );
322 
323 
324  template < typename T >
325  void operator( )( BufferedPut & bp, adios2::IO & IO,
326  adios2::Engine & engine );
327 
328  template < int n, typename... Params > void operator( )( Params &&... );
329  };
330 
332  {
333 
334  template < typename T >
335  void operator( )( adios2::IO & IO, const std::string & name,
336  std::unique_ptr< adios2::Operator > compression,
337  const adios2::Dims & shape = adios2::Dims( ),
338  const adios2::Dims & start = adios2::Dims( ),
339  const adios2::Dims & count = adios2::Dims( ),
340  const bool constantDims = false );
341 
342  template < int n, typename... Params >
343  void operator( )( adios2::IO & IO, Params &&... );
344  };
345 
346 
347 
348  // Helper structs to help distinguish valid attribute/variable
349  // datatypes from invalid ones
350 
351 
352  /*
353  * This struct's purpose is to have specialisations
354  * for vector and array types, as well as the boolean
355  * type (which is not natively supported by ADIOS).
356  */
357  template < typename T > struct AttributeTypes
358  {
359  using Attr = adios2::Attribute< T >;
360  using BasicType = T;
361 
362  static Attr createAttribute( adios2::IO & IO, std::string name,
363  BasicType value );
364 
365  static void
366  readAttribute( adios2::IO & IO, std::string name,
367  std::shared_ptr< Attribute::resource > resource );
368  };
369 
370  template < typename T > struct AttributeTypes< std::vector< T > >
371  {
372  using Attr = adios2::Attribute< T >;
373  using BasicType = T;
374 
375  static Attr createAttribute( adios2::IO & IO, std::string name,
376  const std::vector< T > & value );
377 
378  static void
379  readAttribute( adios2::IO & IO, std::string name,
380  std::shared_ptr< Attribute::resource > resource );
381  };
382 
383  template < typename T, size_t n >
384  struct AttributeTypes< std::array< T, n > >
385  {
386  using Attr = adios2::Attribute< T >;
387  using BasicType = T;
388 
389  static Attr createAttribute( adios2::IO & IO, std::string name,
390  const std::array< T, n > & value );
391 
392  static void
393  readAttribute( adios2::IO & IO, std::string name,
394  std::shared_ptr< Attribute::resource > resource );
395  };
396 
397  template <> struct AttributeTypes< bool >
398  {
399  using rep = detail::bool_representation;
400  using Attr = adios2::Attribute< rep >;
401  using BasicType = rep;
402 
403  static Attr createAttribute( adios2::IO & IO, std::string name,
404  bool value );
405 
406  static void
407  readAttribute( adios2::IO & IO, std::string name,
408  std::shared_ptr< Attribute::resource > resource );
409 
410 
411  static constexpr rep toRep( bool b )
412  {
413  return b ? 1U : 0U;
414  }
415 
416 
417  static constexpr bool fromRep( rep r )
418  {
419  return r != 0;
420  }
421  };
422 
423 
429  template < typename T > struct DatasetTypes
430  {
431  static constexpr bool validType = true;
432  };
433 
434  template < typename T > struct DatasetTypes< std::vector< T > >
435  {
436  static constexpr bool validType = false;
437  };
438 
439  template <> struct DatasetTypes< bool >
440  {
441  static constexpr bool validType = false;
442  };
443 
444 
445  template < typename T, size_t n > struct DatasetTypes< std::array< T, n > >
446  {
447  static constexpr bool validType = false;
448  };
449 
450  /*
451  * This struct's purpose is to have exactly two specialisations:
452  * (1) for types that are legal to use in a dataset
453  * (2) for types that are not legal to use in a dataset
454  * The methods in the latter specialisation will fail at runtime.
455  */
456  template < typename, typename = void > struct DatasetHelper;
457 
458  template < typename T >
460  T, typename std::enable_if< DatasetTypes< T >::validType >::type >
461  {
463 
464 
465  explicit DatasetHelper( openPMD::ADIOS2IOHandlerImpl * impl );
466 
467 
468  void openDataset( InvalidatableFile, const std::string & varName,
470 
471  void readDataset( BufferedGet &, adios2::IO &, adios2::Engine &,
472  std::string const & fileName );
473 
474  static void
475  defineVariable( adios2::IO & IO, const std::string & name,
476  std::unique_ptr< adios2::Operator > compression,
477  const adios2::Dims & shape, const adios2::Dims & start,
478  const adios2::Dims & count, bool constantDims );
479 
480  void writeDataset( BufferedPut &, adios2::IO &, adios2::Engine & );
481  };
482 
483  template < typename T >
485  T, typename std::enable_if< !DatasetTypes< T >::validType >::type >
486  {
487  explicit DatasetHelper( openPMD::ADIOS2IOHandlerImpl * impl );
488 
489 
490  static void throwErr( );
491 
492  template < typename... Params > void openDataset( Params &&... );
493 
494  template < typename... Params > void readDataset( Params &&... );
495 
496  template < typename... Params >
497  static void defineVariable( Params &&... );
498 
499  template < typename... Params > void writeDataset( Params &&... );
500  };
501 
502  // Other datatypes used in the ADIOS2IOHandler implementation
503 
504 
505  struct BufferedActions;
506 
507  /*
508  * IO-heavy action to be executed upon flushing.
509  */
511  {
512  virtual ~BufferedAction( ) = default;
513 
514  virtual void run( BufferedActions & ) = 0;
515  };
516 
518  {
519  std::string name;
521 
522  void run( BufferedActions & ) override;
523  };
524 
526  {
527  std::string name;
529 
530  void run( BufferedActions & ) override;
531  };
532 
534  {
536  std::string name;
537 
538  void run( BufferedActions & ) override;
539  };
540 
541  /*
542  * Manages per-file information about
543  * (1) the file's IO and Engine objects
544  * (2) the file's deferred IO-heavy actions
545  */
547  {
548  BufferedActions( BufferedActions const & ) = delete;
549 
550  std::string m_file;
551  adios2::IO m_IO;
552  std::vector< std::unique_ptr< BufferedAction > > m_buffer;
558  std::unique_ptr< adios2::Engine > m_engine;
559  adios2::Mode m_mode;
560  detail::WriteDataset m_writeDataset;
561  detail::DatasetReader m_readDataset;
562  detail::AttributeReader m_attributeReader;
563  ADIOS2IOHandlerImpl & m_impl;
564 
565  using AttributeMap_t = std::map< std::string, adios2::Params >;
566 
568 
569  ~BufferedActions( );
570 
571  adios2::Engine & getEngine( );
572 
573  template < typename BA > void enqueue( BA && ba );
574 
575 
576  void flush( );
577 
578  /*
579  * Delete all buffered actions without running them.
580  */
581  void drop( );
582 
583  AttributeMap_t const &
584  availableAttributes();
585 
586  std::vector< std::string >
587  availableAttributesPrefixed( std::string const & prefix );
588 
589  /*
590  * See description below.
591  */
592  void
593  invalidateAttributesMap();
594 
595  AttributeMap_t const &
596  availableVariables();
597 
598  std::vector< std::string >
599  availableVariablesPrefixed( std::string const & prefix );
600 
601  /*
602  * See description below.
603  */
604  void
605  invalidateVariablesMap();
606 
607  private:
608  /*
609  * ADIOS2 does not give direct access to its internal attribute and
610  * variable maps, but will instead give access to copies of them.
611  * In order to avoid unnecessary copies, we buffer the returned map.
612  * The downside of this is that we need to pay attention to invalidate
613  * the map whenever an attribute/variable is altered. In that case, we
614  * fetch the map anew.
615  * Revisit once https://github.com/openPMD/openPMD-api/issues/563 has
616  * been resolved
617  * If false, the buffered map has been invalidated and needs to be
618  * queried from ADIOS2 again. If true, the buffered map is equivalent to
619  * the map that would be returned by a call to
620  * IO::Available(Attributes|Variables).
621  */
622  bool m_availableAttributesValid = false;
623  AttributeMap_t m_availableAttributes;
624 
625  bool m_availableVariablesValid = false;
626  AttributeMap_t m_availableVariables;
627  };
628 
629 
630 } // namespace detail
631 #endif // openPMD_HAVE_ADIOS2
632 
633 
635 {
636 #if openPMD_HAVE_ADIOS2
637 
638 friend class ADIOS2IOHandlerImpl;
639 
640 private:
641  ADIOS2IOHandlerImpl m_impl;
642 
643 public:
644  ~ADIOS2IOHandler( ) override
645  {
646  this->flush( );
647  }
648 
649 #else
650 public:
651 #endif
652 
653 #if openPMD_HAVE_MPI
654 
655  ADIOS2IOHandler( std::string path, AccessType, MPI_Comm );
656 
657 #endif
658 
659  ADIOS2IOHandler( std::string path, AccessType );
660 
661  std::string backendName() const override { return "ADIOS2"; }
662 
663  std::future< void > flush( ) override;
664 }; // ADIOS2IOHandler
665 } // namespace openPMD
Definition: ADIOS2IOHandler.hpp:60
Wrapper around a shared pointer to:
Definition: InvalidatableFile.hpp:45
AccessType
File access mode to use during IO.
Definition: AccessType.hpp:28
Definition: ADIOS2IOHandler.hpp:533
Definition: ADIOS2IOHandler.hpp:279
Definition: ADIOS2IOHandler.hpp:331
STL namespace.
Definition: ADIOS2IOHandler.hpp:75
Definition: ADIOS2IOHandler.hpp:546
Definition: ADIOS2IOHandler.hpp:525
This struct&#39;s only field indicates whether the template parameter is a valid datatype to use for a da...
Definition: ADIOS2IOHandler.hpp:66
Definition: ADIOS2IOHandler.hpp:517
Datatype
Concrete datatype of an object available at runtime.
Definition: Datatype.hpp:38
Definition: Container.cpp:51
Interface for communicating between logical and physically persistent data.
Definition: AbstractIOHandler.hpp:68
Public definitions of openPMD-api.
Definition: Date.cpp:28
Layer to mirror structure of logical data and persistent data in file.
Definition: Writable.hpp:55
Definition: ADIOS2IOHandler.hpp:634
Definition: ADIOS2IOHandler.hpp:316
std::string backendName() const override
The currently used backend.
Definition: ADIOS2IOHandler.hpp:661
Definition: ADIOS2IOHandler.hpp:289
Definition: Writable.hpp:43
Definition: ADIOS2IOHandler.hpp:300
Definition: ADIOS2IOHandler.hpp:263
Definition: ADIOS2IOHandler.hpp:64
Definition: ADIOS2IOHandler.hpp:510
std::unique_ptr< adios2::Engine > m_engine
std::optional would be more idiomatic, but it&#39;s not in the C++11 standard
Definition: ADIOS2IOHandler.hpp:558