Skip to content

Commit 2a03187

Browse files
committed
Make shape an optional attribute for constant components
1 parent ff21bbc commit 2a03187

File tree

5 files changed

+113
-37
lines changed

5 files changed

+113
-37
lines changed

include/openPMD/backend/Attributable.hpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121
#pragma once
2222

23+
#include "openPMD/Error.hpp"
2324
#include "openPMD/IO/AbstractIOHandler.hpp"
2425
#include "openPMD/ThrowError.hpp"
2526
#include "openPMD/auxiliary/OutOfRangeMsg.hpp"
@@ -30,6 +31,7 @@
3031
#include <exception>
3132
#include <map>
3233
#include <memory>
34+
#include <optional>
3335
#include <string>
3436
#include <type_traits>
3537
#include <vector>
@@ -113,6 +115,31 @@ namespace internal
113115
return res;
114116
}
115117

118+
inline auto attributes() -> A_MAP &
119+
{
120+
return m_attributes;
121+
}
122+
[[nodiscard]] inline auto attributes() const -> A_MAP const &
123+
{
124+
return m_attributes;
125+
}
126+
[[nodiscard]] inline auto
127+
readAttribute(std::string const &name) const -> Attribute const &
128+
{
129+
if (auto it = m_attributes.find(name); it != m_attributes.end())
130+
{
131+
return it->second;
132+
}
133+
else
134+
{
135+
throw error::ReadError(
136+
error::AffectedObject::Attribute,
137+
error::Reason::NotFound,
138+
std::nullopt,
139+
"Not found: '" + name + "'.");
140+
}
141+
}
142+
116143
private:
117144
/**
118145
* The attributes defined by this Attributable.

src/Iteration.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,8 +595,7 @@ void Iteration::readMeshes(std::string const &meshesPath)
595595
auto att_begin = aList.attributes->begin();
596596
auto att_end = aList.attributes->end();
597597
auto value = std::find(att_begin, att_end, "value");
598-
auto shape = std::find(att_begin, att_end, "shape");
599-
if (value != att_end && shape != att_end)
598+
if (value != att_end)
600599
{
601600
MeshRecordComponent &mrc = m;
602601
IOHandler()->enqueue(IOTask(&mrc, pOpen));

src/ParticleSpecies.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ void ParticleSpecies::read()
7676
auto att_begin = aList.attributes->begin();
7777
auto att_end = aList.attributes->end();
7878
auto value = std::find(att_begin, att_end, "value");
79-
auto shape = std::find(att_begin, att_end, "shape");
80-
if (value != att_end && shape != att_end)
79+
if (value != att_end)
8180
{
8281
RecordComponent &rc = r;
8382
IOHandler()->enqueue(IOTask(&rc, pOpen));

src/RecordComponent.cpp

Lines changed: 68 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,19 @@ RecordComponent &RecordComponent::resetDataset(Dataset d)
109109
throw error::WrongAPIUsage(
110110
"[RecordComponent] Must set specific datatype.");
111111
}
112-
// if( d.extent.empty() )
113-
// throw std::runtime_error("Dataset extent must be at least 1D.");
112+
if (d.extent.empty())
113+
throw std::runtime_error("Dataset extent must be at least 1D.");
114114
if (d.empty())
115+
{
116+
if (d.extent.empty())
117+
{
118+
throw error::Internal(
119+
"A zero-dimensional dataset is not to be considered empty, but "
120+
"undefined. This is an internal safeguard against future "
121+
"changes that might not consider this.");
122+
}
115123
return makeEmpty(std::move(d));
124+
}
116125

117126
rc.m_isEmpty = false;
118127
if (written())
@@ -275,6 +284,13 @@ void RecordComponent::flush(
275284
{
276285
setUnitSI(1);
277286
}
287+
auto constant_component_write_shape = [&]() {
288+
auto extent = getExtent();
289+
return !extent.empty() &&
290+
std::none_of(extent.begin(), extent.end(), [](auto val) {
291+
return val == Dataset::JOINED_DIMENSION;
292+
});
293+
};
278294
if (!written())
279295
{
280296
if (constant())
@@ -294,16 +310,20 @@ void RecordComponent::flush(
294310
Operation::WRITE_ATT>::ChangesOverSteps::IfPossible;
295311
}
296312
IOHandler()->enqueue(IOTask(this, aWrite));
297-
aWrite.name = "shape";
298-
Attribute a(getExtent());
299-
aWrite.dtype = a.dtype;
300-
aWrite.resource = a.getResource();
301-
if (isVBased)
313+
if (constant_component_write_shape())
302314
{
303-
aWrite.changesOverSteps = Parameter<
304-
Operation::WRITE_ATT>::ChangesOverSteps::IfPossible;
315+
316+
aWrite.name = "shape";
317+
Attribute a(getExtent());
318+
aWrite.dtype = a.dtype;
319+
aWrite.resource = a.getResource();
320+
if (isVBased)
321+
{
322+
aWrite.changesOverSteps = Parameter<
323+
Operation::WRITE_ATT>::ChangesOverSteps::IfPossible;
324+
}
325+
IOHandler()->enqueue(IOTask(this, aWrite));
305326
}
306-
IOHandler()->enqueue(IOTask(this, aWrite));
307327
}
308328
else
309329
{
@@ -321,6 +341,13 @@ void RecordComponent::flush(
321341
{
322342
if (constant())
323343
{
344+
if (!constant_component_write_shape())
345+
{
346+
throw error::WrongAPIUsage(
347+
"Extended constant component from a previous shape to "
348+
"one that cannot be written (empty or with joined "
349+
"dimension).");
350+
}
324351
bool isVBased = retrieveSeries().iterationEncoding() ==
325352
IterationEncoding::variableBased;
326353
Parameter<Operation::WRITE_ATT> aWrite;
@@ -385,28 +412,35 @@ namespace
385412
};
386413
} // namespace
387414

415+
inline void breakpoint()
416+
{}
417+
388418
void RecordComponent::readBase(bool require_unit_si)
389419
{
390420
using DT = Datatype;
391-
// auto & rc = get();
392-
Parameter<Operation::READ_ATT> aRead;
421+
auto &rc = get();
393422

394-
if (constant() && !empty())
395-
{
396-
aRead.name = "value";
397-
IOHandler()->enqueue(IOTask(this, aRead));
398-
IOHandler()->flush(internal::defaultFlushParams);
423+
readAttributes(ReadMode::FullyReread);
399424

400-
Attribute a(*aRead.resource);
401-
DT dtype = *aRead.dtype;
425+
auto read_constant =
426+
[&]() // comment for forcing clang-format into putting a newline here
427+
{
428+
Attribute a = rc.readAttribute("value");
429+
DT dtype = a.dtype;
402430
setWritten(false, Attributable::EnqueueAsynchronously::No);
403431
switchNonVectorType<MakeConstant>(dtype, *this, a);
404432
setWritten(true, Attributable::EnqueueAsynchronously::No);
405433

406-
aRead.name = "shape";
407-
IOHandler()->enqueue(IOTask(this, aRead));
408-
IOHandler()->flush(internal::defaultFlushParams);
409-
a = Attribute(*aRead.resource);
434+
if (!containsAttribute("shape"))
435+
{
436+
setWritten(false, Attributable::EnqueueAsynchronously::No);
437+
resetDataset(Dataset(dtype, {}));
438+
setWritten(true, Attributable::EnqueueAsynchronously::No);
439+
440+
return;
441+
}
442+
443+
a = rc.attributes().at("shape");
410444
Extent e;
411445

412446
// uint64_t check
@@ -416,7 +450,7 @@ void RecordComponent::readBase(bool require_unit_si)
416450
else
417451
{
418452
std::ostringstream oss;
419-
oss << "Unexpected datatype (" << *aRead.dtype
453+
oss << "Unexpected datatype (" << a.dtype
420454
<< ") for attribute 'shape' (" << determineDatatype<uint64_t>()
421455
<< " aka uint64_t)";
422456
throw error::ReadError(
@@ -429,9 +463,13 @@ void RecordComponent::readBase(bool require_unit_si)
429463
setWritten(false, Attributable::EnqueueAsynchronously::No);
430464
resetDataset(Dataset(dtype, e));
431465
setWritten(true, Attributable::EnqueueAsynchronously::No);
432-
}
466+
};
433467

434-
readAttributes(ReadMode::FullyReread);
468+
if (constant() && !empty())
469+
{
470+
breakpoint();
471+
read_constant();
472+
}
435473

436474
if (require_unit_si)
437475
{
@@ -445,16 +483,17 @@ void RecordComponent::readBase(bool require_unit_si)
445483
"'" +
446484
myPath().openPMDPath() + "'.");
447485
}
448-
if (!getAttribute("unitSI").getOptional<double>().has_value())
486+
if (auto attr = getAttribute("unitSI");
487+
!attr.getOptional<double>().has_value())
449488
{
450489
throw error::ReadError(
451490
error::AffectedObject::Attribute,
452491
error::Reason::UnexpectedContent,
453492
{},
454493
"Unexpected Attribute datatype for 'unitSI' (expected double, "
455494
"found " +
456-
datatypeToString(Attribute(*aRead.resource).dtype) +
457-
") in '" + myPath().openPMDPath() + "'.");
495+
datatypeToString(attr.dtype) + ") in '" +
496+
myPath().openPMDPath() + "'.");
458497
}
459498
}
460499
}

src/backend/PatchRecordComponent.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,26 @@ PatchRecordComponent &PatchRecordComponent::setUnitSI(double usi)
3737
PatchRecordComponent &PatchRecordComponent::resetDataset(Dataset d)
3838
{
3939
if (written())
40+
{
4041
throw std::runtime_error(
4142
"A Records Dataset can not (yet) be changed after it has been "
4243
"written.");
43-
if (d.extent.empty())
44-
throw std::runtime_error("Dataset extent must be at least 1D.");
44+
}
4545
if (d.empty())
46-
throw std::runtime_error(
47-
"Dataset extent must not be zero in any dimension.");
46+
{
47+
if (d.extent.empty())
48+
{
49+
throw error::Internal(
50+
"A zero-dimensional dataset is not to be considered empty, but "
51+
"undefined. This is an internal safeguard against future "
52+
"changes that might not consider this.");
53+
}
54+
else
55+
{
56+
throw std::runtime_error(
57+
"Dataset extent must not be zero in any dimension.");
58+
}
59+
}
4860

4961
get().m_dataset = std::move(d);
5062
setDirty(true);

0 commit comments

Comments
 (0)