ENH: cleaner separation of global and local world naming

- UPstream::globalComm constant always refers to MPI_COMM_WORLD but
  UPstream::worldComm could be MPI_COMM_WORLD (single world)
  or a dedicated local communicator (for multi-world).

- provide a Pstream wrapped version of MPI_COMM_SELF,
  references as UPstream::selfComm

- UPstream::isUserComm(label)
  test for additional user-defined communicators
This commit is contained in:
Mark Olesen
2022-11-29 11:37:29 +01:00
committed by Andrew Heather
parent 7fe8bdcf99
commit ffeef76d8f
14 changed files with 575 additions and 229 deletions

View File

@ -0,0 +1,3 @@
Test-parallel-comm0.C
EXE = $(FOAM_USER_APPBIN)/Test-parallel-comm0

View File

@ -0,0 +1,161 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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-comm0
Description
Very basic checks on standard communicators
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "IPstream.H"
#include "OPstream.H"
#include "Pair.H"
#include "Tuple2.H"
#include "IOstreams.H"
#include "PstreamReduceOps.H"
using namespace Foam;
void printInfo(const label comm)
{
Info<< "comm:" << comm
<< " nprocs:" << UPstream::nProcs(comm)
<< " all:" << UPstream::allProcs(comm)
<< " sub:" << UPstream::subProcs(comm) << nl;
if (UPstream::selfComm == comm)
{
Pout<< "self all:" << UPstream::allProcs(comm)
<< " sub:" << UPstream::subProcs(comm) << nl;
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::noBanner();
argList::noCheckProcessorDirectories();
argList::addBoolOption("verbose", "Set debug level");
// Capture manually. We need values before proper startup
int nVerbose = 0;
for (int argi = 1; argi < argc; ++argi)
{
if (strcmp(argv[argi], "-verbose") == 0)
{
++nVerbose;
}
}
UPstream::debug = nVerbose;
#include "setRootCase.H"
Info<< nl
<< "nProcs = " << UPstream::nProcs()
<< " with " << UPstream::nComms() << " predefined comm(s)" << nl;
Info<< "worldComm : ";
printInfo(UPstream::worldComm);
Info<< "selfComm : ";
printInfo(UPstream::selfComm);
Info<< nl;
// Reductions (using MPI intrinsics)
{
label val = Pstream::myProcNo(UPstream::worldComm);
label worldVal = returnReduce
(
val,
sumOp<label>(),
Pstream::msgType(),
UPstream::worldComm
);
label selfVal = returnReduce
(
val,
sumOp<label>(),
Pstream::msgType(),
UPstream::selfComm
);
Pout<< "value " << val
<< " (world) reduced " << worldVal
<< " (self) reduced " << selfVal << nl;
}
// Reductions (not using MPI intrinsics)
{
Pair<label> val
(
Pstream::myProcNo(UPstream::worldComm),
Pstream::myProcNo(UPstream::worldComm)
);
Pair<label> worldVal = val;
Pstream::combineReduce
(
worldVal,
minFirstEqOp<label>(),
Pstream::msgType(),
UPstream::worldComm
);
Pair<label> selfVal = val;
Pstream::combineReduce
(
worldVal,
minFirstEqOp<label>(),
Pstream::msgType(),
UPstream::selfComm
);
Pout<< "value " << val
<< " (world) reduced " << worldVal
<< " (self) reduced " << selfVal << nl;
}
Pout<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
Test-parallel-comm1.C
EXE = $(FOAM_USER_APPBIN)/Test-parallel-comm1

View File

@ -0,0 +1,2 @@
/* EXE_INC = */
/* EXE_LIBS = */

View File

@ -1,3 +0,0 @@
Test-parallel-communicators.C
EXE = $(FOAM_USER_APPBIN)/Test-parallel-communicators

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2019 OpenCFD Ltd. Copyright (C) 2019-2022 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -47,16 +47,88 @@ using namespace Foam;
bool startMPI() bool startMPI()
{ {
int nprocs = 0, rank = 0; enum whichComm : int { worldComm = 0, selfComm, nullComm };
int nprocs[3];
int rank[3];
int group_nprocs[3];
int group_rank[3];
MPI_Group mpiGroup;
MPI_Init(nullptr, nullptr); MPI_Init(nullptr, nullptr);
MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MPI_Comm_size(MPI_COMM_WORLD, &nprocs[worldComm]);
MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_rank(MPI_COMM_WORLD, &rank[worldComm]);
if (nprocs && rank == 0) const bool isMaster = (rank[worldComm] == 0);
const string prefix = '[' + Foam::name(rank[worldComm]) + "] ";
MPI_Comm_group(MPI_COMM_WORLD, &mpiGroup);
MPI_Group_size(mpiGroup, &group_nprocs[worldComm]);
MPI_Group_rank(mpiGroup, &group_rank[worldComm]);
if (isMaster && nprocs[worldComm])
{ {
std::cout<< nl << "Using MPI with " << nprocs << " procs" << nl << nl; std::cout
<< nl << "Using MPI with " << nprocs[worldComm]
<< " procs, group:"
<< group_nprocs[worldComm] << nl
<< "World group: " << Foam::name(mpiGroup) << nl
<< nl;
}
MPI_Comm worldMpiComm;
MPI_Comm_dup(MPI_COMM_WORLD, &worldMpiComm);
MPI_Comm_group(MPI_COMM_WORLD, &mpiGroup);
if (isMaster && nprocs[worldComm])
{
std::cout
<< "dup comm group: " << Foam::name(mpiGroup) << nl;
}
MPI_Comm_free(&worldMpiComm);
// May be a bad idea
MPI_Group_free(&mpiGroup);
MPI_Comm_size(MPI_COMM_SELF, &nprocs[selfComm]);
MPI_Comm_rank(MPI_COMM_SELF, &rank[selfComm]);
MPI_Comm_group(MPI_COMM_SELF, &mpiGroup);
MPI_Group_size(mpiGroup, &group_nprocs[selfComm]);
MPI_Group_rank(mpiGroup, &group_rank[selfComm]);
if (isMaster && nprocs[worldComm])
{
std::cout
<< nl
<< "Self group: " << Foam::name(mpiGroup) << nl;
}
// Should be a bad idea
MPI_Group_free(&mpiGroup);
// if (nprocs && isMaster)
{
std::cout
<< prefix
<< "Self: " << rank[selfComm] << " from " << nprocs[selfComm]
<< " procs, group:"
<< group_nprocs[selfComm] << nl;
}
if (isMaster)
{
std::cout
<< "MPI_COMM_NULL: " << MPI_COMM_NULL << nl
<< "MPI_COMM_SELF: " << MPI_COMM_SELF << nl
<< "MPI_COMM_WORLD: " << MPI_COMM_WORLD << nl;
} }
return true; return true;
@ -77,8 +149,8 @@ string message()
{ {
return return
( (
"rank " + name(Pstream::myProcNo()) "rank " + Foam::name(Pstream::myProcNo())
+ " / " + name(Pstream::nProcs()) + "\n" + " / " + Foam::name(Pstream::nProcs()) + "\n"
); );
} }
@ -87,9 +159,21 @@ string message()
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
argList::noBanner();
argList::noCheckProcessorDirectories(); argList::noCheckProcessorDirectories();
argList::addBoolOption("verbose", "Set debug level");
UPstream::debug = 1; // Need to capture manually, since we need values before proper startup
int nVerbose = 0;
for (int argi = 1; argi < argc; ++argi)
{
if (strcmp(argv[argi], "-verbose") == 0)
{
++nVerbose;
}
}
UPstream::debug = nVerbose;
startMPI(); startMPI();

View File

@ -58,43 +58,78 @@ Foam::UPstream::commsTypeNames
void Foam::UPstream::setParRun(const label nProcs, const bool haveThreads) void Foam::UPstream::setParRun(const label nProcs, const bool haveThreads)
{ {
if (nProcs == 0) parRun_ = (nProcs > 0);
{ haveThreads_ = haveThreads;
parRun_ = false;
haveThreads_ = haveThreads;
freeCommunicator(UPstream::worldComm); label comm = -1;
label comm = allocateCommunicator(-1, labelList(Foam::one{}, 0), false);
if (comm != UPstream::worldComm) if (!parRun_)
{
// These are already correct from the static initialisation,
// but just in case of future changes
// Using (world, self) ordering
freeCommunicator(UPstream::selfComm);
freeCommunicator(UPstream::globalComm);
// 0: worldComm
comm = allocateCommunicator(-1, Foam::labelList(Foam::one{}, 0), false);
if (comm != UPstream::globalComm)
{ {
// Failed sanity check
FatalErrorInFunction FatalErrorInFunction
<< "problem : comm:" << comm << "problem : comm:" << comm
<< " UPstream::worldComm:" << UPstream::worldComm << " UPstream::globalComm:" << UPstream::globalComm
<< Foam::exit(FatalError); << Foam::exit(FatalError);
} }
Pout.prefix() = ""; // 1: selfComm
Perr.prefix() = ""; comm = allocateCommunicator(-2, Foam::labelList(Foam::one{}, 0), false);
if (comm != UPstream::selfComm)
{
// Failed sanity check
FatalErrorInFunction
<< "problem : comm:" << comm
<< " UPstream::selfComm:" << UPstream::selfComm
<< Foam::exit(FatalError);
}
Pout.prefix().clear();
Perr.prefix().clear();
} }
else else
{ {
parRun_ = true; // Redo communicators that were created during static initialisation
haveThreads_ = haveThreads; // but this time with Pstream components
// Redo worldComm communicator (this has been created at static // Using (world, self) ordering
// initialisation time) freeCommunicator(UPstream::selfComm);
freeCommunicator(UPstream::worldComm); freeCommunicator(UPstream::globalComm);
label comm = allocateCommunicator(-1, identity(nProcs), true);
if (comm != UPstream::worldComm) // 0: worldComm
comm = allocateCommunicator(-1, identity(nProcs), true);
if (comm != UPstream::globalComm)
{ {
// Failed sanity check
FatalErrorInFunction FatalErrorInFunction
<< "problem : comm:" << comm << "problem : comm:" << comm
<< " UPstream::worldComm:" << UPstream::worldComm << " UPstream::globalComm:" << UPstream::globalComm
<< Foam::exit(FatalError); << Foam::exit(FatalError);
} }
Pout.prefix() = '[' + name(myProcNo(comm)) + "] "; Pout.prefix() = '[' + Foam::name(myProcNo(comm)) + "] ";
Perr.prefix() = '[' + name(myProcNo(comm)) + "] "; Perr.prefix() = Pout.prefix();
// 1: selfComm
comm = allocateCommunicator(-2, Foam::labelList(Foam::one{}, 0), true);
if (comm != UPstream::selfComm)
{
// Failed sanity check
FatalErrorInFunction
<< "problem : comm:" << comm
<< " UPstream::selfComm:" << UPstream::selfComm
<< Foam::exit(FatalError);
}
} }
if (debug) if (debug)
@ -110,23 +145,25 @@ void Foam::UPstream::setParRun(const label nProcs, const bool haveThreads)
Foam::label Foam::UPstream::allocateCommunicator Foam::label Foam::UPstream::allocateCommunicator
( (
const label parentIndex, const label parentIndex,
const labelList& subRanks, const labelUList& subRanks,
const bool doPstream const bool doPstream
) )
{ {
label index; label index;
if (!freeComms_.empty()) if (!freeComms_.empty())
{ {
index = freeComms_.remove(); // LIFO pop // LIFO pop
index = freeComms_.back();
freeComms_.pop_back();
} }
else else
{ {
// Extend storage // Extend storage
index = parentCommunicator_.size(); index = parentComm_.size();
myProcNo_.append(-1); myProcNo_.append(-1);
procIDs_.append(List<int>()); procIDs_.append(List<int>());
parentCommunicator_.append(-1); parentComm_.append(-1);
linearCommunication_.append(List<commsStruct>()); linearCommunication_.append(List<commsStruct>());
treeCommunication_.append(List<commsStruct>()); treeCommunication_.append(List<commsStruct>());
} }
@ -142,8 +179,10 @@ Foam::label Foam::UPstream::allocateCommunicator
// Initialise; overwritten by allocatePstreamCommunicator // Initialise; overwritten by allocatePstreamCommunicator
myProcNo_[index] = 0; myProcNo_[index] = 0;
const label numSubRanks = subRanks.size();
// Convert from label to int // Convert from label to int
procIDs_[index].setSize(subRanks.size()); procIDs_[index].resize_nocopy(numSubRanks);
forAll(procIDs_[index], i) forAll(procIDs_[index], i)
{ {
procIDs_[index][i] = subRanks[i]; procIDs_[index][i] = subRanks[i];
@ -158,11 +197,11 @@ Foam::label Foam::UPstream::allocateCommunicator
<< Foam::abort(FatalError); << Foam::abort(FatalError);
} }
} }
parentCommunicator_[index] = parentIndex; parentComm_[index] = parentIndex;
// Size but do not fill structure - this is done on-the-fly // Size but do not fill structure - this is done on-the-fly
linearCommunication_[index] = List<commsStruct>(procIDs_[index].size()); linearCommunication_[index] = List<commsStruct>(numSubRanks);
treeCommunication_[index] = List<commsStruct>(procIDs_[index].size()); treeCommunication_[index] = List<commsStruct>(numSubRanks);
if (doPstream && parRun()) if (doPstream && parRun())
{ {
@ -179,10 +218,16 @@ void Foam::UPstream::freeCommunicator
const bool doPstream const bool doPstream
) )
{ {
// Filter out any placeholders
if (communicator < 0)
{
return;
}
if (debug) if (debug)
{ {
Pout<< "Communicators : Freeing communicator " << communicator << endl Pout<< "Communicators : Freeing communicator " << communicator << endl
<< " parent : " << parentCommunicator_[communicator] << endl << " parent : " << parentComm_[communicator] << endl
<< " myProcNo : " << myProcNo_[communicator] << endl << " myProcNo : " << myProcNo_[communicator] << endl
<< endl; << endl;
} }
@ -191,13 +236,15 @@ void Foam::UPstream::freeCommunicator
{ {
freePstreamCommunicator(communicator); freePstreamCommunicator(communicator);
} }
myProcNo_[communicator] = -1; myProcNo_[communicator] = -1;
//procIDs_[communicator].clear(); //procIDs_[communicator].clear();
parentCommunicator_[communicator] = -1; parentComm_[communicator] = -1;
linearCommunication_[communicator].clear(); linearCommunication_[communicator].clear();
treeCommunication_[communicator].clear(); treeCommunication_[communicator].clear();
freeComms_.append(communicator); // LIFO push // LIFO push
freeComms_.push_back(communicator);
} }
@ -213,48 +260,44 @@ void Foam::UPstream::freeCommunicators(const bool doPstream)
} }
int Foam::UPstream::baseProcNo(const label myComm, const int myProcID) int Foam::UPstream::baseProcNo(label comm, int procID)
{ {
int procID = myProcID; while (parent(comm) >= 0)
label comm = myComm;
while (parent(comm) != -1)
{ {
const List<int>& parentRanks = UPstream::procID(comm); const auto& parentRanks = UPstream::procID(comm);
procID = parentRanks[procID]; procID = parentRanks[procID];
comm = UPstream::parent(comm); comm = parent(comm);
} }
return procID; return procID;
} }
Foam::label Foam::UPstream::procNo(const label myComm, const int baseProcID) Foam::label Foam::UPstream::procNo(const label comm, const int baseProcID)
{ {
const List<int>& parentRanks = procID(myComm); const auto& parentRanks = procID(comm);
label parentComm = parent(myComm); label parentComm = parent(comm);
if (parentComm == -1) int procID = baseProcID;
if (parentComm >= 0)
{ {
return parentRanks.find(baseProcID); procID = procNo(parentComm, baseProcID);
}
else
{
const label parentRank = procNo(parentComm, baseProcID);
return parentRanks.find(parentRank);
} }
return parentRanks.find(procID);
} }
Foam::label Foam::UPstream::procNo Foam::label Foam::UPstream::procNo
( (
const label myComm, const label comm,
const label currentComm, const label currentComm,
const int currentProcID const int currentProcID
) )
{ {
label physProcID = UPstream::baseProcNo(currentComm, currentProcID); label physProcID = UPstream::baseProcNo(currentComm, currentProcID);
return procNo(myComm, physProcID); return procNo(comm, physProcID);
} }
@ -380,7 +423,7 @@ Foam::DynamicList<int> Foam::UPstream::myProcNo_(10);
Foam::DynamicList<Foam::List<int>> Foam::UPstream::procIDs_(10); Foam::DynamicList<Foam::List<int>> Foam::UPstream::procIDs_(10);
Foam::DynamicList<Foam::label> Foam::UPstream::parentCommunicator_(10); Foam::DynamicList<Foam::label> Foam::UPstream::parentComm_(10);
Foam::DynamicList<Foam::label> Foam::UPstream::freeComms_; Foam::DynamicList<Foam::label> Foam::UPstream::freeComms_;
@ -394,14 +437,25 @@ Foam::DynamicList<Foam::List<Foam::UPstream::commsStruct>>
Foam::UPstream::treeCommunication_(10); Foam::UPstream::treeCommunication_(10);
// Allocate a serial communicator. This gets overwritten in parallel mode Foam::label Foam::UPstream::worldComm(0);
// (by UPstream::setParRun())
Foam::UPstream::communicator serialComm Foam::label Foam::UPstream::warnComm(-1);
(
-1,
Foam::labelList(Foam::one{}, 0), // Predefine worldComm, selfComm slots.
false // These are overwritten in parallel mode (by UPstream::setParRun())
); const Foam::label nPredefinedComm = []()
{
const Foam::labelList singleProc(Foam::one{}, 0);
// 0: worldComm
(void) Foam::UPstream::allocateCommunicator(-1, singleProc, false);
// 1: selfComm
(void) Foam::UPstream::allocateCommunicator(-2, singleProc, false);
return Foam::UPstream::nComms();
}();
bool Foam::UPstream::floatTransfer bool Foam::UPstream::floatTransfer
@ -466,10 +520,6 @@ namespace Foam
addcommsTypeToOpt addcommsTypeToOpt_("commsType"); addcommsTypeToOpt addcommsTypeToOpt_("commsType");
} }
Foam::label Foam::UPstream::worldComm(0);
Foam::label Foam::UPstream::warnComm(-1);
int Foam::UPstream::nPollProcInterfaces int Foam::UPstream::nPollProcInterfaces
( (
Foam::debug::optimisationSwitch("nPollProcInterfaces", 0) Foam::debug::optimisationSwitch("nPollProcInterfaces", 0)

View File

@ -107,9 +107,9 @@ public:
commsStruct commsStruct
( (
const label above, const label above,
const labelList& below, const labelUList& below,
const labelList& allBelow, const labelUList& allBelow,
const labelList& allNotBelow const labelUList& allNotBelow
); );
//- Construct from components; construct allNotBelow_ //- Construct from components; construct allNotBelow_
@ -118,13 +118,16 @@ public:
const label nProcs, const label nProcs,
const label myProcID, const label myProcID,
const label above, const label above,
const labelList& below, const labelUList& below,
const labelList& allBelow const labelUList& allBelow
); );
// Member Functions // Member Functions
//- Reset to default constructed state
void clear();
//- The procID of the processor directly above //- The procID of the processor directly above
label above() const noexcept label above() const noexcept
{ {
@ -203,7 +206,7 @@ private:
//- Names of all worlds //- Names of all worlds
static wordList allWorlds_; static wordList allWorlds_;
//- Per processor the name of the world //- Per processor the world index (into allWorlds_)
static labelList worldIDs_; static labelList worldIDs_;
@ -216,7 +219,7 @@ private:
static DynamicList<List<int>> procIDs_; static DynamicList<List<int>> procIDs_;
//- Parent communicator //- Parent communicator
static DynamicList<label> parentCommunicator_; static DynamicList<label> parentComm_;
//- Free communicators //- Free communicators
static DynamicList<label> freeComms_; static DynamicList<label> freeComms_;
@ -233,21 +236,6 @@ private:
//- Set data for parallel running //- Set data for parallel running
static void setParRun(const label nProcs, const bool haveThreads); static void setParRun(const label nProcs, const bool haveThreads);
//- Calculate linear communication schedule
static List<commsStruct> calcLinearComm(const label nProcs);
//- Calculate tree communication schedule
static List<commsStruct> calcTreeComm(const label nProcs);
//- Helper function for tree communication schedule determination
// Collects all processorIDs below a processor
static void collectReceives
(
const label procID,
const List<DynamicList<label>>& receives,
DynamicList<label>& allReceives
);
//- Allocate a communicator with index //- Allocate a communicator with index
static void allocatePstreamCommunicator static void allocatePstreamCommunicator
( (
@ -255,11 +243,9 @@ private:
const label index const label index
); );
//- Free a communicator //- Free MPI components of communicator.
static void freePstreamCommunicator // Does not touch the first two communicators (SELF, WORLD)
( static void freePstreamCommunicator(const label index);
const label index
);
public: public:
@ -290,12 +276,31 @@ public:
//- MPI buffer-size (bytes) //- MPI buffer-size (bytes)
static const int mpiBufferSize; static const int mpiBufferSize;
//- Default communicator (all processors)
// Standard Communicators
//- Default world communicator (all processors).
//- May differ from globalComm if local worlds are in use
static label worldComm; static label worldComm;
//- Debugging: warn for use of any communicator differing from warnComm //- Debugging: warn for use of any communicator differing from warnComm
static label warnComm; static label warnComm;
//- Communicator for all processors, irrespective of any local worlds
static constexpr label globalComm = 0;
//- A communicator within the current rank only
static constexpr label selfComm = 1;
//- Number of currently defined communicators
static label nComms() noexcept { return parentComm_.size(); }
//- True if communicator appears to be user-allocated
static bool isUserComm(label communicator) noexcept
{
return (communicator > worldComm && communicator > selfComm);
}
// Constructors // Constructors
@ -312,11 +317,12 @@ public:
static label allocateCommunicator static label allocateCommunicator
( (
const label parent, const label parent,
const labelList& subRanks, const labelUList& subRanks,
const bool doPstream = true const bool doPstream = true
); );
//- Free a previously allocated communicator //- Free a previously allocated communicator.
// Ignores placeholder (negative) communicators.
static void freeCommunicator static void freeCommunicator
( (
const label communicator, const label communicator,
@ -326,43 +332,52 @@ public:
//- Free all communicators //- Free all communicators
static void freeCommunicators(const bool doPstream); static void freeCommunicators(const bool doPstream);
//- Helper class for allocating/freeing communicators //- Wrapper class for allocating/freeing communicators
class communicator class communicator
{ {
label comm_; label comm_;
public:
//- Default construct (a placeholder communicator)
communicator() : comm_(-1) {}
//- Move construct
communicator(communicator&&) = default;
//- Move assignment
communicator& operator=(communicator&&) = default;
//- No copy construct //- No copy construct
communicator(const communicator&) = delete; communicator(const communicator&) = delete;
//- No copy assignment //- No copy assignment
void operator=(const communicator&) = delete; void operator=(const communicator&) = delete;
public: //- Allocate a communicator from given parent
communicator communicator
( (
const label parent, const label parent,
const labelList& subRanks, const labelUList& subRanks,
const bool doPstream const bool doPstream
) )
: :
comm_(allocateCommunicator(parent, subRanks, doPstream)) comm_(allocateCommunicator(parent, subRanks, doPstream))
{} {}
//- Free allocated communicator and group
~communicator() ~communicator()
{ {
freeCommunicator(comm_); freeCommunicator(comm_);
} }
operator label() const noexcept operator label() const noexcept { return comm_; }
{
return comm_;
}
}; };
//- Return physical processor number (i.e. processor number in //- Return physical processor number (i.e. processor number in
//- worldComm) given communicator and procssor //- worldComm) given communicator and processor
static int baseProcNo(const label myComm, const int procID); static int baseProcNo(label comm, int procID);
//- Return processor number in communicator (given physical processor //- Return processor number in communicator (given physical processor
//- number) (= reverse of baseProcNo) //- number) (= reverse of baseProcNo)
@ -372,7 +387,7 @@ public:
//- and communicator) //- and communicator)
static label procNo static label procNo
( (
const label myComm, const label comm,
const label currentComm, const label currentComm,
const int currentProcID const int currentProcID
); );
@ -414,13 +429,8 @@ public:
// A no-op and returns true if parRun() == false // A no-op and returns true if parRun() == false
static bool finishedRequest(const label i); static bool finishedRequest(const label i);
static int allocateTag(const char*); static int allocateTag(const char* const msg = nullptr);
static void freeTag(const int tag, const char* const msg = nullptr);
static int allocateTag(const std::string&);
static void freeTag(const char*, const int tag);
static void freeTag(const std::string&, const int tag);
//- Set as parallel run on/off. //- Set as parallel run on/off.
@ -445,7 +455,8 @@ public:
return haveThreads_; return haveThreads_;
} }
//- Number of processes in parallel run, and 1 for serial run //- Number of ranks in parallel run (for given communicator)
//- is 1 for serial run
static label nProcs(const label communicator = worldComm) static label nProcs(const label communicator = worldComm)
{ {
return procIDs_[communicator].size(); return procIDs_[communicator].size();
@ -469,13 +480,14 @@ public:
return myProcNo_[communicator]; return myProcNo_[communicator];
} }
//- The parent communicator
static label parent(const label communicator) static label parent(const label communicator)
{ {
return parentCommunicator_(communicator); return parentComm_(communicator);
} }
//- Process ID of given process index //- Process IDs within a given communicator
static List<int>& procID(label communicator) static List<int>& procID(const label communicator)
{ {
return procIDs_[communicator]; return procIDs_[communicator];
} }
@ -489,7 +501,7 @@ public:
return allWorlds_; return allWorlds_;
} }
//- worldID (index in allWorlds) of all processes //- The indices into allWorlds for all processes
static const labelList& worldIDs() noexcept static const labelList& worldIDs() noexcept
{ {
return worldIDs_; return worldIDs_;
@ -498,13 +510,13 @@ public:
//- My worldID //- My worldID
static label myWorldID() static label myWorldID()
{ {
return worldIDs_[myProcNo(0)]; return worldIDs_[myProcNo(globalComm)];
} }
//- My world //- My world
static const word& myWorld() static const word& myWorld()
{ {
return allWorlds()[myWorldID()]; return allWorlds_[worldIDs_[myProcNo(globalComm)]];
} }

View File

@ -42,9 +42,9 @@ Foam::UPstream::commsStruct::commsStruct() noexcept
Foam::UPstream::commsStruct::commsStruct Foam::UPstream::commsStruct::commsStruct
( (
const label above, const label above,
const labelList& below, const labelUList& below,
const labelList& allBelow, const labelUList& allBelow,
const labelList& allNotBelow const labelUList& allNotBelow
) )
: :
above_(above), above_(above),
@ -59,8 +59,8 @@ Foam::UPstream::commsStruct::commsStruct
const label nProcs, const label nProcs,
const label myProcID, const label myProcID,
const label above, const label above,
const labelList& below, const labelUList& below,
const labelList& allBelow const labelUList& allBelow
) )
: :
above_(above), above_(above),
@ -90,6 +90,17 @@ Foam::UPstream::commsStruct::commsStruct
} }
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::UPstream::commsStruct::clear()
{
above_ = -1;
below_.clear();
allBelow_.clear();
allNotBelow_.clear();
}
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
bool Foam::UPstream::commsStruct::operator==(const commsStruct& comm) const bool Foam::UPstream::commsStruct::operator==(const commsStruct& comm) const

View File

@ -58,10 +58,14 @@ extern int nTags_;
//- Free'd message tags //- Free'd message tags
extern DynamicList<int> freedTags_; extern DynamicList<int> freedTags_;
// Current communicators. First element will be MPI_COMM_WORLD // Current communicators, which may be allocated or predefined
// (eg, MPI_COMM_SELF, MPI_COMM_WORLD)
extern DynamicList<MPI_Comm> MPICommunicators_; extern DynamicList<MPI_Comm> MPICommunicators_;
// Groups associated with the currrent communicators.
extern DynamicList<MPI_Group> MPIGroups_; extern DynamicList<MPI_Group> MPIGroups_;
//- Fatal if comm is outside the allocated range
void checkCommunicator(const label comm, const label toProcNo); void checkCommunicator(const label comm, const label toProcNo);

View File

@ -30,9 +30,9 @@ License
#include "PstreamReduceOps.H" #include "PstreamReduceOps.H"
#include "PstreamGlobals.H" #include "PstreamGlobals.H"
#include "profilingPstream.H" #include "profilingPstream.H"
#include "int.H"
#include "SubList.H" #include "SubList.H"
#include "UPstreamWrapping.H" #include "UPstreamWrapping.H"
#include "int.H"
#include "collatedFileOperation.H" #include "collatedFileOperation.H"
#include <mpi.h> #include <mpi.h>
@ -293,41 +293,56 @@ bool Foam::UPstream::init(int& argc, char**& argv, const bool needsThread)
if (worldIndex != -1) if (worldIndex != -1)
{ {
// During startup, so worldComm == globalComm
wordList worlds(numprocs); wordList worlds(numprocs);
worlds[Pstream::myProcNo()] = world; worlds[Pstream::myProcNo(UPstream::globalComm)] = world;
Pstream::gatherList(worlds); Pstream::gatherList(worlds, UPstream::msgType(), UPstream::globalComm);
Pstream::broadcast(worlds);
// Compact // Compact
if (Pstream::master()) if (Pstream::master(UPstream::globalComm))
{ {
DynamicList<word> allWorlds(numprocs); DynamicList<word> worldNames(numprocs);
for (const word& world : worlds) worldIDs_.resize_nocopy(numprocs);
{
allWorlds.appendUniq(world);
}
allWorlds_ = std::move(allWorlds);
worldIDs_.setSize(numprocs);
forAll(worlds, proci) forAll(worlds, proci)
{ {
const word& world = worlds[proci]; const word& world = worlds[proci];
worldIDs_[proci] = allWorlds_.find(world);
worldIDs_[proci] = worldNames.find(world);
if (worldIDs_[proci] == -1)
{
worldIDs_[proci] = worldNames.size();
worldNames.push_back(world);
}
} }
allWorlds_.transfer(worldNames);
} }
Pstream::broadcasts(UPstream::worldComm, allWorlds_, worldIDs_); Pstream::broadcasts(UPstream::globalComm, allWorlds_, worldIDs_);
const label myWorldId =
worldIDs_[Pstream::myProcNo(UPstream::globalComm)];
DynamicList<label> subRanks; DynamicList<label> subRanks;
forAll(worlds, proci) forAll(worldIDs_, proci)
{ {
if (worlds[proci] == worlds[Pstream::myProcNo()]) if (worldIDs_[proci] == myWorldId)
{ {
subRanks.append(proci); subRanks.push_back(proci);
} }
} }
// Allocate new communicator 1 with parent 0 (= mpi_world) // Allocate new communicator with globalComm as its parent
const label subComm = allocateCommunicator(0, subRanks, true); const label subComm =
UPstream::allocateCommunicator
(
UPstream::globalComm, // parent
subRanks,
true
);
// Override worldComm // Override worldComm
UPstream::worldComm = subComm; UPstream::worldComm = subComm;
@ -337,11 +352,11 @@ bool Foam::UPstream::init(int& argc, char**& argv, const bool needsThread)
if (debug) if (debug)
{ {
// Check // Check
int subNProcs, subRank; int subNumProcs, subRank;
MPI_Comm_size MPI_Comm_size
( (
PstreamGlobals::MPICommunicators_[subComm], PstreamGlobals::MPICommunicators_[subComm],
&subNProcs &subNumProcs
); );
MPI_Comm_rank MPI_Comm_rank
( (
@ -351,19 +366,20 @@ bool Foam::UPstream::init(int& argc, char**& argv, const bool needsThread)
Pout<< "UPstream::init : in world:" << world Pout<< "UPstream::init : in world:" << world
<< " using local communicator:" << subComm << " using local communicator:" << subComm
<< " with procs:" << subNProcs << " rank " << subRank
<< " and rank:" << subRank << " of " << subNumProcs
<< endl; << endl;
} }
// Override Pout prefix (move to setParRun?) // Override Pout prefix (move to setParRun?)
Pout.prefix() = '[' + world + '/' + name(myProcNo(subComm)) + "] "; Pout.prefix() = '[' + world + '/' + name(myProcNo(subComm)) + "] ";
Perr.prefix() = '[' + world + '/' + name(myProcNo(subComm)) + "] "; Perr.prefix() = Pout.prefix();
} }
else else
{ {
// All processors use world 0 // All processors use world 0
worldIDs_.setSize(numprocs, 0); worldIDs_.resize_nocopy(numprocs);
worldIDs_ = 0;
} }
attachOurBuffers(); attachOurBuffers();
@ -490,10 +506,10 @@ void Foam::UPstream::allocatePstreamCommunicator
if (index == PstreamGlobals::MPIGroups_.size()) if (index == PstreamGlobals::MPIGroups_.size())
{ {
// Extend storage with dummy values // Extend storage with dummy values
MPI_Group newGroup = MPI_GROUP_NULL;
PstreamGlobals::MPIGroups_.append(newGroup);
MPI_Comm newComm = MPI_COMM_NULL; MPI_Comm newComm = MPI_COMM_NULL;
PstreamGlobals::MPICommunicators_.append(newComm); MPI_Group newGroup = MPI_GROUP_NULL;
PstreamGlobals::MPIGroups_.push_back(newGroup);
PstreamGlobals::MPICommunicators_.push_back(newComm);
} }
else if (index > PstreamGlobals::MPIGroups_.size()) else if (index > PstreamGlobals::MPIGroups_.size())
{ {
@ -505,34 +521,57 @@ void Foam::UPstream::allocatePstreamCommunicator
if (parentIndex == -1) if (parentIndex == -1)
{ {
// Allocate world communicator // Global communicator. Same as world communicator for single-world
if (index != UPstream::worldComm) if (index != UPstream::globalComm)
{ {
FatalErrorInFunction FatalErrorInFunction
<< "world communicator should always be index " << "world communicator should always be index "
<< UPstream::worldComm << Foam::exit(FatalError); << UPstream::globalComm
<< Foam::exit(FatalError);
} }
PstreamGlobals::MPICommunicators_[index] = MPI_COMM_WORLD; PstreamGlobals::MPICommunicators_[index] = MPI_COMM_WORLD;
MPI_Comm_group(MPI_COMM_WORLD, &PstreamGlobals::MPIGroups_[index]); MPI_Comm_group(MPI_COMM_WORLD, &PstreamGlobals::MPIGroups_[index]);
MPI_Comm_rank MPI_Comm_rank(MPI_COMM_WORLD, &myProcNo_[index]);
(
PstreamGlobals::MPICommunicators_[index],
&myProcNo_[index]
);
// Set the number of processes to the actual number // Set the number of ranks to the actual number
int numProcs; int numProcs;
MPI_Comm_size(PstreamGlobals::MPICommunicators_[index], &numProcs); MPI_Comm_size(MPI_COMM_WORLD, &numProcs);
//procIDs_[index] = identity(numProcs); //procIDs_[index] = identity(numProcs);
procIDs_[index].setSize(numProcs); procIDs_[index].resize_nocopy(numProcs);
forAll(procIDs_[index], i) forAll(procIDs_[index], i)
{ {
procIDs_[index][i] = i; procIDs_[index][i] = i;
} }
} }
else if (parentIndex == -2)
{
// Self communicator
PstreamGlobals::MPICommunicators_[index] = MPI_COMM_SELF;
MPI_Comm_group(MPI_COMM_SELF, &PstreamGlobals::MPIGroups_[index]);
MPI_Comm_rank(MPI_COMM_SELF, &myProcNo_[index]);
// Number of ranks is always 1 (self communicator)
#ifdef FULLDEBUG
int numProcs;
MPI_Comm_size(MPI_COMM_SELF, &numProcs);
if (numProcs != 1)
{
// Already finalized - this is an error
FatalErrorInFunction
<< "MPI_COMM_SELF had " << numProcs << " != 1 ranks!\n"
<< Foam::abort(FatalError);
}
#endif
procIDs_[index].resize_nocopy(1);
procIDs_[index] = 0;
}
else else
{ {
// Create new group // Create new group
@ -558,7 +597,7 @@ void Foam::UPstream::allocatePstreamCommunicator
( (
PstreamGlobals::MPICommunicators_[parentIndex], PstreamGlobals::MPICommunicators_[parentIndex],
PstreamGlobals::MPIGroups_[index], PstreamGlobals::MPIGroups_[index],
Pstream::msgType(), UPstream::msgType(),
&PstreamGlobals::MPICommunicators_[index] &PstreamGlobals::MPICommunicators_[index]
); );
#endif #endif
@ -593,16 +632,26 @@ void Foam::UPstream::allocatePstreamCommunicator
void Foam::UPstream::freePstreamCommunicator(const label communicator) void Foam::UPstream::freePstreamCommunicator(const label communicator)
{ {
if (communicator != 0) // Skip placeholders and pre-defined (not allocated) communicators
if (UPstream::debug)
{ {
if (PstreamGlobals::MPICommunicators_[communicator] != MPI_COMM_NULL) Pout<< "freePstreamCommunicator: " << communicator
<< " from " << PstreamGlobals::MPICommunicators_.size() << endl;
}
// Not touching the first two communicators (SELF, WORLD)
if (communicator > 1)
{
if (MPI_COMM_NULL != PstreamGlobals::MPICommunicators_[communicator])
{ {
// Free communicator. Sets communicator to MPI_COMM_NULL // Free communicator. Sets communicator to MPI_COMM_NULL
MPI_Comm_free(&PstreamGlobals::MPICommunicators_[communicator]); MPI_Comm_free(&PstreamGlobals::MPICommunicators_[communicator]);
} }
if (PstreamGlobals::MPIGroups_[communicator] != MPI_GROUP_NULL)
if (MPI_GROUP_NULL != PstreamGlobals::MPIGroups_[communicator])
{ {
// Free greoup. Sets group to MPI_GROUP_NULL // Free group. Sets group to MPI_GROUP_NULL
MPI_Group_free(&PstreamGlobals::MPIGroups_[communicator]); MPI_Group_free(&PstreamGlobals::MPIGroups_[communicator]);
} }
} }
@ -715,7 +764,7 @@ void Foam::UPstream::waitRequest(const label i)
profilingPstream::addWaitTime(); profilingPstream::addWaitTime();
// Push index onto free cache // Push index onto free cache
PstreamGlobals::freedRequests_.append(i); PstreamGlobals::freedRequests_.push_back(i);
if (debug) if (debug)
{ {
@ -766,69 +815,39 @@ bool Foam::UPstream::finishedRequest(const label i)
} }
int Foam::UPstream::allocateTag(const char* s) int Foam::UPstream::allocateTag(const char* const msg)
{ {
int tag; int tag;
if (PstreamGlobals::freedTags_.size()) if (PstreamGlobals::freedTags_.size())
{ {
tag = PstreamGlobals::freedTags_.remove(); tag = PstreamGlobals::freedTags_.back();
(void)PstreamGlobals::freedTags_.pop_back();
} }
else else
{ {
tag = PstreamGlobals::nTags_++; tag = ++PstreamGlobals::nTags_;
} }
if (debug) if (debug)
{ {
Pout<< "UPstream::allocateTag " Pout<< "UPstream::allocateTag";
<< s << " : tag:" << tag << endl; if (msg) Pout<< ' ' << msg;
Pout<< " : tag:" << tag << endl;
} }
return tag; return tag;
} }
int Foam::UPstream::allocateTag(const std::string& s) void Foam::UPstream::freeTag(const int tag, const char* const msg)
{
int tag;
if (PstreamGlobals::freedTags_.size())
{
tag = PstreamGlobals::freedTags_.remove();
}
else
{
tag = PstreamGlobals::nTags_++;
}
if (debug)
{
Pout<< "UPstream::allocateTag "
<< s.c_str() << " : tag:" << tag << endl;
}
return tag;
}
void Foam::UPstream::freeTag(const char* s, const int tag)
{ {
if (debug) if (debug)
{ {
Pout<< "UPstream::freeTag " Pout<< "UPstream::freeTag ";
<< s << " tag:" << tag << endl; if (msg) Pout<< ' ' << msg;
Pout<< " : tag:" << tag << endl;
} }
PstreamGlobals::freedTags_.append(tag); PstreamGlobals::freedTags_.push_back(tag);
}
void Foam::UPstream::freeTag(const std::string& s, const int tag)
{
if (debug)
{
Pout<< "UPstream::freeTag "
<< s.c_str() << " tag:" << tag << endl;
}
PstreamGlobals::freedTags_.append(tag);
} }

View File

@ -161,8 +161,8 @@ Foam::label Foam::multiWorldConnections::createCommunicator(const edge& worlds)
} }
} }
// Allocate new communicator with parent 0 (= world) // Allocate new communicator with global world
comm = UPstream::allocateCommunicator(0, subRanks, true); comm = UPstream::allocateCommunicator(UPstream::globalComm, subRanks, true);
if (debug & 2) if (debug & 2)
{ {
@ -234,10 +234,10 @@ void Foam::multiWorldConnections::createComms()
// Use MPI_COMM_WORLD // Use MPI_COMM_WORLD
const label oldWorldComm(Pstream::worldComm); const label oldWorldComm(UPstream::worldComm);
const label oldWarnComm(Pstream::warnComm); const label oldWarnComm(UPstream::warnComm);
Pstream::worldComm = 0; UPstream::worldComm = UPstream::globalComm;
Pstream::warnComm = Pstream::worldComm; UPstream::warnComm = UPstream::worldComm;
if (Pstream::parRun()) if (Pstream::parRun())
{ {