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-2022 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 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,
81  const indirectPrimitivePatch& pp,
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-mesh.nInternalFaces()];
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.found())
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 
1545  labelList spec(pp.nPoints(), layerParameters::FIRST_AND_TOTAL);
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 // Map<labelList>::iterator faceFnd = cellToFaces.find(celli);
1971 // if (faceFnd.found())
1972 // {
1973 // labelList& faces = faceFnd();
1974 // faces.appendUniq(patchFacei);
1975 // }
1976 // else
1977 // {
1978 // cellToFaces.insert(celli, labelList(one{}, patchFacei));
1979 // }
1980 // }
1981 //
1982 // forAllConstIters(cellToFaces, iter)
1983 // {
1984 // if (iter().size() == 2)
1985 // {
1986 // twoStr.write(pp.localPoints()[patchPointi]);
1987 // }
1988 // else if (iter().size() > 2)
1989 // {
1990 // multiStr.write(pp.localPoints()[patchPointi]);
1991 //
1992 // const scalar ratio =
1993 // layerParameters::finalLayerThicknessRatio
1994 // (
1995 // patchNLayers[patchPointi],
1996 // expansionRatio[patchPointi]
1997 // );
1998 // // Get thickness of cell next to bulk
1999 // const vector finalDisp
2000 // (
2001 // ratio*patchDisp[patchPointi]
2002 // );
2003 //
2004 // //Pout<< "** point:" << pp.localPoints()[patchPointi]
2005 // // << " on cell:" << iter.key()
2006 // // << " faces:" << iter()
2007 // // << " displacement was:" << patchDisp[patchPointi]
2008 // // << " ratio:" << ratio
2009 // // << " finalDispl:" << finalDisp;
2010 //
2011 // // Half this thickness
2012 // patchDisp[patchPointi] -= 0.8*finalDisp;
2013 //
2014 // //Pout<< " new displacement:"
2015 // // << patchDisp[patchPointi] << endl;
2016 // }
2017 // }
2018 // }
2019 //
2020 // Pout<< "Written " << multiStr.nVertices()
2021 // << " points inbetween three or more faces on same cell to "
2022 // << multiStr.name() << endl;
2023 // }
2025 
2026 
2027  // Check if no extrude possible.
2028  forAll(pointNormals, patchPointi)
2029  {
2030  label meshPointi = pp.meshPoints()[patchPointi];
2031 
2032  if (extrudeStatus[patchPointi] == NOEXTRUDE)
2033  {
2034  // Do not use unmarkExtrusion; forcibly set to zero extrusion.
2035  patchNLayers[patchPointi] = 0;
2036  patchDisp[patchPointi] = Zero;
2037  }
2038  else
2039  {
2040  // Get normal
2041  const vector& n = pointNormals[patchPointi];
2042 
2043  if (!meshTools::visNormal(n, faceNormals, pointFaces[patchPointi]))
2044  {
2046  {
2047  Pout<< "No valid normal for point " << meshPointi
2048  << ' ' << pp.points()[meshPointi]
2049  << "; setting displacement to "
2050  << patchDisp[patchPointi]
2051  << endl;
2052  }
2053 
2054  extrudeStatus[patchPointi] = EXTRUDEREMOVE;
2055  nNoVisNormal++;
2056  }
2057  }
2058  }
2059 
2060  // At illegal points make displacement average of new neighbour positions
2061  forAll(extrudeStatus, patchPointi)
2062  {
2063  if (extrudeStatus[patchPointi] == EXTRUDEREMOVE)
2064  {
2065  point avg(Zero);
2066  label nPoints = 0;
2067 
2068  const labelList& pEdges = pp.pointEdges()[patchPointi];
2069 
2070  forAll(pEdges, i)
2071  {
2072  label edgei = pEdges[i];
2073 
2074  label otherPointi = pp.edges()[edgei].otherVertex(patchPointi);
2075 
2076  if (extrudeStatus[otherPointi] != NOEXTRUDE)
2077  {
2078  avg += localPoints[otherPointi] + patchDisp[otherPointi];
2079  nPoints++;
2080  }
2081  }
2082 
2083  if (nPoints > 0)
2084  {
2086  {
2087  Pout<< "Displacement at illegal point "
2088  << localPoints[patchPointi]
2089  << " set to "
2090  << (avg / nPoints - localPoints[patchPointi])
2091  << endl;
2092  }
2093 
2094  patchDisp[patchPointi] =
2095  avg / nPoints
2096  - localPoints[patchPointi];
2097 
2098  nExtrudeRemove++;
2099  }
2100  else
2101  {
2102  // All surrounding points are not extruded. Leave patchDisp
2103  // intact.
2104  }
2105  }
2106  }
2107 
2108  Info<< "Detected " << returnReduce(nNoVisNormal, sumOp<label>())
2109  << " points with point normal pointing through faces." << nl
2110  << "Reset displacement at "
2111  << returnReduce(nExtrudeRemove, sumOp<label>())
2112  << " points to average of surrounding points." << endl;
2113 
2114  // Make sure displacement is equal on both sides of coupled patches.
2115  syncPatchDisplacement
2116  (
2117  pp,
2118  minThickness,
2119  patchDisp,
2120  patchNLayers,
2121  extrudeStatus
2122  );
2123 
2124  Info<< endl;
2125 }
2126 
2127 
2128 bool Foam::snappyLayerDriver::sameEdgeNeighbour
2129 (
2130  const labelListList& globalEdgeFaces,
2131  const label myGlobalFacei,
2132  const label nbrGlobFacei,
2133  const label edgei
2134 ) const
2135 {
2136  const labelList& eFaces = globalEdgeFaces[edgei];
2137  if (eFaces.size() == 2)
2138  {
2139  return edge(myGlobalFacei, nbrGlobFacei) == edge(eFaces[0], eFaces[1]);
2140  }
2141 
2142  return false;
2143 }
2144 
2145 
2146 void Foam::snappyLayerDriver::getVertexString
2147 (
2148  const indirectPrimitivePatch& pp,
2149  const labelListList& globalEdgeFaces,
2150  const label facei,
2151  const label edgei,
2152  const label myGlobFacei,
2153  const label nbrGlobFacei,
2154  DynamicList<label>& vertices
2155 ) const
2156 {
2157  const labelList& fEdges = pp.faceEdges()[facei];
2158  label fp = fEdges.find(edgei);
2159 
2160  if (fp == -1)
2161  {
2163  << "problem." << abort(FatalError);
2164  }
2165 
2166  // Search back
2167  label startFp = fp;
2168 
2169  forAll(fEdges, i)
2170  {
2171  label prevFp = fEdges.rcIndex(startFp);
2172  if
2173  (
2174  !sameEdgeNeighbour
2175  (
2176  globalEdgeFaces,
2177  myGlobFacei,
2178  nbrGlobFacei,
2179  fEdges[prevFp]
2180  )
2181  )
2182  {
2183  break;
2184  }
2185  startFp = prevFp;
2186  }
2187 
2188  label endFp = fp;
2189  forAll(fEdges, i)
2190  {
2191  label nextFp = fEdges.fcIndex(endFp);
2192  if
2193  (
2194  !sameEdgeNeighbour
2195  (
2196  globalEdgeFaces,
2197  myGlobFacei,
2198  nbrGlobFacei,
2199  fEdges[nextFp]
2200  )
2201  )
2202  {
2203  break;
2204  }
2205  endFp = nextFp;
2206  }
2207 
2208  const face& f = pp.localFaces()[facei];
2209  vertices.clear();
2210  fp = startFp;
2211  while (fp != endFp)
2212  {
2213  vertices.append(f[fp]);
2214  fp = f.fcIndex(fp);
2215  }
2216  vertices.append(f[fp]);
2217  fp = f.fcIndex(fp);
2218  vertices.append(f[fp]);
2219 }
2220 
2221 
2222 // Truncates displacement
2223 // - for all patchFaces in the faceset displacement gets set to zero
2224 // - all displacement < minThickness gets set to zero
2225 Foam::label Foam::snappyLayerDriver::truncateDisplacement
2226 (
2227  const globalIndex& globalFaces,
2228  const labelListList& edgeGlobalFaces,
2229  const indirectPrimitivePatch& pp,
2230  const scalarField& minThickness,
2231  const faceSet& illegalPatchFaces,
2232  pointField& patchDisp,
2233  labelList& patchNLayers,
2234  List<extrudeMode>& extrudeStatus
2235 ) const
2236 {
2237  const fvMesh& mesh = meshRefiner_.mesh();
2238 
2239  label nChanged = 0;
2240 
2241  const Map<label>& meshPointMap = pp.meshPointMap();
2242 
2243  for (const label facei : illegalPatchFaces)
2244  {
2245  if (mesh.isInternalFace(facei))
2246  {
2248  << "Faceset " << illegalPatchFaces.name()
2249  << " contains internal face " << facei << nl
2250  << "It should only contain patch faces" << abort(FatalError);
2251  }
2252 
2253  const face& f = mesh.faces()[facei];
2254 
2255 
2256  forAll(f, fp)
2257  {
2258  const auto fnd = meshPointMap.cfind(f[fp]);
2259  if (fnd.found())
2260  {
2261  const label patchPointi = fnd.val();
2262 
2263  if (extrudeStatus[patchPointi] != NOEXTRUDE)
2264  {
2265  unmarkExtrusion
2266  (
2267  patchPointi,
2268  patchDisp,
2269  patchNLayers,
2270  extrudeStatus
2271  );
2272  nChanged++;
2273  }
2274  }
2275  }
2276  }
2277 
2278  forAll(patchDisp, patchPointi)
2279  {
2280  if (mag(patchDisp[patchPointi]) < minThickness[patchPointi])
2281  {
2282  if
2283  (
2284  unmarkExtrusion
2285  (
2286  patchPointi,
2287  patchDisp,
2288  patchNLayers,
2289  extrudeStatus
2290  )
2291  )
2292  {
2293  nChanged++;
2294  }
2295  }
2296  else if (extrudeStatus[patchPointi] == NOEXTRUDE)
2297  {
2298  // Make sure displacement is 0. Should already be so but ...
2299  patchDisp[patchPointi] = Zero;
2300  patchNLayers[patchPointi] = 0;
2301  }
2302  }
2303 
2304 
2305  const faceList& localFaces = pp.localFaces();
2306 
2307  while (true)
2308  {
2309  syncPatchDisplacement
2310  (
2311  pp,
2312  minThickness,
2313  patchDisp,
2314  patchNLayers,
2315  extrudeStatus
2316  );
2317 
2318 
2319  // Pinch
2320  // ~~~~~
2321 
2322  // Make sure that a face doesn't have two non-consecutive areas
2323  // not extruded (e.g. quad where vertex 0 and 2 are not extruded
2324  // but 1 and 3 are) since this gives topological errors.
2325 
2326  label nPinched = 0;
2327 
2328  forAll(localFaces, i)
2329  {
2330  const face& localF = localFaces[i];
2331 
2332  // Count number of transitions from unsnapped to snapped.
2333  label nTrans = 0;
2334 
2335  extrudeMode prevMode = extrudeStatus[localF.prevLabel(0)];
2336 
2337  forAll(localF, fp)
2338  {
2339  extrudeMode fpMode = extrudeStatus[localF[fp]];
2340 
2341  if (prevMode == NOEXTRUDE && fpMode != NOEXTRUDE)
2342  {
2343  nTrans++;
2344  }
2345  prevMode = fpMode;
2346  }
2347 
2348  if (nTrans > 1)
2349  {
2350  // Multiple pinches. Reset whole face as unextruded.
2351  if
2352  (
2353  unmarkExtrusion
2354  (
2355  localF,
2356  patchDisp,
2357  patchNLayers,
2358  extrudeStatus
2359  )
2360  )
2361  {
2362  nPinched++;
2363  nChanged++;
2364  }
2365  }
2366  }
2367 
2368  reduce(nPinched, sumOp<label>());
2369 
2370  Info<< "truncateDisplacement : Unextruded " << nPinched
2371  << " faces due to non-consecutive vertices being extruded." << endl;
2372 
2373 
2374  // Butterfly
2375  // ~~~~~~~~~
2376 
2377  // Make sure that a string of edges becomes a single face so
2378  // not a butterfly. Occasionally an 'edge' will have a single dangling
2379  // vertex due to face combining. These get extruded as a single face
2380  // (with a dangling vertex) so make sure this extrusion forms a single
2381  // shape.
2382  // - continuous i.e. no butterfly:
2383  // + +
2384  // |\ /|
2385  // | \ / |
2386  // +--+--+
2387  // - extrudes from all but the endpoints i.e. no partial
2388  // extrude
2389  // +
2390  // /|
2391  // / |
2392  // +--+--+
2393  // The common error topology is a pinch somewhere in the middle
2394  label nButterFly = 0;
2395  {
2396  DynamicList<label> stringedVerts;
2397  forAll(pp.edges(), edgei)
2398  {
2399  const labelList& globFaces = edgeGlobalFaces[edgei];
2400 
2401  if (globFaces.size() == 2)
2402  {
2403  label myFacei = pp.edgeFaces()[edgei][0];
2404  label myGlobalFacei = globalFaces.toGlobal
2405  (
2406  pp.addressing()[myFacei]
2407  );
2408  label nbrGlobalFacei =
2409  (
2410  globFaces[0] != myGlobalFacei
2411  ? globFaces[0]
2412  : globFaces[1]
2413  );
2414  getVertexString
2415  (
2416  pp,
2417  edgeGlobalFaces,
2418  myFacei,
2419  edgei,
2420  myGlobalFacei,
2421  nbrGlobalFacei,
2422  stringedVerts
2423  );
2424 
2425  if
2426  (
2427  extrudeStatus[stringedVerts[0]] != NOEXTRUDE
2428  || extrudeStatus[stringedVerts.last()] != NOEXTRUDE
2429  )
2430  {
2431  // Any pinch in the middle
2432  bool pinch = false;
2433  for (label i = 1; i < stringedVerts.size()-1; i++)
2434  {
2435  if (extrudeStatus[stringedVerts[i]] == NOEXTRUDE)
2436  {
2437  pinch = true;
2438  break;
2439  }
2440  }
2441  if (pinch)
2442  {
2443  forAll(stringedVerts, i)
2444  {
2445  if
2446  (
2447  unmarkExtrusion
2448  (
2449  stringedVerts[i],
2450  patchDisp,
2451  patchNLayers,
2452  extrudeStatus
2453  )
2454  )
2455  {
2456  nButterFly++;
2457  nChanged++;
2458  }
2459  }
2460  }
2461  }
2462  }
2463  }
2464  }
2465 
2466  reduce(nButterFly, sumOp<label>());
2467 
2468  Info<< "truncateDisplacement : Unextruded " << nButterFly
2469  << " faces due to stringed edges with inconsistent extrusion."
2470  << endl;
2471 
2472 
2473 
2474  // Consistent number of layers
2475  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
2476 
2477  // Make sure that a face has consistent number of layers for all
2478  // its vertices.
2479 
2480  label nDiffering = 0;
2481 
2482  //forAll(localFaces, i)
2483  //{
2484  // const face& localF = localFaces[i];
2485  //
2486  // label numLayers = -1;
2487  //
2488  // forAll(localF, fp)
2489  // {
2490  // if (patchNLayers[localF[fp]] > 0)
2491  // {
2492  // if (numLayers == -1)
2493  // {
2494  // numLayers = patchNLayers[localF[fp]];
2495  // }
2496  // else if (numLayers != patchNLayers[localF[fp]])
2497  // {
2498  // // Differing number of layers
2499  // if
2500  // (
2501  // unmarkExtrusion
2502  // (
2503  // localF,
2504  // patchDisp,
2505  // patchNLayers,
2506  // extrudeStatus
2507  // )
2508  // )
2509  // {
2510  // nDiffering++;
2511  // nChanged++;
2512  // }
2513  // break;
2514  // }
2515  // }
2516  // }
2517  //}
2518  //
2519  //reduce(nDiffering, sumOp<label>());
2520  //
2521  //Info<< "truncateDisplacement : Unextruded " << nDiffering
2522  // << " faces due to having differing number of layers." << endl;
2523 
2524  if (nPinched+nButterFly+nDiffering == 0)
2525  {
2526  break;
2527  }
2528  }
2529 
2530  return nChanged;
2531 }
2532 
2533 
2534 // Setup layer information (at points and faces) to modify mesh topology in
2535 // regions where layer mesh terminates.
2536 void Foam::snappyLayerDriver::setupLayerInfoTruncation
2537 (
2538  const indirectPrimitivePatch& pp,
2539  const labelList& patchNLayers,
2540  const List<extrudeMode>& extrudeStatus,
2541  const label nBufferCellsNoExtrude,
2542  labelList& nPatchPointLayers,
2543  labelList& nPatchFaceLayers
2544 ) const
2545 {
2546  Info<< nl << "Setting up information for layer truncation ..." << endl;
2547 
2548  const fvMesh& mesh = meshRefiner_.mesh();
2549 
2550  if (nBufferCellsNoExtrude < 0)
2551  {
2552  Info<< nl << "Performing no layer truncation."
2553  << " nBufferCellsNoExtrude set to less than 0 ..." << endl;
2554 
2555  // Face layers if any point gets extruded
2556  forAll(pp.localFaces(), patchFacei)
2557  {
2558  const face& f = pp.localFaces()[patchFacei];
2559 
2560  forAll(f, fp)
2561  {
2562  const label nPointLayers = patchNLayers[f[fp]];
2563  if (nPointLayers > 0)
2564  {
2565  if (nPatchFaceLayers[patchFacei] == -1)
2566  {
2567  nPatchFaceLayers[patchFacei] = nPointLayers;
2568  }
2569  else
2570  {
2571  nPatchFaceLayers[patchFacei] = min
2572  (
2573  nPatchFaceLayers[patchFacei],
2574  nPointLayers
2575  );
2576  }
2577  }
2578  }
2579  }
2580  nPatchPointLayers = patchNLayers;
2581 
2582  // Set any unset patch face layers
2583  forAll(nPatchFaceLayers, patchFacei)
2584  {
2585  if (nPatchFaceLayers[patchFacei] == -1)
2586  {
2587  nPatchFaceLayers[patchFacei] = 0;
2588  }
2589  }
2590  }
2591  else
2592  {
2593  // Determine max point layers per face.
2594  labelList maxLevel(pp.size(), Zero);
2595 
2596  forAll(pp.localFaces(), patchFacei)
2597  {
2598  const face& f = pp.localFaces()[patchFacei];
2599 
2600  // find patch faces where layer terminates (i.e contains extrude
2601  // and noextrude points).
2602 
2603  bool noExtrude = false;
2604  label mLevel = 0;
2605 
2606  forAll(f, fp)
2607  {
2608  if (extrudeStatus[f[fp]] == NOEXTRUDE)
2609  {
2610  noExtrude = true;
2611  }
2612  mLevel = max(mLevel, patchNLayers[f[fp]]);
2613  }
2614 
2615  if (mLevel > 0)
2616  {
2617  // So one of the points is extruded. Check if all are extruded
2618  // or is a mix.
2619 
2620  if (noExtrude)
2621  {
2622  nPatchFaceLayers[patchFacei] = 1;
2623  maxLevel[patchFacei] = mLevel;
2624  }
2625  else
2626  {
2627  maxLevel[patchFacei] = mLevel;
2628  }
2629  }
2630  }
2631 
2632  // We have the seed faces (faces with nPatchFaceLayers != maxLevel)
2633  // Now do a meshwave across the patch where we pick up neighbours
2634  // of seed faces.
2635  // Note: quite inefficient. Could probably be coded better.
2636 
2637  const labelListList& pointFaces = pp.pointFaces();
2638 
2639  label nLevels = gMax(patchNLayers);
2640 
2641  // flag neighbouring patch faces with number of layers to grow
2642  for (label ilevel = 1; ilevel < nLevels; ilevel++)
2643  {
2644  label nBuffer;
2645 
2646  if (ilevel == 1)
2647  {
2648  nBuffer = nBufferCellsNoExtrude - 1;
2649  }
2650  else
2651  {
2652  nBuffer = nBufferCellsNoExtrude;
2653  }
2654 
2655  for (label ibuffer = 0; ibuffer < nBuffer + 1; ibuffer++)
2656  {
2657  labelList tempCounter(nPatchFaceLayers);
2658 
2659  boolList foundNeighbour(pp.nPoints(), false);
2660 
2661  forAll(pp.meshPoints(), patchPointi)
2662  {
2663  forAll(pointFaces[patchPointi], pointFacei)
2664  {
2665  label facei = pointFaces[patchPointi][pointFacei];
2666 
2667  if
2668  (
2669  nPatchFaceLayers[facei] != -1
2670  && maxLevel[facei] > 0
2671  )
2672  {
2673  foundNeighbour[patchPointi] = true;
2674  break;
2675  }
2676  }
2677  }
2678 
2680  (
2681  mesh,
2682  pp.meshPoints(),
2683  foundNeighbour,
2684  orEqOp<bool>(),
2685  false // null value
2686  );
2687 
2688  forAll(pp.meshPoints(), patchPointi)
2689  {
2690  if (foundNeighbour[patchPointi])
2691  {
2692  forAll(pointFaces[patchPointi], pointFacei)
2693  {
2694  label facei = pointFaces[patchPointi][pointFacei];
2695  if
2696  (
2697  nPatchFaceLayers[facei] == -1
2698  && maxLevel[facei] > 0
2699  && ilevel < maxLevel[facei]
2700  )
2701  {
2702  tempCounter[facei] = ilevel;
2703  }
2704  }
2705  }
2706  }
2707  nPatchFaceLayers = tempCounter;
2708  }
2709  }
2710 
2711  forAll(pp.localFaces(), patchFacei)
2712  {
2713  if (nPatchFaceLayers[patchFacei] == -1)
2714  {
2715  nPatchFaceLayers[patchFacei] = maxLevel[patchFacei];
2716  }
2717  }
2718 
2719  forAll(pp.meshPoints(), patchPointi)
2720  {
2721  if (extrudeStatus[patchPointi] != NOEXTRUDE)
2722  {
2723  forAll(pointFaces[patchPointi], pointFacei)
2724  {
2725  label face = pointFaces[patchPointi][pointFacei];
2726  nPatchPointLayers[patchPointi] = max
2727  (
2728  nPatchPointLayers[patchPointi],
2729  nPatchFaceLayers[face]
2730  );
2731  }
2732  }
2733  else
2734  {
2735  nPatchPointLayers[patchPointi] = 0;
2736  }
2737  }
2739  (
2740  mesh,
2741  pp.meshPoints(),
2742  nPatchPointLayers,
2743  maxEqOp<label>(),
2744  label(0) // null value
2745  );
2746  }
2747 }
2748 
2749 
2750 // Does any of the cells use a face from faces?
2751 bool Foam::snappyLayerDriver::cellsUseFace
2752 (
2753  const polyMesh& mesh,
2754  const labelList& cellLabels,
2755  const labelHashSet& faces
2756 )
2757 {
2758  forAll(cellLabels, i)
2759  {
2760  const cell& cFaces = mesh.cells()[cellLabels[i]];
2761 
2762  forAll(cFaces, cFacei)
2763  {
2764  if (faces.found(cFaces[cFacei]))
2765  {
2766  return true;
2767  }
2768  }
2769  }
2770 
2771  return false;
2772 }
2773 
2774 
2775 // Checks the newly added cells and locally unmarks points so they
2776 // will not get extruded next time round. Returns global number of unmarked
2777 // points (0 if all was fine)
2778 Foam::label Foam::snappyLayerDriver::checkAndUnmark
2779 (
2780  const addPatchCellLayer& addLayer,
2781  const dictionary& meshQualityDict,
2782  const bool additionalReporting,
2783  const List<labelPair>& baffles,
2784  const indirectPrimitivePatch& pp,
2785  const fvMesh& newMesh,
2786 
2787  pointField& patchDisp,
2788  labelList& patchNLayers,
2789  List<extrudeMode>& extrudeStatus
2790 )
2791 {
2792  // Check the resulting mesh for errors
2793  Info<< nl << "Checking mesh with layer ..." << endl;
2794  faceSet wrongFaces(newMesh, "wrongFaces", newMesh.nFaces()/1000);
2796  (
2797  false,
2798  newMesh,
2799  meshQualityDict,
2800  identity(newMesh.nFaces()),
2801  baffles,
2802  wrongFaces,
2803  false // dryRun_
2804  );
2805  Info<< "Detected " << returnReduce(wrongFaces.size(), sumOp<label>())
2806  << " illegal faces"
2807  << " (concave, zero area or negative cell pyramid volume)"
2808  << endl;
2809 
2810  // Undo local extrusion if
2811  // - any of the added cells in error
2812 
2813  label nChanged = 0;
2814 
2815  // Get all cells in the layer.
2816  labelListList addedCells
2817  (
2819  (
2820  newMesh,
2821  addLayer.layerFaces()
2822  )
2823  );
2824 
2825  // Check if any of the faces in error uses any face of an added cell
2826  // - if additionalReporting print the few remaining areas for ease of
2827  // finding out where the problems are.
2828 
2829  const label nReportMax = 10;
2830  DynamicField<point> disabledFaceCentres(nReportMax);
2831 
2832  forAll(addedCells, oldPatchFacei)
2833  {
2834  // Get the cells (in newMesh labels) per old patch face (in mesh
2835  // labels)
2836  const labelList& fCells = addedCells[oldPatchFacei];
2837 
2838  if (cellsUseFace(newMesh, fCells, wrongFaces))
2839  {
2840  // Unmark points on old mesh
2841  if
2842  (
2843  unmarkExtrusion
2844  (
2845  pp.localFaces()[oldPatchFacei],
2846  patchDisp,
2847  patchNLayers,
2848  extrudeStatus
2849  )
2850  )
2851  {
2852  if (additionalReporting && (nChanged < nReportMax))
2853  {
2854  disabledFaceCentres.append
2855  (
2856  pp.faceCentres()[oldPatchFacei]
2857  );
2858  }
2859 
2860  nChanged++;
2861  }
2862  }
2863  }
2864 
2865 
2866  label nChangedTotal = returnReduce(nChanged, sumOp<label>());
2867 
2868  if (additionalReporting)
2869  {
2870  // Limit the number of points to be printed so that
2871  // not too many points are reported when running in parallel
2872  // Not accurate, i.e. not always nReportMax points are written,
2873  // but this estimation avoid some communication here.
2874  // The important thing, however, is that when only a few faces
2875  // are disabled, their coordinates are printed, and this should be
2876  // the case
2877  label nReportLocal = nChanged;
2878  if (nChangedTotal > nReportMax)
2879  {
2880  nReportLocal = min
2881  (
2882  max(nChangedTotal / Pstream::nProcs(), 1),
2883  min
2884  (
2885  nChanged,
2886  max(nReportMax / Pstream::nProcs(), 1)
2887  )
2888  );
2889  }
2890 
2891  if (nReportLocal)
2892  {
2893  Pout<< "Checked mesh with layers. Disabled extrusion at " << endl;
2894  for (label i=0; i < nReportLocal; i++)
2895  {
2896  Pout<< " " << disabledFaceCentres[i] << endl;
2897  }
2898  }
2899 
2900  label nReportTotal = returnReduce(nReportLocal, sumOp<label>());
2901 
2902  if (nReportTotal < nChangedTotal)
2903  {
2904  Info<< "Suppressed disabled extrusion message for other "
2905  << nChangedTotal - nReportTotal << " faces." << endl;
2906  }
2907  }
2908 
2909  return nChangedTotal;
2910 }
2911 
2912 
2913 //- Count global number of extruded faces
2914 Foam::label Foam::snappyLayerDriver::countExtrusion
2915 (
2916  const indirectPrimitivePatch& pp,
2917  const List<extrudeMode>& extrudeStatus
2918 )
2919 {
2920  // Count number of extruded patch faces
2921  label nExtruded = 0;
2922  {
2923  const faceList& localFaces = pp.localFaces();
2924 
2925  forAll(localFaces, i)
2926  {
2927  const face& localFace = localFaces[i];
2928 
2929  forAll(localFace, fp)
2930  {
2931  if (extrudeStatus[localFace[fp]] != NOEXTRUDE)
2932  {
2933  nExtruded++;
2934  break;
2935  }
2936  }
2937  }
2938  }
2939 
2940  return returnReduce(nExtruded, sumOp<label>());
2941 }
2942 
2943 
2944 Foam::List<Foam::labelPair> Foam::snappyLayerDriver::getBafflesOnAddedMesh
2945 (
2946  const polyMesh& mesh,
2947  const labelList& newToOldFaces,
2948  const List<labelPair>& baffles
2949 )
2950 {
2951  // The problem is that the baffle faces are now inside the
2952  // mesh (addPatchCellLayer modifies original boundary faces and
2953  // adds new ones. So 2 pass:
2954  // - find the boundary face for all faces originating from baffle
2955  // - use the boundary face for the new baffles
2956 
2957  Map<label> baffleSet(4*baffles.size());
2958  forAll(baffles, bafflei)
2959  {
2960  baffleSet.insert(baffles[bafflei][0], bafflei);
2961  baffleSet.insert(baffles[bafflei][1], bafflei);
2962  }
2963 
2964 
2965  List<labelPair> newBaffles(baffles.size(), labelPair(-1, -1));
2966  for
2967  (
2968  label facei = mesh.nInternalFaces();
2969  facei < mesh.nFaces();
2970  facei++
2971  )
2972  {
2973  label oldFacei = newToOldFaces[facei];
2974 
2975  const auto faceFnd = baffleSet.find(oldFacei);
2976  if (faceFnd.found())
2977  {
2978  label bafflei = faceFnd();
2979  labelPair& p = newBaffles[bafflei];
2980  if (p[0] == -1)
2981  {
2982  p[0] = facei;
2983  }
2984  else if (p[1] == -1)
2985  {
2986  p[1] = facei;
2987  }
2988  else
2989  {
2991  << "Problem:" << facei << " at:"
2992  << mesh.faceCentres()[facei]
2993  << " is on same baffle as " << p[0]
2994  << " at:" << mesh.faceCentres()[p[0]]
2995  << " and " << p[1]
2996  << " at:" << mesh.faceCentres()[p[1]]
2997  << exit(FatalError);
2998  }
2999  }
3000  }
3001  return newBaffles;
3002 }
3003 
3004 
3005 // Collect layer faces and layer cells into mesh fields for ease of handling
3006 void Foam::snappyLayerDriver::getLayerCellsFaces
3007 (
3008  const polyMesh& mesh,
3009  const addPatchCellLayer& addLayer,
3010  const scalarField& oldRealThickness,
3011 
3012  labelList& cellNLayers,
3013  scalarField& faceRealThickness
3014 )
3015 {
3016  cellNLayers.setSize(mesh.nCells());
3017  cellNLayers = 0;
3018  faceRealThickness.setSize(mesh.nFaces());
3019  faceRealThickness = 0;
3020 
3021  // Mark all faces in the layer
3022  const labelListList& layerFaces = addLayer.layerFaces();
3023 
3024  // Mark all cells in the layer.
3025  labelListList addedCells(addPatchCellLayer::addedCells(mesh, layerFaces));
3026 
3027  forAll(addedCells, oldPatchFacei)
3028  {
3029  const labelList& added = addedCells[oldPatchFacei];
3030 
3031  const labelList& layer = layerFaces[oldPatchFacei];
3032 
3033  if (layer.size())
3034  {
3035  // Leave out original internal face
3036  forAll(added, i)
3037  {
3038  cellNLayers[added[i]] = layer.size()-1;
3039  }
3040  }
3041  }
3042 
3043  forAll(layerFaces, oldPatchFacei)
3044  {
3045  const labelList& layer = layerFaces[oldPatchFacei];
3046  const scalar realThickness = oldRealThickness[oldPatchFacei];
3047 
3048  if (layer.size())
3049  {
3050  // Layer contains both original boundary face and new boundary
3051  // face so is nLayers+1. Leave out old internal face.
3052  for (label i = 1; i < layer.size(); i++)
3053  {
3054  faceRealThickness[layer[i]] = realThickness;
3055  }
3056  }
3057  }
3058 }
3059 
3060 
3061 void Foam::snappyLayerDriver::printLayerData
3062 (
3063  const fvMesh& mesh,
3064  const labelList& patchIDs,
3065  const labelList& cellNLayers,
3066  const scalarField& faceWantedThickness,
3067  const scalarField& faceRealThickness
3068 ) const
3069 {
3070  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3071 
3072  const int oldPrecision = Info.stream().precision();
3073 
3074  // Find maximum length of a patch name, for a nicer output
3075  label maxPatchNameLen = 0;
3076  forAll(patchIDs, i)
3077  {
3078  label patchi = patchIDs[i];
3079  word patchName = pbm[patchi].name();
3080  maxPatchNameLen = max(maxPatchNameLen, label(patchName.size()));
3081  }
3082 
3083  Info<< nl
3084  << setf(ios_base::left) << setw(maxPatchNameLen) << "patch"
3085  << setw(0) << " faces layers overall thickness" << nl
3086  << setf(ios_base::left) << setw(maxPatchNameLen) << " "
3087  << setw(0) << " [m] [%]" << nl
3088  << setf(ios_base::left) << setw(maxPatchNameLen) << "-----"
3089  << setw(0) << " ----- ------ --- ---" << endl;
3090 
3091 
3092  forAll(patchIDs, i)
3093  {
3094  label patchi = patchIDs[i];
3095  const polyPatch& pp = pbm[patchi];
3096 
3097  label sumSize = pp.size();
3098 
3099  // Number of layers
3100  const labelList& faceCells = pp.faceCells();
3101  label sumNLayers = 0;
3102  forAll(faceCells, i)
3103  {
3104  sumNLayers += cellNLayers[faceCells[i]];
3105  }
3106 
3107  // Thickness
3108  scalarField::subField patchWanted = pbm[patchi].patchSlice
3109  (
3110  faceWantedThickness
3111  );
3112  scalarField::subField patchReal = pbm[patchi].patchSlice
3113  (
3114  faceRealThickness
3115  );
3116 
3117  scalar sumRealThickness = sum(patchReal);
3118  scalar sumFraction = 0;
3119  forAll(patchReal, i)
3120  {
3121  if (patchWanted[i] > VSMALL)
3122  {
3123  sumFraction += (patchReal[i]/patchWanted[i]);
3124  }
3125  }
3126 
3127 
3128  reduce(sumSize, sumOp<label>());
3129  reduce(sumNLayers, sumOp<label>());
3130  reduce(sumRealThickness, sumOp<scalar>());
3131  reduce(sumFraction, sumOp<scalar>());
3132 
3133 
3134  scalar avgLayers = 0;
3135  scalar avgReal = 0;
3136  scalar avgFraction = 0;
3137  if (sumSize > 0)
3138  {
3139  avgLayers = scalar(sumNLayers)/sumSize;
3140  avgReal = sumRealThickness/sumSize;
3141  avgFraction = sumFraction/sumSize;
3142  }
3143 
3144  Info<< setf(ios_base::left) << setw(maxPatchNameLen)
3145  << pbm[patchi].name() << setprecision(3)
3146  << " " << setw(8) << sumSize
3147  << " " << setw(8) << avgLayers
3148  << " " << setw(8) << avgReal
3149  << " " << setw(8) << 100*avgFraction
3150  << endl;
3151  }
3152  Info<< setprecision(oldPrecision) << endl;
3153 }
3154 
3155 
3156 bool Foam::snappyLayerDriver::writeLayerSets
3157 (
3158  const fvMesh& mesh,
3159  const labelList& cellNLayers,
3160  const scalarField& faceRealThickness
3161 ) const
3162 {
3163  bool allOk = true;
3164  {
3165  label nAdded = 0;
3166  forAll(cellNLayers, celli)
3167  {
3168  if (cellNLayers[celli] > 0)
3169  {
3170  nAdded++;
3171  }
3172  }
3173  cellSet addedCellSet(mesh, "addedCells", nAdded);
3174  forAll(cellNLayers, celli)
3175  {
3176  if (cellNLayers[celli] > 0)
3177  {
3178  addedCellSet.insert(celli);
3179  }
3180  }
3181  addedCellSet.instance() = meshRefiner_.timeName();
3182  Info<< "Writing "
3183  << returnReduce(addedCellSet.size(), sumOp<label>())
3184  << " added cells to cellSet "
3185  << addedCellSet.name() << endl;
3186  bool ok = addedCellSet.write();
3187  allOk = allOk && ok;
3188  }
3189  {
3190  label nAdded = 0;
3191  for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
3192  {
3193  if (faceRealThickness[facei] > 0)
3194  {
3195  nAdded++;
3196  }
3197  }
3198 
3199  faceSet layerFacesSet(mesh, "layerFaces", nAdded);
3200  for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
3201  {
3202  if (faceRealThickness[facei] > 0)
3203  {
3204  layerFacesSet.insert(facei);
3205  }
3206  }
3207  layerFacesSet.instance() = meshRefiner_.timeName();
3208  Info<< "Writing "
3209  << returnReduce(layerFacesSet.size(), sumOp<label>())
3210  << " faces inside added layer to faceSet "
3211  << layerFacesSet.name() << endl;
3212  bool ok = layerFacesSet.write();
3213  allOk = allOk && ok;
3214  }
3215  return allOk;
3216 }
3217 
3218 
3219 bool Foam::snappyLayerDriver::writeLayerData
3220 (
3221  const fvMesh& mesh,
3222  const labelList& patchIDs,
3223  const labelList& cellNLayers,
3224  const scalarField& faceWantedThickness,
3225  const scalarField& faceRealThickness
3226 ) const
3227 {
3228  bool allOk = true;
3229 
3231  {
3232  bool ok = writeLayerSets(mesh, cellNLayers, faceRealThickness);
3233  allOk = allOk && ok;
3234  }
3235 
3237  {
3238  Info<< nl << "Writing fields with layer information:" << incrIndent
3239  << endl;
3240  {
3242  (
3243  IOobject
3244  (
3245  "nSurfaceLayers",
3246  mesh.time().timeName(),
3247  mesh,
3250  false
3251  ),
3252  mesh,
3254  fixedValueFvPatchScalarField::typeName
3255  );
3256  forAll(fld, celli)
3257  {
3258  fld[celli] = cellNLayers[celli];
3259  }
3260  volScalarField::Boundary& fldBf = fld.boundaryFieldRef();
3261 
3262  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3263  forAll(patchIDs, i)
3264  {
3265  label patchi = patchIDs[i];
3266  const polyPatch& pp = pbm[patchi];
3267  const labelList& faceCells = pp.faceCells();
3268  scalarField pfld(faceCells.size());
3269  forAll(faceCells, i)
3270  {
3271  pfld[i] = cellNLayers[faceCells[i]];
3272  }
3273  fldBf[patchi] == pfld;
3274  }
3275  Info<< indent << fld.name() << " : actual number of layers"
3276  << endl;
3277  bool ok = fld.write();
3278  allOk = allOk && ok;
3279  }
3280  {
3282  (
3283  IOobject
3284  (
3285  "thickness",
3286  mesh.time().timeName(),
3287  mesh,
3290  false
3291  ),
3292  mesh,
3294  fixedValueFvPatchScalarField::typeName
3295  );
3296  volScalarField::Boundary& fldBf = fld.boundaryFieldRef();
3297  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3298  forAll(patchIDs, i)
3299  {
3300  label patchi = patchIDs[i];
3301  fldBf[patchi] == pbm[patchi].patchSlice(faceRealThickness);
3302  }
3303  Info<< indent << fld.name() << " : overall layer thickness"
3304  << endl;
3305  bool ok = fld.write();
3306  allOk = allOk && ok;
3307  }
3308  {
3310  (
3311  IOobject
3312  (
3313  "thicknessFraction",
3314  mesh.time().timeName(),
3315  mesh,
3318  false
3319  ),
3320  mesh,
3322  fixedValueFvPatchScalarField::typeName
3323  );
3324  volScalarField::Boundary& fldBf = fld.boundaryFieldRef();
3325  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3326  forAll(patchIDs, i)
3327  {
3328  label patchi = patchIDs[i];
3329 
3330  scalarField::subField patchWanted = pbm[patchi].patchSlice
3331  (
3332  faceWantedThickness
3333  );
3334  scalarField::subField patchReal = pbm[patchi].patchSlice
3335  (
3336  faceRealThickness
3337  );
3338 
3339  // Convert patchReal to relative thickness
3340  scalarField pfld(patchReal.size(), Zero);
3341  forAll(patchReal, i)
3342  {
3343  if (patchWanted[i] > VSMALL)
3344  {
3345  pfld[i] = patchReal[i]/patchWanted[i];
3346  }
3347  }
3348 
3349  fldBf[patchi] == pfld;
3350  }
3351  Info<< indent << fld.name()
3352  << " : overall layer thickness (fraction"
3353  << " of desired thickness)" << endl;
3354  bool ok = fld.write();
3355  allOk = allOk && ok;
3356  }
3357  Info<< decrIndent<< endl;
3358  }
3359 
3360  return allOk;
3361 }
3362 
3363 
3364 void Foam::snappyLayerDriver::dupFaceZonePoints
3365 (
3366  const labelList& patchIDs, // patch indices
3367  const labelList& numLayers, // number of layers per patch
3368  List<labelPair> baffles, // pairs of baffles (input & updated)
3369  labelList& pointToMaster // -1 or index of original point (duplicated
3370  // point)
3371 )
3372 {
3373  fvMesh& mesh = meshRefiner_.mesh();
3374 
3375  // Check outside of baffles for non-manifoldness
3376 
3377  // Points that are candidates for duplication
3378  labelList candidatePoints;
3379  {
3380  // Do full analysis to see if we need to extrude points
3381  // so have to duplicate them
3382  autoPtr<indirectPrimitivePatch> pp
3383  (
3385  (
3386  mesh,
3387  patchIDs
3388  )
3389  );
3390 
3391  // Displacement for all pp.localPoints. Set to large value to
3392  // avoid truncation in syncPatchDisplacement because of
3393  // minThickness.
3394  vectorField patchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT));
3395  labelList patchNLayers(pp().nPoints(), Zero);
3396  label nIdealTotAddedCells = 0;
3397  List<extrudeMode> extrudeStatus(pp().nPoints(), EXTRUDE);
3398  // Get number of layers per point from number of layers per patch
3399  setNumLayers
3400  (
3401  numLayers, // per patch the num layers
3402  patchIDs, // patches that are being moved
3403  *pp, // indirectpatch for all faces moving
3404 
3405  patchNLayers,
3406  extrudeStatus,
3407  nIdealTotAddedCells
3408  );
3409  // Make sure displacement is equal on both sides of coupled patches.
3410  // Note that we explicitly disable the minThickness truncation
3411  // of the patchDisp here.
3412  syncPatchDisplacement
3413  (
3414  *pp,
3415  scalarField(patchDisp.size(), Zero), //minThickness,
3416  patchDisp,
3417  patchNLayers,
3418  extrudeStatus
3419  );
3420 
3421 
3422  // Do duplication only if all patch points decide to extrude. Ignore
3423  // contribution from non-patch points. Note that we need to
3424  // apply this to all mesh points
3425  labelList minPatchState(mesh.nPoints(), labelMax);
3426  forAll(extrudeStatus, patchPointi)
3427  {
3428  label pointi = pp().meshPoints()[patchPointi];
3429  minPatchState[pointi] = extrudeStatus[patchPointi];
3430  }
3431 
3433  (
3434  mesh,
3435  minPatchState,
3436  minEqOp<label>(), // combine op
3437  labelMax // null value
3438  );
3439 
3440  // So now minPatchState:
3441  // - labelMax on non-patch points
3442  // - NOEXTRUDE if any patch point was not extruded
3443  // - EXTRUDE or EXTRUDEREMOVE if all patch points are extruded/
3444  // extrudeRemove.
3445 
3446  label n = 0;
3447  forAll(minPatchState, pointi)
3448  {
3449  label state = minPatchState[pointi];
3450  if (state == EXTRUDE || state == EXTRUDEREMOVE)
3451  {
3452  n++;
3453  }
3454  }
3455  candidatePoints.setSize(n);
3456  n = 0;
3457  forAll(minPatchState, pointi)
3458  {
3459  label state = minPatchState[pointi];
3460  if (state == EXTRUDE || state == EXTRUDEREMOVE)
3461  {
3462  candidatePoints[n++] = pointi;
3463  }
3464  }
3465  }
3466 
3467  // Not duplicate points on either side of baffles that don't get any
3468  // layers
3469  labelPairList nonDupBaffles;
3470 
3471  {
3472  // faceZones that are not being duplicated
3473  DynamicList<label> nonDupZones(mesh.faceZones().size());
3474 
3475  labelHashSet layerIDs(patchIDs);
3476  forAll(mesh.faceZones(), zonei)
3477  {
3478  label mpi, spi;
3480  bool hasInfo = meshRefiner_.getFaceZoneInfo
3481  (
3482  mesh.faceZones()[zonei].name(),
3483  mpi,
3484  spi,
3485  fzType
3486  );
3487  if (hasInfo && !layerIDs.found(mpi) && !layerIDs.found(spi))
3488  {
3489  nonDupZones.append(zonei);
3490  }
3491  }
3492  nonDupBaffles = meshRefinement::subsetBaffles
3493  (
3494  mesh,
3495  nonDupZones,
3497  );
3498  }
3499 
3500 
3501  const localPointRegion regionSide(mesh, nonDupBaffles, candidatePoints);
3502 
3503  autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldPoints
3504  (
3505  regionSide
3506  );
3507 
3508  if (map)
3509  {
3510  // Store point duplication
3511  pointToMaster.setSize(mesh.nPoints(), -1);
3512 
3513  const labelList& pointMap = map().pointMap();
3514  const labelList& reversePointMap = map().reversePointMap();
3515 
3516  forAll(pointMap, pointi)
3517  {
3518  label oldPointi = pointMap[pointi];
3519  label newMasterPointi = reversePointMap[oldPointi];
3520 
3521  if (newMasterPointi != pointi)
3522  {
3523  // Found slave. Mark both master and slave
3524  pointToMaster[pointi] = newMasterPointi;
3525  pointToMaster[newMasterPointi] = newMasterPointi;
3526  }
3527  }
3528 
3529  // Update baffle numbering
3530  {
3531  const labelList& reverseFaceMap = map().reverseFaceMap();
3532  forAll(baffles, i)
3533  {
3534  label f0 = reverseFaceMap[baffles[i].first()];
3535  label f1 = reverseFaceMap[baffles[i].second()];
3536  baffles[i] = labelPair(f0, f1);
3537  }
3538  }
3539 
3540 
3542  {
3543  const_cast<Time&>(mesh.time())++;
3544  Info<< "Writing point-duplicate mesh to time "
3545  << meshRefiner_.timeName() << endl;
3546 
3547  meshRefiner_.write
3548  (
3551  (
3554  ),
3555  mesh.time().path()/meshRefiner_.timeName()
3556  );
3557 
3558  OBJstream str
3559  (
3560  mesh.time().path()
3561  / "duplicatePoints_"
3562  + meshRefiner_.timeName()
3563  + ".obj"
3564  );
3565  Info<< "Writing point-duplicates to " << str.name() << endl;
3566  const pointField& p = mesh.points();
3567  forAll(pointMap, pointi)
3568  {
3569  label newMasteri = reversePointMap[pointMap[pointi]];
3570 
3571  if (newMasteri != pointi)
3572  {
3573  str.writeLine(p[pointi], p[newMasteri]);
3574  }
3575  }
3576  }
3577  }
3578 }
3579 
3580 
3581 void Foam::snappyLayerDriver::mergeFaceZonePoints
3582 (
3583  const labelList& pointToMaster, // -1 or index of duplicated point
3584  labelList& cellNLayers,
3585  scalarField& faceRealThickness,
3586  scalarField& faceWantedThickness
3587 )
3588 {
3589  // Near opposite of dupFaceZonePoints : merge points and baffles introduced
3590  // for internal faceZones
3591 
3592  fvMesh& mesh = meshRefiner_.mesh();
3593 
3594  // Count duplicate points
3595  label nPointPairs = 0;
3596  forAll(pointToMaster, pointi)
3597  {
3598  label otherPointi = pointToMaster[pointi];
3599  if (otherPointi != -1)
3600  {
3601  nPointPairs++;
3602  }
3603  }
3604  reduce(nPointPairs, sumOp<label>());
3605  if (nPointPairs > 0)
3606  {
3607  // Merge any duplicated points
3608  Info<< "Merging " << nPointPairs << " duplicated points ..." << endl;
3609 
3611  {
3612  OBJstream str
3613  (
3614  mesh.time().path()
3615  / "mergePoints_"
3616  + meshRefiner_.timeName()
3617  + ".obj"
3618  );
3619  Info<< "Points to be merged to " << str.name() << endl;
3620  forAll(pointToMaster, pointi)
3621  {
3622  label otherPointi = pointToMaster[pointi];
3623  if (otherPointi != -1)
3624  {
3625  const point& pt = mesh.points()[pointi];
3626  const point& otherPt = mesh.points()[otherPointi];
3627  str.writeLine(pt, otherPt);
3628  }
3629  }
3630  }
3631 
3632 
3633  autoPtr<mapPolyMesh> map = meshRefiner_.mergePoints(pointToMaster);
3634  if (map)
3635  {
3636  inplaceReorder(map().reverseCellMap(), cellNLayers);
3637 
3638  const labelList& reverseFaceMap = map().reverseFaceMap();
3639  inplaceReorder(reverseFaceMap, faceWantedThickness);
3640  inplaceReorder(reverseFaceMap, faceRealThickness);
3641 
3642  Info<< "Merged points in = "
3643  << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
3644  }
3645  }
3646 
3647  if (mesh.faceZones().size() > 0)
3648  {
3649  // Merge any baffles
3650  Info<< "Converting baffles back into zoned faces ..."
3651  << endl;
3652 
3653  autoPtr<mapPolyMesh> map = meshRefiner_.mergeZoneBaffles
3654  (
3655  true, // internal zones
3656  false // baffle zones
3657  );
3658  if (map)
3659  {
3660  inplaceReorder(map().reverseCellMap(), cellNLayers);
3661 
3662  const labelList& faceMap = map().faceMap();
3663 
3664  // Make sure to keep the max since on two patches only one has
3665  // layers.
3666  scalarField newFaceRealThickness(mesh.nFaces(), Zero);
3667  scalarField newFaceWantedThickness(mesh.nFaces(), Zero);
3668  forAll(newFaceRealThickness, facei)
3669  {
3670  label oldFacei = faceMap[facei];
3671  if (oldFacei >= 0)
3672  {
3673  scalar& realThick = newFaceRealThickness[facei];
3674  realThick = max(realThick, faceRealThickness[oldFacei]);
3675  scalar& wanted = newFaceWantedThickness[facei];
3676  wanted = max(wanted, faceWantedThickness[oldFacei]);
3677  }
3678  }
3679  faceRealThickness.transfer(newFaceRealThickness);
3680  faceWantedThickness.transfer(newFaceWantedThickness);
3681  }
3682 
3683  Info<< "Converted baffles in = "
3684  << meshRefiner_.mesh().time().cpuTimeIncrement()
3685  << " s\n" << nl << endl;
3686  }
3687 }
3688 
3689 
3690 Foam::label Foam::snappyLayerDriver::setPointNumLayers
3691 (
3692  const layerParameters& layerParams,
3693 
3694  const labelList& numLayers,
3695  const labelList& patchIDs,
3696  const indirectPrimitivePatch& pp,
3697  const labelListList& edgeGlobalFaces,
3698 
3699  vectorField& patchDisp,
3700  labelList& patchNLayers,
3701  List<extrudeMode>& extrudeStatus
3702 ) const
3703 {
3704  fvMesh& mesh = meshRefiner_.mesh();
3705 
3706  patchDisp.setSize(pp.nPoints());
3707  patchDisp = vector(GREAT, GREAT, GREAT);
3708 
3709  // Number of layers for all pp.localPoints. Note: only valid if
3710  // extrudeStatus = EXTRUDE.
3711  patchNLayers.setSize(pp.nPoints());
3712  patchNLayers = Zero;
3713 
3714  // Ideal number of cells added
3715  label nIdealTotAddedCells = 0;
3716 
3717  // Whether to add edge for all pp.localPoints.
3718  extrudeStatus.setSize(pp.nPoints());
3719  extrudeStatus = EXTRUDE;
3720 
3721 
3722  // Get number of layers per point from number of layers per patch
3723  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3724 
3725  setNumLayers
3726  (
3727  numLayers, // per patch the num layers
3728  patchIDs, // patches that are being moved
3729  pp, // indirectpatch for all faces moving
3730 
3731  patchNLayers,
3732  extrudeStatus,
3733  nIdealTotAddedCells
3734  );
3735 
3736  // Precalculate mesh edge labels for patch edges
3737  labelList meshEdges(pp.meshEdges(mesh.edges(), mesh.pointEdges()));
3738 
3739 
3740  // Disable extrusion on split strings of common points
3741  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3742 
3743  handleNonStringConnected
3744  (
3745  pp,
3746  patchDisp,
3747  patchNLayers,
3748  extrudeStatus
3749  );
3750 
3751 
3752  // Disable extrusion on non-manifold points
3753  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3754 
3755  handleNonManifolds
3756  (
3757  pp,
3758  meshEdges,
3759  edgeGlobalFaces,
3760 
3761  patchDisp,
3762  patchNLayers,
3763  extrudeStatus
3764  );
3765 
3766  // Disable extrusion on feature angles
3767  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3768 
3769  handleFeatureAngle
3770  (
3771  pp,
3772  meshEdges,
3773  layerParams.featureAngle(),
3774 
3775  patchDisp,
3776  patchNLayers,
3777  extrudeStatus
3778  );
3779 
3780  // Disable extrusion on warped faces
3781  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3782  // It is hard to calculate some length scale if not in relative
3783  // mode so disable this check.
3784  if (!layerParams.relativeSizes().found(false))
3785  {
3786  // Undistorted edge length
3787  const scalar edge0Len =
3788  meshRefiner_.meshCutter().level0EdgeLength();
3789  const labelList& cellLevel = meshRefiner_.meshCutter().cellLevel();
3790 
3791  handleWarpedFaces
3792  (
3793  pp,
3794  layerParams.maxFaceThicknessRatio(),
3795  layerParams.relativeSizes(),
3796  edge0Len,
3797  cellLevel,
3798 
3799  patchDisp,
3800  patchNLayers,
3801  extrudeStatus
3802  );
3803  }
3804 
3807  //
3808  //handleMultiplePatchFaces
3809  //(
3810  // pp,
3811  //
3812  // patchDisp,
3813  // patchNLayers,
3814  // extrudeStatus
3815  //);
3816 
3817  addProfiling(grow, "snappyHexMesh::layers::grow");
3818 
3819  // Grow out region of non-extrusion
3820  for (label i = 0; i < layerParams.nGrow(); i++)
3821  {
3822  growNoExtrusion
3823  (
3824  pp,
3825  patchDisp,
3826  patchNLayers,
3827  extrudeStatus
3828  );
3829  }
3830  return nIdealTotAddedCells;
3831 }
3832 
3833 
3835 Foam::snappyLayerDriver::makeMeshMover
3836 (
3837  const layerParameters& layerParams,
3838  const dictionary& motionDict,
3839  const labelList& internalFaceZones,
3840  const scalarIOField& minThickness,
3841  pointVectorField& displacement
3842 ) const
3843 {
3844  // Allocate run-time selectable mesh mover
3845 
3846  fvMesh& mesh = meshRefiner_.mesh();
3847 
3848 
3849  // Set up controls for meshMover
3850  dictionary combinedDict(layerParams.dict());
3851  // Add mesh quality constraints
3852  combinedDict.merge(motionDict);
3853  // Where to get minThickness from
3854  combinedDict.add("minThicknessName", minThickness.name());
3855 
3856  const List<labelPair> internalBaffles
3857  (
3859  (
3860  mesh,
3861  internalFaceZones,
3863  )
3864  );
3865 
3866  // Take over patchDisp as boundary conditions on displacement
3867  // pointVectorField
3868  autoPtr<Foam::externalDisplacementMeshMover> medialAxisMoverPtr
3869  (
3871  (
3872  layerParams.meshShrinker(),
3873  combinedDict,
3874  internalBaffles,
3875  displacement
3876  )
3877  );
3878 
3879 
3880  if (dryRun_)
3881  {
3882  string errorMsg(FatalError.message());
3883  string IOerrorMsg(FatalIOError.message());
3884 
3885  if (errorMsg.size() || IOerrorMsg.size())
3886  {
3887  //errorMsg = "[dryRun] " + errorMsg;
3888  //errorMsg.replaceAll("\n", "\n[dryRun] ");
3889  //IOerrorMsg = "[dryRun] " + IOerrorMsg;
3890  //IOerrorMsg.replaceAll("\n", "\n[dryRun] ");
3891 
3892  IOWarningInFunction(combinedDict)
3893  << nl
3894  << "Missing/incorrect required dictionary entries:"
3895  << nl << nl
3896  << IOerrorMsg.c_str() << nl
3897  << errorMsg.c_str() << nl << nl
3898  << "Exiting dry-run" << nl << endl;
3899 
3900  if (Pstream::parRun())
3901  {
3902  Perr<< "\nFOAM parallel run exiting\n" << endl;
3903  Pstream::exit(0);
3904  }
3905  else
3906  {
3907  Perr<< "\nFOAM exiting\n" << endl;
3908  std::exit(0);
3909  }
3910  }
3911  }
3912  return medialAxisMoverPtr;
3914 
3915 
3917 (
3918  const layerParameters& layerParams,
3919  const label nLayerIter,
3920 
3921  // Mesh quality provision
3922  const dictionary& motionDict,
3923  const label nRelaxedIter,
3924  const label nAllowableErrors,
3925 
3926  const labelList& patchIDs,
3927  const labelList& internalFaceZones,
3928  const List<labelPair>& baffles,
3929  const labelList& numLayers,
3930  const label nIdealTotAddedCells,
3931 
3932  const globalIndex& globalFaces,
3934 
3935  const labelListList& edgeGlobalFaces,
3936  const labelList& edgePatchID,
3937  const labelList& edgeZoneID,
3938  const boolList& edgeFlip,
3939  const labelList& inflateFaceID,
3940 
3941  const scalarField& thickness,
3942  const scalarIOField& minThickness,
3943  const scalarField& expansionRatio,
3944 
3945  // Displacement for all pp.localPoints. Set to large value so does
3946  // not trigger the minThickness truncation (see syncPatchDisplacement below)
3947  vectorField& patchDisp,
3948 
3949  // Number of layers for all pp.localPoints. Note: only valid if
3950  // extrudeStatus = EXTRUDE.
3951  labelList& patchNLayers,
3952 
3953  // Whether to add edge for all pp.localPoints.
3954  List<extrudeMode>& extrudeStatus,
3955 
3956 
3957  polyTopoChange& savedMeshMod,
3958 
3959 
3960  // Per cell 0 or number of layers in the cell column it is part of
3961  labelList& cellNLayers,
3962  // Per face actual overall layer thickness
3963  scalarField& faceRealThickness
3964 )
3965 {
3966  fvMesh& mesh = meshRefiner_.mesh();
3967 
3968  // Overall displacement field
3969  pointVectorField displacement
3970  (
3971  makeLayerDisplacementField
3972  (
3974  numLayers
3975  )
3976  );
3977 
3978  // Allocate run-time selectable mesh mover
3979  autoPtr<externalDisplacementMeshMover> medialAxisMoverPtr
3980  (
3981  makeMeshMover
3982  (
3983  layerParams,
3984  motionDict,
3985  internalFaceZones,
3986  minThickness,
3987  displacement
3988  )
3989  );
3990 
3991 
3992  // Saved old points
3993  const pointField oldPoints(mesh.points());
3994 
3995  for (label iteration = 0; iteration < nLayerIter; iteration++)
3996  {
3997  Info<< nl
3998  << "Layer addition iteration " << iteration << nl
3999  << "--------------------------" << endl;
4000 
4001 
4002  // Unset the extrusion at the pp.
4003  const dictionary& meshQualityDict =
4004  (
4005  iteration < nRelaxedIter
4006  ? motionDict
4007  : motionDict.subDict("relaxed")
4008  );
4009 
4010  if (iteration >= nRelaxedIter)
4011  {
4012  Info<< "Switched to relaxed meshQuality constraints." << endl;
4013  }
4014 
4015 
4016 
4017  // Make sure displacement is equal on both sides of coupled patches.
4018  // Note that this also does the patchDisp < minThickness truncation
4019  // so for the first pass make sure the patchDisp is larger than
4020  // that.
4021  syncPatchDisplacement
4022  (
4023  pp,
4024  minThickness,
4025  patchDisp,
4026  patchNLayers,
4027  extrudeStatus
4028  );
4029 
4030  // Displacement acc. to pointnormals
4031  getPatchDisplacement
4032  (
4033  pp,
4034  thickness,
4035  minThickness,
4036  expansionRatio,
4037 
4038  patchDisp,
4039  patchNLayers,
4040  extrudeStatus
4041  );
4042 
4043  // Shrink mesh by displacement value first.
4044  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4045 
4046  {
4047  const pointField oldPatchPos(pp.localPoints());
4048 
4049  // We have patchDisp which is the outwards pointing
4050  // extrusion distance. Convert into an inwards pointing
4051  // shrink distance
4052  patchDisp = -patchDisp;
4053 
4054  // Take over patchDisp into pointDisplacement field and
4055  // adjust both for multi-patch constraints
4057  (
4058  patchIDs,
4059  pp,
4060  patchDisp,
4061  displacement
4062  );
4063 
4064 
4065  // Move mesh
4066  // ~~~~~~~~~
4067 
4068  // Set up controls for meshMover
4069  dictionary combinedDict(layerParams.dict());
4070  // Add standard quality constraints
4071  combinedDict.merge(motionDict);
4072  // Add relaxed constraints (overrides standard ones)
4073  combinedDict.merge(meshQualityDict);
4074  // Where to get minThickness from
4075  combinedDict.add("minThicknessName", minThickness.name());
4076 
4077  labelList checkFaces(identity(mesh.nFaces()));
4078  medialAxisMoverPtr().move
4079  (
4080  combinedDict,
4081  nAllowableErrors,
4082  checkFaces
4083  );
4084 
4085  pp.movePoints(mesh.points());
4086 
4087  // Unset any moving state
4088  mesh.moving(false);
4089 
4090  // Update patchDisp (since not all might have been honoured)
4091  patchDisp = oldPatchPos - pp.localPoints();
4092  }
4093 
4094  // Truncate displacements that are too small (this will do internal
4095  // ones, coupled ones have already been truncated by
4096  // syncPatchDisplacement)
4097  faceSet dummySet(mesh, "wrongPatchFaces", 0);
4098  truncateDisplacement
4099  (
4100  globalFaces,
4101  edgeGlobalFaces,
4102  pp,
4103  minThickness,
4104  dummySet,
4105  patchDisp,
4106  patchNLayers,
4107  extrudeStatus
4108  );
4109 
4110 
4111  // Dump to .obj file for debugging.
4113  {
4114  dumpDisplacement
4115  (
4116  mesh.time().path()/"layer_" + meshRefiner_.timeName(),
4117  pp,
4118  patchDisp,
4119  extrudeStatus
4120  );
4121 
4122  const_cast<Time&>(mesh.time())++;
4123  Info<< "Writing shrunk mesh to time "
4124  << meshRefiner_.timeName() << endl;
4125 
4126  // See comment in snappySnapDriver why we should not remove
4127  // meshPhi using mesh.clearOut().
4128 
4129  meshRefiner_.write
4130  (
4133  (
4136  ),
4137  mesh.time().path()/meshRefiner_.timeName()
4138  );
4139  }
4140 
4141 
4142  // Mesh topo change engine. Insert current mesh.
4143  polyTopoChange meshMod(mesh);
4144 
4145  // Grow layer of cells on to patch. Handles zero sized displacement.
4146  addPatchCellLayer addLayer(mesh);
4147 
4148  // Determine per point/per face number of layers to extrude. Also
4149  // handles the slow termination of layers when going switching
4150  // layers
4151 
4152  labelList nPatchPointLayers(pp.nPoints(), -1);
4153  labelList nPatchFaceLayers(pp.size(), -1);
4154  setupLayerInfoTruncation
4155  (
4156  pp,
4157  patchNLayers,
4158  extrudeStatus,
4159  layerParams.nBufferCellsNoExtrude(),
4160  nPatchPointLayers,
4161  nPatchFaceLayers
4162  );
4163 
4164  // Calculate displacement for final layer for addPatchLayer.
4165  // (layer of cells next to the original mesh)
4166  vectorField finalDisp(patchNLayers.size(), Zero);
4167 
4168  forAll(nPatchPointLayers, i)
4169  {
4171  (
4172  nPatchPointLayers[i],
4173  expansionRatio[i]
4174  );
4175  finalDisp[i] = ratio*patchDisp[i];
4176  }
4177 
4178 
4179  const scalarField invExpansionRatio(1.0/expansionRatio);
4180 
4181  // Add topo regardless of whether extrudeStatus is extruderemove.
4182  // Not add layer if patchDisp is zero.
4183  addLayer.setRefinement
4184  (
4185  globalFaces,
4186  edgeGlobalFaces,
4187 
4188  invExpansionRatio,
4189  pp,
4190  bitSet(pp.size()), // no flip
4191 
4192  edgePatchID, // boundary patch for extruded boundary edges
4193  edgeZoneID, // zone for extruded edges
4194  edgeFlip,
4195  inflateFaceID,
4196 
4197 
4198  labelList(0), // exposed patchIDs, not used for adding layers
4199  nPatchFaceLayers, // layers per face
4200  nPatchPointLayers, // layers per point
4201  finalDisp, // thickness of layer nearest internal mesh
4202  meshMod
4203  );
4204 
4205  if (debug)
4206  {
4207  const_cast<Time&>(mesh.time())++;
4208  }
4209 
4210  // Compact storage
4211  meshMod.shrink();
4212 
4213  // Store mesh changes for if mesh is correct.
4214  savedMeshMod = meshMod;
4215 
4216 
4217  // With the stored topo changes we create a new mesh so we can
4218  // undo if necessary.
4219 
4220  autoPtr<fvMesh> newMeshPtr;
4221  autoPtr<mapPolyMesh> mapPtr = meshMod.makeMesh
4222  (
4223  newMeshPtr,
4224  IOobject
4225  (
4226  //mesh.name()+"_layer",
4227  mesh.name(),
4228  static_cast<polyMesh&>(mesh).instance(),
4229  mesh.time(), // register with runTime
4230  IOobject::READ_IF_PRESENT, // read fv* if present
4231  static_cast<polyMesh&>(mesh).writeOpt()
4232  ), // io params from original mesh but new name
4233  mesh, // original mesh
4234  true // parallel sync
4235  );
4236  fvMesh& newMesh = *newMeshPtr;
4237  mapPolyMesh& map = *mapPtr;
4238 
4239  // Get timing, but more importantly get memory information
4240  addProfiling(grow, "snappyHexMesh::layers::updateMesh");
4241 
4242  //?necessary? Update fields
4243  newMesh.updateMesh(map);
4244 
4245  // Move mesh if in inflation mode
4246  if (map.hasMotionPoints())
4247  {
4248  newMesh.movePoints(map.preMotionPoints());
4249  }
4250  else
4251  {
4252  // Delete mesh volumes.
4253  newMesh.clearOut();
4254  }
4255 
4256  newMesh.setInstance(meshRefiner_.timeName());
4257 
4258  // Update numbering on addLayer:
4259  // - cell/point labels to be newMesh.
4260  // - patchFaces to remain in oldMesh order.
4261  addLayer.updateMesh
4262  (
4263  map,
4264  identity(pp.size()),
4265  identity(pp.nPoints())
4266  );
4267 
4268  // Collect layer faces and cells for outside loop.
4269  getLayerCellsFaces
4270  (
4271  newMesh,
4272  addLayer,
4273  avgPointData(pp, mag(patchDisp))(), // current thickness
4274 
4275  cellNLayers,
4276  faceRealThickness
4277  );
4278 
4279 
4280  // Count number of added cells
4281  label nAddedCells = 0;
4282  forAll(cellNLayers, celli)
4283  {
4284  if (cellNLayers[celli] > 0)
4285  {
4286  nAddedCells++;
4287  }
4288  }
4289 
4290 
4292  {
4293  Info<< "Writing layer mesh to time " << meshRefiner_.timeName()
4294  << endl;
4295  newMesh.write();
4296  writeLayerSets(newMesh, cellNLayers, faceRealThickness);
4297 
4298  // Reset the instance of the original mesh so next iteration
4299  // it dumps a complete mesh. This is just so that the inbetween
4300  // newMesh does not upset e.g. paraFoam cycling through the
4301  // times.
4302  mesh.setInstance(meshRefiner_.timeName());
4303  }
4304 
4305 
4306  //- Get baffles in newMesh numbering. Note that we cannot detect
4307  // baffles here since the points are duplicated
4308  List<labelPair> internalBaffles;
4309  {
4310  // From old mesh face to corresponding newMesh boundary face
4311  labelList meshToNewMesh(mesh.nFaces(), -1);
4312  for
4313  (
4314  label facei = newMesh.nInternalFaces();
4315  facei < newMesh.nFaces();
4316  facei++
4317  )
4318  {
4319  label newMeshFacei = map.faceMap()[facei];
4320  if (newMeshFacei != -1)
4321  {
4322  meshToNewMesh[newMeshFacei] = facei;
4323  }
4324  }
4325 
4326  List<labelPair> newMeshBaffles(baffles.size());
4327  label newi = 0;
4328  forAll(baffles, i)
4329  {
4330  const labelPair& p = baffles[i];
4331  labelPair newMeshBaffle
4332  (
4333  meshToNewMesh[p[0]],
4334  meshToNewMesh[p[1]]
4335  );
4336  if (newMeshBaffle[0] != -1 && newMeshBaffle[1] != -1)
4337  {
4338  newMeshBaffles[newi++] = newMeshBaffle;
4339  }
4340  }
4341  newMeshBaffles.setSize(newi);
4342 
4343  internalBaffles = meshRefinement::subsetBaffles
4344  (
4345  newMesh,
4346  internalFaceZones,
4347  newMeshBaffles
4348  );
4349 
4350  Info<< "Detected "
4351  << returnReduce(internalBaffles.size(), sumOp<label>())
4352  << " baffles across faceZones of type internal" << nl
4353  << endl;
4354  }
4355 
4356  label nTotChanged = checkAndUnmark
4357  (
4358  addLayer,
4359  meshQualityDict,
4360  layerParams.additionalReporting(),
4361  internalBaffles,
4362  pp,
4363  newMesh,
4364 
4365  patchDisp,
4366  patchNLayers,
4367  extrudeStatus
4368  );
4369 
4370  label nTotExtruded = countExtrusion(pp, extrudeStatus);
4371  label nTotFaces = returnReduce(pp.size(), sumOp<label>());
4372  label nTotAddedCells = returnReduce(nAddedCells, sumOp<label>());
4373 
4374  Info<< "Extruding " << nTotExtruded
4375  << " out of " << nTotFaces
4376  << " faces (" << 100.0*nTotExtruded/nTotFaces << "%)."
4377  << " Removed extrusion at " << nTotChanged << " faces."
4378  << endl
4379  << "Added " << nTotAddedCells << " out of "
4380  << nIdealTotAddedCells
4381  << " cells (" << 100.0*nTotAddedCells/nIdealTotAddedCells
4382  << "%)." << endl;
4383 
4384  if (nTotChanged == 0)
4385  {
4386  break;
4387  }
4388 
4389  // Reset mesh points and start again
4390  mesh.movePoints(oldPoints);
4391  pp.movePoints(mesh.points());
4392  medialAxisMoverPtr().movePoints(mesh.points());
4393 
4394  // Unset any moving state
4395  mesh.moving(false);
4396 
4397  // Grow out region of non-extrusion
4398  for (label i = 0; i < layerParams.nGrow(); i++)
4399  {
4400  growNoExtrusion
4401  (
4402  pp,
4403  patchDisp,
4404  patchNLayers,
4405  extrudeStatus
4406  );
4407  }
4408 
4409  Info<< endl;
4410  }
4411 }
4412 
4413 
4414 void Foam::snappyLayerDriver::mapFaceZonePoints
4415 (
4416  const mapPolyMesh& map,
4417  labelPairList& baffles,
4418  labelList& pointToMaster
4419 ) const
4420 {
4421  fvMesh& mesh = meshRefiner_.mesh();
4422 
4423  // Use geometric detection of points-to-be-merged
4424  // - detect any boundary face created from a duplicated face (=baffle)
4425  // - on these mark any point created from a duplicated point
4426  if (returnReduceOr(pointToMaster.size()))
4427  {
4428  // Estimate number of points-to-be-merged
4429  DynamicList<label> candidates(baffles.size()*4);
4430 
4431  // The problem is that all the internal layer faces also
4432  // have reverseFaceMap pointing to the old baffle face. So instead
4433  // loop over all the boundary faces and see which pair of new boundary
4434  // faces corresponds to the old baffles.
4435 
4436 
4437  // Mark whether old face was on baffle
4438  Map<label> oldFaceToBaffle(2*baffles.size());
4439  forAll(baffles, i)
4440  {
4441  const labelPair& baffle = baffles[i];
4442  oldFaceToBaffle.insert(baffle[0], i);
4443  oldFaceToBaffle.insert(baffle[1], i);
4444  }
4445 
4446  labelPairList newBaffles(baffles.size(), labelPair(-1, -1));
4447 
4448  for
4449  (
4450  label facei = mesh.nInternalFaces();
4451  facei < mesh.nFaces();
4452  facei++
4453  )
4454  {
4455  const label oldFacei = map.faceMap()[facei];
4456  const auto iter = oldFaceToBaffle.find(oldFacei);
4457  if (oldFacei != -1 && iter.found())
4458  {
4459  const label bafflei = iter();
4460  auto& newBaffle = newBaffles[bafflei];
4461  if (newBaffle[0] == -1)
4462  {
4463  newBaffle[0] = facei;
4464  }
4465  else if (newBaffle[1] == -1)
4466  {
4467  newBaffle[1] = facei;
4468  }
4469  else
4470  {
4471  FatalErrorInFunction << "face:" << facei
4472  << " at:" << mesh.faceCentres()[facei]
4473  << " already maps to baffle faces:"
4474  << newBaffle[0]
4475  << " at:" << mesh.faceCentres()[newBaffle[0]]
4476  << " and " << newBaffle[1]
4477  << " at:" << mesh.faceCentres()[newBaffle[1]]
4478  << exit(FatalError);
4479  }
4480 
4481  const face& f = mesh.faces()[facei];
4482  forAll(f, fp)
4483  {
4484  label pointi = f[fp];
4485  label oldPointi = map.pointMap()[pointi];
4486 
4487  if (pointToMaster[oldPointi] != -1)
4488  {
4489  candidates.append(pointi);
4490  }
4491  }
4492  }
4493  }
4494 
4495 
4496  // Compact newBaffles
4497  {
4498  label n = 0;
4499  forAll(newBaffles, i)
4500  {
4501  const labelPair& newBaffle = newBaffles[i];
4502  if (newBaffle[0] != -1 && newBaffle[1] != -1)
4503  {
4504  newBaffles[n++] = newBaffle;
4505  }
4506  }
4507 
4508  newBaffles.setSize(n);
4509  baffles.transfer(newBaffles);
4510  }
4511 
4512 
4513  // Do geometric merge. Ideally we'd like to use a topological
4514  // merge but we've thrown away all layer-wise addressing when
4515  // throwing away addPatchCellLayer engine. Also the addressing
4516  // is extremely complicated. There is no problem with merging
4517  // too many points; the problem would be if merging baffles.
4518  // Trust mergeZoneBaffles to do sufficient checks.
4519  labelList oldToNew;
4520  label nNew = Foam::mergePoints
4521  (
4522  UIndirectList<point>(mesh.points(), candidates),
4523  meshRefiner_.mergeDistance(),
4524  false,
4525  oldToNew
4526  );
4527 
4528  // Extract points to be merged (i.e. multiple points originating
4529  // from a single one)
4530 
4531  labelListList newToOld(invertOneToMany(nNew, oldToNew));
4532 
4533  // Extract points with more than one old one
4534  pointToMaster.setSize(mesh.nPoints());
4535  pointToMaster = -1;
4536 
4537  forAll(newToOld, newi)
4538  {
4539  const labelList& oldPoints = newToOld[newi];
4540  if (oldPoints.size() > 1)
4541  {
4542  labelList meshPoints
4543  (
4544  labelUIndList(candidates, oldPoints)
4545  );
4546  label masteri = min(meshPoints);
4547  forAll(meshPoints, i)
4548  {
4549  pointToMaster[meshPoints[i]] = masteri;
4550  }
4551  }
4552  }
4553  }
4554 }
4555 
4556 
4557 void Foam::snappyLayerDriver::updatePatch
4558 (
4559  const labelList& patchIDs,
4560  const mapPolyMesh& map,
4561  autoPtr<indirectPrimitivePatch>& pp,
4562  labelList& newToOldPatchPoints
4563 ) const
4564 {
4565  // Update the pp to be consistent with the new mesh
4566 
4567  fvMesh& mesh = meshRefiner_.mesh();
4568 
4569  autoPtr<indirectPrimitivePatch> newPp
4570  (
4572  (
4573  mesh,
4574  patchIDs
4575  )
4576  );
4577 
4578  // Map from new back to old patch points
4579  newToOldPatchPoints.setSize(newPp().nPoints());
4580  newToOldPatchPoints = -1;
4581  {
4582  const Map<label>& baseMap = pp().meshPointMap();
4583  const labelList& newMeshPoints = newPp().meshPoints();
4584 
4585  forAll(newMeshPoints, newPointi)
4586  {
4587  const label newMeshPointi = newMeshPoints[newPointi];
4588  const label oldMeshPointi =
4589  map.pointMap()[newMeshPointi];
4590  const auto iter = baseMap.find(oldMeshPointi);
4591  if (iter.found())
4592  {
4593  newToOldPatchPoints[newPointi] = iter();
4594  }
4595  }
4596  }
4597 
4598 
4599  // Reset pp. Note: make sure to use std::move - otherwise it
4600  // will release the pointer before copying and you get
4601  // memory error. Same if using autoPtr::reset.
4602  pp = std::move(newPp);
4603 
4604  // Make sure pp has adressing cached
4605  (void)pp().meshPoints();
4606  (void)pp().meshPointMap();
4607 }
4608 
4609 
4610 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
4611 
4612 Foam::snappyLayerDriver::snappyLayerDriver
4613 (
4614  meshRefinement& meshRefiner,
4615  const labelList& globalToMasterPatch,
4616  const labelList& globalToSlavePatch,
4617  const bool dryRun
4618 )
4619 :
4620  meshRefiner_(meshRefiner),
4621  globalToMasterPatch_(globalToMasterPatch),
4622  globalToSlavePatch_(globalToSlavePatch),
4623  dryRun_(dryRun)
4624 {}
4625 
4626 
4627 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
4628 
4630 (
4631  const layerParameters& layerParams,
4632  const dictionary& motionDict,
4633  const meshRefinement::FaceMergeType mergeType
4634 )
4635 {
4636  // Clip to 30 degrees. Not helpful!
4637  //scalar planarAngle = min(30.0, layerParams.featureAngle());
4638  scalar planarAngle = layerParams.mergePatchFacesAngle();
4639  scalar minCos = Foam::cos(degToRad(planarAngle));
4640 
4641  scalar concaveCos = Foam::cos(degToRad(layerParams.concaveAngle()));
4642 
4643  Info<< nl
4644  << "Merging all faces of a cell" << nl
4645  << "---------------------------" << nl
4646  << " - which are on the same patch" << nl
4647  << " - which make an angle < " << planarAngle
4648  << " degrees"
4649  << " (cos:" << minCos << ')' << nl
4650  << " - as long as the resulting face doesn't become concave"
4651  << " by more than "
4652  << layerParams.concaveAngle() << " degrees" << nl
4653  << " (0=straight, 180=fully concave)" << nl
4654  << endl;
4655 
4656  const fvMesh& mesh = meshRefiner_.mesh();
4657 
4659 
4660  labelList duplicateFace(mesh.nFaces(), -1);
4661  forAll(couples, i)
4662  {
4663  const labelPair& cpl = couples[i];
4664  duplicateFace[cpl[0]] = cpl[1];
4665  duplicateFace[cpl[1]] = cpl[0];
4666  }
4667 
4668  label nChanged = meshRefiner_.mergePatchFacesUndo
4669  (
4670  minCos,
4671  concaveCos,
4672  meshRefiner_.meshedPatches(),
4673  motionDict,
4674  duplicateFace,
4675  mergeType // How to merge co-planar patch faces
4676  );
4677 
4678  nChanged += meshRefiner_.mergeEdgesUndo(minCos, motionDict);
4679 }
4680 
4681 
4683 (
4684  const layerParameters& layerParams,
4685  const dictionary& motionDict,
4686  const labelList& patchIDs,
4687  const label nAllowableErrors,
4688  decompositionMethod& decomposer,
4689  fvMeshDistribute& distributor
4690 )
4691 {
4692  fvMesh& mesh = meshRefiner_.mesh();
4693 
4694  // Undistorted edge length
4695  const scalar edge0Len = meshRefiner_.meshCutter().level0EdgeLength();
4696 
4697  // faceZones of type internal or baffle (for merging points across)
4698  labelList internalOrBaffleFaceZones;
4699  {
4701  fzTypes[0] = surfaceZonesInfo::INTERNAL;
4702  fzTypes[1] = surfaceZonesInfo::BAFFLE;
4703  internalOrBaffleFaceZones = meshRefiner_.getZones(fzTypes);
4704  }
4705 
4706  // faceZones of type internal (for checking mesh quality across and
4707  // merging baffles)
4708  const labelList internalFaceZones
4709  (
4710  meshRefiner_.getZones
4711  (
4713  (
4714  1,
4716  )
4717  )
4718  );
4719 
4720  // Create baffles (pairs of faces that share the same points)
4721  // Baffles stored as owner and neighbour face that have been created.
4722  List<labelPair> baffles;
4723  {
4724  labelList originatingFaceZone;
4725  meshRefiner_.createZoneBaffles
4726  (
4727  identity(mesh.faceZones().size()),
4728  baffles,
4729  originatingFaceZone
4730  );
4731 
4733  {
4734  const_cast<Time&>(mesh.time())++;
4735  Info<< "Writing baffled mesh to time "
4736  << meshRefiner_.timeName() << endl;
4737  meshRefiner_.write
4738  (
4741  (
4744  ),
4745  mesh.time().path()/meshRefiner_.timeName()
4746  );
4747  }
4748  }
4749 
4750 
4751  // Duplicate points on faceZones of type boundary. Should normally already
4752  // be done by snapping phase
4753  {
4754  autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldBoundaryPoints();
4755  if (map)
4756  {
4757  const labelList& reverseFaceMap = map->reverseFaceMap();
4758  forAll(baffles, i)
4759  {
4760  label f0 = reverseFaceMap[baffles[i].first()];
4761  label f1 = reverseFaceMap[baffles[i].second()];
4762  baffles[i] = labelPair(f0, f1);
4763  }
4764  }
4765  }
4766 
4767 
4768 
4769  // Now we have all patches determine the number of layer per patch
4770  // This will be layerParams.numLayers() except for faceZones where one
4771  // side does get layers and the other not in which case we want to
4772  // suppress movement by explicitly setting numLayers 0
4773  labelList numLayers(layerParams.numLayers());
4774  {
4775  labelHashSet layerIDs(patchIDs);
4776  forAll(mesh.faceZones(), zonei)
4777  {
4778  label mpi, spi;
4780  bool hasInfo = meshRefiner_.getFaceZoneInfo
4781  (
4782  mesh.faceZones()[zonei].name(),
4783  mpi,
4784  spi,
4785  fzType
4786  );
4787  if (hasInfo)
4788  {
4789  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
4790  if (layerIDs.found(mpi) && !layerIDs.found(spi))
4791  {
4792  // Only layers on master side. Fix points on slave side
4793  Info<< "On faceZone " << mesh.faceZones()[zonei].name()
4794  << " adding layers to master patch " << pbm[mpi].name()
4795  << " only. Freezing points on slave patch "
4796  << pbm[spi].name() << endl;
4797  numLayers[spi] = 0;
4798  }
4799  else if (!layerIDs.found(mpi) && layerIDs.found(spi))
4800  {
4801  // Only layers on slave side. Fix points on master side
4802  Info<< "On faceZone " << mesh.faceZones()[zonei].name()
4803  << " adding layers to slave patch " << pbm[spi].name()
4804  << " only. Freezing points on master patch "
4805  << pbm[mpi].name() << endl;
4806  numLayers[mpi] = 0;
4807  }
4808  }
4809  }
4810  }
4811 
4812 
4813  // Duplicate points on faceZones that layers are added to
4814  labelList pointToMaster;
4815  dupFaceZonePoints
4816  (
4817  patchIDs, // patch indices
4818  numLayers, // number of layers per patch
4819  baffles,
4820  pointToMaster
4821  );
4822 
4823 
4824  // Add layers to patches
4825  // ~~~~~~~~~~~~~~~~~~~~~
4826 
4827  // Now we have
4828  // - mesh with optional baffles and duplicated points for faceZones
4829  // where layers are to be added
4830  // - pointToMaster : correspondence for duplicated points
4831  // - baffles : list of pairs of faces
4832 
4833 
4834  // Calculate 'base' point extrusion
4836  (
4838  (
4839  mesh,
4840  patchIDs
4841  )
4842  );
4843  // Make sure pp has adressing cached before changing mesh later on
4844  (void)pp().meshPoints();
4845  (void)pp().meshPointMap();
4846 
4847  // Global face indices engine
4848  globalIndex globalFaces(mesh.nFaces());
4849 
4850  // Determine extrudePatch.edgeFaces in global numbering (so across
4851  // coupled patches). This is used only to string up edges between coupled
4852  // faces (all edges between same (global)face indices get extruded).
4853  labelListList edgeGlobalFaces
4854  (
4856  (
4857  mesh,
4858  globalFaces,
4859  *pp
4860  )
4861  );
4862 
4863 
4864  // Point-wise extrusion data
4865  // ~~~~~~~~~~~~~~~~~~~~~~~~~
4866 
4867  // Displacement for all pp.localPoints. Set to large value so does
4868  // not trigger the minThickness truncation (see syncPatchDisplacement below)
4869  vectorField basePatchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT));
4870 
4871  // Number of layers for all pp.localPoints. Note: only valid if
4872  // extrudeStatus = EXTRUDE.
4873  labelList basePatchNLayers(pp().nPoints(), Zero);
4874 
4875  // Whether to add edge for all pp.localPoints.
4876  List<extrudeMode> baseExtrudeStatus(pp().nPoints(), EXTRUDE);
4877 
4878  // Ideal number of cells added
4879  const label nIdealTotAddedCells = setPointNumLayers
4880  (
4881  layerParams,
4882 
4883  numLayers,
4884  patchIDs,
4885  pp(),
4886  edgeGlobalFaces,
4887 
4888  basePatchDisp,
4889  basePatchNLayers,
4890  baseExtrudeStatus
4891  );
4892 
4893  // Overall thickness of all layers
4894  scalarField baseThickness(pp().nPoints());
4895  // Truncation thickness - when to truncate layers
4896  scalarIOField baseMinThickness
4897  (
4898  IOobject
4899  (
4900  "minThickness",
4901  meshRefiner_.timeName(),
4902  mesh,
4904  ),
4905  pp().nPoints()
4906  );
4907  // Expansion ratio
4908  scalarField baseExpansionRatio(pp().nPoints());
4909  calculateLayerThickness
4910  (
4911  pp(),
4912  patchIDs,
4913  layerParams,
4914  meshRefiner_.meshCutter().cellLevel(),
4915  basePatchNLayers,
4916  edge0Len,
4917 
4918  baseThickness,
4919  baseMinThickness,
4920  baseExpansionRatio
4921  );
4922 
4923 
4924  // Per cell 0 or number of layers in the cell column it is part of
4925  labelList cellNLayers;
4926  // Per face actual overall layer thickness
4927  scalarField faceRealThickness;
4928  // Per face wanted overall layer thickness
4929  scalarField faceWantedThickness(mesh.nFaces(), Zero);
4930  {
4931  UIndirectList<scalar>(faceWantedThickness, pp().addressing()) =
4932  avgPointData(pp(), baseThickness);
4933  }
4934 
4935 
4936  // Per patch point the number of layers to add. Is basePatchNLayers
4937  // for nOuterIter = 1.
4938  labelList deltaNLayers
4939  (
4940  (basePatchNLayers+layerParams.nOuterIter()-1)
4941  /layerParams.nOuterIter()
4942  );
4943 
4944  // Per patch point the sum of added layers so far
4945  labelList nAddedLayers(basePatchNLayers.size(), 0);
4946 
4947 
4948  for (label layeri = 0; layeri < layerParams.nOuterIter(); layeri++)
4949  {
4950  // Divide layer addition into outer iterations. E.g. if
4951  // nOutIter = 2, numLayers is 20 for patchA and 1 for patchB
4952  // this will
4953  // - iter0:
4954  // - add 10 layers to patchA and 1 to patchB
4955  // - layers are finalLayerThickness down to the number of layers
4956  // - iter1 : add 10 layer to patchA and 0 to patchB
4957 
4958 
4959  // Exit if nothing to be added
4960  const label nToAdd = gSum(deltaNLayers);
4961  if (debug)
4962  {
4963  Info<< "Outer iteration : " << layeri
4964  << " to add in current iteration : " << nToAdd << endl;
4965  }
4966  if (nToAdd == 0)
4967  {
4968  break;
4969  }
4970 
4971 
4972  // Determine patches for extruded boundary edges. Calculates if any
4973  // additional processor patches need to be constructed.
4974 
4975  labelList edgePatchID;
4976  labelList edgeZoneID;
4977  boolList edgeFlip;
4978  labelList inflateFaceID;
4979  determineSidePatches
4980  (
4981  globalFaces,
4982  edgeGlobalFaces,
4983  *pp,
4984 
4985  edgePatchID,
4986  edgeZoneID,
4987  edgeFlip,
4988  inflateFaceID
4989  );
4990 
4991 
4992  // Point-wise extrusion data
4993  // ~~~~~~~~~~~~~~~~~~~~~~~~~
4994 
4995  // Displacement for all pp.localPoints. Set to large value so does
4996  // not trigger the minThickness truncation (see syncPatchDisplacement
4997  // below)
4998  vectorField patchDisp(basePatchDisp);
4999 
5000  // Number of layers for all pp.localPoints. Note: only valid if
5001  // extrudeStatus = EXTRUDE.
5002  labelList patchNLayers(deltaNLayers);
5003 
5004  // Whether to add edge for all pp.localPoints.
5005  List<extrudeMode> extrudeStatus(baseExtrudeStatus);
5006 
5007  // At this point
5008  // - patchDisp is either zero or a large value
5009  // - patchNLayers is the overall number of layers
5010  // - extrudeStatus is EXTRUDE or NOEXTRUDE
5011 
5012  // Determine (wanted) point-wise overall layer thickness and expansion
5013  // ratio for this deltaNLayers slice of the overall layers
5014  scalarField sliceThickness(pp().nPoints());
5015  {
5016  forAll(baseThickness, pointi)
5017  {
5018  sliceThickness[pointi] = layerParameters::layerThickness
5019  (
5020  basePatchNLayers[pointi], // overall number of layers
5021  baseThickness[pointi], // overall thickness
5022  baseExpansionRatio[pointi], // expansion ratio
5023  basePatchNLayers[pointi]
5024  -nAddedLayers[pointi]
5025  -patchNLayers[pointi], // start index
5026  patchNLayers[pointi] // nLayers to add
5027  );
5028  }
5029  }
5030 
5031 
5032  // Current set of topology changes. (changing mesh clears out
5033  // polyTopoChange)
5034  polyTopoChange meshMod(mesh.boundaryMesh().size());
5035 
5036  // Shrink mesh, add layers. Returns with any mesh changes in meshMod
5037  labelList sliceCellNLayers;
5038  scalarField sliceFaceRealThickness;
5039 
5040  addLayers
5041  (
5042  layerParams,
5043  layerParams.nLayerIter(),
5044 
5045  // Mesh quality
5046  motionDict,
5047  layerParams.nRelaxedIter(),
5048  nAllowableErrors,
5049 
5050  patchIDs,
5051  internalFaceZones,
5052  baffles,
5053  numLayers,
5054  nIdealTotAddedCells,
5055 
5056  // Patch information
5057  globalFaces,
5058  pp(),
5059  edgeGlobalFaces,
5060  edgePatchID,
5061  edgeZoneID,
5062  edgeFlip,
5063  inflateFaceID,
5064 
5065  // Per patch point the wanted thickness
5066  sliceThickness,
5067  baseMinThickness,
5068  baseExpansionRatio,
5069 
5070  // Per patch point the wanted&obtained layers and thickness
5071  patchDisp,
5072  patchNLayers,
5073  extrudeStatus,
5074 
5075  // Complete mesh changes
5076  meshMod,
5077 
5078  // Stats on new mesh
5079  sliceCellNLayers, //cellNLayers,
5080  sliceFaceRealThickness //faceRealThickness
5081  );
5082 
5083 
5084  // Exit if nothing added
5085  const label nTotalAdded = gSum(patchNLayers);
5086  if (debug)
5087  {
5088  Info<< "Outer iteration : " << layeri
5089  << " added in current iteration : " << nTotalAdded
5090  << " out of : " << gSum(deltaNLayers) << endl;
5091  }
5092  if (nTotalAdded == 0)
5093  {
5094  break;
5095  }
5096 
5097 
5098  // Update wanted layer statistics
5099  forAll(patchNLayers, pointi)
5100  {
5101  nAddedLayers[pointi] += patchNLayers[pointi];
5102 
5103  if (patchNLayers[pointi] == 0)
5104  {
5105  // No layers were added. Make sure that overall extrusion
5106  // gets reset as well
5107  unmarkExtrusion
5108  (
5109  pointi,
5110  basePatchDisp,
5111  basePatchNLayers,
5112  baseExtrudeStatus
5113  );
5114  basePatchNLayers[pointi] = nAddedLayers[pointi];
5115  deltaNLayers[pointi] = 0;
5116  }
5117  else
5118  {
5119  // Adjust the number of layers for the next iteration.
5120  // Can never be
5121  // higher than the adjusted overall number of layers.
5122  // Note: is min necessary?
5123  deltaNLayers[pointi] = max
5124  (
5125  0,
5126  min
5127  (
5128  deltaNLayers[pointi],
5129  basePatchNLayers[pointi] - nAddedLayers[pointi]
5130  )
5131  );
5132  }
5133  }
5134 
5135 
5136  // At this point we have a (shrunk) mesh and a set of topology changes
5137  // which will make a valid mesh with layer. Apply these changes to the
5138  // current mesh.
5139 
5140  {
5141  // Apply the stored topo changes to the current mesh.
5142  autoPtr<mapPolyMesh> mapPtr = meshMod.changeMesh(mesh, false);
5143  mapPolyMesh& map = *mapPtr;
5144 
5145  // Hack to remove meshPhi - mapped incorrectly. TBD.
5146  mesh.moving(false);
5147  mesh.clearOut();
5148 
5149  // Update fields
5150  mesh.updateMesh(map);
5151 
5152  // Move mesh (since morphing does not do this)
5153  if (map.hasMotionPoints())
5154  {
5156  }
5157  else
5158  {
5159  // Delete mesh volumes.
5160  mesh.clearOut();
5161  }
5162 
5163  // Reset the instance for if in overwrite mode
5164  mesh.setInstance(meshRefiner_.timeName());
5165 
5166  meshRefiner_.updateMesh(map, labelList(0));
5167 
5168  // Update numbering of accumulated cells
5169  cellNLayers.setSize(map.nOldCells(), 0);
5171  (
5172  map.cellMap(),
5173  label(0),
5174  cellNLayers
5175  );
5176  forAll(cellNLayers, i)
5177  {
5178  cellNLayers[i] += sliceCellNLayers[i];
5179  }
5180 
5181  faceRealThickness.setSize(map.nOldFaces(), scalar(0));
5183  (
5184  map.faceMap(),
5185  scalar(0),
5186  faceRealThickness
5187  );
5188  faceRealThickness += sliceFaceRealThickness;
5189 
5191  (
5192  map.faceMap(),
5193  scalar(0),
5194  faceWantedThickness
5195  );
5196 
5197  // Print data now that we still have patches for the zones
5198  //if (meshRefinement::outputLevel() & meshRefinement::OUTPUTLAYERINFO)
5199  printLayerData
5200  (
5201  mesh,
5202  patchIDs,
5203  cellNLayers,
5204  faceWantedThickness,
5205  faceRealThickness
5206  );
5207 
5208 
5209  // Dump for debugging
5211  {
5212  const_cast<Time&>(mesh.time())++;
5213  Info<< "Writing mesh with layers but disconnected to time "
5214  << meshRefiner_.timeName() << endl;
5215  meshRefiner_.write
5216  (
5219  (
5222  ),
5223  mesh.time().path()/meshRefiner_.timeName()
5224  );
5225  }
5226 
5227 
5228  // Map baffles, pointToMaster
5229  mapFaceZonePoints(map, baffles, pointToMaster);
5230 
5231  // Map patch and layer settings
5232  labelList newToOldPatchPoints;
5233  updatePatch(patchIDs, map, pp, newToOldPatchPoints);
5234 
5235  // Global face indices engine
5236  globalFaces.reset(mesh.nFaces());
5237 
5238  // Patch-edges to global faces using them
5239  edgeGlobalFaces = addPatchCellLayer::globalEdgeFaces
5240  (
5241  mesh,
5242  globalFaces,
5243  pp()
5244  );
5245 
5246  // Map patch-point based data
5248  (
5249  newToOldPatchPoints,
5250  vector::uniform(-1),
5251  basePatchDisp
5252  );
5254  (
5255  newToOldPatchPoints,
5256  label(0),
5257  basePatchNLayers
5258  );
5260  (
5261  newToOldPatchPoints,
5262  extrudeMode::NOEXTRUDE,
5263  baseExtrudeStatus
5264  );
5266  (
5267  newToOldPatchPoints,
5268  scalar(0),
5269  baseThickness
5270  );
5272  (
5273  newToOldPatchPoints,
5274  scalar(0),
5275  baseMinThickness
5276  );
5278  (
5279  newToOldPatchPoints,
5280  GREAT,
5281  baseExpansionRatio
5282  );
5284  (
5285  newToOldPatchPoints,
5286  label(0),
5287  deltaNLayers
5288  );
5290  (
5291  newToOldPatchPoints,
5292  label(0),
5293  nAddedLayers
5294  );
5295  }
5296  }
5297 
5298 
5299  // Merge baffles
5300  mergeFaceZonePoints
5301  (
5302  pointToMaster, // per new mesh point : -1 or index of duplicated point
5303  cellNLayers, // per new cell : number of layers
5304  faceRealThickness, // per new face : actual thickness
5305  faceWantedThickness // per new face : wanted thickness
5306  );
5307 
5308 
5309  // Do final balancing
5310  // ~~~~~~~~~~~~~~~~~~
5311 
5312  if (Pstream::parRun())
5313  {
5314  Info<< nl
5315  << "Doing final balancing" << nl
5316  << "---------------------" << nl
5317  << endl;
5318 
5319  if (debug)
5320  {
5321  const_cast<Time&>(mesh.time())++;
5322  }
5323 
5324  // Balance. No restriction on face zones and baffles.
5325  autoPtr<mapDistributePolyMesh> map = meshRefiner_.balance
5326  (
5327  false,
5328  false,
5329  scalarField(mesh.nCells(), 1.0),
5330  decomposer,
5331  distributor
5332  );
5333 
5334  // Re-distribute flag of layer faces and cells
5335  map().distributeCellData(cellNLayers);
5336  map().distributeFaceData(faceWantedThickness);
5337  map().distributeFaceData(faceRealThickness);
5338  }
5339 
5340 
5341  // Write mesh data
5342  // ~~~~~~~~~~~~~~~
5343 
5344  if (!dryRun_)
5345  {
5346  writeLayerData
5347  (
5348  mesh,
5349  patchIDs,
5350  cellNLayers,
5351  faceWantedThickness,
5352  faceRealThickness
5353  );
5354  }
5355 }
5356 
5357 
5359 (
5360  const dictionary& shrinkDict,
5361  const dictionary& motionDict,
5362  const layerParameters& layerParams,
5363  const meshRefinement::FaceMergeType mergeType,
5364  const bool preBalance,
5365  decompositionMethod& decomposer,
5366  fvMeshDistribute& distributor
5367 )
5368 {
5369  addProfiling(layers, "snappyHexMesh::layers");
5370  const fvMesh& mesh = meshRefiner_.mesh();
5371 
5372  Info<< nl
5373  << "Shrinking and layer addition phase" << nl
5374  << "----------------------------------" << nl
5375  << endl;
5376 
5377 
5378  Info<< "Using mesh parameters " << motionDict << nl << endl;
5379 
5380  // Merge coplanar boundary faces
5381  if
5382  (
5383  mergeType == meshRefinement::FaceMergeType::GEOMETRIC
5384  || mergeType == meshRefinement::FaceMergeType::IGNOREPATCH
5385  )
5386  {
5387  mergePatchFacesUndo(layerParams, motionDict, mergeType);
5388  }
5389 
5390 
5391  // Per patch the number of layers (-1 or 0 if no layer)
5392  const labelList& numLayers = layerParams.numLayers();
5393 
5394  // Patches that need to get a layer
5395  DynamicList<label> patchIDs(numLayers.size());
5396  label nFacesWithLayers = 0;
5397  forAll(numLayers, patchi)
5398  {
5399  if (numLayers[patchi] > 0)
5400  {
5401  const polyPatch& pp = mesh.boundaryMesh()[patchi];
5402 
5403  if (!pp.coupled())
5404  {
5405  patchIDs.append(patchi);
5406  nFacesWithLayers += mesh.boundaryMesh()[patchi].size();
5407  }
5408  else
5409  {
5411  << "Ignoring layers on coupled patch " << pp.name()
5412  << endl;
5413  }
5414  }
5415  }
5416 
5417  // Add contributions from faceZones that get layers
5418  const faceZoneMesh& fZones = mesh.faceZones();
5419  forAll(fZones, zonei)
5420  {
5421  label mpi, spi;
5423  meshRefiner_.getFaceZoneInfo(fZones[zonei].name(), mpi, spi, fzType);
5424 
5425  if (numLayers[mpi] > 0)
5426  {
5427  nFacesWithLayers += fZones[zonei].size();
5428  }
5429  if (numLayers[spi] > 0)
5430  {
5431  nFacesWithLayers += fZones[zonei].size();
5432  }
5433  }
5434 
5435 
5436  patchIDs.shrink();
5437 
5438  if (!returnReduceOr(nFacesWithLayers))
5439  {
5440  Info<< nl << "No layers to generate ..." << endl;
5441  }
5442  else
5443  {
5444  // Check that outside of mesh is not multiply connected.
5445  checkMeshManifold();
5446 
5447  // Check initial mesh
5448  Info<< "Checking initial mesh ..." << endl;
5449  labelHashSet wrongFaces(mesh.nFaces()/100);
5450  motionSmoother::checkMesh(false, mesh, motionDict, wrongFaces, dryRun_);
5451  const label nInitErrors = returnReduce
5452  (
5453  wrongFaces.size(),
5454  sumOp<label>()
5455  );
5456 
5457  Info<< "Detected " << nInitErrors << " illegal faces"
5458  << " (concave, zero area or negative cell pyramid volume)"
5459  << endl;
5460 
5461 
5462  bool faceZoneOnCoupledFace = false;
5463 
5464  if (!preBalance)
5465  {
5466  // Check if there are faceZones on processor boundaries. This
5467  // requires balancing to move them off the processor boundaries.
5468 
5469  // Is face on a faceZone
5470  bitSet isExtrudedZoneFace(mesh.nFaces());
5471  {
5472  // Add contributions from faceZones that get layers
5473  const faceZoneMesh& fZones = mesh.faceZones();
5474  forAll(fZones, zonei)
5475  {
5476  const faceZone& fZone = fZones[zonei];
5477  const word& fzName = fZone.name();
5478 
5479  label mpi, spi;
5481  meshRefiner_.getFaceZoneInfo(fzName, mpi, spi, fzType);
5482 
5483  if (numLayers[mpi] > 0 || numLayers[spi])
5484  {
5485  isExtrudedZoneFace.set(fZone);
5486  }
5487  }
5488  }
5489 
5490  bitSet intOrCoupled
5491  (
5493  );
5494 
5495  for
5496  (
5497  label facei = mesh.nInternalFaces();
5498  facei < mesh.nFaces();
5499  facei++
5500  )
5501  {
5502  if (intOrCoupled[facei] && isExtrudedZoneFace.test(facei))
5503  {
5504  faceZoneOnCoupledFace = true;
5505  break;
5506  }
5507  }
5508 
5509  Pstream::reduceOr(faceZoneOnCoupledFace);
5510  }
5511 
5512 
5513 
5514 
5515  // Balance
5516  if (Pstream::parRun() && (preBalance || faceZoneOnCoupledFace))
5517  {
5518  Info<< nl
5519  << "Doing initial balancing" << nl
5520  << "-----------------------" << nl
5521  << endl;
5522 
5523  scalarField cellWeights(mesh.nCells(), 1);
5524  forAll(numLayers, patchi)
5525  {
5526  if (numLayers[patchi] > 0)
5527  {
5528  const polyPatch& pp = mesh.boundaryMesh()[patchi];
5529  forAll(pp.faceCells(), i)
5530  {
5531  cellWeights[pp.faceCells()[i]] += numLayers[patchi];
5532  }
5533  }
5534  }
5535 
5536  // Add contributions from faceZones that get layers
5537  const faceZoneMesh& fZones = mesh.faceZones();
5538  forAll(fZones, zonei)
5539  {
5540  const faceZone& fZone = fZones[zonei];
5541  const word& fzName = fZone.name();
5542 
5543  label mpi, spi;
5545  meshRefiner_.getFaceZoneInfo(fzName, mpi, spi, fzType);
5546 
5547  if (numLayers[mpi] > 0)
5548  {
5549  // Get the owner side for unflipped faces, neighbour side
5550  // for flipped ones
5551  const labelList& cellIDs = fZone.slaveCells();
5552  forAll(cellIDs, i)
5553  {
5554  if (cellIDs[i] >= 0)
5555  {
5556  cellWeights[cellIDs[i]] += numLayers[mpi];
5557  }
5558  }
5559  }
5560  if (numLayers[spi] > 0)
5561  {
5562  const labelList& cellIDs = fZone.masterCells();
5563  forAll(cellIDs, i)
5564  {
5565  if (cellIDs[i] >= 0)
5566  {
5567  cellWeights[cellIDs[i]] += numLayers[mpi];
5568  }
5569  }
5570  }
5571  }
5572 
5573  // Balance mesh (and meshRefinement). Restrict faceZones to
5574  // be on internal faces only since they will be converted into
5575  // baffles.
5576  autoPtr<mapDistributePolyMesh> map = meshRefiner_.balance
5577  (
5578  true, // keepZoneFaces
5579  false,
5580  cellWeights,
5581  decomposer,
5582  distributor
5583  );
5584  }
5585 
5586 
5587  // Do all topo changes
5588  if (layerParams.nOuterIter() == -1)
5589  {
5590  // For testing. Can be removed once addLayers below works
5591  addLayersSinglePass
5592  (
5593  layerParams,
5594  motionDict,
5595  patchIDs,
5596  nInitErrors,
5597  decomposer,
5598  distributor
5599  );
5600  }
5601  else
5602  {
5603  addLayers
5604  (
5605  layerParams,
5606  motionDict,
5607  patchIDs,
5608  nInitErrors,
5609  decomposer,
5610  distributor
5611  );
5612  }
5613  }
5614 }
5615 
5616 
5617 // ************************************************************************* //
label nPatches
Definition: readKivaGrid.H:396
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:584
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.
virtual const fileName & name() const
Get the name of the output serial stream. (eg, the name of the Fstream file name) ...
Definition: OSstream.H:128
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:51
prefixOSstream Perr
OSstream wrapped stderr (std::cerr) with parallel prefix.
void clearOut()
Clear all geometry and addressing.
Definition: fvMesh.C:232
label nPoints() const
Number of points supporting patch faces.
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:118
#define addProfiling(name, descr)
Define profiling trigger with specified name and description string.
fileName path() const
Return path.
Definition: Time.H:449
Simple container to keep together layer specific information.
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:449
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)
virtual int precision() const
Get precision of output field.
Definition: OSstream.C:305
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:120
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:578
scalar concaveAngle() const
void append(const T &val)
Append an element at the end of the list.
Definition: List.H:491
label find(const T &val, label pos=0) const
Find index of the first occurrence of the value.
Definition: UList.C:204
bool found(const Key &key) const
True if hashed key is found in table.
Definition: HashTableI.H:93
const word & name() const noexcept
Return the object name.
Definition: IOobjectI.H:150
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
virtual bool write(const bool valid=true) const
Write mesh using IO settings from time.
Definition: fvMesh.C:1072
const labelListList & pointEdges() const
static const pointMesh & New(const polyMesh &mesh, Args &&... args)
Get existing or create a new MeshObject.
Definition: MeshObject.C:41
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:49
void inplaceReorder(const labelUList &oldToNew, ListType &input, const bool prune=false)
Inplace reorder the elements of a list.
List< face > faceList
A List of faces.
Definition: faceListFwd.H:41
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.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
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:888
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:639
scalar mergePatchFacesAngle() const
const labelList & masterCells() const
Return labels of master cells (cells next to the master face zone in the prescribed direction) ...
Definition: faceZone.C:382
static writeType writeLevel()
Get/set write level.
static void reduceOr(bool &value, const label communicator=worldComm)
Logical (or) reduction (cf. 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.
const cellList & cells() const
constexpr label labelMin
Definition: label.H:54
GeometricBoundaryField< scalar, fvPatchField, volMesh > Boundary
Type of boundary fields.
const dictionary & dict() const
static List< labelPair > subsetBaffles(const polyMesh &mesh, const labelList &zoneIDs, const List< labelPair > &baffles)
Subset baffles according to zones.
const dimensionSet dimless
Dimensionless.
static int myProcNo(const label communicator=worldComm)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:688
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:361
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...
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.
const dictionary & subDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a sub-dictionary.
Definition: dictionary.C:453
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:422
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
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:1066
label nRelaxedIter() const
Number of iterations after which relaxed motion rules.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:413
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:53
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:84
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.
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:811
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:63
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator) is 1 for serial run.
Definition: UPstream.H:656
Sends/receives parts of mesh+fvfields to neighbouring processors. Used in load balancing.
bool additionalReporting() const
Any additional reporting requested?
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
void setSize(const label n)
Alias for resize()
Definition: List.H:289
virtual void updateMesh(const mapPolyMesh &mpm)
Update mesh corresponding to the given map.
Definition: fvMesh.C:964
FaceMergeType
Enumeration for what to do with co-planar patch faces on a single.
dynamicFvMesh & mesh
virtual bool coupled() const
Return true if this patch is geometrically coupled (i.e. faces and.
Definition: polyPatch.H:464
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 INVALID.
Definition: exprTraits.C:52
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
const labelUList & faceCells() const
Return face-cell addressing.
Definition: polyPatch.C:365
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i)
Definition: labelList.C:31
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:109
Adds layers of cells to outside of polyPatch. Can optionally create stand-alone extruded mesh (addToM...
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 elements in the list.
Definition: UPtrListI.H:99
string message() const
The accumulated error message.
Definition: error.C:315
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1104
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:203
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
Smanip< ios_base::fmtflags > setf(const ios_base::fmtflags flags)
Definition: IOmanip.H:169
IOField< scalar > scalarIOField
scalarField with IO.
Definition: scalarIOField.H:38
Abstract base class for domain decomposition.
label nInternalFaces() const noexcept
Number of internal faces.
Vector< scalar > vector
Definition: vector.H:57
void shrink()
Shrink storage (does not remove any elements; just compacts dynamic lists.
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1091
List< labelPair > labelPairList
List of labelPairs.
Definition: labelPair.H:61
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 value at specified position, never auto-vivify entries.
Definition: bitSetI.H:514
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:67
const word & name() const noexcept
The patch name.
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 time name of given scalar time formatted with the given precision.
Definition: Time.C:760
int debug
Static debugging option.
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:50
Type gMax(const FieldField< Field, Type > &f)
const faceZoneMesh & faceZones() const noexcept
Return face zone mesh.
Definition: polyMesh.H:646
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:467
labelList f(nPoints)
const labelList & slaveCells() const
Return labels of slave cells.
Definition: faceZone.C:393
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:696
const edgeList & edges() const
Return mesh edges. Uses calcEdges.
thicknessModelType
Enumeration defining the layer specification:
const vectorField & faceCentres() const
List< word > wordList
A List of words.
Definition: fileName.H:58
void append(autoPtr< T > &ptr)
Move append an element to the end of the list.
Definition: PtrList.H:304
UIndirectList< label > labelUIndList
UIndirectList of labels.
Definition: IndirectList.H:65
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:388
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.
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:79
A List with indirect addressing. Like IndirectList but does not store addressing. ...
Definition: faMatrix.H:57
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
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:73
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
A subset of mesh faces organised as a primitive patch.
Definition: faceZone.H:60
static void syncEdgeList(const polyMesh &mesh, List< T > &edgeValues, const CombineOp &cop, const T &nullValue, const TransformOp &top, const FlipOp &fop)
Synchronize values on all mesh edges.
List< label > labelList
A List of labels.
Definition: List.H:62
volScalarField & p
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:458
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:166
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.
const labelList & reverseFaceMap() const
Reverse face map.
Definition: mapPolyMesh.H:617
const pointField & preMotionPoints() const
Pre-motion point positions.
Definition: mapPolyMesh.H:756
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:157