From 00a014a57f36b80a02f637cf452f7bd242c0c81d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20P=C3=B6schel?= Date: Tue, 29 Oct 2024 14:56:51 +0100 Subject: [PATCH] Add conversion helpers between map- and array representations --- CMakeLists.txt | 1 + include/openPMD/Mesh.hpp | 3 +- include/openPMD/UnitDimension.hpp | 27 ++++++++++++ include/openPMD/backend/BaseRecord.hpp | 4 +- src/Mesh.cpp | 27 ++++-------- src/UnitDimension.cpp | 61 ++++++++++++++++++++++++++ test/CoreTest.cpp | 17 +++++++ 7 files changed, 118 insertions(+), 22 deletions(-) create mode 100644 src/UnitDimension.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fc5d6d5de5..10e74dc3a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -397,6 +397,7 @@ set(CORE_SOURCE src/Record.cpp src/RecordComponent.cpp src/Series.cpp + src/UnitDimension.cpp src/version.cpp src/WriteIterations.cpp src/auxiliary/Date.cpp diff --git a/include/openPMD/Mesh.hpp b/include/openPMD/Mesh.hpp index 62d574b67a..119111ea53 100644 --- a/include/openPMD/Mesh.hpp +++ b/include/openPMD/Mesh.hpp @@ -20,6 +20,7 @@ */ #pragma once +#include "openPMD/UnitDimension.hpp" #include "openPMD/backend/Attributable.hpp" #include "openPMD/backend/BaseRecord.hpp" #include "openPMD/backend/MeshRecordComponent.hpp" @@ -273,7 +274,7 @@ class Mesh : public BaseRecord * @return A vector of arrays, each array representing the SI unit of one * mesh axis. */ - std::vector> gridUnitDimension() const; + unit_representations::AsArrays gridUnitDimension() const; /** * @tparam T Floating point type of user-selected precision (e.g. float, diff --git a/include/openPMD/UnitDimension.hpp b/include/openPMD/UnitDimension.hpp index 232a6ee1f7..b6b82d4ea3 100644 --- a/include/openPMD/UnitDimension.hpp +++ b/include/openPMD/UnitDimension.hpp @@ -20,10 +20,16 @@ */ #pragma once +#include #include +#include +#include namespace openPMD { + +using UnitDimensionExponent = double; + /** Physical dimension of a record * * Dimensional base quantities of the international system of quantities @@ -38,4 +44,25 @@ enum class UnitDimension : uint8_t N, //!< amount of substance J //!< luminous intensity }; + +namespace unit_representations +{ + using AsMap = std::map; + using AsArray = std::array; + + using AsMaps = std::vector; + using AsArrays = std::vector; + + auto asArray(AsMap const &) -> AsArray; + auto asMap(AsArray const &) -> AsMap; + + auto asArrays(AsMaps const &) -> AsArrays; + auto asMaps(AsArrays const &) -> AsMaps; +} // namespace unit_representations + +namespace auxiliary +{ + void fromMapOfUnitDimension( + double *cursor, std::map const &udim); +} // namespace auxiliary } // namespace openPMD diff --git a/include/openPMD/backend/BaseRecord.hpp b/include/openPMD/backend/BaseRecord.hpp index ba137b10db..87364b46f4 100644 --- a/include/openPMD/backend/BaseRecord.hpp +++ b/include/openPMD/backend/BaseRecord.hpp @@ -474,7 +474,7 @@ class BaseRecord * * @return powers of the 7 base measures in the order specified above */ - std::array unitDimension() const; + unit_representations::AsArray unitDimension() const; void setDatasetDefined(BaseRecordComponent::Data_t &data) override { @@ -928,7 +928,7 @@ auto BaseRecord::emplace(Args &&...args) -> std::pair } template -inline std::array BaseRecord::unitDimension() const +inline unit_representations::AsArray BaseRecord::unitDimension() const { return this->getAttribute("unitDimension") .template get>(); diff --git a/src/Mesh.cpp b/src/Mesh.cpp index b4e0584dcb..5839da7cab 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -22,6 +22,7 @@ #include "openPMD/Error.hpp" #include "openPMD/Series.hpp" #include "openPMD/ThrowError.hpp" +#include "openPMD/UnitDimension.hpp" #include "openPMD/auxiliary/DerefDynamicCast.hpp" #include "openPMD/auxiliary/StringManip.hpp" #include "openPMD/backend/Attribute.hpp" @@ -271,25 +272,12 @@ Mesh &Mesh::setGridUnitSIPerDimension(std::vector gridUnitSI) return *this; } -namespace -{ - template - void fromMapOfUnitDimension( - RandomAccessIterator it, std::map const &udim) - { - for (auto [unit, exponent] : udim) - { - it[static_cast(unit)] = exponent; - } - } -} // namespace - Mesh &Mesh::setUnitDimension(std::map const &udim) { if (!udim.empty()) { std::array tmpUnitDimension = this->unitDimension(); - fromMapOfUnitDimension(tmpUnitDimension.begin(), udim); + auxiliary::fromMapOfUnitDimension(tmpUnitDimension.begin(), udim); setAttribute("unitDimension", tmpUnitDimension); } return *this; @@ -313,14 +301,14 @@ Mesh &Mesh::setGridUnitDimension( auto cursor = rawGridUnitDimension.begin(); for (auto const &udim : udims) { - fromMapOfUnitDimension(cursor, udim); + auxiliary::fromMapOfUnitDimension(&*cursor, udim); cursor += 7; } setAttribute("gridUnitDimension", rawGridUnitDimension); return *this; } -std::vector> Mesh::gridUnitDimension() const +unit_representations::AsArrays Mesh::gridUnitDimension() const { if (containsAttribute("gridUnitDimension")) { @@ -335,7 +323,7 @@ std::vector> Mesh::gridUnitDimension() const "[Mesh::gridUnitDimension()] `gridUnitDimension` attribute " "must have a length equal to a multiple of 7."); } - std::vector> res(rawRes.size() / 7); + unit_representations::AsArrays res(rawRes.size() / 7); for (size_t dim = 0; dim < res.size(); ++dim) { std::copy_n(rawRes.begin() + dim * 7, 7, res.at(dim).begin()); @@ -347,9 +335,10 @@ std::vector> Mesh::gridUnitDimension() const // gridUnitDimension is an optional attribute // if it is missing, the mesh is interpreted as spatial std::array spatialMesh; - fromMapOfUnitDimension(spatialMesh.begin(), {{UnitDimension::L, 1}}); + auxiliary::fromMapOfUnitDimension( + spatialMesh.begin(), {{UnitDimension::L, 1}}); auto dim = retrieveMeshDimensionality(*this); - std::vector> res(dim, spatialMesh); + unit_representations::AsArrays res(dim, spatialMesh); return res; } } diff --git a/src/UnitDimension.cpp b/src/UnitDimension.cpp new file mode 100644 index 0000000000..b2d36e7b8e --- /dev/null +++ b/src/UnitDimension.cpp @@ -0,0 +1,61 @@ +#include "openPMD/UnitDimension.hpp" +#include +#include + +namespace openPMD +{ +namespace auxiliary +{ + void fromMapOfUnitDimension( + double *cursor, std::map const &udim) + { + for (auto [unit, exponent] : udim) + { + cursor[static_cast(unit)] = exponent; + } + } +} // namespace auxiliary + +namespace unit_representations +{ + auto asArray(AsMap const &udim) -> AsArray + { + AsArray res; + auxiliary::fromMapOfUnitDimension(res.begin(), udim); + return res; + } + auto asMap(AsArray const &array) -> AsMap + { + AsMap udim; + for (size_t i = 0; i < array.size(); ++i) + { + if (array[i] != 0) + { + udim[static_cast(i)] = array[i]; + } + } + return udim; + } + + auto asArrays(AsMaps const &vec) -> AsArrays + { + AsArrays res; + std::transform( + vec.begin(), + vec.end(), + std::back_inserter(res), + [](auto const &map) { return asArray(map); }); + return res; + } + auto asMaps(AsArrays const &vec) -> AsMaps + { + AsMaps res; + std::transform( + vec.begin(), + vec.end(), + std::back_inserter(res), + [](auto const &array) { return asMap(array); }); + return res; + } +} // namespace unit_representations +} // namespace openPMD diff --git a/test/CoreTest.cpp b/test/CoreTest.cpp index 2c4648925d..7c3a5c4ed9 100644 --- a/test/CoreTest.cpp +++ b/test/CoreTest.cpp @@ -1,4 +1,5 @@ // expose private and protected members for invasive testing +#include "openPMD/UnitDimension.hpp" #if openPMD_USE_INVASIVE_TESTS #define OPENPMD_private public: #define OPENPMD_protected public: @@ -1280,6 +1281,16 @@ TEST_CASE("custom_geometries", "[core]") Series write("../samples/custom_geometry.json", Access::CREATE); auto E = write.iterations[0].meshes["E"]; E.setAttribute("geometry", "other:customGeometry"); + // gridUnitDimension is technically an openPMD 2.0 addition, but since + // it's a non-breaking addition, we can also use it in openPMD 1.* + // files. However, it only really makes sense to use along with per-axis + // gridUnitSI definitions, which are in fact breaking in comparison to + // openPMD 1.*. + E.setGridUnitDimension( + {{{UnitDimension::theta, 1}}, + {{UnitDimension::M, 1}, + {UnitDimension::L, 1}, + {UnitDimension::T, 2}}}); auto E_x = E["x"]; E_x.resetDataset({Datatype::INT, {10}}); E_x.storeChunk(sampleData, {0}, {10}); @@ -1306,6 +1317,12 @@ TEST_CASE("custom_geometries", "[core]") { Series read("../samples/custom_geometry.json", Access::READ_ONLY); auto E = read.iterations[0].meshes["E"]; + auto compare = unit_representations::AsMaps{ + {{UnitDimension::theta, 1}}, + {{UnitDimension::M, 1}, + {UnitDimension::L, 1}, + {UnitDimension::T, 2}}}; + REQUIRE(unit_representations::asMaps(E.gridUnitDimension()) == compare); REQUIRE( E.getAttribute("geometry").get() == "other:customGeometry");