Skip to content

Commit 196a5bf

Browse files
committed
Initialize Series attributes and datasets from template
1 parent 7491a9b commit 196a5bf

File tree

6 files changed

+255
-2
lines changed

6 files changed

+255
-2
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ set(CORE_SOURCE
410410
src/auxiliary/JSON.cpp
411411
src/auxiliary/JSONMatcher.cpp
412412
src/auxiliary/Mpi.cpp
413+
src/auxiliary/TemplateFile.cpp
413414
src/backend/Attributable.cpp
414415
src/backend/BaseRecordComponent.cpp
415416
src/backend/MeshRecordComponent.cpp

include/openPMD/Iteration.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,15 @@ namespace internal
134134
* Otherwise empty.
135135
*/
136136
std::optional<DeferredParseAccess> m_deferredParseAccess{};
137+
138+
enum TernaryBool
139+
{
140+
Undefined,
141+
True,
142+
False
143+
};
144+
TernaryBool hasMeshes = TernaryBool::Undefined;
145+
TernaryBool hasParticles = TernaryBool::Undefined;
137146
};
138147
} // namespace internal
139148
/** @brief Logical compilation of data from one snapshot (e.g. a single
@@ -271,6 +280,9 @@ class Iteration : public Attributable
271280
Container<Mesh> meshes{};
272281
Container<ParticleSpecies> particles{}; // particleSpecies?
273282

283+
bool hasMeshes() const;
284+
bool hasParticles() const;
285+
274286
virtual ~Iteration() = default;
275287

276288
private:
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#pragma once
2+
3+
#include "openPMD/Series.hpp"
4+
5+
namespace openPMD::auxiliary
6+
{
7+
// @todo replace uint64_t with proper type after merging #1285
8+
Series &initializeFromTemplate(
9+
Series &initializeMe, Series const &fromTemplate, uint64_t iteration);
10+
} // namespace openPMD::auxiliary

src/Iteration.cpp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,50 @@ namespace openPMD
4747
using internal::CloseStatus;
4848
using internal::DeferredParseAccess;
4949

50+
bool Iteration::hasMeshes() const
51+
{
52+
/*
53+
* Currently defined at the Series level, but might be defined at the
54+
* Iteration level in next standard iterations.
55+
* Hence an Iteration:: method.
56+
*/
57+
58+
switch (get().hasMeshes)
59+
{
60+
case internal::IterationData::TernaryBool::True:
61+
return true;
62+
case internal::IterationData::TernaryBool::False:
63+
return false;
64+
case internal::IterationData::TernaryBool::Undefined: {
65+
Series s = retrieveSeries();
66+
return !meshes.empty() || s.containsAttribute("meshesPath");
67+
};
68+
}
69+
throw std::runtime_error("Unreachable!");
70+
}
71+
72+
bool Iteration::hasParticles() const
73+
{
74+
/*
75+
* Currently defined at the Series level, but might be defined at the
76+
* Iteration level in next standard iterations.
77+
* Hence an Iteration:: method.
78+
*/
79+
80+
switch (get().hasParticles)
81+
{
82+
case internal::IterationData::TernaryBool::True:
83+
return true;
84+
case internal::IterationData::TernaryBool::False:
85+
return false;
86+
case internal::IterationData::TernaryBool::Undefined: {
87+
Series s = retrieveSeries();
88+
return !particles.empty() || s.containsAttribute("particlesPath");
89+
};
90+
}
91+
throw std::runtime_error("Unreachable!");
92+
}
93+
5094
Iteration::Iteration() : Attributable(NoInit())
5195
{
5296
setData(std::make_shared<Data_t>());
@@ -354,7 +398,7 @@ void Iteration::flush(internal::FlushParams const &flushParams)
354398
* meshesPath and particlesPath are stored there */
355399
Series s = retrieveSeries();
356400

357-
if (!meshes.empty() || s.containsAttribute("meshesPath"))
401+
if (hasMeshes())
358402
{
359403
if (!s.containsAttribute("meshesPath"))
360404
{
@@ -370,7 +414,7 @@ void Iteration::flush(internal::FlushParams const &flushParams)
370414
meshes.setDirty(false);
371415
}
372416

373-
if (!particles.empty() || s.containsAttribute("particlesPath"))
417+
if (hasParticles())
374418
{
375419
if (!s.containsAttribute("particlesPath"))
376420
{
@@ -562,6 +606,12 @@ void Iteration::read_impl(std::string const &groupPath)
562606
hasMeshes = s.containsAttribute("meshesPath");
563607
hasParticles = s.containsAttribute("particlesPath");
564608
}
609+
{
610+
using TB = internal::IterationData::TernaryBool;
611+
auto &data = get();
612+
data.hasMeshes = hasMeshes ? TB::True : TB::False;
613+
data.hasParticles = hasParticles ? TB::True : TB::False;
614+
}
565615

566616
if (hasMeshes)
567617
{

src/auxiliary/TemplateFile.cpp

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
#include "openPMD/auxiliary/TemplateFile.hpp"
2+
#include "openPMD/DatatypeHelpers.hpp"
3+
4+
#include <iostream>
5+
6+
namespace openPMD::auxiliary
7+
{
8+
namespace
9+
{
10+
// Some forward declarations
11+
template <typename T>
12+
void initializeFromTemplate(
13+
Container<T> &initializeMe, Container<T> const &fromTemplate);
14+
15+
struct SetAttribute
16+
{
17+
template <typename T>
18+
static void
19+
call(Attributable &object, std::string const &name, Attribute attr)
20+
{
21+
object.setAttribute(name, attr.get<T>());
22+
}
23+
24+
template <unsigned n>
25+
static void call(Attributable &, std::string const &name, Attribute)
26+
{
27+
std::cerr << "Unknown datatype for template attribute '" << name
28+
<< "'. Will skip it." << std::endl;
29+
}
30+
};
31+
32+
void copyAttributes(
33+
Attributable &target,
34+
Attributable const &source,
35+
std::vector<std::string> ignore = {})
36+
{
37+
auto shouldBeIgnored = [&ignore](std::string const &attrName) {
38+
// `ignore` is empty by default and normally has only a handful of
39+
// entries otherwise.
40+
// So just use linear search.
41+
for (auto const &ignored : ignore)
42+
{
43+
if (attrName == ignored)
44+
{
45+
return true;
46+
}
47+
}
48+
return false;
49+
};
50+
51+
for (auto const &attrName : source.attributes())
52+
{
53+
if (shouldBeIgnored(attrName))
54+
{
55+
continue;
56+
}
57+
auto attr = source.getAttribute(attrName);
58+
auto dtype = attr.dtype;
59+
switchType<SetAttribute>(dtype, target, attrName, std::move(attr));
60+
}
61+
}
62+
63+
void initializeFromTemplate(
64+
BaseRecordComponent &initializeMe,
65+
BaseRecordComponent const &fromTemplate)
66+
{
67+
copyAttributes(initializeMe, fromTemplate);
68+
}
69+
70+
void initializeFromTemplate(
71+
RecordComponent &initializeMe, RecordComponent const &fromTemplate)
72+
{
73+
if (fromTemplate.getDatatype() != Datatype::UNDEFINED)
74+
{
75+
initializeMe.resetDataset(
76+
Dataset{fromTemplate.getDatatype(), fromTemplate.getExtent()});
77+
}
78+
initializeFromTemplate(
79+
static_cast<BaseRecordComponent &>(initializeMe),
80+
static_cast<BaseRecordComponent const &>(fromTemplate));
81+
}
82+
83+
void initializeFromTemplate(
84+
PatchRecordComponent &initializeMe,
85+
PatchRecordComponent const &fromTemplate)
86+
{
87+
if (fromTemplate.getDatatype() != Datatype::UNDEFINED)
88+
{
89+
initializeMe.resetDataset(
90+
Dataset{fromTemplate.getDatatype(), fromTemplate.getExtent()});
91+
}
92+
initializeFromTemplate(
93+
static_cast<BaseRecordComponent &>(initializeMe),
94+
static_cast<BaseRecordComponent const &>(fromTemplate));
95+
}
96+
97+
void initializeFromTemplate(
98+
ParticleSpecies &initializeMe, ParticleSpecies const &fromTemplate)
99+
{
100+
if (!fromTemplate.particlePatches.empty())
101+
{
102+
initializeFromTemplate(
103+
static_cast<Container<PatchRecord> &>(
104+
initializeMe.particlePatches),
105+
static_cast<Container<PatchRecord> const &>(
106+
fromTemplate.particlePatches));
107+
}
108+
initializeFromTemplate(
109+
static_cast<Container<Record> &>(initializeMe),
110+
static_cast<Container<Record> const &>(fromTemplate));
111+
}
112+
113+
template <typename T>
114+
void initializeFromTemplate(
115+
Container<T> &initializeMe, Container<T> const &fromTemplate)
116+
{
117+
copyAttributes(initializeMe, fromTemplate);
118+
for (auto const &pair : fromTemplate)
119+
{
120+
initializeFromTemplate(initializeMe[pair.first], pair.second);
121+
}
122+
}
123+
124+
void initializeFromTemplate(
125+
Iteration &initializeMe, Iteration const &fromTemplate)
126+
{
127+
copyAttributes(initializeMe, fromTemplate, {"snapshot"});
128+
if (fromTemplate.hasMeshes())
129+
{
130+
initializeFromTemplate(initializeMe.meshes, fromTemplate.meshes);
131+
}
132+
if (fromTemplate.hasParticles())
133+
{
134+
initializeFromTemplate(
135+
initializeMe.particles, fromTemplate.particles);
136+
}
137+
}
138+
} // namespace
139+
140+
Series &initializeFromTemplate(
141+
Series &initializeMe, Series const &fromTemplate, uint64_t iteration)
142+
{
143+
if (!initializeMe.containsAttribute("from_template"))
144+
{
145+
copyAttributes(
146+
initializeMe,
147+
fromTemplate,
148+
{"basePath", "iterationEncoding", "iterationFormat", "openPMD"});
149+
initializeMe.setAttribute("from_template", fromTemplate.name());
150+
}
151+
152+
uint64_t sourceIteration = iteration;
153+
if (!fromTemplate.iterations.contains(sourceIteration))
154+
{
155+
if (fromTemplate.iterations.empty())
156+
{
157+
std::cerr << "Template file has no iterations, will only fill in "
158+
"global attributes."
159+
<< std::endl;
160+
return initializeMe;
161+
}
162+
else
163+
{
164+
sourceIteration = fromTemplate.iterations.begin()->first;
165+
}
166+
}
167+
168+
initializeFromTemplate(
169+
initializeMe.iterations[iteration],
170+
fromTemplate.iterations.at(sourceIteration));
171+
return initializeMe;
172+
}
173+
} // namespace openPMD::auxiliary

test/SerialIOTest.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "openPMD/auxiliary/Environment.hpp"
1515
#include "openPMD/auxiliary/Filesystem.hpp"
1616
#include "openPMD/auxiliary/StringManip.hpp"
17+
#include "openPMD/auxiliary/TemplateFile.hpp"
1718
#include "openPMD/openPMD.hpp"
1819

1920
#include <catch2/catch.hpp>
@@ -1493,6 +1494,12 @@ inline void dtype_test(
14931494

14941495
if (activateTemplateMode.has_value())
14951496
{
1497+
Series out(
1498+
"../samples/dtype_test_from_template." + backend,
1499+
Access::CREATE,
1500+
activateTemplateMode.value());
1501+
auxiliary::initializeFromTemplate(out, s, 1000);
1502+
out.flush();
14961503
return;
14971504
}
14981505
// same implementation types (not necessary aliases) detection

0 commit comments

Comments
 (0)