BUG: processorMeshes removeFiles does not remove collated (fixes #2607)

ENH: extend rmDir to handle removal of empty directories only

- recursively remove directories that only contain other directories
  but no other contents. Treats dead links as non-content.
This commit is contained in:
Mark Olesen 2022-10-05 16:24:46 +02:00
parent 779a2ca084
commit d5cdc60a54
17 changed files with 313 additions and 224 deletions

View File

@ -48,6 +48,14 @@ Description
using namespace Foam;
// Create named file with some dummy content
void touchFileContent(const fileName& file)
{
std::ofstream os(file);
os << "file=<" << file << ">" << nl;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
unsigned testClean(std::initializer_list<Pair<std::string>> tests)
@ -587,14 +595,54 @@ int main(int argc, char *argv[])
if (args.found("system"))
{
const fileName dirA("dirA");
const fileName dirB("dirB");
const fileName dirC("dirC");
const fileName dirD("dirD");
const fileName lnA("lnA");
const fileName lnB("lnB");
const fileName dirB("dirB");
Foam::rmDir(dirA);
// Purge anything existing
Foam::rmDir(dirA, true);
Foam::rmDir(dirB, true);
Foam::rmDir(dirC, true);
Foam::rmDir(dirD, true);
Foam::rm(lnA);
Foam::rm(lnB);
Foam::rmDir(dirB);
{
fileName name(dirA/dirB/dirC/"abc");
Foam::mkDir(name.path());
touchFileContent(name); // Create real file
Foam::mkDir(dirB/dirB/dirB/dirB);
Foam::ln("test", dirB/"linkB"); // Create dead link
Foam::mkDir(dirC);
Foam::ln("../dirD", dirC/"linkC"); // Create real link
Foam::mkDir(dirD);
for (const fileName& d : { dirA, dirB, dirC, dirD })
{
Info<< "Directory: " << d << " = "
<< readDir(d, fileName::UNDEFINED, false, false) << nl;
if (Foam::rmDir(d, false, true))
{
Info<< " Removed empty dir" << nl;
}
else
{
Info<< " Could not remove empty dir" << nl;
}
}
// Force removal before continuing
Foam::rmDir(dirA, true);
Foam::rmDir(dirB, true);
Foam::rmDir(dirC, true);
Foam::rmDir(dirD, true);
}
Info<< nl << "=========================" << nl
<< "Test some copying and deletion" << endl;
@ -618,9 +666,7 @@ int main(int argc, char *argv[])
);
Info<<" create: " << file << endl;
std::ofstream os(file);
os << "file=<" << file << ">" << nl;
touchFileContent(file);
}
const int oldDebug = POSIX::debug;

View File

@ -69,7 +69,7 @@ Foam::boolList Foam::haveMeshFile
void Foam::removeProcAddressing(const faMesh& mesh)
{
IOobject ioAddr
IOobject io
(
"procAddressing",
mesh.facesInstance(),
@ -79,9 +79,9 @@ void Foam::removeProcAddressing(const faMesh& mesh)
for (const auto prefix : {"boundary", "edge", "face", "point"})
{
ioAddr.rename(prefix + word("ProcAddressing"));
io.rename(prefix + word("ProcAddressing"));
const fileName procFile(ioAddr.objectPath());
const fileName procFile(io.objectPath());
Foam::rm(procFile);
}
}
@ -89,7 +89,7 @@ void Foam::removeProcAddressing(const faMesh& mesh)
void Foam::removeProcAddressing(const polyMesh& mesh)
{
IOobject ioAddr
IOobject io
(
"procAddressing",
mesh.facesInstance(),
@ -99,89 +99,18 @@ void Foam::removeProcAddressing(const polyMesh& mesh)
for (const auto prefix : {"boundary", "cell", "face", "point"})
{
ioAddr.rename(prefix + word("ProcAddressing"));
io.rename(prefix + word("ProcAddressing"));
const fileName procFile(ioAddr.objectPath());
const fileName procFile(io.objectPath());
Foam::rm(procFile);
}
}
bool Foam::removeEmptyDir(const fileName& path)
void Foam::removeEmptyDir(const fileName& path)
{
// Return true if empty directory. Note bypass of fileHandler to be
// consistent with polyMesh.removeFiles for now.
{
fileNameList files
(
Foam::readDir
(
path,
fileName::FILE,
false, // filterGz
false // followLink
)
);
if (files.size())
{
return false;
}
}
{
fileNameList dirs
(
Foam::readDir
(
path,
fileName::DIRECTORY,
false, // filterGz
false // followLink
)
);
if (dirs.size())
{
return false;
}
}
{
fileNameList links
(
Foam::readDir
(
path,
fileName::SYMLINK,
false, // filterGz
false // followLink
)
);
if (links.size())
{
return false;
}
}
{
fileNameList other
(
Foam::readDir
(
path,
fileName::UNDEFINED,
false, // filterGz
false // followLink
)
);
if (other.size())
{
return false;
}
}
// Avoid checking success of deletion since initial path might not
// exist (e.g. contain 'region0'). Will stop when trying to delete
// parent directory anyway since now not empty.
Foam::rm(path);
return true;
// Remove directory: silent, emptyOnly
Foam::rmDir(path, true, true);
}

View File

@ -64,8 +64,8 @@ void removeProcAddressing(const faMesh& mesh);
//- Remove procAddressing
void removeProcAddressing(const polyMesh& mesh);
//- Remove empty directory. Return true if removed.
bool removeEmptyDir(const fileName& path);
//- Remove empty directory
void removeEmptyDir(const fileName& path);
//- Remove empty directories from bottom up
void removeEmptyDirs(const fileName& path);

View File

@ -1187,7 +1187,8 @@ int main(int argc, char *argv[])
{
if (decompose)
{
Info<< "Removing existing processor directory" << procDir << endl;
Info<< "Removing existing processor directory:"
<< args.relativePath(procDir) << endl;
fileHandler().rmDir(procDir);
}
}
@ -2348,6 +2349,8 @@ int main(int argc, char *argv[])
// Remove any left-over empty processor directories created
// by loadOrCreateMesh to get around the collated start-up
// problems
Info<< "Removing left-over empty processor directories" << nl;
if (Pstream::master()) //fileHandler().comm()))
{
const auto myProci = UPstream::myProcNo(); //comm()
@ -2364,9 +2367,7 @@ int main(int argc, char *argv[])
&& volMeshDir[proci] != volMeshDir[myProci]
)
{
Info<< "Deleting mesh dir:"
<< volMeshDir[proci] << endl;
Foam::rmDir(volMeshDir[proci]);
Foam::rmDir(volMeshDir[proci], true); // silent
}
if
@ -2375,9 +2376,18 @@ int main(int argc, char *argv[])
&& areaMeshDir[proci] != areaMeshDir[myProci]
)
{
Info<< "Deleting mesh dir:"
<< areaMeshDir[proci] << endl;
Foam::rmDir(areaMeshDir[proci]);
Foam::rmDir(areaMeshDir[proci], true); // silent
}
// Remove empty processor directories
// Eg, <path-name>/processorN/constant/polyMesh
// to <path-name>/processorN
if (proci != myProci)
{
removeEmptyDir
(
volMeshDir[proci].path().path()
);
}
}
@ -2441,7 +2451,8 @@ int main(int argc, char *argv[])
// Remove dummy mesh created by loadOrCreateMesh
const bool oldParRun = Pstream::parRun(false);
mesh.removeFiles();
Foam::rmDir(mesh.objectRegistry::objectPath());
// Silent rmdir
Foam::rmDir(mesh.objectRegistry::objectPath(), true);
Pstream::parRun(oldParRun); // Restore parallel state
}
}

View File

@ -723,9 +723,6 @@ Foam::fileNameList Foam::readDir
// also used as increment if initial size found to be insufficient
static constexpr int maxNnames = 100;
// Basic sanity: cannot strip '.gz' from directory names
const bool stripgz = filtergz && (type != fileName::DIRECTORY);
fileNameList dirEntries;
// Iterate contents (ignores an empty directory name)
@ -768,22 +765,30 @@ Foam::fileNameList Foam::readDir
}
else if
(
(type == fileName::DIRECTORY)
|| (type == fileName::FILE && !fileName::isBackup(name))
(type == fileName::Type::DIRECTORY)
|| (type == fileName::Type::FILE && !fileName::isBackup(name))
)
{
if ((directory/name).type() == type)
fileName::Type detected = (directory/name).type();
if (detected == type)
{
// Only strip '.gz' from non-directory names
if
(
filtergz
&& (detected != fileName::Type::DIRECTORY)
&& name.has_ext("gz")
)
{
name.remove_ext();
}
if (nEntries >= dirEntries.size())
{
dirEntries.resize(dirEntries.size() + maxNnames);
}
if (stripgz && name.has_ext("gz"))
{
name.remove_ext();
}
dirEntries[nEntries] = std::move(name);
++nEntries;
}
@ -1031,20 +1036,31 @@ bool Foam::rm(const fileName& file)
}
bool Foam::rmDir(const fileName& directory, const bool silent)
bool Foam::rmDir
(
const fileName& directory,
const bool silent,
const bool emptyOnly
)
{
if (directory.empty())
{
return false;
}
// Iterate contents (ignores an empty directory name)
// Also retain hidden files/dirs for removal
MSwindows::directoryIterator dirIter(directory, true);
if (!dirIter.exists())
{
if (!silent)
if (!silent && !emptyOnly)
{
WarningInFunction
<< "cannot open directory " << directory << endl;
<< "Cannot open directory " << directory << endl;
}
return false;
}
if (MSwindows::debug)
@ -1053,9 +1069,8 @@ bool Foam::rmDir(const fileName& directory, const bool silent)
<< " : removing directory " << directory << endl;
}
// Process each directory entry, counting any errors encountered
label nErrors = 0;
int nErrors = 0;
for (/*nil*/; dirIter; ++dirIter)
{
@ -1067,13 +1082,23 @@ bool Foam::rmDir(const fileName& directory, const bool silent)
const fileName path(fileName::concat(directory, item));
if (path.type(false) == fileName::DIRECTORY)
fileName::Type detected = path.type(false); // No followLink
if (detected == fileName::DIRECTORY)
{
if (!rmDir(path, true)) // Only report errors at the top-level
// Call silently for lower levels
if (!rmDir(path, true, emptyOnly))
{
++nErrors;
}
}
else if (emptyOnly)
{
// Only remove empty directories (not files)
++nErrors;
// POSIX has extra handling for dead symlinks...
}
else
{
if (!rm(path))
@ -1083,29 +1108,28 @@ bool Foam::rmDir(const fileName& directory, const bool silent)
}
}
if (nErrors)
{
if (!silent)
{
WarningInFunction
<< "failed to remove directory " << directory << nl
<< "could not remove " << nErrors << " sub-entries" << endl;
}
}
else
if (nErrors == 0)
{
// No errors encountered - try to remove the top-level
if (!::RemoveDirectory(directory.c_str()))
{
++nErrors;
if (!silent)
{
WarningInFunction
<< "failed to remove directory " << directory << endl;
}
nErrors = -1; // A top-level error
}
}
return !nErrors;
if (nErrors && !silent && !emptyOnly)
{
WarningInFunction
<< "Failed to remove directory " << directory << endl;
if (nErrors > 0)
{
Info<< "could not remove " << nErrors << " sub-entries" << endl;
}
}
return (nErrors == 0);
}

View File

@ -899,9 +899,6 @@ Foam::fileNameList Foam::readDir
// Initial filename list size and the increment when resizing the list
constexpr int maxNnames = 100;
// Basic sanity: cannot strip '.gz' from directory names
const bool stripgz = filtergz && (type != fileName::DIRECTORY);
fileNameList dirEntries;
// Iterate contents (ignores an empty directory name)
@ -947,22 +944,30 @@ Foam::fileNameList Foam::readDir
}
else if
(
(type == fileName::DIRECTORY)
|| (type == fileName::FILE && !fileName::isBackup(name))
(type == fileName::Type::DIRECTORY)
|| (type == fileName::Type::FILE && !fileName::isBackup(name))
)
{
if ((directory/name).type(followLink) == type)
fileName::Type detected = (directory/name).type(followLink);
if (detected == type)
{
// Only strip '.gz' from non-directory names
if
(
filtergz
&& (detected != fileName::Type::DIRECTORY)
&& name.has_ext("gz")
)
{
name.remove_ext();
}
if (nEntries >= dirEntries.size())
{
dirEntries.resize(dirEntries.size() + maxNnames);
}
if (stripgz && name.has_ext("gz"))
{
name.remove_ext();
}
dirEntries[nEntries] = std::move(name);
++nEntries;
}
@ -1319,18 +1324,28 @@ bool Foam::rm(const fileName& file)
}
bool Foam::rmDir(const fileName& directory, const bool silent)
bool Foam::rmDir
(
const fileName& directory,
const bool silent,
const bool emptyOnly
)
{
if (directory.empty())
{
return false;
}
// Iterate contents (ignores an empty directory name)
// Also retain hidden files/dirs for removal
POSIX::directoryIterator dirIter(directory, true);
if (!dirIter.exists())
{
if (!silent)
if (!silent && !emptyOnly)
{
WarningInFunction
<< "cannot open directory " << directory << endl;
<< "Cannot open directory " << directory << endl;
}
return false;
@ -1347,7 +1362,7 @@ bool Foam::rmDir(const fileName& directory, const bool silent)
}
// Process each directory entry, counting any errors encountered
label nErrors = 0;
int nErrors = 0;
for (/*nil*/; dirIter; ++dirIter)
{
@ -1359,13 +1374,37 @@ bool Foam::rmDir(const fileName& directory, const bool silent)
const fileName path(fileName::concat(directory, item));
if (path.type(false) == fileName::DIRECTORY)
fileName::Type detected = path.type(false); // No followLink
if (detected == fileName::Type::DIRECTORY)
{
if (!rmDir(path, true)) // Only report errors at the top-level
// Call silently for lower levels
if (!rmDir(path, true, emptyOnly))
{
++nErrors;
}
}
else if (emptyOnly)
{
// Only remove empty directories (not files)
++nErrors;
// Check for dead symlinks
if (detected == fileName::Type::SYMLINK)
{
detected = path.type(true); // followLink
if (detected == fileName::Type::UNDEFINED)
{
--nErrors;
if (!rm(path))
{
++nErrors;
}
}
}
}
else
{
if (!rm(path))
@ -1375,30 +1414,28 @@ bool Foam::rmDir(const fileName& directory, const bool silent)
}
}
if (nErrors)
{
if (!silent)
{
WarningInFunction
<< "failed to remove directory " << directory << nl
<< "could not remove " << nErrors << " sub-entries" << endl;
}
}
else
if (nErrors == 0)
{
// No errors encountered - try to remove the top-level
if (!rm(directory))
{
++nErrors;
if (!silent)
{
WarningInFunction
<< "failed to remove directory " << directory << endl;
}
nErrors = -1; // A top-level error
}
}
// clean up
return !nErrors;
if (nErrors && !silent && !emptyOnly)
{
WarningInFunction
<< "Failed to remove directory " << directory << endl;
if (nErrors > 0)
{
Info<< "could not remove " << nErrors << " sub-entries" << endl;
}
}
return (nErrors == 0);
}

View File

@ -357,11 +357,14 @@ public:
virtual bool rm(const fileName&) const = 0;
//- Remove a directory and its contents
// \param dir the directory to remove
// \param silent do not report missing directory
// \param emptyOnly only remove empty directories (recursive)
virtual bool rmDir
(
const fileName& dir,
const bool silent = false
const bool silent = false,
const bool emptyOnly = false
) const = 0;
// //- Open a shared library. Return handle to library. Print error

View File

@ -1018,13 +1018,14 @@ bool Foam::fileOperations::masterUncollatedFileOperation::rm
bool Foam::fileOperations::masterUncollatedFileOperation::rmDir
(
const fileName& dir,
const bool silent
const bool silent,
const bool emptyOnly
) const
{
return masterOp<bool>
(
dir,
rmDirOp(silent),
rmDirOp(silent, emptyOnly),
Pstream::msgType(),
comm_
);

View File

@ -283,15 +283,17 @@ protected:
class rmDirOp
{
bool silent_;
bool emptyOnly_;
public:
rmDirOp(const bool silent=false)
rmDirOp(bool silent=false, bool emptyOnly=false)
:
silent_(silent)
silent_(silent),
emptyOnly_(emptyOnly)
{}
bool operator()(const fileName& f) const
{
return Foam::rmDir(f, silent_);
return Foam::rmDir(f, silent_, emptyOnly_);
}
};
@ -603,11 +605,14 @@ public:
virtual bool rm(const fileName&) const;
//- Remove a directory and its contents
// \param dir the directory to remove
// \param silent do not report missing directory
// \param emptyOnly only remove empty directories (recursive)
virtual bool rmDir
(
const fileName& dir,
const bool silent = false
const bool silent = false,
const bool emptyOnly = false
) const;
// //- Open a shared library. Return handle to library. Print error

View File

@ -317,10 +317,11 @@ bool Foam::fileOperations::uncollatedFileOperation::rm
bool Foam::fileOperations::uncollatedFileOperation::rmDir
(
const fileName& dir,
const bool silent
const bool silent,
const bool emptyOnly
) const
{
return Foam::rmDir(dir, silent);
return Foam::rmDir(dir, silent, emptyOnly);
}

View File

@ -205,10 +205,14 @@ public:
virtual bool rm(const fileName&) const;
//- Remove a directory and its contents
// \param dir the directory to remove
// \param silent do not report missing directory
// \param emptyOnly only remove empty directories (recursive)
virtual bool rmDir
(
const fileName& dir,
const bool silent = false
const bool silent = false,
const bool emptyOnly = false
) const;
// //- Open a shared library. Return handle to library. Print error

View File

@ -178,10 +178,14 @@ double highResLastModified(const fileName&, const bool followLink=true);
// Using an empty directory name returns an empty list.
fileNameList readDir
(
//! The directory name to scan
const fileName& directory,
const fileName::Type type=fileName::FILE,
const bool filtergz=true,
const bool followLink=true
//! The content type (eg, FILE | DIRECTORY)
const fileName::Type type = fileName::Type::FILE,
//! Trim '.gz' from file names
const bool filtergz = true,
//! Check what the symlink points to, not the symlink itself
const bool followLink = true
);
//- Copy the source to the destination (recursively if necessary).
@ -211,10 +215,18 @@ bool mvBak(const fileName& src, const std::string& ext = "bak");
// An empty name is a no-op that always returns false.
bool rm(const fileName& file);
//- Remove a directory and its contents (optionally silencing warnings)
// An empty directory name is a no-op that always returns false,
// but also produces a warning.
bool rmDir(const fileName& directory, const bool silent=false);
//- Remove a directory and its contents recursively,
// returning true if successful.
// An empty directory name is a no-op that always returns false (silently)
bool rmDir
(
//! The directory name to remove
const fileName& directory,
//! Silence warnings
const bool silent = false,
//! Only remove empty directories (recursive)
const bool emptyOnly = false
);
//- Sleep for the specified number of seconds
unsigned int sleep(const unsigned int sec);

View File

@ -98,35 +98,32 @@ Foam::processorFaMeshes::processorFaMeshes
void Foam::processorFaMeshes::removeFiles(const faMesh& mesh)
{
IOobject ioAddr
IOobject io
(
"procAddressing",
mesh.facesInstance(),
faMesh::meshSubDir,
mesh.thisDb(),
IOobject::NO_READ,
IOobject::NO_WRITE,
false // not registered
mesh.thisDb()
);
// procAddressing
rm(ioAddr.objectPath());
fileHandler().rm(fileHandler().filePath(io.objectPath()));
// pointProcAddressing
ioAddr.rename("pointProcAddressing");
rm(ioAddr.objectPath());
io.rename("pointProcAddressing");
fileHandler().rm(fileHandler().filePath(io.objectPath()));
// edgeProcAddressing
ioAddr.rename("edgeProcAddressing");
rm(ioAddr.objectPath());
io.rename("edgeProcAddressing");
fileHandler().rm(fileHandler().filePath(io.objectPath()));
// faceProcAddressing
ioAddr.rename("faceProcAddressing");
rm(ioAddr.objectPath());
io.rename("faceProcAddressing");
fileHandler().rm(fileHandler().filePath(io.objectPath()));
// boundaryProcAddressing
ioAddr.rename("boundaryProcAddressing");
rm(ioAddr.objectPath());
io.rename("boundaryProcAddressing");
fileHandler().rm(fileHandler().filePath(io.objectPath()));
}

View File

@ -195,7 +195,7 @@ void Foam::processorMeshes::reconstructPoints(fvMesh& mesh)
meshes_[proci].thisDb(),
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
false // not registered
)
)
);
@ -230,35 +230,32 @@ void Foam::processorMeshes::reconstructPoints(fvMesh& mesh)
void Foam::processorMeshes::removeFiles(const polyMesh& mesh)
{
IOobject ioAddr
IOobject io
(
"procAddressing",
mesh.facesInstance(),
polyMesh::meshSubDir,
mesh.thisDb(),
IOobject::NO_READ,
IOobject::NO_WRITE,
false // not registered
mesh.thisDb()
);
// procAddressing
rm(ioAddr.objectPath());
fileHandler().rm(fileHandler().filePath(io.objectPath()));
// pointProcAddressing
ioAddr.rename("pointProcAddressing");
rm(ioAddr.objectPath());
io.rename("pointProcAddressing");
fileHandler().rm(fileHandler().filePath(io.objectPath()));
// faceProcAddressing
ioAddr.rename("faceProcAddressing");
rm(ioAddr.objectPath());
io.rename("faceProcAddressing");
fileHandler().rm(fileHandler().filePath(io.objectPath()));
// cellProcAddressing
ioAddr.rename("cellProcAddressing");
rm(ioAddr.objectPath());
io.rename("cellProcAddressing");
fileHandler().rm(fileHandler().filePath(io.objectPath()));
// boundaryProcAddressing
ioAddr.rename("boundaryProcAddressing");
rm(ioAddr.objectPath());
io.rename("boundaryProcAddressing");
fileHandler().rm(fileHandler().filePath(io.objectPath()));
}

View File

@ -2,18 +2,22 @@
cd "${0%/*}" || exit # Run from this directory
. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions # Tutorial run functions
#------------------------------------------------------------------------------
fileHandler="-fileHandler collated"
decomp16="-decomposeParDict system/decomposeParDict.16"
runApplication blockMesh
##- Serial run
#runApplication snappyHexMesh
#runApplication snappyHexMesh -overwrite $fileHandler
#runApplication checkMesh -allTopology -allGeometry
#- Parallel run
runApplication decomposePar
runParallel snappyHexMesh
runParallel checkMesh -allTopology -allGeometry
runParallel -s decompose redistributePar -decompose -constant $fileHandler
runParallel snappyHexMesh -overwrite $fileHandler
runParallel checkMesh -allTopology -allGeometry $fileHandler
#runApplication reconstructParMesh -constant
# runParallel -s redistrib $decomp16 redistributePar -constant $fileHandler
#runApplication reconstructParMesh -constant $fileHandler
#------------------------------------------------------------------------------

View File

@ -10,20 +10,17 @@ FoamFile
version 2.0;
format ascii;
class dictionary;
note "mesh decomposition control dictionary";
object decomposeParDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//- The total number of domains (mandatory)
numberOfSubdomains 8;
//- The decomposition method (mandatory)
method hierarchical;
method hierarchical;
hierarchicalCoeffs
coeffs
{
n (2 2 2);
n (2 2 2);
}
// ************************************************************************* //

View File

@ -0,0 +1,21 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2206 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
object decomposeParDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
numberOfSubdomains 16;
method scotch;
// ************************************************************************* //