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