ENH: reduce startup time for ensight conversion (issue #240).

- Less looping when detecting lagrangian clouds and their fields.

- Avoid using Time::setTime() and IOobjectList in tight loops.
  They both kill performance immensely.

ENH: provide a -noLagrangian option to foamToEnsight and foamToEnsightParts
for even more control.
This commit is contained in:
Mark Olesen
2016-09-23 17:45:47 +02:00
parent 3503c872a5
commit 88ca081c8c
6 changed files with 186 additions and 145 deletions

View File

@ -11,7 +11,7 @@ if (!fieldsToUse.found(fieldName))
{ {
variableGood = variableGood =
( (
fieldName.size() > 2 && fieldName(fieldName.size() - 2, 2) == "_0" fieldName.size() > 2 && fieldName(fieldName.size()-2, 2) == "_0"
? false ? false
: IOobject : IOobject
( (

View File

@ -12,14 +12,16 @@ if (timeDirs.size() > 1 && Pstream::master())
Info<< "Search for moving mesh ... " << flush; Info<< "Search for moving mesh ... " << flush;
forAll(timeDirs, timeI) forAll(timeDirs, timeI)
{ {
const word& timeName = timeDirs[timeI].name();
meshMoving = meshMoving =
( (
timeDirs[timeI].name() != mesh.pointsInstance() timeName != mesh.pointsInstance()
&& isDir(baseDir/timeDirs[timeI].name()/polyMesh::meshSubDir) && isDir(baseDir/timeName/polyMesh::meshSubDir)
&& IOobject && IOobject
( (
"points", "points",
timeDirs[timeI].name(), timeName,
polyMesh::meshSubDir, polyMesh::meshSubDir,
mesh, mesh,
IOobject::NO_READ, IOobject::NO_READ,

View File

@ -0,0 +1,93 @@
// check all time directories for the following:
// The fields for each cloud:
HashTable<HashTable<word>> cloudFields;
// Identify if lagrangian data exist at any time step.
if (timeDirs.size() && !noLagrangian)
{
const fileName& baseDir = mesh.time().path();
const fileName& cloudPrefix = regionPrefix/cloud::prefix;
Info<< "Searching for lagrangian ... " << flush;
forAll(timeDirs, timeI)
{
const word& timeName = timeDirs[timeI].name();
// DO NOT USE -->> runTime.setTime(timeDirs[timeI], timeI); <<--
// It incurs a large overhead when done so frequently.
fileNameList cloudDirs = readDir
(
baseDir/timeName/cloudPrefix,
fileName::DIRECTORY
);
forAll(cloudDirs, cloudI)
{
const word& cloudName = cloudDirs[cloudI];
IOobjectList cloudObjs
(
mesh,
timeName,
cloudPrefix/cloudName
);
// clouds always require "positions"
if (cloudObjs.found("positions"))
{
HashTable<HashTable<word>>::iterator cloudIter =
cloudFields.find(cloudName);
if (cloudIter == cloudFields.end())
{
// A newly discovered cloud
cloudFields.insert(cloudName, HashTable<word>());
cloudIter = cloudFields.find(cloudName);
}
forAllConstIter(IOobjectList, cloudObjs, fieldIter)
{
const IOobject obj = *fieldIter();
// Add field and field type
cloudIter().insert
(
obj.name(),
obj.headerClassName()
);
}
}
}
}
// prune out "positions" again since it gets treated specially
forAllIter(HashTable<HashTable<word>>, cloudFields, cloudIter)
{
cloudIter().erase("positions");
}
if (cloudFields.empty())
{
Info<< "none detected." << endl;
}
}
// sorted list of cloud names
const wordList cloudNames(cloudFields.sortedToc());
if (cloudNames.size())
{
// complete the echo information
Info<< "(";
forAll(cloudNames, cloudNo)
{
Info<< ' ' << cloudNames[cloudNo];
}
Info<< " ) " << endl;
}
// ************************************************************************* //

View File

@ -42,13 +42,16 @@ Usage
\param -noZero \n \param -noZero \n
Exclude the often incomplete initial conditions. Exclude the often incomplete initial conditions.
\param -patches patchList \n \param -noLagrangian \n
Specify particular patches to write. Suppress writing lagrangian positions and fields.
Specifying an empty list suppresses writing the internalMesh.
\param -noPatches \n \param -noPatches \n
Suppress writing any patches. Suppress writing any patches.
\param -patches patchList \n
Specify particular patches to write.
Specifying an empty list suppresses writing the internalMesh.
\param -faceZones zoneList \n \param -faceZones zoneList \n
Specify faceZones to write, with wildcards Specify faceZones to write, with wildcards
@ -126,6 +129,11 @@ int main(int argc, char *argv[])
"write values in nodes" "write values in nodes"
); );
argList::addBoolOption argList::addBoolOption
(
"noLagrangian",
"suppress writing lagrangian positions and fields"
);
argList::addBoolOption
( (
"noPatches", "noPatches",
"suppress writing any patches" "suppress writing any patches"
@ -298,6 +306,8 @@ int main(int argc, char *argv[])
fieldPatterns = wordReList(args.optionLookup("fields")()); fieldPatterns = wordReList(args.optionLookup("fields")());
} }
const bool noLagrangian = args.optionFound("noLagrangian");
word cellZoneName; word cellZoneName;
const bool doCellZone = args.optionReadIfPresent("cellZone", cellZoneName); const bool doCellZone = args.optionReadIfPresent("cellZone", cellZoneName);
@ -334,6 +344,7 @@ int main(int argc, char *argv[])
IOobjectList objects(mesh, runTime.timeName()); IOobjectList objects(mesh, runTime.timeName());
#include "checkMeshMoving.H" #include "checkMeshMoving.H"
#include "findCloudFields.H"
if (Pstream::master()) if (Pstream::master())
{ {
@ -356,99 +367,19 @@ int main(int argc, char *argv[])
<< setw(16) << "model:" << setw(16) << "model:"
<< ensightMesh::geometryName << nl; << ensightMesh::geometryName << nl;
} }
}
// Identify if lagrangian data exists any time step, and add clouds
// to the 'cloudNames' (sorted list)
wordList cloudNames;
{
wordHashSet allCloudNames;
forAll(timeDirs, timeI) // Add the name of the cloud(s) to the case file header
{
runTime.setTime(timeDirs[timeI], timeI);
fileNameList cloudDirs = readDir
(
runTime.timePath()/regionPrefix/cloud::prefix,
fileName::DIRECTORY
);
forAll(cloudDirs, cloudI)
{
IOobjectList cloudObjs
(
mesh,
runTime.timeName(),
cloud::prefix/cloudDirs[cloudI]
);
IOobject* positionsPtr = cloudObjs.lookup(word("positions"));
if (positionsPtr)
{
allCloudNames.insert(cloudDirs[cloudI]);
}
}
}
// sorted for consistency
cloudNames = allCloudNames.sortedToc();
}
// ignore special fields (_0 fields),
// ignore fields we don't handle,
// ignore fields that are not available for all time-steps
HashTable<bool> fieldsToUse;
HashTable<HashTable<word>> allCloudFields;
forAll(cloudNames, cloudNo) forAll(cloudNames, cloudNo)
{ {
const word& cloudName = cloudNames[cloudNo]; const word& cloudName = cloudNames[cloudNo];
// Add the name of the cloud(s) to the case file header
if (Pstream::master())
{
ensightCaseFile ensightCaseFile
<< setw(16) << "measured: 1" << setw(16) << "measured: 1"
<< fileName(dataMask/cloud::prefix/cloudName/"positions").c_str() << fileName
<< nl;
}
// Create a new hash table for each cloud
allCloudFields.insert(cloudName, HashTable<word>());
// Identify the new cloud in the hash table
HashTable<HashTable<word>>::iterator newCloudIter =
allCloudFields.find(cloudName);
// Loop over all times to build list of fields and field types
// for each cloud
forAll(timeDirs, timeI)
{
runTime.setTime(timeDirs[timeI], timeI);
IOobjectList cloudObjs
( (
mesh, dataMask/cloud::prefix/cloudName/"positions"
runTime.timeName(), ).c_str() << nl;
cloud::prefix/cloudName
);
forAllConstIter(IOobjectList, cloudObjs, fieldIter)
{
const IOobject obj = *fieldIter();
if (obj.name() != "positions")
{
// Add field and field type
newCloudIter().insert
(
obj.name(),
obj.headerClassName()
);
}
}
} }
} }
@ -456,6 +387,11 @@ int main(int argc, char *argv[])
<< timer.cpuTimeIncrement() << " s, " << timer.cpuTimeIncrement() << " s, "
<< mem.update().size() << " kB" << nl << endl; << mem.update().size() << " kB" << nl << endl;
// ignore special fields (_0 fields),
// ignore fields we don't handle,
// ignore fields that are not available for all time-steps
HashTable<bool> fieldsToUse;
label nTimeSteps = 0; label nTimeSteps = 0;
forAll(timeDirs, timeIndex) forAll(timeDirs, timeIndex)
{ {
@ -740,13 +676,7 @@ int main(int argc, char *argv[])
forAll(cloudNames, cloudNo) forAll(cloudNames, cloudNo)
{ {
const word& cloudName = cloudNames[cloudNo]; const word& cloudName = cloudNames[cloudNo];
if (!allCloudFields.found(cloudName)) const HashTable<word>& theseCloudFields = cloudFields[cloudName];
{
// extra safety
continue;
}
const HashTable<word>& cloudFields = allCloudFields[cloudName];
fileNameList currentCloudDirs = readDir fileNameList currentCloudDirs = readDir
( (
@ -769,7 +699,7 @@ int main(int argc, char *argv[])
format format
); );
forAllConstIter(HashTable<word>, cloudFields, fieldIter) forAllConstIter(HashTable<word>, theseCloudFields, fieldIter)
{ {
const word& fieldName = fieldIter.key(); const word& fieldName = fieldIter.key();
const word& fieldType = fieldIter(); const word& fieldType = fieldIter();

View File

@ -8,7 +8,10 @@ HashTable<HashTable<word>> cloudFields;
if (timeDirs.size()) if (timeDirs.size())
{ {
IOobjectList objs(mesh, timeDirs.last().name()); const fileName& cloudPrefix = regionPrefix/cloud::prefix;
const word& lastTimeName = timeDirs.last().name();
IOobjectList objs(mesh, lastTimeName);
forAllConstIter(IOobjectList, objs, fieldIter) forAllConstIter(IOobjectList, objs, fieldIter)
{ {
@ -31,14 +34,17 @@ if (timeDirs.size())
// //
// now check for lagrangian/<cloudName> // now check for lagrangian/<cloudName>
// //
fileNameList cloudDirs = readDir fileNameList cloudDirs;
if (!noLagrangian)
{
cloudDirs = readDir
( (
runTime.path() runTime.path()
/ timeDirs.last().name() / lastTimeName
/ regionPrefix / cloudPrefix,
/ cloud::prefix,
fileName::DIRECTORY fileName::DIRECTORY
); );
}
forAll(cloudDirs, cloudI) forAll(cloudDirs, cloudI)
{ {
@ -54,8 +60,8 @@ if (timeDirs.size())
IOobjectList objs IOobjectList objs
( (
mesh, mesh,
timeDirs.last().name(), lastTimeName,
cloud::prefix/cloudName cloudPrefix/cloudName
); );
bool hasPositions = false; bool hasPositions = false;
@ -89,17 +95,27 @@ if (timeDirs.size())
// //
for (label i=0; volumeFields.size() && i < timeDirs.size(); ++i) for (label i=0; volumeFields.size() && i < timeDirs.size(); ++i)
{ {
IOobjectList objs(mesh, timeDirs[i].name()); const word& timeName = timeDirs[i].name();
forAllIter(HashTable<word>, volumeFields, fieldIter) // Everything is potentially missing, unless we discover otherwise
{ wordHashSet missing(volumeFields);
const word& fieldName = fieldIter.key();
if (!objs.found(fieldName)) // Avoid -->> IOobjectList objs(mesh, timeName); <<--
// Too much overhead when done so frequently.
fileNameList contents = readDir
(
runTime.path()
/ timeName,
fileName::FILE
);
forAll(contents, fileI)
{ {
volumeFields.erase(fieldIter); missing.erase(contents[fileI].name());
}
} }
volumeFields.erase(missing);
} }
} }

View File

@ -44,6 +44,9 @@ Usage
\param -noZero \n \param -noZero \n
Exclude the often incomplete initial conditions. Exclude the often incomplete initial conditions.
\param -noLagrangian \n
Suppress writing lagrangian positions and fields.
\param -index \<start\>\n \param -index \<start\>\n
Ignore the time index contained in the time file and use a Ignore the time index contained in the time file and use a
simple indexing when creating the \c Ensight/data/######## files. simple indexing when creating the \c Ensight/data/######## files.
@ -101,6 +104,11 @@ int main(int argc, char *argv[])
"and use simple indexing when creating the files" "and use simple indexing when creating the files"
); );
argList::addBoolOption argList::addBoolOption
(
"noLagrangian",
"suppress writing lagrangian positions and fields"
);
argList::addBoolOption
( (
"noMesh", "noMesh",
"suppress writing the geometry. " "suppress writing the geometry. "
@ -158,7 +166,8 @@ int main(int argc, char *argv[])
// control for renumbering iterations // control for renumbering iterations
label indexingNumber = 0; label indexingNumber = 0;
bool optIndex = args.optionReadIfPresent("index", indexingNumber); const bool optIndex = args.optionReadIfPresent("index", indexingNumber);
const bool noLagrangian = args.optionFound("noLagrangian");
// always write the geometry, unless the -noMesh option is specified // always write the geometry, unless the -noMesh option is specified
bool optNoMesh = args.optionFound("noMesh"); bool optNoMesh = args.optionFound("noMesh");
@ -389,15 +398,9 @@ int main(int argc, char *argv[])
forAllConstIter(HashTable<HashTable<word>>, cloudFields, cloudIter) forAllConstIter(HashTable<HashTable<word>>, cloudFields, cloudIter)
{ {
const word& cloudName = cloudIter.key(); const word& cloudName = cloudIter.key();
const fileName& cloudPrefix = regionPrefix/cloud::prefix;
if if (!isDir(runTime.timePath()/cloudPrefix/cloudName))
(
!isDir
(
runTime.timePath()/regionPrefix/
cloud::prefix/cloudName
)
)
{ {
continue; continue;
} }
@ -406,13 +409,15 @@ int main(int argc, char *argv[])
( (
mesh, mesh,
runTime.timeName(), runTime.timeName(),
cloud::prefix/cloudName cloudPrefix/cloudName
); );
// check that the positions field is present for this time // check that the positions field is present for this time
IOobject* positionPtr = cloudObjs.lookup(word("positions")); if (!cloudObjs.found("positions"))
if (positionPtr != NULL)
{ {
continue;
}
ensightParticlePositions ensightParticlePositions
( (
mesh, mesh,
@ -421,11 +426,6 @@ int main(int argc, char *argv[])
cloudName, cloudName,
format format
); );
}
else
{
continue;
}
Info<< "write " << cloudName << " (" << flush; Info<< "write " << cloudName << " (" << flush;
@ -439,7 +439,7 @@ int main(int argc, char *argv[])
if (!fieldObject) if (!fieldObject)
{ {
Info<< "missing " Info<< "missing "
<< runTime.timeName()/cloud::prefix/cloudName << runTime.timeName()/cloudPrefix/cloudName
/ fieldName / fieldName
<< endl; << endl;
continue; continue;