openPMD-api
Attributable.hpp
1 /* Copyright 2017-2021 Fabian Koller
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/IO/AbstractIOHandler.hpp"
24 #include "openPMD/ThrowError.hpp"
25 #include "openPMD/auxiliary/OutOfRangeMsg.hpp"
26 #include "openPMD/backend/Attribute.hpp"
27 #include "openPMD/backend/Writable.hpp"
28 
29 #include <cstddef>
30 #include <exception>
31 #include <map>
32 #include <memory>
33 #include <string>
34 #include <type_traits>
35 #include <vector>
36 
37 // expose private and protected members for invasive testing
38 #ifndef OPENPMD_protected
39 #define OPENPMD_protected protected:
40 #endif
41 
42 namespace openPMD
43 {
44 namespace traits
45 {
46  template <typename T>
47  struct GenerationPolicy;
48 } // namespace traits
49 class AbstractFilePosition;
50 class Attributable;
51 class Iteration;
52 class Series;
53 
54 namespace internal
55 {
56  class IterationData;
57  class SeriesData;
58 
60  {
61  friend class openPMD::Attributable;
62 
63  public:
65  AttributableData(AttributableData const &) = delete;
67  virtual ~AttributableData() = default;
68 
69  AttributableData &operator=(AttributableData const &) = delete;
70  AttributableData &operator=(AttributableData &&) = delete;
71 
72  using A_MAP = std::map<std::string, Attribute>;
79 
80  template <typename T>
81  T asInternalCopyOf()
82  {
83  auto *self = dynamic_cast<typename T::Data_t *>(this);
84  if (!self)
85  {
86  if constexpr (std::is_same_v<Series, T>)
87  {
88  throw std::runtime_error(
89  "[Attributable::retrieveSeries] Error when trying to "
90  "retrieve the Series object. Note: An instance of the "
91  "Series object must still exist when flushing. A "
92  "common cause for this error is using a flush call on "
93  "a handle (e.g. `Iteration::seriesFlush()`) when the "
94  "original Series object has already gone out of "
95  "scope.");
96  }
97  else
98  {
99  throw std::runtime_error(
100 
101  "[AttributableData::asInternalCopyOf<T>] Error when "
102  "trying to retrieve a containing object. Note: An "
103  "instance of the Series object must still exist when "
104  "flushing. A common cause for this error is using a "
105  "flush call on a handle (e.g. "
106  "`Iteration::seriesFlush()`) when the original Series "
107  "object has already gone out of scope.");
108  }
109  }
110  T res;
111  res.setData(
112  std::shared_ptr<typename T::Data_t>(self, [](auto const *) {}));
113  return res;
114  }
115 
116  private:
120  A_MAP m_attributes;
121  };
122 
123  template <typename, typename>
124  class BaseRecordData;
125 
126  class RecordComponentData;
127 
128  /*
129  * Internal function to turn a handle into an owning handle that will keep
130  * not only itself, but the entire Series alive. Works by hiding a copy of
131  * the Series into the destructor lambda of the internal shared pointer. The
132  * returned handle is entirely safe to use in just the same ways as a normal
133  * handle, just the surrounding Series needs not be kept alive any more
134  * since it is stored within the handle. By storing the Series in the
135  * handle, not in the actual data, reference cycles are avoided.
136  *
137  * Instantiations for T exist for types RecordComponent,
138  * MeshRecordComponent, Mesh, Record, ParticleSpecies, Iteration.
139  */
140  template <typename T>
141  T &makeOwning(T &self, Series);
142 } // namespace internal
143 
144 namespace debug
145 {
146  void printDirty(Series const &);
147 }
148 
155 {
156  // @todo remove unnecessary friend (wew that sounds bitter)
157  using A_MAP = std::map<std::string, Attribute>;
158  friend Writable *getWritable(Attributable *);
159  template <typename T_elem>
160  friend class BaseRecord;
161  template <typename T_elem>
162  friend class BaseRecordInterface;
163  template <typename, typename>
164  friend class internal::BaseRecordData;
165  template <typename T, typename T_key, typename T_container>
166  friend class Container;
167  template <typename T>
168  friend struct traits::GenerationPolicy;
169  friend class Iteration;
170  friend class Series;
171  friend class Writable;
172  friend class WriteIterations;
173  friend class internal::RecordComponentData;
174  friend void debug::printDirty(Series const &);
175  template <typename T>
176  friend T &internal::makeOwning(T &self, Series);
177 
178 protected:
179  // tag for internal constructor
180  struct NoInit
181  {};
182 
184  std::shared_ptr<Data_t> m_attri;
185 
186 public:
187  Attributable();
189 
190  virtual ~Attributable() = default;
191 
206  template <typename T>
207  bool setAttribute(std::string const &key, T value);
208  bool setAttribute(std::string const &key, char const value[]);
219  Attribute getAttribute(std::string const &key) const;
220 
227  bool deleteAttribute(std::string const &key);
228 
233  std::vector<std::string> attributes() const;
238  size_t numAttributes() const;
244  bool containsAttribute(std::string const &key) const;
245 
251  std::string comment() const;
258  Attributable &setComment(std::string const &comment);
259 
274  void seriesFlush(std::string backendConfig = "{}");
275 
288  void iterationFlush(std::string backendConfig = "{}");
289 
296  struct MyPath
297  {
298  std::string directory;
299  std::string seriesName;
300  std::string seriesExtension;
309  std::vector<std::string> group;
310  Access access;
311 
313  std::string filePath() const;
315  std::string openPMDPath() const;
316  };
317 
323  MyPath myPath() const;
324 
329  void touch();
330 
331  // clang-format off
332 OPENPMD_protected
333  // clang-format on
334 
335  Series retrieveSeries() const;
336 
344  [[nodiscard]] auto containingIteration() const -> std::pair<
345  std::optional<internal::IterationData const *>,
346  internal::SeriesData const *>;
347  auto containingIteration() -> std::
348  pair<std::optional<internal::IterationData *>, internal::SeriesData *>;
351  template <bool flush_entire_series>
352  void seriesFlush_impl(internal::FlushParams const &);
353 
354  void flushAttributes(internal::FlushParams const &);
355 
356  enum ReadMode
357  {
373  };
374  void readAttributes(ReadMode);
375 
396  template <typename T>
397  T readFloatingpoint(std::string const &key) const;
419  template <typename T>
420  std::vector<T> readVectorFloatingpoint(std::string const &key) const;
421 
422  /* views into the resources held by m_writable
423  * purely for convenience so code that uses these does not have to go
424  * through m_writable-> */
425  AbstractIOHandler *IOHandler()
426  {
427  return const_cast<AbstractIOHandler *>(
428  static_cast<Attributable const *>(this)->IOHandler());
429  }
430  AbstractIOHandler const *IOHandler() const
431  {
432  auto &opt = m_attri->m_writable.IOHandler;
433  if (!opt || !opt->has_value())
434  {
435  return nullptr;
436  }
437  return &*opt->value();
438  }
439  Writable *&parent()
440  {
441  return m_attri->m_writable.parent;
442  }
443  Writable const *parent() const
444  {
445  return m_attri->m_writable.parent;
446  }
447  Writable &writable()
448  {
449  return m_attri->m_writable;
450  }
451  Writable const &writable() const
452  {
453  return m_attri->m_writable;
454  }
455 
456  inline void setData(std::shared_ptr<internal::AttributableData> attri)
457  {
458  m_attri = std::move(attri);
459  }
460 
461  inline internal::AttributableData &get()
462  {
463  return *m_attri;
464  }
465  inline internal::AttributableData const &get() const
466  {
467  return *m_attri;
468  }
469 
470  bool dirty() const
471  {
472  return writable().dirtySelf;
473  }
476  bool dirtyRecursive() const
477  {
478  return writable().dirtyRecursive;
479  }
480  void setDirty(bool dirty_in)
481  {
482  auto &w = writable();
483  w.dirtySelf = dirty_in;
484  setDirtyRecursive(dirty_in);
485  }
486  /* Amortized O(1) if dirty_in is true, else O(1).
487  *
488  * Must be used carefully with `dirty_in == false` since it is assumed that
489  * all children are not dirty.
490  *
491  * Invariant of dirtyRecursive:
492  * this->dirtyRecursive implies parent->dirtyRecursive.
493  *
494  * Hence:
495  *
496  * * If dirty_in is true: This needs only go up far enough until a parent is
497  * found that itself is dirtyRecursive.
498  * * If dirty_in is false: Only sets `this` to `dirtyRecursive == false`.
499  * The caller must ensure that the invariant holds (e.g. clearing
500  * everything during flushing or reading logic).
501  */
502  void setDirtyRecursive(bool dirty_in)
503  {
504  auto &w = writable();
505  w.dirtyRecursive = dirty_in;
506  if (dirty_in)
507  {
508  auto current = w.parent;
509  while (current && !current->dirtyRecursive)
510  {
511  current->dirtyRecursive = true;
512  current = current->parent;
513  }
514  }
515  }
516  bool written() const
517  {
518  return writable().written;
519  }
520  enum class EnqueueAsynchronously : bool
521  {
522  Yes,
523  No
524  };
525  /*
526  * setWritten() will take effect immediately.
527  * But it might additionally be necessary in some situations to enqueue a
528  * SET_WRITTEN task to the backend:
529  * A single flush() operation might encompass different Iterations. In
530  * file-based Iteration encoding, some objects must be written to every
531  * single file, thus their `written` flag must be restored to `false` for
532  * each Iteration. When flushing multiple Iterations at once, this must
533  * happen as an asynchronous IO task.
534  */
535  void setWritten(bool val, EnqueueAsynchronously);
536 
537 private:
543  virtual void linkHierarchy(Writable &w);
544 }; // Attributable
545 
546 // note: we explicitly instantiate Attributable::setAttributeImpl for all T in
547 // Datatype in Attributable.cpp
548 template <typename T>
549 inline bool Attributable::setAttribute(std::string const &key, T value)
550 {
551  auto &attri = get();
552  if (IOHandler() &&
553  IOHandler()->m_seriesStatus == internal::SeriesStatus::Default &&
554  Access::READ_ONLY == IOHandler()->m_frontendAccess)
555  {
556  auxiliary::OutOfRangeMsg const out_of_range_msg(
557  "Attribute", "can not be set (read-only).");
558  error::throwNoSuchAttribute(out_of_range_msg(key));
559  }
560 
561  setDirty(true);
562  auto it = attri.m_attributes.lower_bound(key);
563  if (it != attri.m_attributes.end() &&
564  !attri.m_attributes.key_comp()(key, it->first))
565  {
566  // key already exists in map, just replace the value
567  it->second = Attribute(std::move(value));
568  return true;
569  }
570  else
571  {
572  // emplace a new map element for an unknown key
573  attri.m_attributes.emplace_hint(
574  it, std::make_pair(key, Attribute(std::move(value))));
575  return false;
576  }
577 }
578 
579 inline bool
580 Attributable::setAttribute(std::string const &key, char const value[])
581 {
582  return this->setAttribute(key, std::string(value));
583 }
584 
585 template <typename T>
586 inline T Attributable::readFloatingpoint(std::string const &key) const
587 {
588  static_assert(
589  std::is_floating_point<T>::value,
590  "Type of attribute must be floating point");
591 
592  return getAttribute(key).get<T>();
593 }
594 
595 template <typename T>
596 inline std::vector<T>
597 Attributable::readVectorFloatingpoint(std::string const &key) const
598 {
599  static_assert(
600  std::is_floating_point<T>::value,
601  "Type of attribute must be floating point");
602 
603  return getAttribute(key).get<std::vector<T> >();
604 }
605 } // namespace openPMD
Interface for communicating between logical and physically persistent data.
Definition: AbstractIOHandler.hpp:186
Layer to manage storage of attributes associated with file objects.
Definition: Attributable.hpp:155
std::vector< T > readVectorFloatingpoint(std::string const &key) const
Retrieve a vector of values of a floating point Attributes of user-defined precision with ensured typ...
Definition: Attributable.hpp:597
bool containsAttribute(std::string const &key) const
Check whether am Attribute with a given key exists.
Definition: Attributable.cpp:104
MyPath myPath() const
The path to this object within its containing Series.
Definition: Attributable.cpp:222
ReadMode
Definition: Attributable.hpp:357
@ FullyReread
Remove all attributes that have been read previously and read everything that the backend currently h...
Definition: Attributable.hpp:372
@ IgnoreExisting
Don't read an attribute from the backend if it has been previously read.
Definition: Attributable.hpp:362
@ OverrideExisting
Read all the attributes that the backend has to offer and override if it has been read previously.
Definition: Attributable.hpp:367
std::vector< std::string > attributes() const
List all currently stored Attributes' keys.
Definition: Attributable.cpp:88
Attribute getAttribute(std::string const &key) const
Retrieve value of Attribute stored with provided key.
Definition: Attributable.cpp:58
void seriesFlush(std::string backendConfig="{}")
Flush the corresponding Series object.
Definition: Attributable.cpp:121
bool dirtyRecursive() const
O(1).
Definition: Attributable.hpp:476
T readFloatingpoint(std::string const &key) const
Retrieve the value of a floating point Attribute of user-defined precision with ensured type-safety.
Definition: Attributable.hpp:586
size_t numAttributes() const
Count all currently stored Attributes.
Definition: Attributable.cpp:99
bool deleteAttribute(std::string const &key)
Remove Attribute of provided value both logically and physically.
Definition: Attributable.cpp:68
auto containingIteration() const -> std::pair< std::optional< internal::IterationData const * >, internal::SeriesData const * >
Returns the corresponding Iteration.
Definition: Attributable.cpp:143
bool setAttribute(std::string const &key, T value)
Populate Attribute of provided name with provided value.
Definition: Attributable.hpp:549
Attributable & setComment(std::string const &comment)
Populate Attribute corresponding to a comment with the user-supplied comment.
Definition: Attributable.cpp:115
std::string comment() const
Retrieve a user-supplied comment associated with the object.
Definition: Attributable.cpp:110
void iterationFlush(std::string backendConfig="{}")
Flush the containing Iteration.
Definition: Attributable.cpp:127
void touch()
Sets the object dirty to make internal procedures think it has been modified.
Definition: Attributable.cpp:248
Variant datatype supporting at least all formats for attributes specified in the openPMD standard.
Definition: Attribute.hpp:56
U get() const
Retrieve a stored specific Attribute and cast if convertible.
Definition: Attribute.hpp:316
Base class for any type of record (e.g.
Definition: BaseRecord.hpp:224
Map-like container that enforces openPMD requirements and handles IO.
Definition: Container.hpp:104
Logical compilation of data from one snapshot (e.g.
Definition: Iteration.hpp:127
Implementation for the root level of the openPMD hierarchy.
Definition: Series.hpp:251
Layer to mirror structure of logical data and persistent data in file.
Definition: Writable.hpp:75
bool written
Whether a Writable has been written to the backend.
Definition: Writable.hpp:186
bool dirtyRecursive
Tracks if there are unwritten changes anywhere in the tree whose ancestor this Writable is.
Definition: Writable.hpp:165
bool dirtySelf
Tracks if there are unwritten changes for this specific Writable.
Definition: Writable.hpp:152
Writing side of the streaming API.
Definition: WriteIterations.hpp:67
Return an error string for read-only access.
Definition: OutOfRangeMsg.hpp:37
Definition: Attributable.hpp:60
Writable m_writable
The Writable associated with this Attributable.
Definition: Attributable.hpp:78
Definition: BaseRecord.hpp:52
Definition: RecordComponent.hpp:59
@ Default
Mutability of objects in the openPMD object model is determined by the open mode (Access enum),...
Public definitions of openPMD-api.
Definition: Date.cpp:29
Access
File access mode to use during IO.
Definition: Access.hpp:30
@ READ_ONLY
Open Series as read-only, fails if Series is not found.
String serialization to describe an Attributable.
Definition: Attributable.hpp:297
std::string filePath() const
Reconstructs a path that can be passed to a Series constructor.
Definition: Attributable.cpp:197
std::string seriesName
e.g., samples/git-samples/
Definition: Attributable.hpp:299
std::string seriesExtension
e.g., dataT
Definition: Attributable.hpp:300
std::vector< std::string > group
e.g., .bp, .h5, .json, ...
Definition: Attributable.hpp:309
std::string openPMDPath() const
Return the path ob the object within the openPMD file.
Definition: Attributable.cpp:202
Definition: Attributable.hpp:181
Container Element Creation Policy.
Definition: Container.hpp:52