692 lines
18 KiB
C
692 lines
18 KiB
C
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
|
\\ / O peration |
|
|
\\ / A nd | www.openfoam.com
|
|
\\/ M anipulation |
|
|
-------------------------------------------------------------------------------
|
|
Copyright (C) 2011-2018 OpenFOAM Foundation
|
|
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"
|
|
|
|
// OpenFOAM includes
|
|
#include "cellSet.H"
|
|
#include "faceSet.H"
|
|
#include "pointSet.H"
|
|
#include "IOobjectList.H"
|
|
#include "IOPtrList.H"
|
|
#include "polyBoundaryMeshEntries.H"
|
|
#include "entry.H"
|
|
#include "Cloud.H"
|
|
#include "areaFaMesh.H"
|
|
|
|
// VTK includes
|
|
#include "vtkDataArraySelection.h"
|
|
|
|
// OpenFOAM/VTK interface
|
|
#include "vtkPVFoamReader.h"
|
|
|
|
// Templates (only needed here)
|
|
#include "vtkPVFoamUpdateTemplates.C"
|
|
|
|
// * * * * * * * * * * * * * * * Private Classes * * * * * * * * * * * * * * //
|
|
|
|
namespace Foam
|
|
{
|
|
|
|
//- A class for reading zone information without requiring a mesh
|
|
class zonesEntries
|
|
:
|
|
public regIOobject,
|
|
public PtrList<entry>
|
|
{
|
|
|
|
public:
|
|
|
|
// Constructors
|
|
|
|
explicit zonesEntries(const IOobject& io)
|
|
:
|
|
regIOobject(io),
|
|
PtrList<entry>(readStream(word("regIOobject")))
|
|
{
|
|
close();
|
|
}
|
|
|
|
// Member functions
|
|
|
|
bool writeData(Ostream&) const
|
|
{
|
|
NotImplemented;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
|
|
|
template<class ZoneType>
|
|
Foam::wordList Foam::vtkPVFoam::getZoneNames
|
|
(
|
|
const ZoneMesh<ZoneType, polyMesh>& zmesh
|
|
)
|
|
{
|
|
wordList names(zmesh.size());
|
|
label nZone = 0;
|
|
|
|
for (const auto& zn : zmesh)
|
|
{
|
|
if (!zn.empty())
|
|
{
|
|
names[nZone++] = zn.name();
|
|
}
|
|
}
|
|
names.setSize(nZone);
|
|
|
|
return names;
|
|
}
|
|
|
|
|
|
Foam::wordList Foam::vtkPVFoam::getZoneNames(const word& zoneType) const
|
|
{
|
|
// mesh not loaded - read from file
|
|
IOobject ioObj
|
|
(
|
|
zoneType,
|
|
dbPtr_().findInstance
|
|
(
|
|
meshDir_,
|
|
zoneType,
|
|
IOobject::READ_IF_PRESENT
|
|
),
|
|
meshDir_,
|
|
dbPtr_(),
|
|
IOobject::READ_IF_PRESENT,
|
|
IOobject::NO_WRITE,
|
|
false
|
|
);
|
|
|
|
wordList names;
|
|
if (ioObj.typeHeaderOk<cellZoneMesh>(false, false))
|
|
{
|
|
zonesEntries zones(ioObj);
|
|
|
|
names.setSize(zones.size());
|
|
label nZone = 0;
|
|
|
|
for (const auto& zn : zones)
|
|
{
|
|
names[nZone++] = zn.keyword();
|
|
}
|
|
}
|
|
|
|
return names;
|
|
}
|
|
|
|
|
|
void Foam::vtkPVFoam::updateInfoInternalMesh
|
|
(
|
|
vtkDataArraySelection* select
|
|
)
|
|
{
|
|
DebugInfo << "<beg> " << FUNCTION_NAME << nl;
|
|
|
|
// Determine mesh parts (internalMesh, patches...)
|
|
// Add internal mesh as first entry
|
|
rangeVolume_.reset(select->GetNumberOfArrays(), 1);
|
|
select->AddArray("internalMesh");
|
|
|
|
DebugInfo << "<end> " << FUNCTION_NAME << nl;
|
|
}
|
|
|
|
|
|
void Foam::vtkPVFoam::updateInfoAreaMesh
|
|
(
|
|
vtkDataArraySelection* select
|
|
)
|
|
{
|
|
DebugInfo << "<beg> " << FUNCTION_NAME << nl;
|
|
|
|
rangeArea_.reset(select->GetNumberOfArrays(), 0);
|
|
|
|
// Use the db directly since this might be called without a mesh,
|
|
// but the region must get added back in
|
|
fileName faMeshPrefix(faMesh::meshSubDir);
|
|
if (meshRegion_ != polyMesh::defaultRegion)
|
|
{
|
|
faMeshPrefix = meshRegion_/faMeshPrefix;
|
|
}
|
|
|
|
// Check for finiteArea mesh
|
|
if
|
|
(
|
|
isFile
|
|
(
|
|
dbPtr_->constantPath()/faMeshPrefix/"faceLabels"
|
|
)
|
|
)
|
|
{
|
|
rangeArea_ += 1;
|
|
select->AddArray("areaMesh");
|
|
}
|
|
|
|
DebugInfo << "<end> " << FUNCTION_NAME << nl;
|
|
}
|
|
|
|
|
|
void Foam::vtkPVFoam::updateInfoLagrangian
|
|
(
|
|
vtkDataArraySelection* select
|
|
)
|
|
{
|
|
DebugInfo
|
|
<< "<beg> " << FUNCTION_NAME << nl
|
|
<< " " << dbPtr_->timePath()/cloud::prefix << nl;
|
|
|
|
// Use the db directly since this might be called without a mesh,
|
|
// but the region must get added back in
|
|
fileName lagrangianPrefix(cloud::prefix);
|
|
if (meshRegion_ != polyMesh::defaultRegion)
|
|
{
|
|
lagrangianPrefix = meshRegion_/cloud::prefix;
|
|
}
|
|
|
|
// List of lagrangian objects across all times
|
|
HashSet<fileName> names;
|
|
|
|
// Get times list. Flush first to force refresh.
|
|
fileHandler().flush();
|
|
|
|
for (const instant& t : dbPtr_().times())
|
|
{
|
|
names.insert
|
|
(
|
|
fileHandler().readDir
|
|
(
|
|
dbPtr_->path()/t.name()/lagrangianPrefix,
|
|
fileName::DIRECTORY
|
|
)
|
|
);
|
|
}
|
|
|
|
rangeClouds_.reset(select->GetNumberOfArrays());
|
|
rangeClouds_ += addToArray(select, "lagrangian/", names.sortedToc());
|
|
|
|
DebugInfo << "<end> " << FUNCTION_NAME << nl;
|
|
}
|
|
|
|
|
|
void Foam::vtkPVFoam::updateInfoPatches
|
|
(
|
|
vtkDataArraySelection* select,
|
|
HashSet<string>& enabledEntries
|
|
)
|
|
{
|
|
DebugInfo
|
|
<< "<beg> " << FUNCTION_NAME
|
|
<< " [volMeshPtr=" << (volMeshPtr_ ? "set" : "null") << "]" << nl;
|
|
|
|
rangePatches_.reset(select->GetNumberOfArrays());
|
|
|
|
if (volMeshPtr_)
|
|
{
|
|
const polyBoundaryMesh& patches = volMeshPtr_->boundaryMesh();
|
|
const HashTable<labelList>& groups = patches.groupPatchIDs();
|
|
DynamicList<string> displayNames(groups.size());
|
|
|
|
// Add (non-zero) patch groups to the list of mesh parts
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
forAllConstIters(groups, iter)
|
|
{
|
|
const auto& groupName = iter.key();
|
|
const auto& patchIDs = iter.val();
|
|
|
|
label nFaces = 0;
|
|
for (auto patchId : patchIDs)
|
|
{
|
|
nFaces += patches[patchId].size();
|
|
}
|
|
|
|
if (!nFaces)
|
|
{
|
|
// Skip if group has no faces
|
|
continue;
|
|
}
|
|
|
|
// Valid patch if nFace > 0 - add patch to GUI list
|
|
const string dpyName = "group/" + groupName;
|
|
displayNames.append(dpyName);
|
|
|
|
// Optionally replace group with patch name selections
|
|
// - must remove the group from the select itself, otherwise
|
|
// it can toggle on, but not toggle off very well
|
|
if
|
|
(
|
|
!reader_->GetShowGroupsOnly()
|
|
&& enabledEntries.erase(dpyName)
|
|
)
|
|
{
|
|
for (auto patchId : patchIDs)
|
|
{
|
|
const polyPatch& pp = patches[patchId];
|
|
if (pp.size())
|
|
{
|
|
enabledEntries.insert("patch/" + pp.name());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Foam::sort(displayNames); // Sorted group names
|
|
rangePatches_ += addToArray(select, displayNames);
|
|
|
|
// Add (non-zero) patches to the list of mesh parts
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
if (!reader_->GetShowGroupsOnly())
|
|
{
|
|
for (const polyPatch& pp : patches)
|
|
{
|
|
if (pp.size())
|
|
{
|
|
// Add patch to GUI list
|
|
select->AddArray(("patch/" + pp.name()).c_str());
|
|
++rangePatches_;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// mesh not loaded - read from file
|
|
// but this could fail if we've supplied a bad region name
|
|
IOobject ioObj
|
|
(
|
|
"boundary",
|
|
dbPtr_().findInstance
|
|
(
|
|
meshDir_,
|
|
"boundary",
|
|
IOobject::READ_IF_PRESENT
|
|
),
|
|
meshDir_,
|
|
dbPtr_(),
|
|
IOobject::READ_IF_PRESENT,
|
|
IOobject::NO_WRITE,
|
|
false
|
|
);
|
|
|
|
// This should only ever fail if the mesh region doesn't exist
|
|
if (ioObj.typeHeaderOk<polyBoundaryMesh>(true, false))
|
|
{
|
|
polyBoundaryMeshEntries patchEntries(ioObj);
|
|
|
|
// Read patches, determine sizes and patch groups
|
|
wordList names(patchEntries.size());
|
|
labelList sizes(patchEntries.size());
|
|
HashTable<labelHashSet> groups(2*patchEntries.size());
|
|
|
|
forAll(patchEntries, patchi)
|
|
{
|
|
const dictionary& patchDict = patchEntries[patchi].dict();
|
|
wordList groupNames;
|
|
|
|
sizes[patchi] = patchDict.get<label>("nFaces");
|
|
names[patchi] = patchEntries[patchi].keyword();
|
|
|
|
if
|
|
(
|
|
sizes[patchi] // Valid patch if nFace > 0
|
|
&& patchDict.readIfPresent("inGroups", groupNames)
|
|
)
|
|
{
|
|
for (const auto& groupName : groupNames)
|
|
{
|
|
groups(groupName).insert(patchi);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Add (non-zero) patch groups to the list of mesh parts
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
DynamicList<string> displayNames(groups.size());
|
|
|
|
forAllConstIters(groups, iter)
|
|
{
|
|
const auto& groupName = iter.key();
|
|
const auto& patchIDs = iter.val();
|
|
|
|
const string dpyName = "group/" + groupName;
|
|
displayNames.append(dpyName);
|
|
|
|
// Optionally replace group with patch name selections
|
|
// - must remove the group from the select itself, otherwise
|
|
// it can toggle on, but not toggle off very well
|
|
if
|
|
(
|
|
!reader_->GetShowGroupsOnly()
|
|
&& enabledEntries.erase(dpyName)
|
|
)
|
|
{
|
|
for (auto patchId : patchIDs)
|
|
{
|
|
enabledEntries.insert("patch/" + names[patchId]);
|
|
}
|
|
}
|
|
}
|
|
|
|
Foam::sort(displayNames); // Sorted group names
|
|
rangePatches_ += addToArray(select, displayNames);
|
|
|
|
// Add (non-zero) patches to the list of mesh parts
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
if (!reader_->GetShowGroupsOnly())
|
|
{
|
|
forAll(names, patchi)
|
|
{
|
|
// Valid patch if nFace > 0 - add patch to GUI list
|
|
if (sizes[patchi])
|
|
{
|
|
select->AddArray(("patch/" + names[patchi]).c_str());
|
|
++rangePatches_;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DebugInfo << "<end> " << FUNCTION_NAME << nl;
|
|
}
|
|
|
|
|
|
void Foam::vtkPVFoam::updateInfoZones
|
|
(
|
|
vtkDataArraySelection* select
|
|
)
|
|
{
|
|
if (!reader_->GetIncludeZones())
|
|
{
|
|
return;
|
|
}
|
|
|
|
DebugInfo
|
|
<< "<beg> " << FUNCTION_NAME
|
|
<< " [volMeshPtr=" << (volMeshPtr_ ? "set" : "null") << "]" << nl;
|
|
|
|
// cellZones
|
|
{
|
|
const wordList names =
|
|
(
|
|
volMeshPtr_
|
|
? getZoneNames(volMeshPtr_->cellZones())
|
|
: getZoneNames("cellZones")
|
|
);
|
|
|
|
rangeCellZones_.reset(select->GetNumberOfArrays());
|
|
rangeCellZones_ += addToArray(select, "cellZone/", names);
|
|
}
|
|
|
|
// faceZones
|
|
{
|
|
const wordList names =
|
|
(
|
|
volMeshPtr_
|
|
? getZoneNames(volMeshPtr_->faceZones())
|
|
: getZoneNames("faceZones")
|
|
);
|
|
|
|
rangeFaceZones_.reset(select->GetNumberOfArrays());
|
|
rangeFaceZones_ += addToArray(select, "faceZone/", names);
|
|
}
|
|
|
|
// pointZones
|
|
{
|
|
const wordList names =
|
|
(
|
|
volMeshPtr_
|
|
? getZoneNames(volMeshPtr_->pointZones())
|
|
: getZoneNames("pointZones")
|
|
);
|
|
|
|
rangePointZones_.reset(select->GetNumberOfArrays());
|
|
rangePointZones_ += addToArray(select, "pointZone/", names);
|
|
}
|
|
|
|
DebugInfo << "<end> " << FUNCTION_NAME << nl;
|
|
}
|
|
|
|
|
|
void Foam::vtkPVFoam::updateInfoSets
|
|
(
|
|
vtkDataArraySelection* select
|
|
)
|
|
{
|
|
if (!reader_->GetIncludeSets())
|
|
{
|
|
return;
|
|
}
|
|
|
|
DebugInfo << "<beg> " << FUNCTION_NAME << nl;
|
|
|
|
// Add names of sets. Search for last time directory with a sets
|
|
// subdirectory. Take care not to search beyond the last mesh.
|
|
|
|
const word facesInstance = dbPtr_().findInstance
|
|
(
|
|
meshDir_,
|
|
"faces",
|
|
IOobject::READ_IF_PRESENT
|
|
);
|
|
|
|
const word setsInstance = dbPtr_().findInstance
|
|
(
|
|
meshDir_/"sets",
|
|
word::null,
|
|
IOobject::READ_IF_PRESENT,
|
|
facesInstance
|
|
);
|
|
|
|
const IOobjectList objects(dbPtr_(), setsInstance, meshDir_/"sets");
|
|
|
|
DebugInfo
|
|
<< " updateInfoSets read "
|
|
<< objects.names() << " from " << setsInstance << nl;
|
|
|
|
|
|
rangeCellSets_.reset(select->GetNumberOfArrays());
|
|
rangeCellSets_ += addToSelection<cellSet>
|
|
(
|
|
select,
|
|
"cellSet/",
|
|
objects
|
|
);
|
|
|
|
rangeFaceSets_.reset(select->GetNumberOfArrays());
|
|
rangeFaceSets_ += addToSelection<faceSet>
|
|
(
|
|
select,
|
|
"faceSet/",
|
|
objects
|
|
);
|
|
|
|
rangePointSets_.reset(select->GetNumberOfArrays());
|
|
rangePointSets_ += addToSelection<pointSet>
|
|
(
|
|
select,
|
|
"pointSet/",
|
|
objects
|
|
);
|
|
|
|
DebugInfo << "<end> " << FUNCTION_NAME << nl;
|
|
}
|
|
|
|
|
|
void Foam::vtkPVFoam::updateInfoContinuumFields
|
|
(
|
|
vtkDataArraySelection* select
|
|
)
|
|
{
|
|
DebugInfo << "<beg> " << FUNCTION_NAME << nl;
|
|
|
|
// Preserve the enabled selections
|
|
HashSet<string> enabled;
|
|
if (!select->GetNumberOfArrays() && !volMeshPtr_)
|
|
{
|
|
// First call: automatically enable pressure, temperature, velocity
|
|
enabled = { "p", "T", "U" };
|
|
}
|
|
else
|
|
{
|
|
enabled = getSelectedArraySet(select);
|
|
}
|
|
|
|
select->RemoveAllArrays(); // Clear existing list
|
|
|
|
// Use the db directly since this might be called without a mesh,
|
|
// but the region name is still required
|
|
word regionPrefix;
|
|
if (meshRegion_ != polyMesh::defaultRegion)
|
|
{
|
|
regionPrefix = meshRegion_;
|
|
}
|
|
|
|
// Objects for this time and mesh region
|
|
IOobjectList objects(dbPtr_(), dbPtr_().timeName(), regionPrefix);
|
|
|
|
updateInfoFields<fvPatchField, volMesh>(select, objects);
|
|
updateInfoFields<faPatchField, areaMesh>(select, objects);
|
|
|
|
setSelectedArrayEntries(select, enabled); // Adjust/restore selected
|
|
|
|
DebugInfo << "<end> " << FUNCTION_NAME << nl;
|
|
}
|
|
|
|
|
|
void Foam::vtkPVFoam::updateInfoPointFields
|
|
(
|
|
vtkDataArraySelection* select
|
|
)
|
|
{
|
|
DebugInfo << "<beg> " << FUNCTION_NAME << nl;
|
|
|
|
// Preserve the enabled selections
|
|
HashSet<string> enabled = getSelectedArraySet(select);
|
|
|
|
select->RemoveAllArrays(); // Clear existing list
|
|
|
|
// Use the db directly since this might be called without a mesh,
|
|
// but the region name is still required
|
|
word regionPrefix;
|
|
if (meshRegion_ != polyMesh::defaultRegion)
|
|
{
|
|
regionPrefix = meshRegion_;
|
|
}
|
|
|
|
// Objects for this time and mesh region
|
|
IOobjectList objects(dbPtr_(), dbPtr_().timeName(), regionPrefix);
|
|
|
|
updateInfoFields<pointPatchField, pointMesh>(select, objects);
|
|
|
|
setSelectedArrayEntries(select, enabled); // Adjust/restore selected
|
|
|
|
DebugInfo << "<end> " << FUNCTION_NAME << nl;
|
|
}
|
|
|
|
|
|
void Foam::vtkPVFoam::updateInfoLagrangianFields
|
|
(
|
|
vtkDataArraySelection* select
|
|
)
|
|
{
|
|
DebugInfo << "<beg> " << FUNCTION_NAME << nl;
|
|
|
|
// Preserve the enabled selections
|
|
HashSet<string> enabled = getSelectedArraySet(select);
|
|
select->RemoveAllArrays();
|
|
|
|
const arrayRange& range = rangeClouds_;
|
|
if (range.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Reuse the previously determined cloud information.
|
|
DynamicList<word> cloudNames(range.size());
|
|
for (auto partId : range)
|
|
{
|
|
cloudNames.append(getReaderPartName(partId));
|
|
}
|
|
|
|
// Use the db directly since this might be called without a mesh,
|
|
// but the region must get added back in
|
|
fileName lagrangianPrefix(cloud::prefix);
|
|
if (meshRegion_ != polyMesh::defaultRegion)
|
|
{
|
|
lagrangianPrefix = meshRegion_/cloud::prefix;
|
|
}
|
|
|
|
// List of lagrangian fields across all clouds and all times.
|
|
// ParaView displays "(partial)" after field names that only apply
|
|
// to some of the clouds.
|
|
HashTable<wordHashSet> fields;
|
|
|
|
fileHandler().flush();
|
|
for (const instant& t : dbPtr_().times())
|
|
{
|
|
for (const auto& cloudName : cloudNames)
|
|
{
|
|
const HashTable<wordHashSet> localFields =
|
|
IOobjectList
|
|
(
|
|
dbPtr_(),
|
|
t.name(),
|
|
lagrangianPrefix/cloudName
|
|
).classes();
|
|
|
|
forAllConstIters(localFields, iter)
|
|
{
|
|
fields(iter.key()) |= iter.val();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Known/supported field-types
|
|
addToSelection<IOField<label>>(select, fields);
|
|
addToSelection<IOField<scalar>>(select, fields);
|
|
addToSelection<IOField<vector>>(select, fields);
|
|
addToSelection<IOField<sphericalTensor>>(select, fields);
|
|
addToSelection<IOField<symmTensor>>(select, fields);
|
|
addToSelection<IOField<tensor>>(select, fields);
|
|
|
|
// Restore the enabled selections
|
|
setSelectedArrayEntries(select, enabled);
|
|
|
|
DebugInfo << "<end> " << FUNCTION_NAME << nl;
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|