mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
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.
This commit is contained in:
committed by
Andrew Heather
parent
03e6aa1a6d
commit
42fbf6d38c
@ -2,10 +2,8 @@
|
||||
========= |
|
||||
\\ / 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 |
|
||||
-------------------------------------------------------------------------------
|
||||
| Copyright (C) 2015 OpenFOAM Foundation
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -32,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"
|
||||
@ -50,7 +55,7 @@ namespace functionObjects
|
||||
{
|
||||
namespace runTimePostPro
|
||||
{
|
||||
defineTypeNameAndDebug(functionObjectSurface, 0);
|
||||
defineTypeName(functionObjectSurface);
|
||||
addToRunTimeSelectionTable(surface, functionObjectSurface, dictionary);
|
||||
}
|
||||
}
|
||||
@ -67,7 +72,7 @@ static vtkSmartPointer<vtkPolyData> getPolyDataFile(const Foam::fileName& fName)
|
||||
// Not extremely elegant...
|
||||
vtkSmartPointer<vtkPolyData> dataset;
|
||||
|
||||
if (fName.ext() == "vtk")
|
||||
if ("vtk" == fName.ext())
|
||||
{
|
||||
auto reader = vtkSmartPointer<vtkPolyDataReader>::New();
|
||||
|
||||
@ -78,7 +83,7 @@ static vtkSmartPointer<vtkPolyData> getPolyDataFile(const Foam::fileName& fName)
|
||||
return dataset;
|
||||
}
|
||||
|
||||
if (fName.ext() == "vtp")
|
||||
if ("vtp" == fName.ext())
|
||||
{
|
||||
auto reader = vtkSmartPointer<vtkXMLPolyDataReader>::New();
|
||||
|
||||
@ -110,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
|
||||
@ -128,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<polySurface>(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<bool>());
|
||||
reduce(fieldAssociation, bitAndOp<unsigned>());
|
||||
}
|
||||
|
||||
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<polySurface>() << endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//// Pout<< "local surface = " << (surf ? surf->nFaces() : 0) << nl;
|
||||
|
||||
auto polyData = getPolyDataFile(fName);
|
||||
|
||||
if (!polyData || polyData->GetNumberOfPoints() == 0)
|
||||
// Create a vtkMultiPieceDataSet with vtkPolyData on the leaves
|
||||
vtkSmartPointer<vtkMultiPieceDataSet> multiPiece;
|
||||
|
||||
// Requesting glyphs on the surface AND only have face data?
|
||||
// - just use the faceCentres directly and attach fields as CellData
|
||||
// (not PointData).
|
||||
|
||||
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<polySurfaceGeoMesh>
|
||||
(
|
||||
multiPiece,
|
||||
surf,
|
||||
fieldName_
|
||||
);
|
||||
}
|
||||
else if (fieldAssociation & FieldAssociation::POINT_DATA)
|
||||
{
|
||||
addDimField<polySurfacePointGeoMesh>
|
||||
(
|
||||
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<vtkCompositeDataGeometryFilter>::New();
|
||||
|
||||
polyData->SetInputData(multiPiece);
|
||||
polyData->Update();
|
||||
|
||||
if (representation_ == rtGlyph)
|
||||
{
|
||||
addGlyphs
|
||||
(
|
||||
position,
|
||||
fieldName_, fieldInfo, // scaling
|
||||
fieldName_, fieldInfo, // colouring
|
||||
maxGlyphLength_,
|
||||
polyData->GetOutput(),
|
||||
surfaceActor_,
|
||||
renderer
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
vtkSmartPointer<vtkCellDataToPointData> cellToPoint;
|
||||
|
||||
// CellData - Need a cell->point filter
|
||||
if (smooth_ && !fieldInfo.hasPointData())
|
||||
{
|
||||
cellToPoint = vtkSmartPointer<vtkCellDataToPointData>::New();
|
||||
cellToPoint->SetInputData(multiPiece);
|
||||
|
||||
polyData->SetInputConnection(cellToPoint->GetOutputPort());
|
||||
}
|
||||
else
|
||||
{
|
||||
polyData->SetInputData(multiPiece);
|
||||
}
|
||||
polyData->Update();
|
||||
|
||||
|
||||
if (!smooth_)
|
||||
{
|
||||
addFeatureEdges(renderer, polyData);
|
||||
}
|
||||
|
||||
auto mapper = vtkSmartPointer<vtkPolyDataMapper>::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<vtkPolyData> 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<bool>());
|
||||
|
||||
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_,
|
||||
@ -174,7 +424,14 @@ addGeometryToScene
|
||||
auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
|
||||
mapper->SetInputData(polyData);
|
||||
|
||||
setField(position, fieldName_, mapper, renderer, polyData);
|
||||
setField
|
||||
(
|
||||
position,
|
||||
fieldName_,
|
||||
queryFieldAssociation(fieldName_, polyData),
|
||||
mapper,
|
||||
renderer
|
||||
);
|
||||
|
||||
surfaceActor_->SetMapper(mapper);
|
||||
|
||||
@ -182,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);
|
||||
}
|
||||
|
||||
|
||||
@ -189,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_);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user