Skip to content

Commit b88bae6

Browse files
committed
Initialize Series attributes from template
Missing: datasets
1 parent 6302a33 commit b88bae6

File tree

6 files changed

+228
-2
lines changed

6 files changed

+228
-2
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ set(CORE_SOURCE
459459
src/auxiliary/Date.cpp
460460
src/auxiliary/Filesystem.cpp
461461
src/auxiliary/JSON.cpp
462+
src/auxiliary/TemplateFile.cpp
462463
src/backend/Attributable.cpp
463464
src/backend/BaseRecordComponent.cpp
464465
src/backend/Container.cpp

include/openPMD/Iteration.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,15 @@ namespace internal
111111
* alone.
112112
*/
113113
std::optional<std::string> m_overrideFilebasedFilename{};
114+
115+
enum TernaryBool
116+
{
117+
Undefined,
118+
True,
119+
False
120+
};
121+
TernaryBool hasMeshes = TernaryBool::Undefined;
122+
TernaryBool hasParticles = TernaryBool::Undefined;
114123
};
115124
} // namespace internal
116125
/** @brief Logical compilation of data from one snapshot (e.g. a single
@@ -233,6 +242,9 @@ class Iteration : public Attributable
233242
Container<Mesh> meshes{};
234243
Container<ParticleSpecies> particles{}; // particleSpecies?
235244

245+
bool hasMeshes() const;
246+
bool hasParticles() const;
247+
236248
virtual ~Iteration() = default;
237249

238250
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
@@ -35,6 +35,50 @@ namespace openPMD
3535
using internal::CloseStatus;
3636
using internal::DeferredParseAccess;
3737

38+
bool Iteration::hasMeshes() const
39+
{
40+
/*
41+
* Currently defined at the Series level, but might be defined at the
42+
* Iteration level in next standard iterations.
43+
* Hence an Iteration:: method.
44+
*/
45+
46+
switch (get().hasMeshes)
47+
{
48+
case internal::IterationData::TernaryBool::True:
49+
return true;
50+
case internal::IterationData::TernaryBool::False:
51+
return false;
52+
case internal::IterationData::TernaryBool::Undefined: {
53+
Series s = retrieveSeries();
54+
return !meshes.empty() || s.containsAttribute("meshesPath");
55+
};
56+
}
57+
throw std::runtime_error("Unreachable!");
58+
}
59+
60+
bool Iteration::hasParticles() const
61+
{
62+
/*
63+
* Currently defined at the Series level, but might be defined at the
64+
* Iteration level in next standard iterations.
65+
* Hence an Iteration:: method.
66+
*/
67+
68+
switch (get().hasParticles)
69+
{
70+
case internal::IterationData::TernaryBool::True:
71+
return true;
72+
case internal::IterationData::TernaryBool::False:
73+
return false;
74+
case internal::IterationData::TernaryBool::Undefined: {
75+
Series s = retrieveSeries();
76+
return !particles.empty() || s.containsAttribute("particlesPath");
77+
};
78+
}
79+
throw std::runtime_error("Unreachable!");
80+
}
81+
3882
Iteration::Iteration() : Attributable{nullptr}
3983
{
4084
Attributable::setData(m_iterationData);
@@ -314,7 +358,7 @@ void Iteration::flush(internal::FlushParams const &flushParams)
314358
* meshesPath and particlesPath are stored there */
315359
Series s = retrieveSeries();
316360

317-
if (!meshes.empty() || s.containsAttribute("meshesPath"))
361+
if (hasMeshes())
318362
{
319363
if (!s.containsAttribute("meshesPath"))
320364
{
@@ -330,7 +374,7 @@ void Iteration::flush(internal::FlushParams const &flushParams)
330374
meshes.dirty() = false;
331375
}
332376

333-
if (!particles.empty() || s.containsAttribute("particlesPath"))
377+
if (hasParticles())
334378
{
335379
if (!s.containsAttribute("particlesPath"))
336380
{
@@ -477,6 +521,12 @@ void Iteration::read_impl(std::string const &groupPath)
477521
hasMeshes = s.containsAttribute("meshesPath");
478522
hasParticles = s.containsAttribute("particlesPath");
479523
}
524+
{
525+
using TB = internal::IterationData::TernaryBool;
526+
auto &data = get();
527+
data.hasMeshes = hasMeshes ? TB::True : TB::False;
528+
data.hasParticles = hasParticles ? TB::True : TB::False;
529+
}
480530

481531
if (hasMeshes)
482532
{

src/auxiliary/TemplateFile.cpp

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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+
ParticleSpecies &initializeMe, ParticleSpecies const &fromTemplate)
72+
{
73+
if (!fromTemplate.particlePatches.empty())
74+
{
75+
initializeFromTemplate(
76+
static_cast<Container<PatchRecord> &>(
77+
initializeMe.particlePatches),
78+
static_cast<Container<PatchRecord> const &>(
79+
fromTemplate.particlePatches));
80+
}
81+
initializeFromTemplate(
82+
static_cast<Container<Record> &>(initializeMe),
83+
static_cast<Container<Record> const &>(fromTemplate));
84+
}
85+
86+
template <typename T>
87+
void initializeFromTemplate(
88+
Container<T> &initializeMe, Container<T> const &fromTemplate)
89+
{
90+
copyAttributes(initializeMe, fromTemplate);
91+
for (auto const &pair : fromTemplate)
92+
{
93+
initializeFromTemplate(initializeMe[pair.first], pair.second);
94+
}
95+
}
96+
97+
void initializeFromTemplate(
98+
Iteration &initializeMe, Iteration const &fromTemplate)
99+
{
100+
copyAttributes(initializeMe, fromTemplate, {"snapshot"});
101+
if (fromTemplate.hasMeshes())
102+
{
103+
initializeFromTemplate(initializeMe.meshes, fromTemplate.meshes);
104+
}
105+
if (fromTemplate.hasParticles())
106+
{
107+
initializeFromTemplate(
108+
initializeMe.particles, fromTemplate.particles);
109+
}
110+
}
111+
} // namespace
112+
113+
Series &initializeFromTemplate(
114+
Series &initializeMe, Series const &fromTemplate, uint64_t iteration)
115+
{
116+
if (!initializeMe.containsAttribute("from_template"))
117+
{
118+
copyAttributes(
119+
initializeMe,
120+
fromTemplate,
121+
{"basePath", "iterationEncoding", "iterationFormat", "openPMD"});
122+
initializeMe.setAttribute("from_template", fromTemplate.name());
123+
}
124+
125+
uint64_t sourceIteration = iteration;
126+
if (!fromTemplate.iterations.contains(sourceIteration))
127+
{
128+
if (fromTemplate.iterations.empty())
129+
{
130+
std::cerr << "Template file has no iterations, will only fill in "
131+
"global attributes."
132+
<< std::endl;
133+
return initializeMe;
134+
}
135+
else
136+
{
137+
sourceIteration = fromTemplate.iterations.begin()->first;
138+
}
139+
}
140+
141+
initializeFromTemplate(
142+
initializeMe.iterations[iteration],
143+
fromTemplate.iterations.at(sourceIteration));
144+
return initializeMe;
145+
}
146+
} // namespace openPMD::auxiliary

test/SerialIOTest.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "openPMD/auxiliary/Environment.hpp"
88
#include "openPMD/auxiliary/Filesystem.hpp"
99
#include "openPMD/auxiliary/StringManip.hpp"
10+
#include "openPMD/auxiliary/TemplateFile.hpp"
1011
#include "openPMD/openPMD.hpp"
1112

1213
#if openPMD_HAVE_ADIOS2
@@ -1438,6 +1439,12 @@ inline void dtype_test(
14381439

14391440
if (activateTemplateMode.has_value())
14401441
{
1442+
Series out(
1443+
"../samples/dtype_test_from_template." + backend,
1444+
Access::CREATE,
1445+
activateTemplateMode.value());
1446+
auxiliary::initializeFromTemplate(out, s, 1000);
1447+
out.flush();
14411448
return;
14421449
}
14431450
// same implementation types (not necessary aliases) detection

0 commit comments

Comments
 (0)