Skip to content

Commit 0a7e0a7

Browse files
gujpre-commit-ci[bot]franzpoeschel
authored
working around an unusual encounter when the joined_dim has actual value "max::size_t - 1" (#1740)
* working around an unusal encounter when the joined_dim has actual value "max::size_t - 1" * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add test for redundant resetDataset() * Merge check into above logic * Better error messages in verifyDataset * Add further safety guards to createDataset and extendDataset tasks * Move joinedDim logic into middle-end for extendDataset * Update include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Franz Pöschel <franz.poeschel@gmail.com>
1 parent 6d02d44 commit 0a7e0a7

File tree

15 files changed

+211
-52
lines changed

15 files changed

+211
-52
lines changed

include/openPMD/Dataset.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,5 +86,6 @@ class Dataset
8686
bool empty() const;
8787

8888
std::optional<size_t> joinedDimension() const;
89+
static std::optional<size_t> joinedDimension(Extent const &);
8990
};
9091
} // namespace openPMD

include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "openPMD/IterationEncoding.hpp"
3535
#include "openPMD/ThrowError.hpp"
3636
#include "openPMD/auxiliary/JSON_internal.hpp"
37+
#include "openPMD/auxiliary/StringManip.hpp"
3738
#include "openPMD/backend/Writable.hpp"
3839
#include "openPMD/config.hpp"
3940
#include <stdexcept>
@@ -489,20 +490,44 @@ class ADIOS2IOHandlerImpl
489490
}
490491
}
491492
auto joinedDim = joinedDimension(shape);
492-
if (joinedDim.has_value())
493+
auto make_runtime_error = [&](char const *message) {
494+
std::stringstream s;
495+
s << "[ADIOS2IOHandlerImpl::verifyDataset()] " << message;
496+
s << "\nNote: Variable '" << varName << "' has shape ";
497+
auxiliary::write_vec_to_stream(s, shape)
498+
<< ", is accessed from offset ";
499+
auxiliary::write_vec_to_stream(s, offset) << " with extent ";
500+
auxiliary::write_vec_to_stream(s, extent);
501+
if (joinedDim.has_value())
502+
{
503+
s << " (joined dimension on index " << *joinedDim << ").";
504+
}
505+
else
506+
{
507+
s << " (no joined dimension).";
508+
}
509+
return std::runtime_error(s.str());
510+
};
511+
if (joinedDim.has_value() ||
512+
var.ShapeID() == adios2::ShapeID::JoinedArray)
493513
{
494514
if (!offset.empty())
495515
{
496-
throw std::runtime_error(
497-
"[ADIOS2] Offset must be an empty vector in case of joined "
498-
"array.");
516+
throw make_runtime_error(
517+
"Offset must be an empty vector in case of joined array.");
518+
}
519+
if (!joinedDim.has_value())
520+
{
521+
throw make_runtime_error(
522+
"Trying to access a dataset as a non-joined array, but it "
523+
"has previously been array.");
499524
}
500525
for (unsigned int i = 0; i < actualDim; i++)
501526
{
502527
if (*joinedDim != i && extent[i] != shape[i])
503528
{
504-
throw std::runtime_error(
505-
"[ADIOS2] store_chunk extent of non-joined dimensions "
529+
throw make_runtime_error(
530+
"store_chunk extent of non-joined dimensions "
506531
"must be equivalent to the total extent.");
507532
}
508533
}
@@ -514,8 +539,7 @@ class ADIOS2IOHandlerImpl
514539
if (!(joinedDim.has_value() && *joinedDim == i) &&
515540
offset[i] + extent[i] > shape[i])
516541
{
517-
throw std::runtime_error(
518-
"[ADIOS2] Dataset access out of bounds.");
542+
throw make_runtime_error("Dataset access out of bounds.");
519543
}
520544
}
521545
}

include/openPMD/IO/IOTask.hpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ struct OPENPMDAPI_EXPORT AbstractParameter
116116
std::string const &currentBackendName,
117117
std::string const &warningMessage);
118118

119+
// Used as a tag in some constructors to make callsites explicitly
120+
// acknowledge that joined dimensions will not be automatically resolved
121+
struct I_dont_want_to_use_joined_dimensions_t
122+
{};
123+
constexpr static I_dont_want_to_use_joined_dimensions_t
124+
I_dont_want_to_use_joined_dimensions{};
125+
119126
protected:
120127
// avoid object slicing
121128
// by allow only child classes to use these things for defining their own
@@ -359,7 +366,17 @@ template <>
359366
struct OPENPMDAPI_EXPORT Parameter<Operation::CREATE_DATASET>
360367
: public AbstractParameter
361368
{
362-
Parameter() = default;
369+
Parameter(Dataset const &ds)
370+
: extent(ds.extent)
371+
, dtype(ds.dtype)
372+
, options(ds.options)
373+
, joinedDimension(ds.joinedDimension())
374+
{}
375+
376+
// default constructor, but callsites need to explicitly acknowledge that
377+
// joined dimensions will not be automatically configured when using it
378+
Parameter(I_dont_want_to_use_joined_dimensions_t)
379+
{}
363380
Parameter(Parameter &&) = default;
364381
Parameter(Parameter const &) = default;
365382
Parameter &operator=(Parameter &&) = default;
@@ -388,7 +405,15 @@ template <>
388405
struct OPENPMDAPI_EXPORT Parameter<Operation::EXTEND_DATASET>
389406
: public AbstractParameter
390407
{
391-
Parameter() = default;
408+
Parameter(Extent e) : joinedDimension(Dataset::joinedDimension(e))
409+
{
410+
this->extent = std::move(e);
411+
}
412+
413+
// default constructor, but callsites need to explicitly acknowledge that
414+
// joined dimensions will not be automatically configured when using it
415+
Parameter(I_dont_want_to_use_joined_dimensions_t)
416+
{}
392417
Parameter(Parameter &&) = default;
393418
Parameter(Parameter const &) = default;
394419
Parameter &operator=(Parameter &&) = default;
@@ -401,6 +426,7 @@ struct OPENPMDAPI_EXPORT Parameter<Operation::EXTEND_DATASET>
401426
}
402427

403428
Extent extent = {};
429+
std::optional<size_t> joinedDimension;
404430
};
405431

406432
template <>

include/openPMD/RecordComponent.tpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -356,18 +356,14 @@ RecordComponent::storeChunk(Offset o, Extent e, F &&createBuffer)
356356
if (!written())
357357
{
358358
auto &rc = get();
359-
Parameter<Operation::CREATE_DATASET> dCreate;
360-
dCreate.name = rc.m_name;
361-
dCreate.extent = getExtent();
362-
dCreate.dtype = getDatatype();
363-
dCreate.joinedDimension = joinedDimension();
364359
if (!rc.m_dataset.has_value())
365360
{
366361
throw error::WrongAPIUsage(
367362
"[RecordComponent] Must specify dataset type and extent before "
368363
"using storeChunk() (see RecordComponent::resetDataset()).");
369364
}
370-
dCreate.options = rc.m_dataset.value().options;
365+
Parameter<Operation::CREATE_DATASET> dCreate(rc.m_dataset.value());
366+
dCreate.name = rc.m_name;
371367
IOHandler()->enqueue(IOTask(this, dCreate));
372368
}
373369
Parameter<Operation::GET_BUFFER_VIEW> getBufferView;

include/openPMD/auxiliary/StringManip.hpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,5 +242,40 @@ namespace auxiliary
242242
});
243243
return std::forward<S>(s);
244244
}
245+
246+
template <typename Stream, typename Vec>
247+
auto write_vec_to_stream(Stream &&s, Vec const &vec) -> Stream &&
248+
{
249+
if (vec.empty())
250+
{
251+
s << "[]";
252+
}
253+
else
254+
{
255+
s << '[';
256+
auto it = vec.begin();
257+
s << *it++;
258+
auto end = vec.end();
259+
for (; it != end; ++it)
260+
{
261+
s << ", " << *it;
262+
}
263+
s << ']';
264+
}
265+
return std::forward<Stream>(s);
266+
}
267+
268+
template <typename Vec>
269+
auto vec_as_string(Vec const &vec) -> std::string
270+
{
271+
if (vec.empty())
272+
{
273+
return "[]";
274+
}
275+
else
276+
{
277+
return write_vec_to_stream(std::stringstream(), vec).str();
278+
}
279+
}
245280
} // namespace auxiliary
246281
} // namespace openPMD

src/Dataset.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ bool Dataset::empty() const
6868
}
6969

7070
std::optional<size_t> Dataset::joinedDimension() const
71+
{
72+
return joinedDimension(extent);
73+
}
74+
75+
std::optional<size_t> Dataset::joinedDimension(Extent const &extent)
7176
{
7277
std::optional<size_t> res;
7378
for (size_t i = 0; i < extent.size(); ++i)

src/IO/ADIOS/ADIOS2IOHandler.cpp

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,10 @@ namespace detail
873873
{
874874
template <typename T, typename... Args>
875875
static void call(
876-
adios2::IO &IO, std::string const &variable, Extent const &newShape)
876+
adios2::IO &IO,
877+
std::string const &variable,
878+
Extent const &newShape,
879+
std::optional<size_t> const &newJoinedDim)
877880
{
878881
auto var = IO.InquireVariable<T>(variable);
879882
if (!var)
@@ -888,6 +891,63 @@ namespace detail
888891
{
889892
dims.push_back(ext);
890893
}
894+
auto oldJoinedDim = joinedDimension(var.Shape());
895+
auto make_runtime_error = [&](char const *message) {
896+
std::stringstream s;
897+
s << "[ADIOS2IOHandlerImpl::extendDataset()] " << message
898+
<< "\nNote: Variable '" << variable << "' has old shape ";
899+
auxiliary::write_vec_to_stream(s, var.Shape());
900+
if (oldJoinedDim.has_value())
901+
{
902+
s << " (joined dimension on index " << *oldJoinedDim << ")";
903+
}
904+
else
905+
{
906+
s << " (no joined dimension)";
907+
}
908+
s << " and is extended to new shape ";
909+
auxiliary::write_vec_to_stream(s, newShape);
910+
if (newJoinedDim.has_value())
911+
{
912+
s << " (joined dimension on index " << *newJoinedDim
913+
<< ").";
914+
}
915+
else
916+
{
917+
s << " (no joined dimension).";
918+
}
919+
return std::runtime_error(s.str());
920+
};
921+
if (oldJoinedDim.has_value() ||
922+
var.ShapeID() == adios2::ShapeID::JoinedArray)
923+
{
924+
if (!oldJoinedDim.has_value())
925+
{
926+
throw make_runtime_error(
927+
"Inconsistent state of variable: Has shape ID "
928+
"JoinedArray, but its shape contains no value "
929+
"adios2::JoinedDim.");
930+
}
931+
if (newJoinedDim != oldJoinedDim)
932+
{
933+
throw make_runtime_error(
934+
"Variable was previously configured with a joined "
935+
"dimension, so the new dataset extent must keep the "
936+
"joined dimension on that index.");
937+
}
938+
dims[*newJoinedDim] = adios2::JoinedDim;
939+
}
940+
else
941+
{
942+
if (newJoinedDim.has_value())
943+
{
944+
throw make_runtime_error(
945+
"Variable was not previously configured with a "
946+
"joined dimension, but is now requested to change "
947+
"extent to a joined array.");
948+
}
949+
}
950+
891951
var.SetShape(dims);
892952
}
893953

@@ -907,7 +967,7 @@ void ADIOS2IOHandlerImpl::extendDataset(
907967
auto &filedata = getFileData(file, IfFileNotOpen::ThrowError);
908968
Datatype dt = detail::fromADIOS2Type(filedata.m_IO.VariableType(name));
909969
switchAdios2VariableType<detail::DatasetExtender>(
910-
dt, filedata.m_IO, name, parameters.extent);
970+
dt, filedata.m_IO, name, parameters.extent, parameters.joinedDimension);
911971
}
912972

913973
void ADIOS2IOHandlerImpl::openFile(

src/IO/AbstractIOHandlerImpl.cpp

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
#include "openPMD/IO/IOTask.hpp"
2525
#include "openPMD/Streaming.hpp"
2626
#include "openPMD/auxiliary/Environment.hpp"
27+
#include "openPMD/auxiliary/StringManip.hpp"
2728
#include "openPMD/auxiliary/Variant.hpp"
28-
#include "openPMD/backend/Attribute.hpp"
2929
#include "openPMD/backend/Writable.hpp"
3030

3131
#include <iostream>
@@ -47,29 +47,6 @@ AbstractIOHandlerImpl::AbstractIOHandlerImpl(AbstractIOHandler *handler)
4747

4848
namespace
4949
{
50-
template <typename Vec>
51-
auto vec_as_string(Vec const &vec) -> std::string
52-
{
53-
if (vec.empty())
54-
{
55-
return "[]";
56-
}
57-
else
58-
{
59-
std::stringstream res;
60-
res << '[';
61-
auto it = vec.begin();
62-
res << *it++;
63-
auto end = vec.end();
64-
for (; it != end; ++it)
65-
{
66-
res << ", " << *it;
67-
}
68-
res << ']';
69-
return res.str();
70-
}
71-
}
72-
7350
template <typename T, typename SFINAE = void>
7451
struct self_or_invoked
7552
{

src/IO/HDF5/HDF5IOHandler.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,12 @@ void HDF5IOHandlerImpl::extendDataset(
840840
throw std::runtime_error(
841841
"[HDF5] Extending an unwritten Dataset is not possible.");
842842

843+
if (parameters.joinedDimension.has_value())
844+
{
845+
error::throwOperationUnsupportedInBackend(
846+
"HDF5", "Joined Arrays currently only supported in ADIOS2");
847+
}
848+
843849
auto res = getFile(writable);
844850
if (!res)
845851
res = getFile(writable->parent);

src/IO/JSON/JSONIOHandlerImpl.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,13 @@ void JSONIOHandlerImpl::extendDataset(
711711
VERIFY_ALWAYS(
712712
access::write(m_handler->m_backendAccess),
713713
"[JSON] Cannot extend a dataset in read-only mode.")
714+
715+
if (parameters.joinedDimension.has_value())
716+
{
717+
error::throwOperationUnsupportedInBackend(
718+
"JSON", "Joined Arrays currently only supported in ADIOS2");
719+
}
720+
714721
setAndGetFilePosition(writable);
715722
refreshFileFromParent(writable);
716723
auto &j = obtainJsonContents(writable);

0 commit comments

Comments
 (0)