distanceSurfaceFilter.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2020-2021 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "distanceSurface.H"
29 #include "regionSplit.H"
30 #include "syncTools.H"
31 #include "ListOps.H"
32 #include "vtkSurfaceWriter.H"
33 
34 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
35 
37 (
38  bitSet& ignoreCells,
39  const isoSurfaceBase& isoCutter
40 ) const
41 {
42  // With the cell/point distance fields we can take a second pass at
43  // pre-filtering.
44  // This duplicates how cut detection is determined in the cell/topo
45  // algorithms but is fairly inexpensive (creates no geometry)
46 
47  bool changed = false;
48 
49  for (label celli = 0; celli < mesh_.nCells(); ++celli)
50  {
51  if (ignoreCells.test(celli))
52  {
53  continue;
54  }
55 
56  auto cut = isoCutter.getCellCutType(celli);
57  if (!(cut & isoSurfaceBase::ANYCUT))
58  {
59  ignoreCells.set(celli);
60  changed = true;
61  }
62  }
63 
64  return changed;
65 }
66 
67 
69 (
70  const bitSet& ignoreCells
71 ) const
72 {
73  // Prepare for region split
74 
75  bitSet blockedFaces(mesh_.nFaces());
76 
77  const labelList& faceOwn = mesh_.faceOwner();
78  const labelList& faceNei = mesh_.faceNeighbour();
79 
80  // Could be more efficient
81  for (label facei = 0; facei < mesh_.nInternalFaces(); ++facei)
82  {
83  // If only one cell is blocked, the face corresponds
84  // to an exposed subMesh face
85 
86  if
87  (
88  ignoreCells.test(faceOwn[facei])
89  != ignoreCells.test(faceNei[facei])
90  )
91  {
92  blockedFaces.set(facei);
93  }
94  }
95 
96  for (const polyPatch& patch : mesh_.boundaryMesh())
97  {
98  if (!patch.coupled())
99  {
100  continue;
101  }
102 
103  forAll(patch, patchFacei)
104  {
105  const label facei = patch.start() + patchFacei;
106  if (ignoreCells.test(faceOwn[facei]))
107  {
108  blockedFaces.set(facei);
109  }
110  }
111  }
112 
113  syncTools::syncFaceList(mesh_, blockedFaces, xorEqOp<unsigned int>());
114 
115  return blockedFaces;
116 }
117 
118 
120 (
121  bitSet& ignoreCells
122 ) const
123 {
124  // For region split
125  bitSet blockedFaces(filterPrepareRegionSplit(ignoreCells));
126 
127  // Split region
128  regionSplit rs(mesh_, blockedFaces);
129  blockedFaces.clearStorage();
130 
131  const labelList& regionColour = rs;
132 
133  // Identical number of regions on all processors
134  labelList nCutsPerRegion(rs.nRegions(), Zero);
135 
136  // Count cut cells (ie, unblocked)
137  forAll(regionColour, celli)
138  {
139  if (!ignoreCells.test(celli))
140  {
141  ++nCutsPerRegion[regionColour[celli]];
142  }
143  }
144 
145  // Sum totals from all processors
146  Pstream::listCombineGather(nCutsPerRegion, plusEqOp<label>());
147 
148 
149  // Define which regions to keep
150  boolList keepRegion(rs.nRegions(), false);
151 
152  if (Pstream::master())
153  {
154  const label largest = findMax(nCutsPerRegion);
155 
156  if (largest == -1)
157  {
158  // Should not happen
159  keepRegion = true;
160  }
161  else
162  {
163  keepRegion[largest] = true;
164  }
165 
166  if (debug)
167  {
168  Info<< "Had " << sum(nCutsPerRegion) << " cuts, in "
169  << nCutsPerRegion.size() << " regions, largest is "
170  << largest << ": " << flatOutput(nCutsPerRegion) << nl;
171  }
172  }
173 
174  Pstream::scatter(keepRegion);
175 
176  forAll(regionColour, celli)
177  {
178  if (!keepRegion.test(regionColour[celli]))
179  {
180  ignoreCells.set(celli);
181  }
182  }
183 }
184 
185 
187 (
188  bitSet& ignoreCells
189 ) const
190 {
191  if (nearestPoints_.empty())
192  {
194  << "Ignoring nearestPoints - no points provided" << nl
195  << endl;
196  return;
197  }
198 
199  // For region split
200  bitSet blockedFaces(filterPrepareRegionSplit(ignoreCells));
201 
202  // Split region
203  regionSplit rs(mesh_, blockedFaces);
204  blockedFaces.clearStorage();
205 
206  const labelList& regionColour = rs;
207 
208  const pointField& cc = mesh_.cellCentres();
209  const pointField& nearPts = nearestPoints_;
210 
211  // The magSqr distance and region
212  typedef Tuple2<scalar, label> nearInfo;
213  List<nearInfo> nearest(nearPts.size(), nearInfo(GREAT, -1));
214 
215  // Also collect cuts per region, may be useful for rejecting
216  // small regions. Code as per filterKeepLargestRegion
217  labelList nCutsPerRegion(rs.nRegions(), Zero);
218 
219  forAll(cc, celli)
220  {
221  if (ignoreCells.test(celli))
222  {
223  continue;
224  }
225 
226  const point& pt = cc[celli];
227  const label regioni = regionColour[celli];
228 
229  ++nCutsPerRegion[regioni];
230 
231  label pointi = 0;
232  for (nearInfo& near : nearest)
233  {
234  const scalar distSqr = magSqr(nearPts[pointi] - pt);
235  ++pointi;
236 
237  if (distSqr < near.first())
238  {
239  near.first() = distSqr;
240  near.second() = regioni;
241  }
242  }
243  }
244 
245  // Sum totals from all processors
246  Pstream::listCombineGather(nCutsPerRegion, plusEqOp<label>());
247 
248  // Get nearest
249  Pstream::listCombineGather(nearest, minFirstEqOp<scalar>());
250 
251 
252  // Define which regions to keep
253 
254  boolList keepRegion(rs.nRegions(), false);
255 
256  if (Pstream::master())
257  {
258  const label largest = findMax(nCutsPerRegion);
259 
260  for (const nearInfo& near : nearest)
261  {
262  const scalar distSqr = near.first();
263  const label regioni = near.second();
264 
265  if (regioni != -1 && distSqr < maxDistanceSqr_)
266  {
267  keepRegion[regioni] = true;
268  }
269  }
270 
271  if (debug)
272  {
273  Info<< "Had " << sum(nCutsPerRegion) << " cuts, in "
274  << nCutsPerRegion.size() << " regions, largest is "
275  << largest << ": " << flatOutput(nCutsPerRegion) << nl;
276 
277  Info<< "nearestPoints (max distance = "
278  << sqrt(maxDistanceSqr_) << ")" << nl;
279 
280  forAll(nearPts, pointi)
281  {
282  const scalar dist = sqrt(nearest[pointi].first());
283  const label regioni = nearest[pointi].second();
284 
285  Info<< " " << nearPts[pointi] << " region "
286  << regioni << " distance "
287  << dist;
288 
289  if (!keepRegion.test(regioni))
290  {
291  Info<< " too far";
292  }
293  Info<< nl;
294  }
295  }
296  }
297 
298  Pstream::scatter(keepRegion);
299 
300  forAll(regionColour, celli)
301  {
302  if (!keepRegion.test(regionColour[celli]))
303  {
304  ignoreCells.set(celli);
305  }
306  }
307 }
308 
309 
311 (
312  bitSet& ignoreCells
313 ) const
314 {
315  const searchableSurface& geom = geometryPtr_();
316 
317  // For face distances
318  const pointField& fc = surface_.faceCentres();
319 
320  // For region split
321  bitSet blockedFaces(filterPrepareRegionSplit(ignoreCells));
322 
323  // Split region
324  regionSplit rs(mesh_, blockedFaces);
325  blockedFaces.clearStorage();
326 
327  const labelList& regionColour = rs;
328 
329  // For each face
330  scalarField faceDistance(fc.size(), GREAT);
331 
332  {
333  List<pointIndexHit> nearest;
334  geom.findNearest
335  (
336  fc,
337  // Use initialized field (GREAT) to limit search too
338  faceDistance,
339  nearest
340  );
341  calcAbsoluteDistance(faceDistance, fc, nearest);
342  }
343 
344  // Identical number of regions on all processors
345  scalarField areaRegion(rs.nRegions(), Zero);
346  scalarField distRegion(rs.nRegions(), Zero);
347 
348  forAll(meshCells_, facei)
349  {
350  const label celli = meshCells_[facei];
351  const label regioni = regionColour[celli];
352 
353  const scalar faceArea = surface_[facei].mag(surface_.points());
354  distRegion[regioni] += (faceDistance[facei] * faceArea);
355  areaRegion[regioni] += (faceArea);
356  }
357 
358  Pstream::listCombineGather(distRegion, plusEqOp<scalar>());
359  Pstream::listCombineGather(areaRegion, plusEqOp<scalar>());
360 
361  if (Pstream::master())
362  {
363  forAll(distRegion, regioni)
364  {
365  distRegion[regioni] /= (areaRegion[regioni] + VSMALL);
366  }
367  }
368 
369  Pstream::listCombineScatter(distRegion);
370 
371 
372  // Define the per-face acceptance based on the region average distance
373 
374  bitSet acceptFaces(fc.size());
375  bool prune(false);
376 
377  forAll(meshCells_, facei)
378  {
379  const label celli = meshCells_[facei];
380  const label regioni = regionColour[celli];
381 
382  // NB: do not filter by individual faces as well since this
383  // has been reported to cause minor holes for surfaces with
384  // high curvature! (2021-06-10)
385 
386  if (absProximity_ < distRegion[regioni])
387  {
388  prune = true;
389  }
390  else
391  {
392  acceptFaces.set(facei);
393  }
394  }
395 
396  // Heavier debugging
397  if (debug & 4)
398  {
399  const fileName outputName(surfaceName() + "-region-proximity-filter");
400 
401  Info<< "Writing debug surface: " << outputName << nl;
402 
404  (
405  surface_.points(),
406  surface_, // faces
407  outputName
408  );
409 
410  writer.write("absolute-distance", faceDistance);
411 
412  // Region segmentation
413  labelField faceRegion
414  (
415  ListOps::create<label>
416  (
417  meshCells_,
418  [&](const label celli){ return regionColour[celli]; }
419  )
420  );
421  writer.write("face-region", faceRegion);
422 
423  // Region-wise filter state
424  labelField faceFilterState
425  (
426  ListOps::createWithValue<label>(surface_.size(), acceptFaces, 1, 0)
427  );
428  writer.write("filter-state", faceFilterState);
429  }
430 
431 
432  // If filtering with region proximity results in zero faces,
433  // revert to face-only proximity filter
434  if (returnReduce(acceptFaces.none(), andOp<bool>()))
435  {
436  acceptFaces.reset();
437  prune = false;
438 
439  // Consider the absolute proximity of the face centres
440  forAll(faceDistance, facei)
441  {
442  if (absProximity_ < faceDistance[facei])
443  {
444  prune = true;
445  }
446  else
447  {
448  acceptFaces.set(facei);
449  }
450  }
451  }
452 
453  if (prune)
454  {
455  labelList pointMap, faceMap;
456  meshedSurface filtered
457  (
458  surface_.subsetMesh(acceptFaces, pointMap, faceMap)
459  );
460  surface_.transfer(filtered);
461 
462  meshCells_ = UIndirectList<label>(meshCells_, faceMap)();
463  }
464 }
465 
466 
468 {
469  const searchableSurface& geom = geometryPtr_();
470 
471  // For face distances
472  const pointField& fc = surface_.faceCentres();
473 
474  // For each face
475  scalarField faceDistance(fc.size(), GREAT);
476  scalarField faceNormalDistance; // Debugging only
477  {
478  List<pointIndexHit> nearest;
479  geom.findNearest
480  (
481  fc,
482  // Use initialized field (GREAT) to limit search too
483  faceDistance,
484  nearest
485  );
486  calcAbsoluteDistance(faceDistance, fc, nearest);
487 
488  // Heavier debugging
489  if (debug & 4)
490  {
491  vectorField norms;
492  geom.getNormal(nearest, norms);
493 
494  faceNormalDistance.resize(fc.size());
495 
496  forAll(nearest, i)
497  {
498  const vector diff(fc[i] - nearest[i].point());
499 
500  faceNormalDistance[i] = Foam::mag((diff & norms[i]));
501  }
502  }
503  }
504 
505  // Note
506  // Proximity checks using the face points (nearest hit) to establish
507  // a length scale are too fragile. Can easily have stretched faces
508  // where the centre is less than say 0.3-0.5 of the centre-point distance
509  // but they are still outside.
510 
511  // Using the absolute proximity of the face centres is more robust.
512 
513  bitSet acceptFaces(fc.size());
514  bool prune(false);
515 
516  // Consider the absolute proximity of the face centres
517  forAll(faceDistance, facei)
518  {
519  if (absProximity_ < faceDistance[facei])
520  {
521  prune = true;
522  if (debug & 2)
523  {
524  Pout<< "trim reject: "
525  << faceDistance[facei] << nl;
526  }
527  }
528  else
529  {
530  acceptFaces.set(facei);
531  }
532  }
533 
534 
535  // Heavier debugging
536  if (debug & 4)
537  {
538  const fileName outputName(surfaceName() + "-face-proximity-filter");
539 
540  Info<< "Writing debug surface: " << outputName << nl;
541 
543  (
544  surface_.points(),
545  surface_, // faces
546  outputName
547  );
548 
549  writer.write("absolute-distance", faceDistance);
550  writer.write("normal-distance", faceNormalDistance);
551 
552  // Region-wise filter state
553  labelField faceFilterState
554  (
555  ListOps::createWithValue<label>(surface_.size(), acceptFaces, 1, 0)
556  );
557  writer.write("filter-state", faceFilterState);
558  }
559 
560 
561  if (prune)
562  {
563  labelList pointMap, faceMap;
564  meshedSurface filtered
565  (
566  surface_.subsetMesh(acceptFaces, pointMap, faceMap)
567  );
568  surface_.transfer(filtered);
569 
570  meshCells_ = UIndirectList<label>(meshCells_, faceMap)();
571  }
572 }
573 
574 
575 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::faceMap
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
Definition: blockMeshMergeTopological.C:94
Foam::isoSurfaceBase
Low-level components common to various iso-surface algorithms.
Definition: isoSurfaceBase.H:66
Foam::fileName
A class for handling file names.
Definition: fileName.H:73
Foam::returnReduce
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
Definition: PstreamReduceOps.H:94
Foam::bitSet
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
Definition: bitSet.H:63
Foam::searchableSurface::getNormal
virtual void getNormal(const List< pointIndexHit > &, vectorField &normal) const =0
From a set of points and indices get the normal.
Foam::Zero
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
Foam::xorEqOp
Definition: ops.H:87
Foam::distanceSurface::filterRegionProximity
void filterRegionProximity(bitSet &ignoreCells) const
Remove region(s) that have far faces.
Definition: distanceSurfaceFilter.C:311
Foam::isoSurfaceBase::getCellCutType
cutType getCellCutType(const label celli) const
Definition: isoSurfaceBase.C:248
Foam::writer::write
virtual void write(const coordSet &, const wordList &, const List< const Field< Type > * > &, Ostream &) const =0
General entry point for writing.
Foam::bitSet::set
void set(const bitSet &bitset)
Set specified bits from another bitset.
Definition: bitSetI.H:574
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::distanceSurface::filterKeepNearestRegions
void filterKeepNearestRegions(bitSet &ignoreCells) const
Keep region(s) closest to the nearest points.
Definition: distanceSurfaceFilter.C:187
Foam::Pout
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Foam::bitSet::test
bool test(const label pos) const
Test value at specified position, never auto-vivify entries.
Definition: bitSetI.H:520
syncTools.H
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
Foam::magSqr
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
Foam::distanceSurface::refineBlockedCells
bool refineBlockedCells(bitSet &ignoreCells, const isoSurfaceBase &isoContext) const
Re-filter the blocked cells based on the anticipated cuts.
Definition: distanceSurfaceFilter.C:37
Foam::diff
scalar diff(const triad &A, const triad &B)
Return a quantity of the difference between two triads.
Definition: triad.C:378
outputName
word outputName("finiteArea-edges.obj")
Foam::surfaceWriters::vtkWriter
A surfaceWriter for VTK legacy (.vtk) or XML (.vtp) format.
Definition: vtkSurfaceWriter.H:130
Foam::MeshedSurface::transfer
void transfer(pointField &pointLst, List< Face > &faceLst)
Transfer the components.
Definition: MeshedSurface.C:1324
Foam::Field< vector >
Foam::Info
messageStream Info
Information stream (stdout output on master, null elsewhere)
Foam::polyPatch
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:68
Foam::searchableSurface
Base class of (analytical or triangulated) surface. Encapsulates all the search routines....
Definition: searchableSurface.H:69
regionSplit.H
Foam::minFirstEqOp
Assign tuple-like container to use the one with the smaller value of first.
Definition: Tuple2.H:249
Foam::andOp
Definition: ops.H:233
Foam::distanceSurface::filterFaceProximity
void filterFaceProximity()
Adjust extracted iso-surface to remove far faces.
Definition: distanceSurfaceFilter.C:467
Foam::regionSplit
This class separates the mesh into distinct unconnected regions, each of which is then given a label ...
Definition: regionSplit.H:140
Foam::regionSplit::nRegions
label nRegions() const
Return total number of regions.
Definition: regionSplit.H:294
Foam::distanceSurface::surfaceName
const word & surfaceName() const
The name of the underlying searchableSurface.
Definition: distanceSurface.H:409
cut
Patchify triangles based on orientation w.r.t other (triangulated or triangulatable) surfaces.
Foam::writer
Base class for graphics format writing. Entry points are.
Definition: writer.H:81
Foam::searchableSurface::findNearest
virtual void findNearest(const pointField &sample, const scalarField &nearestDistSqr, List< pointIndexHit > &) const =0
distanceSurface.H
Foam::flatOutput
FlatOutput::OutputAdaptor< Container, Delimiters > flatOutput(const Container &obj, Delimiters delim)
Global flatOutput() function with specified output delimiters.
Definition: FlatOutput.H:216
Foam::nl
constexpr char nl
Definition: Ostream.H:404
Foam::foamVersion::patch
const std::string patch
OpenFOAM patch number as a std::string.
Foam::Vector< scalar >
Foam::findMax
label findMax(const ListType &input, label start=0)
vtkSurfaceWriter.H
Foam::List< label >
Foam::sqrt
dimensionedScalar sqrt(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:144
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
Foam::sum
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
Definition: DimensionedFieldFunctions.C:327
Foam::List::set
std::enable_if< std::is_same< bool, TypeT >::value, bool >::type set(const label i, bool val=true)
A bitSet::set() method for a list of bool.
Definition: List.H:341
Foam::PackedList::clearStorage
void clearStorage()
Clear the list and delete storage.
Definition: PackedListI.H:520
Foam::distanceSurface::filterKeepLargestRegion
void filterKeepLargestRegion(bitSet &ignoreCells) const
Keep region with the most cuts (after region split)
Definition: distanceSurfaceFilter.C:120
Foam::UIndirectList
A List with indirect addressing.
Definition: faMatrix.H:60
ListOps.H
Various functions to operate on Lists.
Foam::plusEqOp
Definition: ops.H:72
Foam::distanceSurface::filterPrepareRegionSplit
bitSet filterPrepareRegionSplit(const bitSet &ignoreCells) const
Prepare blockedFaces for region split.
Definition: distanceSurfaceFilter.C:69
Foam::Tuple2
A 2-tuple for storing two objects of dissimilar types. The container is similar in purpose to std::pa...
Definition: stringOps.H:60
Foam::point
vector point
Point is a vector.
Definition: point.H:43
Foam::MeshedSurface< face >
writer
vtk::internalMeshWriter writer(topoMesh, topoCells, vtk::formatType::INLINE_ASCII, runTime.path()/"blockTopology")
Foam::MeshedSurface::size
label size() const
The surface size is the number of faces.
Definition: MeshedSurface.H:407
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:328
Foam::MeshedSurface::subsetMesh
MeshedSurface subsetMesh(const UList< bool > &include, labelList &pointMap, labelList &faceMap) const
Return a new surface subsetted on the selected faces.
Definition: MeshedSurface.C:1225