2020 */
2121
2222#include " openPMD/CustomHierarchy.hpp"
23+ #include " openPMD/Error.hpp"
2324#include " openPMD/IO/AbstractIOHandler.hpp"
2425#include " openPMD/IO/Access.hpp"
2526#include " openPMD/IO/IOTask.hpp"
3435#include < initializer_list>
3536#include < iterator>
3637#include < memory>
38+ #include < optional>
3739#include < regex>
3840#include < sstream>
3941
@@ -142,6 +144,11 @@ namespace internal
142144 }
143145
144146 CustomHierarchyData::CustomHierarchyData ()
147+ {
148+ syncAttributables ();
149+ }
150+
151+ void CustomHierarchyData::syncAttributables ()
145152 {
146153 /*
147154 * m_embeddeddatasets and its friends should point to the same instance
@@ -150,10 +157,8 @@ namespace internal
150157 for (auto p : std::initializer_list<Attributable *>{
151158 &m_embeddedDatasets, &m_embeddedMeshes, &m_embeddedParticles})
152159 {
153- static_cast <std::shared_ptr<internal::SharedAttributableData> &>(
154- *p->m_attri ) =
155- static_cast <
156- std::shared_ptr<internal::SharedAttributableData> &>(*this );
160+ p->m_attri ->asSharedPtrOfAttributable () =
161+ this ->asSharedPtrOfAttributable ();
157162 }
158163 }
159164} // namespace internal
@@ -310,20 +315,6 @@ void CustomHierarchy::read(
310315 constantComponentsPushback.push_back (path);
311316 container ().erase (path);
312317 }
313- else
314- {
315- if (path == defaultMeshesPath)
316- {
317- synchronizeContainers (
318- subpath.get ().m_embeddedMeshes , meshes);
319- }
320- // no else, they might be the same
321- if (path == defaultParticlesPath)
322- {
323- synchronizeContainers (
324- subpath.get ().m_embeddedParticles , particles);
325- }
326- }
327318 break ;
328319 }
329320 case internal::ContainedType::Mesh: {
@@ -401,33 +392,6 @@ void CustomHierarchy::read(
401392 }
402393}
403394
404- template <typename T>
405- void CustomHierarchy::synchronizeContainers (
406- Container<T> &target, Container<T> &source)
407- {
408- if (target.m_containerData .get () == source.m_containerData .get ())
409- {
410- return ;
411- }
412- auto &target_container = target.container ();
413- for (auto &pair : source.container ())
414- {
415- pair.second .linkHierarchy (target.writable ());
416- target_container.emplace (std::move (pair));
417- }
418- source.container ().clear ();
419- auto &target_attributes = target.get ().m_attributes ;
420- for (auto &pair : source.get ().m_attributes )
421- {
422- target_attributes.emplace (std::move (pair));
423- }
424- source.get ().m_attributes .clear ();
425- source.setData (target.m_containerData );
426- // We need to do this since we redirect the Attributable pointers for some
427- // members:
428- source.Attributable ::setData (target.m_attri );
429- }
430-
431395void CustomHierarchy::flush_internal (
432396 internal::FlushParams const &flushParams,
433397 internal::MeshesParticlesPath &mpp,
@@ -445,16 +409,12 @@ void CustomHierarchy::flush_internal(
445409 {
446410 if (!meshes.empty ())
447411 {
448- auto &defaultMeshes =
449- (*this )[defaultMeshesPath].asContainerOf <Mesh>();
450- synchronizeContainers (defaultMeshes, meshes);
412+ (*this )[defaultMeshesPath];
451413 }
452414
453415 if (!particles.empty ())
454416 {
455- auto &defaultParticles =
456- (*this )[defaultParticlesPath].asContainerOf <ParticleSpecies>();
457- synchronizeContainers (defaultParticles, particles);
417+ (*this )[defaultParticlesPath];
458418 }
459419
460420 flushAttributes (flushParams);
@@ -585,6 +545,16 @@ bool CustomHierarchy::dirtyRecursive() const
585545 return false ;
586546}
587547
548+ auto CustomHierarchy::operator [](key_type &&key) -> mapped_type &
549+ {
550+ return bracketOperatorImpl (std::move (key));
551+ }
552+
553+ auto CustomHierarchy::operator [](key_type const &key) -> mapped_type &
554+ {
555+ return bracketOperatorImpl (key);
556+ }
557+
588558Container<RecordComponent> &CustomHierarchy::datasets ()
589559{
590560 return get ().m_embeddedDatasets ;
@@ -626,4 +596,95 @@ template auto CustomHierarchy::asContainerOf<RecordComponent>()
626596template auto CustomHierarchy::asContainerOf<Mesh>() -> Container<Mesh> &;
627597template auto CustomHierarchy::asContainerOf<ParticleSpecies>()
628598 -> Container<ParticleSpecies> &;
599+
600+ template <typename KeyType>
601+ auto CustomHierarchy::bracketOperatorImpl (KeyType &&provided_key)
602+ -> mapped_type &
603+ {
604+ auto &cont = container ();
605+ auto find_special_key =
606+ [&cont, &provided_key, this ](
607+ char const *special_key,
608+ auto &alias,
609+ auto &&embeddedAccessor) -> std::optional<mapped_type *> {
610+ if (provided_key == special_key)
611+ {
612+ if (auto it = cont.find (provided_key); it != cont.end ())
613+ {
614+ if (it->second .m_attri ->get () != alias.m_attri ->get () ||
615+ embeddedAccessor (it->second )->m_containerData .get () !=
616+ alias.m_containerData .get ())
617+ {
618+ /*
619+ * This might happen if a user first creates a custom group
620+ * "fields" and sets the default meshes path as "fields"
621+ * only later.
622+ * If the CustomHierarchy::meshes alias carries no data yet,
623+ * we can just redirect it to that group now.
624+ * Otherwise, we need to fail.
625+ */
626+ if (alias.empty () && alias.attributes ().empty ())
627+ {
628+ alias.m_containerData =
629+ embeddedAccessor (it->second )->m_containerData ;
630+ alias.m_attri ->asSharedPtrOfAttributable () =
631+ it->second .m_attri ->asSharedPtrOfAttributable ();
632+ return &it->second ;
633+ }
634+ throw error::WrongAPIUsage (
635+ " Found a group '" + provided_key + " ' at path '" +
636+ myPath ().printGroup () +
637+ " ' which is not synchronous with mesh/particles alias "
638+ " despite '" +
639+ special_key +
640+ " ' being the default meshes/particles path. This can "
641+ " have happened because setting default "
642+ " meshes/particles path too late (after first flush). "
643+ " If that's not the case, this is likely an internal "
644+ " bug." );
645+ }
646+ return &it->second ;
647+ }
648+ else
649+ {
650+ auto *res =
651+ &Container::operator [](std::forward<KeyType>(provided_key));
652+ embeddedAccessor (*res)->m_containerData = alias.m_containerData ;
653+ res->m_attri ->asSharedPtrOfAttributable () =
654+ alias.m_attri ->asSharedPtrOfAttributable ();
655+ res->m_customHierarchyData ->syncAttributables ();
656+ return res;
657+ }
658+ }
659+ else
660+ {
661+ return std::nullopt ;
662+ }
663+ };
664+ if (auto res = find_special_key (
665+ defaultMeshesPath,
666+ meshes,
667+ [](auto &group) {
668+ return &group.m_customHierarchyData ->m_embeddedMeshes ;
669+ });
670+ res.has_value ())
671+ {
672+ return **res;
673+ }
674+ if (auto res = find_special_key (
675+ defaultParticlesPath,
676+ particles,
677+ [](auto &group) {
678+ return &group.m_customHierarchyData ->m_embeddedParticles ;
679+ });
680+ res.has_value ())
681+ {
682+ return **res;
683+ }
684+ else
685+ {
686+ return (*this ).Container ::operator [](
687+ std::forward<KeyType>(provided_key));
688+ }
689+ }
629690} // namespace openPMD
0 commit comments