23 #include "openPMD/Error.hpp"
24 #include "openPMD/RecordComponent.hpp"
25 #include "openPMD/UnitDimension.hpp"
26 #include "openPMD/auxiliary/Variant.hpp"
27 #include "openPMD/backend/BaseRecordComponent.hpp"
28 #include "openPMD/backend/Container.hpp"
33 #include <type_traits>
48 typename T_RecordComponentData =
typename T_elem::Data_t>
51 ,
public T_RecordComponentData
53 using T_RecordComponent = T_elem;
67 typename T_BaseRecord_,
68 typename T_BaseRecordData_,
69 typename T_BaseIterator>
75 template <
typename,
typename,
typename>
78 using T_BaseRecord = T_BaseRecord_;
79 using T_BaseRecordData = T_BaseRecordData_;
80 using T_RecordComponent =
typename T_BaseRecord::T_RecordComponent;
81 using T_Container =
typename T_BaseRecord::T_Container;
83 std::remove_reference_t<decltype(*std::declval<T_BaseIterator>())>;
84 using Left = T_BaseIterator;
87 constexpr
bool operator==(Right
const &)
const noexcept
91 constexpr
bool operator!=(Right
const &)
const noexcept
100 T_BaseRecordData *m_baseRecordData =
nullptr;
102 std::optional<std::pair<std::string const, T_RecordComponent>>;
103 ScalarTuple m_scalarTuple;
104 std::variant<Left, Right> m_iterator;
109 : m_baseRecordData(&baseRecord->get())
110 , m_scalarTuple(std::make_pair(
111 RecordComponent::SCALAR, T_RecordComponent(*baseRecord)))
112 , m_iterator(Right())
115 : m_baseRecordData(&baseRecord->get())
116 , m_scalarTuple(std::make_pair(
117 RecordComponent::SCALAR, T_RecordComponent(*baseRecord)))
118 , m_iterator(std::move(iterator))
140 typename SFINAE = std::enable_if_t<
141 !std::is_same_v<T_BaseRecord, typename Other::T_BaseRecord>>>
143 : m_baseRecordData(other.m_baseRecordData)
145 other.m_scalarTuple.has_value()
146 ? ScalarTuple(std::make_pair(
149 other.m_scalarTuple.value().second)))
150 : ScalarTuple(std::nullopt))
151 , m_iterator(std::visit(
152 auxiliary::overloaded{
153 [](typename Other::Left const &left) {
156 return std::variant<Left, Right>(left);
158 [](
typename Other::Right
const &) {
159 return std::variant<Left, Right>(Right());
164 ScalarIterator &operator++()
167 auxiliary::overloaded{
168 [](Left &left) { ++left; },
170 m_iterator = m_baseRecordData->m_container.end();
176 T_Value *operator->()
179 auxiliary::overloaded{
180 [](Left &left) -> T_Value * {
return left.operator->(); },
181 [
this](Right &) -> T_Value * {
188 return &m_scalarTuple.value();
195 return *operator->();
198 bool operator==(ScalarIterator
const &other)
const
200 return this->m_iterator == other.m_iterator;
203 bool operator!=(ScalarIterator
const &other)
const
205 return !operator==(other);
220 template <
typename T_elem>
226 using T_RecordComponent = T_elem;
236 template <
typename,
typename>
238 template <
typename,
typename,
typename>
243 std::shared_ptr<Data_t> m_baseRecordData;
247 "Internal error: Scalar components cannot have generation policies.");
249 inline Data_t const &get()
const
251 return *m_baseRecordData;
256 return *m_baseRecordData;
262 inline void setData(std::shared_ptr<Data_t> data)
264 m_baseRecordData = std::move(data);
265 T_Container::setData(m_baseRecordData);
266 T_RecordComponent::setData(m_baseRecordData);
284 typename T_Container::InternalContainer::iterator>;
288 typename T_Container::InternalContainer::const_iterator>;
292 typename T_Container::InternalContainer::reverse_iterator>;
296 typename T_Container::InternalContainer::const_reverse_iterator>;
299 template <
typename... Arg>
302 return iterator{
this, std::forward<Arg>(arg)...};
304 template <
typename... Arg>
309 template <
typename... Arg>
314 template <
typename... Arg>
323 if (get().m_datasetDefined)
325 return makeIterator();
329 return makeIterator(T_Container::begin());
335 if (get().m_datasetDefined)
337 return makeIterator();
341 return makeIterator(T_Container::begin());
347 if (get().m_datasetDefined)
349 return makeIterator();
353 return makeIterator(T_Container::cbegin());
359 return makeIterator(T_Container::end());
364 return makeIterator(T_Container::end());
369 return makeIterator(T_Container::cend());
374 if (get().m_datasetDefined)
376 return makeReverseIterator();
380 return makeReverseIterator(this->container().rbegin());
386 if (get().m_datasetDefined)
388 return makeReverseIterator();
392 return makeReverseIterator(this->container().rbegin());
398 if (get().m_datasetDefined)
400 return makeReverseIterator();
404 return makeReverseIterator(this->container().crbegin());
410 return makeReverseIterator(this->container().rend());
415 return makeReverseIterator(this->container().rend());
420 return makeReverseIterator(this->container().crend());
425 mapped_type &operator[](key_type
const &key);
426 mapped_type &operator[](key_type &&key);
427 mapped_type &at(key_type
const &key);
428 mapped_type
const &at(key_type
const &key)
const;
429 size_type erase(key_type
const &key);
431 bool empty()
const noexcept;
434 size_type count(key_type
const &key)
const;
435 size_type size()
const;
437 std::pair<iterator, bool> insert(value_type
const &value);
438 std::pair<iterator, bool> insert(value_type &&value);
441 template <
class InputIt>
442 void insert(InputIt first, InputIt last);
443 void insert(std::initializer_list<value_type> ilist);
445 bool contains(key_type
const &key)
const;
446 template <
class... Args>
447 auto emplace(Args &&...args) -> std::pair<iterator, bool>;
474 if (!T_Container::empty())
477 "A scalar component can not be contained at the same time as "
478 "one or more regular components.");
480 T_RecordComponent::setDatasetDefined(data);
504 template <
typename T_elem,
typename T_RecordComponentData>
505 BaseRecordData<T_elem, T_RecordComponentData>::BaseRecordData()
508 impl.setData({
this, [](
auto const *) {}});
511 std::array<double, 7>{{0., 0., 0., 0., 0., 0., 0.}});
515 template <
typename T_elem>
516 BaseRecord<T_elem>::BaseRecord()
517 : T_Container(Attributable::NoInit())
518 , T_RecordComponent(Attributable::NoInit())
520 setData(std::make_shared<Data_t>());
523 template <
typename T_elem>
524 auto BaseRecord<T_elem>::operator[](key_type
const &key) -> mapped_type &
526 auto it = this->find(key);
527 if (it != this->end())
530 auxiliary::overloaded{
531 [](
typename iterator::Left &l) -> mapped_type & {
534 [
this](
typename iterator::Right &) -> mapped_type & {
539 return static_cast<mapped_type &
>(*this);
545 bool const keyScalar = (key == RecordComponent::SCALAR);
546 if ((keyScalar && !Container<T_elem>::empty() && !scalar()) ||
547 (scalar() && !keyScalar))
548 throw error::WrongAPIUsage(
549 "A scalar component can not be contained at the same time as "
550 "one or more regular components.");
557 T_RecordComponent::get();
559 mapped_type &ret = keyScalar ?
static_cast<mapped_type &
>(*this)
560 : T_Container::operator[](key);
565 template <
typename T_elem>
566 auto BaseRecord<T_elem>::operator[](key_type &&key) -> mapped_type &
568 auto it = this->find(key);
569 if (it != this->end())
572 auxiliary::overloaded{
573 [](
typename iterator::Left &l) -> mapped_type & {
576 [
this](
typename iterator::Right &) -> mapped_type & {
581 return static_cast<mapped_type &
>(*this);
587 bool const keyScalar = (key == RecordComponent::SCALAR);
588 if ((keyScalar && !Container<T_elem>::empty() && !scalar()) ||
589 (scalar() && !keyScalar))
590 throw error::WrongAPIUsage(
591 "A scalar component can not be contained at the same time as "
592 "one or more regular components.");
599 T_RecordComponent::get();
601 mapped_type &ret = keyScalar ?
static_cast<mapped_type &
>(*this)
602 : T_Container::operator[](std::move(key));
607 template <
typename T_elem>
608 auto BaseRecord<T_elem>::at(key_type
const &key) -> mapped_type &
610 return const_cast<mapped_type &
>(
611 static_cast<BaseRecord<T_elem>
const *
>(
this)->at(key));
614 template <
typename T_elem>
615 auto BaseRecord<T_elem>::at(key_type
const &key)
const -> mapped_type
const &
617 bool const keyScalar = (key == RecordComponent::SCALAR);
620 if (!get().m_datasetDefined)
622 throw std::out_of_range(
623 "[at()] Requested scalar entry from non-scalar record.");
625 return static_cast<mapped_type
const &
>(*this);
629 return T_Container::at(key);
633 template <
typename T_elem>
634 auto BaseRecord<T_elem>::erase(key_type
const &key) -> size_type
636 bool const keyScalar = (key == RecordComponent::SCALAR);
638 if (!keyScalar || (keyScalar && this->at(key).constant()))
642 res = this->datasetDefined() ? 1 : 0;
648 this->written() =
false;
649 this->writable().abstractFilePosition.reset();
650 this->get().m_datasetDefined =
false;
655 template <
typename T_elem>
656 auto BaseRecord<T_elem>::erase(iterator it) -> iterator
659 auxiliary::overloaded{
660 [
this](
typename iterator::Left &left) {
661 return makeIterator(T_Container::erase(left));
663 [
this](
typename iterator::Right &) {
670 template <
typename T_elem>
671 auto BaseRecord<T_elem>::empty() const noexcept ->
bool
673 return !scalar() && T_Container::empty();
676 template <
typename T_elem>
677 auto BaseRecord<T_elem>::find(key_type
const &key) -> iterator
680 if (key == RecordComponent::SCALAR && get().m_datasetDefined)
682 if (r.m_datasetDefined)
693 return makeIterator(r.m_container.find(key));
697 template <
typename T_elem>
698 auto BaseRecord<T_elem>::find(key_type
const &key)
const -> const_iterator
701 if (key == RecordComponent::SCALAR && get().m_datasetDefined)
703 if (r.m_datasetDefined)
714 return makeIterator(r.m_container.find(key));
718 template <
typename T_elem>
719 auto BaseRecord<T_elem>::count(key_type
const &key)
const -> size_type
721 if (key == RecordComponent::SCALAR)
723 return get().m_datasetDefined ? 1 : 0;
727 return T_Container::count(key);
731 template <
typename T_elem>
732 auto BaseRecord<T_elem>::size() const -> size_type
740 return T_Container::size();
744 template <
typename T_elem>
745 auto BaseRecord<T_elem>::clear() ->
void
747 if (Access::READ_ONLY == this->IOHandler()->m_frontendAccess)
748 throw std::runtime_error(
749 "Can not clear a container in a read-only Series.");
756 T_Container::clear_unchecked();
762 constexpr
char const *
const NO_SCALAR_INSERT =
763 "[BaseRecord] emplace()/insert()/swap() API invalid for scalar "
764 "records. Use the Record directly as a RecordComponent.";
766 template <
typename BaseRecord>
767 void verifyNonscalar(BaseRecord *
self)
771 throw error::WrongAPIUsage(NO_SCALAR_INSERT);
776 template <
typename T_elem>
777 auto BaseRecord<T_elem>::insert(value_type
const &value)
778 -> std::pair<iterator, bool>
780 detail::verifyNonscalar(
this);
781 auto res = this->container().insert(value);
782 if (res.first->first == RecordComponent::SCALAR)
784 this->container().erase(res.first);
785 throw error::WrongAPIUsage(detail::NO_SCALAR_INSERT);
787 return {makeIterator(std::move(res.first)), res.second};
790 template <
typename T_elem>
791 auto BaseRecord<T_elem>::insert(value_type &&value) -> std::pair<iterator, bool>
793 detail::verifyNonscalar(
this);
794 auto res = this->container().insert(std::move(value));
795 if (res.first->first == RecordComponent::SCALAR)
797 this->container().erase(res.first);
798 throw error::WrongAPIUsage(detail::NO_SCALAR_INSERT);
800 return {makeIterator(std::move(res.first)), res.second};
803 template <
typename T_elem>
804 auto BaseRecord<T_elem>::insert(const_iterator hint, value_type
const &value)
807 detail::verifyNonscalar(
this);
808 auto base_hint = std::visit(
809 auxiliary::overloaded{
810 [](
typename const_iterator::Left left) {
return left; },
811 [
this](
typename const_iterator::Right) {
812 return static_cast<BaseRecord<T_elem>
const *
>(
this)
817 auto res = this->container().insert(base_hint, value);
818 if (res->first == RecordComponent::SCALAR)
820 this->container().erase(res);
821 throw error::WrongAPIUsage(detail::NO_SCALAR_INSERT);
823 return makeIterator(res);
826 template <
typename T_elem>
827 auto BaseRecord<T_elem>::insert(const_iterator hint, value_type &&value)
830 detail::verifyNonscalar(
this);
831 auto base_hint = std::visit(
832 auxiliary::overloaded{
833 [](
typename const_iterator::Left left) {
return left; },
834 [
this](
typename const_iterator::Right) {
835 return static_cast<BaseRecord<T_elem>
const *
>(
this)
840 auto res = this->container().insert(base_hint, std::move(value));
841 if (res->first == RecordComponent::SCALAR)
843 this->container().erase(res);
844 throw error::WrongAPIUsage(detail::NO_SCALAR_INSERT);
846 return makeIterator(res);
849 template <
typename T_elem>
850 template <
typename InputIt>
851 auto BaseRecord<T_elem>::insert(InputIt first, InputIt last) ->
void
853 detail::verifyNonscalar(
this);
854 this->container().insert(first, last);
869 template <
typename T_elem>
870 auto BaseRecord<T_elem>::insert(std::initializer_list<value_type> ilist) ->
void
872 detail::verifyNonscalar(
this);
873 this->container().insert(std::move(ilist));
888 template <
typename T_elem>
889 auto BaseRecord<T_elem>::swap(BaseRecord &other) ->
void
891 detail::verifyNonscalar(
this);
892 detail::verifyNonscalar(&other);
893 this->container().swap(other.container());
896 template <
typename T_elem>
897 auto BaseRecord<T_elem>::contains(key_type
const &key)
const ->
bool
901 return key == RecordComponent::SCALAR;
905 return T_Container::contains(key);
909 template <
typename T_elem>
910 template <
typename... Args>
911 auto BaseRecord<T_elem>::emplace(Args &&...args) -> std::pair<iterator, bool>
913 detail::verifyNonscalar(
this);
914 auto res = this->container().emplace(std::forward<Args>(args)...);
915 if (res.first->first == RecordComponent::SCALAR)
917 this->container().erase(res.first);
918 throw error::WrongAPIUsage(detail::NO_SCALAR_INSERT);
920 return {makeIterator(std::move(res.first)), res.second};
923 template <
typename T_elem>
926 return this->getAttribute(
"unitDimension")
927 .template get<std::array<double, 7>>();
930 template <
typename T_elem>
933 return this->datasetDefined();
936 template <
typename T_elem>
942 aRead.name =
"unitDimension";
943 this->IOHandler()->enqueue(
IOTask(
this, aRead));
944 this->IOHandler()->flush(internal::defaultFlushParams);
948 this->setAttribute(
"unitDimension", val.value());
950 throw std::runtime_error(
951 "Unexpected Attribute datatype for 'unitDimension'");
953 aRead.name =
"timeOffset";
954 this->IOHandler()->enqueue(
IOTask(
this, aRead));
955 this->IOHandler()->flush(internal::defaultFlushParams);
956 if (*aRead.dtype == DT::FLOAT)
959 else if (*aRead.dtype == DT::DOUBLE)
961 "timeOffset",
Attribute(*aRead.resource).
get<
double>());
965 this->setAttribute(
"timeOffset", val.value());
967 throw std::runtime_error(
968 "Unexpected Attribute datatype for 'timeOffset'");
971 template <
typename T_elem>
972 inline void BaseRecord<T_elem>::flush(
973 std::string
const &name, internal::FlushParams
const &flushParams)
975 if (!this->written() && this->empty() && !this->datasetDefined())
976 throw std::runtime_error(
977 "A Record can not be written without any contained "
978 "RecordComponents: " +
985 if (scalar() && !T_Container::empty())
987 throw error::WrongAPIUsage(
988 "A scalar component can not be contained at the same time as "
989 "one or more regular components.");
992 this->flush_impl(name, flushParams);
993 if (flushParams.flushLevel != FlushLevel::SkeletonOnly)
995 this->setDirty(
false);
1001 template <
typename T_elem>
1002 void BaseRecord<T_elem>::eraseScalar()
1004 if (this->written())
1006 Parameter<Operation::DELETE_DATASET> dDelete;
1008 this->IOHandler()->enqueue(IOTask(
this, dDelete));
1009 this->IOHandler()->flush(internal::defaultFlushParams);
1011 auto &data = T_RecordComponent::get();
1013 this->writable().abstractFilePosition.reset();
Layer to manage storage of attributes associated with file objects.
Definition: Attributable.hpp:101
bool setAttribute(std::string const &key, T value)
Populate Attribute of provided name with provided value.
Definition: Attributable.hpp:455
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
std::optional< U > getOptional() const
Retrieve a stored specific Attribute and cast if convertible.
Definition: Attribute.hpp:340
Base class for any type of record (e.g.
Definition: BaseRecord.hpp:224
bool scalar() const
Returns true if this record only contains a single component.
Definition: BaseRecord.hpp:931
std::array< double, 7 > unitDimension() const
Return the physical dimension (quantity) of a record.
Definition: BaseRecord.hpp:924
Map-like container that enforces openPMD requirements and handles IO.
Definition: Container.hpp:104
size_type erase(key_type const &key)
Remove a single element from the container and (if written) from disk.
Definition: Container.hpp:396
Self-contained description of a single IO operation.
Definition: IOTask.hpp:670
Logical compilation of data from one snapshot (e.g.
Definition: Iteration.hpp:127
Container for N-dimensional, homogeneous Records.
Definition: Mesh.hpp:41
Definition: ParticleSpecies.hpp:34
Definition: PatchRecord.hpp:32
Definition: RecordComponent.hpp:120
Definition: Record.hpp:33
The API was used in an illegal way.
Definition: Error.hpp:64
Definition: Attributable.hpp:57
Definition: BaseRecordComponent.hpp:39
Definition: BaseRecord.hpp:52
Definition: Container.hpp:71
Definition: BaseRecord.hpp:71
ScalarIterator(Other const &other)
Auto-convert normal to const iterator.
Definition: BaseRecord.hpp:142
Public definitions of openPMD-api.
Definition: Date.cpp:29
Datatype
Concrete datatype of an object available at runtime.
Definition: Datatype.hpp:51
Definition: IOTask.hpp:555
Parameters recursively passed through the openPMD hierarchy when flushing.
Definition: AbstractIOHandler.hpp:86
Container Element Creation Policy.
Definition: Container.hpp:52