Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 108 additions & 8 deletions include/openPMD/backend/Attribute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ struct DoConvert;
template< typename T, typename U >
struct DoConvert<T, U, false>
{
template< typename PV >
U operator()( PV )
U operator()( T * )
{
throw std::runtime_error("getCast: no cast possible.");
}
Expand All @@ -108,8 +107,7 @@ struct DoConvert<T, U, false>
template< typename T, typename U >
struct DoConvert<T, U, true>
{
template< typename PV >
U operator()( PV pv )
U operator()( T * pv )
{
return static_cast< U >( *pv );
}
Expand All @@ -120,8 +118,8 @@ struct DoConvert<std::vector< T >, std::vector< U >, false>
{
static constexpr bool convertible = std::is_convertible<T, U>::value;

template< typename PV, typename UU = U >
auto operator()( PV pv )
template< typename UU = U >
auto operator()( std::vector< T > const * pv )
-> typename std::enable_if< convertible, std::vector< UU > >::type
{
std::vector< U > u;
Expand All @@ -130,14 +128,101 @@ struct DoConvert<std::vector< T >, std::vector< U >, false>
return u;
}

template< typename PV, typename UU = U >
auto operator()( PV )
template< typename UU = U >
auto operator()( std::vector< T > const * )
-> typename std::enable_if< !convertible, std::vector< UU > >::type
{
throw std::runtime_error("getCast: no vector cast possible.");
}
};

// conversion cast: turn a single value into a 1-element vector
template< typename T, typename U >
struct DoConvert<T, std::vector< U >, false>
{
static constexpr bool convertible = std::is_convertible<T, U>::value;

template< typename UU = U >
auto operator()( T const * pv )
-> typename std::enable_if< convertible, std::vector< UU > >::type
{
std::vector< U > u;
u.reserve( 1 );
u.push_back( static_cast< U >( *pv ) );
return u;
}

template< typename UU = U >
auto operator()( T const * )
-> typename std::enable_if< !convertible, std::vector< UU > >::type
{
throw std::runtime_error(
"getCast: no scalar to vector conversion possible.");
}
};

// conversion cast: array to vector
// if a backend reports a std::array<> for something where the frontend expects
// a vector
template< typename T, typename U, size_t n >
struct DoConvert<std::array< T, n >, std::vector< U >, false>
{
static constexpr bool convertible = std::is_convertible<T, U>::value;

template< typename UU = U >
auto operator()( std::array< T, n > const * pv )
-> typename std::enable_if< convertible, std::vector< UU > >::type
{
std::vector< U > u;
u.reserve( n );
std::copy( pv->begin(), pv->end(), std::back_inserter(u) );
return u;
}

template< typename UU = U >
auto operator()( std::array< T, n > const * )
-> typename std::enable_if< !convertible, std::vector< UU > >::type
{
throw std::runtime_error(
"getCast: no array to vector conversion possible.");
}
};

// conversion cast: vector to array
// if a backend reports a std::vector<> for something where the frontend expects
// an array
template< typename T, typename U, size_t n >
struct DoConvert<std::vector< T >, std::array< U, n >, false>
{
static constexpr bool convertible = std::is_convertible<T, U>::value;

template< typename UU = U >
auto operator()( std::vector< T > const * pv )
-> typename std::enable_if< convertible, std::array< UU, n > >::type
{
std::array< U, n > u;
if( n != pv->size() )
{
throw std::runtime_error(
"getCast: no vector to array conversion possible "
"(wrong requested array size).");
}
for( size_t i = 0; i < n; ++i )
{
u[ i ] = static_cast< U >( ( *pv )[ i ] );
}
return u;
}

template< typename UU = U >
auto operator()( std::vector< T > const * )
-> typename std::enable_if< !convertible, std::array< UU, n > >::type
{
throw std::runtime_error(
"getCast: no vector to array conversion possible.");
}
};

/** Retrieve a stored specific Attribute and cast if convertible.
*
* @throw std::runtime_error if stored object is not static castable to U.
Expand All @@ -150,6 +235,12 @@ getCast( Attribute const & a )
{
auto v = a.getResource();

// icpc 2021.3.0 does not like variantSrc::visit (with mpark-variant)
// we use variantSrc::visit for the other compilers to avoid having an
// endless list of if-then-else
// also, once we switch to C++17, we might throw this out in
// favor of a hopefully working std::visit
#if defined(__ICC) || defined(__INTEL_COMPILER)
if(auto pvalue_c = variantSrc::get_if< char >( &v ) )
return DoConvert<char, U>{}(pvalue_c);
else if(auto pvalue_uc = variantSrc::get_if< unsigned char >( &v ) )
Expand Down Expand Up @@ -226,6 +317,15 @@ getCast( Attribute const & a )
return DoConvert<bool, U>{}(pvalue_b);
else
throw std::runtime_error("getCast: unknown Datatype.");

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

template< typename U >
Expand Down
16 changes: 4 additions & 12 deletions src/Mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,35 +318,27 @@ Mesh::read()
aRead.name = "axisLabels";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush();
if( *aRead.dtype == DT::VEC_STRING )
if( *aRead.dtype == DT::VEC_STRING || *aRead.dtype == DT::STRING)
setAxisLabels(Attribute(*aRead.resource).get< std::vector< std::string > >());
else if( *aRead.dtype == DT::STRING )
setAxisLabels({Attribute(*aRead.resource).get< std::string >()});
else
throw std::runtime_error("Unexpected Attribute datatype for 'axisLabels'");

aRead.name = "gridSpacing";
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush();
Attribute a = Attribute(*aRead.resource);
if( *aRead.dtype == DT::VEC_FLOAT )
if( *aRead.dtype == DT::VEC_FLOAT || *aRead.dtype == DT::FLOAT )
setGridSpacing(a.get< std::vector< float > >());
else if( *aRead.dtype == DT::FLOAT )
setGridSpacing(std::vector< float >({a.get< float >()}));
else if( *aRead.dtype == DT::VEC_DOUBLE )
else if( *aRead.dtype == DT::VEC_DOUBLE || *aRead.dtype == DT::DOUBLE )
setGridSpacing(a.get< std::vector< double > >());
else if( *aRead.dtype == DT::DOUBLE )
setGridSpacing(std::vector< double >({a.get< double >()}));
else
throw std::runtime_error("Unexpected Attribute datatype for 'gridSpacing'");

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

Expand Down
5 changes: 2 additions & 3 deletions src/RecordComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,8 @@ RecordComponent::readBase()

// uint64_t check
Datatype const attrDtype = *aRead.dtype;
if( isSame( attrDtype, determineDatatype< uint64_t >() ) )
e.push_back( a.get< uint64_t >() );
else if( isSame( attrDtype, determineDatatype< std::vector< uint64_t > >() ) )
if( isSame( attrDtype, determineDatatype< std::vector< uint64_t > >() )
|| isSame( attrDtype, determineDatatype< uint64_t >() ) )
for( auto const& val : a.get< std::vector< uint64_t > >() )
e.push_back( val );
else
Expand Down
12 changes: 3 additions & 9 deletions src/backend/MeshRecordComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,12 @@ MeshRecordComponent::read()
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush();
Attribute a = Attribute(*aRead.resource);
if( *aRead.dtype == DT::VEC_FLOAT )
if( *aRead.dtype == DT::VEC_FLOAT || *aRead.dtype == DT::FLOAT )
setPosition(a.get< std::vector< float > >());
else if( *aRead.dtype == DT::FLOAT )
setPosition(std::vector< float >({a.get< float >()}));
else if( *aRead.dtype == DT::VEC_DOUBLE )
else if( *aRead.dtype == DT::VEC_DOUBLE || *aRead.dtype == DT::DOUBLE )
setPosition(a.get< std::vector< double > >());
else if( *aRead.dtype == DT::DOUBLE )
setPosition(std::vector< double >({a.get< double >()}));
else if( *aRead.dtype == DT::VEC_LONG_DOUBLE )
else if( *aRead.dtype == DT::VEC_LONG_DOUBLE || *aRead.dtype == DT::LONG_DOUBLE )
setPosition(a.get< std::vector< long double > >());
else if( *aRead.dtype == DT::LONG_DOUBLE )
setPosition(std::vector< long double >({a.get< long double >()}));
else
throw std::runtime_error( "Unexpected Attribute datatype for 'position'");

Expand Down
15 changes: 1 addition & 14 deletions src/backend/PatchRecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,8 @@ PatchRecord::read()
IOHandler()->enqueue(IOTask(this, aRead));
IOHandler()->flush();

if( *aRead.dtype == Datatype::ARR_DBL_7 )
if( *aRead.dtype == Datatype::ARR_DBL_7 || *aRead.dtype == Datatype::VEC_DOUBLE )
this->setAttribute("unitDimension", Attribute(*aRead.resource).template get< std::array< double, 7 > >());
else if( *aRead.dtype == Datatype::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'");
}
else
throw std::runtime_error("Unexpected Attribute datatype for 'unitDimension'");

Expand Down
70 changes: 70 additions & 0 deletions test/CoreTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -950,3 +950,73 @@ TEST_CASE( "load_chunk_wrong_datatype", "[core]" )
"Type conversion during chunk loading not yet implemented" ) );
}
}

TEST_CASE( "DoConvert_single_value_to_vector", "[core]" )
{
#if openPMD_HAVE_ADIOS2
{
Series write( "../samples/writeSingleMesh.bp", Access::CREATE );
auto E_x = write.iterations[ 0 ].meshes[ "E" ][ "x" ];
E_x.resetDataset( { Datatype::INT, { 10 } } );
E_x.makeConstant( 10 );
}
{
Series read( "../samples/writeSingleMesh.bp", Access::READ_ONLY );
auto E = read.iterations[ 0 ].meshes[ "E" ];
REQUIRE( E.axisLabels() == std::vector< std::string >{ "x" } );
}
#endif
{
char val = 'x';
Attribute attr{ val };

// the following conversions should be possible
REQUIRE( attr.get< char >() == 'x' ); // no conversion
REQUIRE( attr.get< unsigned char >() == 'x' );
REQUIRE( attr.get< signed char >() == 'x' );
// all the previous ones, but make them single-element vectors now
REQUIRE(
attr.get< std::vector< char > >() == std::vector< char >{ 'x' } );
REQUIRE(
attr.get< std::vector< unsigned char > >() ==
std::vector< unsigned char >{ 'x' } );
REQUIRE(
attr.get< std::vector< signed char > >() ==
std::vector< signed char >{ 'x' } );
}
{
std::array< double, 7 > array{{ 0, 1, 2, 3, 4, 5, 6 }};
Attribute attr{ array };

// the following conversions should be possible
REQUIRE( attr.get< std::array< double, 7 > >() == array );
// we don't need array-to-array conversions,
// so array< int, 7 > cannot be loaded here
REQUIRE(
attr.get< std::vector< double > >() ==
std::vector< double >{ 0, 1, 2, 3, 4, 5, 6 } );
REQUIRE(
attr.get< std::vector< int > >() ==
std::vector< int >{ 0, 1, 2, 3, 4, 5, 6 } );
}
{
std::vector< double > vector{ 0, 1, 2, 3, 4, 5, 6 };
std::array< double, 7 > arraydouble{{ 0, 1, 2, 3, 4, 5, 6 }};
std::array< int, 7 > arrayint{{ 0, 1, 2, 3, 4, 5, 6 }};
Attribute attr{ vector };

// the following conversions should be possible
REQUIRE( attr.get< std::array< double, 7 > >() == arraydouble );
REQUIRE( attr.get< std::array< int, 7 > >() == arrayint );
REQUIRE_THROWS_WITH(
( attr.get< std::array< int, 8 > >() ),
Catch::Equals( "getCast: no vector to array conversion possible "
"(wrong requested array size)." ) );
REQUIRE(
attr.get< std::vector< double > >() ==
std::vector< double >{ 0, 1, 2, 3, 4, 5, 6 } );
REQUIRE(
attr.get< std::vector< int > >() ==
std::vector< int >{ 0, 1, 2, 3, 4, 5, 6 } );
}
}