38
38
#include < optional>
39
39
#include < regex>
40
40
#include < sstream>
41
+ #include < tuple>
41
42
42
43
namespace openPMD
43
44
{
44
45
45
46
namespace
46
47
{
48
+ template <typename Iterator>
47
49
std::string
48
- concatWithSep (std::vector<std::string> const &v , std::string const &sep)
50
+ concatWithSep (Iterator &&begin, Iterator const &end , std::string const &sep)
49
51
{
50
- switch (v. size () )
52
+ if (begin == end )
51
53
{
52
- case 0 :
53
54
return " " ;
54
- case 1 :
55
- return *v.begin ();
56
- default :
57
- break ;
58
55
}
59
56
std::stringstream res;
60
- auto it = v.begin ();
61
- res << *it++;
62
- for (; it != v.end (); ++it)
57
+ res << *(begin++);
58
+ for (; begin != end; ++begin)
63
59
{
64
- res << sep << *it ;
60
+ res << sep << *begin ;
65
61
}
66
62
return res.str ();
67
63
}
68
64
65
+ std::string
66
+ concatWithSep (std::vector<std::string> const &v, std::string const &sep)
67
+ {
68
+ return concatWithSep (v.begin (), v.end (), sep);
69
+ }
70
+
69
71
// Not specifying std::regex_constants::optimize here, only using it where
70
72
// it makes sense to.
71
73
constexpr std::regex_constants::syntax_option_type regex_flags =
72
74
std::regex_constants::egrep;
75
+
76
+ template <typename OutParam>
77
+ void setDefaultMeshesParticlesPath (
78
+ std::vector<std::string> const &meshes,
79
+ std::vector<std::string> const &particles,
80
+ OutParam &writeTarget)
81
+ {
82
+ std::regex is_default_path_specification (" [[:alnum:]_]+/" , regex_flags);
83
+ constexpr char const *default_default_mesh = " meshes" ;
84
+ constexpr char const *default_default_particle = " particles" ;
85
+ for (auto [vec, defaultPath, default_default] :
86
+ {std::make_tuple (
87
+ &meshes,
88
+ &writeTarget.m_defaultMeshesPath ,
89
+ default_default_mesh),
90
+ std::make_tuple (
91
+ &particles,
92
+ &writeTarget.m_defaultParticlesPath ,
93
+ default_default_particle)})
94
+ {
95
+ bool set_default = true ;
96
+ /*
97
+ * The first eligible path in meshesPath/particlesPath is used as
98
+ * the default, "meshes"/"particles" otherwise.
99
+ */
100
+ for (auto const &path : *vec)
101
+ {
102
+ if (std::regex_match (path, is_default_path_specification))
103
+ {
104
+ *defaultPath = auxiliary::replace_last (path, " /" , " " );
105
+ set_default = false ;
106
+ break ;
107
+ }
108
+ }
109
+ if (set_default)
110
+ {
111
+ *defaultPath = default_default;
112
+ }
113
+ }
114
+ }
73
115
} // namespace
74
116
75
117
namespace internal
@@ -81,15 +123,43 @@ namespace internal
81
123
std::vector<std::string> const &path,
82
124
std::string const &name)
83
125
{
84
- std::string parentPath =
85
- (path.empty () ? " " : concatWithSep (path, " /" )) + " /" ;
86
- std::string fullPath = path.empty () ? name : parentPath + name;
126
+ /*
127
+ * /group/meshes/E is a mesh if the meshes path contains:
128
+ *
129
+ * 1) '/group/meshes/' (absolute path to mesh container)
130
+ * 2) '/group/meshes/E' (absolute path to mesh itself)
131
+ * 3) 'meshes/' (relative path to mesh container)
132
+ *
133
+ * The potential fourth option 'E' (relative path to mesh itself)
134
+ * is not supported. ("Anything that is named 'E' is a mesh" is not
135
+ * really a semantic that we want to explicitly support.)
136
+ * '/' is never a valid meshes path.
137
+ *
138
+ * All this analogously for particles path.
139
+ */
140
+ std::vector<std::string> pathsToMatch = {
141
+ /* option 2) from above */
142
+ " /" + (path.empty () ? " " : concatWithSep (path, " /" ) + " /" ) +
143
+ name};
144
+ if (!path.empty ())
145
+ {
146
+ // option 1) from above
147
+ pathsToMatch.emplace_back (" /" + concatWithSep (path, " /" ) + " /" );
148
+
149
+ // option 3 from above
150
+ pathsToMatch.emplace_back (*path.rbegin () + " /" );
151
+ }
87
152
return std::any_of (
88
153
regexes.begin (),
89
154
regexes.end (),
90
- [&parentPath, &fullPath](auto const ®ex) {
91
- return std::regex_match (parentPath, regex.second ) ||
92
- std::regex_match (fullPath, regex.second );
155
+ [&pathsToMatch](auto const ®ex) {
156
+ return std::any_of (
157
+ pathsToMatch.begin (),
158
+ pathsToMatch.end (),
159
+ [®ex](std::string const &candidate_path) {
160
+ return std::regex_match (
161
+ candidate_path, regex.second );
162
+ });
93
163
});
94
164
}
95
165
} // namespace
@@ -98,9 +168,10 @@ namespace internal
98
168
std::vector<std::string> const &meshes,
99
169
std::vector<std::string> const &particles)
100
170
{
171
+ std::regex is_default_path_specification (" [[:alnum:]_]+/" , regex_flags);
101
172
for (auto [deque, vec] :
102
- {std::make_pair (&this ->meshesPath , &meshes),
103
- std::make_pair (&this ->particlesPath , &particles)})
173
+ {std::make_tuple (&this ->meshesPath , &meshes),
174
+ std::make_tuple (&this ->particlesPath , &particles)})
104
175
{
105
176
std::transform (
106
177
vec->begin (),
@@ -113,6 +184,7 @@ namespace internal
113
184
str, regex_flags | std::regex_constants::optimize));
114
185
});
115
186
}
187
+ setDefaultMeshesParticlesPath (meshes, particles, *this );
116
188
}
117
189
118
190
ContainedType MeshesParticlesPath::determineType (
@@ -262,10 +334,6 @@ void CustomHierarchy::readParticleSpecies(
262
334
}
263
335
}
264
336
265
- // @todo make this flexible again
266
- constexpr char const *defaultMeshesPath = " meshes" ;
267
- constexpr char const *defaultParticlesPath = " particles" ;
268
-
269
337
void CustomHierarchy::read (internal::MeshesParticlesPath const &mpp)
270
338
{
271
339
std::vector<std::string> currentPath;
@@ -292,6 +360,8 @@ void CustomHierarchy::read(
292
360
293
361
std::deque<std::string> constantComponentsPushback;
294
362
auto &data = get ();
363
+ data.m_defaultMeshesPath = mpp.m_defaultMeshesPath ;
364
+ data.m_defaultParticlesPath = mpp.m_defaultParticlesPath ;
295
365
EraseStaleMeshes meshesMap (data.m_embeddedMeshes );
296
366
EraseStaleParticles particlesMap (data.m_embeddedParticles );
297
367
for (auto const &path : *pList.paths )
@@ -405,16 +475,19 @@ void CustomHierarchy::flush_internal(
405
475
406
476
// No need to do anything in access::readOnly since meshes and particles
407
477
// are initialized as aliases for subgroups at parsing time
478
+ auto &data = get ();
479
+ data.m_defaultMeshesPath = mpp.m_defaultMeshesPath ;
480
+ data.m_defaultParticlesPath = mpp.m_defaultParticlesPath ;
408
481
if (access ::write (IOHandler ()->m_frontendAccess ))
409
482
{
410
483
if (!meshes.empty ())
411
484
{
412
- (*this )[defaultMeshesPath ];
485
+ (*this )[mpp. m_defaultMeshesPath ];
413
486
}
414
487
415
488
if (!particles.empty ())
416
489
{
417
- (*this )[defaultParticlesPath ];
490
+ (*this )[mpp. m_defaultParticlesPath ];
418
491
}
419
492
420
493
flushAttributes (flushParams);
@@ -432,27 +505,53 @@ void CustomHierarchy::flush_internal(
432
505
subpath.flush_internal (flushParams, mpp, currentPath);
433
506
currentPath.pop_back ();
434
507
}
435
- auto &data = get ();
436
508
for (auto &[name, mesh] : data.m_embeddedMeshes )
437
509
{
438
510
if (!mpp.isMesh (currentPath, name))
439
511
{
440
- std::string fullPath = currentPath.empty ()
441
- ? name
442
- : concatWithSep (currentPath, " /" ) + " /" + name;
443
- mpp.meshesPath .emplace (fullPath, std::regex (fullPath, regex_flags));
512
+ std::string extend_meshes_path;
513
+ if (!currentPath.empty () &&
514
+ *currentPath.rbegin () == mpp.m_defaultMeshesPath )
515
+ {
516
+ extend_meshes_path = *currentPath.rbegin () + " /" ;
517
+ }
518
+ else
519
+ {
520
+
521
+ extend_meshes_path = " /" +
522
+ (currentPath.empty ()
523
+ ? " "
524
+ : concatWithSep (currentPath, " /" ) + " /" ) +
525
+ name;
526
+ }
527
+ mpp.meshesPath .emplace (
528
+ extend_meshes_path,
529
+ std::regex (extend_meshes_path, regex_flags));
444
530
}
445
531
mesh.flush (name, flushParams);
446
532
}
447
533
for (auto &[name, particleSpecies] : data.m_embeddedParticles )
448
534
{
449
535
if (!mpp.isParticle (currentPath, name))
450
536
{
451
- std::string fullPath = currentPath.empty ()
452
- ? name
453
- : concatWithSep (currentPath, " /" ) + " /" + name;
537
+ std::string extend_particles_path;
538
+ if (!currentPath.empty () &&
539
+ *currentPath.rbegin () == mpp.m_defaultParticlesPath )
540
+ {
541
+ extend_particles_path = *currentPath.rbegin () + " /" ;
542
+ }
543
+ else
544
+ {
545
+
546
+ extend_particles_path = " /" +
547
+ (currentPath.empty ()
548
+ ? " "
549
+ : concatWithSep (currentPath, " /" ) + " /" ) +
550
+ name;
551
+ }
454
552
mpp.particlesPath .emplace (
455
- fullPath, std::regex (fullPath, regex_flags));
553
+ extend_particles_path,
554
+ std::regex (extend_particles_path, regex_flags));
456
555
}
457
556
particleSpecies.flush (name, flushParams);
458
557
}
@@ -596,10 +695,11 @@ template <typename KeyType>
596
695
auto CustomHierarchy::bracketOperatorImpl (KeyType &&provided_key)
597
696
-> mapped_type &
598
697
{
599
- auto &cont = container ();
698
+ auto &data = get ();
699
+ auto &cont = data.m_container ;
600
700
auto find_special_key =
601
701
[&cont, &provided_key, this ](
602
- char const * special_key,
702
+ std::string const & special_key,
603
703
auto &alias,
604
704
auto &&embeddedAccessor) -> std::optional<mapped_type *> {
605
705
if (provided_key == special_key)
@@ -656,8 +756,15 @@ auto CustomHierarchy::bracketOperatorImpl(KeyType &&provided_key)
656
756
return std::nullopt;
657
757
}
658
758
};
759
+ if (data.m_defaultMeshesPath .empty () || data.m_defaultParticlesPath .empty ())
760
+ {
761
+ auto const &series = retrieveSeries ();
762
+ auto meshes_paths = series.meshesPaths ();
763
+ auto particles_paths = series.particlesPaths ();
764
+ setDefaultMeshesParticlesPath (meshes_paths, particles_paths, data);
765
+ }
659
766
if (auto res = find_special_key (
660
- defaultMeshesPath ,
767
+ data. m_defaultMeshesPath ,
661
768
meshes,
662
769
[](auto &group) {
663
770
return &group.m_customHierarchyData ->m_embeddedMeshes ;
@@ -667,7 +774,7 @@ auto CustomHierarchy::bracketOperatorImpl(KeyType &&provided_key)
667
774
return **res;
668
775
}
669
776
if (auto res = find_special_key (
670
- defaultParticlesPath ,
777
+ data. m_defaultParticlesPath ,
671
778
particles,
672
779
[](auto &group) {
673
780
return &group.m_customHierarchyData ->m_embeddedParticles ;
0 commit comments