Skip to content

Commit a83bc22

Browse files
franzpoeschelax3l
andauthored
Mapping between ADIOS steps and openPMD iterations (#949)
* Backend additions 1) New streaming status: RANDOM_ACCESS, for non-streaming situations 2) Variable attributes, to be written only if the backend has support for steps * Writing changes: Write current step(s) to snapshot attribute Only set snapshot attribute if Iteration is not yet written For v-based iteration encoding, the snapshot attribute is already being set before this PR. Just add a comment there. Also add missing <cstdint> includes Co-authored-by: Axel Huebl <[email protected]> * Reading changes: Use snapshot attribute This means that the snapshot attribute, if present, is used for accessing iterations inside `series.readIterations()`. Fallback to the old behavior (linear progression through iterations) if the attribute is not found. Variable-b. encoding: Allow several (equivalent) iterations per step This means that a single step can be marked by /data/snapshot to represent iterations 0,10,20,30 at the same time. The underlying data is the same, but the API will treat it as 4 times a different iteration with equivalent content. Avoid const_cast by introducing a parsing state and use that when re-parsing. Skip repeated iterations that occur in Append mode Before the explicit iteration-step mapping, these were not seen by reading procedures at all. Now they are, so we skip the second instance. Better error message when calling readIterations() too late This commit includes some refactoring 1. Remove recursion of operator++(), this leads to constant memory usage rather than filling the stack at some point 2. Extract subroutines from operator++() 3. Steal some refactoring that solved some bugs on topic-read-leniently, so it stands to reason that we should apply it here already * Testing In the tests, don't try to read the series with listSeries after already having fully drained it Combined test: append mode and weird iteration order Deactivate troublesome Schema 2021 Append test * Add -wd1011 flag to Icc workflow * Fix priority of JSON/envvar config for ADIOS2 schema * Preview support for Linear read mode without snapshot attribute Currently only available for BP5 engine, will be generalized into Linear read mode in #1291. If the backend does not support the snapshot attribute, then iterate in ascending order, skipping duplicate and non-linear iteration indices. Not possible if the Series is parsed ahead of time. * Test edge cases of snapshot attribute Co-authored-by: Axel Huebl <[email protected]>
1 parent a8436ab commit a83bc22

22 files changed

+1093
-200
lines changed

.github/workflows/intel.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ jobs:
1717
run: |
1818
sudo .github/workflows/dependencies/install_icc
1919
- name: Build
20-
env: {CXXFLAGS: -Werror}
20+
# Due to compiler bugs in Intel compiler, we need to disable warning 1011
21+
# (missing return value), otherwise `if constexpr` functions
22+
# don't compile.
23+
# See https://community.intel.com/t5/Intel-C-Compiler/quot-if-constexpr-quot-and-quot-missing-return-statement-quot-in/td-p/1154551
24+
# Using a local pragma does not work due to the reasons stated there.
25+
env: {CXXFLAGS: -Werror -wd1011}
2126
run: |
2227
set +e; source /opt/intel/oneapi/setvars.sh; set -e
2328
share/openPMD/download_samples.sh build

include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,13 +1155,6 @@ namespace detail
11551155
*/
11561156
void invalidateVariablesMap();
11571157

1158-
private:
1159-
ADIOS2IOHandlerImpl *m_impl;
1160-
std::optional<adios2::Engine> m_engine; //! ADIOS engine
1161-
/**
1162-
* The ADIOS2 engine type, to be passed to adios2::IO::SetEngine
1163-
*/
1164-
std::string m_engineType;
11651158
/*
11661159
* streamStatus is NoStream for file-based ADIOS engines.
11671160
* This is relevant for the method BufferedActions::requireActiveStep,
@@ -1253,6 +1246,14 @@ namespace detail
12531246
};
12541247
StreamStatus streamStatus = StreamStatus::OutsideOfStep;
12551248

1249+
private:
1250+
ADIOS2IOHandlerImpl *m_impl;
1251+
std::optional<adios2::Engine> m_engine; //! ADIOS engine
1252+
/**
1253+
* The ADIOS2 engine type, to be passed to adios2::IO::SetEngine
1254+
*/
1255+
std::string m_engineType;
1256+
12561257
/**
12571258
* See documentation for StreamStatus::Parsing.
12581259
* Will be set true under the circumstance described there in order to

include/openPMD/IO/AbstractIOHandler.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,28 @@ namespace internal
121121
FlushParams const defaultFlushParams{};
122122

123123
struct ParsedFlushParams;
124+
125+
/**
126+
* Some parts of the openPMD object model are read-only when accessing
127+
* a Series in Access::READ_ONLY mode, notably Containers and Attributes.
128+
* They are filled at parse time and not modified afterwards.
129+
* Such state-changing operations are hence allowed under either of two
130+
* conditions:
131+
* 1) The Series is opened in an open mode that allows writing in any way.
132+
* (Currently any but Access::READ_ONLY).
133+
* 2) The Series is in Parsing state. This way, modifying the open mode
134+
* during parsing can be avoided.
135+
*/
136+
enum class SeriesStatus : unsigned char
137+
{
138+
Default, ///< Mutability of objects in the openPMD object model is
139+
///< determined by the open mode (Access enum), normal state in
140+
///< which the user interacts with the Series.
141+
Parsing ///< All objects in the openPMD object model are temporarily
142+
///< mutable to allow inserting newly-parsed data.
143+
///< Special state only active while internal routines are
144+
///< running.
145+
};
124146
} // namespace internal
125147

126148
/** Interface for communicating between logical and physically persistent data.
@@ -192,6 +214,7 @@ class AbstractIOHandler
192214
// why do these need to be separate?
193215
Access const m_backendAccess;
194216
Access const m_frontendAccess;
217+
internal::SeriesStatus m_seriesStatus = internal::SeriesStatus::Default;
195218
std::queue<IOTask> m_work;
196219
}; // AbstractIOHandler
197220

include/openPMD/IO/AbstractIOHandlerImpl.hpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,10 @@ class AbstractIOHandlerImpl
252252
* The advance mode is determined by parameters.mode.
253253
* The return status code shall be stored as parameters.status.
254254
*/
255-
virtual void advance(Writable *, Parameter<Operation::ADVANCE> &)
256-
{}
255+
virtual void advance(Writable *, Parameter<Operation::ADVANCE> &parameters)
256+
{
257+
*parameters.status = AdvanceStatus::RANDOMACCESS;
258+
}
257259

258260
/** Close an openPMD group.
259261
*
@@ -488,8 +490,13 @@ class AbstractIOHandlerImpl
488490
* datatype parameters.dtype. Any existing attribute with the same name
489491
* should be overwritten. If possible, only the value should be changed if
490492
* the datatype stays the same. The attribute should be written to physical
491-
* storage after the operation completes successfully. All datatypes of
492-
* Datatype should be supported in a type-safe way.
493+
* storage after the operation completes successfully. If the parameter
494+
* changesOverSteps is true, then the attribute must be able to hold
495+
* different values across IO steps. If the backend does not support IO
496+
* steps in such a way, the attribute should not be written. (IO steps are
497+
* an optional backend feature and the frontend must implement fallback
498+
* measures in such a case) All datatypes of Datatype should be supported in
499+
* a type-safe way.
493500
*/
494501
virtual void
495502
writeAttribute(Writable *, Parameter<Operation::WRITE_ATT> const &) = 0;

include/openPMD/IO/IOTask.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,7 @@ struct OPENPMDAPI_EXPORT Parameter<Operation::WRITE_ATT>
537537
: AbstractParameter()
538538
, name(p.name)
539539
, dtype(p.dtype)
540+
, changesOverSteps(p.changesOverSteps)
540541
, resource(p.resource)
541542
{}
542543

@@ -548,6 +549,13 @@ struct OPENPMDAPI_EXPORT Parameter<Operation::WRITE_ATT>
548549

549550
std::string name = "";
550551
Datatype dtype = Datatype::UNDEFINED;
552+
/*
553+
* If true, this attribute changes across IO steps.
554+
* It should only be written in backends that support IO steps,
555+
* otherwise writing should be skipped.
556+
* The frontend is responsible for handling both situations.
557+
*/
558+
bool changesOverSteps = false;
551559
Attribute::resource resource;
552560
};
553561

include/openPMD/Iteration.hpp

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@
2828
#include "openPMD/backend/Attributable.hpp"
2929
#include "openPMD/backend/Container.hpp"
3030

31+
#include <cstdint>
32+
#include <deque>
3133
#include <optional>
34+
#include <tuple>
3235

3336
namespace openPMD
3437
{
@@ -282,14 +285,57 @@ class Iteration : public Attributable
282285
void readGorVBased(std::string const &groupPath, bool beginStep);
283286
void read_impl(std::string const &groupPath);
284287

288+
/**
289+
* Status after beginning an IO step. Currently includes:
290+
* * The advance status (OK, OVER, RANDOMACCESS)
291+
* * The opened iterations, in case the snapshot attribute is found
292+
*/
293+
struct BeginStepStatus
294+
{
295+
using AvailableIterations_t = std::optional<std::deque<uint64_t> >;
296+
297+
AdvanceStatus stepStatus{};
298+
/*
299+
* If the iteration attribute `snapshot` is present, the value of that
300+
* attribute. Otherwise empty.
301+
*/
302+
AvailableIterations_t iterationsInOpenedStep;
303+
304+
/*
305+
* Most of the time, the AdvanceStatus part of this struct is what we
306+
* need, so let's make it easy to access.
307+
*/
308+
inline operator AdvanceStatus() const
309+
{
310+
return stepStatus;
311+
}
312+
313+
/*
314+
* Support for std::tie()
315+
*/
316+
inline operator std::tuple<AdvanceStatus &, AvailableIterations_t &>()
317+
{
318+
return std::tuple<AdvanceStatus &, AvailableIterations_t &>{
319+
stepStatus, iterationsInOpenedStep};
320+
}
321+
};
322+
285323
/**
286324
* @brief Begin an IO step on the IO file (or file-like object)
287325
* containing this iteration. In case of group-based iteration
288326
* layout, this will be the complete Series.
289327
*
290-
* @return AdvanceStatus
328+
* @return BeginStepStatus
329+
*/
330+
BeginStepStatus beginStep(bool reread);
331+
332+
/*
333+
* Iteration-independent variant for beginStep().
334+
* Useful in group-based iteration encoding where the Iteration will only
335+
* be known after opening the step.
291336
*/
292-
AdvanceStatus beginStep(bool reread);
337+
static BeginStepStatus
338+
beginStep(std::optional<Iteration> thisObject, Series &series, bool reread);
293339

294340
/**
295341
* @brief End an IO step on the IO file (or file-like object)

include/openPMD/ReadIterations.hpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include "openPMD/Iteration.hpp"
2424
#include "openPMD/Series.hpp"
2525

26+
#include <deque>
27+
#include <iostream>
2628
#include <optional>
2729

2830
namespace openPMD
@@ -54,7 +56,8 @@ class SeriesIterator
5456
using maybe_series_t = std::optional<Series>;
5557

5658
maybe_series_t m_series;
57-
iteration_index_t m_currentIteration = 0;
59+
std::deque<iteration_index_t> m_iterationsInCurrentStep;
60+
uint64_t m_currentIteration{};
5861

5962
public:
6063
//! construct the end() iterator
@@ -71,6 +74,39 @@ class SeriesIterator
7174
bool operator!=(SeriesIterator const &other) const;
7275

7376
static SeriesIterator end();
77+
78+
private:
79+
inline bool setCurrentIteration()
80+
{
81+
if (m_iterationsInCurrentStep.empty())
82+
{
83+
std::cerr << "[ReadIterations] Encountered a step without "
84+
"iterations. Closing the Series."
85+
<< std::endl;
86+
*this = end();
87+
return false;
88+
}
89+
m_currentIteration = *m_iterationsInCurrentStep.begin();
90+
return true;
91+
}
92+
93+
inline std::optional<uint64_t> peekCurrentIteration()
94+
{
95+
if (m_iterationsInCurrentStep.empty())
96+
{
97+
return std::nullopt;
98+
}
99+
else
100+
{
101+
return {*m_iterationsInCurrentStep.begin()};
102+
}
103+
}
104+
105+
std::optional<SeriesIterator *> nextIterationInStep();
106+
107+
std::optional<SeriesIterator *> nextStep();
108+
109+
std::optional<SeriesIterator *> loopBody();
74110
};
75111

76112
/**

include/openPMD/Series.hpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@
3737
#include <mpi.h>
3838
#endif
3939

40+
#include <cstdint>
41+
#include <deque>
4042
#include <map>
4143
#include <optional>
44+
#include <set>
4245
#include <string>
4346

4447
// expose private and protected members for invasive testing
@@ -82,6 +85,12 @@ namespace internal
8285
* the same instance.
8386
*/
8487
std::optional<WriteIterations> m_writeIterations;
88+
/**
89+
* For writing: Remember which iterations have been written in the
90+
* currently active output step. Use this later when writing the
91+
* snapshot attribute.
92+
*/
93+
std::set<uint64_t> m_currentlyActiveIterations;
8594
/**
8695
* Needed if reading a single iteration of a file-based series.
8796
* Users may specify the concrete filename of one iteration instead of
@@ -576,8 +585,10 @@ OPENPMD_private
576585
* Note on re-parsing of a Series:
577586
* If init == false, the parsing process will seek for new
578587
* Iterations/Records/Record Components etc.
588+
* If series.iterations contains the attribute `snapshot`, returns its
589+
* value.
579590
*/
580-
void readGorVBased(bool init = true);
591+
std::optional<std::deque<uint64_t> > readGorVBased(bool init = true);
581592
void readBase();
582593
std::string iterationFilename(uint64_t i);
583594

@@ -627,6 +638,22 @@ OPENPMD_private
627638
internal::AttributableData &file,
628639
iterations_iterator it,
629640
Iteration &iteration);
641+
642+
AdvanceStatus advance(AdvanceMode mode);
643+
644+
/**
645+
* @brief Called at the end of an IO step to store the iterations defined
646+
* in the IO step to the snapshot attribute.
647+
*
648+
* @param doFlush If true, flush the IO handler.
649+
*/
650+
void flushStep(bool doFlush);
651+
652+
/*
653+
* Returns the current content of the /data/snapshot attribute.
654+
* (We could also add this to the public API some time)
655+
*/
656+
std::optional<std::vector<uint64_t> > currentSnapshot() const;
630657
}; // Series
631658
} // namespace openPMD
632659

include/openPMD/Streaming.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ namespace openPMD
1919
*/
2020
enum class AdvanceStatus : unsigned char
2121
{
22-
OK, /* stream goes on */
23-
OVER /* stream is over */
22+
OK, ///< stream goes on
23+
OVER, ///< stream is over
24+
RANDOMACCESS ///< there is no stream, it will never be over
2425
};
2526

2627
/**

include/openPMD/backend/Attributable.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,9 @@ inline bool Attributable::setAttributeImpl(
463463
internal::attr_value_check(key, value, setAttributeMode);
464464

465465
auto &attri = get();
466-
if (IOHandler() && Access::READ_ONLY == IOHandler()->m_frontendAccess)
466+
if (IOHandler() &&
467+
IOHandler()->m_seriesStatus == internal::SeriesStatus::Default &&
468+
Access::READ_ONLY == IOHandler()->m_frontendAccess)
467469
{
468470
auxiliary::OutOfRangeMsg const out_of_range_msg(
469471
"Attribute", "can not be set (read-only).");

0 commit comments

Comments
 (0)