mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
ENH: improvements for vtkWrite function object (issue #926)
- parallel output. The output is now postProcessing/<name> for similar reasoning as mentioned in #866 - better alignment with other function objects, no collision with foamToVTK output. - align the input parameters with those of vtkCloud so that we can specify the ASCII precision and the padding width for the output file names as well. - emit TimeValue field, support file series generation - support internal or boundary meshes, combining the result into a vtm file. - can restrict conversion based on zone names, enclosing volumes, bounding box
This commit is contained in:
@ -3,6 +3,7 @@ abort/abort.C
|
|||||||
codedFunctionObject/codedFunctionObject.C
|
codedFunctionObject/codedFunctionObject.C
|
||||||
ensightWrite/ensightWrite.C
|
ensightWrite/ensightWrite.C
|
||||||
vtkWrite/vtkWrite.C
|
vtkWrite/vtkWrite.C
|
||||||
|
vtkWrite/vtkWriteUpdate.C
|
||||||
|
|
||||||
removeRegisteredObject/removeRegisteredObject.C
|
removeRegisteredObject/removeRegisteredObject.C
|
||||||
|
|
||||||
|
|||||||
@ -27,7 +27,11 @@ License
|
|||||||
#include "dictionary.H"
|
#include "dictionary.H"
|
||||||
#include "Time.H"
|
#include "Time.H"
|
||||||
#include "areaFields.H"
|
#include "areaFields.H"
|
||||||
|
#include "stringListOps.H"
|
||||||
#include "foamVtkInternalWriter.H"
|
#include "foamVtkInternalWriter.H"
|
||||||
|
#include "foamVtkPatchWriter.H"
|
||||||
|
#include "foamVtkSeriesWriter.H"
|
||||||
|
#include "foamVtmWriter.H"
|
||||||
#include "addToRunTimeSelectionTable.H"
|
#include "addToRunTimeSelectionTable.H"
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||||
@ -42,6 +46,73 @@ namespace functionObjects
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||||
|
|
||||||
|
Foam::label Foam::functionObjects::vtkWrite::writeAllVolFields
|
||||||
|
(
|
||||||
|
autoPtr<vtk::internalWriter>& internalWriter,
|
||||||
|
UPtrList<vtk::patchWriter>& patchWriters,
|
||||||
|
const fvMeshSubset& proxy,
|
||||||
|
const wordHashSet& acceptField
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
#undef vtkWrite_WRITE_FIELD
|
||||||
|
#define vtkWrite_WRITE_FIELD(FieldType) \
|
||||||
|
writeVolFields<FieldType> \
|
||||||
|
( \
|
||||||
|
internalWriter, \
|
||||||
|
patchWriters, \
|
||||||
|
proxy, \
|
||||||
|
acceptField \
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
label count = 0;
|
||||||
|
count += vtkWrite_WRITE_FIELD(volScalarField);
|
||||||
|
count += vtkWrite_WRITE_FIELD(volVectorField);
|
||||||
|
count += vtkWrite_WRITE_FIELD(volSphericalTensorField);
|
||||||
|
count += vtkWrite_WRITE_FIELD(volSymmTensorField);
|
||||||
|
count += vtkWrite_WRITE_FIELD(volTensorField);
|
||||||
|
|
||||||
|
#undef vtkWrite_WRITE_FIELD
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Foam::label Foam::functionObjects::vtkWrite::writeAllVolFields
|
||||||
|
(
|
||||||
|
autoPtr<vtk::internalWriter>& internalWriter,
|
||||||
|
const autoPtr<volPointInterpolation>& pInterp,
|
||||||
|
|
||||||
|
UPtrList<vtk::patchWriter>& patchWriters,
|
||||||
|
const UPtrList<PrimitivePatchInterpolation<primitivePatch>>& patchInterps,
|
||||||
|
const fvMeshSubset& proxy,
|
||||||
|
const wordHashSet& acceptField
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
#undef vtkWrite_WRITE_FIELD
|
||||||
|
#define vtkWrite_WRITE_FIELD(FieldType) \
|
||||||
|
writeVolFields<FieldType> \
|
||||||
|
( \
|
||||||
|
internalWriter, pInterp, \
|
||||||
|
patchWriters, patchInterps, \
|
||||||
|
proxy, \
|
||||||
|
acceptField \
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
label count = 0;
|
||||||
|
count += vtkWrite_WRITE_FIELD(volScalarField);
|
||||||
|
count += vtkWrite_WRITE_FIELD(volVectorField);
|
||||||
|
count += vtkWrite_WRITE_FIELD(volSphericalTensorField);
|
||||||
|
count += vtkWrite_WRITE_FIELD(volSymmTensorField);
|
||||||
|
count += vtkWrite_WRITE_FIELD(volTensorField);
|
||||||
|
|
||||||
|
#undef vtkWrite_WRITE_FIELD
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
Foam::functionObjects::vtkWrite::vtkWrite
|
Foam::functionObjects::vtkWrite::vtkWrite
|
||||||
@ -51,19 +122,37 @@ Foam::functionObjects::vtkWrite::vtkWrite
|
|||||||
const dictionary& dict
|
const dictionary& dict
|
||||||
)
|
)
|
||||||
:
|
:
|
||||||
fvMeshFunctionObject(name, runTime, dict),
|
functionObject(name),
|
||||||
|
time_(runTime),
|
||||||
|
outputDir_(),
|
||||||
|
printf_(),
|
||||||
writeOpts_(vtk::formatType::INLINE_BASE64),
|
writeOpts_(vtk::formatType::INLINE_BASE64),
|
||||||
|
verbose_(true),
|
||||||
|
doInternal_(true),
|
||||||
|
doBoundary_(true),
|
||||||
|
oneBoundary_(false),
|
||||||
|
interpolate_(false),
|
||||||
|
decompose_(false),
|
||||||
|
writeIds_(false),
|
||||||
|
meshState_(polyMesh::TOPO_CHANGE),
|
||||||
|
selectRegions_(),
|
||||||
|
selectPatches_(),
|
||||||
selectFields_(),
|
selectFields_(),
|
||||||
dirName_("VTK")
|
selection_(),
|
||||||
|
meshes_(),
|
||||||
|
meshSubsets_(),
|
||||||
|
vtuMappings_(),
|
||||||
|
series_()
|
||||||
{
|
{
|
||||||
if (postProcess)
|
// May still want this? (OCT-2018)
|
||||||
{
|
// if (postProcess)
|
||||||
// Disable for post-process mode.
|
// {
|
||||||
// Emit as FatalError for the try/catch in the caller.
|
// // Disable for post-process mode.
|
||||||
FatalError
|
// // Emit as FatalError for the try/catch in the caller.
|
||||||
<< type() << " disabled in post-process mode"
|
// FatalError
|
||||||
<< exit(FatalError);
|
// << type() << " disabled in post-process mode"
|
||||||
}
|
// << exit(FatalError);
|
||||||
|
// }
|
||||||
|
|
||||||
read(dict);
|
read(dict);
|
||||||
}
|
}
|
||||||
@ -73,16 +162,23 @@ Foam::functionObjects::vtkWrite::vtkWrite
|
|||||||
|
|
||||||
bool Foam::functionObjects::vtkWrite::read(const dictionary& dict)
|
bool Foam::functionObjects::vtkWrite::read(const dictionary& dict)
|
||||||
{
|
{
|
||||||
fvMeshFunctionObject::read(dict);
|
functionObject::read(dict);
|
||||||
|
|
||||||
|
readSelection(dict);
|
||||||
|
|
||||||
|
// We probably cannot trust old information after a reread
|
||||||
|
series_.clear();
|
||||||
|
|
||||||
|
// verbose_ = dict.lookupOrDefault<bool>("verbose", true);
|
||||||
|
doInternal_ = dict.lookupOrDefault<bool>("internal", true);
|
||||||
|
doBoundary_ = dict.lookupOrDefault<bool>("boundary", true);
|
||||||
|
oneBoundary_ = dict.lookupOrDefault<bool>("single", false);
|
||||||
|
interpolate_ = dict.lookupOrDefault<bool>("interpolate", false);
|
||||||
|
|
||||||
//
|
//
|
||||||
// writer options - default is xml base64
|
// Writer options - default is xml base64
|
||||||
//
|
//
|
||||||
writeOpts_ = vtk::formatType::INLINE_BASE64;
|
writeOpts_ = vtk::formatType::INLINE_BASE64;
|
||||||
if (dict.lookupOrDefault("legacy", false))
|
|
||||||
{
|
|
||||||
writeOpts_.legacy(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeOpts_.ascii
|
writeOpts_.ascii
|
||||||
(
|
(
|
||||||
@ -90,33 +186,63 @@ bool Foam::functionObjects::vtkWrite::read(const dictionary& dict)
|
|||||||
&& (IOstream::formatEnum(dict.get<word>("format")) == IOstream::ASCII)
|
&& (IOstream::formatEnum(dict.get<word>("format")) == IOstream::ASCII)
|
||||||
);
|
);
|
||||||
|
|
||||||
// FUTURE?
|
if (dict.lookupOrDefault("legacy", false))
|
||||||
// writeOpts_.precision
|
{
|
||||||
// (
|
writeOpts_.legacy(true);
|
||||||
// dict.lookupOrDefault
|
}
|
||||||
// (
|
|
||||||
// "writePrecision",
|
writeOpts_.precision
|
||||||
// IOstream::defaultPrecision()
|
(
|
||||||
// )
|
dict.lookupOrDefault
|
||||||
// );
|
(
|
||||||
|
"precision",
|
||||||
|
IOstream::defaultPrecision()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// Info<< type() << " " << name() << " output-format: "
|
// Info<< type() << " " << name() << " output-format: "
|
||||||
// << writeOpts_.description() << nl;
|
// << writeOpts_.description() << nl;
|
||||||
|
|
||||||
|
const int padWidth = dict.lookupOrDefault<int>("width", 8);
|
||||||
|
|
||||||
|
// Appropriate printf format - Enforce min/max sanity limits
|
||||||
|
if (padWidth < 1 || padWidth > 31)
|
||||||
|
{
|
||||||
|
printf_.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf_ = "%0" + std::to_string(padWidth) + "d";
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// other options
|
// Other options
|
||||||
//
|
//
|
||||||
dict.readIfPresent("directory", dirName_);
|
|
||||||
|
|
||||||
decompose_ = dict.lookupOrDefault("decompose", false);
|
decompose_ = dict.lookupOrDefault("decompose", false);
|
||||||
writeIds_ = dict.lookupOrDefault("writeIds", false);
|
writeIds_ = dict.lookupOrDefault("writeIds", false);
|
||||||
|
|
||||||
|
|
||||||
//
|
// Output directory
|
||||||
// output fields
|
|
||||||
//
|
outputDir_.clear();
|
||||||
dict.readEntry("fields", selectFields_);
|
dict.readIfPresent("directory", outputDir_);
|
||||||
selectFields_.uniq();
|
|
||||||
|
if (outputDir_.size())
|
||||||
|
{
|
||||||
|
// User-defined output directory
|
||||||
|
outputDir_.expand();
|
||||||
|
if (!outputDir_.isAbsolute())
|
||||||
|
{
|
||||||
|
outputDir_ = time_.globalPath()/outputDir_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Standard postProcessing/ naming
|
||||||
|
outputDir_ = time_.globalPath()/functionObject::outputPrefix/name();
|
||||||
|
}
|
||||||
|
outputDir_.clean();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -133,96 +259,515 @@ bool Foam::functionObjects::vtkWrite::write()
|
|||||||
// const word timeDesc =
|
// const word timeDesc =
|
||||||
// useTimeName ? time_.timeName() : Foam::name(time_.timeIndex());
|
// useTimeName ? time_.timeName() : Foam::name(time_.timeIndex());
|
||||||
|
|
||||||
const word timeDesc = time_.timeName();
|
const word timeDesc = "_" +
|
||||||
|
(
|
||||||
|
printf_.empty()
|
||||||
|
? Foam::name(time_.timeIndex())
|
||||||
|
: word::printf(printf_, time_.timeIndex())
|
||||||
|
);
|
||||||
|
|
||||||
fileName vtkDir = dirName_;
|
const scalar timeValue = time_.value();
|
||||||
if (!vtkDir.isAbsolute())
|
|
||||||
|
update();
|
||||||
|
|
||||||
|
if (meshes_.empty() || (!doInternal_ && !doBoundary_))
|
||||||
{
|
{
|
||||||
vtkDir = time_.path()/vtkDir;
|
// Skip
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
mkDir(vtkDir);
|
|
||||||
|
|
||||||
string vtkName = time_.caseName();
|
|
||||||
|
|
||||||
if (Pstream::parRun())
|
fileName vtkName = time_.globalCaseName();
|
||||||
|
|
||||||
|
vtk::vtmWriter vtmMultiRegion;
|
||||||
|
|
||||||
|
Info<< name() << " output Time: " << time_.timeName() << nl;
|
||||||
|
|
||||||
|
label regioni = 0;
|
||||||
|
for (const word& regionName : meshes_.sortedToc())
|
||||||
{
|
{
|
||||||
// Strip off leading casename, leaving just processor_DDD ending.
|
fileName regionPrefix;
|
||||||
const auto i = vtkName.rfind("processor");
|
if (regionName != polyMesh::defaultRegion)
|
||||||
|
|
||||||
if (i != string::npos)
|
|
||||||
{
|
{
|
||||||
vtkName = vtkName.substr(i);
|
regionPrefix = regionName;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// internal mesh
|
auto& meshProxy = meshSubsets_[regioni];
|
||||||
{
|
auto& vtuMeshCells = vtuMappings_[regioni];
|
||||||
const fileName outputName
|
++regioni;
|
||||||
|
|
||||||
|
const fvMesh& baseMesh = meshProxy.baseMesh();
|
||||||
|
|
||||||
|
wordHashSet acceptField(baseMesh.names<void>(selectFields_));
|
||||||
|
|
||||||
|
// Prune restart fields
|
||||||
|
acceptField.filterKeys
|
||||||
(
|
(
|
||||||
vtkDir/vtkName
|
[](const word& k){ return k.endsWith("_0"); },
|
||||||
+ "_"
|
true // prune
|
||||||
+ timeDesc
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Info<< name() << " output Time: " << time_.timeName() << nl
|
const label nVolFields =
|
||||||
<< " Internal : " << outputName << endl;
|
(
|
||||||
|
(doInternal_ || doBoundary_)
|
||||||
// Number of fields to be written: only needed for legacy vtk format
|
? baseMesh.count
|
||||||
label nVolFields = 0;
|
|
||||||
if (writeOpts_.legacy())
|
|
||||||
{
|
|
||||||
nVolFields =
|
|
||||||
(
|
(
|
||||||
(writeIds_ ? 1 : 0)
|
stringListOps::foundOp<word>(fieldTypes::volume),
|
||||||
+ countFields<volScalarField>()
|
acceptField
|
||||||
+ countFields<volVectorField>()
|
)
|
||||||
+ countFields<volSphericalTensorField>()
|
: 0
|
||||||
+ countFields<volSymmTensorField>()
|
);
|
||||||
+ countFields<volTensorField>()
|
|
||||||
|
// Undecided if we want to automatically support DimensionedFields
|
||||||
|
// or only on demand:
|
||||||
|
const label nDimFields = 0;
|
||||||
|
// (
|
||||||
|
// (doInternal_ || doBoundary_)
|
||||||
|
// ? baseMesh.count
|
||||||
|
// (
|
||||||
|
// stringListOps::foundOp<word>(fieldTypes::internal),
|
||||||
|
// acceptField
|
||||||
|
// )
|
||||||
|
// : 0
|
||||||
|
// );
|
||||||
|
|
||||||
|
|
||||||
|
// Setup for the vtm writer.
|
||||||
|
// For legacy format, the information added is simply ignored.
|
||||||
|
|
||||||
|
fileName vtmOutputBase
|
||||||
|
(
|
||||||
|
outputDir_/regionPrefix/vtkName + timeDesc
|
||||||
|
);
|
||||||
|
|
||||||
|
// Combined internal + boundary in a vtm file
|
||||||
|
vtk::vtmWriter vtmWriter;
|
||||||
|
|
||||||
|
// Collect individual boundaries into a vtm file
|
||||||
|
vtk::vtmWriter vtmBoundaries;
|
||||||
|
|
||||||
|
// Setup the internal writer
|
||||||
|
autoPtr<vtk::internalWriter> internalWriter;
|
||||||
|
|
||||||
|
// Interpolator for volume and dimensioned fields
|
||||||
|
autoPtr<volPointInterpolation> pInterp;
|
||||||
|
|
||||||
|
if (doInternal_)
|
||||||
|
{
|
||||||
|
if (interpolate_)
|
||||||
|
{
|
||||||
|
pInterp.reset(new volPointInterpolation(meshProxy.mesh()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vtuMeshCells.empty())
|
||||||
|
{
|
||||||
|
// Use the appropriate mesh (baseMesh or subMesh)
|
||||||
|
vtuMeshCells.reset(meshProxy.mesh());
|
||||||
|
}
|
||||||
|
|
||||||
|
internalWriter = autoPtr<vtk::internalWriter>::New
|
||||||
|
(
|
||||||
|
meshProxy.mesh(),
|
||||||
|
vtuMeshCells,
|
||||||
|
writeOpts_,
|
||||||
|
// Output name for internal
|
||||||
|
(
|
||||||
|
writeOpts_.legacy()
|
||||||
|
? vtmOutputBase
|
||||||
|
: (vtmOutputBase / "internal")
|
||||||
|
),
|
||||||
|
Pstream::parRun()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Info<< " Internal : "
|
||||||
|
<< internalWriter->output().relative(time_.globalPath())
|
||||||
|
<< endl;
|
||||||
|
|
||||||
|
// No sub-block for internal
|
||||||
|
vtmWriter.append_vtu
|
||||||
|
(
|
||||||
|
"internal",
|
||||||
|
vtmOutputBase.name()/"internal"
|
||||||
|
);
|
||||||
|
|
||||||
|
internalWriter->writeTimeValue(timeValue);
|
||||||
|
internalWriter->writeGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
vtk::vtuCells vtuMeshCells
|
|
||||||
(
|
|
||||||
mesh_,
|
|
||||||
writeOpts_,
|
|
||||||
decompose_
|
|
||||||
);
|
|
||||||
|
|
||||||
// Write mesh
|
// Setup the patch writers
|
||||||
vtk::internalWriter writer
|
|
||||||
(
|
const polyBoundaryMesh& patches = meshProxy.mesh().boundaryMesh();
|
||||||
mesh_,
|
|
||||||
vtuMeshCells,
|
PtrList<vtk::patchWriter> patchWriters;
|
||||||
outputName,
|
PtrList<PrimitivePatchInterpolation<primitivePatch>> patchInterps;
|
||||||
writeOpts_
|
|
||||||
);
|
labelList patchIds;
|
||||||
|
if (doBoundary_)
|
||||||
|
{
|
||||||
|
patchIds = getSelectedPatches(patches);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oneBoundary_ && patchIds.size())
|
||||||
|
{
|
||||||
|
auto writer = autoPtr<vtk::patchWriter>::New
|
||||||
|
(
|
||||||
|
meshProxy.mesh(),
|
||||||
|
patchIds,
|
||||||
|
writeOpts_,
|
||||||
|
// Output name for one patch: "boundary"
|
||||||
|
(
|
||||||
|
writeOpts_.legacy()
|
||||||
|
? (outputDir_/regionPrefix/"boundary"/"boundary" + timeDesc)
|
||||||
|
: (vtmOutputBase / "boundary")
|
||||||
|
),
|
||||||
|
Pstream::parRun()
|
||||||
|
);
|
||||||
|
|
||||||
|
// No sub-block for one-patch
|
||||||
|
vtmWriter.append_vtp
|
||||||
|
(
|
||||||
|
"boundary",
|
||||||
|
vtmOutputBase.name()/"boundary"
|
||||||
|
);
|
||||||
|
|
||||||
|
Info<< " Boundaries: "
|
||||||
|
<< writer->output().relative(time_.globalPath()) << nl;
|
||||||
|
|
||||||
|
|
||||||
|
writer->writeTimeValue(timeValue);
|
||||||
|
writer->writeGeometry();
|
||||||
|
|
||||||
|
// Transfer writer to list for later use
|
||||||
|
patchWriters.resize(1);
|
||||||
|
patchWriters.set(0, writer);
|
||||||
|
|
||||||
|
// Avoid patchInterpolation for each sub-patch
|
||||||
|
patchInterps.resize(1); // == nullptr
|
||||||
|
}
|
||||||
|
else if (patchIds.size())
|
||||||
|
{
|
||||||
|
patchWriters.resize(patchIds.size());
|
||||||
|
if (interpolate_)
|
||||||
|
{
|
||||||
|
patchInterps.resize(patchIds.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
label nPatchWriters = 0;
|
||||||
|
label nPatchInterps = 0;
|
||||||
|
|
||||||
|
for (const label patchId : patchIds)
|
||||||
|
{
|
||||||
|
const polyPatch& pp = patches[patchId];
|
||||||
|
|
||||||
|
auto writer = autoPtr<vtk::patchWriter>::New
|
||||||
|
(
|
||||||
|
meshProxy.mesh(),
|
||||||
|
labelList(one(), pp.index()),
|
||||||
|
writeOpts_,
|
||||||
|
// Output name for patch: "boundary"/name
|
||||||
|
(
|
||||||
|
writeOpts_.legacy()
|
||||||
|
?
|
||||||
|
(
|
||||||
|
outputDir_/regionPrefix/pp.name()
|
||||||
|
/ (pp.name()) + timeDesc
|
||||||
|
)
|
||||||
|
: (vtmOutputBase / "boundary" / pp.name())
|
||||||
|
),
|
||||||
|
Pstream::parRun()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!nPatchWriters)
|
||||||
|
{
|
||||||
|
vtmWriter.beginBlock("boundary");
|
||||||
|
vtmBoundaries.beginBlock("boundary");
|
||||||
|
}
|
||||||
|
|
||||||
|
vtmWriter.append_vtp
|
||||||
|
(
|
||||||
|
pp.name(),
|
||||||
|
vtmOutputBase.name()/"boundary"/pp.name()
|
||||||
|
);
|
||||||
|
|
||||||
|
vtmBoundaries.append_vtp
|
||||||
|
(
|
||||||
|
pp.name(),
|
||||||
|
"boundary"/pp.name()
|
||||||
|
);
|
||||||
|
|
||||||
|
Info<< " Boundary : "
|
||||||
|
<< writer->output().relative(time_.globalPath()) << nl;
|
||||||
|
|
||||||
|
writer->writeTimeValue(timeValue);
|
||||||
|
writer->writeGeometry();
|
||||||
|
|
||||||
|
// Transfer writer to list for later use
|
||||||
|
patchWriters.set(nPatchWriters++, writer);
|
||||||
|
|
||||||
|
if (patchInterps.size())
|
||||||
|
{
|
||||||
|
patchInterps.set
|
||||||
|
(
|
||||||
|
nPatchInterps++,
|
||||||
|
new PrimitivePatchInterpolation<primitivePatch>(pp)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nPatchWriters)
|
||||||
|
{
|
||||||
|
vtmWriter.endBlock("boundary");
|
||||||
|
vtmBoundaries.endBlock("boundary");
|
||||||
|
}
|
||||||
|
|
||||||
|
patchWriters.resize(nPatchWriters);
|
||||||
|
patchInterps.resize(nPatchInterps);
|
||||||
|
}
|
||||||
|
|
||||||
// CellData
|
// CellData
|
||||||
{
|
{
|
||||||
writer.beginCellData(nVolFields);
|
if (internalWriter.valid())
|
||||||
|
|
||||||
// Write cellID field
|
|
||||||
if (writeIds_)
|
|
||||||
{
|
{
|
||||||
writer.writeCellIDs();
|
// cellIds + procIds (parallel)
|
||||||
|
internalWriter->beginCellData
|
||||||
|
(
|
||||||
|
(writeIds_ ? 1 + (internalWriter->parallel() ? 1 : 0) : 0)
|
||||||
|
+ (internalWriter->parallel() ? 1 : 0)
|
||||||
|
+ nVolFields + nDimFields
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write cellID field + procID (parallel only)
|
||||||
|
if (writeIds_)
|
||||||
|
{
|
||||||
|
internalWriter->writeCellIDs();
|
||||||
|
internalWriter->writeProcIDs(); // parallel only
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write volFields
|
if (nVolFields)
|
||||||
writeFields<volScalarField>(writer);
|
{
|
||||||
writeFields<volVectorField>(writer);
|
for (vtk::patchWriter& writer : patchWriters)
|
||||||
writeFields<volSphericalTensorField>(writer);
|
{
|
||||||
writeFields<volSymmTensorField>(writer);
|
writer.beginCellData
|
||||||
writeFields<volTensorField>(writer);
|
(
|
||||||
|
(writeIds_ ? 1 : 0)
|
||||||
|
+ nVolFields
|
||||||
|
);
|
||||||
|
if (writeIds_)
|
||||||
|
{
|
||||||
|
writer.writePatchIDs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
writer.endCellData();
|
writeAllVolFields
|
||||||
|
(
|
||||||
|
internalWriter,
|
||||||
|
patchWriters,
|
||||||
|
meshProxy,
|
||||||
|
acceptField
|
||||||
|
);
|
||||||
|
|
||||||
|
// writeAllDimFields
|
||||||
|
// (
|
||||||
|
// internalWriter,
|
||||||
|
// meshProxy,
|
||||||
|
// acceptField
|
||||||
|
// );
|
||||||
|
|
||||||
|
// End CellData is implicit
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.writeFooter();
|
|
||||||
|
// PointData
|
||||||
|
// - only construct pointMesh on request since it constructs
|
||||||
|
// edge addressing
|
||||||
|
if (interpolate_)
|
||||||
|
{
|
||||||
|
// Begin PointData
|
||||||
|
if (internalWriter.valid())
|
||||||
|
{
|
||||||
|
internalWriter->beginPointData
|
||||||
|
(
|
||||||
|
nVolFields + nDimFields
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
forAll(patchWriters, writeri)
|
||||||
|
{
|
||||||
|
const label nPatchFields =
|
||||||
|
(
|
||||||
|
writeri < patchInterps.size() && patchInterps.set(writeri)
|
||||||
|
? nVolFields
|
||||||
|
: 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nPatchFields)
|
||||||
|
{
|
||||||
|
patchWriters[writeri].beginPointData(nPatchFields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeAllVolFields
|
||||||
|
(
|
||||||
|
internalWriter, pInterp,
|
||||||
|
patchWriters, patchInterps,
|
||||||
|
meshProxy,
|
||||||
|
acceptField
|
||||||
|
);
|
||||||
|
|
||||||
|
// writeAllDimFields
|
||||||
|
// (
|
||||||
|
// internalWriter, pInterp,
|
||||||
|
// meshProxy,
|
||||||
|
// acceptField
|
||||||
|
// );
|
||||||
|
|
||||||
|
// writeAllPointFields
|
||||||
|
// (
|
||||||
|
// internalWriter,
|
||||||
|
// patchWriters,
|
||||||
|
// meshProxy,
|
||||||
|
// acceptField
|
||||||
|
// );
|
||||||
|
|
||||||
|
// End PointData is implicit
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Finish writers
|
||||||
|
if (internalWriter.valid())
|
||||||
|
{
|
||||||
|
internalWriter->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (vtk::patchWriter& writer : patchWriters)
|
||||||
|
{
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
pInterp.clear();
|
||||||
|
patchWriters.clear();
|
||||||
|
patchInterps.clear();
|
||||||
|
|
||||||
|
|
||||||
|
// Collective output
|
||||||
|
|
||||||
|
if (Pstream::master())
|
||||||
|
{
|
||||||
|
// Naming for vtm, file series etc.
|
||||||
|
fileName outputName(vtmOutputBase);
|
||||||
|
|
||||||
|
if (writeOpts_.legacy())
|
||||||
|
{
|
||||||
|
if (doInternal_)
|
||||||
|
{
|
||||||
|
// Add to file-series and emit as JSON
|
||||||
|
|
||||||
|
outputName.ext(vtk::legacy::fileExtension);
|
||||||
|
|
||||||
|
fileName seriesName(vtk::seriesWriter::base(outputName));
|
||||||
|
|
||||||
|
vtk::seriesWriter& series = series_(seriesName);
|
||||||
|
|
||||||
|
// First time?
|
||||||
|
// Load from file, verify against filesystem,
|
||||||
|
// prune time >= currentTime
|
||||||
|
if (series.empty())
|
||||||
|
{
|
||||||
|
series.load(seriesName, true, timeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
series.append(timeValue, timeDesc);
|
||||||
|
series.write(seriesName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (vtmWriter.size())
|
||||||
|
{
|
||||||
|
// Emit ".vtm"
|
||||||
|
|
||||||
|
outputName.ext(vtmWriter.ext());
|
||||||
|
|
||||||
|
vtmWriter.setTime(timeValue);
|
||||||
|
vtmWriter.write(outputName);
|
||||||
|
|
||||||
|
fileName seriesName(vtk::seriesWriter::base(outputName));
|
||||||
|
|
||||||
|
vtk::seriesWriter& series = series_(seriesName);
|
||||||
|
|
||||||
|
// First time?
|
||||||
|
// Load from file, verify against filesystem,
|
||||||
|
// prune time >= currentTime
|
||||||
|
if (series.empty())
|
||||||
|
{
|
||||||
|
series.load(seriesName, true, timeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
series.append(timeValue, outputName);
|
||||||
|
series.write(seriesName);
|
||||||
|
|
||||||
|
// Add to multi-region vtm
|
||||||
|
vtmMultiRegion.add(regionName, regionPrefix, vtmWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vtmBoundaries.size())
|
||||||
|
{
|
||||||
|
// Emit "boundary.vtm" with collection of boundaries
|
||||||
|
|
||||||
|
// Naming for vtm
|
||||||
|
fileName outputName(vtmOutputBase / "boundary");
|
||||||
|
outputName.ext(vtmBoundaries.ext());
|
||||||
|
|
||||||
|
vtmBoundaries.setTime(timeValue);
|
||||||
|
vtmBoundaries.write(outputName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Emit multi-region vtm
|
||||||
|
if (Pstream::master() && meshes_.size() > 1)
|
||||||
|
{
|
||||||
|
fileName outputName
|
||||||
|
(
|
||||||
|
outputDir_/vtkName + "-regions" + timeDesc + ".vtm"
|
||||||
|
);
|
||||||
|
|
||||||
|
vtmMultiRegion.setTime(timeValue);
|
||||||
|
vtmMultiRegion.write(outputName);
|
||||||
|
|
||||||
|
fileName seriesName(vtk::seriesWriter::base(outputName));
|
||||||
|
|
||||||
|
vtk::seriesWriter& series = series_(seriesName);
|
||||||
|
|
||||||
|
// First time?
|
||||||
|
// Load from file, verify against filesystem,
|
||||||
|
// prune time >= currentTime
|
||||||
|
if (series.empty())
|
||||||
|
{
|
||||||
|
series.load(seriesName, true, timeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
series.append(timeValue, outputName);
|
||||||
|
series.write(seriesName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Foam::functionObjects::vtkWrite::end()
|
||||||
|
{
|
||||||
|
meshSubsets_.clear();
|
||||||
|
vtuMappings_.clear();
|
||||||
|
meshes_.clear();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ************************************************************************* //
|
// ************************************************************************* //
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
========= |
|
========= |
|
||||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
\\ / O peration |
|
\\ / O peration |
|
||||||
\\ / A nd | Copyright (C) 2016 OpenFOAM Foundation
|
\\ / A nd | Copyright (C) 2017-2018 OpenCFD Ltd.
|
||||||
\\/ M anipulation | Copyright (C) 2017-2018 OpenCFD Ltd.
|
\\/ M anipulation |
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
License
|
License
|
||||||
This file is part of OpenFOAM.
|
This file is part of OpenFOAM.
|
||||||
@ -28,11 +28,8 @@ Group
|
|||||||
grpUtilitiesFunctionObjects
|
grpUtilitiesFunctionObjects
|
||||||
|
|
||||||
Description
|
Description
|
||||||
This functionObject writes objects registered to the database in VTK format.
|
Writes fields in VTK (xml or legacy) format.
|
||||||
|
Writes cell-values or point-interpolated values for volFields.
|
||||||
Currently only supports writing of the cell-values of volFields but
|
|
||||||
support for other field types, patch fields, Lagrangian data etc. will be
|
|
||||||
added.
|
|
||||||
|
|
||||||
Example of function object specification:
|
Example of function object specification:
|
||||||
\verbatim
|
\verbatim
|
||||||
@ -47,22 +44,80 @@ Description
|
|||||||
decompose false;
|
decompose false;
|
||||||
...
|
...
|
||||||
fields (U p);
|
fields (U p);
|
||||||
|
|
||||||
|
selection
|
||||||
|
{
|
||||||
|
box
|
||||||
|
{
|
||||||
|
action add;
|
||||||
|
source box;
|
||||||
|
box (-0.1 -0.01 -0.1) (0.1 0.30 0.1);
|
||||||
|
}
|
||||||
|
dome
|
||||||
|
{
|
||||||
|
action add;
|
||||||
|
shape sphere;
|
||||||
|
origin (-0.1 -0.01 -0.1);
|
||||||
|
radius 0.25;
|
||||||
|
}
|
||||||
|
centre
|
||||||
|
{
|
||||||
|
action subtract;
|
||||||
|
source sphere;
|
||||||
|
origin (-0.1 -0.01 -0.1);
|
||||||
|
radius 0.1;
|
||||||
|
}
|
||||||
|
blob
|
||||||
|
{
|
||||||
|
action add;
|
||||||
|
source surface;
|
||||||
|
surface triSurfaceMesh;
|
||||||
|
name blob.stl;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
\endverbatim
|
\endverbatim
|
||||||
|
|
||||||
Usage
|
\heading Basic Usage
|
||||||
\table
|
\table
|
||||||
Property | Description | Required | Default
|
Property | Description | Required | Default
|
||||||
type | Type name: vtkWrite | yes |
|
type | Type name: vtkWrite | yes |
|
||||||
fields | Fields to output | yes |
|
fields | Fields to output (wordRe list) | yes |
|
||||||
writeControl | Output control | recommended | timeStep
|
boundary | Convert boundary fields | no | true
|
||||||
directory | The output directory name | no | "VTK"
|
internal | Convert internal fields | no | true
|
||||||
format | ASCII or binary format | no | binary
|
single | Combine patches into a single boundary | no | false
|
||||||
legacy | Legacy VTK output | no | false
|
interpolate | Interpolate for point values | no | false
|
||||||
decompose | decompose polyhedra | no | false
|
|
||||||
writeIds | Write cell ids as field | no | true
|
|
||||||
\endtable
|
\endtable
|
||||||
|
|
||||||
|
\heading Output Options
|
||||||
|
\table
|
||||||
|
Property | Description | Required | Default
|
||||||
|
format | ascii or binary format | no | binary
|
||||||
|
legacy | Legacy VTK output | no | false
|
||||||
|
precision | Write precision in ascii | no | same as IOstream
|
||||||
|
directory | The output directory name | no | postProcessing/NAME
|
||||||
|
width | Padding width for file name | no | 8
|
||||||
|
decompose | Decompose polyhedral cells | no | false
|
||||||
|
writeIds | Write cell/patch ids as field | no | true
|
||||||
|
\endtable
|
||||||
|
|
||||||
|
\heading Output Selection
|
||||||
|
\table
|
||||||
|
Property | Description | Required | Default
|
||||||
|
region | Name for a single region | no | region0
|
||||||
|
regions | List of regions (wordRe list) | no |
|
||||||
|
patches | Limit to listed patches (wordRe list) | no |
|
||||||
|
selection | Cell selection (topoSet actions) | no | empty dict
|
||||||
|
\endtable
|
||||||
|
|
||||||
|
Note
|
||||||
|
The region of interest is defined by the selection dictionary
|
||||||
|
as a set of actions (add,subtract,subset,invert).
|
||||||
|
Omitting the selection dictionary is the same as specifying the
|
||||||
|
conversion of all cells (in the selected regions).
|
||||||
|
Omitting the patches entry is the same as specifying the conversion of all
|
||||||
|
patches.
|
||||||
|
|
||||||
See also
|
See also
|
||||||
Foam::functionObjects::ensightWrite
|
Foam::functionObjects::ensightWrite
|
||||||
Foam::functionObjects::fvMeshFunctionObject
|
Foam::functionObjects::fvMeshFunctionObject
|
||||||
@ -77,10 +132,12 @@ SourceFiles
|
|||||||
#ifndef functionObjects_vtkWrite_H
|
#ifndef functionObjects_vtkWrite_H
|
||||||
#define functionObjects_vtkWrite_H
|
#define functionObjects_vtkWrite_H
|
||||||
|
|
||||||
#include "fvMeshFunctionObject.H"
|
#include "functionObject.H"
|
||||||
#include "foamVtkInternalWriter.H"
|
#include "foamVtkInternalWriter.H"
|
||||||
#include "foamVtkSurfaceMeshWriter.H"
|
#include "foamVtkPatchWriter.H"
|
||||||
#include "wordRes.H"
|
#include "foamVtkSeriesWriter.H"
|
||||||
|
#include "fvMeshSubsetProxy.H"
|
||||||
|
#include "searchableSurfaces.H"
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
@ -95,18 +152,36 @@ namespace functionObjects
|
|||||||
|
|
||||||
class vtkWrite
|
class vtkWrite
|
||||||
:
|
:
|
||||||
public fvMeshFunctionObject
|
public functionObject
|
||||||
{
|
{
|
||||||
// Private data
|
// Private Data
|
||||||
|
|
||||||
|
//- Reference to the time database
|
||||||
|
const Time& time_;
|
||||||
|
|
||||||
|
//- The output directory
|
||||||
|
fileName outputDir_;
|
||||||
|
|
||||||
|
//- The printf format for zero-padding names
|
||||||
|
string printf_;
|
||||||
|
|
||||||
//- VTK output options
|
//- VTK output options
|
||||||
vtk::outputOptions writeOpts_;
|
vtk::outputOptions writeOpts_;
|
||||||
|
|
||||||
//- Name of fields to process
|
//- Verbose output
|
||||||
wordRes selectFields_;
|
bool verbose_;
|
||||||
|
|
||||||
//- Output directory name
|
//- Convert internal mesh
|
||||||
fileName dirName_;
|
bool doInternal_;
|
||||||
|
|
||||||
|
//- Convert boundary mesh
|
||||||
|
bool doBoundary_;
|
||||||
|
|
||||||
|
//- Combine patches into a single file
|
||||||
|
bool oneBoundary_;
|
||||||
|
|
||||||
|
//- Interpolate cell to point values
|
||||||
|
bool interpolate_;
|
||||||
|
|
||||||
//- Decompose polyhedra
|
//- Decompose polyhedra
|
||||||
bool decompose_;
|
bool decompose_;
|
||||||
@ -114,29 +189,99 @@ class vtkWrite
|
|||||||
//- Write cell ids field
|
//- Write cell ids field
|
||||||
bool writeIds_;
|
bool writeIds_;
|
||||||
|
|
||||||
|
//- Track changes in mesh geometry
|
||||||
|
enum polyMesh::readUpdateState meshState_;
|
||||||
|
|
||||||
|
//- Requested names of regions to process
|
||||||
|
wordRes selectRegions_;
|
||||||
|
|
||||||
|
//- Requested names of patches to process
|
||||||
|
wordRes selectPatches_;
|
||||||
|
|
||||||
|
//- Requested names of fields to process
|
||||||
|
wordRes selectFields_;
|
||||||
|
|
||||||
|
//- Dictionary of volume selections
|
||||||
|
dictionary selection_;
|
||||||
|
|
||||||
|
//- Pointers to the requested mesh regions
|
||||||
|
HashTable<const fvMesh*> meshes_;
|
||||||
|
|
||||||
|
//- Subsetting for meshes.
|
||||||
|
// Access index according to sorted mesh names.
|
||||||
|
PtrList<fvMeshSubset> meshSubsets_;
|
||||||
|
|
||||||
|
//- Storage for VTU cells, sizing.
|
||||||
|
// Access index according to sorted mesh names.
|
||||||
|
PtrList<vtk::vtuCells> vtuMappings_;
|
||||||
|
|
||||||
|
//- VTK file series
|
||||||
|
HashTable<vtk::seriesWriter, fileName> series_;
|
||||||
|
|
||||||
|
|
||||||
// Private Member Functions
|
// Private Member Functions
|
||||||
|
|
||||||
//- Count number of selected fields for GeoField type.
|
//- Update mesh subset according to zones, geometry, bounds
|
||||||
// Only needed for legacy vtk format.
|
bool updateSubset(fvMeshSubset& subsetter) const;
|
||||||
template<class GeoField>
|
|
||||||
label countFields() const;
|
|
||||||
|
|
||||||
//- Write selected fields for GeoField type.
|
//- Get patchIds selected in list
|
||||||
template<class GeoField>
|
labelList getSelectedPatches(const polyBoundaryMesh& patches) const;
|
||||||
label writeFields
|
|
||||||
|
//- Read information for selections
|
||||||
|
bool readSelection(const dictionary& dict);
|
||||||
|
|
||||||
|
//- Update meshes, subMeshes etc.
|
||||||
|
bool update();
|
||||||
|
|
||||||
|
|
||||||
|
// Write
|
||||||
|
|
||||||
|
//- Write all volume fields
|
||||||
|
label writeAllVolFields
|
||||||
(
|
(
|
||||||
vtk::internalWriter& writer,
|
autoPtr<vtk::internalWriter>& internalWriter,
|
||||||
bool verbose=true
|
UPtrList<vtk::patchWriter>& patchWriters,
|
||||||
|
const fvMeshSubset& proxy,
|
||||||
|
const wordHashSet& acceptField
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
|
//- Write all volume fields with point interpolation
|
||||||
//- Write selected fields for GeoField type.
|
label writeAllVolFields
|
||||||
template<class GeoField>
|
|
||||||
label writeFields
|
|
||||||
(
|
(
|
||||||
vtk::surfaceMeshWriter& writer,
|
autoPtr<vtk::internalWriter>& internalWriter,
|
||||||
bool verbose=true
|
const autoPtr<volPointInterpolation>& pInterp,
|
||||||
|
UPtrList<vtk::patchWriter>& patchWriters,
|
||||||
|
const UPtrList
|
||||||
|
<
|
||||||
|
PrimitivePatchInterpolation<primitivePatch>
|
||||||
|
>& patchInterps,
|
||||||
|
const fvMeshSubset& proxy,
|
||||||
|
const wordHashSet& acceptField
|
||||||
|
) const;
|
||||||
|
|
||||||
|
//- Write selected GeoField fields.
|
||||||
|
template<class GeoField>
|
||||||
|
label writeVolFields
|
||||||
|
(
|
||||||
|
autoPtr<vtk::internalWriter>& internalWriter,
|
||||||
|
UPtrList<vtk::patchWriter>& patchWriters,
|
||||||
|
const fvMeshSubset& proxy,
|
||||||
|
const wordHashSet& acceptField
|
||||||
|
) const;
|
||||||
|
|
||||||
|
//- Write selected GeoField fields with point interpolation
|
||||||
|
template<class GeoField>
|
||||||
|
label writeVolFields
|
||||||
|
(
|
||||||
|
autoPtr<vtk::internalWriter>& internalWriter,
|
||||||
|
const autoPtr<volPointInterpolation>& pInterp,
|
||||||
|
UPtrList<vtk::patchWriter>& patchWriters,
|
||||||
|
const UPtrList
|
||||||
|
<
|
||||||
|
PrimitivePatchInterpolation<primitivePatch>
|
||||||
|
>& patchInterps,
|
||||||
|
const fvMeshSubset& proxy,
|
||||||
|
const wordHashSet& acceptField
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
|
|
||||||
@ -173,11 +318,20 @@ public:
|
|||||||
//- Read the vtkWrite specification
|
//- Read the vtkWrite specification
|
||||||
virtual bool read(const dictionary& dict);
|
virtual bool read(const dictionary& dict);
|
||||||
|
|
||||||
//- Execute, currently does nothing
|
//- Execute - does nothing
|
||||||
virtual bool execute();
|
virtual bool execute();
|
||||||
|
|
||||||
//- Write fields
|
//- Write fields
|
||||||
virtual bool write();
|
virtual bool write();
|
||||||
|
|
||||||
|
//- On end - provide feedback about disconnecting from catatyst.
|
||||||
|
virtual bool end();
|
||||||
|
|
||||||
|
//- Update for changes of mesh
|
||||||
|
virtual void updateMesh(const mapPolyMesh& mpm);
|
||||||
|
|
||||||
|
//- Update for mesh point-motion
|
||||||
|
virtual void movePoints(const polyMesh& mesh);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
========= |
|
========= |
|
||||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
\\ / O peration |
|
\\ / O peration |
|
||||||
\\ / A nd | Copyright (C) 2017 OpenCFD Ltd.
|
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
|
||||||
\\/ M anipulation |
|
\\/ M anipulation |
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
License
|
License
|
||||||
@ -26,60 +26,147 @@ License
|
|||||||
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||||
|
|
||||||
template<class GeoField>
|
template<class GeoField>
|
||||||
Foam::label
|
Foam::label Foam::functionObjects::vtkWrite::writeVolFields
|
||||||
Foam::functionObjects::vtkWrite::countFields() const
|
(
|
||||||
|
autoPtr<vtk::internalWriter>& internalWriter,
|
||||||
|
UPtrList<vtk::patchWriter>& patchWriters,
|
||||||
|
const fvMeshSubset& proxy,
|
||||||
|
const wordHashSet& acceptField
|
||||||
|
) const
|
||||||
{
|
{
|
||||||
return obr_.names<GeoField>(selectFields_).size();
|
const fvMesh& baseMesh = proxy.baseMesh();
|
||||||
|
|
||||||
|
label count = 0;
|
||||||
|
|
||||||
|
for (const word& fieldName : baseMesh.sortedNames<GeoField>(acceptField))
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
const auto* fieldptr = baseMesh.findObject<GeoField>(fieldName);
|
||||||
|
|
||||||
|
if (!fieldptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tfield = fvMeshSubsetProxy::interpolate(proxy, *fieldptr);
|
||||||
|
const auto& field = tfield();
|
||||||
|
|
||||||
|
// Internal
|
||||||
|
if (internalWriter.valid())
|
||||||
|
{
|
||||||
|
ok = true;
|
||||||
|
internalWriter->write(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boundary
|
||||||
|
label writeri = 0;
|
||||||
|
for (vtk::patchWriter& writer : patchWriters)
|
||||||
|
{
|
||||||
|
ok = true;
|
||||||
|
writer.write(field);
|
||||||
|
++writeri;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
|
||||||
|
if (verbose_)
|
||||||
|
{
|
||||||
|
if (count == 1)
|
||||||
|
{
|
||||||
|
Log << " " << GeoField::typeName << '(';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log << ' ';
|
||||||
|
}
|
||||||
|
Log << fieldName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose_ && count)
|
||||||
|
{
|
||||||
|
Log << ')' << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<class GeoField>
|
template<class GeoField>
|
||||||
Foam::label
|
Foam::label Foam::functionObjects::vtkWrite::writeVolFields
|
||||||
Foam::functionObjects::vtkWrite::writeFields
|
|
||||||
(
|
(
|
||||||
vtk::internalWriter& writer,
|
autoPtr<vtk::internalWriter>& internalWriter,
|
||||||
bool verbose
|
const autoPtr<volPointInterpolation>& pInterp,
|
||||||
|
UPtrList<vtk::patchWriter>& patchWriters,
|
||||||
|
const UPtrList<PrimitivePatchInterpolation<primitivePatch>>& patchInterps,
|
||||||
|
const fvMeshSubset& proxy,
|
||||||
|
const wordHashSet& acceptField
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
const wordList names = obr_.sortedNames<GeoField>(selectFields_);
|
const fvMesh& baseMesh = proxy.baseMesh();
|
||||||
|
|
||||||
if (verbose && names.size())
|
label count = 0;
|
||||||
|
|
||||||
|
for (const word& fieldName : baseMesh.sortedNames<GeoField>(acceptField))
|
||||||
{
|
{
|
||||||
Info<< " " << GeoField::typeName
|
bool ok = false;
|
||||||
<< " " << flatOutput(names) << endl;
|
const auto* fieldptr = baseMesh.findObject<GeoField>(fieldName);
|
||||||
|
|
||||||
|
if (!fieldptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tfield = fvMeshSubsetProxy::interpolate(proxy, *fieldptr);
|
||||||
|
const auto& field = tfield();
|
||||||
|
|
||||||
|
// Internal
|
||||||
|
if (internalWriter.valid() && pInterp.valid())
|
||||||
|
{
|
||||||
|
ok = true;
|
||||||
|
internalWriter->write(field, *pInterp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boundary
|
||||||
|
label writeri = 0;
|
||||||
|
for (vtk::patchWriter& writer : patchWriters)
|
||||||
|
{
|
||||||
|
if (writeri < patchInterps.size() && patchInterps.set(writeri))
|
||||||
|
{
|
||||||
|
ok = true;
|
||||||
|
writer.write(field, patchInterps[writeri]);
|
||||||
|
}
|
||||||
|
++writeri;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
|
||||||
|
if (verbose_)
|
||||||
|
{
|
||||||
|
if (count == 1)
|
||||||
|
{
|
||||||
|
Log << " " << GeoField::typeName << "->point(";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log << ' ';
|
||||||
|
}
|
||||||
|
Log << fieldName;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const word& fieldName : names)
|
if (verbose_ && count)
|
||||||
{
|
{
|
||||||
writer.write(obr_.lookupObject<GeoField>(fieldName));
|
Log << ')' << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
return names.size();
|
return count;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<class GeoField>
|
|
||||||
Foam::label
|
|
||||||
Foam::functionObjects::vtkWrite::writeFields
|
|
||||||
(
|
|
||||||
vtk::surfaceMeshWriter& writer,
|
|
||||||
bool verbose
|
|
||||||
) const
|
|
||||||
{
|
|
||||||
const wordList names = obr_.sortedNames<GeoField>(selectFields_);
|
|
||||||
|
|
||||||
if (verbose && names.size())
|
|
||||||
{
|
|
||||||
Info<< " " << GeoField::typeName
|
|
||||||
<< " " << flatOutput(names) << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const word& fieldName : names)
|
|
||||||
{
|
|
||||||
writer.write(obr_.lookupObject<GeoField>(fieldName));
|
|
||||||
}
|
|
||||||
|
|
||||||
return names.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
274
src/functionObjects/utilities/vtkWrite/vtkWriteUpdate.C
Normal file
274
src/functionObjects/utilities/vtkWrite/vtkWriteUpdate.C
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
|
\\ / O peration |
|
||||||
|
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
|
||||||
|
\\/ M anipulation |
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
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 "vtkWrite.H"
|
||||||
|
#include "cellBitSet.H"
|
||||||
|
#include "topoSetCellSource.H"
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * Local Data Members * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
namespace Foam
|
||||||
|
{
|
||||||
|
// A limited selection of actions
|
||||||
|
const Enum<topoSetSource::setAction> actionNames
|
||||||
|
({
|
||||||
|
{ topoSetSource::ADD, "add" },
|
||||||
|
{ topoSetSource::SUBTRACT, "subtract" },
|
||||||
|
{ topoSetSource::SUBSET, "subset" },
|
||||||
|
{ topoSetSource::INVERT, "invert" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||||
|
|
||||||
|
bool Foam::functionObjects::vtkWrite::updateSubset
|
||||||
|
(
|
||||||
|
fvMeshSubset& subsetter
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
if (selection_.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fvMesh& mesh = subsetter.baseMesh();
|
||||||
|
|
||||||
|
// Start with all cells unselected
|
||||||
|
cellBitSet cellsToSelect(mesh, false);
|
||||||
|
|
||||||
|
for (const entry& dEntry : selection_)
|
||||||
|
{
|
||||||
|
if (!dEntry.isDict())
|
||||||
|
{
|
||||||
|
WarningInFunction
|
||||||
|
<< "Ignoring non-dictionary entry "
|
||||||
|
<< dEntry << endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dictionary& dict = dEntry.dict();
|
||||||
|
|
||||||
|
const auto action = actionNames.get("action", dict);
|
||||||
|
|
||||||
|
// Handle manually
|
||||||
|
if (action == topoSetSource::INVERT)
|
||||||
|
{
|
||||||
|
cellsToSelect.invert(mesh.nCells());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto source = topoSetCellSource::New
|
||||||
|
(
|
||||||
|
dict.get<word>("source"),
|
||||||
|
mesh,
|
||||||
|
dict.optionalSubDict("sourceInfo")
|
||||||
|
);
|
||||||
|
source->verbose(false);
|
||||||
|
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case topoSetSource::ADD:
|
||||||
|
case topoSetSource::SUBTRACT:
|
||||||
|
source->applyToSet(action, cellsToSelect);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case topoSetSource::SUBSET:
|
||||||
|
{
|
||||||
|
cellBitSet other(mesh, false);
|
||||||
|
source->applyToSet(topoSetSource::NEW, other);
|
||||||
|
|
||||||
|
cellsToSelect.subset(other);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Should already have been caught
|
||||||
|
WarningInFunction
|
||||||
|
<< "Ignoring unhandled action '"
|
||||||
|
<< actionNames[action] << "'" << endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subsetter.setCellSubset(cellsToSelect.addressing());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Foam::labelList Foam::functionObjects::vtkWrite::getSelectedPatches
|
||||||
|
(
|
||||||
|
const polyBoundaryMesh& patches
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
DynamicList<label> patchIDs(patches.size());
|
||||||
|
|
||||||
|
for (const polyPatch& pp : patches)
|
||||||
|
{
|
||||||
|
if (isType<emptyPolyPatch>(pp))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (isType<processorPolyPatch>(pp))
|
||||||
|
{
|
||||||
|
break; // No processor patches
|
||||||
|
}
|
||||||
|
|
||||||
|
if
|
||||||
|
(
|
||||||
|
selectPatches_.size()
|
||||||
|
? selectPatches_.match(pp.name())
|
||||||
|
: true
|
||||||
|
)
|
||||||
|
{
|
||||||
|
patchIDs.append(pp.index());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return patchIDs.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Foam::functionObjects::vtkWrite::update()
|
||||||
|
{
|
||||||
|
if
|
||||||
|
(
|
||||||
|
meshState_ == polyMesh::UNCHANGED
|
||||||
|
&& !meshSubsets_.empty()
|
||||||
|
&& !vtuMappings_.empty()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
meshSubsets_.resize(meshes_.size());
|
||||||
|
vtuMappings_.resize(meshes_.size());
|
||||||
|
|
||||||
|
label regioni = 0;
|
||||||
|
for (const word& regionName : meshes_.sortedToc())
|
||||||
|
{
|
||||||
|
const fvMesh& mesh = *(meshes_[regionName]);
|
||||||
|
|
||||||
|
if (meshSubsets_.set(regioni))
|
||||||
|
{
|
||||||
|
meshSubsets_.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Mesh subsetting, or pass through
|
||||||
|
meshSubsets_.set(regioni, new fvMeshSubset(mesh));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vtuMappings_.set(regioni))
|
||||||
|
{
|
||||||
|
// Trigger change for vtk cells too
|
||||||
|
vtuMappings_[regioni].clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// VTU sizing and decomposition information
|
||||||
|
vtuMappings_.set
|
||||||
|
(
|
||||||
|
regioni,
|
||||||
|
new vtk::vtuCells(writeOpts_, decompose_)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
++regioni;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& subsetter : meshSubsets_)
|
||||||
|
{
|
||||||
|
updateSubset(subsetter);
|
||||||
|
}
|
||||||
|
|
||||||
|
meshState_ = polyMesh::UNCHANGED;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
bool Foam::functionObjects::vtkWrite::readSelection(const dictionary& dict)
|
||||||
|
{
|
||||||
|
meshSubsets_.clear();
|
||||||
|
vtuMappings_.clear();
|
||||||
|
meshState_ = polyMesh::TOPO_CHANGE;
|
||||||
|
|
||||||
|
// All possible meshes
|
||||||
|
meshes_ = time_.lookupClass<fvMesh>();
|
||||||
|
|
||||||
|
selectRegions_.clear();
|
||||||
|
dict.readIfPresent("regions", selectRegions_);
|
||||||
|
|
||||||
|
if (selectRegions_.empty())
|
||||||
|
{
|
||||||
|
selectRegions_.resize(1);
|
||||||
|
selectRegions_.first() =
|
||||||
|
dict.lookupOrDefault<word>("region", polyMesh::defaultRegion);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restrict to specified meshes
|
||||||
|
meshes_.filterKeys(selectRegions_);
|
||||||
|
|
||||||
|
if (meshes_.empty())
|
||||||
|
{
|
||||||
|
WarningInFunction
|
||||||
|
<< "No mesh regions selected for function object " << name()
|
||||||
|
<< nl;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectPatches_.clear();
|
||||||
|
dict.readIfPresent("patches", selectPatches_);
|
||||||
|
|
||||||
|
selectFields_.clear();
|
||||||
|
dict.readEntry("fields", selectFields_);
|
||||||
|
selectFields_.uniq();
|
||||||
|
|
||||||
|
// Actions to define selection
|
||||||
|
selection_ = dict.subOrEmptyDict("selection");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::functionObjects::vtkWrite::updateMesh(const mapPolyMesh&)
|
||||||
|
{
|
||||||
|
meshState_ = polyMesh::TOPO_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::functionObjects::vtkWrite::movePoints(const polyMesh&)
|
||||||
|
{
|
||||||
|
// Only move to worse states
|
||||||
|
if (meshState_ == polyMesh::UNCHANGED)
|
||||||
|
{
|
||||||
|
meshState_ = polyMesh::POINTS_MOVED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -52,4 +52,9 @@ maxDi 10.0;
|
|||||||
|
|
||||||
adjustTimeStep yes;
|
adjustTimeStep yes;
|
||||||
|
|
||||||
|
functions
|
||||||
|
{
|
||||||
|
#include "vtkWrite"
|
||||||
|
}
|
||||||
|
|
||||||
// ************************************************************************* //
|
// ************************************************************************* //
|
||||||
|
|||||||
@ -0,0 +1,62 @@
|
|||||||
|
// -*- C++ -*-
|
||||||
|
// Use the vtkWrite function object
|
||||||
|
|
||||||
|
vtkWrite
|
||||||
|
{
|
||||||
|
type vtkWrite;
|
||||||
|
libs ("libutilityFunctionObjects.so");
|
||||||
|
log true;
|
||||||
|
|
||||||
|
writeControl writeTime;
|
||||||
|
writeInterval 1;
|
||||||
|
|
||||||
|
// Fields to output (words or regex)
|
||||||
|
regions (".*");
|
||||||
|
|
||||||
|
internal true;
|
||||||
|
|
||||||
|
boundary true;
|
||||||
|
|
||||||
|
single false;
|
||||||
|
|
||||||
|
interpolate true;
|
||||||
|
|
||||||
|
// Fields to output (words or regex)
|
||||||
|
fields (".*");
|
||||||
|
|
||||||
|
//- Output format (ascii | binary) - Default=binary
|
||||||
|
// format binary;
|
||||||
|
|
||||||
|
//- Use legacy output format - Default=false
|
||||||
|
// legacy false;
|
||||||
|
|
||||||
|
//- Output directory name - Default="postProcessing/<name>"
|
||||||
|
// directory "VTK";
|
||||||
|
|
||||||
|
//- Write cell ids as field - Default=true
|
||||||
|
writeIds false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Solid walls only
|
||||||
|
walls
|
||||||
|
{
|
||||||
|
type vtkWrite;
|
||||||
|
libs ("libutilityFunctionObjects.so");
|
||||||
|
log true;
|
||||||
|
|
||||||
|
writeControl writeTime;
|
||||||
|
writeInterval 1;
|
||||||
|
|
||||||
|
internal false;
|
||||||
|
|
||||||
|
// single true;
|
||||||
|
|
||||||
|
regions ( heater "(?i).*solid" );
|
||||||
|
patches ( "(?i).*solid_to.*" "heater.*(Air|Water)" );
|
||||||
|
|
||||||
|
fields (T);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -15,15 +15,69 @@ vtkWrite
|
|||||||
//- Use legacy output format - Default=false
|
//- Use legacy output format - Default=false
|
||||||
// legacy false;
|
// legacy false;
|
||||||
|
|
||||||
//- Output directory name - Default="VTK"
|
//- Output directory name - Default="postProcessing/<name>"
|
||||||
// directory "VTK";
|
// directory "VTK";
|
||||||
|
|
||||||
//- Write cell ids as field - Default=true
|
//- Write cell ids as field - Default=true
|
||||||
writeIds false;
|
writeIds false;
|
||||||
|
|
||||||
//- Write more frequent than fields
|
//- Write more frequent than fields
|
||||||
writeControl timeStep;
|
writeControl timeStep;
|
||||||
writeInterval 25;
|
writeInterval 25;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subset
|
||||||
|
{
|
||||||
|
type vtkWrite;
|
||||||
|
libs ("libutilityFunctionObjects.so");
|
||||||
|
log true;
|
||||||
|
|
||||||
|
// boundary false;
|
||||||
|
|
||||||
|
interpolate true;
|
||||||
|
|
||||||
|
// Fields to output (words or regex)
|
||||||
|
fields (U p);
|
||||||
|
|
||||||
|
//- Write cell ids as field - Default=true
|
||||||
|
writeIds false;
|
||||||
|
|
||||||
|
//- Write more frequent than fields
|
||||||
|
writeControl timeStep;
|
||||||
|
writeInterval 25;
|
||||||
|
|
||||||
|
// Region of interest
|
||||||
|
selection
|
||||||
|
{
|
||||||
|
dome
|
||||||
|
{
|
||||||
|
action add;
|
||||||
|
source sphere;
|
||||||
|
origin (125 100 0);
|
||||||
|
radius 100;
|
||||||
|
}
|
||||||
|
cylinder
|
||||||
|
{
|
||||||
|
action subtract;
|
||||||
|
source cylinder;
|
||||||
|
p1 (125 100 0);
|
||||||
|
p2 (125 100 100);
|
||||||
|
radius 20;
|
||||||
|
}
|
||||||
|
outlet
|
||||||
|
{
|
||||||
|
action add;
|
||||||
|
source box;
|
||||||
|
box (300 -100 -100) (330 250 100);
|
||||||
|
}
|
||||||
|
clipping
|
||||||
|
{
|
||||||
|
action subset;
|
||||||
|
source box;
|
||||||
|
box (-1000 -1000 -1000) (1000 1000 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ************************************************************************* //
|
// ************************************************************************* //
|
||||||
|
|||||||
Reference in New Issue
Block a user