From 42bb497084ab7602c10456c23dec0f925d46dfcd Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Tue, 9 Oct 2018 15:52:52 +0200 Subject: [PATCH] ENH: improvements for vtkWrite function object (issue #926) - parallel output. The output is now postProcessing/ 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 --- src/functionObjects/utilities/Make/files | 1 + .../utilities/vtkWrite/vtkWrite.C | 729 +++++++++++++++--- .../utilities/vtkWrite/vtkWrite.H | 238 +++++- .../utilities/vtkWrite/vtkWriteTemplates.C | 167 +++- .../utilities/vtkWrite/vtkWriteUpdate.C | 274 +++++++ .../multiRegionHeater/system/controlDict | 5 + .../multiRegionHeater/system/vtkWrite | 62 ++ .../windAroundBuildings/system/vtkWrite | 58 +- 8 files changed, 1358 insertions(+), 176 deletions(-) create mode 100644 src/functionObjects/utilities/vtkWrite/vtkWriteUpdate.C create mode 100644 tutorials/heatTransfer/chtMultiRegionFoam/multiRegionHeater/system/vtkWrite diff --git a/src/functionObjects/utilities/Make/files b/src/functionObjects/utilities/Make/files index 5cd7055b46..68074bcbe1 100644 --- a/src/functionObjects/utilities/Make/files +++ b/src/functionObjects/utilities/Make/files @@ -3,6 +3,7 @@ abort/abort.C codedFunctionObject/codedFunctionObject.C ensightWrite/ensightWrite.C vtkWrite/vtkWrite.C +vtkWrite/vtkWriteUpdate.C removeRegisteredObject/removeRegisteredObject.C diff --git a/src/functionObjects/utilities/vtkWrite/vtkWrite.C b/src/functionObjects/utilities/vtkWrite/vtkWrite.C index 99409e45b1..dfc6ad1fb0 100644 --- a/src/functionObjects/utilities/vtkWrite/vtkWrite.C +++ b/src/functionObjects/utilities/vtkWrite/vtkWrite.C @@ -27,7 +27,11 @@ License #include "dictionary.H" #include "Time.H" #include "areaFields.H" +#include "stringListOps.H" #include "foamVtkInternalWriter.H" +#include "foamVtkPatchWriter.H" +#include "foamVtkSeriesWriter.H" +#include "foamVtmWriter.H" #include "addToRunTimeSelectionTable.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -42,6 +46,73 @@ namespace functionObjects } +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +Foam::label Foam::functionObjects::vtkWrite::writeAllVolFields +( + autoPtr& internalWriter, + UPtrList& patchWriters, + const fvMeshSubset& proxy, + const wordHashSet& acceptField +) const +{ + #undef vtkWrite_WRITE_FIELD + #define vtkWrite_WRITE_FIELD(FieldType) \ + writeVolFields \ + ( \ + 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& internalWriter, + const autoPtr& pInterp, + + UPtrList& patchWriters, + const UPtrList>& patchInterps, + const fvMeshSubset& proxy, + const wordHashSet& acceptField +) const +{ + #undef vtkWrite_WRITE_FIELD + #define vtkWrite_WRITE_FIELD(FieldType) \ + writeVolFields \ + ( \ + 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 * * * * * * * * * * * * * * // Foam::functionObjects::vtkWrite::vtkWrite @@ -51,19 +122,37 @@ Foam::functionObjects::vtkWrite::vtkWrite const dictionary& dict ) : - fvMeshFunctionObject(name, runTime, dict), + functionObject(name), + time_(runTime), + outputDir_(), + printf_(), 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_(), - dirName_("VTK") + selection_(), + meshes_(), + meshSubsets_(), + vtuMappings_(), + series_() { - if (postProcess) - { - // Disable for post-process mode. - // Emit as FatalError for the try/catch in the caller. - FatalError - << type() << " disabled in post-process mode" - << exit(FatalError); - } + // May still want this? (OCT-2018) + // if (postProcess) + // { + // // Disable for post-process mode. + // // Emit as FatalError for the try/catch in the caller. + // FatalError + // << type() << " disabled in post-process mode" + // << exit(FatalError); + // } read(dict); } @@ -73,16 +162,23 @@ Foam::functionObjects::vtkWrite::vtkWrite 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("verbose", true); + doInternal_ = dict.lookupOrDefault("internal", true); + doBoundary_ = dict.lookupOrDefault("boundary", true); + oneBoundary_ = dict.lookupOrDefault("single", false); + interpolate_ = dict.lookupOrDefault("interpolate", false); // - // writer options - default is xml base64 + // Writer options - default is xml base64 // writeOpts_ = vtk::formatType::INLINE_BASE64; - if (dict.lookupOrDefault("legacy", false)) - { - writeOpts_.legacy(true); - } writeOpts_.ascii ( @@ -90,33 +186,63 @@ bool Foam::functionObjects::vtkWrite::read(const dictionary& dict) && (IOstream::formatEnum(dict.get("format")) == IOstream::ASCII) ); - // FUTURE? - // writeOpts_.precision - // ( - // dict.lookupOrDefault - // ( - // "writePrecision", - // IOstream::defaultPrecision() - // ) - // ); + if (dict.lookupOrDefault("legacy", false)) + { + writeOpts_.legacy(true); + } + + writeOpts_.precision + ( + dict.lookupOrDefault + ( + "precision", + IOstream::defaultPrecision() + ) + ); // Info<< type() << " " << name() << " output-format: " // << writeOpts_.description() << nl; + const int padWidth = dict.lookupOrDefault("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); writeIds_ = dict.lookupOrDefault("writeIds", false); - // - // output fields - // - dict.readEntry("fields", selectFields_); - selectFields_.uniq(); + // Output directory + + outputDir_.clear(); + dict.readIfPresent("directory", outputDir_); + + 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; } @@ -133,96 +259,515 @@ bool Foam::functionObjects::vtkWrite::write() // const word timeDesc = // 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_; - if (!vtkDir.isAbsolute()) + const scalar timeValue = time_.value(); + + 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. - const auto i = vtkName.rfind("processor"); - - if (i != string::npos) + fileName regionPrefix; + if (regionName != polyMesh::defaultRegion) { - vtkName = vtkName.substr(i); + regionPrefix = regionName; } - } - // internal mesh - { - const fileName outputName + auto& meshProxy = meshSubsets_[regioni]; + auto& vtuMeshCells = vtuMappings_[regioni]; + ++regioni; + + const fvMesh& baseMesh = meshProxy.baseMesh(); + + wordHashSet acceptField(baseMesh.names(selectFields_)); + + // Prune restart fields + acceptField.filterKeys ( - vtkDir/vtkName - + "_" - + timeDesc + [](const word& k){ return k.endsWith("_0"); }, + true // prune ); - Info<< name() << " output Time: " << time_.timeName() << nl - << " Internal : " << outputName << endl; - - // Number of fields to be written: only needed for legacy vtk format - label nVolFields = 0; - if (writeOpts_.legacy()) - { - nVolFields = + const label nVolFields = + ( + (doInternal_ || doBoundary_) + ? baseMesh.count ( - (writeIds_ ? 1 : 0) - + countFields() - + countFields() - + countFields() - + countFields() - + countFields() + stringListOps::foundOp(fieldTypes::volume), + acceptField + ) + : 0 + ); + + // Undecided if we want to automatically support DimensionedFields + // or only on demand: + const label nDimFields = 0; + // ( + // (doInternal_ || doBoundary_) + // ? baseMesh.count + // ( + // stringListOps::foundOp(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 internalWriter; + + // Interpolator for volume and dimensioned fields + autoPtr 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::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 - vtk::internalWriter writer - ( - mesh_, - vtuMeshCells, - outputName, - writeOpts_ - ); + // Setup the patch writers + + const polyBoundaryMesh& patches = meshProxy.mesh().boundaryMesh(); + + PtrList patchWriters; + PtrList> patchInterps; + + labelList patchIds; + if (doBoundary_) + { + patchIds = getSelectedPatches(patches); + } + + if (oneBoundary_ && patchIds.size()) + { + auto writer = autoPtr::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::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(pp) + ); + } + } + + if (nPatchWriters) + { + vtmWriter.endBlock("boundary"); + vtmBoundaries.endBlock("boundary"); + } + + patchWriters.resize(nPatchWriters); + patchInterps.resize(nPatchInterps); + } // CellData { - writer.beginCellData(nVolFields); - - // Write cellID field - if (writeIds_) + if (internalWriter.valid()) { - 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 - writeFields(writer); - writeFields(writer); - writeFields(writer); - writeFields(writer); - writeFields(writer); + if (nVolFields) + { + for (vtk::patchWriter& writer : patchWriters) + { + writer.beginCellData + ( + (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; } +bool Foam::functionObjects::vtkWrite::end() +{ + meshSubsets_.clear(); + vtuMappings_.clear(); + meshes_.clear(); + + return true; +} + + // ************************************************************************* // diff --git a/src/functionObjects/utilities/vtkWrite/vtkWrite.H b/src/functionObjects/utilities/vtkWrite/vtkWrite.H index fca1d3c605..a31a9b85dc 100644 --- a/src/functionObjects/utilities/vtkWrite/vtkWrite.H +++ b/src/functionObjects/utilities/vtkWrite/vtkWrite.H @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2016 OpenFOAM Foundation - \\/ M anipulation | Copyright (C) 2017-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2017-2018 OpenCFD Ltd. + \\/ M anipulation | ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -28,11 +28,8 @@ Group grpUtilitiesFunctionObjects Description - This functionObject writes objects registered to the database in VTK format. - - Currently only supports writing of the cell-values of volFields but - support for other field types, patch fields, Lagrangian data etc. will be - added. + Writes fields in VTK (xml or legacy) format. + Writes cell-values or point-interpolated values for volFields. Example of function object specification: \verbatim @@ -47,22 +44,80 @@ Description decompose false; ... 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 -Usage + \heading Basic Usage \table - Property | Description | Required | Default - type | Type name: vtkWrite | yes | - fields | Fields to output | yes | - writeControl | Output control | recommended | timeStep - directory | The output directory name | no | "VTK" - format | ASCII or binary format | no | binary - legacy | Legacy VTK output | no | false - decompose | decompose polyhedra | no | false - writeIds | Write cell ids as field | no | true + Property | Description | Required | Default + type | Type name: vtkWrite | yes | + fields | Fields to output (wordRe list) | yes | + boundary | Convert boundary fields | no | true + internal | Convert internal fields | no | true + single | Combine patches into a single boundary | no | false + interpolate | Interpolate for point values | no | false \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 Foam::functionObjects::ensightWrite Foam::functionObjects::fvMeshFunctionObject @@ -77,10 +132,12 @@ SourceFiles #ifndef functionObjects_vtkWrite_H #define functionObjects_vtkWrite_H -#include "fvMeshFunctionObject.H" +#include "functionObject.H" #include "foamVtkInternalWriter.H" -#include "foamVtkSurfaceMeshWriter.H" -#include "wordRes.H" +#include "foamVtkPatchWriter.H" +#include "foamVtkSeriesWriter.H" +#include "fvMeshSubsetProxy.H" +#include "searchableSurfaces.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -95,18 +152,36 @@ namespace functionObjects 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::outputOptions writeOpts_; - //- Name of fields to process - wordRes selectFields_; + //- Verbose output + bool verbose_; - //- Output directory name - fileName dirName_; + //- Convert internal mesh + bool doInternal_; + + //- Convert boundary mesh + bool doBoundary_; + + //- Combine patches into a single file + bool oneBoundary_; + + //- Interpolate cell to point values + bool interpolate_; //- Decompose polyhedra bool decompose_; @@ -114,29 +189,99 @@ class vtkWrite //- Write cell ids field 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 meshes_; + + //- Subsetting for meshes. + // Access index according to sorted mesh names. + PtrList meshSubsets_; + + //- Storage for VTU cells, sizing. + // Access index according to sorted mesh names. + PtrList vtuMappings_; + + //- VTK file series + HashTable series_; + // Private Member Functions - //- Count number of selected fields for GeoField type. - // Only needed for legacy vtk format. - template - label countFields() const; + //- Update mesh subset according to zones, geometry, bounds + bool updateSubset(fvMeshSubset& subsetter) const; - //- Write selected fields for GeoField type. - template - label writeFields + //- Get patchIds selected in list + labelList getSelectedPatches(const polyBoundaryMesh& patches) const; + + //- 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, - bool verbose=true + autoPtr& internalWriter, + UPtrList& patchWriters, + const fvMeshSubset& proxy, + const wordHashSet& acceptField ) const; - - //- Write selected fields for GeoField type. - template - label writeFields + //- Write all volume fields with point interpolation + label writeAllVolFields ( - vtk::surfaceMeshWriter& writer, - bool verbose=true + autoPtr& internalWriter, + const autoPtr& pInterp, + UPtrList& patchWriters, + const UPtrList + < + PrimitivePatchInterpolation + >& patchInterps, + const fvMeshSubset& proxy, + const wordHashSet& acceptField + ) const; + + //- Write selected GeoField fields. + template + label writeVolFields + ( + autoPtr& internalWriter, + UPtrList& patchWriters, + const fvMeshSubset& proxy, + const wordHashSet& acceptField + ) const; + + //- Write selected GeoField fields with point interpolation + template + label writeVolFields + ( + autoPtr& internalWriter, + const autoPtr& pInterp, + UPtrList& patchWriters, + const UPtrList + < + PrimitivePatchInterpolation + >& patchInterps, + const fvMeshSubset& proxy, + const wordHashSet& acceptField ) const; @@ -173,11 +318,20 @@ public: //- Read the vtkWrite specification virtual bool read(const dictionary& dict); - //- Execute, currently does nothing + //- Execute - does nothing virtual bool execute(); //- Write fields 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); }; diff --git a/src/functionObjects/utilities/vtkWrite/vtkWriteTemplates.C b/src/functionObjects/utilities/vtkWrite/vtkWriteTemplates.C index 74ac9392a0..b7103e9230 100644 --- a/src/functionObjects/utilities/vtkWrite/vtkWriteTemplates.C +++ b/src/functionObjects/utilities/vtkWrite/vtkWriteTemplates.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2017 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2018 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -26,60 +26,147 @@ License // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // template -Foam::label -Foam::functionObjects::vtkWrite::countFields() const +Foam::label Foam::functionObjects::vtkWrite::writeVolFields +( + autoPtr& internalWriter, + UPtrList& patchWriters, + const fvMeshSubset& proxy, + const wordHashSet& acceptField +) const { - return obr_.names(selectFields_).size(); + const fvMesh& baseMesh = proxy.baseMesh(); + + label count = 0; + + for (const word& fieldName : baseMesh.sortedNames(acceptField)) + { + bool ok = false; + const auto* fieldptr = baseMesh.findObject(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 -Foam::label -Foam::functionObjects::vtkWrite::writeFields +Foam::label Foam::functionObjects::vtkWrite::writeVolFields ( - vtk::internalWriter& writer, - bool verbose + autoPtr& internalWriter, + const autoPtr& pInterp, + UPtrList& patchWriters, + const UPtrList>& patchInterps, + const fvMeshSubset& proxy, + const wordHashSet& acceptField ) const { - const wordList names = obr_.sortedNames(selectFields_); + const fvMesh& baseMesh = proxy.baseMesh(); - if (verbose && names.size()) + label count = 0; + + for (const word& fieldName : baseMesh.sortedNames(acceptField)) { - Info<< " " << GeoField::typeName - << " " << flatOutput(names) << endl; + bool ok = false; + const auto* fieldptr = baseMesh.findObject(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(fieldName)); + Log << ')' << endl; } - return names.size(); -} - - -template -Foam::label -Foam::functionObjects::vtkWrite::writeFields -( - vtk::surfaceMeshWriter& writer, - bool verbose -) const -{ - const wordList names = obr_.sortedNames(selectFields_); - - if (verbose && names.size()) - { - Info<< " " << GeoField::typeName - << " " << flatOutput(names) << endl; - } - - for (const word& fieldName : names) - { - writer.write(obr_.lookupObject(fieldName)); - } - - return names.size(); + return count; } diff --git a/src/functionObjects/utilities/vtkWrite/vtkWriteUpdate.C b/src/functionObjects/utilities/vtkWrite/vtkWriteUpdate.C new file mode 100644 index 0000000000..aa11f38dd7 --- /dev/null +++ b/src/functionObjects/utilities/vtkWrite/vtkWriteUpdate.C @@ -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 . + +\*---------------------------------------------------------------------------*/ + +#include "vtkWrite.H" +#include "cellBitSet.H" +#include "topoSetCellSource.H" + +// * * * * * * * * * * * * * * Local Data Members * * * * * * * * * * * * * // + +namespace Foam +{ + // A limited selection of actions + const Enum 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("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