Files
openfoam/applications/utilities/postProcessing/dataConversion/foamToEnsight/foamToEnsight.C
Mark Olesen 2fb382bf8a ENH: multiple zone selection for fvMeshSubsetProxy (#973)
- handle tmp fields in interpolate methods

- special method interpolateInternal() for creating a volume field
  with zero-gradient treatment for patches from an internal field.

  This method was previously also called interpolate(), but that
  masked the ability to subset the internal field only.

  Ensight output needs the volume field:
      uses interpolateInternal().

  VTK output has separate handling of internal and patch fields:
      uses interpolate().

ENH: added fvMeshSubset mesh() method for baseMesh or subMesh.

- simplies coding when the fvMeshSubset may or may not be in active use.

ENH: update foamToEnsight to use newer methods in wrapped form

- static interpolate functions with renaming for manual use with
  fvMeshSubset (when fvMeshSubsetProxy may be too limiting in
  functionality)
2018-10-02 17:06:44 +02:00

760 lines
22 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation | Copyright (C) 2016-2018 OpenCFD Ltd.
-------------------------------------------------------------------------------
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/>.
Application
foamToEnsight
Group
grpPostProcessingUtilitie
Description
Translates OpenFOAM data to EnSight format.
An Ensight part is created for the internalMesh and for each patch.
Usage
\b foamToEnsight [OPTION]
Options:
- \par -ascii
Write Ensight data in ASCII format instead of "C Binary"
- \par -noZero
Exclude the often incomplete initial conditions.
- \par -noLagrangian
Suppress writing lagrangian positions and fields.
- \par -noPatches
Suppress writing any patches.
- \par -patches patchList
Specify particular patches to write.
Specifying an empty list suppresses writing the internalMesh.
- \par -faceZones zoneList
Specify faceZones to write, with wildcards
- \par -cellZone zoneName
Specify single cellZone to write (not lagrangian)
- \par -width \<n\>
Width of EnSight data subdir (default: 8)
Note
Writes to \a EnSight directory to avoid collisions with
foamToEnsightParts
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "timeSelector.H"
#include "IOobjectList.H"
#include "IOmanip.H"
#include "OFstream.H"
#include "PstreamCombineReduceOps.H"
#include "HashOps.H"
#include "fvc.H"
#include "volFields.H"
#include "hashedWordList.H"
#include "labelIOField.H"
#include "scalarIOField.H"
#include "tensorIOField.H"
// file-format/conversion
#include "ensightCase.H"
#include "ensightGeoFile.H"
#include "ensightMesh.H"
#include "ensightOutput.H"
#include "fvMeshSubsetProxy.H"
// local files
#include "ensightOutputCloud.H"
#include "memInfo.H"
using namespace Foam;
//- Get the field and subset it
template<class GeoField>
tmp<GeoField> getField(IOobject& io, const fvMeshSubsetProxy& proxy)
{
auto tfield = tmp<GeoField>::New(io, proxy.baseMesh());
return proxy.interpolate(tfield);
}
//- Get the field and subset it, or return nullptr
template<class GeoField>
tmp<GeoField> getField(const IOobject* io, const fvMeshSubsetProxy& proxy)
{
if (io)
{
auto tfield = tmp<GeoField>::New(*io, proxy.baseMesh());
return proxy.interpolate(tfield);
}
return nullptr;
}
//- 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);
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
timeSelector::addOptions();
#include "addRegionOption.H"
argList::addBoolOption
(
"ascii",
"Write in ASCII format instead of 'C Binary'"
);
argList::addBoolOption
(
"nodeValues",
"Write values in nodes"
);
argList::addBoolOption
(
"noLagrangian",
"Suppress writing lagrangian positions and fields"
);
argList::addBoolOption
(
"noPatches",
"Suppress writing any patches"
);
argList::addOption
(
"patches",
"wordRes",
"Specify particular patches to write - eg '(outlet \"inlet.*\")'. "
"An empty list suppresses writing the internalMesh."
);
argList::addOption
(
"faceZones",
"wordRes",
"Specify faceZones to write - eg '( slice \"mfp-.*\" )'."
);
argList::addOption
(
"fields",
"wordRes",
"Specify fields to export (all by default) - eg '( \"U.*\" )'."
);
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"
);
// The volume field types that we handle
const hashedWordList volFieldTypes
{
volScalarField::typeName,
volVectorField::typeName,
volSphericalTensorField::typeName,
volSymmTensorField::typeName,
volTensorField::typeName,
volScalarField::Internal::typeName,
volVectorField::Internal::typeName,
volSphericalTensorField::Internal::typeName,
volSymmTensorField::Internal::typeName,
volTensorField::Internal::typeName
};
#include "setRootCase.H"
// Default to binary output, unless otherwise specified
const IOstream::streamFormat format =
(
args.found("ascii")
? IOstream::ASCII
: IOstream::BINARY
);
const bool nodeValues = args.found("nodeValues");
cpuTime timer;
memInfo mem;
Info<< "Initial memory " << mem.update().size() << " kB" << endl;
#include "createTime.H"
instantList timeDirs = timeSelector::select0(runTime, args);
#include "createNamedMesh.H"
fileName regionPrefix; // Mesh instance (region0 gets filtered out)
if (regionName != polyMesh::defaultRegion)
{
regionPrefix = regionName;
}
//
// General (case) output options
//
ensightCase::options caseOpts(format);
caseOpts.nodeValues(args.found("nodeValues"));
caseOpts.width(args.lookupOrDefault<label>("width", 8));
caseOpts.overwrite(true); // remove existing output directory
// 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
fileName ensightDir = args.lookupOrDefault<word>("name", "EnSight");
if (!ensightDir.isAbsolute())
{
ensightDir = args.globalPath()/ensightDir;
}
//
// Output configuration (geometry related)
//
ensightMesh::options writeOpts(format);
writeOpts.noPatches(args.found("noPatches"));
if (args.found("patches"))
{
writeOpts.patchSelection(args.getList<wordRe>("patches"));
}
if (args.found("faceZones"))
{
writeOpts.faceZoneSelection(args.getList<wordRe>("faceZones"));
}
//
// output configuration (field related)
//
const bool noLagrangian = args.found("noLagrangian");
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);
//
// Open new ensight case file, initialize header etc.
//
ensightCase ensCase
(
ensightDir,
args.globalCaseName(),
caseOpts
);
// Construct the Ensight mesh
ensightMesh ensMesh(meshProxy.mesh(), writeOpts);
if (Pstream::master())
{
Info<< "Converting " << timeDirs.size() << " time steps" << nl;
ensCase.printInfo(Info) << endl;
}
#include "checkMeshMoving.H"
#include "findCloudFields.H"
// test the pre-check variable if there is a moving mesh
// time-set for geometries
// TODO: split off into separate time-set,
// but need to verify ensight spec
Info<< "Startup in "
<< timer.cpuTimeIncrement() << " s, "
<< mem.update().size() << " kB" << nl << endl;
// Get the list of supported classes/fields
HashTable<wordHashSet> usableObjects;
{
// Initially all possible objects that are available at the final time
IOobjectList objects(mesh, timeDirs.last().name());
// Categorize by classes, pre-filter on name (if requested)
usableObjects =
(
fieldPatterns.empty()
? objects.classes()
: objects.classes(fieldPatterns)
);
// Limit to types that we explicitly handle
usableObjects.filterKeys(volFieldTypes);
// Force each field-type into existence (simplifies code logic
// and doesn't cost much) and simultaneously remove all
// "*_0" restart fields
for (const word& fieldType : volFieldTypes)
{
usableObjects
(
fieldType
).filterKeys
(
[](const word& k){ return k.endsWith("_0"); },
true // prune
);
}
}
// ignore special fields (_0 fields),
// ignore fields we don't handle,
// ignore fields that are not available for all time-steps
HashTable<bool> fieldsToUse;
forAll(timeDirs, timeIndex)
{
runTime.setTime(timeDirs[timeIndex], timeIndex);
ensCase.nextTime(timeDirs[timeIndex]);
Info<< "Time [" << timeIndex << "] = " << runTime.timeName() << nl;
polyMesh::readUpdateState meshState = mesh.readUpdate();
if (meshState != polyMesh::UNCHANGED)
{
meshProxy.correct();
ensMesh.expire();
ensMesh.correct();
}
if (timeIndex == 0 || meshMoving)
{
autoPtr<ensightGeoFile> os = ensCase.newGeometry(meshMoving);
ensMesh.write(os);
}
// Cell field data output
// ~~~~~~~~~~~~~~~~~~~~~~
Info<< "Write volume field (";
for (const word& fieldType : volFieldTypes)
{
// For convenience, just force each field-type into existence.
// This simplifies code logic and doesn't cost much at all.
wordHashSet& fieldNames = usableObjects(fieldType);
forAllIters(fieldNames, fieldIter)
{
const word& fieldName = fieldIter.key();
#include "checkData.H"
// Partially complete field?
if (!fieldsToUse[fieldName])
{
fieldNames.erase(fieldIter);
continue;
}
IOobject fieldObject
(
fieldName,
mesh.time().timeName(),
mesh,
IOobject::MUST_READ,
IOobject::NO_WRITE
);
bool wrote = false;
if (fieldType == volScalarField::typeName)
{
autoPtr<ensightFile> os = ensCase.newData<scalar>
(
fieldName
);
wrote = ensightOutput::writeField<scalar>
(
getField<volScalarField>(fieldObject, meshProxy),
ensMesh,
os,
nodeValues
);
}
else if (fieldType == volVectorField::typeName)
{
autoPtr<ensightFile> os = ensCase.newData<vector>
(
fieldName
);
wrote = ensightOutput::writeField<vector>
(
getField<volVectorField>(fieldObject, meshProxy),
ensMesh,
os,
nodeValues
);
}
else if (fieldType == volSphericalTensorField::typeName)
{
autoPtr<ensightFile> os = ensCase.newData<sphericalTensor>
(
fieldObject.name()
);
wrote = ensightOutput::writeField<sphericalTensor>
(
getField<volSphericalTensorField>
(
fieldObject,
meshProxy
),
ensMesh,
os,
nodeValues
);
}
else if (fieldType == volSymmTensorField::typeName)
{
autoPtr<ensightFile> os = ensCase.newData<symmTensor>
(
fieldName
);
wrote = ensightOutput::writeField<symmTensor>
(
getField<volSymmTensorField>
(
fieldObject,
meshProxy
),
ensMesh,
os,
nodeValues
);
}
else if (fieldType == volTensorField::typeName)
{
autoPtr<ensightFile> os = ensCase.newData<tensor>
(
fieldName
);
wrote = ensightOutput::writeField<tensor>
(
getField<volTensorField>(fieldObject, meshProxy),
ensMesh,
os,
nodeValues
);
}
// DimensionedFields
else if
(
fieldType
== volScalarField::Internal::typeName
)
{
autoPtr<ensightFile> os = ensCase.newData<scalar>
(
fieldName
);
wrote = ensightOutput::writeField<scalar>
(
getZeroGradInternalField<volScalarField>
(
fieldObject,
meshProxy
),
ensMesh,
os,
nodeValues
);
}
else if
(
fieldType
== volVectorField::Internal::typeName
)
{
autoPtr<ensightFile> os = ensCase.newData<vector>
(
fieldName
);
wrote = ensightOutput::writeField<vector>
(
getZeroGradInternalField<volVectorField>
(
fieldObject,
meshProxy
),
ensMesh,
os,
nodeValues
);
}
else if
(
fieldType
== volSphericalTensorField::Internal::typeName
)
{
autoPtr<ensightFile> os = ensCase.newData<sphericalTensor>
(
fieldName
);
wrote = ensightOutput::writeField<sphericalTensor>
(
getZeroGradInternalField<volSphericalTensorField>
(
fieldObject,
meshProxy
),
ensMesh,
os,
nodeValues
);
}
else if
(
fieldType
== volSymmTensorField::Internal::typeName
)
{
autoPtr<ensightFile> os = ensCase.newData<symmTensor>
(
fieldName
);
wrote = ensightOutput::writeField<symmTensor>
(
getZeroGradInternalField<volSymmTensorField>
(
fieldObject,
meshProxy
),
ensMesh,
os,
nodeValues
);
}
else if
(
fieldType
== volTensorField::Internal::typeName
)
{
autoPtr<ensightFile> os = ensCase.newData<tensor>
(
fieldName
);
wrote = ensightOutput::writeField<tensor>
(
getZeroGradInternalField<volTensorField>
(
fieldObject,
meshProxy
),
ensMesh,
os,
nodeValues
);
}
else
{
// Do not currently handle this type
// - blacklist for the future.
fieldsToUse.set(fieldName, false);
}
if (wrote)
{
Info<< ' ' << fieldName;
}
}
}
Info<< " )" << nl;
// Cloud field data output
// ~~~~~~~~~~~~~~~~~~~~~~~
forAll(cloudNames, cloudNo)
{
const word& cloudName = cloudNames[cloudNo];
const HashTable<word>& theseCloudFields = cloudFields[cloudName];
fileNameList currentCloudDirs = readDir
(
runTime.timePath()/regionPrefix/cloud::prefix,
fileName::DIRECTORY
);
Info<< "Write " << cloudName << " (";
const bool cloudExists =
returnReduce
(
currentCloudDirs.found(cloudName),
orOp<bool>()
);
{
autoPtr<ensightFile> os = ensCase.newCloud(cloudName);
ensightCloud::writePositions
(
mesh,
cloudName,
cloudExists,
os
);
Info<< " positions";
if (!cloudExists)
{
Info<< "{0}"; // report empty field
}
}
forAllConstIters(theseCloudFields, fieldIter)
{
const word& fieldName = fieldIter.key();
const word& fieldType = fieldIter.object();
IOobject fieldObject
(
fieldName,
mesh.time().timeName(),
cloud::prefix/cloudName,
mesh,
IOobject::MUST_READ
);
bool fieldExists = cloudExists; // No field without positions
if (cloudExists)
{
// Want MUST_READ (globally) and valid=false (locally),
// but that combination does not work.
// So check the header and sync globally
fieldExists =
fieldObject.typeHeaderOk<IOField<scalar>>(false);
reduce(fieldExists, orOp<bool>());
}
bool wrote = false;
if (fieldType == scalarIOField::typeName)
{
autoPtr<ensightFile> os =
ensCase.newCloudData<scalar>(cloudName, fieldName);
wrote = ensightCloud::writeCloudField<scalar>
(
fieldObject, fieldExists, os
);
}
else if (fieldType == vectorIOField::typeName)
{
autoPtr<ensightFile> os =
ensCase.newCloudData<vector>(cloudName, fieldName);
wrote = ensightCloud::writeCloudField<vector>
(
fieldObject, fieldExists, os
);
}
if (wrote)
{
Info<< ' ' << fieldName;
if (!fieldExists)
{
Info<< "{0}"; // report empty field
}
}
}
Info<< " )" << nl;
}
Info<< "Wrote in "
<< timer.cpuTimeIncrement() << " s, "
<< mem.update().size() << " kB" << nl << nl;
}
ensCase.write();
Info<< "End: "
<< timer.elapsedCpuTime() << " s, "
<< mem.update().peak() << " kB (peak)" << nl << endl;
return 0;
}
// ************************************************************************* //