/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2015-2021 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 .
Application
surfaceSubset
Group
grpSurfaceUtilities
Description
A surface analysis tool that subsets the triSurface to choose a
region of interest. Based on subsetMesh.
\*---------------------------------------------------------------------------*/
#include "triSurfaceSearch.H"
#include "MeshedSurfaces.H"
#include "argList.H"
#include "Fstream.H"
#include "IOdictionary.H"
#include "boundBox.H"
#include "indexedOctree.H"
#include "treeDataTriSurface.H"
#include "Random.H"
#include "volumeType.H"
#include "plane.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"A surface analysis tool that subsets the surface to choose a"
" region of interest."
);
argList::noParallel();
argList::addArgument("dict", "The surfaceSubsetDict");
argList::addArgument("input", "The input surface file");
argList::addArgument("output", "The output surface file");
argList args(argc, argv);
Info<< "Reading dictionary " << args[1] << " ..." << endl;
IFstream dictFile(args.get(1));
dictionary meshSubsetDict(dictFile);
Info<< "Reading surface " << args[2] << " ..." << endl;
meshedSurface surf1(args.get(2));
const auto outFileName(args.get(3));
Info<< "Original:" << endl;
surf1.writeStats(Info);
Info<< endl;
labelList markedPoints
(
meshSubsetDict.lookup("localPoints")
);
labelList markedEdges
(
meshSubsetDict.lookup("edges")
);
labelList markedFaces
(
meshSubsetDict.lookup("faces")
);
pointField markedZone
(
meshSubsetDict.lookup("zone")
);
boundBox zoneBb;
if (markedZone.size())
{
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("addFaceNeighbours");
const bool invertSelection =
meshSubsetDict.getOrDefault("invertSelection", false);
// Mark the cells for the subset
// Faces to subset
bitSet facesToSubset(surf1.size(), false);
//
// Faces connected to "localPoints"
//
if (markedPoints.size())
{
Info<< "Found " << markedPoints.size() << " marked point(s)." << endl;
for (const label pointi : markedPoints)
{
if (pointi < 0 || pointi >= surf1.nPoints())
{
FatalErrorInFunction
<< "localPoint label " << pointi << "out of range."
<< " Surface has " << surf1.nPoints() << " localPoints."
<< exit(FatalError);
}
const labelList& curFaces = surf1.pointFaces()[pointi];
facesToSubset.set(curFaces);
}
}
//
// Faces connected to "edges"
//
if (markedEdges.size())
{
Info<< "Found " << markedEdges.size() << " marked edge(s)." << endl;
for (const label edgei : markedEdges)
{
if (edgei < 0 || edgei >= surf1.nEdges())
{
FatalErrorInFunction
<< "edge label " << edgei << "out of range."
<< " Surface has " << surf1.nEdges() << " edges."
<< exit(FatalError);
}
const labelList& curFaces = surf1.edgeFaces()[edgei];
facesToSubset.set(curFaces);
}
}
//
// Faces with centre inside "zone"
//
if (zoneBb.valid())
{
Info<< "Using zone " << zoneBb << endl;
forAll(surf1, facei)
{
const point centre = surf1[facei].centre(surf1.points());
if (zoneBb.contains(centre))
{
facesToSubset.set(facei);
}
}
}
//
// Faces on certain side of surface
//
if (meshSubsetDict.found("surface"))
{
const dictionary& surfDict = meshSubsetDict.subDict("surface");
const auto surfName(surfDict.get("name"));
const volumeType::type volType =
(
surfDict.getOrDefault("outside", false)
? volumeType::OUTSIDE
: volumeType::INSIDE
);
Info<< "Selecting faces with centre located "
<< volumeType::names[volType] << " of surface "
<< surfName << endl;
// Read surface to select on
triSurface selectSurf(surfName);
triSurfaceSearch searchSelectSurf
(
selectSurf,
indexedOctree::perturbTol(),
8
);
const indexedOctree& selectTree =
searchSelectSurf.tree();
// Check if face (centre) is in outside or inside.
forAll(surf1, facei)
{
if (!facesToSubset[facei])
{
const point fc(surf1[facei].centre(surf1.points()));
if (volType == selectTree.getVolumeType(fc))
{
facesToSubset.set(facei);
}
}
}
}
if (meshSubsetDict.found("plane"))
{
const dictionary& planeDict = meshSubsetDict.subDict("plane");
const plane pl(planeDict);
const scalar distance(planeDict.get("distance"));
const scalar cosAngle(planeDict.get("cosAngle"));
// Select all triangles that are close to the plane and
// whose normal aligns with the plane as well.
forAll(surf1.faceCentres(), facei)
{
const point& fc = surf1.faceCentres()[facei];
const point& nf = surf1.faceNormals()[facei];
if (pl.distance(fc) < distance && mag(pl.normal() & nf) > cosAngle)
{
facesToSubset.set(facei);
}
}
}
//
// pick up specified "faces"
//
// Number of additional faces picked up because of addFaceNeighbours
label nFaceNeighbours = 0;
if (markedFaces.size())
{
Info<< "Found " << markedFaces.size() << " marked face(s)." << endl;
// Check and mark faces to pick up
for (const label facei : markedFaces)
{
if (facei < 0 || facei >= surf1.size())
{
FatalErrorInFunction
<< "Face label " << facei << "out of range."
<< " Surface has " << surf1.size() << " faces."
<< exit(FatalError);
}
// Mark the face
facesToSubset.set(facei);
// Mark its neighbours if requested
if (addFaceNeighbours)
{
const labelList& curFaces = surf1.faceFaces()[facei];
for (const label neiFacei : curFaces)
{
if (facesToSubset.set(neiFacei))
{
++nFaceNeighbours;
}
}
}
}
}
if (addFaceNeighbours)
{
Info<< "Added " << nFaceNeighbours
<< " faces because of addFaceNeighbours" << endl;
}
if (invertSelection)
{
Info<< "Inverting selection." << endl;
facesToSubset.flip();
}
// Create subsetted surface
meshedSurface surf2(surf1.subsetMesh(facesToSubset));
Info<< "Subset:" << endl;
surf2.writeStats(Info);
Info<< endl;
Info<< "Writing surface to " << outFileName << endl;
surf2.write(outFileName);
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //