From ee4c3cb7168301c98806dbb891e5bcfcafd33126 Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Wed, 13 Feb 2019 11:22:46 +0100 Subject: [PATCH] ENH: extended runTimePostProcessing (#1206) - Extended runTimePostProcessing to include access to "live" simulation objects such a geometry patches and sampled surfaces stored on the "functionObjectObjects" registry. - Add 'live' runTimePostProcessing of cloud data. Extracts position and fields from the cloud via its objectRegistry writer - For the "live" simulation objects, there are two new volume filters that work directly with the OpenFOAM volume fields: * iso-surface * cutting planes Both use the VTK algorithms directly and support multiple values. Eg, can make multiple iso-levels or multiple planes parallel to each other. - When VTK has been compiled with MPI-support, parallel rendering will be used. - Additional title text properties (shadow, italic etc) - Simplified handling of scalar-bar and visibility switches - Support multiple text positions. Eg, for adding watermark text. --- .../CMakeLists-Project.txt | 45 +- src/runTimePostProcessing/contourFilter.C | 303 ++++++++ src/runTimePostProcessing/contourFilter.H | 140 ++++ .../cuttingPlaneFilter.C | 292 ++++++++ .../cuttingPlaneFilter.H | 160 +++++ .../fieldVisualisationBase.C | 679 ++++++++++++------ .../fieldVisualisationBase.H | 204 +++++- .../functionObjectBase.C | 24 +- .../functionObjectBase.H | 24 +- .../functionObjectCloud.C | 167 +++-- .../functionObjectCloud.H | 25 +- .../functionObjectLine.C | 22 +- .../functionObjectLine.H | 12 +- .../functionObjectSurface.C | 359 ++++++++- .../functionObjectSurface.H | 37 +- src/runTimePostProcessing/geometryBase.C | 23 +- src/runTimePostProcessing/geometryBase.H | 30 +- src/runTimePostProcessing/geometryCloud.C | 263 +++++++ src/runTimePostProcessing/geometryCloud.H | 201 ++++++ .../geometryCloudGather.C | 129 ++++ .../geometryCloudTemplates.C | 168 +++++ src/runTimePostProcessing/geometryPatches.C | 315 ++++++++ src/runTimePostProcessing/geometryPatches.H | 171 +++++ .../geometryPatchesGather.C | 293 ++++++++ .../geometryPatchesTemplates.C | 221 ++++++ src/runTimePostProcessing/geometrySurface.C | 20 +- src/runTimePostProcessing/geometrySurface.H | 19 +- src/runTimePostProcessing/pathline.C | 15 +- src/runTimePostProcessing/pathline.H | 26 +- src/runTimePostProcessing/pointData.C | 11 +- src/runTimePostProcessing/pointData.H | 26 +- .../runTimePostProcessing.C | 255 +++++-- .../runTimePostProcessing.H | 96 ++- .../runTimePostProcessingTemplates.C | 2 +- src/runTimePostProcessing/scalarBar.C | 250 +++++++ src/runTimePostProcessing/scalarBar.H | 138 ++++ src/runTimePostProcessing/scene.C | 76 +- src/runTimePostProcessing/scene.H | 6 +- src/runTimePostProcessing/surface.C | 106 ++- src/runTimePostProcessing/surface.H | 217 +++++- src/runTimePostProcessing/surfaceGather.C | 222 ++++++ src/runTimePostProcessing/surfaceTemplates.C | 289 ++++++++ src/runTimePostProcessing/text.C | 73 +- src/runTimePostProcessing/text.H | 30 +- src/runTimePostProcessing/volumeFilter.C | 144 ++++ src/runTimePostProcessing/volumeFilter.H | 162 +++++ .../volumeFilterTemplates.C | 120 ++++ 47 files changed, 5999 insertions(+), 611 deletions(-) create mode 100644 src/runTimePostProcessing/contourFilter.C create mode 100644 src/runTimePostProcessing/contourFilter.H create mode 100644 src/runTimePostProcessing/cuttingPlaneFilter.C create mode 100644 src/runTimePostProcessing/cuttingPlaneFilter.H create mode 100644 src/runTimePostProcessing/geometryCloud.C create mode 100644 src/runTimePostProcessing/geometryCloud.H create mode 100644 src/runTimePostProcessing/geometryCloudGather.C create mode 100644 src/runTimePostProcessing/geometryCloudTemplates.C create mode 100644 src/runTimePostProcessing/geometryPatches.C create mode 100644 src/runTimePostProcessing/geometryPatches.H create mode 100644 src/runTimePostProcessing/geometryPatchesGather.C create mode 100644 src/runTimePostProcessing/geometryPatchesTemplates.C create mode 100644 src/runTimePostProcessing/scalarBar.C create mode 100644 src/runTimePostProcessing/scalarBar.H create mode 100644 src/runTimePostProcessing/surfaceGather.C create mode 100644 src/runTimePostProcessing/surfaceTemplates.C create mode 100644 src/runTimePostProcessing/volumeFilter.C create mode 100644 src/runTimePostProcessing/volumeFilter.H create mode 100644 src/runTimePostProcessing/volumeFilterTemplates.C diff --git a/src/runTimePostProcessing/CMakeLists-Project.txt b/src/runTimePostProcessing/CMakeLists-Project.txt index d64168a..3db266e 100644 --- a/src/runTimePostProcessing/CMakeLists-Project.txt +++ b/src/runTimePostProcessing/CMakeLists-Project.txt @@ -13,13 +13,40 @@ else() message(FATAL_ERROR " VTK version is too old - requires VTK6 or newer") endif() +#----------------------------------------------------------------------------- +# Test some characteristics +set(test_file ${CMAKE_CURRENT_BINARY_DIR}/check_mpi.cxx) +file(WRITE ${test_file} + "#include \n" + "int main() {\n" + " vtkMPICommunicator* p = vtkMPICommunicator::New();\n" + " p->Delete();\n" + " return 0;\n" + "}" +) +try_compile(FOAM_USING_VTK_MPI + ${CMAKE_CURRENT_BINARY_DIR} ${test_file} + LINK_LIBRARIES vtkParallelMPI + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${PARAVIEW_INCLUDE_DIRS}" +) +if (FOAM_USING_VTK_MPI) + add_definitions(-DFOAM_USING_VTK_MPI) + message("Building with VTK MPI") + include(vtkMPI) +else() + message(WARNING "==== Building without VTK MPI ====") +endif() + +#----------------------------------------------------------------------------- + include_directories( ${LIB_SRC}/OpenFOAM/include ${LIB_SRC}/OpenFOAM/lnInclude ${LIB_SRC}/OSspecific/${WM_OSTYPE}/lnInclude ${LIB_SRC}/finiteVolume/lnInclude - ${LIB_SRC}/surfMesh/lnInclude + ${LIB_SRC}/fileFormats/lnInclude ${LIB_SRC}/conversion/lnInclude + ${LIB_SRC}/surfMesh/lnInclude ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) @@ -45,12 +72,16 @@ set(LIBRARY_OUTPUT_PATH $ENV{FOAM_LIBBIN} file(GLOB SOURCE_FILES fieldVisualisationBase.C + scalarBar.C functionObjectBase.C functionObjectCloud.C functionObjectLine.C functionObjectSurface.C geometryBase.C + geometryCloud.C + geometryCloudGather.C geometryPatches.C + geometryPatchesGather.C geometrySurface.C pathline.C pointData.C @@ -58,16 +89,27 @@ file(GLOB SOURCE_FILES runTimePostProcessingFunctionObject.C scene.C surface.C + surfaceGather.C text.C + contourFilter.C + cuttingPlaneFilter.C + volumeFilter.C ) set(OPENFOAM_LIBRARIES OpenFOAM finiteVolume surfMesh + fileFormats conversion ) +if (FOAM_USING_VTK_MPI) + set(LINK_LIBRARIES vtkParallelMPI) +else() + set(LINK_LIBRARIES) +endif() + add_library( runTimePostProcessing SHARED @@ -84,6 +126,7 @@ set_target_properties( target_link_libraries( runTimePostProcessing ${VTK_LIBRARIES} + ${LINK_LIBRARIES} ${OPENFOAM_LIBRARIES} ) diff --git a/src/runTimePostProcessing/contourFilter.C b/src/runTimePostProcessing/contourFilter.C new file mode 100644 index 0000000..2a6eb42 --- /dev/null +++ b/src/runTimePostProcessing/contourFilter.C @@ -0,0 +1,303 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2019 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 . + +\*---------------------------------------------------------------------------*/ + +// OpenFOAM includes +#include "contourFilter.H" +#include "runTimePostProcessing.H" +#include "addToRunTimeSelectionTable.H" + +// VTK includes +#include "vtkActor.h" +#include "vtkCellDataToPointData.h" +#include "vtkCompositeDataGeometryFilter.h" +#include "vtkCompositeDataSet.h" +#include "vtkCompositePolyDataMapper.h" +#include "vtkContourFilter.h" +#include "vtkMultiPieceDataSet.h" +#include "vtkPolyData.h" +#include "vtkPolyDataMapper.h" +#include "vtkRenderer.h" +#include "vtkSmartPointer.h" + +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +namespace Foam +{ +namespace functionObjects +{ +namespace runTimePostPro +{ + defineTypeName(contourFilter); + addToRunTimeSelectionTable(surface, contourFilter, dictionary); +} +} +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::functionObjects::runTimePostPro::contourFilter::contourFilter +( + const runTimePostProcessing& parent, + const dictionary& dict, + const HashPtrTable>& colours +) +: + volumeFilter(parent, dict, colours), + fieldVisualisationBase(dict, colours), + colourFieldName_(dict.get("colourField")), + values_() +{ + dict.readEntry("values", values_); + + // Extra safety + if (values_.empty()) + { + values_.resize(1); + values_.first() = Zero; + } +} + + +// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // + +bool Foam::functionObjects::runTimePostPro::contourFilter:: +addGeometry +( + const scalar position, + vtkRenderer* renderer +) +{ + if (!visible_) + { + return false; + } + + if (needsCollective()) + { + Info<< type() << " : Not available for collective operation" << endl; + return false; + } + + DebugInfo << " Adding iso-surface" << endl; + + // Bookkeeping for vtkUnstructuredGrid + vtk::vtuAdaptor adaptor; + vtkSmartPointer multiPiece = mesh(adaptor); + + + // Add (scalar/vector) field. + // - always need field(s) for glyphs or colourByField: + + int nCmpt = 0; + { + const auto* ioptr = + parent().mesh().cfindObject(fieldName_); + + if (!nCmpt) + { + nCmpt = addDimField + ( + multiPiece, adaptor, ioptr, fieldName_ + ); + } + if (!nCmpt) + { + nCmpt = addDimField + ( + multiPiece, adaptor, ioptr, fieldName_ + ); + } + } + + + // If the input is vector, need magnitude + + word magFieldName = fieldName_; + + if (nCmpt == 3) + { + addMagField(fieldName_, multiPiece); + magFieldName = "mag(" + fieldName_ + ")"; + } + + // Colouring + nCmpt = 0; + if (colourBy_ == cbField && fieldName_ != colourFieldName_) + { + const auto* ioptr = + parent().mesh().cfindObject(fieldName_); + + if (!nCmpt) + { + nCmpt = addDimField + ( + multiPiece, adaptor, ioptr, colourFieldName_ + ); + } + if (!nCmpt) + { + nCmpt = addDimField + ( + multiPiece, adaptor, ioptr, colourFieldName_ + ); + } + } + + + // Now have a multi-piece dataset that is one of the following: + // + // - one-piece per processor (OpenFOAM = parallel, VTK=parallel) + + + // Re-query field information - we may have stored it differently + // than the original source. + + fieldSummary fieldInfo = queryFieldSummary(magFieldName, multiPiece); + fieldInfo.reduce(); + + fieldSummary colourFieldInfo = + queryFieldSummary(colourFieldName_, multiPiece); + colourFieldInfo.reduce(); + + + // Not rendered on this processor? + // This is where we stop, but could also have an MPI barrier + if (!renderer) + { + return true; + } + + + // Rendering + { + auto contour = vtkSmartPointer::New(); + + vtkSmartPointer cellToPoint; + + // CellData - Need a cell->point filter + if (!fieldInfo.hasPointData() || !colourFieldInfo.hasPointData()) + { + cellToPoint = vtkSmartPointer::New(); + cellToPoint->SetInputData(multiPiece); + + contour->SetInputConnection(cellToPoint->GetOutputPort()); + } + else + { + contour->SetInputData(multiPiece); + } + + contour->SetNumberOfContours(values_.size()); + forAll(values_, valuei) + { + contour->SetValue(valuei, values_[valuei]); + } + + contour->SetInputArrayToProcess + ( + 0, // index: scalars(0) + 0, // port + 0, // connection + vtkDataObject::FIELD_ASSOCIATION_POINTS, + magFieldName.c_str() + ); + + contour->Modified(); + contour->Update(); + + auto polyData = vtkSmartPointer::New(); + + polyData->SetInputConnection(contour->GetOutputPort()); + polyData->Update(); + + auto mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(polyData->GetOutputPort()); + + if (representation_ == rtGlyph) + { + addGlyphs + ( + position, + colourFieldName_, colourFieldInfo, // scaling + colourFieldName_, colourFieldInfo, // colouring + maxGlyphLength_, + polyData->GetOutput(), + surfaceActor_, + renderer + ); + } + else + { + setField + ( + position, + colourFieldName_, + FieldAssociation::POINT_DATA, + mapper, + renderer + ); + + surfaceActor_->SetMapper(mapper); + + setRepresentation(surfaceActor_); + + renderer->AddActor(surfaceActor_); + } + } + + return true; +} + + +void Foam::functionObjects::runTimePostPro::contourFilter:: +addGeometryToScene +( + const scalar position, + vtkRenderer* renderer +) +{ + if (visible_) + { + // Live source + if (addGeometry(position, renderer)) + { + return; + } + + WarningInFunction + << "Unsupported for OpenFOAM parallel and VTK serial" + << endl; + } +} + + +bool Foam::functionObjects::runTimePostPro::contourFilter::clear() +{ + return true; +} + + +// ************************************************************************* // diff --git a/src/runTimePostProcessing/contourFilter.H b/src/runTimePostProcessing/contourFilter.H new file mode 100644 index 0000000..403052d --- /dev/null +++ b/src/runTimePostProcessing/contourFilter.H @@ -0,0 +1,140 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2019 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 . + +Class + Foam::functionObjects::runTimePostPro::contourFilter + +Description + Iso-surface contours of OpenFOAM volume fields. + + Dictionary controls + \table + Property | Description | Required | Default + type | The surface type: isoSurface | yes | + field | The field defining the surface | yes | + colourField | The field to display on the surface | yes | + values | List of iso-values to define the surface(s) | yes | + \endtable + +SourceFiles + contourFilter.C + +\*---------------------------------------------------------------------------*/ + +#ifndef functionObjects_runTimePostPro_contourFilter_H +#define functionObjects_runTimePostPro_contourFilter_H + +#include "volumeFilter.H" +#include "fieldVisualisationBase.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace functionObjects +{ +namespace runTimePostPro +{ + +/*---------------------------------------------------------------------------*\ + Class contourFilter Declaration +\*---------------------------------------------------------------------------*/ + +class contourFilter +: + public volumeFilter, + public fieldVisualisationBase +{ +protected: + + // Protected Data + + //- Name of field to colour by + word colourFieldName_; + + //- The iso values + List values_; + + + // Protected Member Functions + + //- No copy construct + contourFilter(const contourFilter&) = delete; + + //- No copy assignment + void operator=(const contourFilter&) = delete; + + +public: + + //- Run-time type information + TypeNameNoDebug("isoSurface"); + + + // Constructors + + //- Construct from dictionary + contourFilter + ( + const runTimePostProcessing& parent, + const dictionary& dict, + const HashPtrTable>& colours + ); + + + //- Destructor + virtual ~contourFilter() = default; + + + // Member Functions + + //- Add cutting planes to scene (using simulation source) + bool addGeometry + ( + const scalar position, + vtkRenderer* renderer + ); + + //- Add cutting planes to scene (using simulation source) + virtual void addGeometryToScene + ( + const scalar position, + vtkRenderer* renderer + ); + + //- Add cutting planes to scene (using simulation source) + virtual bool clear(); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace runTimePostPro +} // End namespace functionObjects +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/runTimePostProcessing/cuttingPlaneFilter.C b/src/runTimePostProcessing/cuttingPlaneFilter.C new file mode 100644 index 0000000..929d585 --- /dev/null +++ b/src/runTimePostProcessing/cuttingPlaneFilter.C @@ -0,0 +1,292 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2019 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 . + +\*---------------------------------------------------------------------------*/ + +// OpenFOAM includes +#include "cuttingPlaneFilter.H" +#include "runTimePostProcessing.H" +#include "addToRunTimeSelectionTable.H" + +// VTK includes +#include "vtkActor.h" +#include "vtkCellDataToPointData.h" +#include "vtkCompositeDataGeometryFilter.h" +#include "vtkCompositeDataSet.h" +#include "vtkCompositePolyDataMapper.h" +#include "vtkCutter.h" +#include "vtkMultiPieceDataSet.h" +#include "vtkPlane.h" +#include "vtkPolyData.h" +#include "vtkPolyDataMapper.h" +#include "vtkRenderer.h" +#include "vtkSmartPointer.h" + +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +namespace Foam +{ +namespace functionObjects +{ +namespace runTimePostPro +{ + defineTypeName(cuttingPlaneFilter); + addToRunTimeSelectionTable(surface, cuttingPlaneFilter, dictionary); +} +} +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::functionObjects::runTimePostPro::cuttingPlaneFilter::cuttingPlaneFilter +( + const runTimePostProcessing& parent, + const dictionary& dict, + const HashPtrTable>& colours +) +: + volumeFilter(parent, dict, colours), + fieldVisualisationBase(dict, colours), + plane_(dict), + values_() +{ + dict.readIfPresent("offsets", values_); + + if (values_.empty()) + { + values_.resize(1); + values_.first() = Zero; + } +} + + +// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // + +bool Foam::functionObjects::runTimePostPro::cuttingPlaneFilter:: +addGeometry +( + const scalar position, + vtkRenderer* renderer +) +{ + if (!visible_) + { + return false; + } + + if (needsCollective()) + { + Info<< type() << " : Not available for collective operation" << endl; + return false; + } + + DebugInfo << " Adding cutting plane" << endl; + + + // Bookkeeping for vtkUnstructuredGrid + vtk::vtuAdaptor adaptor; + vtkSmartPointer multiPiece = mesh(adaptor); + + + // Add (scalar/vector) field. + // - Need field(s) for glyphs or colourByField: + + int nCmpt = 0; + if (representation_ == rtGlyph || colourBy_ == cbField) + { + const auto* ioptr = + parent().mesh().cfindObject(fieldName_); + + if (!nCmpt) + { + nCmpt = addDimField + ( + multiPiece, adaptor, ioptr, fieldName_ + ); + } + if (!nCmpt) + { + nCmpt = addDimField + ( + multiPiece, adaptor, ioptr, fieldName_ + ); + } + } + + + // Now have a multi-piece dataset that is one of the following: + // + // - one-piece per processor (OpenFOAM = parallel, VTK=parallel) + + + // Re-query field information - we may have stored it differently + // than the original source. + + fieldSummary fieldInfo = queryFieldSummary(fieldName_, multiPiece); + fieldInfo.reduce(); + + + // Not rendered on this processor? + // This is where we stop, but could also have an MPI barrier + if (!renderer) + { + return true; + } + + + // Rendering + { + // OpenFOAM plane -> vtkPlane definition + + auto pln = vtkSmartPointer::New(); + + pln->SetNormal + ( + plane_.normal().x(), + plane_.normal().y(), + plane_.normal().z() + ); + pln->SetOrigin + ( + plane_.origin().x(), + plane_.origin().y(), + plane_.origin().z() + ); + + + // Plane cutting algorithm + + auto cutter = vtkSmartPointer::New(); + + cutter->SetInputData(multiPiece); + cutter->SetCutFunction(pln); + + cutter->SetNumberOfContours(values_.size()); + + forAll(values_, pointi) + { + cutter->SetValue(pointi, values_[pointi]); + } + + cutter->SetInputArrayToProcess + ( + (nCmpt == 3 ? 1 : 0), // index: scalars(0), vectors(1) + 0, // port + 0, // connection + vtkDataObject::FIELD_ASSOCIATION_CELLS, + fieldName_.c_str() + ); + + cutter->Modified(); + cutter->Update(); + + auto polyData = vtkSmartPointer::New(); + + polyData->SetInputConnection(cutter->GetOutputPort()); + polyData->Update(); + + auto mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(polyData->GetOutputPort()); + + if (representation_ == rtGlyph) + { + addGlyphs + ( + position, + fieldName_, fieldInfo, // scaling + fieldName_, fieldInfo, // colouring + maxGlyphLength_, + polyData->GetOutput(), + surfaceActor_, + renderer + ); + } + else + { + vtkSmartPointer cellToPoint; + + // CellData - Need a cell->point filter + if (smooth_ && !fieldInfo.hasPointData()) + { + cellToPoint = vtkSmartPointer::New(); + cellToPoint->SetInputConnection(cutter->GetOutputPort()); + + polyData->SetInputConnection(cellToPoint->GetOutputPort()); + polyData->Update(); + } + + setField + ( + position, + fieldName_, + ( + smooth_ + ? FieldAssociation::POINT_DATA + : FieldAssociation(fieldInfo.association_) + ), + mapper, + renderer + ); + + surfaceActor_->SetMapper(mapper); + + setRepresentation(surfaceActor_); + + renderer->AddActor(surfaceActor_); + } + } + + return true; +} + + +void Foam::functionObjects::runTimePostPro::cuttingPlaneFilter:: +addGeometryToScene +( + const scalar position, + vtkRenderer* renderer +) +{ + if (visible_) + { + // Live source + if (addGeometry(position, renderer)) + { + return; + } + + WarningInFunction + << "Unsupported for OpenFOAM parallel and VTK serial" + << endl; + } +} + + +bool Foam::functionObjects::runTimePostPro::cuttingPlaneFilter::clear() +{ + return true; +} + + +// ************************************************************************* // diff --git a/src/runTimePostProcessing/cuttingPlaneFilter.H b/src/runTimePostProcessing/cuttingPlaneFilter.H new file mode 100644 index 0000000..1c89db2 --- /dev/null +++ b/src/runTimePostProcessing/cuttingPlaneFilter.H @@ -0,0 +1,160 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2019 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 . + +Class + Foam::functionObjects::runTimePostPro::cuttingPlaneFilter + +Description + Cutting planes of OpenFOAM volume fields. + + Example of text object specification: + \verbatim + planes + { + type plane; + planeType pointAndNormal; + + pointAndNormalDict + { + point (0 0 0); + normal (1 0 0); + } + + offsets (0 10 20); + + field T; + } + \endverbatim + + Dictionary controls + \table + Property | Description | Required | Default + type | The surface type: plane | yes | + planeType | Selector for plane description | yes | + offsets | Offets of the origin in the normal direction | no | (0) + field | The field to display | yes | + \endtable + +SourceFiles + cuttingPlaneFilter.C + +\*---------------------------------------------------------------------------*/ + +#ifndef functionObjects_runTimePostPro_cuttingPlaneFilter_H +#define functionObjects_runTimePostPro_cuttingPlaneFilter_H + +#include "plane.H" +#include "volumeFilter.H" +#include "fieldVisualisationBase.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace functionObjects +{ +namespace runTimePostPro +{ + +/*---------------------------------------------------------------------------*\ + Class cuttingPlaneFilter Declaration +\*---------------------------------------------------------------------------*/ + +class cuttingPlaneFilter +: + public volumeFilter, + public fieldVisualisationBase +{ +protected: + + // Protected Data + + //- The definition of the plane + plane plane_; + + //- The offsets to the plane - defaults to (0). + List values_; + + + // Protected Member Functions + + //- No copy construct + cuttingPlaneFilter(const cuttingPlaneFilter&) = delete; + + //- No copy assignment + void operator=(const cuttingPlaneFilter&) = delete; + + +public: + + //- Run-time type information + TypeNameNoDebug("plane"); + + + // Constructors + + //- Construct from dictionary + cuttingPlaneFilter + ( + const runTimePostProcessing& parent, + const dictionary& dict, + const HashPtrTable>& colours + ); + + + //- Destructor + virtual ~cuttingPlaneFilter() = default; + + + // Member Functions + + //- Add cutting planes to scene (using simulation source) + bool addGeometry + ( + const scalar position, + vtkRenderer* renderer + ); + + //- Add cutting planes to scene (using simulation source) + virtual void addGeometryToScene + ( + const scalar position, + vtkRenderer* renderer + ); + + //- Cleanup files etc. + virtual bool clear(); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace runTimePostPro +} // End namespace functionObjects +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/runTimePostProcessing/fieldVisualisationBase.C b/src/runTimePostProcessing/fieldVisualisationBase.C index 5a2f583..08c3f6a 100644 --- a/src/runTimePostProcessing/fieldVisualisationBase.C +++ b/src/runTimePostProcessing/fieldVisualisationBase.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2015-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -27,25 +27,27 @@ License #include "fieldVisualisationBase.H" #include "runTimePostProcessing.H" +#include "doubleVector.H" +#include "foamVtkTools.H" + // VTK includes #include "vtkArrowSource.h" +#include "vtkCellDataToPointData.h" #include "vtkCellData.h" #include "vtkColorTransferFunction.h" -#include "vtkFloatArray.h" +#include "vtkCompositeDataSet.h" +#include "vtkDataObjectTreeIterator.h" +#include "vtkFieldData.h" #include "vtkGlyph3D.h" #include "vtkLookupTable.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkRenderer.h" -#include "vtkScalarBarActor.h" #include "vtkSmartPointer.h" #include "vtkSphereSource.h" -#include "vtkTextActor.h" -#include "vtkTextProperty.h" -#include "vtkCellDataToPointData.h" -// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // const Foam::Enum < @@ -67,13 +69,323 @@ const Foam::Enum Foam::functionObjects::runTimePostPro::fieldVisualisationBase:: colourMapTypeNames ({ - { colourMapType::cmRainbow, "rainbow" }, - { colourMapType::cmBlueWhiteRed, "blueWhiteRed" }, + { colourMapType::cmCoolToWarm, "coolToWarm" }, + { colourMapType::cmCoolToWarm, "blueWhiteRed" }, + { colourMapType::cmColdAndHot, "coldAndHot" }, { colourMapType::cmFire, "fire" }, + { colourMapType::cmRainbow, "rainbow" }, { colourMapType::cmGreyscale, "greyscale" }, + { colourMapType::cmGreyscale, "grayscale" }, + { colourMapType::cmXray, "xray" }, }); +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +Foam::functionObjects::runTimePostPro::fieldVisualisationBase::fieldSummary +Foam::functionObjects::runTimePostPro::fieldVisualisationBase:: +queryFieldSummary +( + const word& fieldName, + vtkDataSet* dataset +) +{ + fieldSummary queried; + + if (dataset) + { + vtkDataArray* array; + + array = vtkDataArray::SafeDownCast + ( + dataset->GetCellData()->GetAbstractArray(fieldName.c_str()) + ); + + if (array) + { + queried.nComponents_ = array->GetNumberOfComponents(); + queried.association_ |= FieldAssociation::CELL_DATA; + queried.range_ += vtk::Tools::rangeOf(array); + } + + array = vtkDataArray::SafeDownCast + ( + dataset->GetPointData()->GetAbstractArray(fieldName.c_str()) + ); + + if (array) + { + queried.nComponents_ = array->GetNumberOfComponents(); + queried.association_ |= FieldAssociation::POINT_DATA; + queried.range_ += vtk::Tools::rangeOf(array); + } + } + + return queried; +} + + +Foam::functionObjects::runTimePostPro::fieldVisualisationBase::fieldSummary +Foam::functionObjects::runTimePostPro::fieldVisualisationBase:: +queryFieldSummary +( + const word& fieldName, + vtkCompositeDataSet* data +) +{ + fieldSummary queried; + + auto iter = vtkSmartPointer::New(); + + iter->SetDataSet(data); + iter->VisitOnlyLeavesOn(); + iter->SkipEmptyNodesOn(); + + for + ( + iter->InitTraversal(); + !iter->IsDoneWithTraversal(); + iter->GoToNextItem() + ) + { + vtkDataSet* dataset = vtkDataSet::SafeDownCast + ( + iter->GetCurrentDataObject() + ); + + if (dataset) + { + fieldSummary local(queryFieldSummary(fieldName, dataset)); + + if (!queried.nComponents_) + { + queried.nComponents_ = local.nComponents_; + } + + queried.association_ |= local.association_; + queried.range_ += local.range_; + } + } + + return queried; +} + + +Foam::functionObjects::runTimePostPro::fieldVisualisationBase::FieldAssociation +Foam::functionObjects::runTimePostPro::fieldVisualisationBase:: +queryFieldAssociation +( + const word& fieldName, + vtkDataSet* dataset +) +{ + unsigned where(FieldAssociation::NO_DATA); + + if (dataset) + { + if (dataset->GetCellData()->HasArray(fieldName.c_str())) + { + where |= FieldAssociation::CELL_DATA; + } + if (dataset->GetPointData()->HasArray(fieldName.c_str())) + { + where |= FieldAssociation::POINT_DATA; + } + } + + return FieldAssociation(where); +} + + +Foam::functionObjects::runTimePostPro::fieldVisualisationBase::FieldAssociation +Foam::functionObjects::runTimePostPro::fieldVisualisationBase:: +queryFieldAssociation +( + const word& fieldName, + vtkCompositeDataSet* data +) +{ + unsigned where(FieldAssociation::NO_DATA); + + auto iter = vtkSmartPointer::New(); + + iter->SetDataSet(data); + iter->VisitOnlyLeavesOn(); + iter->SkipEmptyNodesOn(); + + for + ( + iter->InitTraversal(); + !iter->IsDoneWithTraversal(); + iter->GoToNextItem() + ) + { + vtkDataSet* dataset = vtkDataSet::SafeDownCast + ( + iter->GetCurrentDataObject() + ); + + where |= queryFieldAssociation(fieldName, dataset); + } + + return FieldAssociation(where); +} + + +void Foam::functionObjects::runTimePostPro::fieldVisualisationBase::addMagField +( + const word& fieldName, + vtkFieldData* fieldData +) +{ + if (!fieldData) + { + return; + } + + vtkDataArray* input = vtkDataArray::SafeDownCast + ( + fieldData->GetAbstractArray(fieldName.c_str()) + ); + + if (!input) + { + return; + } + + const word magFieldName = "mag(" + fieldName + ")"; + + vtkDataArray* output = vtkDataArray::SafeDownCast + ( + fieldData->GetAbstractArray(magFieldName.c_str()) + ); + + if (output) + { + return; + } + + + // Simplfy and only handle scalar/vector input + + const int nCmpt = input->GetNumberOfComponents(); + const vtkIdType len = input->GetNumberOfTuples(); + + if (nCmpt == 1) + { + auto data = vtkSmartPointer::New(); + + data->SetName(magFieldName.c_str()); + data->SetNumberOfComponents(1); + data->SetNumberOfTuples(len); + + double scratch; + for (vtkIdType i=0; i < len; ++i) + { + input->GetTuple(i, &scratch); + + scratch = Foam::mag(scratch); + data->SetTuple(i, &scratch); + } + + fieldData->AddArray(data); + } + else if (nCmpt == 3) + { + auto data = vtkSmartPointer::New(); + + data->SetName(magFieldName.c_str()); + data->SetNumberOfComponents(1); + data->SetNumberOfTuples(len); + + doubleVector scratch; + for (vtkIdType i=0; i < len; ++i) + { + input->GetTuple(i, scratch.v_); + + scratch.x() = Foam::mag(scratch); + + data->SetTuple(i, scratch.v_); + } + + fieldData->AddArray(data); + } +} + + +void Foam::functionObjects::runTimePostPro::fieldVisualisationBase::addMagField +( + const word& fieldName, + vtkDataSet* dataset +) +{ + if (dataset) + { + addMagField(fieldName, dataset->GetCellData()); + addMagField(fieldName, dataset->GetPointData()); + } +} + + +void Foam::functionObjects::runTimePostPro::fieldVisualisationBase::addMagField +( + const word& fieldName, + vtkCompositeDataSet* data +) +{ + auto iter = vtkSmartPointer::New(); + + iter->SetDataSet(data); + iter->VisitOnlyLeavesOn(); + iter->SkipEmptyNodesOn(); + + for + ( + iter->InitTraversal(); + !iter->IsDoneWithTraversal(); + iter->GoToNextItem() + ) + { + vtkDataSet* dataset = vtkDataSet::SafeDownCast + ( + iter->GetCurrentDataObject() + ); + addMagField(fieldName, dataset); + } +} + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +void Foam::functionObjects::runTimePostPro::fieldVisualisationBase:: +fieldSummary::reduce() +{ + if (Pstream::parRun()) + { + Foam::reduce(nComponents_, maxOp()); + Foam::reduce(association_, bitOrOp()); + Foam::reduce(range_, minMaxOp()); + } +} + + +Foam::Ostream& Foam::operator<< +( + Ostream& os, + const InfoProxy + < + functionObjects::runTimePostPro::fieldVisualisationBase::fieldSummary + >& proxy +) +{ + os << "nComponents:" << proxy.t_.nComponents_ + << " association:" << label(proxy.t_.association_) + << " min/max:" << proxy.t_.range_; + + return os; +} + + // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // void Foam::functionObjects::runTimePostPro::fieldVisualisationBase:: @@ -82,7 +394,7 @@ setColourMap vtkLookupTable* lut ) const { - label nColours = 256; + constexpr label nColours = 256; lut->SetNumberOfColors(nColours); @@ -90,47 +402,73 @@ setColourMap switch (colourMap_) { - case cmRainbow: - { - ctf->SetColorSpaceToHSV(); - ctf->AddRGBPoint(0, 0, 0, 1); - ctf->AddRGBPoint(0.5, 0, 1, 0); - ctf->AddRGBPoint(1, 1, 0, 0); - break; - } - case cmBlueWhiteRed: + case cmCoolToWarm: // ParaView: "Cool To Warm" { - // Values taken from ParaView settings ctf->SetColorSpaceToDiverging(); - ctf->AddRGBPoint(0.0, 0.231373, 0.298039, 0.752941); + ctf->AddRGBPoint(0.0, 0.231372, 0.298039, 0.752941); ctf->AddRGBPoint(0.5, 0.865003, 0.865003, 0.865003); ctf->AddRGBPoint(1.0, 0.705882, 0.0156863, 0.14902); + // ctf->SetNanColor(1, 1, 0); + break; + } + + case cmColdAndHot: // ParaView : "Cold and Hot" + { + ctf->SetColorSpaceToRGB(); + ctf->AddRGBPoint(0, 0, 1, 1); + ctf->AddRGBPoint(0.45, 0, 0, 1); + ctf->AddRGBPoint(0.5, 0, 0, 0.5019608); + ctf->AddRGBPoint(0.55, 1, 0, 0); + ctf->AddRGBPoint(1, 1, 1, 0); break; } - case cmFire: + + case cmFire: // ParaView: Black-Body Radiation { - // Values taken from ParaView settings ctf->SetColorSpaceToRGB(); ctf->AddRGBPoint(0, 0, 0, 0); ctf->AddRGBPoint(0.4, 0.901961, 0, 0); ctf->AddRGBPoint(0.8, 0.901961, 0.901961, 0); ctf->AddRGBPoint(1, 1, 1, 1); + // ctf->SetNanColor(0, 0.49804, 1); + break; + } + + case cmRainbow: + { + ctf->SetColorSpaceToHSV(); + ctf->AddRGBPoint(0, 0, 0, 1); + ctf->AddRGBPoint(0.5, 0, 1, 0); + ctf->AddRGBPoint(1, 1, 0, 0); + // ctf->SetNanColor(0.498039, 0.498039, 0.498039); break; } - case cmGreyscale: + + case cmGreyscale: // ParaView: grayscale { ctf->SetColorSpaceToRGB(); ctf->AddRGBPoint(0, 0, 0, 0); ctf->AddRGBPoint(1, 1, 1, 1); + // ctf->SetNanColor(1, 0, 0); + break; + } + + case cmXray: // ParaView: "X ray" + { + ctf->SetColorSpaceToRGB(); + ctf->AddRGBPoint(0, 1, 1, 1); + ctf->AddRGBPoint(1, 0, 0, 0); + // ctf->SetNanColor(1, 0, 0); break; } } - for (label i = 0; i < nColours; i++) + double rgba[4] = { 0, 0, 0, 1 }; + for (label i = 0; i < nColours; ++i) { - double* c = ctf->GetColor(scalar(i)/scalar(nColours)); - lut->SetTableValue(i, c[0], c[1], c[2], 1.0); + ctf->GetColor(scalar(i)/scalar(nColours), rgba); + lut->SetTableValue(i, rgba); } } @@ -143,105 +481,11 @@ addScalarBar vtkLookupTable* lut ) const { - // Add scalar bar legend - if (!scalarBar_.visible_) - { - return; - } - - auto sbar = vtkSmartPointer::New(); - sbar->SetLookupTable(lut); - sbar->SetNumberOfLabels(scalarBar_.numberOfLabels_); - - const vector textColour = colours_["text"]->value(position); - - // Work-around to supply our own scalarbar title - // - Default scalar bar title text is scales by the scalar bar box - // dimensions so if the title is a long string, the text is shrunk to fit - // Instead, suppress title and set the title using a vtkTextActor - auto titleActor = vtkSmartPointer::New(); - sbar->SetTitle(" "); - titleActor->SetInput(scalarBar_.title_.c_str()); - titleActor->GetTextProperty()->SetFontFamilyToArial(); - titleActor->GetTextProperty()->SetFontSize(3*scalarBar_.fontSize_); - titleActor->GetTextProperty()->SetJustificationToCentered(); - titleActor->GetTextProperty()->SetVerticalJustificationToBottom(); - titleActor->GetTextProperty()->BoldOn(); - titleActor->GetTextProperty()->ItalicOff(); - titleActor->GetTextProperty()->SetColor - ( - textColour[0], - textColour[1], - textColour[2] - ); - titleActor->GetPositionCoordinate()-> - SetCoordinateSystemToNormalizedViewport(); - - // How to use the standard scalar bar text - // sbar->SetTitle(scalarBar_.title_.c_str()); - // sbar->GetTitleTextProperty()->SetColor - // ( - // textColour[0], - // textColour[1], - // textColour[2] - // ); - // sbar->GetTitleTextProperty()->SetFontSize(scalarBar_.fontSize_); - // sbar->GetTitleTextProperty()->ShadowOff(); - // sbar->GetTitleTextProperty()->BoldOn(); - // sbar->GetTitleTextProperty()->ItalicOff(); - - sbar->GetLabelTextProperty()->SetColor - ( - textColour[0], - textColour[1], - textColour[2] - ); - sbar->GetLabelTextProperty()->SetFontSize(scalarBar_.fontSize_); - sbar->GetLabelTextProperty()->ShadowOff(); - sbar->GetLabelTextProperty()->BoldOff(); - sbar->GetLabelTextProperty()->ItalicOff(); - sbar->SetLabelFormat(scalarBar_.labelFormat_.c_str()); - - sbar->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport(); - sbar->GetPositionCoordinate()->SetValue - ( - scalarBar_.position_.first(), - scalarBar_.position_.second() - ); - if (scalarBar_.vertical_) - { - sbar->SetOrientationToVertical(); - sbar->SetWidth(0.1); - sbar->SetHeight(0.75); - sbar->SetTextPositionToSucceedScalarBar(); - } - else + // Add the scalar bar - only once! + if (renderer && Pstream::master()) { - sbar->SetOrientationToHorizontal(); - - // Adjustments since not using scalarbar title property - sbar->SetWidth(0.75); - sbar->SetHeight(0.07); - sbar->SetBarRatio(0.5); - // sbar->SetHeight(0.1); - // sbar->SetTitleRatio(0.01); - sbar->SetTextPositionToPrecedeScalarBar(); + scalarBar_.add(colours_["text"]->value(position), renderer, lut); } - - titleActor->GetPositionCoordinate()->SetValue - ( - scalarBar_.position_.first() + 0.5*sbar->GetWidth(), - scalarBar_.position_.second() + sbar->GetHeight() - ); - - // sbar->DrawFrameOn(); - // sbar->DrawBackgroundOn(); - // sbar->UseOpacityOff(); - // sbar->VisibilityOff(); - sbar->VisibilityOn(); - - renderer->AddActor(sbar); - renderer->AddActor2D(titleActor); } @@ -250,9 +494,9 @@ setField ( const scalar position, const word& colourFieldName, - vtkPolyDataMapper* mapper, - vtkRenderer* renderer, - vtkPolyData* pData + const FieldAssociation fieldAssociation, + vtkMapper* mapper, + vtkRenderer* renderer ) const { mapper->InterpolateScalarsBeforeMappingOn(); @@ -264,6 +508,7 @@ setField mapper->ScalarVisibilityOff(); break; } + case cbField: { // Create look-up table for colours @@ -276,15 +521,15 @@ setField const char* fieldName = colourFieldName.c_str(); mapper->SelectColorArray(fieldName); - // Set to use either point or cell data - // Note: if both point and cell data exists, preferentially - // choosing point data. This is often the case when using - // glyphs - if (pData->GetPointData()->HasArray(fieldName)) + // Use either point or cell data + // - if both point and cell data exists, preferentially choose + // point data. This is often the case when using glyphs. + + if (fieldAssociation & FieldAssociation::POINT_DATA) { mapper->SetScalarModeToUsePointFieldData(); } - else if (pData->GetCellData()->HasArray(fieldName)) + else if (fieldAssociation & FieldAssociation::CELL_DATA) { mapper->SetScalarModeToUseCellFieldData(); } @@ -300,7 +545,7 @@ setField mapper->SetLookupTable(lut); mapper->ScalarVisibilityOn(); - // Add the bar + // Add the scalar bar addScalarBar(position, renderer, lut); break; } @@ -315,43 +560,20 @@ addGlyphs ( const scalar position, const word& scaleFieldName, + const fieldSummary& scaleFieldInfo, const word& colourFieldName, + const fieldSummary& colourFieldInfo, const scalar maxGlyphLength, + vtkPolyData* data, vtkActor* actor, vtkRenderer* renderer ) const { - auto glyph = vtkSmartPointer::New(); - auto glyphMapper = vtkSmartPointer::New(); - glyphMapper->SetInputConnection(glyph->GetOutputPort()); - - glyph->SetInputData(data); - glyph->ScalingOn(); - - bool needPointData = false; - - // Determine whether we have scalar or vector data - // and if we need to convert CellData -> PointData - - label nComponents = -1; - const char* scaleFieldNameChar = scaleFieldName.c_str(); - if (data->GetPointData()->HasArray(scaleFieldNameChar)) - { - nComponents = - data->GetPointData()->GetArray(scaleFieldNameChar) - ->GetNumberOfComponents(); - } - else if (data->GetCellData()->HasArray(scaleFieldNameChar)) - { - // Need to convert CellData to PointData - needPointData = true; + // Determine whether we have CellData/PointData and (scalar/vector) + // or if we need to a cell->point data filter. - nComponents = - data->GetCellData()->GetArray(scaleFieldNameChar) - ->GetNumberOfComponents(); - } - else + if (!scaleFieldInfo.exists()) { WarningInFunction << "Cannot add glyphs. No such cell or point field: " @@ -359,30 +581,46 @@ addGlyphs return; } - - const bool ok = (nComponents == 1 || nComponents == 3); - - if (!ok) + if (!scaleFieldInfo.isScalar() && !scaleFieldInfo.isVector()) { WarningInFunction << "Glyphs can only be added to scalar or vector data. " << "Unable to process field " << scaleFieldName << endl; return; } - else if (needPointData) + + + // Setup glyphs + + // The min/max data range for the input data (cell or point), + // which will be slightly less after using a cell->point filter + // (since it averages), but is still essentially OK. + + + auto glyph = vtkSmartPointer::New(); + glyph->ScalingOn(); + + auto glyphMapper = vtkSmartPointer::New(); + glyphMapper->SetInputConnection(glyph->GetOutputPort()); + + vtkSmartPointer cellToPoint; + + // The data source is filtered or original (PointData) + if (!scaleFieldInfo.hasPointData() || !colourFieldInfo.hasPointData()) { - auto cellToPoint = vtkSmartPointer::New(); + // CellData - Need a cell->point filter + cellToPoint = vtkSmartPointer::New(); cellToPoint->SetInputData(data); - cellToPoint->Update(); - vtkDataSet* pds = cellToPoint->GetOutput(); - vtkDataArray* pData = pds->GetPointData()->GetArray(scaleFieldNameChar); - // Store in main vtkPolyData - data->GetPointData()->AddArray(pData); + glyph->SetInputConnection(cellToPoint->GetOutputPort()); + } + else + { + glyph->SetInputData(data); } - if (nComponents == 1) + if (scaleFieldInfo.nComponents_ == 1) { auto sphere = vtkSmartPointer::New(); sphere->SetCenter(0, 0, 0); @@ -396,12 +634,12 @@ addGlyphs if (maxGlyphLength > 0) { - // Can get range from point data: - - // double range[2]; - // vtkDataArray* values = - // data->GetPointData()->GetScalars(scaleFieldNameChar); - // values->GetRange(range); + // Using range from the data: + // glyph->SetRange + // ( + // scaleFieldInfo.range_.first(), + // scaleFieldInfo.range_.second() + // ); // Set range according to user-supplied limits glyph->ClampingOn(); @@ -420,14 +658,14 @@ addGlyphs glyph->SetColorModeToColorByScalar(); glyph->SetInputArrayToProcess ( - 0, // scalars - 0, - 0, + 0, // index (0) = scalars + 0, // port + 0, // connection vtkDataObject::FIELD_ASSOCIATION_POINTS, - scaleFieldNameChar + scaleFieldName.c_str() ); } - else if (nComponents == 3) + else if (scaleFieldInfo.nComponents_ == 3) { auto arrow = vtkSmartPointer::New(); arrow->SetTipResolution(10); @@ -440,24 +678,13 @@ addGlyphs if (maxGlyphLength > 0) { - vtkDataArray* values = - data->GetPointData()->GetVectors(scaleFieldNameChar); - - double range[6]; - values->GetRange(range); - - // Attempt to set range for vectors... - // scalar x0 = sqrt(sqr(range_.first())/3.0); - // scalar x1 = sqrt(sqr(range_.second())/3.0); - // range[0] = x0; - // range[1] = x0; - // range[2] = x0; - // range[3] = x1; - // range[4] = x1; - // range[5] = x1; - + // Set range according data limits glyph->ClampingOn(); - glyph->SetRange(range); + glyph->SetRange + ( + scaleFieldInfo.range_.first(), + scaleFieldInfo.range_.second() + ); glyph->SetScaleFactor(maxGlyphLength); } else @@ -470,20 +697,30 @@ addGlyphs glyph->SetColorModeToColorByVector(); glyph->SetInputArrayToProcess ( - 1, // vectors - 0, - 0, + 1, // index (1) = vectors + 0, // port + 0, // connection vtkDataObject::FIELD_ASSOCIATION_POINTS, - scaleFieldNameChar + scaleFieldName.c_str() ); } - if (ok) + // Apply colouring etc. + // We already established PointData, which as either in the original, + // or generated with vtkCellDataToPointData filter. + { glyph->Update(); - setField(position, colourFieldName, glyphMapper, renderer, data); + setField + ( + position, + colourFieldName, + FieldAssociation::POINT_DATA, // Original or after filter + glyphMapper, + renderer + ); glyphMapper->Update(); @@ -505,9 +742,11 @@ fieldVisualisationBase : colours_(colours), fieldName_(dict.get("field")), + smooth_(dict.lookupOrDefault("smooth", false)), colourBy_(cbColour), colourMap_(cmRainbow), - range_() + range_(), + scalarBar_() { colourByTypeNames.readEntry("colourBy", dict, colourBy_); @@ -515,26 +754,24 @@ fieldVisualisationBase { case cbColour: { - scalarBar_.visible_ = false; + scalarBar_.hide(); break; } + case cbField: { dict.readEntry("range", range_); - colourMapTypeNames.readIfPresent("colourMap", dict, colourMap_); - const dictionary& sbDict = dict.subDict("scalarBar"); - sbDict.readEntry("visible", scalarBar_.visible_); + const dictionary* sbar = dict.findDict("scalarBar"); - if (scalarBar_.visible_) + if (sbar) + { + scalarBar_.read(*sbar); + } + else { - sbDict.readEntry("vertical", scalarBar_.vertical_); - sbDict.readEntry("position", scalarBar_.position_); - sbDict.readEntry("title", scalarBar_.title_); - sbDict.readEntry("fontSize", scalarBar_.fontSize_); - sbDict.readEntry("labelFormat", scalarBar_.labelFormat_); - sbDict.readEntry("numberOfLabels", scalarBar_.numberOfLabels_); + scalarBar_.hide(); } break; } diff --git a/src/runTimePostProcessing/fieldVisualisationBase.H b/src/runTimePostProcessing/fieldVisualisationBase.H index 9e2629f..b1a56af 100644 --- a/src/runTimePostProcessing/fieldVisualisationBase.H +++ b/src/runTimePostProcessing/fieldVisualisationBase.H @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2015-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -27,6 +27,20 @@ Class Description Base class for scene objects + Dictionary controls - colour by field + \table + Property | Description | Required | Default + colourBy | Colouring type (color / field) | yes | + range | Lower/upper range to display | yes | + smooth | Request smoother output | no | false + colourMap | Colour map for rendering | no | rainbow + scalarBar | Scalar-bar sub-dictionary | yes | + \endtable + +Colour maps include "coolToWarm" ("blueWhiteRed"), "coldAndHot", +"fire", "rainbow", "greyscale" ("grayscale"), "xray". For historical +reasons, the default is still "rainbow". + SourceFiles fieldVisualisationBase.C @@ -39,27 +53,35 @@ SourceFiles #include "Tuple2.H" #include "Enum.H" #include "vector.H" +#include "MinMax.H" #include "HashPtrTable.H" +#include "scalarBar.H" #include "Function1.H" -// VTK includes #include "vtkSmartPointer.h" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -// Forward declarations +// Forward Declarations class vtkActor; +class vtkCompositeDataSet; +class vtkCompositeDataGeometryFilter; +class vtkCompositePolyDataMapper; +class vtkDataSet; +class vtkDataSetAlgorithm; +class vtkFieldData; class vtkLookupTable; class vtkMapper; class vtkPolyData; class vtkPolyDataMapper; class vtkRenderer; + namespace Foam { namespace functionObjects { -// Forward declarations +// Forward Declarations class runTimePostProcessing; namespace runTimePostPro @@ -73,48 +95,110 @@ class fieldVisualisationBase { public: - // Public enumerations + // Public Enumerations + //- Colouring type enum colourByType { - cbColour, - cbField + cbColour, //!< "colour" : Use specified colour + cbField //!< "field" : Use named field }; + //- Enumeration names for colourByType static const Enum colourByTypeNames; + //- Colour map enumerations enum colourMapType { - cmRainbow, - cmBlueWhiteRed, - cmFire, - cmGreyscale + cmCoolToWarm, //!< ParaView "Cool To Warm" blue-white-read + cmBlueWhiteRed = cmCoolToWarm, + cmColdAndHot, //!< ParaView "Cold and Hot" + cmFire, //!< ParaView "Black-Body Radiation" + cmRainbow, //!< "rainbow" + cmGreyscale, //!< ParaView "Grayscale" + cmXray //!< ParaView "X Ray" }; + //- Enumeration names for colourMapType static const Enum colourMapTypeNames; -protected: + //- Enumeration of the data field associations + // These values are used internally and do NOT correspond to the + // vtkDataObject::FieldAssociations enumeration. + enum FieldAssociation + { + NO_DATA = 0, //!< No associated data + CELL_DATA = 0x1, //!< Associated with cells (faces) + POINT_DATA = 0x2, //!< Associated with points + CELL_POINT_DATA = 0x3 //!< Associated with cells and/or points + }; - // Protected Data - struct scalarBar + //- General field characteristics. + // For convenience, the interface is exposed but external use is + // highly discouraged. + struct fieldSummary { - bool visible_; - bool vertical_; - Tuple2 position_; - string title_; - label fontSize_; - string labelFormat_; - label numberOfLabels_; + int nComponents_; + unsigned association_; + scalarMinMax range_; + + //- Construct null + fieldSummary() + : + nComponents_(0), + association_(0u), + range_() + {} + + //- Parallel reduction. A no-op if Pstream::parRun() is false + void reduce(); + + //- True if nComponents_ == 1 + bool isScalar() const + { + return nComponents_ == 1; + } + + //- True if nComponents_ == 3 + bool isVector() const + { + return nComponents_ == 3; + } + + //- True if association_ is non-zero + bool exists() const + { + return association_; + } + + //- True if there is a POINT_DATA association + bool hasPointData() const + { + return (association_ & FieldAssociation::POINT_DATA); + } + + InfoProxy info() const + { + return InfoProxy(*this); + } }; + +protected: + + // Protected Data + //- Colours const HashPtrTable>& colours_; //- Field name word fieldName_; + //- Requested smoother fields (eg, interpolate cell -> point values) + bool smooth_; + //- Colour by type colourByType colourBy_; @@ -124,16 +208,67 @@ protected: //- Range of values Tuple2 range_; - //- Scalar bar + //- Scalar bar characteristics scalarBar scalarBar_; // Protected Member Functions + //- Query DataSet for field name and its field association + static fieldSummary queryFieldSummary + ( + const word& fieldName, + vtkDataSet* dataset + ); + + //- Query composite DataSet for field name and its FieldAssociation + static fieldSummary queryFieldSummary + ( + const word& fieldName, + vtkCompositeDataSet* data + ); + + //- Query DataSet for field name and its field association + static FieldAssociation queryFieldAssociation + ( + const word& fieldName, + vtkDataSet* dataset + ); + + //- Query composite DataSet for field name and its FieldAssociation + static FieldAssociation queryFieldAssociation + ( + const word& fieldName, + vtkCompositeDataSet* data + ); + + + //- Add "mag(..)" field for filters that only accept scalars + static void addMagField + ( + const word& fieldName, + vtkFieldData* fieldData + ); + + //- Add "mag(..)" field for filters that only accept scalars + static void addMagField + ( + const word& fieldName, + vtkDataSet* dataset + ); + + //- Add "mag(..)" field for filters that only accept scalars + static void addMagField + ( + const word& fieldName, + vtkCompositeDataSet* data + ); + + //- Set the colour map void setColourMap(vtkLookupTable* lut) const; - //- Add scalar bar to renderer + //- Add scalar bar (if visible) to renderer void addScalarBar ( const scalar position, @@ -146,9 +281,9 @@ protected: ( const scalar position, const word& colourFieldName, - vtkPolyDataMapper* mapper, - vtkRenderer* renderer, - vtkPolyData* pData + const FieldAssociation fieldAssociation, + vtkMapper* mapper, + vtkRenderer* renderer ) const; //- Add glyphs @@ -156,13 +291,17 @@ protected: ( const scalar position, const word& scaleFieldName, + const fieldSummary& scaleFieldInfo, const word& colourFieldName, + const fieldSummary& colourFieldInfo, const scalar maxGlyphLength, + vtkPolyData* data, vtkActor* actor, vtkRenderer* renderer ) const; + //- No copy construct fieldVisualisationBase(const fieldVisualisationBase&) = delete; @@ -202,6 +341,19 @@ public: } // End namespace runTimePostPro } // End namespace functionObjects + + +// Ostream +Ostream& operator<< +( + Ostream& os, + const InfoProxy + < + functionObjects::runTimePostPro::fieldVisualisationBase::fieldSummary + >& proxy +); + + } // End namespace Foam // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/src/runTimePostProcessing/functionObjectBase.C b/src/runTimePostProcessing/functionObjectBase.C index 28d4fbf..c44fecc 100644 --- a/src/runTimePostProcessing/functionObjectBase.C +++ b/src/runTimePostProcessing/functionObjectBase.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2016-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -25,20 +25,6 @@ License #include "functionObjectBase.H" -// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // - -namespace Foam -{ -namespace functionObjects -{ -namespace runTimePostPro -{ - defineTypeNameAndDebug(functionObjectBase, 0); -} -} -} - - // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // Foam::fileName @@ -69,7 +55,12 @@ bool Foam::functionObjects::runTimePostPro::functionObjectBase::removeFile { // Foam::rm() ignores empty names etc. - return Foam::rm(getFileName(keyword, subDictName)); + if (Pstream::master()) + { + return Foam::rm(getFileName(keyword, subDictName)); + } + + return false; } @@ -85,6 +76,7 @@ Foam::functionObjects::runTimePostPro::functionObjectBase::functionObjectBase fieldVisualisationBase(dict, colours), state_(state), functionObjectName_(dict.get("functionObject")), + liveObject_(dict.lookupOrDefault("liveObject", true)), clearObjects_(dict.lookupOrDefault("clearObjects", false)) {} diff --git a/src/runTimePostProcessing/functionObjectBase.H b/src/runTimePostProcessing/functionObjectBase.H index 47647fc..abab1a2 100644 --- a/src/runTimePostProcessing/functionObjectBase.H +++ b/src/runTimePostProcessing/functionObjectBase.H @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2016-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -29,11 +29,15 @@ Description Dictionary controls \table - Property | Description | Required | Default - functionObject | The data source | yes | - clearObjects | Remove file after use | no | no + Property | Description | Required | Default + functionObject | The data source | yes | + clearObjects | Remove file after use | no | no + liveObject | Prefer simulation data source | no | true \endtable + The "live" keyword indiates that from within the simulation (in memory) + is preferred over data from disk (for example). + SourceFiles functionObjectBase.C @@ -64,14 +68,17 @@ class functionObjectBase { protected: - // Protected data + // Protected Data //- Reference to the state const stateFunctionObject& state_; - //- The function object name which provides the source data + //- The function object name that provides the source data word functionObjectName_; + //- Flag to indicate "live" (simulation) data source should be used + bool liveObject_; + //- Flag to indicate that source data should be cleared after use bool clearObjects_; @@ -109,6 +116,7 @@ protected: // \note does not change the stateFunctionObject bool removeFile(const word& keyword, const word& subDictName); + //- No copy construct functionObjectBase(const functionObjectBase&) = delete; @@ -118,10 +126,6 @@ protected: public: - //- Run-time type information - TypeName("functionObjectBase"); - - // Constructors //- Construct from dictionary diff --git a/src/runTimePostProcessing/functionObjectCloud.C b/src/runTimePostProcessing/functionObjectCloud.C index e9c3540..54e71f0 100644 --- a/src/runTimePostProcessing/functionObjectCloud.C +++ b/src/runTimePostProcessing/functionObjectCloud.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2015-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -31,11 +31,11 @@ License // VTK includes #include "vtkActor.h" -#include "vtkRenderer.h" -#include "vtkSmartPointer.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkProperty.h" +#include "vtkRenderer.h" +#include "vtkSmartPointer.h" // VTK Readers #include "vtkPolyDataReader.h" @@ -49,13 +49,42 @@ namespace functionObjects { namespace runTimePostPro { - defineTypeNameAndDebug(functionObjectCloud, 0); + defineTypeName(functionObjectCloud); addToRunTimeSelectionTable(pointData, functionObjectCloud, dictionary); } } } +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +namespace +{ + +static vtkSmartPointer getPolyDataFile(const Foam::fileName& fName) +{ + // Very simple - we only support vtp files, which are expected to have + // the scaling and colouring fields. + + vtkSmartPointer dataset; + + if (fName.ext() == "vtp") + { + auto reader = vtkSmartPointer::New(); + + reader->SetFileName(fName.c_str()); + reader->Update(); + dataset = reader->GetOutput(); + + return dataset; + } + + return dataset; +} + +} // End anonymous namespace + + // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::functionObjects::runTimePostPro::functionObjectCloud::functionObjectCloud @@ -71,9 +100,7 @@ Foam::functionObjects::runTimePostPro::functionObjectCloud::functionObjectCloud inputFileName_(), colourFieldName_(dict.get("colourField")), actor_() -{ - actor_ = vtkSmartPointer::New(); -} +{} // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // @@ -85,8 +112,8 @@ Foam::functionObjects::runTimePostPro::functionObjectCloud:: // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // -void Foam::functionObjects::runTimePostPro::functionObjectCloud:: -addGeometryToScene +bool Foam::functionObjects::runTimePostPro::functionObjectCloud:: +addGeometryFromFile ( const scalar position, vtkRenderer* renderer @@ -94,72 +121,118 @@ addGeometryToScene { if (!visible_) { - return; + return false; } + vtkSmartPointer polyData; + + bool good = true; + // The vtkCloud stores 'file' via the stateFunctionObject // (lookup by cloudName). // It only generates VTP format, which means there is a single file // containing all fields. - inputFileName_ = getFileName("file", cloudName_); + if (Pstream::master()) + { + inputFileName_ = getFileName("file", cloudName_); + + if (inputFileName_.size()) + { + polyData = getPolyDataFile(inputFileName_); + + if (!polyData || polyData->GetNumberOfPoints() == 0) + { + good = false; + + WarningInFunction + << "Could not read "<< inputFileName_ << nl + << "Only VTK (.vtp) files are supported" + << endl; + } + else + { + DebugInfo + << " Resolved cloud file " + << inputFileName_ << endl; + } + } + else + { + good = false; - if (inputFileName_.empty()) + WarningInFunction + << "Unable to find function object " << functionObjectName_ + << " output for field " << fieldName_ + << ". Cloud will not be processed" + << endl; + } + } + else { - WarningInFunction - << "Unable to find function object " << functionObjectName_ - << " output for field " << fieldName_ - << ". Cloud will not be processed" - << endl; - return; + inputFileName_.clear(); } + reduce(good, andOp()); - vtkSmartPointer dataset; - - if (inputFileName_.hasExt("vtp")) + if (!good) { - auto reader = vtkSmartPointer::New(); - reader->SetFileName(inputFileName_.c_str()); - reader->Update(); - - dataset = reader->GetOutput(); + return false; } - else + + // Only render on master + if (!renderer || !Pstream::master()) { - // Invalid name - ignore. - // Don't support VTK legacy format at all - it is too wasteful - // and cumbersome. + return true; + } - WarningInFunction - << "Could not read "<< inputFileName_ << nl - << "Only VTK (.vtp) files are supported" - << ". Cloud will not be processed" - << endl; - inputFileName_.clear(); - } + // Rendering + actor_ = vtkSmartPointer::New(); - if (dataset) { + fieldSummary scaleFieldInfo = + queryFieldSummary(fieldName_, polyData); + + fieldSummary colourFieldInfo = + queryFieldSummary(colourFieldName_, polyData); + + // No reduction + auto mapper = vtkSmartPointer::New(); actor_->SetMapper(mapper); + /// dataset->Print(std::cout); + addGlyphs ( position, - fieldName_, - colourFieldName_, + fieldName_, scaleFieldInfo, // scaling + colourFieldName_, colourFieldInfo, // colour maxGlyphLength_, - dataset, + polyData, actor_, renderer ); renderer->AddActor(actor_); } + + return true; +} + + +void Foam::functionObjects::runTimePostPro::functionObjectCloud:: +addGeometryToScene +( + const scalar position, + vtkRenderer* renderer +) +{ + // File source + addGeometryFromFile(position, renderer); } @@ -168,10 +241,16 @@ void Foam::functionObjects::runTimePostPro::functionObjectCloud::updateActors const scalar position ) { - actor_->GetProperty()->SetOpacity(opacity(position)); + if (actor_) + { + const vector colour = pointColour_->value(position); + + vtkProperty* prop = actor_->GetProperty(); - vector pc = pointColour_->value(position); - actor_->GetProperty()->SetColor(pc[0], pc[1], pc[2]); + prop->SetOpacity(opacity(position)); + + prop->SetColor(colour[0], colour[1], colour[2]); + } } diff --git a/src/runTimePostProcessing/functionObjectCloud.H b/src/runTimePostProcessing/functionObjectCloud.H index 514091c..ba8d43a 100644 --- a/src/runTimePostProcessing/functionObjectCloud.H +++ b/src/runTimePostProcessing/functionObjectCloud.H @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2015-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -25,7 +25,17 @@ Class Foam::functionObjects::runTimePostPro::functionObjectCloud Description - Visualisation of cloud data from function object output + Visualisation of cloud data from function object output (file-based only). + + Dictionary controls + \table + Property | Description | Required | Default + type | The point type: functionObjectCloud | yes | + functionObject | The data source | yes | + cloud | The cloud name | no | + field | The field for glyphs scaling | no | + colourField | The field to display | no | + \endtable SourceFiles functionObjectCloud.C @@ -85,7 +95,7 @@ protected: public: //- Run-time type information - TypeName("functionObjectCloud"); + TypeNameNoDebug("functionObjectCloud"); // Constructors @@ -105,7 +115,14 @@ public: // Member Functions - //- Add tube(s) to scene + //- Add cloud to scene (using file source) + bool addGeometryFromFile + ( + const scalar position, + vtkRenderer* renderer + ); + + //- Add cloud to scene virtual void addGeometryToScene ( const scalar position, diff --git a/src/runTimePostProcessing/functionObjectLine.C b/src/runTimePostProcessing/functionObjectLine.C index 1f7d5ff..e27dabc 100644 --- a/src/runTimePostProcessing/functionObjectLine.C +++ b/src/runTimePostProcessing/functionObjectLine.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2015-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -48,7 +48,7 @@ namespace functionObjects { namespace runTimePostPro { - defineTypeNameAndDebug(functionObjectLine, 0); + defineTypeName(functionObjectLine); addToRunTimeSelectionTable(pathline, functionObjectLine, dictionary); } } @@ -65,7 +65,7 @@ static vtkSmartPointer getPolyDataFile(const Foam::fileName& fName) // Not extremely elegant... vtkSmartPointer dataset; - if (fName.ext() == "vtk") + if ("vtk" == fName.ext()) { auto reader = vtkSmartPointer::New(); @@ -76,7 +76,7 @@ static vtkSmartPointer getPolyDataFile(const Foam::fileName& fName) return dataset; } - if (fName.ext() == "vtp") + if ("vtp" == fName.ext()) { auto reader = vtkSmartPointer::New(); @@ -125,7 +125,8 @@ addGeometryToScene vtkRenderer* renderer ) { - if (!visible_) + // Currently master-only + if (!visible_ || !renderer || !Pstream::master()) { return; } @@ -153,10 +154,19 @@ addGeometryToScene return; } + DebugInfo << " Resolved lines " << fName << endl; + auto mapper = vtkSmartPointer::New(); - setField(position, fieldName_, mapper, renderer, polyData); + setField + ( + position, + fieldName_, + queryFieldAssociation(fieldName_, polyData), + mapper, + renderer + ); actor_->SetMapper(mapper); diff --git a/src/runTimePostProcessing/functionObjectLine.H b/src/runTimePostProcessing/functionObjectLine.H index fcb0150..63cc961 100644 --- a/src/runTimePostProcessing/functionObjectLine.H +++ b/src/runTimePostProcessing/functionObjectLine.H @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2015-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -25,7 +25,13 @@ Class Foam::functionObjects::runTimePostPro::functionObjectLine Description - Visualisation of line data from function object output + Visualisation of line data from function object output (file-based only). + + Dictionary controls + \table + Property | Description | Required | Default + type | The line type: functionObjectLine | yes | + \endtable SourceFiles functionObjectLine.C @@ -76,7 +82,7 @@ protected: public: //- Run-time type information - TypeName("functionObjectLine"); + TypeNameNoDebug("functionObjectLine"); // Constructors diff --git a/src/runTimePostProcessing/functionObjectSurface.C b/src/runTimePostProcessing/functionObjectSurface.C index 15ed336..801305f 100644 --- a/src/runTimePostProcessing/functionObjectSurface.C +++ b/src/runTimePostProcessing/functionObjectSurface.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2015-2016 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -30,6 +30,13 @@ License // VTK includes #include "vtkActor.h" +#include "vtkCellData.h" +#include "vtkCellDataToPointData.h" +#include "vtkCompositeDataGeometryFilter.h" +#include "vtkCompositeDataSet.h" +#include "vtkCompositePolyDataMapper.h" +#include "vtkMultiPieceDataSet.h" +#include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkProperty.h" @@ -48,7 +55,7 @@ namespace functionObjects { namespace runTimePostPro { - defineTypeNameAndDebug(functionObjectSurface, 0); + defineTypeName(functionObjectSurface); addToRunTimeSelectionTable(surface, functionObjectSurface, dictionary); } } @@ -65,7 +72,7 @@ static vtkSmartPointer getPolyDataFile(const Foam::fileName& fName) // Not extremely elegant... vtkSmartPointer dataset; - if (fName.ext() == "vtk") + if ("vtk" == fName.ext()) { auto reader = vtkSmartPointer::New(); @@ -76,7 +83,7 @@ static vtkSmartPointer getPolyDataFile(const Foam::fileName& fName) return dataset; } - if (fName.ext() == "vtp") + if ("vtp" == fName.ext()) { auto reader = vtkSmartPointer::New(); @@ -108,17 +115,10 @@ functionObjectSurface {} -// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // - -Foam::functionObjects::runTimePostPro::functionObjectSurface:: -~functionObjectSurface() -{} - - // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // -void Foam::functionObjects::runTimePostPro::functionObjectSurface:: -addGeometryToScene +bool Foam::functionObjects::runTimePostPro::functionObjectSurface:: +addGeometry ( const scalar position, vtkRenderer* renderer @@ -126,39 +126,291 @@ addGeometryToScene { if (!visible_) { - return; + return false; } - fileName fName = getFileName("file", fieldName_); - if (fName.empty()) + DebugInfo << " Resolve surface " << functionObjectName_ << endl; + + const polySurface* surf = + ( + geometryBase::parent_.storedObjects() + .cfindObject(functionObjectName_) + ); + + // Treat surface with no faces/points like a missing surface + surf = ((surf && surf->nPoints()) ? surf : nullptr); + + bool hasSurface = surf; + + + // Retrieve the field association (CELL, POINT) for the given field + + unsigned fieldAssociation(0u); + if (surf) + { + unsigned queried = surf->queryFieldAssociation(fieldName_); + + if (queried & polySurface::FACE_DATA) + { + fieldAssociation |= FieldAssociation::CELL_DATA; + } + if (queried & polySurface::POINT_DATA) + { + fieldAssociation |= FieldAssociation::POINT_DATA; + } + } + + // Reduce the information + if (Pstream::parRun()) + { + if (!hasSurface) + { + // No geometry - set all field association bits ON to ensure + // it does not affect bitwise reduction. + fieldAssociation = (~0u); + } + + reduce(hasSurface, orOp()); + reduce(fieldAssociation, bitAndOp()); + } + + if (!hasSurface) { WarningInFunction - << "Unable to read file name from function object " - << functionObjectName_ << " for field " << fieldName_ - << ". Surface will not be processed" + << "No functionObject surface, or has no faces: " + << functionObjectName_ << endl; - return; + + if (debug) + { + Info<< " Available surfaces:" << nl + << geometryBase::parent_.storedObjects() + .sortedNames() << endl; + } + return false; } + //// Pout<< "local surface = " << (surf ? surf->nFaces() : 0) << nl; + + + // Create a vtkMultiPieceDataSet with vtkPolyData on the leaves + vtkSmartPointer multiPiece; - auto polyData = getPolyDataFile(fName); + // Requesting glyphs on the surface AND only have face data? + // - just use the faceCentres directly and attach fields as CellData + // (not PointData). - if (!polyData || polyData->GetNumberOfPoints() == 0) + if + ( + representation_ == rtGlyph + && (fieldAssociation == FieldAssociation::CELL_DATA) + ) { - WarningInFunction - << "Could not read "<< fName << nl - << "Only VTK (.vtp, .vtk) files are supported" - << endl; - return; + multiPiece = gatherFaceCentres(surf); + } + else + { + multiPiece = gatherSurfacePieces(surf); + } + + + // Add the field (the information is consistent after last reduction). + + // Need field(s) for glyphs or colourByField: + + if (representation_ == rtGlyph || colourBy_ == cbField) + { + if (fieldAssociation == FieldAssociation::CELL_DATA) + { + addDimField + ( + multiPiece, + surf, + fieldName_ + ); + } + else if (fieldAssociation & FieldAssociation::POINT_DATA) + { + addDimField + ( + multiPiece, + surf, + fieldName_ + ); + } + } + + + // Now have a multi-piece dataset that is one of the following: + // + // - one-piece per processor (OpenFOAM = parallel, VTK=parallel) + // - all pieces on master only (OpenFOAM = parallel, VTK=serial) + + // Re-query field information - we may have stored it differently + // than the original source. + + fieldSummary fieldInfo = queryFieldSummary(fieldName_, multiPiece); + fieldInfo.reduce(); + + + // Not rendered on this processor? + // This is where we stop, but could also have an MPI barrier + if (!renderer) + { + return true; + } + + + // Rendering + + { + auto polyData = vtkSmartPointer::New(); + + polyData->SetInputData(multiPiece); + polyData->Update(); + + if (representation_ == rtGlyph) + { + addGlyphs + ( + position, + fieldName_, fieldInfo, // scaling + fieldName_, fieldInfo, // colouring + maxGlyphLength_, + polyData->GetOutput(), + surfaceActor_, + renderer + ); + } + else + { + vtkSmartPointer cellToPoint; + + // CellData - Need a cell->point filter + if (smooth_ && !fieldInfo.hasPointData()) + { + cellToPoint = vtkSmartPointer::New(); + cellToPoint->SetInputData(multiPiece); + + polyData->SetInputConnection(cellToPoint->GetOutputPort()); + } + else + { + polyData->SetInputData(multiPiece); + } + polyData->Update(); + + + if (!smooth_) + { + addFeatureEdges(renderer, polyData); + } + + auto mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(polyData->GetOutputPort()); + + setField + ( + position, + fieldName_, + ( + smooth_ + ? FieldAssociation::POINT_DATA + : FieldAssociation(fieldInfo.association_) + ), + mapper, + renderer + ); + + surfaceActor_->SetMapper(mapper); + + setRepresentation(surfaceActor_); + + renderer->AddActor(surfaceActor_); + } + } + + return true; +} + + +bool Foam::functionObjects::runTimePostPro::functionObjectSurface:: +addGeometryFromFile +( + const scalar position, + vtkRenderer* renderer +) +{ + if (!visible_) + { + return false; } + vtkSmartPointer polyData; + + bool good = true; + + // File reading is serial (master only) + if (Pstream::master()) + { + fileName fName = getFileName("file", fieldName_); + + if (fName.size()) + { + polyData = getPolyDataFile(fName); + + if (!polyData || polyData->GetNumberOfPoints() == 0) + { + good = false; + + WarningInFunction + << "Could not read "<< fName << nl + << "Only VTK (.vtp, .vtk) files are supported" + << endl; + } + else + { + DebugInfo << " Resolved surface " << fName << endl; + } + } + else + { + good = false; + + WarningInFunction + << "Unable to read file name from function object " + << functionObjectName_ << " for field " << fieldName_ + << ". Surface will not be processed" + << endl; + } + } + + reduce(good, andOp()); + + if (!good) + { + return false; + } + + // Only render on master + if (!renderer || !Pstream::master()) + { + return true; + } + + fieldSummary fieldInfo = queryFieldSummary(fieldName_, polyData); + // No reduction (serial) + + + // Render + if (representation_ == rtGlyph) { addGlyphs ( position, - fieldName_, - fieldName_, + fieldName_, fieldInfo, // scaling + fieldName_, fieldInfo, // colouring maxGlyphLength_, polyData, surfaceActor_, @@ -172,7 +424,14 @@ addGeometryToScene auto mapper = vtkSmartPointer::New(); mapper->SetInputData(polyData); - setField(position, fieldName_, mapper, renderer, polyData); + setField + ( + position, + fieldName_, + queryFieldAssociation(fieldName_, polyData), + mapper, + renderer + ); surfaceActor_->SetMapper(mapper); @@ -180,6 +439,44 @@ addGeometryToScene renderer->AddActor(surfaceActor_); } + + return true; +} + + +void Foam::functionObjects::runTimePostPro::functionObjectSurface:: +addGeometryToScene +( + const scalar position, + vtkRenderer* renderer +) +{ + if (!visible_) + { + return; + } + + if (liveObject_) + { + // Live source + if (addGeometry(position, renderer)) + { + return; + } + + WarningInFunction + << "No functionObject live source, or is empty: " + << functionObjectName_ + << " ... attempting with file source" + << endl; + } + else + { + DebugInfo << "Using file source only" << nl; + } + + // File source + addGeometryFromFile(position, renderer); } @@ -187,6 +484,8 @@ bool Foam::functionObjects::runTimePostPro::functionObjectSurface::clear() { if (functionObjectBase::clear()) { + // Even for a "live" data source we allow file cleanup + // (eg, from a previous run, etc) return removeFile("file", fieldName_); } diff --git a/src/runTimePostProcessing/functionObjectSurface.H b/src/runTimePostProcessing/functionObjectSurface.H index c15762e..eb1288a 100644 --- a/src/runTimePostProcessing/functionObjectSurface.H +++ b/src/runTimePostProcessing/functionObjectSurface.H @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2015-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -25,7 +25,22 @@ Class Foam::functionObjects::runTimePostPro::functionObjectSurface Description - Visualisation of surface data from function object output + Visualisation of surface data from function object output, + typically the result of a prior sampled surfaces operation. + + Operates in a "live" mode, in which the previously sampled surfaces + are retrieved from the functionObject registry. + Or in the traditional file-based mode, in which the sampling is used + to generate a file and its name is retrieved from the functionObject + properties. + + File-based import is restricted to "vtk" and "vtp" formats. + + Dictionary controls + \table + Property | Description | Required | Default + type | The type: functionObjectSurface | yes | + \endtable SourceFiles functionObjectfunctionObjectSurface.C @@ -70,7 +85,7 @@ protected: public: //- Run-time type information - TypeName("functionObjectSurface"); + TypeNameNoDebug("functionObjectSurface"); // Constructors @@ -85,11 +100,25 @@ public: //- Destructor - virtual ~functionObjectSurface(); + virtual ~functionObjectSurface() = default; // Member Functions + //- Add functionObjectSurface to scene (using simulation source) + bool addGeometry + ( + const scalar position, + vtkRenderer* renderer + ); + + //- Add functionObjectSurface to scene (using file source) + bool addGeometryFromFile + ( + const scalar position, + vtkRenderer* renderer + ); + //- Add functionObjectSurface(s) to scene virtual void addGeometryToScene ( diff --git a/src/runTimePostProcessing/geometryBase.C b/src/runTimePostProcessing/geometryBase.C index 4f71bcb..bac6e4f 100644 --- a/src/runTimePostProcessing/geometryBase.C +++ b/src/runTimePostProcessing/geometryBase.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2015-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -33,6 +33,18 @@ License // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // +namespace Foam +{ +namespace functionObjects +{ +namespace runTimePostPro +{ + + defineDebugSwitchWithName(geometryBase, "runTimePostPro::geometryBase", 0); +} +} +} + const Foam::Enum < Foam::functionObjects::runTimePostPro::geometryBase::renderModeType @@ -87,7 +99,7 @@ Foam::functionObjects::runTimePostPro::geometryBase::geometryBase : parent_(parent), name_(dict.dictName()), - visible_(dict.get("visible")), + visible_(dict.lookupOrDefault("visible", true)), renderMode_ ( renderModeTypeNames.lookupOrDefault("renderMode", dict, rmGouraud) @@ -121,6 +133,13 @@ Foam::functionObjects::runTimePostPro::geometryBase::parent() const } +bool Foam::functionObjects::runTimePostPro::geometryBase:: +needsCollective() const +{ + return parent_.needsCollective(); +} + + const Foam::word& Foam::functionObjects::runTimePostPro::geometryBase::name() const { diff --git a/src/runTimePostProcessing/geometryBase.H b/src/runTimePostProcessing/geometryBase.H index 28aa261..6fdb2c5 100644 --- a/src/runTimePostProcessing/geometryBase.H +++ b/src/runTimePostProcessing/geometryBase.H @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2015-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -25,12 +25,12 @@ Class Foam::functionObjects::runTimePostPro::geometryBase Description - Base class for surface, text handling + Base class for surface, text handling etc. Dictionary controls \table Property | Description | Required | Default - visible | Display the object | yes | + visible | Display the object | no | yes renderMode | Shading (flat/gouraud/phong) | no | gouraud opacity | Object opacity | no | 1.0 \endtable @@ -51,7 +51,7 @@ SourceFiles // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -// Forward declarations +// Forward Declarations (VTK) class vtkRenderer; class vtkActor; @@ -60,7 +60,7 @@ namespace Foam namespace functionObjects { -// Forward declarations +// Forward Declarations class runTimePostProcessing; namespace runTimePostPro @@ -73,11 +73,11 @@ namespace runTimePostPro class geometryBase { - public: - // Public enumerations + // Public Enumerations + //- Surface shading types enum renderModeType { rmFlat, //!< Flat shading @@ -85,6 +85,7 @@ public: rmPhong //!< Phong shading }; + //- Names for surface shading types static const Enum renderModeTypeNames; @@ -95,7 +96,7 @@ protected: //- Reference to the parent function object const runTimePostProcessing& parent_; - //- Name + //- The surface name word name_; //- Visible flag @@ -111,7 +112,7 @@ protected: const HashPtrTable>& colours_; - // Protected functions + // Protected Functions //- Initialise actor void initialiseActor(vtkActor* actor) const; @@ -125,6 +126,9 @@ protected: public: + //- Debug switch + static int debug; + // Constructors //- Construct from dictionary @@ -147,6 +151,10 @@ public: //- Return the reference to the parent function object const runTimePostProcessing& parent() const; + //- May need to gather geometry parts to render on single-processor + // True when OpenFOAM is running in parallel but VTK is not. + bool needsCollective() const; + //- Return the name const word& name() const; @@ -160,7 +168,7 @@ public: const HashPtrTable>& colours() const; - // Scene interaction + // Scene Interaction //- Add geometry to scene virtual void addGeometryToScene @@ -172,7 +180,7 @@ public: //- Update the actors virtual void updateActors(const scalar position) = 0; - //- Clear files used to create the object(s) + //- Clear any files used to create the object(s) virtual bool clear() = 0; }; diff --git a/src/runTimePostProcessing/geometryCloud.C b/src/runTimePostProcessing/geometryCloud.C new file mode 100644 index 0000000..350c714 --- /dev/null +++ b/src/runTimePostProcessing/geometryCloud.C @@ -0,0 +1,263 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2019 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 . + +\*---------------------------------------------------------------------------*/ + +// OpenFOAM includes +#include "geometryCloud.H" +#include "cloud.H" +#include "fvMesh.H" +#include "runTimePostProcessing.H" +#include "addToRunTimeSelectionTable.H" + +// VTK includes +#include "vtkActor.h" +#include "vtkCompositeDataGeometryFilter.h" +#include "vtkCompositeDataSet.h" +#include "vtkCompositePolyDataMapper.h" +#include "vtkPolyData.h" +#include "vtkPolyDataMapper.h" +#include "vtkProperty.h" +#include "vtkRenderer.h" +#include "vtkSmartPointer.h" + +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +namespace Foam +{ +namespace functionObjects +{ +namespace runTimePostPro +{ + defineTypeName(geometryCloud); + addToRunTimeSelectionTable(pointData, geometryCloud, dictionary); +} +} +} + + +// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // + +bool Foam::functionObjects::runTimePostPro::geometryCloud::addCloudField +( + vtkMultiPieceDataSet* multiPiece, + const objectRegistry& obrTmp, + const word& fieldName +) const +{ + const regIOobject* ioptr = obrTmp.cfindObject(fieldName); + + return (multiPiece) && + ( + addCloudField