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
24 changes: 19 additions & 5 deletions include/openPMD/IO/ADIOS/ADIOS2PreloadAttributes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
#include <adios2.h>
#include <functional>
#include <map>
#include <sstream>
#include <stddef.h>
#include <type_traits>

#include "openPMD/Datatype.hpp"

Expand Down Expand Up @@ -132,8 +134,9 @@ namespace detail
* of PreloadAdiosAttributes is called.
*/
template< typename T >
AttributeWithShape< T >
getAttribute( std::string const & name ) const;
AttributeWithShape< T > getAttribute( std::string const & name ) const;

Datatype attributeType( std::string const & name ) const;
};

template< typename T >
Expand All @@ -147,10 +150,21 @@ namespace detail
"[ADIOS2] Requested attribute not found: " + name );
}
AttributeLocation const & location = it->second;
if( location.dt != determineDatatype< T >() )
Datatype determinedDatatype = determineDatatype< T >();
if( std::is_same< T, signed char >::value )
{
throw std::runtime_error(
"[ADIOS2] Wrong datatype for attribute: " + name );
// workaround: we use Datatype::CHAR to represent ADIOS2 signed char
// (ADIOS2 does not have chars with unspecified signed-ness
// anyway)
determinedDatatype = Datatype::CHAR;
}
if( location.dt != determinedDatatype )
{
std::stringstream errorMsg;
errorMsg << "[ADIOS2] Wrong datatype for attribute: " << name
<< "(location.dt=" << location.dt
<< ", T=" << determineDatatype< T >() << ")";
throw std::runtime_error( errorMsg.str() );
}
AttributeWithShape< T > res;
res.shape = location.shape;
Expand Down
38 changes: 15 additions & 23 deletions src/IO/ADIOS/ADIOS2Auxiliary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,37 +230,29 @@ namespace detail
: toVectorType( basicType );
return openPmdType;
}
else if( shape.size() == 2 && basicType == Datatype::CHAR )
else if(
shape.size() == 2 &&
( basicType == Datatype::CHAR ||
basicType == Datatype::UCHAR ) )
{
return Datatype::VEC_STRING;
}
else
{
throw std::runtime_error(
"[ADIOS2] Unexpected shape for " + attributeName );
std::stringstream errorMsg;
errorMsg << "[ADIOS2] Unexpected shape for "
<< attributeName << ": [";
for( auto const ext : shape )
{
errorMsg << std::to_string( ext ) << ", ";
}
errorMsg << "] of type "
<< datatypeToString( basicType );
throw std::runtime_error( errorMsg.str() );
}
}
}
if( shape.size() <= 1 )
{
// size == 0 <=> global single value variable
auto size = shape.size() == 0 ? 1 : shape[ 0 ];
Datatype openPmdType = size == 1
? basicType
: size == 7 && basicType == Datatype::DOUBLE
? Datatype::ARR_DBL_7
: toVectorType( basicType );
return openPmdType;
}
else if( shape.size() == 2 && basicType == Datatype::CHAR )
{
return Datatype::VEC_STRING;
}
else
{
throw std::runtime_error(
"[ADIOS2] Unexpected shape for " + attributeName );
}
throw std::runtime_error( "Unreachable!" );
}
}
} // namespace detail
Expand Down
119 changes: 98 additions & 21 deletions src/IO/ADIOS/ADIOS2IOHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1659,30 +1659,107 @@ namespace detail
std::string name,
std::shared_ptr< Attribute::resource > resource )
{
detail::AttributeWithShape< char > attr =
preloadedAttributes.getAttribute< char >( name );
if( attr.shape.size() != 2 )
{
throw std::runtime_error( "[ADIOS2] Expecting 2D ADIOS variable" );
}
size_t height = attr.shape[ 0 ];
size_t width = attr.shape[ 1 ];
/*
* char_type parameter only for specifying the "template" type.
*/
auto loadFromDatatype =
[ &preloadedAttributes, &name, &resource ]( auto char_type ) {
using char_t = decltype( char_type );
detail::AttributeWithShape< char_t > attr =
preloadedAttributes.getAttribute< char_t >( name );
if( attr.shape.size() != 2 )
{
throw std::runtime_error(
"[ADIOS2] Expecting 2D ADIOS variable" );
}
char_t const * loadedData = attr.data;
size_t height = attr.shape[ 0 ];
size_t width = attr.shape[ 1 ];

std::vector< std::string > res( height );
for( size_t i = 0; i < height; ++i )
std::vector< std::string > res( height );
if( std::is_signed< char >::value ==

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it ok to add a bit more in-code comments for this part? Just to make it easier for our future selves, reading the workaround.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

std::is_signed< char_t >::value )
{
/*
* This branch is chosen if the signedness of the
* ADIOS variable corresponds with the signedness of the
* char type on the current platform.
* In this case, the C++ standard guarantees that the
* representations for char and (un)signed char are
* identical, reinterpret_cast-ing the loadedData to
* char in order to construct our strings will be fine.
*/
for( size_t i = 0; i < height; ++i )
{
size_t start = i * width;
char const * start_ptr =
reinterpret_cast< char const * >(
loadedData + start );
size_t j = 0;
while( j < width && start_ptr[ j ] != 0 )
{
++j;
}
std::string & str = res[ i ];
str.append( start_ptr, start_ptr + j );
}
}
else
{
/*
* This branch is chosen if the signedness of the
* ADIOS variable is different from the signedness of the
* char type on the current platform.
* In this case, we play it safe, and explicitly convert
* the loadedData to char pointwise.
*/
std::vector< char > converted( width );
for( size_t i = 0; i < height; ++i )
{
size_t start = i * width;
auto const * start_ptr = loadedData + start;
size_t j = 0;
while( j < width && start_ptr[ j ] != 0 )
{
converted[ j ] = start_ptr[ j ];
++j;
}
std::string & str = res[ i ];
str.append( converted.data(), converted.data() + j );
}
}

*resource = res;
};
/*
* If writing char variables in ADIOS2, they might become either int8_t
* or uint8_t on disk depending on the platform.
* So allow reading from both types.
*/
switch( preloadedAttributes.attributeType( name ) )
{
size_t start = i * width;
char const * start_ptr = attr.data + start;
size_t j = 0;
while( j < width && start_ptr[ j ] != 0 )
{
++j;
}
std::string & str = res[ i ];
str.append( start_ptr, start_ptr + j );
/*
* Workaround for two bugs at once:
* ADIOS2 does not have an explicit char type,
* we don't have an explicit schar type.
* Until this is fixed, we use CHAR to represent ADIOS signed char.
*/
case Datatype::CHAR: {
using schar_t = signed char;
loadFromDatatype( schar_t{} );
break;
}
case Datatype::UCHAR: {
using uchar_t = unsigned char;
loadFromDatatype( uchar_t{} );
break;
}
default: {
throw std::runtime_error(
"[ADIOS2] Expecting 2D ADIOS variable of "
"type signed or unsigned char." );
}
}

*resource = res;
}

template< typename T, size_t n >
Expand Down
11 changes: 11 additions & 0 deletions src/IO/ADIOS/ADIOS2PreloadAttributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,17 @@ namespace detail
pair.second );
}
}

Datatype
PreloadAdiosAttributes::attributeType( std::string const & name ) const
{
auto it = m_offsets.find( name );
if( it == m_offsets.end() )
{
return Datatype::UNDEFINED;
}
return it->second.dt;
}
} // namespace detail
} // namespace openPMD

Expand Down
Loading