openfoam/src/OpenFOAM/global/fileOperations/masterUncollatedFileOperation/masterUncollatedFileOperation.C
Mark Olesen 18e0d7e4d6 ENH: bundle broadcasts (#2371)
- additional Pstream::broadcasts() method to serialize/deserialize
  multiple items.

- revoke the broadcast specialisations for std::string and List(s) and
  use a generic broadcasting template. In most cases, the previous
  specialisations would have required two broadcasts:
    (1) for the size
    (2) for the contiguous content.

  Now favour reduced communication over potential local (intermediate)
  storage that would have only benefited a few select cases.

ENH: refine PstreamBuffers access methods

- replace 'bool hasRecvData(label)' with 'label recvDataCount(label)'
  to recover the number of unconsumed receive bytes from specified
  processor.  Can use 'labelList recvDataCounts()' to recover the
  number of unconsumed receive bytes from all processor.

- additional peekRecvData() method (for transcribing contiguous data)

ENH: globalIndex whichProcID - check for isLocal first

- reasonable to assume that local items are searched for more
  frequently, so do preliminary check for isLocal before performing
  a more costly binary search of globalIndex offsets

ENH: masterUncollatedFileOperation - bundled scatter of status
2022-04-29 11:44:28 +02:00

2589 lines
67 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2017-2018 OpenFOAM Foundation
Copyright (C) 2019-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/>.
\*---------------------------------------------------------------------------*/
#include "masterUncollatedFileOperation.H"
#include "addToRunTimeSelectionTable.H"
#include "Pstream.H"
#include "Time.H"
#include "instant.H"
#include "IFstream.H"
#include "IListStream.H"
#include "masterOFstream.H"
#include "decomposedBlockData.H"
#include "registerSwitch.H"
#include "dummyISstream.H"
#include "SubList.H"
#include "unthreadedInitialise.H"
#include "bitSet.H"
/* * * * * * * * * * * * * * * Static Member Data * * * * * * * * * * * * * */
namespace Foam
{
namespace fileOperations
{
defineTypeNameAndDebug(masterUncollatedFileOperation, 0);
addToRunTimeSelectionTable
(
fileOperation,
masterUncollatedFileOperation,
word
);
float masterUncollatedFileOperation::maxMasterFileBufferSize
(
Foam::debug::floatOptimisationSwitch("maxMasterFileBufferSize", 1e9)
);
registerOptSwitch
(
"maxMasterFileBufferSize",
float,
masterUncollatedFileOperation::maxMasterFileBufferSize
);
// Mark as not needing threaded mpi
addNamedToRunTimeSelectionTable
(
fileOperationInitialise,
masterUncollatedFileOperationInitialise,
word,
masterUncollated
);
}
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::labelList Foam::fileOperations::masterUncollatedFileOperation::subRanks
(
const label n
)
{
labelList mainRanks(fileOperation::ioRanks());
if (mainRanks.empty())
{
return identity(n);
}
else
{
DynamicList<label> subRanks(n);
if (!mainRanks.found(0))
{
FatalErrorInFunction
<< "Rank 0 (master) should be in the IO ranks. Currently "
<< mainRanks << nl
<< exit(FatalError);
}
// The lowest numbered rank is the IO rank
const bitSet isIOrank(n, mainRanks);
for (label proci = Pstream::myProcNo(); proci >= 0; --proci)
{
if (isIOrank[proci])
{
// Found my master. Collect all processors with same master
subRanks.append(proci);
for
(
label rank = proci+1;
rank < n && !isIOrank[rank];
++rank
)
{
subRanks.append(rank);
}
break;
}
}
return subRanks;
}
}
Foam::word
Foam::fileOperations::masterUncollatedFileOperation::findInstancePath
(
const instantList& timeDirs,
const instant& t
)
{
// Note:
// - times will include constant (with value 0) as first element.
// For backwards compatibility make sure to find 0 in preference
// to constant.
// - list is sorted so could use binary search
forAllReverse(timeDirs, i)
{
if (t.equal(timeDirs[i].value()))
{
return timeDirs[i].name();
}
}
return word::null;
}
Foam::fileName
Foam::fileOperations::masterUncollatedFileOperation::filePathInfo
(
const bool checkGlobal,
const bool isFile,
const IOobject& io,
const bool search,
pathType& searchType,
word& procsDir,
word& newInstancePath
) const
{
procsDir = word::null;
newInstancePath = word::null;
if (io.instance().isAbsolute())
{
fileName objPath = io.instance()/io.name();
if (isFileOrDir(isFile, objPath))
{
searchType = fileOperation::ABSOLUTE;
return objPath;
}
else
{
searchType = fileOperation::NOTFOUND;
return fileName::null;
}
}
else
{
// 1. Check the writing fileName
fileName writePath(objectPath(io, io.headerClassName()));
if (isFileOrDir(isFile, writePath))
{
searchType = fileOperation::WRITEOBJECT;
return writePath;
}
// 2. Check processors/
if (io.time().processorCase())
{
refPtr<dirIndexList> pDirs(lookupProcessorsPath(io.objectPath()));
for (const dirIndex& dirIdx : pDirs())
{
const fileName& pDir = dirIdx.first();
fileName objPath =
processorsPath(io, io.instance(), pDir)
/io.name();
if (objPath != writePath && isFileOrDir(isFile, objPath))
{
searchType = dirIdx.second().first();
procsDir = pDir;
return objPath;
}
}
}
{
// 3. Check local
fileName localPath = io.objectPath();
if
(
localPath != writePath
&& isFileOrDir(isFile, localPath)
)
{
searchType = fileOperation::OBJECT;
return localPath;
}
}
// Any global checks
if
(
checkGlobal
&& io.time().processorCase()
&& (
io.instance() == io.time().system()
|| io.instance() == io.time().constant()
)
)
{
fileName parentPath =
io.rootPath()/io.time().globalCaseName()
/io.instance()/io.db().dbDir()/io.local()/io.name();
if (isFileOrDir(isFile, parentPath))
{
searchType = fileOperation::PARENTOBJECT;
return parentPath;
}
}
// Check for approximately same time. E.g. if time = 1e-2 and
// directory is 0.01 (due to different time formats)
const auto pathFnd = times_.cfind(io.time().path());
if (search && pathFnd.found())
{
newInstancePath = findInstancePath
(
*pathFnd(),
instant(io.instance())
);
if (newInstancePath.size() && newInstancePath != io.instance())
{
// 1. Try processors equivalent
refPtr<dirIndexList> pDirs
(
lookupProcessorsPath(io.objectPath())
);
for (const dirIndex& dirIdx : pDirs())
{
const fileName& pDir = dirIdx.first();
fileName fName
(
processorsPath(io, newInstancePath, pDir)
/io.name()
);
if (isFileOrDir(isFile, fName))
{
switch (dirIdx.second().first())
{
case fileOperation::PROCUNCOLLATED:
{
searchType =
fileOperation::PROCUNCOLLATEDINSTANCE;
}
break;
case fileOperation::PROCBASEOBJECT:
{
searchType = fileOperation::PROCBASEINSTANCE;
}
break;
case fileOperation::PROCOBJECT:
{
searchType = fileOperation::PROCINSTANCE;
}
break;
default:
break;
}
procsDir = pDir;
return fName;
}
}
// 2. Check local
fileName fName
(
io.rootPath()/io.caseName()
/newInstancePath/io.db().dbDir()/io.local()/io.name()
);
if (isFileOrDir(isFile, fName))
{
searchType = fileOperation::FINDINSTANCE;
return fName;
}
}
}
searchType = fileOperation::NOTFOUND;
return fileName::null;
}
}
Foam::fileName
Foam::fileOperations::masterUncollatedFileOperation::localObjectPath
(
const IOobject& io,
const pathType& searchType,
const word& procDir,
const word& instancePath
) const
{
// Replacement for IOobject::objectPath()
switch (searchType)
{
case fileOperation::ABSOLUTE:
{
return io.instance()/io.name();
}
break;
case fileOperation::OBJECT:
{
return io.path()/io.name();
}
break;
case fileOperation::WRITEOBJECT:
{
return objectPath(io, io.headerClassName());
}
break;
case fileOperation::PROCUNCOLLATED:
{
// Uncollated type, e.g. processor1
const word procName
(
"processor" + Foam::name(Pstream::myProcNo(Pstream::worldComm))
);
return
processorsPath
(
io,
io.instance(),
(
Pstream::parRun()
? procName
: procDir
)
)
/io.name();
}
break;
case fileOperation::PROCBASEOBJECT:
{
// Collated, e.g. processors4
return
processorsPath(io, io.instance(), procDir)
/io.name();
}
break;
case fileOperation::PROCOBJECT:
{
// Processors directory locally provided by the fileHandler itself
return
processorsPath(io, io.instance(), processorsDir(io))
/io.name();
}
break;
case fileOperation::PARENTOBJECT:
{
return
io.rootPath()/io.time().globalCaseName()
/io.instance()/io.db().dbDir()/io.local()/io.name();
}
break;
case fileOperation::FINDINSTANCE:
{
return
io.rootPath()/io.caseName()
/instancePath/io.db().dbDir()/io.local()/io.name();
}
break;
case fileOperation::PROCUNCOLLATEDINSTANCE:
{
// Uncollated type, e.g. processor1
const word procName
(
"processor"
+Foam::name(Pstream::myProcNo(Pstream::worldComm))
);
return
processorsPath
(
io,
instancePath,
(
Pstream::parRun()
? procName
: procDir
)
)
/io.name();
}
break;
case fileOperation::PROCBASEINSTANCE:
{
// Collated, e.g. processors4
return
processorsPath(io, instancePath, procDir)
/io.name();
}
break;
case fileOperation::PROCINSTANCE:
{
// Processors directory locally provided by the fileHandler itself
return
processorsPath(io, instancePath, processorsDir(io))
/io.name();
}
break;
case fileOperation::NOTFOUND:
{
return fileName::null;
}
break;
default:
{
NotImplemented;
return fileName::null;
}
}
}
bool Foam::fileOperations::masterUncollatedFileOperation::uniformFile
(
const fileNameList& filePaths
)
{
const fileName& object0 = filePaths[0];
for (label i = 1; i < filePaths.size(); i++)
{
if (filePaths[i] != object0)
{
return false;
}
}
return true;
}
void Foam::fileOperations::masterUncollatedFileOperation::readAndSend
(
const fileName& filePath,
const labelUList& procs,
PstreamBuffers& pBufs
)
{
IFstream ifs(filePath, IOstreamOption::BINARY);
if (!ifs.good())
{
FatalIOErrorInFunction(filePath)
<< "Cannot open file " << filePath
<< exit(FatalIOError);
}
if (debug)
{
Pout<< "masterUncollatedFileOperation::readAndSend :"
<< " compressed:" << bool(ifs.compression()) << " "
<< filePath << endl;
}
if (ifs.compression() == IOstreamOption::COMPRESSED)
{
// Could use Foam::fileSize, estimate uncompressed size (eg, 2x)
// and then string reserve followed by string assign...
// Uncompress and read file contents into a character buffer
const std::string buf
(
std::istreambuf_iterator<char>(ifs.stdStream()),
std::istreambuf_iterator<char>()
);
for (const label proci : procs)
{
UOPstream os(proci, pBufs);
os.write(buf.data(), buf.length());
}
if (debug)
{
Pout<< "masterUncollatedFileOperation::readStream :"
<< " From " << filePath << " sent " << buf.size()
<< " bytes" << endl;
}
}
else
{
const off_t count(Foam::fileSize(filePath));
// Read file contents into a character buffer
List<char> buf(static_cast<label>(count));
ifs.stdStream().read(buf.data(), count);
for (const label proci : procs)
{
UOPstream os(proci, pBufs);
os.write(buf.cdata(), count);
}
if (debug)
{
Pout<< "masterUncollatedFileOperation::readStream :"
<< " From " << filePath << " sent " << buf.size()
<< " bytes" << endl;
}
}
}
Foam::autoPtr<Foam::ISstream>
Foam::fileOperations::masterUncollatedFileOperation::read
(
IOobject& io,
const label comm,
const bool uniform, // on comms master only
const fileNameList& filePaths, // on comms master only
const boolList& procValid // on comms master and sub-ranks
)
{
autoPtr<ISstream> isPtr;
// const bool uniform = uniformFile(filePaths);
PstreamBuffers pBufs(comm, UPstream::commsTypes::nonBlocking);
if (Pstream::master(comm))
{
if (uniform)
{
if (procValid[0])
{
if (filePaths[0].empty())
{
FatalIOErrorInFunction(filePaths[0])
<< "cannot find file " << io.objectPath()
<< exit(FatalIOError);
}
DynamicList<label> validProcs(Pstream::nProcs(comm));
for (const int proci : Pstream::allProcs(comm))
{
if (procValid[proci])
{
validProcs.append(proci);
}
}
// Read on master and send to all processors
// (including master for simplicity)
if (debug)
{
Pout<< "masterUncollatedFileOperation::readStream :"
<< " For uniform file " << filePaths[0]
<< " sending to " << validProcs
<< " in comm:" << comm << endl;
}
readAndSend(filePaths[0], validProcs, pBufs);
}
}
else
{
if (procValid[0])
{
if (filePaths[0].empty())
{
FatalIOErrorInFunction(filePaths[0])
<< "cannot find file " << io.objectPath()
<< exit(FatalIOError);
}
// Open master
isPtr.reset(new IFstream(filePaths[0]));
// Read header
if (!io.readHeader(*isPtr))
{
FatalIOErrorInFunction(*isPtr)
<< "problem while reading header for object "
<< io.name() << exit(FatalIOError);
}
}
// Read sub-rank files
for (const int proci : Pstream::subProcs(comm))
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::readStream :"
<< " For processor " << proci
<< " opening " << filePaths[proci] << endl;
}
const fileName& fPath = filePaths[proci];
if (procValid[proci] && !fPath.empty())
{
// Note: handle compression ourselves since size cannot
// be determined without actually uncompressing
readAndSend(fPath, labelList(one{}, proci), pBufs);
}
}
}
}
labelList recvSizes;
pBufs.finishedSends(recvSizes);
// isPtr will be valid on master and will be the unbuffered
// IFstream. Else the information is in the PstreamBuffers (and
// the special case of a uniform file)
if (!isPtr)
{
if (procValid[Pstream::myProcNo(comm)])
{
// This processor needs to return something
UIPstream is(Pstream::masterNo(), pBufs);
List<char> buf(recvSizes[Pstream::masterNo()]);
if (!buf.empty())
{
is.read(buf.data(), buf.size());
}
if (debug)
{
Pout<< "masterUncollatedFileOperation::readStream :"
<< " Done reading " << buf.size() << " bytes" << endl;
}
// A local character buffer copy of the Pstream contents.
// Construct with same parameters (ASCII, current version)
// as the IFstream so that it has the same characteristics.
isPtr.reset(new IListStream(std::move(buf)));
// With the proper file name
isPtr->name() = filePaths[Pstream::myProcNo(comm)];
if (!io.readHeader(*isPtr))
{
FatalIOErrorInFunction(*isPtr)
<< "problem while reading header for object "
<< io.name() << exit(FatalIOError);
}
}
else
{
isPtr.reset(new dummyISstream());
}
}
return isPtr;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::fileOperations::masterUncollatedFileOperation::
masterUncollatedFileOperation
(
bool verbose
)
:
fileOperation
(
UPstream::allocateCommunicator
(
UPstream::worldComm,
subRanks(Pstream::nProcs())
)
),
myComm_(comm_)
{
verbose = (verbose && Foam::infoDetailLevel > 0);
if (verbose)
{
DetailInfo
<< "I/O : " << typeName
<< " (maxMasterFileBufferSize " << maxMasterFileBufferSize << ')'
<< endl;
}
if (IOobject::fileModificationChecking == IOobject::timeStampMaster)
{
if (verbose)
{
WarningInFunction
<< "Resetting fileModificationChecking to timeStamp" << endl;
}
IOobject::fileModificationChecking = IOobject::timeStamp;
}
else if (IOobject::fileModificationChecking == IOobject::inotifyMaster)
{
if (verbose)
{
WarningInFunction
<< "Resetting fileModificationChecking to inotify"
<< endl;
}
IOobject::fileModificationChecking = IOobject::inotify;
}
}
Foam::fileOperations::masterUncollatedFileOperation::
masterUncollatedFileOperation
(
const label comm,
bool verbose
)
:
fileOperation(comm),
myComm_(-1)
{
verbose = (verbose && Foam::infoDetailLevel > 0);
if (verbose)
{
DetailInfo
<< "I/O : " << typeName
<< " (maxMasterFileBufferSize " << maxMasterFileBufferSize << ')'
<< endl;
}
if (IOobject::fileModificationChecking == IOobject::timeStampMaster)
{
if (verbose)
{
WarningInFunction
<< "Resetting fileModificationChecking to timeStamp" << endl;
}
IOobject::fileModificationChecking = IOobject::timeStamp;
}
else if (IOobject::fileModificationChecking == IOobject::inotifyMaster)
{
if (verbose)
{
WarningInFunction
<< "Resetting fileModificationChecking to inotify"
<< endl;
}
IOobject::fileModificationChecking = IOobject::inotify;
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::fileOperations::masterUncollatedFileOperation::
~masterUncollatedFileOperation()
{
if (myComm_ != -1 && myComm_ != UPstream::worldComm)
{
UPstream::freeCommunicator(myComm_);
}
}
// * * * * * * * * * * * * * Filesystem Operations * * * * * * * * * * * * * //
bool Foam::fileOperations::masterUncollatedFileOperation::mkDir
(
const fileName& dir,
mode_t mode
) const
{
return masterOp<mode_t>
(
dir,
mkDirOp(mode),
Pstream::msgType(),
comm_
);
}
bool Foam::fileOperations::masterUncollatedFileOperation::chMod
(
const fileName& fName,
mode_t mode
) const
{
return masterOp<mode_t>
(
fName,
chModOp(mode),
Pstream::msgType(),
comm_
);
}
mode_t Foam::fileOperations::masterUncollatedFileOperation::mode
(
const fileName& fName,
const bool followLink
) const
{
return masterOp<mode_t>
(
fName,
modeOp(followLink),
Pstream::msgType(),
comm_
);
}
Foam::fileName::Type Foam::fileOperations::masterUncollatedFileOperation::type
(
const fileName& fName,
const bool followLink
) const
{
return fileName::Type
(
masterOp<label>
(
fName,
typeOp(followLink),
Pstream::msgType(),
comm_
)
);
}
bool Foam::fileOperations::masterUncollatedFileOperation::exists
(
const fileName& fName,
const bool checkGzip,
const bool followLink
) const
{
return masterOp<bool>
(
fName,
existsOp(checkGzip, followLink),
Pstream::msgType(),
comm_
);
}
bool Foam::fileOperations::masterUncollatedFileOperation::isDir
(
const fileName& fName,
const bool followLink
) const
{
return masterOp<bool>
(
fName,
isDirOp(followLink),
Pstream::msgType(),
comm_
);
}
bool Foam::fileOperations::masterUncollatedFileOperation::isFile
(
const fileName& fName,
const bool checkGzip,
const bool followLink
) const
{
return masterOp<bool>
(
fName,
isFileOp(checkGzip, followLink),
Pstream::msgType(),
comm_
);
}
off_t Foam::fileOperations::masterUncollatedFileOperation::fileSize
(
const fileName& fName,
const bool followLink
) const
{
return masterOp<off_t>
(
fName,
fileSizeOp(followLink),
Pstream::msgType(),
comm_
);
}
time_t Foam::fileOperations::masterUncollatedFileOperation::lastModified
(
const fileName& fName,
const bool followLink
) const
{
return masterOp<time_t>
(
fName,
lastModifiedOp(followLink),
Pstream::msgType(),
comm_
);
}
double Foam::fileOperations::masterUncollatedFileOperation::highResLastModified
(
const fileName& fName,
const bool followLink
) const
{
return masterOp<double>
(
fName,
highResLastModifiedOp(followLink),
Pstream::msgType(),
comm_
);
}
bool Foam::fileOperations::masterUncollatedFileOperation::mvBak
(
const fileName& fName,
const std::string& ext
) const
{
return masterOp<bool>
(
fName,
mvBakOp(ext),
Pstream::msgType(),
comm_
);
}
bool Foam::fileOperations::masterUncollatedFileOperation::rm
(
const fileName& fName
) const
{
return masterOp<bool>
(
fName,
rmOp(),
Pstream::msgType(),
comm_
);
}
bool Foam::fileOperations::masterUncollatedFileOperation::rmDir
(
const fileName& dir,
const bool silent
) const
{
return masterOp<bool>
(
dir,
rmDirOp(silent),
Pstream::msgType(),
comm_
);
}
Foam::fileNameList Foam::fileOperations::masterUncollatedFileOperation::readDir
(
const fileName& dir,
const fileName::Type type,
const bool filtergz,
const bool followLink
) const
{
return masterOp<fileNameList>
(
dir,
readDirOp(type, filtergz, followLink),
Pstream::msgType(),
comm_
);
}
bool Foam::fileOperations::masterUncollatedFileOperation::cp
(
const fileName& src,
const fileName& dst,
const bool followLink
) const
{
return masterOp<bool>
(
src,
dst,
cpOp(followLink),
Pstream::msgType(),
comm_
);
}
bool Foam::fileOperations::masterUncollatedFileOperation::ln
(
const fileName& src,
const fileName& dst
) const
{
return masterOp<bool>
(
src,
dst,
lnOp(),
Pstream::msgType(),
comm_
);
}
bool Foam::fileOperations::masterUncollatedFileOperation::mv
(
const fileName& src,
const fileName& dst,
const bool followLink
) const
{
return masterOp<bool>
(
src,
dst,
mvOp(followLink),
Pstream::msgType(),
comm_
);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::fileName Foam::fileOperations::masterUncollatedFileOperation::filePath
(
const bool checkGlobal,
const IOobject& io,
const word& typeName,
const bool search
) const
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::filePath :"
<< " objectPath:" << io.objectPath()
<< " checkGlobal:" << checkGlobal << endl;
}
// Now that we have an IOobject path use it to detect & cache
// processor directory naming
(void)lookupProcessorsPath(io.objectPath());
// Trigger caching of times
(void)findTimes(io.time().path(), io.time().constant());
// Determine master filePath and scatter
fileName objPath;
pathType searchType = NOTFOUND;
word procsDir;
word newInstancePath;
if (Pstream::master(comm_))
{
const bool oldParRun(Pstream::parRun(false));
// All masters search locally. Note that global objects might
// fail (except on master). This gets handled later on (in PARENTOBJECT)
objPath =
filePathInfo
(
checkGlobal,
true,
io,
search,
searchType,
procsDir,
newInstancePath
);
Pstream::parRun(oldParRun);
if (debug)
{
Pout<< "masterUncollatedFileOperation::filePath :"
<< " master objPath:" << objPath
<< " searchType:" << fileOperation::pathTypeNames_[searchType]
<< " procsDir:" << procsDir << " instance:" << newInstancePath
<< endl;
}
}
// Scatter the information about where the master found the object
// Note: use the worldComm to make sure all processors decide
// the same type. Only procsDir is allowed to differ; searchType
// and instance have to be same
{
int masterType(searchType);
Pstream::broadcasts(UPstream::worldComm, masterType, newInstancePath);
searchType = pathType(masterType);
}
if
(
checkGlobal
|| searchType == fileOperation::PARENTOBJECT
|| searchType == fileOperation::PROCBASEOBJECT
|| searchType == fileOperation::PROCBASEINSTANCE
|| io.local() == "uniform"
)
{
// Distribute master path. This makes sure it is seen as uniform
// and only gets read from the master.
Pstream::broadcasts(UPstream::worldComm, objPath, procsDir);
}
else
{
Pstream::broadcast(procsDir, comm_);
// Use the master type to determine if additional information is
// needed to construct the local equivalent
switch (searchType)
{
case fileOperation::PARENTOBJECT:
case fileOperation::PROCBASEOBJECT:
case fileOperation::PROCBASEINSTANCE:
{
// Already handled above
}
break;
case fileOperation::ABSOLUTE:
case fileOperation::WRITEOBJECT:
case fileOperation::PROCUNCOLLATED:
case fileOperation::PROCOBJECT:
case fileOperation::FINDINSTANCE:
case fileOperation::PROCUNCOLLATEDINSTANCE:
case fileOperation::PROCINSTANCE:
{
// Construct equivalent local path
objPath = localObjectPath
(
io,
searchType,
procsDir,
newInstancePath
);
}
break;
case fileOperation::OBJECT:
case fileOperation::NOTFOUND:
{
// Retest all processors separately since some processors might
// have the file and some not (e.g. lagrangian data)
objPath = masterOp<fileName>
(
io.objectPath(),
fileOrNullOp(true), // isFile=true
Pstream::msgType(),
comm_
);
}
break;
}
}
if (debug)
{
Pout<< "masterUncollatedFileOperation::filePath :"
<< " Returning from file searching:" << endl
<< " objectPath:" << io.objectPath() << endl
<< " filePath :" << objPath << endl << endl;
}
return objPath;
}
Foam::fileName Foam::fileOperations::masterUncollatedFileOperation::dirPath
(
const bool checkGlobal,
const IOobject& io,
const bool search
) const
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::dirPath :"
<< " objectPath:" << io.objectPath()
<< " checkGlobal:" << checkGlobal << endl;
}
// Now that we have an IOobject path use it to detect & cache
// processor directory naming
(void)lookupProcessorsPath(io.objectPath());
// Determine master dirPath and broadcast
fileName objPath;
pathType searchType = NOTFOUND;
word procsDir;
word newInstancePath;
if (Pstream::master(comm_))
{
const bool oldParRun(Pstream::parRun(false));
objPath = filePathInfo
(
checkGlobal,
false,
io,
search,
searchType,
procsDir,
newInstancePath
);
Pstream::parRun(oldParRun);
}
{
int masterType(searchType);
// Future?: comm_,
Pstream::broadcasts(UPstream::worldComm, masterType, newInstancePath);
searchType = pathType(masterType);
}
if
(
checkGlobal
|| searchType == fileOperation::PARENTOBJECT
|| searchType == fileOperation::PROCBASEOBJECT
|| searchType == fileOperation::PROCBASEINSTANCE
|| io.local() == "uniform"
)
{
// Distribute master path. This makes sure it is seen as uniform
// and only gets read from the master.
Pstream::broadcasts(UPstream::worldComm, objPath, procsDir);
}
else
{
Pstream::broadcast(procsDir, comm_);
// Use the master type to determine if additional information is
// needed to construct the local equivalent
switch (searchType)
{
case fileOperation::PARENTOBJECT:
case fileOperation::PROCBASEOBJECT:
case fileOperation::PROCBASEINSTANCE:
{
// Already handled above
}
break;
case fileOperation::ABSOLUTE:
case fileOperation::WRITEOBJECT:
case fileOperation::PROCUNCOLLATED:
case fileOperation::PROCOBJECT:
case fileOperation::FINDINSTANCE:
case fileOperation::PROCUNCOLLATEDINSTANCE:
case fileOperation::PROCINSTANCE:
{
// Construct equivalent local path
objPath = localObjectPath
(
io,
searchType,
procsDir,
newInstancePath
);
}
break;
case fileOperation::OBJECT:
case fileOperation::NOTFOUND:
{
// Retest all processors separately since some processors might
// have the file and some not (e.g. lagrangian data)
objPath = masterOp<fileName>
(
io.objectPath(),
fileOrNullOp(false), // isFile=false
Pstream::msgType(),
comm_
);
}
break;
}
}
if (debug)
{
Pout<< "masterUncollatedFileOperation::dirPath :"
<< " Returning from file searching:" << endl
<< " objectPath:" << io.objectPath() << endl
<< " filePath :" << objPath << endl << endl;
}
return objPath;
}
bool Foam::fileOperations::masterUncollatedFileOperation::exists
(
const dirIndexList& pDirs,
IOobject& io
) const
{
// Cut-down version of filePathInfo that does not look for
// different instance or parent directory
const bool isFile = !io.name().empty();
// Generate output filename for object
const fileName writePath(objectPath(io, word::null));
// 1. Test writing name for either directory or a (valid) file
if (isFileOrDir(isFile, writePath))
{
return true;
}
// 2. Check processors/
if (io.time().processorCase())
{
for (const dirIndex& dirIdx : pDirs)
{
const fileName& pDir = dirIdx.first();
fileName procPath =
processorsPath(io, io.instance(), pDir)
/io.name();
if (procPath != writePath && isFileOrDir(isFile, procPath))
{
return true;
}
}
}
// 3. Check local
fileName localPath = io.objectPath();
if (localPath != writePath && isFileOrDir(isFile, localPath))
{
return true;
}
return false;
}
Foam::IOobject
Foam::fileOperations::masterUncollatedFileOperation::findInstance
(
const IOobject& startIO,
const scalar startValue,
const word& stopInstance
) const
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::findInstance :"
<< " Starting searching for name:" << startIO.name()
<< " local:" << startIO.local()
<< " from instance:" << startIO.instance()
<< endl;
}
const Time& time = startIO.time();
IOobject io(startIO);
// Note: - if name is empty, just check the directory itself
// - check both for isFile and headerOk since the latter does a
// filePath so searches for the file.
// - check for an object with local file scope (so no looking up in
// parent directory in case of parallel)
refPtr<dirIndexList> pDirs(lookupProcessorsPath(io.objectPath()));
word foundInstance;
// if (Pstream::master(comm_))
if (Pstream::master(UPstream::worldComm))
{
const bool oldParRun(Pstream::parRun(false));
if (exists(pDirs, io))
{
foundInstance = io.instance();
}
Pstream::parRun(oldParRun);
}
// Do parallel early exit to avoid calling time.times()
// Pstream::broadcast(foundInstance, comm_);
Pstream::broadcast(foundInstance, UPstream::worldComm);
if (!foundInstance.empty())
{
io.instance() = foundInstance;
if (debug)
{
Pout<< "masterUncollatedFileOperation::findInstance :"
<< " for name:" << io.name() << " local:" << io.local()
<< " found starting instance:" << io.instance() << endl;
}
return io;
}
// Search back through the time directories to find the time
// closest to and lower than current time
instantList ts = time.times();
// if (Pstream::master(comm_))
if (Pstream::master(UPstream::worldComm))
{
const bool oldParRun(Pstream::parRun(false));
label instanceI;
for (instanceI = ts.size()-1; instanceI >= 0; --instanceI)
{
if (ts[instanceI].value() <= startValue)
{
break;
}
}
// continue searching from here
for (; instanceI >= 0; --instanceI)
{
// Shortcut: if actual directory is the timeName we've
// already tested it
if (ts[instanceI].name() == time.timeName())
{
continue;
}
io.instance() = ts[instanceI].name();
if (exists(pDirs, io))
{
foundInstance = io.instance();
if (debug)
{
Pout<< "masterUncollatedFileOperation::findInstance :"
<< " for name:" << io.name() << " local:" << io.local()
<< " found at:" << io.instance()
<< endl;
}
break;
}
// Check if hit minimum instance
if (ts[instanceI].name() == stopInstance)
{
if
(
startIO.readOpt() == IOobject::MUST_READ
|| startIO.readOpt() == IOobject::MUST_READ_IF_MODIFIED
)
{
if (io.name().empty())
{
FatalErrorInFunction
<< "Cannot find directory "
<< io.local() << " in times " << time.timeName()
<< " down to " << stopInstance
<< exit(FatalError);
}
else
{
FatalErrorInFunction
<< "Cannot find file \"" << io.name()
<< "\" in directory " << io.local()
<< " in times " << time.timeName()
<< " down to " << stopInstance
<< exit(FatalError);
}
}
foundInstance = io.instance();
if (debug)
{
Pout<< "masterUncollatedFileOperation::findInstance :"
<< " name:" << io.name() << " local:" << io.local()
<< " found at stopinstance:" << io.instance() << endl;
}
break;
}
}
if (foundInstance.empty())
{
// times() usually already includes the constant() so would
// have been checked above. Re-test if
// - times() is empty. Sometimes this can happen (e.g. decomposePar
// with collated)
// - times()[0] is not constant
if (!ts.size() || ts[0].name() != time.constant())
{
// Note. This needs to be a hard-coded constant, rather than the
// constant function of the time, because the latter points to
// the case constant directory in parallel cases
io.instance() = time.constant();
if (exists(pDirs, io))
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::findInstance :"
<< " name:" << io.name()
<< " local:" << io.local()
<< " found at:" << io.instance() << endl;
}
foundInstance = io.instance();
}
}
}
if (foundInstance.empty())
{
if
(
startIO.readOpt() == IOobject::MUST_READ
|| startIO.readOpt() == IOobject::MUST_READ_IF_MODIFIED
)
{
FatalErrorInFunction
<< "Cannot find file \"" << io.name() << "\" in directory "
<< io.local() << " in times " << startIO.instance()
<< " down to " << time.constant()
<< exit(FatalError);
}
else
{
foundInstance = time.constant();
}
}
Pstream::parRun(oldParRun);
}
// Pstream::broadcast(foundInstance, comm_);
Pstream::broadcast(foundInstance, UPstream::worldComm);
io.instance() = foundInstance;
if (debug)
{
Pout<< "masterUncollatedFileOperation::findInstance :"
<< " name:" << io.name() << " local:" << io.local()
<< " returning instance:" << io.instance() << endl;
}
return io;
}
Foam::fileNameList
Foam::fileOperations::masterUncollatedFileOperation::readObjects
(
const objectRegistry& db,
const fileName& instance,
const fileName& local,
word& newInstance
) const
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::readObjects :"
<< " db:" << db.objectPath()
<< " local:" << local << " instance:" << instance << endl;
}
fileNameList objectNames;
newInstance = word::null;
// Note: readObjects uses WORLD to make sure order of objects is the
// same everywhere
if (Pstream::master()) // comm_))
{
// Avoid fileOperation::readObjects from triggering parallel ops
// (through call to filePath which triggers parallel )
const bool oldParRun = UPstream::parRun(false);
//- Use non-time searching version
objectNames = fileOperation::readObjects
(
db,
instance,
local,
newInstance
);
if (newInstance.empty())
{
// Find similar time
// Copy of Time::findInstancePath. We want to avoid the
// parallel call to findTimes. Alternative is to have
// version of findInstancePath that takes instantList ...
const instantList timeDirs
(
fileOperation::findTimes
(
db.time().path(),
db.time().constant()
)
);
const instant t(instance);
forAllReverse(timeDirs, i)
{
if (t.equal(timeDirs[i].value()))
{
objectNames = fileOperation::readObjects
(
db,
timeDirs[i].name(), // newly found time
local,
newInstance
);
break;
}
}
}
UPstream::parRun(oldParRun); // Restore parallel state
}
// Future? comm_
Pstream::broadcasts(UPstream::worldComm, newInstance, objectNames);
if (debug)
{
Pout<< "masterUncollatedFileOperation::readObjects :"
<< " newInstance:" << newInstance
<< " objectNames:" << objectNames << endl;
}
return objectNames;
}
bool Foam::fileOperations::masterUncollatedFileOperation::readHeader
(
IOobject& io,
const fileName& fName,
const word& typeName
) const
{
bool ok = false;
if (debug)
{
Pout<< "masterUncollatedFileOperation::readHeader :" << endl
<< " objectPath:" << io.objectPath() << endl
<< " filePath :" << fName << endl;
}
// Get filePaths on world master
fileNameList filePaths(Pstream::nProcs(Pstream::worldComm));
filePaths[Pstream::myProcNo(Pstream::worldComm)] = fName;
Pstream::gatherList(filePaths, Pstream::msgType(), Pstream::worldComm);
bool uniform = uniformFile(filePaths);
Pstream::broadcast(uniform, UPstream::worldComm);
if (uniform)
{
if (Pstream::master(Pstream::worldComm))
{
if (!fName.empty())
{
IFstream is(fName);
if (is.good())
{
// Regular header or from decomposed data
ok = decomposedBlockData::readHeader(io, is);
}
}
}
Pstream::broadcasts
(
UPstream::worldComm,
ok,
io.headerClassName(),
io.note()
);
}
else
{
if (Pstream::nProcs(comm_) != Pstream::nProcs(Pstream::worldComm))
{
// Re-gather file paths on local master
filePaths.resize(Pstream::nProcs(comm_));
filePaths[Pstream::myProcNo(comm_)] = fName;
Pstream::gatherList(filePaths, Pstream::msgType(), comm_);
}
// Intermediate storage arrays (master only)
boolList result;
wordList headerClassName;
stringList note;
if (Pstream::master(comm_))
{
const label np = Pstream::nProcs(comm_);
result.resize(np, false);
headerClassName.resize(np);
note.resize(np);
forAll(filePaths, proci)
{
if (!filePaths[proci].empty())
{
if (proci > 0 && filePaths[proci] == filePaths[proci-1])
{
result[proci] = result[proci-1];
headerClassName[proci] = headerClassName[proci-1];
note[proci] = note[proci-1];
}
else
{
IFstream is(filePaths[proci]);
if (is.good())
{
result[proci] =
decomposedBlockData::readHeader(io, is);
headerClassName[proci] = io.headerClassName();
note[proci] = io.note();
}
}
}
}
}
// Is a more efficient scatter possible?
PstreamBuffers pBufs(comm_, UPstream::commsTypes::nonBlocking);
if (Pstream::master(comm_))
{
ok = result[0];
io.headerClassName() = headerClassName[0];
io.note() = note[0];
// Scatter to each proc
for (const int proci : pBufs.subProcs())
{
UOPstream os(proci, pBufs);
os << result[proci] << headerClassName[proci] << note[proci];
}
}
pBufs.finishedScatters();
if (!Pstream::master(comm_))
{
UIPstream is(Pstream::masterNo(), pBufs);
is >> ok >> io.headerClassName() >> io.note();
}
}
if (debug)
{
Pout<< "masterUncollatedFileOperation::readHeader :" << " ok:" << ok
<< " class:" << io.headerClassName() << endl;
}
return ok;
}
Foam::autoPtr<Foam::ISstream>
Foam::fileOperations::masterUncollatedFileOperation::readStream
(
regIOobject& io,
const fileName& fName,
const word& typeName,
const bool valid
) const
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::readStream :"
<< " object : " << io.name()
<< " global : " << io.global()
<< " fName : " << fName << " valid:" << valid << endl;
}
autoPtr<ISstream> isPtr;
bool isCollated = false;
IOobject headerIO(io);
// Detect collated format. This could be done on the local communicator
// but we do it on the master node only for now.
if (UPstream::master()) // comm_))
{
if (!fName.empty())
{
// This can happen in lagrangian field reading some processors
// have no file to read from. This will only happen when using
// normal writing since then the fName for the valid processors is
// processorDDD/<instance>/.. . In case of collocated writing
// the fName is already rewritten to processorsNN/.
isPtr.reset(new IFstream(fName));
if (isPtr->good())
{
// Read header data (on copy)
headerIO.readHeader(*isPtr);
isCollated = decomposedBlockData::isCollatedType(headerIO);
if (!isCollated && !Pstream::parRun())
{
// Short circuit: non-collated format. No parallel bits.
// Copy header and return.
if (debug)
{
Pout<< "masterUncollatedFileOperation::readStream :"
<< " For object : " << io.name()
<< " doing straight IFstream input from "
<< fName << endl;
}
io = headerIO;
return isPtr;
}
}
if (!isCollated)
{
// Close file. Reopened below.
isPtr.clear();
}
}
}
Pstream::broadcast(isCollated); //, comm_);
if (isCollated)
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::readStream :"
<< " For object : " << io.name()
<< " starting collating input from " << fName << endl;
}
// Analyse the file path (on (co)master) to see the processors type
// Note: this should really be part of filePath() which should return
// both file and index in file.
fileName path, procDir, local;
procRangeType group;
label nProcs;
splitProcessorPath(fName, path, procDir, local, group, nProcs);
if (!Pstream::parRun())
{
// Analyse the objectpath to find out the processor we're trying
// to access
label proci = detectProcessorPath(io.objectPath());
if (proci == -1)
{
FatalIOErrorInFunction(*isPtr)
<< "Could not detect processor number"
<< " from objectPath:" << io.objectPath()
<< exit(FatalIOError);
}
// The local rank (offset)
if (!group.empty())
{
proci = proci - group.start();
}
if (debug)
{
Pout<< "masterUncollatedFileOperation::readStream :"
<< " For object : " << io.name()
<< " starting input from block " << proci
<< " of " << isPtr->name() << endl;
}
return decomposedBlockData::readBlock(proci, *isPtr, io);
}
else
{
// Are we reading from single-master file ('processors256') or
// from multi-master files ('processors256_0-9')
label readComm = -1;
if (!group.empty())
{
readComm = comm_;
if (UPstream::master(comm_) && !isPtr && !fName.empty())
{
// In multi-master mode also open the file on the other
// masters
isPtr.reset(new IFstream(fName));
if (isPtr->good())
{
// Read header data (on copy)
IOobject headerIO(io);
headerIO.readHeader(*isPtr);
}
}
}
else
{
// Single master so read on world
readComm = UPstream::worldComm;
}
// Get size of file to determine communications type
bool bigSize = false;
if (Pstream::master()) //, comm_))
{
// TBD: handle multiple masters?
bigSize =
(
off_t(Foam::fileSize(fName))
> off_t(maxMasterFileBufferSize)
);
}
// Reduce (not broadcast)
// - if we have multiple master files (FUTURE)
reduce(bigSize, orOp<bool>()); //, UPstream::msgType(), comm_);
const UPstream::commsTypes myCommsType
(
bigSize
? UPstream::commsTypes::scheduled
: UPstream::commsTypes::nonBlocking
);
// Read my data
return decomposedBlockData::readBlocks
(
readComm,
fName,
isPtr,
io,
myCommsType
);
}
}
else
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::readStream :"
<< " For object : " << io.name()
<< " starting separated input from " << fName << endl;
}
if (io.global())
{
// Use worldComm. Note: should not really need to gather filePaths
// since we enforce sending from master anyway ...
fileNameList filePaths(Pstream::nProcs());
filePaths[Pstream::myProcNo()] = fName;
Pstream::gatherList(filePaths);
boolList procValid(UPstream::listGatherValues<bool>(valid));
// NB: local proc validity information required on sub-ranks too!
procValid.resize(Pstream::nProcs());
procValid[Pstream::myProcNo()] = valid;
return read(io, Pstream::worldComm, true, filePaths, procValid);
}
else
{
// Use local communicator
fileNameList filePaths(Pstream::nProcs(comm_));
filePaths[Pstream::myProcNo(comm_)] = fName;
Pstream::gatherList(filePaths, Pstream::msgType(), comm_);
boolList procValid(UPstream::listGatherValues<bool>(valid, comm_));
// NB: local proc validity information required on sub-ranks too!
procValid.resize(Pstream::nProcs(comm_));
procValid[Pstream::myProcNo(comm_)] = valid;
// Uniform in local comm
const bool uniform = uniformFile(filePaths);
return read(io, comm_, uniform, filePaths, procValid);
}
}
}
bool Foam::fileOperations::masterUncollatedFileOperation::read
(
regIOobject& io,
const bool masterOnly,
const IOstreamOption::streamFormat format,
const word& typeName
) const
{
bool ok = true;
if (io.globalObject())
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::read :"
<< " Reading global object " << io.name() << endl;
}
bool ok = false;
if (Pstream::master(UPstream::worldComm))
{
// Do master-only reading always.
const bool oldParRun = UPstream::parRun(false);
ok = io.readData(io.readStream(typeName));
io.close();
UPstream::parRun(oldParRun); // Restore parallel state
}
// Broadcast regIOobjects content
if (Pstream::parRun())
{
Pstream::broadcasts
(
UPstream::worldComm,
ok,
io.headerClassName(),
io.note()
);
if (Pstream::master(UPstream::worldComm))
{
OPBstream toAll
(
UPstream::masterNo(),
UPstream::worldComm,
format
);
bool okWrite = io.writeData(toAll);
ok = ok && okWrite;
}
else
{
IPBstream fromMaster
(
UPstream::masterNo(),
UPstream::worldComm,
format
);
ok = io.readData(fromMaster);
}
}
}
else
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::read :"
<< " Reading local object " << io.name() << endl;
}
ok = io.readData(io.readStream(typeName));
io.close();
}
return ok;
}
bool Foam::fileOperations::masterUncollatedFileOperation::writeObject
(
const regIOobject& io,
IOstreamOption streamOpt,
const bool valid
) const
{
fileName pathName(io.objectPath());
if (debug)
{
Pout<< "masterUncollatedFileOperation::writeObject :"
<< " io:" << pathName << " valid:" << valid << endl;
}
// Make sure to pick up any new times
setTime(io.time());
// Update meta-data for current state
const_cast<regIOobject&>(io).updateMetaData();
autoPtr<OSstream> osPtr(NewOFstream(pathName, streamOpt, valid));
OSstream& os = *osPtr;
// If any of these fail, return (leave error handling to Ostream class)
const bool ok =
(
os.good()
&& io.writeHeader(os)
&& io.writeData(os)
);
if (ok)
{
IOobject::writeEndDivider(os);
}
return ok;
}
Foam::instantList Foam::fileOperations::masterUncollatedFileOperation::findTimes
(
const fileName& directory,
const word& constantName
) const
{
const auto iter = times_.cfind(directory);
if (iter.found())
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::findTimes :"
<< " Found " << iter()->size() << " cached times" << endl;
}
return *iter();
}
else
{
instantList times;
if (Pstream::master()) // comm_))
{
// Do master-only reading always.
const bool oldParRun = UPstream::parRun(false);
times = fileOperation::findTimes(directory, constantName);
UPstream::parRun(oldParRun); // Restore parallel state
}
Pstream::broadcast(times); //, comm_);
// Note: do we also cache if no times have been found since it might
// indicate a directory that is being filled later on ...
instantList* tPtr = new instantList(std::move(times));
times_.set(directory, tPtr);
if (debug)
{
Pout<< "masterUncollatedFileOperation::findTimes :"
<< " Caching times:" << *tPtr << nl
<< " for directory:" << directory << endl;
}
return *tPtr;
}
}
void Foam::fileOperations::masterUncollatedFileOperation::setTime
(
const Time& tm
) const
{
if (tm.subCycling())
{
return;
}
// Mutable access to instantList for modification and sorting
// - cannot use auto type deduction here
HashPtrTable<instantList>::iterator iter = times_.find(tm.path());
if (iter.found())
{
instantList& times = *iter();
const instant timeNow(tm.value(), tm.timeName());
// Exclude constant when checking and sorting
const label skipConst =
(
(!times.empty() && times[0].name() == tm.constant())
? 1
: 0
);
if
(
findSortedIndex
(
SubList<instant>(times, times.size()-skipConst, skipConst),
timeNow
)
== -1
)
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::setTime :"
<< " Caching time " << tm.timeName()
<< " for case:" << tm.path() << endl;
}
times.append(timeNow);
SubList<instant> realTimes
(
times, times.size()-skipConst, skipConst
);
Foam::stableSort(realTimes);
}
}
fileOperation::setTime(tm);
}
Foam::autoPtr<Foam::ISstream>
Foam::fileOperations::masterUncollatedFileOperation::NewIFstream
(
const fileName& filePath
) const
{
autoPtr<ISstream> isPtr;
if (Pstream::parRun())
{
// Insert logic of filePath. We assume that if a file is absolute
// on the master it is absolute also on the sub-ranks etc.
fileNameList filePaths(Pstream::nProcs(Pstream::worldComm));
filePaths[Pstream::myProcNo(Pstream::worldComm)] = filePath;
Pstream::gatherList(filePaths, Pstream::msgType(), Pstream::worldComm);
PstreamBuffers pBufs
(
Pstream::commsTypes::nonBlocking,
Pstream::msgType(),
Pstream::worldComm
);
if (Pstream::master(Pstream::worldComm))
{
const bool uniform = uniformFile(filePaths);
if (uniform)
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::NewIFstream :"
<< " Opening global file " << filePath << endl;
}
readAndSend
(
filePath,
identity(Pstream::nProcs(Pstream::worldComm)-1, 1),
pBufs
);
}
else
{
for (const int proci : Pstream::subProcs(Pstream::worldComm))
{
readAndSend
(
filePaths[proci],
labelList(one{}, proci),
pBufs
);
}
}
}
labelList recvSizes;
pBufs.finishedSends(recvSizes);
if (Pstream::master(Pstream::worldComm))
{
// Read myself
isPtr.reset(new IFstream(filePaths[Pstream::masterNo()]));
}
else
{
if (debug)
{
Pout<< "masterUncollatedFileOperation::NewIFstream :"
<< " Reading " << filePath
<< " from processor " << Pstream::masterNo() << endl;
}
UIPstream is(Pstream::masterNo(), pBufs);
List<char> buf(recvSizes[Pstream::masterNo()]);
is.read(buf.data(), buf.size());
if (debug)
{
Pout<< "masterUncollatedFileOperation::NewIFstream :"
<< " Done reading " << buf.size() << " bytes" << endl;
}
// A local character buffer copy of the Pstream contents.
// Construct with same parameters (ASCII, current version)
// as the IFstream so that it has the same characteristics.
isPtr.reset(new IListStream(std::move(buf)));
// With the proper file name
isPtr->name() = filePath;
}
}
else
{
// Read myself
isPtr.reset(new IFstream(filePath));
}
return isPtr;
}
Foam::autoPtr<Foam::OSstream>
Foam::fileOperations::masterUncollatedFileOperation::NewOFstream
(
const fileName& pathName,
IOstreamOption streamOpt,
const bool valid
) const
{
return autoPtr<OSstream>
(
new masterOFstream
(
pathName,
streamOpt,
false, // append=false
valid
)
);
}
void Foam::fileOperations::masterUncollatedFileOperation::flush() const
{
fileOperation::flush();
times_.clear();
}
Foam::label Foam::fileOperations::masterUncollatedFileOperation::addWatch
(
const fileName& fName
) const
{
label watchFd = -1;
if (Pstream::master()) // comm_))
{
watchFd = monitor().addWatch(fName);
}
Pstream::broadcast(watchFd); //, comm_);
return watchFd;
}
bool Foam::fileOperations::masterUncollatedFileOperation::removeWatch
(
const label watchIndex
) const
{
bool ok = false;
if (Pstream::master()) // comm_))
{
ok = monitor().removeWatch(watchIndex);
}
Pstream::broadcast(ok); //, comm_);
return ok;
}
Foam::label Foam::fileOperations::masterUncollatedFileOperation::findWatch
(
const labelList& watchIndices,
const fileName& fName
) const
{
label index = -1;
if (Pstream::master()) // comm_))
{
forAll(watchIndices, i)
{
if (monitor().getFile(watchIndices[i]) == fName)
{
index = i;
break;
}
}
}
Pstream::broadcast(index); //, comm_);
return index;
}
void Foam::fileOperations::masterUncollatedFileOperation::addWatches
(
regIOobject& rio,
const fileNameList& files
) const
{
const labelList& watchIndices = rio.watchIndices();
DynamicList<label> newWatchIndices;
labelHashSet removedWatches(watchIndices);
for (const fileName& f : files)
{
const label index = findWatch(watchIndices, f);
if (index == -1)
{
newWatchIndices.append(addWatch(f));
}
else
{
// Existing watch
newWatchIndices.append(watchIndices[index]);
removedWatches.erase(index);
}
}
// Remove any unused watches
for (const label index : removedWatches)
{
removeWatch(watchIndices[index]);
}
rio.watchIndices() = newWatchIndices;
}
Foam::fileName Foam::fileOperations::masterUncollatedFileOperation::getFile
(
const label watchIndex
) const
{
fileName fName;
if (Pstream::master()) // comm_))
{
fName = monitor().getFile(watchIndex);
}
Pstream::broadcast(fName); //, comm_);
return fName;
}
void Foam::fileOperations::masterUncollatedFileOperation::updateStates
(
const bool masterOnly,
const bool syncPar
) const
{
if (Pstream::master()) // comm_))
{
monitor().updateStates(true, false);
}
}
Foam::fileMonitor::fileState
Foam::fileOperations::masterUncollatedFileOperation::getState
(
const label watchFd
) const
{
unsigned int state = fileMonitor::UNMODIFIED;
if (Pstream::master()) // comm_))
{
state = monitor().getState(watchFd);
}
Pstream::broadcast(state); //, comm_);
return fileMonitor::fileState(state);
}
void Foam::fileOperations::masterUncollatedFileOperation::setUnmodified
(
const label watchFd
) const
{
if (Pstream::master()) // comm_))
{
monitor().setUnmodified(watchFd);
}
}
// ************************************************************************* //