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-2024 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  {
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::listCombineReduce(regionToCellZone, maxEqOp<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::listCombineReduce(regionToCellZone, maxEqOp<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  return faceLabels.shrink();
3999 }
4000 
4001 
4002 void Foam::meshRefinement::calcPatchNumMasterFaces
4003 (
4004  const bitSet& isMasterFace,
4006  labelList& nMasterFacesPerEdge
4007 ) const
4008 {
4009  // Number of (master)faces per edge
4010  nMasterFacesPerEdge.setSize(patch.nEdges());
4011  nMasterFacesPerEdge = 0;
4012 
4013  forAll(patch.addressing(), faceI)
4014  {
4015  const label meshFaceI = patch.addressing()[faceI];
4016 
4017  if (isMasterFace.test(meshFaceI))
4018  {
4019  const labelList& fEdges = patch.faceEdges()[faceI];
4020  forAll(fEdges, fEdgeI)
4021  {
4022  nMasterFacesPerEdge[fEdges[fEdgeI]]++;
4023  }
4024  }
4025  }
4026 
4028  (
4029  mesh_,
4030  patch.meshEdges(mesh_.edges(), mesh_.pointEdges()),
4031  nMasterFacesPerEdge,
4032  plusEqOp<label>(),
4033  label(0)
4034  );
4035 }
4036 
4037 
4038 Foam::label Foam::meshRefinement::markPatchZones
4039 (
4041  const labelList& nMasterFacesPerEdge,
4042  labelList& faceToZone
4043 ) const
4044 {
4045  List<edgeTopoDistanceData<label>> allEdgeInfo(patch.nEdges());
4046  List<edgeTopoDistanceData<label>> allFaceInfo(patch.size());
4047 
4048 
4049  // Protect all non-manifold edges
4050  {
4051  // label nProtected = 0;
4052 
4053  forAll(nMasterFacesPerEdge, edgeI)
4054  {
4055  if (nMasterFacesPerEdge[edgeI] > 2)
4056  {
4057  allEdgeInfo[edgeI] = edgeTopoDistanceData<label>(0, -2);
4058  // ++nProtected;
4059  }
4060  }
4061  //Info<< "Protected from visiting "
4062  // << returnReduce(nProtected, sumOp<label>())
4063  // << " non-manifold edges" << nl << endl;
4064  }
4065 
4066 
4067  // Hand out zones
4068 
4069  DynamicList<label> changedEdges;
4070  DynamicList<edgeTopoDistanceData<label>> changedInfo;
4071 
4072  const scalar tol = PatchEdgeFaceWave
4073  <
4075  edgeTopoDistanceData<label>
4076  >::propagationTol();
4077 
4078  int dummyTrackData;
4079 
4080  const globalIndex globalFaces(patch.size());
4081 
4082  label faceI = 0;
4083 
4084  label currentZoneI = 0;
4085 
4086  while (true)
4087  {
4088  // Pick an unset face
4089  label globalSeed = labelMax;
4090  for (; faceI < allFaceInfo.size(); faceI++)
4091  {
4092  if (!allFaceInfo[faceI].valid(dummyTrackData))
4093  {
4094  globalSeed = globalFaces.toGlobal(faceI);
4095  break;
4096  }
4097  }
4098 
4099  reduce(globalSeed, minOp<label>());
4100 
4101  if (globalSeed == labelMax)
4102  {
4103  break;
4104  }
4105 
4106  if (globalFaces.isLocal(globalSeed))
4107  {
4108  const label seedFaceI = globalFaces.toLocal(globalSeed);
4109 
4110  edgeTopoDistanceData<label>& faceInfo = allFaceInfo[seedFaceI];
4111 
4112  // Set face
4113  faceInfo = edgeTopoDistanceData<label>(0, currentZoneI);
4114 
4115  // .. and seed its edges
4116  const labelList& fEdges = patch.faceEdges()[seedFaceI];
4117  forAll(fEdges, fEdgeI)
4118  {
4119  label edgeI = fEdges[fEdgeI];
4120 
4121  edgeTopoDistanceData<label>& edgeInfo = allEdgeInfo[edgeI];
4122 
4123  if
4124  (
4125  edgeInfo.updateEdge<int>
4126  (
4127  mesh_,
4128  patch,
4129  edgeI,
4130  seedFaceI,
4131  faceInfo,
4132  tol,
4133  dummyTrackData
4134  )
4135  )
4136  {
4137  changedEdges.append(edgeI);
4138  changedInfo.append(edgeInfo);
4139  }
4140  }
4141  }
4142 
4143 
4144  if (returnReduceAnd(changedEdges.empty()))
4145  {
4146  break;
4147  }
4148 
4149 
4150  // Walk
4151  PatchEdgeFaceWave
4152  <
4154  edgeTopoDistanceData<label>
4155  > calc
4156  (
4157  mesh_,
4158  patch,
4159  changedEdges,
4160  changedInfo,
4161  allEdgeInfo,
4162  allFaceInfo,
4163  returnReduce(patch.nEdges(), sumOp<label>())
4164  );
4165 
4166  currentZoneI++;
4167  }
4168 
4169 
4170  faceToZone.setSize(patch.size());
4171  forAll(allFaceInfo, faceI)
4172  {
4173  if (!allFaceInfo[faceI].valid(dummyTrackData))
4174  {
4176  << "Problem: unvisited face " << faceI
4177  << " at " << patch.faceCentres()[faceI]
4178  << exit(FatalError);
4179  }
4180  faceToZone[faceI] = allFaceInfo[faceI].data();
4181  }
4182 
4183  return currentZoneI;
4184 }
4185 
4186 
4187 void Foam::meshRefinement::consistentOrientation
4188 (
4189  const bitSet& isMasterFace,
4191  const labelList& nMasterFacesPerEdge,
4192  const labelList& faceToZone,
4193  const Map<label>& zoneToOrientation,
4194  bitSet& meshFlipMap
4195 ) const
4196 {
4197  const polyBoundaryMesh& bm = mesh_.boundaryMesh();
4198 
4199  // Data on all edges and faces
4200  List<patchFaceOrientation> allEdgeInfo(patch.nEdges());
4201  List<patchFaceOrientation> allFaceInfo(patch.size());
4202 
4203  // Make sure we don't walk through
4204  // - slaves of coupled faces
4205  // - non-manifold edges
4206  {
4207  // label nProtected = 0;
4208 
4209  forAll(patch.addressing(), faceI)
4210  {
4211  const label meshFaceI = patch.addressing()[faceI];
4212  const label patchI = bm.whichPatch(meshFaceI);
4213 
4214  if
4215  (
4216  patchI != -1
4217  && bm[patchI].coupled()
4218  && !isMasterFace.test(meshFaceI)
4219  )
4220  {
4221  // Slave side. Mark so doesn't get visited.
4223  // ++nProtected;
4224  }
4225  }
4226  //Info<< "Protected from visiting "
4227  // << returnReduce(nProtected, sumOp<label>())
4228  // << " slaves of coupled faces" << nl << endl;
4229  }
4230  {
4231  label nProtected = 0;
4232 
4233  forAll(nMasterFacesPerEdge, edgeI)
4234  {
4235  if (nMasterFacesPerEdge[edgeI] > 2)
4236  {
4237  allEdgeInfo[edgeI] = orientedSurface::NOFLIP;
4238  ++nProtected;
4239  }
4240  }
4241 
4242  Info<< "Protected from visiting "
4243  << returnReduce(nProtected, sumOp<label>())
4244  << " non-manifold edges" << nl << endl;
4245  }
4246 
4247 
4248 
4249  DynamicList<label> changedEdges;
4250  DynamicList<patchFaceOrientation> changedInfo;
4251 
4252  const scalar tol = PatchEdgeFaceWave
4253  <
4255  patchFaceOrientation
4256  >::propagationTol();
4257 
4258  int dummyTrackData;
4259 
4260  globalIndex globalFaces(patch.size());
4261 
4262  while (true)
4263  {
4264  // Pick an unset face
4265  label globalSeed = labelMax;
4266  forAll(allFaceInfo, faceI)
4267  {
4269  {
4270  globalSeed = globalFaces.toGlobal(faceI);
4271  break;
4272  }
4273  }
4274 
4275  reduce(globalSeed, minOp<label>());
4276 
4277  if (globalSeed == labelMax)
4278  {
4279  break;
4280  }
4281 
4282  if (globalFaces.isLocal(globalSeed))
4283  {
4284  const label seedFaceI = globalFaces.toLocal(globalSeed);
4285 
4286  // Determine orientation of seedFace
4287 
4288  patchFaceOrientation& faceInfo = allFaceInfo[seedFaceI];
4289 
4290  // Start off with correct orientation
4291  faceInfo = orientedSurface::NOFLIP;
4292 
4293  if (zoneToOrientation[faceToZone[seedFaceI]] < 0)
4294  {
4295  faceInfo.flip();
4296  }
4297 
4298 
4299  const labelList& fEdges = patch.faceEdges()[seedFaceI];
4300  forAll(fEdges, fEdgeI)
4301  {
4302  label edgeI = fEdges[fEdgeI];
4303 
4304  patchFaceOrientation& edgeInfo = allEdgeInfo[edgeI];
4305 
4306  if
4307  (
4308  edgeInfo.updateEdge<int>
4309  (
4310  mesh_,
4311  patch,
4312  edgeI,
4313  seedFaceI,
4314  faceInfo,
4315  tol,
4316  dummyTrackData
4317  )
4318  )
4319  {
4320  changedEdges.append(edgeI);
4321  changedInfo.append(edgeInfo);
4322  }
4323  }
4324  }
4325 
4326 
4327  if (returnReduceAnd(changedEdges.empty()))
4328  {
4329  break;
4330  }
4331 
4332 
4333 
4334  // Walk
4335  PatchEdgeFaceWave
4336  <
4338  patchFaceOrientation
4339  > calc
4340  (
4341  mesh_,
4342  patch,
4343  changedEdges,
4344  changedInfo,
4345  allEdgeInfo,
4346  allFaceInfo,
4347  returnReduce(patch.nEdges(), sumOp<label>())
4348  );
4349  }
4350 
4351 
4352  // Push master zone info over to slave (since slave faces never visited)
4353  {
4354  labelList neiStatus
4355  (
4356  mesh_.nBoundaryFaces(),
4358  );
4359 
4360  forAll(patch.addressing(), i)
4361  {
4362  const label meshFaceI = patch.addressing()[i];
4363  if (!mesh_.isInternalFace(meshFaceI))
4364  {
4365  neiStatus[meshFaceI-mesh_.nInternalFaces()] =
4366  allFaceInfo[i].flipStatus();
4367  }
4368  }
4369  syncTools::swapBoundaryFaceList(mesh_, neiStatus);
4370 
4371  forAll(patch.addressing(), i)
4372  {
4373  const label meshFaceI = patch.addressing()[i];
4374  const label patchI = bm.whichPatch(meshFaceI);
4375 
4376  if
4377  (
4378  patchI != -1
4379  && bm[patchI].coupled()
4380  && !isMasterFace.test(meshFaceI)
4381  )
4382  {
4383  // Slave side. Take flipped from neighbour
4384  label bFaceI = meshFaceI-mesh_.nInternalFaces();
4385 
4386  if (neiStatus[bFaceI] == orientedSurface::NOFLIP)
4387  {
4389  }
4390  else if (neiStatus[bFaceI] == orientedSurface::FLIP)
4391  {
4393  }
4394  else
4395  {
4397  << "Incorrect status for face " << meshFaceI
4398  << abort(FatalError);
4399  }
4400  }
4401  }
4402  }
4403 
4404 
4405  // Convert to meshFlipMap and adapt faceZones
4406 
4407  meshFlipMap.setSize(mesh_.nFaces());
4408  meshFlipMap = false;
4409 
4410  forAll(allFaceInfo, faceI)
4411  {
4412  label meshFaceI = patch.addressing()[faceI];
4413 
4414  if (allFaceInfo[faceI] == orientedSurface::NOFLIP)
4415  {
4416  meshFlipMap.unset(meshFaceI);
4417  }
4418  else if (allFaceInfo[faceI] == orientedSurface::FLIP)
4419  {
4420  meshFlipMap.set(meshFaceI);
4421  }
4422  else
4423  {
4425  << "Problem : unvisited face " << faceI
4426  << " centre:" << mesh_.faceCentres()[meshFaceI]
4427  << abort(FatalError);
4428  }
4429  }
4430 }
4431 
4432 
4433 void Foam::meshRefinement::zonify
4434 (
4435  // Get per face whether is it master (of a coupled set of faces)
4436  const bitSet& isMasterFace,
4437  const labelList& cellToZone,
4438  const labelList& neiCellZone,
4439  const labelList& faceToZone,
4440  const bitSet& meshFlipMap,
4441  polyTopoChange& meshMod
4442 ) const
4443 {
4444  const labelList& faceOwner = mesh_.faceOwner();
4445  const labelList& faceNeighbour = mesh_.faceNeighbour();
4446 
4447  for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
4448  {
4449  label faceZoneI = faceToZone[faceI];
4450 
4451  if (faceZoneI != -1)
4452  {
4453  // Orient face zone to have slave cells in min cell zone.
4454  // Note: logic to use flipMap should be consistent with logic
4455  // to pick up the freeStandingBaffleFaces!
4456 
4457  label ownZone = cellToZone[faceOwner[faceI]];
4458  label neiZone = cellToZone[faceNeighbour[faceI]];
4459 
4460  bool flip;
4461 
4462  if (ownZone == neiZone)
4463  {
4464  // free-standing face. Use geometrically derived orientation
4465  flip = meshFlipMap.test(faceI);
4466  }
4467  else
4468  {
4469  flip =
4470  (
4471  ownZone == -1
4472  || (neiZone != -1 && ownZone > neiZone)
4473  );
4474  }
4475 
4476  meshMod.setAction
4477  (
4478  polyModifyFace
4479  (
4480  mesh_.faces()[faceI], // modified face
4481  faceI, // label of face
4482  faceOwner[faceI], // owner
4483  faceNeighbour[faceI], // neighbour
4484  false, // face flip
4485  -1, // patch for face
4486  false, // remove from zone
4487  faceZoneI, // zone for face
4488  flip // face flip in zone
4489  )
4490  );
4491  }
4492  }
4493 
4494 
4495  const polyBoundaryMesh& patches = mesh_.boundaryMesh();
4496 
4497  // Set owner as no-flip
4498  forAll(patches, patchI)
4499  {
4500  const polyPatch& pp = patches[patchI];
4501 
4502  label faceI = pp.start();
4503 
4504  forAll(pp, i)
4505  {
4506  label faceZoneI = faceToZone[faceI];
4507 
4508  if (faceZoneI != -1)
4509  {
4510  label ownZone = cellToZone[faceOwner[faceI]];
4511  label neiZone = neiCellZone[faceI-mesh_.nInternalFaces()];
4512 
4513  bool flip;
4514 
4515  if (ownZone == neiZone)
4516  {
4517  // free-standing face. Use geometrically derived orientation
4518  flip = meshFlipMap.test(faceI);
4519  }
4520  else
4521  {
4522  flip =
4523  (
4524  ownZone == -1
4525  || (neiZone != -1 && ownZone > neiZone)
4526  );
4527  }
4528 
4529  meshMod.setAction
4530  (
4531  polyModifyFace
4532  (
4533  mesh_.faces()[faceI], // modified face
4534  faceI, // label of face
4535  faceOwner[faceI], // owner
4536  -1, // neighbour
4537  false, // face flip
4538  patchI, // patch for face
4539  false, // remove from zone
4540  faceZoneI, // zone for face
4541  flip // face flip in zone
4542  )
4543  );
4544  }
4545  faceI++;
4546  }
4547  }
4548 
4549 
4550  // Put the cells into the correct zone
4551  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4552 
4553  forAll(cellToZone, cellI)
4554  {
4555  label zoneI = cellToZone[cellI];
4556 
4557  if (zoneI >= 0)
4558  {
4559  meshMod.setAction
4560  (
4561  polyModifyCell
4562  (
4563  cellI,
4564  false, // removeFromZone
4565  zoneI
4566  )
4567  );
4568  }
4569  }
4570 }
4571 
4572 
4573 void Foam::meshRefinement::allocateInterRegionFaceZone
4574 (
4575  const label ownZone,
4576  const label neiZone,
4577  wordPairHashTable& zonesToFaceZone,
4578  LabelPairMap<word>& zoneIDsToFaceZone
4579 ) const
4580 {
4581  const cellZoneMesh& cellZones = mesh_.cellZones();
4582 
4583  if (ownZone != neiZone)
4584  {
4585  // Make sure lowest number cellZone is master. Non-cellZone
4586  // areas are slave
4587  const bool swap =
4588  (
4589  ownZone == -1
4590  || (neiZone != -1 && ownZone > neiZone)
4591  );
4592 
4593  // Quick check whether we already have pair of zones
4594  labelPair key(ownZone, neiZone);
4595  if (swap)
4596  {
4597  key.flip();
4598  }
4599 
4600  if (!zoneIDsToFaceZone.found(key))
4601  {
4602  // Not found. Allocate.
4603  const word ownZoneName =
4604  (
4605  ownZone != -1
4606  ? cellZones[ownZone].name()
4607  : "none"
4608  );
4609  const word neiZoneName =
4610  (
4611  neiZone != -1
4612  ? cellZones[neiZone].name()
4613  : "none"
4614  );
4615 
4616  // Get lowest zone first
4617  Pair<word> wordKey(ownZoneName, neiZoneName);
4618  if (swap)
4619  {
4620  wordKey.flip();
4621  }
4622 
4623  word fzName = wordKey.first() + "_to_" + wordKey.second();
4624 
4625  zoneIDsToFaceZone.insert(key, fzName);
4626  zonesToFaceZone.insert(wordKey, fzName);
4627  }
4628  }
4629 }
4630 
4631 
4632 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
4633 
4636  const bool doHandleSnapProblems,
4637  const snapParameters& snapParams,
4638  const bool useTopologicalSnapDetection,
4639  const bool removeEdgeConnectedCells,
4640  const scalarField& perpendicularAngle,
4641  const label nErodeCellZones,
4642  const dictionary& motionDict,
4643  Time& runTime,
4644  const labelList& globalToMasterPatch,
4645  const labelList& globalToSlavePatch,
4646 
4647  const pointField& locationsInMesh,
4648  const wordList& zonesInMesh,
4649  const pointField& locationsOutsideMesh,
4650  const bool exitIfLeakPath,
4651  const refPtr<coordSetWriter>& leakPathFormatter,
4652  refPtr<surfaceWriter>& surfFormatter
4653 )
4654 {
4655  // Introduce baffles
4656  // ~~~~~~~~~~~~~~~~~
4657 
4658  // Split the mesh along internal faces wherever there is a pierce between
4659  // two cell centres.
4660 
4661  Info<< "Introducing baffles for "
4662  << returnReduce(countHits(), sumOp<label>())
4663  << " faces that are intersected by the surface." << nl << endl;
4664 
4665  // Swap neighbouring cell centres and cell level
4666  labelList neiLevel(mesh_.nBoundaryFaces());
4667  pointField neiCc(mesh_.nBoundaryFaces());
4668  calcNeighbourData(neiLevel, neiCc);
4669 
4670  labelList ownPatch, neiPatch;
4671 
4672  refPtr<surfaceWriter> dummyWriter(nullptr);
4673  getBafflePatches
4674  (
4675  nErodeCellZones,
4676  globalToMasterPatch,
4677 
4678  locationsInMesh,
4679  zonesInMesh,
4680  locationsOutsideMesh,
4681  exitIfLeakPath,
4682  refPtr<coordSetWriter>(nullptr),
4683  dummyWriter,
4684 
4685  neiLevel,
4686  neiCc,
4687 
4688  ownPatch,
4689  neiPatch
4690  );
4691 
4692  createBaffles(ownPatch, neiPatch);
4693 
4694  if (debug)
4695  {
4696  // Debug:test all is still synced across proc patches
4697  checkData();
4698  }
4699 
4700  Info<< "Created baffles in = "
4701  << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
4702 
4703  printMeshInfo(debug, "After introducing baffles", true);
4704 
4705  if (debug&MESH)
4706  {
4707  const_cast<Time&>(mesh_.time())++;
4708  Pout<< "Writing baffled mesh to time " << timeName()
4709  << endl;
4710  write
4711  (
4712  debugType(debug),
4713  writeType(writeLevel() | WRITEMESH),
4714  runTime.path()/"baffles"
4715  );
4716  Pout<< "Dumped debug data in = "
4717  << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
4718  }
4719 
4720 
4721  // Introduce baffles to delete problem cells
4722  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4723 
4724  // Create some additional baffles where we want surface cells removed.
4725 
4726  if (doHandleSnapProblems)
4727  {
4728  handleSnapProblems
4729  (
4730  snapParams,
4731  useTopologicalSnapDetection,
4732  removeEdgeConnectedCells,
4733  perpendicularAngle,
4734  motionDict,
4735  runTime,
4736  globalToMasterPatch,
4737  globalToSlavePatch
4738  );
4739 
4740  // Removing additional cells might have created disconnected bits
4741  // so re-do the surface intersections
4742  {
4743  // Swap neighbouring cell centres and cell level
4744  neiLevel.setSize(mesh_.nBoundaryFaces());
4745  neiCc.setSize(mesh_.nBoundaryFaces());
4746  calcNeighbourData(neiLevel, neiCc);
4747 
4748  labelList ownPatch, neiPatch;
4749  refPtr<surfaceWriter> dummyWriter(nullptr);
4750  getBafflePatches
4751  (
4752  nErodeCellZones,
4753  globalToMasterPatch,
4754 
4755  locationsInMesh,
4756  zonesInMesh,
4757  locationsOutsideMesh,
4758  exitIfLeakPath,
4759  refPtr<coordSetWriter>(nullptr),
4760  dummyWriter,
4761 
4762  neiLevel,
4763  neiCc,
4764 
4765  ownPatch,
4766  neiPatch
4767  );
4768 
4769  createBaffles(ownPatch, neiPatch);
4770  }
4771 
4772  if (debug)
4773  {
4774  // Debug:test all is still synced across proc patches
4775  checkData();
4776  }
4777  }
4778 
4779 
4780  // Select part of mesh
4781  // ~~~~~~~~~~~~~~~~~~~
4782 
4783  Info<< nl
4784  << "Remove unreachable sections of mesh" << nl
4785  << "-----------------------------------" << nl
4786  << endl;
4787 
4788  if (debug)
4789  {
4790  ++runTime;
4791  }
4792 
4793  splitMeshRegions
4794  (
4795  globalToMasterPatch,
4796  globalToSlavePatch,
4797  locationsInMesh,
4798  locationsOutsideMesh,
4799  true, // Exit if any connection between inside and outside
4800  leakPathFormatter
4801  );
4802 
4803  if (debug)
4804  {
4805  // Debug:test all is still synced across proc patches
4806  checkData();
4807  }
4808  Info<< "Split mesh in = "
4809  << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
4810 
4811  printMeshInfo(debug, "After subsetting", true);
4812 
4813  if (debug&MESH)
4814  {
4815  ++runTime;
4816  Pout<< "Writing subsetted mesh to time " << timeName()
4817  << endl;
4818  write
4819  (
4820  debugType(debug),
4821  writeType(writeLevel() | WRITEMESH),
4822  runTime.path()/timeName()
4823  );
4824  Pout<< "Dumped debug data in = "
4825  << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
4826  }
4827 }
4828 
4829 
4832  const bool samePatch,
4833  const snapParameters& snapParams,
4834  const bool useTopologicalSnapDetection,
4835  const bool removeEdgeConnectedCells,
4836  const scalarField& perpendicularAngle,
4837  const scalar planarAngle,
4838  const dictionary& motionDict,
4839  Time& runTime,
4840  const labelList& globalToMasterPatch,
4841  const labelList& globalToSlavePatch,
4842  const pointField& locationsInMesh,
4843  const pointField& locationsOutsideMesh
4844 )
4845 {
4846  // Merge baffles
4847  // ~~~~~~~~~~~~~
4848 
4849  Info<< nl
4850  << "Merge free-standing baffles" << nl
4851  << "---------------------------" << nl
4852  << endl;
4853 
4854 
4855  // List of pairs of freestanding baffle faces.
4856  List<labelPair> couples
4857  (
4858  freeStandingBaffles // filter out freestanding baffles
4859  (
4861  planarAngle,
4862  samePatch
4863  )
4864  );
4865 
4866  const label nCouples = returnReduce(couples.size(), sumOp<label>());
4867 
4868  Info<< "Detected free-standing baffles : " << nCouples << endl;
4869 
4870  if (nCouples > 0)
4871  {
4872  // Actually merge baffles. Note: not exactly parallellized. Should
4873  // convert baffle faces into processor faces if they resulted
4874  // from them.
4875  mergeBaffles(couples, Map<label>(0));
4876 
4877  // Detect any problem cells resulting from merging of baffles
4878  // and delete them
4879  handleSnapProblems
4880  (
4881  snapParams,
4882  useTopologicalSnapDetection,
4883  removeEdgeConnectedCells,
4884  perpendicularAngle,
4885  motionDict,
4886  runTime,
4887  globalToMasterPatch,
4888  globalToSlavePatch
4889  );
4890 
4891  // Very occasionally removing a problem cell might create a disconnected
4892  // region so re-check
4893 
4894  Info<< nl
4895  << "Remove unreachable sections of mesh" << nl
4896  << "-----------------------------------" << nl
4897  << endl;
4898 
4899  if (debug)
4900  {
4901  ++runTime;
4902  }
4903 
4904  splitMeshRegions
4905  (
4906  globalToMasterPatch,
4907  globalToSlavePatch,
4908  locationsInMesh,
4909  locationsOutsideMesh,
4910  true, // Exit if any connection between inside and outside
4911  refPtr<coordSetWriter>(nullptr) // leakPathFormatter
4912  );
4913 
4914 
4915  if (debug)
4916  {
4917  // Debug:test all is still synced across proc patches
4918  checkData();
4919  }
4920  }
4921  Info<< "Merged free-standing baffles in = "
4922  << runTime.cpuTimeIncrement() << " s\n" << nl << endl;
4923 }
4924 
4925 
4926 Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh
4928  const label nBufferLayers,
4929  const label nErodeCellZones,
4930  const labelList& globalToMasterPatch,
4931  const labelList& globalToSlavePatch,
4932 
4933  const pointField& locationsInMesh,
4934  const wordList& zonesInMesh,
4935  const pointField& locationsOutsideMesh,
4936  const bool exitIfLeakPath,
4937  const refPtr<coordSetWriter>& leakPathFormatter,
4938  refPtr<surfaceWriter>& surfFormatter
4939 )
4940 {
4941  // Determine patches to put intersections into
4942  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4943 
4944 
4945  // Swap neighbouring cell centres and cell level
4946  labelList neiLevel(mesh_.nBoundaryFaces());
4947  pointField neiCc(mesh_.nBoundaryFaces());
4948  calcNeighbourData(neiLevel, neiCc);
4949 
4950  // Find intersections with all unnamed(!) surfaces
4951  labelList ownPatch, neiPatch;
4952  getBafflePatches
4953  (
4954  nErodeCellZones,
4955  globalToMasterPatch,
4956 
4957  locationsInMesh,
4958  zonesInMesh,
4959  locationsOutsideMesh,
4960  exitIfLeakPath,
4961  leakPathFormatter,
4962  surfFormatter,
4963 
4964  neiLevel,
4965  neiCc,
4966 
4967  ownPatch,
4968  neiPatch
4969  );
4970 
4971  // Analyse regions. Reuse regionsplit
4972  boolList blockedFace(mesh_.nFaces(), false);
4973 
4974  forAll(ownPatch, faceI)
4975  {
4976  if (ownPatch[faceI] != -1 || neiPatch[faceI] != -1)
4977  {
4978  blockedFace[faceI] = true;
4979  }
4980  }
4981  syncTools::syncFaceList(mesh_, blockedFace, orEqOp<bool>());
4982 
4983 
4984  regionSplit cellRegion(mesh_, blockedFace);
4985 
4986  // Set unreachable cells to -1
4987  findRegions
4988  (
4989  mesh_,
4990  vector::uniform(mergeDistance_), // perturbVec
4991  locationsInMesh,
4992  locationsOutsideMesh,
4993  cellRegion.nRegions(),
4994  cellRegion,
4995  blockedFace,
4996  // Leak-path
4997  false, // do not exit if outside location found
4998  leakPathFormatter
4999  );
5000 
5001  return splitMesh
5002  (
5003  nBufferLayers,
5004  globalToMasterPatch,
5005  globalToSlavePatch,
5006  cellRegion,
5007  ownPatch,
5008  neiPatch
5009  );
5010 }
5011 
5012 
5013 Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh
5014 (
5015  const label nBufferLayers,
5016  const labelList& globalToMasterPatch,
5017  const labelList& globalToSlavePatch,
5018 
5019  labelList& cellRegion,
5020  labelList& ownPatch,
5021  labelList& neiPatch
5022 )
5023 {
5024  // Walk out nBufferlayers from region boundary
5025  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5026  // (modifies cellRegion, ownPatch)
5027  // Takes over face patch onto points and then back to faces and cells
5028  // (so cell-face-point walk)
5029 
5030  const labelList& faceOwner = mesh_.faceOwner();
5031  const labelList& faceNeighbour = mesh_.faceNeighbour();
5032 
5033 
5034  // Checks
5035  for (label facei = 0; facei < mesh_.nInternalFaces(); facei++)
5036  {
5037  if (ownPatch[facei] == -1 && neiPatch[facei] != -1)
5038  {
5039  FatalErrorInFunction << "Problem in face:" << facei
5040  << " at:" << mesh_.faceCentres()[facei]
5041  << " ownPatch:" << ownPatch[facei]
5042  << " neiPatch:" << neiPatch[facei]
5043  << exit(FatalError);
5044  }
5045  else
5046  {
5047  // Check if cellRegion indeed limited by patch
5048  const label ownRegion = cellRegion[faceOwner[facei]];
5049  const label neiRegion = cellRegion[faceNeighbour[facei]];
5050  if (ownRegion != neiRegion)
5051  {
5052  if (ownPatch[facei] == -1)
5053  {
5054  FatalErrorInFunction << "Problem in face:" << facei
5055  << " at:" << mesh_.faceCentres()[facei]
5056  << " ownPatch:" << ownPatch[facei]
5057  << " ownRegion:" << ownRegion
5058  << " neiPatch:" << neiPatch[facei]
5059  << " neiRegion:" << neiRegion
5060  << exit(FatalError);
5061  }
5062  }
5063  }
5064  }
5065 
5066  // Determine on original data the nearest face. This is used as a fall-back
5067  // to set the patch if the nBufferLayers walking didn't work.
5068  labelList nearestOwnPatch;
5069  if (nBufferLayers)
5070  {
5071  DynamicList<label> startFaces;
5072  forAll(ownPatch, facei)
5073  {
5074  if (ownPatch[facei] != -1)
5075  {
5076  startFaces.append(facei);
5077  }
5078  }
5079 
5080  // Per face the index to the start face.
5081  labelList faceToStart;
5082  autoPtr<mapDistribute> mapPtr;
5083  nearestFace
5084  (
5085  startFaces,
5086  bitSet(mesh_.nFaces()), // no blocked faces
5087  mapPtr,
5088  faceToStart,
5089  nBufferLayers+4 // bit more than nBufferLayers since
5090  // walking face-cell-face
5091  );
5092 
5093  // Use map to push ownPatch to all reached faces
5094  labelList startOwnPatch(ownPatch, startFaces);
5095  mapPtr().distribute(startOwnPatch);
5096 
5097  nearestOwnPatch.setSize(mesh_.nFaces());
5098  nearestOwnPatch = -1;
5099  forAll(faceToStart, facei)
5100  {
5101  const label sloti = faceToStart[facei];
5102  if (sloti != -1)
5103  {
5104  nearestOwnPatch[facei] = startOwnPatch[sloti];
5105  }
5106  }
5107  }
5108 
5109  // Leak closure:
5110  // ~~~~~~~~~~~~~
5111  // We do not want to add buffer layers on the frozen points/faces
5112  // since these are the exact faces needed to close a hole (to an
5113  // locationOutsideMesh). Adding even
5114  // a single layer of cells would mean that in further manipulation there
5115  // is now no path to the locationOutsideMesh so the layer closure does
5116  // not get triggered and we keep the added 1 layer of cells on the
5117  // closure faces.
5118  bitSet isFrozenPoint(mesh_.nPoints());
5119  bitSet isFrozenFace(mesh_.nFaces());
5120 
5121  if (nBufferLayers)
5122  {
5123  const labelListList& pointFaces = mesh_.pointFaces();
5124  const pointZoneMesh& pzs = mesh_.pointZones();
5125  const label pointZonei = pzs.findZoneID("frozenPoints");
5126  if (pointZonei != -1)
5127  {
5128  const pointZone& pz = pzs[pointZonei];
5129  isFrozenPoint.set(pz);
5130  for (const label pointi : pz)
5131  {
5132  isFrozenFace.set(pointFaces[pointi]);
5133  }
5134  }
5135 
5136  const faceZoneMesh& fzs = mesh_.faceZones();
5137  const label faceZonei = fzs.findZoneID("frozenFaces");
5138  if (faceZonei != -1)
5139  {
5140  const faceZone& fz = fzs[faceZonei];
5141  isFrozenFace.set(fz);
5142  for (const label facei : fz)
5143  {
5144  isFrozenPoint.set(mesh_.faces()[facei]);
5145  }
5146  }
5147  }
5148 
5149  // Patch for exposed faces for lack of anything sensible.
5150  label defaultPatch = 0;
5151  if (globalToMasterPatch.size())
5152  {
5153  defaultPatch = globalToMasterPatch[0];
5154  }
5155 
5156  for (label i = 0; i < nBufferLayers; i++)
5157  {
5158  // 1. From cells (via faces) to points
5159 
5160  labelList pointBaffle(mesh_.nPoints(), -1);
5161 
5162  forAll(faceNeighbour, facei)
5163  {
5164  if (!isFrozenFace[facei])
5165  {
5166  const face& f = mesh_.faces()[facei];
5167 
5168  const label ownRegion = cellRegion[faceOwner[facei]];
5169  const label neiRegion = cellRegion[faceNeighbour[facei]];
5170 
5171  if (ownRegion == -1 && neiRegion != -1)
5172  {
5173  // Note max(..) since possibly regionSplit might have split
5174  // off extra unreachable parts of mesh. Note: or can this
5175  // only happen for boundary faces?
5176  forAll(f, fp)
5177  {
5178  if (!isFrozenPoint[f[fp]])
5179  {
5180  pointBaffle[f[fp]] =
5181  max(defaultPatch, ownPatch[facei]);
5182  }
5183  }
5184  }
5185  else if (ownRegion != -1 && neiRegion == -1)
5186  {
5187  label newPatchi = neiPatch[facei];
5188  if (newPatchi == -1)
5189  {
5190  newPatchi = max(defaultPatch, ownPatch[facei]);
5191  }
5192  forAll(f, fp)
5193  {
5194  if (!isFrozenPoint[f[fp]])
5195  {
5196  pointBaffle[f[fp]] = newPatchi;
5197  }
5198  }
5199  }
5200  }
5201  }
5202 
5203  labelList neiCellRegion;
5204  syncTools::swapBoundaryCellList(mesh_, cellRegion, neiCellRegion);
5205 
5206  //for
5207  //(
5208  // label facei = mesh_.nInternalFaces();
5209  // facei < mesh_.nFaces();
5210  // facei++
5211  //)
5212  //{
5213  // if (!isFrozenFace[facei])
5214  // {
5215  // const face& f = mesh_.faces()[facei];
5216  //
5217  // const label ownRegion = cellRegion[faceOwner[facei]];
5218  // const label neiRegion =
5219  // neiCellRegion[facei-mesh_.nInternalFaces()];
5220  //
5221  // if (ownRegion == -1 && neiRegion != -1)
5222  // {
5223  // forAll(f, fp)
5224  // {
5225  // if (!isFrozenPoint[f[fp]])
5226  // {
5227  // pointBaffle[f[fp]] =
5228  // max(defaultPatch, ownPatch[facei]);
5229  // }
5230  // }
5231  // }
5232  // }
5233  //}
5234 
5235  const auto& patches = mesh_.boundaryMesh();
5236  for (const auto& pp : patches)
5237  {
5238  if (pp.coupled())
5239  {
5240  // Note: swapBoundaryCellList only works on cyclic&processor.
5241  // Does not handle e.g. cyclicAMI. TBD?
5242  // Note: we could check check our side being in the set
5243  // since syncPointList below will push over any decision
5244  // made by the other side.
5245  forAll(pp, i)
5246  {
5247  const label facei = pp.start()+i;
5248  if (!isFrozenFace[facei])
5249  {
5250  const face& f = mesh_.faces()[facei];
5251  const label ownRegion = cellRegion[faceOwner[facei]];
5252  const label neiRegion =
5253  neiCellRegion[facei-mesh_.nInternalFaces()];
5254 
5255  // Same logic as for internal faces
5256 
5257  if (ownRegion == -1 && neiRegion != -1)
5258  {
5259  forAll(f, fp)
5260  {
5261  if (!isFrozenPoint[f[fp]])
5262  {
5263  pointBaffle[f[fp]] =
5264  max(defaultPatch, ownPatch[facei]);
5265  }
5266  }
5267  }
5268  else if (ownRegion != -1 && neiRegion == -1)
5269  {
5270  label newPatchI = neiPatch[facei];
5271  if (newPatchI == -1)
5272  {
5273  newPatchI = max(defaultPatch, ownPatch[facei]);
5274  }
5275  forAll(f, fp)
5276  {
5277  if (!isFrozenPoint[f[fp]])
5278  {
5279  pointBaffle[f[fp]] = newPatchI;
5280  }
5281  }
5282  }
5283  }
5284  }
5285  }
5286  else
5287  {
5288  forAll(pp, i)
5289  {
5290  const label facei = pp.start()+i;
5291  if (!isFrozenFace[facei])
5292  {
5293  const face& f = mesh_.faces()[facei];
5294  const label ownRegion = cellRegion[faceOwner[facei]];
5295 
5296  if (ownRegion != -1)
5297  {
5298  forAll(f, fp)
5299  {
5300  if (!isFrozenPoint[f[fp]])
5301  {
5302  pointBaffle[f[fp]] =
5303  max(defaultPatch, ownPatch[facei]);
5304  }
5305  }
5306  }
5307  }
5308  }
5309  }
5310  }
5311 
5312 
5313  // Sync
5315  (
5316  mesh_,
5317  pointBaffle,
5318  maxEqOp<label>(),
5319  label(-1) // null value
5320  );
5321 
5322 
5323  // 2. From points back to faces
5324 
5325  const labelListList& pointFaces = mesh_.pointFaces();
5326 
5327  forAll(pointFaces, pointi)
5328  {
5329  if (pointBaffle[pointi] != -1)
5330  {
5331  const labelList& pFaces = pointFaces[pointi];
5332 
5333  forAll(pFaces, pFacei)
5334  {
5335  const label facei = pFaces[pFacei];
5336 
5337  if (!isFrozenFace[facei] && ownPatch[facei] == -1)
5338  {
5339  ownPatch[facei] = pointBaffle[pointi];
5340  }
5341  }
5342  }
5343  }
5344  syncTools::syncFaceList(mesh_, ownPatch, maxEqOp<label>());
5345 
5346 
5347  // 3. From faces to cells (cellRegion) and back to faces (ownPatch)
5348 
5349  labelList newOwnPatch(ownPatch);
5350 
5351  forAll(ownPatch, facei)
5352  {
5353  if (!isFrozenFace[facei] && ownPatch[facei] != -1)
5354  {
5355  const label own = faceOwner[facei];
5356 
5357  if (cellRegion[own] == -1)
5358  {
5359  cellRegion[own] = labelMax;
5360 
5361  const cell& ownFaces = mesh_.cells()[own];
5362  forAll(ownFaces, j)
5363  {
5364  const label ownFacei = ownFaces[j];
5365  if (!isFrozenFace[ownFacei] && ownPatch[ownFacei] == -1)
5366  {
5367  newOwnPatch[ownFacei] = ownPatch[facei];
5368  }
5369  }
5370  }
5371  if (mesh_.isInternalFace(facei))
5372  {
5373  const label nei = faceNeighbour[facei];
5374 
5375  if (cellRegion[nei] == -1)
5376  {
5377  cellRegion[nei] = labelMax;
5378 
5379  const cell& neiFaces = mesh_.cells()[nei];
5380  forAll(neiFaces, j)
5381  {
5382  const label neiFacei = neiFaces[j];
5383  const bool isFrozen = isFrozenFace[neiFacei];
5384  if (!isFrozen && ownPatch[neiFacei] == -1)
5385  {
5386  newOwnPatch[neiFacei] = ownPatch[facei];
5387  }
5388  }
5389  }
5390  }
5391  }
5392  }
5393 
5394  ownPatch.transfer(newOwnPatch);
5395 
5396  syncTools::syncFaceList(mesh_, ownPatch, maxEqOp<label>());
5397  }
5398 
5399 
5400  // Subset
5401  // ~~~~~~
5402 
5403  // Get cells to remove
5404  DynamicList<label> cellsToRemove(mesh_.nCells());
5405  forAll(cellRegion, celli)
5406  {
5407  if (cellRegion[celli] == -1)
5408  {
5409  cellsToRemove.append(celli);
5410  }
5411  }
5412  cellsToRemove.shrink();
5413 
5414  const label nCellsToKeep = returnReduce
5415  (
5416  mesh_.nCells() - cellsToRemove.size(),
5417  sumOp<label>()
5418  );
5419 
5420  Info<< "Keeping all cells containing inside points" << endl
5421  << "Selected for keeping : " << nCellsToKeep << " cells." << endl;
5422 
5423 
5424  // Remove cells
5425  removeCells cellRemover(mesh_);
5426 
5427  // Pick up patches for exposed faces
5428  const labelList exposedFaces(cellRemover.getExposedFaces(cellsToRemove));
5429  labelList exposedPatches(exposedFaces.size());
5430 
5431  label nUnpatched = 0;
5432 
5433  forAll(exposedFaces, i)
5434  {
5435  label facei = exposedFaces[i];
5436 
5437  if (ownPatch[facei] != -1)
5438  {
5439  exposedPatches[i] = ownPatch[facei];
5440  }
5441  else
5442  {
5443  const label fallbackPatch =
5444  (
5445  nearestOwnPatch.size()
5446  ? nearestOwnPatch[facei]
5447  : defaultPatch
5448  );
5449  if (nUnpatched == 0)
5450  {
5452  << "For exposed face " << facei
5453  << " fc:" << mesh_.faceCentres()[facei]
5454  << " found no patch." << endl
5455  << " Taking patch " << fallbackPatch
5456  << " instead. Suppressing future warnings" << endl;
5457  }
5458  nUnpatched++;
5459 
5460  exposedPatches[i] = fallbackPatch;
5461  }
5462  }
5463 
5464  reduce(nUnpatched, sumOp<label>());
5465  if (nUnpatched > 0)
5466  {
5467  Info<< "Detected " << nUnpatched << " faces out of "
5468  << returnReduce(exposedFaces.size(), sumOp<label>())
5469  << " for which the default patch " << defaultPatch
5470  << " will be used" << endl;
5471  }
5472 
5473  return doRemoveCells
5474  (
5475  cellsToRemove,
5476  exposedFaces,
5477  exposedPatches,
5478  cellRemover
5479  );
5480 }
5481 
5482 
5485  const label nBufferLayers,
5486  const label nErodeCellZones,
5487  const labelList& globalToMasterPatch,
5488  const labelList& globalToSlavePatch,
5489  const pointField& locationsInMesh,
5490  const wordList& zonesInMesh,
5491  const pointField& locationsOutsideMesh
5492 )
5493 {
5494  // Determine patches to put intersections into
5495  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5496 
5497  // Swap neighbouring cell centres and cell level
5498  labelList neiLevel(mesh_.nBoundaryFaces());
5499  pointField neiCc(mesh_.nBoundaryFaces());
5500  calcNeighbourData(neiLevel, neiCc);
5501 
5502  // Find intersections with all unnamed(!) surfaces
5503  labelList ownPatch, neiPatch;
5504  refPtr<surfaceWriter> dummyWriter(nullptr);
5505  getBafflePatches
5506  (
5507  nErodeCellZones,
5508  globalToMasterPatch,
5509 
5510  locationsInMesh,
5511  zonesInMesh,
5512  locationsOutsideMesh,
5513  false, // do not exit. Use leak-closure instead.
5514  refPtr<coordSetWriter>(nullptr),
5515  dummyWriter,
5516 
5517  neiLevel,
5518  neiCc,
5519 
5520  ownPatch,
5521  neiPatch
5522  );
5523 
5524 
5525  labelList cellRegion(mesh_.nCells(), Zero);
5526  // Find any cells inside a limit shell with minLevel -1
5527  labelList levelShell;
5528  limitShells_.findLevel
5529  (
5530  mesh_.cellCentres(),
5531  labelList(mesh_.nCells(), -1), // pick up only shells with -1
5532  levelShell
5533  );
5534  forAll(levelShell, celli)
5535  {
5536  if (levelShell[celli] != -1)
5537  {
5538  // Mark cell region so it gets deleted
5539  cellRegion[celli] = -1;
5540  }
5541  }
5542 
5543  autoPtr<mapPolyMesh> mapPtr = splitMesh
5544  (
5545  nBufferLayers,
5546  globalToMasterPatch,
5547  globalToSlavePatch,
5548  cellRegion,
5549  ownPatch,
5550  neiPatch
5551  );
5552 
5554  {
5555  const_cast<Time&>(mesh_.time())++;
5556  Pout<< "Writing mesh after removing limitShells"
5557  << " to time " << timeName() << endl;
5558  write
5559  (
5560  debugType(debug),
5561  writeType
5562  (
5563  writeLevel()
5564  | WRITEMESH
5565  ),
5566  mesh_.time().path()/timeName()
5567  );
5568  }
5569  return mapPtr;
5570 }
5571 
5572 
5576 )
5577 {
5578  // Topochange container
5579  polyTopoChange meshMod(mesh_);
5580 
5581  label nNonManifPoints = returnReduce
5582  (
5583  regionSide.meshPointMap().size(),
5584  sumOp<label>()
5585  );
5586 
5587  Info<< "dupNonManifoldPoints : Found : " << nNonManifPoints
5588  << " non-manifold points (out of "
5589  << mesh_.globalData().nTotalPoints()
5590  << ')' << endl;
5591 
5592 
5593  autoPtr<mapPolyMesh> mapPtr;
5594 
5595  if (nNonManifPoints)
5596  {
5597  // Topo change engine
5598  duplicatePoints pointDuplicator(mesh_);
5599 
5600  // Insert changes into meshMod
5601  pointDuplicator.setRefinement(regionSide, meshMod);
5602 
5603  // Remove any unnecessary fields
5604  mesh_.clearOut();
5605  mesh_.moving(false);
5606 
5607  // Change the mesh (no inflation, parallel sync)
5608  mapPtr = meshMod.changeMesh(mesh_, false, true);
5609  mapPolyMesh& map = *mapPtr;
5610 
5611  // Update fields
5612  mesh_.updateMesh(map);
5613 
5614  // Move mesh if in inflation mode
5615  if (map.hasMotionPoints())
5616  {
5617  mesh_.movePoints(map.preMotionPoints());
5618  }
5619  else
5620  {
5621  // Delete mesh volumes.
5622  mesh_.clearOut();
5623  }
5624 
5625  // Reset the instance for if in overwrite mode
5626  mesh_.setInstance(timeName());
5627 
5628  // Update intersections. Is mapping only (no faces created, positions
5629  // stay same) so no need to recalculate intersections.
5630  updateMesh(map, labelList());
5631  }
5632 
5633  return mapPtr;
5634 }
5635 
5636 
5638 {
5639  // Analyse which points need to be duplicated
5641 
5642  return dupNonManifoldPoints(regionSide);
5643 }
5644 
5645 
5648  const labelList& pointToDuplicate
5649 )
5650 {
5651  label nPointPairs = 0;
5652  forAll(pointToDuplicate, pointI)
5653  {
5654  label otherPointI = pointToDuplicate[pointI];
5655  if (otherPointI != -1)
5656  {
5657  nPointPairs++;
5658  }
5659  }
5660 
5661  autoPtr<mapPolyMesh> mapPtr;
5662 
5663  if (returnReduceOr(nPointPairs))
5664  {
5665  Map<label> pointToMaster(2*nPointPairs);
5666  forAll(pointToDuplicate, pointI)
5667  {
5668  label otherPointI = pointToDuplicate[pointI];
5669  if (otherPointI != -1)
5670  {
5671  // Slave point
5672  pointToMaster.insert(pointI, otherPointI);
5673  }
5674  }
5675 
5676  // Topochange container
5677  polyTopoChange meshMod(mesh_);
5678 
5679  // Insert changes
5680  polyMeshAdder::mergePoints(mesh_, pointToMaster, meshMod);
5681 
5682  // Remove any unnecessary fields
5683  mesh_.clearOut();
5684  mesh_.moving(false);
5685 
5686  // Change the mesh (no inflation, parallel sync)
5687  mapPtr = meshMod.changeMesh(mesh_, false, true);
5688  mapPolyMesh& map = *mapPtr;
5689 
5690  // Update fields
5691  mesh_.updateMesh(map);
5692 
5693  // Move mesh if in inflation mode
5694  if (map.hasMotionPoints())
5695  {
5696  mesh_.movePoints(map.preMotionPoints());
5697  }
5698  else
5699  {
5700  // Delete mesh volumes.
5701  mesh_.clearOut();
5702  }
5703 
5704  // Reset the instance for if in overwrite mode
5705  mesh_.setInstance(timeName());
5706 
5707  // Update intersections. Is mapping only (no faces created, positions
5708  // stay same) so no need to recalculate intersections.
5709  updateMesh(map, labelList());
5710  }
5711 
5712  return mapPtr;
5713 }
5714 
5715 
5716 // Duplicate points on 'boundary' zones. Do not duplicate points on
5717 // 'internal' or 'baffle' zone. Whether points are on normal patches does
5718 // not matter
5721 {
5722  const labelList boundaryFaceZones
5723  (
5724  getZones
5725  (
5727  (
5728  1,
5730  )
5731  )
5732  );
5733  labelList internalOrBaffleFaceZones;
5734  {
5736  fzTypes[0] = surfaceZonesInfo::INTERNAL;
5737  fzTypes[1] = surfaceZonesInfo::BAFFLE;
5738  internalOrBaffleFaceZones = getZones(fzTypes);
5739  }
5740 
5741 
5742 
5743  // 0 : point used by normal, unzoned boundary faces
5744  // 1 : point used by 'boundary' zone
5745  // 2 : point used by internal/baffle zone
5746  PackedList<2> pointStatus(mesh_.nPoints(), 0u);
5747 
5748  forAll(boundaryFaceZones, j)
5749  {
5750  const faceZone& fZone = mesh_.faceZones()[boundaryFaceZones[j]];
5751  forAll(fZone, i)
5752  {
5753  const face& f = mesh_.faces()[fZone[i]];
5754  forAll(f, fp)
5755  {
5756  pointStatus[f[fp]] = max(pointStatus[f[fp]], 1u);
5757  }
5758  }
5759  }
5760  forAll(internalOrBaffleFaceZones, j)
5761  {
5762  const faceZone& fZone = mesh_.faceZones()[internalOrBaffleFaceZones[j]];
5763  forAll(fZone, i)
5764  {
5765  const face& f = mesh_.faces()[fZone[i]];
5766  forAll(f, fp)
5767  {
5768  pointStatus[f[fp]] = max(pointStatus[f[fp]], 2u);
5769  }
5770  }
5771  }
5772 
5774  (
5775  mesh_,
5776  pointStatus,
5777  maxEqOp<unsigned int>(), // combine op
5778  0u // null value
5779  );
5780 
5781  // Pick up points on boundary zones that are not on internal/baffle zones
5782  label n = 0;
5783  forAll(pointStatus, pointI)
5784  {
5785  if (pointStatus[pointI] == 1u)
5786  {
5787  n++;
5788  }
5789  }
5790 
5791  label globalNPoints = returnReduce(n, sumOp<label>());
5792  Info<< "Duplicating " << globalNPoints << " points on"
5793  << " faceZones of type "
5795  << endl;
5796 
5798 
5799  if (globalNPoints)
5800  {
5801  labelList candidatePoints(n);
5802  n = 0;
5803  forAll(pointStatus, pointI)
5804  {
5805  if (pointStatus[pointI] == 1u)
5806  {
5807  candidatePoints[n++] = pointI;
5808  }
5809  }
5810  localPointRegion regionSide(mesh_, candidatePoints);
5811  map = dupNonManifoldPoints(regionSide);
5812  }
5813  return map;
5814 }
5815 
5816 
5817 // Zoning
5818 Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
5820  const bool allowFreeStandingZoneFaces,
5821  const label nErodeCellZones,
5822  const pointField& locationsInMesh,
5823  const wordList& zonesInMesh,
5824  const pointField& locationsOutsideMesh,
5825  const bool exitIfLeakPath,
5826  const refPtr<coordSetWriter>& leakPathFormatter,
5827  refPtr<surfaceWriter>& surfFormatter,
5828  wordPairHashTable& zonesToFaceZone
5829 )
5830 {
5831  if (locationsInMesh.size() != zonesInMesh.size())
5832  {
5833  FatalErrorInFunction << "problem" << abort(FatalError);
5834  }
5835 
5836  const polyBoundaryMesh& patches = mesh_.boundaryMesh();
5837  const PtrList<surfaceZonesInfo>& surfZones = surfaces_.surfZones();
5838 
5839 
5840  // Swap neighbouring cell centres and cell level
5841  labelList neiLevel(mesh_.nBoundaryFaces());
5842  pointField neiCc(mesh_.nBoundaryFaces());
5843  calcNeighbourData(neiLevel, neiCc);
5844 
5845 
5846  // Add any faceZones, cellZones originating from surface to the mesh
5847  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5848  labelList surfaceToCellZone;
5849  labelListList surfaceToFaceZones;
5850 
5851  labelList namedSurfaces(surfaceZonesInfo::getNamedSurfaces(surfZones));
5852  if (namedSurfaces.size())
5853  {
5854  Info<< "Setting cellZones according to named surfaces:" << endl;
5855  forAll(namedSurfaces, i)
5856  {
5857  label surfI = namedSurfaces[i];
5858 
5859  Info<< "Surface : " << surfaces_.names()[surfI] << nl
5860  << " faceZones : " << surfZones[surfI].faceZoneNames() << nl
5861  << " cellZone : " << surfZones[surfI].cellZoneName()
5862  << endl;
5863  }
5864  Info<< endl;
5865 
5866  // Add zones to mesh
5867  surfaceToCellZone = surfaceZonesInfo::addCellZonesToMesh
5868  (
5869  surfZones,
5870  namedSurfaces,
5871  mesh_
5872  );
5873  surfaceToFaceZones = surfaceZonesInfo::addFaceZonesToMesh
5874  (
5875  surfZones,
5876  namedSurfaces,
5877  mesh_
5878  );
5879  }
5880 
5881 
5882 
5883  // Put the cells into the correct zone
5884  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5885 
5886  // Zone per cell:
5887  // -2 : unset : not allowed!
5888  // -1 : not in any zone (zone 'none')
5889  // >=0: zoneID
5890  // namedSurfaceRegion: zero sized or:
5891  // -1 : face not intersecting a named surface
5892  // >=0 : index of named surface
5893  labelList cellToZone;
5894  labelList namedSurfaceRegion;
5895  bitSet posOrientation;
5896  {
5897  labelList unnamedRegion1;
5898  labelList unnamedRegion2;
5899 
5900  zonify
5901  (
5902  allowFreeStandingZoneFaces,
5903  nErodeCellZones,// Use erosion (>0) or regionSplit to clean up
5904  -1, // Set all cells with cellToZone -2 to -1
5905  locationsInMesh,
5906  zonesInMesh,
5907  locationsOutsideMesh,
5908  exitIfLeakPath,
5909  leakPathFormatter,
5910  surfFormatter,
5911 
5912  cellToZone,
5913  unnamedRegion1,
5914  unnamedRegion2,
5915  namedSurfaceRegion,
5916  posOrientation
5917  );
5918  }
5919 
5920 
5921  // Convert namedSurfaceRegion (index of named surfaces) to
5922  // actual faceZone index
5923 
5924  //- Per face index of faceZone or -1
5925  labelList faceToZone(mesh_.nFaces(), -1);
5926 
5927  forAll(namedSurfaceRegion, faceI)
5928  {
5929  //label surfI = namedSurfaceIndex[faceI];
5930  label globalI = namedSurfaceRegion[faceI];
5931  if (globalI != -1)
5932  {
5933  const labelPair spr = surfaces_.whichSurface(globalI);
5934  faceToZone[faceI] = surfaceToFaceZones[spr.first()][spr.second()];
5935  }
5936  }
5937 
5938 
5939 
5940  // Allocate and assign faceZones from cellZones
5941  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5942 
5943  {
5944  // 1. Detect inter-region face and allocate names
5945 
5946  LabelPairMap<word> zoneIDsToFaceZone;
5947 
5948  for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
5949  {
5950  if (faceToZone[faceI] == -1) // Not named surface
5951  {
5952  // Face not yet in a faceZone. (it might already have been
5953  // done so by a 'named' surface). Check if inbetween different
5954  // cellZones
5955  allocateInterRegionFaceZone
5956  (
5957  cellToZone[mesh_.faceOwner()[faceI]],
5958  cellToZone[mesh_.faceNeighbour()[faceI]],
5959  zonesToFaceZone,
5960  zoneIDsToFaceZone
5961  );
5962  }
5963  }
5964 
5965  labelList neiCellZone;
5966  syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone);
5967 
5968  forAll(neiCellZone, bFaceI)
5969  {
5970  label faceI = bFaceI + mesh_.nInternalFaces();
5971  if (faceToZone[faceI] == -1)
5972  {
5973  allocateInterRegionFaceZone
5974  (
5975  cellToZone[mesh_.faceOwner()[faceI]],
5976  neiCellZone[bFaceI],
5977  zonesToFaceZone,
5978  zoneIDsToFaceZone
5979  );
5980  }
5981  }
5982 
5983 
5984  // 2.Combine faceZoneNames allocated on different processors
5985 
5986  Pstream::mapCombineReduce(zonesToFaceZone, eqOp<word>());
5987 
5988 
5989  // 3. Allocate faceZones from (now synchronised) faceZoneNames
5990  // Note: the faceZoneNames contain the same data but in different
5991  // order. We could sort the contents but instead just loop
5992  // in sortedToc order.
5993 
5994  Info<< "Setting faceZones according to neighbouring cellZones:"
5995  << endl;
5996 
5997  // From cellZone indices to faceZone index
5998  LabelPairMap<label> fZoneLookup(zonesToFaceZone.size());
5999 
6000  const cellZoneMesh& cellZones = mesh_.cellZones();
6001 
6002  {
6003  List<Pair<word>> czs(zonesToFaceZone.sortedToc());
6004 
6005  forAll(czs, i)
6006  {
6007  const Pair<word>& cz = czs[i];
6008  const word& fzName = zonesToFaceZone[cz];
6009 
6010  Info<< indent<< "cellZones : "
6011  << cz[0] << ' ' << cz[1] << nl
6012  << " faceZone : " << fzName << endl;
6013 
6014  label faceZoneI = surfaceZonesInfo::addFaceZone
6015  (
6016  fzName, // name
6017  labelList(0), // addressing
6018  boolList(0), // flipMap
6019  mesh_
6020  );
6021 
6022  label cz0 = cellZones.findZoneID(cz[0]);
6023  label cz1 = cellZones.findZoneID(cz[1]);
6024 
6025  fZoneLookup.insert(labelPair(cz0, cz1), faceZoneI);
6026  }
6027  }
6028 
6029 
6030  // 4. Set faceToZone with new faceZones
6031 
6032 
6033  for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
6034  {
6035  if (faceToZone[faceI] == -1)
6036  {
6037  // Face not yet in a faceZone. (it might already have been
6038  // done so by a 'named' surface). Check if inbetween different
6039  // cellZones
6040 
6041  label ownZone = cellToZone[mesh_.faceOwner()[faceI]];
6042  label neiZone = cellToZone[mesh_.faceNeighbour()[faceI]];
6043  if (ownZone != neiZone)
6044  {
6045  const bool swap =
6046  (
6047  ownZone == -1
6048  || (neiZone != -1 && ownZone > neiZone)
6049  );
6050  labelPair key(ownZone, neiZone);
6051  if (swap)
6052  {
6053  key.flip();
6054  }
6055  faceToZone[faceI] = fZoneLookup[key];
6056  }
6057  }
6058  }
6059  forAll(neiCellZone, bFaceI)
6060  {
6061  label faceI = bFaceI + mesh_.nInternalFaces();
6062  if (faceToZone[faceI] == -1)
6063  {
6064  label ownZone = cellToZone[mesh_.faceOwner()[faceI]];
6065  label neiZone = neiCellZone[bFaceI];
6066  if (ownZone != neiZone)
6067  {
6068  const bool swap =
6069  (
6070  ownZone == -1
6071  || (neiZone != -1 && ownZone > neiZone)
6072  );
6073  labelPair key(ownZone, neiZone);
6074  if (swap)
6075  {
6076  key.flip();
6077  }
6078  faceToZone[faceI] = fZoneLookup[key];
6079  }
6080  }
6081  }
6082  Info<< endl;
6083  }
6084 
6085 
6086 
6087 
6088  // Get coupled neighbour cellZone. Set to -1 on non-coupled patches.
6089  labelList neiCellZone;
6090  syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone);
6091  forAll(patches, patchI)
6092  {
6093  const polyPatch& pp = patches[patchI];
6094 
6095  if (!pp.coupled())
6096  {
6097  label bFaceI = pp.start()-mesh_.nInternalFaces();
6098  forAll(pp, i)
6099  {
6100  neiCellZone[bFaceI++] = -1;
6101  }
6102  }
6103  }
6104 
6105 
6106 
6107  // Get per face whether is it master (of a coupled set of faces)
6108  const bitSet isMasterFace(syncTools::getMasterFaces(mesh_));
6109 
6110 
6111  // faceZones
6112  // ~~~~~~~~~
6113  // Faces on faceZones come in two variants:
6114  // - faces on the outside of a cellZone. They will be oriented to
6115  // point out of the maximum cellZone.
6116  // - free-standing faces. These will be oriented according to the
6117  // local surface normal. We do this in a two step algorithm:
6118  // - do a consistent orientation
6119  // - check number of faces with consistent orientation
6120  // - if <0 flip the whole patch
6121  bitSet meshFlipMap(mesh_.nFaces(), false);
6122  {
6123  // Collect all data on zone faces without cellZones on either side.
6125  (
6127  (
6128  mesh_.faces(),
6129  freeStandingBaffleFaces
6130  (
6131  faceToZone,
6132  cellToZone,
6133  neiCellZone
6134  )
6135  ),
6136  mesh_.points()
6137  );
6138 
6139  label nFreeStanding = returnReduce(patch.size(), sumOp<label>());
6140  if (nFreeStanding > 0)
6141  {
6142  Info<< "Detected " << nFreeStanding << " free-standing zone faces"
6143  << endl;
6144 
6145  if (debug)
6146  {
6147  OBJstream str(mesh_.time().path()/"freeStanding.obj");
6148  Pout<< "meshRefinement::zonify : dumping faceZone faces to "
6149  << str.name() << endl;
6150  str.write(patch.localFaces(), patch.localPoints(), false);
6151  }
6152 
6153 
6154  // Detect non-manifold edges
6155  labelList nMasterFacesPerEdge;
6156  calcPatchNumMasterFaces(isMasterFace, patch, nMasterFacesPerEdge);
6157 
6158  // Mark zones. Even a single original surface might create multiple
6159  // disconnected/non-manifold-connected zones
6160  labelList faceToConnectedZone;
6161  const label nZones = markPatchZones
6162  (
6163  patch,
6164  nMasterFacesPerEdge,
6165  faceToConnectedZone
6166  );
6167 
6168  Map<label> nPosOrientation(2*nZones);
6169  for (label zoneI = 0; zoneI < nZones; zoneI++)
6170  {
6171  nPosOrientation.insert(zoneI, 0);
6172  }
6173 
6174  // Make orientations consistent in a topological way. This just
6175  // checks the first face per zone for whether nPosOrientation
6176  // is negative (which it never is at this point)
6177  consistentOrientation
6178  (
6179  isMasterFace,
6180  patch,
6181  nMasterFacesPerEdge,
6182  faceToConnectedZone,
6183  nPosOrientation,
6184 
6185  meshFlipMap
6186  );
6187 
6188  // Count per region the number of orientations (taking the new
6189  // flipMap into account)
6190  forAll(patch.addressing(), faceI)
6191  {
6192  label meshFaceI = patch.addressing()[faceI];
6193 
6194  if (isMasterFace.test(meshFaceI))
6195  {
6196  label n = 1;
6197  if
6198  (
6199  posOrientation.test(meshFaceI)
6200  == meshFlipMap.test(meshFaceI)
6201  )
6202  {
6203  n = -1;
6204  }
6205 
6206  nPosOrientation.find(faceToConnectedZone[faceI])() += n;
6207  }
6208  }
6209  Pstream::mapCombineReduce(nPosOrientation, plusEqOp<label>());
6210 
6211 
6212  Info<< "Split " << nFreeStanding << " free-standing zone faces"
6213  << " into " << nZones << " disconnected regions with size"
6214  << " (negative denotes wrong orientation) :"
6215  << endl;
6216 
6217  for (label zoneI = 0; zoneI < nZones; zoneI++)
6218  {
6219  Info<< " " << zoneI << "\t" << nPosOrientation[zoneI]
6220  << endl;
6221  }
6222  Info<< endl;
6223 
6224 
6225  // Re-apply with new counts (in nPosOrientation). This will cause
6226  // zones with a negative count to be flipped.
6227  consistentOrientation
6228  (
6229  isMasterFace,
6230  patch,
6231  nMasterFacesPerEdge,
6232  faceToConnectedZone,
6233  nPosOrientation,
6234 
6235  meshFlipMap
6236  );
6237  }
6238  }
6239 
6240 
6241 
6242 
6243  // Topochange container
6244  polyTopoChange meshMod(mesh_);
6245 
6246  // Insert changes to put cells and faces into zone
6247  zonify
6248  (
6249  isMasterFace,
6250  cellToZone,
6251  neiCellZone,
6252  faceToZone,
6253  meshFlipMap,
6254  meshMod
6255  );
6256 
6257  // Remove any unnecessary fields
6258  mesh_.clearOut();
6259  mesh_.moving(false);
6260 
6261  // Change the mesh (no inflation, parallel sync)
6262  autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true);
6263 
6264  // Update fields
6265  mesh_.updateMesh(map());
6266 
6267  // Move mesh if in inflation mode
6268  if (map().hasMotionPoints())
6269  {
6270  mesh_.movePoints(map().preMotionPoints());
6271  }
6272  else
6273  {
6274  // Delete mesh volumes.
6275  mesh_.clearOut();
6276  }
6277 
6278  // Reset the instance for if in overwrite mode
6279  mesh_.setInstance(timeName());
6280 
6281  // Print some stats (note: zones are synchronised)
6282  if (mesh_.cellZones().size() > 0)
6283  {
6284  Info<< "CellZones:" << endl;
6285  forAll(mesh_.cellZones(), zoneI)
6286  {
6287  const cellZone& cz = mesh_.cellZones()[zoneI];
6288  Info<< " " << cz.name()
6289  << "\tsize:" << returnReduce(cz.size(), sumOp<label>())
6290  << endl;
6291  }
6292  Info<< endl;
6293  }
6294  if (mesh_.faceZones().size() > 0)
6295  {
6296  Info<< "FaceZones:" << endl;
6297  forAll(mesh_.faceZones(), zoneI)
6298  {
6299  const faceZone& fz = mesh_.faceZones()[zoneI];
6300  Info<< " " << fz.name()
6301  << "\tsize:" << returnReduce(fz.size(), sumOp<label>())
6302  << endl;
6303  }
6304  Info<< endl;
6305  }
6306 
6307  // None of the faces has changed, only the zones. Still...
6308  updateMesh(map(), labelList());
6309 
6310  return map;
6311 }
6312 
6313 
6314 // ************************************************************************* //
static const word & zeroGradientType() noexcept
The type name for zeroGradient patch fields.
Definition: fvPatchField.H:221
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:116
ZoneMesh< faceZone, polyMesh > faceZoneMesh
A ZoneMesh with the type faceZone.
const labelIOList & zoneIDs
Definition: correctPhi.H:59
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1)
fileName path() const
Return path = rootPath/caseName. Same as TimePaths::path()
Definition: Time.H:503
Definition: ops.H:67
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:493
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:326
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:608
void append(const T &val)
Append an element at the end of the list.
Definition: List.H:521
virtual const labelList & faceNeighbour() const
Return face neighbour.
Definition: polyMesh.C:1122
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
std::enable_if< std::is_same< bool, TypeT >::value, bool >::type set(const label i, bool val=true)
A bitSet::set() method for a list of bool.
Definition: List.H:493
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:807
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:862
engineTime & runTime
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
bool found(const T &val, label pos=0) const
Same as contains()
Definition: UList.H:888
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.
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)
T returnReduce(const T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
DynamicID< faceZoneMesh > faceZoneID
Foam::faceZoneID.
Definition: ZoneIDs.H:43
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.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
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
bool returnReduceAnd(const bool value, const label comm=UPstream::worldComm)
Perform logical (and) MPI Allreduce on a copy. Uses UPstream::reduceAnd.
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:320
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:614
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:718
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.
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.
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:1116
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
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:521
Vector< scalar > vector
Definition: vector.H:57
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:584
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1103
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)
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:173
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 additional search methods and registered IO...
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)
std::enable_if< std::is_same< bool, TypeT >::value, bool >::type test(const label i) const
Test bool value at specified position, always false for out-of-range access.
Definition: UList.H:778
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];} label minLabel=min(start, end);pointMap[start]=pointMap[end]=minLabel;} } 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};const 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< SLList< face > > pFaces[nBCs]
Definition: readKivaGrid.H:235
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:406
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.
auto key(const Type &t) -> typename std::enable_if< std::is_enum< Type >::value, typename std::underlying_type< Type >::type >::type
Definition: foamGltfBase.H:103
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers...
Definition: List.H:55
List< Key > sortedToc() const
The table of contents (the keys) in sorted order.
Definition: HashTable.C:163
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.
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:62
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
static void mapCombineReduce(Container &values, const CombineOp &cop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce) applying cop to inplace combine map values from different processo...
bool returnReduceOr(const bool value, const label comm=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
constexpr scalar degToRad(const scalar deg) noexcept
Conversion from degrees to radians.
List< bool > boolList
A List of bools.
Definition: List.H:60
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.
void reduce(T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce) using linear/tree communication schedule.
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
static void listCombineReduce(List< T > &values, const CombineOp &cop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Combines List elements. After completion all processors have the same data.
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