snappyLayerDriver.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-2015 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 Description
28  All to do with adding cell layers
29 
30 \*----------------------------------------------------------------------------*/
31 
32 #include "snappyLayerDriver.H"
33 #include "fvMesh.H"
34 #include "Time.H"
35 #include "meshRefinement.H"
36 #include "removePoints.H"
37 #include "pointFields.H"
38 #include "motionSmoother.H"
39 #include "unitConversion.H"
40 #include "pointSet.H"
41 #include "faceSet.H"
42 #include "cellSet.H"
43 #include "polyTopoChange.H"
44 #include "mapPolyMesh.H"
45 #include "addPatchCellLayer.H"
46 #include "mapDistributePolyMesh.H"
47 #include "OBJstream.H"
48 #include "layerParameters.H"
49 #include "combineFaces.H"
50 #include "IOmanip.H"
51 #include "globalIndex.H"
52 #include "DynamicField.H"
53 #include "PatchTools.H"
54 #include "slipPointPatchFields.H"
60 #include "localPointRegion.H"
62 #include "scalarIOField.H"
63 #include "profiling.H"
64 
65 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
66 
67 namespace Foam
68 {
69 
70 defineTypeNameAndDebug(snappyLayerDriver, 0);
71 
72 } // End namespace Foam
73 
74 
75 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
76 
77 // For debugging: Dump displacement to .obj files
78 void Foam::snappyLayerDriver::dumpDisplacement
79 (
80  const fileName& prefix,
82  const vectorField& patchDisp,
83  const List<extrudeMode>& extrudeStatus
84 )
85 {
86  OBJstream dispStr(prefix + "_disp.obj");
87  Info<< "Writing all displacements to " << dispStr.name() << endl;
88 
89  forAll(patchDisp, patchPointi)
90  {
91  const point& pt = pp.localPoints()[patchPointi];
92  dispStr.writeLine(pt, pt + patchDisp[patchPointi]);
93  }
94 
95 
96  OBJstream illStr(prefix + "_illegal.obj");
97  Info<< "Writing invalid displacements to " << illStr.name() << endl;
98 
99  forAll(patchDisp, patchPointi)
100  {
101  if (extrudeStatus[patchPointi] != EXTRUDE)
102  {
103  const point& pt = pp.localPoints()[patchPointi];
104  illStr.writeLine(pt, pt + patchDisp[patchPointi]);
105  }
106  }
107 }
108 
109 
110 Foam::tmp<Foam::scalarField> Foam::snappyLayerDriver::avgPointData
111 (
112  const indirectPrimitivePatch& pp,
113  const scalarField& pointFld
114 )
115 {
116  auto tfaceFld = tmp<scalarField>::New(pp.size(), Zero);
117  auto& faceFld = tfaceFld.ref();
118 
119  forAll(pp.localFaces(), facei)
120  {
121  const face& f = pp.localFaces()[facei];
122  if (f.size())
123  {
124  forAll(f, fp)
125  {
126  faceFld[facei] += pointFld[f[fp]];
127  }
128  faceFld[facei] /= f.size();
129  }
130  }
131  return tfaceFld;
132 }
133 
134 
135 // Check that primitivePatch is not multiply connected. Collect non-manifold
136 // points in pointSet.
137 void Foam::snappyLayerDriver::checkManifold
138 (
139  const indirectPrimitivePatch& fp,
140  pointSet& nonManifoldPoints
141 )
142 {
143  // Check for non-manifold points (surface pinched at point)
144  fp.checkPointManifold(false, &nonManifoldPoints);
145 
146  // Check for edge-faces (surface pinched at edge)
147  const labelListList& edgeFaces = fp.edgeFaces();
148 
149  forAll(edgeFaces, edgei)
150  {
151  const labelList& eFaces = edgeFaces[edgei];
152 
153  if (eFaces.size() > 2)
154  {
155  const edge& e = fp.edges()[edgei];
156 
157  nonManifoldPoints.insert(fp.meshPoints()[e[0]]);
158  nonManifoldPoints.insert(fp.meshPoints()[e[1]]);
159  }
160  }
161 }
162 
163 
164 void Foam::snappyLayerDriver::checkMeshManifold() const
165 {
166  const fvMesh& mesh = meshRefiner_.mesh();
167 
168  Info<< nl << "Checking mesh manifoldness ..." << endl;
169 
170  pointSet nonManifoldPoints
171  (
172  mesh,
173  "nonManifoldPoints",
174  mesh.nPoints() / 100
175  );
176 
177  // Build primitivePatch out of faces and check it for problems.
178  checkManifold
179  (
181  (
182  IndirectList<face>
183  (
184  mesh.faces(),
185  identity(mesh.boundaryMesh().range()) // All outside faces
186  ),
187  mesh.points()
188  ),
189  nonManifoldPoints
190  );
191 
192  label nNonManif = returnReduce(nonManifoldPoints.size(), sumOp<label>());
193 
194  if (nNonManif > 0)
195  {
196  Info<< "Outside of mesh is multiply connected across edges or"
197  << " points." << nl
198  << "This is not a fatal error but might cause some unexpected"
199  << " behaviour." << nl
200  //<< "Writing " << nNonManif
201  //<< " points where this happens to pointSet "
202  //<< nonManifoldPoints.name()
203  << endl;
204 
205  //nonManifoldPoints.instance() = meshRefiner_.timeName();
206  //nonManifoldPoints.write();
207  }
208  Info<< endl;
209 }
210 
211 
212 
213 // Unset extrusion on point. Returns true if anything unset.
214 bool Foam::snappyLayerDriver::unmarkExtrusion
215 (
216  const label patchPointi,
217  pointField& patchDisp,
218  labelList& patchNLayers,
219  List<extrudeMode>& extrudeStatus
220 )
221 {
222  if (extrudeStatus[patchPointi] == EXTRUDE)
223  {
224  extrudeStatus[patchPointi] = NOEXTRUDE;
225  patchNLayers[patchPointi] = 0;
226  patchDisp[patchPointi] = Zero;
227  return true;
228  }
229  else if (extrudeStatus[patchPointi] == EXTRUDEREMOVE)
230  {
231  extrudeStatus[patchPointi] = NOEXTRUDE;
232  patchNLayers[patchPointi] = 0;
233  patchDisp[patchPointi] = Zero;
234  return true;
235  }
236 
237  return false;
238 }
239 
240 
241 // Unset extrusion on face. Returns true if anything unset.
242 bool Foam::snappyLayerDriver::unmarkExtrusion
243 (
244  const face& localFace,
245  pointField& patchDisp,
246  labelList& patchNLayers,
247  List<extrudeMode>& extrudeStatus
248 )
249 {
250  bool unextruded = false;
251 
252  forAll(localFace, fp)
253  {
254  if
255  (
256  unmarkExtrusion
257  (
258  localFace[fp],
259  patchDisp,
260  patchNLayers,
261  extrudeStatus
262  )
263  )
264  {
265  unextruded = true;
266  }
267  }
268  return unextruded;
269 }
270 
271 
272 Foam::label Foam::snappyLayerDriver::constrainFp(const label sz, const label fp)
273 {
274  if (fp >= sz)
275  {
276  return 0;
277  }
278  else if (fp < 0)
279  {
280  return sz-1;
281  }
282  else
283  {
284  return fp;
285  }
286 }
287 
288 
289 void Foam::snappyLayerDriver::countCommonPoints
290 (
291  const indirectPrimitivePatch& pp,
292  const label facei,
293 
294  Map<label>& nCommonPoints
295 ) const
296 {
297  const faceList& localFaces = pp.localFaces();
298  const labelListList& pointFaces = pp.pointFaces();
299 
300  const face& f = localFaces[facei];
301 
302  nCommonPoints.clear();
303 
304  forAll(f, fp)
305  {
306  label pointi = f[fp];
307  const labelList& pFaces = pointFaces[pointi];
308 
309  forAll(pFaces, pFacei)
310  {
311  label nbFacei = pFaces[pFacei];
312 
313  if (facei < nbFacei)
314  {
315  // Only check once for each combination of two faces.
316  ++(nCommonPoints(nbFacei, 0));
317  }
318  }
319  }
320 }
321 
322 
323 bool Foam::snappyLayerDriver::checkCommonOrder
324 (
325  const label nCommon,
326  const face& curFace,
327  const face& nbFace
328 ) const
329 {
330  forAll(curFace, fp)
331  {
332  // Get the index in the neighbouring face shared with curFace
333  const label nb = nbFace.find(curFace[fp]);
334 
335  if (nb != -1)
336  {
337 
338  // Check the whole face from nb onwards for shared vertices
339  // with neighbouring face. Rule is that any shared vertices
340  // should be consecutive on both faces i.e. if they are
341  // vertices fp,fp+1,fp+2 on one face they should be
342  // vertices nb, nb+1, nb+2 (or nb+2, nb+1, nb) on the
343  // other face.
344 
345 
346  // Vertices before and after on curFace
347  label fpPlus1 = curFace.fcIndex(fp);
348  label fpMin1 = curFace.rcIndex(fp);
349 
350  // Vertices before and after on nbFace
351  label nbPlus1 = nbFace.fcIndex(nb);
352  label nbMin1 = nbFace.rcIndex(nb);
353 
354  // Find order of walking by comparing next points on both
355  // faces.
356  label curInc = labelMax;
357  label nbInc = labelMax;
358 
359  if (nbFace[nbPlus1] == curFace[fpPlus1])
360  {
361  curInc = 1;
362  nbInc = 1;
363  }
364  else if (nbFace[nbPlus1] == curFace[fpMin1])
365  {
366  curInc = -1;
367  nbInc = 1;
368  }
369  else if (nbFace[nbMin1] == curFace[fpMin1])
370  {
371  curInc = -1;
372  nbInc = -1;
373  }
374  else
375  {
376  curInc = 1;
377  nbInc = -1;
378  }
379 
380 
381  // Pass1: loop until start of common vertices found.
382  label curNb = nb;
383  label curFp = fp;
384 
385  do
386  {
387  curFp = constrainFp(curFace.size(), curFp+curInc);
388  curNb = constrainFp(nbFace.size(), curNb+nbInc);
389  } while (curFace[curFp] == nbFace[curNb]);
390 
391  // Pass2: check equality walking from curFp, curNb
392  // in opposite order.
393 
394  curInc = -curInc;
395  nbInc = -nbInc;
396 
397  for (label commonI = 0; commonI < nCommon; commonI++)
398  {
399  curFp = constrainFp(curFace.size(), curFp+curInc);
400  curNb = constrainFp(nbFace.size(), curNb+nbInc);
401 
402  if (curFace[curFp] != nbFace[curNb])
403  {
404  // Error: gap in string of connected vertices
405  return false;
406  }
407  }
408 
409  // Done the curFace - nbFace combination.
410  break;
411  }
412  }
413 
414  return true;
415 }
416 
417 
418 void Foam::snappyLayerDriver::checkCommonOrder
419 (
420  const indirectPrimitivePatch& pp,
421  const label facei,
422  const Map<label>& nCommonPoints,
423  pointField& patchDisp,
424  labelList& patchNLayers,
425  List<extrudeMode>& extrudeStatus
426 ) const
427 {
428  forAllConstIters(nCommonPoints, iter)
429  {
430  const label nbFacei = iter.key();
431  const label nCommon = iter.val();
432 
433  const face& curFace = pp[facei];
434  const face& nbFace = pp[nbFacei];
435 
436  if
437  (
438  nCommon >= 2
439  && nCommon != nbFace.size()
440  && nCommon != curFace.size()
441  )
442  {
443  bool stringOk = checkCommonOrder(nCommon, curFace, nbFace);
444 
445  if (!stringOk)
446  {
447  // Note: unmark whole face or just the common points?
448  // For now unmark the whole face
449  unmarkExtrusion
450  (
451  pp.localFaces()[facei],
452  patchDisp,
453  patchNLayers,
454  extrudeStatus
455  );
456  unmarkExtrusion
457  (
458  pp.localFaces()[nbFacei],
459  patchDisp,
460  patchNLayers,
461  extrudeStatus
462  );
463  }
464  }
465  }
466 }
467 
468 
469 void Foam::snappyLayerDriver::handleNonStringConnected
470 (
471  const indirectPrimitivePatch& pp,
472  pointField& patchDisp,
473  labelList& patchNLayers,
474  List<extrudeMode>& extrudeStatus
475 ) const
476 {
477  // Detect faces which are connected on non-consecutive vertices.
478  // This is the "<<Number of faces with non-consecutive shared points"
479  // warning from checkMesh. These faces cannot be extruded so
480  // there is no need to even attempt it.
481 
482  List<extrudeMode> oldExtrudeStatus;
483  autoPtr<OBJstream> str;
485  {
486  oldExtrudeStatus = extrudeStatus;
487  str.reset
488  (
489  new OBJstream
490  (
491  meshRefiner_.mesh().time().path()
492  /"nonStringConnected.obj"
493  )
494  );
495  Pout<< "Dumping string edges to " << str().name();
496  }
497 
498 
499  // 1) Local
500  Map<label> nCommonPoints(128);
501 
502  forAll(pp, facei)
503  {
504  countCommonPoints(pp, facei, nCommonPoints);
505 
506  // Faces share pointi. Find any more shared points
507  // and if not in single string unmark all. See
508  // primitiveMesh::checkCommonOrder
509  checkCommonOrder
510  (
511  pp,
512  facei,
513  nCommonPoints,
514 
515  patchDisp,
516  patchNLayers,
517  extrudeStatus
518  );
519  }
520 
521  // 2) TDB. Other face remote
522 
523 
524 
526  {
527  forAll(extrudeStatus, pointi)
528  {
529  if (extrudeStatus[pointi] != oldExtrudeStatus[pointi])
530  {
531  str().write
532  (
533  meshRefiner_.mesh().points()[pp.meshPoints()[pointi]]
534  );
535  }
536  }
537  }
538 }
539 
540 
541 // No extrusion at non-manifold points.
542 void Foam::snappyLayerDriver::handleNonManifolds
543 (
544  const indirectPrimitivePatch& pp,
545  const labelList& meshEdges,
546  const labelListList& edgeGlobalFaces,
547  pointField& patchDisp,
548  labelList& patchNLayers,
549  List<extrudeMode>& extrudeStatus
550 ) const
551 {
552  const fvMesh& mesh = meshRefiner_.mesh();
553 
554  Info<< nl << "Handling non-manifold points ..." << endl;
555 
556  // Detect non-manifold points
557  Info<< nl << "Checking patch manifoldness ..." << endl;
558 
559  pointSet nonManifoldPoints(mesh, "nonManifoldPoints", pp.nPoints());
560 
561  // 1. Local check. Note that we do not check for e.g. two patch faces
562  // being connected via a point since their connection might be ok
563  // through a coupled patch. The ultimate is to do a proper point-face
564  // walk which is done when actually duplicating the points. Here we just
565  // do the obvious problems.
566  {
567  // Check for edge-faces (surface pinched at edge)
568  const labelListList& edgeFaces = pp.edgeFaces();
569 
570  forAll(edgeFaces, edgei)
571  {
572  const labelList& eFaces = edgeFaces[edgei];
573  if (eFaces.size() > 2)
574  {
575  const edge& e = pp.edges()[edgei];
576  nonManifoldPoints.insert(pp.meshPoints()[e[0]]);
577  nonManifoldPoints.insert(pp.meshPoints()[e[1]]);
578  }
579  }
580  }
581 
582  // 2. Remote check for boundary edges on coupled boundaries
583  forAll(edgeGlobalFaces, edgei)
584  {
585  if (edgeGlobalFaces[edgei].size() > 2)
586  {
587  // So boundary edges that are connected to more than 2 processors
588  // i.e. a non-manifold edge which is exactly on a processor
589  // boundary.
590  const edge& e = pp.edges()[edgei];
591  nonManifoldPoints.insert(pp.meshPoints()[e[0]]);
592  nonManifoldPoints.insert(pp.meshPoints()[e[1]]);
593  }
594  }
595 
596 
597  label nNonManif = returnReduce(nonManifoldPoints.size(), sumOp<label>());
598 
599  Info<< "Outside of local patch is multiply connected across edges or"
600  << " points at " << nNonManif << " points." << endl;
601 
602  if (nNonManif > 0)
603  {
604  // Make sure all processors use the same information. The edge might
605  // not exist locally but remotely there might be a problem with this
606  // edge.
607  nonManifoldPoints.sync(mesh);
608 
609  const labelList& meshPoints = pp.meshPoints();
610 
611  forAll(meshPoints, patchPointi)
612  {
613  if (nonManifoldPoints.found(meshPoints[patchPointi]))
614  {
615  unmarkExtrusion
616  (
617  patchPointi,
618  patchDisp,
619  patchNLayers,
620  extrudeStatus
621  );
622  }
623  }
624  }
625 
626  Info<< "Set displacement to zero for all " << nNonManif
627  << " non-manifold points" << endl;
628 
629 
630 
631  // 4. Check for extrusion of baffles i.e. all edges of a face having the
632  // same two neighbouring faces (one of which is the current face).
633  // Note: this is detected locally already before - this test is for the
634  // extremely rare occurrence where the baffle faces are on
635  // different processors.
636  {
637  label nBaffleFaces = 0;
638 
639  const labelListList& faceEdges = pp.faceEdges();
640  forAll(pp, facei)
641  {
642  const labelList& fEdges = faceEdges[facei];
643 
644  const labelList& globFaces0 = edgeGlobalFaces[fEdges[0]];
645  if (globFaces0.size() == 2)
646  {
647  const edge e0(globFaces0[0], globFaces0[1]);
648  bool isBaffle = true;
649  for (label fp = 1; fp < fEdges.size(); fp++)
650  {
651  const labelList& globFaces = edgeGlobalFaces[fEdges[fp]];
652  if
653  (
654  (globFaces.size() != 2)
655  || (edge(globFaces[0], globFaces[1]) != e0)
656  )
657  {
658  isBaffle = false;
659  break;
660  }
661  }
662 
663  if (isBaffle)
664  {
665  bool unextrude = unmarkExtrusion
666  (
667  pp.localFaces()[facei],
668  patchDisp,
669  patchNLayers,
670  extrudeStatus
671  );
672  if (unextrude)
673  {
674  //Pout<< "Detected extrusion of baffle face "
675  // << pp.faceCentres()[facei]
676  // << " since all edges have the same neighbours "
677  // << e0 << endl;
678 
679  nBaffleFaces++;
680  }
681  }
682  }
683  }
684 
685  reduce(nBaffleFaces, sumOp<label>());
686 
687  if (nBaffleFaces)
688  {
689  Info<< "Set displacement to zero for all points on " << nBaffleFaces
690  << " baffle faces" << endl;
691  }
692  }
693 }
694 
695 
696 // Parallel feature edge detection. Assumes non-manifold edges already handled.
697 void Foam::snappyLayerDriver::handleFeatureAngle
698 (
699  const indirectPrimitivePatch& pp,
700  const labelList& meshEdges,
701  const scalar minAngle,
702  pointField& patchDisp,
703  labelList& patchNLayers,
704  List<extrudeMode>& extrudeStatus
705 ) const
706 {
707  const fvMesh& mesh = meshRefiner_.mesh();
708 
709  const scalar minCos = Foam::cos(degToRad(minAngle));
710 
711  Info<< nl << "Handling feature edges (angle < " << minAngle
712  << ") ..." << endl;
713 
714  if (minCos < 1-SMALL && minCos > -1+SMALL)
715  {
716  // Normal component of normals of connected faces.
717  vectorField edgeNormal(mesh.nEdges(), point::max);
718 
719  const labelListList& edgeFaces = pp.edgeFaces();
720 
721  forAll(edgeFaces, edgei)
722  {
723  const labelList& eFaces = pp.edgeFaces()[edgei];
724 
725  label meshEdgei = meshEdges[edgei];
726 
727  forAll(eFaces, i)
728  {
729  nomalsCombine()
730  (
731  edgeNormal[meshEdgei],
732  pp.faceNormals()[eFaces[i]]
733  );
734  }
735  }
736 
738  (
739  mesh,
740  edgeNormal,
741  nomalsCombine(),
742  point::max // null value
743  );
744 
745  autoPtr<OBJstream> str;
747  {
748  str.reset
749  (
750  new OBJstream
751  (
752  mesh.time().path()
753  / "featureEdges_"
754  + meshRefiner_.timeName()
755  + ".obj"
756  )
757  );
758  Info<< "Writing feature edges to " << str().name() << endl;
759  }
760 
761  label nFeats = 0;
762 
763  // Now on coupled edges the edgeNormal will have been truncated and
764  // only be still be the old value where two faces have the same normal
765  forAll(edgeFaces, edgei)
766  {
767  const labelList& eFaces = pp.edgeFaces()[edgei];
768 
769  label meshEdgei = meshEdges[edgei];
770 
771  const vector& n = edgeNormal[meshEdgei];
772 
773  if (n != point::max)
774  {
775  scalar cos = n & pp.faceNormals()[eFaces[0]];
776 
777  if (cos < minCos)
778  {
779  const edge& e = pp.edges()[edgei];
780 
781  unmarkExtrusion
782  (
783  e[0],
784  patchDisp,
785  patchNLayers,
786  extrudeStatus
787  );
788  unmarkExtrusion
789  (
790  e[1],
791  patchDisp,
792  patchNLayers,
793  extrudeStatus
794  );
795 
796  nFeats++;
797 
798  if (str)
799  {
800  str().write(e, pp.localPoints());
801  }
802  }
803  }
804  }
805 
806  Info<< "Set displacement to zero for points on "
807  << returnReduce(nFeats, sumOp<label>())
808  << " feature edges" << endl;
809  }
810 }
811 
812 
813 // No extrusion on cells with warped faces. Calculates the thickness of the
814 // layer and compares it to the space the warped face takes up. Disables
815 // extrusion if layer thickness is more than faceRatio of the thickness of
816 // the face.
817 void Foam::snappyLayerDriver::handleWarpedFaces
818 (
819  const indirectPrimitivePatch& pp,
820  const scalar faceRatio,
821  const boolList& relativeSizes,
822  const scalar edge0Len,
823  const labelList& cellLevel,
824  pointField& patchDisp,
825  labelList& patchNLayers,
826  List<extrudeMode>& extrudeStatus
827 ) const
828 {
829  const fvMesh& mesh = meshRefiner_.mesh();
830  const polyBoundaryMesh& patches = mesh.boundaryMesh();
831 
832  Info<< nl << "Handling cells with warped patch faces ..." << nl;
833 
834  const pointField& points = mesh.points();
835 
836  // Local reference to face centres also used to trigger consistent
837  // [re-]building of demand-driven face centres and areas
838  const vectorField& faceCentres = mesh.faceCentres();
839 
840  label nWarpedFaces = 0;
841 
842  forAll(pp, i)
843  {
844  const face& f = pp[i];
845  label faceI = pp.addressing()[i];
846  label patchI = patches.patchID(faceI);
847 
848  // It is hard to calculate some length scale if not in relative
849  // mode so disable this check.
850 
851  if (relativeSizes[patchI] && f.size() > 3)
852  {
853  label ownLevel = cellLevel[mesh.faceOwner()[faceI]];
854  scalar edgeLen = edge0Len/(1<<ownLevel);
855 
856  // Normal distance to face centre plane
857  const point& fc = faceCentres[faceI];
858  const vector& fn = pp.faceNormals()[i];
859 
860  scalarField vProj(f.size());
861 
862  forAll(f, fp)
863  {
864  vector n = points[f[fp]] - fc;
865  vProj[fp] = (n & fn);
866  }
867 
868  // Get normal 'span' of face
869  scalar minVal = min(vProj);
870  scalar maxVal = max(vProj);
871 
872  if ((maxVal - minVal) > faceRatio * edgeLen)
873  {
874  if
875  (
876  unmarkExtrusion
877  (
878  pp.localFaces()[i],
879  patchDisp,
880  patchNLayers,
881  extrudeStatus
882  )
883  )
884  {
885  nWarpedFaces++;
886  }
887  }
888  }
889  }
890 
891  Info<< "Set displacement to zero on "
892  << returnReduce(nWarpedFaces, sumOp<label>())
893  << " warped faces since layer would be > " << faceRatio
894  << " of the size of the bounding box." << endl;
895 }
896 
897 
900 //void Foam::snappyLayerDriver::handleMultiplePatchFaces
901 //(
902 // const indirectPrimitivePatch& pp,
903 // pointField& patchDisp,
904 // labelList& patchNLayers,
905 // List<extrudeMode>& extrudeStatus
906 //) const
907 //{
908 // const fvMesh& mesh = meshRefiner_.mesh();
909 //
910 // Info<< nl << "Handling cells with multiple patch faces ..." << nl;
911 //
912 // const labelListList& pointFaces = pp.pointFaces();
913 //
914 // // Cells that should not get an extrusion layer
915 // cellSet multiPatchCells(mesh, "multiPatchCells", pp.size());
916 //
917 // // Detect points that use multiple faces on same cell.
918 // forAll(pointFaces, patchPointi)
919 // {
920 // const labelList& pFaces = pointFaces[patchPointi];
921 //
922 // labelHashSet pointCells(pFaces.size());
923 //
924 // forAll(pFaces, i)
925 // {
926 // label celli = mesh.faceOwner()[pp.addressing()[pFaces[i]]];
927 //
928 // if (!pointCells.insert(celli))
929 // {
930 // // Second or more occurrence of cell so cell has two or more
931 // // pp faces connected to this point.
932 // multiPatchCells.insert(celli);
933 // }
934 // }
935 // }
936 //
937 // label nMultiPatchCells = returnReduce
938 // (
939 // multiPatchCells.size(),
940 // sumOp<label>()
941 // );
942 //
943 // Info<< "Detected " << nMultiPatchCells
944 // << " cells with multiple (connected) patch faces." << endl;
945 //
946 // label nChanged = 0;
947 //
948 // if (nMultiPatchCells > 0)
949 // {
950 // multiPatchCells.instance() = meshRefiner_.timeName();
951 // Info<< "Writing " << nMultiPatchCells
952 // << " cells with multiple (connected) patch faces to cellSet "
953 // << multiPatchCells.objectPath() << endl;
954 // multiPatchCells.write();
955 //
956 //
957 // // Go through all points and remove extrusion on any cell in
958 // // multiPatchCells
959 // // (has to be done in separate loop since having one point on
960 // // multipatches has to reset extrusion on all points of cell)
961 //
962 // forAll(pointFaces, patchPointi)
963 // {
964 // if (extrudeStatus[patchPointi] != NOEXTRUDE)
965 // {
966 // const labelList& pFaces = pointFaces[patchPointi];
967 //
968 // forAll(pFaces, i)
969 // {
970 // label celli =
971 // mesh.faceOwner()[pp.addressing()[pFaces[i]]];
972 //
973 // if (multiPatchCells.found(celli))
974 // {
975 // if
976 // (
977 // unmarkExtrusion
978 // (
979 // patchPointi,
980 // patchDisp,
981 // patchNLayers,
982 // extrudeStatus
983 // )
984 // )
985 // {
986 // nChanged++;
987 // }
988 // }
989 // }
990 // }
991 // }
992 //
993 // reduce(nChanged, sumOp<label>());
994 // }
995 //
996 // Info<< "Prevented extrusion on " << nChanged
997 // << " points due to multiple patch faces." << nl << endl;
998 //}
999 
1000 
1001 void Foam::snappyLayerDriver::setNumLayers
1002 (
1003  meshRefinement& meshRefiner,
1004  const labelList& patchToNLayers,
1005  const labelList& patchIDs,
1006  const indirectPrimitivePatch& pp,
1007  labelList& patchNLayers,
1008  List<extrudeMode>& extrudeStatus,
1009  label& nAddedCells
1010 )
1011 {
1012  const fvMesh& mesh = meshRefiner.mesh();
1013 
1014  Info<< nl << "Handling points with inconsistent layer specification ..."
1015  << endl;
1016 
1017  // Get for every point (really only necessary on patch external points)
1018  // the max and min of any patch faces using it.
1019  labelList maxLayers(patchNLayers.size(), labelMin);
1020  labelList minLayers(patchNLayers.size(), labelMax);
1021 
1022  forAll(patchIDs, i)
1023  {
1024  label patchi = patchIDs[i];
1025 
1026  const labelList& meshPoints = mesh.boundaryMesh()[patchi].meshPoints();
1027 
1028  label wantedLayers = patchToNLayers[patchi];
1029 
1030  forAll(meshPoints, patchPointi)
1031  {
1032  label ppPointi = pp.meshPointMap()[meshPoints[patchPointi]];
1033 
1034  maxLayers[ppPointi] = max(wantedLayers, maxLayers[ppPointi]);
1035  minLayers[ppPointi] = min(wantedLayers, minLayers[ppPointi]);
1036  }
1037  }
1038 
1040  (
1041  mesh,
1042  pp.meshPoints(),
1043  maxLayers,
1044  maxEqOp<label>(),
1045  labelMin // null value
1046  );
1048  (
1049  mesh,
1050  pp.meshPoints(),
1051  minLayers,
1052  minEqOp<label>(),
1053  labelMax // null value
1054  );
1055 
1056  // Unmark any point with different min and max
1057  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1058 
1059  //label nConflicts = 0;
1060 
1061  forAll(maxLayers, i)
1062  {
1063  if (maxLayers[i] == labelMin || minLayers[i] == labelMax)
1064  {
1066  << "Patchpoint:" << i << " coord:" << pp.localPoints()[i]
1067  << " maxLayers:" << maxLayers
1068  << " minLayers:" << minLayers
1069  << abort(FatalError);
1070  }
1071  else if (maxLayers[i] == minLayers[i])
1072  {
1073  // Ok setting.
1074  patchNLayers[i] = maxLayers[i];
1075  }
1076  else
1077  {
1078  // Inconsistent num layers between patch faces using point
1079  //if
1080  //(
1081  // unmarkExtrusion
1082  // (
1083  // i,
1084  // patchDisp,
1085  // patchNLayers,
1086  // extrudeStatus
1087  // )
1088  //)
1089  //{
1090  // nConflicts++;
1091  //}
1092  patchNLayers[i] = maxLayers[i];
1093  }
1094  }
1095 
1096 
1097  // Calculate number of cells to create
1098  nAddedCells = 0;
1099  forAll(pp.localFaces(), facei)
1100  {
1101  const face& f = pp.localFaces()[facei];
1102 
1103  // Get max of extrusion per point
1104  label nCells = 0;
1105  forAll(f, fp)
1106  {
1107  nCells = max(nCells, patchNLayers[f[fp]]);
1108  }
1109 
1110  nAddedCells += nCells;
1111  }
1112  reduce(nAddedCells, sumOp<label>());
1113 
1114  //reduce(nConflicts, sumOp<label>());
1115  //
1116  //Info<< "Set displacement to zero for " << nConflicts
1117  // << " points due to points being on multiple regions"
1118  // << " with inconsistent nLayers specification." << endl;
1119 }
1120 
1121 
1122 // Construct pointVectorField with correct boundary conditions for adding
1123 // layers
1125 Foam::snappyLayerDriver::makeLayerDisplacementField
1126 (
1127  const pointMesh& pMesh,
1128  const labelList& numLayers
1129 )
1130 {
1131  // Construct displacement field.
1132  const pointBoundaryMesh& pointPatches = pMesh.boundary();
1133 
1134  wordList patchFieldTypes
1135  (
1136  pointPatches.size(),
1137  slipPointPatchVectorField::typeName
1138  );
1139  wordList actualPatchTypes(patchFieldTypes.size());
1140  forAll(pointPatches, patchi)
1141  {
1142  actualPatchTypes[patchi] = pointPatches[patchi].type();
1143  }
1144 
1145  forAll(numLayers, patchi)
1146  {
1147  // 0 layers: do not allow slip so fixedValue 0
1148  // >0 layers: fixedValue which gets adapted
1149  if (numLayers[patchi] == 0)
1150  {
1151  patchFieldTypes[patchi] =
1152  zeroFixedValuePointPatchVectorField::typeName;
1153  }
1154  else if (numLayers[patchi] > 0)
1155  {
1156  patchFieldTypes[patchi] = fixedValuePointPatchVectorField::typeName;
1157  }
1158  }
1159 
1160  forAll(pointPatches, patchi)
1161  {
1162  if (isA<processorPointPatch>(pointPatches[patchi]))
1163  {
1164  patchFieldTypes[patchi] = calculatedPointPatchVectorField::typeName;
1165  }
1166  else if (isA<cyclicPointPatch>(pointPatches[patchi]))
1167  {
1168  patchFieldTypes[patchi] = cyclicSlipPointPatchVectorField::typeName;
1169  }
1170  }
1171 
1172 
1173  const polyMesh& mesh = pMesh();
1174 
1175  // Note: time().timeName() instead of meshRefinement::timeName() since
1176  // postprocessable field.
1177 
1179  (
1180  IOobject
1181  (
1182  "pointDisplacement",
1183  mesh.time().timeName(),
1184  mesh,
1187  ),
1188  pMesh,
1190  patchFieldTypes,
1191  actualPatchTypes
1192  );
1193 }
1194 
1195 
1196 void Foam::snappyLayerDriver::growNoExtrusion
1197 (
1198  const indirectPrimitivePatch& pp,
1199  pointField& patchDisp,
1200  labelList& patchNLayers,
1201  List<extrudeMode>& extrudeStatus
1202 ) const
1203 {
1204  Info<< nl << "Growing non-extrusion points by one layer ..." << endl;
1205 
1206  List<extrudeMode> grownExtrudeStatus(extrudeStatus);
1207 
1208  const faceList& localFaces = pp.localFaces();
1209 
1210  label nGrown = 0;
1211 
1212  forAll(localFaces, facei)
1213  {
1214  const face& f = localFaces[facei];
1215 
1216  bool hasSqueeze = false;
1217  forAll(f, fp)
1218  {
1219  if (extrudeStatus[f[fp]] == NOEXTRUDE)
1220  {
1221  hasSqueeze = true;
1222  break;
1223  }
1224  }
1225 
1226  if (hasSqueeze)
1227  {
1228  // Squeeze all points of face
1229  forAll(f, fp)
1230  {
1231  if
1232  (
1233  extrudeStatus[f[fp]] == EXTRUDE
1234  && grownExtrudeStatus[f[fp]] != NOEXTRUDE
1235  )
1236  {
1237  grownExtrudeStatus[f[fp]] = NOEXTRUDE;
1238  nGrown++;
1239  }
1240  }
1241  }
1242  }
1243 
1244  extrudeStatus.transfer(grownExtrudeStatus);
1245 
1246 
1247  // Synchronise since might get called multiple times.
1248  // Use the fact that NOEXTRUDE is the minimum value.
1249  {
1250  labelList status(extrudeStatus.size());
1251  forAll(status, i)
1252  {
1253  status[i] = extrudeStatus[i];
1254  }
1256  (
1257  meshRefiner_.mesh(),
1258  pp.meshPoints(),
1259  status,
1260  minEqOp<label>(),
1261  labelMax // null value
1262  );
1263  forAll(status, i)
1264  {
1265  extrudeStatus[i] = extrudeMode(status[i]);
1266  }
1267  }
1268 
1269 
1270  forAll(extrudeStatus, patchPointi)
1271  {
1272  if (extrudeStatus[patchPointi] == NOEXTRUDE)
1273  {
1274  patchDisp[patchPointi] = Zero;
1275  patchNLayers[patchPointi] = 0;
1276  }
1277  }
1278 
1279  reduce(nGrown, sumOp<label>());
1280 
1281  Info<< "Set displacement to zero for an additional " << nGrown
1282  << " points." << endl;
1283 }
1284 
1285 
1287 (
1288  meshRefinement& meshRefiner,
1289  const globalIndex& globalFaces,
1290  const labelListList& edgeGlobalFaces,
1291  const indirectPrimitivePatch& pp,
1292 
1293  labelList& edgePatchID,
1294  labelList& edgeZoneID,
1295  boolList& edgeFlip,
1296  labelList& inflateFaceID
1297 )
1298 {
1299  // Sometimes edges-to-be-extruded are on more than 2 processors.
1300  // Work out which 2 hold the faces to be extruded and thus which procpatch
1301  // the edge-face should be in. As an additional complication this might
1302  // mean that 2 procesors that were only edge-connected now suddenly need
1303  // to become face-connected i.e. have a processor patch between them.
1304 
1305  fvMesh& mesh = meshRefiner.mesh();
1306 
1307  // Determine edgePatchID. Any additional processor boundary gets added to
1308  // patchToNbrProc,nbrProcToPatch and nPatches gets set to the new number
1309  // of patches.
1310  // Note: faceZones are at this point split into baffles so any zone
1311  // information might also come from boundary faces (hence
1312  // zoneFromAnyFace set in call to calcExtrudeInfo)
1313  label nPatches;
1314  Map<label> nbrProcToPatch;
1315  Map<label> patchToNbrProc;
1317  (
1318  true, // zoneFromAnyFace
1319 
1320  mesh,
1321  globalFaces,
1322  edgeGlobalFaces,
1323  pp,
1324 
1325  edgePatchID,
1326  nPatches,
1327  nbrProcToPatch,
1328  patchToNbrProc,
1329  edgeZoneID,
1330  edgeFlip,
1331  inflateFaceID
1332  );
1333 
1334  label nOldPatches = mesh.boundaryMesh().size();
1335  label nAdded = returnReduce(nPatches-nOldPatches, sumOp<label>());
1336  Info<< nl << "Adding in total " << nAdded/2 << " inter-processor patches to"
1337  << " handle extrusion of non-manifold processor boundaries."
1338  << endl;
1339 
1340  if (nAdded > 0)
1341  {
1342  // We might not add patches in same order as in patchToNbrProc
1343  // so prepare to renumber edgePatchID
1344  Map<label> wantedToAddedPatch;
1345 
1346  for (label patchi = nOldPatches; patchi < nPatches; patchi++)
1347  {
1348  label nbrProci = patchToNbrProc[patchi];
1349  word name
1350  (
1352  );
1353 
1354  dictionary patchDict;
1355  patchDict.add("type", processorPolyPatch::typeName);
1356  patchDict.add("myProcNo", Pstream::myProcNo());
1357  patchDict.add("neighbProcNo", nbrProci);
1358  patchDict.add("nFaces", 0);
1359  patchDict.add("startFace", mesh.nFaces());
1360 
1361  //Pout<< "Adding patch " << patchi
1362  // << " name:" << name
1363  // << " between " << Pstream::myProcNo()
1364  // << " and " << nbrProci << endl;
1365 
1366  label procPatchi = meshRefiner.appendPatch
1367  (
1368  mesh,
1369  mesh.boundaryMesh().size(), // new patch index
1370  name,
1371  patchDict
1372  );
1373  wantedToAddedPatch.insert(patchi, procPatchi);
1374  }
1375 
1376  // Renumber edgePatchID
1377  forAll(edgePatchID, i)
1378  {
1379  label patchi = edgePatchID[i];
1380  const auto fnd = wantedToAddedPatch.cfind(patchi);
1381  if (fnd.good())
1382  {
1383  edgePatchID[i] = fnd.val();
1384  }
1385  }
1386 
1387  mesh.clearOut();
1388  const_cast<polyBoundaryMesh&>(mesh.boundaryMesh()).updateMesh();
1389  }
1390 }
1391 
1392 
1393 void Foam::snappyLayerDriver::calculateLayerThickness
1394 (
1395  const indirectPrimitivePatch& pp,
1396  const labelList& patchIDs,
1397  const layerParameters& layerParams,
1398  const labelList& cellLevel,
1399  const labelList& patchNLayers,
1400  const scalar edge0Len,
1401 
1402  scalarField& thickness,
1403  scalarField& minThickness,
1404  scalarField& expansionRatio
1405 ) const
1406 {
1407  const fvMesh& mesh = meshRefiner_.mesh();
1408  const polyBoundaryMesh& patches = mesh.boundaryMesh();
1409 
1410 
1411  // Rework patch-wise layer parameters into minimum per point
1412  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1413  // Note: only layer parameters consistent with layer specification
1414  // method (see layerParameters) will be correct.
1415  scalarField firstLayerThickness(pp.nPoints(), GREAT);
1416  scalarField finalLayerThickness(pp.nPoints(), GREAT);
1417  scalarField totalThickness(pp.nPoints(), GREAT);
1418  scalarField expRatio(pp.nPoints(), GREAT);
1419 
1420  minThickness.setSize(pp.nPoints());
1421  minThickness = GREAT;
1422 
1423  thickness.setSize(pp.nPoints());
1424  thickness = GREAT;
1425 
1426  expansionRatio.setSize(pp.nPoints());
1427  expansionRatio = GREAT;
1428 
1429  for (const label patchi : patchIDs)
1430  {
1431  const labelList& meshPoints = patches[patchi].meshPoints();
1432 
1433  forAll(meshPoints, patchPointi)
1434  {
1435  label ppPointi = pp.meshPointMap()[meshPoints[patchPointi]];
1436 
1437  firstLayerThickness[ppPointi] = min
1438  (
1439  firstLayerThickness[ppPointi],
1440  layerParams.firstLayerThickness()[patchi]
1441  );
1442  finalLayerThickness[ppPointi] = min
1443  (
1444  finalLayerThickness[ppPointi],
1445  layerParams.finalLayerThickness()[patchi]
1446  );
1447  totalThickness[ppPointi] = min
1448  (
1449  totalThickness[ppPointi],
1450  layerParams.thickness()[patchi]
1451  );
1452  expRatio[ppPointi] = min
1453  (
1454  expRatio[ppPointi],
1455  layerParams.expansionRatio()[patchi]
1456  );
1457  minThickness[ppPointi] = min
1458  (
1459  minThickness[ppPointi],
1460  layerParams.minThickness()[patchi]
1461  );
1462  }
1463  }
1464 
1466  (
1467  mesh,
1468  pp.meshPoints(),
1469  firstLayerThickness,
1470  minEqOp<scalar>(),
1471  GREAT // null value
1472  );
1474  (
1475  mesh,
1476  pp.meshPoints(),
1477  finalLayerThickness,
1478  minEqOp<scalar>(),
1479  GREAT // null value
1480  );
1482  (
1483  mesh,
1484  pp.meshPoints(),
1485  totalThickness,
1486  minEqOp<scalar>(),
1487  GREAT // null value
1488  );
1490  (
1491  mesh,
1492  pp.meshPoints(),
1493  expRatio,
1494  minEqOp<scalar>(),
1495  GREAT // null value
1496  );
1498  (
1499  mesh,
1500  pp.meshPoints(),
1501  minThickness,
1502  minEqOp<scalar>(),
1503  GREAT // null value
1504  );
1505 
1506 
1507  // Now the thicknesses are set according to the minimum of connected
1508  // patches.
1509 
1510  // Determine per point the max cell level of connected cells
1511  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1512 
1513  labelList maxPointLevel(pp.nPoints(), labelMin);
1514  {
1515  forAll(pp, i)
1516  {
1517  label ownLevel = cellLevel[mesh.faceOwner()[pp.addressing()[i]]];
1518 
1519  const face& f = pp.localFaces()[i];
1520 
1521  forAll(f, fp)
1522  {
1523  maxPointLevel[f[fp]] = max(maxPointLevel[f[fp]], ownLevel);
1524  }
1525  }
1526 
1528  (
1529  mesh,
1530  pp.meshPoints(),
1531  maxPointLevel,
1532  maxEqOp<label>(),
1533  labelMin // null value
1534  );
1535  }
1536 
1537 
1538  // Rework relative thickness into absolute
1539  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1540  // by multiplying with the internal cell size.
1541  // Note that we cannot loop over the patches since then points on
1542  // multiple patches would get multiplied with edgeLen twice ..
1543  {
1544  // Multiplication factor for relative sizes
1545  scalarField edgeLen(pp.nPoints(), GREAT);
1546 
1548 
1549  bitSet isRelativePoint(mesh.nPoints());
1550 
1551  for (const label patchi : patchIDs)
1552  {
1553  const labelList& meshPoints = patches[patchi].meshPoints();
1554  const layerParameters::thicknessModelType patchSpec =
1555  layerParams.layerModels()[patchi];
1556  const bool relSize = layerParams.relativeSizes()[patchi];
1557 
1558  for (const label meshPointi : meshPoints)
1559  {
1560  const label ppPointi = pp.meshPointMap()[meshPointi];
1561 
1562  // Note: who wins if different specs?
1563 
1564  // Calculate undistorted edge size for this level.
1565  edgeLen[ppPointi] = min
1566  (
1567  edgeLen[ppPointi],
1568  edge0Len/(1<<maxPointLevel[ppPointi])
1569  );
1570  spec[ppPointi] = max(spec[ppPointi], patchSpec);
1571  isRelativePoint[meshPointi] =
1572  isRelativePoint[meshPointi]
1573  || relSize;
1574  }
1575  }
1576 
1578  (
1579  mesh,
1580  pp.meshPoints(),
1581  edgeLen,
1582  minEqOp<scalar>(),
1583  GREAT // null value
1584  );
1586  (
1587  mesh,
1588  pp.meshPoints(),
1589  spec,
1590  maxEqOp<label>(),
1591  label(layerParameters::FIRST_AND_TOTAL) // null value
1592  );
1594  (
1595  mesh,
1596  isRelativePoint,
1597  orEqOp<unsigned int>(),
1598  0
1599  );
1600 
1601 
1602 
1603 
1604  forAll(pp.meshPoints(), pointi)
1605  {
1606  const label meshPointi = pp.meshPoints()[pointi];
1607  const layerParameters::thicknessModelType pointSpec =
1608  static_cast<layerParameters::thicknessModelType>(spec[pointi]);
1609 
1611  {
1612  // This overrules the relative sizes flag for
1613  // first (always absolute) and final (always relative)
1614  finalLayerThickness[pointi] *= edgeLen[pointi];
1615  if (isRelativePoint[meshPointi])
1616  {
1617  totalThickness[pointi] *= edgeLen[pointi];
1618  minThickness[pointi] *= edgeLen[pointi];
1619  }
1620  }
1621  else if (isRelativePoint[meshPointi])
1622  {
1623  firstLayerThickness[pointi] *= edgeLen[pointi];
1624  finalLayerThickness[pointi] *= edgeLen[pointi];
1625  totalThickness[pointi] *= edgeLen[pointi];
1626  minThickness[pointi] *= edgeLen[pointi];
1627  }
1628 
1629  thickness[pointi] = min
1630  (
1631  thickness[pointi],
1633  (
1634  pointSpec,
1635  patchNLayers[pointi],
1636  firstLayerThickness[pointi],
1637  finalLayerThickness[pointi],
1638  totalThickness[pointi],
1639  expRatio[pointi]
1640  )
1641  );
1642  expansionRatio[pointi] = min
1643  (
1644  expansionRatio[pointi],
1645  layerParameters::layerExpansionRatio
1646  (
1647  pointSpec,
1648  patchNLayers[pointi],
1649  firstLayerThickness[pointi],
1650  finalLayerThickness[pointi],
1651  totalThickness[pointi],
1652  expRatio[pointi]
1653  )
1654  );
1655  }
1656  }
1657 
1658  // Synchronise the determined thicknes. Note that this should not be
1659  // necessary since the inputs to the calls to layerThickness,
1660  // layerExpansionRatio above are already parallel consistent
1661 
1663  (
1664  mesh,
1665  pp.meshPoints(),
1666  thickness,
1667  minEqOp<scalar>(),
1668  GREAT // null value
1669  );
1671  (
1672  mesh,
1673  pp.meshPoints(),
1674  expansionRatio,
1675  minEqOp<scalar>(),
1676  GREAT // null value
1677  );
1678 
1679  //Info<< "calculateLayerThickness : " << gMinMax(thickness) << endl;
1680 
1681  // Print a bit
1682  {
1683  const polyBoundaryMesh& patches = mesh.boundaryMesh();
1684 
1685  const int oldPrecision = Info.stream().precision();
1686 
1687  // Find maximum length of a patch name, for a nicer output
1688  label maxPatchNameLen = 0;
1689  forAll(patchIDs, i)
1690  {
1691  label patchi = patchIDs[i];
1692  word patchName = patches[patchi].name();
1693  maxPatchNameLen = max(maxPatchNameLen, label(patchName.size()));
1694  }
1695 
1696  Info<< nl
1697  << setf(ios_base::left) << setw(maxPatchNameLen) << "patch"
1698  << setw(0) << " faces layers avg thickness[m]" << nl
1699  << setf(ios_base::left) << setw(maxPatchNameLen) << " "
1700  << setw(0) << " near-wall overall" << nl
1701  << setf(ios_base::left) << setw(maxPatchNameLen) << "-----"
1702  << setw(0) << " ----- ------ --------- -------" << endl;
1703 
1704 
1705  const bitSet isMasterPoint(syncTools::getMasterPoints(mesh));
1706 
1707  forAll(patchIDs, i)
1708  {
1709  label patchi = patchIDs[i];
1710 
1711  const labelList& meshPoints = patches[patchi].meshPoints();
1713  layerParams.layerModels()[patchi];
1714 
1715  scalar sumThickness = 0;
1716  scalar sumNearWallThickness = 0;
1717  label nMasterPoints = 0;
1718 
1719  forAll(meshPoints, patchPointi)
1720  {
1721  label meshPointi = meshPoints[patchPointi];
1722  if (isMasterPoint[meshPointi])
1723  {
1724  label ppPointi = pp.meshPointMap()[meshPointi];
1725 
1726  sumThickness += thickness[ppPointi];
1727  sumNearWallThickness += layerParams.firstLayerThickness
1728  (
1729  spec,
1730  patchNLayers[ppPointi],
1731  firstLayerThickness[ppPointi],
1732  finalLayerThickness[ppPointi],
1733  thickness[ppPointi],
1734  expansionRatio[ppPointi]
1735  );
1736  nMasterPoints++;
1737  }
1738  }
1739 
1740  label totNPoints = returnReduce(nMasterPoints, sumOp<label>());
1741 
1742  // For empty patches, totNPoints is 0.
1743  scalar avgThickness = 0;
1744  scalar avgNearWallThickness = 0;
1745 
1746  if (totNPoints > 0)
1747  {
1748  avgThickness =
1749  returnReduce(sumThickness, sumOp<scalar>())
1750  / totNPoints;
1751  avgNearWallThickness =
1752  returnReduce(sumNearWallThickness, sumOp<scalar>())
1753  / totNPoints;
1754  }
1755 
1756  Info<< setf(ios_base::left) << setw(maxPatchNameLen)
1757  << patches[patchi].name() << setprecision(3)
1758  << " " << setw(8)
1759  << returnReduce(patches[patchi].size(), sumOp<scalar>())
1760  << " " << setw(6) << layerParams.numLayers()[patchi]
1761  << " " << setw(8) << avgNearWallThickness
1762  << " " << setw(8) << avgThickness
1763  << endl;
1764  }
1765  Info<< setprecision(oldPrecision) << endl;
1766  }
1767 }
1768 
1769 
1770 // Synchronize displacement among coupled patches.
1771 void Foam::snappyLayerDriver::syncPatchDisplacement
1772 (
1773  const fvMesh& mesh,
1774  const indirectPrimitivePatch& pp,
1775  const scalarField& minThickness,
1776  pointField& patchDisp,
1777  labelList& patchNLayers,
1778  List<extrudeMode>& extrudeStatus
1779 )
1780 {
1781  //const fvMesh& mesh = meshRefiner.mesh();
1782  const labelList& meshPoints = pp.meshPoints();
1783 
1784  //label nChangedTotal = 0;
1785 
1786  while (true)
1787  {
1788  label nChanged = 0;
1789 
1790  // Sync displacement (by taking min)
1792  (
1793  mesh,
1794  meshPoints,
1795  patchDisp,
1796  minMagSqrEqOp<vector>(),
1797  point::rootMax // null value
1798  );
1799 
1800  // Unmark if displacement too small
1801  forAll(patchDisp, i)
1802  {
1803  if (mag(patchDisp[i]) < minThickness[i])
1804  {
1805  if
1806  (
1807  unmarkExtrusion
1808  (
1809  i,
1810  patchDisp,
1811  patchNLayers,
1812  extrudeStatus
1813  )
1814  )
1815  {
1816  nChanged++;
1817  }
1818  }
1819  }
1820 
1821  labelList syncPatchNLayers(patchNLayers);
1822 
1824  (
1825  mesh,
1826  meshPoints,
1827  syncPatchNLayers,
1828  minEqOp<label>(),
1829  labelMax // null value
1830  );
1831 
1832  // Reset if differs
1833  // 1. take max
1834  forAll(syncPatchNLayers, i)
1835  {
1836  if (syncPatchNLayers[i] != patchNLayers[i])
1837  {
1838  if
1839  (
1840  unmarkExtrusion
1841  (
1842  i,
1843  patchDisp,
1844  patchNLayers,
1845  extrudeStatus
1846  )
1847  )
1848  {
1849  nChanged++;
1850  }
1851  }
1852  }
1853 
1855  (
1856  mesh,
1857  meshPoints,
1858  syncPatchNLayers,
1859  maxEqOp<label>(),
1860  labelMin // null value
1861  );
1862 
1863  // Reset if differs
1864  // 2. take min
1865  forAll(syncPatchNLayers, i)
1866  {
1867  if (syncPatchNLayers[i] != patchNLayers[i])
1868  {
1869  if
1870  (
1871  unmarkExtrusion
1872  (
1873  i,
1874  patchDisp,
1875  patchNLayers,
1876  extrudeStatus
1877  )
1878  )
1879  {
1880  nChanged++;
1881  }
1882  }
1883  }
1884  //nChangedTotal += nChanged;
1885 
1886  if (!returnReduceOr(nChanged))
1887  {
1888  break;
1889  }
1890  }
1891 
1892  //Info<< "Prevented extrusion on "
1893  // << returnReduce(nChangedTotal, sumOp<label>())
1894  // << " coupled patch points during syncPatchDisplacement." << endl;
1895 }
1896 
1897 
1898 // Calculate displacement vector for all patch points. Uses pointNormal.
1899 // Checks that displaced patch point would be visible from all centres
1900 // of the faces using it.
1901 // extrudeStatus is both input and output and gives the status of each
1902 // patch point.
1903 void Foam::snappyLayerDriver::getPatchDisplacement
1904 (
1905  const indirectPrimitivePatch& pp,
1906  const scalarField& thickness,
1907  const scalarField& minThickness,
1908  const scalarField& expansionRatio,
1909 
1910  pointField& patchDisp,
1911  labelList& patchNLayers,
1912  List<extrudeMode>& extrudeStatus
1913 ) const
1914 {
1915  Info<< nl << "Determining displacement for added points"
1916  << " according to pointNormal ..." << endl;
1917 
1918  const fvMesh& mesh = meshRefiner_.mesh();
1919  const vectorField& faceNormals = pp.faceNormals();
1920  const labelListList& pointFaces = pp.pointFaces();
1921  const pointField& localPoints = pp.localPoints();
1922 
1923  // Determine pointNormal
1924  // ~~~~~~~~~~~~~~~~~~~~~
1925 
1926  pointField pointNormals(PatchTools::pointNormals(mesh, pp));
1927 
1928 
1929  // Determine local length scale on patch
1930  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1931 
1932  // Start off from same thickness everywhere (except where no extrusion)
1933  patchDisp = thickness*pointNormals;
1934 
1935 
1936  label nNoVisNormal = 0;
1937  label nExtrudeRemove = 0;
1938 
1939 
1941 // {
1942 // OBJstream twoStr
1943 // (
1944 // mesh.time().path()
1945 // / "twoFacePoints_"
1946 // + meshRefiner_.timeName()
1947 // + ".obj"
1948 // );
1949 // OBJstream multiStr
1950 // (
1951 // mesh.time().path()
1952 // / "multiFacePoints_"
1953 // + meshRefiner_.timeName()
1954 // + ".obj"
1955 // );
1956 // Pout<< "Writing points inbetween two faces on same cell to "
1957 // << twoStr.name() << endl;
1958 // Pout<< "Writing points inbetween three or more faces on same cell to "
1959 // << multiStr.name() << endl;
1960 // // Check whether inbetween patch faces on same cell
1961 // Map<labelList> cellToFaces;
1962 // forAll(pointNormals, patchPointi)
1963 // {
1964 // const labelList& pFaces = pointFaces[patchPointi];
1965 //
1966 // cellToFaces.clear();
1967 // forAll(pFaces, pFacei)
1968 // {
1969 // const label patchFacei = pFaces[pFacei];
1970 // const label meshFacei = pp.addressing()[patchFacei];
1971 // const label celli = mesh.faceOwner()[meshFacei];
1972 //
1973 // cellToFaces(celli).push_uniq(patchFacei);
1974 // }
1975 //
1976 // forAllConstIters(cellToFaces, iter)
1977 // {
1978 // if (iter().size() == 2)
1979 // {
1980 // twoStr.write(pp.localPoints()[patchPointi]);
1981 // }
1982 // else if (iter().size() > 2)
1983 // {
1984 // multiStr.write(pp.localPoints()[patchPointi]);
1985 //
1986 // const scalar ratio =
1987 // layerParameters::finalLayerThicknessRatio
1988 // (
1989 // patchNLayers[patchPointi],
1990 // expansionRatio[patchPointi]
1991 // );
1992 // // Get thickness of cell next to bulk
1993 // const vector finalDisp
1994 // (
1995 // ratio*patchDisp[patchPointi]
1996 // );
1997 //
1998 // //Pout<< "** point:" << pp.localPoints()[patchPointi]
1999 // // << " on cell:" << iter.key()
2000 // // << " faces:" << iter()
2001 // // << " displacement was:" << patchDisp[patchPointi]
2002 // // << " ratio:" << ratio
2003 // // << " finalDispl:" << finalDisp;
2004 //
2005 // // Half this thickness
2006 // patchDisp[patchPointi] -= 0.8*finalDisp;
2007 //
2008 // //Pout<< " new displacement:"
2009 // // << patchDisp[patchPointi] << endl;
2010 // }
2011 // }
2012 // }
2013 //
2014 // Pout<< "Written " << multiStr.nVertices()
2015 // << " points inbetween three or more faces on same cell to "
2016 // << multiStr.name() << endl;
2017 // }
2019 
2020 
2021  // Check if no extrude possible.
2022  forAll(pointNormals, patchPointi)
2023  {
2024  label meshPointi = pp.meshPoints()[patchPointi];
2025 
2026  if (extrudeStatus[patchPointi] == NOEXTRUDE)
2027  {
2028  // Do not use unmarkExtrusion; forcibly set to zero extrusion.
2029  patchNLayers[patchPointi] = 0;
2030  patchDisp[patchPointi] = Zero;
2031  }
2032  else
2033  {
2034  // Get normal
2035  const vector& n = pointNormals[patchPointi];
2036 
2037  if (!meshTools::visNormal(n, faceNormals, pointFaces[patchPointi]))
2038  {
2040  {
2041  Pout<< "No valid normal for point " << meshPointi
2042  << ' ' << pp.points()[meshPointi]
2043  << "; setting displacement to "
2044  << patchDisp[patchPointi]
2045  << endl;
2046  }
2047 
2048  extrudeStatus[patchPointi] = EXTRUDEREMOVE;
2049  nNoVisNormal++;
2050  }
2051  }
2052  }
2053 
2054  // At illegal points make displacement average of new neighbour positions
2055  forAll(extrudeStatus, patchPointi)
2056  {
2057  if (extrudeStatus[patchPointi] == EXTRUDEREMOVE)
2058  {
2059  point avg(Zero);
2060  label nPoints = 0;
2061 
2062  const labelList& pEdges = pp.pointEdges()[patchPointi];
2063 
2064  forAll(pEdges, i)
2065  {
2066  label edgei = pEdges[i];
2067 
2068  label otherPointi = pp.edges()[edgei].otherVertex(patchPointi);
2069 
2070  if (extrudeStatus[otherPointi] != NOEXTRUDE)
2071  {
2072  avg += localPoints[otherPointi] + patchDisp[otherPointi];
2073  nPoints++;
2074  }
2075  }
2076 
2077  if (nPoints > 0)
2078  {
2080  {
2081  Pout<< "Displacement at illegal point "
2082  << localPoints[patchPointi]
2083  << " set to "
2084  << (avg / nPoints - localPoints[patchPointi])
2085  << endl;
2086  }
2087 
2088  patchDisp[patchPointi] =
2089  avg / nPoints
2090  - localPoints[patchPointi];
2091 
2092  nExtrudeRemove++;
2093  }
2094  else
2095  {
2096  // All surrounding points are not extruded. Leave patchDisp
2097  // intact.
2098  }
2099  }
2100  }
2101 
2102  Info<< "Detected " << returnReduce(nNoVisNormal, sumOp<label>())
2103  << " points with point normal pointing through faces." << nl
2104  << "Reset displacement at "
2105  << returnReduce(nExtrudeRemove, sumOp<label>())
2106  << " points to average of surrounding points." << endl;
2107 
2108  // Make sure displacement is equal on both sides of coupled patches.
2109  syncPatchDisplacement
2110  (
2111  mesh,
2112  pp,
2113  minThickness,
2114  patchDisp,
2115  patchNLayers,
2116  extrudeStatus
2117  );
2118 
2119  Info<< endl;
2120 }
2121 
2122 
2123 bool Foam::snappyLayerDriver::sameEdgeNeighbour
2124 (
2125  const labelListList& globalEdgeFaces,
2126  const label myGlobalFacei,
2127  const label nbrGlobFacei,
2128  const label edgei
2129 ) const
2130 {
2131  const labelList& eFaces = globalEdgeFaces[edgei];
2132  if (eFaces.size() == 2)
2133  {
2134  return edge(myGlobalFacei, nbrGlobFacei) == edge(eFaces[0], eFaces[1]);
2135  }
2136 
2137  return false;
2138 }
2139 
2140 
2141 void Foam::snappyLayerDriver::getVertexString
2142 (
2143  const indirectPrimitivePatch& pp,
2144  const labelListList& globalEdgeFaces,
2145  const label facei,
2146  const label edgei,
2147  const label myGlobFacei,
2148  const label nbrGlobFacei,
2149  DynamicList<label>& vertices
2150 ) const
2151 {
2152  const labelList& fEdges = pp.faceEdges()[facei];
2153  label fp = fEdges.find(edgei);
2154 
2155  if (fp == -1)
2156  {
2158  << "problem." << abort(FatalError);
2159  }
2160 
2161  // Search back
2162  label startFp = fp;
2163 
2164  forAll(fEdges, i)
2165  {
2166  label prevFp = fEdges.rcIndex(startFp);
2167  if
2168  (
2169  !sameEdgeNeighbour
2170  (
2171  globalEdgeFaces,
2172  myGlobFacei,
2173  nbrGlobFacei,
2174  fEdges[prevFp]
2175  )
2176  )
2177  {
2178  break;
2179  }
2180  startFp = prevFp;
2181  }
2182 
2183  label endFp = fp;
2184  forAll(fEdges, i)
2185  {
2186  label nextFp = fEdges.fcIndex(endFp);
2187  if
2188  (
2189  !sameEdgeNeighbour
2190  (
2191  globalEdgeFaces,
2192  myGlobFacei,
2193  nbrGlobFacei,
2194  fEdges[nextFp]
2195  )
2196  )
2197  {
2198  break;
2199  }
2200  endFp = nextFp;
2201  }
2202 
2203  const face& f = pp.localFaces()[facei];
2204  vertices.clear();
2205  fp = startFp;
2206  while (fp != endFp)
2207  {
2208  vertices.append(f[fp]);
2209  fp = f.fcIndex(fp);
2210  }
2211  vertices.append(f[fp]);
2212  fp = f.fcIndex(fp);
2213  vertices.append(f[fp]);
2214 }
2215 
2216 
2217 // Truncates displacement
2218 // - for all patchFaces in the faceset displacement gets set to zero
2219 // - all displacement < minThickness gets set to zero
2220 Foam::label Foam::snappyLayerDriver::truncateDisplacement
2221 (
2222  const globalIndex& globalFaces,
2223  const labelListList& edgeGlobalFaces,
2224  const indirectPrimitivePatch& pp,
2225  const scalarField& minThickness,
2226  const faceSet& illegalPatchFaces,
2227  pointField& patchDisp,
2228  labelList& patchNLayers,
2229  List<extrudeMode>& extrudeStatus
2230 ) const
2231 {
2232  const fvMesh& mesh = meshRefiner_.mesh();
2233 
2234  label nChanged = 0;
2235 
2236  const Map<label>& meshPointMap = pp.meshPointMap();
2237 
2238  for (const label facei : illegalPatchFaces)
2239  {
2240  if (mesh.isInternalFace(facei))
2241  {
2243  << "Faceset " << illegalPatchFaces.name()
2244  << " contains internal face " << facei << nl
2245  << "It should only contain patch faces" << abort(FatalError);
2246  }
2247 
2248  const face& f = mesh.faces()[facei];
2249 
2250 
2251  forAll(f, fp)
2252  {
2253  const auto fnd = meshPointMap.cfind(f[fp]);
2254  if (fnd.good())
2255  {
2256  const label patchPointi = fnd.val();
2257 
2258  if (extrudeStatus[patchPointi] != NOEXTRUDE)
2259  {
2260  unmarkExtrusion
2261  (
2262  patchPointi,
2263  patchDisp,
2264  patchNLayers,
2265  extrudeStatus
2266  );
2267  nChanged++;
2268  }
2269  }
2270  }
2271  }
2272 
2273  forAll(patchDisp, patchPointi)
2274  {
2275  if (mag(patchDisp[patchPointi]) < minThickness[patchPointi])
2276  {
2277  if
2278  (
2279  unmarkExtrusion
2280  (
2281  patchPointi,
2282  patchDisp,
2283  patchNLayers,
2284  extrudeStatus
2285  )
2286  )
2287  {
2288  nChanged++;
2289  }
2290  }
2291  else if (extrudeStatus[patchPointi] == NOEXTRUDE)
2292  {
2293  // Make sure displacement is 0. Should already be so but ...
2294  patchDisp[patchPointi] = Zero;
2295  patchNLayers[patchPointi] = 0;
2296  }
2297  }
2298 
2299 
2300  const faceList& localFaces = pp.localFaces();
2301 
2302  while (true)
2303  {
2304  syncPatchDisplacement
2305  (
2306  mesh,
2307  pp,
2308  minThickness,
2309  patchDisp,
2310  patchNLayers,
2311  extrudeStatus
2312  );
2313 
2314 
2315  // Pinch
2316  // ~~~~~
2317 
2318  // Make sure that a face doesn't have two non-consecutive areas
2319  // not extruded (e.g. quad where vertex 0 and 2 are not extruded
2320  // but 1 and 3 are) since this gives topological errors.
2321 
2322  label nPinched = 0;
2323 
2324  forAll(localFaces, i)
2325  {
2326  const face& localF = localFaces[i];
2327 
2328  // Count number of transitions from unsnapped to snapped.
2329  label nTrans = 0;
2330 
2331  extrudeMode prevMode = extrudeStatus[localF.prevLabel(0)];
2332 
2333  forAll(localF, fp)
2334  {
2335  extrudeMode fpMode = extrudeStatus[localF[fp]];
2336 
2337  if (prevMode == NOEXTRUDE && fpMode != NOEXTRUDE)
2338  {
2339  nTrans++;
2340  }
2341  prevMode = fpMode;
2342  }
2343 
2344  if (nTrans > 1)
2345  {
2346  // Multiple pinches. Reset whole face as unextruded.
2347  if
2348  (
2349  unmarkExtrusion
2350  (
2351  localF,
2352  patchDisp,
2353  patchNLayers,
2354  extrudeStatus
2355  )
2356  )
2357  {
2358  nPinched++;
2359  nChanged++;
2360  }
2361  }
2362  }
2363 
2364  reduce(nPinched, sumOp<label>());
2365 
2366  Info<< "truncateDisplacement : Unextruded " << nPinched
2367  << " faces due to non-consecutive vertices being extruded." << endl;
2368 
2369 
2370  // Butterfly
2371  // ~~~~~~~~~
2372 
2373  // Make sure that a string of edges becomes a single face so
2374  // not a butterfly. Occasionally an 'edge' will have a single dangling
2375  // vertex due to face combining. These get extruded as a single face
2376  // (with a dangling vertex) so make sure this extrusion forms a single
2377  // shape.
2378  // - continuous i.e. no butterfly:
2379  // + +
2380  // |\ /|
2381  // | \ / |
2382  // +--+--+
2383  // - extrudes from all but the endpoints i.e. no partial
2384  // extrude
2385  // +
2386  // /|
2387  // / |
2388  // +--+--+
2389  // The common error topology is a pinch somewhere in the middle
2390  label nButterFly = 0;
2391  {
2392  DynamicList<label> stringedVerts;
2393  forAll(pp.edges(), edgei)
2394  {
2395  const labelList& globFaces = edgeGlobalFaces[edgei];
2396 
2397  if (globFaces.size() == 2)
2398  {
2399  label myFacei = pp.edgeFaces()[edgei][0];
2400  label myGlobalFacei = globalFaces.toGlobal
2401  (
2402  pp.addressing()[myFacei]
2403  );
2404  label nbrGlobalFacei =
2405  (
2406  globFaces[0] != myGlobalFacei
2407  ? globFaces[0]
2408  : globFaces[1]
2409  );
2410  getVertexString
2411  (
2412  pp,
2413  edgeGlobalFaces,
2414  myFacei,
2415  edgei,
2416  myGlobalFacei,
2417  nbrGlobalFacei,
2418  stringedVerts
2419  );
2420 
2421  if
2422  (
2423  extrudeStatus[stringedVerts[0]] != NOEXTRUDE
2424  || extrudeStatus[stringedVerts.last()] != NOEXTRUDE
2425  )
2426  {
2427  // Any pinch in the middle
2428  bool pinch = false;
2429  for (label i = 1; i < stringedVerts.size()-1; i++)
2430  {
2431  if (extrudeStatus[stringedVerts[i]] == NOEXTRUDE)
2432  {
2433  pinch = true;
2434  break;
2435  }
2436  }
2437  if (pinch)
2438  {
2439  forAll(stringedVerts, i)
2440  {
2441  if
2442  (
2443  unmarkExtrusion
2444  (
2445  stringedVerts[i],
2446  patchDisp,
2447  patchNLayers,
2448  extrudeStatus
2449  )
2450  )
2451  {
2452  nButterFly++;
2453  nChanged++;
2454  }
2455  }
2456  }
2457  }
2458  }
2459  }
2460  }
2461 
2462  reduce(nButterFly, sumOp<label>());
2463 
2464  Info<< "truncateDisplacement : Unextruded " << nButterFly
2465  << " faces due to stringed edges with inconsistent extrusion."
2466  << endl;
2467 
2468 
2469 
2470  // Consistent number of layers
2471  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
2472 
2473  // Make sure that a face has consistent number of layers for all
2474  // its vertices.
2475 
2476  label nDiffering = 0;
2477 
2478  //forAll(localFaces, i)
2479  //{
2480  // const face& localF = localFaces[i];
2481  //
2482  // label numLayers = -1;
2483  //
2484  // forAll(localF, fp)
2485  // {
2486  // if (patchNLayers[localF[fp]] > 0)
2487  // {
2488  // if (numLayers == -1)
2489  // {
2490  // numLayers = patchNLayers[localF[fp]];
2491  // }
2492  // else if (numLayers != patchNLayers[localF[fp]])
2493  // {
2494  // // Differing number of layers
2495  // if
2496  // (
2497  // unmarkExtrusion
2498  // (
2499  // localF,
2500  // patchDisp,
2501  // patchNLayers,
2502  // extrudeStatus
2503  // )
2504  // )
2505  // {
2506  // nDiffering++;
2507  // nChanged++;
2508  // }
2509  // break;
2510  // }
2511  // }
2512  // }
2513  //}
2514  //
2515  //reduce(nDiffering, sumOp<label>());
2516  //
2517  //Info<< "truncateDisplacement : Unextruded " << nDiffering
2518  // << " faces due to having differing number of layers." << endl;
2519 
2520  if (nPinched+nButterFly+nDiffering == 0)
2521  {
2522  break;
2523  }
2524  }
2525 
2526  return nChanged;
2527 }
2528 
2529 
2530 // Setup layer information (at points and faces) to modify mesh topology in
2531 // regions where layer mesh terminates.
2532 void Foam::snappyLayerDriver::setupLayerInfoTruncation
2533 (
2534  const indirectPrimitivePatch& pp,
2535  const labelList& patchNLayers,
2536  const List<extrudeMode>& extrudeStatus,
2537  const label nBufferCellsNoExtrude,
2538  labelList& nPatchPointLayers,
2539  labelList& nPatchFaceLayers
2540 ) const
2541 {
2542  Info<< nl << "Setting up information for layer truncation ..." << endl;
2543 
2544  const fvMesh& mesh = meshRefiner_.mesh();
2545 
2546  if (nBufferCellsNoExtrude < 0)
2547  {
2548  Info<< nl << "Performing no layer truncation."
2549  << " nBufferCellsNoExtrude set to less than 0 ..." << endl;
2550 
2551  // Face layers if any point gets extruded
2552  forAll(pp.localFaces(), patchFacei)
2553  {
2554  const face& f = pp.localFaces()[patchFacei];
2555 
2556  forAll(f, fp)
2557  {
2558  const label nPointLayers = patchNLayers[f[fp]];
2559  if (nPointLayers > 0)
2560  {
2561  if (nPatchFaceLayers[patchFacei] == -1)
2562  {
2563  nPatchFaceLayers[patchFacei] = nPointLayers;
2564  }
2565  else
2566  {
2567  nPatchFaceLayers[patchFacei] = min
2568  (
2569  nPatchFaceLayers[patchFacei],
2570  nPointLayers
2571  );
2572  }
2573  }
2574  }
2575  }
2576  nPatchPointLayers = patchNLayers;
2577 
2578  // Set any unset patch face layers
2579  forAll(nPatchFaceLayers, patchFacei)
2580  {
2581  if (nPatchFaceLayers[patchFacei] == -1)
2582  {
2583  nPatchFaceLayers[patchFacei] = 0;
2584  }
2585  }
2586  }
2587  else
2588  {
2589  // Determine max point layers per face.
2590  labelList maxLevel(pp.size(), Zero);
2591 
2592  forAll(pp.localFaces(), patchFacei)
2593  {
2594  const face& f = pp.localFaces()[patchFacei];
2595 
2596  // find patch faces where layer terminates (i.e contains extrude
2597  // and noextrude points).
2598 
2599  bool noExtrude = false;
2600  label mLevel = 0;
2601 
2602  forAll(f, fp)
2603  {
2604  if (extrudeStatus[f[fp]] == NOEXTRUDE)
2605  {
2606  noExtrude = true;
2607  }
2608  mLevel = max(mLevel, patchNLayers[f[fp]]);
2609  }
2610 
2611  if (mLevel > 0)
2612  {
2613  // So one of the points is extruded. Check if all are extruded
2614  // or is a mix.
2615 
2616  if (noExtrude)
2617  {
2618  nPatchFaceLayers[patchFacei] = 1;
2619  maxLevel[patchFacei] = mLevel;
2620  }
2621  else
2622  {
2623  maxLevel[patchFacei] = mLevel;
2624  }
2625  }
2626  }
2627 
2628  // We have the seed faces (faces with nPatchFaceLayers != maxLevel)
2629  // Now do a meshwave across the patch where we pick up neighbours
2630  // of seed faces.
2631  // Note: quite inefficient. Could probably be coded better.
2632 
2633  const labelListList& pointFaces = pp.pointFaces();
2634 
2635  label nLevels = gMax(patchNLayers);
2636 
2637  // flag neighbouring patch faces with number of layers to grow
2638  for (label ilevel = 1; ilevel < nLevels; ilevel++)
2639  {
2640  label nBuffer;
2641 
2642  if (ilevel == 1)
2643  {
2644  nBuffer = nBufferCellsNoExtrude - 1;
2645  }
2646  else
2647  {
2648  nBuffer = nBufferCellsNoExtrude;
2649  }
2650 
2651  for (label ibuffer = 0; ibuffer < nBuffer + 1; ibuffer++)
2652  {
2653  labelList tempCounter(nPatchFaceLayers);
2654 
2655  boolList foundNeighbour(pp.nPoints(), false);
2656 
2657  forAll(pp.meshPoints(), patchPointi)
2658  {
2659  forAll(pointFaces[patchPointi], pointFacei)
2660  {
2661  label facei = pointFaces[patchPointi][pointFacei];
2662 
2663  if
2664  (
2665  nPatchFaceLayers[facei] != -1
2666  && maxLevel[facei] > 0
2667  )
2668  {
2669  foundNeighbour[patchPointi] = true;
2670  break;
2671  }
2672  }
2673  }
2674 
2676  (
2677  mesh,
2678  pp.meshPoints(),
2679  foundNeighbour,
2680  orEqOp<bool>(),
2681  false // null value
2682  );
2683 
2684  forAll(pp.meshPoints(), patchPointi)
2685  {
2686  if (foundNeighbour[patchPointi])
2687  {
2688  forAll(pointFaces[patchPointi], pointFacei)
2689  {
2690  label facei = pointFaces[patchPointi][pointFacei];
2691  if
2692  (
2693  nPatchFaceLayers[facei] == -1
2694  && maxLevel[facei] > 0
2695  && ilevel < maxLevel[facei]
2696  )
2697  {
2698  tempCounter[facei] = ilevel;
2699  }
2700  }
2701  }
2702  }
2703  nPatchFaceLayers = tempCounter;
2704  }
2705  }
2706 
2707  forAll(pp.localFaces(), patchFacei)
2708  {
2709  if (nPatchFaceLayers[patchFacei] == -1)
2710  {
2711  nPatchFaceLayers[patchFacei] = maxLevel[patchFacei];
2712  }
2713  }
2714 
2715  forAll(pp.meshPoints(), patchPointi)
2716  {
2717  if (extrudeStatus[patchPointi] != NOEXTRUDE)
2718  {
2719  forAll(pointFaces[patchPointi], pointFacei)
2720  {
2721  label face = pointFaces[patchPointi][pointFacei];
2722  nPatchPointLayers[patchPointi] = max
2723  (
2724  nPatchPointLayers[patchPointi],
2725  nPatchFaceLayers[face]
2726  );
2727  }
2728  }
2729  else
2730  {
2731  nPatchPointLayers[patchPointi] = 0;
2732  }
2733  }
2735  (
2736  mesh,
2737  pp.meshPoints(),
2738  nPatchPointLayers,
2739  maxEqOp<label>(),
2740  label(0) // null value
2741  );
2742  }
2743 }
2744 
2745 
2746 // Does any of the cells use a face from faces?
2747 bool Foam::snappyLayerDriver::cellsUseFace
2748 (
2749  const polyMesh& mesh,
2750  const labelList& cellLabels,
2751  const labelHashSet& faces
2752 )
2753 {
2754  forAll(cellLabels, i)
2755  {
2756  const cell& cFaces = mesh.cells()[cellLabels[i]];
2757 
2758  forAll(cFaces, cFacei)
2759  {
2760  if (faces.found(cFaces[cFacei]))
2761  {
2762  return true;
2763  }
2764  }
2765  }
2766 
2767  return false;
2768 }
2769 
2770 
2771 // Checks the newly added cells and locally unmarks points so they
2772 // will not get extruded next time round. Returns global number of unmarked
2773 // points (0 if all was fine)
2774 Foam::label Foam::snappyLayerDriver::checkAndUnmark
2775 (
2776  const addPatchCellLayer& addLayer,
2777  const dictionary& meshQualityDict,
2778  const bool additionalReporting,
2779  const List<labelPair>& baffles,
2780  const indirectPrimitivePatch& pp,
2781  const fvMesh& newMesh,
2782 
2783  pointField& patchDisp,
2784  labelList& patchNLayers,
2785  List<extrudeMode>& extrudeStatus
2786 )
2787 {
2788  // Check the resulting mesh for errors
2789  Info<< nl << "Checking mesh with layer ..." << endl;
2790  faceSet wrongFaces(newMesh, "wrongFaces", newMesh.nFaces()/1000);
2792  (
2793  false,
2794  newMesh,
2795  meshQualityDict,
2796  identity(newMesh.nFaces()),
2797  baffles,
2798  wrongFaces,
2799  false // dryRun_
2800  );
2801  Info<< "Detected " << returnReduce(wrongFaces.size(), sumOp<label>())
2802  << " illegal faces"
2803  << " (concave, zero area or negative cell pyramid volume)"
2804  << endl;
2805 
2806  // Undo local extrusion if
2807  // - any of the added cells in error
2808 
2809  label nChanged = 0;
2810 
2811  // Get all cells in the layer.
2812  labelListList addedCells
2813  (
2815  (
2816  newMesh,
2817  addLayer.layerFaces()
2818  )
2819  );
2820 
2821  // Check if any of the faces in error uses any face of an added cell
2822  // - if additionalReporting print the few remaining areas for ease of
2823  // finding out where the problems are.
2824 
2825  const label nReportMax = 10;
2826  DynamicField<point> disabledFaceCentres(nReportMax);
2827 
2828  forAll(addedCells, oldPatchFacei)
2829  {
2830  // Get the cells (in newMesh labels) per old patch face (in mesh
2831  // labels)
2832  const labelList& fCells = addedCells[oldPatchFacei];
2833 
2834  if (cellsUseFace(newMesh, fCells, wrongFaces))
2835  {
2836  // Unmark points on old mesh
2837  if
2838  (
2839  unmarkExtrusion
2840  (
2841  pp.localFaces()[oldPatchFacei],
2842  patchDisp,
2843  patchNLayers,
2844  extrudeStatus
2845  )
2846  )
2847  {
2848  if (additionalReporting && (nChanged < nReportMax))
2849  {
2850  disabledFaceCentres.append
2851  (
2852  pp.faceCentres()[oldPatchFacei]
2853  );
2854  }
2855 
2856  nChanged++;
2857  }
2858  }
2859  }
2860 
2861 
2862  label nChangedTotal = returnReduce(nChanged, sumOp<label>());
2863 
2864  if (additionalReporting)
2865  {
2866  // Limit the number of points to be printed so that
2867  // not too many points are reported when running in parallel
2868  // Not accurate, i.e. not always nReportMax points are written,
2869  // but this estimation avoid some communication here.
2870  // The important thing, however, is that when only a few faces
2871  // are disabled, their coordinates are printed, and this should be
2872  // the case
2873  label nReportLocal = nChanged;
2874  if (nChangedTotal > nReportMax)
2875  {
2876  nReportLocal = min
2877  (
2878  max(nChangedTotal / Pstream::nProcs(), 1),
2879  min
2880  (
2881  nChanged,
2882  max(nReportMax / Pstream::nProcs(), 1)
2883  )
2884  );
2885  }
2886 
2887  if (nReportLocal)
2888  {
2889  Pout<< "Checked mesh with layers. Disabled extrusion at " << endl;
2890  for (label i=0; i < nReportLocal; i++)
2891  {
2892  Pout<< " " << disabledFaceCentres[i] << endl;
2893  }
2894  }
2895 
2896  label nReportTotal = returnReduce(nReportLocal, sumOp<label>());
2897 
2898  if (nReportTotal < nChangedTotal)
2899  {
2900  Info<< "Suppressed disabled extrusion message for other "
2901  << nChangedTotal - nReportTotal << " faces." << endl;
2902  }
2903  }
2904 
2905  return nChangedTotal;
2906 }
2907 
2908 
2909 //- Count global number of extruded faces
2910 Foam::label Foam::snappyLayerDriver::countExtrusion
2911 (
2912  const indirectPrimitivePatch& pp,
2913  const List<extrudeMode>& extrudeStatus
2914 )
2915 {
2916  // Count number of extruded patch faces
2917  label nExtruded = 0;
2918  {
2919  const faceList& localFaces = pp.localFaces();
2920 
2921  forAll(localFaces, i)
2922  {
2923  const face& localFace = localFaces[i];
2924 
2925  forAll(localFace, fp)
2926  {
2927  if (extrudeStatus[localFace[fp]] != NOEXTRUDE)
2928  {
2929  nExtruded++;
2930  break;
2931  }
2932  }
2933  }
2934  }
2935 
2936  return returnReduce(nExtruded, sumOp<label>());
2937 }
2938 
2939 
2940 Foam::List<Foam::labelPair> Foam::snappyLayerDriver::getBafflesOnAddedMesh
2941 (
2942  const polyMesh& mesh,
2943  const labelList& newToOldFaces,
2944  const List<labelPair>& baffles
2945 )
2946 {
2947  // The problem is that the baffle faces are now inside the
2948  // mesh (addPatchCellLayer modifies original boundary faces and
2949  // adds new ones. So 2 pass:
2950  // - find the boundary face for all faces originating from baffle
2951  // - use the boundary face for the new baffles
2952 
2953  Map<label> baffleSet(4*baffles.size());
2954  forAll(baffles, bafflei)
2955  {
2956  baffleSet.insert(baffles[bafflei][0], bafflei);
2957  baffleSet.insert(baffles[bafflei][1], bafflei);
2958  }
2959 
2960 
2961  List<labelPair> newBaffles(baffles.size(), labelPair(-1, -1));
2962  for
2963  (
2964  label facei = mesh.nInternalFaces();
2965  facei < mesh.nFaces();
2966  facei++
2967  )
2968  {
2969  label oldFacei = newToOldFaces[facei];
2970 
2971  const auto faceFnd = baffleSet.find(oldFacei);
2972  if (faceFnd.good())
2973  {
2974  label bafflei = faceFnd();
2975  labelPair& p = newBaffles[bafflei];
2976  if (p[0] == -1)
2977  {
2978  p[0] = facei;
2979  }
2980  else if (p[1] == -1)
2981  {
2982  p[1] = facei;
2983  }
2984  else
2985  {
2987  << "Problem:" << facei << " at:"
2988  << mesh.faceCentres()[facei]
2989  << " is on same baffle as " << p[0]
2990  << " at:" << mesh.faceCentres()[p[0]]
2991  << " and " << p[1]
2992  << " at:" << mesh.faceCentres()[p[1]]
2993  << exit(FatalError);
2994  }
2995  }
2996  }
2997  return newBaffles;
2998 }
2999 
3000 
3001 // Collect layer faces and layer cells into mesh fields for ease of handling
3002 void Foam::snappyLayerDriver::getLayerCellsFaces
3003 (
3004  const polyMesh& mesh,
3005  const addPatchCellLayer& addLayer,
3006  const scalarField& oldRealThickness,
3007 
3008  labelList& cellNLayers,
3009  scalarField& faceRealThickness
3010 )
3011 {
3012  cellNLayers.setSize(mesh.nCells());
3013  cellNLayers = 0;
3014  faceRealThickness.setSize(mesh.nFaces());
3015  faceRealThickness = 0;
3016 
3017  // Mark all faces in the layer
3018  const labelListList& layerFaces = addLayer.layerFaces();
3019 
3020  // Mark all cells in the layer.
3021  labelListList addedCells(addPatchCellLayer::addedCells(mesh, layerFaces));
3022 
3023  forAll(addedCells, oldPatchFacei)
3024  {
3025  const labelList& added = addedCells[oldPatchFacei];
3026 
3027  const labelList& layer = layerFaces[oldPatchFacei];
3028 
3029  if (layer.size())
3030  {
3031  // Leave out original internal face
3032  forAll(added, i)
3033  {
3034  cellNLayers[added[i]] = layer.size()-1;
3035  }
3036  }
3037  }
3038 
3039  forAll(layerFaces, oldPatchFacei)
3040  {
3041  const labelList& layer = layerFaces[oldPatchFacei];
3042  const scalar realThickness = oldRealThickness[oldPatchFacei];
3043 
3044  if (layer.size())
3045  {
3046  // Layer contains both original boundary face and new boundary
3047  // face so is nLayers+1. Leave out old internal face.
3048  for (label i = 1; i < layer.size(); i++)
3049  {
3050  faceRealThickness[layer[i]] = realThickness;
3051  }
3052  }
3053  }
3054 }
3055 
3056 
3057 void Foam::snappyLayerDriver::printLayerData
3058 (
3059  const fvMesh& mesh,
3060  const labelList& patchIDs,
3061  const labelList& cellNLayers,
3062  const scalarField& faceWantedThickness,
3063  const scalarField& faceRealThickness,
3064  const layerParameters& layerParams
3065 ) const
3066 {
3067  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3068 
3069  const int oldPrecision = Info.stream().precision();
3070 
3071  // Find maximum length of a patch name, for a nicer output
3072  label maxPatchNameLen = 0;
3073  forAll(patchIDs, i)
3074  {
3075  label patchi = patchIDs[i];
3076  word patchName = pbm[patchi].name();
3077  maxPatchNameLen = max(maxPatchNameLen, label(patchName.size()));
3078  }
3079 
3080  // Print some global mesh statistics
3081  meshRefiner_.printMeshInfo(false, "Mesh with layers", false);
3082 
3083  Info<< nl
3084  << setf(ios_base::left) << setw(maxPatchNameLen) << "patch"
3085  << setw(0) << " faces layers overall thickness" << nl
3086  << setf(ios_base::left) << setw(maxPatchNameLen) << " "
3087  << setw(0) << " target mesh [m] [%]" << nl
3088  << setf(ios_base::left) << setw(maxPatchNameLen) << "-----"
3089  << setw(0) << " ----- ----- ---- --- ---" << endl;
3090 
3091 
3092  forAll(patchIDs, i)
3093  {
3094  label patchi = patchIDs[i];
3095  const polyPatch& pp = pbm[patchi];
3096 
3097  label sumSize = pp.size();
3098 
3099  // Number of layers
3100  const labelUList& faceCells = pp.faceCells();
3101  label sumNLayers = 0;
3102  forAll(faceCells, i)
3103  {
3104  sumNLayers += cellNLayers[faceCells[i]];
3105  }
3106 
3107  // Thickness
3108  scalarField::subField patchWanted = pbm[patchi].patchSlice
3109  (
3110  faceWantedThickness
3111  );
3112  scalarField::subField patchReal = pbm[patchi].patchSlice
3113  (
3114  faceRealThickness
3115  );
3116 
3117  scalar sumRealThickness = sum(patchReal);
3118  scalar sumFraction = 0;
3119  forAll(patchReal, i)
3120  {
3121  if (patchWanted[i] > VSMALL)
3122  {
3123  sumFraction += (patchReal[i]/patchWanted[i]);
3124  }
3125  }
3126 
3127 
3128  reduce(sumSize, sumOp<label>());
3129  reduce(sumNLayers, sumOp<label>());
3130  reduce(sumRealThickness, sumOp<scalar>());
3131  reduce(sumFraction, sumOp<scalar>());
3132 
3133 
3134  scalar avgLayers = 0;
3135  scalar avgReal = 0;
3136  scalar avgFraction = 0;
3137  if (sumSize > 0)
3138  {
3139  avgLayers = scalar(sumNLayers)/sumSize;
3140  avgReal = sumRealThickness/sumSize;
3141  avgFraction = sumFraction/sumSize;
3142  }
3143 
3144  Info<< setf(ios_base::left) << setw(maxPatchNameLen)
3145  << pbm[patchi].name() << setprecision(3)
3146  << " " << setw(8) << sumSize
3147  << " " << setw(8) << layerParams.numLayers()[patchi]
3148  << " " << setw(8) << avgLayers
3149  << " " << setw(8) << avgReal
3150  << " " << setw(8) << 100*avgFraction
3151  << endl;
3152  }
3153  Info<< setprecision(oldPrecision) << endl;
3154 }
3155 
3156 
3157 bool Foam::snappyLayerDriver::writeLayerSets
3158 (
3159  const fvMesh& mesh,
3160  const labelList& cellNLayers,
3161  const scalarField& faceRealThickness
3162 ) const
3163 {
3164  bool allOk = true;
3165  {
3166  label nAdded = 0;
3167  forAll(cellNLayers, celli)
3168  {
3169  if (cellNLayers[celli] > 0)
3170  {
3171  nAdded++;
3172  }
3173  }
3174  cellSet addedCellSet(mesh, "addedCells", nAdded);
3175  forAll(cellNLayers, celli)
3176  {
3177  if (cellNLayers[celli] > 0)
3178  {
3179  addedCellSet.insert(celli);
3180  }
3181  }
3182  addedCellSet.instance() = meshRefiner_.timeName();
3183  Info<< "Writing "
3184  << returnReduce(addedCellSet.size(), sumOp<label>())
3185  << " added cells to cellSet "
3186  << addedCellSet.name() << endl;
3187  bool ok = addedCellSet.write();
3188  allOk = allOk && ok;
3189  }
3190  {
3191  label nAdded = 0;
3192  for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
3193  {
3194  if (faceRealThickness[facei] > 0)
3195  {
3196  nAdded++;
3197  }
3198  }
3199 
3200  faceSet layerFacesSet(mesh, "layerFaces", nAdded);
3201  for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
3202  {
3203  if (faceRealThickness[facei] > 0)
3204  {
3205  layerFacesSet.insert(facei);
3206  }
3207  }
3208  layerFacesSet.instance() = meshRefiner_.timeName();
3209  Info<< "Writing "
3210  << returnReduce(layerFacesSet.size(), sumOp<label>())
3211  << " faces inside added layer to faceSet "
3212  << layerFacesSet.name() << endl;
3213  bool ok = layerFacesSet.write();
3214  allOk = allOk && ok;
3215  }
3216  return allOk;
3217 }
3218 
3219 
3220 bool Foam::snappyLayerDriver::writeLayerData
3221 (
3222  const fvMesh& mesh,
3223  const labelList& patchIDs,
3224  const labelList& cellNLayers,
3225  const scalarField& faceWantedThickness,
3226  const scalarField& faceRealThickness
3227 ) const
3228 {
3229  bool allOk = true;
3230 
3232  {
3233  bool ok = writeLayerSets(mesh, cellNLayers, faceRealThickness);
3234  allOk = allOk && ok;
3235  }
3236 
3238  {
3239  Info<< nl << "Writing fields with layer information:" << incrIndent
3240  << endl;
3241  {
3243  (
3244  IOobject
3245  (
3246  "nSurfaceLayers",
3247  mesh.time().timeName(),
3248  mesh,
3252  ),
3253  mesh,
3255  fixedValueFvPatchScalarField::typeName
3256  );
3257  forAll(fld, celli)
3258  {
3259  fld[celli] = cellNLayers[celli];
3260  }
3261  volScalarField::Boundary& fldBf = fld.boundaryFieldRef();
3262 
3263  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3264  forAll(patchIDs, i)
3265  {
3266  label patchi = patchIDs[i];
3267  const polyPatch& pp = pbm[patchi];
3268  const labelUList& faceCells = pp.faceCells();
3269  scalarField pfld(faceCells.size());
3270  forAll(faceCells, i)
3271  {
3272  pfld[i] = cellNLayers[faceCells[i]];
3273  }
3274  fldBf[patchi] == pfld;
3275  }
3276  Info<< indent << fld.name() << " : actual number of layers"
3277  << endl;
3278  bool ok = fld.write();
3279  allOk = allOk && ok;
3280  }
3281  {
3283  (
3284  IOobject
3285  (
3286  "thickness",
3287  mesh.time().timeName(),
3288  mesh,
3292  ),
3293  mesh,
3295  fixedValueFvPatchScalarField::typeName
3296  );
3297  volScalarField::Boundary& fldBf = fld.boundaryFieldRef();
3298  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3299  forAll(patchIDs, i)
3300  {
3301  label patchi = patchIDs[i];
3302  fldBf[patchi] == pbm[patchi].patchSlice(faceRealThickness);
3303  }
3304  Info<< indent << fld.name() << " : overall layer thickness"
3305  << endl;
3306  bool ok = fld.write();
3307  allOk = allOk && ok;
3308  }
3309  {
3311  (
3312  IOobject
3313  (
3314  "thicknessFraction",
3315  mesh.time().timeName(),
3316  mesh,
3320  ),
3321  mesh,
3323  fixedValueFvPatchScalarField::typeName
3324  );
3325  volScalarField::Boundary& fldBf = fld.boundaryFieldRef();
3326  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3327  forAll(patchIDs, i)
3328  {
3329  label patchi = patchIDs[i];
3330 
3331  scalarField::subField patchWanted = pbm[patchi].patchSlice
3332  (
3333  faceWantedThickness
3334  );
3335  scalarField::subField patchReal = pbm[patchi].patchSlice
3336  (
3337  faceRealThickness
3338  );
3339 
3340  // Convert patchReal to relative thickness
3341  scalarField pfld(patchReal.size(), Zero);
3342  forAll(patchReal, i)
3343  {
3344  if (patchWanted[i] > VSMALL)
3345  {
3346  pfld[i] = patchReal[i]/patchWanted[i];
3347  }
3348  }
3349 
3350  fldBf[patchi] == pfld;
3351  }
3352  Info<< indent << fld.name()
3353  << " : overall layer thickness (fraction"
3354  << " of desired thickness)" << endl;
3355  bool ok = fld.write();
3356  allOk = allOk && ok;
3357  }
3358  Info<< decrIndent<< endl;
3359  }
3360 
3361  return allOk;
3363 
3364 
3366 (
3367  meshRefinement& meshRefiner,
3368  const labelList& patchIDs, // patch indices
3369  const labelList& numLayers, // number of layers per patch
3370  List<labelPair> baffles, // pairs of baffles (input & updated)
3371  labelList& pointToMaster // -1 or index of original point (duplicated
3372  // point)
3373 )
3374 {
3375  fvMesh& mesh = meshRefiner.mesh();
3376 
3377  // Check outside of baffles for non-manifoldness
3378 
3379  // Points that are candidates for duplication
3380  labelList candidatePoints;
3381  {
3382  // Do full analysis to see if we need to extrude points
3383  // so have to duplicate them
3385  (
3387  (
3388  mesh,
3389  patchIDs
3390  )
3391  );
3392 
3393  // Displacement for all pp.localPoints. Set to large value to
3394  // avoid truncation in syncPatchDisplacement because of
3395  // minThickness.
3396  vectorField patchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT));
3397  labelList patchNLayers(pp().nPoints(), Zero);
3398  label nIdealTotAddedCells = 0;
3399  List<extrudeMode> extrudeStatus(pp().nPoints(), EXTRUDE);
3400  // Get number of layers per point from number of layers per patch
3401  setNumLayers
3402  (
3403  meshRefiner,
3404  numLayers, // per patch the num layers
3405  patchIDs, // patches that are being moved
3406  *pp, // indirectpatch for all faces moving
3407 
3408  patchNLayers,
3409  extrudeStatus,
3410  nIdealTotAddedCells
3411  );
3412  // Make sure displacement is equal on both sides of coupled patches.
3413  // Note that we explicitly disable the minThickness truncation
3414  // of the patchDisp here.
3415  syncPatchDisplacement
3416  (
3417  mesh,
3418  *pp,
3419  scalarField(patchDisp.size(), Zero), //minThickness,
3420  patchDisp,
3421  patchNLayers,
3422  extrudeStatus
3423  );
3424 
3425 
3426  // Do duplication only if all patch points decide to extrude. Ignore
3427  // contribution from non-patch points. Note that we need to
3428  // apply this to all mesh points
3429  labelList minPatchState(mesh.nPoints(), labelMax);
3430  forAll(extrudeStatus, patchPointi)
3431  {
3432  label pointi = pp().meshPoints()[patchPointi];
3433  minPatchState[pointi] = extrudeStatus[patchPointi];
3434  }
3435 
3437  (
3438  mesh,
3439  minPatchState,
3440  minEqOp<label>(), // combine op
3441  labelMax // null value
3442  );
3443 
3444  // So now minPatchState:
3445  // - labelMax on non-patch points
3446  // - NOEXTRUDE if any patch point was not extruded
3447  // - EXTRUDE or EXTRUDEREMOVE if all patch points are extruded/
3448  // extrudeRemove.
3449 
3450  label n = 0;
3451  forAll(minPatchState, pointi)
3452  {
3453  label state = minPatchState[pointi];
3454  if (state == EXTRUDE || state == EXTRUDEREMOVE)
3455  {
3456  n++;
3457  }
3458  }
3459  candidatePoints.setSize(n);
3460  n = 0;
3461  forAll(minPatchState, pointi)
3462  {
3463  label state = minPatchState[pointi];
3464  if (state == EXTRUDE || state == EXTRUDEREMOVE)
3465  {
3466  candidatePoints[n++] = pointi;
3467  }
3468  }
3469  }
3470 
3471  // Not duplicate points on either side of baffles that don't get any
3472  // layers
3473  labelPairList nonDupBaffles;
3474 
3475  {
3476  // faceZones that are not being duplicated
3477  DynamicList<label> nonDupZones(mesh.faceZones().size());
3478 
3479  labelHashSet layerIDs(patchIDs);
3480  forAll(mesh.faceZones(), zonei)
3481  {
3482  label mpi, spi;
3484  bool hasInfo = meshRefiner.getFaceZoneInfo
3485  (
3486  mesh.faceZones()[zonei].name(),
3487  mpi,
3488  spi,
3489  fzType
3490  );
3491  if (hasInfo && !layerIDs.found(mpi) && !layerIDs.found(spi))
3492  {
3493  nonDupZones.append(zonei);
3494  }
3495  }
3496  nonDupBaffles = meshRefinement::subsetBaffles
3497  (
3498  mesh,
3499  nonDupZones,
3501  );
3502  }
3503 
3504 
3505  const localPointRegion regionSide(mesh, nonDupBaffles, candidatePoints);
3506 
3507  autoPtr<mapPolyMesh> map = meshRefiner.dupNonManifoldPoints
3508  (
3509  regionSide
3510  );
3511 
3512  if (map)
3513  {
3514  // Store point duplication
3515  pointToMaster.setSize(mesh.nPoints(), -1);
3516 
3517  const labelList& pointMap = map().pointMap();
3518  const labelList& reversePointMap = map().reversePointMap();
3519 
3520  forAll(pointMap, pointi)
3521  {
3522  label oldPointi = pointMap[pointi];
3523  label newMasterPointi = reversePointMap[oldPointi];
3524 
3525  if (newMasterPointi != pointi)
3526  {
3527  // Found slave. Mark both master and slave
3528  pointToMaster[pointi] = newMasterPointi;
3529  pointToMaster[newMasterPointi] = newMasterPointi;
3530  }
3531  }
3532 
3533  // Update baffle numbering
3534  {
3535  const labelList& reverseFaceMap = map().reverseFaceMap();
3536  forAll(baffles, i)
3537  {
3538  label f0 = reverseFaceMap[baffles[i].first()];
3539  label f1 = reverseFaceMap[baffles[i].second()];
3540  baffles[i] = labelPair(f0, f1);
3541  }
3542  }
3543 
3544 
3546  {
3547  const_cast<Time&>(mesh.time())++;
3548  Info<< "Writing point-duplicate mesh to time "
3549  << meshRefiner.timeName() << endl;
3550 
3551  meshRefiner.write
3552  (
3555  (
3558  ),
3559  mesh.time().path()/meshRefiner.timeName()
3560  );
3561 
3562  OBJstream str
3563  (
3564  mesh.time().path()
3565  / "duplicatePoints_"
3566  + meshRefiner.timeName()
3567  + ".obj"
3568  );
3569  Info<< "Writing point-duplicates to " << str.name() << endl;
3570  const pointField& p = mesh.points();
3571  forAll(pointMap, pointi)
3572  {
3573  label newMasteri = reversePointMap[pointMap[pointi]];
3574 
3575  if (newMasteri != pointi)
3576  {
3577  str.writeLine(p[pointi], p[newMasteri]);
3578  }
3579  }
3580  }
3581  }
3582  return map;
3583 }
3584 
3585 
3586 void Foam::snappyLayerDriver::mergeFaceZonePoints
3587 (
3588  const labelList& pointToMaster, // -1 or index of duplicated point
3589  labelList& cellNLayers,
3590  scalarField& faceRealThickness,
3591  scalarField& faceWantedThickness
3592 )
3593 {
3594  // Near opposite of dupFaceZonePoints : merge points and baffles introduced
3595  // for internal faceZones
3596 
3597  fvMesh& mesh = meshRefiner_.mesh();
3598 
3599  // Count duplicate points
3600  label nPointPairs = 0;
3601  forAll(pointToMaster, pointi)
3602  {
3603  label otherPointi = pointToMaster[pointi];
3604  if (otherPointi != -1)
3605  {
3606  nPointPairs++;
3607  }
3608  }
3609  reduce(nPointPairs, sumOp<label>());
3610  if (nPointPairs > 0)
3611  {
3612  // Merge any duplicated points
3613  Info<< "Merging " << nPointPairs << " duplicated points ..." << endl;
3614 
3616  {
3617  OBJstream str
3618  (
3619  mesh.time().path()
3620  / "mergePoints_"
3621  + meshRefiner_.timeName()
3622  + ".obj"
3623  );
3624  Info<< "Points to be merged to " << str.name() << endl;
3625  forAll(pointToMaster, pointi)
3626  {
3627  label otherPointi = pointToMaster[pointi];
3628  if (otherPointi != -1)
3629  {
3630  const point& pt = mesh.points()[pointi];
3631  const point& otherPt = mesh.points()[otherPointi];
3632  str.writeLine(pt, otherPt);
3633  }
3634  }
3635  }
3636 
3637 
3638  autoPtr<mapPolyMesh> map = meshRefiner_.mergePoints(pointToMaster);
3639  if (map)
3640  {
3641  inplaceReorder(map().reverseCellMap(), cellNLayers);
3642 
3643  const labelList& reverseFaceMap = map().reverseFaceMap();
3644  inplaceReorder(reverseFaceMap, faceWantedThickness);
3645  inplaceReorder(reverseFaceMap, faceRealThickness);
3646 
3647  Info<< "Merged points in = "
3648  << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
3649  }
3650  }
3651 
3652  if (mesh.faceZones().size() > 0)
3653  {
3654  // Merge any baffles
3655  Info<< "Converting baffles back into zoned faces ..."
3656  << endl;
3657 
3658  autoPtr<mapPolyMesh> map = meshRefiner_.mergeZoneBaffles
3659  (
3660  true, // internal zones
3661  false // baffle zones
3662  );
3663  if (map)
3664  {
3665  inplaceReorder(map().reverseCellMap(), cellNLayers);
3666 
3667  const labelList& faceMap = map().faceMap();
3668 
3669  // Make sure to keep the max since on two patches only one has
3670  // layers.
3671  scalarField newFaceRealThickness(mesh.nFaces(), Zero);
3672  scalarField newFaceWantedThickness(mesh.nFaces(), Zero);
3673  forAll(newFaceRealThickness, facei)
3674  {
3675  label oldFacei = faceMap[facei];
3676  if (oldFacei >= 0)
3677  {
3678  scalar& realThick = newFaceRealThickness[facei];
3679  realThick = max(realThick, faceRealThickness[oldFacei]);
3680  scalar& wanted = newFaceWantedThickness[facei];
3681  wanted = max(wanted, faceWantedThickness[oldFacei]);
3682  }
3683  }
3684  faceRealThickness.transfer(newFaceRealThickness);
3685  faceWantedThickness.transfer(newFaceWantedThickness);
3686  }
3687 
3688  Info<< "Converted baffles in = "
3689  << meshRefiner_.mesh().time().cpuTimeIncrement()
3690  << " s\n" << nl << endl;
3691  }
3692 }
3693 
3694 
3695 Foam::label Foam::snappyLayerDriver::setPointNumLayers
3696 (
3697  const layerParameters& layerParams,
3698 
3699  const labelList& numLayers,
3700  const labelList& patchIDs,
3701  const indirectPrimitivePatch& pp,
3702  const labelListList& edgeGlobalFaces,
3703 
3704  vectorField& patchDisp,
3705  labelList& patchNLayers,
3706  List<extrudeMode>& extrudeStatus
3707 ) const
3708 {
3709  fvMesh& mesh = meshRefiner_.mesh();
3710 
3711  patchDisp.setSize(pp.nPoints());
3712  patchDisp = vector(GREAT, GREAT, GREAT);
3713 
3714  // Number of layers for all pp.localPoints. Note: only valid if
3715  // extrudeStatus = EXTRUDE.
3716  patchNLayers.setSize(pp.nPoints());
3717  patchNLayers = Zero;
3718 
3719  // Ideal number of cells added
3720  label nIdealTotAddedCells = 0;
3721 
3722  // Whether to add edge for all pp.localPoints.
3723  extrudeStatus.setSize(pp.nPoints());
3724  extrudeStatus = EXTRUDE;
3725 
3726 
3727  // Get number of layers per point from number of layers per patch
3728  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3729 
3730  setNumLayers
3731  (
3732  meshRefiner_,
3733  numLayers, // per patch the num layers
3734  patchIDs, // patches that are being moved
3735  pp, // indirectpatch for all faces moving
3736 
3737  patchNLayers,
3738  extrudeStatus,
3739  nIdealTotAddedCells
3740  );
3741 
3742  // Precalculate mesh edge labels for patch edges
3743  labelList meshEdges(pp.meshEdges(mesh.edges(), mesh.pointEdges()));
3744 
3745 
3746  // Disable extrusion on split strings of common points
3747  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3748 
3749  handleNonStringConnected
3750  (
3751  pp,
3752  patchDisp,
3753  patchNLayers,
3754  extrudeStatus
3755  );
3756 
3757 
3758  // Disable extrusion on non-manifold points
3759  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3760 
3761  handleNonManifolds
3762  (
3763  pp,
3764  meshEdges,
3765  edgeGlobalFaces,
3766 
3767  patchDisp,
3768  patchNLayers,
3769  extrudeStatus
3770  );
3771 
3772  // Disable extrusion on feature angles
3773  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3774 
3775  handleFeatureAngle
3776  (
3777  pp,
3778  meshEdges,
3779  layerParams.featureAngle(),
3780 
3781  patchDisp,
3782  patchNLayers,
3783  extrudeStatus
3784  );
3785 
3786  // Disable extrusion on warped faces
3787  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3788  // It is hard to calculate some length scale if not in relative
3789  // mode so disable this check.
3790  if (!layerParams.relativeSizes().found(false))
3791  {
3792  // Undistorted edge length
3793  const scalar edge0Len =
3794  meshRefiner_.meshCutter().level0EdgeLength();
3795  const labelList& cellLevel = meshRefiner_.meshCutter().cellLevel();
3796 
3797  handleWarpedFaces
3798  (
3799  pp,
3800  layerParams.maxFaceThicknessRatio(),
3801  layerParams.relativeSizes(),
3802  edge0Len,
3803  cellLevel,
3804 
3805  patchDisp,
3806  patchNLayers,
3807  extrudeStatus
3808  );
3809  }
3810 
3813  //
3814  //handleMultiplePatchFaces
3815  //(
3816  // pp,
3817  //
3818  // patchDisp,
3819  // patchNLayers,
3820  // extrudeStatus
3821  //);
3822 
3823  addProfiling(grow, "snappyHexMesh::layers::grow");
3824 
3825  // Grow out region of non-extrusion
3826  for (label i = 0; i < layerParams.nGrow(); i++)
3827  {
3828  growNoExtrusion
3829  (
3830  pp,
3831  patchDisp,
3832  patchNLayers,
3833  extrudeStatus
3834  );
3835  }
3836  return nIdealTotAddedCells;
3837 }
3838 
3839 
3841 Foam::snappyLayerDriver::makeMeshMover
3842 (
3843  const layerParameters& layerParams,
3844  const dictionary& motionDict,
3845  const labelList& internalFaceZones,
3846  const scalarIOField& minThickness,
3847  pointVectorField& displacement
3848 ) const
3849 {
3850  // Allocate run-time selectable mesh mover
3851 
3852  fvMesh& mesh = meshRefiner_.mesh();
3853 
3854 
3855  // Set up controls for meshMover
3856  dictionary combinedDict(layerParams.dict());
3857  // Add mesh quality constraints
3858  combinedDict.merge(motionDict);
3859  // Where to get minThickness from
3860  combinedDict.add("minThicknessName", minThickness.name());
3861 
3862  const List<labelPair> internalBaffles
3863  (
3865  (
3866  mesh,
3867  internalFaceZones,
3869  )
3870  );
3871 
3872  // Take over patchDisp as boundary conditions on displacement
3873  // pointVectorField
3874  autoPtr<Foam::externalDisplacementMeshMover> medialAxisMoverPtr
3875  (
3877  (
3878  layerParams.meshShrinker(),
3879  combinedDict,
3880  internalBaffles,
3881  displacement
3882  )
3883  );
3884 
3885 
3886  if (dryRun_)
3887  {
3888  string errorMsg(FatalError.message());
3889  string IOerrorMsg(FatalIOError.message());
3890 
3891  if (errorMsg.size() || IOerrorMsg.size())
3892  {
3893  //errorMsg = "[dryRun] " + errorMsg;
3894  //errorMsg.replaceAll("\n", "\n[dryRun] ");
3895  //IOerrorMsg = "[dryRun] " + IOerrorMsg;
3896  //IOerrorMsg.replaceAll("\n", "\n[dryRun] ");
3897 
3898  IOWarningInFunction(combinedDict)
3899  << nl
3900  << "Missing/incorrect required dictionary entries:"
3901  << nl << nl
3902  << IOerrorMsg.c_str() << nl
3903  << errorMsg.c_str() << nl << nl
3904  << "Exiting dry-run" << nl << endl;
3905 
3906  if (UPstream::parRun())
3907  {
3908  Perr<< "\nFOAM parallel run exiting\n" << endl;
3909  UPstream::exit(0);
3910  }
3911  else
3912  {
3913  Perr<< "\nFOAM exiting\n" << endl;
3914  std::exit(0);
3915  }
3916  }
3917  }
3918  return medialAxisMoverPtr;
3920 
3921 
3923 (
3924  const layerParameters& layerParams,
3925  const label nLayerIter,
3926 
3927  // Mesh quality provision
3928  const dictionary& motionDict,
3929  const label nRelaxedIter,
3930  const label nAllowableErrors,
3931 
3932  const labelList& patchIDs,
3933  const labelList& internalFaceZones,
3934  const List<labelPair>& baffles,
3935  const labelList& numLayers,
3936  const label nIdealTotAddedCells,
3937 
3938  const globalIndex& globalFaces,
3940 
3941  const labelListList& edgeGlobalFaces,
3942  const labelList& edgePatchID,
3943  const labelList& edgeZoneID,
3944  const boolList& edgeFlip,
3945  const labelList& inflateFaceID,
3946 
3947  const scalarField& thickness,
3948  const scalarIOField& minThickness,
3949  const scalarField& expansionRatio,
3950 
3951  // Displacement for all pp.localPoints. Set to large value so does
3952  // not trigger the minThickness truncation (see syncPatchDisplacement below)
3953  vectorField& patchDisp,
3954 
3955  // Number of layers for all pp.localPoints. Note: only valid if
3956  // extrudeStatus = EXTRUDE.
3957  labelList& patchNLayers,
3958 
3959  // Whether to add edge for all pp.localPoints.
3960  List<extrudeMode>& extrudeStatus,
3961 
3962 
3963  polyTopoChange& savedMeshMod,
3964 
3965 
3966  // Per cell 0 or number of layers in the cell column it is part of
3967  labelList& cellNLayers,
3968  // Per face actual overall layer thickness
3969  scalarField& faceRealThickness
3970 )
3971 {
3972  fvMesh& mesh = meshRefiner_.mesh();
3973 
3974  // Overall displacement field
3975  pointVectorField displacement
3976  (
3977  makeLayerDisplacementField
3978  (
3980  numLayers
3981  )
3982  );
3983 
3984  // Allocate run-time selectable mesh mover
3985  autoPtr<externalDisplacementMeshMover> medialAxisMoverPtr
3986  (
3987  makeMeshMover
3988  (
3989  layerParams,
3990  motionDict,
3991  internalFaceZones,
3992  minThickness,
3993  displacement
3994  )
3995  );
3996 
3997 
3998  // Saved old points
3999  const pointField oldPoints(mesh.points());
4000 
4001  for (label iteration = 0; iteration < nLayerIter; iteration++)
4002  {
4003  Info<< nl
4004  << "Layer addition iteration " << iteration << nl
4005  << "--------------------------" << endl;
4006 
4007 
4008  // Unset the extrusion at the pp.
4009  const dictionary& meshQualityDict =
4010  (
4011  iteration < nRelaxedIter
4012  ? motionDict
4013  : motionDict.subDict("relaxed")
4014  );
4015 
4016  if (iteration >= nRelaxedIter)
4017  {
4018  Info<< "Switched to relaxed meshQuality constraints." << endl;
4019  }
4020 
4021 
4022 
4023  // Make sure displacement is equal on both sides of coupled patches.
4024  // Note that this also does the patchDisp < minThickness truncation
4025  // so for the first pass make sure the patchDisp is larger than
4026  // that.
4027  syncPatchDisplacement
4028  (
4029  mesh,
4030  pp,
4031  minThickness,
4032  patchDisp,
4033  patchNLayers,
4034  extrudeStatus
4035  );
4036 
4037  // Displacement acc. to pointnormals
4038  getPatchDisplacement
4039  (
4040  pp,
4041  thickness,
4042  minThickness,
4043  expansionRatio,
4044 
4045  patchDisp,
4046  patchNLayers,
4047  extrudeStatus
4048  );
4049 
4050  // Shrink mesh by displacement value first.
4051  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4052 
4053  {
4054  const pointField oldPatchPos(pp.localPoints());
4055 
4056  // We have patchDisp which is the outwards pointing
4057  // extrusion distance. Convert into an inwards pointing
4058  // shrink distance
4059  patchDisp = -patchDisp;
4060 
4061  // Take over patchDisp into pointDisplacement field and
4062  // adjust both for multi-patch constraints
4064  (
4065  patchIDs,
4066  pp,
4067  patchDisp,
4068  displacement
4069  );
4070 
4071 
4072  // Move mesh
4073  // ~~~~~~~~~
4074 
4075  // Set up controls for meshMover
4076  dictionary combinedDict(layerParams.dict());
4077  // Add standard quality constraints
4078  combinedDict.merge(motionDict);
4079  // Add relaxed constraints (overrides standard ones)
4080  combinedDict.merge(meshQualityDict);
4081  // Where to get minThickness from
4082  combinedDict.add("minThicknessName", minThickness.name());
4083 
4084  labelList checkFaces(identity(mesh.nFaces()));
4085  medialAxisMoverPtr().move
4086  (
4087  combinedDict,
4088  nAllowableErrors,
4089  checkFaces
4090  );
4091 
4092  pp.movePoints(mesh.points());
4093 
4094  // Unset any moving state
4095  mesh.moving(false);
4096 
4097  // Update patchDisp (since not all might have been honoured)
4098  patchDisp = oldPatchPos - pp.localPoints();
4099  }
4100 
4101  // Truncate displacements that are too small (this will do internal
4102  // ones, coupled ones have already been truncated by
4103  // syncPatchDisplacement)
4104  faceSet dummySet(mesh, "wrongPatchFaces", 0);
4105  truncateDisplacement
4106  (
4107  globalFaces,
4108  edgeGlobalFaces,
4109  pp,
4110  minThickness,
4111  dummySet,
4112  patchDisp,
4113  patchNLayers,
4114  extrudeStatus
4115  );
4116 
4117 
4118  // Dump to .obj file for debugging.
4120  {
4121  dumpDisplacement
4122  (
4123  mesh.time().path()/"layer_" + meshRefiner_.timeName(),
4124  pp,
4125  patchDisp,
4126  extrudeStatus
4127  );
4128 
4129  const_cast<Time&>(mesh.time())++;
4130  Info<< "Writing shrunk mesh to time "
4131  << meshRefiner_.timeName() << endl;
4132 
4133  // See comment in snappySnapDriver why we should not remove
4134  // meshPhi using mesh.clearOut().
4135 
4136  meshRefiner_.write
4137  (
4140  (
4143  ),
4144  mesh.time().path()/meshRefiner_.timeName()
4145  );
4146  }
4147 
4148 
4149  // Mesh topo change engine. Insert current mesh.
4150  polyTopoChange meshMod(mesh);
4151 
4152  // Grow layer of cells on to patch. Handles zero sized displacement.
4153  addPatchCellLayer addLayer(mesh);
4154 
4155  // Determine per point/per face number of layers to extrude. Also
4156  // handles the slow termination of layers when going switching
4157  // layers
4158 
4159  labelList nPatchPointLayers(pp.nPoints(), -1);
4160  labelList nPatchFaceLayers(pp.size(), -1);
4161  setupLayerInfoTruncation
4162  (
4163  pp,
4164  patchNLayers,
4165  extrudeStatus,
4166  layerParams.nBufferCellsNoExtrude(),
4167  nPatchPointLayers,
4168  nPatchFaceLayers
4169  );
4170 
4171  // Calculate displacement for final layer for addPatchLayer.
4172  // (layer of cells next to the original mesh)
4173  vectorField finalDisp(patchNLayers.size(), Zero);
4174 
4175  forAll(nPatchPointLayers, i)
4176  {
4178  (
4179  nPatchPointLayers[i],
4180  expansionRatio[i]
4181  );
4182  finalDisp[i] = ratio*patchDisp[i];
4183  }
4184 
4185 
4186  const scalarField invExpansionRatio(1.0/expansionRatio);
4187 
4188  // Add topo regardless of whether extrudeStatus is extruderemove.
4189  // Not add layer if patchDisp is zero.
4190  addLayer.setRefinement
4191  (
4192  globalFaces,
4193  edgeGlobalFaces,
4194 
4195  invExpansionRatio,
4196  pp,
4197  bitSet(pp.size()), // no flip
4198 
4199  edgePatchID, // boundary patch for extruded boundary edges
4200  edgeZoneID, // zone for extruded edges
4201  edgeFlip,
4202  inflateFaceID,
4203 
4204 
4205  labelList(0), // exposed patchIDs, not used for adding layers
4206  nPatchFaceLayers, // layers per face
4207  nPatchPointLayers, // layers per point
4208  finalDisp, // thickness of layer nearest internal mesh
4209  meshMod
4210  );
4211 
4212  if (debug)
4213  {
4214  const_cast<Time&>(mesh.time())++;
4215  }
4216 
4217  // Compact storage
4218  meshMod.shrink();
4219 
4220  // Store mesh changes for if mesh is correct.
4221  savedMeshMod = meshMod;
4222 
4223 
4224  // With the stored topo changes we create a new mesh so we can
4225  // undo if necessary.
4226 
4227  autoPtr<fvMesh> newMeshPtr;
4228  autoPtr<mapPolyMesh> mapPtr = meshMod.makeMesh
4229  (
4230  newMeshPtr,
4231  IOobject
4232  (
4233  //mesh.name()+"_layer",
4234  mesh.name(),
4235  static_cast<polyMesh&>(mesh).instance(),
4236  mesh.time(), // register with runTime
4237  IOobject::READ_IF_PRESENT, // read fv* if present
4238  static_cast<polyMesh&>(mesh).writeOpt()
4239  ), // io params from original mesh but new name
4240  mesh, // original mesh
4241  true // parallel sync
4242  );
4243  fvMesh& newMesh = *newMeshPtr;
4244  mapPolyMesh& map = *mapPtr;
4245 
4246  // Get timing, but more importantly get memory information
4247  addProfiling(grow, "snappyHexMesh::layers::updateMesh");
4248 
4249  //?necessary? Update fields
4250  newMesh.updateMesh(map);
4251 
4252  // Move mesh if in inflation mode
4253  if (map.hasMotionPoints())
4254  {
4255  newMesh.movePoints(map.preMotionPoints());
4256  }
4257  else
4258  {
4259  // Delete mesh volumes.
4260  newMesh.clearOut();
4261  }
4262 
4263  newMesh.setInstance(meshRefiner_.timeName());
4264 
4265  // Update numbering on addLayer:
4266  // - cell/point labels to be newMesh.
4267  // - patchFaces to remain in oldMesh order.
4268  addLayer.updateMesh
4269  (
4270  map,
4271  identity(pp.size()),
4272  identity(pp.nPoints())
4273  );
4274 
4275  // Collect layer faces and cells for outside loop.
4276  getLayerCellsFaces
4277  (
4278  newMesh,
4279  addLayer,
4280  avgPointData(pp, mag(patchDisp))(), // current thickness
4281 
4282  cellNLayers,
4283  faceRealThickness
4284  );
4285 
4286 
4287  // Count number of added cells
4288  label nAddedCells = 0;
4289  forAll(cellNLayers, celli)
4290  {
4291  if (cellNLayers[celli] > 0)
4292  {
4293  nAddedCells++;
4294  }
4295  }
4296 
4297 
4299  {
4300  Info<< "Writing layer mesh to time " << meshRefiner_.timeName()
4301  << endl;
4302  newMesh.write();
4303  writeLayerSets(newMesh, cellNLayers, faceRealThickness);
4304 
4305  // Reset the instance of the original mesh so next iteration
4306  // it dumps a complete mesh. This is just so that the inbetween
4307  // newMesh does not upset e.g. paraFoam cycling through the
4308  // times.
4309  mesh.setInstance(meshRefiner_.timeName());
4310  }
4311 
4312 
4313  //- Get baffles in newMesh numbering. Note that we cannot detect
4314  // baffles here since the points are duplicated
4315  List<labelPair> internalBaffles;
4316  {
4317  // From old mesh face to corresponding newMesh boundary face
4318  labelList meshToNewMesh(mesh.nFaces(), -1);
4319  for
4320  (
4321  label facei = newMesh.nInternalFaces();
4322  facei < newMesh.nFaces();
4323  facei++
4324  )
4325  {
4326  label newMeshFacei = map.faceMap()[facei];
4327  if (newMeshFacei != -1)
4328  {
4329  meshToNewMesh[newMeshFacei] = facei;
4330  }
4331  }
4332 
4333  List<labelPair> newMeshBaffles(baffles.size());
4334  label newi = 0;
4335  forAll(baffles, i)
4336  {
4337  const labelPair& p = baffles[i];
4338  labelPair newMeshBaffle
4339  (
4340  meshToNewMesh[p[0]],
4341  meshToNewMesh[p[1]]
4342  );
4343  if (newMeshBaffle[0] != -1 && newMeshBaffle[1] != -1)
4344  {
4345  newMeshBaffles[newi++] = newMeshBaffle;
4346  }
4347  }
4348  newMeshBaffles.setSize(newi);
4349 
4350  internalBaffles = meshRefinement::subsetBaffles
4351  (
4352  newMesh,
4353  internalFaceZones,
4354  newMeshBaffles
4355  );
4356 
4357  Info<< "Detected "
4358  << returnReduce(internalBaffles.size(), sumOp<label>())
4359  << " baffles across faceZones of type internal" << nl
4360  << endl;
4361  }
4362 
4363  label nTotChanged = checkAndUnmark
4364  (
4365  addLayer,
4366  meshQualityDict,
4367  layerParams.additionalReporting(),
4368  internalBaffles,
4369  pp,
4370  newMesh,
4371 
4372  patchDisp,
4373  patchNLayers,
4374  extrudeStatus
4375  );
4376 
4377  label nTotExtruded = countExtrusion(pp, extrudeStatus);
4378  label nTotFaces = returnReduce(pp.size(), sumOp<label>());
4379  label nTotAddedCells = returnReduce(nAddedCells, sumOp<label>());
4380 
4381  Info<< "Extruding " << nTotExtruded
4382  << " out of " << nTotFaces
4383  << " faces (" << 100.0*nTotExtruded/nTotFaces << "%)."
4384  << " Removed extrusion at " << nTotChanged << " faces."
4385  << endl
4386  << "Added " << nTotAddedCells << " out of "
4387  << nIdealTotAddedCells
4388  << " cells (" << 100.0*nTotAddedCells/nIdealTotAddedCells
4389  << "%)." << endl;
4390 
4391  if (nTotChanged == 0)
4392  {
4393  break;
4394  }
4395 
4396  // Reset mesh points and start again
4397  mesh.movePoints(oldPoints);
4398  pp.movePoints(mesh.points());
4399  medialAxisMoverPtr().movePoints(mesh.points());
4400 
4401  // Unset any moving state
4402  mesh.moving(false);
4403 
4404  // Grow out region of non-extrusion
4405  for (label i = 0; i < layerParams.nGrow(); i++)
4406  {
4407  growNoExtrusion
4408  (
4409  pp,
4410  patchDisp,
4411  patchNLayers,
4412  extrudeStatus
4413  );
4414  }
4415 
4416  Info<< endl;
4417  }
4418 }
4419 
4420 
4422 (
4423  meshRefinement& meshRefiner,
4424  const mapPolyMesh& map,
4425  labelPairList& baffles,
4426  labelList& pointToMaster
4427 )
4428 {
4429  fvMesh& mesh = meshRefiner.mesh();
4430 
4431  // Use geometric detection of points-to-be-merged
4432  // - detect any boundary face created from a duplicated face (=baffle)
4433  // - on these mark any point created from a duplicated point
4434  if (returnReduceOr(pointToMaster.size()))
4435  {
4436  // Estimate number of points-to-be-merged
4437  DynamicList<label> candidates(baffles.size()*4);
4438 
4439  // The problem is that all the internal layer faces also
4440  // have reverseFaceMap pointing to the old baffle face. So instead
4441  // loop over all the boundary faces and see which pair of new boundary
4442  // faces corresponds to the old baffles.
4443 
4444 
4445  // Mark whether old face was on baffle
4446  Map<label> oldFaceToBaffle(2*baffles.size());
4447  forAll(baffles, i)
4448  {
4449  const labelPair& baffle = baffles[i];
4450  oldFaceToBaffle.insert(baffle[0], i);
4451  oldFaceToBaffle.insert(baffle[1], i);
4452  }
4453 
4454  labelPairList newBaffles(baffles.size(), labelPair(-1, -1));
4455 
4456  for
4457  (
4458  label facei = mesh.nInternalFaces();
4459  facei < mesh.nFaces();
4460  facei++
4461  )
4462  {
4463  const label oldFacei = map.faceMap()[facei];
4464  const auto iter = oldFaceToBaffle.find(oldFacei);
4465  if (oldFacei != -1 && iter.good())
4466  {
4467  const label bafflei = iter();
4468  auto& newBaffle = newBaffles[bafflei];
4469  if (newBaffle[0] == -1)
4470  {
4471  newBaffle[0] = facei;
4472  }
4473  else if (newBaffle[1] == -1)
4474  {
4475  newBaffle[1] = facei;
4476  }
4477  else
4478  {
4479  FatalErrorInFunction << "face:" << facei
4480  << " at:" << mesh.faceCentres()[facei]
4481  << " already maps to baffle faces:"
4482  << newBaffle[0]
4483  << " at:" << mesh.faceCentres()[newBaffle[0]]
4484  << " and " << newBaffle[1]
4485  << " at:" << mesh.faceCentres()[newBaffle[1]]
4486  << exit(FatalError);
4487  }
4488 
4489  const face& f = mesh.faces()[facei];
4490  forAll(f, fp)
4491  {
4492  label pointi = f[fp];
4493  label oldPointi = map.pointMap()[pointi];
4494 
4495  if (pointToMaster[oldPointi] != -1)
4496  {
4497  candidates.append(pointi);
4498  }
4499  }
4500  }
4501  }
4502 
4503 
4504  // Compact newBaffles
4505  {
4506  label n = 0;
4507  forAll(newBaffles, i)
4508  {
4509  const labelPair& newBaffle = newBaffles[i];
4510  if (newBaffle[0] != -1 && newBaffle[1] != -1)
4511  {
4512  newBaffles[n++] = newBaffle;
4513  }
4514  }
4515 
4516  newBaffles.setSize(n);
4517  baffles.transfer(newBaffles);
4518  }
4519 
4520 
4521  // Do geometric merge. Ideally we'd like to use a topological
4522  // merge but we've thrown away all layer-wise addressing when
4523  // throwing away addPatchCellLayer engine. Also the addressing
4524  // is extremely complicated. There is no problem with merging
4525  // too many points; the problem would be if merging baffles.
4526  // Trust mergeZoneBaffles to do sufficient checks.
4527  labelList oldToNew;
4528  label nNew = Foam::mergePoints
4529  (
4530  UIndirectList<point>(mesh.points(), candidates),
4531  meshRefiner.mergeDistance(),
4532  false,
4533  oldToNew
4534  );
4535 
4536  // Extract points to be merged (i.e. multiple points originating
4537  // from a single one)
4538 
4539  labelListList newToOld(invertOneToMany(nNew, oldToNew));
4540 
4541  // Extract points with more than one old one
4542  pointToMaster.setSize(mesh.nPoints());
4543  pointToMaster = -1;
4544 
4545  forAll(newToOld, newi)
4546  {
4547  const labelList& oldPoints = newToOld[newi];
4548  if (oldPoints.size() > 1)
4549  {
4550  labelList meshPoints
4551  (
4552  labelUIndList(candidates, oldPoints)
4553  );
4554  label masteri = min(meshPoints);
4555  forAll(meshPoints, i)
4556  {
4557  pointToMaster[meshPoints[i]] = masteri;
4558  }
4559  }
4560  }
4561  }
4562 }
4563 
4564 
4565 void Foam::snappyLayerDriver::updatePatch
4566 (
4567  const labelList& patchIDs,
4568  const mapPolyMesh& map,
4570  labelList& newToOldPatchPoints
4571 ) const
4572 {
4573  // Update the pp to be consistent with the new mesh
4574 
4575  fvMesh& mesh = meshRefiner_.mesh();
4576 
4578  (
4580  (
4581  mesh,
4582  patchIDs
4583  )
4584  );
4585 
4586  // Map from new back to old patch points
4587  newToOldPatchPoints.setSize(newPp().nPoints());
4588  newToOldPatchPoints = -1;
4589  {
4590  const Map<label>& baseMap = pp().meshPointMap();
4591  const labelList& newMeshPoints = newPp().meshPoints();
4592 
4593  forAll(newMeshPoints, newPointi)
4594  {
4595  const label newMeshPointi = newMeshPoints[newPointi];
4596  const label oldMeshPointi =
4597  map.pointMap()[newMeshPointi];
4598  const auto iter = baseMap.find(oldMeshPointi);
4599  if (iter.good())
4600  {
4601  newToOldPatchPoints[newPointi] = iter();
4602  }
4603  }
4604  }
4605 
4606 
4607  // Reset pp. Note: make sure to use std::move - otherwise it
4608  // will release the pointer before copying and you get
4609  // memory error. Same if using autoPtr::reset.
4610  pp = std::move(newPp);
4611 
4612  // Make sure pp has adressing cached
4613  (void)pp().meshPoints();
4614  (void)pp().meshPointMap();
4615 }
4616 
4617 
4618 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
4619 
4620 Foam::snappyLayerDriver::snappyLayerDriver
4621 (
4622  meshRefinement& meshRefiner,
4623  const labelList& globalToMasterPatch,
4624  const labelList& globalToSlavePatch,
4625  const bool dryRun
4626 )
4627 :
4628  meshRefiner_(meshRefiner),
4629  globalToMasterPatch_(globalToMasterPatch),
4630  globalToSlavePatch_(globalToSlavePatch),
4631  dryRun_(dryRun)
4632 {}
4633 
4634 
4635 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
4636 
4638 (
4639  const layerParameters& layerParams,
4640  const dictionary& motionDict,
4641  const meshRefinement::FaceMergeType mergeType
4642 )
4643 {
4644  // Clip to 30 degrees. Not helpful!
4645  //scalar planarAngle = min(30.0, layerParams.featureAngle());
4646  scalar planarAngle = layerParams.mergePatchFacesAngle();
4647  scalar minCos = Foam::cos(degToRad(planarAngle));
4648 
4649  scalar concaveCos = Foam::cos(degToRad(layerParams.concaveAngle()));
4650 
4651  Info<< nl
4652  << "Merging all faces of a cell" << nl
4653  << "---------------------------" << nl
4654  << " - which are on the same patch" << nl
4655  << " - which make an angle < " << planarAngle
4656  << " degrees"
4657  << " (cos:" << minCos << ')' << nl
4658  << " - as long as the resulting face doesn't become concave"
4659  << " by more than "
4660  << layerParams.concaveAngle() << " degrees" << nl
4661  << " (0=straight, 180=fully concave)" << nl
4662  << endl;
4663 
4664  const fvMesh& mesh = meshRefiner_.mesh();
4665 
4667 
4668  labelList duplicateFace(mesh.nFaces(), -1);
4669  forAll(couples, i)
4670  {
4671  const labelPair& cpl = couples[i];
4672  duplicateFace[cpl[0]] = cpl[1];
4673  duplicateFace[cpl[1]] = cpl[0];
4674  }
4675 
4676  label nChanged = meshRefiner_.mergePatchFacesUndo
4677  (
4678  minCos,
4679  concaveCos,
4680  meshRefiner_.meshedPatches(),
4681  motionDict,
4682  duplicateFace,
4683  mergeType // How to merge co-planar patch faces
4684  );
4685 
4686  nChanged += meshRefiner_.mergeEdgesUndo(minCos, motionDict);
4687 
4688  return nChanged;
4689 }
4690 
4691 
4693 (
4694  const layerParameters& layerParams,
4695  const dictionary& motionDict,
4696  const labelList& patchIDs,
4697  const label nAllowableErrors,
4698  decompositionMethod& decomposer,
4699  fvMeshDistribute& distributor
4700 )
4701 {
4702  fvMesh& mesh = meshRefiner_.mesh();
4703 
4704  // Undistorted edge length
4705  const scalar edge0Len = meshRefiner_.meshCutter().level0EdgeLength();
4706 
4707  // faceZones of type internal or baffle (for merging points across)
4708  labelList internalOrBaffleFaceZones;
4709  {
4711  fzTypes[0] = surfaceZonesInfo::INTERNAL;
4712  fzTypes[1] = surfaceZonesInfo::BAFFLE;
4713  internalOrBaffleFaceZones = meshRefiner_.getZones(fzTypes);
4714  }
4715 
4716  // faceZones of type internal (for checking mesh quality across and
4717  // merging baffles)
4718  const labelList internalFaceZones
4719  (
4720  meshRefiner_.getZones
4721  (
4723  (
4724  1,
4726  )
4727  )
4728  );
4729 
4730  // Create baffles (pairs of faces that share the same points)
4731  // Baffles stored as owner and neighbour face that have been created.
4732  List<labelPair> baffles;
4733  {
4734  labelList originatingFaceZone;
4735  meshRefiner_.createZoneBaffles
4736  (
4737  identity(mesh.faceZones().size()),
4738  baffles,
4739  originatingFaceZone
4740  );
4741 
4743  {
4744  const_cast<Time&>(mesh.time())++;
4745  Info<< "Writing baffled mesh to time "
4746  << meshRefiner_.timeName() << endl;
4747  meshRefiner_.write
4748  (
4751  (
4754  ),
4755  mesh.time().path()/meshRefiner_.timeName()
4756  );
4757  }
4758  }
4759 
4760 
4761  // Duplicate points on faceZones of type boundary. Should normally already
4762  // be done by snapping phase
4763  {
4764  autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldBoundaryPoints();
4765  if (map)
4766  {
4767  const labelList& reverseFaceMap = map->reverseFaceMap();
4768  forAll(baffles, i)
4769  {
4770  label f0 = reverseFaceMap[baffles[i].first()];
4771  label f1 = reverseFaceMap[baffles[i].second()];
4772  baffles[i] = labelPair(f0, f1);
4773  }
4774  }
4775  }
4776 
4777 
4778 
4779  // Now we have all patches determine the number of layer per patch
4780  // This will be layerParams.numLayers() except for faceZones where one
4781  // side does get layers and the other not in which case we want to
4782  // suppress movement by explicitly setting numLayers 0
4783  labelList numLayers(layerParams.numLayers());
4784  {
4785  labelHashSet layerIDs(patchIDs);
4786  forAll(mesh.faceZones(), zonei)
4787  {
4788  label mpi, spi;
4790  bool hasInfo = meshRefiner_.getFaceZoneInfo
4791  (
4792  mesh.faceZones()[zonei].name(),
4793  mpi,
4794  spi,
4795  fzType
4796  );
4797  if (hasInfo)
4798  {
4800  if (layerIDs.found(mpi) && !layerIDs.found(spi))
4801  {
4802  // Only layers on master side. Fix points on slave side
4803  Info<< "On faceZone " << mesh.faceZones()[zonei].name()
4804  << " adding layers to master patch " << pbm[mpi].name()
4805  << " only. Freezing points on slave patch "
4806  << pbm[spi].name() << endl;
4807  numLayers[spi] = 0;
4808  }
4809  else if (!layerIDs.found(mpi) && layerIDs.found(spi))
4810  {
4811  // Only layers on slave side. Fix points on master side
4812  Info<< "On faceZone " << mesh.faceZones()[zonei].name()
4813  << " adding layers to slave patch " << pbm[spi].name()
4814  << " only. Freezing points on master patch "
4815  << pbm[mpi].name() << endl;
4816  numLayers[mpi] = 0;
4817  }
4818  }
4819  }
4820  }
4821 
4822 
4823  // Duplicate points on faceZones that layers are added to
4824  labelList pointToMaster;
4825  dupFaceZonePoints
4826  (
4827  meshRefiner_,
4828  patchIDs, // patch indices
4829  numLayers, // number of layers per patch
4830  baffles,
4831  pointToMaster
4832  );
4833 
4834 
4835  // Add layers to patches
4836  // ~~~~~~~~~~~~~~~~~~~~~
4837 
4838  // Now we have
4839  // - mesh with optional baffles and duplicated points for faceZones
4840  // where layers are to be added
4841  // - pointToMaster : correspondence for duplicated points
4842  // - baffles : list of pairs of faces
4843 
4844 
4845  // Calculate 'base' point extrusion
4847  (
4849  (
4850  mesh,
4851  patchIDs
4852  )
4853  );
4854  // Make sure pp has adressing cached before changing mesh later on
4855  (void)pp().meshPoints();
4856  (void)pp().meshPointMap();
4857 
4858  // Global face indices engine
4859  globalIndex globalFaces(mesh.nFaces());
4860 
4861  // Determine extrudePatch.edgeFaces in global numbering (so across
4862  // coupled patches). This is used only to string up edges between coupled
4863  // faces (all edges between same (global)face indices get extruded).
4864  labelListList edgeGlobalFaces
4865  (
4867  (
4868  mesh,
4869  globalFaces,
4870  *pp
4871  )
4872  );
4873 
4874 
4875  // Point-wise extrusion data
4876  // ~~~~~~~~~~~~~~~~~~~~~~~~~
4877 
4878  // Displacement for all pp.localPoints. Set to large value so does
4879  // not trigger the minThickness truncation (see syncPatchDisplacement below)
4880  vectorField basePatchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT));
4881 
4882  // Number of layers for all pp.localPoints. Note: only valid if
4883  // extrudeStatus = EXTRUDE.
4884  labelList basePatchNLayers(pp().nPoints(), Zero);
4885 
4886  // Whether to add edge for all pp.localPoints.
4887  List<extrudeMode> baseExtrudeStatus(pp().nPoints(), EXTRUDE);
4888 
4889  // Ideal number of cells added
4890  const label nIdealTotAddedCells = setPointNumLayers
4891  (
4892  layerParams,
4893 
4894  numLayers,
4895  patchIDs,
4896  pp(),
4897  edgeGlobalFaces,
4898 
4899  basePatchDisp,
4900  basePatchNLayers,
4901  baseExtrudeStatus
4902  );
4903 
4904  // Overall thickness of all layers
4905  scalarField baseThickness(pp().nPoints());
4906  // Truncation thickness - when to truncate layers
4907  scalarIOField baseMinThickness
4908  (
4909  IOobject
4910  (
4911  "minThickness",
4912  meshRefiner_.timeName(),
4913  mesh,
4915  ),
4916  pp().nPoints()
4917  );
4918  // Expansion ratio
4919  scalarField baseExpansionRatio(pp().nPoints());
4920  calculateLayerThickness
4921  (
4922  pp(),
4923  patchIDs,
4924  layerParams,
4925  meshRefiner_.meshCutter().cellLevel(),
4926  basePatchNLayers,
4927  edge0Len,
4928 
4929  baseThickness,
4930  baseMinThickness,
4931  baseExpansionRatio
4932  );
4933 
4934 
4935  // Per cell 0 or number of layers in the cell column it is part of
4936  labelList cellNLayers(mesh.nCells(), Zero);
4937  // Per face actual overall layer thickness
4938  scalarField faceRealThickness(mesh.nFaces(), Zero);
4939  // Per face wanted overall layer thickness
4940  scalarField faceWantedThickness(mesh.nFaces(), Zero);
4941  {
4942  UIndirectList<scalar>(faceWantedThickness, pp().addressing()) =
4943  avgPointData(pp(), baseThickness);
4944  }
4945 
4946 
4947  // Per patch point the number of layers to add. Is basePatchNLayers
4948  // for nOuterIter = 1.
4949  labelList deltaNLayers
4950  (
4951  (basePatchNLayers+layerParams.nOuterIter()-1)
4952  /layerParams.nOuterIter()
4953  );
4954 
4955  // Per patch point the sum of added layers so far
4956  labelList nAddedLayers(basePatchNLayers.size(), 0);
4957 
4958 
4959  for (label layeri = 0; layeri < layerParams.nOuterIter(); layeri++)
4960  {
4961  // Divide layer addition into outer iterations. E.g. if
4962  // nOutIter = 2, numLayers is 20 for patchA and 1 for patchB
4963  // this will
4964  // - iter0:
4965  // - add 10 layers to patchA and 1 to patchB
4966  // - layers are finalLayerThickness down to the number of layers
4967  // - iter1 : add 10 layer to patchA and 0 to patchB
4968 
4969 
4970  // Exit if nothing to be added
4971  const label nToAdd = gSum(deltaNLayers);
4972  Info<< nl
4973  << "Outer iteration : " << layeri << nl
4974  << "-------------------" << endl;
4975  if (debug)
4976  {
4977  Info<< "Layers to add in current iteration : " << nToAdd << endl;
4978  }
4979  if (nToAdd == 0)
4980  {
4981  break;
4982  }
4983 
4984 
4985  // Determine patches for extruded boundary edges. Calculates if any
4986  // additional processor patches need to be constructed.
4987 
4988  labelList edgePatchID;
4989  labelList edgeZoneID;
4990  boolList edgeFlip;
4991  labelList inflateFaceID;
4992  determineSidePatches
4993  (
4994  meshRefiner_,
4995  globalFaces,
4996  edgeGlobalFaces,
4997  *pp,
4998 
4999  edgePatchID,
5000  edgeZoneID,
5001  edgeFlip,
5002  inflateFaceID
5003  );
5004 
5005 
5006  // Point-wise extrusion data
5007  // ~~~~~~~~~~~~~~~~~~~~~~~~~
5008 
5009  // Displacement for all pp.localPoints. Set to large value so does
5010  // not trigger the minThickness truncation (see syncPatchDisplacement
5011  // below)
5012  vectorField patchDisp(basePatchDisp);
5013 
5014  // Number of layers for all pp.localPoints. Note: only valid if
5015  // extrudeStatus = EXTRUDE.
5016  labelList patchNLayers(deltaNLayers);
5017 
5018  // Whether to add edge for all pp.localPoints.
5019  List<extrudeMode> extrudeStatus(baseExtrudeStatus);
5020 
5021  // At this point
5022  // - patchDisp is either zero or a large value
5023  // - patchNLayers is the overall number of layers
5024  // - extrudeStatus is EXTRUDE or NOEXTRUDE
5025 
5026  // Determine (wanted) point-wise overall layer thickness and expansion
5027  // ratio for this deltaNLayers slice of the overall layers
5028  scalarField sliceThickness(pp().nPoints());
5029  {
5030  forAll(baseThickness, pointi)
5031  {
5032  sliceThickness[pointi] = layerParameters::layerThickness
5033  (
5034  basePatchNLayers[pointi], // overall number of layers
5035  baseThickness[pointi], // overall thickness
5036  baseExpansionRatio[pointi], // expansion ratio
5037  basePatchNLayers[pointi]
5038  -nAddedLayers[pointi]
5039  -patchNLayers[pointi], // start index
5040  patchNLayers[pointi] // nLayers to add
5041  );
5042  }
5043  }
5044 
5045 
5046  // Current set of topology changes. (changing mesh clears out
5047  // polyTopoChange)
5048  polyTopoChange meshMod(mesh.boundaryMesh().size());
5049 
5050  // Shrink mesh, add layers. Returns with any mesh changes in meshMod
5051  labelList sliceCellNLayers;
5052  scalarField sliceFaceRealThickness;
5053 
5054  addLayers
5055  (
5056  layerParams,
5057  layerParams.nLayerIter(),
5058 
5059  // Mesh quality
5060  motionDict,
5061  layerParams.nRelaxedIter(),
5062  nAllowableErrors,
5063 
5064  patchIDs,
5065  internalFaceZones,
5066  baffles,
5067  numLayers,
5068  nIdealTotAddedCells,
5069 
5070  // Patch information
5071  globalFaces,
5072  pp(),
5073  edgeGlobalFaces,
5074  edgePatchID,
5075  edgeZoneID,
5076  edgeFlip,
5077  inflateFaceID,
5078 
5079  // Per patch point the wanted thickness
5080  sliceThickness,
5081  baseMinThickness,
5082  baseExpansionRatio,
5083 
5084  // Per patch point the wanted&obtained layers and thickness
5085  patchDisp,
5086  patchNLayers,
5087  extrudeStatus,
5088 
5089  // Complete mesh changes
5090  meshMod,
5091 
5092  // Stats on new mesh
5093  sliceCellNLayers, //cellNLayers,
5094  sliceFaceRealThickness //faceRealThickness
5095  );
5096 
5097 
5098  // Exit if nothing added
5099  const label nTotalAdded = gSum(patchNLayers);
5100  if (debug)
5101  {
5102  Info<< nl << "Added in current iteration : " << nTotalAdded
5103  << " out of : " << gSum(deltaNLayers) << endl;
5104  }
5105  if (nTotalAdded == 0)
5106  {
5107  break;
5108  }
5109 
5110 
5111  // Update wanted layer statistics
5112  forAll(patchNLayers, pointi)
5113  {
5114  nAddedLayers[pointi] += patchNLayers[pointi];
5115 
5116  if (patchNLayers[pointi] == 0)
5117  {
5118  // No layers were added. Make sure that overall extrusion
5119  // gets reset as well
5120  unmarkExtrusion
5121  (
5122  pointi,
5123  basePatchDisp,
5124  basePatchNLayers,
5125  baseExtrudeStatus
5126  );
5127  basePatchNLayers[pointi] = nAddedLayers[pointi];
5128  deltaNLayers[pointi] = 0;
5129  }
5130  else
5131  {
5132  // Adjust the number of layers for the next iteration.
5133  // Can never be
5134  // higher than the adjusted overall number of layers.
5135  // Note: is min necessary?
5136  deltaNLayers[pointi] = max
5137  (
5138  0,
5139  min
5140  (
5141  deltaNLayers[pointi],
5142  basePatchNLayers[pointi] - nAddedLayers[pointi]
5143  )
5144  );
5145  }
5146  }
5147 
5148 
5149  // At this point we have a (shrunk) mesh and a set of topology changes
5150  // which will make a valid mesh with layer. Apply these changes to the
5151  // current mesh.
5152 
5153  {
5154  // Apply the stored topo changes to the current mesh.
5155  autoPtr<mapPolyMesh> mapPtr = meshMod.changeMesh(mesh, false);
5156  mapPolyMesh& map = *mapPtr;
5157 
5158  // Hack to remove meshPhi - mapped incorrectly. TBD.
5159  mesh.moving(false);
5160  mesh.clearOut();
5161 
5162  // Update fields
5163  mesh.updateMesh(map);
5164 
5165  // Move mesh (since morphing does not do this)
5166  if (map.hasMotionPoints())
5167  {
5169  }
5170  else
5171  {
5172  // Delete mesh volumes.
5173  mesh.clearOut();
5174  }
5175 
5176  // Reset the instance for if in overwrite mode
5177  mesh.setInstance(meshRefiner_.timeName());
5178 
5179  meshRefiner_.updateMesh(map, labelList(0));
5180 
5181  // Update numbering of accumulated cells
5182  cellNLayers.setSize(map.nOldCells(), 0);
5184  (
5185  map.cellMap(),
5186  label(0),
5187  cellNLayers
5188  );
5189  forAll(cellNLayers, i)
5190  {
5191  cellNLayers[i] += sliceCellNLayers[i];
5192  }
5193 
5194  faceRealThickness.setSize(map.nOldFaces(), scalar(0));
5196  (
5197  map.faceMap(),
5198  scalar(0),
5199  faceRealThickness
5200  );
5201  faceRealThickness += sliceFaceRealThickness;
5202 
5204  (
5205  map.faceMap(),
5206  scalar(0),
5207  faceWantedThickness
5208  );
5209 
5210  // Print data now that we still have patches for the zones
5211  //if (meshRefinement::outputLevel() & meshRefinement::OUTPUTLAYERINFO)
5212  printLayerData
5213  (
5214  mesh,
5215  patchIDs,
5216  cellNLayers,
5217  faceWantedThickness,
5218  faceRealThickness,
5219  layerParams
5220  );
5221 
5222 
5223  // Dump for debugging
5225  {
5226  const_cast<Time&>(mesh.time())++;
5227  Info<< "Writing mesh with layers but disconnected to time "
5228  << meshRefiner_.timeName() << endl;
5229  meshRefiner_.write
5230  (
5233  (
5236  ),
5237  mesh.time().path()/meshRefiner_.timeName()
5238  );
5239  }
5240 
5241 
5242  // Map baffles, pointToMaster
5243  mapFaceZonePoints(meshRefiner_, map, baffles, pointToMaster);
5244 
5245  // Map patch and layer settings
5246  labelList newToOldPatchPoints;
5247  updatePatch(patchIDs, map, pp, newToOldPatchPoints);
5248 
5249  // Global face indices engine
5250  globalFaces.reset(mesh.nFaces());
5251 
5252  // Patch-edges to global faces using them
5253  edgeGlobalFaces = addPatchCellLayer::globalEdgeFaces
5254  (
5255  mesh,
5256  globalFaces,
5257  pp()
5258  );
5259 
5260  // Map patch-point based data
5262  (
5263  newToOldPatchPoints,
5264  vector::uniform(-1),
5265  basePatchDisp
5266  );
5268  (
5269  newToOldPatchPoints,
5270  label(0),
5271  basePatchNLayers
5272  );
5274  (
5275  newToOldPatchPoints,
5276  extrudeMode::NOEXTRUDE,
5277  baseExtrudeStatus
5278  );
5280  (
5281  newToOldPatchPoints,
5282  scalar(0),
5283  baseThickness
5284  );
5286  (
5287  newToOldPatchPoints,
5288  scalar(0),
5289  baseMinThickness
5290  );
5292  (
5293  newToOldPatchPoints,
5294  GREAT,
5295  baseExpansionRatio
5296  );
5298  (
5299  newToOldPatchPoints,
5300  label(0),
5301  deltaNLayers
5302  );
5304  (
5305  newToOldPatchPoints,
5306  label(0),
5307  nAddedLayers
5308  );
5309  }
5310  }
5311 
5312 
5313  // Merge baffles
5314  mergeFaceZonePoints
5315  (
5316  pointToMaster, // per new mesh point : -1 or index of duplicated point
5317  cellNLayers, // per new cell : number of layers
5318  faceRealThickness, // per new face : actual thickness
5319  faceWantedThickness // per new face : wanted thickness
5320  );
5321 
5322 
5323  // Do final balancing
5324  // ~~~~~~~~~~~~~~~~~~
5325 
5326  if (Pstream::parRun())
5327  {
5328  Info<< nl
5329  << "Doing final balancing" << nl
5330  << "---------------------" << nl
5331  << endl;
5332 
5333  if (debug)
5334  {
5335  const_cast<Time&>(mesh.time())++;
5336  }
5337 
5338  // Balance. No restriction on face zones and baffles.
5339  autoPtr<mapDistributePolyMesh> map = meshRefiner_.balance
5340  (
5341  false,
5342  false,
5343  labelList::null(),
5344  scalarField(mesh.nCells(), 1.0),
5345  decomposer,
5346  distributor
5347  );
5348 
5349  // Re-distribute flag of layer faces and cells
5350  map().distributeCellData(cellNLayers);
5351  map().distributeFaceData(faceWantedThickness);
5352  map().distributeFaceData(faceRealThickness);
5353  }
5354 
5355 
5356  // Write mesh data
5357  // ~~~~~~~~~~~~~~~
5358 
5359  if (!dryRun_)
5360  {
5361  writeLayerData
5362  (
5363  mesh,
5364  patchIDs,
5365  cellNLayers,
5366  faceWantedThickness,
5367  faceRealThickness
5368  );
5369  }
5370 }
5371 
5372 
5374 (
5375  const dictionary& shrinkDict,
5376  const dictionary& motionDict,
5377  const layerParameters& layerParams,
5378  const meshRefinement::FaceMergeType mergeType,
5379  const bool preBalance,
5380  decompositionMethod& decomposer,
5381  fvMeshDistribute& distributor
5382 )
5383 {
5384  addProfiling(layers, "snappyHexMesh::layers");
5385  const fvMesh& mesh = meshRefiner_.mesh();
5386 
5387  Info<< nl
5388  << "Shrinking and layer addition phase" << nl
5389  << "----------------------------------" << nl
5390  << endl;
5391 
5392 
5393  Info<< "Using mesh parameters " << motionDict << nl << endl;
5394 
5395  // Merge coplanar boundary faces
5396  if
5397  (
5398  mergeType == meshRefinement::FaceMergeType::GEOMETRIC
5399  || mergeType == meshRefinement::FaceMergeType::IGNOREPATCH
5400  )
5401  {
5402  mergePatchFacesUndo(layerParams, motionDict, mergeType);
5403  }
5404 
5405 
5406  // Per patch the number of layers (-1 or 0 if no layer)
5407  const labelList& numLayers = layerParams.numLayers();
5408 
5409  // Patches that need to get a layer
5410  DynamicList<label> patchIDs(numLayers.size());
5411  label nFacesWithLayers = 0;
5412  forAll(numLayers, patchi)
5413  {
5414  if (numLayers[patchi] > 0)
5415  {
5416  const polyPatch& pp = mesh.boundaryMesh()[patchi];
5417 
5418  if (!pp.coupled())
5419  {
5420  patchIDs.append(patchi);
5421  nFacesWithLayers += mesh.boundaryMesh()[patchi].size();
5422  }
5423  else
5424  {
5426  << "Ignoring layers on coupled patch " << pp.name()
5427  << endl;
5428  }
5429  }
5430  }
5431 
5432  // Add contributions from faceZones that get layers
5433  const faceZoneMesh& fZones = mesh.faceZones();
5434  forAll(fZones, zonei)
5435  {
5436  label mpi, spi;
5438  meshRefiner_.getFaceZoneInfo(fZones[zonei].name(), mpi, spi, fzType);
5439 
5440  if (numLayers[mpi] > 0)
5441  {
5442  nFacesWithLayers += fZones[zonei].size();
5443  }
5444  if (numLayers[spi] > 0)
5445  {
5446  nFacesWithLayers += fZones[zonei].size();
5447  }
5448  }
5449 
5450 
5451  patchIDs.shrink();
5452 
5453  if (!returnReduceOr(nFacesWithLayers))
5454  {
5455  Info<< nl << "No layers to generate ..." << endl;
5456  }
5457  else
5458  {
5459  // Check that outside of mesh is not multiply connected.
5460  checkMeshManifold();
5461 
5462  // Check initial mesh
5463  Info<< "Checking initial mesh ..." << endl;
5464  labelHashSet wrongFaces(mesh.nFaces()/100);
5465  motionSmoother::checkMesh(false, mesh, motionDict, wrongFaces, dryRun_);
5466  const label nInitErrors = returnReduce
5467  (
5468  wrongFaces.size(),
5469  sumOp<label>()
5470  );
5471 
5472  Info<< "Detected " << nInitErrors << " illegal faces"
5473  << " (concave, zero area or negative cell pyramid volume)"
5474  << endl;
5475 
5476 
5477  bool faceZoneOnCoupledFace = false;
5478 
5479  if (!preBalance)
5480  {
5481  // Check if there are faceZones on processor boundaries. This
5482  // requires balancing to move them off the processor boundaries.
5483 
5484  // Is face on a faceZone
5485  bitSet isExtrudedZoneFace(mesh.nFaces());
5486  {
5487  // Add contributions from faceZones that get layers
5488  const faceZoneMesh& fZones = mesh.faceZones();
5489  forAll(fZones, zonei)
5490  {
5491  const faceZone& fZone = fZones[zonei];
5492  const word& fzName = fZone.name();
5493 
5494  label mpi, spi;
5496  meshRefiner_.getFaceZoneInfo(fzName, mpi, spi, fzType);
5497 
5498  if (numLayers[mpi] > 0 || numLayers[spi])
5499  {
5500  isExtrudedZoneFace.set(fZone);
5501  }
5502  }
5503  }
5504 
5505  bitSet intOrCoupled
5506  (
5508  );
5509 
5510  for
5511  (
5512  label facei = mesh.nInternalFaces();
5513  facei < mesh.nFaces();
5514  facei++
5515  )
5516  {
5517  if (intOrCoupled[facei] && isExtrudedZoneFace.test(facei))
5518  {
5519  faceZoneOnCoupledFace = true;
5520  break;
5521  }
5522  }
5523 
5524  Pstream::reduceOr(faceZoneOnCoupledFace);
5525  }
5526 
5527  // Balance
5528  if (Pstream::parRun())
5529  {
5530  // Calculate wanted number of cells after adding layers
5531  // (expressed as weight to be passed into decompositionMethod)
5532 
5533  scalarField cellWeights(mesh.nCells(), 1);
5534  forAll(numLayers, patchi)
5535  {
5536  if (numLayers[patchi] > 0)
5537  {
5538  const polyPatch& pp = mesh.boundaryMesh()[patchi];
5539  for (const label celli : pp.faceCells())
5540  {
5541  cellWeights[celli] += numLayers[patchi];
5542  }
5543  }
5544  }
5545 
5546  // Add contributions from faceZones that get layers
5547  const faceZoneMesh& fZones = mesh.faceZones();
5548  forAll(fZones, zonei)
5549  {
5550  const faceZone& fZone = fZones[zonei];
5551  const word& fzName = fZone.name();
5552 
5553  label mpi, spi;
5555  meshRefiner_.getFaceZoneInfo(fzName, mpi, spi, fzType);
5556 
5557  if (numLayers[mpi] > 0)
5558  {
5559  // Get the owner side for unflipped faces, neighbour side
5560  // for flipped ones
5561  const labelList& cellIDs = fZone.slaveCells();
5562  forAll(cellIDs, i)
5563  {
5564  if (cellIDs[i] >= 0)
5565  {
5566  cellWeights[cellIDs[i]] += numLayers[mpi];
5567  }
5568  }
5569  }
5570  if (numLayers[spi] > 0)
5571  {
5572  const labelList& cellIDs = fZone.masterCells();
5573  forAll(cellIDs, i)
5574  {
5575  if (cellIDs[i] >= 0)
5576  {
5577  cellWeights[cellIDs[i]] += numLayers[mpi];
5578  }
5579  }
5580  }
5581  }
5582 
5583 
5584  // Print starting mesh
5585  Info<< nl;
5586  meshRefiner_.printMeshInfo
5587  (
5588  false,
5589  "Before layer addition",
5590  false //printCellLevel
5591  );
5592 
5593  // Print expected mesh (if all layers added)
5594  {
5595  const scalar nNewCells = sum(cellWeights);
5596  const scalar nNewCellsAll =
5597  returnReduce(nNewCells, sumOp<scalar>());
5598  const scalar nIdealNewCells = nNewCellsAll / Pstream::nProcs();
5599  const scalar unbalance = returnReduce
5600  (
5601  mag(1.0-nNewCells/nIdealNewCells),
5602  maxOp<scalar>()
5603  );
5604 
5605  Info<< "Ideal layer addition"
5606  << " : cells:" << nNewCellsAll
5607  << " unbalance:" << unbalance
5608  << nl << endl;
5609  }
5610 
5611 
5612  if (preBalance || faceZoneOnCoupledFace)
5613  {
5614  Info<< nl
5615  << "Doing initial balancing" << nl
5616  << "-----------------------" << nl
5617  << endl;
5618 
5619  // Balance mesh (and meshRefinement). Restrict faceZones to
5620  // be on internal faces only since they will be converted into
5621  // baffles.
5622  autoPtr<mapDistributePolyMesh> map = meshRefiner_.balance
5623  (
5624  true, // keepZoneFaces
5625  false,
5626  labelList::null(),
5627  cellWeights,
5628  decomposer,
5629  distributor
5630  );
5631  }
5632  }
5633 
5634 
5635  // Do all topo changes
5636  if (layerParams.nOuterIter() == -1)
5637  {
5638  // For testing. Can be removed once addLayers below works
5639  addLayersSinglePass
5640  (
5641  layerParams,
5642  motionDict,
5643  patchIDs,
5644  nInitErrors,
5645  decomposer,
5646  distributor
5647  );
5648  }
5649  else
5650  {
5651  addLayers
5652  (
5653  layerParams,
5654  motionDict,
5655  patchIDs,
5656  nInitErrors,
5657  decomposer,
5658  distributor
5659  );
5660  }
5661  }
5662 }
5663 
5664 
5665 // ************************************************************************* //
label nPatches
Definition: readKivaGrid.H:394
const labelListList & pointEdges() const
Return point-edge addressing.
static bool checkMesh(const bool report, const polyMesh &mesh, const dictionary &dict, labelHashSet &wrongFaces, const bool dryRun=false)
Check mesh with mesh settings in dict. Collects incorrect faces.
const labelList patchIDs(pbm.indices(polyPatchNames, true))
const polyBoundaryMesh & pbm
prefixOSstream Perr
OSstream wrapped stderr (std::cerr) with parallel prefix.
label nPoints() const
Number of points supporting patch faces.
const List< face_type > & localFaces() const
Return patch faces addressing into local point list.
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
fileName path() const
Return path = rootPath/caseName. Same as TimePaths::path()
Definition: Time.H:503
Simple container to keep together layer specific information.
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:491
A list of face labels.
Definition: faceSet.H:47
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
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)
const Field< point_type > & localPoints() const
Return pointField of points in patch.
void transfer(List< T > &list)
Transfer the contents of the argument List into this list and annul the argument list.
Definition: List.C:337
label nPoints() const noexcept
Number of mesh points.
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:600
scalar concaveAngle() const
void append(const T &val)
Append an element at the end of the list.
Definition: List.H:526
bool found(const Key &key) const
Same as contains()
Definition: HashTable.H:1370
bool getFaceZoneInfo(const word &fzName, label &masterPatchID, label &slavePatchID, surfaceZonesInfo::faceZoneType &fzType) const
Lookup faceZone information. Return false if no information.
const word & name() const noexcept
Return the object name.
Definition: IOobjectI.H:195
const labelListList & pointEdges() const
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
Unit conversion functions.
static FOAM_NO_DANGLING_REFERENCE const pointMesh & New(const polyMesh &mesh, Args &&... args)
Get existing or create MeshObject registered with typeName.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
void inplaceReorder(const labelUList &oldToNew, ListType &input, const bool prune=false)
Inplace reorder the elements of a list.
UIndirectList< label > labelUIndList
UIndirectList of labels.
Definition: IndirectList.H:65
T & first()
Access first element of the list, position [0].
Definition: UList.H:886
dimensioned< vector > dimensionedVector
Dimensioned vector obtained from generic dimensioned type.
static word newName(const label myProcNo, const label neighbProcNo)
Return the name of a processorPolyPatch ("procBoundary..") constructed from the pair of processor IDs...
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:529
const labelList & patchID() const
Per boundary face label the patch index.
virtual void movePoints(const pointField &)
Move points, returns volumes swept by faces in motion.
Definition: fvMesh.C:884
static void calcExtrudeInfo(const bool zoneFromAnyFace, const polyMesh &, const globalIndex &globalFaces, const labelListList &globalEdgeFaces, const indirectPrimitivePatch &pp, labelList &edgePatchID, label &nPatches, Map< label > &nbrProcToPatch, Map< label > &patchToNbrProc, labelList &edgeZoneID, boolList &edgeFlip, labelList &inflateFaceID)
Determine extrude information per patch edge:
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:1586
scalar mergePatchFacesAngle() const
#define addProfiling(Name,...)
Define profiling trigger with specified name and description string. The description is generated by ...
static writeType writeLevel()
Get/set write level.
void clearOut(const bool isMeshUpdate=false)
Clear all geometry and addressing.
Definition: fvMesh.C:227
GeometricField< vector, pointPatchField, pointMesh > pointVectorField
label nBufferCellsNoExtrude() const
Create buffer region for new layer terminations.
labelRange range() const noexcept
The face range for all boundary faces.
List< labelPair > labelPairList
List of labelPair.
Definition: labelPair.H:33
const cellList & cells() const
entry * add(entry *entryPtr, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:625
constexpr label labelMin
Definition: label.H:54
GeometricBoundaryField< scalar, fvPatchField, volMesh > Boundary
Type of boundary fields.
const labelList & slaveCells() const
Deprecated(2023-09) same as backCells.
Definition: faceZone.H:528
const dictionary & dict() const
static List< labelPair > subsetBaffles(const polyMesh &mesh, const labelList &zoneIDs, const List< labelPair > &baffles)
Subset baffles according to zones.
Ignore writing from objectRegistry::writeObject()
IOField< scalar > scalarIOField
IO for a Field of scalar.
Definition: scalarIOField.H:32
const dimensionSet dimless
Dimensionless.
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Negative if the process is not a...
Definition: UPstream.H:1611
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:360
label nFaces() const noexcept
Number of mesh faces.
PrimitivePatch< IndirectList< face >, const pointField & > indirectPrimitivePatch
A PrimitivePatch with an IndirectList for the faces, const reference for the point field...
virtual const fileName & name() const override
Get the name of the output serial stream. (eg, the name of the Fstream file name) ...
Definition: OSstream.H:134
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1, const label comm)
const dictionary & subDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a sub-dictionary.
Definition: dictionary.C:441
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:69
static bitSet getInternalOrCoupledFaces(const polyMesh &mesh)
Get per face whether it is internal or coupled.
Definition: syncTools.C:165
const labelList & numLayers() const
How many layers to add.
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
Definition: mapPolyMesh.H:158
bool isInternalFace(const label faceIndex) const noexcept
Return true if given face label is internal to the mesh.
UList< label > labelUList
A UList of labels.
Definition: UList.H:76
void setSize(const label n)
Dummy function, to make FixedList consistent with List.
Definition: FixedList.H:437
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)
const fvMesh & mesh() const
Reference to mesh.
static labelPairList findDuplicateFacePairs(const polyMesh &)
Helper routine to find all baffles (two boundary faces.
void reduce(T &value, [[maybe_unused]] BinaryOp bop, [[maybe_unused]] const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce)
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1063
label nRelaxedIter() const
Number of iterations after which relaxed motion rules.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:286
const labelList & reverseFaceMap() const noexcept
Reverse face map.
Definition: mapPolyMesh.H:620
label fcIndex(const label i) const noexcept
The forward circular index. The next index in the list which returns to the first at the end of the l...
Definition: UListI.H:90
label mergePoints(const PointList &points, labelList &pointToUnique, labelList &uniquePoints, const scalar mergeTol=SMALL, const bool verbose=false)
Calculate merge mapping, preserving the original point order. All points closer/equal mergeTol are to...
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:72
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition: HashSet.H:85
static label appendPatch(fvMesh &, const label insertPatchi, const word &, const dictionary &)
Helper:append patch to end of mesh.
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:152
virtual void movePoints(const Field< point_type > &)
Correct patch after moving points.
const Field< point_type > & faceNormals() const
Return face unit normals for patch.
List< face > faceList
List of faces.
Definition: faceListFwd.H:39
pointField vertices(const blockVertexList &bvl)
static void updateList(const labelList &newToOld, const T &nullValue, List< T > &elems)
Helper: reorder list according to map.
bool merge(const dictionary &dict)
Merge entries from the given dictionary.
Definition: dictionary.C:799
const labelList & masterCells() const
Deprecated(2023-09) same as frontCells.
Definition: faceZone.H:521
A list of faces which address into the list of points.
Omanip< int > setprecision(const int i)
Definition: IOmanip.H:205
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:61
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run. ...
Definition: UPstream.H:1602
Sends/receives parts of mesh+fvfields to neighbouring processors. Used in load balancing.
bool additionalReporting() const
Any additional reporting requested?
const Map< label > & meshPointMap() const
Mesh point map.
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
static labelListList globalEdgeFaces(const polyMesh &, const globalIndex &globalFaces, const indirectPrimitivePatch &pp, const bitSet &orientation)
Per patch edge the pp faces (in global indices) using it.
void setSize(const label n)
Alias for resize()
Definition: List.H:325
virtual void updateMesh(const mapPolyMesh &mpm)
Update mesh corresponding to the given map.
Definition: fvMesh.C:960
FaceMergeType
Enumeration for what to do with co-planar patch faces on a single.
dynamicFvMesh & mesh
static void exit(int errNo=1)
Shutdown (finalize) MPI as required and exit program with errNo.
Definition: UPstream.C:61
Type gSum(const FieldField< Field, Type > &f)
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
const pointField & points
autoPtr< mapPolyMesh > makeMesh(autoPtr< Type > &newMesh, const IOobject &io, const polyMesh &mesh, const labelUList &patchMap, const bool syncParallel=true, const bool orderCells=false, const bool orderPoints=false)
Create new mesh with old mesh patches. Additional dictionaries.
const labelList & cellMap() const noexcept
Old cell map.
Definition: mapPolyMesh.H:537
labelListList invertOneToMany(const label len, const labelUList &map)
Invert one-to-many map. Unmapped elements will be size 0.
Definition: ListOps.C:125
const_iterator cfind(const Key &key) const
Find and return an const_iterator set at the hashed entry.
Definition: HashTableI.H:113
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
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:130
virtual int precision() const override
Get precision of output field.
Definition: OSstream.C:327
Adds layers of cells to outside (or inside) of polyMesh. Can optionally create stand-alone extruded m...
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition: polyMesh.H:609
A class for handling words, derived from Foam::string.
Definition: word.H:63
label nPoints
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
static bitSet getMasterPoints(const polyMesh &mesh)
Get per point whether it is uncoupled or a master of a coupled set of points.
Definition: syncTools.C:61
const labelList & pointMap() const noexcept
Old point map.
Definition: mapPolyMesh.H:484
label size() const noexcept
The number of entries in the list.
Definition: UPtrListI.H:106
string message() const
The accumulated error message.
Definition: error.C:332
const Field< point_type > & faceCentres() const
Return face centres for patch.
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1101
Reading is optional [identical to LAZY_READ].
static void setDisplacement(const labelList &patchIDs, const indirectPrimitivePatch &pp, pointField &patchDisp, pointVectorField &displacement)
Set displacement field from displacement on patch points.
label nOuterIter() const
Outer loop to add layer by layer. Can be set to >= max layers.
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition: tmp.H:206
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
const labelListList & edgeFaces() const
Return edge-face addressing.
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints, -1);const cellModel &hex=cellModel::ref(cellModel::HEX);labelList hexLabels(8);label activeCells=0;labelList pointMap(nPoints);forAll(pointMap, i){ pointMap[i]=i;}for(label i=0;i< nPoints;i++){ if(f[i] > 0.0) { hexLabels[0]=i;hexLabels[1]=i1tab[i];hexLabels[2]=i3tab[i1tab[i]];hexLabels[3]=i3tab[i];hexLabels[4]=i8tab[i];hexLabels[5]=i1tab[i8tab[i]];hexLabels[6]=i3tab[i1tab[i8tab[i]]];hexLabels[7]=i3tab[i8tab[i]];cellShapes[activeCells].reset(hex, hexLabels);edgeList edges=cellShapes[activeCells].edges();forAll(edges, ei) { if(edges[ei].mag(points)< SMALL) { label start=pointMap[edges[ei].start()];while(start !=pointMap[start]) { start=pointMap[start];} label end=pointMap[edges[ei].end()];while(end !=pointMap[end]) { end=pointMap[end];} pointMap[start]=pointMap[end]=Foam::min(start, end);} } cellZoning[activeCells]=idreg[i];activeCells++;}}cellShapes.setSize(activeCells);cellZoning.setSize(activeCells);forAll(cellShapes, celli){ cellShape &cs=cellShapes[celli];forAll(cs, i) { cs[i]=pointMap[cs[i]];} cs.collapse();}label bcIDs[11]={-1, 0, 2, 4, -1, 5, -1, 6, 7, 8, 9};constexpr label nBCs=12;const word *kivaPatchTypes[nBCs]={ &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &symmetryPolyPatch::typeName, &wedgePolyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &symmetryPolyPatch::typeName, &oldCyclicPolyPatch::typeName};enum patchTypeNames{ PISTON, VALVE, LINER, CYLINDERHEAD, AXIS, WEDGE, INFLOW, OUTFLOW, PRESIN, PRESOUT, SYMMETRYPLANE, CYCLIC};const char *kivaPatchNames[nBCs]={ "piston", "valve", "liner", "cylinderHead", "axis", "wedge", "inflow", "outflow", "presin", "presout", "symmetryPlane", "cyclic"};List< DynamicList< face > > pFaces[nBCs]
Definition: readKivaGrid.H:233
T returnReduce(const T &value, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
Abstract base class for domain decomposition.
label nInternalFaces() const noexcept
Number of internal faces.
OSstream & stream(OSstream *alternative=nullptr, int communicator=-1)
Return OSstream for output operations.
Vector< scalar > vector
Definition: vector.H:57
const Field< point_type > & points() const noexcept
Return reference to global points.
void shrink()
Shrink storage (does not remove any elements; just compacts dynamic lists.
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1088
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
bool visNormal(const vector &n, const vectorField &faceNormals, const labelList &faceLabels)
Check if n is in same direction as normals of all faceLabels.
Definition: meshTools.C:30
void setRefinement(const globalIndex &globalFaces, const labelListList &globalEdgeFaces, const scalarField &expansionRatio, const indirectPrimitivePatch &pp, const bitSet &flip, const labelList &sidePatchID, const labelList &sideZoneID, const boolList &sideFlip, const labelList &inflateFaceID, const labelList &exposedPatchID, const labelList &nFaceLayers, const labelList &nPointLayers, const vectorField &firstLayerDisp, polyTopoChange &meshMod)
Play commands into polyTopoChange to create layers on top.
bool returnReduceOr(const bool value, const int communicator=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
errorManip< error > abort(error &err)
Definition: errorManip.H:139
label nEdges() const
Number of mesh edges.
static void syncPointList(const polyMesh &mesh, List< T > &pointValues, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh points.
bool test(const label pos) const
Test for True value at specified position, never auto-vivify entries.
Definition: bitSet.H:329
label find(const T &val) const
Find index of the first occurrence of the value.
Definition: UList.C:172
virtual bool write(const bool writeOnProc=true) const
Write mesh using IO settings from time.
Definition: fvMesh.C:1068
label nLayerIter() const
Number of overall layer addition iterations.
word timeName() const
Replacement for Time::timeName() that returns oldInstance (if overwrite_)
A polyBoundaryMesh is a polyPatch list with registered IO, a reference to the associated polyMesh...
const labelListList & pointFaces() const
Return point-face addressing.
Istream and Ostream manipulators taking arguments.
static scalar finalLayerThicknessRatio(const label nLayers, const scalar expansionRatio)
Determine ratio of final layer thickness to.
static word timeName(const scalar t, const int precision=precision_)
Return a time name for the given scalar time value formatted with the given precision.
Definition: Time.C:714
Smanip< std::ios_base::fmtflags > setf(std::ios_base::fmtflags flags)
Definition: IOmanip.H:169
int debug
Static debugging option.
static void mapFaceZonePoints(meshRefinement &meshRefiner, const mapPolyMesh &map, labelPairList &baffles, labelList &pointToMaster)
Map numbering after adding cell layers.
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:51
Type gMax(const FieldField< Field, Type > &f)
const faceZoneMesh & faceZones() const noexcept
Return face zone mesh.
Definition: polyMesh.H:671
void doLayers(const dictionary &shrinkDict, const dictionary &motionDict, const layerParameters &layerParams, const meshRefinement::FaceMergeType mergeType, const bool preBalance, decompositionMethod &decomposer, fvMeshDistribute &distributor)
Add layers according to the dictionary settings.
defineTypeNameAndDebug(combustionModel, 0)
label nOldCells() const noexcept
Number of old cells.
Definition: mapPolyMesh.H:472
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:509
labelList f(nPoints)
static autoPtr< indirectPrimitivePatch > makePatch(const polyMesh &, const labelList &)
Create patch from set of patches.
double cpuTimeIncrement() const
Return CPU time [seconds] since last call to cpuTimeIncrement(), resetCpuTimeIncrement().
Definition: cpuTimePosix.C:86
static void determineSidePatches(meshRefinement &meshRefiner, const globalIndex &globalFaces, const labelListList &edgeGlobalFaces, const indirectPrimitivePatch &pp, labelList &edgePatchID, labelList &edgeZoneID, boolList &edgeFlip, labelList &inflateFaceID)
Helper: see what zones and patches edges should be extruded into.
bool hasMotionPoints() const noexcept
Has valid preMotionPoints?
Definition: mapPolyMesh.H:767
scalar mergeDistance() const
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;for(const word &name :lagrangianScalarNames){ IOField< scalar > fld(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
Helper class which maintains intersections of (changing) mesh with (static) surfaces.
void updateMesh(const mapPolyMesh &, const labelList &faceMap, const labelList &pointMap)
Update any locally stored mesh information. Gets additional.
bool moving() const noexcept
Is mesh moving.
Definition: polyMesh.H:732
const edgeList & edges() const
Return mesh edges. Uses calcEdges.
thicknessModelType
Enumeration defining the layer specification:
const vectorField & faceCentres() const
List< word > wordList
List of word.
Definition: fileName.H:59
label mergePatchFacesUndo(const layerParameters &layerParams, const dictionary &motionDict, const meshRefinement::FaceMergeType mergeType)
Merge patch faces on same cell. Return total number of faces/edges changed.
label nGrow() const
If points get not extruded do nGrow layers of connected faces.
vector point
Point is a vector.
Definition: point.H:37
void setInstance(const fileName &instance, const IOobjectOption::writeOption wOpt=IOobject::AUTO_WRITE)
Set the instance for mesh files.
Definition: polyMeshIO.C:29
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.
const word & name() const
Return reference to name.
Definition: fvMesh.H:387
const dimensionSet dimLength(0, 1, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:50
label newPointi
Definition: readKivaGrid.H:497
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
label nCells() const noexcept
Number of mesh cells.
autoPtr< mapPolyMesh > dupNonManifoldPoints(const localPointRegion &)
Find boundary points that connect to more than one cell.
labelList meshEdges(const edgeList &allEdges, const labelListList &cellEdges, const labelList &faceCells) const
Return labels of patch edges in the global edge list using cell addressing.
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:78
A List with indirect addressing. Like IndirectList but does not store addressing. ...
Definition: faMatrix.H:52
Direct mesh changes based on v1.3 polyTopoChange syntax.
const polyBoundaryMesh & patches
const word & name() const noexcept
The zone name.
static autoPtr< externalDisplacementMeshMover > New(const word &type, const dictionary &dict, const List< labelPair > &baffles, pointVectorField &pointDisplacement, const bool dryRun=false)
Return a reference to the selected meshMover model.
Nothing to be read.
Automatically write from objectRegistry::writeObject()
static scalar layerThickness(const thicknessModelType, const label nLayers, const scalar firstLayerThickness, const scalar finalLayerThickness, const scalar totalThickness, const scalar expansionRatio)
Determine overall thickness. Uses two of the four parameters.
messageStream Info
Information stream (stdout output on master, null elsewhere)
constexpr label labelMax
Definition: label.H:55
const labelListList & faceEdges() const
Return face-edge addressing.
SubField< scalar > subField
Declare type of subField.
Definition: Field.H:177
writeType
Enumeration for what to write. Used as a bit-pattern.
label n
labelListList addedCells() const
Added cells given current mesh & layerfaces.
Field< vector > vectorField
Specialisation of Field<T> for vector.
label nOldFaces() const noexcept
Number of old faces.
Definition: mapPolyMesh.H:464
faceZoneType
What to do with faceZone faces.
#define IOWarningInFunction(ios)
Report an IO warning using Foam::Warning.
debugType
Enumeration for what to debug. Used as a bit-pattern.
void addLayers(const layerParameters &layerParams, const label nLayerIter, const dictionary &motionDict, const label nRelaxedIter, const label nAllowableErrors, const labelList &patchIDs, const labelList &internalFaceZones, const List< labelPair > &baffles, const labelList &numLayers, const label nIdealTotAddedCells, const globalIndex &globalFaces, indirectPrimitivePatch &pp, const labelListList &edgeGlobalFaces, const labelList &edgePatchID, const labelList &edgeZoneID, const boolList &edgeFlip, const labelList &inflateFaceID, const scalarField &thickness, const scalarIOField &minThickness, const scalarField &expansionRatio, vectorField &patchDisp, labelList &patchNLayers, List< extrudeMode > &extrudeStatus, polyTopoChange &savedMeshMod, labelList &cellNLayers, scalarField &faceRealThickness)
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:75
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
A subset of mesh faces organised as a primitive patch.
Definition: faceZone.H:60
const labelList & faceMap() const noexcept
Old face map.
Definition: mapPolyMesh.H:503
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.
bool write() const
Write mesh and all data.
List< label > labelList
A List of labels.
Definition: List.H:61
volScalarField & p
A class for managing temporary objects.
Definition: HashPtrTable.H:50
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:69
static tmp< pointField > pointNormals(const polyMesh &, const PrimitivePatch< FaceList, PointField > &, const bitSet &flipMap=bitSet::null())
Return parallel consistent point normals for patches using mesh points.
static autoPtr< mapPolyMesh > dupFaceZonePoints(meshRefinement &meshRefiner, const labelList &patchIDs, const labelList &numLayers, List< labelPair > baffles, labelList &pointToMaster)
Duplicate points on faceZones with layers. Re-used when adding buffer layers. Can be made private aga...
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:500
static void reduceOr(bool &value, const int communicator=worldComm)
Logical (or) reduction (MPI_AllReduce)
constexpr scalar degToRad(const scalar deg) noexcept
Conversion from degrees to radians.
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:180
List< bool > boolList
A List of bools.
Definition: List.H:59
labelList cellIDs
A primitive field of type <T> with automated input and output.
const pointField & preMotionPoints() const noexcept
Pre-motion point positions.
Definition: mapPolyMesh.H:759
static const List< label > & null() noexcept
Return a null List (reference to a nullObject). Behaves like an empty List.
Definition: List.H:158
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Do not request registration (bool: false)
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
Namespace for OpenFOAM.
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127