diff --git a/applications/test/00-machine-sizes/Test-machine-sizes.cpp b/applications/test/00-machine-sizes/Test-machine-sizes.cpp index 0e88c4c943ae5d02a22f0445c9264739c6155af0..917782dfde1f3f0228e9ebff3acf2ae632abea05 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 0000000000000000000000000000000000000000..5e1dc7bafdba3010d60bbbdc568983fbc54663e3 --- /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 0000000000000000000000000000000000000000..18e6fe47afacb902cddccf82632772447704fd88 --- /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 0000000000000000000000000000000000000000..9c747c61fa7fd2821a039ac161cda0875daaf75c --- /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/applications/test/parallel-barrier1/Make/files b/applications/test/parallel-barrier1/Make/files new file mode 100644 index 0000000000000000000000000000000000000000..9256068d0b27adc0c9041e0174c053b42de2fdf9 --- /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 0000000000000000000000000000000000000000..18e6fe47afacb902cddccf82632772447704fd88 --- /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 0000000000000000000000000000000000000000..586539752d4be48a5f69d17fa7ac6b7db00fa372 --- /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 e9ff4fc3710f635eac09cbde093e011ed05bda0f..d0e23c298cc3969f8412033640e77c51e0190747 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 @@ -777,10 +849,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 +895,7 @@ public: const UPstream::commsTypes commsType, const int fromProcNo, const int tag = UPstream::msgType(), - const label communicator = worldComm + const int communicator = worldComm ); @@ -824,7 +924,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/OpenFOAM/db/IOstreams/Pstreams/UPstreamTraits.H b/src/OpenFOAM/db/IOstreams/Pstreams/UPstreamTraits.H index 188d3db64342c791a08d10e62fecf868a6dea289..224300002d4750fd6cee9714d7d63c4e28815165 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 diff --git a/src/Pstream/dummy/UPstream.C b/src/Pstream/dummy/UPstream.C index 50cf97f6071b64310a52ffa9d3096d6dc810a1f4..4f70336800be6bb6fb4291aaf9792ee1cea6ec2d 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.C b/src/Pstream/mpi/PstreamGlobals.C index e5383b722e0831fbb0c247de998666ef6e1d6ce0..b7aa206aea7f0afa884b9bfcb3db4446e4b6e12a 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 2ccb245e3388439885e5250181b91a0ef028a17f..bf46434ad788e1b16eb3cb1f1d4658906491988b 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 @@ -113,7 +195,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 82659a6b070d29f8a5acf4c22303eea4f3febaa3..a3bfe3ab4296730979bc254c236f2dd175fa948c 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(); } @@ -1050,10 +1064,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 +1113,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; }