openfoam/applications/utilities/postProcessing/graphics/PVReaders/vtkPVFoam/vtkPVFoam.C
Henry Weller 8959b8e00a ENH: Improvements to the fileHandler and collated IO
Improvements to existing functionality
--------------------------------------
  - MPI is initialised without thread support if it is not needed e.g. uncollated
  - Use native c++11 threading; avoids problem with static destruction order.
  - etc/cellModels now only read if needed.
  - etc/controlDict can now be read from the environment variable FOAM_CONTROLDICT
  - Uniform files (e.g. '0/uniform/time') are now read only once on the master only
    (with the masterUncollated or collated file handlers)
  - collated format writes to 'processorsNNN' instead of 'processors'.  The file
    format is unchanged.
  - Thread buffer and file buffer size are no longer limited to 2Gb.

The global controlDict file contains parameters for file handling.  Under some
circumstances, e.g. running in parallel on a system without NFS, the user may
need to set some parameters, e.g. fileHandler, before the global controlDict
file is read from file.  To support this, OpenFOAM now allows the global
controlDict to be read as a string set to the FOAM_CONTROLDICT environment
variable.

The FOAM_CONTROLDICT environment variable can be set to the content the global
controlDict file, e.g. from a sh/bash shell:

    export FOAM_CONTROLDICT=$(foamDictionary $FOAM_ETC/controlDict)

FOAM_CONTROLDICT can then be passed to mpirun using the -x option, e.g.:

    mpirun -np 2 -x FOAM_CONTROLDICT simpleFoam -parallel

Note that while this avoids the need for NFS to read the OpenFOAM configuration
the executable still needs to load shared libraries which must either be copied
locally or available via NFS or equivalent.

New: Multiple IO ranks
----------------------
The masterUncollated and collated fileHandlers can now use multiple ranks for
writing e.g.:

    mpirun -np 6 simpleFoam -parallel -ioRanks '(0 3)'

In this example ranks 0 ('processor0') and 3 ('processor3') now handle all the
I/O.  Rank 0 handles 0,1,2 and rank 3 handles 3,4,5.  The set of IO ranks should always
include 0 as first element and be sorted in increasing order.

The collated fileHandler uses the directory naming processorsNNN_XXX-YYY where
NNN is the total number of processors and XXX and YYY are first and last
processor in the rank, e.g. in above example the directories would be

    processors6_0-2
    processors6_3-5

and each of the collated files in these contains data of the local ranks
only. The same naming also applies when e.g. running decomposePar:

decomposePar -fileHandler collated -ioRanks '(0 3)'

New: Distributed data
---------------------

The individual root directories can be placed on different hosts with different
paths if necessary.  In the current framework it is necessary to specify the
root per slave process but this has been simplified with the option of specifying
the root per host with the -hostRoots command line option:

    mpirun -np 6 simpleFoam -parallel -ioRanks '(0 3)' \
        -hostRoots '("machineA" "/tmp/" "machineB" "/tmp")'

The hostRoots option is followed by a list of machine name + root directory, the
machine name can contain regular expressions.

New: hostCollated
-----------------

The new hostCollated fileHandler automatically sets the 'ioRanks' according to
the host name with the lowest rank e.g. to run simpleFoam on 6 processors with
ranks 0-2 on machineA and ranks 3-5 on machineB with the machines specified in
the hostfile:

    mpirun -np 6 --hostfile hostfile simpleFoam -parallel -fileHandler hostCollated

This is equivalent to

    mpirun -np 6 --hostfile hostfile simpleFoam -parallel -fileHandler collated -ioRanks '(0 3)'

This example will write directories:

    processors6_0-2/
    processors6_3-5/

A typical example would use distributed data e.g. no two nodes, machineA and
machineB, each with three processes:

    decomposePar -fileHandler collated -case cavity

    # Copy case (constant/*, system/*, processors6/) to master:
    rsync -a cavity machineA:/tmp/

    # Create root on slave:
    ssh machineB mkdir -p /tmp/cavity

    # Run
    mpirun --hostfile hostfile icoFoam \
        -case /tmp/cavity -parallel -fileHandler hostCollated \
        -hostRoots '("machineA" "/tmp" "machineB" "/tmp")'

Contributed by Mattijs Janssens
2018-03-21 12:42:22 +00:00

997 lines
26 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "vtkPVFoam.H"
#include "vtkPVFoamReader.h"
// OpenFOAM includes
#include "areaFaMesh.H"
#include "faMesh.H"
#include "fvMesh.H"
#include "Time.H"
#include "patchZones.H"
#include "IOobjectList.H"
#include "collatedFileOperation.H"
// VTK includes
#include "vtkDataArraySelection.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkRenderer.h"
#include "vtkTextActor.h"
#include "vtkTextProperty.h"
#include "vtkSmartPointer.h"
#include "vtkInformation.h"
// Templates (only needed here)
#include "vtkPVFoamUpdateTemplates.C"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(vtkPVFoam, 0);
// file-scope
static word updateStateName(polyMesh::readUpdateState state)
{
switch (state)
{
case polyMesh::UNCHANGED: return "UNCHANGED";
case polyMesh::POINTS_MOVED: return "POINTS_MOVED";
case polyMesh::TOPO_CHANGE: return "TOPO_CHANGE";
case polyMesh::TOPO_PATCH_CHANGE: return "TOPO_PATCH_CHANGE";
};
return "UNKNOWN";
}
}
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
namespace Foam
{
// file-scope
//- Create a text actor
vtkSmartPointer<vtkTextActor> createTextActor
(
const std::string& s,
const Foam::point& pt
)
{
vtkSmartPointer<vtkTextActor> txt =
vtkSmartPointer<vtkTextActor>::New();
txt->SetInput(s.c_str());
// Set text properties
vtkTextProperty* tprop = txt->GetTextProperty();
tprop->SetFontFamilyToArial();
tprop->BoldOn();
tprop->ShadowOff();
tprop->SetLineSpacing(1.0);
tprop->SetFontSize(14);
tprop->SetColor(1.0, 0.0, 1.0);
tprop->SetJustificationToCentered();
txt->GetPositionCoordinate()->SetCoordinateSystemToWorld();
txt->GetPositionCoordinate()->SetValue(pt.x(), pt.y(), pt.z());
return txt;
}
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::vtkPVFoam::resetCounters()
{
// Reset array range information (ids and sizes)
rangeVolume_.reset();
rangePatches_.reset();
rangeClouds_.reset();
rangeCellZones_.reset();
rangeFaceZones_.reset();
rangePointZones_.reset();
rangeCellSets_.reset();
rangeFaceSets_.reset();
rangePointSets_.reset();
}
template<class Container>
bool Foam::vtkPVFoam::addOutputBlock
(
vtkMultiBlockDataSet* output,
const HashTable<Container, string>& cache,
const arrayRange& selector,
const bool singleDataset
) const
{
const auto blockNo = output->GetNumberOfBlocks();
vtkSmartPointer<vtkMultiBlockDataSet> block;
int datasetNo = 0;
const List<label> partIds = selector.intersection(selectedPartIds_);
for (const auto partId : partIds)
{
const auto& longName = selectedPartIds_[partId];
const word shortName = getFoamName(longName);
auto iter = cache.find(longName);
if (iter.found() && iter.object().dataset)
{
auto dataset = iter.object().dataset;
if (singleDataset)
{
output->SetBlock(blockNo, dataset);
output->GetMetaData(blockNo)->Set
(
vtkCompositeDataSet::NAME(),
shortName.c_str()
);
++datasetNo;
break;
}
else if (datasetNo == 0)
{
block = vtkSmartPointer<vtkMultiBlockDataSet>::New();
output->SetBlock(blockNo, block);
output->GetMetaData(blockNo)->Set
(
vtkCompositeDataSet::NAME(),
selector.name()
);
}
block->SetBlock(datasetNo, dataset);
block->GetMetaData(datasetNo)->Set
(
vtkCompositeDataSet::NAME(),
shortName.c_str()
);
++datasetNo;
}
}
return datasetNo;
}
int Foam::vtkPVFoam::setTime(const std::vector<double>& requestTimes)
{
if (requestTimes.empty())
{
return 0;
}
Time& runTime = dbPtr_();
// Get times list
instantList Times = runTime.times();
int nearestIndex = timeIndex_;
for (const double& timeValue : requestTimes)
{
const int index = Time::findClosestTimeIndex(Times, timeValue);
if (index >= 0 && index != timeIndex_)
{
nearestIndex = index;
break;
}
}
if (nearestIndex < 0)
{
nearestIndex = 0;
}
if (debug)
{
Info<< "<beg> setTime(";
unsigned reqi = 0;
for (const double& timeValue : requestTimes)
{
if (reqi) Info<< ", ";
Info<< timeValue;
++reqi;
}
Info<< ") - previousIndex = " << timeIndex_
<< ", nearestIndex = " << nearestIndex << nl;
}
// See what has changed
if (timeIndex_ != nearestIndex)
{
timeIndex_ = nearestIndex;
runTime.setTime(Times[nearestIndex], nearestIndex);
// When mesh changes, so do fields
meshState_ =
(
volMeshPtr_
? volMeshPtr_->readUpdate()
: polyMesh::TOPO_CHANGE
);
reader_->UpdateProgress(0.05);
// this seems to be needed for catching Lagrangian fields
updateInfo();
}
if (debug)
{
Info<< "<end> setTime() - selectedTime="
<< Times[nearestIndex].name() << " index=" << timeIndex_
<< "/" << Times.size()
<< " meshUpdateState=" << updateStateName(meshState_) << nl;
}
return nearestIndex;
}
Foam::word Foam::vtkPVFoam::getReaderPartName(const int partId) const
{
return getFoamName(reader_->GetPartArrayName(partId));
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::vtkPVFoam::vtkPVFoam
(
const char* const vtkFileName,
vtkPVFoamReader* reader
)
:
reader_(reader),
dbPtr_(nullptr),
volMeshPtr_(nullptr),
areaMeshPtr_(nullptr),
meshRegion_(polyMesh::defaultRegion),
meshDir_(polyMesh::meshSubDir),
timeIndex_(-1),
decomposePoly_(false),
meshState_(polyMesh::TOPO_CHANGE),
rangeVolume_("volMesh"),
rangeArea_("areaMesh"),
rangePatches_("patch"),
rangeClouds_("lagrangian"),
rangeCellZones_("cellZone"),
rangeFaceZones_("faceZone"),
rangePointZones_("pointZone"),
rangeCellSets_("cellSet"),
rangeFaceSets_("faceSet"),
rangePointSets_("pointSet")
{
if (debug)
{
Info<< "vtkPVFoam - " << vtkFileName << nl;
printMemory();
}
fileName FileName(vtkFileName);
// Make sure not to use the threaded version - it does not like
// being loaded as a shared library - static cleanup order is problematic.
// For now just disable the threaded writer.
fileOperations::collatedFileOperation::maxThreadFileBufferSize = 0;
// avoid argList and get rootPath/caseName directly from the file
fileName fullCasePath(FileName.path());
if (!isDir(fullCasePath))
{
return;
}
if (fullCasePath == ".")
{
fullCasePath = cwd();
}
// The name of the executable, unless already present in the environment
setEnv("FOAM_EXECUTABLE", "paraview", false);
// Set the case as an environment variable - some BCs might use this
if (fullCasePath.name().find("processors", 0) == 0)
{
// FileName e.g. "cavity/processors256/processor1.OpenFOAM
// Remove the processors section so it goes into processorDDD
// checking below.
fullCasePath = fullCasePath.path()/fileName(FileName.name()).lessExt();
}
if (fullCasePath.name().find("processor", 0) == 0)
{
// Give filehandler opportunity to analyse number of processors
(void)fileHandler().filePath(fullCasePath);
const fileName globalCase = fullCasePath.path();
setEnv("FOAM_CASE", globalCase, true);
setEnv("FOAM_CASENAME", globalCase.name(), true);
}
else
{
setEnv("FOAM_CASE", fullCasePath, true);
setEnv("FOAM_CASENAME", fullCasePath.name(), true);
}
// look for 'case{region}.OpenFOAM'
// could be stringent and insist the prefix match the directory name...
// Note: cannot use fileName::name() due to the embedded '{}'
string caseName(fileName(FileName).lessExt());
const auto beg = caseName.find_last_of("/{");
const auto end = caseName.find('}', beg);
if
(
beg != std::string::npos && caseName[beg] == '{'
&& end != std::string::npos && end == caseName.size()-1
)
{
meshRegion_ = caseName.substr(beg+1, end-beg-1);
// some safety
if (meshRegion_.empty())
{
meshRegion_ = polyMesh::defaultRegion;
}
if (meshRegion_ != polyMesh::defaultRegion)
{
meshDir_ = meshRegion_/polyMesh::meshSubDir;
}
}
if (debug)
{
Info<< "fullCasePath=" << fullCasePath << nl
<< "FOAM_CASE=" << getEnv("FOAM_CASE") << nl
<< "FOAM_CASENAME=" << getEnv("FOAM_CASENAME") << nl
<< "region=" << meshRegion_ << nl;
}
// Create time object
dbPtr_.reset
(
new Time
(
Time::controlDictName,
fileName(fullCasePath.path()),
fileName(fullCasePath.name())
)
);
dbPtr_().functionObjects().off();
updateInfo();
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::vtkPVFoam::~vtkPVFoam()
{
if (debug)
{
Info<< "~vtkPVFoam" << nl;
}
delete volMeshPtr_;
delete areaMeshPtr_;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::vtkPVFoam::updateInfo()
{
if (debug)
{
Info<< "<beg> updateInfo"
<< " [volMeshPtr=" << (volMeshPtr_ ? "set" : "nullptr")
<< "] timeIndex="
<< timeIndex_ << nl;
}
resetCounters();
// Part selection
{
vtkDataArraySelection* select = reader_->GetPartSelection();
// There are two ways to ensure we have the correct list of parts:
// 1. remove everything and then set particular entries 'on'
// 2. build a 'char **' list and call SetArraysWithDefault()
//
// Nr. 2 has the potential advantage of not touching the modification
// time of the vtkDataArraySelection, but the qt/paraview proxy
// layer doesn't care about that anyhow.
HashSet<string> enabled;
if (!select->GetNumberOfArrays() && !volMeshPtr_)
{
// Fake enable 'internalMesh' on the first call
enabled = { "internalMesh" };
}
else
{
// Preserve the enabled selections
enabled = getSelectedArraySet(select);
}
select->RemoveAllArrays(); // Clear existing list
// Update mesh parts list - add Lagrangian at the bottom
updateInfoInternalMesh(select);
updateInfoPatches(select, enabled);
updateInfoSets(select);
updateInfoZones(select);
updateInfoAreaMesh(select);
updateInfoLagrangian(select);
setSelectedArrayEntries(select, enabled); // Adjust/restore selected
}
// Volume and area fields - includes save/restore of selected
updateInfoContinuumFields(reader_->GetVolFieldSelection());
// Point fields - includes save/restore of selected
updateInfoPointFields(reader_->GetPointFieldSelection());
// Lagrangian fields - includes save/restore of selected
updateInfoLagrangianFields(reader_->GetLagrangianFieldSelection());
if (debug)
{
Info<< "<end> updateInfo" << nl;
}
}
void Foam::vtkPVFoam::Update
(
vtkMultiBlockDataSet* output,
vtkMultiBlockDataSet* outputLagrangian
)
{
if (debug)
{
cout<< "<beg> Foam::vtkPVFoam::Update\n";
output->Print(cout);
if (outputLagrangian) outputLagrangian->Print(cout);
printMemory();
}
reader_->UpdateProgress(0.1);
const int caching = reader_->GetMeshCaching();
const bool oldDecomp = decomposePoly_;
decomposePoly_ = !reader_->GetUseVTKPolyhedron();
// Set up mesh parts selection(s)
// Update cached, saved, unneed values.
{
vtkDataArraySelection* selection = reader_->GetPartSelection();
const int n = selection->GetNumberOfArrays();
selectedPartIds_.clear();
HashSet<string> nowActive;
for (int id=0; id < n; ++id)
{
const string str(selection->GetArrayName(id));
const bool status = selection->GetArraySetting(id);
if (status)
{
selectedPartIds_.set(id, str); // id -> name
nowActive.set(str);
}
if (debug > 1)
{
Info<< " part[" << id << "] = " << status
<< " : " << str << nl;
}
}
// Dispose of unneeded components
cachedVtp_.retain(nowActive);
cachedVtu_.retain(nowActive);
if
(
!caching
|| meshState_ == polyMesh::TOPO_CHANGE
|| meshState_ == polyMesh::TOPO_PATCH_CHANGE
)
{
// Eliminate cached values that would be unreliable
forAllIters(cachedVtp_, iter)
{
iter.object().clearGeom();
iter.object().clear();
}
forAllIters(cachedVtu_, iter)
{
iter.object().clearGeom();
iter.object().clear();
}
}
else if (oldDecomp != decomposePoly_)
{
// poly-decompose changed - dispose of cached values
forAllIters(cachedVtu_, iter)
{
iter.object().clearGeom();
iter.object().clear();
}
}
}
reader_->UpdateProgress(0.15);
// Update the OpenFOAM mesh
{
if (debug)
{
Info<< "<beg> updateFoamMesh" << nl;
printMemory();
}
if (!caching)
{
delete volMeshPtr_;
delete areaMeshPtr_;
volMeshPtr_ = nullptr;
areaMeshPtr_ = nullptr;
}
// Check to see if the OpenFOAM mesh has been created
if (!volMeshPtr_)
{
if (debug)
{
Info<< "Creating OpenFOAM mesh for region " << meshRegion_
<< " at time=" << dbPtr_().timeName() << nl;
}
volMeshPtr_ = new fvMesh
(
IOobject
(
meshRegion_,
dbPtr_().timeName(),
dbPtr_(),
IOobject::MUST_READ
)
);
meshState_ = polyMesh::TOPO_CHANGE; // New mesh
}
else
{
if (debug)
{
Info<< "Using existing OpenFOAM mesh" << nl;
}
}
if (rangeArea_.intersects(selectedPartIds_))
{
if (!areaMeshPtr_)
{
areaMeshPtr_ = new faMesh(*volMeshPtr_);
}
}
else
{
delete areaMeshPtr_;
areaMeshPtr_ = nullptr;
}
if (debug)
{
Info<< "<end> updateFoamMesh" << nl;
printMemory();
}
}
reader_->UpdateProgress(0.4);
convertMeshVolume();
convertMeshPatches();
reader_->UpdateProgress(0.6);
if (reader_->GetIncludeZones())
{
convertMeshCellZones();
convertMeshFaceZones();
convertMeshPointZones();
reader_->UpdateProgress(0.65);
}
if (reader_->GetIncludeSets())
{
convertMeshCellSets();
convertMeshFaceSets();
convertMeshPointSets();
reader_->UpdateProgress(0.7);
}
convertMeshArea();
convertMeshLagrangian();
reader_->UpdateProgress(0.8);
// Update fields
convertVolFields();
convertPointFields();
convertAreaFields();
convertLagrangianFields();
// Assemble multiblock output
addOutputBlock(output, cachedVtu_, rangeVolume_, true); // One dataset
addOutputBlock(output, cachedVtp_, rangePatches_);
addOutputBlock(output, cachedVtu_, rangeCellZones_);
addOutputBlock(output, cachedVtp_, rangeFaceZones_);
addOutputBlock(output, cachedVtp_, rangePointZones_);
addOutputBlock(output, cachedVtu_, rangeCellSets_);
addOutputBlock(output, cachedVtp_, rangeFaceSets_);
addOutputBlock(output, cachedVtp_, rangePointSets_);
addOutputBlock(output, cachedVtp_, rangeArea_);
addOutputBlock
(
(outputLagrangian ? outputLagrangian : output),
cachedVtp_,
rangeClouds_
);
if (debug)
{
Info<< "done reader part" << nl << nl;
}
reader_->UpdateProgress(0.95);
meshState_ = polyMesh::UNCHANGED;
if (caching & 2)
{
// Suppress caching of Lagrangian since it normally always changes.
cachedVtp_.filterKeys
(
[](const word& k){ return k.startsWith("lagrangian/"); },
true // prune
);
}
else
{
cachedVtp_.clear();
cachedVtu_.clear();
}
}
void Foam::vtkPVFoam::UpdateFinalize()
{
if (!reader_->GetMeshCaching())
{
delete volMeshPtr_;
delete areaMeshPtr_;
volMeshPtr_ = nullptr;
areaMeshPtr_ = nullptr;
}
reader_->UpdateProgress(1.0);
}
std::vector<double> Foam::vtkPVFoam::findTimes(const bool skipZero) const
{
std::vector<double> times;
if (dbPtr_.valid())
{
const Time& runTime = dbPtr_();
instantList timeLst = runTime.times();
// find the first time for which this mesh appears to exist
label begIndex = timeLst.size();
forAll(timeLst, timei)
{
if
(
IOobject
(
"points",
timeLst[timei].name(),
meshDir_,
runTime
).typeHeaderOk<pointIOField>(false, false)
)
{
begIndex = timei;
break;
}
}
label nTimes = timeLst.size() - begIndex;
// skip "constant" time whenever possible
if (begIndex == 0 && nTimes > 1)
{
if (timeLst[begIndex].name() == runTime.constant())
{
++begIndex;
--nTimes;
}
}
// skip "0/" time if requested and possible
if (skipZero && nTimes > 1 && timeLst[begIndex].name() == "0")
{
++begIndex;
--nTimes;
}
times.reserve(nTimes);
while (nTimes-- > 0)
{
times.push_back(timeLst[begIndex++].value());
}
}
else
{
if (debug)
{
cout<< "no valid dbPtr:\n";
}
}
return times;
}
void Foam::vtkPVFoam::renderPatchNames
(
vtkRenderer* renderer,
const bool show
)
{
// Always remove old actors first
for (auto& actor : patchTextActors_)
{
renderer->RemoveViewProp(actor);
}
patchTextActors_.clear();
if (show && volMeshPtr_)
{
// get the display patches, strip off any prefix/suffix
hashedWordList selectedPatches = getSelected
(
reader_->GetPartSelection(),
rangePatches_
);
if (selectedPatches.empty())
{
return;
}
const polyBoundaryMesh& pbMesh = volMeshPtr_->boundaryMesh();
// Find the total number of zones
// Each zone will take the patch name
// Number of zones per patch ... zero zones should be skipped
labelList nZones(pbMesh.size(), 0);
// Per global zone number the average face centre position
List<DynamicList<point>> zoneCentre(pbMesh.size());
// Loop through all patches to determine zones, and centre of each zone
forAll(pbMesh, patchi)
{
const polyPatch& pp = pbMesh[patchi];
// Only include the patch if it is selected
if (!selectedPatches.found(pp.name()))
{
continue;
}
const labelListList& edgeFaces = pp.edgeFaces();
const vectorField& n = pp.faceNormals();
boolList featEdge(pp.nEdges(), false);
forAll(edgeFaces, edgei)
{
const labelList& eFaces = edgeFaces[edgei];
if (eFaces.size() == 1)
{
// Note: could also do ones with > 2 faces but this gives
// too many zones for baffles
featEdge[edgei] = true;
}
else if (mag(n[eFaces[0]] & n[eFaces[1]]) < 0.5)
{
featEdge[edgei] = true;
}
}
// Do topological analysis of patch, find disconnected regions
patchZones pZones(pp, featEdge);
nZones[patchi] = pZones.nZones();
labelList zoneNFaces(pZones.nZones(), 0);
// Create storage for additional zone centres
forAll(zoneNFaces, zonei)
{
zoneCentre[patchi].append(Zero);
}
// Do averaging per individual zone
forAll(pp, facei)
{
const label zonei = pZones[facei];
zoneCentre[patchi][zonei] += pp[facei].centre(pp.points());
zoneNFaces[zonei]++;
}
forAll(zoneCentre[patchi], zonei)
{
zoneCentre[patchi][zonei] /= zoneNFaces[zonei];
}
}
// Count number of zones we're actually going to display.
// This is truncated to a max per patch
const label MAXPATCHZONES = 20;
label displayZoneI = 0;
forAll(pbMesh, patchi)
{
displayZoneI += min(MAXPATCHZONES, nZones[patchi]);
}
if (debug)
{
Info<< "displayed zone centres = " << displayZoneI << nl
<< "zones per patch = " << nZones << nl;
}
// Set the size of the patch labels to max number of zones
patchTextActors_.setSize(displayZoneI);
if (debug)
{
Info<< "constructing patch labels" << nl;
}
// Actor index
displayZoneI = 0;
forAll(pbMesh, patchi)
{
const polyPatch& pp = pbMesh[patchi];
// Only selected patches will have a non-zero number of zones
const label nDisplayZones = min(MAXPATCHZONES, nZones[patchi]);
label increment = 1;
if (nZones[patchi] >= MAXPATCHZONES)
{
increment = nZones[patchi]/MAXPATCHZONES;
}
label globalZoneI = 0;
for (label i = 0; i < nDisplayZones; ++i, globalZoneI += increment)
{
if (debug)
{
Info<< "patch name = " << pp.name() << nl
<< "anchor = " << zoneCentre[patchi][globalZoneI] << nl
<< "globalZoneI = " << globalZoneI << nl;
}
// Into a list for later removal
patchTextActors_[displayZoneI++] = createTextActor
(
pp.name(),
zoneCentre[patchi][globalZoneI]
);
}
}
// Resize the patch names list to the actual number of patch names added
patchTextActors_.setSize(displayZoneI);
}
// Add text to each renderer
for (auto& actor : patchTextActors_)
{
renderer->AddViewProp(actor);
}
}
void Foam::vtkPVFoam::PrintSelf(ostream& os, vtkIndent indent) const
{
os << indent << "Number of nodes: "
<< (volMeshPtr_ ? volMeshPtr_->nPoints() : 0) << "\n";
os << indent << "Number of cells: "
<< (volMeshPtr_ ? volMeshPtr_->nCells() : 0) << "\n";
os << indent << "Number of available time steps: "
<< (dbPtr_.valid() ? dbPtr_().times().size() : 0) << "\n";
os << indent << "mesh region: " << meshRegion_ << "\n";
}
void Foam::vtkPVFoam::printInfo() const
{
std::cout
<< "Region: " << meshRegion_ << "\n"
<< "nPoints: " << (volMeshPtr_ ? volMeshPtr_->nPoints() : 0) << "\n"
<< "nCells: " << (volMeshPtr_ ? volMeshPtr_->nCells() : 0) << "\n"
<< "nTimes: "
<< (dbPtr_.valid() ? dbPtr_().times().size() : 0) << "\n";
std::vector<double> times = this->findTimes(reader_->GetSkipZeroTime());
std::cout<<" " << times.size() << "(";
for (const double& val : times)
{
std::cout<< ' ' << val;
}
std::cout << " )" << std::endl;
}
// ************************************************************************* //