Loading...
Searching...
No Matches
meshRefinementBaffles.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-2014 OpenFOAM Foundation
9 Copyright (C) 2015-2025 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 "meshRefinement.H"
30#include "fvMesh.H"
31#include "syncTools.H"
32#include "Time.H"
33#include "refinementSurfaces.H"
34#include "faceSet.H"
36#include "polyTopoChange.H"
37#include "meshTools.H"
38#include "polyModifyFace.H"
39#include "polyModifyCell.H"
40#include "polyAddFace.H"
41#include "polyRemoveFace.H"
42#include "localPointRegion.H"
43#include "duplicatePoints.H"
44#include "regionSplit.H"
45#include "removeCells.H"
46#include "unitConversion.H"
47#include "OBJstream.H"
49#include "PatchEdgeFaceWave.H"
51#include "polyMeshAdder.H"
52#include "IOmanip.H"
54#include "shellSurfaces.H"
56#include "volFields.H"
57#include "holeToFace.H"
58
59#include "FaceCellWave.H"
60#include "wallPoints.H"
61#include "searchableSurfaces.H"
62
63// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
64
65Foam::label Foam::meshRefinement::createBaffle
66(
67 const label faceI,
68 const label ownPatch,
69 const label neiPatch,
70 polyTopoChange& meshMod
71) const
72{
73 const face& f = mesh_.faces()[faceI];
74 label zoneID = mesh_.faceZones().whichZone(faceI);
75 bool zoneFlip = false;
76
77 if (zoneID >= 0)
78 {
79 const faceZone& fZone = mesh_.faceZones()[zoneID];
80 zoneFlip = fZone.flipMap()[fZone.whichFace(faceI)];
81 }
82
83 meshMod.setAction
84 (
85 polyModifyFace
86 (
87 f, // modified face
88 faceI, // label of face
89 mesh_.faceOwner()[faceI], // owner
90 -1, // neighbour
91 false, // face flip
92 ownPatch, // patch for face
93 false, // remove from zone
94 zoneID, // zone for face
95 zoneFlip // face flip in zone
96 )
97 );
98
99
100 label dupFaceI = -1;
101
102 if (mesh_.isInternalFace(faceI))
103 {
104 if (neiPatch == -1)
105 {
107 << "No neighbour patch for internal face " << faceI
108 << " fc:" << mesh_.faceCentres()[faceI]
109 << " ownPatch:" << ownPatch << abort(FatalError);
110 }
111
112 bool reverseFlip = false;
113 if (zoneID >= 0)
114 {
115 reverseFlip = !zoneFlip;
116 }
117
118 dupFaceI = meshMod.setAction
119 (
120 polyAddFace
121 (
122 f.reverseFace(), // modified face
123 mesh_.faceNeighbour()[faceI],// owner
124 -1, // neighbour
125 -1, // masterPointID
126 -1, // masterEdgeID
127 faceI, // masterFaceID,
128 true, // face flip
129 neiPatch, // patch for face
130 zoneID, // zone for face
131 reverseFlip // face flip in zone
132 )
133 );
134 }
135 return dupFaceI;
136}
137
138
145//bool Foam::meshRefinement::validBaffleTopology
146//(
147// const label faceI,
148// const vector& n1,
149// const vector& n2,
150// const vector& testDir
151//) const
152//{
153//
154// label patchI = mesh_.boundaryMesh().whichPatch(faceI);
155// if (patchI == -1 || mesh_.boundaryMesh()[patchI].coupled())
156// {
157// return true;
158// }
159// else if (mag(n1&n2) > cos(degToRad(30.0)))
160// {
161// // Both normals aligned. Check that test vector perpendicularish to
162// // surface normal
163// scalar magTestDir = mag(testDir);
164// if (magTestDir > VSMALL)
165// {
166// if (mag(n1&(testDir/magTestDir)) < cos(degToRad(45.0)))
167// {
168// //Pout<< "** disabling baffling face "
169// // << mesh_.faceCentres()[faceI] << endl;
170// return false;
171// }
172// }
173// }
174// return true;
175//}
176
177
178void Foam::meshRefinement::getIntersections
179(
180 const labelList& surfacesToTest,
181 const pointField& neiCc,
182 const labelList& testFaces,
183
184 labelList& globalRegion1,
185 labelList& globalRegion2
186) const
187{
189 if (debug&OBJINTERSECTIONS)
190 {
191 mkDir(mesh_.time().path()/timeName());
192 str.reset
193 (
194 new OBJstream
195 (
196 mesh_.time().path()/timeName()/"intersections.obj"
197 )
198 );
199
200 Pout<< "getIntersections : Writing surface intersections to file "
201 << str().name() << nl << endl;
202 }
203
204
205 globalRegion1.setSize(mesh_.nFaces());
206 globalRegion1 = -1;
207 globalRegion2.setSize(mesh_.nFaces());
208 globalRegion2 = -1;
209
210
211 // Collect segments
212 // ~~~~~~~~~~~~~~~~
213
214 pointField start(testFaces.size());
215 pointField end(testFaces.size());
216
217 {
218 labelList minLevel;
219 calcCellCellRays
220 (
221 neiCc,
222 labelList(neiCc.size(), -1),
223 testFaces,
224 start,
225 end,
226 minLevel
227 );
228 }
229
230
231 // Do test for intersections
232 // ~~~~~~~~~~~~~~~~~~~~~~~~~
233
234 labelList surface1;
236 labelList region1;
237 labelList surface2;
239 labelList region2;
240 surfaces_.findNearestIntersection
241 (
242 surfacesToTest,
243 start,
244 end,
245
246 surface1,
247 hit1,
248 region1,
249
250 surface2,
251 hit2,
252 region2
253 );
254
255
256 forAll(testFaces, i)
257 {
258 label faceI = testFaces[i];
259
260 if (hit1[i].hit() && hit2[i].hit())
261 {
262 if (str)
263 {
264 str().writeLine(start[i], hit1[i].point());
265 str().writeLine(hit1[i].point(), hit2[i].point());
266 str().writeLine(hit2[i].point(), end[i]);
267 }
268
269 // Pick up the patches
270 globalRegion1[faceI] =
271 surfaces_.globalRegion(surface1[i], region1[i]);
272 globalRegion2[faceI] =
273 surfaces_.globalRegion(surface2[i], region2[i]);
274
275 if (globalRegion1[faceI] == -1 || globalRegion2[faceI] == -1)
276 {
278 << "problem." << abort(FatalError);
279 }
280 }
281 }
282}
283
284
285void Foam::meshRefinement::getBafflePatches
286(
287 const label nErodeCellZones,
288 const labelList& globalToMasterPatch,
289 const pointField& locationsInMesh,
290 const wordList& zonesInMesh,
291 const pointField& locationsOutsideMesh,
292 const bool exitIfLeakPath,
293 const refPtr<coordSetWriter>& leakPathFormatter,
294 refPtr<surfaceWriter>& surfFormatter,
295 const labelList& neiLevel,
296 const pointField& neiCc,
297
298 labelList& ownPatch,
299 labelList& neiPatch
300) const
301{
302 // This determines the patches for the intersected faces to
303 // - remove the outside of the mesh
304 // - introduce baffles for (non-faceZone) intersections
305 // Any baffles for faceZones (faceType 'baffle'/'boundary') get introduced
306 // later
307
308
309 // 1. Determine intersections with unnamed surfaces and cell zones
310 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
311 // Notice that this also does hole-closure so the unnamed* is not just
312 // the surface intersections.
313
314 labelList cellToZone;
315 labelList unnamedRegion1;
316 labelList unnamedRegion2;
317 labelList namedSurfaceRegion;
318 {
319 bitSet posOrientation;
320 zonify
321 (
322 true, // allowFreeStandingZoneFaces
323 nErodeCellZones,
324 -2, // zone to put unreached cells into
325 locationsInMesh,
326 zonesInMesh,
327 locationsOutsideMesh,
328 exitIfLeakPath,
329 leakPathFormatter,
330 surfFormatter,
331
332 cellToZone,
333 unnamedRegion1,
334 unnamedRegion2,
335 namedSurfaceRegion,
336 posOrientation
337 );
338 }
339
340
341 // 2. Baffle all boundary faces
342 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
343 // The logic is that all intersections with unnamed surfaces become
344 // baffles except where we're inbetween a cellZone and background
345 // or inbetween two different cellZones.
346
347 labelList neiCellZone;
348 syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone);
349
350 ownPatch.setSize(mesh_.nFaces());
351 ownPatch = -1;
352 neiPatch.setSize(mesh_.nFaces());
353 neiPatch = -1;
354
355 forAll(ownPatch, faceI)
356 {
357 if (unnamedRegion1[faceI] != -1 || unnamedRegion2[faceI] != -1)
358 {
359 label ownMasterPatch = -1;
360 if (unnamedRegion1[faceI] != -1)
361 {
362 ownMasterPatch = globalToMasterPatch[unnamedRegion1[faceI]];
363 }
364 label neiMasterPatch = -1;
365 if (unnamedRegion2[faceI] != -1)
366 {
367 neiMasterPatch = globalToMasterPatch[unnamedRegion2[faceI]];
368 }
369
370
371 // Now we always want to produce a baffle except if
372 // one side is a valid cellZone
373
374 label ownZone = cellToZone[mesh_.faceOwner()[faceI]];
375 label neiZone = -1;
376
377 if (mesh_.isInternalFace(faceI))
378 {
379 neiZone = cellToZone[mesh_.faceNeighbour()[faceI]];
380 }
381 else
382 {
383 neiZone = neiCellZone[faceI-mesh_.nInternalFaces()];
384 }
385
386
387 if
388 (
389 (ownZone != neiZone)
390 && (
391 (ownZone >= 0 && neiZone != -2)
392 || (neiZone >= 0 && ownZone != -2)
393 )
394 && (
395 namedSurfaceRegion.size() == 0
396 || namedSurfaceRegion[faceI] == -1
397 )
398 )
399 {
400 // One side is a valid cellZone and the neighbour is a different
401 // one (or -1 but not -2). Do not baffle unless the user has
402 // put both an unnamed and a named surface there. In that
403 // case assume that the user wants both: baffle and faceZone.
404 }
405 else
406 {
407 ownPatch[faceI] = ownMasterPatch;
408 neiPatch[faceI] = neiMasterPatch;
409 }
410 }
411 }
412
413 // No need to parallel sync since intersection data (surfaceIndex_ etc.)
414 // already guaranteed to be synced...
415 // However:
416 // - owncc and neicc are reversed on different procs so might pick
417 // up different regions reversed? No problem. Neighbour on one processor
418 // might not be owner on the other processor but the neighbour is
419 // not used when creating baffles from proc faces.
420 // - tolerances issues occasionally crop up.
421 syncTools::syncFaceList(mesh_, ownPatch, maxEqOp<label>());
422 syncTools::syncFaceList(mesh_, neiPatch, maxEqOp<label>());
423}
424
425
426Foam::Map<Foam::labelPair> Foam::meshRefinement::getZoneBafflePatches
427(
428 const bool allowBoundary,
429 const labelList& globalToMasterPatch,
430 const labelList& globalToSlavePatch
431) const
432{
433 Map<labelPair> bafflePatch(mesh_.nFaces()/1000);
434
435 const PtrList<surfaceZonesInfo>& surfZones = surfaces_.surfZones();
436 const faceZoneMesh& fZones = mesh_.faceZones();
437
438 forAll(surfZones, surfI)
439 {
440 const wordList& faceZoneNames = surfZones[surfI].faceZoneNames();
441
442 forAll(faceZoneNames, fzi)
443 {
444 // Get zone
445 const word& faceZoneName = faceZoneNames[fzi];
446 label zoneI = fZones.findZoneID(faceZoneName);
447 const faceZone& fZone = fZones[zoneI];
448
449 // Get patch allocated for zone
450 label globalRegionI = surfaces_.globalRegion(surfI, fzi);
451 labelPair zPatches
452 (
453 globalToMasterPatch[globalRegionI],
454 globalToSlavePatch[globalRegionI]
455 );
456
457 Info<< "For zone " << fZone.name() << " found patches "
458 << mesh_.boundaryMesh()[zPatches[0]].name() << " and "
459 << mesh_.boundaryMesh()[zPatches[1]].name()
460 << endl;
461
462 forAll(fZone, i)
463 {
464 label faceI = fZone[i];
465
466 if (allowBoundary || mesh_.isInternalFace(faceI))
467 {
468 labelPair patches = zPatches;
469 if (fZone.flipMap()[i])
470 {
472 }
473
474 if (!bafflePatch.insert(faceI, patches))
475 {
477 << "Face " << faceI
478 << " fc:" << mesh_.faceCentres()[faceI]
479 << " in zone " << fZone.name()
480 << " is in multiple zones!"
481 << abort(FatalError);
482 }
483 }
485 }
486 }
487 return bafflePatch;
488}
489
490
491Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::createBaffles
492(
493 const labelList& ownPatch,
494 const labelList& neiPatch
495)
496{
497 if
498 (
499 ownPatch.size() != mesh_.nFaces()
500 || neiPatch.size() != mesh_.nFaces()
501 )
502 {
504 << "Illegal size :"
505 << " ownPatch:" << ownPatch.size()
506 << " neiPatch:" << neiPatch.size()
507 << ". Should be number of faces:" << mesh_.nFaces()
508 << abort(FatalError);
509 }
510
511 if (debug)
512 {
513 labelList syncedOwnPatch(ownPatch);
514 syncTools::syncFaceList(mesh_, syncedOwnPatch, maxEqOp<label>());
515 labelList syncedNeiPatch(neiPatch);
516 syncTools::syncFaceList(mesh_, syncedNeiPatch, maxEqOp<label>());
517
518 forAll(syncedOwnPatch, faceI)
519 {
520 if
521 (
522 (ownPatch[faceI] == -1 && syncedOwnPatch[faceI] != -1)
523 || (neiPatch[faceI] == -1 && syncedNeiPatch[faceI] != -1)
524 )
525 {
527 << "Non synchronised at face:" << faceI
528 << " on patch:" << mesh_.boundaryMesh().whichPatch(faceI)
529 << " fc:" << mesh_.faceCentres()[faceI] << endl
530 << "ownPatch:" << ownPatch[faceI]
531 << " syncedOwnPatch:" << syncedOwnPatch[faceI]
532 << " neiPatch:" << neiPatch[faceI]
533 << " syncedNeiPatch:" << syncedNeiPatch[faceI]
534 << abort(FatalError);
535 }
536 }
537 }
538
539 // Topochange container
540 polyTopoChange meshMod(mesh_);
541
542 label nBaffles = 0;
543
544 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
545 {
546 if (ownPatch[faceI] != -1)
547 {
548 // Create baffle or repatch face. Return label of inserted baffle
549 // face.
550 createBaffle
551 (
552 faceI,
553 ownPatch[faceI], // owner side patch
554 neiPatch[faceI], // neighbour side patch
555 meshMod
556 );
557 nBaffles++;
558 }
559 }
560 const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
561
562 forAll(pbm, patchI)
563 {
564 const polyPatch& pp = pbm[patchI];
565
566 label coupledPatchI = -1;
567 if
568 (
569 pp.coupled()
570 && !refCast<const coupledPolyPatch>(pp).separated()
571 )
572 {
573 coupledPatchI = patchI;
574 }
575
576 forAll(pp, i)
577 {
578 label faceI = pp.start()+i;
579
580 if (ownPatch[faceI] != -1)
581 {
582 createBaffle
583 (
584 faceI,
585 ownPatch[faceI], // owner side patch
586 neiPatch[faceI], // neighbour side patch
587 meshMod
588 );
589
590 if (coupledPatchI != -1)
591 {
592 faceToCoupledPatch_.insert(faceI, coupledPatchI);
593 }
594
595 nBaffles++;
596 }
597 }
598 }
599
600
602 if (returnReduceOr(nBaffles))
603 {
604 // Remove any unnecessary fields
605 mesh_.clearOut();
606 mesh_.moving(false);
607
608 // Change the mesh (no inflation, parallel sync)
609 mapPtr = meshMod.changeMesh(mesh_, false, true);
610 mapPolyMesh& map = *mapPtr;
611
612 // Update fields
613 mesh_.updateMesh(map);
614
615 // Move mesh if in inflation mode
616 if (map.hasMotionPoints())
617 {
618 mesh_.movePoints(map.preMotionPoints());
619 }
620 else
621 {
622 // Delete mesh volumes.
623 mesh_.clearOut();
624 }
625
626
627 // Reset the instance for if in overwrite mode
628 mesh_.setInstance(timeName());
629
630 //- Redo the intersections on the newly create baffle faces. Note that
631 // this changes also the cell centre positions.
632 faceSet baffledFacesSet(mesh_, "baffledFacesSet", 2*nBaffles);
633
634 const labelList& reverseFaceMap = map.reverseFaceMap();
635 const labelList& faceMap = map.faceMap();
636
637 // Pick up owner side of baffle
638 forAll(ownPatch, oldFaceI)
639 {
640 label faceI = reverseFaceMap[oldFaceI];
641
642 if (ownPatch[oldFaceI] != -1 && faceI >= 0)
643 {
644 const cell& ownFaces = mesh_.cells()[mesh_.faceOwner()[faceI]];
645
646 baffledFacesSet.insert(ownFaces);
647 }
648 }
649 // Pick up neighbour side of baffle (added faces)
650 forAll(faceMap, faceI)
651 {
652 label oldFaceI = faceMap[faceI];
653
654 if (oldFaceI >= 0 && reverseFaceMap[oldFaceI] != faceI)
655 {
656 const cell& ownFaces = mesh_.cells()[mesh_.faceOwner()[faceI]];
657
658 baffledFacesSet.insert(ownFaces);
659 }
660 }
661 baffledFacesSet.sync(mesh_);
662
663 updateMesh(map, baffledFacesSet.toc());
664 }
665
666 return mapPtr;
667}
668
669
671(
673) const
674{
675 const faceZoneMesh& faceZones = mesh_.faceZones();
676
677 DynamicList<label> zoneIDs(faceZones.size());
678
679 forAll(faceZones, zoneI)
680 {
681 const faceZone& fZone = faceZones[zoneI];
682
683 label mpI, spI;
685 bool hasInfo = getFaceZoneInfo(fZone.name(), mpI, spI, fzType);
686
687 if (hasInfo && fzTypes.found(fzType))
688 {
689 zoneIDs.append(zoneI);
690 }
691 }
692 return zoneIDs;
694
695
696// Subset those baffles where both faces are on the same zone
698(
699 const polyMesh& mesh,
700 const labelList& zoneIDs,
701 const List<labelPair>& baffles
702)
703{
704 const faceZoneMesh& faceZones = mesh.faceZones();
705
706 // Mark zone per face
707 labelList faceToZone(mesh.nFaces(), -1);
708
709 for (const label zoneID : zoneIDs)
710 {
711 labelUIndList(faceToZone, faceZones[zoneID]) = zoneID;
712 }
713
714
715 // Subset baffles
716 DynamicList<labelPair> newBaffles(baffles.size());
717 forAll(baffles, i)
718 {
719 const labelPair& p = baffles[i];
720 if (faceToZone[p[0]] != -1 && (faceToZone[p[0]] == faceToZone[p[1]]))
721 {
722 newBaffles.append(p);
723 }
724 }
725
726 return newBaffles;
727}
728
729
731(
732 const polyMesh& mesh,
733 const labelList& faceMap,
734 List<labelPair>& baffles
735)
736{
737 // Create old-to-new map just for boundary faces. (since multiple faces
738 // get created from the same baffle face)
739 labelList reverseFaceMap(mesh.nFaces(), -1);
740 for
741 (
742 label facei = mesh.nInternalFaces();
743 facei < mesh.nFaces();
744 facei++
745 )
746 {
747 label oldFacei = faceMap[facei];
748 if (oldFacei != -1)
749 {
750 reverseFaceMap[oldFacei] = facei;
751 }
752 }
753
754
755 DynamicList<labelPair> newBaffles(baffles.size());
756 forAll(baffles, i)
757 {
758 const labelPair& p = baffles[i];
759 labelPair newBaffle
760 (
761 reverseFaceMap[p[0]],
762 reverseFaceMap[p[1]]
763 );
764 if (newBaffle[0] != -1 && newBaffle[1] != -1)
765 {
766 newBaffles.append(newBaffle);
767 }
768 }
769 baffles = std::move(newBaffles);
770}
771
772
774(
775 const labelList& zoneIDs,
777 labelList& ownPatch,
778 labelList& neiPatch,
779 labelList& nBaffles
780) const
781{
782 const faceZoneMesh& faceZones = mesh_.faceZones();
783
784 // Per (internal) face the patch related to the faceZone
785 ownPatch.setSize(mesh_.nFaces());
786 ownPatch= -1;
787 neiPatch.setSize(mesh_.nFaces());
788 neiPatch = -1;
789 faceZoneID.setSize(mesh_.nFaces());
790 faceZoneID = -1;
791 nBaffles.setSize(zoneIDs.size());
792 nBaffles = Zero;
793
794
795 //- Get per face whether it is internal or coupled
796 const bitSet isInternalOrCoupled
797 (
799 );
800
801 forAll(zoneIDs, j)
802 {
803 label zoneI = zoneIDs[j];
804 const faceZone& fz = faceZones[zoneI];
805 const word& masterName = faceZoneToMasterPatch_[fz.name()];
806 label masterPatchI = mesh_.boundaryMesh().findPatchID(masterName);
807 const word& slaveName = faceZoneToSlavePatch_[fz.name()];
808 label slavePatchI = mesh_.boundaryMesh().findPatchID(slaveName);
809
810 if (masterPatchI == -1 || slavePatchI == -1)
811 {
813 << "Problem: masterPatchI:" << masterPatchI
814 << " slavePatchI:" << slavePatchI << exit(FatalError);
815 }
816
817 forAll(fz, i)
818 {
819 label faceI = fz[i];
820 if (isInternalOrCoupled[faceI])
821 {
822 if (fz.flipMap()[i])
823 {
824 ownPatch[faceI] = slavePatchI;
825 neiPatch[faceI] = masterPatchI;
826 }
827 else
828 {
829 ownPatch[faceI] = masterPatchI;
830 neiPatch[faceI] = slavePatchI;
831 }
832 faceZoneID[faceI] = zoneI;
833
834 nBaffles[j]++;
835 }
836 }
837 }
838}
840
842(
843 const labelList& zoneIDs,
844 List<labelPair>& baffles,
845 labelList& originatingFaceZone
846)
847{
849
850 if (zoneIDs.size() > 0)
851 {
852 const faceZoneMesh& faceZones = mesh_.faceZones();
853
854 // Split internal faces on interface surfaces
855 Info<< "Converting zoned faces into baffles ..." << endl;
856
857 // Get faceZone and patch(es) per face (or -1 if face not on faceZone)
859 labelList ownPatch;
860 labelList neiPatch;
861 labelList nBaffles;
862 getZoneFaces(zoneIDs, faceZoneID, ownPatch, neiPatch, nBaffles);
863
864 label nLocalBaffles = sum(nBaffles);
865
866 label nTotalBaffles = returnReduce(nLocalBaffles, sumOp<label>());
867
868 if (nTotalBaffles > 0)
869 {
871
872 Info<< nl
873 << setf(ios_base::left)
874 << setw(30) << "FaceZone"
875 << setw(10) << "FaceType"
876 << setw(10) << "nBaffles"
877 << nl
878 << setw(30) << "--------"
879 << setw(10) << "--------"
880 << setw(10) << "--------"
881 << endl;
882
883 forAll(zoneIDs, j)
884 {
885 label zoneI = zoneIDs[j];
886 const faceZone& fz = faceZones[zoneI];
887
888 label mpI, spI;
890 bool hasInfo = getFaceZoneInfo(fz.name(), mpI, spI, fzType);
891
892 if (hasInfo)
893 {
894 Info<< setf(ios_base::left)
895 << setw(30) << fz.name()
896 << setw(10)
898 << setw(10) << nBaffles[j]
899 << nl;
900 }
901 }
902 Info<< endl;
903
904 // Create baffles.
905 map = createBaffles(ownPatch, neiPatch);
906
907 // Get pairs of faces created.
908 // Just loop over faceMap and store baffle if we encounter a slave
909 // face.
910
911 baffles.setSize(nLocalBaffles);
912 originatingFaceZone.setSize(nLocalBaffles);
913 label baffleI = 0;
914
915 const labelList& faceMap = map().faceMap();
916 const labelList& reverseFaceMap = map().reverseFaceMap();
917
918 for
919 (
920 label faceI = mesh_.nInternalFaces();
921 faceI < mesh_.nFaces();
922 faceI++
923 )
924 {
925 label oldFaceI = faceMap[faceI];
926 label masterFaceI = reverseFaceMap[oldFaceI];
927 if (masterFaceI != faceI && ownPatch[oldFaceI] != -1)
928 {
929 baffles[baffleI] = labelPair(masterFaceI, faceI);
930 originatingFaceZone[baffleI] = faceZoneID[oldFaceI];
931 baffleI++;
932 }
933 }
934
935 if (baffleI != baffles.size())
936 {
938 << "Had " << baffles.size() << " baffles to create "
939 << " but encountered " << baffleI
940 << " slave faces originating from patcheable faces."
941 << abort(FatalError);
942 }
943
944 if (debug&MESH)
945 {
946 const_cast<Time&>(mesh_.time())++;
947 Pout<< "Writing zone-baffled mesh to time " << timeName()
948 << endl;
949 write
950 (
953 mesh_.time().path()/"baffles"
954 );
955 }
956 }
957 Info<< "Created " << nTotalBaffles << " baffles in = "
958 << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl;
959 }
960 else
961 {
962 baffles.clear();
963 originatingFaceZone.clear();
964 }
965
966 return map;
967}
968
969
970Foam::List<Foam::labelPair> Foam::meshRefinement::freeStandingBaffles
971(
972 const List<labelPair>& couples,
973 const scalar planarAngle,
974 const bool samePatch
975) const
976{
977 // Done by counting the number of baffles faces per mesh edge. If edge
978 // has 2 boundary faces and both are baffle faces it is the edge of a baffle
979 // region.
980
981 // All duplicate faces on edge of the patch are to be merged.
982 // So we count for all edges of duplicate faces how many duplicate
983 // faces use them.
984 labelList nBafflesPerEdge(mesh_.nEdges(), Zero);
985
986
987 // This algorithm is quite tricky. We don't want to use edgeFaces and
988 // also want it to run in parallel so it is now an algorithm over
989 // all (boundary) faces instead.
990 // We want to pick up any edges that are only used by the baffle
991 // or internal faces but not by any other boundary faces. So
992 // - increment count on an edge by 1 if it is used by any (uncoupled)
993 // boundary face.
994 // - increment count on an edge by 1000000 if it is used by a baffle face
995 // - sum in parallel
996 //
997 // So now any edge that is used by baffle faces only will have the
998 // value 2*1000000+2*1.
999
1000
1001 const label baffleValue = 1000000;
1002
1003
1004 // Count number of boundary faces per edge
1005 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1006
1007 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
1008
1009 forAll(patches, patchI)
1010 {
1011 const polyPatch& pp = patches[patchI];
1012
1013 // Count number of boundary faces. Discard coupled boundary faces.
1014 if (!pp.coupled())
1015 {
1016 label faceI = pp.start();
1017
1018 forAll(pp, i)
1019 {
1020 const labelList& fEdges = mesh_.faceEdges(faceI);
1021
1022 forAll(fEdges, fEdgeI)
1023 {
1024 nBafflesPerEdge[fEdges[fEdgeI]]++;
1025 }
1026 faceI++;
1027 }
1028 }
1029 }
1030
1031
1034
1035
1036 // Count number of duplicate boundary faces per edge
1037 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1038
1039 forAll(couples, i)
1040 {
1041 {
1042 label f0 = couples[i].first();
1043 const labelList& fEdges0 = mesh_.faceEdges(f0, fe0);
1044 forAll(fEdges0, fEdgeI)
1045 {
1046 nBafflesPerEdge[fEdges0[fEdgeI]] += baffleValue;
1047 }
1048 }
1049
1050 {
1051 label f1 = couples[i].second();
1052 const labelList& fEdges1 = mesh_.faceEdges(f1, fe1);
1053 forAll(fEdges1, fEdgeI)
1054 {
1055 nBafflesPerEdge[fEdges1[fEdgeI]] += baffleValue;
1056 }
1057 }
1058 }
1059
1060 // Add nBaffles on shared edges
1062 (
1063 mesh_,
1064 nBafflesPerEdge,
1065 plusEqOp<label>(), // in-place add
1066 label(0) // initial value
1067 );
1068
1069
1070 // Baffles which are not next to other boundaries and baffles will have
1071 // nBafflesPerEdge value 2*baffleValue+2*1 (from 2 boundary faces which
1072 // are both baffle faces)
1073
1074 List<labelPair> filteredCouples(couples.size());
1075 label filterI = 0;
1076
1077 forAll(couples, i)
1078 {
1079 const labelPair& couple = couples[i];
1080
1081 if
1082 (
1083 !samePatch
1084 || (
1085 patches.whichPatch(couple.first())
1086 == patches.whichPatch(couple.second())
1087 )
1088 )
1089 {
1090 const labelList& fEdges = mesh_.faceEdges(couple.first());
1091
1092 forAll(fEdges, fEdgeI)
1093 {
1094 label edgeI = fEdges[fEdgeI];
1095
1096 if (nBafflesPerEdge[edgeI] == 2*baffleValue+2*1)
1097 {
1098 filteredCouples[filterI++] = couple;
1099 break;
1100 }
1101 }
1102 }
1103 }
1104 filteredCouples.setSize(filterI);
1105
1106
1107 label nFiltered = returnReduce(filteredCouples.size(), sumOp<label>());
1108
1109 Info<< "freeStandingBaffles : detected "
1110 << nFiltered
1111 << " free-standing baffles out of "
1112 << returnReduce(couples.size(), sumOp<label>())
1113 << nl << endl;
1114
1115
1116 if (nFiltered > 0)
1117 {
1118 // Collect segments
1119 // ~~~~~~~~~~~~~~~~
1120
1121 pointField start(filteredCouples.size());
1122 pointField end(filteredCouples.size());
1123
1124 const pointField& cellCentres = mesh_.cellCentres();
1125
1126 forAll(filteredCouples, i)
1127 {
1128 const labelPair& couple = filteredCouples[i];
1129 start[i] = cellCentres[mesh_.faceOwner()[couple.first()]];
1130 end[i] = cellCentres[mesh_.faceOwner()[couple.second()]];
1131 }
1132
1133 // Extend segments a bit
1134 {
1135 const vectorField smallVec(ROOTSMALL*(end-start));
1136 start -= smallVec;
1137 end += smallVec;
1138 }
1139
1140
1141 // Do test for intersections
1142 // ~~~~~~~~~~~~~~~~~~~~~~~~~
1143 labelList surface1;
1145 labelList region1;
1146 vectorField normal1;
1147
1148 labelList surface2;
1150 labelList region2;
1151 vectorField normal2;
1152
1153 surfaces_.findNearestIntersection
1154 (
1155 identity(surfaces_.surfaces().size()),
1156 start,
1157 end,
1158
1159 surface1,
1160 hit1,
1161 region1,
1162 normal1,
1163
1164 surface2,
1165 hit2,
1166 region2,
1167 normal2
1168 );
1169
1170 //mkDir(mesh_.time().path()/timeName());
1171 //OBJstream str
1172 //(
1173 // mesh_.time().path()/timeName()/"flatBaffles.obj"
1174 //);
1175
1176 const scalar planarAngleCos = Foam::cos(degToRad(planarAngle));
1177
1178 label filterI = 0;
1179 forAll(filteredCouples, i)
1180 {
1181 const labelPair& couple = filteredCouples[i];
1182
1183 // Note: for a baffle-surface we do not want to merge the baffle.
1184 // We could either check for hitting the same triangle (but you
1185 // might hit same point on neighbouring triangles due to tolerance
1186 // issues) or better just to compare the hit point.
1187 // This might still go wrong for a ray in the plane of the triangle
1188 // which might hit two different points on the same triangle due
1189 // to tolerances...
1190
1191 if
1192 (
1193 hit1[i].hit()
1194 && hit2[i].hit()
1195 && hit1[i].point().dist(hit2[i].point()) > mergeDistance_
1196 )
1197 {
1198 // Two different hits. Check angle.
1199 //str.write
1200 //(
1201 // linePointRef(hit1[i].point(), hit2[i].point()),
1202 // normal1[i],
1203 // normal2[i]
1204 //);
1205
1206 if ((normal1[i]&normal2[i]) > planarAngleCos)
1207 {
1208 // Both normals aligned
1209 vector n = end[i]-start[i];
1210 scalar magN = mag(n);
1211 if (magN > VSMALL)
1212 {
1213 filteredCouples[filterI++] = couple;
1214 }
1215 }
1216 }
1217 else if (hit1[i].hit() || hit2[i].hit())
1218 {
1219 // Single hit. Do not include in freestanding baffles.
1220 }
1221 }
1222
1223 filteredCouples.setSize(filterI);
1224
1225 Info<< "freeStandingBaffles : detected "
1226 << returnReduce(filterI, sumOp<label>())
1227 << " planar (within " << planarAngle
1228 << " degrees) free-standing baffles out of "
1229 << nFiltered
1230 << nl << endl;
1231 }
1232
1233 return filteredCouples;
1234}
1236
1238(
1239 const List<labelPair>& couples,
1240 const Map<label>& faceToPatch
1241)
1242{
1243 autoPtr<mapPolyMesh> mapPtr;
1244
1245 if (returnReduceOr(couples.size() || faceToPatch.size()))
1246 {
1247 // Mesh change engine
1248 polyTopoChange meshMod(mesh_);
1249
1250 const faceList& faces = mesh_.faces();
1251 const labelList& faceOwner = mesh_.faceOwner();
1252 const faceZoneMesh& faceZones = mesh_.faceZones();
1253
1254 forAll(couples, i)
1255 {
1256 label face0 = couples[i].first();
1257 label face1 = couples[i].second();
1258
1259 // face1 < 0 signals a coupled face that has been converted to
1260 // baffle
1261
1262 label own0 = faceOwner[face0];
1263 label own1 = faceOwner[face1];
1264
1265 if (face1 < 0 || own0 < own1)
1266 {
1267 // Use face0 as the new internal face.
1268 label zoneID = faceZones.whichZone(face0);
1269 bool zoneFlip = false;
1270
1271 if (zoneID >= 0)
1272 {
1273 const faceZone& fZone = faceZones[zoneID];
1274 zoneFlip = fZone.flipMap()[fZone.whichFace(face0)];
1275 }
1276
1277 label nei = (face1 < 0 ? -1 : own1);
1278
1279 meshMod.setAction(polyRemoveFace(face1));
1280 meshMod.setAction
1281 (
1283 (
1284 faces[face0], // modified face
1285 face0, // label of face being modified
1286 own0, // owner
1287 nei, // neighbour
1288 false, // face flip
1289 -1, // patch for face
1290 false, // remove from zone
1291 zoneID, // zone for face
1292 zoneFlip // face flip in zone
1293 )
1294 );
1295 }
1296 else
1297 {
1298 // Use face1 as the new internal face.
1299 label zoneID = faceZones.whichZone(face1);
1300 bool zoneFlip = false;
1301
1302 if (zoneID >= 0)
1303 {
1304 const faceZone& fZone = faceZones[zoneID];
1305 zoneFlip = fZone.flipMap()[fZone.whichFace(face1)];
1306 }
1307
1308 meshMod.setAction(polyRemoveFace(face0));
1309 meshMod.setAction
1310 (
1312 (
1313 faces[face1], // modified face
1314 face1, // label of face being modified
1315 own1, // owner
1316 own0, // neighbour
1317 false, // face flip
1318 -1, // patch for face
1319 false, // remove from zone
1320 zoneID, // zone for face
1321 zoneFlip // face flip in zone
1322 )
1323 );
1324 }
1325 }
1326
1327 forAllConstIters(faceToPatch, iter)
1328 {
1329 const label faceI = iter.key();
1330 const label patchI = iter.val();
1331
1332 if (!mesh_.isInternalFace(faceI))
1333 {
1335 << "problem: face:" << faceI
1336 << " at:" << mesh_.faceCentres()[faceI]
1337 << "(wanted patch:" << patchI
1338 << ") is an internal face" << exit(FatalError);
1339 }
1340
1341 label zoneID = faceZones.whichZone(faceI);
1342 bool zoneFlip = false;
1343
1344 if (zoneID >= 0)
1345 {
1346 const faceZone& fZone = faceZones[zoneID];
1347 zoneFlip = fZone.flipMap()[fZone.whichFace(faceI)];
1348 }
1349
1350 meshMod.setAction
1351 (
1353 (
1354 faces[faceI], // modified face
1355 faceI, // label of face being modified
1356 faceOwner[faceI], // owner
1357 -1, // neighbour
1358 false, // face flip
1359 patchI, // patch for face
1360 false, // remove from zone
1361 zoneID, // zone for face
1362 zoneFlip // face flip in zone
1363 )
1364 );
1365 }
1366
1367
1368 // Remove any unnecessary fields
1369 mesh_.clearOut();
1370 mesh_.moving(false);
1371
1372 // Change the mesh (no inflation)
1373 mapPtr = meshMod.changeMesh(mesh_, false, true);
1374 mapPolyMesh& map = *mapPtr;
1375
1376 // Update fields
1377 mesh_.updateMesh(map);
1378
1379 // Move mesh (since morphing does not do this)
1380 if (map.hasMotionPoints())
1381 {
1382 mesh_.movePoints(map.preMotionPoints());
1383 }
1384 else
1385 {
1386 // Delete mesh volumes.
1387 mesh_.clearOut();
1388 }
1389
1390 // Reset the instance for if in overwrite mode
1391 mesh_.setInstance(timeName());
1392
1393 // Update intersections. Recalculate intersections on merged faces since
1394 // this seems to give problems? Note: should not be necessary since
1395 // baffles preserve intersections from when they were created.
1396 labelList newExposedFaces(2*couples.size());
1397 label newI = 0;
1398
1399 forAll(couples, i)
1400 {
1401 const label newFace0 = map.reverseFaceMap()[couples[i].first()];
1402 if (newFace0 != -1)
1403 {
1404 newExposedFaces[newI++] = newFace0;
1405 }
1406
1407 const label newFace1 = map.reverseFaceMap()[couples[i].second()];
1408 if (newFace1 != -1)
1409 {
1410 newExposedFaces[newI++] = newFace1;
1411 }
1412 }
1413 newExposedFaces.setSize(newI);
1414 updateMesh(map, newExposedFaces);
1415 }
1416
1417 return mapPtr;
1418}
1420
1422(
1423 const bool doInternalZones,
1424 const bool doBaffleZones
1425)
1426{
1428 {
1430 if (doInternalZones)
1431 {
1433 }
1434 if (doBaffleZones)
1435 {
1437 }
1438 zoneIDs = getZones(fzTypes);
1439 }
1440
1441 List<labelPair> zoneBaffles
1442 (
1444 (
1445 mesh_,
1446 zoneIDs,
1448 )
1449 );
1450
1451 autoPtr<mapPolyMesh> mapPtr;
1452 if (returnReduceOr(zoneBaffles.size()))
1453 {
1454 mapPtr = mergeBaffles(zoneBaffles, Map<label>(0));
1455 }
1456 return mapPtr;
1457}
1458
1459
1460// Finds region per cell for cells inside closed named surfaces
1461void Foam::meshRefinement::findCellZoneGeometric
1462(
1463 const pointField& neiCc,
1464 const labelList& closedNamedSurfaces, // indices of closed surfaces
1465 labelList& namedSurfaceRegion, // per face: named surface region
1466 const labelList& surfaceToCellZone, // cell zone index per surface
1467
1468 labelList& cellToZone
1469) const
1470{
1471 const pointField& cellCentres = mesh_.cellCentres();
1472 const labelList& faceOwner = mesh_.faceOwner();
1473 const labelList& faceNeighbour = mesh_.faceNeighbour();
1474
1475 // Check if cell centre is inside
1476 labelList insideSurfaces;
1477 surfaces_.findInside
1478 (
1479 closedNamedSurfaces,
1480 cellCentres,
1481 insideSurfaces
1482 );
1483
1484 forAll(insideSurfaces, cellI)
1485 {
1486 label surfI = insideSurfaces[cellI];
1487
1488 if (surfI != -1)
1489 {
1490 if (cellToZone[cellI] == -2)
1491 {
1492 cellToZone[cellI] = surfaceToCellZone[surfI];
1493 }
1494 else if (cellToZone[cellI] == -1)
1495 {
1496 // ? Allow named surface to override background zone (-1)
1497 // This is used in the multiRegionHeater tutorial where the
1498 // locationInMesh is inside a named surface.
1499 cellToZone[cellI] = surfaceToCellZone[surfI];
1500 }
1501 }
1502 }
1503
1504
1505 // Some cells with cell centres close to surface might have
1506 // had been put into wrong surface. Recheck with perturbed cell centre.
1507
1508
1509 // 1. Collect points
1510
1511 // Count points to test.
1512 label nCandidates = 0;
1513 forAll(namedSurfaceRegion, faceI)
1514 {
1515 if (namedSurfaceRegion[faceI] != -1)
1516 {
1517 if (mesh_.isInternalFace(faceI))
1518 {
1519 nCandidates += 2;
1520 }
1521 else
1522 {
1523 nCandidates += 1;
1524 }
1525 }
1526 }
1527
1528 // Collect points.
1529 pointField candidatePoints(nCandidates);
1530 nCandidates = 0;
1531 forAll(namedSurfaceRegion, faceI)
1532 {
1533 if (namedSurfaceRegion[faceI] != -1)
1534 {
1535 label own = faceOwner[faceI];
1536 const point& ownCc = cellCentres[own];
1537
1538 if (mesh_.isInternalFace(faceI))
1539 {
1540 label nei = faceNeighbour[faceI];
1541 const point& neiCc = cellCentres[nei];
1542 // Perturbed cc
1543 const vector d = 1e-4*(neiCc - ownCc);
1544 candidatePoints[nCandidates++] = ownCc-d;
1545 candidatePoints[nCandidates++] = neiCc+d;
1546 }
1547 else
1548 {
1549 //const point& neiFc = mesh_.faceCentres()[faceI];
1550 const point& neiFc = neiCc[faceI-mesh_.nInternalFaces()];
1551
1552 // Perturbed cc
1553 const vector d = 1e-4*(neiFc - ownCc);
1554 candidatePoints[nCandidates++] = ownCc-d;
1555 }
1556 }
1557 }
1558
1559
1560 // 2. Test points for inside
1561
1562 surfaces_.findInside
1563 (
1564 closedNamedSurfaces,
1565 candidatePoints,
1566 insideSurfaces
1567 );
1568
1569
1570 // 3. Update zone information
1571
1572 nCandidates = 0;
1573 forAll(namedSurfaceRegion, faceI)
1574 {
1575 if (namedSurfaceRegion[faceI] != -1)
1576 {
1577 label own = faceOwner[faceI];
1578
1579 if (mesh_.isInternalFace(faceI))
1580 {
1581 label ownSurfI = insideSurfaces[nCandidates++];
1582 if (ownSurfI != -1)
1583 {
1584 cellToZone[own] = surfaceToCellZone[ownSurfI];
1585 }
1586
1587 label neiSurfI = insideSurfaces[nCandidates++];
1588 if (neiSurfI != -1)
1589 {
1590 label nei = faceNeighbour[faceI];
1591
1592 cellToZone[nei] = surfaceToCellZone[neiSurfI];
1593 }
1594 }
1595 else
1596 {
1597 label ownSurfI = insideSurfaces[nCandidates++];
1598 if (ownSurfI != -1)
1599 {
1600 cellToZone[own] = surfaceToCellZone[ownSurfI];
1601 }
1602 }
1603 }
1604 }
1605
1606
1607 // Adapt the namedSurfaceRegion
1608 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1609 // for if any cells were not completely covered.
1610
1611 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
1612 {
1613 label ownZone = cellToZone[mesh_.faceOwner()[faceI]];
1614 label neiZone = cellToZone[mesh_.faceNeighbour()[faceI]];
1615
1616 if (namedSurfaceRegion[faceI] == -1 && (ownZone != neiZone))
1617 {
1618 // Give face the zone of min cell zone (but only if the
1619 // cellZone originated from a closed, named surface)
1620
1621 label minZone;
1622 if (ownZone == -1)
1623 {
1624 minZone = neiZone;
1625 }
1626 else if (neiZone == -1)
1627 {
1628 minZone = ownZone;
1629 }
1630 else
1631 {
1632 minZone = min(ownZone, neiZone);
1633 }
1634
1635 // Make sure the cellZone originated from a closed surface. Use
1636 // hardcoded region 0 inside named surface.
1637 label geomSurfI = surfaceToCellZone.find(minZone);
1638
1639 if (geomSurfI != -1)
1640 {
1641 namedSurfaceRegion[faceI] =
1642 surfaces_.globalRegion(geomSurfI, 0);
1643 }
1644 }
1645 }
1646
1647 labelList neiCellZone;
1648 syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone);
1649
1650 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
1651
1652 forAll(patches, patchI)
1653 {
1654 const polyPatch& pp = patches[patchI];
1655
1656 if (pp.coupled())
1657 {
1658 forAll(pp, i)
1659 {
1660 label faceI = pp.start()+i;
1661 label ownZone = cellToZone[mesh_.faceOwner()[faceI]];
1662 label neiZone = neiCellZone[faceI-mesh_.nInternalFaces()];
1663
1664 if (namedSurfaceRegion[faceI] == -1 && (ownZone != neiZone))
1665 {
1666 // Give face the min cell zone
1667 label minZone;
1668 if (ownZone == -1)
1669 {
1670 minZone = neiZone;
1671 }
1672 else if (neiZone == -1)
1673 {
1674 minZone = ownZone;
1675 }
1676 else
1677 {
1678 minZone = min(ownZone, neiZone);
1679 }
1680
1681 // Make sure the cellZone originated from a closed surface.
1682 // Use hardcoded region 0 inside named surface.
1683 label geomSurfI = surfaceToCellZone.find(minZone);
1684
1685 if (geomSurfI != -1)
1686 {
1687 namedSurfaceRegion[faceI] =
1688 surfaces_.globalRegion(geomSurfI, 0);
1689 }
1690 }
1691 }
1692 }
1693 }
1694
1695 // Sync
1696 syncTools::syncFaceList(mesh_, namedSurfaceRegion, maxEqOp<label>());
1697}
1698
1699
1700void Foam::meshRefinement::findCellZoneInsideWalk
1701(
1702 const pointField& locationsInMesh,
1703 const labelList& zonesInMesh,
1704 const labelList& faceToZone, // per face -1 or some index >= 0
1705
1706 labelList& cellToZone
1707) const
1708{
1709 // Analyse regions. Reuse regionsplit
1710 boolList blockedFace(mesh_.nFaces());
1711 //selectSeparatedCoupledFaces(blockedFace);
1712
1713 forAll(blockedFace, faceI)
1714 {
1715 if (faceToZone[faceI] == -1)
1716 {
1717 blockedFace[faceI] = false;
1718 }
1719 else
1720 {
1721 blockedFace[faceI] = true;
1722 }
1723 }
1724 // No need to sync since faceToZone already is synced
1725
1726 // Set region per cell based on walking
1727 regionSplit cellRegion(mesh_, blockedFace);
1728 blockedFace.clear();
1729
1730 // Force calculation of face decomposition (used in findCell)
1731 (void)mesh_.tetBasePtIs();
1732
1733 // For all locationsInMesh find the cell
1734 forAll(locationsInMesh, i)
1735 {
1736 // Get location and index of zone ("none" for cellZone -1)
1737 const point& insidePoint = locationsInMesh[i];
1738 label zoneID = zonesInMesh[i];
1739
1740 // Find the region containing the insidePoint
1741 label keepRegionI = findRegion
1742 (
1743 mesh_,
1744 cellRegion,
1745 vector::uniform(mergeDistance_),
1746 insidePoint
1747 );
1748
1749 Info<< "For cellZone "
1750 << (
1751 zoneID == -1
1752 ? "none"
1753 : mesh_.cellZones()[zoneID].name()
1754 )
1755 << " found point " << insidePoint
1756 << " in global region " << keepRegionI
1757 << " out of " << cellRegion.nRegions() << " regions." << endl;
1758
1759 if (keepRegionI == -1)
1760 {
1762 << "Point " << insidePoint
1763 << " is not inside the mesh." << nl
1764 << "Bounding box of the mesh:" << mesh_.bounds()
1765 << exit(FatalError);
1766 }
1767
1768
1769
1770 // Set all cells with this region to the zoneID
1771 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1772
1773 label nWarnings = 0;
1774
1775 forAll(cellRegion, cellI)
1776 {
1777 if (cellRegion[cellI] == keepRegionI)
1778 {
1779 if (cellToZone[cellI] == -2)
1780 {
1781 // First visit of cell
1782 cellToZone[cellI] = zoneID;
1783 }
1784 else if (cellToZone[cellI] != zoneID)
1785 {
1786 if (cellToZone[cellI] != -1 && nWarnings < 10)
1787 {
1789 << "Cell " << cellI
1790 << " at " << mesh_.cellCentres()[cellI]
1791 << " is inside cellZone "
1792 << (
1793 zoneID == -1
1794 ? "none"
1795 : mesh_.cellZones()[zoneID].name()
1796 )
1797 << " from locationInMesh " << insidePoint
1798 << " but already marked as being in zone "
1799 << mesh_.cellZones()[cellToZone[cellI]].name()
1800 << endl
1801 << "This can happen if your surfaces are not"
1802 << " (sufficiently) closed."
1803 << endl;
1804 nWarnings++;
1805 }
1806
1807 // Override the zone
1808 cellToZone[cellI] = zoneID;
1809 }
1810 }
1811 }
1812 }
1813}
1814
1815
1816void Foam::meshRefinement::findCellZoneInsideWalk
1817(
1818 const pointField& locationsInMesh,
1819 const wordList& zoneNamesInMesh,
1820 const labelList& faceToZone, // per face -1 or some index >= 0
1821
1822 labelList& cellToZone
1823) const
1824{
1825 const cellZoneMesh& czs = mesh_.cellZones();
1826
1827 labelList zoneIDs(zoneNamesInMesh.size());
1828 forAll(zoneNamesInMesh, i)
1829 {
1830 zoneIDs[i] = czs.findZoneID(zoneNamesInMesh[i]);
1831 }
1832 findCellZoneInsideWalk
1833 (
1834 locationsInMesh,
1835 zoneIDs,
1836 faceToZone,
1837 cellToZone
1838 );
1839}
1840
1841
1842bool Foam::meshRefinement::calcRegionToZone
1843(
1844 const label backgroundZoneID,
1845 const label surfZoneI,
1846 const label ownRegion,
1847 const label neiRegion,
1848
1849 labelList& regionToCellZone
1850) const
1851{
1852 bool changed = false;
1853
1854 // Check whether inbetween different regions
1855 if (ownRegion != neiRegion)
1856 {
1857 // Jump. Change one of the sides to my type.
1858
1859 // 1. Interface between my type and unset region.
1860 // Set region to keepRegion
1861
1862 if (regionToCellZone[ownRegion] == -2)
1863 {
1864 if (surfZoneI == -1)
1865 {
1866 // Special: face is -on faceZone -not real boundary
1867 // -not on cellZone
1868 // so make regions same on either side
1869 if (regionToCellZone[neiRegion] != -2)
1870 {
1871 regionToCellZone[ownRegion] = regionToCellZone[neiRegion];
1872 changed = true;
1873 }
1874 }
1875 else if (regionToCellZone[neiRegion] == surfZoneI)
1876 {
1877 // Face between unset and my region. Put unset
1878 // region into background region
1879 //MEJ: see comment in findCellZoneTopo
1880 if (backgroundZoneID != -2)
1881 {
1882 regionToCellZone[ownRegion] = backgroundZoneID;
1883 changed = true;
1884 }
1885 }
1886 else if (regionToCellZone[neiRegion] != -2)
1887 {
1888 // Face between unset and other region.
1889 // Put unset region into my region
1890 regionToCellZone[ownRegion] = surfZoneI;
1891 changed = true;
1892 }
1893 }
1894 else if (regionToCellZone[neiRegion] == -2)
1895 {
1896 if (surfZoneI == -1)
1897 {
1898 // Special: face is -on faceZone -not real boundary
1899 // -not on cellZone
1900 // so make regions same on either side
1901 regionToCellZone[neiRegion] = regionToCellZone[ownRegion];
1902 changed = true;
1903 }
1904 else if (regionToCellZone[ownRegion] == surfZoneI)
1905 {
1906 // Face between unset and my region. Put unset
1907 // region into background region
1908 if (backgroundZoneID != -2)
1909 {
1910 regionToCellZone[neiRegion] = backgroundZoneID;
1911 changed = true;
1912 }
1913 }
1914 else if (regionToCellZone[ownRegion] != -2)
1915 {
1916 // Face between unset and other region.
1917 // Put unset region into my region
1918 regionToCellZone[neiRegion] = surfZoneI;
1919 changed = true;
1920 }
1921 }
1922 }
1923 return changed;
1924}
1925
1926
1927void Foam::meshRefinement::findCellZoneTopo
1928(
1929 const label backgroundZoneID,
1930 const pointField& locationsInMesh,
1931 const labelList& unnamedSurfaceRegion,
1932 const labelList& namedSurfaceRegion,
1933 const labelList& surfaceToCellZone,
1934 labelList& cellToZone
1935) const
1936{
1937 // This routine fixes small problems with left over unassigned regions
1938 // (after all off the unreachable bits of the mesh have been removed).
1939 // This routine splits the mesh into regions, based on the intersection
1940 // with a surface. The problem is that we know the surface which
1941 // (intersected) face belongs to (in namedSurfaceRegion) but we don't
1942 // know which side of the face it relates to. So all we are doing here
1943 // is get the correspondence between surface/cellZone and regionSplit
1944 // region. See the logic in calcRegionToZone.
1945 // Basically it looks at the neighbours of a face on a named surface.
1946 // If one side is in the cellZone corresponding to the surface and
1947 // the other side is unassigned (-2) it sets this to the background zone.
1948 // So the zone to set these unmarked cells to is provided as argument:
1949 // - backgroundZoneID = -2 : do not change so remove cells
1950 // - backgroundZoneID = -1 : put into background
1951
1952 // Assumes:
1953 // - region containing keepPoint does not go into a cellZone
1954 // - all other regions can be found by crossing faces marked in
1955 // namedSurfaceRegion.
1956
1957 // Analyse regions. Reuse regionsplit
1958 boolList blockedFace(mesh_.nFaces());
1959
1960 forAll(unnamedSurfaceRegion, faceI)
1961 {
1962 if
1963 (
1964 unnamedSurfaceRegion[faceI] == -1
1965 && namedSurfaceRegion[faceI] == -1
1966 )
1967 {
1968 blockedFace[faceI] = false;
1969 }
1970 else
1971 {
1972 blockedFace[faceI] = true;
1973 }
1974 }
1975 // No need to sync since namedSurfaceRegion already is synced
1976
1977 // Set region per cell based on walking
1978 regionSplit cellRegion(mesh_, blockedFace);
1979 blockedFace.clear();
1980
1981 // Per mesh region the zone the cell should be put in.
1982 // -2 : not analysed yet
1983 // -1 : keepPoint region. Not put into any cellzone.
1984 // >= 0 : index of cellZone
1985 labelList regionToCellZone(cellRegion.nRegions(), -2);
1986
1987 // See which cells already are set in the cellToZone (from geometric
1988 // searching) and use these to take over their zones.
1989 // Note: could be improved to count number of cells per region.
1990 forAll(cellToZone, cellI)
1991 {
1992 label regionI = cellRegion[cellI];
1993 if (cellToZone[cellI] != -2 && regionToCellZone[regionI] == -2)
1994 {
1995 regionToCellZone[regionI] = cellToZone[cellI];
1996 }
1997 }
1998
1999 // Synchronise regionToCellZone.
2000 // Note:
2001 // - region numbers are identical on all processors
2002 // - keepRegion is identical ,,
2003 // - cellZones are identical ,,
2004 Pstream::listReduce(regionToCellZone, maxOp<label>());
2005
2006
2007 // Find the region containing the keepPoint
2008 forAll(locationsInMesh, i)
2009 {
2010 const point& keepPoint = locationsInMesh[i];
2011 label keepRegionI = findRegion
2012 (
2013 mesh_,
2014 cellRegion,
2015 vector::uniform(mergeDistance_),
2016 keepPoint
2017 );
2018
2019 Info<< "Found point " << keepPoint
2020 << " in global region " << keepRegionI
2021 << " out of " << cellRegion.nRegions() << " regions." << endl;
2022
2023 if (keepRegionI == -1)
2024 {
2026 << "Point " << keepPoint
2027 << " is not inside the mesh." << nl
2028 << "Bounding box of the mesh:" << mesh_.bounds()
2029 << exit(FatalError);
2030 }
2031
2032 // Mark default region with zone -1. Note that all regions should
2033 // already be matched to a cellZone through the loop over cellToZone.
2034 // This is just to mop up any remaining regions. Not sure whether
2035 // this is needed?
2036 if (regionToCellZone[keepRegionI] == -2)
2037 {
2038 regionToCellZone[keepRegionI] = -1;
2039 }
2040 }
2041
2042
2043 // Find correspondence between cell zone and surface
2044 // by changing cell zone every time we cross a surface.
2045 while (true)
2046 {
2047 // Synchronise regionToCellZone.
2048 // Note:
2049 // - region numbers are identical on all processors
2050 // - keepRegion is identical ,,
2051 // - cellZones are identical ,,
2052 // This done at top of loop to account for geometric matching
2053 // not being synchronised.
2054 Pstream::listReduce(regionToCellZone, maxOp<label>());
2055
2056
2057 bool changed = false;
2058
2059 // Internal faces
2060
2061 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
2062 {
2063 label regionI = namedSurfaceRegion[faceI];
2064
2065 // Connected even if no cellZone defined for surface
2066 if (unnamedSurfaceRegion[faceI] == -1 && regionI != -1)
2067 {
2068 // Calculate region to zone from cellRegions on either side
2069 // of internal face.
2070
2071 label surfI = surfaces_.whichSurface(regionI).first();
2072
2073 bool changedCell = calcRegionToZone
2074 (
2075 backgroundZoneID,
2076 surfaceToCellZone[surfI],
2077 cellRegion[mesh_.faceOwner()[faceI]],
2078 cellRegion[mesh_.faceNeighbour()[faceI]],
2079 regionToCellZone
2080 );
2081
2082 changed = changed | changedCell;
2083 }
2084 }
2085
2086 // Boundary faces
2087
2088 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
2089
2090 // Get coupled neighbour cellRegion
2091 labelList neiCellRegion;
2092 syncTools::swapBoundaryCellList(mesh_, cellRegion, neiCellRegion);
2093
2094 // Calculate region to zone from cellRegions on either side of coupled
2095 // face.
2096 forAll(patches, patchI)
2097 {
2098 const polyPatch& pp = patches[patchI];
2099
2100 if (pp.coupled())
2101 {
2102 forAll(pp, i)
2103 {
2104 label faceI = pp.start()+i;
2105
2106 label regionI = namedSurfaceRegion[faceI];
2107
2108 // Connected even if no cellZone defined for surface
2109 if (unnamedSurfaceRegion[faceI] == -1 && regionI != -1)
2110 {
2111 label surfI = surfaces_.whichSurface(regionI).first();
2112
2113 bool changedCell = calcRegionToZone
2114 (
2115 backgroundZoneID,
2116 surfaceToCellZone[surfI],
2117 cellRegion[mesh_.faceOwner()[faceI]],
2118 neiCellRegion[faceI-mesh_.nInternalFaces()],
2119 regionToCellZone
2120 );
2121
2122 changed = changed | changedCell;
2123 }
2124 }
2125 }
2126 }
2127
2128 if (!returnReduceOr(changed))
2129 {
2130 break;
2131 }
2132 }
2133
2134
2135 if (debug)
2136 {
2137 Pout<< "meshRefinement::findCellZoneTopo :"
2138 << " nRegions:" << regionToCellZone.size()
2139 << " of which visited (-1 = background, >= 0 : cellZone) :"
2140 << endl;
2141
2142 forAll(regionToCellZone, regionI)
2143 {
2144 if (regionToCellZone[regionI] != -2)
2145 {
2146 Pout<< "Region " << regionI
2147 << " becomes cellZone:" << regionToCellZone[regionI]
2148 << endl;
2149 }
2150 }
2151 }
2152
2153 // Rework into cellToZone
2154 forAll(cellToZone, cellI)
2155 {
2156 label regionI = cellRegion[cellI];
2157 if (cellToZone[cellI] == -2 && regionToCellZone[regionI] != -2)
2158 {
2159 cellToZone[cellI] = regionToCellZone[regionI];
2160 }
2161 }
2162}
2163
2164
2165void Foam::meshRefinement::erodeCellZone
2166(
2167 const label nErodeCellZones,
2168 const label backgroundZoneID,
2169 const labelList& unnamedSurfaceRegion,
2170 const labelList& namedSurfaceRegion,
2171 labelList& cellToZone
2172) const
2173{
2174 // This routine fixes small problems with left over unassigned regions
2175 // (after all off the unreachable bits of the mesh have been removed).
2176 // The problem is that the cell zone information might be inconsistent
2177 // with the face zone information. So what we do here is to erode
2178 // any cell zones until we hit a named face.
2179 // - backgroundZoneID = -2 : do not change so remove cells
2180 // - backgroundZoneID = -1 : put into background
2181 // Note that is the opposite of findCellZoneTopo which moves unassigned
2182 // regions into a neighbouring region(=cellZone) unless there is an
2183 // intersected faces inbetween the two.
2184
2185 for (label iter = 0; iter < nErodeCellZones; iter++)
2186 {
2187 label nChanged = 0;
2188
2189 labelList erodedCellToZone(cellToZone);
2190
2191 // Do internal faces
2192 for (label facei = 0; facei < mesh_.nInternalFaces(); facei++)
2193 {
2194 if
2195 (
2196 unnamedSurfaceRegion[facei] == -1
2197 && namedSurfaceRegion[facei] == -1
2198 )
2199 {
2200 label own = mesh_.faceOwner()[facei];
2201 label nei = mesh_.faceNeighbour()[facei];
2202 if (cellToZone[own] == -2 && cellToZone[nei] >= -1)
2203 {
2204 erodedCellToZone[nei] = backgroundZoneID;
2205 nChanged++;
2206 }
2207 else if (cellToZone[nei] == -2 && cellToZone[own] >= -1)
2208 {
2209 erodedCellToZone[own] = backgroundZoneID;
2210 nChanged++;
2211 }
2212 }
2213 }
2214
2215 // Do boundary faces
2216
2217 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
2218
2219 // Get coupled neighbour cellRegion
2220 labelList neiCellZone;
2221 syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone);
2222
2223 // Calculate region to zone from cellRegions on either side of coupled
2224 // face.
2225 forAll(patches, patchi)
2226 {
2227 const polyPatch& pp = patches[patchi];
2228
2229 if (pp.coupled())
2230 {
2231 forAll(pp, i)
2232 {
2233 label facei = pp.start()+i;
2234 if
2235 (
2236 unnamedSurfaceRegion[facei] == -1
2237 && namedSurfaceRegion[facei] == -1
2238 )
2239 {
2240 label own = mesh_.faceOwner()[facei];
2241 label bFacei = facei-mesh_.nInternalFaces();
2242 if (neiCellZone[bFacei] == -2 && cellToZone[own] >= -1)
2243 {
2244 erodedCellToZone[own] = backgroundZoneID;
2245 nChanged++;
2246 }
2247 }
2248 }
2249 }
2250 }
2251
2252 cellToZone.transfer(erodedCellToZone);
2253
2254 reduce(nChanged, sumOp<label>());
2255 if (debug)
2256 {
2257 Pout<< "erodeCellZone : eroded " << nChanged
2258 << " cells (moved from cellZone to background zone "
2259 << backgroundZoneID << endl;
2260 }
2261
2262 if (nChanged == 0)
2263 {
2264 break;
2265 }
2266 }
2267}
2268
2269
2270void Foam::meshRefinement::growCellZone
2271(
2272 const label nGrowCellZones,
2273 const label backgroundZoneID,
2274 labelList& unnamedSurfaceRegion1,
2275 labelList& unnamedSurfaceRegion2,
2276 labelList& namedSurfaceRegion, // potentially zero size if no faceZones
2277 labelList& cellToZone
2278) const
2279{
2280 if (nGrowCellZones != 1)
2281 {
2283 << "Growing currently only supported for single layer."
2284 << " Exiting zone growing" << endl;
2285 return;
2286 }
2287
2288
2289 // See meshRefinement::markProximityRefinementWave:
2290 // - walk out nGrowCellZones iterations from outside of cellZone
2291 // and wall into unassigned cells
2292 // - detect any cells inbetween
2293 // - multiple zones
2294 // - zone and wall
2295 // and
2296 // - assign cells to the cellZone
2297 // - unblock faces (along the path) inbetween
2298
2299
2300 // Special tag for walls
2301 const FixedList<label, 3> wallTag
2302 ({
2303 labelMax, // Special value for wall face. Loses out to cellZone tag
2304 labelMax,
2305 labelMax
2306 });
2307
2308 // Work arrays
2309 pointField origins(1);
2310 scalarField distSqrs(1);
2311 List<FixedList<label, 3>> surfaces(1);
2312
2313
2314 // Set up blockage. Marked with 0 distance (so always wins)
2315
2316 //List<wallPoints> allFaceInfo(mesh_.nFaces());
2317 //for (label facei = 0; facei < mesh_.nInternalFaces(); facei++)
2318 //{
2319 // if
2320 // (
2321 // unnamedSurfaceRegion1[facei] != -1
2322 // || unnamedSurfaceRegion2[facei] != -1
2323 // )
2324 // {
2325 // origins[0] = mesh_.faceCentres()[facei];
2326 // distSqrs[0] = 0.0; // Initial distance
2327 // surfaces[0] = wallTag;
2328 // allFaceInfo[facei] = wallPoints(origins, distSqrs, surfaces);
2329 // }
2330 //}
2331 List<wallPoints> allCellInfo(mesh_.nCells());
2332 forAll(cellToZone, celli)
2333 {
2334 if (cellToZone[celli] >= 0)
2335 {
2336 const FixedList<label, 3> zoneTag
2337 ({
2338 cellToZone[celli], // zone
2339 0, // 'region'
2340 labelMax // 'sub-region'
2341 });
2342
2343 origins[0] = mesh_.cellCentres()[celli];
2344 distSqrs[0] = 0.0; // Initial distance
2345 surfaces[0] = zoneTag;
2346 allCellInfo[celli] = wallPoints(origins, distSqrs, surfaces);
2347 }
2348 }
2349
2350
2351 DynamicList<wallPoints> faceDist(mesh_.nFaces()/128);
2352 DynamicList<label> changedFaces(mesh_.nFaces()/128);
2353
2354 for (label facei = 0; facei < mesh_.nInternalFaces(); facei++)
2355 {
2356 const label own = mesh_.faceOwner()[facei];
2357 const label nei = mesh_.faceNeighbour()[facei];
2358 if (cellToZone[own] >= 0 && cellToZone[nei] < 0)
2359 {
2360 // boundary between cellZone (own) and background/unvisited (nei)
2361
2362 origins[0] = mesh_.faceCentres()[facei];
2363 distSqrs[0] = 0.0; // Initial distance
2364 surfaces[0] = FixedList<label, 3>
2365 ({
2366 cellToZone[own], // zone
2367 0, // 'region'
2368 labelMax // 'sub-region'
2369 });
2370 faceDist.append(wallPoints(origins, distSqrs, surfaces));
2371 changedFaces.append(facei);
2372 }
2373 else if (cellToZone[own] < 0 && cellToZone[nei] >= 0)
2374 {
2375 // boundary between cellZone (nei) and background/unvisited (own)
2376
2377 origins[0] = mesh_.faceCentres()[facei];
2378 distSqrs[0] = 0.0; // Initial distance
2379 surfaces[0] = FixedList<label, 3>
2380 ({
2381 cellToZone[nei], // zone
2382 0, // 'region'
2383 labelMax // 'sub-region'
2384 });
2385 faceDist.append(wallPoints(origins, distSqrs, surfaces));
2386 changedFaces.append(facei);
2387 }
2388 else if
2389 (
2390 unnamedSurfaceRegion1[facei] != -1
2391 || unnamedSurfaceRegion2[facei] != -1
2392 )
2393 {
2394 // Seed (yet unpatched) wall faces
2395
2396 origins[0] = mesh_.faceCentres()[facei];
2397 distSqrs[0] = 0.0; // Initial distance
2398 surfaces[0] = wallTag;
2399 faceDist.append(wallPoints(origins, distSqrs, surfaces));
2400 changedFaces.append(facei);
2401 }
2402 }
2403
2404 // Get coupled neighbour cellRegion
2405 labelList neiCellZone;
2406 syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone);
2407
2408 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
2409
2410 // Calculate region to zone from cellRegions on either side of coupled
2411 // face.
2412 forAll(patches, patchi)
2413 {
2414 const polyPatch& pp = patches[patchi];
2415
2416 if (pp.coupled())
2417 {
2418 // Like internal faces
2419 forAll(pp, i)
2420 {
2421 label facei = pp.start()+i;
2422 label own = mesh_.faceOwner()[facei];
2423 label bFacei = facei-mesh_.nInternalFaces();
2424 if (cellToZone[own] >= 0 && neiCellZone[bFacei] < 0)
2425 {
2426 origins[0] = mesh_.faceCentres()[facei];
2427 distSqrs[0] = 0.0;
2428 surfaces[0] = FixedList<label, 3>
2429 ({
2430 cellToZone[own], // zone
2431 0, // 'region'
2432 labelMax // 'sub-region'
2433 });
2434 faceDist.append(wallPoints(origins, distSqrs, surfaces));
2435 changedFaces.append(facei);
2436 }
2437 else if (cellToZone[own] < 0 && neiCellZone[bFacei] >= 0)
2438 {
2439 // Handled on nbr processor
2440 }
2441 else if
2442 (
2443 unnamedSurfaceRegion1[facei] != -1
2444 || unnamedSurfaceRegion2[facei] != -1
2445 )
2446 {
2447 // Seed (yet unpatched) wall faces
2448 origins[0] = mesh_.faceCentres()[facei];
2449 distSqrs[0] = 0.0; // Initial distance
2450 surfaces[0] = wallTag;
2451 faceDist.append(wallPoints(origins, distSqrs, surfaces));
2452 changedFaces.append(facei);
2453 }
2454 }
2455 }
2456 else
2457 {
2458 // Seed wall faces
2459 forAll(pp, i)
2460 {
2461 label facei = pp.start()+i;
2462 label own = mesh_.faceOwner()[facei];
2463 if (cellToZone[own] < 0)
2464 {
2465 origins[0] = mesh_.faceCentres()[facei];
2466 distSqrs[0] = 0.0; // Initial distance
2467 surfaces[0] = wallTag;
2468 faceDist.append(wallPoints(origins, distSqrs, surfaces));
2469 changedFaces.append(facei);
2470 }
2471 }
2472 }
2473 }
2474
2475
2476 List<wallPoints> allFaceInfo(mesh_.nFaces());
2477
2478 // No blocked faces, limitless gap size
2479 const bitSet isBlockedFace(mesh_.nFaces());
2480 //List<scalarList> regionToBlockSize(surfaces_.surfaces().size());
2481 //{
2482 // forAll(surfaces_.surfaces(), surfi)
2483 // {
2484 // const label geomi = surfaces_.surfaces()[surfi];
2485 // const auto& s = surfaces_.geometry()[geomi];
2486 // const label nRegions = s.regions().size();
2487 // regionToBlockSize[surfi].setSize(nRegions, Foam::sqr(GREAT));
2488 // }
2489 //}
2490
2491 //- regionToBlockSize is indexed with cellZone index (>= 0) and region
2492 //- on cellZone (currently always 0)
2493 const auto& czs = mesh_.cellZones();
2494 List<scalarList> regionToBlockSize(czs.size());
2495 for (auto& blockSizes : regionToBlockSize)
2496 {
2497 blockSizes.setSize(1, Foam::sqr(GREAT));
2498 }
2499
2500
2501 wallPoints::trackData td(isBlockedFace, regionToBlockSize);
2503 (
2504 mesh_,
2505 changedFaces,
2506 faceDist,
2509 0, // max iterations
2510 td
2511 );
2512 wallDistCalc.iterate(nGrowCellZones);
2513
2514
2515 // Check for cells which are within nGrowCellZones of two cellZones or
2516 // one cellZone and a wall
2517 // TBD. Currently only one layer of growth handled.
2518
2519 bitSet isChangedCell(mesh_.nCells());
2520
2521 forAll(allCellInfo, celli)
2522 {
2523 if (allCellInfo[celli].valid(wallDistCalc.data()))
2524 {
2525 const List<FixedList<label, 3>>& surfaces =
2526 allCellInfo[celli].surface();
2527
2528 if (surfaces.size())
2529 {
2530 // Cell close to cellZone. Remove any free-standing baffles.
2531 // Done by marking as changed cell. Wip.
2532 isChangedCell.set(celli);
2533 }
2534
2535 if (surfaces.size() > 1)
2536 {
2537 // Check if inbetween two cellZones or cellZone and wall
2538 // Find 'nearest' other cellZone
2539 scalar minDistSqr = Foam::sqr(GREAT);
2540 label minZone = -1;
2541 for (label i = 0; i < surfaces.size(); i++)
2542 {
2543 const label zonei = surfaces[i][0];
2544 const scalar d2 = allCellInfo[celli].distSqr()[i];
2545 if (zonei != cellToZone[celli] && d2 < minDistSqr)
2546 {
2547 minDistSqr = d2;
2548 minZone = zonei; // zoneID
2549 }
2550 }
2551
2552 if (minDistSqr < Foam::sqr(GREAT))
2553 {
2554 if (minZone != cellToZone[celli] && minZone != wallTag[0])
2555 {
2556 cellToZone[celli] = minZone;
2557 isChangedCell.set(celli);
2558 }
2559 }
2560 }
2561 }
2562 }
2563
2564 // Fix any left-over unvisited cells
2565 if (backgroundZoneID != -2)
2566 {
2567 forAll(cellToZone, celli)
2568 {
2569 if (cellToZone[celli] == -2)
2570 {
2571 cellToZone[celli] = backgroundZoneID;
2572 }
2573 }
2574 }
2575
2576
2577 // Make sure to unset faces of changed cell
2578
2579 syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone);
2580
2581
2582 label nUnnamed = 0;
2583 label nNamed = 0;
2584 for (const label celli : isChangedCell)
2585 {
2586 const cell& cFaces = mesh_.cells()[celli];
2587 for (const label facei : cFaces)
2588 {
2589 const label own = mesh_.faceOwner()[facei];
2590 const label ownZone = cellToZone[own];
2591
2592 label nbrZone = -1;
2593 if (mesh_.isInternalFace(facei))
2594 {
2595 const label neiZone = cellToZone[mesh_.faceNeighbour()[facei]];
2596 nbrZone = (own != celli ? ownZone : neiZone);
2597 }
2598 else
2599 {
2600 nbrZone = neiCellZone[facei-mesh_.nInternalFaces()];
2601 }
2602
2603 if (nbrZone == cellToZone[celli])
2604 {
2605 if
2606 (
2607 unnamedSurfaceRegion1[facei] != -1
2608 || unnamedSurfaceRegion2[facei] != -1
2609 )
2610 {
2611 unnamedSurfaceRegion1[facei] = -1;
2612 unnamedSurfaceRegion2[facei] = -1;
2613 nUnnamed++;
2614 }
2615 if
2616 (
2617 namedSurfaceRegion.size()
2618 && namedSurfaceRegion[facei] != -1
2619 )
2620 {
2621 namedSurfaceRegion[facei] = -1;
2622 nNamed++;
2623 }
2624 }
2625 }
2626 }
2627
2628 reduce(nUnnamed, sumOp<label>());
2629 reduce(nNamed, sumOp<label>());
2630
2631 // Do always; might bypass if nNamed,nUnnamed zero
2633 (
2634 mesh_,
2635 unnamedSurfaceRegion1,
2637 );
2639 (
2640 mesh_,
2641 unnamedSurfaceRegion2,
2643 );
2644 if (namedSurfaceRegion.size())
2645 {
2647 (
2648 mesh_,
2649 namedSurfaceRegion,
2651 );
2652 }
2653
2654 if (debug)
2655 {
2656 Pout<< "growCellZone : grown cellZones by "
2657 << returnReduce(isChangedCell.count(), sumOp<label>())
2658 << " cells (moved from background to nearest cellZone)"
2659 << endl;
2660 Pout<< "growCellZone : unmarked " << nUnnamed
2661 << " unzoned intersections; " << nNamed << " zoned intersections; "
2662 << endl;
2663 }
2664}
2665
2666
2667void Foam::meshRefinement::makeConsistentFaceIndex
2668(
2669 const labelList& surfaceMap,
2670 const labelList& cellToZone,
2671 labelList& namedSurfaceRegion
2672) const
2673{
2674 // Make namedSurfaceRegion consistent with cellToZone - clear out any
2675 // blocked faces inbetween same cell zone (or background (=-1))
2676 // Do not do this for surfaces relating to 'pure' faceZones i.e.
2677 // faceZones without a cellZone. Note that we cannot check here
2678 // for different cellZones on either side but no namedSurfaceRegion
2679 // since cellZones can now originate from locationsInMesh as well
2680 // (instead of only through named surfaces)
2681
2682 const labelList& faceOwner = mesh_.faceOwner();
2683 const labelList& faceNeighbour = mesh_.faceNeighbour();
2684
2685 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
2686 {
2687 label ownZone = cellToZone[faceOwner[faceI]];
2688 label neiZone = cellToZone[faceNeighbour[faceI]];
2689 label globalI = namedSurfaceRegion[faceI];
2690
2691 if
2692 (
2693 ownZone == neiZone
2694 && globalI != -1
2695 && surfaceMap[surfaces_.whichSurface(globalI).first()] == -1
2696 )
2697 {
2698 namedSurfaceRegion[faceI] = -1;
2699 }
2700 }
2701
2702 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
2703
2704 // Get coupled neighbour cellZone
2705 labelList neiCellZone;
2706 syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone);
2707
2708 // Use coupled cellZone to do check
2709 forAll(patches, patchI)
2710 {
2711 const polyPatch& pp = patches[patchI];
2712
2713 if (pp.coupled())
2714 {
2715 forAll(pp, i)
2716 {
2717 label faceI = pp.start()+i;
2718
2719 label ownZone = cellToZone[faceOwner[faceI]];
2720 label neiZone = neiCellZone[faceI-mesh_.nInternalFaces()];
2721 label globalI = namedSurfaceRegion[faceI];
2722
2723 if
2724 (
2725 ownZone == neiZone
2726 && globalI != -1
2727 && surfaceMap[surfaces_.whichSurface(globalI).first()] == -1
2728 )
2729 {
2730 namedSurfaceRegion[faceI] = -1;
2731 }
2732 }
2733 }
2734 else
2735 {
2736 // Unzonify boundary faces
2737 forAll(pp, i)
2738 {
2739 label faceI = pp.start()+i;
2740 label globalI = namedSurfaceRegion[faceI];
2741
2742 if
2743 (
2744 globalI != -1
2745 && surfaceMap[surfaces_.whichSurface(globalI).first()] == -1
2746 )
2747 {
2748 namedSurfaceRegion[faceI] = -1;
2749 }
2750 }
2751 }
2752 }
2753}
2754
2755
2756void Foam::meshRefinement::getIntersections
2757(
2758 const labelList& surfacesToTest,
2759 const pointField& neiCc,
2760 const labelList& testFaces,
2761
2762 labelList& namedSurfaceRegion,
2763 bitSet& posOrientation
2764) const
2765{
2766 namedSurfaceRegion.setSize(mesh_.nFaces());
2767 namedSurfaceRegion = -1;
2768
2769 posOrientation.setSize(mesh_.nFaces());
2770 posOrientation = false;
2771
2772 // Statistics: number of faces per faceZone
2773 labelList nSurfFaces(surfaces_.surfZones().size(), Zero);
2774
2775 // Collect segments
2776 // ~~~~~~~~~~~~~~~~
2777
2778 pointField start(testFaces.size());
2779 pointField end(testFaces.size());
2780
2781 {
2782 labelList minLevel;
2783 calcCellCellRays
2784 (
2785 neiCc,
2786 labelList(neiCc.size(), -1),
2787 testFaces,
2788 start,
2789 end,
2790 minLevel
2791 );
2792 }
2793
2794 // Do test for intersections
2795 // ~~~~~~~~~~~~~~~~~~~~~~~~~
2796 // Note that we intersect all intersected faces again. Could reuse
2797 // the information already in surfaceIndex_.
2798
2799 labelList surface1;
2800 labelList region1;
2802 vectorField normal1;
2803 labelList surface2;
2804 labelList region2;
2806 vectorField normal2;
2807
2808 surfaces_.findNearestIntersection
2809 (
2810 surfacesToTest,
2811 start,
2812 end,
2813
2814 surface1,
2815 hit1,
2816 region1,
2817 normal1,
2818
2819 surface2,
2820 hit2,
2821 region2,
2822 normal2
2823 );
2824
2825 forAll(testFaces, i)
2826 {
2827 label faceI = testFaces[i];
2828 const vector& area = mesh_.faceAreas()[faceI];
2829
2830 if (surface1[i] != -1)
2831 {
2832 // If both hit should probably choose 'nearest'
2833 if
2834 (
2835 surface2[i] != -1
2836 && (
2837 magSqr(hit2[i].hitPoint())
2838 < magSqr(hit1[i].hitPoint())
2839 )
2840 )
2841 {
2842 namedSurfaceRegion[faceI] = surfaces_.globalRegion
2843 (
2844 surface2[i],
2845 region2[i]
2846 );
2847 posOrientation.set(faceI, ((area&normal2[i]) > 0));
2848 nSurfFaces[surface2[i]]++;
2849 }
2850 else
2851 {
2852 namedSurfaceRegion[faceI] = surfaces_.globalRegion
2853 (
2854 surface1[i],
2855 region1[i]
2856 );
2857 posOrientation.set(faceI, ((area&normal1[i]) > 0));
2858 nSurfFaces[surface1[i]]++;
2859 }
2860 }
2861 else if (surface2[i] != -1)
2862 {
2863 namedSurfaceRegion[faceI] = surfaces_.globalRegion
2864 (
2865 surface2[i],
2866 region2[i]
2867 );
2868 posOrientation.set(faceI, ((area&normal2[i]) > 0));
2869 nSurfFaces[surface2[i]]++;
2870 }
2871 }
2872
2873
2874 // surfaceIndex might have different surfaces on both sides if
2875 // there happen to be a (obviously thin) surface with different
2876 // regions between the cell centres. If one is on a named surface
2877 // and the other is not this might give problems so sync.
2879 (
2880 mesh_,
2881 namedSurfaceRegion,
2883 );
2884
2885 // Print a bit
2886 if (debug)
2887 {
2888 forAll(nSurfFaces, surfI)
2889 {
2890 Pout<< "Surface:"
2891 << surfaces_.names()[surfI]
2892 << " nZoneFaces:" << nSurfFaces[surfI] << nl;
2893 }
2894 Pout<< endl;
2895 }
2896}
2897
2898
2899void Foam::meshRefinement::zonify
2900(
2901 const bool allowFreeStandingZoneFaces,
2902 const label nErodeCellZones,
2903 const label backgroundZoneID,
2904 const pointField& locationsInMesh,
2905 const wordList& zonesInMesh,
2906 const pointField& locationsOutsideMesh,
2907 const bool exitIfLeakPath,
2908 const refPtr<coordSetWriter>& leakPathFormatter,
2909 refPtr<surfaceWriter>& surfFormatter,
2910
2911 labelList& cellToZone,
2912 labelList& unnamedRegion1,
2913 labelList& unnamedRegion2,
2914 labelList& namedSurfaceRegion,
2915 bitSet& posOrientation
2916) const
2917{
2918 // Determine zones for cells and faces
2919 // cellToZone:
2920 // -2 : unset
2921 // -1 : not in any zone (zone 'none' or background zone)
2922 // >=0 : zoneID
2923 // namedSurfaceRegion, posOrientation:
2924 // -1 : face not intersected by named surface
2925 // >=0 : globalRegion (surface+region)
2926 // (and posOrientation: surface normal v.s. face normal)
2927
2928 const PtrList<surfaceZonesInfo>& surfZones = surfaces_.surfZones();
2929
2930 // Collect inside and outside into single list
2931 const List<pointField> allLocations
2932 (
2934 (
2935 locationsInMesh,
2936 zonesInMesh,
2937 locationsOutsideMesh
2938 )
2939 );
2940
2941 // Swap neighbouring cell centres and cell level
2942 labelList neiLevel(mesh_.nBoundaryFaces());
2943 pointField neiCc(mesh_.nBoundaryFaces());
2944 calcNeighbourData(neiLevel, neiCc);
2945
2946
2947 labelList namedSurfaces(surfaceZonesInfo::getNamedSurfaces(surfZones));
2948 labelList unnamedSurfaces(surfaceZonesInfo::getUnnamedSurfaces(surfZones));
2949
2950 // Get map from surface to cellZone (or -1)
2951 labelList surfaceToCellZone;
2952 if (namedSurfaces.size())
2953 {
2954 // Get/add cellZones corresponding to surface names
2955 surfaceToCellZone = surfaceZonesInfo::addCellZonesToMesh
2956 (
2957 surfZones,
2958 namedSurfaces,
2959 mesh_
2960 );
2961 }
2962
2963
2964 cellToZone.setSize(mesh_.nCells());
2965 cellToZone = -2;
2966
2967 namedSurfaceRegion.clear();
2968 posOrientation.clear();
2969
2970
2971
2972 // 1. Test all (unnamed & named) surfaces
2973
2974 // Unnamed surfaces. Global surface region of intersection (or -1)
2975 getIntersections
2976 (
2977 unnamedSurfaces,
2978 neiCc,
2979 intersectedFaces(),
2980 unnamedRegion1,
2981 unnamedRegion2
2982 );
2983
2984 // Extend with hole closing faces (only if locationsOutsideMesh)
2985 labelList unnamedFaces;
2986 labelList unnamedClosureFaces;
2987 labelList unnamedToClosure;
2988 autoPtr<mapDistribute> unnamedMapPtr;
2989 if (locationsOutsideMesh.size())
2990 {
2991 unnamedFaces = ListOps::findIndices
2992 (
2993 unnamedRegion1,
2994 [](const label x){return x != -1;}
2995 );
2996
2997 const globalIndex globalUnnamedFaces(unnamedFaces.size());
2998
2999 unnamedMapPtr = holeToFace::calcClosure
3000 (
3001 mesh_,
3002 allLocations,
3003 unnamedFaces,
3004 globalUnnamedFaces,
3005 true, // allow erosion
3006
3007 unnamedClosureFaces,
3008 unnamedToClosure
3009 );
3010
3011 if (debug)
3012 {
3013 Pout<< "meshRefinement::zonify : found wall closure faces:"
3014 << unnamedClosureFaces.size()
3015 << " map:" << bool(unnamedMapPtr) << endl;
3016 }
3017
3018
3019 // Add to unnamedRegion1, unnamedRegion2
3020 if (unnamedMapPtr)
3021 {
3022 // Dump leak path
3023 if (leakPathFormatter)
3024 {
3025 boolList blockedFace(mesh_.nFaces(), false);
3026 UIndirectList<bool>(blockedFace, unnamedFaces) = true;
3027 const fileName fName
3028 (
3029 writeLeakPath
3030 (
3031 mesh_,
3032 locationsInMesh,
3033 locationsOutsideMesh,
3034 blockedFace,
3035 leakPathFormatter.constCast()
3036 )
3037 );
3038 Info<< "Dumped leak path to " << fName << endl;
3039 }
3040
3041 auto& err =
3042 (
3043 exitIfLeakPath
3046 );
3047
3048 err << "Locations in mesh " << locationsInMesh
3049 << " connect to one of the locations outside mesh "
3050 << locationsOutsideMesh << endl;
3051
3052 if (exitIfLeakPath)
3053 {
3055 }
3056
3057
3058 labelList packedRegion1
3059 (
3060 UIndirectList<label>(unnamedRegion1, unnamedFaces)
3061 );
3062 unnamedMapPtr->distribute(packedRegion1);
3063 labelList packedRegion2
3064 (
3065 UIndirectList<label>(unnamedRegion2, unnamedFaces)
3066 );
3067 unnamedMapPtr->distribute(packedRegion2);
3068 forAll(unnamedClosureFaces, i)
3069 {
3070 const label sloti = unnamedToClosure[i];
3071
3072 if (sloti != -1)
3073 {
3074 const label facei = unnamedClosureFaces[i];
3075 const label region1 = unnamedRegion1[facei];
3076 const label slotRegion1 = packedRegion1[sloti];
3077 const label region2 = unnamedRegion2[facei];
3078 const label slotRegion2 = packedRegion2[sloti];
3079
3080 if (slotRegion1 != region1 || slotRegion2 != region2)
3081 {
3082 unnamedRegion1[facei] = slotRegion1;
3083 unnamedRegion2[facei] = slotRegion2;
3084 }
3085 }
3086 }
3087 }
3088 }
3089
3090
3091 // Extend with hole closing faces (only if locationsOutsideMesh)
3092 labelList namedFaces;
3093 labelList namedClosureFaces;
3094 labelList namedToClosure;
3095 autoPtr<mapDistribute> namedMapPtr;
3096 if (namedSurfaces.size())
3097 {
3098 getIntersections
3099 (
3100 namedSurfaces,
3101 neiCc,
3102 intersectedFaces(),
3103 namedSurfaceRegion,
3104 posOrientation
3105 );
3106
3107 // Ideally we'd like to close 'cellZone' surfaces. The problem is
3108 // that we don't (easily) know which locationsInMesh should be inside
3109 // the surface and which aren't. With 'insidePoint' definition of
3110 // cellZone we have a location inside the cellZone but how do we
3111 // know where the locationsInMesh are? Are they inside the cellZone
3112 // as well? Only with the 'locationsInMesh' notation where we specify
3113 // the cellZone and the seedpoint could we make sure that we cannot
3114 // walk from one to the other.
3115 // For now disable hole closure on cellZones
3116
3117 //if (locationsOutsideMesh.size())
3118 //{
3119 // namedFaces = ListOps::findIndices
3120 // (
3121 // namedSurfaceRegion,
3122 // [](const label x){return x != -1;}
3123 // );
3124 //
3125 // {
3126 // OBJstream str(mesh_.time().timePath()/"namedFaces.obj");
3127 // Pout<< "Writing " << namedFaces.size() << " zone faces to "
3128 // << str.name() << endl;
3129 // str.write
3130 // (
3131 // UIndirectList<face>(mesh_.faces(), namedFaces)(),
3132 // mesh_.points()
3133 // );
3134 // }
3135 //
3136 //
3137 // const globalIndex globalNamedFaces(namedFaces.size());
3138 //
3139 // namedMapPtr = holeToFace::calcClosure
3140 // (
3141 // mesh_,
3142 // allLocations,
3143 // namedFaces, // or also unnamedFaces?
3144 // globalNamedFaces,
3145 // true, // allow erosion
3146 //
3147 // namedClosureFaces,
3148 // namedToClosure
3149 // );
3150 //
3151 // if (debug)
3152 // {
3153 // Pout<< "meshRefinement::zonify :"
3154 // << " found faceZone closure faces:"
3155 // << namedClosureFaces.size()
3156 // << " map:" << bool(namedMapPtr) << endl;
3157 // }
3158 //
3159 // // Add to namedSurfaceRegion, posOrientation
3160 // if (namedMapPtr)
3161 // {
3162 // WarningInFunction
3163 // << "Detected and closed leak path"
3164 // << " through zoned surfaces from "
3165 // << locationsInMesh << " to " << locationsOutsideMesh
3166 // << endl;
3167 //
3168 // // Dump leak path
3169 // if (leakPathFormatter)
3170 // {
3171 // boolList blockedFace(mesh_.nFaces(), false);
3172 // UIndirectList<bool>(blockedFace, unnamedFaces) = true;
3173 // UIndirectList<bool>(blockedFace, namedFaces) = true;
3174 // const fileName fName
3175 // (
3176 // writeLeakPath
3177 // (
3178 // mesh_,
3179 // locationsInMesh,
3180 // locationsOutsideMesh,
3181 // blockedFace,
3182 // leakPathFormatter.constCast()
3183 // )
3184 // );
3185 // Info<< "Dumped leak path to " << fName << endl;
3186 // }
3187 //
3188 // labelList packedSurfaceRegion
3189 // (
3190 // UIndirectList<label>(namedSurfaceRegion, namedFaces)
3191 // );
3192 // namedMapPtr->distribute(packedSurfaceRegion);
3193 // boolList packedOrientation(posOrientation.size());
3194 // forAll(namedFaces, i)
3195 // {
3196 // const label facei = namedFaces[i];
3197 // packedOrientation[i] = posOrientation[facei];
3198 // }
3199 // namedMapPtr->distribute(packedOrientation);
3200 // forAll(namedClosureFaces, i)
3201 // {
3202 // const label sloti = namedToClosure[i];
3203 // if (sloti != -1)
3204 // {
3205 // const label facei = namedClosureFaces[i];
3206 // const label regioni = namedSurfaceRegion[facei];
3207 // const label slotRegioni = packedSurfaceRegion[sloti];
3208 // const bool orient = posOrientation[facei];
3209 // const bool slotOrient = packedOrientation[sloti];
3210 //
3211 // if (slotRegioni != regioni || slotOrient != orient)
3212 // {
3213 // namedSurfaceRegion[facei] = slotRegioni;
3214 // posOrientation[facei] = slotOrient;
3215 // }
3216 // }
3217 // }
3218 // }
3219 //}
3220 }
3221
3222
3223 // 1b. Add any hole closure faces to frozenPoints pointZone
3224 {
3225 bitSet isClosureFace(mesh_.nFaces());
3226 isClosureFace.set(unnamedClosureFaces);
3227 isClosureFace.set(namedClosureFaces);
3228 const labelList closureFaces(isClosureFace.sortedToc());
3229
3231 (
3232 UIndirectList<face>(mesh_.faces(), closureFaces),
3233 mesh_.points()
3234 );
3235
3236 // Count number of faces per edge
3237 const labelList nEdgeFaces(countEdgeFaces(pp));
3238
3239 // Freeze all internal points
3240 bitSet isFrozenPoint(mesh_.nPoints());
3241 forAll(nEdgeFaces, edgei)
3242 {
3243 if (nEdgeFaces[edgei] != 1)
3244 {
3245 const edge& e = pp.edges()[edgei];
3246 isFrozenPoint.set(pp.meshPoints()[e[0]]);
3247 isFrozenPoint.set(pp.meshPoints()[e[1]]);
3248 }
3249 }
3250
3251 // Lookup/add pointZone and include its points
3252 pointZoneMesh& pointZones =
3253 const_cast<pointZoneMesh&>(mesh_.pointZones());
3254 const label zonei =
3255 const_cast<meshRefinement&>(*this).addPointZone("frozenPoints");
3256 const bitSet oldSet(mesh_.nPoints(), pointZones[zonei]);
3257 isFrozenPoint.set(oldSet);
3258
3260 (
3261 mesh_,
3262 isFrozenPoint,
3264 0u
3265 );
3266
3267 // Override addressing
3268 pointZones.clearAddressing();
3269 pointZones[zonei] = isFrozenPoint.sortedToc();
3270
3271 if (debug)
3272 {
3273 mkDir(mesh_.time().timePath());
3274 const pointZone& pz = pointZones[zonei];
3275 OBJstream str(mesh_.time().timePath()/pz.name()+".obj");
3276 Pout<< "Writing " << pz.size() << " frozen points to "
3277 << str.name() << endl;
3278 for (const label pointi : pz)
3279 {
3280 str.write(mesh_.points()[pointi]);
3281 }
3282 }
3283
3284 if (returnReduceOr(unnamedClosureFaces.size()) && surfFormatter)
3285 {
3287 (
3288 mesh_.time().globalPath()
3290 / mesh_.pointsInstance()
3291 / "unnamedClosureFaces"
3292 );
3293 outputName.clean(); // Remove unneeded ".."
3294
3295 Info<< "Writing "
3296 << returnReduce(unnamedClosureFaces.size(), sumOp<label>())
3297 << " unnamedClosureFaces to " << outputName << endl;
3298
3299 const indirectPrimitivePatch setPatch
3300 (
3301 IndirectList<face>(mesh_.faces(), unnamedClosureFaces),
3302 mesh_.points()
3303 );
3304
3305 surfFormatter->open
3306 (
3307 setPatch.localPoints(),
3308 setPatch.localFaces(),
3310 );
3311 surfFormatter->write();
3312 surfFormatter->clear();
3313 }
3314 if (returnReduceOr(namedClosureFaces.size()) && surfFormatter)
3315 {
3317 (
3318 mesh_.time().globalPath()
3320 / mesh_.pointsInstance()
3321 / "namedClosureFaces"
3322 );
3323 outputName.clean(); // Remove unneeded ".."
3324
3325 Info<< "Writing "
3326 << returnReduce(namedClosureFaces.size(), sumOp<label>())
3327 << " namedClosureFaces to " << outputName << endl;
3328
3329 const indirectPrimitivePatch setPatch
3330 (
3331 IndirectList<face>(mesh_.faces(), namedClosureFaces),
3332 mesh_.points()
3333 );
3334
3335 surfFormatter->open
3336 (
3337 setPatch.localPoints(),
3338 setPatch.localFaces(),
3340 );
3341 surfFormatter->write();
3342 surfFormatter->clear();
3343 }
3344 }
3345
3346
3347
3348 // 2. Walk from locationsInMesh. Hard set cellZones.
3349 // Note: walk through faceZones! (these might get overridden later)
3350
3351 if (locationsInMesh.size())
3352 {
3353 Info<< "Setting cellZones according to locationsInMesh:" << endl;
3354
3355 labelList locationsZoneIDs(zonesInMesh.size(), -1);
3356 forAll(locationsInMesh, i)
3357 {
3358 const word& name = zonesInMesh[i];
3359
3360 Info<< "Location : " << locationsInMesh[i] << nl
3361 << " cellZone : " << name << endl;
3362
3363 if (name != "none")
3364 {
3365 label zoneID = mesh_.cellZones().findZoneID(name);
3366 if (zoneID == -1)
3367 {
3368 FatalErrorInFunction << "problem" << abort(FatalError);
3369 }
3370 locationsZoneIDs[i] = zoneID;
3371 }
3372 }
3373 Info<< endl;
3374
3375
3376 // Assign cellZone according to seed points. Walk through named surfaces
3377 findCellZoneInsideWalk
3378 (
3379 locationsInMesh, // locations
3380 locationsZoneIDs, // index of cellZone (or -1)
3381 unnamedRegion1, // per face -1 (unblocked) or >= 0 (blocked)
3382 cellToZone
3383 );
3384 }
3385
3386
3387 // 3. Mark named-surfaces-with-insidePoint. Hard set cellZones.
3388
3389 labelList locationSurfaces
3390 (
3392 );
3393
3394 if (locationSurfaces.size())
3395 {
3396 Info<< "Found " << locationSurfaces.size()
3397 << " named surfaces with a provided inside point."
3398 << " Assigning cells inside these surfaces"
3399 << " to the corresponding cellZone."
3400 << nl << endl;
3401
3402 // Collect per surface the -insidePoint -cellZone
3403 // Usually only a single inside point per surface so no clever
3404 // counting - just use DynamicField
3405 DynamicField<point> insidePoints(locationSurfaces.size());
3406 DynamicList<label> insidePointCellZoneIDs(locationSurfaces.size());
3407 forAll(locationSurfaces, i)
3408 {
3409 const label surfI = locationSurfaces[i];
3410 const auto& surfInsidePoints = surfZones[surfI].zoneInsidePoints();
3411
3412 const word& name = surfZones[surfI].cellZoneName();
3413 label zoneID = -1;
3414 if (name != "none")
3415 {
3416 zoneID = mesh_.cellZones().findZoneID(name);
3417 if (zoneID == -1)
3418 {
3420 << "Specified non-existing cellZone " << name
3421 << " for surface " << surfaces_.names()[surfI]
3422 << abort(FatalError);
3423 }
3424 }
3425
3426 for (const auto& pt : surfInsidePoints)
3427 {
3428 insidePoints.append(pt);
3429 insidePointCellZoneIDs.append(zoneID);
3430 }
3431 }
3432
3433
3434 // Stop at unnamed or named surface
3435 labelList allRegion1(mesh_.nFaces(), -1);
3436 forAll(namedSurfaceRegion, faceI)
3437 {
3438 allRegion1[faceI] = max
3439 (
3440 unnamedRegion1[faceI],
3441 namedSurfaceRegion[faceI]
3442 );
3443 }
3444
3445 findCellZoneInsideWalk
3446 (
3447 insidePoints, // locations
3448 insidePointCellZoneIDs, // index of cellZone
3449 allRegion1, // per face -1 (unblocked) or >= 0 (blocked)
3450 cellToZone
3451 );
3452 }
3453
3454
3455 // 4. Mark named-surfaces-with-geometric faces. Do geometric test. Soft set
3456 // cellZones. Correct through making consistent.
3457
3458 // Closed surfaces with cellZone specified.
3459 labelList closedNamedSurfaces
3460 (
3462 (
3463 surfZones,
3464 surfaces_.geometry(),
3465 surfaces_.surfaces()
3466 )
3467 );
3468
3469 if (closedNamedSurfaces.size())
3470 {
3471 Info<< "Found " << closedNamedSurfaces.size()
3472 << " closed, named surfaces. Assigning cells in/outside"
3473 << " these surfaces to the corresponding cellZone."
3474 << nl << endl;
3475
3476 findCellZoneGeometric
3477 (
3478 neiCc,
3479 closedNamedSurfaces, // indices of closed surfaces
3480 namedSurfaceRegion, // per face index of named surface + region
3481 surfaceToCellZone, // cell zone index per surface
3482
3483 cellToZone
3484 );
3485 }
3486
3487
3488 // 5. Find any unassigned regions (from regionSplit)
3489
3490 if (namedSurfaces.size())
3491 {
3492 if (nErodeCellZones == 0)
3493 {
3494 Info<< "Walking from known cellZones; crossing a faceZone "
3495 << "face changes cellZone" << nl << endl;
3496
3497 // Put unassigned regions into any connected cellZone
3498 findCellZoneTopo
3499 (
3500 backgroundZoneID,
3501 pointField(0),
3502 unnamedRegion1, // Intersections with unnamed surfaces
3503 namedSurfaceRegion, // Intersections with named surfaces
3504 surfaceToCellZone,
3505 cellToZone
3506 );
3507 }
3508 else if (nErodeCellZones < 0)
3509 {
3510 Info<< "Growing cellZones by " << -nErodeCellZones
3511 << " layers" << nl << endl;
3512
3513 growCellZone
3514 (
3515 -nErodeCellZones,
3516 backgroundZoneID,
3517 unnamedRegion1,
3518 unnamedRegion2,
3519 namedSurfaceRegion,
3520 cellToZone
3521 );
3522 }
3523 else
3524 {
3525 Info<< "Eroding cellZone cells to make these consistent with"
3526 << " faceZone faces" << nl << endl;
3527
3528 // Erode cell zone cells (connected to an unassigned cell)
3529 // and put them into backgroundZone
3530 erodeCellZone
3531 (
3532 nErodeCellZones,
3533 backgroundZoneID,
3534 unnamedRegion1,
3535 namedSurfaceRegion,
3536 cellToZone
3537 );
3538 }
3539
3540
3541 // Make sure namedSurfaceRegion is unset inbetween same cell zones.
3542 if (!allowFreeStandingZoneFaces)
3543 {
3544 Info<< "Only keeping zone faces inbetween different cellZones."
3545 << nl << endl;
3546
3547 // Surfaces with faceZone but no cellZone
3548 labelList standaloneNamedSurfaces
3549 (
3551 (
3552 surfZones
3553 )
3554 );
3555
3556 // Construct map from surface index to index in
3557 // standaloneNamedSurfaces (or -1)
3558 labelList surfaceMap(surfZones.size(), -1);
3559 forAll(standaloneNamedSurfaces, i)
3560 {
3561 surfaceMap[standaloneNamedSurfaces[i]] = i;
3562 }
3563
3564 makeConsistentFaceIndex
3565 (
3566 surfaceMap,
3567 cellToZone,
3568 namedSurfaceRegion
3569 );
3570 }
3571 }
3572 else if (nErodeCellZones < 0 && gMax(cellToZone) >= 0)
3573 {
3574 // With multiple locationsInMesh and non-trivial cellZones we allow
3575 // some growing of the cellZones to avoid any background cells
3576
3577 Info<< "Growing cellZones by " << -nErodeCellZones
3578 << " layers" << nl << endl;
3579
3580 growCellZone
3581 (
3582 -nErodeCellZones,
3583 backgroundZoneID,
3584 unnamedRegion1,
3585 unnamedRegion2,
3586 namedSurfaceRegion, // note: potentially zero sized
3587 cellToZone
3588 );
3589
3590 // Make sure namedSurfaceRegion is unset inbetween same cell zones.
3591 if (!allowFreeStandingZoneFaces && namedSurfaceRegion.size())
3592 {
3593 Info<< "Only keeping zone faces inbetween different cellZones."
3594 << nl << endl;
3595
3596 // Surfaces with faceZone but no cellZone
3597 labelList standaloneNamedSurfaces
3598 (
3600 (
3601 surfZones
3602 )
3603 );
3604
3605 // Construct map from surface index to index in
3606 // standaloneNamedSurfaces (or -1)
3607 labelList surfaceMap(surfZones.size(), -1);
3608 forAll(standaloneNamedSurfaces, i)
3609 {
3610 surfaceMap[standaloneNamedSurfaces[i]] = i;
3611 }
3612
3613 makeConsistentFaceIndex
3614 (
3615 surfaceMap,
3616 cellToZone,
3617 namedSurfaceRegion
3618 );
3619 }
3620 }
3621
3622
3623 // Some stats
3624 if (debug)
3625 {
3626 label nZones = gMax(cellToZone)+1;
3627
3628 label nUnvisited = 0;
3629 label nBackgroundCells = 0;
3630 labelList nZoneCells(nZones, Zero);
3631 forAll(cellToZone, cellI)
3632 {
3633 label zoneI = cellToZone[cellI];
3634 if (zoneI >= 0)
3635 {
3636 nZoneCells[zoneI]++;
3637 }
3638 else if (zoneI == -1)
3639 {
3640 nBackgroundCells++;
3641 }
3642 else if (zoneI == -2)
3643 {
3644 nUnvisited++;
3645 }
3646 else
3647 {
3649 << "problem" << exit(FatalError);
3650 }
3651 }
3652 reduce(nUnvisited, sumOp<label>());
3653 reduce(nBackgroundCells, sumOp<label>());
3654 forAll(nZoneCells, zoneI)
3655 {
3656 reduce(nZoneCells[zoneI], sumOp<label>());
3657 }
3658 Info<< "nUnvisited :" << nUnvisited << endl;
3659 Info<< "nBackgroundCells:" << nBackgroundCells << endl;
3660 Info<< "nZoneCells :" << nZoneCells << endl;
3661 }
3662 if (debug&MESH)
3663 {
3664 const_cast<Time&>(mesh_.time())++;
3665 Pout<< "Writing cell zone allocation on mesh to time "
3666 << timeName() << endl;
3667 write
3668 (
3669 debugType(debug),
3670 writeType(writeLevel() | WRITEMESH),
3671 mesh_.time().path()/"cell2Zone"
3672 );
3673 volScalarField volCellToZone
3674 (
3675 IOobject
3676 (
3677 "cellToZone",
3678 timeName(),
3679 mesh_,
3683 ),
3684 mesh_,
3687 );
3688
3689 forAll(cellToZone, cellI)
3690 {
3691 volCellToZone[cellI] = cellToZone[cellI];
3692 }
3693 volCellToZone.write();
3694
3695
3696 //mkDir(mesh_.time().path()/timeName());
3697 //OBJstream str
3698 //(
3699 // mesh_.time().path()/timeName()/"zoneBoundaryFaces.obj"
3700 //);
3701 //Pout<< "Writing zone boundaries to " << str.name() << endl;
3702 //for (label facei = 0; facei < mesh_.nInternalFaces(); facei++)
3703 //{
3704 // const label ownZone = cellToZone[mesh_.faceOwner()[facei]];
3705 // const label neiZone = cellToZone[mesh_.faceNeighbour()[facei]];
3706 // if (ownZone != neiZone)
3707 // {
3708 // str.write(mesh_.faces()[facei], mesh_.points(), false);
3709 // }
3710 //}
3711 //labelList neiCellZone;
3712 //syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone);
3713 //for
3714 //(
3715 // label facei = mesh_.nInternalFaces();
3716 // facei < mesh_.nFaces();
3717 // facei++
3718 //)
3719 //{
3720 // const label ownZone = cellToZone[mesh_.faceOwner()[facei]];
3721 // const label bFacei = facei-mesh_.nInternalFaces();
3722 // const label neiZone = neiCellZone[bFacei];
3723 // if (ownZone != neiZone)
3724 // {
3725 // str.write(mesh_.faces()[facei], mesh_.points(), false);
3726 // }
3727 //}
3728
3729 //mkDir(mesh_.time().path()/timeName());
3730 //OBJstream str1
3731 //(
3732 // mesh_.time().path()/timeName()/"unnamedRegion1.obj"
3733 //);
3734 //OBJstream str2
3735 //(
3736 // mesh_.time().path()/timeName()/"unnamedRegion2.obj"
3737 //);
3738 //Pout<< "Writing unnamed1 to " << str1.name() << endl;
3739 //Pout<< "Writing unnamed2 to " << str2.name() << endl;
3740 //for (label facei = 0; facei < mesh_.nFaces(); facei++)
3741 //{
3742 // if
3743 // (
3744 // unnamedRegion1[facei] < -1
3745 // || unnamedRegion2[facei] < -1
3746 // )
3747 // {
3748 // FatalErrorInFunction << "face:"
3749 // << mesh_.faceCentres()[facei]
3750 // << " unnamed1:" << unnamedRegion1[facei]
3751 // << " unnamed2:" << unnamedRegion2[facei]
3752 // << exit(FatalError);
3753 // }
3754 //
3755 // if (unnamedRegion1[facei] >= 0)
3756 // {
3757 // str1.write(mesh_.faces()[facei], mesh_.points(), false);
3758 // }
3759 //
3760 // if (unnamedRegion2[facei] >= 0)
3761 // {
3762 // str2.write(mesh_.faces()[facei], mesh_.points(), false);
3763 // }
3764 //}
3765
3766 //if (namedSurfaceRegion.size())
3767 //{
3768 // OBJstream strNamed
3769 // (
3770 // mesh_.time().path()/timeName()/"namedSurfaceRegion.obj"
3771 // );
3772 // Pout<< "Writing named to " << strNamed.name() << endl;
3773 // for (label facei = 0; facei < mesh_.nFaces(); facei++)
3774 // {
3775 // const face& f = mesh_.faces()[facei];
3776 // if (namedSurfaceRegion[facei] < -1)
3777 // {
3778 // FatalErrorInFunction << "face:"
3779 // << mesh_.faceCentres()[facei]
3780 // << " unnamed1:" << unnamedRegion1[facei]
3781 // << " unnamed2:" << unnamedRegion2[facei]
3782 // << " named:" << namedSurfaceRegion[facei]
3783 // << exit(FatalError);
3784 // }
3785 // if (namedSurfaceRegion[facei] >= 0)
3786 // {
3787 // strNamed.write(f, mesh_.points(), false);
3788 // }
3789 // }
3790 //}
3791 }
3792}
3793
3794
3795void Foam::meshRefinement::handleSnapProblems
3796(
3797 const snapParameters& snapParams,
3798 const bool useTopologicalSnapDetection,
3799 const bool removeEdgeConnectedCells,
3800 const scalarField& perpendicularAngle,
3801 const dictionary& motionDict,
3802 Time& runTime,
3803 const labelList& globalToMasterPatch,
3804 const labelList& globalToSlavePatch
3805)
3806{
3807 Info<< nl
3808 << "Introducing baffles to block off problem cells" << nl
3809 << "----------------------------------------------" << nl
3810 << endl;
3811
3812 labelList facePatch;
3814 if (useTopologicalSnapDetection)
3815 {
3816 markFacesOnProblemCells
3817 (
3818 motionDict,
3819 removeEdgeConnectedCells,
3820 perpendicularAngle,
3821 globalToMasterPatch,
3822
3823 facePatch,
3824 faceZone
3825 );
3826 }
3827 else
3828 {
3829 markFacesOnProblemCellsGeometric
3830 (
3831 snapParams,
3832 motionDict,
3833 globalToMasterPatch,
3834 globalToSlavePatch,
3835
3836 facePatch,
3837 faceZone
3838 );
3839 }
3840 Info<< "Analyzed problem cells in = "
3841 << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
3842
3843 if (debug&MESH)
3844 {
3845 faceSet problemFaces(mesh_, "problemFaces", mesh_.nFaces()/100);
3846
3847 forAll(facePatch, faceI)
3848 {
3849 if (facePatch[faceI] != -1)
3850 {
3851 problemFaces.insert(faceI);
3852 }
3853 }
3854 problemFaces.instance() = timeName();
3855 Pout<< "Dumping " << problemFaces.size()
3856 << " problem faces to " << problemFaces.objectPath() << endl;
3857 problemFaces.write();
3858 }
3859
3860 Info<< "Introducing baffles to delete problem cells." << nl << endl;
3861
3862 if (debug)
3863 {
3864 ++runTime;
3865 }
3866
3867
3868 // Add faces-to-baffle to faceZone. For now do this outside of topoChanges
3869 {
3870 const faceZoneMesh& fzs = mesh_.faceZones();
3871 List<DynamicList<label>> zoneToFaces(fzs.size());
3872 List<DynamicList<bool>> zoneToFlip(fzs.size());
3873
3874 // Start off with original contents
3875 forAll(fzs, zonei)
3876 {
3877 zoneToFaces[zonei].append(fzs[zonei]);
3878 zoneToFlip[zonei].append(fzs[zonei].flipMap());
3879 }
3880
3881 // Add any to-be-patched face
3882 forAll(facePatch, facei)
3883 {
3884 if (facePatch[facei] != -1)
3885 {
3886 label zonei = faceZone[facei];
3887 if (zonei != -1)
3888 {
3889 zoneToFaces[zonei].append(facei);
3890 zoneToFlip[zonei].append(false);
3891 }
3892 }
3893 }
3894
3895 forAll(zoneToFaces, zonei)
3896 {
3898 (
3899 fzs[zonei].name(),
3900 zoneToFaces[zonei],
3901 zoneToFlip[zonei],
3902 mesh_
3903 );
3904 }
3905 }
3906
3907
3908 // Create baffles with same owner and neighbour for now.
3909 createBaffles(facePatch, facePatch);
3910
3911 if (debug)
3912 {
3913 // Debug:test all is still synced across proc patches
3914 checkData();
3915 }
3916 Info<< "Created baffles in = "
3917 << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
3918
3919 printMeshInfo(debug, "After introducing baffles", true);
3920
3921 if (debug&MESH)
3922 {
3923 const_cast<Time&>(mesh_.time())++;
3924 Pout<< "Writing extra baffled mesh to time "
3925 << timeName() << endl;
3926 write
3927 (
3928 debugType(debug),
3929 writeType(writeLevel() | WRITEMESH),
3930 runTime.path()/"extraBaffles"
3931 );
3932 Pout<< "Dumped debug data in = "
3933 << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
3934 }
3935}
3936
3937
3938Foam::labelList Foam::meshRefinement::freeStandingBaffleFaces
3939(
3940 const labelList& faceToZone,
3941 const labelList& cellToZone,
3942 const labelList& neiCellZone
3943) const
3944{
3945 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
3946 const labelList& faceOwner = mesh_.faceOwner();
3947 const labelList& faceNeighbour = mesh_.faceNeighbour();
3948
3949
3950 // We want to pick up the faces to orient. These faces come in
3951 // two variants:
3952 // - faces originating from stand-alone faceZones
3953 // (these will most likely have no cellZone on either side so
3954 // ownZone and neiZone both -1)
3955 // - sticky-up faces originating from a 'bulge' in a outside of
3956 // a cellZone. These will have the same cellZone on either side.
3957 // How to orient these is not really clearly defined so do them
3958 // same as stand-alone faceZone faces for now. (Normally these will
3959 // already have been removed by the 'allowFreeStandingZoneFaces=false'
3960 // default setting)
3961
3962 // Note that argument neiCellZone will have -1 on uncoupled boundaries.
3963
3964 DynamicList<label> faceLabels(mesh_.nFaces()/100);
3965
3966 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
3967 {
3968 if (faceToZone[faceI] != -1)
3969 {
3970 // Free standing baffle?
3971 label ownZone = cellToZone[faceOwner[faceI]];
3972 label neiZone = cellToZone[faceNeighbour[faceI]];
3973 if (ownZone == neiZone)
3974 {
3975 faceLabels.append(faceI);
3976 }
3977 }
3978 }
3979 forAll(patches, patchI)
3980 {
3981 const polyPatch& pp = patches[patchI];
3982
3983 forAll(pp, i)
3984 {
3985 label faceI = pp.start()+i;
3986 if (faceToZone[faceI] != -1)
3987 {
3988 // Free standing baffle?
3989 label ownZone = cellToZone[faceOwner[faceI]];
3990 label neiZone = neiCellZone[faceI-mesh_.nInternalFaces()];
3991 if (ownZone == neiZone)
3992 {
3993 faceLabels.append(faceI);
3994 }
3995 }
3996 }
3997 }
3998
3999 return labelList(std::move(faceLabels));
4000}
4001
4002
4003void Foam::meshRefinement::calcPatchNumMasterFaces
4004(
4005 const bitSet& isMasterFace,
4006 const indirectPrimitivePatch& patch,
4007 labelList& nMasterFacesPerEdge
4008) const
4009{
4010 // Number of (master)faces per edge
4011 nMasterFacesPerEdge.setSize(patch.nEdges());
4012 nMasterFacesPerEdge = 0;
4013
4014 forAll(patch.addressing(), faceI)
4015 {
4016 const label meshFaceI = patch.addressing()[faceI];
4017
4018 if (isMasterFace.test(meshFaceI))
4019 {
4020 const labelList& fEdges = patch.faceEdges()[faceI];
4021 forAll(fEdges, fEdgeI)
4022 {
4023 nMasterFacesPerEdge[fEdges[fEdgeI]]++;
4024 }
4025 }
4026 }
4027
4029 (
4030 mesh_,
4031 patch.meshEdges(mesh_.edges(), mesh_.pointEdges()),
4032 nMasterFacesPerEdge,
4034 label(0)
4035 );
4036}
4037
4038
4039Foam::label Foam::meshRefinement::markPatchZones
4040(
4041 const indirectPrimitivePatch& patch,
4042 const labelList& nMasterFacesPerEdge,
4043 labelList& faceToZone
4044) const
4045{
4046 List<edgeTopoDistanceData<label>> allEdgeInfo(patch.nEdges());
4048
4049
4050 // Protect all non-manifold edges
4051 {
4052 // label nProtected = 0;
4053
4054 forAll(nMasterFacesPerEdge, edgeI)
4055 {
4056 if (nMasterFacesPerEdge[edgeI] > 2)
4057 {
4058 allEdgeInfo[edgeI] = edgeTopoDistanceData<label>(0, -2);
4059 // ++nProtected;
4060 }
4061 }
4062 //Info<< "Protected from visiting "
4063 // << returnReduce(nProtected, sumOp<label>())
4064 // << " non-manifold edges" << nl << endl;
4065 }
4066
4067
4068 // Hand out zones
4069
4070 DynamicList<label> changedEdges;
4072
4073 const scalar tol = PatchEdgeFaceWave
4074 <
4077 >::propagationTol();
4078
4079 int dummyTrackData;
4080
4081 const globalIndex globalFaces(patch.size());
4082
4083 label faceI = 0;
4084
4085 label currentZoneI = 0;
4086
4087 while (true)
4088 {
4089 // Pick an unset face
4090 label globalSeed = labelMax;
4091 for (; faceI < allFaceInfo.size(); faceI++)
4092 {
4093 if (!allFaceInfo[faceI].valid(dummyTrackData))
4094 {
4095 globalSeed = globalFaces.toGlobal(faceI);
4096 break;
4097 }
4098 }
4099
4100 reduce(globalSeed, minOp<label>());
4101
4102 if (globalSeed == labelMax)
4103 {
4104 break;
4105 }
4106
4107 if (globalFaces.isLocal(globalSeed))
4108 {
4109 const label seedFaceI = globalFaces.toLocal(globalSeed);
4110
4111 edgeTopoDistanceData<label>& faceInfo = allFaceInfo[seedFaceI];
4112
4113 // Set face
4114 faceInfo = edgeTopoDistanceData<label>(0, currentZoneI);
4115
4116 // .. and seed its edges
4117 const labelList& fEdges = patch.faceEdges()[seedFaceI];
4118 forAll(fEdges, fEdgeI)
4119 {
4120 label edgeI = fEdges[fEdgeI];
4121
4122 edgeTopoDistanceData<label>& edgeInfo = allEdgeInfo[edgeI];
4123
4124 if
4125 (
4126 edgeInfo.updateEdge<int>
4127 (
4128 mesh_,
4129 patch,
4130 edgeI,
4131 seedFaceI,
4132 faceInfo,
4133 tol,
4134 dummyTrackData
4135 )
4136 )
4137 {
4138 changedEdges.append(edgeI);
4139 changedInfo.append(edgeInfo);
4140 }
4141 }
4142 }
4143
4144
4145 if (returnReduceAnd(changedEdges.empty()))
4146 {
4147 break;
4148 }
4149
4150
4151 // Walk
4153 <
4156 > calc
4157 (
4158 mesh_,
4159 patch,
4160 changedEdges,
4161 changedInfo,
4162 allEdgeInfo,
4164 returnReduce(patch.nEdges(), sumOp<label>())
4165 );
4166
4167 currentZoneI++;
4168 }
4169
4170
4171 faceToZone.setSize(patch.size());
4172 forAll(allFaceInfo, faceI)
4173 {
4174 if (!allFaceInfo[faceI].valid(dummyTrackData))
4175 {
4177 << "Problem: unvisited face " << faceI
4178 << " at " << patch.faceCentres()[faceI]
4179 << exit(FatalError);
4180 }
4181 faceToZone[faceI] = allFaceInfo[faceI].data();
4182 }
4183
4184 return currentZoneI;
4185}
4186
4187
4188void Foam::meshRefinement::consistentOrientation
4189(
4190 const bitSet& isMasterFace,
4191 const indirectPrimitivePatch& patch,
4192 const labelList& nMasterFacesPerEdge,
4193 const labelList& faceToZone,
4194 const Map<label>& zoneToOrientation,
4195 bitSet& meshFlipMap
4196) const
4197{
4198 const polyBoundaryMesh& bm = mesh_.boundaryMesh();
4199
4200 // Data on all edges and faces
4201 List<patchFaceOrientation> allEdgeInfo(patch.nEdges());
4203
4204 // Make sure we don't walk through
4205 // - slaves of coupled faces
4206 // - non-manifold edges
4207 {
4208 // label nProtected = 0;
4209
4210 forAll(patch.addressing(), faceI)
4211 {
4212 const label meshFaceI = patch.addressing()[faceI];
4213 const label patchI = bm.whichPatch(meshFaceI);
4214
4215 if
4216 (
4217 patchI != -1
4218 && bm[patchI].coupled()
4219 && !isMasterFace.test(meshFaceI)
4220 )
4221 {
4222 // Slave side. Mark so doesn't get visited.
4224 // ++nProtected;
4225 }
4226 }
4227 //Info<< "Protected from visiting "
4228 // << returnReduce(nProtected, sumOp<label>())
4229 // << " slaves of coupled faces" << nl << endl;
4230 }
4231 {
4232 label nProtected = 0;
4233
4234 forAll(nMasterFacesPerEdge, edgeI)
4235 {
4236 if (nMasterFacesPerEdge[edgeI] > 2)
4237 {
4238 allEdgeInfo[edgeI] = orientedSurface::NOFLIP;
4239 ++nProtected;
4240 }
4241 }
4242
4243 Info<< "Protected from visiting "
4244 << returnReduce(nProtected, sumOp<label>())
4245 << " non-manifold edges" << nl << endl;
4246 }
4247
4248
4249
4250 DynamicList<label> changedEdges;
4252
4253 const scalar tol = PatchEdgeFaceWave
4254 <
4257 >::propagationTol();
4258
4259 int dummyTrackData;
4260
4261 globalIndex globalFaces(patch.size());
4262
4263 while (true)
4264 {
4265 // Pick an unset face
4266 label globalSeed = labelMax;
4267 forAll(allFaceInfo, faceI)
4268 {
4270 {
4271 globalSeed = globalFaces.toGlobal(faceI);
4272 break;
4273 }
4274 }
4275
4276 reduce(globalSeed, minOp<label>());
4277
4278 if (globalSeed == labelMax)
4279 {
4280 break;
4281 }
4282
4283 if (globalFaces.isLocal(globalSeed))
4284 {
4285 const label seedFaceI = globalFaces.toLocal(globalSeed);
4286
4287 // Determine orientation of seedFace
4288
4289 patchFaceOrientation& faceInfo = allFaceInfo[seedFaceI];
4290
4291 // Start off with correct orientation
4292 faceInfo = orientedSurface::NOFLIP;
4293
4294 if (zoneToOrientation[faceToZone[seedFaceI]] < 0)
4295 {
4296 faceInfo.flip();
4297 }
4298
4299
4300 const labelList& fEdges = patch.faceEdges()[seedFaceI];
4301 forAll(fEdges, fEdgeI)
4302 {
4303 label edgeI = fEdges[fEdgeI];
4304
4305 patchFaceOrientation& edgeInfo = allEdgeInfo[edgeI];
4306
4307 if
4308 (
4309 edgeInfo.updateEdge<int>
4310 (
4311 mesh_,
4312 patch,
4313 edgeI,
4314 seedFaceI,
4315 faceInfo,
4316 tol,
4317 dummyTrackData
4318 )
4319 )
4320 {
4321 changedEdges.append(edgeI);
4322 changedInfo.append(edgeInfo);
4323 }
4324 }
4325 }
4326
4327
4328 if (returnReduceAnd(changedEdges.empty()))
4329 {
4330 break;
4331 }
4332
4333
4334
4335 // Walk
4337 <
4340 > calc
4341 (
4342 mesh_,
4343 patch,
4344 changedEdges,
4345 changedInfo,
4346 allEdgeInfo,
4348 returnReduce(patch.nEdges(), sumOp<label>())
4349 );
4350 }
4351
4352
4353 // Push master zone info over to slave (since slave faces never visited)
4354 {
4355 labelList neiStatus
4356 (
4357 mesh_.nBoundaryFaces(),
4359 );
4360
4361 forAll(patch.addressing(), i)
4362 {
4363 const label meshFaceI = patch.addressing()[i];
4364 if (!mesh_.isInternalFace(meshFaceI))
4365 {
4366 neiStatus[meshFaceI-mesh_.nInternalFaces()] =
4367 allFaceInfo[i].flipStatus();
4368 }
4369 }
4370 syncTools::swapBoundaryFaceList(mesh_, neiStatus);
4371
4372 forAll(patch.addressing(), i)
4373 {
4374 const label meshFaceI = patch.addressing()[i];
4375 const label patchI = bm.whichPatch(meshFaceI);
4376
4377 if
4378 (
4379 patchI != -1
4380 && bm[patchI].coupled()
4381 && !isMasterFace.test(meshFaceI)
4382 )
4383 {
4384 // Slave side. Take flipped from neighbour
4385 label bFaceI = meshFaceI-mesh_.nInternalFaces();
4386
4387 if (neiStatus[bFaceI] == orientedSurface::NOFLIP)
4388 {
4390 }
4391 else if (neiStatus[bFaceI] == orientedSurface::FLIP)
4392 {
4394 }
4395 else
4396 {
4398 << "Incorrect status for face " << meshFaceI
4399 << abort(FatalError);
4400 }
4401 }
4402 }
4403 }
4404
4405
4406 // Convert to meshFlipMap and adapt faceZones
4407
4408 meshFlipMap.setSize(mesh_.nFaces());
4409 meshFlipMap = false;
4410
4411 forAll(allFaceInfo, faceI)
4412 {
4413 label meshFaceI = patch.addressing()[faceI];
4414
4416 {
4417 meshFlipMap.unset(meshFaceI);
4418 }
4419 else if (allFaceInfo[faceI] == orientedSurface::FLIP)
4420 {
4421 meshFlipMap.set(meshFaceI);
4422 }
4423 else
4424 {
4426 << "Problem : unvisited face " << faceI
4427 << " centre:" << mesh_.faceCentres()[meshFaceI]
4428 << abort(FatalError);
4429 }
4430 }
4431}
4432
4433
4434void Foam::meshRefinement::zonify
4435(
4436 // Get per face whether is it master (of a coupled set of faces)
4437 const bitSet& isMasterFace,
4438 const labelList& cellToZone,
4439 const labelList& neiCellZone,
4440 const labelList& faceToZone,
4441 const bitSet& meshFlipMap,
4442 polyTopoChange& meshMod
4443) const
4444{
4445 const labelList& faceOwner = mesh_.faceOwner();
4446 const labelList& faceNeighbour = mesh_.faceNeighbour();
4447
4448 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
4449 {
4450 label faceZoneI = faceToZone[faceI];
4451
4452 if (faceZoneI != -1)
4453 {
4454 // Orient face zone to have slave cells in min cell zone.
4455 // Note: logic to use flipMap should be consistent with logic
4456 // to pick up the freeStandingBaffleFaces!
4457
4458 label ownZone = cellToZone[faceOwner[faceI]];
4459 label neiZone = cellToZone[faceNeighbour[faceI]];
4460
4461 bool flip;
4462
4463 if (ownZone == neiZone)
4464 {
4465 // free-standing face. Use geometrically derived orientation
4466 flip = meshFlipMap.test(faceI);
4467 }
4468 else
4469 {
4470 flip =
4471 (
4472 ownZone == -1
4473 || (neiZone != -1 && ownZone > neiZone)
4474 );
4475 }
4476
4477 meshMod.setAction
4478 (
4480 (
4481 mesh_.faces()[faceI], // modified face
4482 faceI, // label of face
4483 faceOwner[faceI], // owner
4484 faceNeighbour[faceI], // neighbour
4485 false, // face flip
4486 -1, // patch for face
4487 false, // remove from zone
4488 faceZoneI, // zone for face
4489 flip // face flip in zone
4490 )
4491 );
4492 }
4493 }
4494
4495
4496 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
4497
4498 // Set owner as no-flip
4499 forAll(patches, patchI)
4500 {
4501 const polyPatch& pp = patches[patchI];
4502
4503 label faceI = pp.start();
4504
4505 forAll(pp, i)
4506 {
4507 label faceZoneI = faceToZone[faceI];
4508
4509 if (faceZoneI != -1)
4510 {
4511 label ownZone = cellToZone[faceOwner[faceI]];
4512 label neiZone = neiCellZone[faceI-mesh_.nInternalFaces()];
4513
4514 bool flip;
4515
4516 if (ownZone == neiZone)
4517 {
4518 // free-standing face. Use geometrically derived orientation
4519 flip = meshFlipMap.test(faceI);
4520 }
4521 else
4522 {
4523 flip =
4524 (
4525 ownZone == -1
4526 || (neiZone != -1 && ownZone > neiZone)
4527 );
4528 }
4529
4530 meshMod.setAction
4531 (
4533 (
4534 mesh_.faces()[faceI], // modified face
4535 faceI, // label of face
4536 faceOwner[faceI], // owner
4537 -1, // neighbour
4538 false, // face flip
4539 patchI, // patch for face
4540 false, // remove from zone
4541 faceZoneI, // zone for face
4542 flip // face flip in zone
4543 )
4544 );
4545 }
4546 faceI++;
4547 }
4548 }
4549
4550
4551 // Put the cells into the correct zone
4552 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4553
4554 forAll(cellToZone, cellI)
4555 {
4556 label zoneI = cellToZone[cellI];
4557
4558 if (zoneI >= 0)
4559 {
4560 meshMod.setAction
4561 (
4563 (
4564 cellI,
4565 false, // removeFromZone
4566 zoneI
4567 )
4568 );
4569 }
4570 }
4571}
4572
4573
4574void Foam::meshRefinement::allocateInterRegionFaceZone
4575(
4576 const label ownZone,
4577 const label neiZone,
4578 wordPairHashTable& zonesToFaceZone,
4579 LabelPairMap<word>& zoneIDsToFaceZone
4580) const
4581{
4582 const cellZoneMesh& cellZones = mesh_.cellZones();
4583
4584 if (ownZone != neiZone)
4585 {
4586 // Make sure lowest number cellZone is master. Non-cellZone
4587 // areas are slave
4588 const bool swap =
4589 (
4590 ownZone == -1
4591 || (neiZone != -1 && ownZone > neiZone)
4592 );
4593
4594 // Quick check whether we already have pair of zones
4595 labelPair key(ownZone, neiZone);
4596 if (swap)
4597 {
4598 key.flip();
4599 }
4600
4601 if (!zoneIDsToFaceZone.found(key))
4602 {
4603 // Not found. Allocate.
4604 const word ownZoneName =
4605 (
4606 ownZone != -1
4607 ? cellZones[ownZone].name()
4608 : "none"
4609 );
4610 const word neiZoneName =
4611 (
4612 neiZone != -1
4613 ? cellZones[neiZone].name()
4614 : "none"
4615 );
4616
4617 // Get lowest zone first
4618 Pair<word> wordKey(ownZoneName, neiZoneName);
4619 if (swap)
4620 {
4621 wordKey.flip();
4622 }
4623
4624 word fzName = wordKey.first() + "_to_" + wordKey.second();
4625
4626 zoneIDsToFaceZone.insert(key, fzName);
4627 zonesToFaceZone.insert(wordKey, fzName);
4628 }
4629 }
4630}
4631
4632
4633// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
4634
4636(
4637 const bool doHandleSnapProblems,
4638 const snapParameters& snapParams,
4639 const bool useTopologicalSnapDetection,
4640 const bool removeEdgeConnectedCells,
4641 const scalarField& perpendicularAngle,
4642 const label nErodeCellZones,
4643 const dictionary& motionDict,
4644 Time& runTime,
4645 const labelList& globalToMasterPatch,
4646 const labelList& globalToSlavePatch,
4647
4648 const pointField& locationsInMesh,
4649 const wordList& zonesInMesh,
4650 const pointField& locationsOutsideMesh,
4651 const bool exitIfLeakPath,
4652 const refPtr<coordSetWriter>& leakPathFormatter,
4653 refPtr<surfaceWriter>& surfFormatter
4654)
4655{
4656 // Introduce baffles
4657 // ~~~~~~~~~~~~~~~~~
4658
4659 // Split the mesh along internal faces wherever there is a pierce between
4660 // two cell centres.
4661
4662 Info<< "Introducing baffles for "
4664 << " faces that are intersected by the surface." << nl << endl;
4665
4666 // Swap neighbouring cell centres and cell level
4667 labelList neiLevel(mesh_.nBoundaryFaces());
4668 pointField neiCc(mesh_.nBoundaryFaces());
4669 calcNeighbourData(neiLevel, neiCc);
4670
4671 labelList ownPatch, neiPatch;
4672
4673 refPtr<surfaceWriter> dummyWriter(nullptr);
4674 getBafflePatches
4675 (
4676 nErodeCellZones,
4677 globalToMasterPatch,
4678
4679 locationsInMesh,
4680 zonesInMesh,
4681 locationsOutsideMesh,
4682 exitIfLeakPath,
4683 refPtr<coordSetWriter>(nullptr),
4684 dummyWriter,
4685
4686 neiLevel,
4687 neiCc,
4688
4689 ownPatch,
4690 neiPatch
4691 );
4692
4693 createBaffles(ownPatch, neiPatch);
4694
4695 if (debug)
4696 {
4697 // Debug:test all is still synced across proc patches
4698 checkData();
4699 }
4700
4701 Info<< "Created baffles in = "
4702 << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
4703
4704 printMeshInfo(debug, "After introducing baffles", true);
4705
4706 if (debug&MESH)
4707 {
4708 const_cast<Time&>(mesh_.time())++;
4709 Pout<< "Writing baffled mesh to time " << timeName()
4710 << endl;
4711 write
4712 (
4715 runTime.path()/"baffles"
4716 );
4717 Pout<< "Dumped debug data in = "
4718 << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
4719 }
4720
4721
4722 // Introduce baffles to delete problem cells
4723 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4724
4725 // Create some additional baffles where we want surface cells removed.
4726
4727 if (doHandleSnapProblems)
4728 {
4729 handleSnapProblems
4730 (
4731 snapParams,
4732 useTopologicalSnapDetection,
4733 removeEdgeConnectedCells,
4734 perpendicularAngle,
4735 motionDict,
4736 runTime,
4737 globalToMasterPatch,
4738 globalToSlavePatch
4739 );
4740
4741 // Removing additional cells might have created disconnected bits
4742 // so re-do the surface intersections
4743 {
4744 // Swap neighbouring cell centres and cell level
4745 neiLevel.setSize(mesh_.nBoundaryFaces());
4746 neiCc.setSize(mesh_.nBoundaryFaces());
4747 calcNeighbourData(neiLevel, neiCc);
4748
4749 labelList ownPatch, neiPatch;
4750 refPtr<surfaceWriter> dummyWriter(nullptr);
4751 getBafflePatches
4752 (
4753 nErodeCellZones,
4754 globalToMasterPatch,
4755
4756 locationsInMesh,
4757 zonesInMesh,
4758 locationsOutsideMesh,
4759 exitIfLeakPath,
4760 refPtr<coordSetWriter>(nullptr),
4761 dummyWriter,
4762
4763 neiLevel,
4764 neiCc,
4765
4766 ownPatch,
4767 neiPatch
4768 );
4769
4770 createBaffles(ownPatch, neiPatch);
4771 }
4772
4773 if (debug)
4774 {
4775 // Debug:test all is still synced across proc patches
4776 checkData();
4777 }
4778 }
4779
4780
4781 // Select part of mesh
4782 // ~~~~~~~~~~~~~~~~~~~
4783
4784 Info<< nl
4785 << "Remove unreachable sections of mesh" << nl
4786 << "-----------------------------------" << nl
4787 << endl;
4788
4789 if (debug)
4790 {
4791 ++runTime;
4792 }
4793
4795 (
4796 globalToMasterPatch,
4797 globalToSlavePatch,
4798 locationsInMesh,
4799 locationsOutsideMesh,
4800 true, // Exit if any connection between inside and outside
4801 leakPathFormatter
4802 );
4803
4804 if (debug)
4805 {
4806 // Debug:test all is still synced across proc patches
4807 checkData();
4808 }
4809 Info<< "Split mesh in = "
4810 << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
4811
4812 printMeshInfo(debug, "After subsetting", true);
4813
4814 if (debug&MESH)
4815 {
4816 ++runTime;
4817 Pout<< "Writing subsetted mesh to time " << timeName()
4818 << endl;
4819 write
4820 (
4823 runTime.path()/timeName()
4824 );
4825 Pout<< "Dumped debug data in = "
4826 << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
4827 }
4828}
4829
4830
4832(
4833 const bool samePatch,
4834 const snapParameters& snapParams,
4835 const bool useTopologicalSnapDetection,
4836 const bool removeEdgeConnectedCells,
4837 const scalarField& perpendicularAngle,
4838 const scalar planarAngle,
4839 const dictionary& motionDict,
4840 Time& runTime,
4841 const labelList& globalToMasterPatch,
4842 const labelList& globalToSlavePatch,
4843 const pointField& locationsInMesh,
4844 const pointField& locationsOutsideMesh
4845)
4846{
4847 // Merge baffles
4848 // ~~~~~~~~~~~~~
4849
4850 Info<< nl
4851 << "Merge free-standing baffles" << nl
4852 << "---------------------------" << nl
4853 << endl;
4854
4855
4856 // List of pairs of freestanding baffle faces.
4857 List<labelPair> couples
4858 (
4859 freeStandingBaffles // filter out freestanding baffles
4860 (
4862 planarAngle,
4863 samePatch
4864 )
4865 );
4866
4867 const label nCouples = returnReduce(couples.size(), sumOp<label>());
4868
4869 Info<< "Detected free-standing baffles : " << nCouples << endl;
4870
4871 if (nCouples > 0)
4872 {
4873 // Actually merge baffles. Note: not exactly parallellized. Should
4874 // convert baffle faces into processor faces if they resulted
4875 // from them.
4876 mergeBaffles(couples, Map<label>(0));
4877
4878 // Detect any problem cells resulting from merging of baffles
4879 // and delete them
4880 handleSnapProblems
4881 (
4882 snapParams,
4883 useTopologicalSnapDetection,
4884 removeEdgeConnectedCells,
4885 perpendicularAngle,
4886 motionDict,
4887 runTime,
4888 globalToMasterPatch,
4889 globalToSlavePatch
4890 );
4891
4892 // Very occasionally removing a problem cell might create a disconnected
4893 // region so re-check
4894
4895 Info<< nl
4896 << "Remove unreachable sections of mesh" << nl
4897 << "-----------------------------------" << nl
4898 << endl;
4899
4900 if (debug)
4901 {
4902 ++runTime;
4903 }
4904
4906 (
4907 globalToMasterPatch,
4908 globalToSlavePatch,
4909 locationsInMesh,
4910 locationsOutsideMesh,
4911 true, // Exit if any connection between inside and outside
4912 refPtr<coordSetWriter>(nullptr) // leakPathFormatter
4913 );
4914
4915
4916 if (debug)
4917 {
4918 // Debug:test all is still synced across proc patches
4919 checkData();
4920 }
4921 }
4922 Info<< "Merged free-standing baffles in = "
4923 << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
4924}
4925
4926
4927Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh
4928(
4929 const label nBufferLayers,
4930 const label nErodeCellZones,
4931 const labelList& globalToMasterPatch,
4932 const labelList& globalToSlavePatch,
4933
4934 const pointField& locationsInMesh,
4935 const wordList& zonesInMesh,
4936 const pointField& locationsOutsideMesh,
4937 const bool exitIfLeakPath,
4938 const refPtr<coordSetWriter>& leakPathFormatter,
4939 refPtr<surfaceWriter>& surfFormatter
4940)
4941{
4942 // Determine patches to put intersections into
4943 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4944
4945
4946 // Swap neighbouring cell centres and cell level
4947 labelList neiLevel(mesh_.nBoundaryFaces());
4948 pointField neiCc(mesh_.nBoundaryFaces());
4949 calcNeighbourData(neiLevel, neiCc);
4950
4951 // Find intersections with all unnamed(!) surfaces
4952 labelList ownPatch, neiPatch;
4953 getBafflePatches
4954 (
4955 nErodeCellZones,
4956 globalToMasterPatch,
4957
4958 locationsInMesh,
4959 zonesInMesh,
4960 locationsOutsideMesh,
4961 exitIfLeakPath,
4962 leakPathFormatter,
4963 surfFormatter,
4964
4965 neiLevel,
4966 neiCc,
4967
4968 ownPatch,
4969 neiPatch
4970 );
4971
4972 // Analyse regions. Reuse regionsplit
4973 boolList blockedFace(mesh_.nFaces(), false);
4974
4975 forAll(ownPatch, faceI)
4976 {
4977 if (ownPatch[faceI] != -1 || neiPatch[faceI] != -1)
4978 {
4979 blockedFace[faceI] = true;
4980 }
4981 }
4982 syncTools::syncFaceList(mesh_, blockedFace, orEqOp<bool>());
4983
4984
4985 regionSplit cellRegion(mesh_, blockedFace);
4986
4987 // Set unreachable cells to -1
4989 (
4990 mesh_,
4991 vector::uniform(mergeDistance_), // perturbVec
4992 locationsInMesh,
4993 locationsOutsideMesh,
4994 cellRegion.nRegions(),
4995 cellRegion,
4996 blockedFace,
4997 // Leak-path
4998 false, // do not exit if outside location found
4999 leakPathFormatter
5000 );
5001
5002 return splitMesh
5003 (
5004 nBufferLayers,
5005 globalToMasterPatch,
5006 globalToSlavePatch,
5007 cellRegion,
5008 ownPatch,
5009 neiPatch
5010 );
5011}
5012
5013
5014Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh
5015(
5016 const label nBufferLayers,
5017 const labelList& globalToMasterPatch,
5018 const labelList& globalToSlavePatch,
5019
5020 labelList& cellRegion,
5021 labelList& ownPatch,
5022 labelList& neiPatch
5023)
5024{
5025 // Walk out nBufferlayers from region boundary
5026 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5027 // (modifies cellRegion, ownPatch)
5028 // Takes over face patch onto points and then back to faces and cells
5029 // (so cell-face-point walk)
5030
5031 const labelList& faceOwner = mesh_.faceOwner();
5032 const labelList& faceNeighbour = mesh_.faceNeighbour();
5033
5034
5035 // Checks
5036 for (label facei = 0; facei < mesh_.nInternalFaces(); facei++)
5037 {
5038 if (ownPatch[facei] == -1 && neiPatch[facei] != -1)
5039 {
5040 FatalErrorInFunction << "Problem in face:" << facei
5041 << " at:" << mesh_.faceCentres()[facei]
5042 << " ownPatch:" << ownPatch[facei]
5043 << " neiPatch:" << neiPatch[facei]
5044 << exit(FatalError);
5045 }
5046 else
5047 {
5048 // Check if cellRegion indeed limited by patch
5049 const label ownRegion = cellRegion[faceOwner[facei]];
5050 const label neiRegion = cellRegion[faceNeighbour[facei]];
5051 if (ownRegion != neiRegion)
5052 {
5053 if (ownPatch[facei] == -1)
5054 {
5055 FatalErrorInFunction << "Problem in face:" << facei
5056 << " at:" << mesh_.faceCentres()[facei]
5057 << " ownPatch:" << ownPatch[facei]
5058 << " ownRegion:" << ownRegion
5059 << " neiPatch:" << neiPatch[facei]
5060 << " neiRegion:" << neiRegion
5061 << exit(FatalError);
5062 }
5063 }
5064 }
5065 }
5066
5067 // Determine on original data the nearest face. This is used as a fall-back
5068 // to set the patch if the nBufferLayers walking didn't work.
5069 labelList nearestOwnPatch;
5070 if (nBufferLayers)
5071 {
5072 DynamicList<label> startFaces;
5073 forAll(ownPatch, facei)
5074 {
5075 if (ownPatch[facei] != -1)
5076 {
5077 startFaces.append(facei);
5078 }
5079 }
5080
5081 // Per face the index to the start face.
5082 labelList faceToStart;
5084 nearestFace
5085 (
5086 startFaces,
5087 bitSet(mesh_.nFaces()), // no blocked faces
5088 mapPtr,
5089 faceToStart,
5090 nBufferLayers+4 // bit more than nBufferLayers since
5091 // walking face-cell-face
5092 );
5093
5094 // Use map to push ownPatch to all reached faces
5095 labelList startOwnPatch(ownPatch, startFaces);
5096 mapPtr().distribute(startOwnPatch);
5097
5098 nearestOwnPatch.setSize(mesh_.nFaces());
5099 nearestOwnPatch = -1;
5100 forAll(faceToStart, facei)
5101 {
5102 const label sloti = faceToStart[facei];
5103 if (sloti != -1)
5104 {
5105 nearestOwnPatch[facei] = startOwnPatch[sloti];
5106 }
5107 }
5108 }
5109
5110 // Leak closure:
5111 // ~~~~~~~~~~~~~
5112 // We do not want to add buffer layers on the frozen points/faces
5113 // since these are the exact faces needed to close a hole (to an
5114 // locationOutsideMesh). Adding even
5115 // a single layer of cells would mean that in further manipulation there
5116 // is now no path to the locationOutsideMesh so the layer closure does
5117 // not get triggered and we keep the added 1 layer of cells on the
5118 // closure faces.
5119 bitSet isFrozenPoint(mesh_.nPoints());
5120 bitSet isFrozenFace(mesh_.nFaces());
5121
5122 if (nBufferLayers)
5123 {
5124 const labelListList& pointFaces = mesh_.pointFaces();
5125 const pointZoneMesh& pzs = mesh_.pointZones();
5126 const label pointZonei = pzs.findZoneID("frozenPoints");
5127 if (pointZonei != -1)
5128 {
5129 const pointZone& pz = pzs[pointZonei];
5130 isFrozenPoint.set(pz);
5131 for (const label pointi : pz)
5132 {
5133 isFrozenFace.set(pointFaces[pointi]);
5134 }
5135 }
5136
5137 const faceZoneMesh& fzs = mesh_.faceZones();
5138 const label faceZonei = fzs.findZoneID("frozenFaces");
5139 if (faceZonei != -1)
5140 {
5141 const faceZone& fz = fzs[faceZonei];
5142 isFrozenFace.set(fz);
5143 for (const label facei : fz)
5144 {
5145 isFrozenPoint.set(mesh_.faces()[facei]);
5146 }
5147 }
5148 }
5149
5150 // Patch for exposed faces for lack of anything sensible.
5151 label defaultPatch = 0;
5152 if (globalToMasterPatch.size())
5153 {
5154 defaultPatch = globalToMasterPatch[0];
5155 }
5156
5157 for (label i = 0; i < nBufferLayers; i++)
5158 {
5159 // 1. From cells (via faces) to points
5160
5161 labelList pointBaffle(mesh_.nPoints(), -1);
5162
5163 forAll(faceNeighbour, facei)
5164 {
5165 if (!isFrozenFace[facei])
5166 {
5167 const face& f = mesh_.faces()[facei];
5168
5169 const label ownRegion = cellRegion[faceOwner[facei]];
5170 const label neiRegion = cellRegion[faceNeighbour[facei]];
5171
5172 if (ownRegion == -1 && neiRegion != -1)
5173 {
5174 // Note max(..) since possibly regionSplit might have split
5175 // off extra unreachable parts of mesh. Note: or can this
5176 // only happen for boundary faces?
5177 forAll(f, fp)
5178 {
5179 if (!isFrozenPoint[f[fp]])
5180 {
5181 pointBaffle[f[fp]] =
5182 max(defaultPatch, ownPatch[facei]);
5183 }
5184 }
5185 }
5186 else if (ownRegion != -1 && neiRegion == -1)
5187 {
5188 label newPatchi = neiPatch[facei];
5189 if (newPatchi == -1)
5190 {
5191 newPatchi = max(defaultPatch, ownPatch[facei]);
5192 }
5193 forAll(f, fp)
5194 {
5195 if (!isFrozenPoint[f[fp]])
5196 {
5197 pointBaffle[f[fp]] = newPatchi;
5198 }
5199 }
5200 }
5201 }
5202 }
5203
5204 labelList neiCellRegion;
5205 syncTools::swapBoundaryCellList(mesh_, cellRegion, neiCellRegion);
5206
5207 //for
5208 //(
5209 // label facei = mesh_.nInternalFaces();
5210 // facei < mesh_.nFaces();
5211 // facei++
5212 //)
5213 //{
5214 // if (!isFrozenFace[facei])
5215 // {
5216 // const face& f = mesh_.faces()[facei];
5217 //
5218 // const label ownRegion = cellRegion[faceOwner[facei]];
5219 // const label neiRegion =
5220 // neiCellRegion[facei-mesh_.nInternalFaces()];
5221 //
5222 // if (ownRegion == -1 && neiRegion != -1)
5223 // {
5224 // forAll(f, fp)
5225 // {
5226 // if (!isFrozenPoint[f[fp]])
5227 // {
5228 // pointBaffle[f[fp]] =
5229 // max(defaultPatch, ownPatch[facei]);
5230 // }
5231 // }
5232 // }
5233 // }
5234 //}
5235
5236 const auto& patches = mesh_.boundaryMesh();
5237 for (const auto& pp : patches)
5238 {
5239 if (pp.coupled())
5240 {
5241 // Note: swapBoundaryCellList only works on cyclic&processor.
5242 // Does not handle e.g. cyclicAMI. TBD?
5243 // Note: we could check check our side being in the set
5244 // since syncPointList below will push over any decision
5245 // made by the other side.
5246 forAll(pp, i)
5247 {
5248 const label facei = pp.start()+i;
5249 if (!isFrozenFace[facei])
5250 {
5251 const face& f = mesh_.faces()[facei];
5252 const label ownRegion = cellRegion[faceOwner[facei]];
5253 const label neiRegion =
5254 neiCellRegion[facei-mesh_.nInternalFaces()];
5255
5256 // Same logic as for internal faces
5257
5258 if (ownRegion == -1 && neiRegion != -1)
5259 {
5260 forAll(f, fp)
5261 {
5262 if (!isFrozenPoint[f[fp]])
5263 {
5264 pointBaffle[f[fp]] =
5265 max(defaultPatch, ownPatch[facei]);
5266 }
5267 }
5268 }
5269 else if (ownRegion != -1 && neiRegion == -1)
5270 {
5271 label newPatchI = neiPatch[facei];
5272 if (newPatchI == -1)
5273 {
5274 newPatchI = max(defaultPatch, ownPatch[facei]);
5275 }
5276 forAll(f, fp)
5277 {
5278 if (!isFrozenPoint[f[fp]])
5279 {
5280 pointBaffle[f[fp]] = newPatchI;
5281 }
5282 }
5283 }
5284 }
5285 }
5286 }
5287 else
5288 {
5289 forAll(pp, i)
5290 {
5291 const label facei = pp.start()+i;
5292 if (!isFrozenFace[facei])
5293 {
5294 const face& f = mesh_.faces()[facei];
5295 const label ownRegion = cellRegion[faceOwner[facei]];
5296
5297 if (ownRegion != -1)
5298 {
5299 forAll(f, fp)
5300 {
5301 if (!isFrozenPoint[f[fp]])
5302 {
5303 pointBaffle[f[fp]] =
5304 max(defaultPatch, ownPatch[facei]);
5305 }
5306 }
5307 }
5308 }
5309 }
5310 }
5311 }
5312
5313
5314 // Sync
5316 (
5317 mesh_,
5318 pointBaffle,
5320 label(-1) // null value
5321 );
5322
5323
5324 // 2. From points back to faces
5325
5326 const labelListList& pointFaces = mesh_.pointFaces();
5327
5328 forAll(pointFaces, pointi)
5329 {
5330 if (pointBaffle[pointi] != -1)
5331 {
5332 const labelList& pFaces = pointFaces[pointi];
5333
5334 forAll(pFaces, pFacei)
5335 {
5336 const label facei = pFaces[pFacei];
5337
5338 if (!isFrozenFace[facei] && ownPatch[facei] == -1)
5339 {
5340 ownPatch[facei] = pointBaffle[pointi];
5341 }
5342 }
5343 }
5344 }
5345 syncTools::syncFaceList(mesh_, ownPatch, maxEqOp<label>());
5346
5347
5348 // 3. From faces to cells (cellRegion) and back to faces (ownPatch)
5349
5350 labelList newOwnPatch(ownPatch);
5351
5352 forAll(ownPatch, facei)
5353 {
5354 if (!isFrozenFace[facei] && ownPatch[facei] != -1)
5355 {
5356 const label own = faceOwner[facei];
5357
5358 if (cellRegion[own] == -1)
5359 {
5360 cellRegion[own] = labelMax;
5361
5362 const cell& ownFaces = mesh_.cells()[own];
5363 forAll(ownFaces, j)
5364 {
5365 const label ownFacei = ownFaces[j];
5366 if (!isFrozenFace[ownFacei] && ownPatch[ownFacei] == -1)
5367 {
5368 newOwnPatch[ownFacei] = ownPatch[facei];
5369 }
5370 }
5371 }
5372 if (mesh_.isInternalFace(facei))
5373 {
5374 const label nei = faceNeighbour[facei];
5375
5376 if (cellRegion[nei] == -1)
5377 {
5378 cellRegion[nei] = labelMax;
5379
5380 const cell& neiFaces = mesh_.cells()[nei];
5381 forAll(neiFaces, j)
5382 {
5383 const label neiFacei = neiFaces[j];
5384 const bool isFrozen = isFrozenFace[neiFacei];
5385 if (!isFrozen && ownPatch[neiFacei] == -1)
5386 {
5387 newOwnPatch[neiFacei] = ownPatch[facei];
5388 }
5389 }
5390 }
5391 }
5392 }
5393 }
5394
5395 ownPatch.transfer(newOwnPatch);
5396
5397 syncTools::syncFaceList(mesh_, ownPatch, maxEqOp<label>());
5398 }
5399
5400
5401 // Subset
5402 // ~~~~~~
5403
5404 // Get cells to remove
5405 DynamicList<label> cellsToRemove(mesh_.nCells());
5406 forAll(cellRegion, celli)
5407 {
5408 if (cellRegion[celli] == -1)
5409 {
5410 cellsToRemove.append(celli);
5411 }
5412 }
5413 cellsToRemove.shrink();
5414
5415 const label nCellsToKeep = returnReduce
5416 (
5417 mesh_.nCells() - cellsToRemove.size(),
5418 sumOp<label>()
5419 );
5420
5421 Info<< "Keeping all cells containing inside points" << endl
5422 << "Selected for keeping : " << nCellsToKeep << " cells." << endl;
5423
5424
5425 // Remove cells
5426 removeCells cellRemover(mesh_);
5427
5428 // Pick up patches for exposed faces
5429 const labelList exposedFaces(cellRemover.getExposedFaces(cellsToRemove));
5430 labelList exposedPatches(exposedFaces.size());
5431
5432 label nUnpatched = 0;
5433
5434 forAll(exposedFaces, i)
5435 {
5436 label facei = exposedFaces[i];
5437
5438 if (ownPatch[facei] != -1)
5439 {
5440 exposedPatches[i] = ownPatch[facei];
5441 }
5442 else
5443 {
5444 const label fallbackPatch =
5445 (
5446 nearestOwnPatch.size()
5447 ? nearestOwnPatch[facei]
5448 : defaultPatch
5449 );
5450 if (nUnpatched == 0)
5451 {
5453 << "For exposed face " << facei
5454 << " fc:" << mesh_.faceCentres()[facei]
5455 << " found no patch." << endl
5456 << " Taking patch " << fallbackPatch
5457 << " instead. Suppressing future warnings" << endl;
5458 }
5459 nUnpatched++;
5460
5461 exposedPatches[i] = fallbackPatch;
5462 }
5463 }
5464
5465 reduce(nUnpatched, sumOp<label>());
5466 if (nUnpatched > 0)
5467 {
5468 Info<< "Detected " << nUnpatched << " faces out of "
5469 << returnReduce(exposedFaces.size(), sumOp<label>())
5470 << " for which the default patch " << defaultPatch
5471 << " will be used" << endl;
5472 }
5473
5474 return doRemoveCells
5475 (
5476 cellsToRemove,
5477 exposedFaces,
5478 exposedPatches,
5479 cellRemover
5480 );
5481}
5482
5483
5485(
5486 const label nBufferLayers,
5487 const label nErodeCellZones,
5488 const labelList& globalToMasterPatch,
5489 const labelList& globalToSlavePatch,
5490 const pointField& locationsInMesh,
5491 const wordList& zonesInMesh,
5492 const pointField& locationsOutsideMesh
5493)
5494{
5495 // Determine patches to put intersections into
5496 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5497
5498 // Swap neighbouring cell centres and cell level
5499 labelList neiLevel(mesh_.nBoundaryFaces());
5500 pointField neiCc(mesh_.nBoundaryFaces());
5501 calcNeighbourData(neiLevel, neiCc);
5502
5503 // Find intersections with all unnamed(!) surfaces
5504 labelList ownPatch, neiPatch;
5505 refPtr<surfaceWriter> dummyWriter(nullptr);
5506 getBafflePatches
5507 (
5508 nErodeCellZones,
5509 globalToMasterPatch,
5510
5511 locationsInMesh,
5512 zonesInMesh,
5513 locationsOutsideMesh,
5514 false, // do not exit. Use leak-closure instead.
5515 refPtr<coordSetWriter>(nullptr),
5516 dummyWriter,
5517
5518 neiLevel,
5519 neiCc,
5520
5521 ownPatch,
5522 neiPatch
5523 );
5524
5525
5526 labelList cellRegion(mesh_.nCells(), Zero);
5527 // Find any cells inside a limit shell with minLevel -1
5528 labelList levelShell;
5529 limitShells_.findLevel
5530 (
5531 mesh_.cellCentres(),
5532 labelList(mesh_.nCells(), -1), // pick up only shells with -1
5533 levelShell
5534 );
5535 forAll(levelShell, celli)
5536 {
5537 if (levelShell[celli] != -1)
5538 {
5539 // Mark cell region so it gets deleted
5540 cellRegion[celli] = -1;
5541 }
5542 }
5543
5544 autoPtr<mapPolyMesh> mapPtr = splitMesh
5545 (
5546 nBufferLayers,
5547 globalToMasterPatch,
5548 globalToSlavePatch,
5549 cellRegion,
5550 ownPatch,
5551 neiPatch
5552 );
5553
5555 {
5556 const_cast<Time&>(mesh_.time())++;
5557 Pout<< "Writing mesh after removing limitShells"
5558 << " to time " << timeName() << endl;
5559 write
5560 (
5562 writeType
5563 (
5564 writeLevel()
5565 | WRITEMESH
5566 ),
5567 mesh_.time().path()/timeName()
5568 );
5569 }
5570 return mapPtr;
5571}
5572
5573
5575(
5577)
5578{
5579 // Topochange container
5580 polyTopoChange meshMod(mesh_);
5581
5582 label nNonManifPoints = returnReduce
5583 (
5584 regionSide.meshPointMap().size(),
5585 sumOp<label>()
5586 );
5587
5588 Info<< "dupNonManifoldPoints : Found : " << nNonManifPoints
5589 << " non-manifold points (out of "
5590 << mesh_.globalData().nTotalPoints()
5591 << ')' << endl;
5592
5593
5594 autoPtr<mapPolyMesh> mapPtr;
5595
5596 if (nNonManifPoints)
5597 {
5598 // Topo change engine
5599 duplicatePoints pointDuplicator(mesh_);
5600
5601 // Insert changes into meshMod
5602 pointDuplicator.setRefinement(regionSide, meshMod);
5603
5604 // Remove any unnecessary fields
5605 mesh_.clearOut();
5606 mesh_.moving(false);
5607
5608 // Change the mesh (no inflation, parallel sync)
5609 mapPtr = meshMod.changeMesh(mesh_, false, true);
5610 mapPolyMesh& map = *mapPtr;
5611
5612 // Update fields
5613 mesh_.updateMesh(map);
5614
5615 // Move mesh if in inflation mode
5616 if (map.hasMotionPoints())
5617 {
5618 mesh_.movePoints(map.preMotionPoints());
5619 }
5620 else
5621 {
5622 // Delete mesh volumes.
5623 mesh_.clearOut();
5624 }
5625
5626 // Reset the instance for if in overwrite mode
5627 mesh_.setInstance(timeName());
5628
5629 // Update intersections. Is mapping only (no faces created, positions
5630 // stay same) so no need to recalculate intersections.
5631 updateMesh(map, labelList());
5632 }
5633
5634 return mapPtr;
5635}
5636
5637
5639{
5640 // Analyse which points need to be duplicated
5642
5644}
5645
5646
5648(
5649 const labelList& pointToDuplicate
5650)
5651{
5652 label nPointPairs = 0;
5653 forAll(pointToDuplicate, pointI)
5654 {
5655 label otherPointI = pointToDuplicate[pointI];
5656 if (otherPointI != -1)
5657 {
5658 nPointPairs++;
5659 }
5660 }
5661
5662 autoPtr<mapPolyMesh> mapPtr;
5663
5664 if (returnReduceOr(nPointPairs))
5665 {
5666 Map<label> pointToMaster(2*nPointPairs);
5667 forAll(pointToDuplicate, pointI)
5668 {
5669 label otherPointI = pointToDuplicate[pointI];
5670 if (otherPointI != -1)
5671 {
5672 // Slave point
5673 pointToMaster.insert(pointI, otherPointI);
5674 }
5675 }
5676
5677 // Topochange container
5678 polyTopoChange meshMod(mesh_);
5679
5680 // Insert changes
5681 polyMeshAdder::mergePoints(mesh_, pointToMaster, meshMod);
5682
5683 // Remove any unnecessary fields
5684 mesh_.clearOut();
5685 mesh_.moving(false);
5686
5687 // Change the mesh (no inflation, parallel sync)
5688 mapPtr = meshMod.changeMesh(mesh_, false, true);
5689 mapPolyMesh& map = *mapPtr;
5690
5691 // Update fields
5692 mesh_.updateMesh(map);
5693
5694 // Move mesh if in inflation mode
5695 if (map.hasMotionPoints())
5696 {
5697 mesh_.movePoints(map.preMotionPoints());
5698 }
5699 else
5700 {
5701 // Delete mesh volumes.
5702 mesh_.clearOut();
5703 }
5704
5705 // Reset the instance for if in overwrite mode
5706 mesh_.setInstance(timeName());
5707
5708 // Update intersections. Is mapping only (no faces created, positions
5709 // stay same) so no need to recalculate intersections.
5710 updateMesh(map, labelList());
5711 }
5712
5713 return mapPtr;
5714}
5715
5716
5717// Duplicate points on 'boundary' zones. Do not duplicate points on
5718// 'internal' or 'baffle' zone. Whether points are on normal patches does
5719// not matter
5722{
5723 const labelList boundaryFaceZones
5724 (
5725 getZones
5726 (
5728 (
5729 1,
5731 )
5732 )
5733 );
5734 labelList internalOrBaffleFaceZones;
5735 {
5737 fzTypes[0] = surfaceZonesInfo::INTERNAL;
5738 fzTypes[1] = surfaceZonesInfo::BAFFLE;
5739 internalOrBaffleFaceZones = getZones(fzTypes);
5740 }
5741
5742
5743
5744 // 0 : point used by normal, unzoned boundary faces
5745 // 1 : point used by 'boundary' zone
5746 // 2 : point used by internal/baffle zone
5747 PackedList<2> pointStatus(mesh_.nPoints(), 0u);
5748
5749 forAll(boundaryFaceZones, j)
5750 {
5751 const faceZone& fZone = mesh_.faceZones()[boundaryFaceZones[j]];
5752 forAll(fZone, i)
5753 {
5754 const face& f = mesh_.faces()[fZone[i]];
5755 forAll(f, fp)
5756 {
5757 pointStatus[f[fp]] = max(pointStatus[f[fp]], 1u);
5758 }
5759 }
5760 }
5761 forAll(internalOrBaffleFaceZones, j)
5762 {
5763 const faceZone& fZone = mesh_.faceZones()[internalOrBaffleFaceZones[j]];
5764 forAll(fZone, i)
5765 {
5766 const face& f = mesh_.faces()[fZone[i]];
5767 forAll(f, fp)
5768 {
5769 pointStatus[f[fp]] = max(pointStatus[f[fp]], 2u);
5770 }
5771 }
5772 }
5773
5775 (
5776 mesh_,
5777 pointStatus,
5778 maxEqOp<unsigned int>(), // combine op
5779 0u // null value
5780 );
5781
5782 // Pick up points on boundary zones that are not on internal/baffle zones
5783 label n = 0;
5784 forAll(pointStatus, pointI)
5785 {
5786 if (pointStatus[pointI] == 1u)
5787 {
5788 n++;
5789 }
5790 }
5791
5792 label globalNPoints = returnReduce(n, sumOp<label>());
5793 Info<< "Duplicating " << globalNPoints << " points on"
5794 << " faceZones of type "
5796 << endl;
5797
5799
5800 if (globalNPoints)
5801 {
5802 labelList candidatePoints(n);
5803 n = 0;
5804 forAll(pointStatus, pointI)
5805 {
5806 if (pointStatus[pointI] == 1u)
5807 {
5808 candidatePoints[n++] = pointI;
5809 }
5810 }
5811 localPointRegion regionSide(mesh_, candidatePoints);
5813 }
5814 return map;
5815}
5816
5817
5818// Zoning
5819Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
5820(
5821 const bool allowFreeStandingZoneFaces,
5822 const label nErodeCellZones,
5823 const pointField& locationsInMesh,
5824 const wordList& zonesInMesh,
5825 const pointField& locationsOutsideMesh,
5826 const bool exitIfLeakPath,
5827 const refPtr<coordSetWriter>& leakPathFormatter,
5828 refPtr<surfaceWriter>& surfFormatter,
5829 wordPairHashTable& zonesToFaceZone
5830)
5831{
5832 if (locationsInMesh.size() != zonesInMesh.size())
5833 {
5834 FatalErrorInFunction << "problem" << abort(FatalError);
5835 }
5836
5837 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
5838 const PtrList<surfaceZonesInfo>& surfZones = surfaces_.surfZones();
5839
5840
5841 // Swap neighbouring cell centres and cell level
5842 labelList neiLevel(mesh_.nBoundaryFaces());
5843 pointField neiCc(mesh_.nBoundaryFaces());
5844 calcNeighbourData(neiLevel, neiCc);
5845
5846
5847 // Add any faceZones, cellZones originating from surface to the mesh
5848 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5849 labelList surfaceToCellZone;
5850 labelListList surfaceToFaceZones;
5851
5852 labelList namedSurfaces(surfaceZonesInfo::getNamedSurfaces(surfZones));
5853 if (namedSurfaces.size())
5854 {
5855 Info<< "Setting cellZones according to named surfaces:" << endl;
5856 forAll(namedSurfaces, i)
5857 {
5858 label surfI = namedSurfaces[i];
5859
5860 Info<< "Surface : " << surfaces_.names()[surfI] << nl
5861 << " faceZones : " << surfZones[surfI].faceZoneNames() << nl
5862 << " cellZone : " << surfZones[surfI].cellZoneName()
5863 << endl;
5864 }
5865 Info<< endl;
5866
5867 // Add zones to mesh
5868 surfaceToCellZone = surfaceZonesInfo::addCellZonesToMesh
5869 (
5870 surfZones,
5871 namedSurfaces,
5872 mesh_
5873 );
5874 surfaceToFaceZones = surfaceZonesInfo::addFaceZonesToMesh
5875 (
5876 surfZones,
5877 namedSurfaces,
5878 mesh_
5879 );
5880 }
5881
5882
5883
5884 // Put the cells into the correct zone
5885 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5886
5887 // Zone per cell:
5888 // -2 : unset : not allowed!
5889 // -1 : not in any zone (zone 'none')
5890 // >=0: zoneID
5891 // namedSurfaceRegion: zero sized or:
5892 // -1 : face not intersecting a named surface
5893 // >=0 : index of named surface
5894 labelList cellToZone;
5895 labelList namedSurfaceRegion;
5896 bitSet posOrientation;
5897 {
5898 labelList unnamedRegion1;
5899 labelList unnamedRegion2;
5900
5901 zonify
5902 (
5903 allowFreeStandingZoneFaces,
5904 nErodeCellZones,// Use erosion (>0) or regionSplit to clean up
5905 -1, // Set all cells with cellToZone -2 to -1
5906 locationsInMesh,
5907 zonesInMesh,
5908 locationsOutsideMesh,
5909 exitIfLeakPath,
5910 leakPathFormatter,
5911 surfFormatter,
5912
5913 cellToZone,
5914 unnamedRegion1,
5915 unnamedRegion2,
5916 namedSurfaceRegion,
5917 posOrientation
5918 );
5919 }
5920
5921
5922 // Convert namedSurfaceRegion (index of named surfaces) to
5923 // actual faceZone index
5924
5925 //- Per face index of faceZone or -1
5926 labelList faceToZone(mesh_.nFaces(), -1);
5927
5928 forAll(namedSurfaceRegion, faceI)
5929 {
5930 //label surfI = namedSurfaceIndex[faceI];
5931 label globalI = namedSurfaceRegion[faceI];
5932 if (globalI != -1)
5933 {
5934 const labelPair spr = surfaces_.whichSurface(globalI);
5935 faceToZone[faceI] = surfaceToFaceZones[spr.first()][spr.second()];
5936 }
5937 }
5938
5939
5940
5941 // Allocate and assign faceZones from cellZones
5942 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5943
5944 {
5945 // 1. Detect inter-region face and allocate names
5946
5947 LabelPairMap<word> zoneIDsToFaceZone;
5948
5949 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
5950 {
5951 if (faceToZone[faceI] == -1) // Not named surface
5952 {
5953 // Face not yet in a faceZone. (it might already have been
5954 // done so by a 'named' surface). Check if inbetween different
5955 // cellZones
5956 allocateInterRegionFaceZone
5957 (
5958 cellToZone[mesh_.faceOwner()[faceI]],
5959 cellToZone[mesh_.faceNeighbour()[faceI]],
5960 zonesToFaceZone,
5961 zoneIDsToFaceZone
5962 );
5963 }
5964 }
5965
5966 labelList neiCellZone;
5967 syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone);
5968
5969 forAll(neiCellZone, bFaceI)
5970 {
5971 label faceI = bFaceI + mesh_.nInternalFaces();
5972 if (faceToZone[faceI] == -1)
5973 {
5974 allocateInterRegionFaceZone
5975 (
5976 cellToZone[mesh_.faceOwner()[faceI]],
5977 neiCellZone[bFaceI],
5978 zonesToFaceZone,
5979 zoneIDsToFaceZone
5980 );
5981 }
5982 }
5983
5984
5985 // 2.Combine faceZoneNames allocated on different processors
5986
5987 Pstream::mapCombineReduce(zonesToFaceZone, eqOp<word>());
5988
5989
5990 // 3. Allocate faceZones from (now synchronised) faceZoneNames
5991 // Note: the faceZoneNames contain the same data but in different
5992 // order. We could sort the contents but instead just loop
5993 // in sortedToc order.
5994
5995 Info<< "Setting faceZones according to neighbouring cellZones:"
5996 << endl;
5997
5998 // From cellZone indices to faceZone index
5999 LabelPairMap<label> fZoneLookup(zonesToFaceZone.size());
6000
6001 const cellZoneMesh& cellZones = mesh_.cellZones();
6002
6003 {
6004 List<Pair<word>> czs(zonesToFaceZone.sortedToc());
6005
6006 forAll(czs, i)
6007 {
6008 const Pair<word>& cz = czs[i];
6009 const word& fzName = zonesToFaceZone[cz];
6010
6011 Info<< indent<< "cellZones : "
6012 << cz[0] << ' ' << cz[1] << nl
6013 << " faceZone : " << fzName << endl;
6014
6015 label faceZoneI = surfaceZonesInfo::addFaceZone
6016 (
6017 fzName, // name
6018 labelList(0), // addressing
6019 boolList(0), // flipMap
6020 mesh_
6021 );
6022
6023 label cz0 = cellZones.findZoneID(cz[0]);
6024 label cz1 = cellZones.findZoneID(cz[1]);
6025
6026 fZoneLookup.insert(labelPair(cz0, cz1), faceZoneI);
6027 }
6028 }
6029
6030
6031 // 4. Set faceToZone with new faceZones
6032
6033
6034 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
6035 {
6036 if (faceToZone[faceI] == -1)
6037 {
6038 // Face not yet in a faceZone. (it might already have been
6039 // done so by a 'named' surface). Check if inbetween different
6040 // cellZones
6041
6042 label ownZone = cellToZone[mesh_.faceOwner()[faceI]];
6043 label neiZone = cellToZone[mesh_.faceNeighbour()[faceI]];
6044 if (ownZone != neiZone)
6045 {
6046 const bool swap =
6047 (
6048 ownZone == -1
6049 || (neiZone != -1 && ownZone > neiZone)
6050 );
6051 labelPair key(ownZone, neiZone);
6052 if (swap)
6053 {
6054 key.flip();
6055 }
6056 faceToZone[faceI] = fZoneLookup[key];
6057 }
6058 }
6059 }
6060 forAll(neiCellZone, bFaceI)
6061 {
6062 label faceI = bFaceI + mesh_.nInternalFaces();
6063 if (faceToZone[faceI] == -1)
6064 {
6065 label ownZone = cellToZone[mesh_.faceOwner()[faceI]];
6066 label neiZone = neiCellZone[bFaceI];
6067 if (ownZone != neiZone)
6068 {
6069 const bool swap =
6070 (
6071 ownZone == -1
6072 || (neiZone != -1 && ownZone > neiZone)
6073 );
6074 labelPair key(ownZone, neiZone);
6075 if (swap)
6076 {
6077 key.flip();
6078 }
6079 faceToZone[faceI] = fZoneLookup[key];
6080 }
6081 }
6082 }
6083 Info<< endl;
6084 }
6085
6086
6087
6088
6089 // Get coupled neighbour cellZone. Set to -1 on non-coupled patches.
6090 labelList neiCellZone;
6091 syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone);
6092 forAll(patches, patchI)
6093 {
6094 const polyPatch& pp = patches[patchI];
6095
6096 if (!pp.coupled())
6097 {
6098 label bFaceI = pp.start()-mesh_.nInternalFaces();
6099 forAll(pp, i)
6100 {
6101 neiCellZone[bFaceI++] = -1;
6102 }
6103 }
6104 }
6105
6106
6107
6108 // Get per face whether is it master (of a coupled set of faces)
6109 const bitSet isMasterFace(syncTools::getMasterFaces(mesh_));
6110
6111
6112 // faceZones
6113 // ~~~~~~~~~
6114 // Faces on faceZones come in two variants:
6115 // - faces on the outside of a cellZone. They will be oriented to
6116 // point out of the maximum cellZone.
6117 // - free-standing faces. These will be oriented according to the
6118 // local surface normal. We do this in a two step algorithm:
6119 // - do a consistent orientation
6120 // - check number of faces with consistent orientation
6121 // - if <0 flip the whole patch
6122 bitSet meshFlipMap(mesh_.nFaces(), false);
6123 {
6124 // Collect all data on zone faces without cellZones on either side.
6125 const indirectPrimitivePatch patch
6126 (
6128 (
6129 mesh_.faces(),
6130 freeStandingBaffleFaces
6131 (
6132 faceToZone,
6133 cellToZone,
6134 neiCellZone
6135 )
6136 ),
6137 mesh_.points()
6138 );
6139
6140 label nFreeStanding = returnReduce(patch.size(), sumOp<label>());
6141 if (nFreeStanding > 0)
6142 {
6143 Info<< "Detected " << nFreeStanding << " free-standing zone faces"
6144 << endl;
6145
6146 if (debug)
6147 {
6148 OBJstream str(mesh_.time().path()/"freeStanding.obj");
6149 Pout<< "meshRefinement::zonify : dumping faceZone faces to "
6150 << str.name() << endl;
6151 str.write(patch.localFaces(), patch.localPoints(), false);
6152 }
6153
6154
6155 // Detect non-manifold edges
6156 labelList nMasterFacesPerEdge;
6157 calcPatchNumMasterFaces(isMasterFace, patch, nMasterFacesPerEdge);
6158
6159 // Mark zones. Even a single original surface might create multiple
6160 // disconnected/non-manifold-connected zones
6161 labelList faceToConnectedZone;
6162 const label nZones = markPatchZones
6163 (
6164 patch,
6165 nMasterFacesPerEdge,
6166 faceToConnectedZone
6167 );
6168
6169 Map<label> nPosOrientation(2*nZones);
6170 for (label zoneI = 0; zoneI < nZones; zoneI++)
6171 {
6172 nPosOrientation.insert(zoneI, 0);
6173 }
6174
6175 // Make orientations consistent in a topological way. This just
6176 // checks the first face per zone for whether nPosOrientation
6177 // is negative (which it never is at this point)
6178 consistentOrientation
6179 (
6180 isMasterFace,
6181 patch,
6182 nMasterFacesPerEdge,
6183 faceToConnectedZone,
6184 nPosOrientation,
6185
6186 meshFlipMap
6187 );
6188
6189 // Count per region the number of orientations (taking the new
6190 // flipMap into account)
6191 forAll(patch.addressing(), faceI)
6192 {
6193 label meshFaceI = patch.addressing()[faceI];
6194
6195 if (isMasterFace.test(meshFaceI))
6196 {
6197 label n = 1;
6198 if
6199 (
6200 posOrientation.test(meshFaceI)
6201 == meshFlipMap.test(meshFaceI)
6202 )
6203 {
6204 n = -1;
6205 }
6206
6207 nPosOrientation.find(faceToConnectedZone[faceI])() += n;
6208 }
6209 }
6210 Pstream::mapCombineReduce(nPosOrientation, plusEqOp<label>());
6211
6212
6213 Info<< "Split " << nFreeStanding << " free-standing zone faces"
6214 << " into " << nZones << " disconnected regions with size"
6215 << " (negative denotes wrong orientation) :"
6216 << endl;
6217
6218 for (label zoneI = 0; zoneI < nZones; zoneI++)
6219 {
6220 Info<< " " << zoneI << "\t" << nPosOrientation[zoneI]
6221 << endl;
6222 }
6223 Info<< endl;
6224
6225
6226 // Re-apply with new counts (in nPosOrientation). This will cause
6227 // zones with a negative count to be flipped.
6228 consistentOrientation
6229 (
6230 isMasterFace,
6231 patch,
6232 nMasterFacesPerEdge,
6233 faceToConnectedZone,
6234 nPosOrientation,
6235
6236 meshFlipMap
6237 );
6238 }
6239 }
6240
6241
6242
6243
6244 // Topochange container
6245 polyTopoChange meshMod(mesh_);
6246
6247 // Insert changes to put cells and faces into zone
6248 zonify
6249 (
6250 isMasterFace,
6251 cellToZone,
6252 neiCellZone,
6253 faceToZone,
6254 meshFlipMap,
6255 meshMod
6256 );
6257
6258 // Remove any unnecessary fields
6259 mesh_.clearOut();
6260 mesh_.moving(false);
6261
6262 // Change the mesh (no inflation, parallel sync)
6263 autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true);
6264
6265 // Update fields
6266 mesh_.updateMesh(map());
6267
6268 // Move mesh if in inflation mode
6269 if (map().hasMotionPoints())
6270 {
6271 mesh_.movePoints(map().preMotionPoints());
6272 }
6273 else
6274 {
6275 // Delete mesh volumes.
6276 mesh_.clearOut();
6277 }
6278
6279 // Reset the instance for if in overwrite mode
6280 mesh_.setInstance(timeName());
6281
6282 // Print some stats (note: zones are synchronised)
6283 if (mesh_.cellZones().size() > 0)
6284 {
6285 Info<< "CellZones:" << endl;
6286 forAll(mesh_.cellZones(), zoneI)
6287 {
6288 const cellZone& cz = mesh_.cellZones()[zoneI];
6289 Info<< " " << cz.name()
6290 << "\tsize:" << returnReduce(cz.size(), sumOp<label>())
6291 << endl;
6292 }
6293 Info<< endl;
6294 }
6295 if (mesh_.faceZones().size() > 0)
6296 {
6297 Info<< "FaceZones:" << endl;
6298 forAll(mesh_.faceZones(), zoneI)
6299 {
6300 const faceZone& fz = mesh_.faceZones()[zoneI];
6301 Info<< " " << fz.name()
6302 << "\tsize:" << returnReduce(fz.size(), sumOp<label>())
6303 << endl;
6304 }
6305 Info<< endl;
6306 }
6307
6308 // None of the faces has changed, only the zones. Still...
6309 updateMesh(map(), labelList());
6310
6311 return map;
6312}
6313
6314
6315// ************************************************************************* //
Istream and Ostream manipulators taking arguments.
label n
labelList faceLabels(nFaceLabels)
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
const polyBoundaryMesh & pbm
Dynamically sized Field. Similar to DynamicList, but inheriting from a Field instead of a List.
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition DynamicList.H:68
void append(const T &val)
Copy append an element to the end of this list.
Wave propagation of information through grid. Every iteration information goes through one layer of c...
A 1D vector of objects of type <T> with a fixed length <N>.
Definition FixedList.H:73
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
Definition HashSet.H:229
List< Key > sortedToc() const
The table of contents (the keys) in sorted order.
Definition HashTable.C:156
List< Key > toc() const
The table of contents (the keys) in unsorted order.
Definition HashTable.C:141
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition HashTableI.H:152
iterator find(const Key &key)
Find and return an iterator set at the hashed entry.
Definition HashTableI.H:86
label size() const noexcept
The number of elements in table.
Definition HashTable.H:358
@ NO_REGISTER
Do not request registration (bool: false).
@ NO_READ
Nothing to be read.
@ NO_WRITE
Ignore writing from objectRegistry::writeObject().
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition IOobject.H:191
A List with indirect addressing.
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition List.H:72
void transfer(List< T > &list)
Transfer the contents of the argument List into this list and annul the argument list.
Definition List.C:347
bool set(const label i, bool val=true)
A bitSet::set() method for a list of bool.
Definition List.H:469
void append(const T &val)
Append an element at the end of the list.
Definition List.H:497
void setSize(label n)
Alias for resize().
Definition List.H:536
void clear()
Clear the list, i.e. set size to zero.
Definition ListI.H:133
A HashTable to objects of type <T> with a label key.
Definition Map.H:54
An OFstream that keeps track of vertices and provides convenience output methods for OBJ files.
Definition OBJstream.H:58
virtual Ostream & write(const char c) override
Write character.
Definition OBJstream.C:69
virtual const fileName & name() const override
Read/write access to the name of the stream.
Definition OSstream.H:134
A dynamic list of packed unsigned integers, with the number of bits per item specified by the <Width>...
Definition PackedList.H:146
An ordered pair of two objects of type <T> with first() and second() elements.
Definition Pair.H:66
const T & first() const noexcept
Access the first element.
Definition Pair.H:137
const T & second() const noexcept
Access the second element.
Definition Pair.H:147
Wave propagation of information along patch. Every iteration information goes through one layer of fa...
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
const labelList & meshPoints() const
Return labelList of mesh points in patch.
static void mapCombineReduce(Container &values, CombineOp cop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Forwards to Pstream::mapReduce with an in-place cop.
static void listReduce(UList< T > &values, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce list elements (list must be equal size on all ranks), applying bop to each list element.
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers....
Definition PtrList.H:67
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition Time.H:75
fileName path() const
The path for the case = rootPath/caseName.
Definition TimePathsI.H:102
A List with indirect addressing. Like IndirectList but does not store addressing.
T & first()
Access first element of the list, position [0].
Definition UList.H:957
bool found(const T &val, label pos=0) const
Same as contains().
Definition UList.H:983
void size(const label n)
Older name for setAddressableSize.
Definition UList.H:118
label find(const T &val) const
Find index of the first occurrence of the value.
Definition UList.C:160
label size() const noexcept
The number of entries in the list.
Definition UPtrListI.H:106
static Form uniform(const Cmpt &s)
Return a VectorSpace with all elements = s.
label findZoneID(const word &zoneName) const
Find zone index by name, return -1 if not found.
Definition ZoneMesh.C:757
label whichZone(const label objectIndex) const
Given a global object index, return the zone it is in.
Definition ZoneMesh.C:410
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition autoPtr.H:65
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
Definition bitSet.H:61
bool test(const label pos) const
Test for True value at specified position, never auto-vivify entries.
Definition bitSet.H:334
A subset of mesh cells.
Definition cellZone.H:61
A cell is defined as a list of faces with extra functionality.
Definition cell.H:56
double cpuTimeIncrement() const
Return CPU time [seconds] since last call to cpuTimeIncrement(), resetCpuTimeIncrement().
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition dictionary.H:133
Duplicate points.
void setRefinement(const localPointRegion &regionSide, polyTopoChange &)
Play commands into polyTopoChange to duplicate points. Gets.
For use with PatchEdgeFaceWave. Determines topological distance to starting edges....
An edge is a list of two vertex labels. This can correspond to a directed graph edge or an edge on a ...
Definition edge.H:62
A list of face labels.
Definition faceSet.H:50
virtual void sync(const polyMesh &mesh)
Sync faceSet across coupled patches.
Definition faceSet.C:152
A subset of mesh faces organised as a primitive patch.
Definition faceZone.H:63
label whichFace(const label meshFaceID) const
The local index of the given mesh face, -1 if not in the zone.
Definition faceZone.H:394
const boolList & flipMap() const noexcept
Return face flip map.
Definition faceZone.H:389
A face is a list of labels corresponding to mesh vertices.
Definition face.H:71
A class for handling file names.
Definition fileName.H:75
static word outputPrefix
Directory prefix.
static const word & zeroGradientType() noexcept
The type name for zeroGradient patch fields.
Calculates a non-overlapping list of offsets based on an input size (eg, number of cells) from differ...
Definition globalIndex.H:77
static autoPtr< mapDistribute > calcClosure(const polyMesh &mesh, const List< pointField > &zonePoints, const labelList &blockedFaces, const globalIndex &globalBlockedFaces, const bool erode, labelList &closureFaces, labelList &closureToBlocked)
Optional direct use to generate the set of faces and the method to.
Takes mesh with 'baffles' (= boundary faces sharing points). Determines for selected points on bounda...
static labelPairList findDuplicateFacePairs(const polyMesh &)
Helper routine to find all baffles (two boundary faces.
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
const pointField & preMotionPoints() const noexcept
Pre-motion point positions.
const labelList & reverseFaceMap() const noexcept
Reverse face map.
const labelList & faceMap() const noexcept
Old face map.
bool hasMotionPoints() const noexcept
Has valid preMotionPoints?
Helper class which maintains intersections of (changing) mesh with (static) surfaces.
labelList getZones(const List< surfaceZonesInfo::faceZoneType > &fzTypes) const
Get zones of given type.
bool getFaceZoneInfo(const word &fzName, label &masterPatchID, label &slavePatchID, surfaceZonesInfo::faceZoneType &fzType) const
Lookup faceZone information. Return false if no information.
autoPtr< mapPolyMesh > removeLimitShells(const label nBufferLayers, const label nErodeCellZones, const labelList &globalToMasterPatch, const labelList &globalToSlavePatch, const pointField &locationsInMesh, const wordList &regionsInMesh, const pointField &locationsOutsideMesh)
Remove cells from limitRegions if level -1.
void printMeshInfo(const bool debug, const string &msg, const bool printCellLevel) const
Print some mesh stats.
static void mapBaffles(const polyMesh &mesh, const labelList &faceMap, List< labelPair > &baffles)
Map baffles after layer addition. Gets new-to-old face map.
static List< labelPair > subsetBaffles(const polyMesh &mesh, const labelList &zoneIDs, const List< labelPair > &baffles)
Subset baffles according to zones.
void updateMesh(const mapPolyMesh &, const labelList &changedFaces)
Update for external change to mesh. changedFaces are in new mesh.
writeType
Enumeration for what to write. Used as a bit-pattern.
void mergeFreeStandingBaffles(const bool samePatch, const snapParameters &snapParams, const bool useTopologicalSnapDetection, const bool removeEdgeConnectedCells, const scalarField &perpendicularAngle, const scalar planarAngle, const dictionary &motionDict, Time &runTime, const labelList &globalToMasterPatch, const labelList &globalToSlavePatch, const pointField &locationsInMesh, const pointField &locationsOutsideMesh)
Merge free-standing baffles.
autoPtr< mapPolyMesh > mergePoints(const labelList &pointToDuplicate)
Merge duplicate points.
autoPtr< mapPolyMesh > dupNonManifoldPoints()
Find boundary points that connect to more than one cell.
autoPtr< mapPolyMesh > dupNonManifoldBoundaryPoints()
Find boundary points that are on faceZones of type boundary.
void getZoneFaces(const labelList &zoneIDs, labelList &faceZoneID, labelList &ownPatch, labelList &neiPatch, labelList &nBaffles) const
Get per-face information (faceZone, master/slave patch).
autoPtr< mapPolyMesh > mergeBaffles(const List< labelPair > &, const Map< label > &faceToPatch)
Merge baffles. Gets pairs of faces and boundary faces to move.
label countHits() const
Count number of intersections (local).
word timeName() const
Replacement for Time::timeName() that returns oldInstance (if overwrite_).
debugType
Enumeration for what to debug. Used as a bit-pattern.
autoPtr< mapPolyMesh > dupNonManifoldPoints(const localPointRegion &)
Find boundary points that connect to more than one cell.
autoPtr< mapPolyMesh > splitMeshRegions(const labelList &globalToMasterPatch, const labelList &globalToSlavePatch, const pointField &locationsInMesh, const pointField &locationsOutsideMesh, const bool exitIfLeakPath, const refPtr< coordSetWriter > &leakPathFormatter)
Split mesh. Keep part containing point. Return empty map if.
const fvMesh & mesh() const
Reference to mesh.
label addPointZone(const word &name)
Add pointZone if does not exist. Return index of zone.
static label findRegions(const polyMesh &, const vector &perturbVec, const pointField &locationsInMesh, const pointField &locationsOutsideMesh, const label nRegions, labelList &cellRegion, const boolList &blockedFace, const bool exitIfLeakPath, const refPtr< coordSetWriter > &leakPathFormatter)
Find regions points are in.
autoPtr< mapPolyMesh > mergeZoneBaffles(const bool doInternalZones, const bool doBaffleZones)
Merge all baffles on faceZones.
void baffleAndSplitMesh(const bool handleSnapProblems, const snapParameters &snapParams, const bool useTopologicalSnapDetection, const bool removeEdgeConnectedCells, const scalarField &perpendicularAngle, const label nErodeCellZones, const dictionary &motionDict, Time &runTime, const labelList &globalToMasterPatch, const labelList &globalToSlavePatch, const pointField &locationsInMesh, const wordList &regionsInMesh, const pointField &locationsOutsideMesh, const bool exitIfLeakPath, const refPtr< coordSetWriter > &leakPathFormatter, refPtr< surfaceWriter > &surfFormatter)
Split off unreachable areas of mesh.
autoPtr< mapPolyMesh > createBaffles(const labelList &ownPatch, const labelList &neiPatch)
Create baffle for every internal face where ownPatch != -1.
static writeType writeLevel()
Get/set write level.
autoPtr< mapPolyMesh > createZoneBaffles(const labelList &zoneIDs, List< labelPair > &baffles, labelList &originatingFaceZone)
Create baffles for faces on faceZones. Return created baffles.
Transport of orientation for use in PatchEdgeFaceWave.
A subset of mesh points.
Definition pointZone.H:64
A polyBoundaryMesh is a polyPatch list with registered IO, a reference to the associated polyMesh,...
label whichPatch(const label meshFacei) const
Return patch index for a given mesh face index. Uses binary search.
static void mergePoints(const polyMesh &, const Map< label > &pointToMaster, polyTopoChange &meshMod)
Helper: Merge points.
Mesh consisting of general polyhedral cells.
Definition polyMesh.H:79
Class describing modification of a cell.
Class describing modification of a face.
A patch is a list of labels that address the faces in the global face list.
Definition polyPatch.H:73
Class containing data for face removal.
Direct mesh changes based on v1.3 polyTopoChange syntax.
label setAction(const topoAction &action)
For compatibility with polyTopoChange: set topological action.
autoPtr< mapPolyMesh > changeMesh(polyMesh &mesh, const labelUList &patchMap, const bool inflate, const bool syncParallel=true, const bool orderCells=false, const bool orderPoints=false)
Inplace changes mesh without change of patches.
A class for managing references or pointers (no reference counting).
Definition refPtr.H:54
static List< pointField > zonePoints(const pointField &locationsInMesh, const wordList &zonesInMesh, const pointField &locationsOutsideMesh)
Helper: per zone (entry in zonesInMesh) the locations with.
Determines the 'side' for every face and connected to a singly-connected (through edges) region of fa...
Definition regionSide.H:60
This class separates the mesh into distinct unconnected regions, each of which is then given a label ...
label nRegions() const
Return total number of regions.
Given list of cells to remove, insert all the topology changes.
Definition removeCells.H:60
Simple container to keep together snap specific information.
static labelList getInsidePointNamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of surfaces with a cellZone that have 'insidePoint'.
static labelList getUnnamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of unnamed surfaces (surfaces without faceZoneName).
static labelList getStandaloneNamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of named surfaces without a cellZone.
faceZoneType
What to do with faceZone faces.
static label addFaceZone(const word &name, const labelList &addressing, const boolList &flipMap, polyMesh &mesh)
static const Enum< faceZoneType > faceZoneTypeNames
static labelList getNamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of named surfaces (surfaces with faceZoneName).
static labelList addCellZonesToMesh(const PtrList< surfaceZonesInfo > &surfList, const labelList &namedSurfaces, polyMesh &mesh)
static labelListList addFaceZonesToMesh(const PtrList< surfaceZonesInfo > &surfList, const labelList &namedSurfaces, polyMesh &mesh)
static labelList getClosedNamedSurfaces(const PtrList< surfaceZonesInfo > &surfList, const searchableSurfaces &allGeometry, const labelList &surfaces)
Get indices of surfaces with a cellZone that are closed and.
static void swapBoundaryCellList(const polyMesh &mesh, const UList< T > &cellData, List< T > &neighbourCellData, const bool parRun=UPstream::parRun())
Extract and swap to obtain neighbour cell values for all boundary faces.
static bitSet getMasterFaces(const polyMesh &mesh)
Get per face whether it is uncoupled or a master of a coupled set of faces.
Definition syncTools.C:119
static bitSet getInternalOrCoupledFaces(const polyMesh &mesh)
Get per face whether it is internal or coupled.
Definition syncTools.C:165
static void swapBoundaryFaceList(const polyMesh &mesh, UList< T > &faceValues, const bool parRun=UPstream::parRun())
Swap coupled boundary face values. Uses eqOp.
Definition syncTools.H:524
static void syncFaceList(const polyMesh &mesh, UList< T > &faceValues, const CombineOp &cop, const bool parRun=UPstream::parRun())
Synchronize values on all mesh faces.
Definition syncTools.H:465
static void syncPointList(const polyMesh &mesh, List< T > &pointValues, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh points.
static void syncEdgeList(const polyMesh &mesh, List< T > &edgeValues, const CombineOp &cop, const T &nullValue, const TransformOp &top, const FlipOp &fop)
Synchronize values on all mesh edges.
Class used to pass additional data in.
Definition wallPoints.H:67
For use with FaceCellWave. Determines topological distance to starting faces.
Definition wallPoints.H:60
A class for handling words, derived from Foam::string.
Definition word.H:66
const word & name() const noexcept
The zone name.
volScalarField & p
const polyBoundaryMesh & patches
bool coupled
dynamicFvMesh & mesh
engineTime & runTime
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
word outputName("finiteArea-edges.obj")
auto & name
const labelIOList & zoneIDs
Definition correctPhi.H:59
List< wallPoints > allFaceInfo(mesh_.nFaces())
wallPoints::trackData td(isBlockedFace, regionToBlockSize)
const bitSet isBlockedFace(intersectedFaces())
FaceCellWave< wallPoints, wallPoints::trackData > wallDistCalc(mesh_, changedFaces, faceDist, allFaceInfo, allCellInfo, 0, td)
List< wallPoints > allCellInfo(mesh_.nCells())
word timeName
Definition getTimeIndex.H:3
#define WarningInFunction
Report a warning using Foam::Warning.
labelList findIndices(const ListType &input, const UnaryPredicate &pred, label start=0)
Linear search to find all occurences of given element.
const char * end
Definition SVGTools.H:223
Namespace for handling debugging switches.
Definition debug.C:45
const wordList area
Standard area field types (scalar, vector, tensor, etc).
const std::string patch
OpenFOAM patch number as a std::string.
constexpr auto key(const Type &t) noexcept
Helper function to return the enum value.
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
Type & refCast(U &obj)
A dynamic_cast (for references) to Type reference.
Definition typeInfo.H:172
Pair< label > labelPair
A pair of labels.
Definition Pair.H:54
List< word > wordList
List of word.
Definition fileName.H:60
bool returnReduceOr(const bool value, const int communicator=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:40
const dimensionSet dimless
Dimensionless.
HashTable< word, wordPair, Foam::Hash< wordPair > > wordPairHashTable
HashTable of wordPair.
ZoneMesh< pointZone, polyMesh > pointZoneMesh
A ZoneMesh with pointZone content on a polyMesh.
List< labelList > labelListList
List of labelList.
Definition labelList.H:38
List< label > labelList
A List of labels.
Definition List.H:62
dimensionedSymmTensor sqr(const dimensionedVector &dv)
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition POSIX.C:616
GeometricField< scalar, fvPatchField, volMesh > volScalarField
UIndirectList< label > labelUIndList
UIndirectList of labels.
void reverse(UList< T > &list, const label n)
Reverse the first n elements of the list.
Definition UListI.H:539
Smanip< std::ios_base::fmtflags > setf(std::ios_base::fmtflags flags)
Definition IOmanip.H:169
HashTable< T, labelPair, Foam::Hash< labelPair > > LabelPairMap
messageStream Info
Information stream (stdout output on master, null elsewhere).
ZoneMesh< faceZone, polyMesh > faceZoneMesh
A ZoneMesh with faceZone content on a polyMesh.
List< face > faceList
List of faces.
Definition faceListFwd.H:41
label checkData(const objectRegistry &obr, const instantList &timeDirs, wordList &objectNames, const fileName &local=fileName::null)
Check if fields are good to use (available at all times).
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
constexpr label labelMax
Definition label.H:55
T returnReduce(const T &value, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
Omanip< int > setw(const int i)
Definition IOmanip.H:199
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
Ostream & indent(Ostream &os)
Indent stream.
Definition Ostream.H:481
constexpr scalar degToRad() noexcept
Multiplication factor for degrees to radians conversion.
ZoneMesh< cellZone, polyMesh > cellZoneMesh
A ZoneMesh with cellZone content on a polyMesh.
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
void reduce(T &value, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce).
bool returnReduceAnd(const bool value, const int communicator=UPstream::worldComm)
Perform logical (and) MPI Allreduce on a copy. Uses UPstream::reduceAnd.
PrimitivePatch< IndirectList< face >, const pointField & > indirectPrimitivePatch
A PrimitivePatch with an IndirectList for the faces, const reference for the point field.
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:26
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i), works like std::iota() but returning a...
errorManip< error > abort(error &err)
Definition errorManip.H:139
Field< vector > vectorField
Specialisation of Field<T> for vector.
vector point
Point is a vector.
Definition point.H:37
List< bool > boolList
A List of bools.
Definition List.H:60
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1, const label comm)
static constexpr const zero Zero
Global zero (0).
Definition zero.H:127
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition exprTraits.C:127
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
PrimitivePatch< UIndirectList< face >, const pointField & > uindirectPrimitivePatch
A PrimitivePatch with UIndirectList for the faces, const reference for the point field.
vectorField pointField
pointField is a vectorField.
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
Vector< scalar > vector
Definition vector.H:57
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition errorManip.H:125
DynamicID< faceZoneMesh > faceZoneID
Foam::faceZoneID.
Definition ZoneIDs.H:43
Type gMax(const FieldField< Field, Type > &f)
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
dimensionedScalar cos(const dimensionedScalar &ds)
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
runTime write()
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints, -1);const cellModel &hex=cellModel::ref(cellModel::HEX);labelList hexLabels(8);label activeCells=0;labelList pointMap(nPoints);forAll(pointMap, i){ pointMap[i]=i;}for(label i=0;i< nPoints;i++){ if(f[i] > 0.0) { hexLabels[0]=i;hexLabels[1]=i1tab[i];hexLabels[2]=i3tab[i1tab[i]];hexLabels[3]=i3tab[i];hexLabels[4]=i8tab[i];hexLabels[5]=i1tab[i8tab[i]];hexLabels[6]=i3tab[i1tab[i8tab[i]]];hexLabels[7]=i3tab[i8tab[i]];cellShapes[activeCells].reset(hex, hexLabels);edgeList edges=cellShapes[activeCells].edges();forAll(edges, ei) { if(edges[ei].mag(points)< SMALL) { label start=pointMap[edges[ei].start()];while(start !=pointMap[start]) { start=pointMap[start];} label end=pointMap[edges[ei].end()];while(end !=pointMap[end]) { end=pointMap[end];} pointMap[start]=pointMap[end]=Foam::min(start, end);} } cellZoning[activeCells]=idreg[i];activeCells++;}}cellShapes.setSize(activeCells);cellZoning.setSize(activeCells);forAll(cellShapes, celli){ cellShape &cs=cellShapes[celli];forAll(cs, i) { cs[i]=pointMap[cs[i]];} cs.collapse();}label bcIDs[11]={-1, 0, 2, 4, -1, 5, -1, 6, 7, 8, 9};constexpr label nBCs=12;const word *kivaPatchTypes[nBCs]={ &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &symmetryPolyPatch::typeName, &wedgePolyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &symmetryPolyPatch::typeName, &oldCyclicPolyPatch::typeName};enum patchTypeNames{ PISTON, VALVE, LINER, CYLINDERHEAD, AXIS, WEDGE, INFLOW, OUTFLOW, PRESIN, PRESOUT, SYMMETRYPLANE, CYCLIC};const char *kivaPatchNames[nBCs]={ "piston", "valve", "liner", "cylinderHead", "axis", "wedge", "inflow", "outflow", "presin", "presout", "symmetryPlane", "cyclic"};List< DynamicList< face > > pFaces[nBCs]
labelList f(nPoints)
insidePoints((1 2 3))
volScalarField & e
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299
#define forAllConstIters(container, iter)
Iterate across all elements of the container object with const access.
Definition stdFoam.H:235
Unit conversion functions.