Skip to content
Snippets Groups Projects
Commit ca88abba authored by Mark OLESEN's avatar Mark OLESEN
Browse files

ENH: add proximityRegions filter to distanceSurface (#2108)

- combines region-based and proximity-based filtering

  proxityRegions (post-filter):
    Checks the distance of the resulting faces against the original
    search surface. Filters based on the area-weighted distance
    of each topologically connected region.
    If the area-weighted distance of a region is greater than
    \c absProximity, the entire region is rejected.

STYLE: 'proxityFaces' as newer synonym for 'proximity' filter
parent b6b4ab07
No related branches found
No related tags found
1 merge request!460ENH: add proximityRegions filter to distanceSurface (#2108)
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2018-2020 OpenCFD Ltd. Copyright (C) 2018-2021 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
...@@ -59,10 +59,10 @@ Usage ...@@ -59,10 +59,10 @@ Usage
bounds | Limit with bounding box | no | bounds | Limit with bounding box | no |
surfaceType | Type of surface | yes | surfaceType | Type of surface | yes |
surfaceName | Name of surface in \c triSurface/ | no | dict name surfaceName | Name of surface in \c triSurface/ | no | dict name
topology | Topology filter (none/largestRegion/nearestPoints/proximity) | no | none topology | Topology filter name | no | none
nearestPoints | Points for point-based segmentation | no | nearestPoints | Points for point-based segmentation | no |
maxDistance | Max search distance for nearestPoints | no | GREAT maxDistance | Max search distance for nearestPoints | no | GREAT
relProximity | Max limit of absolute vs normal distance | no | 1e3 absProximity | Max proximity of face centres | no | 1e-5
\endtable \endtable
SourceFiles SourceFiles
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2020 OpenCFD Ltd. Copyright (C) 2016-2021 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
...@@ -52,7 +52,9 @@ Foam::distanceSurface::topoFilterNames_ ...@@ -52,7 +52,9 @@ Foam::distanceSurface::topoFilterNames_
{ topologyFilterType::NONE, "none" }, { topologyFilterType::NONE, "none" },
{ topologyFilterType::LARGEST_REGION, "largestRegion" }, { topologyFilterType::LARGEST_REGION, "largestRegion" },
{ topologyFilterType::NEAREST_POINTS, "nearestPoints" }, { topologyFilterType::NEAREST_POINTS, "nearestPoints" },
{ topologyFilterType::PROXIMITY, "proximity" }, { topologyFilterType::PROXIMITY_REGIONS, "proximityRegions" },
{ topologyFilterType::PROXIMITY_FACES, "proximityFaces" },
{ topologyFilterType::PROXIMITY_FACES, "proximity" },
}); });
...@@ -76,8 +78,9 @@ static inline void checkAllHits(const UList<pointIndexHit>& nearest) ...@@ -76,8 +78,9 @@ static inline void checkAllHits(const UList<pointIndexHit>& nearest)
if (notHit) if (notHit)
{ {
FatalErrorInFunction FatalErrorInFunction
<< "Had " << notHit << " from " << nearest.size() << "Had " << notHit << " faces/cells from "
<< " without a point hit" << endl << nearest.size() << " without a point hit." << nl
<< "May be caused by a severely degenerate input surface" << nl
<< abort(FatalError); << abort(FatalError);
} }
} }
...@@ -184,15 +187,18 @@ static inline void calcNormalDistance_filtered ...@@ -184,15 +187,18 @@ static inline void calcNormalDistance_filtered
// the normal at the edge, which creates a zero-crossing // the normal at the edge, which creates a zero-crossing
// extending to infinity. // extending to infinity.
// //
// Ad hoc treatment: require that the surface hit // Ad hoc treatment: require that the surface hit point is within a
// point is within a somewhat generous bounding box // somewhat generous bounding box for the cell (+10%)
// for the cell //
// Provisioning for filtering based on the cell points,
// but its usefulness remains to be seen (2020-12-09)
template<bool WantPointFilter = false> template<bool WantPointFilter = false>
static bitSet simpleGeometricFilter static bitSet simpleGeometricFilter
( (
bitSet& ignoreCells, bitSet& ignoreCells,
const List<pointIndexHit>& nearest, const List<pointIndexHit>& nearest,
const polyMesh& mesh const polyMesh& mesh,
const scalar boundBoxInflate = 0.1 // 10% to catch corners
) )
{ {
// A deny filter. Initially false (accept everything) // A deny filter. Initially false (accept everything)
...@@ -215,9 +221,7 @@ static bitSet simpleGeometricFilter ...@@ -215,9 +221,7 @@ static bitSet simpleGeometricFilter
cellBb.clear(); cellBb.clear();
cellBb.add(mesh.points(), cPoints); cellBb.add(mesh.points(), cPoints);
cellBb.inflate(boundBoxInflate);
// Expand slightly to catch corners
cellBb.inflate(0.1);
if (!cellBb.contains(pt)) if (!cellBb.contains(pt))
{ {
...@@ -455,10 +459,30 @@ void Foam::distanceSurface::createGeometry() ...@@ -455,10 +459,30 @@ void Foam::distanceSurface::createGeometry()
checkAllHits(nearest); checkAllHits(nearest);
// Geometric pre-filtering when distance == 0 // Geometric pre-filtering when distance == 0
// NOTE (2021-05-31)
// Can skip the prefilter if we use proximity-regions filter anyhow
// but it makes the iso algorithm more expensive and doesn't help
// unless we start relying on area-based weighting for rejecting regions.
if (filterCells) if (filterCells)
{ {
ignoreCellPoints = ignoreCellPoints = simpleGeometricFilter<false>
simpleGeometricFilter<false>(ignoreCells, nearest, fvmesh); (
ignoreCells,
nearest,
fvmesh,
// Inflate bound box.
// - To catch corners: approx. 10%
// - Extra generous for PROXIMITY_REGIONS
// (extra weighting for 'bad' faces)
(
topologyFilterType::PROXIMITY_REGIONS == topoFilter_
? 1
: 0.1
)
);
} }
if (withSignDistance_) if (withSignDistance_)
...@@ -617,6 +641,8 @@ void Foam::distanceSurface::createGeometry() ...@@ -617,6 +641,8 @@ void Foam::distanceSurface::createGeometry()
refineBlockedCells(ignoreCells, isoCutter); refineBlockedCells(ignoreCells, isoCutter);
filterKeepNearestRegions(ignoreCells); filterKeepNearestRegions(ignoreCells);
} }
// Note: apply similar filtering for PROXIMITY_REGIONS later instead
} }
// Don't need point filter beyond this point // Don't need point filter beyond this point
...@@ -627,6 +653,7 @@ void Foam::distanceSurface::createGeometry() ...@@ -627,6 +653,7 @@ void Foam::distanceSurface::createGeometry()
{ {
Pout<< "Writing cell distance:" << cellDistance.objectPath() << endl; Pout<< "Writing cell distance:" << cellDistance.objectPath() << endl;
cellDistance.write(); cellDistance.write();
pointScalarField pDist pointScalarField pDist
( (
IOobject IOobject
...@@ -660,6 +687,21 @@ void Foam::distanceSurface::createGeometry() ...@@ -660,6 +687,21 @@ void Foam::distanceSurface::createGeometry()
); );
// Restrict ignored cells to those actually cut
if (filterCells && topologyFilterType::PROXIMITY_REGIONS == topoFilter_)
{
isoSurfaceBase isoCutter
(
mesh_,
cellDistance,
pointDistance_,
distance_
);
refineBlockedCells(ignoreCells, isoCutter);
}
// ALGO_POINT still needs cell, point fields (for interpolate) // ALGO_POINT still needs cell, point fields (for interpolate)
// The others can do straight transfer // The others can do straight transfer
...@@ -667,7 +709,8 @@ void Foam::distanceSurface::createGeometry() ...@@ -667,7 +709,8 @@ void Foam::distanceSurface::createGeometry()
if if
( (
isoParams_.algorithm() != isoSurfaceParams::ALGO_POINT isoParams_.algorithm() != isoSurfaceParams::ALGO_POINT
|| topologyFilterType::PROXIMITY == topoFilter_ || topologyFilterType::PROXIMITY_FACES == topoFilter_
|| topologyFilterType::PROXIMITY_REGIONS == topoFilter_
) )
{ {
surface_.transfer(static_cast<meshedSurface&>(*isoSurfacePtr_)); surface_.transfer(static_cast<meshedSurface&>(*isoSurfacePtr_));
...@@ -678,9 +721,13 @@ void Foam::distanceSurface::createGeometry() ...@@ -678,9 +721,13 @@ void Foam::distanceSurface::createGeometry()
pointDistance_.clear(); pointDistance_.clear();
} }
if (topologyFilterType::PROXIMITY == topoFilter_) if (topologyFilterType::PROXIMITY_FACES == topoFilter_)
{
filterFaceProximity();
}
else if (topologyFilterType::PROXIMITY_REGIONS == topoFilter_)
{ {
filterByProximity(); filterRegionProximity(ignoreCells);
} }
if (debug) if (debug)
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2020 OpenCFD Ltd. Copyright (C) 2016-2021 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
...@@ -45,7 +45,7 @@ Usage ...@@ -45,7 +45,7 @@ Usage
type distanceSurface; type distanceSurface;
surfaceType triSurfaceMesh; surfaceType triSurfaceMesh;
surfaceName something.obj; surfaceName something.obj;
topology proximity; topology proximityFaces;
} }
surface2 surface2
...@@ -77,27 +77,37 @@ Usage ...@@ -77,27 +77,37 @@ Usage
bounds | Limit with bounding box | no | bounds | Limit with bounding box | no |
surfaceType | Type of surface | yes | surfaceType | Type of surface | yes |
surfaceName | Name of surface in \c triSurface/ | no | dict name surfaceName | Name of surface in \c triSurface/ | no | dict name
topology | Topology filter (none/largestRegion/nearestPoints/proximity) | no | none topology | Topology filter name | no | none
nearestPoints | Points for point-based segmentation | no | nearestPoints | Points for point-based segmentation | no |
maxDistance | Max search distance for nearestPoints | no | GREAT maxDistance | Max search distance for nearestPoints | no | GREAT
absProximity | Max proximity of face centres | no | 1e-5 absProximity | Max proximity of face centres | no | 1e-5
\endtable \endtable
Filtering (for zero-distance only) Topology/Filtering (for zero-distance only).
These represent different ways to tackle the "ragged edge" problem.
- \c none :
No filtering
- \c proximityFaces or \c proximity (post-filter):
Checks the resulting faces against the original search surface
and rejects faces with a distance greater than \c absProximity.
- \c proxityRegions (post-filter):
Checks the distance of the resulting faces against the original
search surface. Filters based on the area-weighted distance
of each topologically connected region.
If the area-weighted distance of a region is greater than
\c absProximity, the entire region is rejected.
- \c largestRegion (pre-filter): - \c largestRegion (pre-filter):
The cut cells are checked for topological connectivity and the The cut cells are checked for topological connectivity and the
region with the most number of cut cells is retained. region with the most number of cut cells is retained.
This handles the "ragged" edge problem.
- \c nearestPoints (pre-filter): - \c nearestPoints (pre-filter):
The cut cells split into regions, the regions closest to the The cut cells split into regions, the regions closest to the
user-defined points are retained. user-defined points are retained.
Uses \c maxDistance for additional control. Uses \c maxDistance for additional control.
- \c proximity (post-filter):
Checks the resulting faces against the original search surface
and rejects faces with a distance greater than \c absProximity.
. .
Note Note
...@@ -116,6 +126,7 @@ Changed default algorithm from cell to topo (2020-12). ...@@ -116,6 +126,7 @@ Changed default algorithm from cell to topo (2020-12).
SourceFiles SourceFiles
distanceSurface.C distanceSurface.C
distanceSurfaceFilter.C
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
...@@ -146,7 +157,9 @@ class distanceSurface ...@@ -146,7 +157,9 @@ class distanceSurface
NONE, //!< No additional filtering NONE, //!< No additional filtering
LARGEST_REGION, //!< Retain largest region LARGEST_REGION, //!< Retain largest region
NEAREST_POINTS, //!< Retain regions nearest to the points NEAREST_POINTS, //!< Retain regions nearest to the points
PROXIMITY //!< Post-filter for surface proximity PROXIMITY_REGIONS, //!< Retain regions with good surface proximity
PROXIMITY_FACES, //!< Retain faces with good surface proximity
PROXIMITY = PROXIMITY_FACES
}; };
//- Names for the topology filter //- Names for the topology filter
...@@ -262,15 +275,18 @@ protected: ...@@ -262,15 +275,18 @@ protected:
//- Prepare blockedFaces for region split //- Prepare blockedFaces for region split
bitSet filterPrepareRegionSplit(const bitSet& ignoreCells) const; bitSet filterPrepareRegionSplit(const bitSet& ignoreCells) const;
//- Adjust extracted iso-surface to remove far faces
void filterByProximity();
//- Keep region with the most cuts (after region split) //- Keep region with the most cuts (after region split)
void filterKeepLargestRegion(bitSet& ignoreCells) const; void filterKeepLargestRegion(bitSet& ignoreCells) const;
//- Keep region(s) closest to the nearest points //- Keep region(s) closest to the nearest points
void filterKeepNearestRegions(bitSet& ignoreCells) const; void filterKeepNearestRegions(bitSet& ignoreCells) const;
//- Remove region(s) that have far faces
void filterRegionProximity(bitSet& ignoreCells) const;
//- Adjust extracted iso-surface to remove far faces
void filterFaceProximity();
public: public:
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2020 OpenCFD Ltd. Copyright (C) 2020-2021 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
...@@ -28,6 +28,7 @@ License ...@@ -28,6 +28,7 @@ License
#include "distanceSurface.H" #include "distanceSurface.H"
#include "regionSplit.H" #include "regionSplit.H"
#include "syncTools.H" #include "syncTools.H"
#include "ListOps.H"
#include "vtkSurfaceWriter.H" #include "vtkSurfaceWriter.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
...@@ -306,16 +307,148 @@ void Foam::distanceSurface::filterKeepNearestRegions ...@@ -306,16 +307,148 @@ void Foam::distanceSurface::filterKeepNearestRegions
} }
void Foam::distanceSurface::filterByProximity() void Foam::distanceSurface::filterRegionProximity
(
bitSet& ignoreCells
) const
{ {
const searchableSurface& geom = geometryPtr_(); const searchableSurface& geom = geometryPtr_();
// Filtering for faces // For face distances
const pointField& fc = surface_.faceCentres(); const pointField& fc = surface_.faceCentres();
bitSet faceSelection(fc.size()); // For region split
label nTrimmed = 0; bitSet blockedFaces(filterPrepareRegionSplit(ignoreCells));
// Split region
regionSplit rs(mesh_, blockedFaces);
blockedFaces.clearStorage();
const labelList& regionColour = rs;
// For each face
scalarField faceDistance(fc.size(), GREAT);
{
List<pointIndexHit> nearest;
geom.findNearest
(
fc,
// Use initialized field (GREAT) to limit search too
faceDistance,
nearest
);
calcAbsoluteDistance(faceDistance, fc, nearest);
}
// Identical number of regions on all processors
scalarField areaRegion(rs.nRegions(), Zero);
scalarField distRegion(rs.nRegions(), Zero);
forAll(meshCells_, facei)
{
const label celli = meshCells_[facei];
const label regioni = regionColour[celli];
const scalar faceArea = surface_[facei].mag(surface_.points());
distRegion[regioni] += (faceDistance[facei] * faceArea);
areaRegion[regioni] += (faceArea);
}
Pstream::listCombineGather(distRegion, plusEqOp<scalar>());
Pstream::listCombineGather(areaRegion, plusEqOp<scalar>());
if (Pstream::master())
{
forAll(distRegion, regioni)
{
distRegion[regioni] /= (areaRegion[regioni] + VSMALL);
}
}
Pstream::listCombineScatter(distRegion);
// Define the per-face acceptance based on the region average distance
bitSet acceptFaces(fc.size());
bool prune(false);
forAll(meshCells_, facei)
{
const label celli = meshCells_[facei];
const label regioni = regionColour[celli];
// NB: do not filter by individual faces as well since this
// has been reported to cause minor holes for surfaces with
// high curvature! (2021-06-10)
if (absProximity_ < distRegion[regioni])
{
prune = true;
}
else
{
acceptFaces.set(facei);
}
}
// Heavier debugging
if (debug & 4)
{
const fileName outputName(surfaceName() + "-region-proximity-filter");
Info<< "Writing debug surface: " << outputName << nl;
surfaceWriters::vtkWriter writer
(
surface_.points(),
surface_, // faces
outputName
);
writer.write("absolute-distance", faceDistance);
// Region segmentation
labelField faceRegion
(
ListOps::create<label>
(
meshCells_,
[&](const label celli){ return regionColour[celli]; }
)
);
writer.write("face-region", faceRegion);
// Region-wise filter state
labelField faceFilterState
(
ListOps::createWithValue<label>(surface_.size(), acceptFaces, 1, 0)
);
writer.write("filter-state", faceFilterState);
}
if (prune)
{
labelList pointMap, faceMap;
meshedSurface filtered
(
surface_.subsetMesh(acceptFaces, pointMap, faceMap)
);
surface_.transfer(filtered);
meshCells_ = UIndirectList<label>(meshCells_, faceMap)();
}
}
void Foam::distanceSurface::filterFaceProximity()
{
const searchableSurface& geom = geometryPtr_();
// For face distances
const pointField& fc = surface_.faceCentres();
// For each face // For each face
scalarField faceDistance(fc.size(), GREAT); scalarField faceDistance(fc.size(), GREAT);
...@@ -356,38 +489,32 @@ void Foam::distanceSurface::filterByProximity() ...@@ -356,38 +489,32 @@ void Foam::distanceSurface::filterByProximity()
// Using the absolute proximity of the face centres is more robust. // Using the absolute proximity of the face centres is more robust.
bitSet acceptFaces(fc.size());
bool prune(false);
// Consider the absolute proximity of the face centres // Consider the absolute proximity of the face centres
forAll(faceDistance, facei) forAll(faceDistance, facei)
{ {
if (faceDistance[facei] <= absProximity_) if (absProximity_ < faceDistance[facei])
{ {
faceSelection.set(facei); prune = true;
}
else
{
++nTrimmed;
if (debug & 2) if (debug & 2)
{ {
Pout<< "trim reject: " Pout<< "trim reject: "
<< faceDistance[facei] << nl; << faceDistance[facei] << nl;
} }
} }
else
{
acceptFaces.set(facei);
}
} }
// Heavier debugging // Heavier debugging
if (debug & 4) if (debug & 4)
{ {
labelField faceFilterStatus(faceSelection.size(), Zero); const fileName outputName(surfaceName() + "-face-proximity-filter");
for (const label facei : faceSelection)
{
faceFilterStatus[facei] = 1;
}
const fileName outputName(surfaceName() + "-proximity-filter");
Info<< "Writing debug surface: " << outputName << nl; Info<< "Writing debug surface: " << outputName << nl;
...@@ -400,16 +527,22 @@ void Foam::distanceSurface::filterByProximity() ...@@ -400,16 +527,22 @@ void Foam::distanceSurface::filterByProximity()
writer.write("absolute-distance", faceDistance); writer.write("absolute-distance", faceDistance);
writer.write("normal-distance", faceNormalDistance); writer.write("normal-distance", faceNormalDistance);
writer.write("filter-state", faceFilterStatus);
// Region-wise filter state
labelField faceFilterState
(
ListOps::createWithValue<label>(surface_.size(), acceptFaces, 1, 0)
);
writer.write("filter-state", faceFilterState);
} }
if (returnReduce(nTrimmed, sumOp<label>()) != 0) if (prune)
{ {
labelList pointMap, faceMap; labelList pointMap, faceMap;
meshedSurface filtered meshedSurface filtered
( (
surface_.subsetMesh(faceSelection, pointMap, faceMap) surface_.subsetMesh(acceptFaces, pointMap, faceMap)
); );
surface_.transfer(filtered); surface_.transfer(filtered);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment