diff --git a/include/openPMD/Iteration.hpp b/include/openPMD/Iteration.hpp index 43ee1084bb..a11e037333 100644 --- a/include/openPMD/Iteration.hpp +++ b/include/openPMD/Iteration.hpp @@ -136,6 +136,13 @@ namespace internal std::optional m_deferredParseAccess{}; }; } // namespace internal + +namespace traits +{ + template <> + struct GenerationPolicy; +} + /** @brief Logical compilation of data from one snapshot (e.g. a single * simulation cycle). * @@ -153,6 +160,7 @@ class Iteration : public Attributable friend class Writable; friend class StatefulIterator; friend class StatefulSnapshotsContainer; + friend struct traits::GenerationPolicy; public: Iteration(Iteration const &) = default; @@ -303,7 +311,7 @@ class Iteration : public Attributable void flushFileBased( std::string const &, IterationIndex_t, internal::FlushParams const &); void flushGroupBased(IterationIndex_t, internal::FlushParams const &); - void flushVariableBased(IterationIndex_t, internal::FlushParams const &); + void flushVariableBased(internal::FlushParams const &); void flush(internal::FlushParams const &); void deferParseAccess(internal::DeferredParseAccess); /* @@ -419,9 +427,10 @@ class Iteration : public Attributable /** * @brief Link with parent. * - * @param w The Writable representing the parent. + * @param parent The Writable representing the parent. */ - virtual void linkHierarchy(Writable &w); + void linkHierarchy(internal::AttributableData &parent) override; + using Attributable::linkHierarchy; /** * @brief Access an iteration in read mode that has potentially not been @@ -478,4 +487,36 @@ class IndexedIteration : public Iteration : Iteration(std::forward(it)), iterationIndex(index) {} }; + +namespace traits +{ + template <> + struct GenerationPolicy + { + constexpr static bool is_noop = false; + template + void operator()(T &ret, Container *c) + { + if (ret.IOHandler()->m_encoding == IterationEncoding::variableBased) + { + for (auto &pair : + static_cast(ret).get().m_attributes) + { + static_cast(*c).get().m_attributes.emplace( + std::move(pair)); + } + static_cast< + std::shared_ptr &>( + *ret.m_attri) = + static_cast< + std::shared_ptr &>( + *c->m_attri); + internal::AttributableData *attr_of_shared_parent = + c->m_attri->frontend_parent; + ret.linkHierarchy(*attr_of_shared_parent); + ret.m_attri->frontend_parent = c->m_attri.get(); + } + } + }; +} // namespace traits } // namespace openPMD diff --git a/include/openPMD/ParticleSpecies.hpp b/include/openPMD/ParticleSpecies.hpp index af7aa50375..4f9d502eba 100644 --- a/include/openPMD/ParticleSpecies.hpp +++ b/include/openPMD/ParticleSpecies.hpp @@ -61,10 +61,10 @@ namespace traits struct GenerationPolicy { constexpr static bool is_noop = false; - template - void operator()(T &ret) + template + void operator()(T &ret, C const *) { - ret.particlePatches.linkHierarchy(ret.writable()); + ret.particlePatches.linkHierarchy(ret); } }; } // namespace traits diff --git a/include/openPMD/backend/Attributable.hpp b/include/openPMD/backend/Attributable.hpp index 732b2d1b5c..c9390f8535 100644 --- a/include/openPMD/backend/Attributable.hpp +++ b/include/openPMD/backend/Attributable.hpp @@ -152,6 +152,33 @@ namespace internal std::shared_ptr(self, [](auto const *) {})); return res; } + + internal::AttributableData *frontend_parent = nullptr; + + /** Tracks if there are unwritten changes for this specific Writable. + * + * Manipulate via Attributable::dirty() and Attributable::setDirty(). + */ + bool dirtySelf = true; + /** + * Tracks if there are unwritten changes anywhere in the + * tree whose ancestor this Writable is. + * + * Invariant: this->dirtyRecursive implies parent->dirtyRecursive. + * + * dirtySelf and dirtyRecursive are separated since that allows + * specifying that `this` is not dirty, but some child is. + * + * Manipulate via Attributable::dirtyRecursive() and + * Attributable::setDirtyRecursive(). + */ + bool dirtyRecursive = true; + + /** + * If frontend_parent is not null, then this is a key such that: + * &(*frontend_parent)[key] == this + */ + std::string ownKeyWithinParent; }; template @@ -506,18 +533,17 @@ OPENPMD_protected bool dirty() const { - return writable().dirtySelf; + return m_attri->dirtySelf; } /** O(1). */ bool dirtyRecursive() const { - return writable().dirtyRecursive; + return m_attri->dirtyRecursive; } void setDirty(bool dirty_in) { - auto &w = writable(); - w.dirtySelf = dirty_in; + m_attri->dirtySelf = dirty_in; setDirtyRecursive(dirty_in); } /* Amortized O(1) if dirty_in is true, else O(1). @@ -538,15 +564,15 @@ OPENPMD_protected */ void setDirtyRecursive(bool dirty_in) { - auto &w = writable(); - w.dirtyRecursive = dirty_in; + auto &a = *m_attri; + a.dirtyRecursive = dirty_in; if (dirty_in) { - auto current = w.parent; + auto current = a.frontend_parent; while (current && !current->dirtyRecursive) { current->dirtyRecursive = true; - current = current->parent; + current = current->frontend_parent; } } } @@ -575,9 +601,10 @@ OPENPMD_protected /** * @brief Link with parent. * - * @param w The Writable representing the parent. + * @param parent The Writable representing the parent. */ - virtual void linkHierarchy(Writable &w); + void linkHierarchy(Attributable &parent); + virtual void linkHierarchy(internal::AttributableData &parent); }; // Attributable // note: we explicitly instantiate Attributable::setAttributeImpl for all T in diff --git a/include/openPMD/backend/Container.hpp b/include/openPMD/backend/Container.hpp index 8dda5b992b..4e683fc57d 100644 --- a/include/openPMD/backend/Container.hpp +++ b/include/openPMD/backend/Container.hpp @@ -51,8 +51,8 @@ namespace traits struct GenerationPolicy { constexpr static bool is_noop = true; - template - void operator()(T &) + template + void operator()(Args &&...) {} }; } // namespace traits @@ -297,18 +297,18 @@ class Container : virtual public Attributable } T t = T(); - t.linkHierarchy(writable()); + t.linkHierarchy(*this); auto &ret = container().insert({key, std::move(t)}).first->second; if constexpr (std::is_same_v) { - ret.writable().ownKeyWithinParent = key; + ret.m_attri->ownKeyWithinParent = key; } else { - ret.writable().ownKeyWithinParent = std::to_string(key); + ret.m_attri->ownKeyWithinParent = std::to_string(key); } traits::GenerationPolicy gen; - gen(ret); + gen(ret, this); return ret; } } @@ -338,19 +338,19 @@ class Container : virtual public Attributable } T t = T(); - t.linkHierarchy(writable()); + t.linkHierarchy(*this); auto &ret = container().insert({key, std::move(t)}).first->second; if constexpr (std::is_same_v) { - ret.writable().ownKeyWithinParent = std::move(key); + ret.m_attri->ownKeyWithinParent = std::move(key); } else { - ret.writable().ownKeyWithinParent = + ret.m_attri->ownKeyWithinParent = std::to_string(std::move(key)); } traits::GenerationPolicy gen; - gen(ret); + gen(ret, this); return ret; } } diff --git a/include/openPMD/backend/Writable.hpp b/include/openPMD/backend/Writable.hpp index bfb9c67e03..1117f532ef 100644 --- a/include/openPMD/backend/Writable.hpp +++ b/include/openPMD/backend/Writable.hpp @@ -62,6 +62,16 @@ namespace debug void printDirty(Series const &); } +class Iteration; + +namespace traits +{ + template + struct GenerationPolicy; + template <> + struct GenerationPolicy; +} // namespace traits + /** @brief Layer to mirror structure of logical data and persistent data in * file. * @@ -105,6 +115,7 @@ class Writable final friend void debug::printDirty(Series const &); friend struct Parameter; friend struct Parameter; + friend struct traits::GenerationPolicy; private: Writable(internal::AttributableData *); @@ -154,29 +165,6 @@ OPENPMD_private internal::AttributableData *attributable = nullptr; Writable *parent = nullptr; - /** Tracks if there are unwritten changes for this specific Writable. - * - * Manipulate via Attributable::dirty() and Attributable::setDirty(). - */ - bool dirtySelf = true; - /** - * Tracks if there are unwritten changes anywhere in the - * tree whose ancestor this Writable is. - * - * Invariant: this->dirtyRecursive implies parent->dirtyRecursive. - * - * dirtySelf and dirtyRecursive are separated since that allows specifying - * that `this` is not dirty, but some child is. - * - * Manipulate via Attributable::dirtyRecursive() and - * Attributable::setDirtyRecursive(). - */ - bool dirtyRecursive = true; - /** - * If parent is not null, then this is a key such that: - * &(*parent)[key] == this - */ - std::string ownKeyWithinParent; /** * @brief Whether a Writable has been written to the backend. * diff --git a/src/IO/AbstractIOHandlerImpl.cpp b/src/IO/AbstractIOHandlerImpl.cpp index faad2f49a9..ea4e35218f 100644 --- a/src/IO/AbstractIOHandlerImpl.cpp +++ b/src/IO/AbstractIOHandlerImpl.cpp @@ -25,6 +25,7 @@ #include "openPMD/Streaming.hpp" #include "openPMD/auxiliary/Environment.hpp" #include "openPMD/auxiliary/StringManip.hpp" +#include "openPMD/auxiliary/TypeTraits.hpp" #include "openPMD/auxiliary/Variant.hpp" #include "openPMD/backend/Writable.hpp" @@ -149,7 +150,9 @@ std::future AbstractIOHandlerImpl::flush() "] CREATE_DATASET: ", parameter.name, ", extent=", - [¶meter]() { return vec_as_string(parameter.extent); }); + [¶meter]() { + return auxiliary::vec_as_string(parameter.extent); + }); createDataset(i.writable, parameter); break; } @@ -283,7 +286,28 @@ std::future AbstractIOHandlerImpl::flush() "] WRITE_ATT: (", parameter.dtype, ") ", - parameter.name); + parameter.name, + "=", + [&]() { + return std::visit( + [&](auto const &val) { + using dtype = std::remove_cv_t< + std::remove_reference_t>; + if constexpr ( + auxiliary::IsArray_v || + auxiliary::IsVector_v) + { + return auxiliary::vec_as_string(val); + } + else + { + std::stringstream res; + res << val; + return res.str(); + } + }, + parameter.resource); + }); writeAttribute(i.writable, parameter); break; } @@ -297,9 +321,13 @@ std::future AbstractIOHandlerImpl::flush() "->", i.writable, "] READ_DATASET, offset=", - [¶meter]() { return vec_as_string(parameter.offset); }, + [¶meter]() { + return auxiliary::vec_as_string(parameter.offset); + }, ", extent=", - [¶meter]() { return vec_as_string(parameter.extent); }); + [¶meter]() { + return auxiliary::vec_as_string(parameter.extent); + }); readDataset(i.writable, parameter); break; } diff --git a/src/Iteration.cpp b/src/Iteration.cpp index 64cfaa39d0..4ad1d11edd 100644 --- a/src/Iteration.cpp +++ b/src/Iteration.cpp @@ -53,8 +53,8 @@ Iteration::Iteration() : Attributable(NoInit()) setTime(static_cast(0)); setDt(static_cast(1)); setTimeUnitSI(1); - meshes.writable().ownKeyWithinParent = "meshes"; - particles.writable().ownKeyWithinParent = "particles"; + meshes.m_attri->ownKeyWithinParent = "meshes"; + particles.m_attri->ownKeyWithinParent = "particles"; } template @@ -292,16 +292,9 @@ void Iteration::flushGroupBased( } } -void Iteration::flushVariableBased( - IterationIndex_t i, internal::FlushParams const &flushParams) +void Iteration::flushVariableBased(internal::FlushParams const &flushParams) { - if (!written()) - { - /* create iteration path */ - Parameter pOpen; - pOpen.path = ""; - IOHandler()->enqueue(IOTask(this, pOpen)); - } + setDirty(true); switch (flushParams.flushLevel) { @@ -313,27 +306,6 @@ void Iteration::flushVariableBased( flush(flushParams); break; } - - if (!written()) - { - /* create iteration path */ - Parameter pOpen; - pOpen.path = ""; - IOHandler()->enqueue(IOTask(this, pOpen)); - /* - * In v-based encoding, the snapshot attribute must always be written. - * Reason: Even in backends that don't support changing attributes, - * variable-based iteration encoding can be used to write one single - * iteration. Then, this attribute determines which iteration it is. - */ - Parameter wAttr; - wAttr.changesOverSteps = - Parameter::ChangesOverSteps::IfPossible; - wAttr.name = "snapshot"; - wAttr.resource = (unsigned long long)i; - wAttr.dtype = Datatype::ULONGLONG; - IOHandler()->enqueue(IOTask(this, wAttr)); - } } void Iteration::flush(internal::FlushParams const &flushParams) @@ -904,11 +876,11 @@ void Iteration::setStepStatus(StepStatus status) } } -void Iteration::linkHierarchy(Writable &w) +void Iteration::linkHierarchy(internal::AttributableData &parent) { - Attributable::linkHierarchy(w); - meshes.linkHierarchy(this->writable()); - particles.linkHierarchy(this->writable()); + Attributable::linkHierarchy(parent); + meshes.linkHierarchy(*this); + particles.linkHierarchy(*this); } void Iteration::runDeferredParseAccess() diff --git a/src/ParticleSpecies.cpp b/src/ParticleSpecies.cpp index 4006cc82ba..fcd97e71a3 100644 --- a/src/ParticleSpecies.cpp +++ b/src/ParticleSpecies.cpp @@ -30,7 +30,7 @@ namespace openPMD { ParticleSpecies::ParticleSpecies() { - particlePatches.writable().ownKeyWithinParent = "particlePatches"; + particlePatches.m_attri->ownKeyWithinParent = "particlePatches"; } void ParticleSpecies::read() diff --git a/src/Series.cpp b/src/Series.cpp index 72e8740340..88cdb5e0d7 100644 --- a/src/Series.cpp +++ b/src/Series.cpp @@ -39,9 +39,8 @@ #include "openPMD/auxiliary/Variant.hpp" #include "openPMD/backend/Attributable.hpp" #include "openPMD/backend/Attribute.hpp" +#include "openPMD/backend/Writable.hpp" #include "openPMD/snapshots/ContainerImpls.hpp" -#include "openPMD/snapshots/IteratorTraits.hpp" -#include "openPMD/snapshots/RandomAccessIterator.hpp" #include "openPMD/snapshots/Snapshots.hpp" #include "openPMD/snapshots/StatefulIterator.hpp" #include "openPMD/version.hpp" @@ -55,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -862,8 +862,8 @@ void Series::init( std::make_shared>>( std::make_unique(parsed_directory, at)); auto &series = get(); - series.iterations.linkHierarchy(writable()); - series.m_rankTable.m_attributable.linkHierarchy(writable()); + series.iterations.linkHierarchy(*this); + series.m_rankTable.m_attributable.linkHierarchy(*this); series.m_deferred_initialization = [called_this_already = false, filepath, options, at, comm...]( Series &s) mutable { @@ -1079,9 +1079,9 @@ void Series::initSeries( std::move(ioHandler)); } - series.iterations.linkHierarchy(writable); - series.iterations.writable().ownKeyWithinParent = "data"; - series.m_rankTable.m_attributable.linkHierarchy(writable); + series.iterations.linkHierarchy(*this); + series.iterations.m_attri->ownKeyWithinParent = "data"; + series.m_rankTable.m_attributable.linkHierarchy(*this); series.m_name = input->name; @@ -1489,9 +1489,20 @@ void Series::flushGorVBased( case IO::HasBeenOpened: if (!it->second.written()) { - it->second.parent() = getWritable(&series.iterations); - series.m_currentlyActiveIterations.emplace(it->first); + if (iterationEncoding() != IterationEncoding::variableBased) + { + it->second.parent() = getWritable(&series.iterations); + } + else if ( + &it->second.writable() != &series.iterations.writable()) + { + throw error::Internal( + "In variable-based encoding, the container of " + "Iterations must be the same backend object as the " + "Iterations themselves."); + } } + series.m_currentlyActiveIterations.emplace(it->first); switch (iterationEncoding()) { using IE = IterationEncoding; @@ -1499,7 +1510,7 @@ void Series::flushGorVBased( it->second.flushGroupBased(it->first, flushParams); break; case IE::variableBased: - it->second.flushVariableBased(it->first, flushParams); + it->second.flushVariableBased(flushParams); break; default: throw std::runtime_error( @@ -2060,6 +2071,16 @@ creating new iterations. { // parse for the first time, resp. delay the parsing process Iteration &i = series.iterations[index]; + // if (iterationEncoding() == IterationEncoding::variableBased) + // { + // static_cast< + // std::shared_ptr &>( + // *i.m_attri) = + // static_cast< + // std::shared_ptr &>( + // *series.iterations.m_attri); + // i.linkHierarchy(writable()); + // } i.deferParseAccess({path, index, false, beginStep}); if (!series.m_parseLazily) { @@ -2398,7 +2419,7 @@ Series::iterations_iterator Series::indexOf(Iteration const &iteration) for (auto it = series.iterations.begin(); it != series.iterations.end(); ++it) { - if (&it->second.Attributable::get() == &iteration.Attributable::get()) + if (it->second.m_attri.get() == iteration.m_attri.get()) { return it; } @@ -2619,8 +2640,16 @@ void Series::flushStep(bool doFlush) * one IO step. */ Parameter wAttr; + /* + * In v-based encoding, the snapshot attribute must always be written. + * Reason: Even in backends that don't support changing attributes, + * variable-based iteration encoding can be used to write one single + * iteration. Then, this attribute determines which iteration it is. + */ wAttr.changesOverSteps = - Parameter::ChangesOverSteps::Yes; + iterationEncoding() == IterationEncoding::variableBased + ? Parameter::ChangesOverSteps::IfPossible + : Parameter::ChangesOverSteps::Yes; wAttr.name = "snapshot"; wAttr.resource = std::vector{ series.m_currentlyActiveIterations.begin(), @@ -2873,11 +2902,13 @@ void Series::openIteration(IterationIndex_t index, Iteration &iteration) Parameter pOpen; pOpen.path = auxiliary::replace_first(basePath(), "%T/", ""); IOHandler()->enqueue(IOTask(&series.iterations, pOpen)); - /* open iteration path */ - pOpen.path = iterationEncoding() == IterationEncoding::variableBased - ? "" - : std::to_string(index); - IOHandler()->enqueue(IOTask(&iteration, pOpen)); + if (iterationEncoding() != IterationEncoding::variableBased) + { + /* open iteration path */ + pOpen.path = std::to_string(index); + IOHandler()->enqueue(IOTask(&iteration, pOpen)); + } + break; } case IE::groupBased: @@ -3613,7 +3644,9 @@ namespace debug { void printDirty(Series const &series) { - auto print = [](Attributable const &attr) { + std::stringstream graph; + graph << "digraph\n{node [shape=\"box\"];\n"; + auto print = [&graph](Attributable const &attr) { size_t indent = 0; { auto current = attr.parent(); @@ -3631,11 +3664,25 @@ namespace debug }; make_indent(); auto const &w = attr.writable(); - std::cout << w.ownKeyWithinParent << '\n'; + std::cout << attr.m_attri->ownKeyWithinParent << '\t' + << attr.m_attri.get() << " -> " << &attr.writable() + << '\n'; make_indent(); - std::cout << "Self: " << w.dirtySelf - << "\tRec: " << w.dirtyRecursive << '\n'; - std::cout << std::endl; + std::cout << "Self:\t" << attr.m_attri->dirtySelf + << "\tRec: " << attr.m_attri->dirtyRecursive << '\n'; + std::cout << '\n'; + graph << "{rank = same; "; + graph << "_" << attr.m_attri.get() << "[color=green, label = \"A " + << attr.m_attri.get() << " '" + << attr.m_attri->ownKeyWithinParent << "'\"]; "; + graph << "_" << &w << "[color=blue, label = \"W " << &w << " '" + << attr.m_attri->ownKeyWithinParent << "'\"]; "; + graph << "}\n"; + graph << "_" << &w << " -> _" << attr.m_attri.get() + << "[dir=none];\n"; + graph << "_" << w.parent << " -> _" << &w << ";\n"; + graph << "_" << attr.m_attri->frontend_parent << " -> _" + << attr.m_attri.get() << '\n'; }; print(series); print(series.iterations); @@ -3691,6 +3738,9 @@ namespace debug } } } + graph << "}"; + std::cout << graph.str(); + std::cout.flush(); } } // namespace debug } // namespace openPMD diff --git a/src/backend/Attributable.cpp b/src/backend/Attributable.cpp index 4fd2e7edb4..1e874f3a85 100644 --- a/src/backend/Attributable.cpp +++ b/src/backend/Attributable.cpp @@ -155,14 +155,14 @@ auto Attributable::containingIteration() const -> std::pair< internal::SeriesData const *> { constexpr size_t search_queue_size = 3; - Writable const *search_queue[search_queue_size]{nullptr}; + internal::AttributableData const *search_queue[search_queue_size]{nullptr}; size_t search_queue_idx = 0; - Writable const *findSeries = &writable(); + internal::AttributableData const *findSeries = m_attri.get(); while (true) { search_queue[search_queue_idx] = findSeries; // we don't need to push the last Writable since it's the Series anyway - findSeries = findSeries->parent; + findSeries = findSeries->frontend_parent; if (!findSeries) { break; @@ -175,14 +175,14 @@ auto Attributable::containingIteration() const -> std::pair< // End of the queue: // Iteration -> Series.iterations -> Series auto *series = &auxiliary::deref_dynamic_cast( - search_queue[search_queue_idx]->attributable); + search_queue[search_queue_idx]); auto maybe_iteration = search_queue [(search_queue_idx + (search_queue_size - 2)) % search_queue_size]; if (maybe_iteration) { auto *iteration = &auxiliary::deref_dynamic_cast( - maybe_iteration->attributable); + maybe_iteration); return std::make_pair(std::make_optional(iteration), series); } else @@ -229,19 +229,19 @@ std::string Attributable::MyPath::openPMDPath() const auto Attributable::myPath() const -> MyPath { MyPath res; - Writable const *findSeries = &writable(); - while (findSeries->parent) + internal::AttributableData *findSeries = m_attri.get(); + while (findSeries->frontend_parent) { // we don't need to push_back the ownKeyWithinParent of the Series class // so it's alright that this loop doesn't ask the key of the last found // Writable res.group.push_back(findSeries->ownKeyWithinParent); - findSeries = findSeries->parent; + findSeries = findSeries->frontend_parent; } std::reverse(res.group.begin(), res.group.end()); - auto &seriesData = auxiliary::deref_dynamic_cast( - findSeries->attributable); + auto &seriesData = + auxiliary::deref_dynamic_cast(findSeries); Series series; series.setData( std::shared_ptr{ @@ -529,11 +529,18 @@ void Attributable::setWritten(bool val, EnqueueAsynchronously ea) writable().written = val; } -void Attributable::linkHierarchy(Writable &w) +void Attributable::linkHierarchy(Attributable &parent) +{ + this->linkHierarchy(*parent.m_attri); +} + +void Attributable::linkHierarchy(internal::AttributableData &a) { + Writable &w = a->m_writable; auto handler = w.IOHandler; writable().IOHandler = handler; writable().parent = &w; + m_attri->frontend_parent = &a; setDirty(true); } diff --git a/test/CoreTest.cpp b/test/CoreTest.cpp index 92839ed131..f247f1bc8c 100644 --- a/test/CoreTest.cpp +++ b/test/CoreTest.cpp @@ -186,11 +186,12 @@ TEST_CASE("attribute_dtype_test", "[core]") } } -TEST_CASE("myPath", "[core]") +void myPath(std::string const &filename, IterationEncoding ie) { + auto filepath = "../samples/" + filename + ".json"; #if openPMD_USE_INVASIVE_TESTS using vec_t = std::vector; - auto pathOf = [](Attributable &attr) { + auto pathOf = [&](Attributable &attr) { auto res = attr.myPath(); #if false std::cout << "Directory:\t" << res.directory << "\nSeries name:\t" @@ -198,13 +199,14 @@ TEST_CASE("myPath", "[core]") << std::endl; #endif REQUIRE(res.directory == "../samples/"); - REQUIRE(res.seriesName == "myPath"); + REQUIRE(res.seriesName == filename); REQUIRE(res.seriesExtension == ".json"); - REQUIRE(res.filePath() == "../samples/myPath.json"); + REQUIRE(res.filePath() == filepath); return res.group; }; - Series series("../samples/myPath.json", Access::CREATE); + Series series(filepath, Access::CREATE); + series.setIterationEncoding(ie); REQUIRE(pathOf(series) == vec_t{}); auto iteration = series.iterations[1234]; REQUIRE(pathOf(iteration) == vec_t{"data", "1234"}); @@ -306,6 +308,13 @@ TEST_CASE("myPath", "[core]") #endif } +TEST_CASE("myPath", "[core]") +{ + myPath("myPath_g", IterationEncoding::groupBased); + myPath("myPath_%T", IterationEncoding::fileBased); + myPath("myPath_v", IterationEncoding::variableBased); +} + TEST_CASE("output_default_test", "[core]") { using IE = IterationEncoding;