Skip to content
Open
Show file tree
Hide file tree
Changes from 85 commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
5668d4f
Keep a list of children per Attributable
franzpoeschel Apr 30, 2026
979bd8e
Steal CustomHierarchy.hpp from old branch
franzpoeschel May 5, 2026
4fc1d45
Add DeferredInitPolicy
franzpoeschel May 6, 2026
7605170
Derive CustomHierarchy from Container<CustomHierarchy>
franzpoeschel May 6, 2026
28a25b5
Untested: reopening as custom hierarchy might work now
franzpoeschel May 6, 2026
09e6510
little test
franzpoeschel May 6, 2026
1936e2a
Emplace custom classes into backend hierarchy
franzpoeschel May 6, 2026
383d550
wip: reading
franzpoeschel May 7, 2026
9576ae6
Seem like CustomHierarchy::read() is now somewhat working lmao
franzpoeschel May 7, 2026
5eb21fa
Sync state more carefully
franzpoeschel May 8, 2026
f82e89d
Recursive reading
franzpoeschel May 8, 2026
975e276
TODO comments
franzpoeschel May 8, 2026
5b9e404
object storage for custom hierarchies
franzpoeschel May 11, 2026
c78fb8f
Fix object storage
franzpoeschel May 11, 2026
6533c29
Add preferCurrentBackpointer
franzpoeschel May 11, 2026
2ae61e8
TMP: example changes
franzpoeschel May 11, 2026
fdbd7a7
WIP customHierarchyFlush
franzpoeschel May 11, 2026
1326ef7
Guard against unset meshes/particles path
franzpoeschel May 12, 2026
e252776
Fix custom hierarchy flushing
franzpoeschel May 18, 2026
65dd454
Cleanup
franzpoeschel May 18, 2026
8fd607e
Rename
franzpoeschel May 18, 2026
7f05065
Add custom hierarchy flush at series level
franzpoeschel May 18, 2026
c351ee2
Rudimentary test, to be extended
franzpoeschel May 18, 2026
11801b0
Fix ADIOS2 path creation relative to /
franzpoeschel May 18, 2026
3483451
Reading test
franzpoeschel May 18, 2026
98886bf
Add more CustomHierarchyFlush operations
franzpoeschel May 18, 2026
ac37e0d
Add series.iterations to test
franzpoeschel May 18, 2026
43b112a
explain that nonsense lmao
franzpoeschel May 20, 2026
0237984
Untested rudimentary support for Datasets
franzpoeschel Jul 17, 2026
7856cda
Also support resizing across instances
franzpoeschel Jul 17, 2026
4b17d03
Add todo note
franzpoeschel Jul 17, 2026
49fb0c2
Support writing custom datasets
franzpoeschel Jul 20, 2026
cf78b62
Support reading custom datasets
franzpoeschel Jul 20, 2026
a862737
Fix tests / examples
franzpoeschel Jul 20, 2026
d169906
cleanup
franzpoeschel Jul 20, 2026
103890a
WIP: Merge new backend structures
franzpoeschel Jul 20, 2026
0392b1f
we are getting close
franzpoeschel Jul 21, 2026
d2f4336
Some fixes
franzpoeschel Jul 21, 2026
efef913
put group meta info into writable
franzpoeschel Jul 21, 2026
c76fe36
Fixes
franzpoeschel Jul 21, 2026
3ba8368
Add comment
franzpoeschel Aug 19, 2026
567a95f
fix deferred init for custom hierarchies that are actually datasets
franzpoeschel Aug 20, 2026
bd7803c
try supporting custom constant datasets
franzpoeschel Aug 20, 2026
ad6aa33
Add CustomHierarchy to visitHierarchy
franzpoeschel Aug 20, 2026
71d69db
Fix constant component reading
franzpoeschel Aug 21, 2026
bd84480
cleanup
franzpoeschel Aug 21, 2026
4a3226c
wip: autoread
franzpoeschel Aug 21, 2026
535b296
Add ElementAccessPolicy
franzpoeschel Aug 24, 2026
e112fd1
Read upon access
franzpoeschel Aug 24, 2026
cf12f64
Erase DeferredInitPolicy
franzpoeschel Aug 24, 2026
c4ffcce
Unify operator[] implementations
franzpoeschel Aug 24, 2026
19cf622
Fix explicit reading
franzpoeschel Aug 24, 2026
64dd2c6
clang-tidy
franzpoeschel Aug 25, 2026
7e0eebc
clang-tidy
franzpoeschel Aug 25, 2026
0143897
Fix CI
franzpoeschel Aug 25, 2026
57a04cb
Auto-read datasets as well
franzpoeschel Aug 25, 2026
f338c08
Read datasets indiscriminately
franzpoeschel Aug 25, 2026
75ef538
Take custom hierarchy test out of schema checking
franzpoeschel Aug 25, 2026
341bf76
WIP: Add datasets() and subgroups()
franzpoeschel Sep 8, 2026
280a468
Add CustomDataset class
franzpoeschel Sep 9, 2026
fcbbe0e
Enlist also CustomDataset in backend structures
franzpoeschel Sep 9, 2026
4033a75
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 9, 2026
8dc54c7
Add ::refresh()
franzpoeschel Sep 9, 2026
77ee8d6
CI: Fix sample exclusion patterns in JSON schema check
Oct 6, 2026
3648be4
WINDOWS stuff
Oct 6, 2026
b9a65e8
Revert "WINDOWS stuff"
franzpoeschel Oct 8, 2026
0583bd4
Fix access specifiers
franzpoeschel Oct 8, 2026
c5e7cf5
Dont define openpmd_export for downstream code
franzpoeschel Oct 8, 2026
70c84b9
null safety
franzpoeschel Oct 12, 2026
bf1d916
Fix range insert overloads for Container and BaseRecord
Oct 12, 2026
99d9db7
Fix use-after-free in BaseRecord insert/emplace error paths
Oct 12, 2026
3f08a17
Clarify ownership of dirty-flag reset during flushing
Oct 12, 2026
1cdef6b
Fix double traversal of custom hierarchies in HierarchyVisitor
Oct 13, 2026
7c15394
Fix post-increment/decrement contract of access_policy_iterator
Oct 13, 2026
81f5aa1
Fix documentation and internal error reporting
Oct 13, 2026
243f187
Reject casting non-empty groups into datasets
Oct 13, 2026
b6b6bc9
Add tests for optional attribute accessors
Oct 13, 2026
a5c9c9f
Remove erased objects from custom-hierarchy object storage
Oct 13, 2026
53e74db
Add review report for custom hierarchies API
Oct 13, 2026
b3a6719
Revert "Add review report for custom hierarchies API"
franzpoeschel Oct 13, 2026
668579c
cleanup
franzpoeschel Oct 13, 2026
eb13705
remove dead code
franzpoeschel Oct 13, 2026
0ebe80a
Restrict access of internal-only custom-hierarchy APIs
Oct 13, 2026
91d6f79
cleanup
franzpoeschel Oct 22, 2026
d3127a9
Custom hierarchies: Containers as transient views into a shared inter…
franzpoeschel Oct 27, 2026
d5e7f14
CI fixes
franzpoeschel Oct 28, 2026
7d4a43a
Fix use after free
franzpoeschel Oct 29, 2026
6517f02
Parameterize ChildView over constness
Oct 29, 2026
874b58a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 29, 2026
9123123
CI: silence clang-tidy optional warnings and fix test output paths
Oct 29, 2026
f68ccb9
Use stable child-cache type ids and explicit template instantiation
Oct 29, 2026
6b80416
Centralize typed child insertion in ChildrenCache
Oct 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ jobs:
find ../../../build/samples/ \
! -path '*thetaMode*' \
! -path '*/many_iterations/*' \
! -path '*/custom_hierarchy/*' \
! -name 'profiling.json' \
! -name '*config.json' \
! -path '*/file_state_lifetime/*' \
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ include(${openPMD_SOURCE_DIR}/cmake/dependencies/pybind11.cmake)
set(CORE_SOURCE
src/config.cpp
src/ChunkInfo.cpp
src/CustomHierarchy.cpp
src/Dataset.cpp
src/Datatype.cpp
src/Error.cpp
Expand Down Expand Up @@ -429,7 +430,9 @@ set(CORE_SOURCE
src/backend/Attribute.cpp
src/backend/BaseRecord.cpp
src/backend/BaseRecordComponent.cpp
src/backend/ChildrenCache.cpp
src/backend/Container.cpp
src/backend/HierarchyVisitor.cpp
src/backend/MeshRecordComponent.cpp
src/backend/PatchRecord.cpp
src/backend/PatchRecordComponent.cpp
Expand Down Expand Up @@ -869,9 +872,11 @@ if(openPMD_BUILD_TESTING)
list(APPEND ${out_list}
test/Files_Core/automatic_variable_encoding.cpp
test/Files_Core/read_nonexistent_attribute.cpp
test/Files_Core/custom_hierarchy.cpp
)
elseif(${test_name} STREQUAL "Auxiliary")
list(APPEND ${out_list}
test/Files_Auxiliary/children_cache.cpp
test/Files_Auxiliary/drain_set.cpp
)
endif()
Expand Down
257 changes: 257 additions & 0 deletions include/openPMD/CustomHierarchy.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/* Copyright 2023 Franz Poeschel
*
* This file is part of openPMD-api.
*
* openPMD-api is free software: you can redistribute it and/or modify
* it under the terms of of either the GNU General Public License or
* the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* openPMD-api is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with openPMD-api.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once

#include "openPMD/Error.hpp"
#include "openPMD/IO/AbstractIOHandler.hpp"
#include "openPMD/Mesh.hpp"
#include "openPMD/ParticleSpecies.hpp"
#include "openPMD/RecordComponent.hpp"
#include "openPMD/auxiliary/Export.hpp"
#include "openPMD/auxiliary/TypeTraits.hpp"
#include "openPMD/backend/Attributable.hpp"
#include "openPMD/backend/Container.hpp"
#include "openPMD/backend/Writable.hpp"

#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>

namespace openPMD
{
class CustomHierarchy;
namespace internal
{
struct CustomHierarchyData : public AttributableData
{
bool has_been_read = false;
};
} // namespace internal

class CustomDataset : public RecordComponent
{
template <typename, typename, typename>
friend class Container;
template <typename>
friend class ConvertibleContainer;
friend class CustomHierarchy;
friend class Attributable;

private:
CustomDataset() = default;
};

/*
* This is its own class, so the return value of asContainerOf() is also
* convsertible again.
*/
template <typename MappedType>
class ConvertibleContainer : public Container<MappedType>
{
template <typename>
friend class ConvertibleContainer;
friend class CustomHierarchy;

protected:
using Container_t = Container<MappedType>;

using Container_t::Container_t;

private:
explicit ConvertibleContainer() = default;
ConvertibleContainer(Attributable::NoInit)
: Container_t(Attributable::NoInit{})
{}

public:
[[nodiscard]] auto isDataset() const -> bool;
auto asDataset() -> CustomDataset;

auto datasets() -> ConvertibleContainer<CustomDataset>;
auto subgroups() -> ConvertibleContainer<CustomHierarchy>;
};

namespace traits
{
template <>
struct ElementAccessPolicy<CustomHierarchy>
{
static void call(CustomHierarchy &);
static void call(CustomHierarchy const &);
};

namespace detail
{
template <typename Container, typename Iterator>
void emplace_object_as_customly_managed(Container &, Iterator &it)
{
auto &writable = it->second.writable();

// If this frontend instance is the first one for its backend
// object, the reference graph (Writable::attributable -> frontend
// data -> SharedAttributableData) is rooted at it. The typed cache
// now owns that instance, so only record that the object was
// created through the custom hierarchy.
auto backpointer = writable.attributable;
auto emplaced_pointer = it->second.m_attri.get();
if (backpointer == emplaced_pointer)
{
(**it->second.m_attri).managed_as_custom_hierarchy = true;
}
}
} // namespace detail

template <>
struct GenerationPolicy<CustomHierarchy>
{
constexpr static bool is_noop = false;
template <typename Container, typename Iterator>
void operator()(Container &cont, Iterator &it)
{
detail::emplace_object_as_customly_managed<Container, Iterator>(
cont, it);
}
};

template <>
struct GenerationPolicy<CustomDataset>
{
constexpr static bool is_noop = false;
template <typename Container, typename Iterator>
void operator()(Container &cont, Iterator &it)
{
detail::emplace_object_as_customly_managed<Container, Iterator>(
cont, it);
}
};
} // namespace traits

class CustomHierarchy : public ConvertibleContainer<CustomHierarchy>
{
friend class Iteration;
friend class Container<CustomHierarchy>;
friend class Attributable;
friend struct traits::ElementAccessPolicy<CustomHierarchy>;
template <typename, typename, typename>
friend class Container;
template <typename>
friend class ConvertibleContainer;
template <typename>
friend struct internal::ChildMaterializer;

private:
using Parent_t = ConvertibleContainer<CustomHierarchy>;
using Container_t = typename Parent_t::Container_t;
using Data_t = internal::CustomHierarchyData;

std::shared_ptr<Data_t> m_customHierarchyData;

inline void setData(std::shared_ptr<Data_t> customHierarchyData)
{
m_customHierarchyData = std::move(customHierarchyData);
ConvertibleContainer<CustomHierarchy>::setData(m_customHierarchyData);
}

inline auto get() -> Data_t &
{
return *m_customHierarchyData;
}

[[nodiscard]] inline auto get() const -> Data_t const &
{
return *m_customHierarchyData;
}

protected:
CustomHierarchy();
CustomHierarchy(NoInit);
CustomHierarchy(std::shared_ptr<internal::SharedAttributableData> other);
CustomHierarchy(Attributable const &other);

void flush_internal(
internal::FlushParams const &, std::vector<std::string> currentPath);
void flush(std::string const &path, internal::FlushParams const &);

/**
* @brief Link with parent.
*
* @param w The Writable representing the parent.
*/
void linkHierarchy(Writable &w) override;

public:
CustomHierarchy(CustomHierarchy const &other) = default;
CustomHierarchy(CustomHierarchy &&other) = default;

CustomHierarchy &operator=(CustomHierarchy const &) = default;
CustomHierarchy &operator=(CustomHierarchy &&) = default;

// TODO should we automatically read upon returning / instantiating a
// CustomHierarchy object? i.e. upon Attributable::customHierarchy() and
// CustomHierarchy::operator[]().
//
// set max_recursion_depth = 0 for infinite cycling
// recursion depth includes the current object
// recursion will not continue expanding into regions that are already known
// (hence not transitively expand into unknown subregions of known regions)
auto read(size_t max_recursion_depth = 1) -> CustomHierarchy &;

void printRecursively();

private:
void printRecursively(std::string indent);

protected:
void visitHierarchyImpl(HierarchyVisitor &v, bool recursive) override;
};
namespace internal
{
template <>
struct ChildMaterializer<CustomHierarchy>
{
static auto materialize(
std::string const &,
std::shared_ptr<SharedAttributableData> const &child)
-> std::optional<CustomHierarchy>
{
return CustomHierarchy(child);
}
};

template <>
struct ChildMaterializer<CustomDataset>
{
static auto materialize(
std::string const &,
std::shared_ptr<SharedAttributableData> const &child)
-> std::optional<CustomDataset>
{
CustomHierarchy custom{child};
if (!custom.isDataset())
{
return std::nullopt;
}
return custom.asDataset();
}
};
} // namespace internal
} // namespace openPMD
20 changes: 10 additions & 10 deletions include/openPMD/Iteration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,14 @@ namespace internal

class Meshes : public Container<Mesh>
{
public:
void visitHierarchy(HierarchyVisitor &v, bool recursive) override;
protected:
void visitHierarchyImpl(HierarchyVisitor &v, bool recursive) override;
};

class Particles : public Container<ParticleSpecies>
{
public:
void visitHierarchy(HierarchyVisitor &v, bool recursive) override;
protected:
void visitHierarchyImpl(HierarchyVisitor &v, bool recursive) override;
};

/** @brief Logical compilation of data from one snapshot (e.g. a single
Expand Down Expand Up @@ -301,8 +301,6 @@ class Iteration
[[deprecated("This attribute is no longer set by the openPMD-api.")]] bool
closedByWriter() const;

void visitHierarchy(HierarchyVisitor &v, bool recursive) override;

Meshes meshes{};
Particles particles{};

Expand Down Expand Up @@ -478,6 +476,8 @@ class Iteration
protected:
void scientificDefaults_impl(
internal::WriteOrRead, OpenpmdStandard) override;

void visitHierarchyImpl(HierarchyVisitor &v, bool recursive) override;
}; // Iteration

namespace traits
Expand All @@ -486,8 +486,8 @@ namespace traits
struct GenerationPolicy<Iteration>
{
constexpr static bool is_noop = false;
template <typename Iterator>
void operator()(Iterator &it)
template <typename Container, typename Iterator>
void operator()(Container &, Iterator &it)
{
it->second.get().m_iterationIndex = it->first;
}
Expand Down Expand Up @@ -544,7 +544,7 @@ class IndexedIteration : public Iteration

class Iterations : public Container<Iteration, Iteration::IterationIndex_t>
{
public:
void visitHierarchy(HierarchyVisitor &v, bool recursive) override;
protected:
void visitHierarchyImpl(HierarchyVisitor &v, bool recursive) override;
};
} // namespace openPMD
4 changes: 2 additions & 2 deletions include/openPMD/Mesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,6 @@ class Mesh : public BaseRecord<MeshRecordComponent>
typename = std::enable_if_t<std::is_floating_point<T>::value>>
Mesh &setTimeOffset(T timeOffset);

void visitHierarchy(HierarchyVisitor &v, bool recursive) override;

private:
Mesh();

Expand All @@ -340,6 +338,8 @@ class Mesh : public BaseRecord<MeshRecordComponent>
protected:
void scientificDefaults_impl(
internal::WriteOrRead, OpenpmdStandard) override;

void visitHierarchyImpl(HierarchyVisitor &v, bool recursive) override;
}; // Mesh

template <typename T>
Expand Down
5 changes: 3 additions & 2 deletions include/openPMD/ParticlePatches.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ class ParticlePatches : public Container<PatchRecord>
size_t numPatches() const;
~ParticlePatches() override = default;

void visitHierarchy(HierarchyVisitor &v, bool recursive) override;

private:
ParticlePatches() = default;
void read();

protected:
void visitHierarchyImpl(HierarchyVisitor &v, bool recursive) override;
}; // ParticlePatches

} // namespace openPMD
Loading
Loading