Skip to content

Commit

Permalink
Add and use Attribute::getOptional<T>()
Browse files Browse the repository at this point in the history
Useful for backends that might not report back the right numerical type
  • Loading branch information
franzpoeschel committed May 19, 2022
1 parent eb2cb87 commit 1a23a03
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 47 deletions.
126 changes: 126 additions & 0 deletions include/openPMD/backend/Attribute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <complex>
#include <cstdint>
#include <iterator>
#include <optional>
#include <stdexcept>
#include <string>
#include <type_traits>
Expand Down Expand Up @@ -117,6 +118,9 @@ class Attribute
*/
template <typename U>
U get() const;

template <typename U>
std::optional<U> getOptional() const;
};

template <typename T, typename U>
Expand Down Expand Up @@ -226,6 +230,110 @@ auto doConvert(T *pv) -> U
}
#endif

template <typename T, typename U>
auto doConvertOptional(T *pv) -> std::optional<U>
{
(void)pv;
if constexpr (std::is_convertible_v<T, U>)
{
return static_cast<U>(*pv);
}
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsVector_v<U>)
{
if constexpr (std::is_convertible_v<
typename T::value_type,
typename U::value_type>)
{
U res{};
res.reserve(pv->size());
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
return res;
}
else
{
return {};
}
}
// conversion cast: array to vector
// if a backend reports a std::array<> for something where
// the frontend expects a vector
else if constexpr (auxiliary::IsArray_v<T> && auxiliary::IsVector_v<U>)
{
if constexpr (std::is_convertible_v<
typename T::value_type,
typename U::value_type>)
{
U res{};
res.reserve(pv->size());
std::copy(pv->begin(), pv->end(), std::back_inserter(res));
return res;
}
else
{
return {};
}
}
// conversion cast: vector to array
// if a backend reports a std::vector<> for something where
// the frontend expects an array
else if constexpr (auxiliary::IsVector_v<T> && auxiliary::IsArray_v<U>)
{
if constexpr (std::is_convertible_v<
typename T::value_type,
typename U::value_type>)
{
U res{};
if (res.size() != pv->size())
{
throw std::runtime_error(
"getCast: no vector to array conversion possible (wrong "
"requested array size).");
}
for (size_t i = 0; i < res.size(); ++i)
{
res[i] = static_cast<typename U::value_type>((*pv)[i]);
}
return res;
}
else
{
return {};
}
}
// conversion cast: turn a single value into a 1-element vector
else if constexpr (auxiliary::IsVector_v<U>)
{
if constexpr (std::is_convertible_v<T, typename U::value_type>)
{
U res{};
res.reserve(1);
res.push_back(static_cast<typename U::value_type>(*pv));
return res;
}
else
{
return {};
}
}
else
{
return {};
}
#if defined(__INTEL_COMPILER)
/*
* ICPC has trouble with if constexpr, thinking that return statements are
* missing afterwards. Deactivate the warning.
* Note that putting a statement here will not help to fix this since it will
* then complain about unreachable code.
* https://community.intel.com/t5/Intel-C-Compiler/quot-if-constexpr-quot-and-quot-missing-return-statement-quot-in/td-p/1154551
*/
#pragma warning(disable : 1011)
}
#pragma warning(default : 1011)
#else
}
#endif

/** Retrieve a stored specific Attribute and cast if convertible.
*
* @throw std::runtime_error if stored object is not static castable to U.
Expand All @@ -245,10 +353,28 @@ inline U getCast(Attribute const &a)
v);
}

template <typename U>
inline std::optional<U> getCastOptional(Attribute const &a)
{
auto v = a.getResource();

return std::visit(
[](auto &&containedValue) -> U {
using containedType = std::decay_t<decltype(containedValue)>;
return doConvert<containedType, U>(&containedValue);
},
v);
}

template <typename U>
U Attribute::get() const
{
return getCast<U>(Variant::getResource());
}

template <typename U>
std::optional<U> Attribute::getOptional() const
{
return getCastOptional<U>(Variant::getResource());
}
} // namespace openPMD
30 changes: 10 additions & 20 deletions include/openPMD/backend/BaseRecord.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,24 +317,10 @@ inline void BaseRecord<T_elem>::readBase()
aRead.name = "unitDimension";
this->IOHandler()->enqueue(IOTask(this, aRead));
this->IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::ARR_DBL_7)
this->setAttribute(
"unitDimension",
Attribute(*aRead.resource).template get<std::array<double, 7> >());
else if (*aRead.dtype == DT::VEC_DOUBLE)
{
auto vec =
Attribute(*aRead.resource).template get<std::vector<double> >();
if (vec.size() == 7)
{
std::array<double, 7> arr;
std::copy(vec.begin(), vec.end(), arr.begin());
this->setAttribute("unitDimension", arr);
}
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'unitDimension'");
}
if (auto val =
Attribute(*aRead.resource).getOptional<std::array<double, 7> >();
val.has_value())
this->setAttribute("unitDimension", val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'unitDimension'");
Expand All @@ -344,10 +330,14 @@ inline void BaseRecord<T_elem>::readBase()
this->IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::FLOAT)
this->setAttribute(
"timeOffset", Attribute(*aRead.resource).template get<float>());
"timeOffset", Attribute(*aRead.resource).get<float>());
else if (*aRead.dtype == DT::DOUBLE)
this->setAttribute(
"timeOffset", Attribute(*aRead.resource).template get<double>());
"timeOffset", Attribute(*aRead.resource).get<double>());
// conversion cast if a backend reports an integer type
else if (auto val = Attribute(*aRead.resource).getOptional<double>();
val.has_value())
this->setAttribute("timeOffset", val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'timeOffset'");
Expand Down
12 changes: 10 additions & 2 deletions src/Iteration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,10 @@ void Iteration::read_impl(std::string const &groupPath)
setDt(Attribute(*aRead.resource).get<double>());
else if (*aRead.dtype == DT::LONG_DOUBLE)
setDt(Attribute(*aRead.resource).get<long double>());
// conversion cast if a backend reports an integer type
else if (auto val = Attribute(*aRead.resource).getOptional<double>();
val.has_value())
setDt(val.value());
else
throw std::runtime_error("Unexpected Attribute datatype for 'dt'");

Expand All @@ -429,14 +433,18 @@ void Iteration::read_impl(std::string const &groupPath)
setTime(Attribute(*aRead.resource).get<double>());
else if (*aRead.dtype == DT::LONG_DOUBLE)
setTime(Attribute(*aRead.resource).get<long double>());
else if (auto val = Attribute(*aRead.resource).getOptional<double>();
val.has_value())
setTime(val.value());
else
throw std::runtime_error("Unexpected Attribute datatype for 'time'");

aRead.name = "timeUnitSI";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::DOUBLE)
setTimeUnitSI(Attribute(*aRead.resource).get<double>());
if (auto val = Attribute(*aRead.resource).getOptional<double>();
val.has_value())
setTimeUnitSI(val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'timeUnitSI'");
Expand Down
15 changes: 10 additions & 5 deletions src/Mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,25 +337,30 @@ void Mesh::read()
else if (
*aRead.dtype == DT::VEC_LONG_DOUBLE || *aRead.dtype == DT::LONG_DOUBLE)
setGridSpacing(a.get<std::vector<long double> >());
// conversion cast if a backend reports an integer type
else if (auto val = a.getOptional<std::vector<double> >(); val.has_value())
setGridSpacing(val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'gridSpacing'");

aRead.name = "gridGlobalOffset";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::VEC_DOUBLE || *aRead.dtype == DT::DOUBLE)
setGridGlobalOffset(
Attribute(*aRead.resource).get<std::vector<double> >());
if (auto val =
Attribute(*aRead.resource).getOptional<std::vector<double> >();
val.has_value())
setGridGlobalOffset(val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'gridGlobalOffset'");

aRead.name = "gridUnitSI";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::DOUBLE)
setGridUnitSI(Attribute(*aRead.resource).get<double>());
if (auto val = Attribute(*aRead.resource).getOptional<double>();
val.has_value())
setGridUnitSI(val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'gridUnitSI'");
Expand Down
5 changes: 3 additions & 2 deletions src/RecordComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,9 @@ void RecordComponent::readBase()
aRead.name = "unitSI";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::DOUBLE)
setUnitSI(Attribute(*aRead.resource).get<double>());
if (auto val = Attribute(*aRead.resource).getOptional<double>();
val.has_value())
setUnitSI(val.value());
else
throw std::runtime_error("Unexpected Attribute datatype for 'unitSI'");

Expand Down
26 changes: 15 additions & 11 deletions src/Series.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1241,31 +1241,33 @@ void Series::readGorVBased(bool do_init)
void Series::readBase()
{
auto &series = get();
using DT = Datatype;
Parameter<Operation::READ_ATT> aRead;

aRead.name = "openPMD";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::STRING)
setOpenPMD(Attribute(*aRead.resource).get<std::string>());
if (auto val = Attribute(*aRead.resource).getOptional<std::string>();
val.has_value())
setOpenPMD(val.value());
else
throw std::runtime_error("Unexpected Attribute datatype for 'openPMD'");

aRead.name = "openPMDextension";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == determineDatatype<uint32_t>())
setOpenPMDextension(Attribute(*aRead.resource).get<uint32_t>());
if (auto val = Attribute(*aRead.resource).getOptional<uint32_t>();
val.has_value())
setOpenPMDextension(val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'openPMDextension'");

aRead.name = "basePath";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::STRING)
setAttribute("basePath", Attribute(*aRead.resource).get<std::string>());
if (auto val = Attribute(*aRead.resource).getOptional<std::string>();
val.has_value())
setAttribute("basePath", val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'basePath'");
Expand All @@ -1280,13 +1282,14 @@ void Series::readBase()
aRead.name = "meshesPath";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::STRING)
if (auto val = Attribute(*aRead.resource).getOptional<std::string>();
val.has_value())
{
/* allow setting the meshes path after completed IO */
for (auto &it : series.iterations)
it.second.meshes.written() = false;

setMeshesPath(Attribute(*aRead.resource).get<std::string>());
setMeshesPath(val.value());

for (auto &it : series.iterations)
it.second.meshes.written() = true;
Expand All @@ -1304,13 +1307,14 @@ void Series::readBase()
aRead.name = "particlesPath";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == DT::STRING)
if (auto val = Attribute(*aRead.resource).getOptional<std::string>();
val.has_value())
{
/* allow setting the meshes path after completed IO */
for (auto &it : series.iterations)
it.second.particles.written() = false;

setParticlesPath(Attribute(*aRead.resource).get<std::string>());
setParticlesPath(val.value());

for (auto &it : series.iterations)
it.second.particles.written() = true;
Expand Down
3 changes: 3 additions & 0 deletions src/backend/MeshRecordComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ void MeshRecordComponent::read()
else if (
*aRead.dtype == DT::VEC_LONG_DOUBLE || *aRead.dtype == DT::LONG_DOUBLE)
setPosition(a.get<std::vector<long double> >());
// conversion cast if a backend reports an integer type
else if (auto val = a.getOptional<std::vector<double> >(); val.has_value())
setPosition(val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'position'");
Expand Down
9 changes: 4 additions & 5 deletions src/backend/PatchRecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,10 @@ void PatchRecord::read()
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);

if (*aRead.dtype == Datatype::ARR_DBL_7 ||
*aRead.dtype == Datatype::VEC_DOUBLE)
this->setAttribute(
"unitDimension",
Attribute(*aRead.resource).template get<std::array<double, 7> >());
if (auto val =
Attribute(*aRead.resource).getOptional<std::array<double, 7> >();
val.has_value())
this->setAttribute("unitDimension", val.value());
else
throw std::runtime_error(
"Unexpected Attribute datatype for 'unitDimension'");
Expand Down
5 changes: 3 additions & 2 deletions src/backend/PatchRecordComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ void PatchRecordComponent::read()
aRead.name = "unitSI";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush(internal::defaultFlushParams);
if (*aRead.dtype == Datatype::DOUBLE)
setUnitSI(Attribute(*aRead.resource).get<double>());
if (auto val = Attribute(*aRead.resource).getOptional<double>();
val.has_value())
setUnitSI(val.value());
else
throw std::runtime_error("Unexpected Attribute datatype for 'unitSI'");

Expand Down

0 comments on commit 1a23a03

Please sign in to comment.