Skip to content

Commit 6c87958

Browse files
committed
Support custom datasets
1 parent 191d05b commit 6c87958

File tree

6 files changed

+159
-4
lines changed

6 files changed

+159
-4
lines changed

include/openPMD/CustomHierarchy.hpp

+26-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#pragma once
2222

2323
#include "openPMD/IO/AbstractIOHandler.hpp"
24+
#include "openPMD/RecordComponent.hpp"
2425
#include "openPMD/backend/Container.hpp"
2526

2627
#include <iostream>
@@ -38,7 +39,13 @@ namespace internal
3839
std::set<std::string> paths;
3940
[[nodiscard]] bool ignore(std::string const &name) const;
4041
};
41-
using CustomHierarchyData = ContainerData<CustomHierarchy>;
42+
43+
struct CustomHierarchyData : ContainerData<CustomHierarchy>
44+
{
45+
explicit CustomHierarchyData();
46+
47+
Container<RecordComponent> m_embeddedDatasets;
48+
};
4249
} // namespace internal
4350

4451
class CustomHierarchy : public Container<CustomHierarchy>
@@ -48,15 +55,29 @@ class CustomHierarchy : public Container<CustomHierarchy>
4855

4956
private:
5057
using Container_t = Container<CustomHierarchy>;
51-
using Data_t = typename Container_t::ContainerData;
52-
static_assert(std::is_same_v<Data_t, internal::CustomHierarchyData>);
58+
using Data_t = internal::CustomHierarchyData;
59+
static_assert(std::is_base_of_v<Container_t::ContainerData, Data_t>);
60+
61+
std::shared_ptr<Data_t> m_customHierarchyData;
62+
63+
void init();
64+
65+
[[nodiscard]] Data_t &get()
66+
{
67+
return *m_customHierarchyData;
68+
}
69+
[[nodiscard]] Data_t const &get() const
70+
{
71+
return *m_customHierarchyData;
72+
}
5373

5474
protected:
5575
CustomHierarchy();
5676
CustomHierarchy(NoInit);
5777

5878
inline void setData(std::shared_ptr<Data_t> data)
5979
{
80+
m_customHierarchyData = data;
6081
Container_t::setData(std::move(data));
6182
}
6283

@@ -70,5 +91,7 @@ class CustomHierarchy : public Container<CustomHierarchy>
7091

7192
CustomHierarchy &operator=(CustomHierarchy const &) = default;
7293
CustomHierarchy &operator=(CustomHierarchy &&) = default;
94+
95+
Container<RecordComponent> datasets();
7396
};
7497
} // namespace openPMD

include/openPMD/RecordComponent.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ class RecordComponent : public BaseRecordComponent
128128
friend class DynamicMemoryView;
129129
friend class internal::RecordComponentData;
130130
friend class MeshRecordComponent;
131+
friend class CustomHierarchy;
131132

132133
public:
133134
enum class Allocation

include/openPMD/backend/Container.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ namespace internal
6161
class SeriesData;
6262
template <typename>
6363
class EraseStaleEntries;
64+
struct CustomHierarchyData;
6465

6566
template <
6667
typename T,
@@ -109,6 +110,8 @@ class Container : virtual public Attributable
109110
template <typename>
110111
friend class internal::EraseStaleEntries;
111112
friend class SeriesIterator;
113+
friend struct internal::CustomHierarchyData;
114+
friend class CustomHierarchy;
112115

113116
protected:
114117
using ContainerData = internal::ContainerData<T, T_key, T_container>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#pragma once
2+
3+
#include "openPMD/RecordComponent.hpp"
4+
5+
namespace openPMD
6+
{
7+
class EmbeddedDataset : public RecordComponent
8+
{};
9+
} // namespace openPMD

src/CustomHierarchy.cpp

+68-1
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@
2323
#include "openPMD/IO/AbstractIOHandler.hpp"
2424
#include "openPMD/IO/Access.hpp"
2525
#include "openPMD/IO/IOTask.hpp"
26+
#include "openPMD/RecordComponent.hpp"
2627
#include "openPMD/backend/Attributable.hpp"
2728

29+
#include <deque>
30+
#include <memory>
31+
2832
namespace openPMD
2933
{
3034
namespace internal
@@ -33,9 +37,25 @@ namespace internal
3337
{
3438
return paths.find(name) != paths.end();
3539
}
40+
41+
CustomHierarchyData::CustomHierarchyData()
42+
{
43+
/*
44+
* m_embeddeddatasets should point to the same instance of Attributable
45+
* Can only use a non-owning pointer in here in order to avoid shared
46+
* pointer cycles.
47+
* When handing this object out to users, we create a copy that has a
48+
* proper owning pointer (see CustomHierarchy::datasets()).
49+
*/
50+
m_embeddedDatasets.Attributable::setData(
51+
std::shared_ptr<AttributableData>(this, [](auto const *) {}));
52+
}
3653
} // namespace internal
3754

38-
CustomHierarchy::CustomHierarchy() = default;
55+
CustomHierarchy::CustomHierarchy()
56+
{
57+
setData(std::make_shared<Data_t>());
58+
}
3959
CustomHierarchy::CustomHierarchy(NoInit) : Container_t(NoInit())
4060
{}
4161

@@ -49,7 +69,10 @@ void CustomHierarchy::read(internal::MeshesParticlesPath const &mpp)
4969
Attributable::readAttributes(ReadMode::FullyReread);
5070
Parameter<Operation::LIST_PATHS> pList;
5171
IOHandler()->enqueue(IOTask(this, pList));
72+
Parameter<Operation::LIST_DATASETS> dList;
73+
IOHandler()->enqueue(IOTask(this, dList));
5274
IOHandler()->flush(internal::defaultFlushParams);
75+
std::deque<std::string> constantComponentsPushback;
5376
for (auto const &path : *pList.paths)
5477
{
5578
if (mpp.ignore(path))
@@ -61,6 +84,39 @@ void CustomHierarchy::read(internal::MeshesParticlesPath const &mpp)
6184
auto subpath = this->operator[](path);
6285
IOHandler()->enqueue(IOTask(&subpath, pOpen));
6386
subpath.read(mpp);
87+
if (subpath.size() == 0 && subpath.containsAttribute("shape") &&
88+
subpath.containsAttribute("value"))
89+
{
90+
// This is not a group, but a constant record component
91+
// Writable::~Writable() will deal with removing this from the
92+
// backend again.
93+
std::cout << "IS CONSTANT COMPONENT: " << path << std::endl;
94+
constantComponentsPushback.push_back(path);
95+
container().erase(path);
96+
}
97+
}
98+
auto &data = get();
99+
for (auto const &path : *dList.datasets)
100+
{
101+
auto &rc = data.m_embeddedDatasets[path];
102+
Parameter<Operation::OPEN_DATASET> dOpen;
103+
dOpen.name = path;
104+
IOHandler()->enqueue(IOTask(&rc, dOpen));
105+
IOHandler()->flush(internal::defaultFlushParams);
106+
rc.written() = false;
107+
rc.resetDataset(Dataset(*dOpen.dtype, *dOpen.extent));
108+
rc.written() = true;
109+
rc.read();
110+
}
111+
112+
for (auto const &path : constantComponentsPushback)
113+
{
114+
auto &rc = data.m_embeddedDatasets[path];
115+
Parameter<Operation::OPEN_PATH> pOpen;
116+
pOpen.path = path;
117+
IOHandler()->enqueue(IOTask(&rc, pOpen));
118+
rc.get().m_isConstant = true;
119+
rc.read();
64120
}
65121
}
66122

@@ -82,6 +138,17 @@ void CustomHierarchy::flush(
82138
}
83139
subpath.flush(name, flushParams);
84140
}
141+
for (auto &[name, dataset] : get().m_embeddedDatasets)
142+
{
143+
dataset.flush(name, flushParams);
144+
}
85145
flushAttributes(flushParams);
86146
}
147+
148+
Container<RecordComponent> CustomHierarchy::datasets()
149+
{
150+
Container<RecordComponent> res = get().m_embeddedDatasets;
151+
res.Attributable::setData(m_customHierarchyData);
152+
return res;
153+
}
87154
} // namespace openPMD

test/CoreTest.cpp

+52
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ TEST_CASE("custom_hierarchies", "[core]")
181181
REQUIRE(read.iterations[0].size() == 2);
182182
REQUIRE(read.iterations[0].count("custom") == 1);
183183
REQUIRE(read.iterations[0].count("no_attributes") == 1);
184+
REQUIRE(read.iterations[0]["custom"].size() == 1);
185+
REQUIRE(read.iterations[0]["custom"].count("hierarchy") == 1);
186+
REQUIRE(read.iterations[0]["custom"]["hierarchy"].size() == 0);
187+
REQUIRE(read.iterations[0]["no_attributes"].size() == 0);
184188
REQUIRE(
185189
read.iterations[0]["custom"]
186190
.getAttribute("string")
@@ -190,6 +194,54 @@ TEST_CASE("custom_hierarchies", "[core]")
190194
.getAttribute("number")
191195
.get<int>() == 3);
192196
read.close();
197+
198+
write = Series(filePath, Access::READ_WRITE);
199+
{
200+
write.iterations[0]["custom"]["hierarchy"];
201+
write.iterations[0]["custom"].datasets()["emptyDataset"].makeEmpty(
202+
Datatype::FLOAT, 3);
203+
write.iterations[0]["custom"]["hierarchy"].setAttribute("number", 3);
204+
write.iterations[0]["no_attributes"];
205+
auto iteration_level_ds =
206+
write.iterations[0].datasets()["iteration_level_dataset"];
207+
iteration_level_ds.resetDataset({Datatype::INT, {10}});
208+
std::vector<int> data(10, 5);
209+
iteration_level_ds.storeChunk(data);
210+
write.close();
211+
}
212+
213+
read = Series(filePath, Access::READ_ONLY);
214+
{
215+
REQUIRE(read.iterations[0].size() == 2);
216+
REQUIRE(read.iterations[0].count("custom") == 1);
217+
REQUIRE(read.iterations[0].count("no_attributes") == 1);
218+
REQUIRE(read.iterations[0]["custom"].size() == 1);
219+
REQUIRE(read.iterations[0]["custom"].count("hierarchy") == 1);
220+
REQUIRE(read.iterations[0]["custom"]["hierarchy"].size() == 0);
221+
REQUIRE(read.iterations[0]["no_attributes"].size() == 0);
222+
223+
REQUIRE(read.iterations[0].datasets().size() == 1);
224+
REQUIRE(read.iterations[0]["custom"].datasets().size() == 1);
225+
REQUIRE(
226+
read.iterations[0]["custom"]["hierarchy"].datasets().size() == 0);
227+
REQUIRE(read.iterations[0]["no_attributes"].datasets().size() == 0);
228+
229+
auto iteration_level_ds =
230+
read.iterations[0].datasets()["iteration_level_dataset"];
231+
REQUIRE(iteration_level_ds.getDatatype() == Datatype::INT);
232+
REQUIRE(iteration_level_ds.getExtent() == Extent{10});
233+
auto loaded_chunk = iteration_level_ds.loadChunk<int>();
234+
iteration_level_ds.seriesFlush();
235+
for (size_t i = 0; i < 10; ++i)
236+
{
237+
REQUIRE(loaded_chunk.get()[i] == 5);
238+
}
239+
240+
auto constant_dataset =
241+
read.iterations[0]["custom"].datasets()["emptyDataset"];
242+
REQUIRE(constant_dataset.getDatatype() == Datatype::FLOAT);
243+
REQUIRE(constant_dataset.getExtent() == Extent{0, 0, 0});
244+
}
193245
}
194246

195247
TEST_CASE("myPath", "[core]")

0 commit comments

Comments
 (0)