This method waits until all the threads have completed IO operations and then clears any cached information about the files on disk. This replaces the deactivation of threading by means of zeroing the buffer size when writing and reading of a file happen in sequence. It also allows paraFoam to update the list of available times. Patch contributed by Mattijs Janssens Resolves bug report https://bugs.openfoam.org/view.php?id=2962
995 lines
26 KiB
C
995 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. Flush first to force refresh.
|
|
fileHandler().flush();
|
|
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);
|
|
|
|
// 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_();
|
|
// Get times list. Flush first to force refresh.
|
|
fileHandler().flush();
|
|
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;
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|