Files
openfoam/src/OpenFOAM/global/fileOperations/collatedFileOperation/collatedFileOperation.C
Mark Olesen fe17c8ad5f CONFIG: improve prefix matching for system libraries (#1607)
- missed detection of system libraries when installed with multiarch
  paths like /usr/lib/x86_64-linux-gnu

CONFIG: improve handling of group/user config files (#928)

- changed bashrc handling of FOAM_CONFIG_NOUSER to use
  FOAM_CONFIG_MODE instead. Propagate into foamEtcFile to make this
  a stickier control.

  This change allows better control, but also enables cluster
  installations to define their own value within the OpenFOAM prefs.sh
  file to prevent users accidentally mis-configuring things if
  necessary.

- remove undocumented handling of an (a)ll mode in foamEtcFile to
  avoid potential pitfalls.

- add support for FOAM_CONFIG_ETC handling.
  This allows injection of an extra search layer when finding
  project etc files

ENH: improvements to foamConfigurePaths (#928)

- handle FOAM_CONFIG_ETC implicitly, or explicitly with the new
  -etc option.

STYLE: more explicit wording in foamConfigurePaths usage (#1602)

- document that an absolute path (eg, -scotch-path) overrides/ignores
  the equivalent ThirdParty setting (eg, -scotch)

- longer options -system-compiler and -third-compiler for -system
  and -third, respectively. Clearer as to their purpose.

- adjust the location sanity check to look for META-INFO directory.
2020-03-16 12:03:58 +01:00

711 lines
18 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
-------------------------------------------------------------------------------
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 "collatedFileOperation.H"
#include "addToRunTimeSelectionTable.H"
#include "Pstream.H"
#include "Time.H"
#include "threadedCollatedOFstream.H"
#include "decomposedBlockData.H"
#include "registerSwitch.H"
#include "masterOFstream.H"
#include "OFstream.H"
/* * * * * * * * * * * * * * * Static Member Data * * * * * * * * * * * * * */
namespace Foam
{
namespace fileOperations
{
defineTypeNameAndDebug(collatedFileOperation, 0);
addToRunTimeSelectionTable
(
fileOperation,
collatedFileOperation,
word
);
float collatedFileOperation::maxThreadFileBufferSize
(
debug::floatOptimisationSwitch("maxThreadFileBufferSize", 1e9)
);
registerOptSwitch
(
"maxThreadFileBufferSize",
float,
collatedFileOperation::maxThreadFileBufferSize
);
// Mark as needing threaded mpi
addNamedToRunTimeSelectionTable
(
fileOperationInitialise,
collatedFileOperationInitialise,
word,
collated
);
}
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::labelList Foam::fileOperations::collatedFileOperation::ioRanks()
{
labelList ioRanks;
string ioRanksString(getEnv("FOAM_IORANKS"));
if (!ioRanksString.empty())
{
IStringStream is(ioRanksString);
is >> ioRanks;
}
return ioRanks;
}
bool Foam::fileOperations::collatedFileOperation::isMasterRank
(
const label proci
)
const
{
if (Pstream::parRun())
{
return Pstream::master(comm_);
}
else if (ioRanks_.size())
{
// Found myself in IO rank
return ioRanks_.found(proci);
}
else
{
// Assume all in single communicator
return proci == 0;
}
}
bool Foam::fileOperations::collatedFileOperation::appendObject
(
const regIOobject& io,
const fileName& pathName,
IOstream::streamFormat fmt,
IOstream::versionNumber ver,
IOstream::compressionType cmp
) const
{
// Append to processors/ file
label proci = detectProcessorPath(io.objectPath());
if (debug)
{
Pout<< "collatedFileOperation::writeObject :"
<< " For local object : " << io.name()
<< " appending processor " << proci
<< " data to " << pathName << endl;
}
if (proci == -1)
{
FatalErrorInFunction
<< "Not a valid processor path " << pathName
<< exit(FatalError);
}
const bool isMaster = isMasterRank(proci);
// Determine the local rank if the pathName is a per-rank one
label localProci = proci;
{
fileName path, procDir, local;
label groupStart, groupSize, nProcs;
splitProcessorPath
(
pathName,
path,
procDir,
local,
groupStart,
groupSize,
nProcs
);
if (groupSize > 0 && groupStart != -1)
{
localProci = proci-groupStart;
}
}
// Create string from all data to write
string buf;
{
OStringStream os(fmt, ver);
if (isMaster)
{
if (!io.writeHeader(os))
{
return false;
}
}
// Write the data to the Ostream
if (!io.writeData(os))
{
return false;
}
if (isMaster)
{
IOobject::writeEndDivider(os);
}
buf = os.str();
}
// Note: cannot do append + compression. This is a limitation
// of ogzstream (or rather most compressed formats)
OFstream os
(
pathName,
IOstream::BINARY,
ver,
IOstream::UNCOMPRESSED, // no compression
!isMaster
);
if (!os.good())
{
FatalIOErrorInFunction(os)
<< "Cannot open for appending"
<< exit(FatalIOError);
}
if (isMaster)
{
IOobject::writeBanner(os)
<< "FoamFile\n{\n"
<< " version " << os.version() << ";\n"
<< " format " << os.format() << ";\n"
<< " class " << decomposedBlockData::typeName
<< ";\n"
<< " location " << pathName << ";\n"
<< " object " << pathName.name() << ";\n"
<< "}" << nl;
IOobject::writeDivider(os) << nl;
}
// Write data
UList<char> slice
(
const_cast<char*>(buf.data()),
label(buf.size())
);
os << nl << "// Processor" << localProci << nl << slice << nl;
return os.good();
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::fileOperations::collatedFileOperation::collatedFileOperation
(
bool verbose
)
:
masterUncollatedFileOperation
(
(
ioRanks().size()
? UPstream::allocateCommunicator
(
UPstream::worldComm,
subRanks(Pstream::nProcs())
)
: UPstream::worldComm
),
false
),
myComm_(comm_),
writer_(maxThreadFileBufferSize, comm_),
nProcs_(Pstream::nProcs()),
ioRanks_(ioRanks())
{
verbose = (verbose && Foam::infoDetailLevel > 0);
if (verbose)
{
Info<< "I/O : " << typeName
<< " (maxThreadFileBufferSize " << maxThreadFileBufferSize
<< ')' << endl;
if (maxThreadFileBufferSize == 0)
{
Info<< " Threading not activated "
"since maxThreadFileBufferSize = 0." << nl
<< " Writing may run slowly for large file sizes."
<< endl;
}
else
{
Info<< " Threading activated "
"since maxThreadFileBufferSize > 0." << nl
<< " Requires large enough buffer to collect all data"
" or thread support " << nl
<< " enabled in MPI. If thread support cannot be "
"enabled, deactivate" << nl
<< " threading by setting maxThreadFileBufferSize "
"to 0 in" << nl
<< " OpenFOAM etc/controlDict"
<< endl;
}
if (ioRanks_.size())
{
// Print a bit of information
stringList ioRanks(Pstream::nProcs());
if (Pstream::master(comm_))
{
ioRanks[Pstream::myProcNo()] = hostName()+"."+name(pid());
}
Pstream::gatherList(ioRanks);
Info<< " IO nodes:" << nl;
for (const string& ranks : ioRanks)
{
if (!ranks.empty())
{
Info<< " " << ranks << nl;
}
}
}
if
(
regIOobject::fileModificationChecking
== regIOobject::inotifyMaster
)
{
WarningInFunction
<< "Resetting fileModificationChecking to inotify" << endl;
}
if
(
regIOobject::fileModificationChecking
== regIOobject::timeStampMaster
)
{
WarningInFunction
<< "Resetting fileModificationChecking to timeStamp" << endl;
}
}
}
Foam::fileOperations::collatedFileOperation::collatedFileOperation
(
const label comm,
const labelList& ioRanks,
const word& typeName,
bool verbose
)
:
masterUncollatedFileOperation(comm, false),
myComm_(-1),
writer_(maxThreadFileBufferSize, comm),
nProcs_(Pstream::nProcs()),
ioRanks_(ioRanks)
{
verbose = (verbose && Foam::infoDetailLevel > 0);
if (verbose)
{
Info<< "I/O : " << typeName
<< " (maxThreadFileBufferSize " << maxThreadFileBufferSize
<< ')' << endl;
if (maxThreadFileBufferSize == 0)
{
Info<< " Threading not activated "
"since maxThreadFileBufferSize = 0." << nl
<< " Writing may run slowly for large file sizes."
<< endl;
}
else
{
Info<< " Threading activated "
"since maxThreadFileBufferSize > 0." << nl
<< " Requires large enough buffer to collect all data"
" or thread support " << nl
<< " enabled in MPI. If thread support cannot be "
"enabled, deactivate" << nl
<< " threading by setting maxThreadFileBufferSize "
"to 0 in the OpenFOAM etc/controlDict" << nl
<< endl;
}
if
(
regIOobject::fileModificationChecking
== regIOobject::inotifyMaster
)
{
WarningInFunction
<< "Resetting fileModificationChecking to inotify" << endl;
}
if
(
regIOobject::fileModificationChecking
== regIOobject::timeStampMaster
)
{
WarningInFunction
<< "Resetting fileModificationChecking to timeStamp" << endl;
}
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::fileOperations::collatedFileOperation::~collatedFileOperation()
{
if (myComm_ != -1 && myComm_ != UPstream::worldComm)
{
UPstream::freeCommunicator(myComm_);
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::fileName Foam::fileOperations::collatedFileOperation::objectPath
(
const IOobject& io,
const word& typeName
) const
{
// Replacement for objectPath
if (io.time().processorCase())
{
return masterUncollatedFileOperation::localObjectPath
(
io,
fileOperation::PROCOBJECT,
"dummy", // not used for processorsobject
io.instance()
);
}
else
{
return masterUncollatedFileOperation::localObjectPath
(
io,
fileOperation::OBJECT,
word::null,
io.instance()
);
}
}
bool Foam::fileOperations::collatedFileOperation::writeObject
(
const regIOobject& io,
IOstream::streamFormat fmt,
IOstream::versionNumber ver,
IOstream::compressionType cmp,
const bool valid
) const
{
const Time& tm = io.time();
const fileName& inst = io.instance();
if (inst.isAbsolute() || !tm.processorCase())
{
mkDir(io.path());
fileName pathName(io.objectPath());
if (debug)
{
Pout<< "collatedFileOperation::writeObject :"
<< " For object : " << io.name()
<< " falling back to master-only output to " << io.path()
<< endl;
}
masterOFstream os
(
pathName,
fmt,
ver,
cmp,
false,
valid
);
// 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;
}
else
{
// Construct the equivalent processors/ directory
fileName path(processorsPath(io, inst, processorsDir(io)));
mkDir(path);
fileName pathName(path/io.name());
if (io.global())
{
if (debug)
{
Pout<< "collatedFileOperation::writeObject :"
<< " For global object : " << io.name()
<< " falling back to master-only output to " << pathName
<< endl;
}
masterOFstream os
(
pathName,
fmt,
ver,
cmp,
false,
valid
);
// 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;
}
else if (!Pstream::parRun())
{
// Special path for e.g. decomposePar. Append to
// processorsDDD/ file
if (debug)
{
Pout<< "collatedFileOperation::writeObject :"
<< " For object : " << io.name()
<< " appending to " << pathName << endl;
}
return appendObject(io, pathName, fmt, ver, cmp);
}
else
{
// Re-check static maxThreadFileBufferSize variable to see
// if needs to use threading
bool useThread = (maxThreadFileBufferSize > 0);
if (debug)
{
Pout<< "collatedFileOperation::writeObject :"
<< " For object : " << io.name()
<< " starting collating output to " << pathName
<< " useThread:" << useThread << endl;
}
if (!useThread)
{
writer_.waitAll();
}
threadedCollatedOFstream os
(
writer_,
pathName,
fmt,
ver,
cmp,
useThread
);
// If any of these fail, return (leave error handling to Ostream
// class)
if (!os.good())
{
return false;
}
if (Pstream::master(comm_) && !io.writeHeader(os))
{
return false;
}
// Write the data to the Ostream
if (!io.writeData(os))
{
return false;
}
if (Pstream::master(comm_))
{
IOobject::writeEndDivider(os);
}
return true;
}
}
}
void Foam::fileOperations::collatedFileOperation::flush() const
{
if (debug)
{
Pout<< "collatedFileOperation::flush : clearing and waiting for thread"
<< endl;
}
masterUncollatedFileOperation::flush();
// Wait for thread to finish (note: also removes thread)
writer_.waitAll();
}
Foam::word Foam::fileOperations::collatedFileOperation::processorsDir
(
const fileName& fName
) const
{
if (Pstream::parRun())
{
const List<int>& procs(UPstream::procID(comm_));
word procDir(processorsBaseDir+Foam::name(Pstream::nProcs()));
if (procs.size() != Pstream::nProcs())
{
procDir +=
+ "_"
+ Foam::name(procs[0])
+ "-"
+ Foam::name(procs.last());
}
return procDir;
}
else
{
word procDir(processorsBaseDir+Foam::name(nProcs_));
if (ioRanks_.size())
{
// Detect current processor number
label proci = detectProcessorPath(fName);
if (proci != -1)
{
// Find lowest io rank
label minProc = 0;
label maxProc = nProcs_-1;
forAll(ioRanks_, i)
{
if (ioRanks_[i] >= nProcs_)
{
break;
}
else if (ioRanks_[i] <= proci)
{
minProc = ioRanks_[i];
}
else
{
maxProc = ioRanks_[i]-1;
break;
}
}
procDir +=
+ "_"
+ Foam::name(minProc)
+ "-"
+ Foam::name(maxProc);
}
}
return procDir;
}
}
Foam::word Foam::fileOperations::collatedFileOperation::processorsDir
(
const IOobject& io
) const
{
return processorsDir(io.objectPath());
}
void Foam::fileOperations::collatedFileOperation::setNProcs(const label nProcs)
{
nProcs_ = nProcs;
if (debug)
{
Pout<< "collatedFileOperation::setNProcs :"
<< " Setting number of processors to " << nProcs_ << endl;
}
}
// ************************************************************************* //