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  tmp<scalarField> tfaceFld(new scalarField(pp.size(), Zero));
117  scalarField& 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  const labelList& patchToNLayers,
1004  const labelList& patchIDs,
1005  const indirectPrimitivePatch& pp,
1006  labelList& patchNLayers,
1007  List<extrudeMode>& extrudeStatus,
1008  label& nAddedCells
1009 ) const
1010 {
1011  const fvMesh& mesh = meshRefiner_.mesh();
1012 
1013  Info<< nl << "Handling points with inconsistent layer specification ..."
1014  << endl;
1015 
1016  // Get for every point (really only necessary on patch external points)
1017  // the max and min of any patch faces using it.
1018  labelList maxLayers(patchNLayers.size(), labelMin);
1019  labelList minLayers(patchNLayers.size(), labelMax);
1020 
1021  forAll(patchIDs, i)
1022  {
1023  label patchi = patchIDs[i];
1024 
1025  const labelList& meshPoints = mesh.boundaryMesh()[patchi].meshPoints();
1026 
1027  label wantedLayers = patchToNLayers[patchi];
1028 
1029  forAll(meshPoints, patchPointi)
1030  {
1031  label ppPointi = pp.meshPointMap()[meshPoints[patchPointi]];
1032 
1033  maxLayers[ppPointi] = max(wantedLayers, maxLayers[ppPointi]);
1034  minLayers[ppPointi] = min(wantedLayers, minLayers[ppPointi]);
1035  }
1036  }
1037 
1039  (
1040  mesh,
1041  pp.meshPoints(),
1042  maxLayers,
1043  maxEqOp<label>(),
1044  labelMin // null value
1045  );
1047  (
1048  mesh,
1049  pp.meshPoints(),
1050  minLayers,
1051  minEqOp<label>(),
1052  labelMax // null value
1053  );
1054 
1055  // Unmark any point with different min and max
1056  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1057 
1058  //label nConflicts = 0;
1059 
1060  forAll(maxLayers, i)
1061  {
1062  if (maxLayers[i] == labelMin || minLayers[i] == labelMax)
1063  {
1065  << "Patchpoint:" << i << " coord:" << pp.localPoints()[i]
1066  << " maxLayers:" << maxLayers
1067  << " minLayers:" << minLayers
1068  << abort(FatalError);
1069  }
1070  else if (maxLayers[i] == minLayers[i])
1071  {
1072  // Ok setting.
1073  patchNLayers[i] = maxLayers[i];
1074  }
1075  else
1076  {
1077  // Inconsistent num layers between patch faces using point
1078  //if
1079  //(
1080  // unmarkExtrusion
1081  // (
1082  // i,
1083  // patchDisp,
1084  // patchNLayers,
1085  // extrudeStatus
1086  // )
1087  //)
1088  //{
1089  // nConflicts++;
1090  //}
1091  patchNLayers[i] = maxLayers[i];
1092  }
1093  }
1094 
1095 
1096  // Calculate number of cells to create
1097  nAddedCells = 0;
1098  forAll(pp.localFaces(), facei)
1099  {
1100  const face& f = pp.localFaces()[facei];
1101 
1102  // Get max of extrusion per point
1103  label nCells = 0;
1104  forAll(f, fp)
1105  {
1106  nCells = max(nCells, patchNLayers[f[fp]]);
1107  }
1108 
1109  nAddedCells += nCells;
1110  }
1111  reduce(nAddedCells, sumOp<label>());
1112 
1113  //reduce(nConflicts, sumOp<label>());
1114  //
1115  //Info<< "Set displacement to zero for " << nConflicts
1116  // << " points due to points being on multiple regions"
1117  // << " with inconsistent nLayers specification." << endl;
1118 }
1119 
1120 
1121 // Construct pointVectorField with correct boundary conditions for adding
1122 // layers
1124 Foam::snappyLayerDriver::makeLayerDisplacementField
1125 (
1126  const pointMesh& pMesh,
1127  const labelList& numLayers
1128 )
1129 {
1130  // Construct displacement field.
1131  const pointBoundaryMesh& pointPatches = pMesh.boundary();
1132 
1133  wordList patchFieldTypes
1134  (
1135  pointPatches.size(),
1136  slipPointPatchVectorField::typeName
1137  );
1138  wordList actualPatchTypes(patchFieldTypes.size());
1139  forAll(pointPatches, patchi)
1140  {
1141  actualPatchTypes[patchi] = pointPatches[patchi].type();
1142  }
1143 
1144  forAll(numLayers, patchi)
1145  {
1146  // 0 layers: do not allow slip so fixedValue 0
1147  // >0 layers: fixedValue which gets adapted
1148  if (numLayers[patchi] == 0)
1149  {
1150  patchFieldTypes[patchi] =
1151  zeroFixedValuePointPatchVectorField::typeName;
1152  }
1153  else if (numLayers[patchi] > 0)
1154  {
1155  patchFieldTypes[patchi] = fixedValuePointPatchVectorField::typeName;
1156  }
1157  }
1158 
1159  forAll(pointPatches, patchi)
1160  {
1161  if (isA<processorPointPatch>(pointPatches[patchi]))
1162  {
1163  patchFieldTypes[patchi] = calculatedPointPatchVectorField::typeName;
1164  }
1165  else if (isA<cyclicPointPatch>(pointPatches[patchi]))
1166  {
1167  patchFieldTypes[patchi] = cyclicSlipPointPatchVectorField::typeName;
1168  }
1169  }
1170 
1171 
1172  const polyMesh& mesh = pMesh();
1173 
1174  // Note: time().timeName() instead of meshRefinement::timeName() since
1175  // postprocessable field.
1176 
1178  (
1179  IOobject
1180  (
1181  "pointDisplacement",
1182  mesh.time().timeName(),
1183  mesh,
1186  ),
1187  pMesh,
1189  patchFieldTypes,
1190  actualPatchTypes
1191  );
1192 }
1193 
1194 
1195 void Foam::snappyLayerDriver::growNoExtrusion
1196 (
1197  const indirectPrimitivePatch& pp,
1198  pointField& patchDisp,
1199  labelList& patchNLayers,
1200  List<extrudeMode>& extrudeStatus
1201 ) const
1202 {
1203  Info<< nl << "Growing non-extrusion points by one layer ..." << endl;
1204 
1205  List<extrudeMode> grownExtrudeStatus(extrudeStatus);
1206 
1207  const faceList& localFaces = pp.localFaces();
1208 
1209  label nGrown = 0;
1210 
1211  forAll(localFaces, facei)
1212  {
1213  const face& f = localFaces[facei];
1214 
1215  bool hasSqueeze = false;
1216  forAll(f, fp)
1217  {
1218  if (extrudeStatus[f[fp]] == NOEXTRUDE)
1219  {
1220  hasSqueeze = true;
1221  break;
1222  }
1223  }
1224 
1225  if (hasSqueeze)
1226  {
1227  // Squeeze all points of face
1228  forAll(f, fp)
1229  {
1230  if
1231  (
1232  extrudeStatus[f[fp]] == EXTRUDE
1233  && grownExtrudeStatus[f[fp]] != NOEXTRUDE
1234  )
1235  {
1236  grownExtrudeStatus[f[fp]] = NOEXTRUDE;
1237  nGrown++;
1238  }
1239  }
1240  }
1241  }
1242 
1243  extrudeStatus.transfer(grownExtrudeStatus);
1244 
1245 
1246  // Synchronise since might get called multiple times.
1247  // Use the fact that NOEXTRUDE is the minimum value.
1248  {
1249  labelList status(extrudeStatus.size());
1250  forAll(status, i)
1251  {
1252  status[i] = extrudeStatus[i];
1253  }
1255  (
1256  meshRefiner_.mesh(),
1257  pp.meshPoints(),
1258  status,
1259  minEqOp<label>(),
1260  labelMax // null value
1261  );
1262  forAll(status, i)
1263  {
1264  extrudeStatus[i] = extrudeMode(status[i]);
1265  }
1266  }
1267 
1268 
1269  forAll(extrudeStatus, patchPointi)
1270  {
1271  if (extrudeStatus[patchPointi] == NOEXTRUDE)
1272  {
1273  patchDisp[patchPointi] = Zero;
1274  patchNLayers[patchPointi] = 0;
1275  }
1276  }
1277 
1278  reduce(nGrown, sumOp<label>());
1279 
1280  Info<< "Set displacement to zero for an additional " << nGrown
1281  << " points." << endl;
1282 }
1283 
1284 
1285 void Foam::snappyLayerDriver::determineSidePatches
1286 (
1287  const globalIndex& globalFaces,
1288  const labelListList& edgeGlobalFaces,
1289  const indirectPrimitivePatch& pp,
1290 
1291  labelList& edgePatchID,
1292  labelList& edgeZoneID,
1293  boolList& edgeFlip,
1294  labelList& inflateFaceID
1295 )
1296 {
1297  // Sometimes edges-to-be-extruded are on more than 2 processors.
1298  // Work out which 2 hold the faces to be extruded and thus which procpatch
1299  // the edge-face should be in. As an additional complication this might
1300  // mean that 2 procesors that were only edge-connected now suddenly need
1301  // to become face-connected i.e. have a processor patch between them.
1302 
1303  fvMesh& mesh = meshRefiner_.mesh();
1304 
1305  // Determine edgePatchID. Any additional processor boundary gets added to
1306  // patchToNbrProc,nbrProcToPatch and nPatches gets set to the new number
1307  // of patches.
1308  // Note: faceZones are at this point split into baffles so any zone
1309  // information might also come from boundary faces (hence
1310  // zoneFromAnyFace set in call to calcExtrudeInfo)
1311  label nPatches;
1312  Map<label> nbrProcToPatch;
1313  Map<label> patchToNbrProc;
1315  (
1316  true, // zoneFromAnyFace
1317 
1318  mesh,
1319  globalFaces,
1320  edgeGlobalFaces,
1321  pp,
1322 
1323  edgePatchID,
1324  nPatches,
1325  nbrProcToPatch,
1326  patchToNbrProc,
1327  edgeZoneID,
1328  edgeFlip,
1329  inflateFaceID
1330  );
1331 
1332  label nOldPatches = mesh.boundaryMesh().size();
1333  label nAdded = returnReduce(nPatches-nOldPatches, sumOp<label>());
1334  Info<< nl << "Adding in total " << nAdded/2 << " inter-processor patches to"
1335  << " handle extrusion of non-manifold processor boundaries."
1336  << endl;
1337 
1338  if (nAdded > 0)
1339  {
1340  // We might not add patches in same order as in patchToNbrProc
1341  // so prepare to renumber edgePatchID
1342  Map<label> wantedToAddedPatch;
1343 
1344  for (label patchi = nOldPatches; patchi < nPatches; patchi++)
1345  {
1346  label nbrProci = patchToNbrProc[patchi];
1347  word name
1348  (
1350  );
1351 
1352  dictionary patchDict;
1353  patchDict.add("type", processorPolyPatch::typeName);
1354  patchDict.add("myProcNo", Pstream::myProcNo());
1355  patchDict.add("neighbProcNo", nbrProci);
1356  patchDict.add("nFaces", 0);
1357  patchDict.add("startFace", mesh.nFaces());
1358 
1359  //Pout<< "Adding patch " << patchi
1360  // << " name:" << name
1361  // << " between " << Pstream::myProcNo()
1362  // << " and " << nbrProci << endl;
1363 
1364  label procPatchi = meshRefiner_.appendPatch
1365  (
1366  mesh,
1367  mesh.boundaryMesh().size(), // new patch index
1368  name,
1369  patchDict
1370  );
1371  wantedToAddedPatch.insert(patchi, procPatchi);
1372  }
1373 
1374  // Renumber edgePatchID
1375  forAll(edgePatchID, i)
1376  {
1377  label patchi = edgePatchID[i];
1378  const auto fnd = wantedToAddedPatch.cfind(patchi);
1379  if (fnd.good())
1380  {
1381  edgePatchID[i] = fnd.val();
1382  }
1383  }
1384 
1385  mesh.clearOut();
1386  const_cast<polyBoundaryMesh&>(mesh.boundaryMesh()).updateMesh();
1387  }
1388 }
1389 
1390 
1391 void Foam::snappyLayerDriver::calculateLayerThickness
1392 (
1393  const indirectPrimitivePatch& pp,
1394  const labelList& patchIDs,
1395  const layerParameters& layerParams,
1396  const labelList& cellLevel,
1397  const labelList& patchNLayers,
1398  const scalar edge0Len,
1399 
1400  scalarField& thickness,
1401  scalarField& minThickness,
1402  scalarField& expansionRatio
1403 ) const
1404 {
1405  const fvMesh& mesh = meshRefiner_.mesh();
1406  const polyBoundaryMesh& patches = mesh.boundaryMesh();
1407 
1408 
1409  // Rework patch-wise layer parameters into minimum per point
1410  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1411  // Note: only layer parameters consistent with layer specification
1412  // method (see layerParameters) will be correct.
1413  scalarField firstLayerThickness(pp.nPoints(), GREAT);
1414  scalarField finalLayerThickness(pp.nPoints(), GREAT);
1415  scalarField totalThickness(pp.nPoints(), GREAT);
1416  scalarField expRatio(pp.nPoints(), GREAT);
1417 
1418  minThickness.setSize(pp.nPoints());
1419  minThickness = GREAT;
1420 
1421  thickness.setSize(pp.nPoints());
1422  thickness = GREAT;
1423 
1424  expansionRatio.setSize(pp.nPoints());
1425  expansionRatio = GREAT;
1426 
1427  for (const label patchi : patchIDs)
1428  {
1429  const labelList& meshPoints = patches[patchi].meshPoints();
1430 
1431  forAll(meshPoints, patchPointi)
1432  {
1433  label ppPointi = pp.meshPointMap()[meshPoints[patchPointi]];
1434 
1435  firstLayerThickness[ppPointi] = min
1436  (
1437  firstLayerThickness[ppPointi],
1438  layerParams.firstLayerThickness()[patchi]
1439  );
1440  finalLayerThickness[ppPointi] = min
1441  (
1442  finalLayerThickness[ppPointi],
1443  layerParams.finalLayerThickness()[patchi]
1444  );
1445  totalThickness[ppPointi] = min
1446  (
1447  totalThickness[ppPointi],
1448  layerParams.thickness()[patchi]
1449  );
1450  expRatio[ppPointi] = min
1451  (
1452  expRatio[ppPointi],
1453  layerParams.expansionRatio()[patchi]
1454  );
1455  minThickness[ppPointi] = min
1456  (
1457  minThickness[ppPointi],
1458  layerParams.minThickness()[patchi]
1459  );
1460  }
1461  }
1462 
1464  (
1465  mesh,
1466  pp.meshPoints(),
1467  firstLayerThickness,
1468  minEqOp<scalar>(),
1469  GREAT // null value
1470  );
1472  (
1473  mesh,
1474  pp.meshPoints(),
1475  finalLayerThickness,
1476  minEqOp<scalar>(),
1477  GREAT // null value
1478  );
1480  (
1481  mesh,
1482  pp.meshPoints(),
1483  totalThickness,
1484  minEqOp<scalar>(),
1485  GREAT // null value
1486  );
1488  (
1489  mesh,
1490  pp.meshPoints(),
1491  expRatio,
1492  minEqOp<scalar>(),
1493  GREAT // null value
1494  );
1496  (
1497  mesh,
1498  pp.meshPoints(),
1499  minThickness,
1500  minEqOp<scalar>(),
1501  GREAT // null value
1502  );
1503 
1504 
1505  // Now the thicknesses are set according to the minimum of connected
1506  // patches.
1507 
1508  // Determine per point the max cell level of connected cells
1509  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1510 
1511  labelList maxPointLevel(pp.nPoints(), labelMin);
1512  {
1513  forAll(pp, i)
1514  {
1515  label ownLevel = cellLevel[mesh.faceOwner()[pp.addressing()[i]]];
1516 
1517  const face& f = pp.localFaces()[i];
1518 
1519  forAll(f, fp)
1520  {
1521  maxPointLevel[f[fp]] = max(maxPointLevel[f[fp]], ownLevel);
1522  }
1523  }
1524 
1526  (
1527  mesh,
1528  pp.meshPoints(),
1529  maxPointLevel,
1530  maxEqOp<label>(),
1531  labelMin // null value
1532  );
1533  }
1534 
1535 
1536  // Rework relative thickness into absolute
1537  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1538  // by multiplying with the internal cell size.
1539  // Note that we cannot loop over the patches since then points on
1540  // multiple patches would get multiplied with edgeLen twice ..
1541  {
1542  // Multiplication factor for relative sizes
1543  scalarField edgeLen(pp.nPoints(), GREAT);
1544 
1546 
1547  bitSet isRelativePoint(mesh.nPoints());
1548 
1549  for (const label patchi : patchIDs)
1550  {
1551  const labelList& meshPoints = patches[patchi].meshPoints();
1552  const layerParameters::thicknessModelType patchSpec =
1553  layerParams.layerModels()[patchi];
1554  const bool relSize = layerParams.relativeSizes()[patchi];
1555 
1556  for (const label meshPointi : meshPoints)
1557  {
1558  const label ppPointi = pp.meshPointMap()[meshPointi];
1559 
1560  // Note: who wins if different specs?
1561 
1562  // Calculate undistorted edge size for this level.
1563  edgeLen[ppPointi] = min
1564  (
1565  edgeLen[ppPointi],
1566  edge0Len/(1<<maxPointLevel[ppPointi])
1567  );
1568  spec[ppPointi] = max(spec[ppPointi], patchSpec);
1569  isRelativePoint[meshPointi] =
1570  isRelativePoint[meshPointi]
1571  || relSize;
1572  }
1573  }
1574 
1576  (
1577  mesh,
1578  pp.meshPoints(),
1579  edgeLen,
1580  minEqOp<scalar>(),
1581  GREAT // null value
1582  );
1584  (
1585  mesh,
1586  pp.meshPoints(),
1587  spec,
1588  maxEqOp<label>(),
1589  label(layerParameters::FIRST_AND_TOTAL) // null value
1590  );
1592  (
1593  mesh,
1594  isRelativePoint,
1595  orEqOp<unsigned int>(),
1596  0
1597  );
1598 
1599 
1600 
1601 
1602  forAll(pp.meshPoints(), pointi)
1603  {
1604  const label meshPointi = pp.meshPoints()[pointi];
1605  const layerParameters::thicknessModelType pointSpec =
1606  static_cast<layerParameters::thicknessModelType>(spec[pointi]);
1607 
1609  {
1610  // This overrules the relative sizes flag for
1611  // first (always absolute) and final (always relative)
1612  finalLayerThickness[pointi] *= edgeLen[pointi];
1613  if (isRelativePoint[meshPointi])
1614  {
1615  totalThickness[pointi] *= edgeLen[pointi];
1616  minThickness[pointi] *= edgeLen[pointi];
1617  }
1618  }
1619  else if (isRelativePoint[meshPointi])
1620  {
1621  firstLayerThickness[pointi] *= edgeLen[pointi];
1622  finalLayerThickness[pointi] *= edgeLen[pointi];
1623  totalThickness[pointi] *= edgeLen[pointi];
1624  minThickness[pointi] *= edgeLen[pointi];
1625  }
1626 
1627  thickness[pointi] = min
1628  (
1629  thickness[pointi],
1631  (
1632  pointSpec,
1633  patchNLayers[pointi],
1634  firstLayerThickness[pointi],
1635  finalLayerThickness[pointi],
1636  totalThickness[pointi],
1637  expRatio[pointi]
1638  )
1639  );
1640  expansionRatio[pointi] = min
1641  (
1642  expansionRatio[pointi],
1643  layerParameters::layerExpansionRatio
1644  (
1645  pointSpec,
1646  patchNLayers[pointi],
1647  firstLayerThickness[pointi],
1648  finalLayerThickness[pointi],
1649  totalThickness[pointi],
1650  expRatio[pointi]
1651  )
1652  );
1653  }
1654  }
1655 
1656  // Synchronise the determined thicknes. Note that this should not be
1657  // necessary since the inputs to the calls to layerThickness,
1658  // layerExpansionRatio above are already parallel consistent
1659 
1661  (
1662  mesh,
1663  pp.meshPoints(),
1664  thickness,
1665  minEqOp<scalar>(),
1666  GREAT // null value
1667  );
1669  (
1670  mesh,
1671  pp.meshPoints(),
1672  expansionRatio,
1673  minEqOp<scalar>(),
1674  GREAT // null value
1675  );
1676 
1677  //Info<< "calculateLayerThickness : min:" << gMin(thickness)
1678  // << " max:" << gMax(thickness) << endl;
1679 
1680  // Print a bit
1681  {
1682  const polyBoundaryMesh& patches = mesh.boundaryMesh();
1683 
1684  const int oldPrecision = Info.stream().precision();
1685 
1686  // Find maximum length of a patch name, for a nicer output
1687  label maxPatchNameLen = 0;
1688  forAll(patchIDs, i)
1689  {
1690  label patchi = patchIDs[i];
1691  word patchName = patches[patchi].name();
1692  maxPatchNameLen = max(maxPatchNameLen, label(patchName.size()));
1693  }
1694 
1695  Info<< nl
1696  << setf(ios_base::left) << setw(maxPatchNameLen) << "patch"
1697  << setw(0) << " faces layers avg thickness[m]" << nl
1698  << setf(ios_base::left) << setw(maxPatchNameLen) << " "
1699  << setw(0) << " near-wall overall" << nl
1700  << setf(ios_base::left) << setw(maxPatchNameLen) << "-----"
1701  << setw(0) << " ----- ------ --------- -------" << endl;
1702 
1703 
1704  const bitSet isMasterPoint(syncTools::getMasterPoints(mesh));
1705 
1706  forAll(patchIDs, i)
1707  {
1708  label patchi = patchIDs[i];
1709 
1710  const labelList& meshPoints = patches[patchi].meshPoints();
1712  layerParams.layerModels()[patchi];
1713 
1714  scalar sumThickness = 0;
1715  scalar sumNearWallThickness = 0;
1716  label nMasterPoints = 0;
1717 
1718  forAll(meshPoints, patchPointi)
1719  {
1720  label meshPointi = meshPoints[patchPointi];
1721  if (isMasterPoint[meshPointi])
1722  {
1723  label ppPointi = pp.meshPointMap()[meshPointi];
1724 
1725  sumThickness += thickness[ppPointi];
1726  sumNearWallThickness += layerParams.firstLayerThickness
1727  (
1728  spec,
1729  patchNLayers[ppPointi],
1730  firstLayerThickness[ppPointi],
1731  finalLayerThickness[ppPointi],
1732  thickness[ppPointi],
1733  expansionRatio[ppPointi]
1734  );
1735  nMasterPoints++;
1736  }
1737  }
1738 
1739  label totNPoints = returnReduce(nMasterPoints, sumOp<label>());
1740 
1741  // For empty patches, totNPoints is 0.
1742  scalar avgThickness = 0;
1743  scalar avgNearWallThickness = 0;
1744 
1745  if (totNPoints > 0)
1746  {
1747  avgThickness =
1748  returnReduce(sumThickness, sumOp<scalar>())
1749  / totNPoints;
1750  avgNearWallThickness =
1751  returnReduce(sumNearWallThickness, sumOp<scalar>())
1752  / totNPoints;
1753  }
1754 
1755  Info<< setf(ios_base::left) << setw(maxPatchNameLen)
1756  << patches[patchi].name() << setprecision(3)
1757  << " " << setw(8)
1758  << returnReduce(patches[patchi].size(), sumOp<scalar>())
1759  << " " << setw(6) << layerParams.numLayers()[patchi]
1760  << " " << setw(8) << avgNearWallThickness
1761  << " " << setw(8) << avgThickness
1762  << endl;
1763  }
1764  Info<< setprecision(oldPrecision) << endl;
1765  }
1766 }
1767 
1768 
1769 // Synchronize displacement among coupled patches.
1770 void Foam::snappyLayerDriver::syncPatchDisplacement
1771 (
1772  const indirectPrimitivePatch& pp,
1773  const scalarField& minThickness,
1774  pointField& patchDisp,
1775  labelList& patchNLayers,
1776  List<extrudeMode>& extrudeStatus
1777 ) const
1778 {
1779  const fvMesh& mesh = meshRefiner_.mesh();
1780  const labelList& meshPoints = pp.meshPoints();
1781 
1782  //label nChangedTotal = 0;
1783 
1784  while (true)
1785  {
1786  label nChanged = 0;
1787 
1788  // Sync displacement (by taking min)
1790  (
1791  mesh,
1792  meshPoints,
1793  patchDisp,
1794  minMagSqrEqOp<vector>(),
1795  point::rootMax // null value
1796  );
1797 
1798  // Unmark if displacement too small
1799  forAll(patchDisp, i)
1800  {
1801  if (mag(patchDisp[i]) < minThickness[i])
1802  {
1803  if
1804  (
1805  unmarkExtrusion
1806  (
1807  i,
1808  patchDisp,
1809  patchNLayers,
1810  extrudeStatus
1811  )
1812  )
1813  {
1814  nChanged++;
1815  }
1816  }
1817  }
1818 
1819  labelList syncPatchNLayers(patchNLayers);
1820 
1822  (
1823  mesh,
1824  meshPoints,
1825  syncPatchNLayers,
1826  minEqOp<label>(),
1827  labelMax // null value
1828  );
1829 
1830  // Reset if differs
1831  // 1. take max
1832  forAll(syncPatchNLayers, i)
1833  {
1834  if (syncPatchNLayers[i] != patchNLayers[i])
1835  {
1836  if
1837  (
1838  unmarkExtrusion
1839  (
1840  i,
1841  patchDisp,
1842  patchNLayers,
1843  extrudeStatus
1844  )
1845  )
1846  {
1847  nChanged++;
1848  }
1849  }
1850  }
1851 
1853  (
1854  mesh,
1855  meshPoints,
1856  syncPatchNLayers,
1857  maxEqOp<label>(),
1858  labelMin // null value
1859  );
1860 
1861  // Reset if differs
1862  // 2. take min
1863  forAll(syncPatchNLayers, i)
1864  {
1865  if (syncPatchNLayers[i] != patchNLayers[i])
1866  {
1867  if
1868  (
1869  unmarkExtrusion
1870  (
1871  i,
1872  patchDisp,
1873  patchNLayers,
1874  extrudeStatus
1875  )
1876  )
1877  {
1878  nChanged++;
1879  }
1880  }
1881  }
1882  //nChangedTotal += nChanged;
1883 
1884  if (!returnReduceOr(nChanged))
1885  {
1886  break;
1887  }
1888  }
1889 
1890  //Info<< "Prevented extrusion on "
1891  // << returnReduce(nChangedTotal, sumOp<label>())
1892  // << " coupled patch points during syncPatchDisplacement." << endl;
1893 }
1894 
1895 
1896 // Calculate displacement vector for all patch points. Uses pointNormal.
1897 // Checks that displaced patch point would be visible from all centres
1898 // of the faces using it.
1899 // extrudeStatus is both input and output and gives the status of each
1900 // patch point.
1901 void Foam::snappyLayerDriver::getPatchDisplacement
1902 (
1903  const indirectPrimitivePatch& pp,
1904  const scalarField& thickness,
1905  const scalarField& minThickness,
1906  const scalarField& expansionRatio,
1907 
1908  pointField& patchDisp,
1909  labelList& patchNLayers,
1910  List<extrudeMode>& extrudeStatus
1911 ) const
1912 {
1913  Info<< nl << "Determining displacement for added points"
1914  << " according to pointNormal ..." << endl;
1915 
1916  const fvMesh& mesh = meshRefiner_.mesh();
1917  const vectorField& faceNormals = pp.faceNormals();
1918  const labelListList& pointFaces = pp.pointFaces();
1919  const pointField& localPoints = pp.localPoints();
1920 
1921  // Determine pointNormal
1922  // ~~~~~~~~~~~~~~~~~~~~~
1923 
1924  pointField pointNormals(PatchTools::pointNormals(mesh, pp));
1925 
1926 
1927  // Determine local length scale on patch
1928  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1929 
1930  // Start off from same thickness everywhere (except where no extrusion)
1931  patchDisp = thickness*pointNormals;
1932 
1933 
1934  label nNoVisNormal = 0;
1935  label nExtrudeRemove = 0;
1936 
1937 
1939 // {
1940 // OBJstream twoStr
1941 // (
1942 // mesh.time().path()
1943 // / "twoFacePoints_"
1944 // + meshRefiner_.timeName()
1945 // + ".obj"
1946 // );
1947 // OBJstream multiStr
1948 // (
1949 // mesh.time().path()
1950 // / "multiFacePoints_"
1951 // + meshRefiner_.timeName()
1952 // + ".obj"
1953 // );
1954 // Pout<< "Writing points inbetween two faces on same cell to "
1955 // << twoStr.name() << endl;
1956 // Pout<< "Writing points inbetween three or more faces on same cell to "
1957 // << multiStr.name() << endl;
1958 // // Check whether inbetween patch faces on same cell
1959 // Map<labelList> cellToFaces;
1960 // forAll(pointNormals, patchPointi)
1961 // {
1962 // const labelList& pFaces = pointFaces[patchPointi];
1963 //
1964 // cellToFaces.clear();
1965 // forAll(pFaces, pFacei)
1966 // {
1967 // const label patchFacei = pFaces[pFacei];
1968 // const label meshFacei = pp.addressing()[patchFacei];
1969 // const label celli = mesh.faceOwner()[meshFacei];
1970 //
1971 // cellToFaces(celli).push_uniq(patchFacei);
1972 // }
1973 //
1974 // forAllConstIters(cellToFaces, iter)
1975 // {
1976 // if (iter().size() == 2)
1977 // {
1978 // twoStr.write(pp.localPoints()[patchPointi]);
1979 // }
1980 // else if (iter().size() > 2)
1981 // {
1982 // multiStr.write(pp.localPoints()[patchPointi]);
1983 //
1984 // const scalar ratio =
1985 // layerParameters::finalLayerThicknessRatio
1986 // (
1987 // patchNLayers[patchPointi],
1988 // expansionRatio[patchPointi]
1989 // );
1990 // // Get thickness of cell next to bulk
1991 // const vector finalDisp
1992 // (
1993 // ratio*patchDisp[patchPointi]
1994 // );
1995 //
1996 // //Pout<< "** point:" << pp.localPoints()[patchPointi]
1997 // // << " on cell:" << iter.key()
1998 // // << " faces:" << iter()
1999 // // << " displacement was:" << patchDisp[patchPointi]
2000 // // << " ratio:" << ratio
2001 // // << " finalDispl:" << finalDisp;
2002 //
2003 // // Half this thickness
2004 // patchDisp[patchPointi] -= 0.8*finalDisp;
2005 //
2006 // //Pout<< " new displacement:"
2007 // // << patchDisp[patchPointi] << endl;
2008 // }
2009 // }
2010 // }
2011 //
2012 // Pout<< "Written " << multiStr.nVertices()
2013 // << " points inbetween three or more faces on same cell to "
2014 // << multiStr.name() << endl;
2015 // }
2017 
2018 
2019  // Check if no extrude possible.
2020  forAll(pointNormals, patchPointi)
2021  {
2022  label meshPointi = pp.meshPoints()[patchPointi];
2023 
2024  if (extrudeStatus[patchPointi] == NOEXTRUDE)
2025  {
2026  // Do not use unmarkExtrusion; forcibly set to zero extrusion.
2027  patchNLayers[patchPointi] = 0;
2028  patchDisp[patchPointi] = Zero;
2029  }
2030  else
2031  {
2032  // Get normal
2033  const vector& n = pointNormals[patchPointi];
2034 
2035  if (!meshTools::visNormal(n, faceNormals, pointFaces[patchPointi]))
2036  {
2038  {
2039  Pout<< "No valid normal for point " << meshPointi
2040  << ' ' << pp.points()[meshPointi]
2041  << "; setting displacement to "
2042  << patchDisp[patchPointi]
2043  << endl;
2044  }
2045 
2046  extrudeStatus[patchPointi] = EXTRUDEREMOVE;
2047  nNoVisNormal++;
2048  }
2049  }
2050  }
2051 
2052  // At illegal points make displacement average of new neighbour positions
2053  forAll(extrudeStatus, patchPointi)
2054  {
2055  if (extrudeStatus[patchPointi] == EXTRUDEREMOVE)
2056  {
2057  point avg(Zero);
2058  label nPoints = 0;
2059 
2060  const labelList& pEdges = pp.pointEdges()[patchPointi];
2061 
2062  forAll(pEdges, i)
2063  {
2064  label edgei = pEdges[i];
2065 
2066  label otherPointi = pp.edges()[edgei].otherVertex(patchPointi);
2067 
2068  if (extrudeStatus[otherPointi] != NOEXTRUDE)
2069  {
2070  avg += localPoints[otherPointi] + patchDisp[otherPointi];
2071  nPoints++;
2072  }
2073  }
2074 
2075  if (nPoints > 0)
2076  {
2078  {
2079  Pout<< "Displacement at illegal point "
2080  << localPoints[patchPointi]
2081  << " set to "
2082  << (avg / nPoints - localPoints[patchPointi])
2083  << endl;
2084  }
2085 
2086  patchDisp[patchPointi] =
2087  avg / nPoints
2088  - localPoints[patchPointi];
2089 
2090  nExtrudeRemove++;
2091  }
2092  else
2093  {
2094  // All surrounding points are not extruded. Leave patchDisp
2095  // intact.
2096  }
2097  }
2098  }
2099 
2100  Info<< "Detected " << returnReduce(nNoVisNormal, sumOp<label>())
2101  << " points with point normal pointing through faces." << nl
2102  << "Reset displacement at "
2103  << returnReduce(nExtrudeRemove, sumOp<label>())
2104  << " points to average of surrounding points." << endl;
2105 
2106  // Make sure displacement is equal on both sides of coupled patches.
2107  syncPatchDisplacement
2108  (
2109  pp,
2110  minThickness,
2111  patchDisp,
2112  patchNLayers,
2113  extrudeStatus
2114  );
2115 
2116  Info<< endl;
2117 }
2118 
2119 
2120 bool Foam::snappyLayerDriver::sameEdgeNeighbour
2121 (
2122  const labelListList& globalEdgeFaces,
2123  const label myGlobalFacei,
2124  const label nbrGlobFacei,
2125  const label edgei
2126 ) const
2127 {
2128  const labelList& eFaces = globalEdgeFaces[edgei];
2129  if (eFaces.size() == 2)
2130  {
2131  return edge(myGlobalFacei, nbrGlobFacei) == edge(eFaces[0], eFaces[1]);
2132  }
2133 
2134  return false;
2135 }
2136 
2137 
2138 void Foam::snappyLayerDriver::getVertexString
2139 (
2140  const indirectPrimitivePatch& pp,
2141  const labelListList& globalEdgeFaces,
2142  const label facei,
2143  const label edgei,
2144  const label myGlobFacei,
2145  const label nbrGlobFacei,
2146  DynamicList<label>& vertices
2147 ) const
2148 {
2149  const labelList& fEdges = pp.faceEdges()[facei];
2150  label fp = fEdges.find(edgei);
2151 
2152  if (fp == -1)
2153  {
2155  << "problem." << abort(FatalError);
2156  }
2157 
2158  // Search back
2159  label startFp = fp;
2160 
2161  forAll(fEdges, i)
2162  {
2163  label prevFp = fEdges.rcIndex(startFp);
2164  if
2165  (
2166  !sameEdgeNeighbour
2167  (
2168  globalEdgeFaces,
2169  myGlobFacei,
2170  nbrGlobFacei,
2171  fEdges[prevFp]
2172  )
2173  )
2174  {
2175  break;
2176  }
2177  startFp = prevFp;
2178  }
2179 
2180  label endFp = fp;
2181  forAll(fEdges, i)
2182  {
2183  label nextFp = fEdges.fcIndex(endFp);
2184  if
2185  (
2186  !sameEdgeNeighbour
2187  (
2188  globalEdgeFaces,
2189  myGlobFacei,
2190  nbrGlobFacei,
2191  fEdges[nextFp]
2192  )
2193  )
2194  {
2195  break;
2196  }
2197  endFp = nextFp;
2198  }
2199 
2200  const face& f = pp.localFaces()[facei];
2201  vertices.clear();
2202  fp = startFp;
2203  while (fp != endFp)
2204  {
2205  vertices.append(f[fp]);
2206  fp = f.fcIndex(fp);
2207  }
2208  vertices.append(f[fp]);
2209  fp = f.fcIndex(fp);
2210  vertices.append(f[fp]);
2211 }
2212 
2213 
2214 // Truncates displacement
2215 // - for all patchFaces in the faceset displacement gets set to zero
2216 // - all displacement < minThickness gets set to zero
2217 Foam::label Foam::snappyLayerDriver::truncateDisplacement
2218 (
2219  const globalIndex& globalFaces,
2220  const labelListList& edgeGlobalFaces,
2221  const indirectPrimitivePatch& pp,
2222  const scalarField& minThickness,
2223  const faceSet& illegalPatchFaces,
2224  pointField& patchDisp,
2225  labelList& patchNLayers,
2226  List<extrudeMode>& extrudeStatus
2227 ) const
2228 {
2229  const fvMesh& mesh = meshRefiner_.mesh();
2230 
2231  label nChanged = 0;
2232 
2233  const Map<label>& meshPointMap = pp.meshPointMap();
2234 
2235  for (const label facei : illegalPatchFaces)
2236  {
2237  if (mesh.isInternalFace(facei))
2238  {
2240  << "Faceset " << illegalPatchFaces.name()
2241  << " contains internal face " << facei << nl
2242  << "It should only contain patch faces" << abort(FatalError);
2243  }
2244 
2245  const face& f = mesh.faces()[facei];
2246 
2247 
2248  forAll(f, fp)
2249  {
2250  const auto fnd = meshPointMap.cfind(f[fp]);
2251  if (fnd.good())
2252  {
2253  const label patchPointi = fnd.val();
2254 
2255  if (extrudeStatus[patchPointi] != NOEXTRUDE)
2256  {
2257  unmarkExtrusion
2258  (
2259  patchPointi,
2260  patchDisp,
2261  patchNLayers,
2262  extrudeStatus
2263  );
2264  nChanged++;
2265  }
2266  }
2267  }
2268  }
2269 
2270  forAll(patchDisp, patchPointi)
2271  {
2272  if (mag(patchDisp[patchPointi]) < minThickness[patchPointi])
2273  {
2274  if
2275  (
2276  unmarkExtrusion
2277  (
2278  patchPointi,
2279  patchDisp,
2280  patchNLayers,
2281  extrudeStatus
2282  )
2283  )
2284  {
2285  nChanged++;
2286  }
2287  }
2288  else if (extrudeStatus[patchPointi] == NOEXTRUDE)
2289  {
2290  // Make sure displacement is 0. Should already be so but ...
2291  patchDisp[patchPointi] = Zero;
2292  patchNLayers[patchPointi] = 0;
2293  }
2294  }
2295 
2296 
2297  const faceList& localFaces = pp.localFaces();
2298 
2299  while (true)
2300  {
2301  syncPatchDisplacement
2302  (
2303  pp,
2304  minThickness,
2305  patchDisp,
2306  patchNLayers,
2307  extrudeStatus
2308  );
2309 
2310 
2311  // Pinch
2312  // ~~~~~
2313 
2314  // Make sure that a face doesn't have two non-consecutive areas
2315  // not extruded (e.g. quad where vertex 0 and 2 are not extruded
2316  // but 1 and 3 are) since this gives topological errors.
2317 
2318  label nPinched = 0;
2319 
2320  forAll(localFaces, i)
2321  {
2322  const face& localF = localFaces[i];
2323 
2324  // Count number of transitions from unsnapped to snapped.
2325  label nTrans = 0;
2326 
2327  extrudeMode prevMode = extrudeStatus[localF.prevLabel(0)];
2328 
2329  forAll(localF, fp)
2330  {
2331  extrudeMode fpMode = extrudeStatus[localF[fp]];
2332 
2333  if (prevMode == NOEXTRUDE && fpMode != NOEXTRUDE)
2334  {
2335  nTrans++;
2336  }
2337  prevMode = fpMode;
2338  }
2339 
2340  if (nTrans > 1)
2341  {
2342  // Multiple pinches. Reset whole face as unextruded.
2343  if
2344  (
2345  unmarkExtrusion
2346  (
2347  localF,
2348  patchDisp,
2349  patchNLayers,
2350  extrudeStatus
2351  )
2352  )
2353  {
2354  nPinched++;
2355  nChanged++;
2356  }
2357  }
2358  }
2359 
2360  reduce(nPinched, sumOp<label>());
2361 
2362  Info<< "truncateDisplacement : Unextruded " << nPinched
2363  << " faces due to non-consecutive vertices being extruded." << endl;
2364 
2365 
2366  // Butterfly
2367  // ~~~~~~~~~
2368 
2369  // Make sure that a string of edges becomes a single face so
2370  // not a butterfly. Occasionally an 'edge' will have a single dangling
2371  // vertex due to face combining. These get extruded as a single face
2372  // (with a dangling vertex) so make sure this extrusion forms a single
2373  // shape.
2374  // - continuous i.e. no butterfly:
2375  // + +
2376  // |\ /|
2377  // | \ / |
2378  // +--+--+
2379  // - extrudes from all but the endpoints i.e. no partial
2380  // extrude
2381  // +
2382  // /|
2383  // / |
2384  // +--+--+
2385  // The common error topology is a pinch somewhere in the middle
2386  label nButterFly = 0;
2387  {
2388  DynamicList<label> stringedVerts;
2389  forAll(pp.edges(), edgei)
2390  {
2391  const labelList& globFaces = edgeGlobalFaces[edgei];
2392 
2393  if (globFaces.size() == 2)
2394  {
2395  label myFacei = pp.edgeFaces()[edgei][0];
2396  label myGlobalFacei = globalFaces.toGlobal
2397  (
2398  pp.addressing()[myFacei]
2399  );
2400  label nbrGlobalFacei =
2401  (
2402  globFaces[0] != myGlobalFacei
2403  ? globFaces[0]
2404  : globFaces[1]
2405  );
2406  getVertexString
2407  (
2408  pp,
2409  edgeGlobalFaces,
2410  myFacei,
2411  edgei,
2412  myGlobalFacei,
2413  nbrGlobalFacei,
2414  stringedVerts
2415  );
2416 
2417  if
2418  (
2419  extrudeStatus[stringedVerts[0]] != NOEXTRUDE
2420  || extrudeStatus[stringedVerts.last()] != NOEXTRUDE
2421  )
2422  {
2423  // Any pinch in the middle
2424  bool pinch = false;
2425  for (label i = 1; i < stringedVerts.size()-1; i++)
2426  {
2427  if (extrudeStatus[stringedVerts[i]] == NOEXTRUDE)
2428  {
2429  pinch = true;
2430  break;
2431  }
2432  }
2433  if (pinch)
2434  {
2435  forAll(stringedVerts, i)
2436  {
2437  if
2438  (
2439  unmarkExtrusion
2440  (
2441  stringedVerts[i],
2442  patchDisp,
2443  patchNLayers,
2444  extrudeStatus
2445  )
2446  )
2447  {
2448  nButterFly++;
2449  nChanged++;
2450  }
2451  }
2452  }
2453  }
2454  }
2455  }
2456  }
2457 
2458  reduce(nButterFly, sumOp<label>());
2459 
2460  Info<< "truncateDisplacement : Unextruded " << nButterFly
2461  << " faces due to stringed edges with inconsistent extrusion."
2462  << endl;
2463 
2464 
2465 
2466  // Consistent number of layers
2467  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
2468 
2469  // Make sure that a face has consistent number of layers for all
2470  // its vertices.
2471 
2472  label nDiffering = 0;
2473 
2474  //forAll(localFaces, i)
2475  //{
2476  // const face& localF = localFaces[i];
2477  //
2478  // label numLayers = -1;
2479  //
2480  // forAll(localF, fp)
2481  // {
2482  // if (patchNLayers[localF[fp]] > 0)
2483  // {
2484  // if (numLayers == -1)
2485  // {
2486  // numLayers = patchNLayers[localF[fp]];
2487  // }
2488  // else if (numLayers != patchNLayers[localF[fp]])
2489  // {
2490  // // Differing number of layers
2491  // if
2492  // (
2493  // unmarkExtrusion
2494  // (
2495  // localF,
2496  // patchDisp,
2497  // patchNLayers,
2498  // extrudeStatus
2499  // )
2500  // )
2501  // {
2502  // nDiffering++;
2503  // nChanged++;
2504  // }
2505  // break;
2506  // }
2507  // }
2508  // }
2509  //}
2510  //
2511  //reduce(nDiffering, sumOp<label>());
2512  //
2513  //Info<< "truncateDisplacement : Unextruded " << nDiffering
2514  // << " faces due to having differing number of layers." << endl;
2515 
2516  if (nPinched+nButterFly+nDiffering == 0)
2517  {
2518  break;
2519  }
2520  }
2521 
2522  return nChanged;
2523 }
2524 
2525 
2526 // Setup layer information (at points and faces) to modify mesh topology in
2527 // regions where layer mesh terminates.
2528 void Foam::snappyLayerDriver::setupLayerInfoTruncation
2529 (
2530  const indirectPrimitivePatch& pp,
2531  const labelList& patchNLayers,
2532  const List<extrudeMode>& extrudeStatus,
2533  const label nBufferCellsNoExtrude,
2534  labelList& nPatchPointLayers,
2535  labelList& nPatchFaceLayers
2536 ) const
2537 {
2538  Info<< nl << "Setting up information for layer truncation ..." << endl;
2539 
2540  const fvMesh& mesh = meshRefiner_.mesh();
2541 
2542  if (nBufferCellsNoExtrude < 0)
2543  {
2544  Info<< nl << "Performing no layer truncation."
2545  << " nBufferCellsNoExtrude set to less than 0 ..." << endl;
2546 
2547  // Face layers if any point gets extruded
2548  forAll(pp.localFaces(), patchFacei)
2549  {
2550  const face& f = pp.localFaces()[patchFacei];
2551 
2552  forAll(f, fp)
2553  {
2554  const label nPointLayers = patchNLayers[f[fp]];
2555  if (nPointLayers > 0)
2556  {
2557  if (nPatchFaceLayers[patchFacei] == -1)
2558  {
2559  nPatchFaceLayers[patchFacei] = nPointLayers;
2560  }
2561  else
2562  {
2563  nPatchFaceLayers[patchFacei] = min
2564  (
2565  nPatchFaceLayers[patchFacei],
2566  nPointLayers
2567  );
2568  }
2569  }
2570  }
2571  }
2572  nPatchPointLayers = patchNLayers;
2573 
2574  // Set any unset patch face layers
2575  forAll(nPatchFaceLayers, patchFacei)
2576  {
2577  if (nPatchFaceLayers[patchFacei] == -1)
2578  {
2579  nPatchFaceLayers[patchFacei] = 0;
2580  }
2581  }
2582  }
2583  else
2584  {
2585  // Determine max point layers per face.
2586  labelList maxLevel(pp.size(), Zero);
2587 
2588  forAll(pp.localFaces(), patchFacei)
2589  {
2590  const face& f = pp.localFaces()[patchFacei];
2591 
2592  // find patch faces where layer terminates (i.e contains extrude
2593  // and noextrude points).
2594 
2595  bool noExtrude = false;
2596  label mLevel = 0;
2597 
2598  forAll(f, fp)
2599  {
2600  if (extrudeStatus[f[fp]] == NOEXTRUDE)
2601  {
2602  noExtrude = true;
2603  }
2604  mLevel = max(mLevel, patchNLayers[f[fp]]);
2605  }
2606 
2607  if (mLevel > 0)
2608  {
2609  // So one of the points is extruded. Check if all are extruded
2610  // or is a mix.
2611 
2612  if (noExtrude)
2613  {
2614  nPatchFaceLayers[patchFacei] = 1;
2615  maxLevel[patchFacei] = mLevel;
2616  }
2617  else
2618  {
2619  maxLevel[patchFacei] = mLevel;
2620  }
2621  }
2622  }
2623 
2624  // We have the seed faces (faces with nPatchFaceLayers != maxLevel)
2625  // Now do a meshwave across the patch where we pick up neighbours
2626  // of seed faces.
2627  // Note: quite inefficient. Could probably be coded better.
2628 
2629  const labelListList& pointFaces = pp.pointFaces();
2630 
2631  label nLevels = gMax(patchNLayers);
2632 
2633  // flag neighbouring patch faces with number of layers to grow
2634  for (label ilevel = 1; ilevel < nLevels; ilevel++)
2635  {
2636  label nBuffer;
2637 
2638  if (ilevel == 1)
2639  {
2640  nBuffer = nBufferCellsNoExtrude - 1;
2641  }
2642  else
2643  {
2644  nBuffer = nBufferCellsNoExtrude;
2645  }
2646 
2647  for (label ibuffer = 0; ibuffer < nBuffer + 1; ibuffer++)
2648  {
2649  labelList tempCounter(nPatchFaceLayers);
2650 
2651  boolList foundNeighbour(pp.nPoints(), false);
2652 
2653  forAll(pp.meshPoints(), patchPointi)
2654  {
2655  forAll(pointFaces[patchPointi], pointFacei)
2656  {
2657  label facei = pointFaces[patchPointi][pointFacei];
2658 
2659  if
2660  (
2661  nPatchFaceLayers[facei] != -1
2662  && maxLevel[facei] > 0
2663  )
2664  {
2665  foundNeighbour[patchPointi] = true;
2666  break;
2667  }
2668  }
2669  }
2670 
2672  (
2673  mesh,
2674  pp.meshPoints(),
2675  foundNeighbour,
2676  orEqOp<bool>(),
2677  false // null value
2678  );
2679 
2680  forAll(pp.meshPoints(), patchPointi)
2681  {
2682  if (foundNeighbour[patchPointi])
2683  {
2684  forAll(pointFaces[patchPointi], pointFacei)
2685  {
2686  label facei = pointFaces[patchPointi][pointFacei];
2687  if
2688  (
2689  nPatchFaceLayers[facei] == -1
2690  && maxLevel[facei] > 0
2691  && ilevel < maxLevel[facei]
2692  )
2693  {
2694  tempCounter[facei] = ilevel;
2695  }
2696  }
2697  }
2698  }
2699  nPatchFaceLayers = tempCounter;
2700  }
2701  }
2702 
2703  forAll(pp.localFaces(), patchFacei)
2704  {
2705  if (nPatchFaceLayers[patchFacei] == -1)
2706  {
2707  nPatchFaceLayers[patchFacei] = maxLevel[patchFacei];
2708  }
2709  }
2710 
2711  forAll(pp.meshPoints(), patchPointi)
2712  {
2713  if (extrudeStatus[patchPointi] != NOEXTRUDE)
2714  {
2715  forAll(pointFaces[patchPointi], pointFacei)
2716  {
2717  label face = pointFaces[patchPointi][pointFacei];
2718  nPatchPointLayers[patchPointi] = max
2719  (
2720  nPatchPointLayers[patchPointi],
2721  nPatchFaceLayers[face]
2722  );
2723  }
2724  }
2725  else
2726  {
2727  nPatchPointLayers[patchPointi] = 0;
2728  }
2729  }
2731  (
2732  mesh,
2733  pp.meshPoints(),
2734  nPatchPointLayers,
2735  maxEqOp<label>(),
2736  label(0) // null value
2737  );
2738  }
2739 }
2740 
2741 
2742 // Does any of the cells use a face from faces?
2743 bool Foam::snappyLayerDriver::cellsUseFace
2744 (
2745  const polyMesh& mesh,
2746  const labelList& cellLabels,
2747  const labelHashSet& faces
2748 )
2749 {
2750  forAll(cellLabels, i)
2751  {
2752  const cell& cFaces = mesh.cells()[cellLabels[i]];
2753 
2754  forAll(cFaces, cFacei)
2755  {
2756  if (faces.found(cFaces[cFacei]))
2757  {
2758  return true;
2759  }
2760  }
2761  }
2762 
2763  return false;
2764 }
2765 
2766 
2767 // Checks the newly added cells and locally unmarks points so they
2768 // will not get extruded next time round. Returns global number of unmarked
2769 // points (0 if all was fine)
2770 Foam::label Foam::snappyLayerDriver::checkAndUnmark
2771 (
2772  const addPatchCellLayer& addLayer,
2773  const dictionary& meshQualityDict,
2774  const bool additionalReporting,
2775  const List<labelPair>& baffles,
2776  const indirectPrimitivePatch& pp,
2777  const fvMesh& newMesh,
2778 
2779  pointField& patchDisp,
2780  labelList& patchNLayers,
2781  List<extrudeMode>& extrudeStatus
2782 )
2783 {
2784  // Check the resulting mesh for errors
2785  Info<< nl << "Checking mesh with layer ..." << endl;
2786  faceSet wrongFaces(newMesh, "wrongFaces", newMesh.nFaces()/1000);
2788  (
2789  false,
2790  newMesh,
2791  meshQualityDict,
2792  identity(newMesh.nFaces()),
2793  baffles,
2794  wrongFaces,
2795  false // dryRun_
2796  );
2797  Info<< "Detected " << returnReduce(wrongFaces.size(), sumOp<label>())
2798  << " illegal faces"
2799  << " (concave, zero area or negative cell pyramid volume)"
2800  << endl;
2801 
2802  // Undo local extrusion if
2803  // - any of the added cells in error
2804 
2805  label nChanged = 0;
2806 
2807  // Get all cells in the layer.
2808  labelListList addedCells
2809  (
2811  (
2812  newMesh,
2813  addLayer.layerFaces()
2814  )
2815  );
2816 
2817  // Check if any of the faces in error uses any face of an added cell
2818  // - if additionalReporting print the few remaining areas for ease of
2819  // finding out where the problems are.
2820 
2821  const label nReportMax = 10;
2822  DynamicField<point> disabledFaceCentres(nReportMax);
2823 
2824  forAll(addedCells, oldPatchFacei)
2825  {
2826  // Get the cells (in newMesh labels) per old patch face (in mesh
2827  // labels)
2828  const labelList& fCells = addedCells[oldPatchFacei];
2829 
2830  if (cellsUseFace(newMesh, fCells, wrongFaces))
2831  {
2832  // Unmark points on old mesh
2833  if
2834  (
2835  unmarkExtrusion
2836  (
2837  pp.localFaces()[oldPatchFacei],
2838  patchDisp,
2839  patchNLayers,
2840  extrudeStatus
2841  )
2842  )
2843  {
2844  if (additionalReporting && (nChanged < nReportMax))
2845  {
2846  disabledFaceCentres.append
2847  (
2848  pp.faceCentres()[oldPatchFacei]
2849  );
2850  }
2851 
2852  nChanged++;
2853  }
2854  }
2855  }
2856 
2857 
2858  label nChangedTotal = returnReduce(nChanged, sumOp<label>());
2859 
2860  if (additionalReporting)
2861  {
2862  // Limit the number of points to be printed so that
2863  // not too many points are reported when running in parallel
2864  // Not accurate, i.e. not always nReportMax points are written,
2865  // but this estimation avoid some communication here.
2866  // The important thing, however, is that when only a few faces
2867  // are disabled, their coordinates are printed, and this should be
2868  // the case
2869  label nReportLocal = nChanged;
2870  if (nChangedTotal > nReportMax)
2871  {
2872  nReportLocal = min
2873  (
2874  max(nChangedTotal / Pstream::nProcs(), 1),
2875  min
2876  (
2877  nChanged,
2878  max(nReportMax / Pstream::nProcs(), 1)
2879  )
2880  );
2881  }
2882 
2883  if (nReportLocal)
2884  {
2885  Pout<< "Checked mesh with layers. Disabled extrusion at " << endl;
2886  for (label i=0; i < nReportLocal; i++)
2887  {
2888  Pout<< " " << disabledFaceCentres[i] << endl;
2889  }
2890  }
2891 
2892  label nReportTotal = returnReduce(nReportLocal, sumOp<label>());
2893 
2894  if (nReportTotal < nChangedTotal)
2895  {
2896  Info<< "Suppressed disabled extrusion message for other "
2897  << nChangedTotal - nReportTotal << " faces." << endl;
2898  }
2899  }
2900 
2901  return nChangedTotal;
2902 }
2903 
2904 
2905 //- Count global number of extruded faces
2906 Foam::label Foam::snappyLayerDriver::countExtrusion
2907 (
2908  const indirectPrimitivePatch& pp,
2909  const List<extrudeMode>& extrudeStatus
2910 )
2911 {
2912  // Count number of extruded patch faces
2913  label nExtruded = 0;
2914  {
2915  const faceList& localFaces = pp.localFaces();
2916 
2917  forAll(localFaces, i)
2918  {
2919  const face& localFace = localFaces[i];
2920 
2921  forAll(localFace, fp)
2922  {
2923  if (extrudeStatus[localFace[fp]] != NOEXTRUDE)
2924  {
2925  nExtruded++;
2926  break;
2927  }
2928  }
2929  }
2930  }
2931 
2932  return returnReduce(nExtruded, sumOp<label>());
2933 }
2934 
2935 
2936 Foam::List<Foam::labelPair> Foam::snappyLayerDriver::getBafflesOnAddedMesh
2937 (
2938  const polyMesh& mesh,
2939  const labelList& newToOldFaces,
2940  const List<labelPair>& baffles
2941 )
2942 {
2943  // The problem is that the baffle faces are now inside the
2944  // mesh (addPatchCellLayer modifies original boundary faces and
2945  // adds new ones. So 2 pass:
2946  // - find the boundary face for all faces originating from baffle
2947  // - use the boundary face for the new baffles
2948 
2949  Map<label> baffleSet(4*baffles.size());
2950  forAll(baffles, bafflei)
2951  {
2952  baffleSet.insert(baffles[bafflei][0], bafflei);
2953  baffleSet.insert(baffles[bafflei][1], bafflei);
2954  }
2955 
2956 
2957  List<labelPair> newBaffles(baffles.size(), labelPair(-1, -1));
2958  for
2959  (
2960  label facei = mesh.nInternalFaces();
2961  facei < mesh.nFaces();
2962  facei++
2963  )
2964  {
2965  label oldFacei = newToOldFaces[facei];
2966 
2967  const auto faceFnd = baffleSet.find(oldFacei);
2968  if (faceFnd.good())
2969  {
2970  label bafflei = faceFnd();
2971  labelPair& p = newBaffles[bafflei];
2972  if (p[0] == -1)
2973  {
2974  p[0] = facei;
2975  }
2976  else if (p[1] == -1)
2977  {
2978  p[1] = facei;
2979  }
2980  else
2981  {
2983  << "Problem:" << facei << " at:"
2984  << mesh.faceCentres()[facei]
2985  << " is on same baffle as " << p[0]
2986  << " at:" << mesh.faceCentres()[p[0]]
2987  << " and " << p[1]
2988  << " at:" << mesh.faceCentres()[p[1]]
2989  << exit(FatalError);
2990  }
2991  }
2992  }
2993  return newBaffles;
2994 }
2995 
2996 
2997 // Collect layer faces and layer cells into mesh fields for ease of handling
2998 void Foam::snappyLayerDriver::getLayerCellsFaces
2999 (
3000  const polyMesh& mesh,
3001  const addPatchCellLayer& addLayer,
3002  const scalarField& oldRealThickness,
3003 
3004  labelList& cellNLayers,
3005  scalarField& faceRealThickness
3006 )
3007 {
3008  cellNLayers.setSize(mesh.nCells());
3009  cellNLayers = 0;
3010  faceRealThickness.setSize(mesh.nFaces());
3011  faceRealThickness = 0;
3012 
3013  // Mark all faces in the layer
3014  const labelListList& layerFaces = addLayer.layerFaces();
3015 
3016  // Mark all cells in the layer.
3017  labelListList addedCells(addPatchCellLayer::addedCells(mesh, layerFaces));
3018 
3019  forAll(addedCells, oldPatchFacei)
3020  {
3021  const labelList& added = addedCells[oldPatchFacei];
3022 
3023  const labelList& layer = layerFaces[oldPatchFacei];
3024 
3025  if (layer.size())
3026  {
3027  // Leave out original internal face
3028  forAll(added, i)
3029  {
3030  cellNLayers[added[i]] = layer.size()-1;
3031  }
3032  }
3033  }
3034 
3035  forAll(layerFaces, oldPatchFacei)
3036  {
3037  const labelList& layer = layerFaces[oldPatchFacei];
3038  const scalar realThickness = oldRealThickness[oldPatchFacei];
3039 
3040  if (layer.size())
3041  {
3042  // Layer contains both original boundary face and new boundary
3043  // face so is nLayers+1. Leave out old internal face.
3044  for (label i = 1; i < layer.size(); i++)
3045  {
3046  faceRealThickness[layer[i]] = realThickness;
3047  }
3048  }
3049  }
3050 }
3051 
3052 
3053 void Foam::snappyLayerDriver::printLayerData
3054 (
3055  const fvMesh& mesh,
3056  const labelList& patchIDs,
3057  const labelList& cellNLayers,
3058  const scalarField& faceWantedThickness,
3059  const scalarField& faceRealThickness,
3060  const layerParameters& layerParams
3061 ) const
3062 {
3063  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3064 
3065  const int oldPrecision = Info.stream().precision();
3066 
3067  // Find maximum length of a patch name, for a nicer output
3068  label maxPatchNameLen = 0;
3069  forAll(patchIDs, i)
3070  {
3071  label patchi = patchIDs[i];
3072  word patchName = pbm[patchi].name();
3073  maxPatchNameLen = max(maxPatchNameLen, label(patchName.size()));
3074  }
3075 
3076  // Print some global mesh statistics
3077  meshRefiner_.printMeshInfo(false, "Mesh with layers", false);
3078 
3079  Info<< nl
3080  << setf(ios_base::left) << setw(maxPatchNameLen) << "patch"
3081  << setw(0) << " faces layers overall thickness" << nl
3082  << setf(ios_base::left) << setw(maxPatchNameLen) << " "
3083  << setw(0) << " target mesh [m] [%]" << nl
3084  << setf(ios_base::left) << setw(maxPatchNameLen) << "-----"
3085  << setw(0) << " ----- ----- ---- --- ---" << endl;
3086 
3087 
3088  forAll(patchIDs, i)
3089  {
3090  label patchi = patchIDs[i];
3091  const polyPatch& pp = pbm[patchi];
3092 
3093  label sumSize = pp.size();
3094 
3095  // Number of layers
3096  const labelList& faceCells = pp.faceCells();
3097  label sumNLayers = 0;
3098  forAll(faceCells, i)
3099  {
3100  sumNLayers += cellNLayers[faceCells[i]];
3101  }
3102 
3103  // Thickness
3104  scalarField::subField patchWanted = pbm[patchi].patchSlice
3105  (
3106  faceWantedThickness
3107  );
3108  scalarField::subField patchReal = pbm[patchi].patchSlice
3109  (
3110  faceRealThickness
3111  );
3112 
3113  scalar sumRealThickness = sum(patchReal);
3114  scalar sumFraction = 0;
3115  forAll(patchReal, i)
3116  {
3117  if (patchWanted[i] > VSMALL)
3118  {
3119  sumFraction += (patchReal[i]/patchWanted[i]);
3120  }
3121  }
3122 
3123 
3124  reduce(sumSize, sumOp<label>());
3125  reduce(sumNLayers, sumOp<label>());
3126  reduce(sumRealThickness, sumOp<scalar>());
3127  reduce(sumFraction, sumOp<scalar>());
3128 
3129 
3130  scalar avgLayers = 0;
3131  scalar avgReal = 0;
3132  scalar avgFraction = 0;
3133  if (sumSize > 0)
3134  {
3135  avgLayers = scalar(sumNLayers)/sumSize;
3136  avgReal = sumRealThickness/sumSize;
3137  avgFraction = sumFraction/sumSize;
3138  }
3139 
3140  Info<< setf(ios_base::left) << setw(maxPatchNameLen)
3141  << pbm[patchi].name() << setprecision(3)
3142  << " " << setw(8) << sumSize
3143  << " " << setw(8) << layerParams.numLayers()[patchi]
3144  << " " << setw(8) << avgLayers
3145  << " " << setw(8) << avgReal
3146  << " " << setw(8) << 100*avgFraction
3147  << endl;
3148  }
3149  Info<< setprecision(oldPrecision) << endl;
3150 }
3151 
3152 
3153 bool Foam::snappyLayerDriver::writeLayerSets
3154 (
3155  const fvMesh& mesh,
3156  const labelList& cellNLayers,
3157  const scalarField& faceRealThickness
3158 ) const
3159 {
3160  bool allOk = true;
3161  {
3162  label nAdded = 0;
3163  forAll(cellNLayers, celli)
3164  {
3165  if (cellNLayers[celli] > 0)
3166  {
3167  nAdded++;
3168  }
3169  }
3170  cellSet addedCellSet(mesh, "addedCells", nAdded);
3171  forAll(cellNLayers, celli)
3172  {
3173  if (cellNLayers[celli] > 0)
3174  {
3175  addedCellSet.insert(celli);
3176  }
3177  }
3178  addedCellSet.instance() = meshRefiner_.timeName();
3179  Info<< "Writing "
3180  << returnReduce(addedCellSet.size(), sumOp<label>())
3181  << " added cells to cellSet "
3182  << addedCellSet.name() << endl;
3183  bool ok = addedCellSet.write();
3184  allOk = allOk && ok;
3185  }
3186  {
3187  label nAdded = 0;
3188  for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
3189  {
3190  if (faceRealThickness[facei] > 0)
3191  {
3192  nAdded++;
3193  }
3194  }
3195 
3196  faceSet layerFacesSet(mesh, "layerFaces", nAdded);
3197  for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
3198  {
3199  if (faceRealThickness[facei] > 0)
3200  {
3201  layerFacesSet.insert(facei);
3202  }
3203  }
3204  layerFacesSet.instance() = meshRefiner_.timeName();
3205  Info<< "Writing "
3206  << returnReduce(layerFacesSet.size(), sumOp<label>())
3207  << " faces inside added layer to faceSet "
3208  << layerFacesSet.name() << endl;
3209  bool ok = layerFacesSet.write();
3210  allOk = allOk && ok;
3211  }
3212  return allOk;
3213 }
3214 
3215 
3216 bool Foam::snappyLayerDriver::writeLayerData
3217 (
3218  const fvMesh& mesh,
3219  const labelList& patchIDs,
3220  const labelList& cellNLayers,
3221  const scalarField& faceWantedThickness,
3222  const scalarField& faceRealThickness
3223 ) const
3224 {
3225  bool allOk = true;
3226 
3228  {
3229  bool ok = writeLayerSets(mesh, cellNLayers, faceRealThickness);
3230  allOk = allOk && ok;
3231  }
3232 
3234  {
3235  Info<< nl << "Writing fields with layer information:" << incrIndent
3236  << endl;
3237  {
3239  (
3240  IOobject
3241  (
3242  "nSurfaceLayers",
3243  mesh.time().timeName(),
3244  mesh,
3248  ),
3249  mesh,
3251  fixedValueFvPatchScalarField::typeName
3252  );
3253  forAll(fld, celli)
3254  {
3255  fld[celli] = cellNLayers[celli];
3256  }
3257  volScalarField::Boundary& fldBf = fld.boundaryFieldRef();
3258 
3259  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3260  forAll(patchIDs, i)
3261  {
3262  label patchi = patchIDs[i];
3263  const polyPatch& pp = pbm[patchi];
3264  const labelList& faceCells = pp.faceCells();
3265  scalarField pfld(faceCells.size());
3266  forAll(faceCells, i)
3267  {
3268  pfld[i] = cellNLayers[faceCells[i]];
3269  }
3270  fldBf[patchi] == pfld;
3271  }
3272  Info<< indent << fld.name() << " : actual number of layers"
3273  << endl;
3274  bool ok = fld.write();
3275  allOk = allOk && ok;
3276  }
3277  {
3279  (
3280  IOobject
3281  (
3282  "thickness",
3283  mesh.time().timeName(),
3284  mesh,
3288  ),
3289  mesh,
3291  fixedValueFvPatchScalarField::typeName
3292  );
3293  volScalarField::Boundary& fldBf = fld.boundaryFieldRef();
3294  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3295  forAll(patchIDs, i)
3296  {
3297  label patchi = patchIDs[i];
3298  fldBf[patchi] == pbm[patchi].patchSlice(faceRealThickness);
3299  }
3300  Info<< indent << fld.name() << " : overall layer thickness"
3301  << endl;
3302  bool ok = fld.write();
3303  allOk = allOk && ok;
3304  }
3305  {
3307  (
3308  IOobject
3309  (
3310  "thicknessFraction",
3311  mesh.time().timeName(),
3312  mesh,
3316  ),
3317  mesh,
3319  fixedValueFvPatchScalarField::typeName
3320  );
3321  volScalarField::Boundary& fldBf = fld.boundaryFieldRef();
3322  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3323  forAll(patchIDs, i)
3324  {
3325  label patchi = patchIDs[i];
3326 
3327  scalarField::subField patchWanted = pbm[patchi].patchSlice
3328  (
3329  faceWantedThickness
3330  );
3331  scalarField::subField patchReal = pbm[patchi].patchSlice
3332  (
3333  faceRealThickness
3334  );
3335 
3336  // Convert patchReal to relative thickness
3337  scalarField pfld(patchReal.size(), Zero);
3338  forAll(patchReal, i)
3339  {
3340  if (patchWanted[i] > VSMALL)
3341  {
3342  pfld[i] = patchReal[i]/patchWanted[i];
3343  }
3344  }
3345 
3346  fldBf[patchi] == pfld;
3347  }
3348  Info<< indent << fld.name()
3349  << " : overall layer thickness (fraction"
3350  << " of desired thickness)" << endl;
3351  bool ok = fld.write();
3352  allOk = allOk && ok;
3353  }
3354  Info<< decrIndent<< endl;
3355  }
3356 
3357  return allOk;
3358 }
3359 
3360 
3361 void Foam::snappyLayerDriver::dupFaceZonePoints
3362 (
3363  const labelList& patchIDs, // patch indices
3364  const labelList& numLayers, // number of layers per patch
3365  List<labelPair> baffles, // pairs of baffles (input & updated)
3366  labelList& pointToMaster // -1 or index of original point (duplicated
3367  // point)
3368 )
3369 {
3370  fvMesh& mesh = meshRefiner_.mesh();
3371 
3372  // Check outside of baffles for non-manifoldness
3373 
3374  // Points that are candidates for duplication
3375  labelList candidatePoints;
3376  {
3377  // Do full analysis to see if we need to extrude points
3378  // so have to duplicate them
3379  autoPtr<indirectPrimitivePatch> pp
3380  (
3382  (
3383  mesh,
3384  patchIDs
3385  )
3386  );
3387 
3388  // Displacement for all pp.localPoints. Set to large value to
3389  // avoid truncation in syncPatchDisplacement because of
3390  // minThickness.
3391  vectorField patchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT));
3392  labelList patchNLayers(pp().nPoints(), Zero);
3393  label nIdealTotAddedCells = 0;
3394  List<extrudeMode> extrudeStatus(pp().nPoints(), EXTRUDE);
3395  // Get number of layers per point from number of layers per patch
3396  setNumLayers
3397  (
3398  numLayers, // per patch the num layers
3399  patchIDs, // patches that are being moved
3400  *pp, // indirectpatch for all faces moving
3401 
3402  patchNLayers,
3403  extrudeStatus,
3404  nIdealTotAddedCells
3405  );
3406  // Make sure displacement is equal on both sides of coupled patches.
3407  // Note that we explicitly disable the minThickness truncation
3408  // of the patchDisp here.
3409  syncPatchDisplacement
3410  (
3411  *pp,
3412  scalarField(patchDisp.size(), Zero), //minThickness,
3413  patchDisp,
3414  patchNLayers,
3415  extrudeStatus
3416  );
3417 
3418 
3419  // Do duplication only if all patch points decide to extrude. Ignore
3420  // contribution from non-patch points. Note that we need to
3421  // apply this to all mesh points
3422  labelList minPatchState(mesh.nPoints(), labelMax);
3423  forAll(extrudeStatus, patchPointi)
3424  {
3425  label pointi = pp().meshPoints()[patchPointi];
3426  minPatchState[pointi] = extrudeStatus[patchPointi];
3427  }
3428 
3430  (
3431  mesh,
3432  minPatchState,
3433  minEqOp<label>(), // combine op
3434  labelMax // null value
3435  );
3436 
3437  // So now minPatchState:
3438  // - labelMax on non-patch points
3439  // - NOEXTRUDE if any patch point was not extruded
3440  // - EXTRUDE or EXTRUDEREMOVE if all patch points are extruded/
3441  // extrudeRemove.
3442 
3443  label n = 0;
3444  forAll(minPatchState, pointi)
3445  {
3446  label state = minPatchState[pointi];
3447  if (state == EXTRUDE || state == EXTRUDEREMOVE)
3448  {
3449  n++;
3450  }
3451  }
3452  candidatePoints.setSize(n);
3453  n = 0;
3454  forAll(minPatchState, pointi)
3455  {
3456  label state = minPatchState[pointi];
3457  if (state == EXTRUDE || state == EXTRUDEREMOVE)
3458  {
3459  candidatePoints[n++] = pointi;
3460  }
3461  }
3462  }
3463 
3464  // Not duplicate points on either side of baffles that don't get any
3465  // layers
3466  labelPairList nonDupBaffles;
3467 
3468  {
3469  // faceZones that are not being duplicated
3470  DynamicList<label> nonDupZones(mesh.faceZones().size());
3471 
3472  labelHashSet layerIDs(patchIDs);
3473  forAll(mesh.faceZones(), zonei)
3474  {
3475  label mpi, spi;
3477  bool hasInfo = meshRefiner_.getFaceZoneInfo
3478  (
3479  mesh.faceZones()[zonei].name(),
3480  mpi,
3481  spi,
3482  fzType
3483  );
3484  if (hasInfo && !layerIDs.found(mpi) && !layerIDs.found(spi))
3485  {
3486  nonDupZones.append(zonei);
3487  }
3488  }
3489  nonDupBaffles = meshRefinement::subsetBaffles
3490  (
3491  mesh,
3492  nonDupZones,
3494  );
3495  }
3496 
3497 
3498  const localPointRegion regionSide(mesh, nonDupBaffles, candidatePoints);
3499 
3500  autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldPoints
3501  (
3502  regionSide
3503  );
3504 
3505  if (map)
3506  {
3507  // Store point duplication
3508  pointToMaster.setSize(mesh.nPoints(), -1);
3509 
3510  const labelList& pointMap = map().pointMap();
3511  const labelList& reversePointMap = map().reversePointMap();
3512 
3513  forAll(pointMap, pointi)
3514  {
3515  label oldPointi = pointMap[pointi];
3516  label newMasterPointi = reversePointMap[oldPointi];
3517 
3518  if (newMasterPointi != pointi)
3519  {
3520  // Found slave. Mark both master and slave
3521  pointToMaster[pointi] = newMasterPointi;
3522  pointToMaster[newMasterPointi] = newMasterPointi;
3523  }
3524  }
3525 
3526  // Update baffle numbering
3527  {
3528  const labelList& reverseFaceMap = map().reverseFaceMap();
3529  forAll(baffles, i)
3530  {
3531  label f0 = reverseFaceMap[baffles[i].first()];
3532  label f1 = reverseFaceMap[baffles[i].second()];
3533  baffles[i] = labelPair(f0, f1);
3534  }
3535  }
3536 
3537 
3539  {
3540  const_cast<Time&>(mesh.time())++;
3541  Info<< "Writing point-duplicate mesh to time "
3542  << meshRefiner_.timeName() << endl;
3543 
3544  meshRefiner_.write
3545  (
3548  (
3551  ),
3552  mesh.time().path()/meshRefiner_.timeName()
3553  );
3554 
3555  OBJstream str
3556  (
3557  mesh.time().path()
3558  / "duplicatePoints_"
3559  + meshRefiner_.timeName()
3560  + ".obj"
3561  );
3562  Info<< "Writing point-duplicates to " << str.name() << endl;
3563  const pointField& p = mesh.points();
3564  forAll(pointMap, pointi)
3565  {
3566  label newMasteri = reversePointMap[pointMap[pointi]];
3567 
3568  if (newMasteri != pointi)
3569  {
3570  str.writeLine(p[pointi], p[newMasteri]);
3571  }
3572  }
3573  }
3574  }
3575 }
3576 
3577 
3578 void Foam::snappyLayerDriver::mergeFaceZonePoints
3579 (
3580  const labelList& pointToMaster, // -1 or index of duplicated point
3581  labelList& cellNLayers,
3582  scalarField& faceRealThickness,
3583  scalarField& faceWantedThickness
3584 )
3585 {
3586  // Near opposite of dupFaceZonePoints : merge points and baffles introduced
3587  // for internal faceZones
3588 
3589  fvMesh& mesh = meshRefiner_.mesh();
3590 
3591  // Count duplicate points
3592  label nPointPairs = 0;
3593  forAll(pointToMaster, pointi)
3594  {
3595  label otherPointi = pointToMaster[pointi];
3596  if (otherPointi != -1)
3597  {
3598  nPointPairs++;
3599  }
3600  }
3601  reduce(nPointPairs, sumOp<label>());
3602  if (nPointPairs > 0)
3603  {
3604  // Merge any duplicated points
3605  Info<< "Merging " << nPointPairs << " duplicated points ..." << endl;
3606 
3608  {
3609  OBJstream str
3610  (
3611  mesh.time().path()
3612  / "mergePoints_"
3613  + meshRefiner_.timeName()
3614  + ".obj"
3615  );
3616  Info<< "Points to be merged to " << str.name() << endl;
3617  forAll(pointToMaster, pointi)
3618  {
3619  label otherPointi = pointToMaster[pointi];
3620  if (otherPointi != -1)
3621  {
3622  const point& pt = mesh.points()[pointi];
3623  const point& otherPt = mesh.points()[otherPointi];
3624  str.writeLine(pt, otherPt);
3625  }
3626  }
3627  }
3628 
3629 
3630  autoPtr<mapPolyMesh> map = meshRefiner_.mergePoints(pointToMaster);
3631  if (map)
3632  {
3633  inplaceReorder(map().reverseCellMap(), cellNLayers);
3634 
3635  const labelList& reverseFaceMap = map().reverseFaceMap();
3636  inplaceReorder(reverseFaceMap, faceWantedThickness);
3637  inplaceReorder(reverseFaceMap, faceRealThickness);
3638 
3639  Info<< "Merged points in = "
3640  << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
3641  }
3642  }
3643 
3644  if (mesh.faceZones().size() > 0)
3645  {
3646  // Merge any baffles
3647  Info<< "Converting baffles back into zoned faces ..."
3648  << endl;
3649 
3650  autoPtr<mapPolyMesh> map = meshRefiner_.mergeZoneBaffles
3651  (
3652  true, // internal zones
3653  false // baffle zones
3654  );
3655  if (map)
3656  {
3657  inplaceReorder(map().reverseCellMap(), cellNLayers);
3658 
3659  const labelList& faceMap = map().faceMap();
3660 
3661  // Make sure to keep the max since on two patches only one has
3662  // layers.
3663  scalarField newFaceRealThickness(mesh.nFaces(), Zero);
3664  scalarField newFaceWantedThickness(mesh.nFaces(), Zero);
3665  forAll(newFaceRealThickness, facei)
3666  {
3667  label oldFacei = faceMap[facei];
3668  if (oldFacei >= 0)
3669  {
3670  scalar& realThick = newFaceRealThickness[facei];
3671  realThick = max(realThick, faceRealThickness[oldFacei]);
3672  scalar& wanted = newFaceWantedThickness[facei];
3673  wanted = max(wanted, faceWantedThickness[oldFacei]);
3674  }
3675  }
3676  faceRealThickness.transfer(newFaceRealThickness);
3677  faceWantedThickness.transfer(newFaceWantedThickness);
3678  }
3679 
3680  Info<< "Converted baffles in = "
3681  << meshRefiner_.mesh().time().cpuTimeIncrement()
3682  << " s\n" << nl << endl;
3683  }
3684 }
3685 
3686 
3687 Foam::label Foam::snappyLayerDriver::setPointNumLayers
3688 (
3689  const layerParameters& layerParams,
3690 
3691  const labelList& numLayers,
3692  const labelList& patchIDs,
3693  const indirectPrimitivePatch& pp,
3694  const labelListList& edgeGlobalFaces,
3695 
3696  vectorField& patchDisp,
3697  labelList& patchNLayers,
3698  List<extrudeMode>& extrudeStatus
3699 ) const
3700 {
3701  fvMesh& mesh = meshRefiner_.mesh();
3702 
3703  patchDisp.setSize(pp.nPoints());
3704  patchDisp = vector(GREAT, GREAT, GREAT);
3705 
3706  // Number of layers for all pp.localPoints. Note: only valid if
3707  // extrudeStatus = EXTRUDE.
3708  patchNLayers.setSize(pp.nPoints());
3709  patchNLayers = Zero;
3710 
3711  // Ideal number of cells added
3712  label nIdealTotAddedCells = 0;
3713 
3714  // Whether to add edge for all pp.localPoints.
3715  extrudeStatus.setSize(pp.nPoints());
3716  extrudeStatus = EXTRUDE;
3717 
3718 
3719  // Get number of layers per point from number of layers per patch
3720  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3721 
3722  setNumLayers
3723  (
3724  numLayers, // per patch the num layers
3725  patchIDs, // patches that are being moved
3726  pp, // indirectpatch for all faces moving
3727 
3728  patchNLayers,
3729  extrudeStatus,
3730  nIdealTotAddedCells
3731  );
3732 
3733  // Precalculate mesh edge labels for patch edges
3734  labelList meshEdges(pp.meshEdges(mesh.edges(), mesh.pointEdges()));
3735 
3736 
3737  // Disable extrusion on split strings of common points
3738  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3739 
3740  handleNonStringConnected
3741  (
3742  pp,
3743  patchDisp,
3744  patchNLayers,
3745  extrudeStatus
3746  );
3747 
3748 
3749  // Disable extrusion on non-manifold points
3750  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3751 
3752  handleNonManifolds
3753  (
3754  pp,
3755  meshEdges,
3756  edgeGlobalFaces,
3757 
3758  patchDisp,
3759  patchNLayers,
3760  extrudeStatus
3761  );
3762 
3763  // Disable extrusion on feature angles
3764  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3765 
3766  handleFeatureAngle
3767  (
3768  pp,
3769  meshEdges,
3770  layerParams.featureAngle(),
3771 
3772  patchDisp,
3773  patchNLayers,
3774  extrudeStatus
3775  );
3776 
3777  // Disable extrusion on warped faces
3778  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3779  // It is hard to calculate some length scale if not in relative
3780  // mode so disable this check.
3781  if (!layerParams.relativeSizes().found(false))
3782  {
3783  // Undistorted edge length
3784  const scalar edge0Len =
3785  meshRefiner_.meshCutter().level0EdgeLength();
3786  const labelList& cellLevel = meshRefiner_.meshCutter().cellLevel();
3787 
3788  handleWarpedFaces
3789  (
3790  pp,
3791  layerParams.maxFaceThicknessRatio(),
3792  layerParams.relativeSizes(),
3793  edge0Len,
3794  cellLevel,
3795 
3796  patchDisp,
3797  patchNLayers,
3798  extrudeStatus
3799  );
3800  }
3801 
3804  //
3805  //handleMultiplePatchFaces
3806  //(
3807  // pp,
3808  //
3809  // patchDisp,
3810  // patchNLayers,
3811  // extrudeStatus
3812  //);
3813 
3814  addProfiling(grow, "snappyHexMesh::layers::grow");
3815 
3816  // Grow out region of non-extrusion
3817  for (label i = 0; i < layerParams.nGrow(); i++)
3818  {
3819  growNoExtrusion
3820  (
3821  pp,
3822  patchDisp,
3823  patchNLayers,
3824  extrudeStatus
3825  );
3826  }
3827  return nIdealTotAddedCells;
3828 }
3829 
3830 
3832 Foam::snappyLayerDriver::makeMeshMover
3833 (
3834  const layerParameters& layerParams,
3835  const dictionary& motionDict,
3836  const labelList& internalFaceZones,
3837  const scalarIOField& minThickness,
3838  pointVectorField& displacement
3839 ) const
3840 {
3841  // Allocate run-time selectable mesh mover
3842 
3843  fvMesh& mesh = meshRefiner_.mesh();
3844 
3845 
3846  // Set up controls for meshMover
3847  dictionary combinedDict(layerParams.dict());
3848  // Add mesh quality constraints
3849  combinedDict.merge(motionDict);
3850  // Where to get minThickness from
3851  combinedDict.add("minThicknessName", minThickness.name());
3852 
3853  const List<labelPair> internalBaffles
3854  (
3856  (
3857  mesh,
3858  internalFaceZones,
3860  )
3861  );
3862 
3863  // Take over patchDisp as boundary conditions on displacement
3864  // pointVectorField
3865  autoPtr<Foam::externalDisplacementMeshMover> medialAxisMoverPtr
3866  (
3868  (
3869  layerParams.meshShrinker(),
3870  combinedDict,
3871  internalBaffles,
3872  displacement
3873  )
3874  );
3875 
3876 
3877  if (dryRun_)
3878  {
3879  string errorMsg(FatalError.message());
3880  string IOerrorMsg(FatalIOError.message());
3881 
3882  if (errorMsg.size() || IOerrorMsg.size())
3883  {
3884  //errorMsg = "[dryRun] " + errorMsg;
3885  //errorMsg.replaceAll("\n", "\n[dryRun] ");
3886  //IOerrorMsg = "[dryRun] " + IOerrorMsg;
3887  //IOerrorMsg.replaceAll("\n", "\n[dryRun] ");
3888 
3889  IOWarningInFunction(combinedDict)
3890  << nl
3891  << "Missing/incorrect required dictionary entries:"
3892  << nl << nl
3893  << IOerrorMsg.c_str() << nl
3894  << errorMsg.c_str() << nl << nl
3895  << "Exiting dry-run" << nl << endl;
3896 
3897  if (UPstream::parRun())
3898  {
3899  Perr<< "\nFOAM parallel run exiting\n" << endl;
3900  UPstream::exit(0);
3901  }
3902  else
3903  {
3904  Perr<< "\nFOAM exiting\n" << endl;
3905  std::exit(0);
3906  }
3907  }
3908  }
3909  return medialAxisMoverPtr;
3911 
3912 
3914 (
3915  const layerParameters& layerParams,
3916  const label nLayerIter,
3917 
3918  // Mesh quality provision
3919  const dictionary& motionDict,
3920  const label nRelaxedIter,
3921  const label nAllowableErrors,
3922 
3923  const labelList& patchIDs,
3924  const labelList& internalFaceZones,
3925  const List<labelPair>& baffles,
3926  const labelList& numLayers,
3927  const label nIdealTotAddedCells,
3928 
3929  const globalIndex& globalFaces,
3931 
3932  const labelListList& edgeGlobalFaces,
3933  const labelList& edgePatchID,
3934  const labelList& edgeZoneID,
3935  const boolList& edgeFlip,
3936  const labelList& inflateFaceID,
3937 
3938  const scalarField& thickness,
3939  const scalarIOField& minThickness,
3940  const scalarField& expansionRatio,
3941 
3942  // Displacement for all pp.localPoints. Set to large value so does
3943  // not trigger the minThickness truncation (see syncPatchDisplacement below)
3944  vectorField& patchDisp,
3945 
3946  // Number of layers for all pp.localPoints. Note: only valid if
3947  // extrudeStatus = EXTRUDE.
3948  labelList& patchNLayers,
3949 
3950  // Whether to add edge for all pp.localPoints.
3951  List<extrudeMode>& extrudeStatus,
3952 
3953 
3954  polyTopoChange& savedMeshMod,
3955 
3956 
3957  // Per cell 0 or number of layers in the cell column it is part of
3958  labelList& cellNLayers,
3959  // Per face actual overall layer thickness
3960  scalarField& faceRealThickness
3961 )
3962 {
3963  fvMesh& mesh = meshRefiner_.mesh();
3964 
3965  // Overall displacement field
3966  pointVectorField displacement
3967  (
3968  makeLayerDisplacementField
3969  (
3971  numLayers
3972  )
3973  );
3974 
3975  // Allocate run-time selectable mesh mover
3976  autoPtr<externalDisplacementMeshMover> medialAxisMoverPtr
3977  (
3978  makeMeshMover
3979  (
3980  layerParams,
3981  motionDict,
3982  internalFaceZones,
3983  minThickness,
3984  displacement
3985  )
3986  );
3987 
3988 
3989  // Saved old points
3990  const pointField oldPoints(mesh.points());
3991 
3992  for (label iteration = 0; iteration < nLayerIter; iteration++)
3993  {
3994  Info<< nl
3995  << "Layer addition iteration " << iteration << nl
3996  << "--------------------------" << endl;
3997 
3998 
3999  // Unset the extrusion at the pp.
4000  const dictionary& meshQualityDict =
4001  (
4002  iteration < nRelaxedIter
4003  ? motionDict
4004  : motionDict.subDict("relaxed")
4005  );
4006 
4007  if (iteration >= nRelaxedIter)
4008  {
4009  Info<< "Switched to relaxed meshQuality constraints." << endl;
4010  }
4011 
4012 
4013 
4014  // Make sure displacement is equal on both sides of coupled patches.
4015  // Note that this also does the patchDisp < minThickness truncation
4016  // so for the first pass make sure the patchDisp is larger than
4017  // that.
4018  syncPatchDisplacement
4019  (
4020  pp,
4021  minThickness,
4022  patchDisp,
4023  patchNLayers,
4024  extrudeStatus
4025  );
4026 
4027  // Displacement acc. to pointnormals
4028  getPatchDisplacement
4029  (
4030  pp,
4031  thickness,
4032  minThickness,
4033  expansionRatio,
4034 
4035  patchDisp,
4036  patchNLayers,
4037  extrudeStatus
4038  );
4039 
4040  // Shrink mesh by displacement value first.
4041  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4042 
4043  {
4044  const pointField oldPatchPos(pp.localPoints());
4045 
4046  // We have patchDisp which is the outwards pointing
4047  // extrusion distance. Convert into an inwards pointing
4048  // shrink distance
4049  patchDisp = -patchDisp;
4050 
4051  // Take over patchDisp into pointDisplacement field and
4052  // adjust both for multi-patch constraints
4054  (
4055  patchIDs,
4056  pp,
4057  patchDisp,
4058  displacement
4059  );
4060 
4061 
4062  // Move mesh
4063  // ~~~~~~~~~
4064 
4065  // Set up controls for meshMover
4066  dictionary combinedDict(layerParams.dict());
4067  // Add standard quality constraints
4068  combinedDict.merge(motionDict);
4069  // Add relaxed constraints (overrides standard ones)
4070  combinedDict.merge(meshQualityDict);
4071  // Where to get minThickness from
4072  combinedDict.add("minThicknessName", minThickness.name());
4073 
4074  labelList checkFaces(identity(mesh.nFaces()));
4075  medialAxisMoverPtr().move
4076  (
4077  combinedDict,
4078  nAllowableErrors,
4079  checkFaces
4080  );
4081 
4082  pp.movePoints(mesh.points());
4083 
4084  // Unset any moving state
4085  mesh.moving(false);
4086 
4087  // Update patchDisp (since not all might have been honoured)
4088  patchDisp = oldPatchPos - pp.localPoints();
4089  }
4090 
4091  // Truncate displacements that are too small (this will do internal
4092  // ones, coupled ones have already been truncated by
4093  // syncPatchDisplacement)
4094  faceSet dummySet(mesh, "wrongPatchFaces", 0);
4095  truncateDisplacement
4096  (
4097  globalFaces,
4098  edgeGlobalFaces,
4099  pp,
4100  minThickness,
4101  dummySet,
4102  patchDisp,
4103  patchNLayers,
4104  extrudeStatus
4105  );
4106 
4107 
4108  // Dump to .obj file for debugging.
4110  {
4111  dumpDisplacement
4112  (
4113  mesh.time().path()/"layer_" + meshRefiner_.timeName(),
4114  pp,
4115  patchDisp,
4116  extrudeStatus
4117  );
4118 
4119  const_cast<Time&>(mesh.time())++;
4120  Info<< "Writing shrunk mesh to time "
4121  << meshRefiner_.timeName() << endl;
4122 
4123  // See comment in snappySnapDriver why we should not remove
4124  // meshPhi using mesh.clearOut().
4125 
4126  meshRefiner_.write
4127  (
4130  (
4133  ),
4134  mesh.time().path()/meshRefiner_.timeName()
4135  );
4136  }
4137 
4138 
4139  // Mesh topo change engine. Insert current mesh.
4140  polyTopoChange meshMod(mesh);
4141 
4142  // Grow layer of cells on to patch. Handles zero sized displacement.
4143  addPatchCellLayer addLayer(mesh);
4144 
4145  // Determine per point/per face number of layers to extrude. Also
4146  // handles the slow termination of layers when going switching
4147  // layers
4148 
4149  labelList nPatchPointLayers(pp.nPoints(), -1);
4150  labelList nPatchFaceLayers(pp.size(), -1);
4151  setupLayerInfoTruncation
4152  (
4153  pp,
4154  patchNLayers,
4155  extrudeStatus,
4156  layerParams.nBufferCellsNoExtrude(),
4157  nPatchPointLayers,
4158  nPatchFaceLayers
4159  );
4160 
4161  // Calculate displacement for final layer for addPatchLayer.
4162  // (layer of cells next to the original mesh)
4163  vectorField finalDisp(patchNLayers.size(), Zero);
4164 
4165  forAll(nPatchPointLayers, i)
4166  {
4168  (
4169  nPatchPointLayers[i],
4170  expansionRatio[i]
4171  );
4172  finalDisp[i] = ratio*patchDisp[i];
4173  }
4174 
4175 
4176  const scalarField invExpansionRatio(1.0/expansionRatio);
4177 
4178  // Add topo regardless of whether extrudeStatus is extruderemove.
4179  // Not add layer if patchDisp is zero.
4180  addLayer.setRefinement
4181  (
4182  globalFaces,
4183  edgeGlobalFaces,
4184 
4185  invExpansionRatio,
4186  pp,
4187  bitSet(pp.size()), // no flip
4188 
4189  edgePatchID, // boundary patch for extruded boundary edges
4190  edgeZoneID, // zone for extruded edges
4191  edgeFlip,
4192  inflateFaceID,
4193 
4194 
4195  labelList(0), // exposed patchIDs, not used for adding layers
4196  nPatchFaceLayers, // layers per face
4197  nPatchPointLayers, // layers per point
4198  finalDisp, // thickness of layer nearest internal mesh
4199  meshMod
4200  );
4201 
4202  if (debug)
4203  {
4204  const_cast<Time&>(mesh.time())++;
4205  }
4206 
4207  // Compact storage
4208  meshMod.shrink();
4209 
4210  // Store mesh changes for if mesh is correct.
4211  savedMeshMod = meshMod;
4212 
4213 
4214  // With the stored topo changes we create a new mesh so we can
4215  // undo if necessary.
4216 
4217  autoPtr<fvMesh> newMeshPtr;
4218  autoPtr<mapPolyMesh> mapPtr = meshMod.makeMesh
4219  (
4220  newMeshPtr,
4221  IOobject
4222  (
4223  //mesh.name()+"_layer",
4224  mesh.name(),
4225  static_cast<polyMesh&>(mesh).instance(),
4226  mesh.time(), // register with runTime
4227  IOobject::READ_IF_PRESENT, // read fv* if present
4228  static_cast<polyMesh&>(mesh).writeOpt()
4229  ), // io params from original mesh but new name
4230  mesh, // original mesh
4231  true // parallel sync
4232  );
4233  fvMesh& newMesh = *newMeshPtr;
4234  mapPolyMesh& map = *mapPtr;
4235 
4236  // Get timing, but more importantly get memory information
4237  addProfiling(grow, "snappyHexMesh::layers::updateMesh");
4238 
4239  //?necessary? Update fields
4240  newMesh.updateMesh(map);
4241 
4242  // Move mesh if in inflation mode
4243  if (map.hasMotionPoints())
4244  {
4245  newMesh.movePoints(map.preMotionPoints());
4246  }
4247  else
4248  {
4249  // Delete mesh volumes.
4250  newMesh.clearOut();
4251  }
4252 
4253  newMesh.setInstance(meshRefiner_.timeName());
4254 
4255  // Update numbering on addLayer:
4256  // - cell/point labels to be newMesh.
4257  // - patchFaces to remain in oldMesh order.
4258  addLayer.updateMesh
4259  (
4260  map,
4261  identity(pp.size()),
4262  identity(pp.nPoints())
4263  );
4264 
4265  // Collect layer faces and cells for outside loop.
4266  getLayerCellsFaces
4267  (
4268  newMesh,
4269  addLayer,
4270  avgPointData(pp, mag(patchDisp))(), // current thickness
4271 
4272  cellNLayers,
4273  faceRealThickness
4274  );
4275 
4276 
4277  // Count number of added cells
4278  label nAddedCells = 0;
4279  forAll(cellNLayers, celli)
4280  {
4281  if (cellNLayers[celli] > 0)
4282  {
4283  nAddedCells++;
4284  }
4285  }
4286 
4287 
4289  {
4290  Info<< "Writing layer mesh to time " << meshRefiner_.timeName()
4291  << endl;
4292  newMesh.write();
4293  writeLayerSets(newMesh, cellNLayers, faceRealThickness);
4294 
4295  // Reset the instance of the original mesh so next iteration
4296  // it dumps a complete mesh. This is just so that the inbetween
4297  // newMesh does not upset e.g. paraFoam cycling through the
4298  // times.
4299  mesh.setInstance(meshRefiner_.timeName());
4300  }
4301 
4302 
4303  //- Get baffles in newMesh numbering. Note that we cannot detect
4304  // baffles here since the points are duplicated
4305  List<labelPair> internalBaffles;
4306  {
4307  // From old mesh face to corresponding newMesh boundary face
4308  labelList meshToNewMesh(mesh.nFaces(), -1);
4309  for
4310  (
4311  label facei = newMesh.nInternalFaces();
4312  facei < newMesh.nFaces();
4313  facei++
4314  )
4315  {
4316  label newMeshFacei = map.faceMap()[facei];
4317  if (newMeshFacei != -1)
4318  {
4319  meshToNewMesh[newMeshFacei] = facei;
4320  }
4321  }
4322 
4323  List<labelPair> newMeshBaffles(baffles.size());
4324  label newi = 0;
4325  forAll(baffles, i)
4326  {
4327  const labelPair& p = baffles[i];
4328  labelPair newMeshBaffle
4329  (
4330  meshToNewMesh[p[0]],
4331  meshToNewMesh[p[1]]
4332  );
4333  if (newMeshBaffle[0] != -1 && newMeshBaffle[1] != -1)
4334  {
4335  newMeshBaffles[newi++] = newMeshBaffle;
4336  }
4337  }
4338  newMeshBaffles.setSize(newi);
4339 
4340  internalBaffles = meshRefinement::subsetBaffles
4341  (
4342  newMesh,
4343  internalFaceZones,
4344  newMeshBaffles
4345  );
4346 
4347  Info<< "Detected "
4348  << returnReduce(internalBaffles.size(), sumOp<label>())
4349  << " baffles across faceZones of type internal" << nl
4350  << endl;
4351  }
4352 
4353  label nTotChanged = checkAndUnmark
4354  (
4355  addLayer,
4356  meshQualityDict,
4357  layerParams.additionalReporting(),
4358  internalBaffles,
4359  pp,
4360  newMesh,
4361 
4362  patchDisp,
4363  patchNLayers,
4364  extrudeStatus
4365  );
4366 
4367  label nTotExtruded = countExtrusion(pp, extrudeStatus);
4368  label nTotFaces = returnReduce(pp.size(), sumOp<label>());
4369  label nTotAddedCells = returnReduce(nAddedCells, sumOp<label>());
4370 
4371  Info<< "Extruding " << nTotExtruded
4372  << " out of " << nTotFaces
4373  << " faces (" << 100.0*nTotExtruded/nTotFaces << "%)."
4374  << " Removed extrusion at " << nTotChanged << " faces."
4375  << endl
4376  << "Added " << nTotAddedCells << " out of "
4377  << nIdealTotAddedCells
4378  << " cells (" << 100.0*nTotAddedCells/nIdealTotAddedCells
4379  << "%)." << endl;
4380 
4381  if (nTotChanged == 0)
4382  {
4383  break;
4384  }
4385 
4386  // Reset mesh points and start again
4387  mesh.movePoints(oldPoints);
4388  pp.movePoints(mesh.points());
4389  medialAxisMoverPtr().movePoints(mesh.points());
4390 
4391  // Unset any moving state
4392  mesh.moving(false);
4393 
4394  // Grow out region of non-extrusion
4395  for (label i = 0; i < layerParams.nGrow(); i++)
4396  {
4397  growNoExtrusion
4398  (
4399  pp,
4400  patchDisp,
4401  patchNLayers,
4402  extrudeStatus
4403  );
4404  }
4405 
4406  Info<< endl;
4407  }
4408 }
4409 
4410 
4411 void Foam::snappyLayerDriver::mapFaceZonePoints
4412 (
4413  const mapPolyMesh& map,
4414  labelPairList& baffles,
4415  labelList& pointToMaster
4416 ) const
4417 {
4418  fvMesh& mesh = meshRefiner_.mesh();
4419 
4420  // Use geometric detection of points-to-be-merged
4421  // - detect any boundary face created from a duplicated face (=baffle)
4422  // - on these mark any point created from a duplicated point
4423  if (returnReduceOr(pointToMaster.size()))
4424  {
4425  // Estimate number of points-to-be-merged
4426  DynamicList<label> candidates(baffles.size()*4);
4427 
4428  // The problem is that all the internal layer faces also
4429  // have reverseFaceMap pointing to the old baffle face. So instead
4430  // loop over all the boundary faces and see which pair of new boundary
4431  // faces corresponds to the old baffles.
4432 
4433 
4434  // Mark whether old face was on baffle
4435  Map<label> oldFaceToBaffle(2*baffles.size());
4436  forAll(baffles, i)
4437  {
4438  const labelPair& baffle = baffles[i];
4439  oldFaceToBaffle.insert(baffle[0], i);
4440  oldFaceToBaffle.insert(baffle[1], i);
4441  }
4442 
4443  labelPairList newBaffles(baffles.size(), labelPair(-1, -1));
4444 
4445  for
4446  (
4447  label facei = mesh.nInternalFaces();
4448  facei < mesh.nFaces();
4449  facei++
4450  )
4451  {
4452  const label oldFacei = map.faceMap()[facei];
4453  const auto iter = oldFaceToBaffle.find(oldFacei);
4454  if (oldFacei != -1 && iter.good())
4455  {
4456  const label bafflei = iter();
4457  auto& newBaffle = newBaffles[bafflei];
4458  if (newBaffle[0] == -1)
4459  {
4460  newBaffle[0] = facei;
4461  }
4462  else if (newBaffle[1] == -1)
4463  {
4464  newBaffle[1] = facei;
4465  }
4466  else
4467  {
4468  FatalErrorInFunction << "face:" << facei
4469  << " at:" << mesh.faceCentres()[facei]
4470  << " already maps to baffle faces:"
4471  << newBaffle[0]
4472  << " at:" << mesh.faceCentres()[newBaffle[0]]
4473  << " and " << newBaffle[1]
4474  << " at:" << mesh.faceCentres()[newBaffle[1]]
4475  << exit(FatalError);
4476  }
4477 
4478  const face& f = mesh.faces()[facei];
4479  forAll(f, fp)
4480  {
4481  label pointi = f[fp];
4482  label oldPointi = map.pointMap()[pointi];
4483 
4484  if (pointToMaster[oldPointi] != -1)
4485  {
4486  candidates.append(pointi);
4487  }
4488  }
4489  }
4490  }
4491 
4492 
4493  // Compact newBaffles
4494  {
4495  label n = 0;
4496  forAll(newBaffles, i)
4497  {
4498  const labelPair& newBaffle = newBaffles[i];
4499  if (newBaffle[0] != -1 && newBaffle[1] != -1)
4500  {
4501  newBaffles[n++] = newBaffle;
4502  }
4503  }
4504 
4505  newBaffles.setSize(n);
4506  baffles.transfer(newBaffles);
4507  }
4508 
4509 
4510  // Do geometric merge. Ideally we'd like to use a topological
4511  // merge but we've thrown away all layer-wise addressing when
4512  // throwing away addPatchCellLayer engine. Also the addressing
4513  // is extremely complicated. There is no problem with merging
4514  // too many points; the problem would be if merging baffles.
4515  // Trust mergeZoneBaffles to do sufficient checks.
4516  labelList oldToNew;
4517  label nNew = Foam::mergePoints
4518  (
4519  UIndirectList<point>(mesh.points(), candidates),
4520  meshRefiner_.mergeDistance(),
4521  false,
4522  oldToNew
4523  );
4524 
4525  // Extract points to be merged (i.e. multiple points originating
4526  // from a single one)
4527 
4528  labelListList newToOld(invertOneToMany(nNew, oldToNew));
4529 
4530  // Extract points with more than one old one
4531  pointToMaster.setSize(mesh.nPoints());
4532  pointToMaster = -1;
4533 
4534  forAll(newToOld, newi)
4535  {
4536  const labelList& oldPoints = newToOld[newi];
4537  if (oldPoints.size() > 1)
4538  {
4539  labelList meshPoints
4540  (
4541  labelUIndList(candidates, oldPoints)
4542  );
4543  label masteri = min(meshPoints);
4544  forAll(meshPoints, i)
4545  {
4546  pointToMaster[meshPoints[i]] = masteri;
4547  }
4548  }
4549  }
4550  }
4551 }
4552 
4553 
4554 void Foam::snappyLayerDriver::updatePatch
4555 (
4556  const labelList& patchIDs,
4557  const mapPolyMesh& map,
4558  autoPtr<indirectPrimitivePatch>& pp,
4559  labelList& newToOldPatchPoints
4560 ) const
4561 {
4562  // Update the pp to be consistent with the new mesh
4563 
4564  fvMesh& mesh = meshRefiner_.mesh();
4565 
4566  autoPtr<indirectPrimitivePatch> newPp
4567  (
4569  (
4570  mesh,
4571  patchIDs
4572  )
4573  );
4574 
4575  // Map from new back to old patch points
4576  newToOldPatchPoints.setSize(newPp().nPoints());
4577  newToOldPatchPoints = -1;
4578  {
4579  const Map<label>& baseMap = pp().meshPointMap();
4580  const labelList& newMeshPoints = newPp().meshPoints();
4581 
4582  forAll(newMeshPoints, newPointi)
4583  {
4584  const label newMeshPointi = newMeshPoints[newPointi];
4585  const label oldMeshPointi =
4586  map.pointMap()[newMeshPointi];
4587  const auto iter = baseMap.find(oldMeshPointi);
4588  if (iter.good())
4589  {
4590  newToOldPatchPoints[newPointi] = iter();
4591  }
4592  }
4593  }
4594 
4595 
4596  // Reset pp. Note: make sure to use std::move - otherwise it
4597  // will release the pointer before copying and you get
4598  // memory error. Same if using autoPtr::reset.
4599  pp = std::move(newPp);
4600 
4601  // Make sure pp has adressing cached
4602  (void)pp().meshPoints();
4603  (void)pp().meshPointMap();
4604 }
4605 
4606 
4607 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
4608 
4609 Foam::snappyLayerDriver::snappyLayerDriver
4610 (
4611  meshRefinement& meshRefiner,
4612  const labelList& globalToMasterPatch,
4613  const labelList& globalToSlavePatch,
4614  const bool dryRun
4615 )
4616 :
4617  meshRefiner_(meshRefiner),
4618  globalToMasterPatch_(globalToMasterPatch),
4619  globalToSlavePatch_(globalToSlavePatch),
4620  dryRun_(dryRun)
4621 {}
4622 
4623 
4624 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
4625 
4627 (
4628  const layerParameters& layerParams,
4629  const dictionary& motionDict,
4630  const meshRefinement::FaceMergeType mergeType
4631 )
4632 {
4633  // Clip to 30 degrees. Not helpful!
4634  //scalar planarAngle = min(30.0, layerParams.featureAngle());
4635  scalar planarAngle = layerParams.mergePatchFacesAngle();
4636  scalar minCos = Foam::cos(degToRad(planarAngle));
4637 
4638  scalar concaveCos = Foam::cos(degToRad(layerParams.concaveAngle()));
4639 
4640  Info<< nl
4641  << "Merging all faces of a cell" << nl
4642  << "---------------------------" << nl
4643  << " - which are on the same patch" << nl
4644  << " - which make an angle < " << planarAngle
4645  << " degrees"
4646  << " (cos:" << minCos << ')' << nl
4647  << " - as long as the resulting face doesn't become concave"
4648  << " by more than "
4649  << layerParams.concaveAngle() << " degrees" << nl
4650  << " (0=straight, 180=fully concave)" << nl
4651  << endl;
4652 
4653  const fvMesh& mesh = meshRefiner_.mesh();
4654 
4656 
4657  labelList duplicateFace(mesh.nFaces(), -1);
4658  forAll(couples, i)
4659  {
4660  const labelPair& cpl = couples[i];
4661  duplicateFace[cpl[0]] = cpl[1];
4662  duplicateFace[cpl[1]] = cpl[0];
4663  }
4664 
4665  label nChanged = meshRefiner_.mergePatchFacesUndo
4666  (
4667  minCos,
4668  concaveCos,
4669  meshRefiner_.meshedPatches(),
4670  motionDict,
4671  duplicateFace,
4672  mergeType // How to merge co-planar patch faces
4673  );
4674 
4675  nChanged += meshRefiner_.mergeEdgesUndo(minCos, motionDict);
4676 }
4677 
4678 
4680 (
4681  const layerParameters& layerParams,
4682  const dictionary& motionDict,
4683  const labelList& patchIDs,
4684  const label nAllowableErrors,
4685  decompositionMethod& decomposer,
4686  fvMeshDistribute& distributor
4687 )
4688 {
4689  fvMesh& mesh = meshRefiner_.mesh();
4690 
4691  // Undistorted edge length
4692  const scalar edge0Len = meshRefiner_.meshCutter().level0EdgeLength();
4693 
4694  // faceZones of type internal or baffle (for merging points across)
4695  labelList internalOrBaffleFaceZones;
4696  {
4698  fzTypes[0] = surfaceZonesInfo::INTERNAL;
4699  fzTypes[1] = surfaceZonesInfo::BAFFLE;
4700  internalOrBaffleFaceZones = meshRefiner_.getZones(fzTypes);
4701  }
4702 
4703  // faceZones of type internal (for checking mesh quality across and
4704  // merging baffles)
4705  const labelList internalFaceZones
4706  (
4707  meshRefiner_.getZones
4708  (
4710  (
4711  1,
4713  )
4714  )
4715  );
4716 
4717  // Create baffles (pairs of faces that share the same points)
4718  // Baffles stored as owner and neighbour face that have been created.
4719  List<labelPair> baffles;
4720  {
4721  labelList originatingFaceZone;
4722  meshRefiner_.createZoneBaffles
4723  (
4724  identity(mesh.faceZones().size()),
4725  baffles,
4726  originatingFaceZone
4727  );
4728 
4730  {
4731  const_cast<Time&>(mesh.time())++;
4732  Info<< "Writing baffled mesh to time "
4733  << meshRefiner_.timeName() << endl;
4734  meshRefiner_.write
4735  (
4738  (
4741  ),
4742  mesh.time().path()/meshRefiner_.timeName()
4743  );
4744  }
4745  }
4746 
4747 
4748  // Duplicate points on faceZones of type boundary. Should normally already
4749  // be done by snapping phase
4750  {
4751  autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldBoundaryPoints();
4752  if (map)
4753  {
4754  const labelList& reverseFaceMap = map->reverseFaceMap();
4755  forAll(baffles, i)
4756  {
4757  label f0 = reverseFaceMap[baffles[i].first()];
4758  label f1 = reverseFaceMap[baffles[i].second()];
4759  baffles[i] = labelPair(f0, f1);
4760  }
4761  }
4762  }
4763 
4764 
4765 
4766  // Now we have all patches determine the number of layer per patch
4767  // This will be layerParams.numLayers() except for faceZones where one
4768  // side does get layers and the other not in which case we want to
4769  // suppress movement by explicitly setting numLayers 0
4770  labelList numLayers(layerParams.numLayers());
4771  {
4772  labelHashSet layerIDs(patchIDs);
4773  forAll(mesh.faceZones(), zonei)
4774  {
4775  label mpi, spi;
4777  bool hasInfo = meshRefiner_.getFaceZoneInfo
4778  (
4779  mesh.faceZones()[zonei].name(),
4780  mpi,
4781  spi,
4782  fzType
4783  );
4784  if (hasInfo)
4785  {
4787  if (layerIDs.found(mpi) && !layerIDs.found(spi))
4788  {
4789  // Only layers on master side. Fix points on slave side
4790  Info<< "On faceZone " << mesh.faceZones()[zonei].name()
4791  << " adding layers to master patch " << pbm[mpi].name()
4792  << " only. Freezing points on slave patch "
4793  << pbm[spi].name() << endl;
4794  numLayers[spi] = 0;
4795  }
4796  else if (!layerIDs.found(mpi) && layerIDs.found(spi))
4797  {
4798  // Only layers on slave side. Fix points on master side
4799  Info<< "On faceZone " << mesh.faceZones()[zonei].name()
4800  << " adding layers to slave patch " << pbm[spi].name()
4801  << " only. Freezing points on master patch "
4802  << pbm[mpi].name() << endl;
4803  numLayers[mpi] = 0;
4804  }
4805  }
4806  }
4807  }
4808 
4809 
4810  // Duplicate points on faceZones that layers are added to
4811  labelList pointToMaster;
4812  dupFaceZonePoints
4813  (
4814  patchIDs, // patch indices
4815  numLayers, // number of layers per patch
4816  baffles,
4817  pointToMaster
4818  );
4819 
4820 
4821  // Add layers to patches
4822  // ~~~~~~~~~~~~~~~~~~~~~
4823 
4824  // Now we have
4825  // - mesh with optional baffles and duplicated points for faceZones
4826  // where layers are to be added
4827  // - pointToMaster : correspondence for duplicated points
4828  // - baffles : list of pairs of faces
4829 
4830 
4831  // Calculate 'base' point extrusion
4833  (
4835  (
4836  mesh,
4837  patchIDs
4838  )
4839  );
4840  // Make sure pp has adressing cached before changing mesh later on
4841  (void)pp().meshPoints();
4842  (void)pp().meshPointMap();
4843 
4844  // Global face indices engine
4845  globalIndex globalFaces(mesh.nFaces());
4846 
4847  // Determine extrudePatch.edgeFaces in global numbering (so across
4848  // coupled patches). This is used only to string up edges between coupled
4849  // faces (all edges between same (global)face indices get extruded).
4850  labelListList edgeGlobalFaces
4851  (
4853  (
4854  mesh,
4855  globalFaces,
4856  *pp
4857  )
4858  );
4859 
4860 
4861  // Point-wise extrusion data
4862  // ~~~~~~~~~~~~~~~~~~~~~~~~~
4863 
4864  // Displacement for all pp.localPoints. Set to large value so does
4865  // not trigger the minThickness truncation (see syncPatchDisplacement below)
4866  vectorField basePatchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT));
4867 
4868  // Number of layers for all pp.localPoints. Note: only valid if
4869  // extrudeStatus = EXTRUDE.
4870  labelList basePatchNLayers(pp().nPoints(), Zero);
4871 
4872  // Whether to add edge for all pp.localPoints.
4873  List<extrudeMode> baseExtrudeStatus(pp().nPoints(), EXTRUDE);
4874 
4875  // Ideal number of cells added
4876  const label nIdealTotAddedCells = setPointNumLayers
4877  (
4878  layerParams,
4879 
4880  numLayers,
4881  patchIDs,
4882  pp(),
4883  edgeGlobalFaces,
4884 
4885  basePatchDisp,
4886  basePatchNLayers,
4887  baseExtrudeStatus
4888  );
4889 
4890  // Overall thickness of all layers
4891  scalarField baseThickness(pp().nPoints());
4892  // Truncation thickness - when to truncate layers
4893  scalarIOField baseMinThickness
4894  (
4895  IOobject
4896  (
4897  "minThickness",
4898  meshRefiner_.timeName(),
4899  mesh,
4901  ),
4902  pp().nPoints()
4903  );
4904  // Expansion ratio
4905  scalarField baseExpansionRatio(pp().nPoints());
4906  calculateLayerThickness
4907  (
4908  pp(),
4909  patchIDs,
4910  layerParams,
4911  meshRefiner_.meshCutter().cellLevel(),
4912  basePatchNLayers,
4913  edge0Len,
4914 
4915  baseThickness,
4916  baseMinThickness,
4917  baseExpansionRatio
4918  );
4919 
4920 
4921  // Per cell 0 or number of layers in the cell column it is part of
4922  labelList cellNLayers;
4923  // Per face actual overall layer thickness
4924  scalarField faceRealThickness;
4925  // Per face wanted overall layer thickness
4926  scalarField faceWantedThickness(mesh.nFaces(), Zero);
4927  {
4928  UIndirectList<scalar>(faceWantedThickness, pp().addressing()) =
4929  avgPointData(pp(), baseThickness);
4930  }
4931 
4932 
4933  // Per patch point the number of layers to add. Is basePatchNLayers
4934  // for nOuterIter = 1.
4935  labelList deltaNLayers
4936  (
4937  (basePatchNLayers+layerParams.nOuterIter()-1)
4938  /layerParams.nOuterIter()
4939  );
4940 
4941  // Per patch point the sum of added layers so far
4942  labelList nAddedLayers(basePatchNLayers.size(), 0);
4943 
4944 
4945  for (label layeri = 0; layeri < layerParams.nOuterIter(); layeri++)
4946  {
4947  // Divide layer addition into outer iterations. E.g. if
4948  // nOutIter = 2, numLayers is 20 for patchA and 1 for patchB
4949  // this will
4950  // - iter0:
4951  // - add 10 layers to patchA and 1 to patchB
4952  // - layers are finalLayerThickness down to the number of layers
4953  // - iter1 : add 10 layer to patchA and 0 to patchB
4954 
4955 
4956  // Exit if nothing to be added
4957  const label nToAdd = gSum(deltaNLayers);
4958  Info<< nl
4959  << "Outer iteration : " << layeri << nl
4960  << "-------------------" << endl;
4961  if (debug)
4962  {
4963  Info<< " Layers to add in current iteration : " << nToAdd << endl;
4964  }
4965  if (nToAdd == 0)
4966  {
4967  break;
4968  }
4969 
4970 
4971  // Determine patches for extruded boundary edges. Calculates if any
4972  // additional processor patches need to be constructed.
4973 
4974  labelList edgePatchID;
4975  labelList edgeZoneID;
4976  boolList edgeFlip;
4977  labelList inflateFaceID;
4978  determineSidePatches
4979  (
4980  globalFaces,
4981  edgeGlobalFaces,
4982  *pp,
4983 
4984  edgePatchID,
4985  edgeZoneID,
4986  edgeFlip,
4987  inflateFaceID
4988  );
4989 
4990 
4991  // Point-wise extrusion data
4992  // ~~~~~~~~~~~~~~~~~~~~~~~~~
4993 
4994  // Displacement for all pp.localPoints. Set to large value so does
4995  // not trigger the minThickness truncation (see syncPatchDisplacement
4996  // below)
4997  vectorField patchDisp(basePatchDisp);
4998 
4999  // Number of layers for all pp.localPoints. Note: only valid if
5000  // extrudeStatus = EXTRUDE.
5001  labelList patchNLayers(deltaNLayers);
5002 
5003  // Whether to add edge for all pp.localPoints.
5004  List<extrudeMode> extrudeStatus(baseExtrudeStatus);
5005 
5006  // At this point
5007  // - patchDisp is either zero or a large value
5008  // - patchNLayers is the overall number of layers
5009  // - extrudeStatus is EXTRUDE or NOEXTRUDE
5010 
5011  // Determine (wanted) point-wise overall layer thickness and expansion
5012  // ratio for this deltaNLayers slice of the overall layers
5013  scalarField sliceThickness(pp().nPoints());
5014  {
5015  forAll(baseThickness, pointi)
5016  {
5017  sliceThickness[pointi] = layerParameters::layerThickness
5018  (
5019  basePatchNLayers[pointi], // overall number of layers
5020  baseThickness[pointi], // overall thickness
5021  baseExpansionRatio[pointi], // expansion ratio
5022  basePatchNLayers[pointi]
5023  -nAddedLayers[pointi]
5024  -patchNLayers[pointi], // start index
5025  patchNLayers[pointi] // nLayers to add
5026  );
5027  }
5028  }
5029 
5030 
5031  // Current set of topology changes. (changing mesh clears out
5032  // polyTopoChange)
5033  polyTopoChange meshMod(mesh.boundaryMesh().size());
5034 
5035  // Shrink mesh, add layers. Returns with any mesh changes in meshMod
5036  labelList sliceCellNLayers;
5037  scalarField sliceFaceRealThickness;
5038 
5039  addLayers
5040  (
5041  layerParams,
5042  layerParams.nLayerIter(),
5043 
5044  // Mesh quality
5045  motionDict,
5046  layerParams.nRelaxedIter(),
5047  nAllowableErrors,
5048 
5049  patchIDs,
5050  internalFaceZones,
5051  baffles,
5052  numLayers,
5053  nIdealTotAddedCells,
5054 
5055  // Patch information
5056  globalFaces,
5057  pp(),
5058  edgeGlobalFaces,
5059  edgePatchID,
5060  edgeZoneID,
5061  edgeFlip,
5062  inflateFaceID,
5063 
5064  // Per patch point the wanted thickness
5065  sliceThickness,
5066  baseMinThickness,
5067  baseExpansionRatio,
5068 
5069  // Per patch point the wanted&obtained layers and thickness
5070  patchDisp,
5071  patchNLayers,
5072  extrudeStatus,
5073 
5074  // Complete mesh changes
5075  meshMod,
5076 
5077  // Stats on new mesh
5078  sliceCellNLayers, //cellNLayers,
5079  sliceFaceRealThickness //faceRealThickness
5080  );
5081 
5082 
5083  // Exit if nothing added
5084  const label nTotalAdded = gSum(patchNLayers);
5085  if (debug)
5086  {
5087  Info<< nl << " Added in current iteration : " << nTotalAdded
5088  << " out of : " << gSum(deltaNLayers) << endl;
5089  }
5090  if (nTotalAdded == 0)
5091  {
5092  break;
5093  }
5094 
5095 
5096  // Update wanted layer statistics
5097  forAll(patchNLayers, pointi)
5098  {
5099  nAddedLayers[pointi] += patchNLayers[pointi];
5100 
5101  if (patchNLayers[pointi] == 0)
5102  {
5103  // No layers were added. Make sure that overall extrusion
5104  // gets reset as well
5105  unmarkExtrusion
5106  (
5107  pointi,
5108  basePatchDisp,
5109  basePatchNLayers,
5110  baseExtrudeStatus
5111  );
5112  basePatchNLayers[pointi] = nAddedLayers[pointi];
5113  deltaNLayers[pointi] = 0;
5114  }
5115  else
5116  {
5117  // Adjust the number of layers for the next iteration.
5118  // Can never be
5119  // higher than the adjusted overall number of layers.
5120  // Note: is min necessary?
5121  deltaNLayers[pointi] = max
5122  (
5123  0,
5124  min
5125  (
5126  deltaNLayers[pointi],
5127  basePatchNLayers[pointi] - nAddedLayers[pointi]
5128  )
5129  );
5130  }
5131  }
5132 
5133 
5134  // At this point we have a (shrunk) mesh and a set of topology changes
5135  // which will make a valid mesh with layer. Apply these changes to the
5136  // current mesh.
5137 
5138  {
5139  // Apply the stored topo changes to the current mesh.
5140  autoPtr<mapPolyMesh> mapPtr = meshMod.changeMesh(mesh, false);
5141  mapPolyMesh& map = *mapPtr;
5142 
5143  // Hack to remove meshPhi - mapped incorrectly. TBD.
5144  mesh.moving(false);
5145  mesh.clearOut();
5146 
5147  // Update fields
5148  mesh.updateMesh(map);
5149 
5150  // Move mesh (since morphing does not do this)
5151  if (map.hasMotionPoints())
5152  {
5154  }
5155  else
5156  {
5157  // Delete mesh volumes.
5158  mesh.clearOut();
5159  }
5160 
5161  // Reset the instance for if in overwrite mode
5162  mesh.setInstance(meshRefiner_.timeName());
5163 
5164  meshRefiner_.updateMesh(map, labelList(0));
5165 
5166  // Update numbering of accumulated cells
5167  cellNLayers.setSize(map.nOldCells(), 0);
5169  (
5170  map.cellMap(),
5171  label(0),
5172  cellNLayers
5173  );
5174  forAll(cellNLayers, i)
5175  {
5176  cellNLayers[i] += sliceCellNLayers[i];
5177  }
5178 
5179  faceRealThickness.setSize(map.nOldFaces(), scalar(0));
5181  (
5182  map.faceMap(),
5183  scalar(0),
5184  faceRealThickness
5185  );
5186  faceRealThickness += sliceFaceRealThickness;
5187 
5189  (
5190  map.faceMap(),
5191  scalar(0),
5192  faceWantedThickness
5193  );
5194 
5195  // Print data now that we still have patches for the zones
5196  //if (meshRefinement::outputLevel() & meshRefinement::OUTPUTLAYERINFO)
5197  printLayerData
5198  (
5199  mesh,
5200  patchIDs,
5201  cellNLayers,
5202  faceWantedThickness,
5203  faceRealThickness,
5204  layerParams
5205  );
5206 
5207 
5208  // Dump for debugging
5210  {
5211  const_cast<Time&>(mesh.time())++;
5212  Info<< "Writing mesh with layers but disconnected to time "
5213  << meshRefiner_.timeName() << endl;
5214  meshRefiner_.write
5215  (
5218  (
5221  ),
5222  mesh.time().path()/meshRefiner_.timeName()
5223  );
5224  }
5225 
5226 
5227  // Map baffles, pointToMaster
5228  mapFaceZonePoints(map, baffles, pointToMaster);
5229 
5230  // Map patch and layer settings
5231  labelList newToOldPatchPoints;
5232  updatePatch(patchIDs, map, pp, newToOldPatchPoints);
5233 
5234  // Global face indices engine
5235  globalFaces.reset(mesh.nFaces());
5236 
5237  // Patch-edges to global faces using them
5238  edgeGlobalFaces = addPatchCellLayer::globalEdgeFaces
5239  (
5240  mesh,
5241  globalFaces,
5242  pp()
5243  );
5244 
5245  // Map patch-point based data
5247  (
5248  newToOldPatchPoints,
5249  vector::uniform(-1),
5250  basePatchDisp
5251  );
5253  (
5254  newToOldPatchPoints,
5255  label(0),
5256  basePatchNLayers
5257  );
5259  (
5260  newToOldPatchPoints,
5261  extrudeMode::NOEXTRUDE,
5262  baseExtrudeStatus
5263  );
5265  (
5266  newToOldPatchPoints,
5267  scalar(0),
5268  baseThickness
5269  );
5271  (
5272  newToOldPatchPoints,
5273  scalar(0),
5274  baseMinThickness
5275  );
5277  (
5278  newToOldPatchPoints,
5279  GREAT,
5280  baseExpansionRatio
5281  );
5283  (
5284  newToOldPatchPoints,
5285  label(0),
5286  deltaNLayers
5287  );
5289  (
5290  newToOldPatchPoints,
5291  label(0),
5292  nAddedLayers
5293  );
5294  }
5295  }
5296 
5297 
5298  // Merge baffles
5299  mergeFaceZonePoints
5300  (
5301  pointToMaster, // per new mesh point : -1 or index of duplicated point
5302  cellNLayers, // per new cell : number of layers
5303  faceRealThickness, // per new face : actual thickness
5304  faceWantedThickness // per new face : wanted thickness
5305  );
5306 
5307 
5308  // Do final balancing
5309  // ~~~~~~~~~~~~~~~~~~
5310 
5311  if (Pstream::parRun())
5312  {
5313  Info<< nl
5314  << "Doing final balancing" << nl
5315  << "---------------------" << nl
5316  << endl;
5317 
5318  if (debug)
5319  {
5320  const_cast<Time&>(mesh.time())++;
5321  }
5322 
5323  // Balance. No restriction on face zones and baffles.
5324  autoPtr<mapDistributePolyMesh> map = meshRefiner_.balance
5325  (
5326  false,
5327  false,
5328  scalarField(mesh.nCells(), 1.0),
5329  decomposer,
5330  distributor
5331  );
5332 
5333  // Re-distribute flag of layer faces and cells
5334  map().distributeCellData(cellNLayers);
5335  map().distributeFaceData(faceWantedThickness);
5336  map().distributeFaceData(faceRealThickness);
5337  }
5338 
5339 
5340  // Write mesh data
5341  // ~~~~~~~~~~~~~~~
5342 
5343  if (!dryRun_)
5344  {
5345  writeLayerData
5346  (
5347  mesh,
5348  patchIDs,
5349  cellNLayers,
5350  faceWantedThickness,
5351  faceRealThickness
5352  );
5353  }
5354 }
5355 
5356 
5358 (
5359  const dictionary& shrinkDict,
5360  const dictionary& motionDict,
5361  const layerParameters& layerParams,
5362  const meshRefinement::FaceMergeType mergeType,
5363  const bool preBalance,
5364  decompositionMethod& decomposer,
5365  fvMeshDistribute& distributor
5366 )
5367 {
5368  addProfiling(layers, "snappyHexMesh::layers");
5369  const fvMesh& mesh = meshRefiner_.mesh();
5370 
5371  Info<< nl
5372  << "Shrinking and layer addition phase" << nl
5373  << "----------------------------------" << nl
5374  << endl;
5375 
5376 
5377  Info<< "Using mesh parameters " << motionDict << nl << endl;
5378 
5379  // Merge coplanar boundary faces
5380  if
5381  (
5382  mergeType == meshRefinement::FaceMergeType::GEOMETRIC
5383  || mergeType == meshRefinement::FaceMergeType::IGNOREPATCH
5384  )
5385  {
5386  mergePatchFacesUndo(layerParams, motionDict, mergeType);
5387  }
5388 
5389 
5390  // Per patch the number of layers (-1 or 0 if no layer)
5391  const labelList& numLayers = layerParams.numLayers();
5392 
5393  // Patches that need to get a layer
5394  DynamicList<label> patchIDs(numLayers.size());
5395  label nFacesWithLayers = 0;
5396  forAll(numLayers, patchi)
5397  {
5398  if (numLayers[patchi] > 0)
5399  {
5400  const polyPatch& pp = mesh.boundaryMesh()[patchi];
5401 
5402  if (!pp.coupled())
5403  {
5404  patchIDs.append(patchi);
5405  nFacesWithLayers += mesh.boundaryMesh()[patchi].size();
5406  }
5407  else
5408  {
5410  << "Ignoring layers on coupled patch " << pp.name()
5411  << endl;
5412  }
5413  }
5414  }
5415 
5416  // Add contributions from faceZones that get layers
5417  const faceZoneMesh& fZones = mesh.faceZones();
5418  forAll(fZones, zonei)
5419  {
5420  label mpi, spi;
5422  meshRefiner_.getFaceZoneInfo(fZones[zonei].name(), mpi, spi, fzType);
5423 
5424  if (numLayers[mpi] > 0)
5425  {
5426  nFacesWithLayers += fZones[zonei].size();
5427  }
5428  if (numLayers[spi] > 0)
5429  {
5430  nFacesWithLayers += fZones[zonei].size();
5431  }
5432  }
5433 
5434 
5435  patchIDs.shrink();
5436 
5437  if (!returnReduceOr(nFacesWithLayers))
5438  {
5439  Info<< nl << "No layers to generate ..." << endl;
5440  }
5441  else
5442  {
5443  // Check that outside of mesh is not multiply connected.
5444  checkMeshManifold();
5445 
5446  // Check initial mesh
5447  Info<< "Checking initial mesh ..." << endl;
5448  labelHashSet wrongFaces(mesh.nFaces()/100);
5449  motionSmoother::checkMesh(false, mesh, motionDict, wrongFaces, dryRun_);
5450  const label nInitErrors = returnReduce
5451  (
5452  wrongFaces.size(),
5453  sumOp<label>()
5454  );
5455 
5456  Info<< "Detected " << nInitErrors << " illegal faces"
5457  << " (concave, zero area or negative cell pyramid volume)"
5458  << endl;
5459 
5460 
5461  bool faceZoneOnCoupledFace = false;
5462 
5463  if (!preBalance)
5464  {
5465  // Check if there are faceZones on processor boundaries. This
5466  // requires balancing to move them off the processor boundaries.
5467 
5468  // Is face on a faceZone
5469  bitSet isExtrudedZoneFace(mesh.nFaces());
5470  {
5471  // Add contributions from faceZones that get layers
5472  const faceZoneMesh& fZones = mesh.faceZones();
5473  forAll(fZones, zonei)
5474  {
5475  const faceZone& fZone = fZones[zonei];
5476  const word& fzName = fZone.name();
5477 
5478  label mpi, spi;
5480  meshRefiner_.getFaceZoneInfo(fzName, mpi, spi, fzType);
5481 
5482  if (numLayers[mpi] > 0 || numLayers[spi])
5483  {
5484  isExtrudedZoneFace.set(fZone);
5485  }
5486  }
5487  }
5488 
5489  bitSet intOrCoupled
5490  (
5492  );
5493 
5494  for
5495  (
5496  label facei = mesh.nInternalFaces();
5497  facei < mesh.nFaces();
5498  facei++
5499  )
5500  {
5501  if (intOrCoupled[facei] && isExtrudedZoneFace.test(facei))
5502  {
5503  faceZoneOnCoupledFace = true;
5504  break;
5505  }
5506  }
5507 
5508  Pstream::reduceOr(faceZoneOnCoupledFace);
5509  }
5510 
5511  // Balance
5512  if (Pstream::parRun())
5513  {
5514  // Calculate wanted number of cells after adding layers
5515  // (expressed as weight to be passed into decompositionMethod)
5516 
5517  scalarField cellWeights(mesh.nCells(), 1);
5518  forAll(numLayers, patchi)
5519  {
5520  if (numLayers[patchi] > 0)
5521  {
5522  const polyPatch& pp = mesh.boundaryMesh()[patchi];
5523  forAll(pp.faceCells(), i)
5524  {
5525  cellWeights[pp.faceCells()[i]] += numLayers[patchi];
5526  }
5527  }
5528  }
5529 
5530  // Add contributions from faceZones that get layers
5531  const faceZoneMesh& fZones = mesh.faceZones();
5532  forAll(fZones, zonei)
5533  {
5534  const faceZone& fZone = fZones[zonei];
5535  const word& fzName = fZone.name();
5536 
5537  label mpi, spi;
5539  meshRefiner_.getFaceZoneInfo(fzName, mpi, spi, fzType);
5540 
5541  if (numLayers[mpi] > 0)
5542  {
5543  // Get the owner side for unflipped faces, neighbour side
5544  // for flipped ones
5545  const labelList& cellIDs = fZone.slaveCells();
5546  forAll(cellIDs, i)
5547  {
5548  if (cellIDs[i] >= 0)
5549  {
5550  cellWeights[cellIDs[i]] += numLayers[mpi];
5551  }
5552  }
5553  }
5554  if (numLayers[spi] > 0)
5555  {
5556  const labelList& cellIDs = fZone.masterCells();
5557  forAll(cellIDs, i)
5558  {
5559  if (cellIDs[i] >= 0)
5560  {
5561  cellWeights[cellIDs[i]] += numLayers[mpi];
5562  }
5563  }
5564  }
5565  }
5566 
5567 
5568  // Print starting mesh
5569  Info<< nl;
5570  meshRefiner_.printMeshInfo
5571  (
5572  false,
5573  "Before layer addition",
5574  false //printCellLevel
5575  );
5576 
5577  // Print expected mesh (if all layers added)
5578  {
5579  const scalar nNewCells = sum(cellWeights);
5580  const scalar nNewCellsAll =
5581  returnReduce(nNewCells, sumOp<scalar>());
5582  const scalar nIdealNewCells = nNewCellsAll / Pstream::nProcs();
5583  const scalar unbalance = returnReduce
5584  (
5585  mag(1.0-nNewCells/nIdealNewCells),
5586  maxOp<scalar>()
5587  );
5588 
5589  Info<< "Ideal layer addition"
5590  << " : cells:" << nNewCellsAll
5591  << " unbalance:" << unbalance
5592  << nl << endl;
5593  }
5594 
5595 
5596  if (preBalance || faceZoneOnCoupledFace)
5597  {
5598  Info<< nl
5599  << "Doing initial balancing" << nl
5600  << "-----------------------" << nl
5601  << endl;
5602 
5603  // Balance mesh (and meshRefinement). Restrict faceZones to
5604  // be on internal faces only since they will be converted into
5605  // baffles.
5606  autoPtr<mapDistributePolyMesh> map = meshRefiner_.balance
5607  (
5608  true, // keepZoneFaces
5609  false,
5610  cellWeights,
5611  decomposer,
5612  distributor
5613  );
5614  }
5615  }
5616 
5617 
5618  // Do all topo changes
5619  if (layerParams.nOuterIter() == -1)
5620  {
5621  // For testing. Can be removed once addLayers below works
5622  addLayersSinglePass
5623  (
5624  layerParams,
5625  motionDict,
5626  patchIDs,
5627  nInitErrors,
5628  decomposer,
5629  distributor
5630  );
5631  }
5632  else
5633  {
5634  addLayers
5635  (
5636  layerParams,
5637  motionDict,
5638  patchIDs,
5639  nInitErrors,
5640  decomposer,
5641  distributor
5642  );
5643  }
5644  }
5645 }
5646 
5647 
5648 // ************************************************************************* //
label nPatches
Definition: readKivaGrid.H:396
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.
void clearOut()
Clear all geometry and addressing.
Definition: fvMesh.C:227
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:116
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1)
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:493
A list of face labels.
Definition: faceSet.H:47
label nOldCells() const
Number of old cells.
Definition: mapPolyMesh.H:469
const labelList & cellMap() const
Old cell map.
Definition: mapPolyMesh.H:534
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
const Field< point_type > & localPoints() const
Return pointField of points in patch.
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:598
scalar concaveAngle() const
void append(const T &val)
Append an element at the end of the list.
Definition: List.H:517
bool found(const Key &key) const
Same as contains()
Definition: HashTable.H:1354
const word & name() const noexcept
Return the object name.
Definition: IOobjectI.H:195
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:56
const labelListList & pointEdges() const
static const pointMesh & New(const polyMesh &mesh, Args &&... args)
Get existing or create a new MeshObject. Registered with typeName.
Definition: MeshObject.C:53
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.
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
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:531
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:918
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:1049
scalar mergePatchFacesAngle() const
static writeType writeLevel()
Get/set write level.
static void reduceOr(bool &value, const label communicator=worldComm)
Logical (or) reduction (MPI_AllReduce)
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
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()). Can be negative if the process i...
Definition: UPstream.H:1074
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:128
T returnReduce(const T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
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:169
bool hasMotionPoints() const
Has valid preMotionPoints?
Definition: mapPolyMesh.H:764
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:157
bool isInternalFace(const label faceIndex) const noexcept
Return true if given face label is internal to the mesh.
void setSize(const label n)
Dummy function, to make FixedList consistent with List.
Definition: FixedList.H:436
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)
static labelPairList findDuplicateFacePairs(const polyMesh &)
Helper routine to find all baffles (two boundary faces.
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1078
label nRelaxedIter() const
Number of iterations after which relaxed motion rules.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
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:97
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:81
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition: HashSet.H:85
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:1065
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.
#define addProfiling(Name, Descr)
Define profiling trigger with specified name and description string.
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
void setSize(const label n)
Alias for resize()
Definition: List.H:316
virtual void updateMesh(const mapPolyMesh &mpm)
Update mesh corresponding to the given map.
Definition: fvMesh.C:994
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:55
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.
labelListList invertOneToMany(const label len, const labelUList &map)
Invert one-to-many map. Unmapped elements will be size 0.
Definition: ListOps.C:107
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:137
virtual int precision() const override
Get precision of output field.
Definition: OSstream.C:334
Adds layers of cells to outside of polyPatch. Can optionally create stand-alone extruded mesh (addToM...
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition: polyMesh.H:608
A class for handling words, derived from Foam::string.
Definition: word.H:63
label nPoints
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
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
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:331
const Field< point_type > & faceCentres() const
Return face centres for patch.
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1116
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.
Smanip< ios_base::fmtflags > setf(const ios_base::fmtflags flags)
Definition: IOmanip.H:169
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.
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:1103
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.
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:326
label find(const T &val) const
Find index of the first occurrence of the value.
Definition: UList.C:173
virtual bool write(const bool writeOnProc=true) const
Write mesh using IO settings from time.
Definition: fvMesh.C:1102
label nLayerIter() const
Number of overall layer addition iterations.
A polyBoundaryMesh is a polyPatch list with additional search methods and registered IO...
OSstream & stream(OSstream *alternative=nullptr)
Return OSstream for output operations. Use the alternative stream for serial-only output if it is a v...
Definition: messageStream.C:68
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
int debug
Static debugging option.
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:51
Type gMax(const FieldField< Field, Type > &f)
const faceZoneMesh & faceZones() const noexcept
Return face zone mesh.
Definition: polyMesh.H:670
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)
void mergePatchFacesUndo(const layerParameters &layerParams, const dictionary &motionDict, const meshRefinement::FaceMergeType mergeType)
Merge patch faces on same cell.
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:511
labelList f(nPoints)
static autoPtr< indirectPrimitivePatch > makePatch(const polyMesh &, const labelList &)
Create patch from set of patches.
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints, -1);const cellModel &hex=cellModel::ref(cellModel::HEX);labelList hexLabels(8);label activeCells=0;labelList pointMap(nPoints);forAll(pointMap, i){ pointMap[i]=i;}for(label i=0;i< nPoints;i++){ if(f[i] > 0.0) { hexLabels[0]=i;hexLabels[1]=i1tab[i];hexLabels[2]=i3tab[i1tab[i]];hexLabels[3]=i3tab[i];hexLabels[4]=i8tab[i];hexLabels[5]=i1tab[i8tab[i]];hexLabels[6]=i3tab[i1tab[i8tab[i]]];hexLabels[7]=i3tab[i8tab[i]];cellShapes[activeCells].reset(hex, hexLabels);edgeList edges=cellShapes[activeCells].edges();forAll(edges, ei) { if(edges[ei].mag(points)< SMALL) { label start=pointMap[edges[ei].start()];while(start !=pointMap[start]) { start=pointMap[start];} label end=pointMap[edges[ei].end()];while(end !=pointMap[end]) { end=pointMap[end];} label minLabel=min(start, end);pointMap[start]=pointMap[end]=minLabel;} } cellZoning[activeCells]=idreg[i];activeCells++;}}cellShapes.setSize(activeCells);cellZoning.setSize(activeCells);forAll(cellShapes, celli){ cellShape &cs=cellShapes[celli];forAll(cs, i) { cs[i]=pointMap[cs[i]];} cs.collapse();}label bcIDs[11]={-1, 0, 2, 4, -1, 5, -1, 6, 7, 8, 9};const label nBCs=12;const word *kivaPatchTypes[nBCs]={ &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &symmetryPolyPatch::typeName, &wedgePolyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &symmetryPolyPatch::typeName, &oldCyclicPolyPatch::typeName};enum patchTypeNames{ PISTON, VALVE, LINER, CYLINDERHEAD, AXIS, WEDGE, INFLOW, OUTFLOW, PRESIN, PRESOUT, SYMMETRYPLANE, CYCLIC};const char *kivaPatchNames[nBCs]={ "piston", "valve", "liner", "cylinderHead", "axis", "wedge", "inflow", "outflow", "presin", "presout", "symmetryPlane", "cyclic"};List< SLList< face > > pFaces[nBCs]
Definition: readKivaGrid.H:235
double cpuTimeIncrement() const
Return CPU time (in seconds) since last call to cpuTimeIncrement()
Definition: cpuTimePosix.C:80
const labelList & faceMap() const
Old face map.
Definition: mapPolyMesh.H:500
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:731
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 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:496
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
label nCells() const noexcept
Number of mesh cells.
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:56
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.
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
Reduce inplace (cf. MPI Allreduce) using specified communication schedule.
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:128
writeType
Enumeration for what to write. Used as a bit-pattern.
static labelListList globalEdgeFaces(const polyMesh &, const globalIndex &globalFaces, const indirectPrimitivePatch &pp)
Per patch edge the pp faces (in global indices) using it.
label n
labelListList addedCells() const
Added cells given current mesh & layerfaces.
Field< vector > vectorField
Specialisation of Field<T> for vector.
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:74
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
A subset of mesh faces organised as a primitive patch.
Definition: faceZone.H:60
static void syncEdgeList(const polyMesh &mesh, List< T > &edgeValues, const CombineOp &cop, const T &nullValue, const TransformOp &top, const FlipOp &fop)
Synchronize values on all mesh edges.
List< label > labelList
A List of labels.
Definition: List.H:62
volScalarField & p
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.
bool returnReduceOr(const bool value, const label comm=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:502
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:172
label nOldFaces() const
Number of old faces.
Definition: mapPolyMesh.H:461
List< bool > boolList
A List of bools.
Definition: List.H:60
labelList cellIDs
A primitive field of type <T> with automated input and output.
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Do not request registration (bool: false)
const labelList & reverseFaceMap() const
Reverse face map.
Definition: mapPolyMesh.H:617
const pointField & preMotionPoints() const
Pre-motion point positions.
Definition: mapPolyMesh.H:756
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
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