mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
1231 lines
30 KiB
C
1231 lines
30 KiB
C
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
|
\\ / O peration |
|
|
\\ / A nd | Copyright (C) 2019 OpenCFD Ltd.
|
|
\\/ M anipulation |
|
|
-------------------------------------------------------------------------------
|
|
| Copyright (C) 2017-2018 OpenFOAM Foundation
|
|
-------------------------------------------------------------------------------
|
|
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 "fileOperation.H"
|
|
#include "uncollatedFileOperation.H"
|
|
#include "regIOobject.H"
|
|
#include "argList.H"
|
|
#include "HashSet.H"
|
|
#include "objectRegistry.H"
|
|
#include "decomposedBlockData.H"
|
|
#include "polyMesh.H"
|
|
#include "registerSwitch.H"
|
|
#include "Time.H"
|
|
|
|
/* * * * * * * * * * * * * * * Static Member Data * * * * * * * * * * * * * */
|
|
|
|
namespace Foam
|
|
{
|
|
autoPtr<fileOperation> fileOperation::fileHandlerPtr_;
|
|
|
|
defineTypeNameAndDebug(fileOperation, 0);
|
|
defineRunTimeSelectionTable(fileOperation, word);
|
|
|
|
word fileOperation::defaultFileHandler
|
|
(
|
|
debug::optimisationSwitches().lookupOrAddDefault<word>
|
|
(
|
|
"fileHandler",
|
|
//Foam::fileOperations::uncollatedFileOperation::typeName,
|
|
"uncollated",
|
|
keyType::LITERAL
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
Foam::word Foam::fileOperation::processorsBaseDir = "processors";
|
|
|
|
const Foam::Enum<Foam::fileOperation::pathType>
|
|
Foam::fileOperation::pathTypeNames_
|
|
({
|
|
{ fileOperation::NOTFOUND, "notFound" },
|
|
{ fileOperation::ABSOLUTE, "absolute" },
|
|
{ fileOperation::OBJECT, "objectPath" },
|
|
{ fileOperation::WRITEOBJECT, "writeObject" },
|
|
{ fileOperation::PROCUNCOLLATED, "uncollatedProc" },
|
|
{ fileOperation::PROCBASEOBJECT, "globalProc" },
|
|
{ fileOperation::PROCOBJECT, "localProc" },
|
|
{ fileOperation::PARENTOBJECT, "parentObjectPath" },
|
|
{ fileOperation::FINDINSTANCE, "findInstance" },
|
|
{ fileOperation::PROCUNCOLLATEDINSTANCE, "uncollatedProcInstance" },
|
|
{ fileOperation::PROCBASEINSTANCE, "globalProcInstance" },
|
|
{ fileOperation::PROCINSTANCE, "localProcInstance" }
|
|
});
|
|
|
|
|
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
|
|
|
Foam::fileMonitor& Foam::fileOperation::monitor() const
|
|
{
|
|
if (!monitorPtr_.valid())
|
|
{
|
|
monitorPtr_.reset
|
|
(
|
|
new fileMonitor
|
|
(
|
|
regIOobject::fileModificationChecking == IOobject::inotify
|
|
|| regIOobject::fileModificationChecking == IOobject::inotifyMaster
|
|
)
|
|
);
|
|
}
|
|
return *monitorPtr_;
|
|
}
|
|
|
|
|
|
Foam::instantList Foam::fileOperation::sortTimes
|
|
(
|
|
const fileNameList& dirEntries,
|
|
const word& constantName
|
|
)
|
|
{
|
|
// Initialise instant list
|
|
instantList Times(dirEntries.size() + 1);
|
|
label nTimes = 0;
|
|
|
|
// Check for "constant"
|
|
bool haveConstant = false;
|
|
forAll(dirEntries, i)
|
|
{
|
|
if (dirEntries[i] == constantName)
|
|
{
|
|
Times[nTimes].value() = 0;
|
|
Times[nTimes].name() = dirEntries[i];
|
|
nTimes++;
|
|
haveConstant = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Read and parse all the entries in the directory
|
|
forAll(dirEntries, i)
|
|
{
|
|
scalar timeValue;
|
|
if (readScalar(dirEntries[i], timeValue))
|
|
{
|
|
Times[nTimes].value() = timeValue;
|
|
Times[nTimes].name() = dirEntries[i];
|
|
nTimes++;
|
|
}
|
|
}
|
|
|
|
// Reset the length of the times list
|
|
Times.setSize(nTimes);
|
|
|
|
if (haveConstant)
|
|
{
|
|
if (nTimes > 2)
|
|
{
|
|
std::sort(&Times[1], Times.end(), instant::less());
|
|
}
|
|
}
|
|
else if (nTimes > 1)
|
|
{
|
|
std::sort(&Times[0], Times.end(), instant::less());
|
|
}
|
|
|
|
return Times;
|
|
}
|
|
|
|
|
|
void Foam::fileOperation::mergeTimes
|
|
(
|
|
const instantList& extraTimes,
|
|
const word& constantName,
|
|
instantList& times
|
|
)
|
|
{
|
|
if (extraTimes.size())
|
|
{
|
|
bool haveConstant =
|
|
(
|
|
times.size() > 0
|
|
&& times[0].name() == constantName
|
|
);
|
|
|
|
bool haveExtraConstant =
|
|
(
|
|
extraTimes.size() > 0
|
|
&& extraTimes[0].name() == constantName
|
|
);
|
|
|
|
// Combine times
|
|
instantList combinedTimes(times.size()+extraTimes.size());
|
|
label sz = 0;
|
|
label extrai = 0;
|
|
if (haveExtraConstant)
|
|
{
|
|
extrai = 1;
|
|
if (!haveConstant)
|
|
{
|
|
combinedTimes[sz++] = extraTimes[0]; // constant
|
|
}
|
|
}
|
|
forAll(times, i)
|
|
{
|
|
combinedTimes[sz++] = times[i];
|
|
}
|
|
for (; extrai < extraTimes.size(); extrai++)
|
|
{
|
|
combinedTimes[sz++] = extraTimes[extrai];
|
|
}
|
|
combinedTimes.setSize(sz);
|
|
times.transfer(combinedTimes);
|
|
|
|
// Sort
|
|
if (times.size() > 1)
|
|
{
|
|
label starti = 0;
|
|
if (times[0].name() == constantName)
|
|
{
|
|
starti = 1;
|
|
}
|
|
std::sort(×[starti], times.end(), instant::less());
|
|
|
|
// Filter out duplicates
|
|
label newi = starti+1;
|
|
for (label i = newi; i < times.size(); i++)
|
|
{
|
|
if (times[i].value() != times[i-1].value())
|
|
{
|
|
if (newi != i)
|
|
{
|
|
times[newi] = times[i];
|
|
}
|
|
newi++;
|
|
}
|
|
}
|
|
|
|
times.setSize(newi);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool Foam::fileOperation::isFileOrDir(const bool isFile, const fileName& f)
|
|
{
|
|
return
|
|
(isFile && Foam::isFile(f))
|
|
|| (!isFile && Foam::isDir(f));
|
|
}
|
|
|
|
|
|
Foam::tmpNrc<Foam::fileOperation::dirIndexList>
|
|
Foam::fileOperation::lookupAndCacheProcessorsPath
|
|
(
|
|
const fileName& fName,
|
|
const bool syncPar
|
|
) const
|
|
{
|
|
// If path is local to a processor (e.g. contains 'processor2')
|
|
// find the corresponding actual processor directory (e.g. 'processors4')
|
|
// and index (2)
|
|
|
|
fileName path;
|
|
fileName pDir;
|
|
fileName local;
|
|
label gStart;
|
|
label gSz;
|
|
label numProcs;
|
|
label proci =
|
|
splitProcessorPath(fName, path, pDir, local, gStart, gSz, numProcs);
|
|
|
|
if (proci != -1)
|
|
{
|
|
const fileName procPath(path/pDir);
|
|
|
|
HashTable<dirIndexList>::const_iterator iter =
|
|
procsDirs_.find(procPath);
|
|
|
|
if (iter != procsDirs_.end())
|
|
{
|
|
return iter();
|
|
}
|
|
|
|
// Read all directories to see any beginning with processor
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
DynamicList<dirIndex> procDirs;
|
|
|
|
// Note: use parallel synchronised reading so cache will be same
|
|
// order on all processors
|
|
fileNameList dirNames(readDir(path, fileName::Type::DIRECTORY));
|
|
|
|
// Extract info from processorsDDD or processorDDD:
|
|
// - highest processor number
|
|
// - directory+offset containing data for proci
|
|
label maxProc = -1;
|
|
|
|
forAll(dirNames, i)
|
|
{
|
|
const fileName& dirN = dirNames[i];
|
|
|
|
// Analyse directory name
|
|
fileName rp, rd, rl;
|
|
label rStart, rSize, rNum;
|
|
label readProci =
|
|
splitProcessorPath(dirN, rp, rd, rl, rStart, rSize, rNum);
|
|
maxProc = max(maxProc, readProci);
|
|
|
|
if (proci == readProci)
|
|
{
|
|
// Found "processorDDD". No need for index.
|
|
procDirs.append
|
|
(
|
|
dirIndex
|
|
(
|
|
dirN,
|
|
Tuple2<pathType, label>(PROCUNCOLLATED, -1)
|
|
)
|
|
);
|
|
}
|
|
else if (proci >= rStart && proci < rStart+rSize)
|
|
{
|
|
// "processorsDDD_start-end"
|
|
// Found the file that contains the data for proci
|
|
procDirs.append
|
|
(
|
|
dirIndex
|
|
(
|
|
dirN,
|
|
Tuple2<pathType, label>(PROCOBJECT, proci-rStart)
|
|
)
|
|
);
|
|
}
|
|
if (rNum != -1)
|
|
{
|
|
// Direct detection of processorsDDD
|
|
maxProc = rNum-1;
|
|
|
|
if (rStart == -1)
|
|
{
|
|
// "processorsDDD"
|
|
procDirs.append
|
|
(
|
|
dirIndex
|
|
(
|
|
dirN,
|
|
Tuple2<pathType, label>(PROCBASEOBJECT, proci)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if (!Pstream::parRun())
|
|
{
|
|
// If (as a side effect) we found the number of decompositions
|
|
// use it
|
|
if (maxProc != -1)
|
|
{
|
|
const_cast<fileOperation&>(*this).setNProcs(maxProc+1);
|
|
}
|
|
}
|
|
|
|
if
|
|
(
|
|
(syncPar && returnReduce(procDirs.size(), sumOp<label>()))
|
|
|| (!syncPar && procDirs.size())
|
|
)
|
|
{
|
|
procsDirs_.insert(procPath, procDirs);
|
|
|
|
if (debug)
|
|
{
|
|
Pout<< "fileOperation::lookupProcessorsPath : For:" << procPath
|
|
<< " detected:" << procDirs << endl;
|
|
}
|
|
|
|
// Make sure to return a reference
|
|
return procsDirs_[procPath];
|
|
}
|
|
}
|
|
return tmpNrc<dirIndexList>(new dirIndexList(0, dirIndex()));
|
|
}
|
|
|
|
|
|
Foam::tmpNrc<Foam::fileOperation::dirIndexList>
|
|
Foam::fileOperation::lookupProcessorsPath(const fileName& fName) const
|
|
{
|
|
// Use parallel synchronisation
|
|
return lookupAndCacheProcessorsPath(fName, true);
|
|
}
|
|
|
|
|
|
bool Foam::fileOperation::exists(IOobject& io) const
|
|
{
|
|
// Generate output filename for object
|
|
fileName objPath(objectPath(io, word::null));
|
|
|
|
// Test for either directory or a (valid) file & IOobject
|
|
bool ok;
|
|
if (io.name().empty())
|
|
{
|
|
ok = isDir(objPath);
|
|
}
|
|
else
|
|
{
|
|
ok =
|
|
isFile(objPath)
|
|
&& io.typeHeaderOk<IOList<label>>(false);// object with local scope
|
|
}
|
|
|
|
if (!ok)
|
|
{
|
|
// Re-test with searched for objectPath. This is for backwards
|
|
// compatibility
|
|
fileName originalPath(filePath(io.objectPath()));
|
|
if (originalPath != objPath)
|
|
{
|
|
// Test for either directory or a (valid) file & IOobject
|
|
if (io.name().empty())
|
|
{
|
|
ok = isDir(originalPath);
|
|
}
|
|
else
|
|
{
|
|
ok =
|
|
isFile(originalPath)
|
|
&& io.typeHeaderOk<IOList<label>>(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
|
|
|
Foam::fileOperation::fileOperation(label comm)
|
|
:
|
|
comm_(comm)
|
|
{}
|
|
|
|
|
|
Foam::autoPtr<Foam::fileOperation> Foam::fileOperation::New
|
|
(
|
|
const word& handlerType,
|
|
bool verbose
|
|
)
|
|
{
|
|
if (debug)
|
|
{
|
|
InfoInFunction << "Constructing fileHandler" << endl;
|
|
}
|
|
|
|
auto cstrIter = wordConstructorTablePtr_->cfind(handlerType);
|
|
|
|
if (!cstrIter.found())
|
|
{
|
|
FatalErrorInFunction
|
|
<< "Unknown fileHandler type "
|
|
<< handlerType << nl << nl
|
|
<< "Valid fileHandler types :" << endl
|
|
<< wordConstructorTablePtr_->sortedToc()
|
|
<< abort(FatalError);
|
|
}
|
|
|
|
return autoPtr<fileOperation>(cstrIter()(verbose));
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
|
|
|
Foam::fileName Foam::fileOperation::objectPath
|
|
(
|
|
const IOobject& io,
|
|
const word& typeName
|
|
) const
|
|
{
|
|
return io.objectPath();
|
|
}
|
|
|
|
|
|
bool Foam::fileOperation::writeObject
|
|
(
|
|
const regIOobject& io,
|
|
IOstream::streamFormat fmt,
|
|
IOstream::versionNumber ver,
|
|
IOstream::compressionType cmp,
|
|
const bool valid
|
|
) const
|
|
{
|
|
if (valid)
|
|
{
|
|
fileName pathName(io.objectPath());
|
|
|
|
mkDir(pathName.path());
|
|
|
|
autoPtr<Ostream> osPtr
|
|
(
|
|
NewOFstream
|
|
(
|
|
pathName,
|
|
fmt,
|
|
ver,
|
|
cmp
|
|
)
|
|
);
|
|
|
|
if (!osPtr.valid())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Ostream& os = osPtr();
|
|
|
|
// If any of these fail, return (leave error handling to Ostream class)
|
|
if (!os.good())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!io.writeHeader(os))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Write the data to the Ostream
|
|
if (!io.writeData(os))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
IOobject::writeEndDivider(os);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
Foam::fileName Foam::fileOperation::filePath(const fileName& fName) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Pout<< "fileOperation::filePath :" << " fName:" << fName << endl;
|
|
}
|
|
|
|
fileName path;
|
|
fileName pDir;
|
|
fileName local;
|
|
label gStart;
|
|
label gSz;
|
|
label numProcs;
|
|
label proci =
|
|
splitProcessorPath(fName, path, pDir, local, gStart, gSz, numProcs);
|
|
|
|
if (numProcs != -1)
|
|
{
|
|
WarningInFunction << "Filename is already adapted:" << fName << endl;
|
|
}
|
|
|
|
// Give preference to processors variant
|
|
if (proci != -1)
|
|
{
|
|
// Get all processor directories
|
|
tmpNrc<dirIndexList> procDirs(lookupProcessorsPath(fName));
|
|
forAll(procDirs(), i)
|
|
{
|
|
const fileName& procDir = procDirs()[i].first();
|
|
|
|
fileName collatedName(path/procDir/local);
|
|
if (exists(collatedName))
|
|
{
|
|
if (debug)
|
|
{
|
|
Pout<< "fileOperation::filePath : " << collatedName << endl;
|
|
}
|
|
return collatedName;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (exists(fName))
|
|
{
|
|
if (debug)
|
|
{
|
|
Pout<< "fileOperation::filePath : " << fName << endl;
|
|
}
|
|
return fName;
|
|
}
|
|
else
|
|
{
|
|
if (debug)
|
|
{
|
|
Pout<< "fileOperation::filePath : Not found" << endl;
|
|
}
|
|
return fileName::null;
|
|
}
|
|
}
|
|
|
|
|
|
Foam::label Foam::fileOperation::addWatch(const fileName& fName) const
|
|
{
|
|
return monitor().addWatch(fName);
|
|
}
|
|
|
|
|
|
bool Foam::fileOperation::removeWatch(const label watchIndex) const
|
|
{
|
|
return monitor().removeWatch(watchIndex);
|
|
}
|
|
|
|
|
|
Foam::label Foam::fileOperation::findWatch
|
|
(
|
|
const labelList& watchIndices,
|
|
const fileName& fName
|
|
) const
|
|
{
|
|
forAll(watchIndices, i)
|
|
{
|
|
if (getFile(watchIndices[i]) == fName)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
void Foam::fileOperation::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::fileOperation::getFile(const label watchIndex) const
|
|
{
|
|
return monitor().getFile(watchIndex);
|
|
}
|
|
|
|
|
|
void Foam::fileOperation::updateStates
|
|
(
|
|
const bool masterOnly,
|
|
const bool syncPar
|
|
) const
|
|
{
|
|
monitor().updateStates(masterOnly, Pstream::parRun());
|
|
}
|
|
|
|
|
|
Foam::fileMonitor::fileState Foam::fileOperation::getState
|
|
(
|
|
const label watchFd
|
|
) const
|
|
{
|
|
return monitor().getState(watchFd);
|
|
}
|
|
|
|
|
|
void Foam::fileOperation::setUnmodified(const label watchFd) const
|
|
{
|
|
monitor().setUnmodified(watchFd);
|
|
}
|
|
|
|
|
|
Foam::instantList Foam::fileOperation::findTimes
|
|
(
|
|
const fileName& directory,
|
|
const word& constantName
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Pout<< "fileOperation::findTimes : Finding times in directory "
|
|
<< directory << endl;
|
|
}
|
|
|
|
// Read directory entries into a list
|
|
fileNameList dirEntries
|
|
(
|
|
Foam::readDir
|
|
(
|
|
directory,
|
|
fileName::DIRECTORY
|
|
)
|
|
);
|
|
|
|
instantList times = sortTimes(dirEntries, constantName);
|
|
|
|
|
|
// Get all processor directories
|
|
tmpNrc<dirIndexList> procDirs(lookupProcessorsPath(directory));
|
|
forAll(procDirs(), i)
|
|
{
|
|
const fileName& procDir = procDirs()[i].first();
|
|
fileName collDir(processorsPath(directory, procDir));
|
|
if (!collDir.empty() && collDir != directory)
|
|
{
|
|
fileNameList extraEntries
|
|
(
|
|
Foam::readDir
|
|
(
|
|
collDir,
|
|
fileName::DIRECTORY
|
|
)
|
|
);
|
|
mergeTimes
|
|
(
|
|
sortTimes(extraEntries, constantName),
|
|
constantName,
|
|
times
|
|
);
|
|
}
|
|
}
|
|
|
|
if (debug)
|
|
{
|
|
Pout<< "fileOperation::findTimes : Found times:" << times << endl;
|
|
}
|
|
return times;
|
|
}
|
|
|
|
|
|
Foam::IOobject Foam::fileOperation::findInstance
|
|
(
|
|
const IOobject& startIO,
|
|
const scalar startValue,
|
|
const word& stopInstance
|
|
) const
|
|
{
|
|
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)
|
|
|
|
if (exists(io))
|
|
{
|
|
if (debug)
|
|
{
|
|
InfoInFunction
|
|
<< "Found exact match for \"" << io.name()
|
|
<< "\" in " << io.instance()/io.local()
|
|
<< endl;
|
|
}
|
|
|
|
return io;
|
|
}
|
|
|
|
// Search back through the time directories to find the time
|
|
// closest to and lower than current time
|
|
|
|
instantList ts = time.times();
|
|
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() == startIO.instance()
|
|
&& ts[instanceI].name() != stopInstance
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
io.instance() = ts[instanceI].name();
|
|
if (exists(io))
|
|
{
|
|
if (debug)
|
|
{
|
|
InfoInFunction
|
|
<< "Found exact match for \"" << io.name()
|
|
<< "\" in " << io.instance()/io.local()
|
|
<< endl;
|
|
}
|
|
|
|
return io;
|
|
}
|
|
|
|
// Check if hit minimum instance
|
|
if (ts[instanceI].name() == stopInstance)
|
|
{
|
|
if (debug)
|
|
{
|
|
InfoInFunction
|
|
<< "Hit stopInstance " << stopInstance << endl;
|
|
}
|
|
|
|
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 " << startIO.instance()
|
|
<< " down to " << stopInstance
|
|
<< exit(FatalError);
|
|
}
|
|
else
|
|
{
|
|
FatalErrorInFunction
|
|
<< "Cannot find file \"" << io.name()
|
|
<< "\" in directory " << io.local()
|
|
<< " in times " << startIO.instance()
|
|
<< " down to " << stopInstance
|
|
<< exit(FatalError);
|
|
}
|
|
}
|
|
|
|
return io;
|
|
}
|
|
}
|
|
|
|
// 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(io))
|
|
{
|
|
if (debug)
|
|
{
|
|
InfoInFunction
|
|
<< "Found constant match for \"" << io.name()
|
|
<< "\" in " << io.instance()/io.local()
|
|
<< endl;
|
|
}
|
|
return io;
|
|
}
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
return io;
|
|
}
|
|
|
|
|
|
Foam::fileNameList Foam::fileOperation::readObjects
|
|
(
|
|
const objectRegistry& db,
|
|
const fileName& instance,
|
|
const fileName& local,
|
|
word& newInstance
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Pout<< "fileOperation::readObjects :"
|
|
<< " db:" << db.objectPath()
|
|
<< " instance:" << instance << endl;
|
|
}
|
|
|
|
fileName path(db.path(instance, db.dbDir()/local));
|
|
|
|
newInstance = word::null;
|
|
fileNameList objectNames;
|
|
|
|
if (Foam::isDir(path))
|
|
{
|
|
newInstance = instance;
|
|
objectNames = Foam::readDir(path, fileName::FILE);
|
|
}
|
|
else
|
|
{
|
|
// Get processors equivalent of path
|
|
fileName procsPath(filePath(path));
|
|
|
|
if (!procsPath.empty())
|
|
{
|
|
newInstance = instance;
|
|
objectNames = Foam::readDir(procsPath, fileName::FILE);
|
|
}
|
|
}
|
|
return objectNames;
|
|
}
|
|
|
|
|
|
void Foam::fileOperation::setNProcs(const label nProcs)
|
|
{}
|
|
|
|
|
|
Foam::label Foam::fileOperation::nProcs
|
|
(
|
|
const fileName& dir,
|
|
const fileName& local
|
|
) const
|
|
{
|
|
label nProcs = 0;
|
|
if (Pstream::master(comm_))
|
|
{
|
|
fileNameList dirNames(Foam::readDir(dir, fileName::Type::DIRECTORY));
|
|
|
|
// Detect any processorsDDD or processorDDD
|
|
label maxProc = -1;
|
|
forAll(dirNames, i)
|
|
{
|
|
const fileName& dirN = dirNames[i];
|
|
|
|
fileName path, pDir, local;
|
|
label start, size, n;
|
|
maxProc = max
|
|
(
|
|
maxProc,
|
|
splitProcessorPath(dirN, path, pDir, local, start, size, n)
|
|
);
|
|
if (n != -1)
|
|
{
|
|
// Direct detection of processorsDDD
|
|
maxProc = n-1;
|
|
break;
|
|
}
|
|
}
|
|
nProcs = maxProc+1;
|
|
|
|
|
|
if (nProcs == 0 && Foam::isDir(dir/processorsBaseDir))
|
|
{
|
|
fileName pointsFile
|
|
(
|
|
dir
|
|
/processorsBaseDir
|
|
/"constant"
|
|
/local
|
|
/polyMesh::meshSubDir
|
|
/"points"
|
|
);
|
|
|
|
if (Foam::isFile(pointsFile))
|
|
{
|
|
nProcs = decomposedBlockData::numBlocks(pointsFile);
|
|
}
|
|
else
|
|
{
|
|
WarningInFunction << "Cannot read file " << pointsFile
|
|
<< " to determine the number of decompositions."
|
|
<< " Returning 1" << endl;
|
|
}
|
|
}
|
|
}
|
|
Pstream::scatter(nProcs, Pstream::msgType(), comm_);
|
|
return nProcs;
|
|
}
|
|
|
|
|
|
void Foam::fileOperation::flush() const
|
|
{
|
|
if (debug)
|
|
{
|
|
Pout<< "fileOperation::flush : clearing processor directories cache"
|
|
<< endl;
|
|
}
|
|
procsDirs_.clear();
|
|
}
|
|
|
|
|
|
Foam::fileName Foam::fileOperation::processorsCasePath
|
|
(
|
|
const IOobject& io,
|
|
const word& procsDir
|
|
) const
|
|
{
|
|
return io.rootPath()/io.time().globalCaseName()/procsDir;
|
|
}
|
|
|
|
|
|
Foam::fileName Foam::fileOperation::processorsPath
|
|
(
|
|
const IOobject& io,
|
|
const word& instance,
|
|
const word& procsDir
|
|
) const
|
|
{
|
|
return
|
|
processorsCasePath(io, procsDir)
|
|
/instance
|
|
/io.db().dbDir()
|
|
/io.local();
|
|
}
|
|
|
|
|
|
Foam::fileName Foam::fileOperation::processorsPath
|
|
(
|
|
const fileName& dir,
|
|
const word& procsDir
|
|
) const
|
|
{
|
|
// Check if directory is processorDDD
|
|
word caseName(dir.name());
|
|
|
|
std::string::size_type pos = caseName.find("processor");
|
|
if (pos == 0)
|
|
{
|
|
if (caseName.size() <= 9 || caseName[9] == 's')
|
|
{
|
|
WarningInFunction << "Directory " << dir
|
|
<< " does not end in old-style processorDDD" << endl;
|
|
}
|
|
|
|
return dir.path()/procsDir;
|
|
}
|
|
|
|
return fileName::null;
|
|
}
|
|
|
|
|
|
Foam::label Foam::fileOperation::splitProcessorPath
|
|
(
|
|
const fileName& objectPath,
|
|
fileName& path,
|
|
fileName& procDir,
|
|
fileName& local,
|
|
|
|
label& groupStart,
|
|
label& groupSize,
|
|
|
|
label& nProcs
|
|
)
|
|
{
|
|
path.clear();
|
|
procDir.clear();
|
|
local.clear();
|
|
|
|
// Potentially detected start of number of processors in local group
|
|
groupStart = -1;
|
|
groupSize = 0;
|
|
|
|
// Potentially detected number of processors
|
|
nProcs = -1;
|
|
|
|
// Search for processor at start of line or /processor
|
|
std::string::size_type pos = objectPath.find("processor");
|
|
if (pos == string::npos)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// "processorDDD"
|
|
// "processorsNNN"
|
|
// "processorsNNN_AA-BB"
|
|
|
|
|
|
if (pos > 0 && objectPath[pos-1] != '/')
|
|
{
|
|
// Directory not starting with "processor" e.g. "somenamewithprocessor"
|
|
return -1;
|
|
}
|
|
|
|
procDir = objectPath;
|
|
|
|
// Strip leading directory
|
|
if (pos > 0)
|
|
{
|
|
path = objectPath.substr(0, pos-1);
|
|
procDir = objectPath.substr(pos);
|
|
}
|
|
|
|
// Strip trailing local directory
|
|
pos = procDir.find('/');
|
|
if (pos != string::npos)
|
|
{
|
|
local = procDir.substr(pos+1);
|
|
procDir = procDir.substr(0, pos);
|
|
}
|
|
|
|
// Now procDir is e.g.
|
|
// - processor0
|
|
// - processors0
|
|
// - processorBananas
|
|
|
|
// Look for number after "processor"
|
|
|
|
fileName f(procDir.substr(9));
|
|
|
|
if (f.size() && f[0] == 's')
|
|
{
|
|
// "processsorsNNN"
|
|
|
|
f = f.substr(1);
|
|
|
|
// Detect "processorsNNN_AA-BB"
|
|
{
|
|
std::string::size_type fromStart = f.find("_");
|
|
std::string::size_type toStart = f.find("-");
|
|
if (fromStart != string::npos && toStart != string::npos)
|
|
{
|
|
string nProcsName(f.substr(0, fromStart));
|
|
string fromName(f.substr(fromStart+1, toStart-(fromStart+1)));
|
|
string toName(f.substr(toStart+1));
|
|
|
|
label groupEnd = -1;
|
|
if
|
|
(
|
|
Foam::read(fromName.c_str(), groupStart)
|
|
&& Foam::read(toName.c_str(), groupEnd)
|
|
&& Foam::read(nProcsName.c_str(), nProcs)
|
|
)
|
|
{
|
|
groupSize = groupEnd-groupStart+1;
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Detect "processorsN"
|
|
label n;
|
|
if (Foam::read(f.c_str(), n))
|
|
{
|
|
nProcs = n;
|
|
}
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
// Detect "processorN"
|
|
label proci;
|
|
if (Foam::read(f.c_str(), proci))
|
|
{
|
|
return proci;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Foam::label Foam::fileOperation::detectProcessorPath(const fileName& fName)
|
|
{
|
|
fileName path, pDir, local;
|
|
label start, size, nProcs;
|
|
return splitProcessorPath(fName, path, pDir, local, start, size, nProcs);
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
|
|
|
|
const Foam::fileOperation& Foam::fileHandler()
|
|
{
|
|
if (!fileOperation::fileHandlerPtr_.valid())
|
|
{
|
|
word handler(getEnv("FOAM_FILEHANDLER"));
|
|
|
|
if (handler.empty())
|
|
{
|
|
handler = fileOperation::defaultFileHandler;
|
|
}
|
|
|
|
fileOperation::fileHandlerPtr_ = fileOperation::New(handler, true);
|
|
}
|
|
|
|
return *fileOperation::fileHandlerPtr_;
|
|
}
|
|
|
|
|
|
void Foam::fileHandler(autoPtr<fileOperation>& newHandler)
|
|
{
|
|
if
|
|
(
|
|
newHandler.valid() && fileOperation::fileHandlerPtr_.valid()
|
|
&& newHandler->type() == fileOperation::fileHandlerPtr_->type()
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
fileOperation::fileHandlerPtr_.clear();
|
|
|
|
if (newHandler.valid())
|
|
{
|
|
fileOperation::fileHandlerPtr_ = std::move(newHandler);
|
|
}
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|