meshRefinementProblemCells.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2023 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "meshRefinement.H"
27 #include "fvMesh.H"
28 #include "syncTools.H"
29 #include "Time.H"
30 #include "refinementSurfaces.H"
31 #include "pointSet.H"
32 #include "faceSet.H"
33 #include "indirectPrimitivePatch.H"
34 #include "cellSet.H"
35 #include "searchableSurfaces.H"
36 #include "dynamicMeshCheck.H"
37 #include "IOmanip.H"
38 #include "unitConversion.H"
39 #include "snappySnapDriver.H"
40 
41 #include "snapParameters.H"
42 #include "motionSmoother.H"
43 #include "topoDistanceData.H"
44 #include "FaceCellWave.H"
45 
46 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
47 
48 void Foam::meshRefinement::markBoundaryFace
49 (
50  const label facei,
51  boolList& isBoundaryFace,
52  boolList& isBoundaryEdge,
53  boolList& isBoundaryPoint
54 ) const
55 {
56  isBoundaryFace[facei] = true;
57 
58  const labelList& fEdges = mesh_.faceEdges(facei);
59 
60  forAll(fEdges, fp)
61  {
62  isBoundaryEdge[fEdges[fp]] = true;
63  }
64 
65  const face& f = mesh_.faces()[facei];
66 
67  forAll(f, fp)
68  {
69  isBoundaryPoint[f[fp]] = true;
70  }
71 }
72 
73 
74 void Foam::meshRefinement::findNearest
75 (
76  const labelList& meshFaces,
77  List<pointIndexHit>& nearestInfo,
78  labelList& nearestSurface,
79  labelList& nearestRegion,
80  vectorField& nearestNormal
81 ) const
82 {
83  pointField fc(meshFaces.size());
84  forAll(meshFaces, i)
85  {
86  fc[i] = mesh_.faceCentres()[meshFaces[i]];
87  }
88 
89  const labelList allSurfaces(identityMap(surfaces_.surfaces().size()));
90 
91  surfaces_.findNearest
92  (
93  allSurfaces,
94  fc,
95  scalarField(fc.size(), sqr(great)), // sqr of attraction
96  nearestSurface,
97  nearestInfo
98  );
99 
100  // Do normal testing per surface.
101  nearestNormal.setSize(nearestInfo.size());
102  nearestRegion.setSize(nearestInfo.size());
103 
104  forAll(allSurfaces, surfi)
105  {
106  DynamicList<pointIndexHit> localHits;
107 
108  forAll(nearestSurface, i)
109  {
110  if (nearestSurface[i] == surfi)
111  {
112  localHits.append(nearestInfo[i]);
113  }
114  }
115 
116  const label geomi = surfaces_.surfaces()[surfi];
117 
118  pointField localNormals;
119  surfaces_.geometry()[geomi].getNormal(localHits, localNormals);
120 
121  labelList localRegion;
122  surfaces_.geometry()[geomi].getRegion(localHits, localRegion);
123 
124  label localI = 0;
125  forAll(nearestSurface, i)
126  {
127  if (nearestSurface[i] == surfi)
128  {
129  nearestNormal[i] = localNormals[localI];
130  nearestRegion[i] = localRegion[localI];
131  localI++;
132  }
133  }
134  }
135 }
136 
137 
138 Foam::Map<Foam::label> Foam::meshRefinement::findEdgeConnectedProblemCells
139 (
140  const scalarField& perpendicularAngle,
141  const labelList& globalToPatch
142 ) const
143 {
144  // Construct addressing engine from all patches added for meshing.
145  autoPtr<indirectPrimitivePatch> ppPtr
146  (
148  (
149  mesh_,
150  meshedPatches()
151  )
152  );
153  const indirectPrimitivePatch& pp = ppPtr();
154 
155 
156  // 1. Collect faces to test
157  // ~~~~~~~~~~~~~~~~~~~~~~~~
158 
159  DynamicList<label> candidateFaces(pp.size()/20);
160 
161  const labelListList& edgeFaces = pp.edgeFaces();
162 
163  const labelList& cellLevel = meshCutter_.cellLevel();
164 
165  forAll(edgeFaces, edgei)
166  {
167  const labelList& eFaces = edgeFaces[edgei];
168 
169  if (eFaces.size() == 2)
170  {
171  const label face0 = pp.addressing()[eFaces[0]];
172  const label face1 = pp.addressing()[eFaces[1]];
173 
174  const label cell0 = mesh_.faceOwner()[face0];
175  const label cell1 = mesh_.faceOwner()[face1];
176 
177  if (cellLevel[cell0] > cellLevel[cell1])
178  {
179  // cell0 smaller.
180  const vector& n0 = pp.faceNormals()[eFaces[0]];
181  const vector& n1 = pp.faceNormals()[eFaces[1]];
182 
183  if (mag(n0 & n1) < 0.1)
184  {
185  candidateFaces.append(face0);
186  }
187  }
188  else if (cellLevel[cell1] > cellLevel[cell0])
189  {
190  // cell1 smaller.
191  const vector& n0 = pp.faceNormals()[eFaces[0]];
192  const vector& n1 = pp.faceNormals()[eFaces[1]];
193 
194  if (mag(n0 & n1) < 0.1)
195  {
196  candidateFaces.append(face1);
197  }
198  }
199  }
200  }
201  candidateFaces.shrink();
202 
203  Info<< "Testing " << returnReduce(candidateFaces.size(), sumOp<label>())
204  << " faces on edge-connected cells of differing level."
205  << endl;
206 
207  if (debug&meshRefinement::MESH)
208  {
209  faceSet fSet(mesh_, "edgeConnectedFaces", candidateFaces);
210  fSet.instance() = name();
211  Pout<< "Writing " << fSet.size()
212  << " with problematic topology to faceSet "
213  << fSet.objectPath() << endl;
214  fSet.write();
215  }
216 
217 
218  // 2. Find nearest surface on candidate faces
219  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
220 
221  List<pointIndexHit> nearestInfo;
222  labelList nearestSurface;
223  labelList nearestRegion;
224  vectorField nearestNormal;
225  findNearest
226  (
227  candidateFaces,
228  nearestInfo,
229  nearestSurface,
230  nearestRegion,
231  nearestNormal
232  );
233 
234 
235  // 3. Test angle to surface
236  // ~~~~~~~~~~~~~~~~~~~~~~~~
237 
238  Map<label> candidateCells(candidateFaces.size());
239 
240  faceSet perpFaces(mesh_, "perpendicularFaces", pp.size()/100);
241 
242  forAll(candidateFaces, i)
243  {
244  const label facei = candidateFaces[i];
245  const vector n = mesh_.faceAreas()[facei]/mag(mesh_.faceAreas()[facei]);
246 
247  const label region = surfaces_.globalRegion
248  (
249  nearestSurface[i],
250  nearestRegion[i]
251  );
252 
253  const scalar angle = degToRad(perpendicularAngle[region]);
254 
255  if (angle >= 0)
256  {
257  if (mag(n & nearestNormal[i]) < Foam::sin(angle))
258  {
259  perpFaces.insert(facei);
260  candidateCells.insert
261  (
262  mesh_.faceOwner()[facei],
263  globalToPatch[region]
264  );
265  }
266  }
267  }
268 
269  if (debug&meshRefinement::MESH)
270  {
271  perpFaces.instance() = name();
272  Pout<< "Writing " << perpFaces.size()
273  << " faces that are perpendicular to the surface to set "
274  << perpFaces.objectPath() << endl;
275  perpFaces.write();
276  }
277  return candidateCells;
278 }
279 
280 
281 bool Foam::meshRefinement::isCollapsedFace
282 (
283  const pointField& points,
284  const pointField& neiCc,
285  const scalar minFaceArea,
286  const scalar maxNonOrtho,
287  const label facei
288 ) const
289 {
290  // Severe nonorthogonality threshold
291  const scalar severeNonorthogonalityThreshold =
292  ::cos(degToRad(maxNonOrtho));
293 
294  const vector s = mesh_.faces()[facei].area(points);
295  const scalar magS = mag(s);
296 
297  // Check face area
298  if (magS < minFaceArea)
299  {
300  return true;
301  }
302 
303  // Check orthogonality
304  const point& ownCc = mesh_.cellCentres()[mesh_.faceOwner()[facei]];
305 
306  if (mesh_.isInternalFace(facei))
307  {
308  const label nei = mesh_.faceNeighbour()[facei];
309  const vector d = mesh_.cellCentres()[nei] - ownCc;
310 
311  const scalar dDotS = (d & s)/(mag(d)*magS + vSmall);
312 
313  if (dDotS < severeNonorthogonalityThreshold)
314  {
315  return true;
316  }
317  else
318  {
319  return false;
320  }
321  }
322  else
323  {
324  const label patchi = mesh_.boundaryMesh().whichPatch(facei);
325 
326  if (mesh_.boundaryMesh()[patchi].coupled())
327  {
328  const vector d = neiCc[facei-mesh_.nInternalFaces()] - ownCc;
329 
330  const scalar dDotS = (d & s)/(mag(d)*magS + vSmall);
331 
332  if (dDotS < severeNonorthogonalityThreshold)
333  {
334  return true;
335  }
336  else
337  {
338  return false;
339  }
340  }
341  else
342  {
343  // Collapsing normal boundary face does not cause problems with
344  // non-orthogonality
345  return false;
346  }
347  }
348 }
349 
350 
351 bool Foam::meshRefinement::isCollapsedCell
352 (
353  const pointField& points,
354  const scalar volFraction,
355  const label celli
356 ) const
357 {
358  const scalar vol = mesh_.cells()[celli].mag(points, mesh_.faces());
359 
360  if (vol/mesh_.cellVolumes()[celli] < volFraction)
361  {
362  return true;
363  }
364  else
365  {
366  return false;
367  }
368 }
369 
370 
371 Foam::labelList Foam::meshRefinement::nearestPatch
372 (
373  const labelList& adaptPatchIDs
374 ) const
375 {
376  const polyBoundaryMesh& patches = mesh_.boundaryMesh();
377 
378  labelList nearestAdaptPatch;
379 
380  if (adaptPatchIDs.size())
381  {
382  nearestAdaptPatch.setSize(mesh_.nFaces(), adaptPatchIDs[0]);
383 
384 
385  // Count number of faces in adaptPatchIDs
386  label nFaces = 0;
387  forAll(adaptPatchIDs, i)
388  {
389  const polyPatch& pp = patches[adaptPatchIDs[i]];
390  nFaces += pp.size();
391  }
392 
393  // Field on cells and faces.
394  List<topoDistanceData> cellData(mesh_.nCells());
395  List<topoDistanceData> faceData(mesh_.nFaces());
396 
397  // Start of changes
398  labelList patchFaces(nFaces);
399  List<topoDistanceData> patchData(nFaces);
400  nFaces = 0;
401  forAll(adaptPatchIDs, i)
402  {
403  const label patchi = adaptPatchIDs[i];
404  const polyPatch& pp = patches[patchi];
405 
406  forAll(pp, i)
407  {
408  patchFaces[nFaces] = pp.start()+i;
409  patchData[nFaces] = topoDistanceData(patchi, 0);
410  nFaces++;
411  }
412  }
413 
414  // Propagate information inwards
415  FaceCellWave<topoDistanceData> deltaCalc
416  (
417  mesh_,
418  patchFaces,
419  patchData,
420  faceData,
421  cellData,
422  mesh_.globalData().nTotalCells()+1
423  );
424 
425  // And extract
426 
427  bool haveWarned = false;
428  forAll(faceData, facei)
429  {
430  if (!faceData[facei].valid(deltaCalc.data()))
431  {
432  if (!haveWarned)
433  {
435  << "Did not visit some faces, e.g. face " << facei
436  << " at " << mesh_.faceCentres()[facei] << endl
437  << "Assigning these cells to patch "
438  << adaptPatchIDs[0]
439  << endl;
440  haveWarned = true;
441  }
442  }
443  else
444  {
445  nearestAdaptPatch[facei] = faceData[facei].data();
446  }
447  }
448  }
449  else
450  {
451  // Use patch 0
452  nearestAdaptPatch.setSize(mesh_.nFaces(), 0);
453  }
454 
455  return nearestAdaptPatch;
456 }
457 
458 
459 Foam::labelList Foam::meshRefinement::markFacesOnProblemCells
460 (
461  const dictionary& motionDict,
462  const bool removeEdgeConnectedCells,
463  const scalarField& perpendicularAngle,
464  const labelList& globalToPatch
465 ) const
466 {
467  const labelList& cellLevel = meshCutter_.cellLevel();
468  const labelList& pointLevel = meshCutter_.pointLevel();
469  const polyBoundaryMesh& patches = mesh_.boundaryMesh();
470 
471 
472  // Mark all points and edges on baffle patches (so not on any inlets,
473  // outlets etc.)
474  boolList isBoundaryPoint(mesh_.nPoints(), false);
475  boolList isBoundaryEdge(mesh_.nEdges(), false);
476  boolList isBoundaryFace(mesh_.nFaces(), false);
477 
478  // Fill boundary data. All elements on meshed patches get marked.
479  // Get the labels of added patches.
480  labelList adaptPatchIDs(meshedPatches());
481 
482  forAll(adaptPatchIDs, i)
483  {
484  const polyPatch& pp = patches[adaptPatchIDs[i]];
485 
486  label facei = pp.start();
487 
488  forAll(pp, j)
489  {
490  markBoundaryFace
491  (
492  facei,
493  isBoundaryFace,
494  isBoundaryEdge,
495  isBoundaryPoint
496  );
497 
498  facei++;
499  }
500  }
501 
502 
503  // Per face the nearest adaptPatch
504  const labelList nearestAdaptPatch(nearestPatch(adaptPatchIDs));
505 
506 
507  // Per internal face (boundary faces not used) the patch that the
508  // baffle should get (or -1)
509  labelList facePatch(mesh_.nFaces(), -1);
510 
511  // Swap neighbouring cell centres and cell level
512  labelList neiLevel(mesh_.nFaces()-mesh_.nInternalFaces());
513  pointField neiCc(mesh_.nFaces()-mesh_.nInternalFaces());
514  calcNeighbourData(neiLevel, neiCc);
515 
516 
517  // Count of faces marked for baffling
518  label nBaffleFaces = 0;
519  PackedBoolList isMasterFace(syncTools::getMasterFaces(mesh_));
520 
521  // Count of faces not baffled since would not cause a collapse
522  label nPrevented = 0;
523 
524  if (removeEdgeConnectedCells && max(perpendicularAngle) >= 0)
525  {
526  Info<< "markFacesOnProblemCells :"
527  << " Checking for edge-connected cells of highly differing sizes."
528  << endl;
529 
530  // Pick up the cells that need to be removed and (a guess for)
531  // the patch they should be patched with.
532  Map<label> problemCells
533  (
534  findEdgeConnectedProblemCells
535  (
536  perpendicularAngle,
537  globalToPatch
538  )
539  );
540 
541  // Baffle all faces of cells that need to be removed
542  forAllConstIter(Map<label>, problemCells, iter)
543  {
544  const cell& cFaces = mesh_.cells()[iter.key()];
545 
546  forAll(cFaces, i)
547  {
548  const label facei = cFaces[i];
549 
550  if (facePatch[facei] == -1 && mesh_.isInternalFace(facei))
551  {
552  facePatch[facei] = nearestAdaptPatch[facei];
553  nBaffleFaces++;
554 
555  // Mark face as a 'boundary'
556  markBoundaryFace
557  (
558  facei,
559  isBoundaryFace,
560  isBoundaryEdge,
561  isBoundaryPoint
562  );
563  }
564  }
565  }
566  Info<< "markFacesOnProblemCells : Marked "
567  << returnReduce(nBaffleFaces, sumOp<label>())
568  << " additional internal faces to be converted into baffles"
569  << " due to "
570  << returnReduce(problemCells.size(), sumOp<label>())
571  << " cells edge-connected to lower level cells." << endl;
572 
573  if (debug&meshRefinement::MESH)
574  {
575  cellSet problemCellSet(mesh_, "problemCells", problemCells.toc());
576  problemCellSet.instance() = name();
577  Pout<< "Writing " << problemCellSet.size()
578  << " cells that are edge connected to coarser cell to set "
579  << problemCellSet.objectPath() << endl;
580  problemCellSet.write();
581  }
582  }
583 
585  (
586  mesh_,
587  isBoundaryPoint,
588  orEqOp<bool>(),
589  false // null value
590  );
591 
593  (
594  mesh_,
595  isBoundaryEdge,
596  orEqOp<bool>(),
597  false // null value
598  );
599 
601  (
602  mesh_,
603  isBoundaryFace,
604  orEqOp<bool>()
605  );
606 
607 
608  // See if checking for collapse
609  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
610 
611  // Collapse checking parameters
612  const scalar volFraction =
613  motionDict.lookupOrDefault<scalar>("minVolCollapseRatio", -1);
614 
615  const bool checkCollapse = (volFraction > 0);
616  scalar minArea = -1;
617  scalar maxNonOrtho = -1;
618 
619 
620  // Find nearest (non-baffle) surface
621  pointField newPoints;
622 
623  if (checkCollapse)
624  {
625  minArea = motionDict.lookup<scalar>("minArea");
626  maxNonOrtho = motionDict.lookup<scalar>("maxNonOrtho");
627 
628  Info<< "markFacesOnProblemCells :"
629  << " Deleting all-anchor surface cells only if"
630  << " snapping them violates mesh quality constraints:" << nl
631  << " snapped/original cell volume < " << volFraction << nl
632  << " face area < " << minArea << nl
633  << " non-orthogonality > " << maxNonOrtho << nl
634  << endl;
635 
636  // Construct addressing engine.
637  autoPtr<indirectPrimitivePatch> ppPtr
638  (
640  (
641  mesh_,
642  adaptPatchIDs
643  )
644  );
645  const indirectPrimitivePatch& pp = ppPtr();
646  const pointField& localPoints = pp.localPoints();
647  const labelList& meshPoints = pp.meshPoints();
648 
649  List<pointIndexHit> hitinfo;
650  labelList hitSurface;
651  surfaces_.findNearest
652  (
653  surfaceZonesInfo::getUnnamedSurfaces(surfaces_.surfZones()),
654  localPoints,
655  scalarField(localPoints.size(), sqr(great)), // sqr of attraction
656  hitSurface,
657  hitinfo
658  );
659 
660  // Start off from current points
661  newPoints = mesh_.points();
662 
663  forAll(hitinfo, i)
664  {
665  if (hitinfo[i].hit())
666  {
667  newPoints[meshPoints[i]] = hitinfo[i].hitPoint();
668  }
669  }
670 
671  if (debug&meshRefinement::MESH)
672  {
673  const_cast<Time&>(mesh_.time())++;
674  pointField oldPoints(mesh_.points());
675  mesh_.movePoints(newPoints);
676  Pout<< "Writing newPoints mesh to time " << name()
677  << endl;
678  write
679  (
680  debugType(debug),
681  writeType(writeLevel() | WRITEMESH),
682  mesh_.time().path()/"newPoints"
683  );
684  mesh_.movePoints(oldPoints);
685  }
686  }
687 
688 
689 
690  // For each cell count the number of anchor points that are on
691  // the boundary:
692  // 8 : check the number of (baffle) boundary faces. If 3 or more block
693  // off the cell since the cell would get squeezed down to a diamond
694  // (probably; if the 3 or more faces are unrefined (only use the
695  // anchor points))
696  // 7 : store. Used to check later on whether there are points with
697  // 3 or more of these cells. (note that on a flat surface a boundary
698  // point will only have 4 cells connected to it)
699 
700 
701  // Does cell have exactly 7 of its 8 anchor points on the boundary?
702  PackedBoolList hasSevenBoundaryAnchorPoints(mesh_.nCells());
703  // If so what is the remaining non-boundary anchor point?
704  labelHashSet nonBoundaryAnchors(mesh_.nCells()/10000);
705 
706  // On-the-fly addressing storage.
707  DynamicList<label> dynFEdges;
708  DynamicList<label> dynCPoints;
709 
710  forAll(cellLevel, celli)
711  {
712  const labelList& cPoints = mesh_.cellPoints(celli, dynCPoints);
713 
714  // Get number of anchor points (pointLevel <= cellLevel)
715 
716  label nBoundaryAnchors = 0;
717  label nonBoundaryAnchor = -1;
718 
719  forAll(cPoints, i)
720  {
721  const label pointi = cPoints[i];
722 
723  if (pointLevel[pointi] <= cellLevel[celli])
724  {
725  // Anchor point
726  if (isBoundaryPoint[pointi])
727  {
728  nBoundaryAnchors++;
729  }
730  else
731  {
732  // Anchor point which is not on the surface
733  nonBoundaryAnchor = pointi;
734  }
735  }
736  }
737 
738  if (nBoundaryAnchors == 8)
739  {
740  const cell& cFaces = mesh_.cells()[celli];
741 
742  if
743  (
744  checkCollapse
745  && !isCollapsedCell(newPoints, volFraction, celli)
746  )
747  {
748  nPrevented++;
749 
750  // Pout<< "Preventing baffling/removal of 8 anchor point"
751  // << " cell "
752  // << celli << " at " << mesh_.cellCentres()[celli]
753  // << " since new volume "
754  // << mesh_.cells()[celli].mag(newPoints, mesh_.faces())
755  // << " old volume " << mesh_.cellVolumes()[celli]
756  // << endl;
757  }
758  else
759  {
760  // Block all faces of cell
761  forAll(cFaces, cf)
762  {
763  const label facei = cFaces[cf];
764 
765  if
766  (
767  facePatch[facei] == -1
768  && mesh_.isInternalFace(facei)
769  )
770  {
771  facePatch[facei] = nearestAdaptPatch[facei];
772  nBaffleFaces++;
773 
774  // Mark face as a 'boundary'
775  markBoundaryFace
776  (
777  facei,
778  isBoundaryFace,
779  isBoundaryEdge,
780  isBoundaryPoint
781  );
782  }
783  }
784  }
785  }
786  else if (nBoundaryAnchors == 7)
787  {
788  // Mark the cell. Store the (single!) non-boundary anchor point.
789  hasSevenBoundaryAnchorPoints.set(celli, 1u);
790  nonBoundaryAnchors.insert(nonBoundaryAnchor);
791  }
792  }
793 
794 
795  // Loop over all points. If a point is connected to 4 or more cells
796  // with 7 anchor points on the boundary set those cell's non-boundary faces
797  // to baffles
798 
799  DynamicList<label> dynPCells;
800 
801  forAllConstIter(labelHashSet, nonBoundaryAnchors, iter)
802  {
803  label pointi = iter.key();
804 
805  const labelList& pCells = mesh_.pointCells(pointi, dynPCells);
806 
807  // Count number of 'hasSevenBoundaryAnchorPoints' cells.
808  label n = 0;
809 
810  forAll(pCells, i)
811  {
812  if (hasSevenBoundaryAnchorPoints.get(pCells[i]) == 1u)
813  {
814  n++;
815  }
816  }
817 
818  if (n > 3)
819  {
820  // Point in danger of being what? Remove all 7-cells.
821  forAll(pCells, i)
822  {
823  label celli = pCells[i];
824 
825  if (hasSevenBoundaryAnchorPoints.get(celli) == 1u)
826  {
827  if
828  (
829  checkCollapse
830  && !isCollapsedCell(newPoints, volFraction, celli)
831  )
832  {
833  nPrevented++;
834  // Pout<< "Preventing baffling of 7 anchor cell "
835  // << celli
836  // << " at " << mesh_.cellCentres()[celli]
837  // << " since new volume "
838  // << mesh_.cells()[celli].mag
839  // (newPoints, mesh_.faces())
840  // << " old volume " << mesh_.cellVolumes()[celli]
841  // << endl;
842  }
843  else
844  {
845  const cell& cFaces = mesh_.cells()[celli];
846 
847  forAll(cFaces, cf)
848  {
849  const label facei = cFaces[cf];
850 
851  if
852  (
853  facePatch[facei] == -1
854  && mesh_.isInternalFace(facei)
855  )
856  {
857  facePatch[facei] = nearestAdaptPatch[facei];
858  nBaffleFaces++;
859 
860  // Mark face as a 'boundary'
861  markBoundaryFace
862  (
863  facei,
864  isBoundaryFace,
865  isBoundaryEdge,
866  isBoundaryPoint
867  );
868  }
869  }
870  }
871  }
872  }
873  }
874  }
875 
876 
877  // Sync all. (note that pointdata and facedata not used anymore but sync
878  // anyway)
879 
881  (
882  mesh_,
883  isBoundaryPoint,
884  orEqOp<bool>(),
885  false // null value
886  );
887 
889  (
890  mesh_,
891  isBoundaryEdge,
892  orEqOp<bool>(),
893  false // null value
894  );
895 
897  (
898  mesh_,
899  isBoundaryFace,
900  orEqOp<bool>()
901  );
902 
903 
904  // Find faces with all edges on the boundary and make them baffles
905  for (label facei = 0; facei < mesh_.nInternalFaces(); facei++)
906  {
907  if (facePatch[facei] == -1)
908  {
909  const labelList& fEdges = mesh_.faceEdges(facei, dynFEdges);
910  label nFaceBoundaryEdges = 0;
911 
912  forAll(fEdges, fe)
913  {
914  if (isBoundaryEdge[fEdges[fe]])
915  {
916  nFaceBoundaryEdges++;
917  }
918  }
919 
920  if (nFaceBoundaryEdges == fEdges.size())
921  {
922  if
923  (
924  checkCollapse
925  && !isCollapsedFace
926  (
927  newPoints,
928  neiCc,
929  minArea,
930  maxNonOrtho,
931  facei
932  )
933  )
934  {
935  nPrevented++;
936  // Pout<< "Preventing baffling (to avoid collapse) of face "
937  // << facei
938  // << " with all boundary edges "
939  // << " at " << mesh_.faceCentres()[facei]
940  // << endl;
941  }
942  else
943  {
944  facePatch[facei] = nearestAdaptPatch[facei];
945  nBaffleFaces++;
946 
947  // Do NOT update boundary data since this would grow blocked
948  // faces across gaps.
949  }
950  }
951  }
952  }
953 
955  {
956  const polyPatch& pp = patches[patchi];
957 
958  if (pp.coupled())
959  {
960  label facei = pp.start();
961 
962  forAll(pp, i)
963  {
964  if (facePatch[facei] == -1)
965  {
966  const labelList& fEdges = mesh_.faceEdges(facei, dynFEdges);
967  label nFaceBoundaryEdges = 0;
968 
969  forAll(fEdges, fe)
970  {
971  if (isBoundaryEdge[fEdges[fe]])
972  {
973  nFaceBoundaryEdges++;
974  }
975  }
976 
977  if (nFaceBoundaryEdges == fEdges.size())
978  {
979  if
980  (
981  checkCollapse
982  && !isCollapsedFace
983  (
984  newPoints,
985  neiCc,
986  minArea,
987  maxNonOrtho,
988  facei
989  )
990  )
991  {
992  nPrevented++;
993  // Pout<< "Preventing baffling of coupled face "
994  // << facei
995  // << " with all boundary edges "
996  // << " at " << mesh_.faceCentres()[facei]
997  // << endl;
998  }
999  else
1000  {
1001  facePatch[facei] = nearestAdaptPatch[facei];
1002  if (isMasterFace[facei])
1003  {
1004  nBaffleFaces++;
1005  }
1006 
1007  // Do NOT update boundary data since this would grow
1008  // blocked faces across gaps.
1009  }
1010  }
1011  }
1012 
1013  facei++;
1014  }
1015  }
1016  }
1017 
1018 
1019  // Because of isCollapsedFace one side can decide not to baffle whereas
1020  // the other side does so sync. Baffling is preferred over not baffling.
1021  if (checkCollapse) // Or always?
1022  {
1024  (
1025  mesh_,
1026  facePatch,
1027  maxEqOp<label>()
1028  );
1029  }
1030 
1031  Info<< "markFacesOnProblemCells : marked "
1032  << returnReduce(nBaffleFaces, sumOp<label>())
1033  << " additional internal faces to be converted into baffles."
1034  << endl;
1035 
1036  if (checkCollapse)
1037  {
1038  Info<< "markFacesOnProblemCells : prevented "
1039  << returnReduce(nPrevented, sumOp<label>())
1040  << " internal faces from getting converted into baffles."
1041  << endl;
1042  }
1043 
1044  return facePatch;
1045 }
1046 
1047 
1048 // Mark faces to be baffled to prevent snapping problems. Does
1049 // test to find nearest surface and checks which faces would get squashed.
1050 Foam::labelList Foam::meshRefinement::markFacesOnProblemCellsGeometric
1051 (
1052  const snapParameters& snapParams,
1053  const dictionary& motionDict
1054 ) const
1055 {
1056  pointField oldPoints(mesh_.points());
1057 
1058  // Repeat (most of) snappySnapDriver::doSnap
1059  {
1060  labelList adaptPatchIDs(meshedPatches());
1061 
1062  // Construct addressing engine.
1063  autoPtr<indirectPrimitivePatch> ppPtr
1064  (
1066  (
1067  mesh_,
1068  adaptPatchIDs
1069  )
1070  );
1071  indirectPrimitivePatch& pp = ppPtr();
1072 
1073  // Distance to attract to nearest feature on surface
1074  const scalarField snapDist
1075  (
1076  snappySnapDriver::calcSnapDistance(mesh_, snapParams, pp)
1077  );
1078 
1079 
1080  // Construct iterative mesh mover.
1081  Info<< "Constructing mesh displacer ..." << endl;
1082  Info<< "Using mesh parameters " << motionDict << nl << endl;
1083 
1084  const pointMesh& pMesh = pointMesh::New(mesh_);
1085 
1086  motionSmoother meshMover
1087  (
1088  mesh_,
1089  pp,
1090  adaptPatchIDs,
1091  meshRefinement::makeDisplacementField(pMesh, adaptPatchIDs)(),
1092  motionDict
1093  );
1094 
1095 
1096  // Check initial mesh
1097  Info<< "Checking initial mesh ..." << endl;
1098  labelHashSet wrongFaces(mesh_.nFaces()/100);
1099  motionSmoother::checkMesh(false, mesh_, motionDict, wrongFaces);
1100  const label nInitErrors = returnReduce
1101  (
1102  wrongFaces.size(),
1103  sumOp<label>()
1104  );
1105 
1106  Info<< "Detected " << nInitErrors << " illegal faces"
1107  << " (concave, zero area or negative cell pyramid volume)"
1108  << endl;
1109 
1110 
1111  Info<< "Checked initial mesh in = "
1112  << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl;
1113 
1114  // Pre-smooth patch vertices (so before determining nearest)
1116  (
1117  *this,
1118  snapParams,
1119  nInitErrors,
1120  List<labelPair>(0), // baffles
1121  meshMover
1122  );
1123 
1124  pointField nearestPoint;
1125  vectorField nearestNormal;
1126  const vectorField disp
1127  (
1129  (
1130  *this,
1131  snapDist, // attraction
1132  pp,
1133  nearestPoint,
1134  nearestNormal
1135  )
1136  );
1137 
1138  const labelList& meshPoints = pp.meshPoints();
1139 
1140  pointField newPoints(mesh_.points());
1141  forAll(meshPoints, i)
1142  {
1143  newPoints[meshPoints[i]] += disp[i];
1144  }
1145 
1147  (
1148  mesh_,
1149  newPoints,
1150  minMagSqrEqOp<point>(), // combine op
1151  vector(great, great, great) // null value (note: cannot use vGreat)
1152  );
1153 
1154  mesh_.movePoints(newPoints);
1155  }
1156 
1157 
1158  // Per face the nearest adaptPatch
1159  const labelList nearestAdaptPatch(nearestPatch(meshedPatches()));
1160 
1161  // Per face (internal or coupled!) the patch that the
1162  // baffle should get (or -1).
1163  labelList facePatch(mesh_.nFaces(), -1);
1164  // Count of baffled faces
1165  label nBaffleFaces = 0;
1166 
1167  {
1168  faceSet wrongFaces(mesh_, "wrongFaces", 100);
1169  {
1170  // motionSmoother::checkMesh(false, mesh_, motionDict, wrongFaces);
1171 
1172  // Just check the errors from squashing
1173  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1174 
1175  const labelList allFaces(identityMap(mesh_.nFaces()));
1176  label nWrongFaces = 0;
1177 
1178  // const scalar minV(motionDict.lookup<scalar>("minVol", true));
1179  // if (minV > -great)
1180  //{
1181  // dynamicMeshCheck::checkFacePyramids
1182  // (
1183  // false,
1184  // minV,
1185  // mesh_,
1186  // mesh_.cellCentres(),
1187  // mesh_.points(),
1188  // allFaces,
1189  // List<labelPair>(0),
1190  // &wrongFaces
1191  // );
1192  //
1193  // label nNewWrongFaces = returnReduce
1194  // (
1195  // wrongFaces.size(),
1196  // sumOp<label>()
1197  // );
1198  //
1199  // Info<< " faces with pyramid volume < "
1200  // << setw(5) << minV
1201  // << " m^3 : "
1202  // << nNewWrongFaces-nWrongFaces << endl;
1203  //
1204  // nWrongFaces = nNewWrongFaces;
1205  //}
1206 
1207  scalar minArea(motionDict.lookup<scalar>("minArea"));
1208  if (minArea > -small)
1209  {
1211  (
1212  false,
1213  minArea,
1214  mesh_,
1215  mesh_.faceAreas(),
1216  allFaces,
1217  &wrongFaces
1218  );
1219 
1220  label nNewWrongFaces = returnReduce
1221  (
1222  wrongFaces.size(),
1223  sumOp<label>()
1224  );
1225 
1226  Info<< " faces with area < "
1227  << setw(5) << minArea
1228  << " m^2 : "
1229  << nNewWrongFaces-nWrongFaces << endl;
1230 
1231  nWrongFaces = nNewWrongFaces;
1232  }
1233 
1234  scalar minDet(motionDict.lookup<scalar>("minDeterminant"));
1235  if (minDet > -1)
1236  {
1238  (
1239  false,
1240  minDet,
1241  mesh_,
1242  mesh_.faceAreas(),
1243  allFaces,
1244  &wrongFaces
1245  );
1246 
1247  label nNewWrongFaces = returnReduce
1248  (
1249  wrongFaces.size(),
1250  sumOp<label>()
1251  );
1252 
1253  Info<< " faces on cells with determinant < "
1254  << setw(5) << minDet << " : "
1255  << nNewWrongFaces-nWrongFaces << endl;
1256 
1257  nWrongFaces = nNewWrongFaces;
1258  }
1259  }
1260 
1261 
1262  forAllConstIter(faceSet, wrongFaces, iter)
1263  {
1264  label patchi = mesh_.boundaryMesh().whichPatch(iter.key());
1265 
1266  if (patchi == -1 || mesh_.boundaryMesh()[patchi].coupled())
1267  {
1268  facePatch[iter.key()] = nearestAdaptPatch[iter.key()];
1269  nBaffleFaces++;
1270 
1271  // Pout<< " " << iter.key()
1272  // //<< " on patch " << mesh_.boundaryMesh()[patchi].name()
1273  // << " is destined for patch " << facePatch[iter.key()]
1274  // << endl;
1275  }
1276  }
1277  }
1278 
1279 
1280  // Restore points.
1281  mesh_.movePoints(oldPoints);
1282 
1283 
1284  Info<< "markFacesOnProblemCellsGeometric : marked "
1285  << returnReduce(nBaffleFaces, sumOp<label>())
1286  << " additional internal and coupled faces"
1287  << " to be converted into baffles." << endl;
1288 
1290  (
1291  mesh_,
1292  facePatch,
1293  maxEqOp<label>()
1294  );
1295 
1296  return facePatch;
1297 }
1298 
1299 
1300 // ************************************************************************* //
Istream and Ostream manipulators taking arguments.
label n
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:477
void setSize(const label)
Reset size of List.
Definition: List.C:281
A HashTable to objects of type <T> with a label key.
Definition: Map.H:52
virtual Ostream & write(const char)=0
Write character.
label size() const
Return the number of elements in the UPtrList.
Definition: UPtrListI.H:29
static tmp< pointVectorField > makeDisplacementField(const pointMesh &pMesh, const labelList &adaptPatchIDs)
Helper function to make a pointVectorField with correct.
static autoPtr< indirectPrimitivePatch > makePatch(const polyMesh &, const labelList &)
Create patch from set of patches.
static bool checkMesh(const bool report, const polyMesh &mesh, const dictionary &dict, labelHashSet &wrongFaces)
Check mesh with mesh settings in dict. Collects incorrect faces.
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1373
const labelListList & faceEdges() const
static vectorField calcNearestSurface(const meshRefinement &meshRefiner, const scalarField &snapDist, const indirectPrimitivePatch &, pointField &nearestPoint, vectorField &nearestNormal)
Per patch point calculate point on nearest surface. Set as.
static void preSmoothPatch(const meshRefinement &meshRefiner, const snapParameters &snapParams, const label nInitErrors, const List< labelPair > &baffles, motionSmoother &)
Smooth the mesh (patch and internal) to increase visibility.
static scalarField calcSnapDistance(const fvMesh &mesh, const snapParameters &snapParams, const indirectPrimitivePatch &)
Calculate edge length per patch point.
static labelList getUnnamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of unnamed surfaces (surfaces without faceZoneName)
static void syncPointList(const polyMesh &, List< T > &, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronise values on all mesh points.
static PackedBoolList getMasterFaces(const polyMesh &)
Get per face whether it is uncoupled or a master of a.
Definition: syncTools.C:153
static void syncEdgeList(const polyMesh &, List< T > &, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronise values on all mesh edges.
static void syncFaceList(const polyMesh &mesh, UList< T > &l, const CombineOp &cop)
Synchronise values on all mesh faces.
Definition: syncTools.H:387
PolyMesh checking routines. Checks various criteria for a mesh and supplied geometry,...
label patchi
const pointField & points
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.name(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
const fvPatchList & patches
#define WarningInFunction
Report a warning using Foam::Warning.
bool valid(const PtrList< ModelType > &l)
bool checkFaceArea(const bool report, const scalar minArea, const polyMesh &, const vectorField &faceAreas, const labelList &checkFaces, labelHashSet *setPtr)
Check for small faces.
bool checkCellDeterminant(const bool report, const scalar minDet, const polyMesh &, const vectorField &faceAreas, const labelList &checkFaces, labelHashSet *setPtr)
Check the area of internal faces v.s. boundary faces.
void write(std::ostream &os, const bool binary, List< floatScalar > &fField)
Write floats ascii or binary.
List< label > labelList
A List of labels.
Definition: labelList.H:56
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
word name(const bool)
Return a word representation of a bool.
Definition: boolIO.C:39
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
dimensionedScalar sin(const dimensionedScalar &ds)
messageStream Info
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
List< bool > boolList
Bool container classes.
Definition: boolList.H:50
vector point
Point is a vector.
Definition: point.H:41
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:57
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
dimensioned< scalar > mag(const dimensioned< Type > &)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
Field< vector > vectorField
Specialisation of Field<T> for vector.
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
HashSet< label, Hash< label > > labelHashSet
A HashSet with label keys.
Definition: HashSet.H:211
labelList identityMap(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
PrimitivePatch< IndirectList< face >, const pointField & > indirectPrimitivePatch
Foam::indirectPrimitivePatch.
static const char nl
Definition: Ostream.H:260
dimensionedScalar cos(const dimensionedScalar &ds)
scalar degToRad(const scalar deg)
Conversion from degrees to radians.
labelList f(nPoints)
Unit conversion functions.