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 
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 
403  surfaceWriters::vtkWriter writer
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 
542  surfaceWriters::vtkWriter writer
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
Foam::labelList
List< label > labelList
A List of labels.
Definition: List.H:63
Foam::pointField
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
Foam::PackedList::reset
void reset()
Definition: PackedListI.H:498
Foam::scalarField
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Definition: primitiveFieldsFwd.H:46
Foam::faceMap
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
Definition: blockMeshMergeTopological.C:87
Foam::isoSurfaceBase
Low-level components common to various iso-surface algorithms.
Definition: isoSurfaceBase.H:62
Foam::fileName
A class for handling file names.
Definition: fileName.H:71
Foam::returnReduce
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
Definition: PstreamReduceOps.H:88
Foam::bitSet
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
Definition: bitSet.H:59
Foam::searchableSurface::getNormal
virtual void getNormal(const List< pointIndexHit > &, vectorField &normal) const =0
Foam::Zero
static constexpr const zero Zero
Definition: zero.H:131
Foam::xorEqOp
Definition: ops.H:81
Foam::distanceSurface::filterRegionProximity
void filterRegionProximity(bitSet &ignoreCells) const
Definition: distanceSurfaceFilter.C:304
Foam::isoSurfaceBase::getCellCutType
cutType getCellCutType(const label celli) const
Definition: isoSurfaceBase.C:241
Foam::writer::write
virtual void write(const coordSet &, const wordList &, const List< const Field< Type > * > &, Ostream &) const =0
Foam::bitSet::set
void set(const bitSet &bitset)
Definition: bitSetI.H:567
Foam::UPstream::master
static bool master(const label communicator=worldComm)
Definition: UPstream.H:453
Foam::Pstream::scatter
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Definition: gatherScatter.C:144
Foam::boolList
List< bool > boolList
A List of bools.
Definition: List.H:61
Foam::endl
Ostream & endl(Ostream &os)
Definition: Ostream.H:381
Foam::distanceSurface::filterKeepNearestRegions
void filterKeepNearestRegions(bitSet &ignoreCells) const
Definition: distanceSurfaceFilter.C:180
Foam::Pout
prefixOSstream Pout
Foam::bitSet::test
bool test(const label pos) const
Definition: bitSetI.H:513
syncTools.H
forAll
#define forAll(list, i)
Definition: stdFoam.H:349
Foam::vectorField
Field< vector > vectorField
Specialisation of Field<T> for vector.
Definition: primitiveFieldsFwd.H:48
Foam::magSqr
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
Foam::distanceSurface::refineBlockedCells
bool refineBlockedCells(bitSet &ignoreCells, const isoSurfaceBase &isoContext) const
Definition: distanceSurfaceFilter.C:30
Foam::diff
scalar diff(const triad &A, const triad &B)
Definition: triad.C:371
outputName
word outputName("finiteArea-edges.obj")
Foam::syncTools::syncFaceList
static void syncFaceList(const polyMesh &mesh, UList< T > &faceValues, const CombineOp &cop)
Definition: syncTools.H:392
Foam::primitiveMesh::nCells
label nCells() const noexcept
Definition: primitiveMeshI.H:89
Foam::surfaceWriters::vtkWriter
A surfaceWriter for VTK legacy (.vtk) or XML (.vtp) format.
Definition: vtkSurfaceWriter.H:126
Foam::MeshedSurface::transfer
void transfer(pointField &pointLst, List< Face > &faceLst)
Definition: MeshedSurface.C:1317
Foam::Field
Generic templated field type.
Definition: Field.H:59
Foam::labelField
Field< label > labelField
Specialisation of Field<T> for label.
Definition: labelField.H:48
Foam::Info
messageStream Info
Foam::polyPatch
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:64
Foam::searchableSurface
Base class of (analytical or triangulated) surface. Encapsulates all the search routines....
Definition: searchableSurface.H:65
regionSplit.H
Foam::minFirstEqOp
Definition: Tuple2.H:254
Foam::andOp
Definition: ops.H:227
Foam::distanceSurface::filterFaceProximity
void filterFaceProximity()
Definition: distanceSurfaceFilter.C:460
Foam::regionSplit
This class separates the mesh into distinct unconnected regions, each of which is then given a label ...
Definition: regionSplit.H:136
Foam::regionSplit::nRegions
label nRegions() const
Definition: regionSplit.H:290
Foam::distanceSurface::surfaceName
const word & surfaceName() const
Definition: distanceSurface.H:405
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:77
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)
Definition: FlatOutput.H:217
Foam::vector
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:47
Foam::bitSet::none
bool none() const
Definition: bitSetI.H:480
Foam::Pstream::listCombineGather
static void listCombineGather(const List< commsStruct > &comms, List< T > &Value, const CombineOp &cop, const int tag, const label comm)
Definition: combineGatherScatter.C:284
Foam::nl
constexpr char nl
Definition: Ostream.H:424
Foam::meshedSurface
MeshedSurface< face > meshedSurface
Definition: MeshedSurfacesFwd.H:34
Foam::foamVersion::patch
const std::string patch
Foam::Vector< scalar >
Foam::findMax
label findMax(const ListType &input, label start=0)
vtkSurfaceWriter.H
Foam::List
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:58
Foam::sqrt
dimensionedScalar sqrt(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:137
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:320
Foam::List::set
std::enable_if< std::is_same< bool, TypeT >::value, bool >::type set(const label i, bool val=true)
Definition: List.H:337
Foam::PackedList::clearStorage
void clearStorage()
Definition: PackedListI.H:513
Foam::distanceSurface::filterKeepLargestRegion
void filterKeepLargestRegion(bitSet &ignoreCells) const
Definition: distanceSurfaceFilter.C:113
Foam::UIndirectList
A List with indirect addressing.
Definition: faMatrix.H:56
ListOps.H
Various functions to operate on Lists.
Foam::plusEqOp
Definition: ops.H:66
Foam::distanceSurface::filterPrepareRegionSplit
bitSet filterPrepareRegionSplit(const bitSet &ignoreCells) const
Definition: distanceSurfaceFilter.C:62
Foam::Tuple2
A 2-tuple for storing two objects of dissimilar types. The container is similar in purpose to std::pa...
Definition: stringOps.H:56
Foam::Pstream::listCombineScatter
static void listCombineScatter(const List< commsStruct > &comms, List< T > &Value, const int tag, const label comm)
Definition: combineGatherScatter.C:426
Foam::point
vector point
Point is a vector.
Definition: point.H:37
Foam::MeshedSurface
A surface geometry mesh with zone information, not to be confused with the similarly named surfaceMes...
Definition: triSurfaceTools.H:76
writer
vtk::internalMeshWriter writer(topoMesh, topoCells, vtk::formatType::INLINE_ASCII, runTime.path()/"blockTopology")
Foam::MeshedSurface::size
label size() const
Definition: MeshedSurface.H:403
WarningInFunction
#define WarningInFunction
Definition: messageStream.H:353
Foam::MeshedSurface::subsetMesh
MeshedSurface subsetMesh(const UList< bool > &include, labelList &pointMap, labelList &faceMap) const
Definition: MeshedSurface.C:1218
Foam::isoSurfaceBase::ANYCUT
@ ANYCUT
Any cut type (bitmask)
Definition: isoSurfaceBase.H:78