From c448ea2a1174246deeb9e17631fc3baec4c16228 Mon Sep 17 00:00:00 2001 From: Mark Olesen <Mark.Olesen@esi-group.com> Date: Tue, 25 Feb 2025 08:39:16 +0100 Subject: [PATCH 1/3] ENH: add simple handling for point-to-point barriers - provides an additional means of process synchronization --- .../test/parallel-barrier1/Make/files | 3 + .../test/parallel-barrier1/Make/options | 2 + .../Test-parallel-barrier1.cxx | 125 ++++++++++++++++++ src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H | 34 ++++- src/Pstream/dummy/UPstream.C | 26 +++- src/Pstream/mpi/PstreamGlobals.H | 2 +- src/Pstream/mpi/UPstream.C | 67 +++++++++- 7 files changed, 248 insertions(+), 11 deletions(-) create mode 100644 applications/test/parallel-barrier1/Make/files create mode 100644 applications/test/parallel-barrier1/Make/options create mode 100644 applications/test/parallel-barrier1/Test-parallel-barrier1.cxx diff --git a/applications/test/parallel-barrier1/Make/files b/applications/test/parallel-barrier1/Make/files new file mode 100644 index 00000000000..9256068d0b2 --- /dev/null +++ b/applications/test/parallel-barrier1/Make/files @@ -0,0 +1,3 @@ +Test-parallel-barrier1.cxx + +EXE = $(FOAM_USER_APPBIN)/Test-parallel-barrier1 diff --git a/applications/test/parallel-barrier1/Make/options b/applications/test/parallel-barrier1/Make/options new file mode 100644 index 00000000000..18e6fe47afa --- /dev/null +++ b/applications/test/parallel-barrier1/Make/options @@ -0,0 +1,2 @@ +/* EXE_INC = */ +/* EXE_LIBS = */ diff --git a/applications/test/parallel-barrier1/Test-parallel-barrier1.cxx b/applications/test/parallel-barrier1/Test-parallel-barrier1.cxx new file mode 100644 index 00000000000..586539752d4 --- /dev/null +++ b/applications/test/parallel-barrier1/Test-parallel-barrier1.cxx @@ -0,0 +1,125 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2025 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. + +Application + Test-parallel-barrier1 + +Description + Simple test of local barriers communication + +\*---------------------------------------------------------------------------*/ + +#include "argList.H" +#include "clockTime.H" +#include "IPstream.H" +#include "OPstream.H" + +using namespace Foam; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +int main(int argc, char *argv[]) +{ + argList::noCheckProcessorDirectories(); + argList::addVerboseOption(); + argList::addOption("delay", "sec", "Seconds to sleep (default 2)"); + + #include "setRootCase.H" + + if (!UPstream::parRun()) + { + Info<< "###############" << nl + << "Not running in parallel. Stopping now" << nl + << "###############" << endl; + return 1; + } + + const auto delay = args.getOrDefault<label>("delay", 2); + + Info<< nl + << "Testing local barrier, sleep=" << delay << endl; + + + const auto myProci = UPstream::myProcNo(UPstream::worldComm); + const auto numProc = UPstream::nProcs(UPstream::worldComm); + + constexpr int uniqTag = 1516; + + clockTime timing; + + if (UPstream::master(UPstream::worldComm)) + { + // Wait for the last rank + UPstream::wait_done(numProc-1, UPstream::worldComm); + + // Wait for any other rank + if (numProc > 2) + { + int from = UPstream::wait_done(-1, UPstream::worldComm, uniqTag); + Pout<< "done signal from: " << from << endl; + } + } + else if (myProci == numProc-1) + { + Foam::sleep(delay); + UPstream::send_done(UPstream::masterNo(), UPstream::worldComm); + } + + // Cascade sequencing (and delays) + if (numProc > 7) + { + if (myProci == 2) + { + Foam::sleep(2*delay); + UPstream::send_done(4, UPstream::worldComm); + } + else if (myProci == 4) + { + UPstream::wait_done(2, UPstream::worldComm); + Foam::sleep(2*delay); + UPstream::send_done(5, UPstream::worldComm); + } + else if (myProci == 5) + { + UPstream::wait_done(4, UPstream::worldComm); + } + } + + // Some arbitrary signaling rank + if ((numProc > 2) && (myProci == numProc/2)) + { + Pout<< "send done signal " << myProci << " -> 0" << endl; + UPstream::send_done(UPstream::masterNo(), UPstream::worldComm, uniqTag); + } + + Pout<< "done: " << timing.elapsedTime() << " s" << endl; + + Info<< "\nEnd\n" << endl; + + return 0; +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H b/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H index e9ff4fc3710..a6c940fb9d7 100644 --- a/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H +++ b/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H @@ -777,10 +777,38 @@ public: //- Impose a synchronisation barrier (optionally non-blocking) static void barrier ( - const label communicator, + const int communicator, UPstream::Request* req = nullptr ); + //- Impose a point-to-point synchronisation barrier + //- by sending a zero-byte \em "done" message to given rank. + // A no-op for non-parallel + static void send_done + ( + //! The destination rank + const int toProcNo, + //! The communicator index (eg, UPstream::worldComm) + const int communicator, + //! Message tag (must match on receiving side) + const int tag = UPstream::msgType()+1970 + ); + + //- Impose a point-to-point synchronisation barrier + //- by receiving a zero-byte \em "done" message from given rank + // A no-op for non-parallel + // \returns the source rank (useful for ANY_SOURCE messages) + // or -1 for non-parallel + static int wait_done + ( + //! The source rank (negative == ANY_SOURCE) + const int fromProcNo, + //! The communicator index (eg, UPstream::worldComm) + const int communicator, + //! Message tag (must match on sending side) + const int tag = UPstream::msgType()+1970 + ); + //- Probe for an incoming message. // // \param commsType Non-blocking or not @@ -795,7 +823,7 @@ public: const UPstream::commsTypes commsType, const int fromProcNo, const int tag = UPstream::msgType(), - const label communicator = worldComm + const int communicator = worldComm ); @@ -824,7 +852,7 @@ public: //- Transfer the (wrapped) MPI request to the internal global list //- and invalidate the parameter (ignores null requests) - // A no-op for non-parallel, + // A no-op for non-parallel static void addRequest(UPstream::Request& req); //- Non-blocking comms: cancel and free outstanding request. diff --git a/src/Pstream/dummy/UPstream.C b/src/Pstream/dummy/UPstream.C index 50cf97f6071..4f70336800b 100644 --- a/src/Pstream/dummy/UPstream.C +++ b/src/Pstream/dummy/UPstream.C @@ -109,20 +109,40 @@ void Foam::UPstream::freeCommunicatorComponents(const label index) {} -void Foam::UPstream::barrier(const label communicator, UPstream::Request* req) +void Foam::UPstream::barrier(const int communicator, UPstream::Request* req) {} +void Foam::UPstream::send_done +( + const int toProc, + const int communicator, + const int tag +) +{} + + +int Foam::UPstream::wait_done +( + const int fromProc, + const int communicator, + const int tag +) +{ + return -1; +} + + std::pair<int,int64_t> Foam::UPstream::probeMessage ( const UPstream::commsTypes commsType, const int fromProcNo, const int tag, - const label communicator + const int communicator ) { - return std::pair<int,int64_t>(-1, 0); + return {-1, 0}; } diff --git a/src/Pstream/mpi/PstreamGlobals.H b/src/Pstream/mpi/PstreamGlobals.H index 2ccb245e338..db6396e2e10 100644 --- a/src/Pstream/mpi/PstreamGlobals.H +++ b/src/Pstream/mpi/PstreamGlobals.H @@ -113,7 +113,7 @@ inline void push_request // Transcribe as UPstream::Request *req = UPstream::Request(request); } - else + else if (MPI_REQUEST_NULL != request) { // Push onto list of requests PstreamGlobals::outstandingRequests_.push_back(request); diff --git a/src/Pstream/mpi/UPstream.C b/src/Pstream/mpi/UPstream.C index 82659a6b070..1edb6af58b0 100644 --- a/src/Pstream/mpi/UPstream.C +++ b/src/Pstream/mpi/UPstream.C @@ -1050,10 +1050,10 @@ bool Foam::UPstream::setSharedMemoryCommunicators() } -void Foam::UPstream::barrier(const label communicator, UPstream::Request* req) +void Foam::UPstream::barrier(const int communicator, UPstream::Request* req) { // No-op for non-parallel or not on communicator - if (!UPstream::parRun() || !UPstream::is_rank(communicator)) + if (!UPstream::is_parallel(communicator)) { PstreamGlobals::reset_request(req); return; @@ -1099,19 +1099,78 @@ void Foam::UPstream::barrier(const label communicator, UPstream::Request* req) } +void Foam::UPstream::send_done +( + const int toProcNo, + const int communicator, + const int tag // Message tag (must match on receiving side) +) +{ + if (!UPstream::is_parallel(communicator)) + { + // Nothing to do + return; + } + + { + MPI_Send + ( + nullptr, 0, MPI_BYTE, toProcNo, tag, + PstreamGlobals::MPICommunicators_[communicator] + ); + } +} + + +int Foam::UPstream::wait_done +( + const int fromProcNo, + const int communicator, + const int tag // Message tag (must match on sending side) +) +{ + if (!UPstream::is_parallel(communicator)) + { + // Nothing to do + return -1; + } + else if (fromProcNo < 0) + { + MPI_Status status; + MPI_Recv + ( + nullptr, 0, MPI_BYTE, MPI_ANY_SOURCE, tag, + PstreamGlobals::MPICommunicators_[communicator], + &status + ); + return status.MPI_SOURCE; + } + else + { + MPI_Recv + ( + nullptr, 0, MPI_BYTE, fromProcNo, tag, + PstreamGlobals::MPICommunicators_[communicator], + MPI_STATUS_IGNORE + ); + return fromProcNo; + } +} + + std::pair<int,int64_t> Foam::UPstream::probeMessage ( const UPstream::commsTypes commsType, const int fromProcNo, const int tag, - const label communicator + const int communicator ) { std::pair<int,int64_t> result(-1, 0); // No-op for non-parallel or not on communicator - if (!UPstream::parRun() || !UPstream::is_rank(communicator)) + if (!UPstream::is_parallel(communicator)) { return result; } -- GitLab From bf60a124ab1e96d1f698c251e52e28d9cde8e26d Mon Sep 17 00:00:00 2001 From: Mark Olesen <Mark.Olesen@esi-group.com> Date: Tue, 25 Feb 2025 09:52:12 +0100 Subject: [PATCH 2/3] ENH: add mapping for fundamental and OpenFOAM types -> MPI data types - this will allow send/recv/broadcast of various data types directly without reinterpreting as bytes. No performance benefit, but makes programming more flexible. In addition to the normal MPI types, also provide some convenient groupings such as double[3], double[9] etc. - the additional user-defined data types are created on the start of MPI and removed before shutdown --- src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H | 72 +++++ src/Pstream/mpi/PstreamGlobals.C | 249 ++++++++++++++++++ src/Pstream/mpi/PstreamGlobals.H | 82 ++++++ src/Pstream/mpi/UPstream.C | 14 + 4 files changed, 417 insertions(+) diff --git a/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H b/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H index a6c940fb9d7..d0e23c298cc 100644 --- a/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H +++ b/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H @@ -90,6 +90,78 @@ public: sync //!< (MPI_Ssend, MPI_Issend) }; + //- Mapping of some fundamental and aggregate types to MPI data types + enum class dataTypes : int + { + // NOTE: changes here require adjustment in + // PstreamGlobals, UPstreamTraits + + // Builtin Types [8]: + DataTypes_begin, //!< (internal use) begin all data types + type_byte = DataTypes_begin, //!< byte, char, unsigned char, ... + type_int32, + type_int64, + type_uint32, + type_uint64, + type_float, + type_double, + type_long_double, + + //! (internal use) end of builtin data types marker + BuiltinTypes_end, UserTypes_begin = BuiltinTypes_end, + //!< (internal use) begin of user data types marker + + // User Types [6]: + type_3float = UserTypes_begin, //!< 3*float (eg, floatVector) + type_3double, //!< 3*double (eg, doubleVector) + type_6float, //!< 6*float (eg, floatSymmTensor, complex vector) + type_6double, //!< 6*double (eg, doubleSymmTensor, complex vector) + type_9float, //!< 9*float (eg, floatTensor) + type_9double, //!< 9*double (eg, doubleTensor) + + // Internal markers + invalid, //!< invalid type (NULL) + //! (internal use) end of user data types marker + UserTypes_end = invalid, DataTypes_end = invalid + //!< (internal use) end of all data types marker + }; + + //- Mapping of some MPI op codes. + // Currently excluding min/max location until they are needed + enum class opCodes : int + { + // NOTE: changes here require adjustment in + // PstreamGlobals, UPstreamTraits + + ReduceOps_begin, //!< (internal use) begin reduce/window + + // Reduce or window operations [10] + op_min = ReduceOps_begin, //!< min(x,y) + op_max, //!< max(x,y) + op_sum, //!< (x + y) + op_prod, //!< (x * y) + op_bool_and, //!< Logical \c and + op_bool_or, //!< Logical \c or + op_bool_xor, //!< Logical \c xor + op_bit_and, //!< Bit-wise \c and for (unsigned) integral types + op_bit_or, //!< Bit-wise \c or for (unsigned) integral types + op_bit_xor, //!< Bit-wise \c xor for (unsigned) integral types + + //! (internal use) end of reduce-ops marker + ReduceOps_end, WindowOps_begin = ReduceOps_end, + //!< (internal use) begin end of window-ops marker + + // Window-only operations [2] + op_replace = WindowOps_begin, //!< Replace (window only) + op_no_op, //!< No-op (window only) + + // Internal markers + invalid, //!< invalid op (NULL) + //! (internal use) end of window-ops marker + WindowOps_end = invalid, OpCodes_end = invalid + //!< (internal use) end of all ops marker + }; + // Public Classes diff --git a/src/Pstream/mpi/PstreamGlobals.C b/src/Pstream/mpi/PstreamGlobals.C index e5383b722e0..b7aa206aea7 100644 --- a/src/Pstream/mpi/PstreamGlobals.C +++ b/src/Pstream/mpi/PstreamGlobals.C @@ -34,6 +34,12 @@ Foam::DynamicList<bool> Foam::PstreamGlobals::pendingMPIFree_; Foam::DynamicList<MPI_Comm> Foam::PstreamGlobals::MPICommunicators_; Foam::DynamicList<MPI_Request> Foam::PstreamGlobals::outstandingRequests_; +Foam::PstreamGlobals::DataTypeLookupTable +Foam::PstreamGlobals::MPIdataTypes_(MPI_DATATYPE_NULL); + +Foam::PstreamGlobals::OpCodesLookupTable +Foam::PstreamGlobals::MPIopCodes_(MPI_OP_NULL); + // * * * * * * * * * * * * * * * Communicators * * * * * * * * * * * * * * * // @@ -60,4 +66,247 @@ void Foam::PstreamGlobals::initCommunicator(const label index) } +// * * * * * * * * * * * * * * * * Data Types * * * * * * * * * * * * * * * // + +void Foam::PstreamGlobals::initDataTypes() +{ + static_assert + ( + PstreamGlobals::DataTypeLookupTable::max_size() + == (int(UPstream::dataTypes::DataTypes_end)+1), + "Lookup table size != number of dataTypes enumerations" + ); + + // From enumeration to MPI datatype + #undef defineType + #define defineType(Idx, BaseType) \ + MPIdataTypes_[int(UPstream::dataTypes::Idx)] = BaseType; + + // Intrinsic Types [8]: + defineType(type_byte, MPI_BYTE); + defineType(type_int32, MPI_INT32_T); + defineType(type_int64, MPI_INT64_T); + defineType(type_uint32, MPI_UINT32_T); + defineType(type_uint64, MPI_UINT64_T); + defineType(type_float, MPI_FLOAT); + defineType(type_double, MPI_DOUBLE); + defineType(type_long_double, MPI_LONG_DOUBLE); + + #undef defineType + + // User-define types + #undef defineUserType + #define defineUserType(Idx, Count, BaseType, Name) \ + { \ + auto& dt = MPIdataTypes_[int(UPstream::dataTypes::Idx)]; \ + MPI_Type_contiguous(Count, BaseType, &dt); \ + MPI_Type_set_name(dt, Name); \ + MPI_Type_commit(&dt); \ + } + + // User Types [6]: + defineUserType(type_3float, 3, MPI_FLOAT, "float[3]"); + defineUserType(type_3double, 3, MPI_DOUBLE, "double[3]"); + defineUserType(type_6float, 6, MPI_FLOAT, "float[6]"); + defineUserType(type_6double, 6, MPI_DOUBLE, "double[6]"); + defineUserType(type_9float, 9, MPI_FLOAT, "float[9]"); + defineUserType(type_9double, 9, MPI_DOUBLE, "double[9]"); + + #undef defineUserType +} + + +void Foam::PstreamGlobals::deinitDataTypes() +{ + // User types only + auto first = + ( + MPIdataTypes_.begin() + int(UPstream::dataTypes::UserTypes_begin) + ); + const auto last = + ( + MPIdataTypes_.begin() + int(UPstream::dataTypes::UserTypes_end) + ); + + for (; first != last; ++first) + { + if (MPI_DATATYPE_NULL != *first) + { + MPI_Type_free(&(*first)); + } + } +} + + +// Debugging +bool Foam::PstreamGlobals::checkDataTypes() +{ + // Check all types, not just user types + auto first = + ( + MPIdataTypes_.begin() + ); + const auto last = + ( + MPIdataTypes_.begin() + int(UPstream::dataTypes::DataTypes_end) + ); + + for (; (first != last); ++first) + { + if (MPI_DATATYPE_NULL == *first) + { + return false; + } + } + + return true; +} + + +// Debugging +void Foam::PstreamGlobals::printDataTypes(bool all) +{ + int rank = -1; + if + ( + (MPI_SUCCESS != MPI_Comm_rank(MPI_COMM_WORLD, &rank)) + || (rank != 0) + ) + { + return; + } + + const auto print = [&](auto firstIndex, auto lastIndex) + { + auto first = + ( + PstreamGlobals::MPIdataTypes_.begin() + int(firstIndex) + ); + const auto last = + ( + PstreamGlobals::MPIdataTypes_.begin() + int(lastIndex) + ); + + for (; (first != last); ++first) + { + std::cerr + << " name = " + << PstreamGlobals::dataType_name(*first) << '\n'; + } + }; + + if (all) + { + std::cerr << "enumerated data types:\n"; + print + ( + UPstream::dataTypes::DataTypes_begin, + UPstream::dataTypes::DataTypes_end + ); + } + else + { + // User types only. + std::cerr << "enumerated user-defined data types:\n"; + print + ( + UPstream::dataTypes::UserTypes_begin, + UPstream::dataTypes::UserTypes_end + ); + } +} + + +std::string Foam::PstreamGlobals::dataType_name(MPI_Datatype datatype) +{ + if (MPI_DATATYPE_NULL == datatype) + { + return std::string("(null)"); + } + + char buf[MPI_MAX_OBJECT_NAME]; + int len; + + if (MPI_SUCCESS == MPI_Type_get_name(datatype, buf, &len)) + { + if (len > 0) + { + return std::string(buf, len); + } + else + { + return std::string("(anon)"); + } + } + + return std::string("???"); +} + + +// * * * * * * * * * * * * * * * * Op Codes * * * * * * * * * * * * * * * * // + +void Foam::PstreamGlobals::initOpCodes() +{ + static_assert + ( + PstreamGlobals::OpCodesLookupTable::max_size() + == (int(UPstream::opCodes::OpCodes_end)+1), + "Lookup table size != number of opCodes enumerations" + ); + + // From enumeration to MPI datatype + #undef defineCode + #define defineCode(Idx, CodeType) \ + MPIopCodes_[int(UPstream::opCodes::Idx)] = CodeType; + + defineCode(op_min, MPI_MIN); + defineCode(op_max, MPI_MAX); + defineCode(op_sum, MPI_SUM); + defineCode(op_prod, MPI_PROD); + + // TBD: still need to sort out if they are MPI_C_BOOL or MPI_CXX_BOOL + // ... + defineCode(op_bool_and, MPI_LAND); + defineCode(op_bool_or, MPI_LOR); + defineCode(op_bool_xor, MPI_LXOR); + + defineCode(op_bit_and, MPI_BAND); + defineCode(op_bit_or, MPI_BOR); + defineCode(op_bit_xor, MPI_BXOR); + + // Do not include MPI_MINLOC, MPI_MAXLOC since they are tied to + // float_int, double_int and larger or other types + + // window-only + defineCode(op_replace, MPI_REPLACE); + defineCode(op_no_op, MPI_NO_OP); + + #undef defineCode +} + + +void Foam::PstreamGlobals::deinitOpCodes() +{} + + +bool Foam::PstreamGlobals::checkOpCodes() +{ + auto first = MPIopCodes_.begin(); + const auto last = + ( + MPIopCodes_.begin() + int(UPstream::opCodes::OpCodes_end) + ); + + for (; (first != last); ++first) + { + if (MPI_OP_NULL == *first) + { + return false; + } + } + + return true; +} + + // ************************************************************************* // diff --git a/src/Pstream/mpi/PstreamGlobals.H b/src/Pstream/mpi/PstreamGlobals.H index db6396e2e10..bf46434ad78 100644 --- a/src/Pstream/mpi/PstreamGlobals.H +++ b/src/Pstream/mpi/PstreamGlobals.H @@ -40,6 +40,7 @@ SourceFiles #define Foam_PstreamGlobals_H #include "DynamicList.H" +#include "FixedList.H" #include "UPstream.H" // For UPstream::Request #include "openfoam_mpi.H" @@ -62,6 +63,17 @@ extern DynamicList<MPI_Comm> MPICommunicators_; //- Outstanding non-blocking operations. extern DynamicList<MPI_Request> outstandingRequests_; +typedef Foam::FixedList<MPI_Datatype, 15> DataTypeLookupTable; + +//- MPI data types corresponding to some fundamental and OpenFOAM types. +//- Indexed by UPstream::dataTypes enum +extern DataTypeLookupTable MPIdataTypes_; + +typedef Foam::FixedList<MPI_Op, 13> OpCodesLookupTable; + +//- MPI operation types, indexed by UPstream::opCodes enum +extern OpCodesLookupTable MPIopCodes_; + // * * * * * * * * * * * * * * * Communicators * * * * * * * * * * * * * * * // @@ -89,6 +101,76 @@ inline bool warnCommunicator(int comm) noexcept } +// * * * * * * * * * * * * * * * * Data Types * * * * * * * * * * * * * * * // + +//- Create mapping into MPIdataTypes_ and define user data types +void initDataTypes(); + +//- Free any user data types +void deinitDataTypes(); + +//- Debugging only: check if data type mappings are non-null +bool checkDataTypes(); + +//- Debugging only: print data type names (all or just user-defined) +void printDataTypes(bool all = false); + +//- Lookup of dataTypes enumeration as an MPI_Datatype +inline MPI_Datatype getDataType(UPstream::dataTypes id) +{ + return MPIdataTypes_[static_cast<int>(id)]; +} + +//- Fatal if data type is not valid +inline void checkDataType(UPstream::dataTypes id) +{ + if (id == UPstream::dataTypes::invalid) + { + FatalErrorInFunction + << "Invalid data type" + << Foam::abort(FatalError); + } +} + +//- Return MPI internal name for specified MPI_Datatype +std::string dataType_name(MPI_Datatype datatype); + +//- Return MPI internal name for dataTypes enumeration +inline std::string dataType_name(UPstream::dataTypes id) +{ + return dataType_name(MPIdataTypes_[static_cast<int>(id)]); +} + + +// * * * * * * * * * * * * * * * * Op Codes * * * * * * * * * * * * * * * * // + +//- Create mapping into MPIopCodes_ +void initOpCodes(); + +//- Free any user-defined op codes +void deinitOpCodes(); + +//- Debugging only: check if op code mappings are non-null +bool checkOpCodes(); + +//- Lookup of opCodes enumeration as an MPI_Op +inline MPI_Op getOpCode(UPstream::opCodes id) +{ + return MPIopCodes_[static_cast<int>(id)]; +} + +//- Fatal if opcode is not valid +inline void checkOpCode(UPstream::opCodes id) +{ + if (id == UPstream::opCodes::invalid) + { + FatalErrorInFunction + << "Invalid operation code" + << Foam::abort(FatalError); + } +} + + // * * * * * * * * * * * * * * * * Requests * * * * * * * * * * * * * * * * // //- Reset UPstream::Request to MPI_REQUEST_NULL diff --git a/src/Pstream/mpi/UPstream.C b/src/Pstream/mpi/UPstream.C index 1edb6af58b0..a3bfe3ab429 100644 --- a/src/Pstream/mpi/UPstream.C +++ b/src/Pstream/mpi/UPstream.C @@ -251,6 +251,17 @@ bool Foam::UPstream::init(int& argc, char**& argv, const bool needsThread) ourMpi = true; } + // Define data type mappings and user data types. Defined now so that + // any OpenFOAM Pstream operations may make immediate use of them. + PstreamGlobals::initDataTypes(); + PstreamGlobals::initOpCodes(); + + if (UPstream::debug) + { + PstreamGlobals::printDataTypes(); + } + + // Check argument list for local world label worldIndex = -1; for (int argi = 1; argi < argc; ++argi) @@ -591,6 +602,9 @@ void Foam::UPstream::shutdown(int errNo) } } + // Free any user data types + PstreamGlobals::deinitDataTypes(); + PstreamGlobals::deinitOpCodes(); MPI_Finalize(); } -- GitLab From dccdb263e8b7cf336aff102df008e52786af07ce Mon Sep 17 00:00:00 2001 From: Mark Olesen <Mark.Olesen@esi-group.com> Date: Tue, 25 Feb 2025 09:52:20 +0100 Subject: [PATCH 3/3] 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 --- .../00-machine-sizes/Test-machine-sizes.cpp | 173 ++++++++- applications/test/UPstreamTraits/Make/files | 3 + applications/test/UPstreamTraits/Make/options | 2 + .../UPstreamTraits/Test-UPstreamTraits.cxx | 267 ++++++++++++++ .../db/IOstreams/Pstreams/UPstreamTraits.H | 328 +++++++++++++++++- 5 files changed, 766 insertions(+), 7 deletions(-) create mode 100644 applications/test/UPstreamTraits/Make/files create mode 100644 applications/test/UPstreamTraits/Make/options create mode 100644 applications/test/UPstreamTraits/Test-UPstreamTraits.cxx diff --git a/applications/test/00-machine-sizes/Test-machine-sizes.cpp b/applications/test/00-machine-sizes/Test-machine-sizes.cpp index 0e88c4c943a..917782dfde1 100644 --- a/applications/test/00-machine-sizes/Test-machine-sizes.cpp +++ b/applications/test/00-machine-sizes/Test-machine-sizes.cpp @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2018-2022 OpenCFD Ltd. + Copyright (C) 2018-2025 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -25,9 +25,12 @@ License Description Test the sizeof for basic types. + Also tests how the data mapping of OpenFOAM types to UPstream (MPI) + type ids are handled. + Can be compiled and run without any OpenFOAM libraries. - g++ -std=c++11 -oTest-machine-sizes Test-machine-sizes.cpp + g++ -std=c++17 -oTest-machine-sizes Test-machine-sizes.cpp \*---------------------------------------------------------------------------*/ @@ -37,6 +40,114 @@ Description #include <iostream> #include <limits> #include <typeinfo> +#include <type_traits> + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Partial copy from UPstream.H + +//- Some MPI data types +// +//- Mapping of some fundamental and aggregate types to MPI data types +enum class dataTypes : int +{ + // Builtin Types [8]: + DataTypes_begin, //!< Begin builtin types (internal use) + type_byte = DataTypes_begin, // also for char, unsigned char + type_int32, + type_int64, + type_uint32, + type_uint64, + type_float, + type_double, + type_long_double, + invalid +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Partial copy from UPstreamTraits.H + +//- A supported UPstream data type (intrinsic or user-defined) +template<class T> +struct UPstream_base_dataType : std::false_type +{ + static constexpr auto datatype_id = dataTypes::invalid; +}; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +// Specializations of the above, +// each to match the elements of UPstream::dataTypes + +#undef defineUPstreamDataTraits +#define defineUPstreamDataTraits(TypeId, Type) \ + template<> struct UPstream_base_dataType<Type> : std::true_type \ + { \ + static constexpr auto datatype_id = dataTypes::TypeId; \ + }; + + +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); + +#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, // is_base + std::remove_cv_t<T>, + std::conditional_t + < + ( + 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 value (assuming it is contiguous) + > + >; + + static constexpr auto datatype_id = + UPstream_base_dataType<base>::datatype_id; +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // template<class T> void print(const char* name, bool showLimits = true) @@ -47,28 +158,78 @@ void print(const char* name, bool showLimits = true) if (showLimits) { std::cout - << " \"max\"=" << std::numeric_limits<T>::max(); + << " max=<"; + + if constexpr (sizeof(T) == 1) + { + std::cout << int(std::numeric_limits<T>::max()); + } + else + { + std::cout << std::numeric_limits<T>::max(); + } + std::cout << '>'; + } + + // A declared or deduced MPI type, or aliased + std::cout + << " is_mpi=" << UPstream_base_dataType<T>::value + << " (" << int(UPstream_base_dataType<T>::datatype_id) << ")"; + + if (UPstream_alias_dataType<T>::value) + { + if (UPstream_alias_dataType<T>::is_base) + { + std::cout<< " is_base"; + } + else + { + std::cout<< " is_alias (" + << int(UPstream_alias_dataType<T>::datatype_id) << ")"; + } + } + else + { + std::cout<< " no_alias"; } std::cout<< '\n'; } + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // Main program: int main(int argc, char *argv[]) { std::cout<< "c++ = " << __cplusplus << '\n'; - std::cout<< "machine sizes\n---\n\n"; - + std::cout<< "machine sizes (and some MPI traits)\n---\n\n"; + + print<int8_t>("int8_t"); + print<uint8_t>("uint8_t"); + print<int16_t>("int16_t"); + print<uint16_t>("uint16_t"); + print<int32_t>("int32_t"); + print<uint32_t>("uint32_t"); + print<int64_t>("int64_t"); + print<uint64_t>("uint64_t"); + + std::cout << '\n'; + print<char>("char"); + print<unsigned char>("unsigned char"); print<short>("short"); print<int>("int"); + print<unsigned>("unsigned"); print<long>("long"); print<unsigned long>("unsigned long"); - print<std::size_t>("std::size_t"); print<long long>("long long"); + std::cout << '\n'; + print<std::size_t>("std::size_t"); + print<std::streamsize>("std::streamsize"); + + std::cout << '\n'; print<float>("float"); print<double>("double"); print<long double>("long double"); diff --git a/applications/test/UPstreamTraits/Make/files b/applications/test/UPstreamTraits/Make/files new file mode 100644 index 00000000000..5e1dc7bafdb --- /dev/null +++ b/applications/test/UPstreamTraits/Make/files @@ -0,0 +1,3 @@ +Test-UPstreamTraits.cxx + +EXE = $(FOAM_USER_APPBIN)/Test-UPstreamTraits diff --git a/applications/test/UPstreamTraits/Make/options b/applications/test/UPstreamTraits/Make/options new file mode 100644 index 00000000000..18e6fe47afa --- /dev/null +++ b/applications/test/UPstreamTraits/Make/options @@ -0,0 +1,2 @@ +/* EXE_INC = */ +/* EXE_LIBS = */ diff --git a/applications/test/UPstreamTraits/Test-UPstreamTraits.cxx b/applications/test/UPstreamTraits/Test-UPstreamTraits.cxx new file mode 100644 index 00000000000..9c747c61fa7 --- /dev/null +++ b/applications/test/UPstreamTraits/Test-UPstreamTraits.cxx @@ -0,0 +1,267 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2025 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. + +Description + Simple compilation tests and access for UPstream types + +\*---------------------------------------------------------------------------*/ + +#include "pTraits.H" +#include "contiguous.H" +#include "FixedList.H" +#include "boolVector.H" // A FixedList pretending to be a vector +#include "barycentric.H" +#include "complex.H" +#include "vector.H" +#include "tensor.H" +#include "uLabel.H" +#include "Switch.H" +#include "IOstreams.H" +#include "UPstream.H" + +#include <type_traits> + +using namespace Foam; + +// Just for debugging +const List<std::string> dataType_names +({ + "byte", + "int32", + "int64", + "uint32", + "uint64", + "float", + "double", + "long_double", + + "float(2)", + "double(2)", + "float(3)", + "double(3)", + "float(6)", + "double(6)", + "float(9)", + "double(9)" +}); + +//- Test for pTraits typeName member : default is false +template<class T, class = void> +struct check_has_typeName : std::false_type {}; + +//- Test for pTraits zero +template<class T> +struct check_has_typeName +< + T, + std::void_t<decltype(pTraits<std::remove_cv_t<T>>::typeName)> +> +: + std::true_type +{}; + + +// Possible future change... +// //- A supported UPstream data type (intrinsic or user-defined) +// template<> +// struct UPstream_base_dataType<complex> : std::true_type +// { +// static constexpr auto datatype_id = []() +// { +// if constexpr (sizeof(complex) == 2*sizeof(float)) +// return UPstream::dataTypes::type_2float; +// else +// return UPstream::dataTypes::type_2double; +// }(); +// }; + + +template<class T> +void printTypeName(const bool showSize = false) +{ + // Both float and double have pTraits typeName = "scalar"! + if constexpr (std::is_same_v<float, std::remove_cv_t<T>>) + { + Info<< "<float>"; + } + else if constexpr (std::is_same_v<double, std::remove_cv_t<T>>) + { + Info<< "<double>"; + } + else if constexpr (check_has_typeName<T>::value) + { + Info<< pTraits<std::remove_cv_t<T>>::typeName; + } + else + { + Info<< typeid(T).name(); + } + if (showSize) + { + Info<< " (" << sizeof(T) << " bytes)"; + } +} + +template<class Type, bool UseTypeName = true> +void printPstreamTraits(const std::string_view name = std::string_view()) +{ + Info<< "========" << nl; + Info<< "type: "; + if (!name.empty()) + { + Info<< name << ' '; + } + if constexpr (UseTypeName) + { + printTypeName<Type>(true); + } + else + { + Info<< typeid(Type).name(); + Info<< " (" << sizeof(Type) << " bytes)"; + } + + Info<< ", cmpt:"; + printTypeName<typename Foam::pTraits_cmptType<Type>::type>(true); + + Info<< nl + << " is_contiguous:" + << is_contiguous<Type>::value + << ", is base:" + << UPstream_base_dataType<Type>::value + << ", is cmpt:" + << UPstream_dataType<Type>::value << nl; + + Info<< "is base:" + << UPstream_base_dataType<Type>::value + << " (type:" << int(UPstream_base_dataType<Type>::datatype_id) + << ") is alias:" << UPstream_alias_dataType<Type>::value + << " (type:" << int(UPstream_alias_dataType<Type>::datatype_id) + << ")" << nl; + + + { + int index = int(UPstream_base_dataType<Type>::datatype_id); + Info<< "datatype: " << index; + + if (index < dataType_names.size()) + { + Info<< ' ' << dataType_names[index]; + } + Info<< nl; + } + + { + // Use element or component type (or byte-wise) for data type + using base = typename UPstream_dataType<Type>::base; + constexpr auto datatype = UPstream_dataType<Type>::datatype_id; + + Info<< "datatype => "; + printTypeName<base>(); + Info<< " (" << sizeof(Type)/sizeof(base) << " elems)" << nl + << "datatype: " << static_cast<int>(datatype) << nl; + } +} + + +template<class BinaryOp> +void printOpCodeTraits(BinaryOp bop, std::string_view name) +{ + Info<< "op: " << name << ' '; + if constexpr (UPstream_opType<BinaryOp>::value) + { + Info<< "supported"; + } + else + { + Info<< "unknown"; + } + Info<< ": " << int(UPstream_opType<BinaryOp>::opcode_id) << nl; +} + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +// Main program: + +int main() +{ + printPstreamTraits<bool>(); + printPstreamTraits<label>(); + + printPstreamTraits<int>("<int>"); + printPstreamTraits<long>("<long>"); + printPstreamTraits<unsigned>("<unsigned>"); + printPstreamTraits<unsigned int>("<unsigned int>"); + printPstreamTraits<unsigned long>("<long long>"); + + printPstreamTraits<const float>(); + printPstreamTraits<floatVector>(); + + printPstreamTraits<scalar>(); + printPstreamTraits<double>(); + printPstreamTraits<doubleVector>(); + + // Avoid typeName for barycentric. It is declared, but not defined + printPstreamTraits<barycentric, false>("barycentric"); + + printPstreamTraits<complex>(); // Uses specialized pTraits_... + + printPstreamTraits<boolVector>(); // Uses specialized pTraits_... + printPstreamTraits<floatVector>(); + printPstreamTraits<doubleVector>(); + printPstreamTraits<tensor>(); + printPstreamTraits<word>(); + printPstreamTraits<std::string>(); + + // This will not identify properly at the moment... + printPstreamTraits<FixedList<doubleVector, 4>>(); + printPstreamTraits<labelPair>(); + + Info<< nl + << "========" << nl + << "Mapping of binary reduction ops" << nl; + + printOpCodeTraits(minOp<scalar>{}, "min"); + printOpCodeTraits(maxOp<vector>{}, "max"); + printOpCodeTraits(sumOp<vector>{}, "sum"); + printOpCodeTraits(plusOp<vector>{}, "plus"); + printOpCodeTraits(multiplyOp<scalar>{}, "multiply"); + printOpCodeTraits(divideOp<vector>{}, "divide"); + printOpCodeTraits(minMagSqrOp<vector>{}, "minMagSqr"); + + printOpCodeTraits(bitAndOp<vector>{}, "bitAnd<vector>"); + printOpCodeTraits(bitOrOp<vector>{}, "bitOr<vector>"); + + printOpCodeTraits(bitOrOp<float>{}, "bitOr<float>"); + printOpCodeTraits(bitAndOp<unsigned>{}, "bitAnd<unsigned>"); + printOpCodeTraits(bitOrOp<unsigned>{}, "bitOr<unsigned>"); + + Info<< nl << "End\n" << endl; + + return 0; +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/db/IOstreams/Pstreams/UPstreamTraits.H b/src/OpenFOAM/db/IOstreams/Pstreams/UPstreamTraits.H index 188d3db6434..224300002d4 100644 --- a/src/OpenFOAM/db/IOstreams/Pstreams/UPstreamTraits.H +++ b/src/OpenFOAM/db/IOstreams/Pstreams/UPstreamTraits.H @@ -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 -- GitLab