openPMD-api
 
Loading...
Searching...
No Matches
Attributable.hpp
1/* Copyright 2017-2025 Fabian Koller, Axel Huebl, 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/Error.hpp"
24#include "openPMD/IO/AbstractIOHandler.hpp"
25#include "openPMD/ThrowError.hpp"
26#include "openPMD/auxiliary/OutOfRangeMsg.hpp"
27#include "openPMD/backend/Attribute.hpp"
28#include "openPMD/backend/Writable.hpp"
29
30#include <cstddef>
31#include <map>
32#include <memory>
33#include <optional>
34#include <string>
35#include <type_traits>
36#include <vector>
37
38// expose private and protected members for invasive testing
39#ifndef OPENPMD_protected
40#define OPENPMD_protected protected:
41#endif
42
43namespace openPMD
44{
45namespace traits
46{
47 template <typename T>
48 struct GenerationPolicy;
49} // namespace traits
51class Attributable;
52class Iteration;
53class Series;
54
55namespace internal
56{
57 class IterationData;
58 class SeriesData;
59 struct HomogenizeExtents;
60
61 class SharedAttributableData
62 {
63 friend class openPMD::Attributable;
64
65 public:
66 SharedAttributableData(AttributableData *);
67 SharedAttributableData(SharedAttributableData const &) = delete;
68 SharedAttributableData(SharedAttributableData &&) = delete;
69 virtual ~SharedAttributableData() = default;
70
71 SharedAttributableData &
72 operator=(SharedAttributableData const &) = delete;
73 SharedAttributableData &operator=(SharedAttributableData &&) = delete;
74
75 using A_MAP = std::map<std::string, Attribute>;
82
87 };
88
89 /*
90 * This is essentially a two-level pointer.
91 *
92 * 1. level: Our public API hands out handles to users that are (shared)
93 * pointers to an internal object (PIMPL).
94 * 2. level: Multiple internal objects might refer to the same item in an
95 * openPMD file, e.g. to the same backend object.
96 * So, the internal object for an Attributable is a shared pointer to the
97 * unique object identifying this item.
98 *
99 * Such sharing occurs in the CustomHierarchy class where multiple
100 * containers refer to the same group in the openPMD hierarchy
101 * (container of groups, of meshes, of particle species, of datasets).
102 * This might also become relevant for links as in HDF5 if we choose to
103 * implement them.
104 */
105
106 class AttributableData : public std::shared_ptr<SharedAttributableData>
107 {
108 friend class openPMD::Attributable;
109
110 using SharedData_t = std::shared_ptr<SharedAttributableData>;
111 using A_MAP = SharedData_t::element_type::A_MAP;
112
113 public:
114 AttributableData();
115 AttributableData(SharedAttributableData *);
116 AttributableData(AttributableData const &) = delete;
117 AttributableData(AttributableData &&) = delete;
118 virtual ~AttributableData() = default;
119
120 AttributableData &operator=(AttributableData const &) = delete;
121 AttributableData &operator=(AttributableData &&) = delete;
122
123 // Make copies explicit, only to be used under the conditions described
124 // above
125 void cloneFrom(AttributableData const &other);
126
127 template <typename T>
128 T asInternalCopyOf()
129 {
130 auto *self = dynamic_cast<typename T::Data_t *>(this);
131 if (!self)
132 {
133 if constexpr (std::is_same_v<Series, T>)
134 {
135 throw std::runtime_error(
136 "[Attributable::retrieveSeries] Error when trying to "
137 "retrieve the Series object. Note: An instance of the "
138 "Series object must still exist when flushing. A "
139 "common cause for this error is using a flush call on "
140 "a handle (e.g. `Iteration::seriesFlush()`) when the "
141 "original Series object has already gone out of "
142 "scope.");
143 }
144 else
145 {
146 throw std::runtime_error(
147
148 "[AttributableData::asInternalCopyOf<T>] Error when "
149 "trying to retrieve a containing object. Note: An "
150 "instance of the Series object must still exist when "
151 "flushing. A common cause for this error is using a "
152 "flush call on a handle (e.g. "
153 "`Iteration::seriesFlush()`) when the original Series "
154 "object has already gone out of scope.");
155 }
156 }
157 T res;
158 res.setData(
159 std::shared_ptr<typename T::Data_t>(self, [](auto const *) {}));
160 return res;
161 }
162
163 inline auto attributes() -> A_MAP &
164 {
165 return operator*().m_attributes;
166 }
167 [[nodiscard]] inline auto attributes() const -> A_MAP const &
168 {
169 return operator*().m_attributes;
170 }
171 [[nodiscard]] inline auto readAttribute(std::string const &name) const
172 -> Attribute const &
173 {
174 auto const &attr = attributes();
175 if (auto it = attr.find(name); it != attr.end())
176 {
177 return it->second;
178 }
179 else
180 {
181 throw error::ReadError(
182 error::AffectedObject::Attribute,
183 error::Reason::NotFound,
184 std::nullopt,
185 "Not found: '" + name + "'.");
186 }
187 }
188 };
189
190 template <typename, typename>
191 class BaseRecordData;
192
194
195 /*
196 * Internal function to turn a handle into an owning handle that will keep
197 * not only itself, but the entire Series alive. Works by hiding a copy of
198 * the Series into the destructor lambda of the internal shared pointer. The
199 * returned handle is entirely safe to use in just the same ways as a normal
200 * handle, just the surrounding Series needs not be kept alive any more
201 * since it is stored within the handle. By storing the Series in the
202 * handle, not in the actual data, reference cycles are avoided.
203 *
204 * Instantiations for T exist for types RecordComponent,
205 * MeshRecordComponent, Mesh, Record, ParticleSpecies, Iteration.
206 */
207 template <typename T>
208 T &makeOwning(T &self, Series);
209} // namespace internal
210
211namespace debug
212{
213 void printDirty(Series const &);
214}
215
221class Attributable
222{
223 // @todo remove unnecessary friend (wew that sounds bitter)
224 using A_MAP = std::map<std::string, Attribute>;
225 friend Writable *getWritable(Attributable *);
226 template <typename T_elem>
227 friend class BaseRecord;
228 template <typename T_elem>
229 friend class BaseRecordInterface;
230 template <typename, typename>
231 friend class internal::BaseRecordData;
232 template <typename T, typename T_key, typename T_container>
233 friend class Container;
234 template <typename T>
235 friend struct traits::GenerationPolicy;
236 friend class Iteration;
237 friend class Series;
238 friend class Writable;
240 friend void debug::printDirty(Series const &);
241 template <typename T>
242 friend T &internal::makeOwning(T &self, Series);
243 friend class StatefulSnapshotsContainer;
244 friend class internal::AttributableData;
245 friend class Snapshots;
246 friend struct internal::HomogenizeExtents;
247
248protected:
249 // tag for internal constructor
250 struct NoInit
251 {};
252
253 using Data_t = internal::AttributableData;
254 std::shared_ptr<Data_t> m_attri;
255
256public:
257 Attributable();
258 Attributable(NoInit) noexcept;
259
260 virtual ~Attributable() = default;
261
276 template <typename T>
277 bool setAttribute(std::string const &key, T value);
278 bool setAttribute(std::string const &key, char const value[]);
281
289 Attribute getAttribute(std::string const &key) const;
290
297 bool deleteAttribute(std::string const &key);
298
303 std::vector<std::string> attributes() const;
308 size_t numAttributes() const;
314 bool containsAttribute(std::string const &key) const;
315
321 std::string comment() const;
328 Attributable &setComment(std::string const &comment);
329
344 void seriesFlush(std::string backendConfig = "{}");
345
358 void iterationFlush(std::string backendConfig = "{}");
359
366 struct MyPath
367 {
368 std::string directory;
369 std::string seriesName;
370 std::string seriesExtension;
379 std::vector<std::string> group;
380 Access access;
381
383 std::string filePath() const;
385 std::string openPMDPath() const;
386 };
387
393 MyPath myPath() const;
394
399 void touch();
400
401 [[nodiscard]] OpenpmdStandard openPMDStandard() const;
402
403 // clang-format off
404OPENPMD_protected
405 // clang-format on
406
407 Series retrieveSeries() const;
408
416 [[nodiscard]] auto containingIteration() const -> std::pair<
417 std::optional<internal::IterationData const *>,
418 internal::SeriesData const *>;
419 auto containingIteration() -> std::
420 pair<std::optional<internal::IterationData *>, internal::SeriesData *>;
422
423 template <bool flush_entire_series>
424 void seriesFlush_impl(internal::FlushParams const &);
425
426 void flushAttributes(internal::FlushParams const &);
427
446 void readAttributes(ReadMode);
447
468 template <typename T>
469 T readFloatingpoint(std::string const &key) const;
491 template <typename T>
492 std::vector<T> readVectorFloatingpoint(std::string const &key) const;
493
494 /* views into the resources held by m_writable
495 * purely for convenience so code that uses these does not have to go
496 * through m_writable-> */
497 AbstractIOHandler *IOHandler()
498 {
499 return const_cast<AbstractIOHandler *>(
500 static_cast<Attributable const *>(this)->IOHandler());
501 }
502 AbstractIOHandler const *IOHandler() const
503 {
504 auto &opt = writable().IOHandler;
505 if (!opt || !opt->has_value())
506 {
507 return nullptr;
508 }
509 return &*opt->value();
510 }
511 Writable *&parent()
512 {
513 return writable().parent;
514 }
515 Writable const *parent() const
516 {
517 return writable().parent;
518 }
519 Writable &writable()
520 {
521 return (*m_attri)->m_writable;
522 }
523 Writable const &writable() const
524 {
525 return (*m_attri)->m_writable;
526 }
527
528 inline void setData(std::shared_ptr<internal::AttributableData> attri)
529 {
530 m_attri = std::move(attri);
531 }
532
533 inline internal::SharedAttributableData &get()
534 {
535 return **m_attri;
536 }
537 inline internal::SharedAttributableData const &get() const
538 {
539 return **m_attri;
540 }
541
542 bool dirty() const
543 {
544 return writable().dirtySelf;
545 }
548 bool dirtyRecursive() const
549 {
550 return writable().dirtyRecursive;
551 }
552 void setDirty(bool dirty_in)
553 {
554 auto &w = writable();
555 w.dirtySelf = dirty_in;
556 setDirtyRecursive(dirty_in);
557 }
558 /* Amortized O(1) if dirty_in is true, else O(1).
559 *
560 * Must be used carefully with `dirty_in == false` since it is assumed that
561 * all children are not dirty.
562 *
563 * Invariant of dirtyRecursive:
564 * this->dirtyRecursive implies parent->dirtyRecursive.
565 *
566 * Hence:
567 *
568 * * If dirty_in is true: This needs only go up far enough until a parent is
569 * found that itself is dirtyRecursive.
570 * * If dirty_in is false: Only sets `this` to `dirtyRecursive == false`.
571 * The caller must ensure that the invariant holds (e.g. clearing
572 * everything during flushing or reading logic).
573 */
574 void setDirtyRecursive(bool dirty_in)
575 {
576 auto &w = writable();
577 w.dirtyRecursive = dirty_in;
578 if (dirty_in)
579 {
580 auto current = w.parent;
581 while (current && !current->dirtyRecursive)
582 {
583 current->dirtyRecursive = true;
584 current = current->parent;
585 }
586 }
587 }
588 bool written() const
589 {
590 return writable().written;
591 }
592 enum class EnqueueAsynchronously : bool
593 {
594 Yes,
595 No
596 };
597 /*
598 * setWritten() will take effect immediately.
599 * But it might additionally be necessary in some situations to enqueue a
600 * SET_WRITTEN task to the backend:
601 * A single flush() operation might encompass different Iterations. In
602 * file-based Iteration encoding, some objects must be written to every
603 * single file, thus their `written` flag must be restored to `false` for
604 * each Iteration. When flushing multiple Iterations at once, this must
605 * happen as an asynchronous IO task.
606 */
607 void setWritten(bool val, EnqueueAsynchronously);
608
609private:
615 virtual void linkHierarchy(Writable &w);
616}; // Attributable
617
618// note: we explicitly instantiate Attributable::setAttributeImpl for all T in
619// Datatype in Attributable.cpp
620template <typename T>
621inline bool Attributable::setAttribute(std::string const &key, T value)
622{
623 auto &attri = get();
624 if (IOHandler() &&
625 IOHandler()->m_seriesStatus == internal::SeriesStatus::Default &&
626 Access::READ_ONLY == IOHandler()->m_frontendAccess)
627 {
628 auxiliary::OutOfRangeMsg const out_of_range_msg(
629 "Attribute", "can not be set (read-only).");
630 error::throwNoSuchAttribute(out_of_range_msg(key));
631 }
632
633 setDirty(true);
634 auto it = attri.m_attributes.lower_bound(key);
635 if (it != attri.m_attributes.end() &&
636 !attri.m_attributes.key_comp()(key, it->first))
637 {
638 // key already exists in map, just replace the value
639 it->second = Attribute(std::move(value));
640 return true;
641 }
642 else
643 {
644 // emplace a new map element for an unknown key
645 attri.m_attributes.emplace_hint(
646 it, std::make_pair(key, Attribute(std::move(value))));
647 return false;
648 }
649}
650
651inline bool
652Attributable::setAttribute(std::string const &key, char const value[])
653{
654 return this->setAttribute(key, std::string(value));
655}
656
657template <typename T>
658inline T Attributable::readFloatingpoint(std::string const &key) const
659{
660 static_assert(
661 std::is_floating_point<T>::value,
662 "Type of attribute must be floating point");
663
664 return getAttribute(key).get<T>();
665}
666
667template <typename T>
668inline std::vector<T>
669Attributable::readVectorFloatingpoint(std::string const &key) const
670{
671 static_assert(
672 std::is_floating_point<T>::value,
673 "Type of attribute must be floating point");
674
675 return getAttribute(key).get<std::vector<T> >();
676}
677} // namespace openPMD
Definition AbstractFilePosition.hpp:26
Interface for communicating between logical and physically persistent data.
Definition AbstractIOHandler.hpp:206
Layer to manage storage of attributes associated with file objects.
Definition Attributable.hpp:222
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:669
bool containsAttribute(std::string const &key) const
Check whether am Attribute with a given key exists.
Definition Attributable.cpp:121
MyPath myPath() const
The path to this object within its containing Series.
Definition Attributable.cpp:236
ReadMode
Definition Attributable.hpp:429
@ FullyReread
Remove all attributes that have been read previously and read everything that the backend currently h...
Definition Attributable.hpp:444
@ IgnoreExisting
Don't read an attribute from the backend if it has been previously read.
Definition Attributable.hpp:434
@ OverrideExisting
Read all the attributes that the backend has to offer and override if it has been read previously.
Definition Attributable.hpp:439
std::vector< std::string > attributes() const
List all currently stored Attributes' keys.
Definition Attributable.cpp:105
Attribute getAttribute(std::string const &key) const
Retrieve value of Attribute stored with provided key.
Definition Attributable.cpp:75
void seriesFlush(std::string backendConfig="{}")
Flush the corresponding Series object.
Definition Attributable.cpp:138
bool dirtyRecursive() const
O(1).
Definition Attributable.hpp:548
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:658
size_t numAttributes() const
Count all currently stored Attributes.
Definition Attributable.cpp:116
bool deleteAttribute(std::string const &key)
Remove Attribute of provided value both logically and physically.
Definition Attributable.cpp:85
auto containingIteration() const -> std::pair< std::optional< internal::IterationData const * >, internal::SeriesData const * >
Returns the corresponding Iteration.
Definition Attributable.cpp:160
bool setAttribute(std::string const &key, T value)
Populate Attribute of provided name with provided value.
Definition Attributable.hpp:621
Attributable & setComment(std::string const &comment)
Populate Attribute corresponding to a comment with the user-supplied comment.
Definition Attributable.cpp:132
std::string comment() const
Retrieve a user-supplied comment associated with the object.
Definition Attributable.cpp:127
void iterationFlush(std::string backendConfig="{}")
Flush the containing Iteration.
Definition Attributable.cpp:144
void touch()
Sets the object dirty to make internal procedures think it has been modified.
Definition Attributable.cpp:263
Definition Attribute.hpp:64
U get() const
Retrieve a stored specific Attribute and cast if convertible.
Definition Attribute.cpp:105
Logical compilation of data from one snapshot (e.g.
Definition Iteration.hpp:146
Implementation for the root level of the openPMD hierarchy.
Definition Series.hpp:288
Layer to mirror structure of logical data and persistent data in file.
Definition Writable.hpp:76
Return an error string for read-only access.
Definition OutOfRangeMsg.hpp:37
Definition Error.hpp:112
Definition Attributable.hpp:107
Definition BaseRecord.hpp:52
Definition Iteration.hpp:102
Definition RecordComponent.hpp:63
Data members for Series.
Definition Series.hpp:90
Definition Attributable.hpp:62
Writable m_writable
The Writable associated with this Attributable.
Definition Attributable.hpp:81
A_MAP m_attributes
The attributes defined by this Attributable.
Definition Attributable.hpp:86
Public definitions of openPMD-api.
Definition Date.cpp:29
Access
File access mode to use during IO.
Definition Access.hpp:58
@ T
time
Definition UnitDimension.hpp:41
STL namespace.
String serialization to describe an Attributable.
Definition Attributable.hpp:367
std::string filePath() const
Reconstructs a path that can be passed to a Series constructor.
Definition Attributable.cpp:214
std::string seriesName
e.g., samples/git-samples/
Definition Attributable.hpp:369
std::string seriesExtension
e.g., dataT
Definition Attributable.hpp:370
std::vector< std::string > group
e.g., .bp, .h5, .json, ...
Definition Attributable.hpp:379
std::string openPMDPath() const
Return the path ob the object within the openPMD file.
Definition Attributable.cpp:219
Definition Attributable.hpp:251
Definition RecordComponent.hpp:550
Container Element Creation Policy.
Definition Container.hpp:52