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