snappyRefineDriver.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) 2011-2015 OpenFOAM Foundation
9  Copyright (C) 2015-2023 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "snappyRefineDriver.H"
30 #include "meshRefinement.H"
31 #include "fvMesh.H"
32 #include "Time.H"
33 #include "cellSet.H"
34 #include "syncTools.H"
35 #include "refinementParameters.H"
36 #include "refinementSurfaces.H"
37 #include "refinementFeatures.H"
38 #include "shellSurfaces.H"
39 #include "mapDistributePolyMesh.H"
40 #include "unitConversion.H"
41 #include "snapParameters.H"
42 #include "localPointRegion.H"
43 #include "IOmanip.H"
44 #include "labelVector.H"
45 #include "profiling.H"
46 #include "searchableSurfaces.H"
47 #include "fvMeshSubset.H"
48 #include "interpolationTable.H"
49 #include "snappyVoxelMeshDriver.H"
50 #include "regionSplit.H"
51 #include "removeCells.H"
52 
53 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
54 
55 namespace Foam
56 {
58 } // End namespace Foam
59 
60 
61 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
62 
63 Foam::snappyRefineDriver::snappyRefineDriver
64 (
65  meshRefinement& meshRefiner,
66  decompositionMethod& decomposer,
67  fvMeshDistribute& distributor,
68  const labelUList& globalToMasterPatch,
69  const labelUList& globalToSlavePatch,
70  coordSetWriter& setFormatter,
71  refPtr<surfaceWriter>& surfFormatter,
72  const bool dryRun
73 )
74 :
75  meshRefiner_(meshRefiner),
76  decomposer_(decomposer),
77  distributor_(distributor),
78  globalToMasterPatch_(globalToMasterPatch),
79  globalToSlavePatch_(globalToSlavePatch),
80  setFormatter_(setFormatter),
81  surfFormatter_(surfFormatter),
82  dryRun_(dryRun)
83 {}
84 
85 
86 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
87 
88 Foam::label Foam::snappyRefineDriver::featureEdgeRefine
89 (
90  const refinementParameters& refineParams,
91  const label maxIter,
92  const label minRefine
93 )
94 {
95  if (dryRun_)
96  {
97  return 0;
98  }
99 
100  if (refineParams.minRefineCells() == -1)
101  {
102  // Special setting to be able to restart shm on meshes with inconsistent
103  // cellLevel/pointLevel
104  return 0;
105  }
106 
107  addProfiling(edge, "snappyHexMesh::refine::edge");
108  const fvMesh& mesh = meshRefiner_.mesh();
109 
110  label iter = 0;
111 
112  if (meshRefiner_.features().size() && maxIter > 0)
113  {
114  for (; iter < maxIter; iter++)
115  {
116  Info<< nl
117  << "Feature refinement iteration " << iter << nl
118  << "------------------------------" << nl
119  << endl;
120 
121  labelList candidateCells
122  (
123  meshRefiner_.refineCandidates
124  (
125  refineParams.locationsInMesh(),
126  refineParams.curvature(),
127  refineParams.planarAngle(),
128 
129  true, // featureRefinement
130  false, // featureDistanceRefinement
131  false, // internalRefinement
132  false, // surfaceRefinement
133  false, // curvatureRefinement
134  false, // smallFeatureRefinement
135  false, // gapRefinement
136  false, // bigGapRefinement
137  false, // spreadGapSize
138  refineParams.maxGlobalCells(),
139  refineParams.maxLocalCells()
140  )
141  );
142  labelList cellsToRefine
143  (
144  meshRefiner_.meshCutter().consistentRefinement
145  (
146  candidateCells,
147  true
148  )
149  );
150  Info<< "Determined cells to refine in = "
151  << mesh.time().cpuTimeIncrement() << " s" << endl;
152 
153 
154 
155  const label nCellsToRefine =
156  returnReduce(cellsToRefine.size(), sumOp<label>());
157 
158  Info<< "Selected for feature refinement : " << nCellsToRefine
159  << " cells (out of " << mesh.globalData().nTotalCells()
160  << ')' << endl;
161 
162  if (nCellsToRefine <= minRefine)
163  {
164  Info<< "Stopping refining since too few cells selected."
165  << nl << endl;
166  break;
167  }
168 
169 
170  if (debug > 0)
171  {
172  const_cast<Time&>(mesh.time())++;
173  }
174 
175 
176  if (returnReduceOr(mesh.nCells() >= refineParams.maxLocalCells()))
177  {
178  meshRefiner_.balanceAndRefine
179  (
180  "feature refinement iteration " + name(iter),
181  decomposer_,
182  distributor_,
183  cellsToRefine,
184  refineParams.maxLoadUnbalance(),
185  refineParams.maxCellUnbalance()
186  );
187  }
188  else
189  {
190  meshRefiner_.refineAndBalance
191  (
192  "feature refinement iteration " + name(iter),
193  decomposer_,
194  distributor_,
195  cellsToRefine,
196  refineParams.maxLoadUnbalance(),
197  refineParams.maxCellUnbalance()
198  );
199  }
200  }
201  }
202  return iter;
203 }
204 
205 
206 Foam::label Foam::snappyRefineDriver::smallFeatureRefine
207 (
208  const refinementParameters& refineParams,
209  const label maxIter
210 )
211 {
212  if (dryRun_)
213  {
214  return 0;
215  }
216 
217  if (refineParams.minRefineCells() == -1)
218  {
219  // Special setting to be able to restart shm on meshes with inconsistent
220  // cellLevel/pointLevel
221  return 0;
222  }
223 
224  addProfiling(feature, "snappyHexMesh::refine::smallFeature");
225  const fvMesh& mesh = meshRefiner_.mesh();
226 
227  label iter = 0;
228 
229  // See if any surface has an extendedGapLevel
230  const labelList surfaceMaxLevel(meshRefiner_.surfaces().maxGapLevel());
231  const labelList shellMaxLevel(meshRefiner_.shells().maxGapLevel());
232  const labelList curvMaxLevel(meshRefiner_.surfaces().maxCurvatureLevel());
233 
234  if
235  (
236  max(surfaceMaxLevel) == 0
237  && max(shellMaxLevel) == 0
238  && max(curvMaxLevel) == 0
239  )
240  {
241  return iter;
242  }
243 
244  for (; iter < maxIter; iter++)
245  {
246  Info<< nl
247  << "Small surface feature refinement iteration " << iter << nl
248  << "--------------------------------------------" << nl
249  << endl;
250 
251 
252  // Determine cells to refine
253  // ~~~~~~~~~~~~~~~~~~~~~~~~~
254 
255  labelList candidateCells
256  (
257  meshRefiner_.refineCandidates
258  (
259  refineParams.locationsInMesh(),
260  refineParams.curvature(),
261  refineParams.planarAngle(),
262 
263  false, // featureRefinement
264  false, // featureDistanceRefinement
265  false, // internalRefinement
266  false, // surfaceRefinement
267  false, // curvatureRefinement
268  true, // smallFeatureRefinement
269  false, // gapRefinement
270  false, // bigGapRefinement
271  false, // spreadGapSize
272  refineParams.maxGlobalCells(),
273  refineParams.maxLocalCells()
274  )
275  );
276 
277  labelList cellsToRefine
278  (
279  meshRefiner_.meshCutter().consistentRefinement
280  (
281  candidateCells,
282  true
283  )
284  );
285  Info<< "Determined cells to refine in = "
286  << mesh.time().cpuTimeIncrement() << " s" << endl;
287 
288 
289  const label nCellsToRefine =
290  returnReduce(cellsToRefine.size(), sumOp<label>());
291 
292  Info<< "Selected for refinement : " << nCellsToRefine
293  << " cells (out of " << mesh.globalData().nTotalCells()
294  << ')' << endl;
295 
296  // Stop when no cells to refine or have done minimum necessary
297  // iterations and not enough cells to refine.
298  if (nCellsToRefine == 0)
299  {
300  Info<< "Stopping refining since too few cells selected."
301  << nl << endl;
302  break;
303  }
304 
305 
306  if (debug)
307  {
308  const_cast<Time&>(mesh.time())++;
309  }
310 
311 
312  if (returnReduceOr(mesh.nCells() >= refineParams.maxLocalCells()))
313  {
314  meshRefiner_.balanceAndRefine
315  (
316  "small feature refinement iteration " + name(iter),
317  decomposer_,
318  distributor_,
319  cellsToRefine,
320  refineParams.maxLoadUnbalance(),
321  refineParams.maxCellUnbalance()
322  );
323  }
324  else
325  {
326  meshRefiner_.refineAndBalance
327  (
328  "small feature refinement iteration " + name(iter),
329  decomposer_,
330  distributor_,
331  cellsToRefine,
332  refineParams.maxLoadUnbalance(),
333  refineParams.maxCellUnbalance()
334  );
335  }
336  }
337  return iter;
338 }
339 
340 
341 Foam::label Foam::snappyRefineDriver::surfaceOnlyRefine
342 (
343  const refinementParameters& refineParams,
344  const label maxIter,
345  const label leakBlockageIter
346 )
347 {
348  if (dryRun_)
349  {
350  return 0;
351  }
352 
353  if (refineParams.minRefineCells() == -1)
354  {
355  // Special setting to be able to restart shm on meshes with inconsistent
356  // cellLevel/pointLevel
357  return 0;
358  }
359 
360  addProfiling(surface, "snappyHexMesh::refine::surface");
361  const fvMesh& mesh = meshRefiner_.mesh();
362  const refinementSurfaces& surfaces = meshRefiner_.surfaces();
363 
364  // Determine the maximum refinement level over all surfaces. This
365  // determines the minimum number of surface refinement iterations.
366  label overallMaxLevel = max(meshRefiner_.surfaces().maxLevel());
367 
368  label iter;
369  for (iter = 0; iter < maxIter; iter++)
370  {
371  Info<< nl
372  << "Surface refinement iteration " << iter << nl
373  << "------------------------------" << nl
374  << endl;
375 
376 
377  // Do optional leak closing (by removing cells)
378  if (iter >= leakBlockageIter)
379  {
380  // Block off intersections with unzoned surfaces with specified
381  // leakLevel < iter
382  const labelList unnamedSurfaces
383  (
385  (
386  surfaces.surfZones()
387  )
388  );
389 
390  DynamicList<label> selectedSurfaces(unnamedSurfaces.size());
391  for (const label surfi : unnamedSurfaces)
392  {
393  const label regioni = surfaces.globalRegion(surfi, 0);
394 
395  // Take shortcut: assume all cells on surface are refined to
396  // its refinement level at iteration iter. So just use the
397  // iteration to see if the surface is a candidate.
398  if (iter > surfaces.leakLevel()[regioni])
399  {
400  selectedSurfaces.append(surfi);
401  }
402  }
403 
404  if
405  (
406  selectedSurfaces.size()
407  && refineParams.locationsOutsideMesh().size()
408  )
409  {
410  meshRefiner_.blockLeakFaces
411  (
412  globalToMasterPatch_,
413  globalToSlavePatch_,
414  refineParams.locationsInMesh(),
415  refineParams.zonesInMesh(),
416  refineParams.locationsOutsideMesh(),
417  selectedSurfaces
418  );
419  }
420  }
421 
422 
423  // Determine cells to refine
424  // ~~~~~~~~~~~~~~~~~~~~~~~~~
425  // Only look at surface intersections (minLevel and surface curvature),
426  // do not do internal refinement (refinementShells)
427 
428  labelList candidateCells
429  (
430  meshRefiner_.refineCandidates
431  (
432  refineParams.locationsInMesh(),
433  refineParams.curvature(),
434  refineParams.planarAngle(),
435 
436  false, // featureRefinement
437  false, // featureDistanceRefinement
438  false, // internalRefinement
439  true, // surfaceRefinement
440  true, // curvatureRefinement
441  false, // smallFeatureRefinement
442  false, // gapRefinement
443  false, // bigGapRefinement
444  false, // spreadGapSize
445  refineParams.maxGlobalCells(),
446  refineParams.maxLocalCells()
447  )
448  );
449  labelList cellsToRefine
450  (
451  meshRefiner_.meshCutter().consistentRefinement
452  (
453  candidateCells,
454  true
455  )
456  );
457  Info<< "Determined cells to refine in = "
458  << mesh.time().cpuTimeIncrement() << " s" << endl;
459 
460 
461  const label nCellsToRefine =
462  returnReduce(cellsToRefine.size(), sumOp<label>());
463 
464  Info<< "Selected for refinement : " << nCellsToRefine
465  << " cells (out of " << mesh.globalData().nTotalCells()
466  << ')' << endl;
467 
468  // Stop when no cells to refine or have done minimum necessary
469  // iterations and not enough cells to refine.
470  if
471  (
472  nCellsToRefine == 0
473  || (
474  iter >= overallMaxLevel
475  && nCellsToRefine <= refineParams.minRefineCells()
476  )
477  )
478  {
479  Info<< "Stopping refining since too few cells selected."
480  << nl << endl;
481  break;
482  }
483 
484 
485  if (debug)
486  {
487  const_cast<Time&>(mesh.time())++;
488  }
489 
490 
491  if (returnReduceOr(mesh.nCells() >= refineParams.maxLocalCells()))
492  {
493  meshRefiner_.balanceAndRefine
494  (
495  "surface refinement iteration " + name(iter),
496  decomposer_,
497  distributor_,
498  cellsToRefine,
499  refineParams.maxLoadUnbalance(),
500  refineParams.maxCellUnbalance()
501  );
502  }
503  else
504  {
505  meshRefiner_.refineAndBalance
506  (
507  "surface refinement iteration " + name(iter),
508  decomposer_,
509  distributor_,
510  cellsToRefine,
511  refineParams.maxLoadUnbalance(),
512  refineParams.maxCellUnbalance()
513  );
514  }
515  }
516  return iter;
517 }
518 
519 
520 Foam::label Foam::snappyRefineDriver::gapOnlyRefine
521 (
522  const refinementParameters& refineParams,
523  const label maxIter
524 )
525 {
526  if (dryRun_)
527  {
528  return 0;
529  }
530 
531  if (refineParams.minRefineCells() == -1)
532  {
533  // Special setting to be able to restart shm on meshes with inconsistent
534  // cellLevel/pointLevel
535  return 0;
536  }
537 
538  const fvMesh& mesh = meshRefiner_.mesh();
539 
540  // Determine the maximum refinement level over all surfaces. This
541  // determines the minimum number of surface refinement iterations.
542 
543  label maxIncrement = 0;
544  const labelList& maxLevel = meshRefiner_.surfaces().maxLevel();
545  const labelList& gapLevel = meshRefiner_.surfaces().gapLevel();
546 
547  forAll(maxLevel, i)
548  {
549  maxIncrement = max(maxIncrement, gapLevel[i]-maxLevel[i]);
550  }
551 
552  label iter = 0;
553 
554  if (maxIncrement == 0)
555  {
556  return iter;
557  }
558 
559  for (iter = 0; iter < maxIter; iter++)
560  {
561  Info<< nl
562  << "Gap refinement iteration " << iter << nl
563  << "--------------------------" << nl
564  << endl;
565 
566 
567  // Determine cells to refine
568  // ~~~~~~~~~~~~~~~~~~~~~~~~~
569  // Only look at surface intersections (minLevel and surface curvature),
570  // do not do internal refinement (refinementShells)
571 
572  labelList candidateCells
573  (
574  meshRefiner_.refineCandidates
575  (
576  refineParams.locationsInMesh(),
577  refineParams.curvature(),
578  refineParams.planarAngle(),
579 
580  false, // featureRefinement
581  false, // featureDistanceRefinement
582  false, // internalRefinement
583  false, // surfaceRefinement
584  false, // curvatureRefinement
585  false, // smallFeatureRefinement
586  true, // gapRefinement
587  false, // bigGapRefinement
588  false, // spreadGapSize
589  refineParams.maxGlobalCells(),
590  refineParams.maxLocalCells()
591  )
592  );
593 
595  {
596  Pout<< "Writing current mesh to time "
597  << meshRefiner_.timeName() << endl;
598  meshRefiner_.write
599  (
602  (
605  ),
606  mesh.time().path()/meshRefiner_.timeName()
607  );
608  Pout<< "Dumped mesh in = "
609  << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
610 
611 
612  Pout<< "Dumping " << candidateCells.size()
613  << " cells to cellSet candidateCellsFromGap." << endl;
614  cellSet c(mesh, "candidateCellsFromGap", candidateCells);
615  c.instance() = meshRefiner_.timeName();
616  c.write();
617  }
618 
619  // Grow by one layer to make sure we're covering the gap
620  {
621  boolList isCandidateCell(mesh.nCells(), false);
622  forAll(candidateCells, i)
623  {
624  isCandidateCell[candidateCells[i]] = true;
625  }
626 
627  for (label i=0; i<1; i++)
628  {
629  boolList newIsCandidateCell(isCandidateCell);
630 
631  // Internal faces
632  for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
633  {
634  label own = mesh.faceOwner()[facei];
635  label nei = mesh.faceNeighbour()[facei];
636 
637  if (isCandidateCell[own] != isCandidateCell[nei])
638  {
639  newIsCandidateCell[own] = true;
640  newIsCandidateCell[nei] = true;
641  }
642  }
643 
644  // Get coupled boundary condition values
645  boolList neiIsCandidateCell;
647  (
648  mesh,
649  isCandidateCell,
650  neiIsCandidateCell
651  );
652 
653  // Boundary faces
654  for
655  (
656  label facei = mesh.nInternalFaces();
657  facei < mesh.nFaces();
658  facei++
659  )
660  {
661  label own = mesh.faceOwner()[facei];
662  label bFacei = facei-mesh.nInternalFaces();
663 
664  if (isCandidateCell[own] != neiIsCandidateCell[bFacei])
665  {
666  newIsCandidateCell[own] = true;
667  }
668  }
669 
670  isCandidateCell.transfer(newIsCandidateCell);
671  }
672 
673  label n = 0;
674  forAll(isCandidateCell, celli)
675  {
676  if (isCandidateCell[celli])
677  {
678  n++;
679  }
680  }
681  candidateCells.setSize(n);
682  n = 0;
683  forAll(isCandidateCell, celli)
684  {
685  if (isCandidateCell[celli])
686  {
687  candidateCells[n++] = celli;
688  }
689  }
690  }
691 
692 
694  {
695  Pout<< "Dumping " << candidateCells.size()
696  << " cells to cellSet candidateCellsFromGapPlusBuffer." << endl;
697  cellSet c(mesh, "candidateCellsFromGapPlusBuffer", candidateCells);
698  c.instance() = meshRefiner_.timeName();
699  c.write();
700  }
701 
702 
703  labelList cellsToRefine
704  (
705  meshRefiner_.meshCutter().consistentRefinement
706  (
707  candidateCells,
708  true
709  )
710  );
711  Info<< "Determined cells to refine in = "
712  << mesh.time().cpuTimeIncrement() << " s" << endl;
713 
714 
715  const label nCellsToRefine =
716  returnReduce(cellsToRefine.size(), sumOp<label>());
717 
718  Info<< "Selected for refinement : " << nCellsToRefine
719  << " cells (out of " << mesh.globalData().nTotalCells()
720  << ')' << endl;
721 
722  // Stop when no cells to refine or have done minimum necessary
723  // iterations and not enough cells to refine.
724  if
725  (
726  nCellsToRefine == 0
727  || (
728  iter >= maxIncrement
729  && nCellsToRefine <= refineParams.minRefineCells()
730  )
731  )
732  {
733  Info<< "Stopping refining since too few cells selected."
734  << nl << endl;
735  break;
736  }
737 
738 
739  if (debug)
740  {
741  const_cast<Time&>(mesh.time())++;
742  }
743 
744 
745  if (returnReduceOr(mesh.nCells() >= refineParams.maxLocalCells()))
746  {
747  meshRefiner_.balanceAndRefine
748  (
749  "gap refinement iteration " + name(iter),
750  decomposer_,
751  distributor_,
752  cellsToRefine,
753  refineParams.maxLoadUnbalance(),
754  refineParams.maxCellUnbalance()
755  );
756  }
757  else
758  {
759  meshRefiner_.refineAndBalance
760  (
761  "gap refinement iteration " + name(iter),
762  decomposer_,
763  distributor_,
764  cellsToRefine,
765  refineParams.maxLoadUnbalance(),
766  refineParams.maxCellUnbalance()
767  );
768  }
769  }
770  return iter;
771 }
772 
773 
774 Foam::label Foam::snappyRefineDriver::surfaceProximityBlock
775 (
776  const refinementParameters& refineParams,
777  const label maxIter
778 )
779 {
780  if (refineParams.minRefineCells() == -1)
781  {
782  // Special setting to be able to restart shm on meshes with inconsistent
783  // cellLevel/pointLevel
784  return 0;
785  }
786 
787  fvMesh& mesh = meshRefiner_.mesh();
788 
789  if (min(meshRefiner_.surfaces().blockLevel()) == labelMax)
790  {
791  return 0;
792  }
793 
794  label iter = 0;
795 
796  for (iter = 0; iter < maxIter; iter++)
797  {
798  Info<< nl
799  << "Gap blocking iteration " << iter << nl
800  << "------------------------" << nl
801  << endl;
802 
803 
804  // Determine cells to block
805  // ~~~~~~~~~~~~~~~~~~~~~~~~
806 
807  meshRefiner_.removeGapCells
808  (
809  refineParams.planarAngle(),
810  meshRefiner_.surfaces().blockLevel(),
811  globalToMasterPatch_,
812  refineParams.nFilterIter()
813  );
814 
815  if (debug)
816  {
817  const_cast<Time&>(mesh.time())++;
818  Pout<< "Writing gap blocking iteration "
819  << iter << " mesh to time " << meshRefiner_.timeName()
820  << endl;
821  meshRefiner_.write
822  (
825  (
828  ),
829  mesh.time().path()/meshRefiner_.timeName()
830  );
831  }
832  }
833  return iter;
834 }
835 
836 
837 Foam::label Foam::snappyRefineDriver::bigGapOnlyRefine
838 (
839  const refinementParameters& refineParams,
840  const bool spreadGapSize,
841  const label maxIter
842 )
843 {
844  if (refineParams.minRefineCells() == -1)
845  {
846  // Special setting to be able to restart shm on meshes with inconsistent
847  // cellLevel/pointLevel
848  return 0;
849  }
850 
851  if (dryRun_)
852  {
853  return 0;
854  }
855 
856  const fvMesh& mesh = meshRefiner_.mesh();
857 
858  label iter = 0;
859 
860  // See if any surface has an extendedGapLevel
861  labelList surfaceMaxLevel(meshRefiner_.surfaces().maxGapLevel());
862  labelList shellMaxLevel(meshRefiner_.shells().maxGapLevel());
863 
864  label overallMaxLevel(max(max(surfaceMaxLevel), max(shellMaxLevel)));
865 
866  if (overallMaxLevel == 0)
867  {
868  return iter;
869  }
870 
871 
872  for (; iter < maxIter; iter++)
873  {
874  Info<< nl
875  << "Big gap refinement iteration " << iter << nl
876  << "------------------------------" << nl
877  << endl;
878 
879 
880  // Determine cells to refine
881  // ~~~~~~~~~~~~~~~~~~~~~~~~~
882 
883  labelList candidateCells
884  (
885  meshRefiner_.refineCandidates
886  (
887  refineParams.locationsInMesh(),
888  refineParams.curvature(),
889  refineParams.planarAngle(),
890 
891  false, // featureRefinement
892  false, // featureDistanceRefinement
893  false, // internalRefinement
894  false, // surfaceRefinement
895  false, // curvatureRefinement
896  false, // smallFeatureRefinement
897  false, // gapRefinement
898  true, // bigGapRefinement
899  spreadGapSize, // spreadGapSize
900  refineParams.maxGlobalCells(),
901  refineParams.maxLocalCells()
902  )
903  );
904 
905 
907  {
908  Pout<< "Writing current mesh to time "
909  << meshRefiner_.timeName() << endl;
910  meshRefiner_.write
911  (
914  (
917  ),
918  mesh.time().path()/meshRefiner_.timeName()
919  );
920  Pout<< "Dumped mesh in = "
921  << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
922 
923  Pout<< "Dumping " << candidateCells.size()
924  << " cells to cellSet candidateCellsFromBigGap." << endl;
925  cellSet c(mesh, "candidateCellsFromBigGap", candidateCells);
926  c.instance() = meshRefiner_.timeName();
927  c.write();
928  }
929 
930  labelList cellsToRefine
931  (
932  meshRefiner_.meshCutter().consistentRefinement
933  (
934  candidateCells,
935  true
936  )
937  );
938  Info<< "Determined cells to refine in = "
939  << mesh.time().cpuTimeIncrement() << " s" << endl;
940 
941 
942  const label nCellsToRefine =
943  returnReduce(cellsToRefine.size(), sumOp<label>());
944 
945  Info<< "Selected for refinement : " << nCellsToRefine
946  << " cells (out of " << mesh.globalData().nTotalCells()
947  << ')' << endl;
948 
949  // Stop when no cells to refine or have done minimum necessary
950  // iterations and not enough cells to refine.
951  if
952  (
953  nCellsToRefine == 0
954  || (
955  iter >= overallMaxLevel
956  && nCellsToRefine <= refineParams.minRefineCells()
957  )
958  )
959  {
960  Info<< "Stopping refining since too few cells selected."
961  << nl << endl;
962  break;
963  }
964 
965 
966  if (debug)
967  {
968  const_cast<Time&>(mesh.time())++;
969  }
970 
971 
972  if (returnReduceOr(mesh.nCells() >= refineParams.maxLocalCells()))
973  {
974  meshRefiner_.balanceAndRefine
975  (
976  "big gap refinement iteration " + name(iter),
977  decomposer_,
978  distributor_,
979  cellsToRefine,
980  refineParams.maxLoadUnbalance(),
981  refineParams.maxCellUnbalance()
982  );
983  }
984  else
985  {
986  meshRefiner_.refineAndBalance
987  (
988  "big gap refinement iteration " + name(iter),
989  decomposer_,
990  distributor_,
991  cellsToRefine,
992  refineParams.maxLoadUnbalance(),
993  refineParams.maxCellUnbalance()
994  );
995  }
996  }
997  return iter;
998 }
999 
1000 
1001 Foam::label Foam::snappyRefineDriver::danglingCellRefine
1002 (
1003  const refinementParameters& refineParams,
1004  const label nFaces,
1005  const label maxIter
1006 )
1007 {
1008  if (refineParams.minRefineCells() == -1)
1009  {
1010  // Special setting to be able to restart shm on meshes with inconsistent
1011  // cellLevel/pointLevel
1012  return 0;
1013  }
1014 
1015  if (dryRun_)
1016  {
1017  return 0;
1018  }
1019 
1020  addProfiling(dangling, "snappyHexMesh::refine::danglingCell");
1021  const fvMesh& mesh = meshRefiner_.mesh();
1022 
1023  label iter;
1024  for (iter = 0; iter < maxIter; iter++)
1025  {
1026  Info<< nl
1027  << "Dangling coarse cells refinement iteration " << iter << nl
1028  << "--------------------------------------------" << nl
1029  << endl;
1030 
1031 
1032  // Determine cells to refine
1033  // ~~~~~~~~~~~~~~~~~~~~~~~~~
1034 
1035  const cellList& cells = mesh.cells();
1036  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
1037 
1038  labelList candidateCells;
1039  {
1040  cellSet candidateCellSet(mesh, "candidateCells", cells.size()/1000);
1041 
1042  forAll(cells, celli)
1043  {
1044  label nIntFaces = 0;
1045  for (const label meshFacei : cells[celli])
1046  {
1047  const label patchi = pbm.patchID(meshFacei);
1048 
1049  if (patchi < 0 || pbm[patchi].coupled())
1050  {
1051  ++nIntFaces;
1052  }
1053  }
1054 
1055  if (nIntFaces == nFaces)
1056  {
1057  candidateCellSet.insert(celli);
1058  }
1059  }
1060 
1062  {
1063  Pout<< "Dumping " << candidateCellSet.size()
1064  << " cells to cellSet candidateCellSet." << endl;
1065  candidateCellSet.instance() = meshRefiner_.timeName();
1066  candidateCellSet.write();
1067  }
1068  candidateCells = candidateCellSet.toc();
1069  }
1070 
1071 
1072 
1073  labelList cellsToRefine
1074  (
1075  meshRefiner_.meshCutter().consistentRefinement
1076  (
1077  candidateCells,
1078  true
1079  )
1080  );
1081  Info<< "Determined cells to refine in = "
1082  << mesh.time().cpuTimeIncrement() << " s" << endl;
1083 
1084 
1085  const label nCellsToRefine =
1086  returnReduce(cellsToRefine.size(), sumOp<label>());
1087 
1088  Info<< "Selected for refinement : " << nCellsToRefine
1089  << " cells (out of " << mesh.globalData().nTotalCells()
1090  << ')' << endl;
1091 
1092  // Stop when no cells to refine. After a few iterations check if too
1093  // few cells
1094  if
1095  (
1096  nCellsToRefine == 0
1097  || (
1098  iter >= 1
1099  && nCellsToRefine <= refineParams.minRefineCells()
1100  )
1101  )
1102  {
1103  Info<< "Stopping refining since too few cells selected."
1104  << nl << endl;
1105 
1106  if (refineParams.balanceAtEnd())
1107  {
1108  Info<< "Final mesh balancing" << endl;
1109 
1110  meshRefiner_.balance
1111  (
1112  "",
1113  decomposer_,
1114  distributor_,
1115  cellsToRefine,
1116  0,
1117  -1
1118  );
1119  }
1120 
1121  break;
1122  }
1123 
1124 
1125  if (debug)
1126  {
1127  const_cast<Time&>(mesh.time())++;
1128  }
1129 
1130 
1131  if (returnReduceOr(mesh.nCells() >= refineParams.maxLocalCells()))
1132  {
1133  meshRefiner_.balanceAndRefine
1134  (
1135  "coarse cell refinement iteration " + name(iter),
1136  decomposer_,
1137  distributor_,
1138  cellsToRefine,
1139  refineParams.maxLoadUnbalance(),
1140  refineParams.maxCellUnbalance()
1141  );
1142  }
1143  else
1144  {
1145  meshRefiner_.refineAndBalance
1146  (
1147  "coarse cell refinement iteration " + name(iter),
1148  decomposer_,
1149  distributor_,
1150  cellsToRefine,
1151  refineParams.maxLoadUnbalance(),
1152  refineParams.maxCellUnbalance()
1153  );
1154  }
1155  }
1156  return iter;
1157 }
1158 
1159 
1160 // Detect cells with opposing intersected faces of differing refinement
1161 // level and refine them.
1162 Foam::label Foam::snappyRefineDriver::refinementInterfaceRefine
1163 (
1164  const refinementParameters& refineParams,
1165  const label maxIter
1166 )
1167 {
1168  if (refineParams.minRefineCells() == -1)
1169  {
1170  // Special setting to be able to restart shm on meshes with inconsistent
1171  // cellLevel/pointLevel
1172  return 0;
1173  }
1174 
1175  if (dryRun_)
1176  {
1177  return 0;
1178  }
1179 
1180  addProfiling(interface, "snappyHexMesh::refine::transition");
1181  const fvMesh& mesh = meshRefiner_.mesh();
1182 
1183  label iter = 0;
1184 
1185  if (refineParams.interfaceRefine())
1186  {
1187  for (;iter < maxIter; iter++)
1188  {
1189  Info<< nl
1190  << "Refinement transition refinement iteration " << iter << nl
1191  << "--------------------------------------------" << nl
1192  << endl;
1193 
1194  const labelList& surfaceIndex = meshRefiner_.surfaceIndex();
1195  const hexRef8& cutter = meshRefiner_.meshCutter();
1196  const vectorField& fA = mesh.faceAreas();
1197  const labelList& faceOwner = mesh.faceOwner();
1198 
1199 
1200  // Determine cells to refine
1201  // ~~~~~~~~~~~~~~~~~~~~~~~~~
1202 
1203  const cellList& cells = mesh.cells();
1204 
1205  labelList candidateCells;
1206  {
1207  // Pass1: pick up cells with differing face level
1208 
1209  cellSet transitionCells
1210  (
1211  mesh,
1212  "transitionCells",
1213  cells.size()/100
1214  );
1215 
1216  forAll(cells, celli)
1217  {
1218  const cell& cFaces = cells[celli];
1219  label cLevel = cutter.cellLevel()[celli];
1220 
1221  forAll(cFaces, cFacei)
1222  {
1223  label facei = cFaces[cFacei];
1224 
1225  if (surfaceIndex[facei] != -1)
1226  {
1227  label fLevel = cutter.faceLevel(facei);
1228  if (fLevel != cLevel)
1229  {
1230  transitionCells.insert(celli);
1231  }
1232  }
1233  }
1234  }
1235 
1236 
1237  cellSet candidateCellSet
1238  (
1239  mesh,
1240  "candidateCells",
1241  cells.size()/1000
1242  );
1243 
1244  // Pass2: check for oppositeness
1245 
1246  //for (const label celli : transitionCells)
1247  //{
1248  // const cell& cFaces = cells[celli];
1249  // const point& cc = cellCentres[celli];
1250  // const scalar rCVol = pow(cellVolumes[celli], -5.0/3.0);
1251  //
1252  // // Determine principal axes of cell
1253  // symmTensor R(Zero);
1254  //
1255  // forAll(cFaces, i)
1256  // {
1257  // label facei = cFaces[i];
1258  //
1259  // const point& fc = faceCentres[facei];
1260  //
1261  // // Calculate face-pyramid volume
1262  // scalar pyrVol = 1.0/3.0 * fA[facei] & (fc-cc);
1263  //
1264  // if (faceOwner[facei] != celli)
1265  // {
1266  // pyrVol = -pyrVol;
1267  // }
1268  //
1269  // // Calculate face-pyramid centre
1270  // vector pc = (3.0/4.0)*fc + (1.0/4.0)*cc;
1271  //
1272  // R += pyrVol*sqr(pc-cc)*rCVol;
1273  // }
1274  //
1275  // //- MEJ: Problem: truncation errors cause complex evs
1276  // vector lambdas(eigenValues(R));
1277  // const tensor axes(eigenVectors(R, lambdas));
1278  //
1279  //
1280  // // Check if this cell has
1281  // // - opposing sides intersected
1282  // // - which are of different refinement level
1283  // // - plus the inbetween face
1284  //
1285  // labelVector plusFaceLevel(labelVector(-1, -1, -1));
1286  // labelVector minFaceLevel(labelVector(-1, -1, -1));
1287  //
1288  // forAll(cFaces, cFacei)
1289  // {
1290  // label facei = cFaces[cFacei];
1291  //
1292  // if (surfaceIndex[facei] != -1)
1293  // {
1294  // label fLevel = cutter.faceLevel(facei);
1295  //
1296  // // Get outwards pointing normal
1297  // vector n = fA[facei]/mag(fA[facei]);
1298  // if (faceOwner[facei] != celli)
1299  // {
1300  // n = -n;
1301  // }
1302  //
1303  // // What is major direction and sign
1304  // direction cmpt = vector::X;
1305  // scalar maxComp = (n&axes.x());
1306  //
1307  // scalar yComp = (n&axes.y());
1308  // scalar zComp = (n&axes.z());
1309  //
1310  // if (mag(yComp) > mag(maxComp))
1311  // {
1312  // maxComp = yComp;
1313  // cmpt = vector::Y;
1314  // }
1315  //
1316  // if (mag(zComp) > mag(maxComp))
1317  // {
1318  // maxComp = zComp;
1319  // cmpt = vector::Z;
1320  // }
1321  //
1322  // if (maxComp > 0)
1323  // {
1324  // plusFaceLevel[cmpt] = max
1325  // (
1326  // plusFaceLevel[cmpt],
1327  // fLevel
1328  // );
1329  // }
1330  // else
1331  // {
1332  // minFaceLevel[cmpt] = max
1333  // (
1334  // minFaceLevel[cmpt],
1335  // fLevel
1336  // );
1337  // }
1338  // }
1339  // }
1340  //
1341  // // Check if we picked up any opposite differing level
1342  // for (direction dir = 0; dir < vector::nComponents; dir++)
1343  // {
1344  // if
1345  // (
1346  // plusFaceLevel[dir] != -1
1347  // && minFaceLevel[dir] != -1
1348  // && plusFaceLevel[dir] != minFaceLevel[dir]
1349  // )
1350  // {
1351  // candidateCellSet.insert(celli);
1352  // }
1353  // }
1354  //}
1355 
1356  const scalar oppositeCos = Foam::cos(degToRad(135.0));
1357 
1358  for (const label celli : transitionCells)
1359  {
1360  const cell& cFaces = cells[celli];
1361  label cLevel = cutter.cellLevel()[celli];
1362 
1363  // Detect opposite intersection
1364  bool foundOpposite = false;
1365 
1366  forAll(cFaces, cFacei)
1367  {
1368  label facei = cFaces[cFacei];
1369 
1370  if
1371  (
1372  surfaceIndex[facei] != -1
1373  && cutter.faceLevel(facei) > cLevel
1374  )
1375  {
1376  // Get outwards pointing normal
1377  vector n = fA[facei]/mag(fA[facei]);
1378  if (faceOwner[facei] != celli)
1379  {
1380  n = -n;
1381  }
1382 
1383  // Check for any opposite intersection
1384  forAll(cFaces, cFaceI2)
1385  {
1386  label face2i = cFaces[cFaceI2];
1387 
1388  if
1389  (
1390  face2i != facei
1391  && surfaceIndex[face2i] != -1
1392  && cutter.faceLevel(face2i) > cLevel
1393  )
1394  {
1395  // Get outwards pointing normal
1396  vector n2 = fA[face2i]/mag(fA[face2i]);
1397  if (faceOwner[face2i] != celli)
1398  {
1399  n2 = -n2;
1400  }
1401 
1402 
1403  if ((n&n2) < oppositeCos)
1404  {
1405  foundOpposite = true;
1406  break;
1407  }
1408  }
1409  }
1410 
1411  if (foundOpposite)
1412  {
1413  break;
1414  }
1415  }
1416  }
1417 
1418 
1419  if (foundOpposite)
1420  {
1421  candidateCellSet.insert(celli);
1422  }
1423  }
1424 
1426  {
1427  Pout<< "Dumping " << candidateCellSet.size()
1428  << " cells to cellSet candidateCellSet." << endl;
1429  candidateCellSet.instance() = meshRefiner_.timeName();
1430  candidateCellSet.write();
1431  }
1432  candidateCells = candidateCellSet.toc();
1433  }
1434 
1435 
1436 
1437  labelList cellsToRefine
1438  (
1439  meshRefiner_.meshCutter().consistentRefinement
1440  (
1441  candidateCells,
1442  true
1443  )
1444  );
1445  Info<< "Determined cells to refine in = "
1446  << mesh.time().cpuTimeIncrement() << " s" << endl;
1447 
1448 
1449  const label nCellsToRefine =
1450  returnReduce(cellsToRefine.size(), sumOp<label>());
1451 
1452  Info<< "Selected for refinement : " << nCellsToRefine
1453  << " cells (out of " << mesh.globalData().nTotalCells()
1454  << ')' << endl;
1455 
1456  // Stop when no cells to refine. After a few iterations check if too
1457  // few cells
1458  if
1459  (
1460  nCellsToRefine == 0
1461  || (
1462  iter >= 1
1463  && nCellsToRefine <= refineParams.minRefineCells()
1464  )
1465  )
1466  {
1467  Info<< "Stopping refining since too few cells selected."
1468  << nl << endl;
1469  break;
1470  }
1471 
1472 
1473  if (debug)
1474  {
1475  const_cast<Time&>(mesh.time())++;
1476  }
1477 
1478 
1479  if (returnReduceOr(mesh.nCells() >= refineParams.maxLocalCells()))
1480  {
1481  meshRefiner_.balanceAndRefine
1482  (
1483  "interface cell refinement iteration " + name(iter),
1484  decomposer_,
1485  distributor_,
1486  cellsToRefine,
1487  refineParams.maxLoadUnbalance(),
1488  refineParams.maxCellUnbalance()
1489  );
1490  }
1491  else
1492  {
1493  meshRefiner_.refineAndBalance
1494  (
1495  "interface cell refinement iteration " + name(iter),
1496  decomposer_,
1497  distributor_,
1498  cellsToRefine,
1499  refineParams.maxLoadUnbalance(),
1500  refineParams.maxCellUnbalance()
1501  );
1502  }
1503  }
1504  }
1505  return iter;
1506 }
1507 
1508 bool Foam::snappyRefineDriver::usesHigherLevel
1509 (
1510  const labelUList& boundaryPointLevel,
1511  const labelUList& f,
1512  const label cLevel
1513 ) const
1514 {
1515  for (const label pointi : f)
1516  {
1517  if (boundaryPointLevel[pointi] > cLevel)
1518  {
1519  return true;
1520  }
1521  }
1522  return false;
1523 }
1524 
1525 
1526 Foam::label Foam::snappyRefineDriver::boundaryRefinementInterfaceRefine
1527 (
1528  const refinementParameters& refineParams,
1529  const label maxIter
1530 )
1531 {
1532  if (refineParams.minRefineCells() == -1)
1533  {
1534  // Special setting to be able to restart shm on meshes with inconsistent
1535  // cellLevel/pointLevel
1536  return 0;
1537  }
1538 
1539  if (dryRun_)
1540  {
1541  return 0;
1542  }
1543 
1544  addProfiling(interface, "snappyHexMesh::refine::transition");
1545  const fvMesh& mesh = meshRefiner_.mesh();
1546 
1547  label iter = 0;
1548 
1549  if (refineParams.interfaceRefine())
1550  {
1551  for (;iter < maxIter; iter++)
1552  {
1553  Info<< nl
1554  << "Boundary refinement iteration " << iter << nl
1555  << "-------------------------------" << nl
1556  << endl;
1557 
1558  const labelList& surfaceIndex = meshRefiner_.surfaceIndex();
1559  const hexRef8& cutter = meshRefiner_.meshCutter();
1560  const labelList& cellLevel = cutter.cellLevel();
1561  const faceList& faces = mesh.faces();
1562  const cellList& cells = mesh.cells();
1563 
1564 
1565  // Determine cells to refine
1566  // ~~~~~~~~~~~~~~~~~~~~~~~~~
1567 
1568  // Point/face on boundary
1569  bitSet isBoundaryPoint(mesh.nPoints());
1570  bitSet isBoundaryFace(mesh.nFaces());
1571  {
1572  forAll(surfaceIndex, facei)
1573  {
1574  if (surfaceIndex[facei] != -1)
1575  {
1576  isBoundaryFace.set(facei);
1577  isBoundaryPoint.set(faces[facei]);
1578  }
1579  }
1580  const labelList meshPatchIDs(meshRefiner_.meshedPatches());
1581  for (const label patchi : meshPatchIDs)
1582  {
1583  const polyPatch& pp = mesh.boundaryMesh()[patchi];
1584  forAll(pp, i)
1585  {
1586  isBoundaryFace.set(pp.start()+i);
1587  isBoundaryPoint.set(pp[i]);
1588  }
1589  }
1590 
1592  (
1593  mesh,
1594  isBoundaryPoint,
1595  orEqOp<unsigned int>(),
1596  0
1597  );
1598  }
1599 
1600  // Mark max boundary face level onto boundary points. All points
1601  // not on a boundary face stay 0.
1602  labelList boundaryPointLevel(mesh.nPoints(), 0);
1603  {
1604  forAll(cells, celli)
1605  {
1606  const cell& cFaces = cells[celli];
1607  const label cLevel = cellLevel[celli];
1608 
1609  for (const label facei : cFaces)
1610  {
1611  if (isBoundaryFace(facei))
1612  {
1613  const face& f = faces[facei];
1614  for (const label pointi : f)
1615  {
1616  boundaryPointLevel[pointi] =
1617  max
1618  (
1619  boundaryPointLevel[pointi],
1620  cLevel
1621  );
1622  }
1623  }
1624  }
1625  }
1626 
1628  (
1629  mesh,
1630  boundaryPointLevel,
1631  maxEqOp<label>(),
1632  label(0)
1633  );
1634  }
1635 
1636 
1637  // Detect cells with a point but not face on the boundary
1638  labelList candidateCells;
1639  {
1640  const cellList& cells = mesh.cells();
1641 
1642  cellSet candidateCellSet
1643  (
1644  mesh,
1645  "candidateCells",
1646  cells.size()/100
1647  );
1648 
1649  forAll(cells, celli)
1650  {
1651  const cell& cFaces = cells[celli];
1652  const label cLevel = cellLevel[celli];
1653 
1654  bool isBoundaryCell = false;
1655  for (const label facei : cFaces)
1656  {
1657  if (isBoundaryFace(facei))
1658  {
1659  isBoundaryCell = true;
1660  break;
1661  }
1662  }
1663 
1664  if (!isBoundaryCell)
1665  {
1666  for (const label facei : cFaces)
1667  {
1668  const face& f = mesh.faces()[facei];
1669  if (usesHigherLevel(boundaryPointLevel, f, cLevel))
1670  {
1671  candidateCellSet.insert(celli);
1672  break;
1673  }
1674  }
1675  }
1676  }
1678  {
1679  Pout<< "Dumping " << candidateCellSet.size()
1680  << " cells to cellSet candidateCellSet." << endl;
1681  candidateCellSet.instance() = meshRefiner_.timeName();
1682  candidateCellSet.write();
1683  }
1684  candidateCells = candidateCellSet.toc();
1685  }
1686 
1687  labelList cellsToRefine
1688  (
1689  meshRefiner_.meshCutter().consistentRefinement
1690  (
1691  candidateCells,
1692  true
1693  )
1694  );
1695  Info<< "Determined cells to refine in = "
1696  << mesh.time().cpuTimeIncrement() << " s" << endl;
1697 
1698 
1699  const label nCellsToRefine =
1700  returnReduce(cellsToRefine.size(), sumOp<label>());
1701 
1702  Info<< "Selected for refinement : " << nCellsToRefine
1703  << " cells (out of " << mesh.globalData().nTotalCells()
1704  << ')' << endl;
1705 
1706  // Stop when no cells to refine. After a few iterations check if too
1707  // few cells
1708  if
1709  (
1710  nCellsToRefine == 0
1711  // || (
1712  // iter >= 1
1713  // && nCellsToRefine <= refineParams.minRefineCells()
1714  // )
1715  )
1716  {
1717  Info<< "Stopping refining since too few cells selected."
1718  << nl << endl;
1719  break;
1720  }
1721 
1722 
1723  if (debug)
1724  {
1725  const_cast<Time&>(mesh.time())++;
1726  }
1727 
1728 
1729  if (returnReduceOr(mesh.nCells() >= refineParams.maxLocalCells()))
1730  {
1731  meshRefiner_.balanceAndRefine
1732  (
1733  "boundary cell refinement iteration " + name(iter),
1734  decomposer_,
1735  distributor_,
1736  cellsToRefine,
1737  refineParams.maxLoadUnbalance(),
1738  refineParams.maxCellUnbalance()
1739  );
1740  }
1741  else
1742  {
1743  meshRefiner_.refineAndBalance
1744  (
1745  "boundary cell refinement iteration " + name(iter),
1746  decomposer_,
1747  distributor_,
1748  cellsToRefine,
1749  refineParams.maxLoadUnbalance(),
1750  refineParams.maxCellUnbalance()
1751  );
1752  }
1753  }
1754  }
1755  return iter;
1756 }
1757 
1758 
1759 void Foam::snappyRefineDriver::removeInsideCells
1760 (
1761  const refinementParameters& refineParams,
1762  const label nBufferLayers
1763 )
1764 {
1765  // Skip if no limitRegion and zero bufferLayers
1766  if (meshRefiner_.limitShells().shells().size() == 0 && nBufferLayers == 0)
1767  {
1768  return;
1769  }
1770 
1771  if (dryRun_)
1772  {
1773  return;
1774  }
1775 
1776  Info<< nl
1777  << "Removing mesh beyond surface intersections" << nl
1778  << "------------------------------------------" << nl
1779  << endl;
1780 
1781  const fvMesh& mesh = meshRefiner_.mesh();
1782 
1783  if (debug)
1784  {
1785  const_cast<Time&>(mesh.time())++;
1786  }
1787 
1788  // Remove any cells inside limitShells with level -1
1789  if (meshRefiner_.limitShells().shells().size())
1790  {
1791  meshRefiner_.removeLimitShells
1792  (
1793  nBufferLayers,
1794  1,
1795  globalToMasterPatch_,
1796  globalToSlavePatch_,
1797  refineParams.locationsInMesh(),
1798  refineParams.zonesInMesh(),
1799  refineParams.locationsOutsideMesh()
1800  );
1801  }
1802 
1803 
1804  // Fix any additional (e.g. locationsOutsideMesh). Note: probably not
1805  // necessary.
1806  meshRefiner_.splitMesh
1807  (
1808  nBufferLayers, // nBufferLayers
1809  refineParams.nErodeCellZone(),
1810  globalToMasterPatch_,
1811  globalToSlavePatch_,
1812  refineParams.locationsInMesh(),
1813  refineParams.zonesInMesh(),
1814  refineParams.locationsOutsideMesh(),
1815  !refineParams.useLeakClosure(),
1816  setFormatter_,
1817  surfFormatter_
1818  );
1819 
1821  {
1822  const_cast<Time&>(mesh.time())++;
1823 
1824  Pout<< "Writing subsetted mesh to time "
1825  << meshRefiner_.timeName() << endl;
1826  meshRefiner_.write
1827  (
1830  (
1833  ),
1834  mesh.time().path()/meshRefiner_.timeName()
1835  );
1836  Pout<< "Dumped mesh in = "
1837  << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
1838  }
1839 }
1840 
1841 
1842 Foam::label Foam::snappyRefineDriver::shellRefine
1843 (
1844  const refinementParameters& refineParams,
1845  const label maxIter
1846 )
1847 {
1848  if (dryRun_)
1849  {
1850  return 0;
1851  }
1852 
1853  if (refineParams.minRefineCells() == -1)
1854  {
1855  // Special setting to be able to restart shm on meshes with inconsistent
1856  // cellLevel/pointLevel
1857  return 0;
1858  }
1859 
1860  addProfiling(shell, "snappyHexMesh::refine::shell");
1861  const fvMesh& mesh = meshRefiner_.mesh();
1862 
1863  // Mark current boundary faces with 0. Have meshRefiner maintain them.
1864  meshRefiner_.userFaceData().setSize(1);
1865 
1866  // mark list to remove any refined faces
1867  meshRefiner_.userFaceData()[0].first() = meshRefinement::REMOVE;
1868  meshRefiner_.userFaceData()[0].second() = ListOps::createWithValue<label>
1869  (
1870  mesh.nFaces(),
1871  meshRefiner_.intersectedFaces(),
1872  0, // set value
1873  -1 // default value
1874  );
1875 
1876  // Determine the maximum refinement level over all volume refinement
1877  // regions. This determines the minimum number of shell refinement
1878  // iterations.
1879  label overallMaxShellLevel = meshRefiner_.shells().maxLevel();
1880 
1881  label iter;
1882  for (iter = 0; iter < maxIter; iter++)
1883  {
1884  Info<< nl
1885  << "Shell refinement iteration " << iter << nl
1886  << "----------------------------" << nl
1887  << endl;
1888 
1889  labelList candidateCells
1890  (
1891  meshRefiner_.refineCandidates
1892  (
1893  refineParams.locationsInMesh(),
1894  refineParams.curvature(),
1895  refineParams.planarAngle(),
1896 
1897  false, // featureRefinement
1898  true, // featureDistanceRefinement
1899  true, // internalRefinement
1900  false, // surfaceRefinement
1901  false, // curvatureRefinement
1902  false, // smallFeatureRefinement
1903  false, // gapRefinement
1904  false, // bigGapRefinement
1905  false, // spreadGapSize
1906  refineParams.maxGlobalCells(),
1907  refineParams.maxLocalCells()
1908  )
1909  );
1910 
1912  {
1913  Pout<< "Dumping " << candidateCells.size()
1914  << " cells to cellSet candidateCellsFromShells." << endl;
1915 
1916  cellSet c(mesh, "candidateCellsFromShells", candidateCells);
1917  c.instance() = meshRefiner_.timeName();
1918  c.write();
1919  }
1920 
1921  // Problem choosing starting faces for bufferlayers (bFaces)
1922  // - we can't use the current intersected boundary faces
1923  // (intersectedFaces) since this grows indefinitely
1924  // - if we use 0 faces we don't satisfy bufferLayers from the
1925  // surface.
1926  // - possibly we want to have bFaces only the initial set of faces
1927  // and maintain the list while doing the refinement.
1928  labelList bFaces
1929  (
1930  findIndices(meshRefiner_.userFaceData()[0].second(), 0)
1931  );
1932 
1933  //Info<< "Collected boundary faces : "
1934  // << returnReduce(bFaces.size(), sumOp<label>()) << endl;
1935 
1936  labelList cellsToRefine;
1937 
1938  if (refineParams.nBufferLayers() <= 2)
1939  {
1940  cellsToRefine = meshRefiner_.meshCutter().consistentSlowRefinement
1941  (
1942  refineParams.nBufferLayers(),
1943  candidateCells, // cells to refine
1944  bFaces, // faces for nBufferLayers
1945  1, // point difference
1946  meshRefiner_.intersectedPoints() // points to check
1947  );
1948  }
1949  else
1950  {
1951  cellsToRefine = meshRefiner_.meshCutter().consistentSlowRefinement2
1952  (
1953  refineParams.nBufferLayers(),
1954  candidateCells, // cells to refine
1955  bFaces // faces for nBufferLayers
1956  );
1957  }
1958 
1959  Info<< "Determined cells to refine in = "
1960  << mesh.time().cpuTimeIncrement() << " s" << endl;
1961 
1962 
1963  const label nCellsToRefine =
1964  returnReduce(cellsToRefine.size(), sumOp<label>());
1965 
1966  Info<< "Selected for internal refinement : " << nCellsToRefine
1967  << " cells (out of " << mesh.globalData().nTotalCells()
1968  << ')' << endl;
1969 
1970  // Stop when no cells to refine or have done minimum necessary
1971  // iterations and not enough cells to refine.
1972  if
1973  (
1974  nCellsToRefine == 0
1975  || (
1976  iter >= overallMaxShellLevel
1977  && nCellsToRefine <= refineParams.minRefineCells()
1978  )
1979  )
1980  {
1981  Info<< "Stopping refining since too few cells selected."
1982  << nl << endl;
1983  break;
1984  }
1985 
1986 
1987  if (debug)
1988  {
1989  const_cast<Time&>(mesh.time())++;
1990  }
1991 
1992  if (returnReduceOr(mesh.nCells() >= refineParams.maxLocalCells()))
1993  {
1994  meshRefiner_.balanceAndRefine
1995  (
1996  "shell refinement iteration " + name(iter),
1997  decomposer_,
1998  distributor_,
1999  cellsToRefine,
2000  refineParams.maxLoadUnbalance(),
2001  refineParams.maxCellUnbalance()
2002  );
2003  }
2004  else
2005  {
2006  meshRefiner_.refineAndBalance
2007  (
2008  "shell refinement iteration " + name(iter),
2009  decomposer_,
2010  distributor_,
2011  cellsToRefine,
2012  refineParams.maxLoadUnbalance(),
2013  refineParams.maxCellUnbalance()
2014  );
2015  }
2016  }
2017  meshRefiner_.userFaceData().clear();
2018 
2019  return iter;
2020 }
2021 
2022 
2023 Foam::label Foam::snappyRefineDriver::directionalShellRefine
2024 (
2025  const refinementParameters& refineParams,
2026  const label maxIter
2027 )
2028 {
2029  if (dryRun_)
2030  {
2031  return 0;
2032  }
2033 
2034  addProfiling(shell, "snappyHexMesh::refine::directionalShell");
2035  const fvMesh& mesh = meshRefiner_.mesh();
2036  const shellSurfaces& shells = meshRefiner_.shells();
2037 
2038  labelList& cellLevel =
2039  const_cast<labelIOList&>(meshRefiner_.meshCutter().cellLevel());
2040  labelList& pointLevel =
2041  const_cast<labelIOList&>(meshRefiner_.meshCutter().pointLevel());
2042 
2043 
2044  // Determine the minimum and maximum cell levels that are candidates for
2045  // directional refinement
2046  const labelPairList dirSelect(shells.directionalSelectLevel());
2047  label overallMinLevel = labelMax;
2048  label overallMaxLevel = labelMin;
2049  forAll(dirSelect, shelli)
2050  {
2051  overallMinLevel = min(dirSelect[shelli].first(), overallMinLevel);
2052  overallMaxLevel = max(dirSelect[shelli].second(), overallMaxLevel);
2053  }
2054 
2055  if (overallMinLevel > overallMaxLevel)
2056  {
2057  return 0;
2058  }
2059 
2060  // Maintain directional refinement levels
2061  List<labelVector> dirCellLevel(cellLevel.size());
2062  forAll(cellLevel, celli)
2063  {
2064  dirCellLevel[celli] = labelVector::uniform(cellLevel[celli]);
2065  }
2066 
2067  label iter;
2068  for (iter = 0; iter < maxIter; iter++)
2069  {
2070  Info<< nl
2071  << "Directional shell refinement iteration " << iter << nl
2072  << "----------------------------------------" << nl
2073  << endl;
2074 
2075  label nAllRefine = 0;
2076 
2077  for (direction dir = 0; dir < vector::nComponents; dir++)
2078  {
2079  // Select the cells that need to be refined in certain direction:
2080  // - cell inside/outside shell
2081  // - original cellLevel (using mapping) mentioned in levelIncrement
2082  // - dirCellLevel not yet up to cellLevel+levelIncrement
2083 
2084 
2085  // Extract component of directional level
2086  labelList currentLevel(dirCellLevel.size());
2087  forAll(dirCellLevel, celli)
2088  {
2089  currentLevel[celli] = dirCellLevel[celli][dir];
2090  }
2091 
2092  labelList candidateCells
2093  (
2094  meshRefiner_.directionalRefineCandidates
2095  (
2096  refineParams.maxGlobalCells(),
2097  refineParams.maxLocalCells(),
2098  currentLevel,
2099  dir
2100  )
2101  );
2102 
2103  // Extend to keep 2:1 ratio
2104  labelList cellsToRefine
2105  (
2106  meshRefiner_.meshCutter().consistentRefinement
2107  (
2108  currentLevel,
2109  candidateCells,
2110  true
2111  )
2112  );
2113 
2114  Info<< "Determined cells to refine in = "
2115  << mesh.time().cpuTimeIncrement() << " s" << endl;
2116 
2117  const label nCellsToRefine =
2118  returnReduce(cellsToRefine.size(), sumOp<label>());
2119 
2120  Info<< "Selected for direction " << vector::componentNames[dir]
2121  << " refinement : " << nCellsToRefine
2122  << " cells (out of " << mesh.globalData().nTotalCells()
2123  << ')' << endl;
2124 
2125  nAllRefine += nCellsToRefine;
2126 
2127  // Stop when no cells to refine or have done minimum necessary
2128  // iterations and not enough cells to refine.
2129  if (nCellsToRefine > 0)
2130  {
2131  if (debug)
2132  {
2133  const_cast<Time&>(mesh.time())++;
2134  }
2135 
2136  const bitSet isRefineCell(mesh.nCells(), cellsToRefine);
2137 
2138  autoPtr<mapPolyMesh> map
2139  (
2140  meshRefiner_.directionalRefine
2141  (
2142  "directional refinement iteration " + name(iter),
2143  dir,
2144  cellsToRefine
2145  )
2146  );
2147 
2148  Info<< "Refined mesh in = "
2149  << mesh.time().cpuTimeIncrement() << " s" << endl;
2150 
2152  (
2153  map().cellMap(),
2154  labelVector(0, 0, 0),
2155  dirCellLevel
2156  );
2157 
2158  // Note: edges will have been split. The points might have
2159  // inherited pointLevel from either side of the edge which
2160  // might not be the same for coupled edges so sync
2162  (
2163  mesh,
2164  pointLevel,
2165  maxEqOp<label>(),
2166  labelMin
2167  );
2168 
2169  forAll(map().cellMap(), celli)
2170  {
2171  if (isRefineCell[map().cellMap()[celli]])
2172  {
2173  dirCellLevel[celli][dir]++;
2174  }
2175  }
2176 
2177  // Do something with the pointLevel. See discussion about the
2178  // cellLevel. Do we keep min/max ?
2179  forAll(map().pointMap(), pointi)
2180  {
2181  label oldPointi = map().pointMap()[pointi];
2182  if (map().reversePointMap()[oldPointi] != pointi)
2183  {
2184  // Is added point (splitting an edge)
2185  pointLevel[pointi]++;
2186  }
2187  }
2188  }
2189  }
2190 
2191 
2192  if (nAllRefine == 0)
2193  {
2194  Info<< "Stopping refining since no cells selected."
2195  << nl << endl;
2196  break;
2197  }
2198 
2199  meshRefiner_.printMeshInfo
2200  (
2201  debug,
2202  "After directional refinement iteration " + name(iter),
2203  true
2204  );
2205 
2207  {
2208  Pout<< "Writing directional refinement iteration "
2209  << iter << " mesh to time " << meshRefiner_.timeName() << endl;
2210  meshRefiner_.write
2211  (
2214  (
2217  ),
2218  mesh.time().path()/meshRefiner_.timeName()
2219  );
2220  }
2221  }
2222 
2223  // Adjust cellLevel from dirLevel? As max? Or the min?
2224  // For now: use max. The idea is that if there is a wall
2225  // any directional refinement is likely to be aligned with
2226  // the wall (wall layers) so any snapping/layering would probably
2227  // want to use this highest refinement level.
2228 
2229  forAll(cellLevel, celli)
2230  {
2231  cellLevel[celli] = cmptMax(dirCellLevel[celli]);
2232  }
2233 
2234  return iter;
2235 }
2236 
2237 
2238 void Foam::snappyRefineDriver::mergeAndSmoothRatio
2239 (
2240  const scalarList& allSeedPointDist,
2241  const label nSmoothExpansion,
2242  List<Tuple2<scalar, scalar>>& keyAndValue
2243 )
2244 {
2245  // Merge duplicate distance from coupled locations to get unique
2246  // distances to operate on, do on master
2247  SortableList<scalar> unmergedDist(allSeedPointDist);
2248  DynamicList<scalar> mergedDist;
2249 
2250  scalar prevDist = GREAT;
2251  forAll(unmergedDist, i)
2252  {
2253  scalar curDist = unmergedDist[i];
2254  scalar difference = mag(curDist - prevDist);
2255  if (difference > meshRefiner_.mergeDistance())
2256  //if (difference > 0.01)
2257  {
2258  mergedDist.append(curDist);
2259  prevDist = curDist;
2260  }
2261  }
2262 
2263  // Sort the unique distances
2264  SortableList<scalar> sortedDist(mergedDist);
2265  labelList indexSet = sortedDist.indices();
2266 
2267  // Get updated position starting from original (undistorted) mesh
2268  scalarList seedPointsNewLocation = sortedDist;
2269 
2270  scalar initResidual = 0.0;
2271  scalar prevIterResidual = GREAT;
2272 
2273  for (label iter = 0; iter < nSmoothExpansion; iter++)
2274  {
2275 
2276  // Position based edge averaging algorithm operated on
2277  // all seed plane locations in normalized form.
2278  //
2279  // 0 1 2 3 4 5 6 (edge numbers)
2280  // ---x---x---x---x---x---x---
2281  // 0 1 2 3 4 5 (point numbers)
2282  //
2283  // Average of edge 1-3 in terms of position
2284  // = (point3 - point0)/3
2285  // Keeping points 0-1 frozen, new position of point 2
2286  // = position2 + (average of edge 1-3 as above)
2287  for(label i = 2; i<mergedDist.size()-1; i++)
2288  {
2289  scalar oldX00 = sortedDist[i-2];
2290  scalar oldX1 = sortedDist[i+1];
2291  scalar curX0 = seedPointsNewLocation[i-1];
2292  seedPointsNewLocation[i] = curX0 + (oldX1 - oldX00)/3;
2293  }
2294 
2295  const scalarField residual(seedPointsNewLocation-sortedDist);
2296  {
2297  scalar res(sumMag(residual));
2298 
2299  if (iter == 0)
2300  {
2301  initResidual = res;
2302  }
2303  res /= initResidual;
2304 
2305  if (mag(prevIterResidual - res) < SMALL)
2306  {
2307  if (debug)
2308  {
2309  Pout<< "Converged with iteration " << iter
2310  << " initResidual: " << initResidual
2311  << " final residual : " << res << endl;
2312  }
2313  break;
2314  }
2315  else
2316  {
2317  prevIterResidual = res;
2318  }
2319  }
2320 
2321  // Update the field for next iteration, avoid moving points
2322  sortedDist = seedPointsNewLocation;
2323 
2324  }
2325 
2326  keyAndValue.setSize(mergedDist.size());
2327 
2328  forAll(mergedDist, i)
2329  {
2330  keyAndValue[i].first() = mergedDist[i];
2331  label index = indexSet[i];
2332  keyAndValue[i].second() = seedPointsNewLocation[index];
2333  }
2334 }
2335 
2336 
2337 Foam::label Foam::snappyRefineDriver::directionalSmooth
2338 (
2339  const refinementParameters& refineParams
2340 )
2341 {
2342  addProfiling(split, "snappyHexMesh::refine::smooth");
2343  Info<< nl
2344  << "Directional expansion ratio smoothing" << nl
2345  << "-------------------------------------" << nl
2346  << endl;
2347 
2348  fvMesh& baseMesh = meshRefiner_.mesh();
2349  const searchableSurfaces& geometry = meshRefiner_.surfaces().geometry();
2350  const shellSurfaces& shells = meshRefiner_.shells();
2351 
2352  label iter = 0;
2353 
2354  forAll(shells.nSmoothExpansion(), shellI)
2355  {
2356  if
2357  (
2358  shells.nSmoothExpansion()[shellI] > 0
2359  || shells.nSmoothPosition()[shellI] > 0
2360  )
2361  {
2362  label surfi = shells.shells()[shellI];
2363  const vector& userDirection = shells.smoothDirection()[shellI];
2364 
2365 
2366  // Extract inside points
2368  {
2369  // Get inside points
2370  List<volumeType> volType;
2371  geometry[surfi].getVolumeType(baseMesh.points(), volType);
2372 
2373  label nInside = 0;
2374  forAll(volType, pointi)
2375  {
2376  if (volType[pointi] == volumeType::INSIDE)
2377  {
2378  nInside++;
2379  }
2380  }
2381  pointLabels.setSize(nInside);
2382  nInside = 0;
2383  forAll(volType, pointi)
2384  {
2385  if (volType[pointi] == volumeType::INSIDE)
2386  {
2387  pointLabels[nInside++] = pointi;
2388  }
2389  }
2390 
2391  //bitSet isInsidePoint(baseMesh.nPoints());
2392  //forAll(volType, pointi)
2393  //{
2394  // if (volType[pointi] == volumeType::INSIDE)
2395  // {
2396  // isInsidePoint.set(pointi);
2397  // }
2398  //}
2399  //pointLabels = isInsidePoint.used();
2400  }
2401 
2402  // Mark all directed edges
2403  bitSet isXEdge(baseMesh.edges().size());
2404  {
2405  const edgeList& edges = baseMesh.edges();
2406  forAll(edges, edgei)
2407  {
2408  const edge& e = edges[edgei];
2409  vector eVec(e.vec(baseMesh.points()));
2410  eVec /= mag(eVec);
2411  if (mag(eVec&userDirection) > 0.9)
2412  {
2413  isXEdge.set(edgei);
2414  }
2415  }
2416  }
2417 
2418  // Get the extreme of smoothing region and
2419  // normalize all points within
2420  const scalar totalLength =
2421  geometry[surfi].bounds().span()
2422  & userDirection;
2423  const scalar startPosition =
2424  geometry[surfi].bounds().min()
2425  & userDirection;
2426 
2427  scalarField normalizedPosition(pointLabels.size(), Zero);
2428  forAll(pointLabels, i)
2429  {
2430  label pointi = pointLabels[i];
2431  normalizedPosition[i] =
2432  (
2433  ((baseMesh.points()[pointi]&userDirection) - startPosition)
2434  / totalLength
2435  );
2436  }
2437 
2438  // Sort the normalized position
2439  labelList order(sortedOrder(normalizedPosition));
2440 
2441  DynamicList<scalar> seedPointDist;
2442 
2443  // Select points from finest refinement (one point-per plane)
2444  scalar prevDist = GREAT;
2445  forAll(order, i)
2446  {
2447  label pointi = order[i];
2448  scalar curDist = normalizedPosition[pointi];
2449  if (mag(curDist - prevDist) > meshRefiner_.mergeDistance())
2450  {
2451  seedPointDist.append(curDist);
2452  prevDist = curDist;
2453  }
2454  }
2455 
2456  // Collect data from all processors
2457  scalarList allSeedPointDist;
2458  {
2459  List<scalarList> gatheredDist(Pstream::nProcs());
2460  gatheredDist[Pstream::myProcNo()] = seedPointDist;
2461  Pstream::gatherList(gatheredDist);
2462 
2463  // Combine processor lists into one big list.
2464  allSeedPointDist =
2465  ListListOps::combine<scalarList>
2466  (
2467  gatheredDist, accessOp<scalarList>()
2468  );
2469  }
2470 
2471  // Pre-set the points not to smooth (after expansion)
2472  bitSet isFrozenPoint(baseMesh.nPoints(), true);
2473 
2474  {
2475  scalar minSeed = min(allSeedPointDist);
2476  scalar maxSeed = max(allSeedPointDist);
2477  Pstream::broadcasts(UPstream::worldComm, minSeed, maxSeed);
2478 
2479  forAll(normalizedPosition, posI)
2480  {
2481  const scalar pos = normalizedPosition[posI];
2482  if
2483  (
2484  (mag(pos-minSeed) < meshRefiner_.mergeDistance())
2485  || (mag(pos-maxSeed) < meshRefiner_.mergeDistance())
2486  )
2487  {
2488  // Boundary point: freeze
2489  isFrozenPoint.set(pointLabels[posI]);
2490  }
2491  else
2492  {
2493  // Internal to moving region
2494  isFrozenPoint.unset(pointLabels[posI]);
2495  }
2496  }
2497  }
2498 
2499  Info<< "Smoothing " << geometry[surfi].name() << ':' << nl
2500  << " Direction : " << userDirection << nl
2501  << " Number of points : "
2502  << returnReduce(pointLabels.size(), sumOp<label>())
2503  << " (out of " << baseMesh.globalData().nTotalPoints()
2504  << ")" << nl
2505  << " Smooth expansion iterations : "
2506  << shells.nSmoothExpansion()[shellI] << nl
2507  << " Smooth position iterations : "
2508  << shells.nSmoothPosition()[shellI] << nl
2509  << " Number of planes : "
2510  << allSeedPointDist.size()
2511  << endl;
2512 
2513  // Make lookup from original normalized distance to new value
2514  List<Tuple2<scalar, scalar>> keyAndValue(allSeedPointDist.size());
2515 
2516  // Filter unique seed distances and iterate for user given steps
2517  // or until convergence. Then get back map from old to new distance
2518  if (Pstream::master())
2519  {
2520  mergeAndSmoothRatio
2521  (
2522  allSeedPointDist,
2523  shells.nSmoothExpansion()[shellI],
2524  keyAndValue
2525  );
2526  }
2527 
2528  Pstream::broadcast(keyAndValue);
2529 
2530  // Construct an iterpolation table for further queries
2531  // - although normalized values are used for query,
2532  // it might flow out of bounds due to precision, hence clamped
2533  const interpolationTable<scalar> table
2534  (
2535  keyAndValue,
2537  "undefined"
2538  );
2539 
2540  // Now move the points directly on the baseMesh.
2541  pointField baseNewPoints(baseMesh.points());
2542  forAll(pointLabels, i)
2543  {
2544  label pointi = pointLabels[i];
2545  const point& curPoint = baseMesh.points()[pointi];
2546  scalar curDist = normalizedPosition[i];
2547  scalar newDist = table(curDist);
2548  scalar newPosition = startPosition + newDist*totalLength;
2549  baseNewPoints[pointi] +=
2550  userDirection * (newPosition - (curPoint &userDirection));
2551  }
2552 
2553  // Moving base mesh with expansion ratio smoothing
2554  vectorField disp(baseNewPoints-baseMesh.points());
2556  (
2557  baseMesh,
2558  disp,
2559  maxMagSqrEqOp<vector>(),
2560  point::zero
2561  );
2562  baseMesh.movePoints(baseMesh.points()+disp);
2563 
2564  // Reset any moving state
2565  baseMesh.moving(false);
2566 
2568  {
2569  const_cast<Time&>(baseMesh.time())++;
2570 
2571  Pout<< "Writing directional expansion ratio smoothed"
2572  << " mesh to time " << meshRefiner_.timeName() << endl;
2573 
2574  meshRefiner_.write
2575  (
2578  (
2581  ),
2582  baseMesh.time().path()/meshRefiner_.timeName()
2583  );
2584  }
2585 
2586  // Now we have moved the points in user specified region. Smooth
2587  // them with neighbour points to avoid skewed cells
2588  // Instead of moving actual mesh, operate on copy
2589  pointField baseMeshPoints(baseMesh.points());
2590  scalar initResidual = 0.0;
2591  scalar prevIterResidual = GREAT;
2592  for (iter = 0; iter < shells.nSmoothPosition()[shellI]; iter++)
2593  {
2594  {
2595  const edgeList& edges = baseMesh.edges();
2596  const labelListList& pointEdges = baseMesh.pointEdges();
2597 
2598  pointField unsmoothedPoints(baseMeshPoints);
2599 
2600  scalarField sumOther(baseMesh.nPoints(), Zero);
2601  labelList nSumOther(baseMesh.nPoints(), Zero);
2602  labelList nSumXEdges(baseMesh.nPoints(), Zero);
2603  forAll(edges, edgei)
2604  {
2605  const edge& e = edges[edgei];
2606  sumOther[e[0]] +=
2607  (unsmoothedPoints[e[1]]&userDirection);
2608  nSumOther[e[0]]++;
2609  sumOther[e[1]] +=
2610  (unsmoothedPoints[e[0]]&userDirection);
2611  nSumOther[e[1]]++;
2612  if (isXEdge[edgei])
2613  {
2614  nSumXEdges[e[0]]++;
2615  nSumXEdges[e[1]]++;
2616  }
2617  }
2618 
2620  (
2621  baseMesh,
2622  nSumXEdges,
2623  plusEqOp<label>(),
2624  label(0)
2625  );
2626 
2627  forAll(pointLabels, i)
2628  {
2629  label pointi = pointLabels[i];
2630 
2631  if (nSumXEdges[pointi] < 2)
2632  {
2633  // Hanging node. Remove the (single!) X edge so it
2634  // will follow points above or below instead
2635  const labelList& pEdges = pointEdges[pointi];
2636  forAll(pEdges, pE)
2637  {
2638  label edgei = pEdges[pE];
2639  if (isXEdge[edgei])
2640  {
2641  const edge& e = edges[edgei];
2642  label otherPt = e.otherVertex(pointi);
2643  nSumOther[pointi]--;
2644  sumOther[pointi] -=
2645  (
2646  unsmoothedPoints[otherPt]
2647  & userDirection
2648  );
2649  }
2650  }
2651  }
2652  }
2653 
2655  (
2656  baseMesh,
2657  sumOther,
2658  plusEqOp<scalar>(),
2659  scalar(0)
2660  );
2662  (
2663  baseMesh,
2664  nSumOther,
2665  plusEqOp<label>(),
2666  label(0)
2667  );
2668 
2669  forAll(pointLabels, i)
2670  {
2671  label pointi = pointLabels[i];
2672 
2673  if ((nSumOther[pointi] >= 2) && !isFrozenPoint[pointi])
2674  {
2675  scalar smoothPos =
2676  0.5
2677  *(
2678  (unsmoothedPoints[pointi]&userDirection)
2679  +sumOther[pointi]/nSumOther[pointi]
2680  );
2681 
2682  vector& v = baseNewPoints[pointi];
2683  v += (smoothPos-(v&userDirection))*userDirection;
2684  }
2685  }
2686 
2687  const vectorField residual(baseNewPoints - baseMeshPoints);
2688  {
2689  scalar res(gSum(mag(residual)));
2690 
2691  if (iter == 0)
2692  {
2693  initResidual = res;
2694  }
2695  res /= initResidual;
2696 
2697  if (mag(prevIterResidual - res) < SMALL)
2698  {
2699  Info<< "Converged smoothing in iteration " << iter
2700  << " initResidual: " << initResidual
2701  << " final residual : " << res << endl;
2702  break;
2703  }
2704  else
2705  {
2706  prevIterResidual = res;
2707  }
2708  }
2709 
2710  // Just copy new location instead of moving base mesh
2711  baseMeshPoints = baseNewPoints;
2712  }
2713  }
2714 
2715  // Move mesh to new location
2716  vectorField dispSmooth(baseMeshPoints-baseMesh.points());
2718  (
2719  baseMesh,
2720  dispSmooth,
2721  maxMagSqrEqOp<vector>(),
2722  point::zero
2723  );
2724  baseMesh.movePoints(baseMesh.points()+dispSmooth);
2725 
2726  // Reset any moving state
2727  baseMesh.moving(false);
2728 
2730  {
2731  const_cast<Time&>(baseMesh.time())++;
2732 
2733  Pout<< "Writing positional smoothing iteration "
2734  << iter << " mesh to time " << meshRefiner_.timeName()
2735  << endl;
2736  meshRefiner_.write
2737  (
2740  (
2743  ),
2744  baseMesh.time().path()/meshRefiner_.timeName()
2745  );
2746  }
2747  }
2748  }
2749  return iter;
2750 }
2751 
2752 
2753 void Foam::snappyRefineDriver::baffleAndSplitMesh
2754 (
2755  const refinementParameters& refineParams,
2756  const snapParameters& snapParams,
2757  const bool handleSnapProblems,
2758  const dictionary& motionDict
2759 )
2760 {
2761  if (dryRun_)
2762  {
2763  return;
2764  }
2765 
2766  addProfiling(split, "snappyHexMesh::refine::splitting");
2767  Info<< nl
2768  << "Splitting mesh at surface intersections" << nl
2769  << "---------------------------------------" << nl
2770  << endl;
2771 
2772  const fvMesh& mesh = meshRefiner_.mesh();
2773 
2774  if (debug)
2775  {
2776  const_cast<Time&>(mesh.time())++;
2777  }
2778 
2779  // Introduce baffles at surface intersections. Note:
2780  // meshRefinement::surfaceIndex() will
2781  // be like boundary face from now on so not coupled anymore.
2782  meshRefiner_.baffleAndSplitMesh
2783  (
2784  handleSnapProblems, // detect&remove potential snap problem
2785 
2786  // Snap problem cell detection
2787  snapParams,
2788  refineParams.useTopologicalSnapDetection(),
2789  false, // perpendicular edge connected cells
2790  scalarField(0), // per region perpendicular angle
2791  refineParams.nErodeCellZone(),
2792 
2793  motionDict,
2794  const_cast<Time&>(mesh.time()),
2795  globalToMasterPatch_,
2796  globalToSlavePatch_,
2797  refineParams.locationsInMesh(),
2798  refineParams.zonesInMesh(),
2799  refineParams.locationsOutsideMesh(),
2800  !refineParams.useLeakClosure(),
2801  setFormatter_,
2802  surfFormatter_
2803  );
2804 
2805 
2806  if (!handleSnapProblems) // merge free standing baffles?
2807  {
2808  meshRefiner_.mergeFreeStandingBaffles
2809  (
2810  snapParams,
2811  refineParams.useTopologicalSnapDetection(),
2812  false, // perpendicular edge connected cells
2813  scalarField(0), // per region perpendicular angle
2814  refineParams.planarAngle(),
2815  motionDict,
2816  const_cast<Time&>(mesh.time()),
2817  globalToMasterPatch_,
2818  globalToSlavePatch_,
2819  refineParams.locationsInMesh(),
2820  refineParams.locationsOutsideMesh()
2821  );
2822  }
2823 }
2824 
2825 
2826 void Foam::snappyRefineDriver::zonify
2827 (
2828  const refinementParameters& refineParams,
2829  wordPairHashTable& zonesToFaceZone
2830 )
2831 {
2832  // Mesh is at its finest. Do zoning
2833  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2834  // This puts all faces with intersection across a zoneable surface
2835  // into that surface's faceZone. All cells inside faceZone get given the
2836  // same cellZone.
2837 
2838  const labelList namedSurfaces =
2839  surfaceZonesInfo::getNamedSurfaces(meshRefiner_.surfaces().surfZones());
2840 
2841  if
2842  (
2843  namedSurfaces.size()
2844  || refineParams.zonesInMesh().size()
2845  )
2846  {
2847  Info<< nl
2848  << "Introducing zones for interfaces" << nl
2849  << "--------------------------------" << nl
2850  << endl;
2851 
2852  const fvMesh& mesh = meshRefiner_.mesh();
2853 
2854  if (debug)
2855  {
2856  const_cast<Time&>(mesh.time())++;
2857  }
2858 
2859  meshRefiner_.zonify
2860  (
2861  refineParams.allowFreeStandingZoneFaces(),
2862  refineParams.nErodeCellZone(),
2863  refineParams.locationsInMesh(),
2864  refineParams.zonesInMesh(),
2865  refineParams.locationsOutsideMesh(),
2866  !refineParams.useLeakClosure(),
2867  setFormatter_,
2868  surfFormatter_,
2869  zonesToFaceZone
2870  );
2871 
2873  {
2874  Pout<< "Writing zoned mesh to time "
2875  << meshRefiner_.timeName() << endl;
2876  meshRefiner_.write
2877  (
2880  (
2883  ),
2884  mesh.time().path()/meshRefiner_.timeName()
2885  );
2886  }
2887 
2888  // Check that all faces are synced
2890  }
2891 }
2892 
2893 
2894 void Foam::snappyRefineDriver::splitAndMergeBaffles
2895 (
2896  const refinementParameters& refineParams,
2897  const snapParameters& snapParams,
2898  const bool handleSnapProblems,
2899  const dictionary& motionDict
2900 )
2901 {
2902  if (dryRun_)
2903  {
2904  return;
2905  }
2906 
2907  Info<< nl
2908  << "Handling cells with snap problems" << nl
2909  << "---------------------------------" << nl
2910  << endl;
2911 
2912  const fvMesh& mesh = meshRefiner_.mesh();
2913 
2914  // Introduce baffles and split mesh
2915  if (debug)
2916  {
2917  const_cast<Time&>(mesh.time())++;
2918  }
2919 
2920  const scalarField& perpAngle = meshRefiner_.surfaces().perpendicularAngle();
2921 
2922  meshRefiner_.baffleAndSplitMesh
2923  (
2924  handleSnapProblems,
2925 
2926  // Snap problem cell detection
2927  snapParams,
2928  refineParams.useTopologicalSnapDetection(),
2929  handleSnapProblems, // remove perp edge connected cells
2930  perpAngle, // perp angle
2931  refineParams.nErodeCellZone(),
2932 
2933  motionDict,
2934  const_cast<Time&>(mesh.time()),
2935  globalToMasterPatch_,
2936  globalToSlavePatch_,
2937  refineParams.locationsInMesh(),
2938  refineParams.zonesInMesh(),
2939  refineParams.locationsOutsideMesh(),
2940  !refineParams.useLeakClosure(),
2941  setFormatter_,
2942  surfFormatter_
2943  );
2944 
2945  // Merge free-standing baffles always
2946  meshRefiner_.mergeFreeStandingBaffles
2947  (
2948  snapParams,
2949  refineParams.useTopologicalSnapDetection(),
2950  handleSnapProblems,
2951  perpAngle,
2952  refineParams.planarAngle(),
2953  motionDict,
2954  const_cast<Time&>(mesh.time()),
2955  globalToMasterPatch_,
2956  globalToSlavePatch_,
2957  refineParams.locationsInMesh(),
2958  refineParams.locationsOutsideMesh()
2959  );
2960 
2961  if (debug)
2962  {
2963  const_cast<Time&>(mesh.time())++;
2964  }
2965 
2966  // Duplicate points on baffles that are on more than one cell
2967  // region. This will help snapping pull them to separate surfaces.
2968  meshRefiner_.dupNonManifoldPoints();
2969 
2970 
2971  // Merge all baffles that are still remaining after duplicating points.
2972  List<labelPair> couples(localPointRegion::findDuplicateFacePairs(mesh));
2973 
2974  const label nCouples = returnReduce(couples.size(), sumOp<label>());
2975 
2976  Info<< "Detected unsplittable baffles : " << nCouples << endl;
2977 
2978  if (nCouples > 0)
2979  {
2980  // Actually merge baffles. Note: not exactly parallellized. Should
2981  // convert baffle faces into processor faces if they resulted
2982  // from them.
2983  meshRefiner_.mergeBaffles(couples, Map<label>(0));
2984 
2985  if (debug)
2986  {
2987  // Debug:test all is still synced across proc patches
2988  meshRefiner_.checkData();
2989  }
2990 
2991  // Remove any now dangling parts
2992  meshRefiner_.splitMeshRegions
2993  (
2994  globalToMasterPatch_,
2995  globalToSlavePatch_,
2996  refineParams.locationsInMesh(),
2997  refineParams.locationsOutsideMesh(),
2998  true, // exit if any path to outside points
2999  setFormatter_
3000  );
3001 
3002  if (debug)
3003  {
3004  // Debug:test all is still synced across proc patches
3005  meshRefiner_.checkData();
3006  }
3007 
3008  Info<< "Merged free-standing baffles in = "
3009  << mesh.time().cpuTimeIncrement() << " s." << endl;
3010  }
3011 
3013  {
3014  Pout<< "Writing handleProblemCells mesh to time "
3015  << meshRefiner_.timeName() << endl;
3016  meshRefiner_.write
3017  (
3020  (
3023  ),
3024  mesh.time().path()/meshRefiner_.timeName()
3025  );
3026  }
3027 }
3028 
3029 
3031 (
3032  meshRefinement& meshRefiner,
3033  const refinementParameters& refineParams,
3034  const HashTable<Pair<word>>& faceZoneToPatches
3035 )
3036 {
3037  if (faceZoneToPatches.size())
3038  {
3039  Info<< nl
3040  << "Adding patches for face zones" << nl
3041  << "-----------------------------" << nl
3042  << endl;
3043 
3044  Info<< setf(ios_base::left)
3045  << setw(6) << "Patch"
3046  << setw(20) << "Type"
3047  << setw(30) << "Name"
3048  << setw(30) << "FaceZone"
3049  << setw(10) << "FaceType"
3050  << nl
3051  << setw(6) << "-----"
3052  << setw(20) << "----"
3053  << setw(30) << "----"
3054  << setw(30) << "--------"
3055  << setw(10) << "--------"
3056  << endl;
3057 
3058  const polyMesh& mesh = meshRefiner.mesh();
3059 
3060  // Add patches for added inter-region faceZones
3061  forAllConstIters(faceZoneToPatches, iter)
3062  {
3063  const word& fzName = iter.key();
3064  const Pair<word>& patchNames = iter.val();
3065 
3066  // Get any user-defined faceZone data
3068  dictionary patchInfo = refineParams.getZoneInfo(fzName, fzType);
3069 
3070  const word& masterName = fzName;
3071  //const word slaveName = fzName + "_slave";
3072  //const word slaveName = czNames.second()+"_to_"+czNames.first();
3073  const word& slaveName = patchNames.second();
3074 
3075  label mpi = meshRefiner.addMeshedPatch(masterName, patchInfo);
3076 
3077  Info<< setf(ios_base::left)
3078  << setw(6) << mpi
3079  << setw(20) << mesh.boundaryMesh()[mpi].type()
3080  << setw(30) << masterName
3081  << setw(30) << fzName
3082  << setw(10) << surfaceZonesInfo::faceZoneTypeNames[fzType]
3083  << nl;
3084 
3085 
3086  label sli = meshRefiner.addMeshedPatch(slaveName, patchInfo);
3087 
3088  Info<< setf(ios_base::left)
3089  << setw(6) << sli
3090  << setw(20) << mesh.boundaryMesh()[sli].type()
3091  << setw(30) << slaveName
3092  << setw(30) << fzName
3093  << setw(10) << surfaceZonesInfo::faceZoneTypeNames[fzType]
3094  << nl;
3095 
3096  meshRefiner.addFaceZone(fzName, masterName, slaveName, fzType);
3097  }
3098 
3099  Info<< endl;
3100  }
3101 }
3102 
3103 
3104 void Foam::snappyRefineDriver::mergePatchFaces
3105 (
3106  const meshRefinement::FaceMergeType mergeType,
3107  const refinementParameters& refineParams,
3108  const dictionary& motionDict
3109 )
3110 {
3111  if (dryRun_)
3112  {
3113  return;
3114  }
3115 
3116  addProfiling(merge, "snappyHexMesh::refine::merge");
3117  Info<< nl
3118  << "Merge refined boundary faces" << nl
3119  << "----------------------------" << nl
3120  << endl;
3121 
3122  const fvMesh& mesh = meshRefiner_.mesh();
3123 
3124  if
3125  (
3126  mergeType == meshRefinement::FaceMergeType::GEOMETRIC
3127  || mergeType == meshRefinement::FaceMergeType::IGNOREPATCH
3128  )
3129  {
3130  meshRefiner_.mergePatchFacesUndo
3131  (
3132  Foam::cos(degToRad(45.0)),
3133  Foam::cos(degToRad(45.0)),
3134  meshRefiner_.meshedPatches(),
3135  motionDict,
3136  labelList(mesh.nFaces(), -1),
3137  mergeType
3138  );
3139  }
3140  else
3141  {
3142  // Still merge refined boundary faces if all four are on same patch
3143  meshRefiner_.mergePatchFaces
3144  (
3145  Foam::cos(degToRad(45.0)),
3146  Foam::cos(degToRad(45.0)),
3147  4, // only merge faces split into 4
3148  meshRefiner_.meshedPatches(),
3149  meshRefinement::FaceMergeType::GEOMETRIC // no merge across patches
3150  );
3151  }
3152 
3153  if (debug)
3154  {
3155  meshRefiner_.checkData();
3156  }
3157 
3158  meshRefiner_.mergeEdgesUndo(Foam::cos(degToRad(45.0)), motionDict);
3159 
3160  if (debug)
3161  {
3162  meshRefiner_.checkData();
3163  }
3164 }
3165 
3166 
3167 void Foam::snappyRefineDriver::deleteSmallRegions
3168 (
3169  const refinementParameters& refineParams
3170 )
3171 {
3172  const fvMesh& mesh = meshRefiner_.mesh();
3173  const cellZoneMesh& czm = mesh.cellZones();
3174 
3175  //const labelList zoneIDs
3176  //(
3177  // meshRefiner_.getZones
3178  // (
3179  // List<surfaceZonesInfo::faceZoneType> fzTypes
3180  // ({
3181  // surfaceZonesInfo::BAFFLE,
3182  // surfaceZonesInfo::BOUNDARY,
3183  // });
3184  // )
3185  //);
3187 
3188  // Get faceZone and patch(es) per face (or -1 if face not on faceZone)
3190  labelList ownPatch;
3191  labelList neiPatch;
3192  labelList nB; // local count of faces per faceZone
3193  meshRefiner_.getZoneFaces(zoneIDs, faceZoneID, ownPatch, neiPatch, nB);
3194 
3195 
3196  // Mark all faces on outside of zones. Note: assumes that faceZones
3197  // are consistent with the outside of cellZones ...
3198 
3199  boolList isBlockedFace(mesh.nFaces(), false);
3200  meshRefiner_.selectSeparatedCoupledFaces(isBlockedFace);
3201 
3202  forAll(ownPatch, facei)
3203  {
3204  if (ownPatch[facei] != -1)
3205  {
3206  isBlockedFace[facei] = true;
3207  }
3208  }
3209 
3210  // Map from cell to zone. Not that background cells are not -1 but
3211  // cellZones.size()
3212  labelList cellToZone(mesh.nCells(), czm.size());
3213  for (const auto& cz : czm)
3214  {
3215  UIndirectList<label>(cellToZone, cz) = cz.index();
3216  }
3217 
3218  // Walk to split into regions
3219  const regionSplit cellRegion(mesh, isBlockedFace);
3220 
3221  // Count number of cells per zone and per region
3222  labelList nCellsPerRegion(cellRegion.nRegions(), 0);
3223  labelList regionToZone(cellRegion.nRegions(), -2);
3224  labelList nCellsPerZone(czm.size()+1, 0);
3225  forAll(cellRegion, celli)
3226  {
3227  const label regioni = cellRegion[celli];
3228  const label zonei = cellToZone[celli];
3229 
3230  // Zone for this region
3231  regionToZone[regioni] = zonei;
3232 
3233  nCellsPerRegion[regioni]++;
3234  nCellsPerZone[zonei]++;
3235  }
3236  Pstream::listCombineReduce(nCellsPerRegion, plusEqOp<label>());
3237  Pstream::listCombineReduce(regionToZone, maxEqOp<label>());
3238  Pstream::listCombineReduce(nCellsPerZone, plusEqOp<label>());
3239 
3240 
3241  // Mark small regions. Note that all processors have the same information
3242  // so should take the same decision. Alternative is to do master only
3243  // and scatter the decision.
3244  forAll(nCellsPerRegion, regioni)
3245  {
3246  const label zonei = regionToZone[regioni];
3247  label& nRegionCells = nCellsPerRegion[regioni];
3248 
3249  if
3250  (
3251  nRegionCells < refineParams.minCellFraction()*nCellsPerZone[zonei]
3252  || nRegionCells < refineParams.nMinCells()
3253  )
3254  {
3255  Info<< "Deleting region " << regioni
3256  << " (size " << nRegionCells
3257  << ") of zone size " << nCellsPerZone[zonei]
3258  << endl;
3259 
3260  // Mark region to be deleted. 0 size (= global) should never
3261  // occur.
3262  nRegionCells = 0;
3263  }
3264  }
3265 
3266  DynamicList<label> cellsToRemove(mesh.nCells()/128);
3267  forAll(cellRegion, celli)
3268  {
3269  if (nCellsPerRegion[cellRegion[celli]] == 0)
3270  {
3271  cellsToRemove.append(celli);
3272  }
3273  }
3274  const label nTotCellsToRemove = returnReduce
3275  (
3276  cellsToRemove.size(),
3277  sumOp<label>()
3278  );
3279  if (nTotCellsToRemove > 0)
3280  {
3281  Info<< "Deleting " << nTotCellsToRemove
3282  << " cells in small regions" << endl;
3283 
3284  removeCells cellRemover(mesh);
3285 
3286  cellsToRemove.shrink();
3287  const labelList exposedFaces
3288  (
3289  cellRemover.getExposedFaces(cellsToRemove)
3290  );
3291  const labelList exposedPatch
3292  (
3293  UIndirectList<label>(ownPatch, exposedFaces)
3294  );
3295  (void)meshRefiner_.doRemoveCells
3296  (
3297  cellsToRemove,
3298  exposedFaces,
3299  exposedPatch,
3300  cellRemover
3301  );
3302  }
3303 }
3304 
3305 
3307 (
3308  const dictionary& refineDict,
3309  const refinementParameters& refineParams,
3310  const snapParameters& snapParams,
3311  const bool prepareForSnapping,
3312  const meshRefinement::FaceMergeType mergeType,
3313  const dictionary& motionDict
3314 )
3315 {
3316  addProfiling(refine, "snappyHexMesh::refine");
3317  Info<< nl
3318  << "Refinement phase" << nl
3319  << "----------------" << nl
3320  << endl;
3321 
3322  const fvMesh& mesh = meshRefiner_.mesh();
3323 
3324 
3325  // Check that all the keep points are inside the mesh.
3326  refineParams.findCells(true, mesh, refineParams.locationsInMesh());
3327 
3328  // Check that all the keep points are inside the mesh.
3329  if (dryRun_)
3330  {
3331  refineParams.findCells(true, mesh, refineParams.locationsOutsideMesh());
3332  }
3333 
3334  // Estimate cell sizes
3335  if (dryRun_)
3336  {
3337  snappyVoxelMeshDriver voxelDriver
3338  (
3339  meshRefiner_,
3340  globalToMasterPatch_,
3341  globalToSlavePatch_
3342  );
3343  voxelDriver.doRefine(refineParams);
3344  }
3345 
3346 
3347  // Refine around feature edges
3348  featureEdgeRefine
3349  (
3350  refineParams,
3351  100, // maxIter
3352  0 // min cells to refine
3353  );
3354 
3355 
3356  // Initial automatic gap-level refinement: gaps smaller than the cell size
3357  if
3358  (
3359  max(meshRefiner_.surfaces().maxGapLevel()) > 0
3360  || max(meshRefiner_.shells().maxGapLevel()) > 0
3361  || max(meshRefiner_.surfaces().maxCurvatureLevel()) > 0
3362  )
3363  {
3364  // In case we use automatic gap level refinement do some pre-refinement
3365  // (fast) since it is so slow.
3366 
3367  // Refine based on surface
3368  surfaceOnlyRefine
3369  (
3370  refineParams,
3371  20, // maxIter
3372  labelMax // no leak detection yet since all in same cell
3373  );
3374 
3375  // Refine cells that contain a gap
3376  smallFeatureRefine
3377  (
3378  refineParams,
3379  100 // maxIter
3380  );
3381  }
3382 
3383 
3384  // Refine based on surface
3385  surfaceOnlyRefine
3386  (
3387  refineParams,
3388  100, // maxIter
3389  0 // Iteration to start leak detection and closure
3390  );
3391 
3392  // Pass1 of automatic gap-level refinement: surface-intersected cells
3393  // in narrow gaps. Done early so we can remove the inside
3394  gapOnlyRefine
3395  (
3396  refineParams,
3397  100 // maxIter
3398  );
3399 
3400  // Remove cells inbetween two surfaces
3401  surfaceProximityBlock
3402  (
3403  refineParams,
3404  1 //100 // maxIter
3405  );
3406 
3407  // Remove cells (a certain distance) beyond surface intersections
3408  removeInsideCells
3409  (
3410  refineParams,
3411  1 // nBufferLayers
3412  );
3413 
3414  // Pass2 of automatic gap-level refinement: all cells in gaps
3415  bigGapOnlyRefine
3416  (
3417  refineParams,
3418  true, // spreadGapSize
3419  100 // maxIter
3420  );
3421 
3422  // Internal mesh refinement
3423  shellRefine
3424  (
3425  refineParams,
3426  100 // maxIter
3427  );
3428 
3429  // Remove any extra cells from limitRegion with level -1, without
3430  // adding any buffer layer this time
3431  removeInsideCells
3432  (
3433  refineParams,
3434  0 // nBufferLayers
3435  );
3436 
3437  // Refine any hexes with 5 or 6 faces refined to make smooth edges
3438  danglingCellRefine
3439  (
3440  refineParams,
3441  21, // 1 coarse face + 5 refined faces
3442  100 // maxIter
3443  );
3444  danglingCellRefine
3445  (
3446  refineParams,
3447  24, // 0 coarse faces + 6 refined faces
3448  100 // maxIter
3449  );
3450 
3451  // Refine any cells with differing refinement level on either side
3452  refinementInterfaceRefine
3453  (
3454  refineParams,
3455  10 // maxIter
3456  );
3457 
3458  // Directional shell refinement
3459  directionalShellRefine
3460  (
3461  refineParams,
3462  100 // maxIter
3463  );
3464 
3465  // Block gaps (always, ignore surface leakLevel)
3466  if (refineParams.locationsOutsideMesh().size())
3467  {
3468  // For now: only check leaks on meshed surfaces. The problem is that
3469  // blockLeakFaces always generates baffles any not just faceZones ...
3470  const labelList unnamedSurfaces
3471  (
3473  (
3474  meshRefiner_.surfaces().surfZones()
3475  )
3476  );
3477  if (unnamedSurfaces.size() && !dryRun_)
3478  {
3479  meshRefiner_.blockLeakFaces
3480  (
3481  globalToMasterPatch_,
3482  globalToSlavePatch_,
3483  refineParams.locationsInMesh(),
3484  refineParams.zonesInMesh(),
3485  refineParams.locationsOutsideMesh(),
3486  unnamedSurfaces
3487  );
3488  }
3489  }
3490 
3491  if
3492  (
3493  max(meshRefiner_.shells().nSmoothExpansion()) > 0
3494  || max(meshRefiner_.shells().nSmoothPosition()) > 0
3495  )
3496  {
3497  directionalSmooth(refineParams);
3498  }
3499 
3500 
3501  // Introduce baffles at surface intersections. Remove sections unreachable
3502  // from keepPoint.
3503  baffleAndSplitMesh
3504  (
3505  refineParams,
3506  snapParams,
3507  prepareForSnapping,
3508  motionDict
3509  );
3510 
3511 
3512  //- Commented out for now since causes zoning errors (sigsegv) on
3513  // case with faceZones. TBD.
3516  //boundaryRefinementInterfaceRefine
3517  //(
3518  // refineParams,
3519  // 10 // maxIter
3520  //);
3521 
3522 
3523  // Mesh is at its finest. Do optional zoning (cellZones and faceZones)
3524  wordPairHashTable zonesToFaceZone;
3525  zonify(refineParams, zonesToFaceZone);
3526 
3527  // Create pairs of patches for faceZones
3528  {
3529  HashTable<Pair<word>> faceZoneToPatches(zonesToFaceZone.size());
3530 
3531  // Note: zonesToFaceZone contains the same data on different
3532  // processors but in different order. We could sort the
3533  // contents but instead just loop in sortedToc order.
3534  List<Pair<word>> czs(zonesToFaceZone.sortedToc());
3535 
3536  forAll(czs, i)
3537  {
3538  const Pair<word>& czNames = czs[i];
3539  const word& fzName = zonesToFaceZone[czNames];
3540 
3541  const word& masterName = fzName;
3542  const word slaveName = czNames.second() + "_to_" + czNames.first();
3543  Pair<word> patches(masterName, slaveName);
3544  faceZoneToPatches.insert(fzName, patches);
3545  }
3546  addFaceZones(meshRefiner_, refineParams, faceZoneToPatches);
3547  }
3548 
3549  // Pull baffles apart
3550  splitAndMergeBaffles
3551  (
3552  refineParams,
3553  snapParams,
3554  prepareForSnapping,
3555  motionDict
3556  );
3557 
3558  // Do something about cells with refined faces on the boundary
3559  if (prepareForSnapping)
3560  {
3561  mergePatchFaces(mergeType, refineParams, motionDict);
3562  }
3563 
3564 
3565  if (refineParams.minCellFraction() > 0 || refineParams.nMinCells() > 0)
3566  {
3567  // Some small disconnected bits of mesh might remain since at
3568  // this point faceZones have not been converted into e.g. baffles.
3569  // We don't know whether e.g. the baffles are reset to be cyclicAMI
3570  // thus reconnecting. For now check if there are any particularly
3571  // small regions.
3572  deleteSmallRegions(refineParams);
3573  }
3574 
3575 
3576  if (!dryRun_ && Pstream::parRun())
3577  {
3578  Info<< nl
3579  << "Doing final balancing" << nl
3580  << "---------------------" << nl
3581  << endl;
3582 
3583  // Do final balancing. Keep zoned faces on one processor since the
3584  // snap phase will convert them to baffles and this only works for
3585  // internal faces.
3586  meshRefiner_.balance
3587  (
3588  true, // keepZoneFaces
3589  false, // keepBaffles
3590  scalarField(mesh.nCells(), 1), // cellWeights
3591  decomposer_,
3592  distributor_
3593  );
3594  }
3595 }
3596 
3597 
3598 // ************************************************************************* //
List< scalar > scalarList
List of scalar.
Definition: scalarList.H:32
const polyBoundaryMesh & pbm
dimensioned< typename typeOfMag< Type >::type > sumMag(const DimensionedField< Type, GeoMesh > &f1)
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
void cmptMax(FieldField< Field, typename FieldField< Field, Type >::cmptType > &cf, const FieldField< Field, Type > &f)
const labelIOList & zoneIDs
Definition: correctPhi.H:59
fileName path() const
Return path = rootPath/caseName. Same as TimePaths::path()
Definition: Time.H:503
uint8_t direction
Definition: direction.H:46
List< cell > cellList
List of cell.
Definition: cellListFwd.H:39
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
labelList pointLabels(nPoints, -1)
label nPoints() const noexcept
Number of mesh points.
Clamp value to the start/end value.
virtual const labelList & faceNeighbour() const
Return face neighbour.
Definition: polyMesh.C:1122
List< edge > edgeList
List of edge.
Definition: edgeList.H:32
labelList sortedOrder(const UList< T > &input)
Return the (stable) sort order for the list.
const bitSet isBlockedFace(intersectedFaces())
static void swapBoundaryCellList(const polyMesh &mesh, const UList< T > &cellData, List< T > &neighbourCellData, const bool parRun=UPstream::parRun())
Extract and swap to obtain neighbour cell values for all boundary faces.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
Unit conversion functions.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
static void gatherList(const UList< commsStruct > &comms, UList< T > &values, const int tag, const label comm)
Gather data, but keep individual values separate. Uses the specified communication schedule...
const labelList & patchID() const
Per boundary face label the patch index.
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:1061
static writeType writeLevel()
Get/set write level.
#define addProfiling(Name,...)
Define profiling trigger with specified name and description string. The description is generated by ...
labelList findIndices(const ListType &input, typename ListType::const_reference val, label start=0)
Linear search to find all occurrences of given element.
interfaceProperties interface(alpha1, U, thermo->transportPropertiesDict())
List< labelPair > labelPairList
List of labelPair.
Definition: labelPair.H:33
const cellList & cells() const
static const Enum< faceZoneType > faceZoneTypeNames
::Foam::direction nComponents(const expressions::valueTypeCode) noexcept
The number of components associated with given valueTypeCode.
Definition: exprTraits.C:40
constexpr label labelMin
Definition: label.H:54
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Can be negative if the process i...
Definition: UPstream.H:1086
set value to -1 any face that was refined
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:360
static label worldComm
Communicator for all ranks. May differ from commGlobal() if local worlds are in use.
Definition: UPstream.H:421
label nFaces() const noexcept
Number of mesh faces.
T returnReduce(const T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
DynamicID< faceZoneMesh > faceZoneID
Foam::faceZoneID.
Definition: ZoneIDs.H:43
A class for managing references or pointers (no reference counting)
Definition: HashPtrTable.H:49
UList< label > labelUList
A UList of labels.
Definition: UList.H:78
static void broadcast(Type &value, const label comm=UPstream::worldComm)
Broadcast content (contiguous or non-contiguous) to all communicator ranks. Does nothing in non-paral...
static labelPairList findDuplicateFacePairs(const polyMesh &)
Helper routine to find all baffles (two boundary faces.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
List< face > faceList
List of faces.
Definition: faceListFwd.H:39
static void updateList(const labelList &newToOld, const T &nullValue, List< T > &elems)
Helper: reorder list according to map.
dimensionedScalar pos(const dimensionedScalar &ds)
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run. ...
Definition: UPstream.H:1077
Sends/receives parts of mesh+fvfields to neighbouring processors. Used in load balancing.
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
IOList< label > labelIOList
IO for a List of label.
Definition: labelIOList.H:32
void setSize(const label n)
Alias for resize()
Definition: List.H:320
FaceMergeType
Enumeration for what to do with co-planar patch faces on a single.
dynamicFvMesh & mesh
Type gSum(const FieldField< Field, Type > &f)
dimensionedScalar cos(const dimensionedScalar &ds)
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
const cellShapeList & cells
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i), works like std::iota() but returning a...
Definition: labelLists.C:44
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition: polyMesh.H:609
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
label size() const noexcept
The number of entries in the list.
Definition: UPtrListI.H:106
wordList patchNames(nPatches)
Base class for writing coordSet(s) and tracks with fields.
static void checkCoupledFaceZones(const polyMesh &)
Helper function: check that face zones are synced.
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1116
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
const globalMeshData & globalData() const
Return parallel info (demand-driven)
Definition: polyMesh.C:1311
Abstract base class for domain decomposition.
label nInternalFaces() const noexcept
Number of internal faces.
Vector< scalar > vector
Definition: vector.H:57
A location inside the volume.
Definition: volumeType.H:65
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1103
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
static labelList getUnnamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of unnamed surfaces (surfaces without faceZoneName)
static void syncPointList(const polyMesh &mesh, List< T > &pointValues, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh points.
const wordList surface
Standard surface field types (scalar, vector, tensor, etc)
Istream and Ostream manipulators taking arguments.
Smanip< std::ios_base::fmtflags > setf(std::ios_base::fmtflags flags)
Definition: IOmanip.H:169
int debug
Static debugging option.
const faceZoneMesh & faceZones() const noexcept
Return face zone mesh.
Definition: polyMesh.H:671
defineTypeNameAndDebug(combustionModel, 0)
labelList f(nPoints)
label nTotalCells() const noexcept
Total global number of mesh cells.
double cpuTimeIncrement() const
Return CPU time [seconds] since last call to cpuTimeIncrement(), resetCpuTimeIncrement().
Definition: cpuTimePosix.C:86
static void addFaceZones(meshRefinement &meshRefiner, const refinementParameters &refineParams, const HashTable< Pair< word >> &faceZoneToPatches)
Helper: add faceZones and patches.
static void broadcasts(const label comm, Type &arg1, Args &&... args)
Broadcast multiple items to all communicator ranks. Does nothing in non-parallel. ...
Helper class which maintains intersections of (changing) mesh with (static) surfaces.
static bool split(const std::string &line, std::string &key, std::string &val)
Definition: cpuInfo.C:32
void doRefine(const dictionary &refineDict, const refinementParameters &refineParams, const snapParameters &snapParams, const bool prepareForSnapping, const meshRefinement::FaceMergeType mergeType, const dictionary &motionDict)
Do all the refinement.
vector point
Point is a vector.
Definition: point.H:37
label nCells() const noexcept
Number of mesh cells.
const vectorField & faceAreas() const
const dimensionedScalar c
Speed of light in a vacuum.
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1094
const polyBoundaryMesh & patches
const cellZoneMesh & cellZones() const noexcept
Return cell zone mesh.
Definition: polyMesh.H:679
static labelList getNamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of named surfaces (surfaces with faceZoneName)
messageStream Info
Information stream (stdout output on master, null elsewhere)
constexpr label labelMax
Definition: label.H:55
static Vector< label > uniform(const label &s)
Return a VectorSpace with all elements = s.
Definition: VectorSpaceI.H:157
writeType
Enumeration for what to write. Used as a bit-pattern.
label n
Field< vector > vectorField
Specialisation of Field<T> for vector.
faceZoneType
What to do with faceZone faces.
debugType
Enumeration for what to debug. Used as a bit-pattern.
Vector< label > labelVector
Vector of labels.
Definition: labelVector.H:47
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
List< label > labelList
A List of labels.
Definition: List.H:62
T * first()
The first entry in the list.
Definition: UILList.H:542
bool coupled
bool returnReduceOr(const bool value, const label comm=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
constexpr scalar degToRad(const scalar deg) noexcept
Conversion from degrees to radians.
List< bool > boolList
A List of bools.
Definition: List.H:60
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
HashTable< word, wordPair, Foam::Hash< wordPair > > wordPairHashTable
HashTable of wordPair.
ZoneMesh< cellZone, polyMesh > cellZoneMesh
A ZoneMesh with the type cellZone.
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
Namespace for OpenFOAM.
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28
static void listCombineReduce(List< T > &values, const CombineOp &cop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Combines List elements. After completion all processors have the same data.
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127