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