Commit a9762812 authored by Mark Olesen's avatar Mark Olesen Committed by Andrew Heather
Browse files

ENH: overhaul ensight handling (#1579)

- includes restructuring and simplification of low-level ensight part
  handling and refactor of backends to improve code reuse.

foamToEnsight
-------------

  * new cellZone support.
    This was previously only possible via a separate foamToEnsightParts
    utility that was not parallelized.

  * support for point fields.

  * `-nearCellValue` option (as per foamToVTK)

  * data indexing now uses values from the time index.
    This is consistent with the ensightWrite function object and
    can help with restarts.

  * existing ensight directories are removed, unless the -no-overwrite
    option is supplied

foamToEnsightParts
------------------
  * now redundant and removed.

ensightOutputSurface (new class)
--------------------------------
  * a lightweight wrapper for point/face references that is tailored
    for the ensightSurfaceWriter. It uses compact face/point information
    and is serial only, since this is the format requirements from the
    surfaceWriter class.

ensightMesh (revised class)
---------------------------
  * now only holds a polyMesh reference, which removes its dependency
    on finiteVolume and allows it to be relocated under fileFormats
    instead of conversion.

Removed classes: ensightParts, ensighPartFaces, ensightPartCells

- these were used by foamToEnsightParts, but not needed anymore.
parent c7e8f22b
......@@ -3,13 +3,11 @@ EXE_INC = \
-I$(LIB_SRC)/fileFormats/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/conversion/lnInclude \
-I$(LIB_SRC)/dynamicMesh/lnInclude \
-I$(LIB_SRC)/lagrangian/intermediate/lnInclude
EXE_LIBS = \
-lfiniteVolume \
-lfileFormats \
-ldynamicMesh \
-lconversion \
-llagrangianIntermediate \
-lgenericPatchFields
// Check for "points" in any of the result directories
bool meshMoving = false;
bool hasMovingMesh = false;
if (timeDirs.size() > 1 && Pstream::master())
{
......@@ -12,7 +12,7 @@ if (timeDirs.size() > 1 && Pstream::master())
{
const word& timeName = inst.name();
meshMoving =
hasMovingMesh =
(
timeName != mesh.pointsInstance()
&& IOobject
......@@ -27,13 +27,13 @@ if (timeDirs.size() > 1 && Pstream::master())
).typeHeaderOk<pointIOField>(true, false)
);
if (meshMoving)
if (hasMovingMesh)
{
break;
}
}
if (meshMoving)
if (hasMovingMesh)
{
Info<< "found. Writing meshes for every timestep." << endl;
}
......@@ -43,4 +43,4 @@ if (timeDirs.size() > 1 && Pstream::master())
}
}
reduce(meshMoving, orOp<bool>());
reduce(hasMovingMesh, orOp<bool>());
......@@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018 OpenCFD Ltd.
Copyright (C) 2018-2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
......@@ -31,9 +31,8 @@ Description
// Cloud field data output
if (doLagrangian)
{
forAll(cloudNames, cloudNo)
for (const word& cloudName : cloudNames)
{
const word& cloudName = cloudNames[cloudNo];
const HashTable<word>& theseCloudFields = cloudFields[cloudName];
fileNameList currentCloudDirs
......@@ -48,12 +47,7 @@ if (doLagrangian)
Info<< "Write " << cloudName << " (";
const bool cloudExists =
returnReduce
(
currentCloudDirs.found(cloudName),
orOp<bool>()
);
returnReduce(currentCloudDirs.found(cloudName), orOp<bool>());
{
autoPtr<ensightFile> os = ensCase.newCloud(cloudName);
......
......@@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018 OpenCFD Ltd.
Copyright (C) 2018-2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
......@@ -35,25 +35,19 @@ Description
{
Info<< "Write volume field (";
writeAllVolFields
(
ensCase,
ensMesh,
meshProxy,
objects,
nodeValues
);
writeAllDimFields
(
ensCase,
ensMesh,
meshProxy,
objects,
nodeValues
);
writeAllVolFields(ensCase, ensMesh, objects, nearCellValue);
writeAllDimFields(ensCase, ensMesh, objects);
Info<< " )" << nl;
// PointData
// - only construct pointMesh on request (it constructs edge addressing)
if (doPointValues)
{
Info<< "Write point field (";
writeAllPointFields(ensCase, ensMesh, objects);
Info<< " )" << nl;
}
}
......
......@@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2018 OpenCFD Ltd.
Copyright (C) 2016-2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
......@@ -32,7 +32,11 @@ Group
Description
Translate OpenFOAM data to EnSight format.
An Ensight part is created for the internalMesh and for each patch.
An Ensight part is created for cellZones (unzoned cells are "internalMesh")
and patches.
- Handles volume fields, dimensioned fields, point fields
- Handles mesh topology changes.
Usage
\b foamToEnsight [OPTION]
......@@ -51,36 +55,65 @@ Usage
The quoting is required to avoid shell expansions and to pass the
information as a single argument.
- \par -nearCellValue
Use zero-gradient cell values on patches
- \par -nodeValues
Force interpolation of values to nodes
- \par -no-boundary
Suppress writing any patches.
Suppress output for all boundary patches
- \par -no-internal
Suppress writing the internal mesh.
Suppress output for internal (volume) mesh
- \par -no-cellZones
Suppress cellZone handling
- \par -no-lagrangian
Suppress writing lagrangian positions and fields.
- \par -patches patch or patch list
Specify particular patches to write.
- \par -no-mesh
Suppress writing the geometry. Can be useful for converting partial
results for a static geometry.
- \par -faceZones zone or zone list
Specify faceZones to write, with wildcards
- \par -cellZone zoneName
Specify single cellZone to write (not lagrangian)
- \par -no-point-data
Suppress conversion of pointFields. No interpolated PointData.
- \par -noZero
Exclude the often incomplete initial conditions.
- \par -index \<start\>
Use consecutive indexing for \c data/######## files with the
specified start index.
Ignore the time index contained in the uniform/time file.
- \par -name \<subdir\>
Define sub-directory name to use for Ensight data (default: "EnSight")
- \par -width \<n\>
Width of Ensight data subdir (default: 8)
Note
Writes to \a EnSight directory to avoid collisions with
foamToEnsightParts
- \par -cellZones NAME | LIST
Specify single zone or multiple cell zones (name or regex) to write
- \par -faceZones NAME | LIST
Specify single zone or multiple face zones (name or regex) to write
- \par -patches NAME | LIST
Specify single patch or multiple patches (name or regex) to write
For example,
\verbatim
-patches top
-patches '( front \".*back\" )'
\endverbatim
- \par -excludePatches NAME | LIST
Specify single or multiple patches (name or regex) not to convert.
For example,
\verbatim
-excludePatches '( inlet_1 inlet_2 "proc.*" )'
\endverbatim
\*---------------------------------------------------------------------------*/
......@@ -93,6 +126,7 @@ Note
#include "HashOps.H"
#include "fvc.H"
#include "fvMesh.H"
#include "fieldTypes.H"
#include "volFields.H"
#include "scalarIOField.H"
......@@ -104,26 +138,18 @@ Note
#include "ensightMesh.H"
#include "ensightOutputCloud.H"
#include "ensightOutputVolField.H"
#include "fvMeshSubsetProxy.H"
// local files
#include "readFields.H"
#include "writeVolFields.H"
#include "writeDimFields.H"
#include "writePointFields.H"
#include "memInfo.H"
using namespace Foam;
//- Get internal field and make it a zero-gradient volume field with subsetting
template<class GeoField>
tmp<GeoField>
getZeroGradInternalField(IOobject& io, const fvMeshSubsetProxy& proxy)
{
auto tfield = tmp<typename GeoField::Internal>::New(io, proxy.baseMesh());
return proxy.interpolateInternal(tfield);
}
#undef foamToEnsight_useTimeIndex
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
......@@ -131,8 +157,8 @@ int main(int argc, char *argv[])
{
argList::addNote
(
"Translate OpenFOAM data to Ensight format with a part for"
" the internalMesh and for each patch."
"Translate OpenFOAM data to Ensight format with individual parts"
" for cellZones, unzoned cells and patches"
);
timeSelector::addOptions();
......@@ -147,10 +173,42 @@ int main(int argc, char *argv[])
"ascii",
"Write in ASCII format instead of 'C Binary'"
);
argList::addOption
(
"index",
"start",
"Starting index for consecutive number of Ensight data/ files."
" Ignore the time index contained in the uniform/time file."
, true // mark as an advanced option
);
argList::addOption
(
"name",
"subdir",
"Sub-directory name for Ensight output (default: 'EnSight')"
);
argList::addBoolOption
(
"no-overwrite",
"Suppress removal of existing EnSight output directory"
);
argList::addOption
(
"width",
"n",
"Width of Ensight data subdir"
);
argList::addBoolOption
(
"nearCellValue",
"Use zero-gradient cell values on patches"
, true // mark as an advanced option
);
argList::addBoolOption
(
"nodeValues",
"Write values at nodes"
"Force interpolation of values to nodes"
, true // mark as an advanced option
);
argList::addBoolOption
(
......@@ -165,12 +223,37 @@ int main(int argc, char *argv[])
"Suppress writing the internal mesh"
);
argList::addBoolOption
(
"no-cellZones",
"Suppress writing any cellZones"
);
argList::addBoolOption
(
"no-lagrangian", // noLagrangian
"Suppress writing lagrangian positions and fields"
);
argList::addOptionCompat("no-lagrangian", {"noLagrangian", 1806});
argList::addBoolOption
(
"no-point-data",
"Suppress conversion of pointFields and disables -nodeValues"
);
argList::addBoolOption
(
"no-mesh", // noMesh
"Suppress writing the geometry."
" Can be useful for converting partial results for a static geometry"
, true // mark as an advanced option
);
// Future?
// argList::addBoolOption
// (
// "one-boundary", // allPatches
// "Combine all patches into a single part"
// );
argList::addOption
(
"patches",
......@@ -179,6 +262,14 @@ int main(int argc, char *argv[])
"Eg, 'inlet' or '(outlet \"inlet.*\")'"
);
argList::addOption
(
"excludePatches",
"wordRes",
"Specify single patch or multiple patches to exclude from writing."
" Eg, 'outlet' or '( inlet \".*Wall\" )'"
, true // mark as an advanced option
);
argList::addOption
(
"faceZones",
"wordRes",
......@@ -194,22 +285,13 @@ int main(int argc, char *argv[])
);
argList::addOption
(
"cellZone",
"word",
"Specify cellZone to write"
);
argList::addOption
(
"name",
"subdir",
"Sub-directory name for Ensight output (default: 'EnSight')"
);
argList::addOption
(
"width",
"n",
"Width of Ensight data subdir"
"cellZones",
"wordRes",
"Specify single or multiple cellZones to write\n"
"Eg, 'cells' or '( slice \"mfp-.*\" )'."
);
argList::addOptionCompat("cellZone", {"cellZones", 1912});
#include "setRootCase.H"
......@@ -221,8 +303,6 @@ int main(int argc, char *argv[])
: IOstream::BINARY
);
const bool nodeValues = args.found("nodeValues");
cpuTime timer;
memInfo mem;
Info<< "Initial memory " << mem.update().size() << " kB" << endl;
......@@ -239,19 +319,50 @@ int main(int argc, char *argv[])
regionPrefix = regionName;
}
//
// Configuration
//
const bool doBoundary = !args.found("no-boundary");
const bool doInternal = !args.found("no-internal");
const bool doCellZones = !args.found("no-cellZones");
const bool doLagrangian = !args.found("no-lagrangian");
const bool doPointValues = !args.found("no-point-data");
const bool nearCellValue = args.found("nearCellValue") && doBoundary;
// Control for numbering iterations
label indexingNumber(0);
const bool doConsecutive = args.readIfPresent("index", indexingNumber);
// Write the geometry, unless otherwise specified
bool doGeometry = !args.found("no-mesh");
if (nearCellValue)
{
Info<< "Using neighbouring cell value instead of patch value"
<< nl << endl;
}
if (!doPointValues)
{
Info<< "Point fields and interpolated point data"
<< " disabled with the '-no-point-data' option"
<< nl;
}
//
// General (case) output options
//
ensightCase::options caseOpts(format);
caseOpts.nodeValues(args.found("nodeValues"));
// Forced point interpolation?
caseOpts.nodeValues(doPointValues && args.found("nodeValues"));
caseOpts.width(args.get<label>("width", 8));
caseOpts.overwrite(true); // remove existing output directory
caseOpts.overwrite(!args.found("no-overwrite")); // Remove existing?
// Can also have separate directory for lagrangian
// caseOpts.separateCloud(true);
// Define sub-directory name to use for EnSight data.
// The path to the ensight directory is at case level only
// - For parallel cases, data only written from master
......@@ -261,23 +372,32 @@ int main(int argc, char *argv[])
outputDir = args.globalPath()/outputDir;
}
//
// Output configuration (geometry related)
//
ensightMesh::options writeOpts(format);
writeOpts.useBoundaryMesh(!args.found("no-boundary"));
writeOpts.useInternalMesh(!args.found("no-internal"));
const bool doLagrangian = !args.found("no-lagrangian");
ensightMesh::options writeOpts;
writeOpts.useBoundaryMesh(doBoundary);
writeOpts.useInternalMesh(doInternal);
writeOpts.useCellZones(doCellZones);
if (args.found("patches"))
{
writeOpts.patchSelection(args.getList<wordRe>("patches"));
}
if (args.found("excludePatches"))
{
writeOpts.patchExclude(args.getList<wordRe>("excludePatches"));
}
if (args.found("faceZones"))
{
writeOpts.faceZoneSelection(args.getList<wordRe>("faceZones"));
}
if (args.found("cellZones"))
{
writeOpts.cellZoneSelection(args.getList<wordRe>("cellZones"));
}
// Report the setup
writeOpts.print(Info);
//
// Output configuration (field related)
......@@ -286,22 +406,11 @@ int main(int argc, char *argv[])
wordRes fieldPatterns;
args.readListIfPresent<wordRe>("fields", fieldPatterns);
word cellZoneName;
if (args.readIfPresent("cellZone", cellZoneName))
{
Info<< "Converting cellZone " << cellZoneName
<< " only, with new outside faces as \"oldInternalFaces\"."
<< nl;
}
// Ignored (unproxied) if cellZoneName is empty
fvMeshSubsetProxy meshProxy(mesh, fvMeshSubsetProxy::ZONE, cellZoneName);
// New ensight case file, initialize header etc.
ensightCase ensCase(outputDir, args.globalCaseName(), caseOpts);
// Construct the Ensight mesh
ensightMesh ensMesh(meshProxy.mesh(), writeOpts);
// Construct ensight mesh
ensightMesh ensMesh(mesh, writeOpts);
if (Pstream::master())
{
......@@ -335,50 +444,64 @@ int main(int argc, char *argv[])
// Remove "*_0" restart fields
objects.prune_0();
// Only retain volume and dimensioned fields.
objects.filterClasses
(
[](const word& clsName){
return
(
fieldTypes::volume.found(clsName)
|| fieldTypes::internal.found(clsName)
);
}
);
if (!doPointValues)
{
// Prune point fields if disabled
objects.filterClasses
(
[](const word& clsName)
{
return fieldTypes::point.found(clsName);
},
true // prune
);
}
wordList objectNames(objects.sortedNames());
// Check availability for all times...
checkData(meshProxy.baseMesh(), timeDirs, objectNames);
checkData(mesh, timeDirs, objectNames);
testedObjectNames = objectNames;
}
if (hasMovingMesh && !doGeometry)
{
Info<< "has moving mesh: ignoring '-no-mesh' option" << endl;
doGeometry = true;
}
forAll(timeDirs, timeIndex)
forAll(timeDirs, timei)
{
runTime.setTime(timeDirs[timeIndex], timeIndex);