ENH: improve surfaceSplitByPatch controls (#1600)

- uses MeshedSurface instead of triSurface to prevent automatic
  triangulation.

- supports '-patches' and '-excludePatches' controls as per foamToVTK.
  For example,

    surfaceSplitByPatch -patches '( ".*rider.*" )'  motorBike.obj

ENH: use MeshedSurface for surfaceSubset
This commit is contained in:
Mark Olesen 2020-03-11 18:43:33 +01:00
parent 465630bbb0
commit e3d4443871
2 changed files with 172 additions and 152 deletions

View File

@ -31,12 +31,33 @@ Group
grpSurfaceUtilities
Description
Writes regions of triSurface to separate files.
Writes surface regions to separate files.
Usage
\b surfaceSplitByPatch [OPTION]
Options:
- \par -patches NAME | LIST
Specify single patch or multiple patches (name or regex) to extract
For example,
\verbatim
-patches top
-patches '( front \".*back\" )'
\endverbatim
- \par -excludePatches NAME | LIST
Specify single or multiple patches (name or regex) not to extract.
For example,
\verbatim
-excludePatches '( inlet_1 inlet_2 "proc.*")'
\endverbatim
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "triSurface.H"
#include "MeshedSurfaces.H"
#include "stringListOps.H"
#include "geometricSurfacePatch.H"
using namespace Foam;
@ -48,65 +69,103 @@ int main(int argc, char *argv[])
(
"Write surface mesh regions to separate files"
);
argList::noParallel();
argList::addOption
(
"patches",
"wordRes",
"Specify single patch or multiple patches to write\n"
"Eg, 'top' or '( front \".*back\" )'"
);
argList::addOption
(
"excludePatches",
"wordRes",
"Specify single patch or multiple patches to exclude from writing."
" Eg, 'outlet' or '( inlet \".*Wall\" )'"
);
argList::addArgument("input", "The input surface file");
argList args(argc, argv);
const fileName surfName = args[1];
Info<< "Reading surf from " << surfName << " ..." << nl << endl;
const fileName surfBase(surfName.lessExt());
fileName surfBase = surfName.lessExt();
word extension = surfName.ext();
triSurface surf(surfName);
Info<< "Writing regions to separate files ..."
<< nl << endl;
const word extension(surfName.ext());
const geometricSurfacePatchList& patches = surf.patches();
Info<< nl
<< "Read surface from " << surfName << " ..." << nl << endl;
forAll(patches, patchi)
meshedSurface surf(surfName);
const surfZoneList& zones = surf.surfZones();
Info<< " " << surf.size() << " faces with "
<< zones.size() << " zones" << nl << nl;
wordRes includePatches, excludePatches;
if (args.readListIfPresent<wordRe>("patches", includePatches))
{
const geometricSurfacePatch& pp = patches[patchi];
Info<< "Including patches " << flatOutput(includePatches)
<< nl << endl;
}
if (args.readListIfPresent<wordRe>("excludePatches", excludePatches))
{
Info<< "Excluding patches " << flatOutput(excludePatches)
<< nl << endl;
}
word patchName(pp.name());
// Identity if both whitelist and blacklist are empty
const labelList zoneIndices
(
stringListOps::findMatching
(
zones,
includePatches,
excludePatches,
nameOp<surfZone>()
)
);
Info<< "Writing regions to "
<< zoneIndices.size() << " separate files ..." << nl << endl;
// Faces to subset
bitSet includeMap(surf.size());
for (const label zonei : zoneIndices)
{
const surfZone& zn = zones[zonei];
includeMap.reset();
includeMap.set(zn.range());
word patchName(zn.name());
if (patchName.empty())
{
patchName = geometricSurfacePatch::defaultName(patchi);
// In case people expect the same names as with triSurface
patchName = geometricSurfacePatch::defaultName(zonei);
}
fileName outFile(surfBase + '_' + patchName + '.' + extension);
Info<< " Writing patch " << patchName << " to file " << outFile
<< endl;
Info<< " Zone " << zonei << " (" << zn.size() << " faces) "
<< patchName
<< " to file " << outFile << nl;
// Collect faces of region
boolList includeMap(surf.size(), false);
forAll(surf, facei)
{
const labelledTri& f = surf[facei];
if (f.region() == patchi)
{
includeMap[facei] = true;
}
}
// Subset triSurface
triSurface subSurf(surf.subsetMesh(includeMap));
subSurf.write(outFile);
// Subset and write
surf.subsetMesh(includeMap).write(outFile);
}
Info<< "End\n" << endl;
Info<< "\nEnd\n" << endl;
return 0;
}

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2015-2019 OpenCFD Ltd.
Copyright (C) 2015-2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -36,8 +36,8 @@ Description
\*---------------------------------------------------------------------------*/
#include "triSurface.H"
#include "triSurfaceSearch.H"
#include "MeshedSurfaces.H"
#include "argList.H"
#include "Fstream.H"
#include "IOdictionary.H"
@ -56,7 +56,7 @@ int main(int argc, char *argv[])
{
argList::addNote
(
"A surface analysis tool that subsets the triSurface to choose a"
"A surface analysis tool that subsets the surface to choose a"
" region of interest."
);
@ -71,7 +71,8 @@ int main(int argc, char *argv[])
dictionary meshSubsetDict(dictFile);
Info<< "Reading surface " << args[2] << " ..." << endl;
triSurface surf1(args[2]);
meshedSurface surf1(args[2]);
const fileName outFileName(args[3]);
@ -101,134 +102,115 @@ int main(int argc, char *argv[])
meshSubsetDict.lookup("zone")
);
if (markedZone.size() && markedZone.size() != 2)
boundBox zoneBb;
if (markedZone.size())
{
FatalErrorInFunction
<< "zone specification should be two points, min and max of "
<< "the boundingbox" << endl
<< "zone:" << markedZone
<< exit(FatalError);
if (markedZone.size() != 2)
{
FatalErrorInFunction
<< "zone specification should be two points, min and max of "
<< "the boundingbox" << endl
<< "zone:" << markedZone
<< exit(FatalError);
}
zoneBb.min() = markedZone[0];
zoneBb.max() = markedZone[1];
if (!zoneBb.valid())
{
WarningInFunction
<< "Defined zone is invalid: " << zoneBb << nl;
}
}
const bool addFaceNeighbours =
meshSubsetDict.get<bool>("addFaceNeighbours");
const bool invertSelection =
meshSubsetDict.lookupOrDefault("invertSelection", false);
meshSubsetDict.getOrDefault("invertSelection", false);
// Mark the cells for the subset
// Faces to subset
boolList facesToSubset(surf1.size(), false);
bitSet facesToSubset(surf1.size(), false);
//
// pick up faces connected to "localPoints"
// Faces connected to "localPoints"
//
if (markedPoints.size())
{
Info<< "Found " << markedPoints.size() << " marked point(s)." << endl;
// pick up cells sharing the point
forAll(markedPoints, pointi)
for (const label pointi : markedPoints)
{
if
(
markedPoints[pointi] < 0
|| markedPoints[pointi] >= surf1.nPoints()
)
if (pointi < 0 || pointi >= surf1.nPoints())
{
FatalErrorInFunction
<< "localPoint label " << markedPoints[pointi]
<< "out of range."
<< " The mesh has got "
<< surf1.nPoints() << " localPoints."
<< "localPoint label " << pointi << "out of range."
<< " Surface has " << surf1.nPoints() << " localPoints."
<< exit(FatalError);
}
const labelList& curFaces =
surf1.pointFaces()[markedPoints[pointi]];
const labelList& curFaces = surf1.pointFaces()[pointi];
forAll(curFaces, i)
{
facesToSubset[curFaces[i]] = true;
}
facesToSubset.set(curFaces);
}
}
//
// pick up faces connected to "edges"
// Faces connected to "edges"
//
if (markedEdges.size())
{
Info<< "Found " << markedEdges.size() << " marked edge(s)." << endl;
// pick up cells sharing the edge
forAll(markedEdges, edgeI)
for (const label edgei : markedEdges)
{
if
(
markedEdges[edgeI] < 0
|| markedEdges[edgeI] >= surf1.nEdges()
)
if (edgei < 0 || edgei >= surf1.nEdges())
{
FatalErrorInFunction
<< "edge label " << markedEdges[edgeI]
<< "out of range."
<< " The mesh has got "
<< surf1.nEdges() << " edges."
<< "edge label " << edgei << "out of range."
<< " Surface has " << surf1.nEdges() << " edges."
<< exit(FatalError);
}
const labelList& curFaces = surf1.edgeFaces()[markedEdges[edgeI]];
const labelList& curFaces = surf1.edgeFaces()[edgei];
forAll(curFaces, i)
{
facesToSubset[curFaces[i]] = true;
}
facesToSubset.set(curFaces);
}
}
//
// pick up faces with centre inside "zone"
// Faces with centre inside "zone"
//
if (markedZone.size() == 2)
if (zoneBb.valid())
{
const point& min = markedZone[0];
const point& max = markedZone[1];
Info<< "Using zone min:" << min << " max:" << max << endl;
Info<< "Using zone " << zoneBb << endl;
forAll(surf1, facei)
{
const point centre = surf1[facei].centre(surf1.points());
if
(
(centre.x() >= min.x())
&& (centre.y() >= min.y())
&& (centre.z() >= min.z())
&& (centre.x() <= max.x())
&& (centre.y() <= max.y())
&& (centre.z() <= max.z())
)
if (zoneBb.contains(centre))
{
facesToSubset[facei] = true;
facesToSubset.set(facei);
}
}
}
//
// pick up faces on certain side of surface
// Faces on certain side of surface
//
if (meshSubsetDict.found("surface"))
@ -237,18 +219,16 @@ int main(int argc, char *argv[])
const fileName surfName(surfDict.get<fileName>("name"));
const bool outside(surfDict.get<bool>("outside"));
const volumeType::type volType =
(
surfDict.getOrDefault("outside", false)
? volumeType::OUTSIDE
: volumeType::INSIDE
);
if (outside)
{
Info<< "Selecting all triangles with centre outside surface "
<< surfName << endl;
}
else
{
Info<< "Selecting all triangles with centre inside surface "
<< surfName << endl;
}
Info<< "Selecting faces with centre located "
<< volumeType::names[volType] << " of surface "
<< surfName << endl;
// Read surface to select on
triSurface selectSurf(surfName);
@ -264,22 +244,15 @@ int main(int argc, char *argv[])
searchSelectSurf.tree();
// Check if face (centre) is in outside or inside.
forAll(facesToSubset, facei)
forAll(surf1, facei)
{
if (!facesToSubset[facei])
{
const point fc(surf1[facei].centre(surf1.points()));
volumeType t = selectTree.getVolumeType(fc);
if
(
outside
? (t == volumeType::OUTSIDE)
: (t == volumeType::INSIDE)
)
if (volType == selectTree.getVolumeType(fc))
{
facesToSubset[facei] = true;
facesToSubset.set(facei);
}
}
}
@ -304,7 +277,7 @@ int main(int argc, char *argv[])
if (pl.distance(fc) < distance && mag(pl.normal() & nf) > cosAngle)
{
facesToSubset[facei] = true;
facesToSubset.set(facei);
}
}
}
@ -323,38 +296,29 @@ int main(int argc, char *argv[])
Info<< "Found " << markedFaces.size() << " marked face(s)." << endl;
// Check and mark faces to pick up
forAll(markedFaces, facei)
for (const label facei : markedFaces)
{
if
(
markedFaces[facei] < 0
|| markedFaces[facei] >= surf1.size()
)
if (facei < 0 || facei >= surf1.size())
{
FatalErrorInFunction
<< "Face label " << markedFaces[facei] << "out of range."
<< " The mesh has got "
<< surf1.size() << " faces."
<< "Face label " << facei << "out of range."
<< " Surface has " << surf1.size() << " faces."
<< exit(FatalError);
}
// Mark the face
facesToSubset[markedFaces[facei]] = true;
facesToSubset.set(facei);
// mark its neighbours if requested
// Mark its neighbours if requested
if (addFaceNeighbours)
{
const labelList& curFaces =
surf1.faceFaces()[markedFaces[facei]];
const labelList& curFaces = surf1.faceFaces()[facei];
forAll(curFaces, i)
for (const label neiFacei : curFaces)
{
label facei = curFaces[i];
if (!facesToSubset[facei])
if (facesToSubset.set(neiFacei))
{
facesToSubset[facei] = true;
nFaceNeighbours++;
++nFaceNeighbours;
}
}
}
@ -372,15 +336,12 @@ int main(int argc, char *argv[])
{
Info<< "Inverting selection." << endl;
forAll(facesToSubset, i)
{
facesToSubset[i] = facesToSubset[i] ? false : true;
}
facesToSubset.flip();
}
// Create subsetted surface
triSurface surf2(surf1.subsetMesh(facesToSubset));
meshedSurface surf2(surf1.subsetMesh(facesToSubset));
Info<< "Subset:" << endl;
surf2.writeStats(Info);