snappyVoxelMeshDriver.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) 2018-2022 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 "snappyVoxelMeshDriver.H"
29 #include "meshRefinement.H"
30 #include "fvMesh.H"
31 #include "Time.H"
32 #include "refinementParameters.H"
33 #include "refinementSurfaces.H"
34 #include "refinementFeatures.H"
35 #include "shellSurfaces.H"
36 #include "searchableSurfaces.H"
37 #include "voxelMeshSearch.H"
38 #include "IOmanip.H"
39 
40 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
41 
42 namespace Foam
43 {
44  defineTypeNameAndDebug(snappyVoxelMeshDriver, 0);
45 } // End namespace Foam
46 
47 
48 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
49 
50 void Foam::snappyVoxelMeshDriver::addNeighbours
51 (
52  const labelList& cellLevel,
53  const labelVector& voxel,
54  const label voxeli,
55  DynamicList<labelVector>& front
56 ) const
57 {
58  const labelVector off(voxelMeshSearch::offset(n_));
59 
60  for (direction dir = 0; dir < 3; ++dir)
61  {
62  if (voxel[dir] > 0)
63  {
64  labelVector left(voxel);
65  left[dir] -= 1;
66  if (cellLevel[voxeli-off[dir]] == -1)
67  {
68  front.append(left);
69  }
70  }
71  if (voxel[dir] < n_[dir]-1)
72  {
73  labelVector right(voxel);
74  right[dir] += 1;
75  if (cellLevel[voxeli+off[dir]] == -1)
76  {
77  front.append(right);
78  }
79  }
80  }
81 }
82 
83 
84 // Insert cell level for all volume refinement
85 Foam::tmp<Foam::pointField> Foam::snappyVoxelMeshDriver::voxelCentres() const
86 {
87  tmp<pointField> tcc(tmp<pointField>::New(n_.x()*n_.y()*n_.z()));
88  pointField& cc = tcc.ref();
89 
90  const labelVector off(voxelMeshSearch::offset(n_));
91  label voxeli = voxelMeshSearch::index(n_, labelVector(0, 0, 0));
92  for (label k = 0; k < n_[2]; k++)
93  {
94  const label start1 = voxeli;
95  for (label j = 0; j < n_[1]; j++)
96  {
97  const label start0 = voxeli;
98  for (label i = 0; i < n_[0]; i++)
99  {
100  const labelVector voxel(i, j, k);
101  cc[voxeli] = voxelMeshSearch::centre(bb_, n_, voxel);
102  voxeli += off[0];
103  }
104  voxeli = start0 + off[1];
105  }
106  voxeli = start1 + off[2];
107  }
108  return tcc;
109 }
110 
111 
112 void Foam::snappyVoxelMeshDriver::isInside
113 (
114  const pointField& cc,
115  boolList& isVoxelInMesh
116 ) const
117 {
118  const fvMesh& mesh = meshRefiner_.mesh();
119 
120  isVoxelInMesh.setSize(cc.size());
121  if (isVoxelInMesh.size() < mesh.globalData().nTotalCells())
122  {
123  forAll(cc, voxeli)
124  {
125  const label celli = mesh.findCell
126  (
127  cc[voxeli],
129  );
130  isVoxelInMesh[voxeli] = (celli != -1);
131  }
132  Pstream::listCombineGather(isVoxelInMesh, orEqOp<bool>());
133  }
134  else
135  {
136  for (label celli = 0; celli < mesh.nCells(); ++celli)
137  {
138  const boundBox cellBb(mesh.cellBb(celli));
139 
141  (
142  isVoxelInMesh,
143  bb_,
144  n_,
145  cellBb,
146  1,
147  orEqOp<bool>()
148  );
149  }
150  Pstream::listCombineGather(isVoxelInMesh, orEqOp<bool>());
151  }
152 }
153 
154 
155 void Foam::snappyVoxelMeshDriver::markSurfaceRefinement
156 (
157  labelList& voxelLevel,
158  labelList& globalRegion
159 ) const
160 {
161  // Insert cell level for all refinementSurfaces
162 
163  const refinementSurfaces& s = meshRefiner_.surfaces();
164  forAll(s.surfaces(), surfi)
165  {
166  label geomi = s.surfaces()[surfi];
167  const searchableSurface& geom = s.geometry()[geomi];
168  //Pout<< "Geometry:" << s.names()[surfi] << endl;
169  if (isA<triSurface>(geom))
170  {
171  const triSurface& ts = refCast<const triSurface>(geom);
172  const pointField& points = ts.points();
173 
174  for (const labelledTri& tri : ts)
175  {
176  label regioni = tri.region();
177  label globalRegioni = s.regionOffset()[surfi]+regioni;
178  const boundBox triBb(tri.box(points));
179 
180  // Fill cellLevel
181  label level = s.minLevel()[globalRegioni];
183  (
184  voxelLevel,
185  bb_,
186  n_,
187  triBb,
188  level,
189  maxEqOp<label>()
190  );
192  (
193  globalRegion,
194  bb_,
195  n_,
196  triBb,
197  globalRegioni,
198  maxEqOp<label>()
199  );
200  }
201  }
202  // else: maybe do intersection tests?
203  }
204 }
205 
206 
207 void Foam::snappyVoxelMeshDriver::findVoxels
208 (
209  const labelList& voxelLevel,
210  const pointField& locationsOutsideMesh,
211  labelList& voxels
212 ) const
213 {
214  voxels.setSize(locationsOutsideMesh.size());
215  voxels = -1;
216  forAll(locationsOutsideMesh, loci)
217  {
218  const point& pt = locationsOutsideMesh[loci];
219  label voxeli = voxelMeshSearch::index(bb_, n_, pt, false);
220 
221  if (voxeli == -1 || voxelLevel[voxeli] == labelMax)
222  {
223  WarningInFunction << "Location outside mesh "
224  << pt << " is outside mesh with bounding box "
225  << bb_ << endl;
226  }
227  else
228  {
229  voxels[loci] = voxeli;
230  }
231  }
232 }
233 
234 
235 void Foam::snappyVoxelMeshDriver::floodFill
236 (
237  const label startVoxeli,
238  const label newLevel,
239  labelList& voxelLevel
240 ) const
241 {
242  DynamicList<labelVector> front;
243  front.append(voxelMeshSearch::index3(n_, startVoxeli));
244 
245  DynamicList<labelVector> newFront;
246  while (true)
247  {
248  newFront.clear();
249  for (const auto& voxel : front)
250  {
251  label voxeli = voxelMeshSearch::index(n_, voxel);
252  if (voxelLevel[voxeli] == -1)
253  {
254  voxelLevel[voxeli] = 0;
255  addNeighbours
256  (
257  voxelLevel,
258  voxel,
259  voxeli,
260  newFront
261  );
262  }
263  }
264 
265  if (newFront.empty())
266  {
267  break;
268  }
269  front.transfer(newFront);
270  }
271 }
272 
273 
274 void Foam::snappyVoxelMeshDriver::max
275 (
276  const labelList& maxLevel,
277  labelList& voxelLevel
278 ) const
279 {
280  // Mark voxels with level
281  const labelVector off(voxelMeshSearch::offset(n_));
282 
283  label voxeli = voxelMeshSearch::index(n_, labelVector(0, 0, 0));
284  for (label k = 0; k < n_[2]; k++)
285  {
286  const label start1 = voxeli;
287  for (label j = 0; j < n_[1]; j++)
288  {
289  const label start0 = voxeli;
290  for (label i = 0; i < n_[0]; i++)
291  {
292  voxelLevel[voxeli] = Foam::max
293  (
294  voxelLevel[voxeli],
295  maxLevel[voxeli]
296  );
297  voxeli += off[0];
298  }
299  voxeli = start0 + off[1];
300  }
301  voxeli = start1 + off[2];
302  }
303 }
304 
305 
306 Foam::labelList Foam::snappyVoxelMeshDriver::count
307 (
308  const labelList& voxelLevel
309 ) const
310 {
311 
312  label maxLevel = 0;
313  for (const auto level : voxelLevel)
314  {
315  if (level != labelMax)
316  {
317  maxLevel = Foam::max(maxLevel, level);
318  }
319  }
320  labelList count(maxLevel+1, 0);
321 
322  const labelVector off(voxelMeshSearch::offset(n_));
323 
324  label voxeli = voxelMeshSearch::index(n_, labelVector(0, 0, 0));
325  for (label k = 0; k < n_[2]; k++)
326  {
327  const label start1 = voxeli;
328  for (label j = 0; j < n_[1]; j++)
329  {
330  const label start0 = voxeli;
331  for (label i = 0; i < n_[0]; i++)
332  {
333  label level = voxelLevel[voxeli];
334 
335  if (level != -1 && level != labelMax)
336  {
337  ++count[level];
338  }
339  voxeli += off[0];
340  }
341  voxeli = start0 + off[1];
342  }
343  voxeli = start1 + off[2];
344  }
345 
346  return count;
347 }
348 
349 
350 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
351 
352 Foam::snappyVoxelMeshDriver::snappyVoxelMeshDriver
353 (
354  meshRefinement& meshRefiner,
355  const labelUList& globalToMasterPatch,
356  const labelUList& globalToSlavePatch
357 )
358 :
359  meshRefiner_(meshRefiner),
360  globalToMasterPatch_(globalToMasterPatch),
361  globalToSlavePatch_(globalToSlavePatch),
362  bb_(meshRefiner_.mesh().bounds())
363 {
364  label maxLevel = labelMin;
365 
366  // Feature refinement
367  const labelListList& featLevels = meshRefiner_.features().levels();
368  forAll(featLevels, feati)
369  {
370  maxLevel = Foam::max(maxLevel, Foam::max(featLevels[feati]));
371  }
372 
373  // Surface refinement
374  const labelList& surfaceLevels = meshRefiner_.surfaces().maxLevel();
375  maxLevel = Foam::max(maxLevel, Foam::max(surfaceLevels));
376 
377  // Shell refinement
378  maxLevel = Foam::max(maxLevel, meshRefiner_.shells().maxLevel());
379 
380  const scalar level0Len = meshRefiner_.meshCutter().level0EdgeLength();
381 
382  const int oldWidth = Sout.width();
383 
384  Info<< nl
385  << "Cell size estimate :" << nl
386  << " Level "
387  << setw(2) << label(0) << setw(oldWidth)
388  << " : " << level0Len << nl
389  << " Level "
390  << setw(2) << maxLevel << setw(oldWidth)
391  << " : " << level0Len/pow(2.0, maxLevel) << nl
392  << endl;
393 
394 
395  // Define voxel mesh with similar dimensions as mesh
396  const vector meshSpan(bb_.span());
397  n_ = labelVector
398  (
399  round(meshSpan.x()/level0Len),
400  round(meshSpan.y()/level0Len),
401  round(meshSpan.z()/level0Len)
402  );
403  label nTot = n_.x()*n_.y()*n_.z();
404  while (nTot < 1000000) //1048576)
405  {
406  n_ *= 2;
407  nTot = n_.x()*n_.y()*n_.z();
408  }
409 
410  Info<< "Voxellating initial mesh : " << n_ << nl << endl;
411 
412  tmp<pointField> tcc(voxelCentres());
413  const pointField& cc = tcc();
414 
415  Info<< "Voxel refinement :" << nl
416  << " Initial : (" << nTot << ')' << endl;
417 
418  boolList isVoxelInMesh;
419  isInside(cc, isVoxelInMesh);
420 
421  if (Pstream::master())
422  {
423  voxelLevel_.setSize(nTot, -1);
424  globalRegion_.setSize(nTot, -1);
425 
426  // Remove cells outside initial mesh
427  forAll(isVoxelInMesh, voxeli)
428  {
429  if (!isVoxelInMesh[voxeli])
430  {
431  voxelLevel_[voxeli] = labelMax;
432  globalRegion_[voxeli] = -1;
433  }
434  }
435 
436  //if (debug)
437  {
438  Info<< " After removing outside cells : " << count(voxelLevel_)
439  << endl;
440  }
441  }
442 }
443 
444 
445 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
446 
448 (
449  const refinementParameters& refineParams
450 )
451 {
452  const scalar level0Len = meshRefiner_.meshCutter().level0EdgeLength();
453 
454  tmp<pointField> tcc(voxelCentres());
455  const pointField& cc = tcc();
456 
457  boolList isVoxelInMesh;
458  isInside(cc, isVoxelInMesh);
459 
460  if (Pstream::master())
461  {
462  // Mark voxels containing (parts of) triangles
463  markSurfaceRefinement(voxelLevel_, globalRegion_);
464 
465  //if (debug)
466  {
467  Info<< " After surface refinement : " << count(voxelLevel_)
468  << endl;
469  }
470 
471 
472  // Find outside locations (and their current refinement level)
473  const pointField& outsidePoints = refineParams.locationsOutsideMesh();
474  labelList outsideMeshVoxels;
475  findVoxels
476  (
477  voxelLevel_,
478  outsidePoints,
479  outsideMeshVoxels
480  );
481  labelList outsideOldLevel(outsideMeshVoxels.size(), -1);
482  forAll(outsideMeshVoxels, loci)
483  {
484  label voxeli = outsideMeshVoxels[loci];
485  if (voxeli >= 0)
486  {
487  outsideOldLevel[loci] = voxelLevel_[outsideMeshVoxels[loci]];
488  if (outsideOldLevel[loci] >= 0)
489  {
490  WarningInFunction << "Location outside mesh "
491  << outsidePoints[loci]
492  << " is inside mesh or close to surface" << endl;
493  }
494  }
495  }
496 
497 
498  // Find inside locations
499  labelList insideMeshVoxels;
500  findVoxels
501  (
502  voxelLevel_,
503  refineParams.locationsInMesh(),
504  insideMeshVoxels
505  );
506 
507  forAll(insideMeshVoxels, loci)
508  {
509  label voxeli = insideMeshVoxels[loci];
510  if (voxeli != -1)
511  {
512  if (voxelLevel_[voxeli] != -1)
513  {
514  WarningInFunction << "Location inside mesh "
515  << refineParams.locationsInMesh()[loci]
516  << " is marked as a surface voxel " << voxeli
517  << " with cell level " << voxelLevel_[voxeli] << endl;
518  }
519  else
520  {
521  // Flood-fill out from voxel
522  floodFill(voxeli, 0, voxelLevel_);
523  }
524  }
525  }
526 
527  //if (debug)
528  {
529  Info<< " After keeping inside voxels : " << count(voxelLevel_)
530  << endl;
531  }
532 
533 
534  // Re-check the outside locations to see if they have been bled into
535  {
536  forAll(outsideMeshVoxels, loci)
537  {
538  label voxeli = outsideMeshVoxels[loci];
539  if (voxeli >= 0 && voxelLevel_[voxeli] != outsideOldLevel[loci])
540  {
541  WarningInFunction << "Location outside mesh "
542  << outsidePoints[loci]
543  << " is reachable from an inside location" << nl
544  << "Either your locations are too close to the"
545  << " geometry or there might be a leak in the"
546  << " geometry" << endl;
547  }
548  }
549  }
550 
551 
552  // Shell refinement : find ccs inside higher shells
553  labelList maxLevel;
554  meshRefiner_.shells().findHigherLevel(cc, voxelLevel_, maxLevel);
555 
556  // Assign max of maxLevel and voxelLevel
557  max(maxLevel, voxelLevel_);
558 
559  // Determine number of levels
560  const labelList levelCounts(count(voxelLevel_));
561 
562  //if (debug)
563  {
564  Info<< " After shell refinement : " << levelCounts << endl;
565  }
566 
567 
568  const vector meshSpan(bb_.span());
569  const vector voxel0Size
570  (
571  meshSpan[0]/n_[0],
572  meshSpan[1]/n_[1],
573  meshSpan[2]/n_[2]
574  );
575  label cellCount = 0;
576  forAll(levelCounts, leveli)
577  {
578  const scalar s = level0Len/pow(2.0, leveli);
579  const scalar nCellsPerVoxel
580  (
581  voxel0Size[0]/s
582  *voxel0Size[1]/s
583  *voxel0Size[2]/s
584  );
585  cellCount += levelCounts[leveli]*nCellsPerVoxel;
586  }
587  Info<< "Estimated cell count : " << cellCount << endl;
588  }
589 }
590 
591 
592 // ************************************************************************* //
static labelVector offset(const labelVector &nDivs)
Change in combined voxel index for change in components.
const refinementSurfaces & surfaces() const
Reference to surface search engines.
uint8_t direction
Definition: direction.H:46
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
const shellSurfaces & shells() const
Reference to refinement shells (regions)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
label maxLevel() const
Highest shell level.
constexpr label labelMin
Definition: label.H:54
label k
Boltzmann constant.
scalar level0EdgeLength() const
Typical edge length between unrefined points.
Definition: hexRef8.H:499
void doRefine(const refinementParameters &refineParams)
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
unsigned int count(const UList< bool > &bools, const bool val=true)
Count number of &#39;true&#39; entries.
Definition: BitOps.H:73
static label index(const labelVector &nDivs, const labelVector &voxel)
Find cells. Returns number of cells found.
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
void setSize(const label n)
Alias for resize()
Definition: List.H:316
dynamicFvMesh & mesh
const pointField & points
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition: tmp.H:206
OSstream Sout
OSstream wrapped stdout (std::cout)
const globalMeshData & globalData() const
Return parallel info (demand-driven)
Definition: polyMesh.C:1311
static labelVector index3(const labelVector &nDivs, const label voxeli)
Combined voxel index to individual indices.
Vector< scalar > vector
Definition: vector.H:57
const refinementFeatures & features() const
Reference to feature edge mesh.
const hexRef8 & meshCutter() const
Reference to meshcutting engine.
Istream and Ostream manipulators taking arguments.
static point centre(const boundBox &bb, const labelVector &nDivs, const labelVector &voxel)
Voxel index to voxel centre.
defineTypeNameAndDebug(combustionModel, 0)
label nTotalCells() const noexcept
Return total number of cells in decomposed mesh.
Helper class which maintains intersections of (changing) mesh with (static) surfaces.
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
vector point
Point is a vector.
Definition: point.H:37
#define WarningInFunction
Report a warning using Foam::Warning.
label nCells() const noexcept
Number of mesh cells.
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
static void fill(Container &elems, const boundBox &bb, const labelVector &nDivs, const boundBox &subBb, const Type val)
Fill voxels indicated by bounding box.
vector span() const
The bounding box span (from minimum to maximum)
Definition: boundBoxI.H:192
label findCell(const point &p, const cellDecomposition=CELL_TETS) const
Find cell enclosing this location and return index.
Definition: polyMesh.C:1511
messageStream Info
Information stream (stdout output on master, null elsewhere)
constexpr label labelMax
Definition: label.H:55
Vector< label > labelVector
Vector of labels.
Definition: labelVector.H:47
static void listCombineGather(const List< commsStruct > &comms, List< T > &values, const CombineOp &cop, const int tag, const label comm)
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
virtual int width() const override
Get width of output field.
Definition: OSstream.C:322
List< label > labelList
A List of labels.
Definition: List.H:62
A class for managing temporary objects.
Definition: HashPtrTable.H:50
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
List< bool > boolList
A List of bools.
Definition: List.H:60
boundBox cellBb(const label celli) const
The bounding box for given cell index.
const labelListList & levels() const
Per featureEdgeMesh the list of level.
Namespace for OpenFOAM.
const labelList & maxLevel() const
From global region number to refinement level.