Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
a69e466
Add memory selection task to backend
franzpoeschel Aug 7, 2025
9e76f84
Add future helper
franzpoeschel Aug 7, 2025
7af995b
Fixes for UniquePtr.hpp
franzpoeschel Aug 7, 2025
56a9b71
Main implementation of LoadStoreChunk.hpp
franzpoeschel Aug 7, 2025
882b34f
Adapt RecordComponent implementation based on new API
franzpoeschel Aug 7, 2025
c737cac
Testing
franzpoeschel Aug 7, 2025
fa790f3
Enable support for const unique pointers?
franzpoeschel Aug 7, 2025
c7af808
Use a better trick to cheat clang-tidy
franzpoeschel Aug 7, 2025
4def377
clang-tidy fixes
franzpoeschel Aug 8, 2025
26c01aa
Fix linker error
franzpoeschel Jan 8, 2026
c79d407
Reimplement unique_ptr-based span API
franzpoeschel Jan 8, 2026
3eddd1e
Fix parallel tests
franzpoeschel Jan 8, 2026
a4ce159
Type signature fix
franzpoeschel Jan 8, 2026
f8e7232
Destructor for ~DeferredComputation
franzpoeschel Jan 9, 2026
f822646
CI fixes
franzpoeschel Feb 3, 2026
60133c1
wip: runtime-erase pointer type
franzpoeschel Feb 9, 2026
0a53706
Continue
franzpoeschel Feb 9, 2026
9e84ad9
Mostly working again
franzpoeschel Feb 9, 2026
7e77daa
bad fix, remove the bool from python bindings
franzpoeschel Feb 9, 2026
30e2973
Remove bool dataset operations from python API
franzpoeschel Feb 9, 2026
519296a
Fix Coretest
franzpoeschel Feb 9, 2026
6d1715d
instantiate some more methods
franzpoeschel Feb 10, 2026
a76f545
wip: unify common code into compiled functions
franzpoeschel Feb 10, 2026
7f9386a
clang-tidy fixes
franzpoeschel Feb 10, 2026
1c235a2
CI fixes
franzpoeschel Feb 11, 2026
e14d8f2
Remove CRT pattern and core namespace
franzpoeschel Feb 12, 2026
275366e
Move wrappers to header code
franzpoeschel Feb 12, 2026
64ea972
Interface cleanup
franzpoeschel Feb 13, 2026
905d161
Fix after rebase
franzpoeschel Mar 12, 2026
e5c1a26
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 12, 2026
c9e9652
Suffix ..Raw for internal legacy store/load operations
franzpoeschel Apr 7, 2026
3e512d6
Remove enqueue prefixes, these are now default
franzpoeschel Apr 7, 2026
9a02c83
Add future task with caching option
franzpoeschel Apr 7, 2026
38ab670
Add unsafeNoAutomaticFlush
franzpoeschel Apr 7, 2026
3b310fd
add missing constructor
franzpoeschel Apr 7, 2026
c9078a3
remove internal use of raw methods
franzpoeschel Apr 7, 2026
797fcc0
Remove raw methods
franzpoeschel Apr 7, 2026
d67a89e
Avoid if constexpr, msvc doesnt get it
franzpoeschel Apr 8, 2026
f7eb3b4
Add Doxygen documentation to new functions and types
Apr 8, 2026
5324d74
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 8, 2026
8c912b2
CI fixes
franzpoeschel Apr 8, 2026
d872119
Documentation fixes
franzpoeschel Apr 14, 2026
030663a
AI code review
franzpoeschel Apr 20, 2026
c5429f1
Rename forget() -> invalidate()
franzpoeschel Apr 20, 2026
9ca1989
Fix MSVC compilation
franzpoeschel Apr 21, 2026
87ce5f2
Fix move constructor / assignment operator
franzpoeschel Apr 22, 2026
488823e
Add a small test for DeferredComputation
franzpoeschel Apr 22, 2026
0c5edeb
Remove core namespace leftover
franzpoeschel Jun 10, 2026
0970418
Fix operation counting
franzpoeschel Sep 14, 2026
4cbf3c5
nodiscard specifications
franzpoeschel Sep 17, 2026
3f07839
Steal some API additions from later branches
franzpoeschel Sep 17, 2026
aea3953
Track flush counter per record component
franzpoeschel Sep 28, 2026
9a09f88
Remember buffer sizes
franzpoeschel Sep 28, 2026
b8d694b
Document the bufferSize() and withContiguousContainer() APIs
Sep 28, 2026
9a68ee5
Add regression test for per-record-component flush counter
Sep 28, 2026
d85cf97
Preserve full-selection expansion in loadChunkRaw()
Sep 28, 2026
1cabf48
Add tests for container buffer-size validation
Sep 28, 2026
1a24f6b
Check pointer equality in deleter, forbid swapping out pointers
franzpoeschel Sep 29, 2026
2e4f4b2
rename test files
franzpoeschel Sep 29, 2026
2070177
clean up joined dim logic
franzpoeschel Sep 29, 2026
64cccf9
Mutual exclusion between memory selection and joined dimension
franzpoeschel Sep 29, 2026
6b3dac3
Avoid use-after-free, use unique_ptr for storing component handle
franzpoeschel Sep 29, 2026
ab501fb
Fix joined-dimension load path in buffer-based loadChunk/storeChunk
Sep 29, 2026
f2f23f8
Document collective semantics of deferred I/O automatic flush
Sep 29, 2026
34ceee6
Move joined_dim_buffer_api test to its own file
Sep 29, 2026
b1d69ee
Reject chunk loading of joined arrays in loadChunk_impl
Sep 29, 2026
5344209
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 29, 2026
841ff5a
Bound-checking for memory selections
franzpoeschel Sep 29, 2026
0f0a286
Reject unsupported memory selections before enqueueing the chunk
Sep 29, 2026
b9bd62b
Ban memory selections on ADIOS2 versions without reset support
Sep 29, 2026
d500916
Make ConfigureLoadStore copyable
franzpoeschel Sep 30, 2026
e39b7ea
Fix non-ADIOS2 builds
franzpoeschel Oct 2, 2026
e6142c7
Add missing const
franzpoeschel Oct 2, 2026
5656caa
fix capture specification for MSVC's benefit
franzpoeschel Oct 5, 2026
8502718
[rebase me] make buffer-type loadstorechunk config objects copyable
franzpoeschel Oct 5, 2026
7e669d7
msvc fixes...
franzpoeschel Oct 5, 2026
0e33cdc
Reject reusing a buffer configuration after store()/load()
Oct 6, 2026
b5e4bc6
Run pending task when move-assigning a DeferredComputation
Oct 6, 2026
894655c
Treat {0} offset / {-1u} extent as defaults in chaining API
Oct 6, 2026
0b2e43a
Consolidate newly added tests into fewer files
Oct 7, 2026
6809ef3
Cleanup
franzpoeschel Oct 7, 2026
476347f
Do not run Futures upon destruction
franzpoeschel Oct 21, 2026
2d38f97
Fix copying semantics
franzpoeschel Oct 21, 2026
fceffd0
Handle const contiguous buffers
franzpoeschel Oct 21, 2026
1723b0e
Fix deferred API contracts and joined-dimension loads
Oct 21, 2026
ac97218
Shorten deferred API documentation
Oct 21, 2026
7648f39
further cleanup
franzpoeschel Oct 22, 2026
ff6cab3
Fix GCC 11 signature mismatch and Doxygen param warnings
Oct 22, 2026
314a69c
Guard chunk size and memory selection computations against overflow
Oct 27, 2026
e811302
Restore read-mode sanity check in ADIOS2 touch
Oct 27, 2026
12d085e
Update buffer configuration reuse comment
Oct 27, 2026
ba52921
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 29, 2026
c8d9e45
Add get_deferred
franzpoeschel Oct 29, 2026
ab43296
Enqueue flush counter after chunk tasks and clean up
Oct 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/ci/spack-envs/gcc13_py312_mpich_h5_ad2/spack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
#
spack:
specs:
- adios2@2.10 +mpi
# Pin the oldest ADIOS2 release that still lacks resetting of memory
# selections (added upstream in 2.11.0, backported to 2.10.1). This exercises
# the code path that rejects memory selections for such versions.
- adios2@2.10.0 +mpi
- hdf5 +mpi
- mpich

Expand Down
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ set(CORE_SOURCE
src/Format.cpp
src/Iteration.cpp
src/IterationEncoding.cpp
src/LoadStoreChunk.cpp
src/Mesh.cpp
src/ParticlePatches.cpp
src/ParticleSpecies.cpp
Expand All @@ -419,6 +420,7 @@ set(CORE_SOURCE
src/version.cpp
src/auxiliary/Date.cpp
src/auxiliary/Filesystem.cpp
src/auxiliary/Future.cpp
src/auxiliary/JSON.cpp
src/auxiliary/JSONMatcher.cpp
src/auxiliary/Memory.cpp
Expand Down Expand Up @@ -858,6 +860,12 @@ if(openPMD_BUILD_TESTING)
test/Files_SerialIO/components_without_extent.cpp
test/Files_SerialIO/read_while_writing.cpp
test/Files_SerialIO/file_state_lifetime.cpp
test/Files_SerialIO/deferred_load_unrelated_flush.cpp
test/Files_SerialIO/container_buffer_size.cpp
test/Files_SerialIO/buffer_config_single_use.cpp
test/Files_SerialIO/builder_sentinels.cpp
test/Files_SerialIO/joined_dim_buffer_api.cpp
test/Files_SerialIO/memory_selection.cpp
)
elseif(${test_name} STREQUAL "ParallelIO" AND openPMD_HAVE_MPI)
list(APPEND ${out_list}
Expand Down
10 changes: 10 additions & 0 deletions docs/source/details/mpi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Functionality Behavior Description
``::makeConstant`` [3]_ *backend-specific* declare, write
``::storeChunk`` [1]_ independent write
``::loadChunk`` independent read
``::prepareLoadStore()`` independent configure deferred I/O
``DeferredComputation`` [5]_ **collective** deferred store/load + flush
``::availableChunks`` [4]_ collective read, immediate result
============================ ================== ================================

Expand All @@ -49,6 +51,14 @@ Functionality Behavior Description
.. [4] We usually open iterations delayed on first access. This first access is usually the ``flush()`` call after a ``storeChunk``/``loadChunk`` operation. If the first access is non-collective, an explicit, collective ``Iteration::open()`` can be used to have the files already open.
Alternatively, iterations might be accessed for the first time by immediate operations such as ``::availableChunks()``.

.. [5] The experimental deferred I/O API (``RecordComponent::prepareLoadStore()`` followed by ``store()`` / ``load()``) returns handles of type ``auxiliary::DeferredComputation``.
By default, invoking such a handle via ``get()`` / ``operator()()`` triggers the automatic flush of the underlying Series (the data operation itself is enqueued by ``store()`` / ``load()``).
Since ``Series::flush()`` is collective, this makes invoking the handle a collective operation: every rank must invoke its handles in a consistent order, even if the number of ``store()`` / ``load()`` calls differs between ranks.
The per-``RecordComponent`` flush counter is used to skip a flush if the component has already been flushed by another component in the meantime, so a given invocation does not necessarily flush.
The returned handles are marked ``[[nodiscard]]`` and the automatic flush is only triggered by an explicit invocation, so handles cannot be dropped unnoticed.
Call ``unsafeNoAutomaticFlush()`` on the configuration object to disable the automatic flush and instead call ``Series::flush()`` explicitly and collectively at a suitable point.
The destructor of a still-valid handle does not flush; it only prints a warning. Buffers passed to ``store()`` / ``load()`` must stay valid until the next flush point unless owned by the operation.

.. warning::

The openPMD-api will by default flush only those Iterations which are dirty, i.e. have been written to.
Expand Down
2 changes: 1 addition & 1 deletion docs/source/dev/design.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Therefore, enabling users to handle hierarchical, self-describing file formats w

.. literalinclude:: IOTask.hpp
:language: cpp
:lines: 50-81
:lines: 57-91

Every task is designed to be a fully self-contained description of one such atomic operation. By describing a required minimal step of work (without any side-effect), these operations are the foundation of the unified handling mechanism across suitable file formats.
The actual low-level exchange of data is implemented in ``IOHandlers``, one per file format (possibly two if handlingi MPI-parallel work is possible and requires different behaviour).
Expand Down
14 changes: 12 additions & 2 deletions docs/source/usage/workflow.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ Flush points are triggered by:
Flush point guarantees affect only the corresponding iteration.
* Calling ``Writable::seriesFlush()`` or ``Attributable::seriesFlush()``.
* The streaming API (i.e. ``Series.readIterations()`` and ``Series.writeIteration()``) automatically before accessing the next iteration.
* Invoking a handle returned by the experimental deferred I/O API (``RecordComponent::prepareLoadStore()`` followed by ``store()`` / ``load()``) via ``get()`` / ``operator()()``, unless automatic flushing was disabled via ``unsafeNoAutomaticFlush()``.

.. note::

The automatic flush performed by the deferred I/O handles is MPI-collective (see :ref:`details-mpi`).
Each rank must invoke such a handle the same number of times and in the same order, *even if* the number of ``store()`` / ``load()`` calls differs per rank.
The flush is skipped if the pertaining ``RecordComponent`` has already been flushed by another component in the meantime (tracked via per-component flush counters), so a given invocation does not necessarily flush.
To avoid this coupling, disable the automatic flush with ``unsafeNoAutomaticFlush()`` and call ``Series::flush()`` explicitly at a collective point.
The handles are returned with the ``[[nodiscard]]`` attribute and the flush is only triggered by explicitly invoking them; destroying a still-valid handle skips the flush and prints a warning.
Note that ``store()`` / ``load()`` enqueue immediately, so buffers not owned by the operation must stay valid until the next flush point.

Attributes are (currently) unaffected by this:

Expand All @@ -142,8 +152,8 @@ Attributes are (currently) unaffected by this:
For user-guided selection of such implementations, ``Series::flush`` and ``Attributable::seriesFlush()`` take an optional JSON/TOML string as a parameter.
See the section on :ref:`backend-specific configuration <backendconfig>` for details.

Deferred Data API Contract
--------------------------
Verbose Logging
---------------

A verbose debug log can optionally be printed to the standard error output by specifying the environment variable ``OPENPMD_VERBOSE=1``.
Note that this functionality is at the current time still relatively basic.
12 changes: 12 additions & 0 deletions include/openPMD/Dataset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ namespace openPMD
using Extent = std::vector<std::uint64_t>;
using Offset = std::vector<std::uint64_t>;

/** Selection of a region of memory for storing chunks.
*
* Used to specify a non-contiguous memory region when storing
* data chunks. This allows writing data that is not contiguous
* in memory.
*/
struct MemorySelection
{
Offset offset;
Extent extent;
};

class Dataset
{
friend class RecordComponent;
Expand Down
62 changes: 59 additions & 3 deletions include/openPMD/Datatype.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,11 @@ inline size_t toBits(Datatype d)
return toBytes(d) * CHAR_BIT;
}

/** Check if a Datatype is a signed type
*
* @param d Datatype to test
* @return true if signed type (integer, floating point, complex), else false
*/
constexpr bool isSigned(Datatype d);

/** Compare if a Datatype is a vector type
Expand Down Expand Up @@ -602,6 +607,13 @@ inline bool isSameFloatingPoint(Datatype d)
return isSameFloatingPoint(d, determineDatatype<T_FP>());
}

/** Compare if two Datatypes are equivalent floating point types
*
* @param d1 First Datatype to compare
* @param d2 Second Datatype to compare
* @return true if both types are floating point and have same bitness, else
* false
*/
inline bool isSameFloatingPoint(Datatype d1, Datatype d2)
{
// template
Expand Down Expand Up @@ -629,6 +641,13 @@ inline bool isSameComplexFloatingPoint(Datatype d)
return isSameComplexFloatingPoint(d, determineDatatype<T_CFP>());
}

/** Compare if two Datatypes are equivalent complex floating point types
*
* @param d1 First Datatype to compare
* @param d2 Second Datatype to compare
* @return true if both types are complex floating point and have same bitness,
* else false
*/
inline bool isSameComplexFloatingPoint(Datatype d1, Datatype d2)
{
// template
Expand Down Expand Up @@ -656,6 +675,13 @@ inline bool isSameInteger(Datatype d)
return isSameInteger(d, determineDatatype<T_Int>());
}

/** Compare if two Datatypes are equivalent integer types
*
* @param d1 First Datatype to compare
* @param d2 Second Datatype to compare
* @return true if both types are integers, same signedness and same bitness,
* else false
*/
inline bool isSameInteger(Datatype d1, Datatype d2)
{
// template
Expand Down Expand Up @@ -708,13 +734,24 @@ constexpr bool isChar(Datatype d)
template <typename T_Char>
constexpr bool isSameChar(Datatype d);

/** Compare if two Datatypes are equivalent char types
*
* @param d1 First Datatype to compare
* @param d2 Second Datatype to compare
* @return true if both types are chars with same signedness and size, else
* false
*/
constexpr bool isSameChar(Datatype d1, Datatype d2);

/** Comparison for two Datatypes
*
* Besides returning true for the same types, identical implementations on
* some platforms, e.g. if long and long long are the same or double and
* long double will also return true.
*
* @param d First Datatype to compare
* @param e Second Datatype to compare
* @return true if the datatypes are equivalent
*/
constexpr bool isSame(openPMD::Datatype d, openPMD::Datatype e);

Expand All @@ -726,15 +763,34 @@ constexpr bool isSame(openPMD::Datatype d, openPMD::Datatype e);
*/
Datatype basicDatatype(Datatype dt);

/** Convert a scalar Datatype to its vector variant
*
* @param dt Scalar Datatype to convert
* @return Vector Datatype (e.g., INT becomes VEC_INT)
*/
Datatype toVectorType(Datatype dt);

/** Convert a Datatype to its string representation
*
* @param dt Datatype to convert
* @return String representation of the Datatype
*/
std::string datatypeToString(Datatype dt);

/** Convert a string to a Datatype
*
* @param s String representation of a Datatype
* @return The corresponding Datatype
*/
Datatype stringToDatatype(const std::string &s);

void warnWrongDtype(std::string const &key, Datatype store, Datatype request);

std::ostream &operator<<(std::ostream &, openPMD::Datatype const &);
/** Stream operator for Datatype
*
* @param os Output stream
* @param dt Datatype to output
* @return Reference to the stream
*/
std::ostream &operator<<(std::ostream &os, openPMD::Datatype const &dt);

template <typename T>
constexpr auto datatypeIndex() -> size_t
Expand Down
55 changes: 33 additions & 22 deletions include/openPMD/Datatype.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,36 +223,52 @@ namespace detail
template <typename T>
constexpr bool is_char_v = is_char<T>::value;

template <typename T_Char1, typename T_Char2>
inline bool isSameChar()
struct IsChar
{
return
// both must be char types
is_char_v<T_Char1> && is_char_v<T_Char2> &&
// both must have equivalent sign
std::is_signed_v<T_Char1> == std::is_signed_v<T_Char2> &&
// both must have equivalent size
sizeof(T_Char1) == sizeof(T_Char2);
template <typename T>
static constexpr bool call()
{
return is_char_v<T>;
}
template <size_t N>
static constexpr bool call()
{
return false;
}
};

constexpr inline bool isChar(Datatype dtype)
{
return switchType<IsChar>(dtype);
}

template <typename T1>
struct IsSameChar
struct DtypeSize
{
template <typename T2>
static bool call()
template <typename T>
static constexpr size_t call()
{
return isSameChar<T1, T2>();
return sizeof(T);
}

static constexpr char const *errorMsg = "IsSameChar";
static constexpr char const *errorMsg = "DtypeSize";
};

constexpr inline size_t dtypeSize(Datatype dtype)
{
return switchType<DtypeSize>(dtype);
}
} // namespace detail

template <typename T_Char>
constexpr inline bool isSameChar(Datatype d)
{
return switchType<detail::IsSameChar<T_Char>>(d);
return isSameChar(d, determineDatatype<T_Char>());
}

constexpr bool isSameChar(Datatype d1, Datatype d2)
{
return detail::isChar(d1) && detail::isChar(d2) &&
isSigned(d1) == isSigned(d2) &&
detail::dtypeSize(d1) == detail::dtypeSize(d2);
}

namespace detail
Expand Down Expand Up @@ -285,11 +301,6 @@ constexpr inline bool isSigned(Datatype d)
return switchType<detail::IsSigned>(d);
}

constexpr inline bool isSameChar(Datatype d, Datatype e)
{
return isChar(d) && isChar(e) && isSigned(d) == isSigned(e);
}

constexpr bool isSame(openPMD::Datatype const d, openPMD::Datatype const e)
{
return
Expand Down
4 changes: 4 additions & 0 deletions include/openPMD/IO/ADIOS/ADIOS2File.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
*/
#pragma once

#include "openPMD/Dataset.hpp"
#include "openPMD/IO/ADIOS/ADIOS2Auxiliary.hpp"
#include "openPMD/IO/ADIOS/ADIOS2PreloadAttributes.hpp"
#include "openPMD/IO/ADIOS/ADIOS2PreloadVariables.hpp"
Expand Down Expand Up @@ -107,11 +108,14 @@ struct WriteDataset
static void call(Params &&...);
};

/** Buffered put operation with unique pointer */
struct BufferedUniquePtrPut
{
std::string name;
Offset offset;
Extent extent;
/** Optional memory selection for non-contiguous memory regions */
std::optional<MemorySelection> memorySelection;
UniquePtrWithLambda<void> data;
Datatype dtype = Datatype::UNDEFINED;

Expand Down
Loading
Loading