ENH: add UPstreamTraits support to map data types and opcodes
The front-end traits: - UPstream_dataType trait: This wrapper is unwinds the type to check against base/alias, but also checks if it is a component aggregate of a supported UPstream data type. This will be that main entry point for usage. - UPstream_opType trait: Provides a mapping of OpenFOAM ops to their MPI equivalent. The \c opcode_id is the corresponding internal representation. The lower-level traits (not normally used within coding) - UPstream_base_dataType trait: Tests true/false if the specified data type has an internal MPI equivalent. The \c datatype_id is the corresponding internal enumeration. Even if this tests as false, it will always return \c type_byte as the fallback for general contiguous data - UPstream_alias_dataType trait: Provides mapping for <int/long/long long,...> to the fundamental 32/64 integrals, since <int/long/long long,...> may not otherwise directly map on all systems. NOTE: can use the updates Test-machine-sizes test application to determine if all data types and aliases are properly defined on different systems
This commit is contained in:
@ -26,7 +26,28 @@ License
|
||||
Description
|
||||
A set of traits associated with UPstream communication
|
||||
|
||||
SourceFiles
|
||||
- UPstream_dataType trait:
|
||||
This wrapper is unwinds the type to check against base/alias, but also
|
||||
checks if it is a component aggregate of a supported UPstream data type.
|
||||
This will be that main entry point for usage.
|
||||
|
||||
- UPstream_opType trait:
|
||||
Provides a mapping of OpenFOAM ops to their MPI equivalent.
|
||||
The \c opcode_id is the corresponding internal representation.
|
||||
|
||||
Note
|
||||
Additional helper traits:
|
||||
|
||||
- UPstream_base_dataType trait:
|
||||
Tests true/false if the specified data type has an internal
|
||||
MPI equivalent. The \c datatype_id is the corresponding
|
||||
internal enumeration. Even if this tests as false, it will
|
||||
always return \c type_byte as the fallback for general contiguous data
|
||||
|
||||
- UPstream_alias_dataType trait:
|
||||
Provides mapping for <int/long/long long,...> to the fundamental
|
||||
32/64 bit integrals, since <int/long/long long,...> may not otherwise
|
||||
directly map on all systems.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
@ -36,9 +57,314 @@ SourceFiles
|
||||
#include "UPstream.H"
|
||||
#include <cstdint>
|
||||
#include <ios> // For streamsize
|
||||
#include <type_traits>
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
// Forward Declarations
|
||||
|
||||
// Some vector-space types
|
||||
// -----------------------
|
||||
//! \cond
|
||||
template<class T> class Vector;
|
||||
template<class T> class SymmTensor;
|
||||
template<class T> class Tensor;
|
||||
//! \endcond
|
||||
|
||||
// -------------------------
|
||||
// Some binary operators (as per ops.H), but since ListOps.H is included
|
||||
// by UPstream.H, don't need to forward declare
|
||||
// -------------------------
|
||||
// template<class T> struct minOp;
|
||||
// template<class T> struct maxOp;
|
||||
// template<class T> struct plusOp;
|
||||
// template<class T> struct sumOp;
|
||||
// template<class T> struct multiplyOp;
|
||||
// template<class T> struct bitAndOp;
|
||||
// template<class T> struct bitOrOp;
|
||||
// template<class T> struct bitXorOp;
|
||||
|
||||
//! \cond
|
||||
template<class T> struct UPstream_dataType;
|
||||
template<class T> struct UPstream_opType;
|
||||
//! \endcond
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
// Base traits
|
||||
|
||||
//- A supported UPstream (MPI) reduce/window operation type
|
||||
template<class T>
|
||||
struct UPstream_opType : std::false_type
|
||||
{
|
||||
static constexpr auto opcode_id = UPstream::opCodes::invalid;
|
||||
};
|
||||
|
||||
|
||||
//- A supported UPstream data type (intrinsic or user-defined)
|
||||
template<class T>
|
||||
struct UPstream_base_dataType : std::false_type
|
||||
{
|
||||
static constexpr auto datatype_id = UPstream::dataTypes::invalid;
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
// Trait specializations (op-codes)
|
||||
|
||||
//- Map minOp\<T\> to \c UPstream::opCodes::op_min
|
||||
template<class T>
|
||||
struct UPstream_opType<Foam::minOp<T>> : std::true_type
|
||||
{
|
||||
static constexpr auto opcode_id = UPstream::opCodes::op_min;
|
||||
};
|
||||
|
||||
//- Map maxOp\<T\> to \c UPstream::opCodes::op_max
|
||||
template<class T>
|
||||
struct UPstream_opType<Foam::maxOp<T>> : std::true_type
|
||||
{
|
||||
static constexpr auto opcode_id = UPstream::opCodes::op_max;
|
||||
};
|
||||
|
||||
//- Map sumOp\<T\> to \c UPstream::opCodes::op_sum
|
||||
template<class T>
|
||||
struct UPstream_opType<Foam::sumOp<T>> : std::true_type
|
||||
{
|
||||
static constexpr auto opcode_id = UPstream::opCodes::op_sum;
|
||||
};
|
||||
|
||||
//- Map plusOp\<T\> to \c UPstream::opCodes::op_sum
|
||||
//- as a recognized alternative to sumOp\<T\>
|
||||
template<class T>
|
||||
struct UPstream_opType<Foam::plusOp<T>> : std::true_type
|
||||
{
|
||||
static constexpr auto opcode_id = UPstream::opCodes::op_sum;
|
||||
};
|
||||
|
||||
//- Map multiplyOp\<T\> to \c UPstream::opCodes::op_prod
|
||||
template<class T>
|
||||
struct UPstream_opType<Foam::multiplyOp<T>> : std::true_type
|
||||
{
|
||||
static constexpr auto opcode_id = UPstream::opCodes::op_prod;
|
||||
};
|
||||
|
||||
// NOTE (2025-02):
|
||||
// currently no mappings provided for
|
||||
// (op_bool_and, op_bool_or, op_bool_xor) until the calling semantics
|
||||
// have been properly defined
|
||||
|
||||
|
||||
// These are only viable for unsigned integral types,
|
||||
// probably not for signed integral types.
|
||||
// Be extra restrictive for now
|
||||
|
||||
//- Map bitAndOp\<T\> to \c UPstream::opCodes::op_bit_and
|
||||
//- (for unsigned integrals)
|
||||
template<class T>
|
||||
struct UPstream_opType<Foam::bitAndOp<T>>
|
||||
:
|
||||
// ie, std::unsigned_integral<T> concept
|
||||
std::bool_constant<std::is_integral_v<T> && !std::is_signed_v<T>>
|
||||
{
|
||||
static constexpr auto opcode_id = []() constexpr noexcept
|
||||
{
|
||||
if constexpr (std::is_integral_v<T> && !std::is_signed_v<T>)
|
||||
return UPstream::opCodes::op_bit_and;
|
||||
else
|
||||
return UPstream::opCodes::invalid;
|
||||
}();
|
||||
};
|
||||
|
||||
//- Map bitOrOp\<T\> to \c UPstream::opCodes::op_bit_or
|
||||
//- (for unsigned integrals)
|
||||
template<class T>
|
||||
struct UPstream_opType<Foam::bitOrOp<T>>
|
||||
:
|
||||
// ie, std::unsigned_integral<T> concept
|
||||
std::bool_constant<std::is_integral_v<T> && !std::is_signed_v<T>>
|
||||
{
|
||||
static constexpr auto opcode_id = []() constexpr noexcept
|
||||
{
|
||||
if constexpr (std::is_integral_v<T> && !std::is_signed_v<T>)
|
||||
return UPstream::opCodes::op_bit_or;
|
||||
else
|
||||
return UPstream::opCodes::invalid;
|
||||
}();
|
||||
};
|
||||
|
||||
//- Map bitXorOp\<T\> to \c UPstream::opCodes::op_bit_xor
|
||||
//- (for unsigned integrals)
|
||||
template<class T>
|
||||
struct UPstream_opType<Foam::bitXorOp<T>>
|
||||
:
|
||||
// ie, std::unsigned_integral<T> concept
|
||||
std::bool_constant<std::is_integral_v<T> && !std::is_signed_v<T>>
|
||||
{
|
||||
static constexpr auto opcode_id = []() constexpr noexcept
|
||||
{
|
||||
if constexpr (std::is_integral_v<T> && !std::is_signed_v<T>)
|
||||
return UPstream::opCodes::op_bit_xor;
|
||||
else
|
||||
return UPstream::opCodes::invalid;
|
||||
}();
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
// Trait specializations (data types)
|
||||
|
||||
// Specializations to match elements of UPstream::dataTypes
|
||||
#undef defineUPstreamDataTraits
|
||||
#define defineUPstreamDataTraits(TypeId, Type) \
|
||||
\
|
||||
/*! \brief Map \c Type to UPstream::dataTypes::TypeId */ \
|
||||
template<> struct UPstream_base_dataType<Type> : std::true_type \
|
||||
{ \
|
||||
static constexpr auto datatype_id = UPstream::dataTypes::TypeId; \
|
||||
}; \
|
||||
/*! \brief Map \c const \c Type to \c UPstream::dataTypes::TypeId */ \
|
||||
template<> struct UPstream_base_dataType<const Type> : std::true_type \
|
||||
{ \
|
||||
static constexpr auto datatype_id = UPstream::dataTypes::TypeId; \
|
||||
};
|
||||
|
||||
|
||||
// Intrinsic Types [8]:
|
||||
// Note: uses 'int32_t,int64_t,...' instead of 'int,long,...' to minimize
|
||||
// the possibility of duplicates types.
|
||||
// OpenFOAM defines Foam::label as either int32_t,int64_t (not int,long) too.
|
||||
defineUPstreamDataTraits(type_byte, char);
|
||||
defineUPstreamDataTraits(type_byte, unsigned char);
|
||||
defineUPstreamDataTraits(type_int32, int32_t);
|
||||
defineUPstreamDataTraits(type_int64, int64_t);
|
||||
defineUPstreamDataTraits(type_uint32, uint32_t);
|
||||
defineUPstreamDataTraits(type_uint64, uint64_t);
|
||||
defineUPstreamDataTraits(type_float, float);
|
||||
defineUPstreamDataTraits(type_double, double);
|
||||
defineUPstreamDataTraits(type_long_double, long double);
|
||||
|
||||
// User Types [6]:
|
||||
defineUPstreamDataTraits(type_3float, Vector<float>);
|
||||
defineUPstreamDataTraits(type_3double, Vector<double>);
|
||||
defineUPstreamDataTraits(type_6float, SymmTensor<float>);
|
||||
defineUPstreamDataTraits(type_6double, SymmTensor<double>);
|
||||
defineUPstreamDataTraits(type_9float, Tensor<float>);
|
||||
defineUPstreamDataTraits(type_9double, Tensor<double>);
|
||||
|
||||
#undef defineUPstreamDataTraits
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
|
||||
//- Explicit handling of data type aliases. This is necessary since
|
||||
//- different systems map things like 'unsigned long' differently but we
|
||||
//- restrict ourselves to int32/int64 types
|
||||
template<class T>
|
||||
struct UPstream_alias_dataType
|
||||
:
|
||||
std::bool_constant
|
||||
<
|
||||
// Base type (no alias needed)
|
||||
UPstream_base_dataType<std::remove_cv_t<T>>::value ||
|
||||
(
|
||||
// Or some int 32/64 type to re-map
|
||||
std::is_integral_v<T>
|
||||
&& (sizeof(T) == sizeof(int32_t) || sizeof(T) == sizeof(int64_t))
|
||||
)
|
||||
>
|
||||
{
|
||||
// Is it using the base type? (no alias needed)
|
||||
static constexpr bool is_base =
|
||||
UPstream_base_dataType<std::remove_cv_t<T>>::value;
|
||||
|
||||
using base = std::conditional_t
|
||||
<
|
||||
UPstream_base_dataType<std::remove_cv_t<T>>::value,
|
||||
std::remove_cv_t<T>, // <- using base
|
||||
std::conditional_t // <- using alias
|
||||
<
|
||||
(
|
||||
std::is_integral_v<T>
|
||||
&& (sizeof(T) == sizeof(int32_t) || sizeof(T) == sizeof(int64_t))
|
||||
),
|
||||
std::conditional_t
|
||||
<
|
||||
(sizeof(T) == sizeof(int32_t)),
|
||||
std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>,
|
||||
std::conditional_t<std::is_signed_v<T>, int64_t, uint64_t>
|
||||
>,
|
||||
char // Fallback is a byte (eg, arbitrary contiguous data)
|
||||
>
|
||||
>;
|
||||
|
||||
static constexpr auto datatype_id =
|
||||
UPstream_base_dataType<base>::datatype_id;
|
||||
};
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
|
||||
//- A supported UPstream data type (fundamental or user-defined)
|
||||
//- or a component aggregate of a supported UPstream data type.
|
||||
//
|
||||
// Is true for the following conditions:
|
||||
// - The \c Type is directly supported
|
||||
// - The \c cmptType (eg, from VectorSpace) exists and is directly supported
|
||||
// - Fallback to byte-wise representation (ie, for contiguous)
|
||||
// .
|
||||
template<class T>
|
||||
struct UPstream_dataType
|
||||
:
|
||||
std::bool_constant
|
||||
<
|
||||
UPstream_alias_dataType<T>::value
|
||||
|| UPstream_alias_dataType<typename pTraits_cmptType<T>::type>::value
|
||||
>
|
||||
{
|
||||
// Is it using the base type? (ie, not using components)
|
||||
static constexpr bool is_base = UPstream_alias_dataType<T>::value;
|
||||
|
||||
//- The underlying data type (if supported) or byte
|
||||
using base = std::conditional_t
|
||||
<
|
||||
UPstream_alias_dataType<T>::value,
|
||||
typename UPstream_alias_dataType<T>::base, // <- using base
|
||||
typename UPstream_alias_dataType
|
||||
<typename pTraits_cmptType<T>::type>::base // <- using components
|
||||
>;
|
||||
|
||||
//- The corresponding UPstream::dataTypes enumeration
|
||||
static constexpr auto datatype_id =
|
||||
UPstream_base_dataType<base>::datatype_id;
|
||||
|
||||
//- The size in terms of the number of underlying data elements
|
||||
static std::streamsize size(std::streamsize count) noexcept
|
||||
{
|
||||
if constexpr (UPstream_alias_dataType<T>::value)
|
||||
{
|
||||
// using base: no multiplier
|
||||
return count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// using components: with multiplier
|
||||
return count*(sizeof(T)/sizeof(base));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user