Skip to content

Commit

Permalink
Add conversion helpers between map- and array representations
Browse files Browse the repository at this point in the history
  • Loading branch information
franzpoeschel committed Oct 29, 2024
1 parent 227cb94 commit 00a014a
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 22 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion include/openPMD/Mesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -273,7 +274,7 @@ class Mesh : public BaseRecord<MeshRecordComponent>
* @return A vector of arrays, each array representing the SI unit of one
* mesh axis.
*/
std::vector<std::array<double, 7>> gridUnitDimension() const;
unit_representations::AsArrays gridUnitDimension() const;

/**
* @tparam T Floating point type of user-selected precision (e.g. float,
Expand Down
27 changes: 27 additions & 0 deletions include/openPMD/UnitDimension.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,16 @@
*/
#pragma once

#include <array>
#include <cstdint>
#include <map>
#include <vector>

namespace openPMD
{

using UnitDimensionExponent = double;

/** Physical dimension of a record
*
* Dimensional base quantities of the international system of quantities
Expand All @@ -38,4 +44,25 @@ enum class UnitDimension : uint8_t
N, //!< amount of substance
J //!< luminous intensity
};

namespace unit_representations
{
using AsMap = std::map<UnitDimension, UnitDimensionExponent>;
using AsArray = std::array<UnitDimensionExponent, 7>;

using AsMaps = std::vector<AsMap>;
using AsArrays = std::vector<AsArray>;

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<UnitDimension, double> const &udim);
} // namespace auxiliary
} // namespace openPMD
4 changes: 2 additions & 2 deletions include/openPMD/backend/BaseRecord.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ class BaseRecord
*
* @return powers of the 7 base measures in the order specified above
*/
std::array<double, 7> unitDimension() const;
unit_representations::AsArray unitDimension() const;

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

template <typename T_elem>
inline std::array<double, 7> BaseRecord<T_elem>::unitDimension() const
inline unit_representations::AsArray BaseRecord<T_elem>::unitDimension() const
{
return this->getAttribute("unitDimension")
.template get<std::array<double, 7>>();
Expand Down
27 changes: 8 additions & 19 deletions src/Mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -271,25 +272,12 @@ Mesh &Mesh::setGridUnitSIPerDimension(std::vector<double> gridUnitSI)
return *this;
}

namespace
{
template <typename RandomAccessIterator>
void fromMapOfUnitDimension(
RandomAccessIterator it, std::map<UnitDimension, double> const &udim)
{
for (auto [unit, exponent] : udim)
{
it[static_cast<uint8_t>(unit)] = exponent;
}
}
} // namespace

Mesh &Mesh::setUnitDimension(std::map<UnitDimension, double> const &udim)
{
if (!udim.empty())
{
std::array<double, 7> tmpUnitDimension = this->unitDimension();
fromMapOfUnitDimension(tmpUnitDimension.begin(), udim);
auxiliary::fromMapOfUnitDimension(tmpUnitDimension.begin(), udim);
setAttribute("unitDimension", tmpUnitDimension);
}
return *this;
Expand All @@ -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<std::array<double, 7>> Mesh::gridUnitDimension() const
unit_representations::AsArrays Mesh::gridUnitDimension() const
{
if (containsAttribute("gridUnitDimension"))
{
Expand All @@ -335,7 +323,7 @@ std::vector<std::array<double, 7>> Mesh::gridUnitDimension() const
"[Mesh::gridUnitDimension()] `gridUnitDimension` attribute "
"must have a length equal to a multiple of 7.");
}
std::vector<std::array<double, 7>> 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());
Expand All @@ -347,9 +335,10 @@ std::vector<std::array<double, 7>> Mesh::gridUnitDimension() const
// gridUnitDimension is an optional attribute
// if it is missing, the mesh is interpreted as spatial
std::array<double, 7> spatialMesh;
fromMapOfUnitDimension(spatialMesh.begin(), {{UnitDimension::L, 1}});
auxiliary::fromMapOfUnitDimension(
spatialMesh.begin(), {{UnitDimension::L, 1}});
auto dim = retrieveMeshDimensionality(*this);
std::vector<std::array<double, 7>> res(dim, spatialMesh);
unit_representations::AsArrays res(dim, spatialMesh);
return res;
}
}
Expand Down
61 changes: 61 additions & 0 deletions src/UnitDimension.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include "openPMD/UnitDimension.hpp"
#include <algorithm>
#include <iterator>

namespace openPMD
{
namespace auxiliary
{
void fromMapOfUnitDimension(
double *cursor, std::map<UnitDimension, double> const &udim)
{
for (auto [unit, exponent] : udim)
{
cursor[static_cast<uint8_t>(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<UnitDimension>(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
17 changes: 17 additions & 0 deletions test/CoreTest.cpp
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -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});
Expand All @@ -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<std::string>() ==
"other:customGeometry");
Expand Down

0 comments on commit 00a014a

Please sign in to comment.