519 lines
14 KiB
C++
519 lines
14 KiB
C++
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / F ield | foam-extend: Open Source CFD
|
|
\\ / O peration | Version: 4.1
|
|
\\ / A nd | Web: http://www.foam-extend.org
|
|
\\/ M anipulation | For copyright notice see file Copyright
|
|
-------------------------------------------------------------------------------
|
|
License
|
|
This file is part of foam-extend.
|
|
|
|
foam-extend 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.
|
|
|
|
foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
Description
|
|
Select cells in relation to surface.
|
|
|
|
Divides cells into three sets:
|
|
- cutCells : cells cut by surface or close to surface.
|
|
- outside : cells not in cutCells and reachable from set of
|
|
user-defined points (outsidePoints)
|
|
- inside : same but not reachable.
|
|
|
|
Finally the wanted sets are combined into a cellSet 'selected'. Apart
|
|
from straightforward adding the contents there are a few extra rules to
|
|
make sure that the surface of the 'outside' of the mesh is singly
|
|
connected.
|
|
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
#include "argList.H"
|
|
#include "objectRegistry.H"
|
|
#include "foamTime.H"
|
|
#include "polyMesh.H"
|
|
#include "IOdictionary.H"
|
|
#include "twoDPointCorrector.H"
|
|
#include "OFstream.H"
|
|
#include "meshTools.H"
|
|
|
|
#include "triSurface.H"
|
|
#include "triSurfaceSearch.H"
|
|
#include "meshSearch.H"
|
|
#include "cellClassification.H"
|
|
#include "cellSet.H"
|
|
#include "cellInfo.H"
|
|
#include "MeshWave.H"
|
|
#include "edgeStats.H"
|
|
#include "treeDataTriSurface.H"
|
|
#include "indexedOctree.H"
|
|
|
|
using namespace Foam;
|
|
|
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
|
|
|
// cellType for cells included/not included in mesh.
|
|
static const label MESH = cellClassification::INSIDE;
|
|
static const label NONMESH = cellClassification::OUTSIDE;
|
|
|
|
|
|
void writeSet(const cellSet& cells, const string& msg)
|
|
{
|
|
Info<< "Writing " << msg << " (" << cells.size() << ") to cellSet "
|
|
<< cells.instance()/cells.local()/cells.name()
|
|
<< endl << endl;
|
|
cells.write();
|
|
}
|
|
|
|
|
|
void getType(const labelList& elems, const label type, labelHashSet& set)
|
|
{
|
|
forAll(elems, i)
|
|
{
|
|
if (elems[i] == type)
|
|
{
|
|
set.insert(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void cutBySurface
|
|
(
|
|
const polyMesh& mesh,
|
|
const meshSearch& queryMesh,
|
|
const triSurfaceSearch& querySurf,
|
|
|
|
const pointField& outsidePts,
|
|
const bool selectCut,
|
|
const bool selectInside,
|
|
const bool selectOutside,
|
|
const scalar nearDist,
|
|
|
|
cellClassification& cellType
|
|
)
|
|
{
|
|
// Cut with surface and classify as inside/outside/cut
|
|
cellType =
|
|
cellClassification
|
|
(
|
|
mesh,
|
|
queryMesh,
|
|
querySurf,
|
|
outsidePts
|
|
);
|
|
|
|
// Get inside/outside/cutCells cellSets.
|
|
cellSet inside(mesh, "inside", mesh.nCells()/10);
|
|
getType(cellType, cellClassification::INSIDE, inside);
|
|
writeSet(inside, "inside cells");
|
|
|
|
cellSet outside(mesh, "outside", mesh.nCells()/10);
|
|
getType(cellType, cellClassification::OUTSIDE, outside);
|
|
writeSet(outside, "outside cells");
|
|
|
|
cellSet cutCells(mesh, "cutCells", mesh.nCells()/10);
|
|
getType(cellType, cellClassification::CUT, cutCells);
|
|
writeSet(cutCells, "cells cut by surface");
|
|
|
|
|
|
// Change cellType to reflect selected part of mesh. Use
|
|
// MESH to denote selected part, NONMESH for all
|
|
// other cells.
|
|
// Is a bit of a hack but allows us to reuse all the functionality
|
|
// in cellClassification.
|
|
|
|
forAll(cellType, cellI)
|
|
{
|
|
label cType = cellType[cellI];
|
|
|
|
if (cType == cellClassification::CUT)
|
|
{
|
|
if (selectCut)
|
|
{
|
|
cellType[cellI] = MESH;
|
|
}
|
|
else
|
|
{
|
|
cellType[cellI] = NONMESH;
|
|
}
|
|
}
|
|
else if (cType == cellClassification::INSIDE)
|
|
{
|
|
if (selectInside)
|
|
{
|
|
cellType[cellI] = MESH;
|
|
}
|
|
else
|
|
{
|
|
cellType[cellI] = NONMESH;
|
|
}
|
|
}
|
|
else if (cType == cellClassification::OUTSIDE)
|
|
{
|
|
if (selectOutside)
|
|
{
|
|
cellType[cellI] = MESH;
|
|
}
|
|
else
|
|
{
|
|
cellType[cellI] = NONMESH;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FatalErrorIn("cutBySurface")
|
|
<< "Multiple mesh regions in original mesh" << endl
|
|
<< "Please use splitMeshRegions to separate these"
|
|
<< exit(FatalError);
|
|
}
|
|
}
|
|
|
|
|
|
if (nearDist > 0)
|
|
{
|
|
Info<< "Removing cells with points closer than " << nearDist
|
|
<< " to the surface ..." << nl << endl;
|
|
|
|
const pointField& pts = mesh.points();
|
|
const indexedOctree<treeDataTriSurface>& tree = querySurf.tree();
|
|
|
|
label nRemoved = 0;
|
|
|
|
forAll(pts, pointI)
|
|
{
|
|
const point& pt = pts[pointI];
|
|
|
|
pointIndexHit hitInfo = tree.findNearest(pt, sqr(nearDist));
|
|
|
|
if (hitInfo.hit())
|
|
{
|
|
const labelList& pCells = mesh.pointCells()[pointI];
|
|
|
|
forAll(pCells, i)
|
|
{
|
|
if (cellType[pCells[i]] != NONMESH)
|
|
{
|
|
cellType[pCells[i]] = NONMESH;
|
|
nRemoved++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// tmp<pointField> tnearest = querySurf.calcNearest(pts);
|
|
// const pointField& nearest = tnearest();
|
|
//
|
|
// label nRemoved = 0;
|
|
//
|
|
// forAll(nearest, pointI)
|
|
// {
|
|
// if (mag(nearest[pointI] - pts[pointI]) < nearDist)
|
|
// {
|
|
// const labelList& pCells = mesh.pointCells()[pointI];
|
|
//
|
|
// forAll(pCells, i)
|
|
// {
|
|
// if (cellType[pCells[i]] != NONMESH)
|
|
// {
|
|
// cellType[pCells[i]] = NONMESH;
|
|
// nRemoved++;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
Info<< "Removed " << nRemoved << " cells since too close to surface"
|
|
<< nl << endl;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// We're meshing the outside. Subset the currently selected mesh cells with the
|
|
// ones reachable from the outsidepoints.
|
|
label selectOutsideCells
|
|
(
|
|
const polyMesh& mesh,
|
|
const meshSearch& queryMesh,
|
|
const pointField& outsidePts,
|
|
cellClassification& cellType
|
|
)
|
|
{
|
|
//
|
|
// Check all outsidePts and for all of them inside a mesh cell
|
|
// collect the faces to start walking from
|
|
//
|
|
|
|
// Outside faces
|
|
labelHashSet outsideFacesMap(outsidePts.size() * 6 * 2);
|
|
dynamicLabelList outsideFaces(outsideFacesMap.size());
|
|
// CellInfo on outside faces
|
|
DynamicList<cellInfo> outsideFacesInfo(outsideFacesMap.size());
|
|
|
|
// cellInfo for mesh cell
|
|
const cellInfo meshInfo(MESH);
|
|
|
|
forAll(outsidePts, outsidePtI)
|
|
{
|
|
// Find cell containing point. Linear search.
|
|
label cellI = queryMesh.findCell(outsidePts[outsidePtI], -1, false);
|
|
|
|
if (cellType[cellI] == MESH)
|
|
{
|
|
Info<< "Marking cell " << cellI << " containing outside point "
|
|
<< outsidePts[outsidePtI] << " with type " << cellType[cellI]
|
|
<< " ..." << endl;
|
|
|
|
//
|
|
// Mark this cell and its faces to start walking from
|
|
//
|
|
|
|
// Mark faces of cellI
|
|
const labelList& cFaces = mesh.cells()[cellI];
|
|
forAll(cFaces, i)
|
|
{
|
|
label faceI = cFaces[i];
|
|
|
|
if (outsideFacesMap.insert(faceI))
|
|
{
|
|
outsideFaces.append(faceI);
|
|
outsideFacesInfo.append(meshInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Floodfill starting from outsideFaces (of type meshInfo)
|
|
MeshWave<cellInfo> regionCalc
|
|
(
|
|
mesh,
|
|
outsideFaces.shrink(),
|
|
outsideFacesInfo.shrink(),
|
|
mesh.nCells() // max iterations
|
|
);
|
|
|
|
// Now regionCalc should hold info on cells that are reachable from
|
|
// changedFaces. Use these to subset cellType
|
|
const List<cellInfo>& allCellInfo = regionCalc.allCellInfo();
|
|
|
|
label nChanged = 0;
|
|
|
|
forAll(allCellInfo, cellI)
|
|
{
|
|
if (cellType[cellI] == MESH)
|
|
{
|
|
// Original cell was selected for meshing. Check if cell was
|
|
// reached from outsidePoints
|
|
if (allCellInfo[cellI].type() != MESH)
|
|
{
|
|
cellType[cellI] = NONMESH;
|
|
nChanged++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nChanged;
|
|
}
|
|
|
|
|
|
// Main program:
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
argList::noParallel();
|
|
|
|
# include "setRootCase.H"
|
|
# include "createTime.H"
|
|
# include "createPolyMesh.H"
|
|
|
|
// Mesh edge statistics calculator
|
|
edgeStats edgeCalc(mesh);
|
|
|
|
|
|
IOdictionary refineDict
|
|
(
|
|
IOobject
|
|
(
|
|
"selectCellsDict",
|
|
runTime.system(),
|
|
mesh,
|
|
IOobject::MUST_READ,
|
|
IOobject::NO_WRITE
|
|
)
|
|
);
|
|
|
|
fileName surfName(refineDict.lookup("surface"));
|
|
pointField outsidePts(refineDict.lookup("outsidePoints"));
|
|
bool useSurface(readBool(refineDict.lookup("useSurface")));
|
|
bool selectCut(readBool(refineDict.lookup("selectCut")));
|
|
bool selectInside(readBool(refineDict.lookup("selectInside")));
|
|
bool selectOutside(readBool(refineDict.lookup("selectOutside")));
|
|
scalar nearDist(readScalar(refineDict.lookup("nearDistance")));
|
|
|
|
|
|
if (useSurface)
|
|
{
|
|
Info<< "Cells to be used for meshing (0=false, 1=true):" << nl
|
|
<< " cells cut by surface : " << selectCut << nl
|
|
<< " cells inside of surface : " << selectInside << nl
|
|
<< " cells outside of surface : " << selectOutside << nl
|
|
<< " cells with points further than : " << nearDist << nl
|
|
<< endl;
|
|
}
|
|
else
|
|
{
|
|
Info<< "Cells to be used for meshing (0=false, 1=true):" << nl
|
|
<< " cells reachable from outsidePoints:" << selectOutside << nl
|
|
<< endl;
|
|
}
|
|
|
|
// Print edge stats on original mesh.
|
|
(void)edgeCalc.minLen(Info);
|
|
|
|
// Search engine on mesh. Face decomposition since faces might be warped.
|
|
meshSearch queryMesh(mesh, true);
|
|
|
|
// Check all 'outside' points
|
|
forAll(outsidePts, outsideI)
|
|
{
|
|
const point& outsidePoint = outsidePts[outsideI];
|
|
|
|
if (queryMesh.findCell(outsidePoint, -1, false) == -1)
|
|
{
|
|
FatalErrorIn(args.executable())
|
|
<< "outsidePoint " << outsidePoint
|
|
<< " is not inside any cell"
|
|
<< exit(FatalError);
|
|
}
|
|
}
|
|
|
|
// Cell status (compared to surface if provided): inside/outside/cut.
|
|
// Start off from everything selected and cut later.
|
|
cellClassification cellType
|
|
(
|
|
mesh,
|
|
labelList
|
|
(
|
|
mesh.nCells(),
|
|
cellClassification::MESH
|
|
)
|
|
);
|
|
|
|
|
|
// Surface
|
|
autoPtr<triSurface> surf(nullptr);
|
|
// Search engine on surface.
|
|
autoPtr<triSurfaceSearch> querySurf(nullptr);
|
|
|
|
if (useSurface)
|
|
{
|
|
surf.reset(new triSurface(surfName));
|
|
|
|
// Dump some stats
|
|
surf().writeStats(Info);
|
|
|
|
// Search engine on surface.
|
|
querySurf.reset(new triSurfaceSearch(surf));
|
|
|
|
// Set cellType[cellI] according to relation to surface
|
|
cutBySurface
|
|
(
|
|
mesh,
|
|
queryMesh,
|
|
querySurf,
|
|
|
|
outsidePts,
|
|
selectCut,
|
|
selectInside,
|
|
selectOutside,
|
|
nearDist,
|
|
|
|
cellType
|
|
);
|
|
}
|
|
|
|
|
|
// Now 'trim' all the corners from the mesh so meshing/surface extraction
|
|
// becomes easier.
|
|
|
|
label nHanging, nRegionEdges, nRegionPoints, nOutside;
|
|
|
|
do
|
|
{
|
|
Info<< "Removing cells which after subsetting would have all points"
|
|
<< " on outside ..." << nl << endl;
|
|
|
|
nHanging = cellType.fillHangingCells
|
|
(
|
|
MESH, // meshType
|
|
NONMESH, // fill type
|
|
mesh.nCells()
|
|
);
|
|
|
|
|
|
Info<< "Removing edges connecting cells unconnected by faces ..."
|
|
<< nl << endl;
|
|
|
|
nRegionEdges = cellType.fillRegionEdges
|
|
(
|
|
MESH, // meshType
|
|
NONMESH, // fill type
|
|
mesh.nCells()
|
|
);
|
|
|
|
|
|
Info<< "Removing points connecting cells unconnected by faces ..."
|
|
<< nl << endl;
|
|
|
|
nRegionPoints = cellType.fillRegionPoints
|
|
(
|
|
MESH, // meshType
|
|
NONMESH, // fill type
|
|
mesh.nCells()
|
|
);
|
|
|
|
nOutside = 0;
|
|
if (selectOutside)
|
|
{
|
|
// Since we're selecting the cells reachable from outsidePoints
|
|
// and the set might have changed, redo the outsideCells
|
|
// calculation
|
|
nOutside = selectOutsideCells
|
|
(
|
|
mesh,
|
|
queryMesh,
|
|
outsidePts,
|
|
cellType
|
|
);
|
|
}
|
|
} while
|
|
(
|
|
nHanging != 0
|
|
|| nRegionEdges != 0
|
|
|| nRegionPoints != 0
|
|
|| nOutside != 0
|
|
);
|
|
|
|
cellSet selectedCells(mesh, "selected", mesh.nCells()/10);
|
|
getType(cellType, MESH, selectedCells);
|
|
|
|
writeSet(selectedCells, "cells selected for meshing");
|
|
|
|
|
|
Info << "End\n" << endl;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|