diff --git a/applications/test/parallel-comm2/Test-parallel-comm2.C b/applications/test/parallel-comm2/Test-parallel-comm2.C index 6da3b03faaef51b62cb939c8b9f07a18d57b80fc..5b28c75b7c9bf5a7be4be1db750a21f5e7468599 100644 --- a/applications/test/parallel-comm2/Test-parallel-comm2.C +++ b/applications/test/parallel-comm2/Test-parallel-comm2.C @@ -41,14 +41,7 @@ Description #include "PstreamReduceOps.H" #include "SHA1.H" -// Include MPI without any C++ bindings -#ifndef MPICH_SKIP_MPICXX -#define MPICH_SKIP_MPICXX -#endif -#ifndef OMPI_SKIP_MPICXX -#define OMPI_SKIP_MPICXX -#endif -#include <mpi.h> +#include "openfoam_mpi.H" using namespace Foam; diff --git a/applications/test/parallel-comm3a/Test-parallel-comm3a.C b/applications/test/parallel-comm3a/Test-parallel-comm3a.C index 2dc745988be51582e74d61c9a2089679f5bb8461..c0eb29074f046f931e9f40f6f1684093dead57cf 100644 --- a/applications/test/parallel-comm3a/Test-parallel-comm3a.C +++ b/applications/test/parallel-comm3a/Test-parallel-comm3a.C @@ -41,14 +41,7 @@ Description #include "StringStream.H" #include "Random.H" -// Include MPI without any C++ bindings -#ifndef MPICH_SKIP_MPICXX -#define MPICH_SKIP_MPICXX -#endif -#ifndef OMPI_SKIP_MPICXX -#define OMPI_SKIP_MPICXX -#endif -#include <mpi.h> +#include "openfoam_mpi.H" using namespace Foam; diff --git a/applications/test/parallel-external-init/Test-parallel-external-init.C b/applications/test/parallel-external-init/Test-parallel-external-init.C index 6c1af9fedc03113a1d89617c062ac26142242ee6..626c4902bbaa214a7f2642c9fe7ce978dfe89646 100644 --- a/applications/test/parallel-external-init/Test-parallel-external-init.C +++ b/applications/test/parallel-external-init/Test-parallel-external-init.C @@ -40,14 +40,7 @@ Description #include "Pstream.H" #include <iostream> -// Include MPI without any C++ bindings -#ifndef MPICH_SKIP_MPICXX -#define MPICH_SKIP_MPICXX -#endif -#ifndef OMPI_SKIP_MPICXX -#define OMPI_SKIP_MPICXX -#endif -#include <mpi.h> +#include "openfoam_mpi.H" using namespace Foam; diff --git a/applications/test/processorTopology/Test-processorTopology.C b/applications/test/processorTopology/Test-processorTopology.C index 786e5bf07086218c92caccc3d5255ab1d439fe8a..ef3622556cc5187fbf424ab9cf0a8e5cc6f53e17 100644 --- a/applications/test/processorTopology/Test-processorTopology.C +++ b/applications/test/processorTopology/Test-processorTopology.C @@ -34,14 +34,7 @@ Description #include "globalMeshData.H" #include "OFstream.H" -// Include MPI without any C++ bindings -#ifndef MPICH_SKIP_MPICXX -#define MPICH_SKIP_MPICXX -#endif -#ifndef OMPI_SKIP_MPICXX -#define OMPI_SKIP_MPICXX -#endif -#include <mpi.h> +#include "openfoam_mpi.H" using namespace Foam; diff --git a/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H b/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H index f4ebe300a350c583101d9c153f320ce863f50e16..bf22b7bac0bae4752400b33f136fa9e24dd0783e 100644 --- a/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H +++ b/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H @@ -56,6 +56,9 @@ namespace Foam //- Implementation details for UPstream/Pstream/MPI etc. namespace PstreamDetail {} +//- Interface handling for UPstream/Pstream/MPI etc. +namespace PstreamUtils {} + /*---------------------------------------------------------------------------*\ Class UPstream Declaration \*---------------------------------------------------------------------------*/ @@ -88,6 +91,9 @@ public: // Public Classes + //- Wrapper for MPI_Comm + class Communicator; // Forward Declaration + //- Wrapper for MPI_Request class Request; // Forward Declaration @@ -1218,12 +1224,119 @@ public: }; +/*---------------------------------------------------------------------------*\ + Class UPstream::Communicator Declaration +\*---------------------------------------------------------------------------*/ + +//- An opaque wrapper for MPI_Comm with a vendor-independent +//- representation without any \c <mpi.h> header. +// The MPI standard states that MPI_Comm is always an opaque object. +// Generally it is either an integer (eg, mpich) or a pointer (eg, openmpi). +class UPstream::Communicator +{ +public: + + // Public Types + + //- Storage for MPI_Comm (as integer or pointer) + typedef std::intptr_t value_type; + + +private: + + // Private Data + + //- The MPI_Comm (as wrapped value) + value_type value_; + +public: + + // Generated Methods + + //- Copy construct + Communicator(const Communicator&) noexcept = default; + + //- Move construct + Communicator(Communicator&&) noexcept = default; + + //- Copy assignment + Communicator& operator=(const Communicator&) noexcept = default; + + //- Move assignment + Communicator& operator=(Communicator&&) noexcept = default; + + + // Member Operators + + //- Test for equality + bool operator==(const Communicator& rhs) const noexcept + { + return (value_ == rhs.value_); + } + + //- Test for inequality + bool operator!=(const Communicator& rhs) const noexcept + { + return (value_ != rhs.value_); + } + + + // Constructors + + //- Default construct as MPI_COMM_NULL + Communicator() noexcept; + + //- Construct from MPI_Comm (as pointer type) + explicit Communicator(const void* p) noexcept + : + value_(reinterpret_cast<value_type>(p)) + {} + + //- Construct from MPI_Comm (as integer type) + explicit Communicator(value_type val) noexcept + : + value_(val) + {} + + + // Factory Methods + + //- Transcribe internally indexed communicator to wrapped value. + // Example, + // \code + // PstreamUtils::Cast::to_mpi + // ( + // UPstream::Communicator::lookup(UPstream::commWorld()) + // ) + // \endcode + static Communicator lookup(const label comm); + + + // Member Functions + + //- Return raw value + value_type value() const noexcept { return value_; } + + //- Return as pointer value + const void* pointer() const noexcept + { + return reinterpret_cast<const void*>(value_); + } + + //- True if not equal to MPI_COMM_NULL + bool good() const noexcept; + + //- Reset to default constructed value (MPI_COMM_NULL) + void reset() noexcept; +}; + + /*---------------------------------------------------------------------------*\ Class UPstream::Request Declaration \*---------------------------------------------------------------------------*/ //- An opaque wrapper for MPI_Request with a vendor-independent -//- representation independent of any \c <mpi.h> header +//- representation without any \c <mpi.h> header. // The MPI standard states that MPI_Request is always an opaque object. // Generally it is either an integer (eg, mpich) or a pointer (eg, openmpi). class UPstream::Request diff --git a/src/OpenFOAM/global/argList/argList.C b/src/OpenFOAM/global/argList/argList.C index 7f6966855a716405f2b0560a974e24c81ab0331a..05934efede7ebcaae7c341c20f21f0c6d9cb7e74 100644 --- a/src/OpenFOAM/global/argList/argList.C +++ b/src/OpenFOAM/global/argList/argList.C @@ -271,8 +271,8 @@ static bool printRootsSubscription if (index == -1) { - sortedProcs.append(host); - sortedRoots.append(i); + sortedProcs.push_back(host); + sortedRoots.push_back(i); } else if (roots[sortedRoots[index]] != root) { @@ -360,9 +360,9 @@ void Foam::argList::addArgument const string& usage ) { - validArgs.append(argName); + validArgs.push_back(argName); - // The first program argument starts at 1 - obtain index after the append + // The first program argument starts at 1 - obtain index after push_back() const label index = validArgs.size(); @@ -470,7 +470,7 @@ void Foam::argList::addNote(const string& note) { if (!note.empty()) { - notes.append(note); + notes.push_back(note); } } @@ -1096,8 +1096,8 @@ Foam::argList::argList if (strcmp(optName, "lib") == 0) { // The '-lib' option: - // Append name(s) to libs for later opening - libs().append(this->getList<fileName>(argi)); + // Add name(s) to libs for later opening + libs().push_back(this->getList<fileName>(argi)); } else if (strcmp(optName, "debug-switch") == 0) { @@ -1262,7 +1262,7 @@ void Foam::argList::parse const string timeString = clock::clockTime(); // Print the banner once only for parallel runs - if (Pstream::master() && bannerEnabled()) + if (UPstream::master() && bannerEnabled()) { IOobject::writeBanner(Info, true) << "Build : "; @@ -1353,16 +1353,16 @@ void Foam::argList::parse // Collect machine/pid, and check that the build is identical if (runControl_.parRun()) { - if (Pstream::master()) + if (UPstream::master()) { - hostMachine.resize(Pstream::nProcs()-1); - hostProcs.resize(Pstream::nProcs()-1); + hostMachine.resize(UPstream::nProcs()-1); + hostProcs.resize(UPstream::nProcs()-1); string procBuild; label procPid; int proci = 0; - for (const int subproci : Pstream::subProcs()) + for (const int subproci : UPstream::subProcs()) { - IPstream fromSubproc(Pstream::commsTypes::scheduled, subproci); + IPstream fromSubproc(UPstream::commsTypes::scheduled, subproci); fromSubproc >> procBuild >> hostMachine[proci] >> procPid; @@ -1384,8 +1384,8 @@ void Foam::argList::parse { OPstream toMaster ( - Pstream::commsTypes::scheduled, - Pstream::masterNo() + UPstream::commsTypes::scheduled, + UPstream::masterNo() ); toMaster << foamVersion::build << Foam::hostName() << Foam::pid(); } @@ -1395,14 +1395,34 @@ void Foam::argList::parse // Case is a single processor run unless it is running parallel int nProcs = 1; - // Roots if running distributed + // Roots if running distributed. Only sized on the master fileNameList roots; + enum distributedCodes + { + NON_DISTRIBUTED = 0, + DISTRIBUTED = 1, + DISTRIBUTED_SINGLE_ROOT = 2, + DISTRIBUTED_MULTIPLE_ROOTS = 3 + }; + + // Track which type of distributed roots etc are being used + label distributedType + ( + runControl_.distributed() + ? distributedCodes::DISTRIBUTED + : distributedCodes::NON_DISTRIBUTED + ); + + // Some cases where knowing the writeFormat can be useful... + // label writeFormat(-1); + + // If this actually is a parallel run if (runControl_.parRun()) { // For the master - if (Pstream::master()) + if (UPstream::master()) { // Establish rootPath_/globalCase_/case_ for master setCasePaths(); @@ -1414,7 +1434,7 @@ void Foam::argList::parse { bool adjustOpt = false; - if (isDir(source)) + if (Foam::isDir(source)) { source /= "decomposeParDict"; adjustOpt = true; @@ -1441,6 +1461,7 @@ void Foam::argList::parse { source = "-roots"; runControl_.distributed(true); + distributedType = distributedCodes::DISTRIBUTED; if (roots.empty()) { @@ -1457,6 +1478,7 @@ void Foam::argList::parse { source = "-hostRoots"; runControl_.distributed(true); + distributedType = distributedCodes::DISTRIBUTED; ITstream is(this->lookup("hostRoots")); @@ -1471,7 +1493,7 @@ void Foam::argList::parse } // Match machine names to roots - roots.resize(Pstream::nProcs()-1, fileName::null); + roots.resize(UPstream::nProcs()-1, fileName::null); for (const auto& hostRoot : hostRoots) { labelList matched @@ -1511,7 +1533,7 @@ void Foam::argList::parse dictNProcs = roots.size()+1; } } - else if (checkProcessorDirectories_ && Pstream::nProcs() > 1) + else if (checkProcessorDirectories_ && UPstream::nProcs() > 1) { // Check values from decomposeParDict @@ -1526,7 +1548,7 @@ void Foam::argList::parse // the masterUncollated/collated handler. Note that we // also have to protect the actual dictionary parsing since // it might trigger file access (e.g. #include, #codeStream) - const bool oldParRun = Pstream::parRun(false); + const bool oldParRun = UPstream::parRun(false); // Note: non-parallel running might update // fileOperation::nProcs() so store & restore below const label nOldProcs = fileHandler().nProcs(); @@ -1550,6 +1572,8 @@ void Foam::argList::parse { nDomainsReadOpt = IOobjectOption::MUST_READ; runControl_.distributed(true); + distributedType = distributedCodes::DISTRIBUTED; + decompDict.readEntry("roots", roots); if (roots.empty()) @@ -1587,10 +1611,10 @@ void Foam::argList::parse } } - Pstream::parRun(oldParRun); // Restore parallel state + UPstream::parRun(oldParRun); // Restore parallel state const_cast<fileOperation&>(fileHandler()).nProcs(nOldProcs); - if (Pstream::nProcs() == 1) + if (UPstream::nProcs() == 1) { Warning << "Running parallel on single processor. This only" @@ -1599,22 +1623,55 @@ void Foam::argList::parse } } - // Convenience: - // when a single root is specified, use it for all processes - if (roots.size() == 1) + + // Distributed roots + if (!roots.empty()) { - const fileName rootName(roots[0]); - roots.resize(Pstream::nProcs()-1, rootName); + for (fileName& dir : roots) + { + dir.expand(); + } - // Adjust dictNProcs for command-line '-roots' option - if (dictNProcs <= 0) + // Identical root specified everywhere? + // - use optimized single-root variant + if (roots.size() > 1 && roots.uniform()) { - dictNProcs = roots.size()+1; + roots.resize(1); + } + + if (roots.size() == 1) + { + // Single root specified, use it for all processes + distributedType = + distributedCodes::DISTRIBUTED_SINGLE_ROOT; + + // Adjust dictNProcs for command-line '-roots' option + if (dictNProcs <= 0) + { + dictNProcs = UPstream::nProcs(); + } + } + else if (roots.size() > 1) + { + distributedType = + distributedCodes::DISTRIBUTED_MULTIPLE_ROOTS; + + if (roots.size() != UPstream::nProcs()-1) + { + FatalError + << "Number of roots " << roots.size() + << " != number of sub-ranks " + << UPstream::nProcs()-1 + << exit(FatalError); + } } } + // // Check number of processors. + // + // nProcs => number of actual procs // dictNProcs => number of procs specified in decompositionDict // nProcDirs => number of processor directories @@ -1626,73 +1683,33 @@ void Foam::argList::parse if ( checkProcessorDirectories_ - && Pstream::nProcs() > 1 - && dictNProcs > Pstream::nProcs() + && UPstream::nProcs() > 1 ) { - FatalError - << this->relativePath(source) - << " specifies " << dictNProcs - << " processors but job was started with " - << Pstream::nProcs() << " processors." - << exit(FatalError); - } - - // Distributed data - if (roots.size()) - { - if (roots.size() != Pstream::nProcs()-1) + if (dictNProcs > UPstream::nProcs()) { FatalError - << "number of entries in roots " - << roots.size() - << " is not equal to the number of sub-processes " - << Pstream::nProcs()-1 + << this->relativePath(source) + << " specifies " << dictNProcs + << " processors but job was started with " + << UPstream::nProcs() << " ranks." << exit(FatalError); } - for (fileName& dir : roots) - { - dir.expand(); - } - - // Distribute the master's argument list (with new root) - const bool hadCaseOpt = options_.contains("case"); - for (const int subproci : Pstream::subProcs()) - { - options_.set("case", roots[subproci-1]/globalCase_); - - OPstream toProc(Pstream::commsTypes::scheduled, subproci); - - toProc - << args_ << options_ - << runControl_.distributed() - << label(runControl_.dryRun()) - << label(runControl_.verbose()); - } - options_.erase("case"); - - // Restore [-case dir] - if (hadCaseOpt) - { - options_.set("case", rootPath_/globalCase_); - } - } - else - { // Possibly going to fewer processors. // Check if all procDirs are there. + // NOTE: Only works when not using distributed roots! if ( - checkProcessorDirectories_ - && Pstream::nProcs() > 1 + // Can only rely on directory scanning *without* distributed roots! + roots.empty() && dictNProcs >= 1 - && dictNProcs < Pstream::nProcs() + && dictNProcs < UPstream::nProcs() ) { label nProcDirs = 0; { - const bool oldParRun(UPstream::parRun(false)); + const bool oldParRun = UPstream::parRun(false); // Don't cache processor directories (probably not // needed since master-only const int oldCacheLevel(fileOperation::cacheLevel(0)); @@ -1711,60 +1728,110 @@ void Foam::argList::parse UPstream::parRun(oldParRun); } - if (nProcDirs < UPstream::nProcs()) { FatalError - << "number of processor directories = " - << nProcDirs - << " is not equal to the number of processors = " + << "Number of processor directories = " << nProcDirs + << " is not equal to the number of ranks = " << UPstream::nProcs() << exit(FatalError); } } + } - // Distribute the master's argument list (unaltered) - for (const int proci : UPstream::subProcs()) - { - OPstream toProc(UPstream::commsTypes::scheduled, proci); - toProc - << args_ << options_ - << runControl_.distributed() - << label(runControl_.dryRun()) - << label(runControl_.verbose()); - } + // Broadcast the master's argument list (unaltered) + { + OPBstream toProcs(UPstream::worldComm); + + toProcs + << args_ << options_ + << distributedType + << label(runControl_.dryRun()) + << label(runControl_.verbose()); } } else { - // Collect the master's argument list - bool isDistributed; + // Receive the broadcasted master's argument list label numDryRun, numVerbose; - IPstream fromMaster - ( - Pstream::commsTypes::scheduled, - Pstream::masterNo() - ); + IPBstream fromMaster(UPstream::worldComm); fromMaster >> args_ >> options_ - >> isDistributed + >> distributedType >> numDryRun >> numVerbose; - runControl_.distributed(isDistributed); + runControl_.distributed(distributedType); runControl_.dryRun(numDryRun); runControl_.verbose(numVerbose); + } + + + // Final handling of distributed roots (if any) + if + ( + distributedType == distributedCodes::DISTRIBUTED_SINGLE_ROOT + ) + { + // The same root for all sub-ranks + // - use broadcast to transmit value + + fileName newCasePath; + + if (UPstream::master()) + { + newCasePath = roots[0]/globalCase_; + OPBstream::send(newCasePath); // worldComm + } + else + { + IPBstream::recv(newCasePath); // worldComm + options_.set("case", newCasePath); + } + } + else if + ( + distributedType == distributedCodes::DISTRIBUTED_MULTIPLE_ROOTS + ) + { + // Different roots for each sub-rank + // - use point-to-point communication to transmit values - // Establish rootPath_/globalCase_/case_ for sub-process + fileName newCasePath; + + if (UPstream::master()) + { + for (const int subproci : UPstream::subProcs()) + { + newCasePath = roots[subproci-1]/globalCase_; + OPstream::send(newCasePath, subproci); // worldComm + } + } + else + { + IPstream::recv(newCasePath, UPstream::masterNo()); // worldComm + options_.set("case", newCasePath); + } + } + + + // Establish rootPath_/globalCase_/case_ for sub-process + if (!UPstream::master()) + { setCasePaths(); } - nProcs = Pstream::nProcs(); - if (Pstream::nProcs() > 1) + + nProcs = UPstream::nProcs(); + if (UPstream::nProcs() > 1) { - case_ = globalCase_/("processor" + Foam::name(Pstream::myProcNo())); + case_ = + ( + globalCase_ + / ("processor" + Foam::name(UPstream::myProcNo())) + ); } else { @@ -1778,6 +1845,7 @@ void Foam::argList::parse case_ = globalCase_; // Redundant, but extra safety? } + // If needed, adjust fileHandler for distributed roots if (runControl_.distributed() && fileOperation::fileHandlerPtr_) { @@ -1834,7 +1902,7 @@ void Foam::argList::parse List<fileNameList> rankToDirs(UPstream::nProcs()); if (UPstream::master()) { - const bool oldParRun = Pstream::parRun(false); + const bool oldParRun = UPstream::parRun(false); // Note: non-parallel running might update // fileOperation::nProcs() so store & restore below const label nOldProcs = fileHandler().nProcs(); @@ -2025,7 +2093,7 @@ void Foam::argList::parse sigQuit::set(bannerEnabled()); sigSegv::set(bannerEnabled()); - if (Pstream::master() && bannerEnabled()) + if (UPstream::master() && bannerEnabled()) { Info<< "fileModificationChecking : " << "Monitoring run-time modified files using " @@ -2274,7 +2342,7 @@ bool Foam::argList::check(bool checkArgs, bool checkOpts) const { bool ok = true; - if (Pstream::master()) + if (UPstream::master()) { const label nargs = args_.size()-1; if (checkArgs && nargs != validArgs.size()) @@ -2332,7 +2400,7 @@ bool Foam::argList::checkRootCase() const const fileName pathDir(fileHandler().filePath(path(), false)); - if (checkProcessorDirectories_ && pathDir.empty() && Pstream::master()) + if (checkProcessorDirectories_ && pathDir.empty() && UPstream::master()) { // Allow non-existent processor directories on sub-processes, // to be created later (e.g. redistributePar) diff --git a/src/OpenFOAM/include/openfoam_mpi.H b/src/OpenFOAM/include/openfoam_mpi.H new file mode 100644 index 0000000000000000000000000000000000000000..7359bd8a925229b4784aa18b4b05ad91904cc351 --- /dev/null +++ b/src/OpenFOAM/include/openfoam_mpi.H @@ -0,0 +1,99 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2022-2024 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 + Header for low-level interfaces between MPI and OpenFOAM. + The detail interfaces are subject to change. + +\*---------------------------------------------------------------------------*/ + +#ifndef Foam_UPstreamMPI_H +#define Foam_UPstreamMPI_H + +#include "UPstream.H" + +// Include MPI without any C++ bindings +#ifndef MPICH_SKIP_MPICXX +#define MPICH_SKIP_MPICXX +#endif +#ifndef OMPI_SKIP_MPICXX +#define OMPI_SKIP_MPICXX +#endif +#include <mpi.h> + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace PstreamUtils +{ + +// Casting helpers +struct Cast +{ + // Cast UPstream::Communicator to MPI_Comm (pointer) + template<typename Type = MPI_Comm> + static typename std::enable_if<std::is_pointer<Type>::value, Type>::type + to_mpi(const UPstream::Communicator& arg) noexcept + { + return reinterpret_cast<Type>(arg.value()); + } + + // Cast UPstream::Communicator to MPI_Comm (integer) + template<typename Type = MPI_Comm> + static typename std::enable_if<std::is_integral<Type>::value, Type>::type + to_mpi(const UPstream::Communicator& arg) noexcept + { + return static_cast<Type>(arg.value()); + } + + // Cast UPstream::Request to MPI_Request (pointer) + template<typename Type = MPI_Request> + static typename std::enable_if<std::is_pointer<Type>::value, Type>::type + to_mpi(const UPstream::Request& arg) noexcept + { + return reinterpret_cast<Type>(arg.value()); + } + + // Cast UPstream::Request to MPI_Request (integer) + template<typename Type = MPI_Request> + static typename std::enable_if<std::is_integral<Type>::value, Type>::type + to_mpi(const UPstream::Request& arg) noexcept + { + return static_cast<Type>(arg.value()); + } +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace PstreamUtils +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/Pstream/dummy/Make/files b/src/Pstream/dummy/Make/files index d5e68ac4088c2c8429edf8fa300e62256f273c3a..d560719fc826d5d7d3bfcf3cee2a4b0d9d25cc40 100644 --- a/src/Pstream/dummy/Make/files +++ b/src/Pstream/dummy/Make/files @@ -1,6 +1,7 @@ UPstream.C UPstreamAllToAll.C UPstreamBroadcast.C +UPstreamCommunicator.C UPstreamGatherScatter.C UPstreamReduce.C UPstreamRequest.C diff --git a/src/Pstream/dummy/UPstreamCommunicator.C b/src/Pstream/dummy/UPstreamCommunicator.C new file mode 100644 index 0000000000000000000000000000000000000000..e7c713f85c5dee460d74bd7863be5e3fb2f7a2f1 --- /dev/null +++ b/src/Pstream/dummy/UPstreamCommunicator.C @@ -0,0 +1,59 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2024 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/>. + +\*---------------------------------------------------------------------------*/ + +#include "UPstream.H" + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::UPstream::Communicator::Communicator() noexcept +: + UPstream::Communicator(nullptr) +{} + + +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +Foam::UPstream::Communicator +Foam::UPstream::Communicator::lookup(const label comm) +{ + return UPstream::Communicator(nullptr); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +bool Foam::UPstream::Communicator::good() const noexcept +{ + return false; +} + + +void Foam::UPstream::Communicator::reset() noexcept +{} + + +// ************************************************************************* // diff --git a/src/Pstream/dummy/UPstreamRequest.C b/src/Pstream/dummy/UPstreamRequest.C index dd56a0569dcaf686992c13e5c37b07f72eb6ce0d..4e67be859414f6d259b56fb8b0a4c5bef89d1bc1 100644 --- a/src/Pstream/dummy/UPstreamRequest.C +++ b/src/Pstream/dummy/UPstreamRequest.C @@ -47,6 +47,15 @@ void Foam::UPstream::Request::reset() noexcept {} +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +// Foam::UPstream::Request +// Foam::UPstream::Request::lookup(const label req) +// { +// return UPstream::Request(nullptr); +// } + + // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // Foam::label Foam::UPstream::nRequests() noexcept { return 0; } diff --git a/src/Pstream/mpi/Make/files b/src/Pstream/mpi/Make/files index 48a99bac4014cad74ac7c75f8de79fb793bfc069..c8b08f8a6a29b5a0a7235371adc47aaf6dbae59f 100644 --- a/src/Pstream/mpi/Make/files +++ b/src/Pstream/mpi/Make/files @@ -2,6 +2,7 @@ PstreamGlobals.C UPstream.C UPstreamAllToAll.C UPstreamBroadcast.C +UPstreamCommunicator.C UPstreamGatherScatter.C UPstreamReduce.C UPstreamRequest.C diff --git a/src/Pstream/mpi/PstreamGlobals.H b/src/Pstream/mpi/PstreamGlobals.H index fb8aaec350db158e4f2b9f6456f14943bcc13098..c912c9876f217db227f439ed1bbe0b79a4c20ab2 100644 --- a/src/Pstream/mpi/PstreamGlobals.H +++ b/src/Pstream/mpi/PstreamGlobals.H @@ -40,16 +40,8 @@ SourceFiles #define Foam_PstreamGlobals_H #include "DynamicList.H" -#include "UPstream.H" // for UPstream::Request - -// Include MPI without any C++ bindings -#ifndef MPICH_SKIP_MPICXX -#define MPICH_SKIP_MPICXX -#endif -#ifndef OMPI_SKIP_MPICXX -#define OMPI_SKIP_MPICXX -#endif -#include <mpi.h> +#include "UPstream.H" // For UPstream::Request +#include "openfoam_mpi.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/src/Pstream/mpi/UPstream.C b/src/Pstream/mpi/UPstream.C index 2116461c1b4c5bd32e51bc83d1cce95e58f46e99..93bd24cda175e7bcac3cd600b228012f11149818 100644 --- a/src/Pstream/mpi/UPstream.C +++ b/src/Pstream/mpi/UPstream.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation - Copyright (C) 2016-2023 OpenCFD Ltd. + Copyright (C) 2016-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -548,7 +548,7 @@ void Foam::UPstream::allocateCommunicatorComponents if (index != UPstream::commGlobal()) { FatalErrorInFunction - << "world communicator should always be index " + << "base world communicator should always be index " << UPstream::commGlobal() << Foam::exit(FatalError); } @@ -687,36 +687,30 @@ void Foam::UPstream::allocateCommunicatorComponents void Foam::UPstream::freeCommunicatorComponents(const label index) { - // Skip placeholders and pre-defined (not allocated) communicators if (UPstream::debug) { Pout<< "freeCommunicatorComponents: " << index << " from " << PstreamGlobals::MPICommunicators_.size() << endl; } - // Not touching the first two communicators (SELF, WORLD) - // or anything out-of bounds. + // Only free communicators that we have specifically allocated ourselves // - // No UPstream communicator indices when MPI is initialized outside - // of OpenFOAM - thus needs a bounds check too! + // Bounds checking needed since there are no UPstream communicator indices + // when MPI is initialized outside of OpenFOAM if ( - index > 1 - && index < PstreamGlobals::MPICommunicators_.size() + (index >= 0 && index < PstreamGlobals::MPICommunicators_.size()) + && PstreamGlobals::pendingMPIFree_[index] ) { - if - ( - PstreamGlobals::pendingMPIFree_[index] - && (MPI_COMM_NULL != PstreamGlobals::MPICommunicators_[index]) - ) + PstreamGlobals::pendingMPIFree_[index] = false; + + // Free communicator. Sets communicator to MPI_COMM_NULL + if (MPI_COMM_NULL != PstreamGlobals::MPICommunicators_[index]) { - // Free communicator. Sets communicator to MPI_COMM_NULL MPI_Comm_free(&PstreamGlobals::MPICommunicators_[index]); } - - PstreamGlobals::pendingMPIFree_[index] = false; } } diff --git a/src/Pstream/mpi/UPstreamCommunicator.C b/src/Pstream/mpi/UPstreamCommunicator.C new file mode 100644 index 0000000000000000000000000000000000000000..9040f2f0dc24345c5cfeede063b583e4c680e0eb --- /dev/null +++ b/src/Pstream/mpi/UPstreamCommunicator.C @@ -0,0 +1,73 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2024 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/>. + +\*---------------------------------------------------------------------------*/ + +#include "UPstream.H" +#include "PstreamGlobals.H" + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::UPstream::Communicator::Communicator() noexcept +: + UPstream::Communicator(MPI_COMM_NULL) +{} + + +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +Foam::UPstream::Communicator +Foam::UPstream::Communicator::lookup(const label comm) +{ + if (comm < 0 || comm >= PstreamGlobals::MPICommunicators_.size()) + { + WarningInFunction + << "Illegal communicator " << comm << nl + << "Should be within range [0," + << PstreamGlobals::MPICommunicators_.size() + << ')' << endl; + + return UPstream::Communicator(MPI_COMM_NULL); + } + + return UPstream::Communicator(PstreamGlobals::MPICommunicators_[comm]); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +bool Foam::UPstream::Communicator::good() const noexcept +{ + return MPI_COMM_NULL != PstreamUtils::Cast::to_mpi(*this); +} + + +void Foam::UPstream::Communicator::reset() noexcept +{ + *this = UPstream::Communicator(MPI_COMM_NULL); +} + + +// ************************************************************************* // diff --git a/src/Pstream/mpi/UPstreamRequest.C b/src/Pstream/mpi/UPstreamRequest.C index ec773ec1063d43150073966f8e96fddb40c1d6fb..04947e202822c9f1862687430debc654e315fa6a 100644 --- a/src/Pstream/mpi/UPstreamRequest.C +++ b/src/Pstream/mpi/UPstreamRequest.C @@ -42,7 +42,7 @@ Foam::UPstream::Request::Request() noexcept bool Foam::UPstream::Request::good() const noexcept { - return MPI_REQUEST_NULL != PstreamDetail::Request::get(*this); + return MPI_REQUEST_NULL != PstreamUtils::Cast::to_mpi(*this); } @@ -52,6 +52,26 @@ void Foam::UPstream::Request::reset() noexcept } +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +// Foam::UPstream::Request +// Foam::UPstream::Request::lookup(const label req) +// { +// if (req < 0 || req >= PstreamGlobals::outstandingRequests_.size()) +// { +// WarningInFunction +// << "Illegal request " << req << nl +// << "Should be within range [0," +// << PstreamGlobals::outstandingRequests_.size() +// << ')' << endl; +// +// return UPstream::Communicator(MPI_REQUEST_NULL); +// } +// +// return UPstream::Request(PstreamGlobals::outstandingRequests_[req]); +// } + + // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // Foam::label Foam::UPstream::nRequests() noexcept @@ -80,7 +100,7 @@ void Foam::UPstream::addRequest(UPstream::Request& req) // Transcribe as a MPI_Request PstreamGlobals::outstandingRequests_.push_back ( - PstreamDetail::Request::get(req) + PstreamUtils::Cast::to_mpi(req) ); // Invalidate parameter @@ -121,7 +141,7 @@ void Foam::UPstream::cancelRequest(UPstream::Request& req) } { - MPI_Request request = PstreamDetail::Request::get(req); + MPI_Request request = PstreamUtils::Cast::to_mpi(req); if (MPI_REQUEST_NULL != request) // Active handle is mandatory { MPI_Cancel(&request); @@ -142,7 +162,7 @@ void Foam::UPstream::cancelRequests(UList<UPstream::Request>& requests) for (auto& req : requests) { - MPI_Request request = PstreamDetail::Request::get(req); + MPI_Request request = PstreamUtils::Cast::to_mpi(req); if (MPI_REQUEST_NULL != request) // Active handle is mandatory { MPI_Cancel(&request); @@ -203,7 +223,7 @@ void Foam::UPstream::freeRequest(UPstream::Request& req) } { - MPI_Request request = PstreamDetail::Request::get(req); + MPI_Request request = PstreamUtils::Cast::to_mpi(req); if (MPI_REQUEST_NULL != request) // Active handle is mandatory { // if (cancel) @@ -227,7 +247,7 @@ void Foam::UPstream::freeRequests(UList<UPstream::Request>& requests) for (auto& req : requests) { - MPI_Request request = PstreamDetail::Request::get(req); + MPI_Request request = PstreamUtils::Cast::to_mpi(req); if (MPI_REQUEST_NULL != request) // Active handle is mandatory { // if (cancel) @@ -329,7 +349,7 @@ void Foam::UPstream::waitRequests(UList<UPstream::Request>& requests) for (auto& req : requests) { - MPI_Request request = PstreamDetail::Request::get(req); + MPI_Request request = PstreamUtils::Cast::to_mpi(req); if (MPI_REQUEST_NULL != request) // Apply some prefiltering { @@ -526,7 +546,7 @@ bool Foam::UPstream::waitSomeRequests for (auto& req : requests) { - waitRequests[count] = PstreamDetail::Request::get(req); + waitRequests[count] = PstreamUtils::Cast::to_mpi(req); ++count; } @@ -617,7 +637,7 @@ Foam::label Foam::UPstream::waitAnyRequest(UList<UPstream::Request>& requests) // for the return index. for (auto& req : requests) { - waitRequests[count] = PstreamDetail::Request::get(req); + waitRequests[count] = PstreamUtils::Cast::to_mpi(req); ++count; } @@ -675,13 +695,13 @@ Foam::label Foam::UPstream::waitAnyRequest(UList<UPstream::Request>& requests) /// int count = 0; /// MPI_Request waitRequests[2]; /// -/// waitRequests[count] = PstreamDetail::Request::get(req0); +/// waitRequests[count] = PstreamUtils::Cast::to_mpi(req0); /// if (MPI_REQUEST_NULL != waitRequests[count]) /// { /// ++count; /// } /// -/// waitRequests[count] = PstreamDetail::Request::get(req1); +/// waitRequests[count] = PstreamUtils::Cast::to_mpi(req1); /// if (MPI_REQUEST_NULL != waitRequests[count]) /// { /// ++count; @@ -765,7 +785,7 @@ void Foam::UPstream::waitRequest(UPstream::Request& req) return; } - MPI_Request request = PstreamDetail::Request::get(req); + MPI_Request request = PstreamUtils::Cast::to_mpi(req); // No-op for null request if (MPI_REQUEST_NULL == request) @@ -831,7 +851,7 @@ bool Foam::UPstream::finishedRequest(UPstream::Request& req) return true; } - MPI_Request request = PstreamDetail::Request::get(req); + MPI_Request request = PstreamUtils::Cast::to_mpi(req); // Fast-path (no-op) for null request if (MPI_REQUEST_NULL == request) @@ -924,7 +944,7 @@ bool Foam::UPstream::finishedRequests(UList<UPstream::Request>& requests) for (auto& req : requests) { - MPI_Request request = PstreamDetail::Request::get(req); + MPI_Request request = PstreamUtils::Cast::to_mpi(req); if (MPI_REQUEST_NULL != request) // Apply some prefiltering { diff --git a/src/Pstream/mpi/UPstreamWrapping.H b/src/Pstream/mpi/UPstreamWrapping.H index 7ce3b5ba49fee06cbbaa192adb18f98c213b0caf..33092eda41ca4dadf7562cf34bf462aad75c3c2d 100644 --- a/src/Pstream/mpi/UPstreamWrapping.H +++ b/src/Pstream/mpi/UPstreamWrapping.H @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2012-2016 OpenFOAM Foundation - Copyright (C) 2022-2023 OpenCFD Ltd. + Copyright (C) 2022-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -38,16 +38,7 @@ SourceFiles #ifndef Foam_UPstreamWrapping_H #define Foam_UPstreamWrapping_H -#include "UPstream.H" - -// Include MPI without any C++ bindings -#ifndef MPICH_SKIP_MPICXX -#define MPICH_SKIP_MPICXX -#endif -#ifndef OMPI_SKIP_MPICXX -#define OMPI_SKIP_MPICXX -#endif -#include <mpi.h> +#include "openfoam_mpi.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -56,27 +47,6 @@ namespace Foam namespace PstreamDetail { -// Helper for casting to MPI_Request -struct Request -{ - // To pointer - template<typename Type = MPI_Request> - static typename std::enable_if<std::is_pointer<Type>::value, Type>::type - get(const UPstream::Request& req) noexcept - { - return reinterpret_cast<Type>(req.value()); - } - - // To integer - template<typename Type = MPI_Request> - static typename std::enable_if<std::is_integral<Type>::value, Type>::type - get(const UPstream::Request& req) noexcept - { - return static_cast<Type>(req.value()); - } -}; - - // MPI_Bcast, using root=0 template<class Type> void broadcast0