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 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "meshRefinement.H"
30 #include "fvMesh.H"
31 #include "syncTools.H"
32 #include "Time.H"
33 #include "refinementSurfaces.H"
34 #include "faceSet.H"
35 #include "indirectPrimitivePatch.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"
48 #include "patchFaceOrientation.H"
49 #include "PatchEdgeFaceWave.H"
50 #include "edgeTopoDistanceData.H"
51 #include "polyMeshAdder.H"
52 #include "IOmanip.H"
53 #include "refinementParameters.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 
65 Foam::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 
178 void 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 {
188  autoPtr<OBJstream> str;
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;
235  List<pointIndexHit> hit1;
236  labelList region1;
237  labelList surface2;
238  List<pointIndexHit> hit2;
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 
285 void 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 
426 Foam::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  }
484  }
485  }
486  }
487  return bafflePatch;
488 }
489 
490 
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 
601  autoPtr<mapPolyMesh> mapPtr;
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;
693 }
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 }
839 
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  {
870  Pstream::listReduce(nBaffles, sumOp<label>());
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  (
951  debugType(debug),
952  writeType(writeLevel() | WRITEMESH),
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 
970 Foam::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 
1032  DynamicList<label> fe0;
1033  DynamicList<label> fe1;
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;
1144  List<pointIndexHit> hit1;
1145  labelList region1;
1146  vectorField normal1;
1147 
1148  labelList surface2;
1149  List<pointIndexHit> hit2;
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 }
1235 
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 }
1419 
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  (
1443  subsetBaffles
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
1461 void 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 
1700 void 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 
1816 void 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 
1842 bool 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 
1927 void 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 
2165 void 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 
2270 void 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);
2502  FaceCellWave<wallPoints, wallPoints::trackData> wallDistCalc
2503  (
2504  mesh_,
2505  changedFaces,
2506  faceDist,
2507  allFaceInfo,
2508  allCellInfo,
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,
2636  maxEqOp<label>()
2637  );
2639  (
2640  mesh_,
2641  unnamedSurfaceRegion2,
2642  maxEqOp<label>()
2643  );
2644  if (namedSurfaceRegion.size())
2645  {
2647  (
2648  mesh_,
2649  namedSurfaceRegion,
2650  maxEqOp<label>()
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 
2667 void 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 
2756 void 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;
2801  List<pointIndexHit> hit1;
2802  vectorField normal1;
2803  labelList surface2;
2804  labelList region2;
2805  List<pointIndexHit> hit2;
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,
2882  maxEqOp<label>()
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 
2899 void 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,
3263  orEqOp<unsigned int>(),
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  {
3286  fileName outputName
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(),
3309  outputName
3310  );
3311  surfFormatter->write();
3312  surfFormatter->clear();
3313  }
3314  if (returnReduceOr(namedClosureFaces.size()) && surfFormatter)
3315  {
3316  fileName outputName
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(),
3339  outputName
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 
3795 void 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;
3813  labelList faceZone;
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 
3938 Foam::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 
4003 void Foam::meshRefinement::calcPatchNumMasterFaces
4004 (
4005  const bitSet& isMasterFace,
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,
4033  plusEqOp<label>(),
4034  label(0)
4035  );
4036 }
4037 
4038 
4039 Foam::label Foam::meshRefinement::markPatchZones
4040 (
4042  const labelList& nMasterFacesPerEdge,
4043  labelList& faceToZone
4044 ) const
4045 {
4046  List<edgeTopoDistanceData<label>> allEdgeInfo(patch.nEdges());
4047  List<edgeTopoDistanceData<label>> allFaceInfo(patch.size());
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;
4071  DynamicList<edgeTopoDistanceData<label>> changedInfo;
4072 
4073  const scalar tol = PatchEdgeFaceWave
4074  <
4076  edgeTopoDistanceData<label>
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
4152  PatchEdgeFaceWave
4153  <
4155  edgeTopoDistanceData<label>
4156  > calc
4157  (
4158  mesh_,
4159  patch,
4160  changedEdges,
4161  changedInfo,
4162  allEdgeInfo,
4163  allFaceInfo,
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 
4188 void Foam::meshRefinement::consistentOrientation
4189 (
4190  const bitSet& isMasterFace,
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());
4202  List<patchFaceOrientation> allFaceInfo(patch.size());
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;
4251  DynamicList<patchFaceOrientation> changedInfo;
4252 
4253  const scalar tol = PatchEdgeFaceWave
4254  <
4256  patchFaceOrientation
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
4336  PatchEdgeFaceWave
4337  <
4339  patchFaceOrientation
4340  > calc
4341  (
4342  mesh_,
4343  patch,
4344  changedEdges,
4345  changedInfo,
4346  allEdgeInfo,
4347  allFaceInfo,
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 
4415  if (allFaceInfo[faceI] == orientedSurface::NOFLIP)
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 
4434 void 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  (
4479  polyModifyFace
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  (
4532  polyModifyFace
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  (
4562  polyModifyCell
4563  (
4564  cellI,
4565  false, // removeFromZone
4566  zoneI
4567  )
4568  );
4569  }
4570  }
4571 }
4572 
4573 
4574 void 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 
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 "
4663  << returnReduce(countHits(), sumOp<label>())
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  (
4713  debugType(debug),
4714  writeType(writeLevel() | WRITEMESH),
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 
4794  splitMeshRegions
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  (
4821  debugType(debug),
4822  writeType(writeLevel() | WRITEMESH),
4823  runTime.path()/timeName()
4824  );
4825  Pout<< "Dumped debug data in = "
4826  << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
4827  }
4828 }
4829 
4830 
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 
4905  splitMeshRegions
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 
4927 Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh
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
4988  findRegions
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 
5014 Foam::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;
5083  autoPtr<mapDistribute> mapPtr;
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,
5319  maxEqOp<label>(),
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 
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  (
5561  debugType(debug),
5562  writeType
5563  (
5564  writeLevel()
5565  | WRITEMESH
5566  ),
5567  mesh_.time().path()/timeName()
5568  );
5569  }
5570  return mapPtr;
5571 }
5572 
5573 
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 
5643  return dupNonManifoldPoints(regionSide);
5644 }
5645 
5646 
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);
5812  map = dupNonManifoldPoints(regionSide);
5813  }
5814  return map;
5815 }
5816 
5817 
5818 // Zoning
5819 Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
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.
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 // ************************************************************************* //
static const word & zeroGradientType() noexcept
The type name for zeroGradient patch fields.
Definition: fvPatchField.H:202
This class separates the mesh into distinct unconnected regions, each of which is then given a label ...
Definition: regionSplit.H:136
Class containing data for face removal.
const polyBoundaryMesh & pbm
const T & first() const noexcept
Access the first element.
Definition: Pair.H:137
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
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
ZoneMesh< faceZone, polyMesh > faceZoneMesh
A ZoneMesh with the type faceZone.
const labelIOList & zoneIDs
Definition: correctPhi.H:59
fileName path() const
Return path = rootPath/caseName. Same as TimePaths::path()
Definition: Time.H:503
Definition: ops.H:65
labelList findIndices(const ListType &input, const UnaryPredicate &pred, label start=0)
Linear search to find all occurences of given element.
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:491
static void mapBaffles(const polyMesh &mesh, const labelList &faceMap, List< labelPair > &baffles)
Map baffles after layer addition. Gets new-to-old face map.
List< wallPoints > allCellInfo(mesh_.nCells())
autoPtr< mapPolyMesh > mergePoints(const labelList &pointToDuplicate)
Merge duplicate points.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
Class describing modification of a face.
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:68
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
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
void transfer(List< T > &list)
Transfer the contents of the argument List into this list and annul the argument list.
Definition: List.C:337
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:600
void append(const T &val)
Append an element at the end of the list.
Definition: List.H:526
virtual const labelList & faceNeighbour() const
Return face neighbour.
Definition: polyMesh.C:1107
const bitSet isBlockedFace(intersectedFaces())
static void swapBoundaryCellList(const polyMesh &mesh, const UList< T > &cellData, List< T > &neighbourCellData, const bool parRun=UPstream::parRun())
Extract and swap to obtain neighbour cell values for all boundary faces.
wallPoints::trackData td(isBlockedFace, regionToBlockSize)
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Unit conversion functions.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
static labelListList addFaceZonesToMesh(const PtrList< surfaceZonesInfo > &surfList, const labelList &namedSurfaces, polyMesh &mesh)
void setSize(const label n, unsigned int val=0u)
Alias for resize()
Definition: PackedList.H:820
UIndirectList< label > labelUIndList
UIndirectList of labels.
Definition: IndirectList.H:65
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
T & first()
Access first element of the list, position [0].
Definition: UList.H:886
engineTime & runTime
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:529
bool found(const T &val, label pos=0) const
Same as contains()
Definition: UList.H:912
autoPtr< mapPolyMesh > createBaffles(const labelList &ownPatch, const labelList &neiPatch)
Create baffle for every internal face where ownPatch != -1.
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.
static labelList addCellZonesToMesh(const PtrList< surfaceZonesInfo > &surfList, const labelList &namedSurfaces, polyMesh &mesh)
static const Enum< faceZoneType > faceZoneTypeNames
label whichFace(const label globalCellID) const
Helper function to re-direct to zone::localID(...)
Definition: faceZone.C:397
labelList getZones(const List< surfaceZonesInfo::faceZoneType > &fzTypes) const
Get zones of given type.
static List< labelPair > subsetBaffles(const polyMesh &mesh, const labelList &zoneIDs, const List< labelPair > &baffles)
Subset baffles according to zones.
Ignore writing from objectRegistry::writeObject()
const dimensionSet dimless
Dimensionless.
label nFaces() const noexcept
Number of mesh faces.
static labelList getStandaloneNamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of named surfaces without a cellZone.
std::enable_if_t< std::is_same_v< bool, TypeT >, bool > test(const label i) const
Test bool value at specified position, always false for out-of-range access.
Definition: UList.H:800
PrimitivePatch< IndirectList< face >, const pointField & > indirectPrimitivePatch
A PrimitivePatch with an IndirectList for the faces, const reference for the point field...
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.
virtual const fileName & name() const override
Get the name of the output serial stream. (eg, the name of the Fstream file name) ...
Definition: OSstream.H:134
labelList faceLabels(nFaceLabels)
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
DynamicID< faceZoneMesh > faceZoneID
Foam::faceZoneID.
Definition: ZoneIDs.H:43
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1, const label comm)
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:69
static bitSet getInternalOrCoupledFaces(const polyMesh &mesh)
Get per face whether it is internal or coupled.
Definition: syncTools.C:165
A class for managing references or pointers (no reference counting)
Definition: HashPtrTable.H:49
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
Definition: mapPolyMesh.H:158
bool isInternalFace(const label faceIndex) const noexcept
Return true if given face label is internal to the mesh.
const labelList & meshPoints() const
Return labelList of mesh points in patch.
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
Takes mesh with &#39;baffles&#39; (= boundary faces sharing points). Determines for selected points on bounda...
static labelPairList findDuplicateFacePairs(const polyMesh &)
Helper routine to find all baffles (two boundary faces.
void reduce(T &value, [[maybe_unused]] BinaryOp bop, [[maybe_unused]] const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce)
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:286
const labelList & reverseFaceMap() const noexcept
Reverse face map.
Definition: mapPolyMesh.H:620
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:72
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:152
label size() const noexcept
The number of elements in table.
Definition: HashTable.H:358
word timeName
Definition: getTimeIndex.H:3
void write(vtk::formatter &fmt, const Type &val, const label n=1)
Component-wise write of a value (N times)
A list of faces which address into the list of points.
Determines the &#39;side&#39; for every face and connected to a singly-connected (through edges) region of fa...
Definition: regionSide.H:59
word outputName("finiteArea-edges.obj")
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
void setSize(const label n)
Alias for resize()
Definition: List.H:325
Duplicate points.
dynamicFvMesh & mesh
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
dimensionedScalar cos(const dimensionedScalar &ds)
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
label findZoneID(const word &zoneName) const
Find zone index by name, return -1 if not found.
Definition: ZoneMesh.C:723
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i), works like std::iota() but returning a...
Definition: labelLists.C:44
static void mergePoints(const polyMesh &, const Map< label > &pointToMaster, polyTopoChange &meshMod)
Helper: Merge points.
const wordList area
Standard area field types (scalar, vector, tensor, etc)
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:130
A class for handling words, derived from Foam::string.
Definition: word.H:63
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
iterator find(const Key &key)
Find and return an iterator set at the hashed entry.
Definition: HashTableI.H:86
Simple container to keep together snap specific information.
bool returnReduceAnd(const bool value, const int communicator=UPstream::worldComm)
Perform logical (and) MPI Allreduce on a copy. Uses UPstream::reduceAnd.
label size() const noexcept
The number of entries in the list.
Definition: UPtrListI.H:106
const Field< point_type > & faceCentres() const
Return face centres for 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.
autoPtr< mapPolyMesh > mergeZoneBaffles(const bool doInternalZones, const bool doBaffleZones)
Merge all baffles on faceZones.
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1101
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
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]
Definition: readKivaGrid.H:233
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.
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.
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
label nInternalFaces() const noexcept
Number of internal faces.
void reverse(UList< T > &list, const label n)
Reverse the first n elements of the list.
Definition: UListI.H:514
Vector< scalar > vector
Definition: vector.H:57
auto key(const Type &t) -> std::enable_if_t< std::is_enum_v< Type >, std::underlying_type_t< Type > >
Definition: foamGltfBase.H:103
ZoneMesh< pointZone, polyMesh > pointZoneMesh
A ZoneMesh with the type pointZone.
void append(const T &val)
Copy append an element to the end of this list.
Definition: DynamicList.H:609
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1088
label whichPatch(const label meshFacei) const
Return patch index for a given mesh face index. Uses binary search.
static labelList getInsidePointNamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of surfaces with a cellZone that have &#39;insidePoint&#39;.
FaceCellWave< wallPoints, wallPoints::trackData > wallDistCalc(mesh_, changedFaces, faceDist, allFaceInfo, allCellInfo, 0, td)
A HashTable similar to std::unordered_map.
Definition: HashTable.H:108
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
static labelList getUnnamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of unnamed surfaces (surfaces without faceZoneName)
bool returnReduceOr(const bool value, const int communicator=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
errorManip< error > abort(error &err)
Definition: errorManip.H:139
const T * set(const label i) const
Return const pointer to element (can be nullptr), or nullptr for out-of-range access (ie...
Definition: PtrList.H:159
static void syncPointList(const polyMesh &mesh, List< T > &pointValues, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh points.
bool test(const label pos) const
Test for True value at specified position, never auto-vivify entries.
Definition: bitSet.H:329
label find(const T &val) const
Find index of the first occurrence of the value.
Definition: UList.C:172
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.
A polyBoundaryMesh is a polyPatch list with registered IO, a reference to the associated polyMesh...
Istream and Ostream manipulators taking arguments.
An OFstream that keeps track of vertices and provides convenience output methods for OBJ files...
Definition: OBJstream.H:55
Smanip< std::ios_base::fmtflags > setf(std::ios_base::fmtflags flags)
Definition: IOmanip.H:169
int debug
Static debugging option.
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:51
Type gMax(const FieldField< Field, Type > &f)
const faceZoneMesh & faceZones() const noexcept
Return face zone mesh.
Definition: polyMesh.H:671
labelList f(nPoints)
A subset of mesh cells.
Definition: cellZone.H:58
double cpuTimeIncrement() const
Return CPU time [seconds] since last call to cpuTimeIncrement(), resetCpuTimeIncrement().
Definition: cpuTimePosix.C:86
bool hasMotionPoints() const noexcept
Has valid preMotionPoints?
Definition: mapPolyMesh.H:767
label whichZone(const label objectIndex) const
Given a global object index, return the zone it is in.
Definition: ZoneMesh.C:410
autoPtr< mapPolyMesh > createZoneBaffles(const labelList &zoneIDs, List< labelPair > &baffles, labelList &originatingFaceZone)
Create baffles for faces on faceZones. Return created baffles.
const vectorField & faceCentres() const
List< word > wordList
List of word.
Definition: fileName.H:59
PrimitivePatch< UIndirectList< face >, const pointField & > uindirectPrimitivePatch
A PrimitivePatch with UIndirectList for the faces, const reference for the point field.
autoPtr< mapPolyMesh > dupNonManifoldPoints()
Find boundary points that connect to more than one cell.
vector point
Point is a vector.
Definition: point.H:37
autoPtr< mapPolyMesh > dupNonManifoldBoundaryPoints()
Find boundary points that are on faceZones of type boundary.
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
Definition: bitSet.H:59
#define WarningInFunction
Report a warning using Foam::Warning.
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers...
Definition: List.H:54
List< Key > sortedToc() const
The table of contents (the keys) in sorted order.
Definition: HashTable.C:156
Direct mesh changes based on v1.3 polyTopoChange syntax.
const char * end
Definition: SVGTools.H:223
const polyBoundaryMesh & patches
const word & name() const noexcept
The zone name.
void getZoneFaces(const labelList &zoneIDs, labelList &faceZoneID, labelList &ownPatch, labelList &neiPatch, labelList &nBaffles) const
Get per-face information (faceZone, master/slave patch)
Nothing to be read.
static word outputPrefix
Directory prefix.
const std::string patch
OpenFOAM patch number as a std::string.
void setRefinement(const localPointRegion &regionSide, polyTopoChange &)
Play commands into polyTopoChange to duplicate points. Gets.
label nRegions() const
Return total number of regions.
Definition: regionSplit.H:337
static List< pointField > zonePoints(const pointField &locationsInMesh, const wordList &zonesInMesh, const pointField &locationsOutsideMesh)
Helper: per zone (entry in zonesInMesh) the locations with.
autoPtr< mapPolyMesh > mergeBaffles(const List< labelPair > &, const Map< label > &faceToPatch)
Merge baffles. Gets pairs of faces and boundary faces to move.
static labelList getClosedNamedSurfaces(const PtrList< surfaceZonesInfo > &surfList, const searchableSurfaces &allGeometry, const labelList &surfaces)
Get indices of surfaces with a cellZone that are closed and.
const boolList & flipMap() const noexcept
Return face flip map.
Definition: faceZone.H:367
static labelList getNamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of named surfaces (surfaces with faceZoneName)
messageStream Info
Information stream (stdout output on master, null elsewhere)
constexpr label labelMax
Definition: label.H:55
writeType
Enumeration for what to write. Used as a bit-pattern.
label n
Field< vector > vectorField
Specialisation of Field<T> for vector.
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.
Definition: holeToFace.C:1190
faceZoneType
What to do with faceZone faces.
debugType
Enumeration for what to debug. Used as a bit-pattern.
std::enable_if_t< std::is_same_v< bool, TypeT >, bool > set(const label i, bool val=true)
A bitSet::set() method for a list of bool.
Definition: List.H:498
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:75
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
A subset of mesh faces organised as a primitive patch.
Definition: faceZone.H:60
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.
List< label > labelList
A List of labels.
Definition: List.H:61
volScalarField & p
insidePoints((1 2 3))
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:69
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)
bool coupled
const T & second() const noexcept
Access the second element.
Definition: Pair.H:147
constexpr scalar degToRad(const scalar deg) noexcept
Conversion from degrees to radians.
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...
List< bool > boolList
A List of bools.
Definition: List.H:59
A List with indirect addressing.
Definition: IndirectList.H:60
const pointField & preMotionPoints() const noexcept
Pre-motion point positions.
Definition: mapPolyMesh.H:759
List< wallPoints > allFaceInfo(mesh_.nFaces())
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Do not request registration (bool: false)
HashTable< word, wordPair, Foam::Hash< wordPair > > wordPairHashTable
HashTable of wordPair.
ZoneMesh< cellZone, polyMesh > cellZoneMesh
A ZoneMesh with the type cellZone.
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
static label addFaceZone(const word &name, const labelList &addressing, const boolList &flipMap, polyMesh &mesh)
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28
A HashTable to objects of type <T> with a label key.
label setAction(const topoAction &action)
For compatibility with polyTopoChange: set topological action.
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127