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