Skip to content

Commit d1015ee

Browse files
Non-spatial meshes (#1534)
* Implement per-dimension gridUnitSI * Add gridUnitDimension * Add an alias API + some bit of testing * Add upgrade notice for old API call We cannot upgrade users silently in this place * Use openPMD 2.0 standard setting * Two little fixes * Default value for gridUnitSI set upon flush * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fallback for undefined gridUnitSI * Add conversion helpers between map- and array representations * Windows fixes * Tests and bugfixes * CI fixes * Introduce a member that stores the openPMD version as enum * Use enum-type standard check for meshes * Move auxiliary namespace under unit_representation * Remove retrieveSeries_optional * Cleaner Python API, Python tests * Python documentation, C++ documentation fixes * Better function argument types * CI fixes * Extend Python test --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 4a040bb commit d1015ee

File tree

21 files changed

+839
-88
lines changed

21 files changed

+839
-88
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ set(CORE_SOURCE
401401
src/Record.cpp
402402
src/RecordComponent.cpp
403403
src/Series.cpp
404+
src/UnitDimension.cpp
404405
src/version.cpp
405406
src/WriteIterations.cpp
406407
src/auxiliary/Date.cpp

include/openPMD/IO/AbstractIOHandler.hpp

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "openPMD/IO/IOTask.hpp"
2626
#include "openPMD/IterationEncoding.hpp"
2727
#include "openPMD/config.hpp"
28+
#include "openPMD/version.hpp"
2829

2930
#if openPMD_HAVE_MPI
3031
#include <mpi.h>
@@ -75,6 +76,20 @@ enum class FlushLevel
7576
CreateOrOpenFiles
7677
};
7778

79+
enum class OpenpmdStandard
80+
{
81+
v_1_0_0,
82+
v_1_0_1,
83+
v_1_1_0,
84+
v_2_0_0
85+
};
86+
87+
namespace auxiliary
88+
{
89+
auto parseStandard(std::string const &) -> OpenpmdStandard;
90+
auto formatStandard(OpenpmdStandard) -> char const *;
91+
} // namespace auxiliary
92+
7893
namespace internal
7994
{
8095
/**
@@ -189,38 +204,7 @@ class AbstractIOHandler
189204
friend class detail::ADIOS2File;
190205

191206
private:
192-
IterationEncoding m_encoding = IterationEncoding::groupBased;
193-
194-
void setIterationEncoding(IterationEncoding encoding)
195-
{
196-
/*
197-
* In file-based iteration encoding, the APPEND mode is handled entirely
198-
* by the frontend, the backend should just treat it as CREATE mode.
199-
* Similar for READ_LINEAR which should be treated as READ_RANDOM_ACCESS
200-
* in the backend.
201-
*/
202-
if (encoding == IterationEncoding::fileBased)
203-
{
204-
switch (m_backendAccess)
205-
{
206-
207-
case Access::READ_LINEAR:
208-
// do we really want to have those as const members..?
209-
*const_cast<Access *>(&m_backendAccess) =
210-
Access::READ_RANDOM_ACCESS;
211-
break;
212-
case Access::APPEND:
213-
*const_cast<Access *>(&m_backendAccess) = Access::CREATE;
214-
break;
215-
case Access::READ_RANDOM_ACCESS:
216-
case Access::READ_WRITE:
217-
case Access::CREATE:
218-
break;
219-
}
220-
}
221-
222-
m_encoding = encoding;
223-
}
207+
void setIterationEncoding(IterationEncoding encoding);
224208

225209
public:
226210
#if openPMD_HAVE_MPI
@@ -284,8 +268,14 @@ class AbstractIOHandler
284268
*/
285269
Access m_backendAccess;
286270
Access m_frontendAccess;
287-
internal::SeriesStatus m_seriesStatus = internal::SeriesStatus::Default;
288271
std::queue<IOTask> m_work;
272+
273+
/**************************************************************************
274+
* Since the AbstractIOHandler is linked to every object of the frontend, *
275+
* it stores a number of members that are needed by methods traversing *
276+
* the object hierarchy. Those members are found below. *
277+
**************************************************************************/
278+
289279
/**
290280
* This is to avoid that the destructor tries flushing again if an error
291281
* happened. Otherwise, this would lead to confusing error messages.
@@ -294,6 +284,9 @@ class AbstractIOHandler
294284
* The destructor will only attempt flushing again if this is true.
295285
*/
296286
bool m_lastFlushSuccessful = false;
287+
internal::SeriesStatus m_seriesStatus = internal::SeriesStatus::Default;
288+
IterationEncoding m_encoding = IterationEncoding::groupBased;
289+
OpenpmdStandard m_standard = auxiliary::parseStandard(getStandardDefault());
297290
}; // AbstractIOHandler
298291

299292
} // namespace openPMD

include/openPMD/Mesh.hpp

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020
*/
2121
#pragma once
2222

23+
#include "openPMD/UnitDimension.hpp"
2324
#include "openPMD/backend/Attributable.hpp"
2425
#include "openPMD/backend/BaseRecord.hpp"
2526
#include "openPMD/backend/MeshRecordComponent.hpp"
2627

27-
#include <array>
2828
#include <ostream>
2929
#include <string>
3030
#include <type_traits>
@@ -155,7 +155,7 @@ class Mesh : public BaseRecord<MeshRecordComponent>
155155
*/
156156
template <
157157
typename T,
158-
typename = std::enable_if_t<std::is_floating_point<T>::value> >
158+
typename = std::enable_if_t<std::is_floating_point<T>::value>>
159159
Mesh &setGridSpacing(std::vector<T> const &gridSpacing);
160160

161161
/**
@@ -184,22 +184,119 @@ class Mesh : public BaseRecord<MeshRecordComponent>
184184
* Mesh::gridSpacing and Mesh::gridGlobalOffset, in order to convert from
185185
* simulation units to SI units.
186186
*
187+
* Valid for openPMD version 1.*.
188+
* In order to specify the gridUnitSI per dimension (openPMD 2.*),
189+
* use the vector overload or `setGridUnitSIPerDimension()`.
190+
*
187191
* @param gridUnitSI unit-conversion factor to multiply each value in
188192
* Mesh::gridSpacing and Mesh::gridGlobalOffset, in order to convert from
189193
* simulation units to SI units.
190194
* @return Reference to modified mesh.
191195
*/
192196
Mesh &setGridUnitSI(double gridUnitSI);
193197

198+
/** Alias for `setGridUnitSIPerDimension(std::vector<double>)`.
199+
*
200+
* Set the unit-conversion factors per axis to multiply each value in
201+
* Mesh::gridSpacing and Mesh::gridGlobalOffset, in order to convert from
202+
* simulation units to SI units.
203+
*
204+
* Valid for openPMD 2.*.
205+
* The legacy behavior (openPMD 1.*, a scalar gridUnitSI) is implemented
206+
* by `setGridUnitSI(double)`.
207+
*
208+
* @param gridUnitSI unit-conversion factor to multiply each value in
209+
* Mesh::gridSpacing and Mesh::gridGlobalOffset, in order to convert from
210+
* simulation units to SI units.
211+
*
212+
* @return Reference to modified mesh.
213+
*/
214+
Mesh &setGridUnitSI(std::vector<double> const &gridUnitSI);
215+
216+
/**
217+
* @return A vector of the gridUnitSI per grid axis in the order of
218+
* the axisLabels. If the gridUnitSI is defined as a scalar
219+
* (legacy openPMD), the dimensionality is determined and a vector of
220+
* `dimensionality` times the scalar vector is returned.
221+
*/
222+
std::vector<double> gridUnitSIPerDimension() const;
223+
224+
/* Set the unit-conversion factors per axis to multiply each value in
225+
* Mesh::gridSpacing and Mesh::gridGlobalOffset, in order to convert from
226+
* simulation units to SI units.
227+
*
228+
* Valid for openPMD 2.*.
229+
* The legacy behavior (openPMD 1.*, a scalar gridUnitSI) is implemented
230+
* by `setGridUnitSI(double)`.
231+
*
232+
* @param gridUnitSI unit-conversion factor to multiply each value in
233+
* Mesh::gridSpacing and Mesh::gridGlobalOffset, in order to convert from
234+
* simulation units to SI units.
235+
*
236+
* @return Reference to modified mesh.
237+
*/
238+
Mesh &setGridUnitSIPerDimension(std::vector<double> const &gridUnitSI);
239+
194240
/** Set the powers of the 7 base measures characterizing the record's unit
195241
* in SI.
196242
*
197243
* @param unitDimension map containing pairs of (UnitDimension, double)
198244
* that represent the power of the particular base.
199245
* @return Reference to modified mesh.
200246
*/
247+
Mesh &setUnitDimension(unit_representations::AsMap const &unitDimension);
248+
249+
/** Set the powers of the 7 base measures characterizing the record's unit
250+
* in SI.
251+
*
252+
* @param unitDimension array containing seven doubles, each
253+
* representing the power of the particular base in order.
254+
* @return Reference to modified mesh.
255+
*/
256+
Mesh &setUnitDimension(unit_representations::AsArray const &unitDimension);
257+
258+
/**
259+
* @brief Set the unitDimension for each axis of the current grid.
260+
*
261+
* @param gridUnitDimension A vector of the unitDimensions for each
262+
* axis of the grid in the order of the axisLabels, in dict representation.
263+
264+
* Behavior note: This is an updating method, meaning that an SI unit that
265+
* has been defined before and is in the next call not explicitly set
266+
* in the `std::map<UnitDimension, double>` will keep its previous value.
267+
*
268+
* @return Reference to modified mesh.
269+
*/
201270
Mesh &
202-
setUnitDimension(std::map<UnitDimension, double> const &unitDimension);
271+
setGridUnitDimension(unit_representations::AsMaps const &gridUnitDimension);
272+
273+
/**
274+
* @brief Set the unitDimension for each axis of the current grid.
275+
*
276+
* @param gridUnitDimension A vector of the unitDimensions for each
277+
* axis of the grid in the order of the axisLabels, in array representation.
278+
279+
* Behavior note: This is an updating method, meaning that an SI unit that
280+
* has been defined before and is in the next call not explicitly set
281+
* in the `std::map<UnitDimension, double>` will keep its previous value.
282+
*
283+
* @return Reference to modified mesh.
284+
*/
285+
Mesh &setGridUnitDimension(
286+
unit_representations::AsArrays const &gridUnitDimension);
287+
288+
/**
289+
* @brief Return the physical dimensions of the mesh axes.
290+
291+
* If the attribute is not defined, the axes are assumed to be spatial
292+
* and the return value will be according to this assumption.
293+
* If the attribute is defined, the dimensionality of the return value is
294+
* not checked against the dimensionality of the mesh.
295+
*
296+
* @return A vector of arrays, each array representing the SI unit of one
297+
* mesh axis.
298+
*/
299+
unit_representations::AsArrays gridUnitDimension() const;
203300

204301
/**
205302
* @tparam T Floating point type of user-selected precision (e.g. float,
@@ -222,7 +319,7 @@ class Mesh : public BaseRecord<MeshRecordComponent>
222319
*/
223320
template <
224321
typename T,
225-
typename = std::enable_if_t<std::is_floating_point<T>::value> >
322+
typename = std::enable_if_t<std::is_floating_point<T>::value>>
226323
Mesh &setTimeOffset(T timeOffset);
227324

228325
private:

include/openPMD/Record.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
#pragma once
2222

2323
#include "openPMD/RecordComponent.hpp"
24+
#include "openPMD/UnitDimension.hpp"
2425
#include "openPMD/backend/BaseRecord.hpp"
2526

26-
#include <map>
2727
#include <string>
2828
#include <type_traits>
2929

@@ -40,7 +40,8 @@ class Record : public BaseRecord<RecordComponent>
4040
Record &operator=(Record const &) = default;
4141
~Record() override = default;
4242

43-
Record &setUnitDimension(std::map<UnitDimension, double> const &);
43+
Record &setUnitDimension(unit_representations::AsMap const &);
44+
Record &setUnitDimension(unit_representations::AsArray const &);
4445

4546
template <typename T>
4647
T timeOffset() const;

include/openPMD/UnitDimension.hpp

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

23+
#include <array>
2324
#include <cstdint>
25+
#include <map>
26+
#include <vector>
2427

2528
namespace openPMD
2629
{
30+
31+
using UnitDimensionExponent = double;
32+
2733
/** Physical dimension of a record
2834
*
2935
* Dimensional base quantities of the international system of quantities
@@ -38,4 +44,25 @@ enum class UnitDimension : uint8_t
3844
N, //!< amount of substance
3945
J //!< luminous intensity
4046
};
47+
48+
namespace unit_representations
49+
{
50+
using AsMap = std::map<UnitDimension, UnitDimensionExponent>;
51+
using AsArray = std::array<UnitDimensionExponent, 7>;
52+
53+
using AsMaps = std::vector<AsMap>;
54+
using AsArrays = std::vector<AsArray>;
55+
56+
auto asArray(AsMap const &) -> AsArray;
57+
auto asMap(AsArray const &, bool skip_zeros = true) -> AsMap;
58+
59+
auto asArrays(AsMaps const &) -> AsArrays;
60+
auto asMaps(AsArrays const &, bool skip_zeros = true) -> AsMaps;
61+
62+
namespace auxiliary
63+
{
64+
void fromMapOfUnitDimension(
65+
double *cursor, std::map<UnitDimension, double> const &udim);
66+
} // namespace auxiliary
67+
} // namespace unit_representations
4168
} // namespace openPMD

include/openPMD/backend/Attributable.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,8 @@ class Attributable
328328
*/
329329
void touch();
330330

331+
[[nodiscard]] OpenpmdStandard openPMDStandard() const;
332+
331333
// clang-format off
332334
OPENPMD_protected
333335
// clang-format on

include/openPMD/backend/BaseRecord.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ class BaseRecord
474474
*
475475
* @return powers of the 7 base measures in the order specified above
476476
*/
477-
std::array<double, 7> unitDimension() const;
477+
unit_representations::AsArray unitDimension() const;
478478

479479
void setDatasetDefined(BaseRecordComponent::Data_t &data) override
480480
{
@@ -928,7 +928,7 @@ auto BaseRecord<T_elem>::emplace(Args &&...args) -> std::pair<iterator, bool>
928928
}
929929

930930
template <typename T_elem>
931-
inline std::array<double, 7> BaseRecord<T_elem>::unitDimension() const
931+
inline unit_representations::AsArray BaseRecord<T_elem>::unitDimension() const
932932
{
933933
return this->getAttribute("unitDimension")
934934
.template get<std::array<double, 7>>();

include/openPMD/binding/python/UnitDimension.hpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace openPMD
2424
{
2525
namespace python
2626
{
27-
constexpr auto doc_unit_dimension = R"docstr(
27+
constexpr auto doc_unit_dimension = &R"docstr(
2828
Return the physical dimension (quantity) of a record
2929
3030
Annotating the physical dimension of a record allows us to read data
@@ -40,7 +40,29 @@ See https://en.wikipedia.org/wiki/International_System_of_Quantities#Base_quanti
4040
See https://github.com/openPMD/openPMD-standard/blob/1.1.0/STANDARD.md#required-for-each-record
4141
4242
Returns the powers of the 7 base measures in the order specified above.
43-
)docstr";
43+
)docstr"[1];
44+
45+
constexpr auto doc_mesh_unit_dimension = &R"docstr(
46+
Return the physical dimension (quantity) of the record axes
47+
48+
Annotating the physical dimension of the record axes allows us to read data
49+
sets with arbitrary names and understand their purpose simply by
50+
dimensional analysis. The dimensional base quantities in openPMD are
51+
in order: length (L), mass (M), time (T), electric current (I),
52+
thermodynamic temperature (theta), amount of substance (N),
53+
luminous intensity (J) after the international system of quantities
54+
(ISQ).
55+
This attribute may be left out, the axes will then be interpreted as spatial.
56+
57+
See https://en.wikipedia.org/wiki/Dimensional_analysis
58+
See https://en.wikipedia.org/wiki/International_System_of_Quantities#Base_quantities
59+
See https://github.com/openPMD/openPMD-standard/blob/1.1.0/STANDARD.md#required-for-each-record
60+
61+
Returns the powers of the 7 base measures in the order specified above, listed
62+
for each axis in the order of the axisLabels.
63+
This attribute has been introduced as part of openPMD 2.0.0 in:
64+
Ref.: https://github.com/openPMD/openPMD-standard/pull/193
65+
)docstr"[1];
4466

4567
} // namespace python
4668
} // namespace openPMD

0 commit comments

Comments
 (0)