diff --git a/include/openPMD/Series.hpp b/include/openPMD/Series.hpp index 094ed24fbf..6035415e00 100644 --- a/include/openPMD/Series.hpp +++ b/include/openPMD/Series.hpp @@ -179,6 +179,8 @@ namespace internal */ bool m_wroteAtLeastOneIOStep = false; + bool m_containedAtLeastOneIteration = false; + /** * Remember the preference that the backend specified for parsing. * Not used in file-based iteration encoding, empty then. @@ -750,6 +752,10 @@ OPENPMD_private * (We could also add this to the public API some time) */ std::optional > currentSnapshot() const; + + Iteration resetIteration(IterationIndex_t); + std::pair + resetIteration(decltype(iterations)::iterator); }; // Series } // namespace openPMD diff --git a/src/ReadIterations.cpp b/src/ReadIterations.cpp index 9662cb489c..a787b0301d 100644 --- a/src/ReadIterations.cpp +++ b/src/ReadIterations.cpp @@ -444,9 +444,12 @@ std::optional SeriesIterator::loopBody() } } + auto oldIterationIndex = data.currentIteration; + auto guardReturn = - [&series, &iterations]( + [&series, &iterations, oldIterationIndex]( auto const &option) -> std::optional { + series.resetIteration(oldIterationIndex); if (!option.has_value() || *option.value() == end()) { return option; @@ -489,6 +492,7 @@ std::optional SeriesIterator::loopBody() { // we had this iteration already, skip it iteration.endStep(); + series.resetIteration(index); return std::nullopt; // empty, go into next iteration } } @@ -496,6 +500,7 @@ std::optional SeriesIterator::loopBody() { // we had this iteration already, skip it series.advance(AdvanceMode::ENDSTEP); + series.resetIteration(index); return std::nullopt; } }; @@ -514,6 +519,7 @@ std::optional SeriesIterator::loopBody() if (series.iterationEncoding() == IterationEncoding::fileBased) { // this one is handled above, stream is over once it proceeds to here + series.resetIteration(oldIterationIndex); this->close(); return {this}; } @@ -529,7 +535,7 @@ void SeriesIterator::deactivateDeadIteration(iteration_index_t index) { case IterationEncoding::fileBased: { Parameter param; - Iteration resetted = data.series->iterations[index].resetIteration(); + Iteration resetted = data.series->resetIteration(index); auto writable = &resetted.writable(); param.keep_this_data_alive = std::move(resetted); data.series->IOHandler()->enqueue(IOTask(writable, std::move(param))); diff --git a/src/Series.cpp b/src/Series.cpp index da638ce2ee..eb238554b2 100644 --- a/src/Series.cpp +++ b/src/Series.cpp @@ -749,7 +749,7 @@ void Series::flushFileBased( bool flushIOHandler) { auto &series = get(); - if (end == begin) + if (end == begin && !series.m_containedAtLeastOneIteration) throw std::runtime_error( "fileBased output can not be written with no iterations."); @@ -2469,6 +2469,25 @@ auto Series::currentSnapshot() const } } +Iteration Series::resetIteration(IterationIndex_t idx) +{ + auto it = iterations.find(idx); + if (it == iterations.end()) + { + return {}; + } + return resetIteration(it).second; +} + +auto Series::resetIteration(decltype(iterations)::iterator it) + -> std::pair +{ + auto res = it->second; + it = iterations.container().erase(it); + get().m_containedAtLeastOneIteration = true; + return {it, res}; +} + namespace { CleanedFilename cleanFilename( diff --git a/test/SerialIOTest.cpp b/test/SerialIOTest.cpp index 53e6007c72..af75cf5a71 100644 --- a/test/SerialIOTest.cpp +++ b/test/SerialIOTest.cpp @@ -187,6 +187,8 @@ void write_and_read_many_iterations( Series read( filename, Access::READ_ONLY, "{\"defer_iteration_parsing\": true}"); + std::cout << "BEFORE: " << read.iterations.size() << " ITERATIONS" + << std::endl; for (auto iteration : read.iterations) { iteration.second.open(); @@ -206,10 +208,14 @@ void write_and_read_many_iterations( REQUIRE(array[i] == float(i)); } } + std::cout << "AFTER: " << read.iterations.size() << " ITERATIONS" + << std::endl; + REQUIRE(read.iterations.size() == 10); } Series list(filename, Access::READ_ONLY); helper::listSeries(list); + REQUIRE(list.iterations.size() == 0); } TEST_CASE("write_and_read_many_iterations", "[serial]")