snappyLayerDriver.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2011-2015 OpenFOAM Foundation
9 Copyright (C) 2015-2022 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
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
27Description
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"
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"
60#include "localPointRegion.H"
62#include "scalarIOField.H"
63#include "profiling.H"
64
65// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
66
67namespace Foam
68{
69
71
72} // End namespace Foam
73
74
75// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
76
77// For debugging: Dump displacement to .obj files
78void Foam::snappyLayerDriver::dumpDisplacement
79(
80 const fileName& prefix,
81 const indirectPrimitivePatch& pp,
82 const vectorField& patchDisp,
83 const List<extrudeMode>& extrudeStatus
84)
85{
86 OBJstream dispStr(prefix + "_disp.obj");
87 Info<< "Writing all displacements to " << dispStr.name() << endl;
88
89 forAll(patchDisp, patchPointi)
90 {
91 const point& pt = pp.localPoints()[patchPointi];
92 dispStr.write(linePointRef(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.write(linePointRef(pt, pt + patchDisp[patchPointi]));
105 }
106 }
107}
108
109
110Foam::tmp<Foam::scalarField> Foam::snappyLayerDriver::avgPointData
111(
112 const indirectPrimitivePatch& pp,
113 const scalarField& pointFld
114)
115{
116 tmp<scalarField> tfaceFld(new scalarField(pp.size(), Zero));
117 scalarField& faceFld = tfaceFld.ref();
118
119 forAll(pp.localFaces(), facei)
120 {
121 const face& f = pp.localFaces()[facei];
122 if (f.size())
123 {
124 forAll(f, fp)
125 {
126 faceFld[facei] += pointFld[f[fp]];
127 }
128 faceFld[facei] /= f.size();
129 }
130 }
131 return tfaceFld;
132}
133
134
135// Check that primitivePatch is not multiply connected. Collect non-manifold
136// points in pointSet.
137void 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
164void 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.
214bool 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.
242bool 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
272Foam::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
289void 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
323bool 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
418void 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
469void 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.
542void 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.
697void 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;
746 if (debug&meshRefinement::MESH)
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 const point& p0 = pp.localPoints()[e[0]];
801 const point& p1 = pp.localPoints()[e[1]];
802 str().write(linePointRef(p0, p1));
803 }
804 }
805 }
806 }
807
808 Info<< "Set displacement to zero for points on "
809 << returnReduce(nFeats, sumOp<label>())
810 << " feature edges" << endl;
811 }
812}
813
814
815// No extrusion on cells with warped faces. Calculates the thickness of the
816// layer and compares it to the space the warped face takes up. Disables
817// extrusion if layer thickness is more than faceRatio of the thickness of
818// the face.
819void Foam::snappyLayerDriver::handleWarpedFaces
820(
821 const indirectPrimitivePatch& pp,
822 const scalar faceRatio,
823 const boolList& relativeSizes,
824 const scalar edge0Len,
825 const labelList& cellLevel,
826 pointField& patchDisp,
827 labelList& patchNLayers,
828 List<extrudeMode>& extrudeStatus
829) const
830{
831 const fvMesh& mesh = meshRefiner_.mesh();
832 const polyBoundaryMesh& patches = mesh.boundaryMesh();
833
834 Info<< nl << "Handling cells with warped patch faces ..." << nl;
835
836 const pointField& points = mesh.points();
837
838 // Local reference to face centres also used to trigger consistent
839 // [re-]building of demand-driven face centres and areas
840 const vectorField& faceCentres = mesh.faceCentres();
841
842 label nWarpedFaces = 0;
843
844 forAll(pp, i)
845 {
846 const face& f = pp[i];
847 label faceI = pp.addressing()[i];
848 label patchI = patches.patchID()[faceI-mesh.nInternalFaces()];
849
850 // It is hard to calculate some length scale if not in relative
851 // mode so disable this check.
852
853 if (relativeSizes[patchI] && f.size() > 3)
854 {
855 label ownLevel = cellLevel[mesh.faceOwner()[faceI]];
856 scalar edgeLen = edge0Len/(1<<ownLevel);
857
858 // Normal distance to face centre plane
859 const point& fc = faceCentres[faceI];
860 const vector& fn = pp.faceNormals()[i];
861
862 scalarField vProj(f.size());
863
864 forAll(f, fp)
865 {
866 vector n = points[f[fp]] - fc;
867 vProj[fp] = (n & fn);
868 }
869
870 // Get normal 'span' of face
871 scalar minVal = min(vProj);
872 scalar maxVal = max(vProj);
873
874 if ((maxVal - minVal) > faceRatio * edgeLen)
875 {
876 if
877 (
878 unmarkExtrusion
879 (
880 pp.localFaces()[i],
881 patchDisp,
882 patchNLayers,
883 extrudeStatus
884 )
885 )
886 {
887 nWarpedFaces++;
888 }
889 }
890 }
891 }
892
893 Info<< "Set displacement to zero on "
894 << returnReduce(nWarpedFaces, sumOp<label>())
895 << " warped faces since layer would be > " << faceRatio
896 << " of the size of the bounding box." << endl;
897}
898
899
902//void Foam::snappyLayerDriver::handleMultiplePatchFaces
903//(
904// const indirectPrimitivePatch& pp,
905// pointField& patchDisp,
906// labelList& patchNLayers,
907// List<extrudeMode>& extrudeStatus
908//) const
909//{
910// const fvMesh& mesh = meshRefiner_.mesh();
911//
912// Info<< nl << "Handling cells with multiple patch faces ..." << nl;
913//
914// const labelListList& pointFaces = pp.pointFaces();
915//
916// // Cells that should not get an extrusion layer
917// cellSet multiPatchCells(mesh, "multiPatchCells", pp.size());
918//
919// // Detect points that use multiple faces on same cell.
920// forAll(pointFaces, patchPointi)
921// {
922// const labelList& pFaces = pointFaces[patchPointi];
923//
924// labelHashSet pointCells(pFaces.size());
925//
926// forAll(pFaces, i)
927// {
928// label celli = mesh.faceOwner()[pp.addressing()[pFaces[i]]];
929//
930// if (!pointCells.insert(celli))
931// {
932// // Second or more occurrence of cell so cell has two or more
933// // pp faces connected to this point.
934// multiPatchCells.insert(celli);
935// }
936// }
937// }
938//
939// label nMultiPatchCells = returnReduce
940// (
941// multiPatchCells.size(),
942// sumOp<label>()
943// );
944//
945// Info<< "Detected " << nMultiPatchCells
946// << " cells with multiple (connected) patch faces." << endl;
947//
948// label nChanged = 0;
949//
950// if (nMultiPatchCells > 0)
951// {
952// multiPatchCells.instance() = meshRefiner_.timeName();
953// Info<< "Writing " << nMultiPatchCells
954// << " cells with multiple (connected) patch faces to cellSet "
955// << multiPatchCells.objectPath() << endl;
956// multiPatchCells.write();
957//
958//
959// // Go through all points and remove extrusion on any cell in
960// // multiPatchCells
961// // (has to be done in separate loop since having one point on
962// // multipatches has to reset extrusion on all points of cell)
963//
964// forAll(pointFaces, patchPointi)
965// {
966// if (extrudeStatus[patchPointi] != NOEXTRUDE)
967// {
968// const labelList& pFaces = pointFaces[patchPointi];
969//
970// forAll(pFaces, i)
971// {
972// label celli =
973// mesh.faceOwner()[pp.addressing()[pFaces[i]]];
974//
975// if (multiPatchCells.found(celli))
976// {
977// if
978// (
979// unmarkExtrusion
980// (
981// patchPointi,
982// patchDisp,
983// patchNLayers,
984// extrudeStatus
985// )
986// )
987// {
988// nChanged++;
989// }
990// }
991// }
992// }
993// }
994//
995// reduce(nChanged, sumOp<label>());
996// }
997//
998// Info<< "Prevented extrusion on " << nChanged
999// << " points due to multiple patch faces." << nl << endl;
1000//}
1001
1002
1003void Foam::snappyLayerDriver::setNumLayers
1004(
1005 const labelList& patchToNLayers,
1006 const labelList& patchIDs,
1007 const indirectPrimitivePatch& pp,
1008 labelList& patchNLayers,
1009 List<extrudeMode>& extrudeStatus,
1010 label& nAddedCells
1011) const
1012{
1013 const fvMesh& mesh = meshRefiner_.mesh();
1014
1015 Info<< nl << "Handling points with inconsistent layer specification ..."
1016 << endl;
1017
1018 // Get for every point (really only necessary on patch external points)
1019 // the max and min of any patch faces using it.
1020 labelList maxLayers(patchNLayers.size(), labelMin);
1021 labelList minLayers(patchNLayers.size(), labelMax);
1022
1023 forAll(patchIDs, i)
1024 {
1025 label patchi = patchIDs[i];
1026
1027 const labelList& meshPoints = mesh.boundaryMesh()[patchi].meshPoints();
1028
1029 label wantedLayers = patchToNLayers[patchi];
1030
1031 forAll(meshPoints, patchPointi)
1032 {
1033 label ppPointi = pp.meshPointMap()[meshPoints[patchPointi]];
1034
1035 maxLayers[ppPointi] = max(wantedLayers, maxLayers[ppPointi]);
1036 minLayers[ppPointi] = min(wantedLayers, minLayers[ppPointi]);
1037 }
1038 }
1039
1041 (
1042 mesh,
1043 pp.meshPoints(),
1044 maxLayers,
1045 maxEqOp<label>(),
1046 labelMin // null value
1047 );
1049 (
1050 mesh,
1051 pp.meshPoints(),
1052 minLayers,
1053 minEqOp<label>(),
1054 labelMax // null value
1055 );
1056
1057 // Unmark any point with different min and max
1058 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1059
1060 //label nConflicts = 0;
1061
1062 forAll(maxLayers, i)
1063 {
1064 if (maxLayers[i] == labelMin || minLayers[i] == labelMax)
1065 {
1067 << "Patchpoint:" << i << " coord:" << pp.localPoints()[i]
1068 << " maxLayers:" << maxLayers
1069 << " minLayers:" << minLayers
1070 << abort(FatalError);
1071 }
1072 else if (maxLayers[i] == minLayers[i])
1073 {
1074 // Ok setting.
1075 patchNLayers[i] = maxLayers[i];
1076 }
1077 else
1078 {
1079 // Inconsistent num layers between patch faces using point
1080 //if
1081 //(
1082 // unmarkExtrusion
1083 // (
1084 // i,
1085 // patchDisp,
1086 // patchNLayers,
1087 // extrudeStatus
1088 // )
1089 //)
1090 //{
1091 // nConflicts++;
1092 //}
1093 patchNLayers[i] = maxLayers[i];
1094 }
1095 }
1096
1097
1098 // Calculate number of cells to create
1099 nAddedCells = 0;
1100 forAll(pp.localFaces(), facei)
1101 {
1102 const face& f = pp.localFaces()[facei];
1103
1104 // Get max of extrusion per point
1105 label nCells = 0;
1106 forAll(f, fp)
1107 {
1108 nCells = max(nCells, patchNLayers[f[fp]]);
1109 }
1110
1111 nAddedCells += nCells;
1112 }
1113 reduce(nAddedCells, sumOp<label>());
1114
1115 //reduce(nConflicts, sumOp<label>());
1116 //
1117 //Info<< "Set displacement to zero for " << nConflicts
1118 // << " points due to points being on multiple regions"
1119 // << " with inconsistent nLayers specification." << endl;
1120}
1121
1122
1123// Construct pointVectorField with correct boundary conditions for adding
1124// layers
1126Foam::snappyLayerDriver::makeLayerDisplacementField
1127(
1128 const pointMesh& pMesh,
1129 const labelList& numLayers
1130)
1131{
1132 // Construct displacement field.
1133 const pointBoundaryMesh& pointPatches = pMesh.boundary();
1134
1135 wordList patchFieldTypes
1136 (
1137 pointPatches.size(),
1138 slipPointPatchVectorField::typeName
1139 );
1140 wordList actualPatchTypes(patchFieldTypes.size());
1141 forAll(pointPatches, patchi)
1142 {
1143 actualPatchTypes[patchi] = pointPatches[patchi].type();
1144 }
1145
1146 forAll(numLayers, patchi)
1147 {
1148 // 0 layers: do not allow slip so fixedValue 0
1149 // >0 layers: fixedValue which gets adapted
1150 if (numLayers[patchi] == 0)
1151 {
1152 patchFieldTypes[patchi] =
1153 zeroFixedValuePointPatchVectorField::typeName;
1154 }
1155 else if (numLayers[patchi] > 0)
1156 {
1157 patchFieldTypes[patchi] = fixedValuePointPatchVectorField::typeName;
1158 }
1159 }
1160
1161 forAll(pointPatches, patchi)
1162 {
1163 if (isA<processorPointPatch>(pointPatches[patchi]))
1164 {
1165 patchFieldTypes[patchi] = calculatedPointPatchVectorField::typeName;
1166 }
1167 else if (isA<cyclicPointPatch>(pointPatches[patchi]))
1168 {
1169 patchFieldTypes[patchi] = cyclicSlipPointPatchVectorField::typeName;
1170 }
1171 }
1172
1173
1174 const polyMesh& mesh = pMesh();
1175
1176 // Note: time().timeName() instead of meshRefinement::timeName() since
1177 // postprocessable field.
1178
1180 (
1181 IOobject
1182 (
1183 "pointDisplacement",
1184 mesh.time().timeName(),
1185 mesh,
1188 ),
1189 pMesh,
1191 patchFieldTypes,
1192 actualPatchTypes
1193 );
1194}
1195
1196
1197void Foam::snappyLayerDriver::growNoExtrusion
1198(
1199 const indirectPrimitivePatch& pp,
1200 pointField& patchDisp,
1201 labelList& patchNLayers,
1202 List<extrudeMode>& extrudeStatus
1203) const
1204{
1205 Info<< nl << "Growing non-extrusion points by one layer ..." << endl;
1206
1207 List<extrudeMode> grownExtrudeStatus(extrudeStatus);
1208
1209 const faceList& localFaces = pp.localFaces();
1210
1211 label nGrown = 0;
1212
1213 forAll(localFaces, facei)
1214 {
1215 const face& f = localFaces[facei];
1216
1217 bool hasSqueeze = false;
1218 forAll(f, fp)
1219 {
1220 if (extrudeStatus[f[fp]] == NOEXTRUDE)
1221 {
1222 hasSqueeze = true;
1223 break;
1224 }
1225 }
1226
1227 if (hasSqueeze)
1228 {
1229 // Squeeze all points of face
1230 forAll(f, fp)
1231 {
1232 if
1233 (
1234 extrudeStatus[f[fp]] == EXTRUDE
1235 && grownExtrudeStatus[f[fp]] != NOEXTRUDE
1236 )
1237 {
1238 grownExtrudeStatus[f[fp]] = NOEXTRUDE;
1239 nGrown++;
1240 }
1241 }
1242 }
1243 }
1244
1245 extrudeStatus.transfer(grownExtrudeStatus);
1246
1247
1248 // Synchronise since might get called multiple times.
1249 // Use the fact that NOEXTRUDE is the minimum value.
1250 {
1251 labelList status(extrudeStatus.size());
1252 forAll(status, i)
1253 {
1254 status[i] = extrudeStatus[i];
1255 }
1257 (
1258 meshRefiner_.mesh(),
1259 pp.meshPoints(),
1260 status,
1261 minEqOp<label>(),
1262 labelMax // null value
1263 );
1264 forAll(status, i)
1265 {
1266 extrudeStatus[i] = extrudeMode(status[i]);
1267 }
1268 }
1269
1270
1271 forAll(extrudeStatus, patchPointi)
1272 {
1273 if (extrudeStatus[patchPointi] == NOEXTRUDE)
1274 {
1275 patchDisp[patchPointi] = Zero;
1276 patchNLayers[patchPointi] = 0;
1277 }
1278 }
1279
1280 reduce(nGrown, sumOp<label>());
1281
1282 Info<< "Set displacement to zero for an additional " << nGrown
1283 << " points." << endl;
1284}
1285
1286
1287void Foam::snappyLayerDriver::determineSidePatches
1288(
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.found())
1382 {
1383 edgePatchID[i] = fnd.val();
1384 }
1385 }
1386
1387 mesh.clearOut();
1388 const_cast<polyBoundaryMesh&>(mesh.boundaryMesh()).updateMesh();
1389 }
1390}
1391
1392
1393void 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
1547 labelList spec(pp.nPoints(), layerParameters::FIRST_AND_TOTAL);
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.
1772void Foam::snappyLayerDriver::syncPatchDisplacement
1773(
1774 const indirectPrimitivePatch& pp,
1775 const scalarField& minThickness,
1776 pointField& patchDisp,
1777 labelList& patchNLayers,
1778 List<extrudeMode>& extrudeStatus
1779) const
1780{
1781 const fvMesh& mesh = meshRefiner_.mesh();
1782 const labelList& meshPoints = pp.meshPoints();
1783
1784 label nChangedTotal = 0;
1785
1786 while (true)
1787 {
1788 label nChanged = 0;
1789
1790 // Sync displacement (by taking min)
1792 (
1793 mesh,
1794 meshPoints,
1795 patchDisp,
1796 minMagSqrEqOp<vector>(),
1797 point::rootMax // null value
1798 );
1799
1800 // Unmark if displacement too small
1801 forAll(patchDisp, i)
1802 {
1803 if (mag(patchDisp[i]) < minThickness[i])
1804 {
1805 if
1806 (
1807 unmarkExtrusion
1808 (
1809 i,
1810 patchDisp,
1811 patchNLayers,
1812 extrudeStatus
1813 )
1814 )
1815 {
1816 nChanged++;
1817 }
1818 }
1819 }
1820
1821 labelList syncPatchNLayers(patchNLayers);
1822
1824 (
1825 mesh,
1826 meshPoints,
1827 syncPatchNLayers,
1828 minEqOp<label>(),
1829 labelMax // null value
1830 );
1831
1832 // Reset if differs
1833 // 1. take max
1834 forAll(syncPatchNLayers, i)
1835 {
1836 if (syncPatchNLayers[i] != patchNLayers[i])
1837 {
1838 if
1839 (
1840 unmarkExtrusion
1841 (
1842 i,
1843 patchDisp,
1844 patchNLayers,
1845 extrudeStatus
1846 )
1847 )
1848 {
1849 nChanged++;
1850 }
1851 }
1852 }
1853
1855 (
1856 mesh,
1857 meshPoints,
1858 syncPatchNLayers,
1859 maxEqOp<label>(),
1860 labelMin // null value
1861 );
1862
1863 // Reset if differs
1864 // 2. take min
1865 forAll(syncPatchNLayers, i)
1866 {
1867 if (syncPatchNLayers[i] != patchNLayers[i])
1868 {
1869 if
1870 (
1871 unmarkExtrusion
1872 (
1873 i,
1874 patchDisp,
1875 patchNLayers,
1876 extrudeStatus
1877 )
1878 )
1879 {
1880 nChanged++;
1881 }
1882 }
1883 }
1884 nChangedTotal += nChanged;
1885
1886 if (!returnReduce(nChanged, sumOp<label>()))
1887 {
1888 break;
1889 }
1890 }
1891
1892 //Info<< "Prevented extrusion on "
1893 // << returnReduce(nChangedTotal, sumOp<label>())
1894 // << " coupled patch points during syncPatchDisplacement." << endl;
1895}
1896
1897
1898// Calculate displacement vector for all patch points. Uses pointNormal.
1899// Checks that displaced patch point would be visible from all centres
1900// of the faces using it.
1901// extrudeStatus is both input and output and gives the status of each
1902// patch point.
1903void Foam::snappyLayerDriver::getPatchDisplacement
1904(
1905 const indirectPrimitivePatch& pp,
1906 const scalarField& thickness,
1907 const scalarField& minThickness,
1908 const scalarField& expansionRatio,
1909
1910 pointField& patchDisp,
1911 labelList& patchNLayers,
1912 List<extrudeMode>& extrudeStatus
1913) const
1914{
1915 Info<< nl << "Determining displacement for added points"
1916 << " according to pointNormal ..." << endl;
1917
1918 const fvMesh& mesh = meshRefiner_.mesh();
1919 const vectorField& faceNormals = pp.faceNormals();
1920 const labelListList& pointFaces = pp.pointFaces();
1921 const pointField& localPoints = pp.localPoints();
1922
1923 // Determine pointNormal
1924 // ~~~~~~~~~~~~~~~~~~~~~
1925
1926 pointField pointNormals(PatchTools::pointNormals(mesh, pp));
1927
1928
1929 // Determine local length scale on patch
1930 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1931
1932 // Start off from same thickness everywhere (except where no extrusion)
1933 patchDisp = thickness*pointNormals;
1934
1935
1936 label nNoVisNormal = 0;
1937 label nExtrudeRemove = 0;
1938
1939
1941// {
1942// OBJstream twoStr
1943// (
1944// mesh.time().path()
1945// / "twoFacePoints_"
1946// + meshRefiner_.timeName()
1947// + ".obj"
1948// );
1949// OBJstream multiStr
1950// (
1951// mesh.time().path()
1952// / "multiFacePoints_"
1953// + meshRefiner_.timeName()
1954// + ".obj"
1955// );
1956// Pout<< "Writing points inbetween two faces on same cell to "
1957// << twoStr.name() << endl;
1958// Pout<< "Writing points inbetween three or more faces on same cell to "
1959// << multiStr.name() << endl;
1960// // Check whether inbetween patch faces on same cell
1961// Map<labelList> cellToFaces;
1962// forAll(pointNormals, patchPointi)
1963// {
1964// const labelList& pFaces = pointFaces[patchPointi];
1965//
1966// cellToFaces.clear();
1967// forAll(pFaces, pFacei)
1968// {
1969// const label patchFacei = pFaces[pFacei];
1970// const label meshFacei = pp.addressing()[patchFacei];
1971// const label celli = mesh.faceOwner()[meshFacei];
1972// Map<labelList>::iterator faceFnd = cellToFaces.find(celli);
1973// if (faceFnd.found())
1974// {
1975// labelList& faces = faceFnd();
1976// faces.appendUniq(patchFacei);
1977// }
1978// else
1979// {
1980// cellToFaces.insert(celli, labelList(one{}, patchFacei));
1981// }
1982// }
1983//
1984// forAllConstIters(cellToFaces, iter)
1985// {
1986// if (iter().size() == 2)
1987// {
1988// twoStr.write(pp.localPoints()[patchPointi]);
1989// }
1990// else if (iter().size() > 2)
1991// {
1992// multiStr.write(pp.localPoints()[patchPointi]);
1993//
1994// const scalar ratio =
1995// layerParameters::finalLayerThicknessRatio
1996// (
1997// patchNLayers[patchPointi],
1998// expansionRatio[patchPointi]
1999// );
2000// // Get thickness of cell next to bulk
2001// const vector finalDisp
2002// (
2003// ratio*patchDisp[patchPointi]
2004// );
2005//
2006// //Pout<< "** point:" << pp.localPoints()[patchPointi]
2007// // << " on cell:" << iter.key()
2008// // << " faces:" << iter()
2009// // << " displacement was:" << patchDisp[patchPointi]
2010// // << " ratio:" << ratio
2011// // << " finalDispl:" << finalDisp;
2012//
2013// // Half this thickness
2014// patchDisp[patchPointi] -= 0.8*finalDisp;
2015//
2016// //Pout<< " new displacement:"
2017// // << patchDisp[patchPointi] << endl;
2018// }
2019// }
2020// }
2021//
2022// Pout<< "Written " << multiStr.nVertices()
2023// << " points inbetween three or more faces on same cell to "
2024// << multiStr.name() << endl;
2025// }
2027
2028
2029 // Check if no extrude possible.
2030 forAll(pointNormals, patchPointi)
2031 {
2032 label meshPointi = pp.meshPoints()[patchPointi];
2033
2034 if (extrudeStatus[patchPointi] == NOEXTRUDE)
2035 {
2036 // Do not use unmarkExtrusion; forcibly set to zero extrusion.
2037 patchNLayers[patchPointi] = 0;
2038 patchDisp[patchPointi] = Zero;
2039 }
2040 else
2041 {
2042 // Get normal
2043 const vector& n = pointNormals[patchPointi];
2044
2045 if (!meshTools::visNormal(n, faceNormals, pointFaces[patchPointi]))
2046 {
2048 {
2049 Pout<< "No valid normal for point " << meshPointi
2050 << ' ' << pp.points()[meshPointi]
2051 << "; setting displacement to "
2052 << patchDisp[patchPointi]
2053 << endl;
2054 }
2055
2056 extrudeStatus[patchPointi] = EXTRUDEREMOVE;
2057 nNoVisNormal++;
2058 }
2059 }
2060 }
2061
2062 // At illegal points make displacement average of new neighbour positions
2063 forAll(extrudeStatus, patchPointi)
2064 {
2065 if (extrudeStatus[patchPointi] == EXTRUDEREMOVE)
2066 {
2067 point avg(Zero);
2068 label nPoints = 0;
2069
2070 const labelList& pEdges = pp.pointEdges()[patchPointi];
2071
2072 forAll(pEdges, i)
2073 {
2074 label edgei = pEdges[i];
2075
2076 label otherPointi = pp.edges()[edgei].otherVertex(patchPointi);
2077
2078 if (extrudeStatus[otherPointi] != NOEXTRUDE)
2079 {
2080 avg += localPoints[otherPointi] + patchDisp[otherPointi];
2081 nPoints++;
2082 }
2083 }
2084
2085 if (nPoints > 0)
2086 {
2088 {
2089 Pout<< "Displacement at illegal point "
2090 << localPoints[patchPointi]
2091 << " set to "
2092 << (avg / nPoints - localPoints[patchPointi])
2093 << endl;
2094 }
2095
2096 patchDisp[patchPointi] =
2097 avg / nPoints
2098 - localPoints[patchPointi];
2099
2100 nExtrudeRemove++;
2101 }
2102 else
2103 {
2104 // All surrounding points are not extruded. Leave patchDisp
2105 // intact.
2106 }
2107 }
2108 }
2109
2110 Info<< "Detected " << returnReduce(nNoVisNormal, sumOp<label>())
2111 << " points with point normal pointing through faces." << nl
2112 << "Reset displacement at "
2113 << returnReduce(nExtrudeRemove, sumOp<label>())
2114 << " points to average of surrounding points." << endl;
2115
2116 // Make sure displacement is equal on both sides of coupled patches.
2117 syncPatchDisplacement
2118 (
2119 pp,
2120 minThickness,
2121 patchDisp,
2122 patchNLayers,
2123 extrudeStatus
2124 );
2125
2126 Info<< endl;
2127}
2128
2129
2130bool Foam::snappyLayerDriver::sameEdgeNeighbour
2131(
2132 const labelListList& globalEdgeFaces,
2133 const label myGlobalFacei,
2134 const label nbrGlobFacei,
2135 const label edgei
2136) const
2137{
2138 const labelList& eFaces = globalEdgeFaces[edgei];
2139 if (eFaces.size() == 2)
2140 {
2141 return edge(myGlobalFacei, nbrGlobFacei) == edge(eFaces[0], eFaces[1]);
2142 }
2143
2144 return false;
2145}
2146
2147
2148void Foam::snappyLayerDriver::getVertexString
2149(
2150 const indirectPrimitivePatch& pp,
2151 const labelListList& globalEdgeFaces,
2152 const label facei,
2153 const label edgei,
2154 const label myGlobFacei,
2155 const label nbrGlobFacei,
2156 DynamicList<label>& vertices
2157) const
2158{
2159 const labelList& fEdges = pp.faceEdges()[facei];
2160 label fp = fEdges.find(edgei);
2161
2162 if (fp == -1)
2163 {
2165 << "problem." << abort(FatalError);
2166 }
2167
2168 // Search back
2169 label startFp = fp;
2170
2171 forAll(fEdges, i)
2172 {
2173 label prevFp = fEdges.rcIndex(startFp);
2174 if
2175 (
2176 !sameEdgeNeighbour
2177 (
2178 globalEdgeFaces,
2179 myGlobFacei,
2180 nbrGlobFacei,
2181 fEdges[prevFp]
2182 )
2183 )
2184 {
2185 break;
2186 }
2187 startFp = prevFp;
2188 }
2189
2190 label endFp = fp;
2191 forAll(fEdges, i)
2192 {
2193 label nextFp = fEdges.fcIndex(endFp);
2194 if
2195 (
2196 !sameEdgeNeighbour
2197 (
2198 globalEdgeFaces,
2199 myGlobFacei,
2200 nbrGlobFacei,
2201 fEdges[nextFp]
2202 )
2203 )
2204 {
2205 break;
2206 }
2207 endFp = nextFp;
2208 }
2209
2210 const face& f = pp.localFaces()[facei];
2211 vertices.clear();
2212 fp = startFp;
2213 while (fp != endFp)
2214 {
2215 vertices.append(f[fp]);
2216 fp = f.fcIndex(fp);
2217 }
2218 vertices.append(f[fp]);
2219 fp = f.fcIndex(fp);
2220 vertices.append(f[fp]);
2221}
2222
2223
2224// Truncates displacement
2225// - for all patchFaces in the faceset displacement gets set to zero
2226// - all displacement < minThickness gets set to zero
2227Foam::label Foam::snappyLayerDriver::truncateDisplacement
2228(
2229 const globalIndex& globalFaces,
2230 const labelListList& edgeGlobalFaces,
2231 const indirectPrimitivePatch& pp,
2232 const scalarField& minThickness,
2233 const faceSet& illegalPatchFaces,
2234 pointField& patchDisp,
2235 labelList& patchNLayers,
2236 List<extrudeMode>& extrudeStatus
2237) const
2238{
2239 const fvMesh& mesh = meshRefiner_.mesh();
2240
2241 label nChanged = 0;
2242
2243 const Map<label>& meshPointMap = pp.meshPointMap();
2244
2245 for (const label facei : illegalPatchFaces)
2246 {
2247 if (mesh.isInternalFace(facei))
2248 {
2250 << "Faceset " << illegalPatchFaces.name()
2251 << " contains internal face " << facei << nl
2252 << "It should only contain patch faces" << abort(FatalError);
2253 }
2254
2255 const face& f = mesh.faces()[facei];
2256
2257
2258 forAll(f, fp)
2259 {
2260 const auto fnd = meshPointMap.cfind(f[fp]);
2261 if (fnd.found())
2262 {
2263 const label patchPointi = fnd.val();
2264
2265 if (extrudeStatus[patchPointi] != NOEXTRUDE)
2266 {
2267 unmarkExtrusion
2268 (
2269 patchPointi,
2270 patchDisp,
2271 patchNLayers,
2272 extrudeStatus
2273 );
2274 nChanged++;
2275 }
2276 }
2277 }
2278 }
2279
2280 forAll(patchDisp, patchPointi)
2281 {
2282 if (mag(patchDisp[patchPointi]) < minThickness[patchPointi])
2283 {
2284 if
2285 (
2286 unmarkExtrusion
2287 (
2288 patchPointi,
2289 patchDisp,
2290 patchNLayers,
2291 extrudeStatus
2292 )
2293 )
2294 {
2295 nChanged++;
2296 }
2297 }
2298 else if (extrudeStatus[patchPointi] == NOEXTRUDE)
2299 {
2300 // Make sure displacement is 0. Should already be so but ...
2301 patchDisp[patchPointi] = Zero;
2302 patchNLayers[patchPointi] = 0;
2303 }
2304 }
2305
2306
2307 const faceList& localFaces = pp.localFaces();
2308
2309 while (true)
2310 {
2311 syncPatchDisplacement
2312 (
2313 pp,
2314 minThickness,
2315 patchDisp,
2316 patchNLayers,
2317 extrudeStatus
2318 );
2319
2320
2321 // Pinch
2322 // ~~~~~
2323
2324 // Make sure that a face doesn't have two non-consecutive areas
2325 // not extruded (e.g. quad where vertex 0 and 2 are not extruded
2326 // but 1 and 3 are) since this gives topological errors.
2327
2328 label nPinched = 0;
2329
2330 forAll(localFaces, i)
2331 {
2332 const face& localF = localFaces[i];
2333
2334 // Count number of transitions from unsnapped to snapped.
2335 label nTrans = 0;
2336
2337 extrudeMode prevMode = extrudeStatus[localF.prevLabel(0)];
2338
2339 forAll(localF, fp)
2340 {
2341 extrudeMode fpMode = extrudeStatus[localF[fp]];
2342
2343 if (prevMode == NOEXTRUDE && fpMode != NOEXTRUDE)
2344 {
2345 nTrans++;
2346 }
2347 prevMode = fpMode;
2348 }
2349
2350 if (nTrans > 1)
2351 {
2352 // Multiple pinches. Reset whole face as unextruded.
2353 if
2354 (
2355 unmarkExtrusion
2356 (
2357 localF,
2358 patchDisp,
2359 patchNLayers,
2360 extrudeStatus
2361 )
2362 )
2363 {
2364 nPinched++;
2365 nChanged++;
2366 }
2367 }
2368 }
2369
2370 reduce(nPinched, sumOp<label>());
2371
2372 Info<< "truncateDisplacement : Unextruded " << nPinched
2373 << " faces due to non-consecutive vertices being extruded." << endl;
2374
2375
2376 // Butterfly
2377 // ~~~~~~~~~
2378
2379 // Make sure that a string of edges becomes a single face so
2380 // not a butterfly. Occasionally an 'edge' will have a single dangling
2381 // vertex due to face combining. These get extruded as a single face
2382 // (with a dangling vertex) so make sure this extrusion forms a single
2383 // shape.
2384 // - continuous i.e. no butterfly:
2385 // + +
2386 // |\ /|
2387 // | \ / |
2388 // +--+--+
2389 // - extrudes from all but the endpoints i.e. no partial
2390 // extrude
2391 // +
2392 // /|
2393 // / |
2394 // +--+--+
2395 // The common error topology is a pinch somewhere in the middle
2396 label nButterFly = 0;
2397 {
2398 DynamicList<label> stringedVerts;
2399 forAll(pp.edges(), edgei)
2400 {
2401 const labelList& globFaces = edgeGlobalFaces[edgei];
2402
2403 if (globFaces.size() == 2)
2404 {
2405 label myFacei = pp.edgeFaces()[edgei][0];
2406 label myGlobalFacei = globalFaces.toGlobal
2407 (
2408 pp.addressing()[myFacei]
2409 );
2410 label nbrGlobalFacei =
2411 (
2412 globFaces[0] != myGlobalFacei
2413 ? globFaces[0]
2414 : globFaces[1]
2415 );
2416 getVertexString
2417 (
2418 pp,
2419 edgeGlobalFaces,
2420 myFacei,
2421 edgei,
2422 myGlobalFacei,
2423 nbrGlobalFacei,
2424 stringedVerts
2425 );
2426
2427 if
2428 (
2429 extrudeStatus[stringedVerts[0]] != NOEXTRUDE
2430 || extrudeStatus[stringedVerts.last()] != NOEXTRUDE
2431 )
2432 {
2433 // Any pinch in the middle
2434 bool pinch = false;
2435 for (label i = 1; i < stringedVerts.size()-1; i++)
2436 {
2437 if (extrudeStatus[stringedVerts[i]] == NOEXTRUDE)
2438 {
2439 pinch = true;
2440 break;
2441 }
2442 }
2443 if (pinch)
2444 {
2445 forAll(stringedVerts, i)
2446 {
2447 if
2448 (
2449 unmarkExtrusion
2450 (
2451 stringedVerts[i],
2452 patchDisp,
2453 patchNLayers,
2454 extrudeStatus
2455 )
2456 )
2457 {
2458 nButterFly++;
2459 nChanged++;
2460 }
2461 }
2462 }
2463 }
2464 }
2465 }
2466 }
2467
2468 reduce(nButterFly, sumOp<label>());
2469
2470 Info<< "truncateDisplacement : Unextruded " << nButterFly
2471 << " faces due to stringed edges with inconsistent extrusion."
2472 << endl;
2473
2474
2475
2476 // Consistent number of layers
2477 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
2478
2479 // Make sure that a face has consistent number of layers for all
2480 // its vertices.
2481
2482 label nDiffering = 0;
2483
2484 //forAll(localFaces, i)
2485 //{
2486 // const face& localF = localFaces[i];
2487 //
2488 // label numLayers = -1;
2489 //
2490 // forAll(localF, fp)
2491 // {
2492 // if (patchNLayers[localF[fp]] > 0)
2493 // {
2494 // if (numLayers == -1)
2495 // {
2496 // numLayers = patchNLayers[localF[fp]];
2497 // }
2498 // else if (numLayers != patchNLayers[localF[fp]])
2499 // {
2500 // // Differing number of layers
2501 // if
2502 // (
2503 // unmarkExtrusion
2504 // (
2505 // localF,
2506 // patchDisp,
2507 // patchNLayers,
2508 // extrudeStatus
2509 // )
2510 // )
2511 // {
2512 // nDiffering++;
2513 // nChanged++;
2514 // }
2515 // break;
2516 // }
2517 // }
2518 // }
2519 //}
2520 //
2521 //reduce(nDiffering, sumOp<label>());
2522 //
2523 //Info<< "truncateDisplacement : Unextruded " << nDiffering
2524 // << " faces due to having differing number of layers." << endl;
2525
2526 if (nPinched+nButterFly+nDiffering == 0)
2527 {
2528 break;
2529 }
2530 }
2531
2532 return nChanged;
2533}
2534
2535
2536// Setup layer information (at points and faces) to modify mesh topology in
2537// regions where layer mesh terminates.
2538void Foam::snappyLayerDriver::setupLayerInfoTruncation
2539(
2540 const indirectPrimitivePatch& pp,
2541 const labelList& patchNLayers,
2542 const List<extrudeMode>& extrudeStatus,
2543 const label nBufferCellsNoExtrude,
2544 labelList& nPatchPointLayers,
2545 labelList& nPatchFaceLayers
2546) const
2547{
2548 Info<< nl << "Setting up information for layer truncation ..." << endl;
2549
2550 const fvMesh& mesh = meshRefiner_.mesh();
2551
2552 if (nBufferCellsNoExtrude < 0)
2553 {
2554 Info<< nl << "Performing no layer truncation."
2555 << " nBufferCellsNoExtrude set to less than 0 ..." << endl;
2556
2557 // Face layers if any point gets extruded
2558 forAll(pp.localFaces(), patchFacei)
2559 {
2560 const face& f = pp.localFaces()[patchFacei];
2561
2562 forAll(f, fp)
2563 {
2564 const label nPointLayers = patchNLayers[f[fp]];
2565 if (nPointLayers > 0)
2566 {
2567 if (nPatchFaceLayers[patchFacei] == -1)
2568 {
2569 nPatchFaceLayers[patchFacei] = nPointLayers;
2570 }
2571 else
2572 {
2573 nPatchFaceLayers[patchFacei] = min
2574 (
2575 nPatchFaceLayers[patchFacei],
2576 nPointLayers
2577 );
2578 }
2579 }
2580 }
2581 }
2582 nPatchPointLayers = patchNLayers;
2583
2584 // Set any unset patch face layers
2585 forAll(nPatchFaceLayers, patchFacei)
2586 {
2587 if (nPatchFaceLayers[patchFacei] == -1)
2588 {
2589 nPatchFaceLayers[patchFacei] = 0;
2590 }
2591 }
2592 }
2593 else
2594 {
2595 // Determine max point layers per face.
2596 labelList maxLevel(pp.size(), Zero);
2597
2598 forAll(pp.localFaces(), patchFacei)
2599 {
2600 const face& f = pp.localFaces()[patchFacei];
2601
2602 // find patch faces where layer terminates (i.e contains extrude
2603 // and noextrude points).
2604
2605 bool noExtrude = false;
2606 label mLevel = 0;
2607
2608 forAll(f, fp)
2609 {
2610 if (extrudeStatus[f[fp]] == NOEXTRUDE)
2611 {
2612 noExtrude = true;
2613 }
2614 mLevel = max(mLevel, patchNLayers[f[fp]]);
2615 }
2616
2617 if (mLevel > 0)
2618 {
2619 // So one of the points is extruded. Check if all are extruded
2620 // or is a mix.
2621
2622 if (noExtrude)
2623 {
2624 nPatchFaceLayers[patchFacei] = 1;
2625 maxLevel[patchFacei] = mLevel;
2626 }
2627 else
2628 {
2629 maxLevel[patchFacei] = mLevel;
2630 }
2631 }
2632 }
2633
2634 // We have the seed faces (faces with nPatchFaceLayers != maxLevel)
2635 // Now do a meshwave across the patch where we pick up neighbours
2636 // of seed faces.
2637 // Note: quite inefficient. Could probably be coded better.
2638
2639 const labelListList& pointFaces = pp.pointFaces();
2640
2641 label nLevels = gMax(patchNLayers);
2642
2643 // flag neighbouring patch faces with number of layers to grow
2644 for (label ilevel = 1; ilevel < nLevels; ilevel++)
2645 {
2646 label nBuffer;
2647
2648 if (ilevel == 1)
2649 {
2650 nBuffer = nBufferCellsNoExtrude - 1;
2651 }
2652 else
2653 {
2654 nBuffer = nBufferCellsNoExtrude;
2655 }
2656
2657 for (label ibuffer = 0; ibuffer < nBuffer + 1; ibuffer++)
2658 {
2659 labelList tempCounter(nPatchFaceLayers);
2660
2661 boolList foundNeighbour(pp.nPoints(), false);
2662
2663 forAll(pp.meshPoints(), patchPointi)
2664 {
2665 forAll(pointFaces[patchPointi], pointFacei)
2666 {
2667 label facei = pointFaces[patchPointi][pointFacei];
2668
2669 if
2670 (
2671 nPatchFaceLayers[facei] != -1
2672 && maxLevel[facei] > 0
2673 )
2674 {
2675 foundNeighbour[patchPointi] = true;
2676 break;
2677 }
2678 }
2679 }
2680
2682 (
2683 mesh,
2684 pp.meshPoints(),
2685 foundNeighbour,
2686 orEqOp<bool>(),
2687 false // null value
2688 );
2689
2690 forAll(pp.meshPoints(), patchPointi)
2691 {
2692 if (foundNeighbour[patchPointi])
2693 {
2694 forAll(pointFaces[patchPointi], pointFacei)
2695 {
2696 label facei = pointFaces[patchPointi][pointFacei];
2697 if
2698 (
2699 nPatchFaceLayers[facei] == -1
2700 && maxLevel[facei] > 0
2701 && ilevel < maxLevel[facei]
2702 )
2703 {
2704 tempCounter[facei] = ilevel;
2705 }
2706 }
2707 }
2708 }
2709 nPatchFaceLayers = tempCounter;
2710 }
2711 }
2712
2713 forAll(pp.localFaces(), patchFacei)
2714 {
2715 if (nPatchFaceLayers[patchFacei] == -1)
2716 {
2717 nPatchFaceLayers[patchFacei] = maxLevel[patchFacei];
2718 }
2719 }
2720
2721 forAll(pp.meshPoints(), patchPointi)
2722 {
2723 if (extrudeStatus[patchPointi] != NOEXTRUDE)
2724 {
2725 forAll(pointFaces[patchPointi], pointFacei)
2726 {
2727 label face = pointFaces[patchPointi][pointFacei];
2728 nPatchPointLayers[patchPointi] = max
2729 (
2730 nPatchPointLayers[patchPointi],
2731 nPatchFaceLayers[face]
2732 );
2733 }
2734 }
2735 else
2736 {
2737 nPatchPointLayers[patchPointi] = 0;
2738 }
2739 }
2741 (
2742 mesh,
2743 pp.meshPoints(),
2744 nPatchPointLayers,
2745 maxEqOp<label>(),
2746 label(0) // null value
2747 );
2748 }
2749}
2750
2751
2752// Does any of the cells use a face from faces?
2753bool Foam::snappyLayerDriver::cellsUseFace
2754(
2755 const polyMesh& mesh,
2756 const labelList& cellLabels,
2757 const labelHashSet& faces
2758)
2759{
2760 forAll(cellLabels, i)
2761 {
2762 const cell& cFaces = mesh.cells()[cellLabels[i]];
2763
2764 forAll(cFaces, cFacei)
2765 {
2766 if (faces.found(cFaces[cFacei]))
2767 {
2768 return true;
2769 }
2770 }
2771 }
2772
2773 return false;
2774}
2775
2776
2777// Checks the newly added cells and locally unmarks points so they
2778// will not get extruded next time round. Returns global number of unmarked
2779// points (0 if all was fine)
2780Foam::label Foam::snappyLayerDriver::checkAndUnmark
2781(
2782 const addPatchCellLayer& addLayer,
2783 const dictionary& meshQualityDict,
2784 const bool additionalReporting,
2785 const List<labelPair>& baffles,
2786 const indirectPrimitivePatch& pp,
2787 const fvMesh& newMesh,
2788
2789 pointField& patchDisp,
2790 labelList& patchNLayers,
2791 List<extrudeMode>& extrudeStatus
2792)
2793{
2794 // Check the resulting mesh for errors
2795 Info<< nl << "Checking mesh with layer ..." << endl;
2796 faceSet wrongFaces(newMesh, "wrongFaces", newMesh.nFaces()/1000);
2798 (
2799 false,
2800 newMesh,
2801 meshQualityDict,
2802 identity(newMesh.nFaces()),
2803 baffles,
2804 wrongFaces,
2805 false // dryRun_
2806 );
2807 Info<< "Detected " << returnReduce(wrongFaces.size(), sumOp<label>())
2808 << " illegal faces"
2809 << " (concave, zero area or negative cell pyramid volume)"
2810 << endl;
2811
2812 // Undo local extrusion if
2813 // - any of the added cells in error
2814
2815 label nChanged = 0;
2816
2817 // Get all cells in the layer.
2818 labelListList addedCells
2819 (
2821 (
2822 newMesh,
2823 addLayer.layerFaces()
2824 )
2825 );
2826
2827 // Check if any of the faces in error uses any face of an added cell
2828 // - if additionalReporting print the few remaining areas for ease of
2829 // finding out where the problems are.
2830
2831 const label nReportMax = 10;
2832 DynamicField<point> disabledFaceCentres(nReportMax);
2833
2834 forAll(addedCells, oldPatchFacei)
2835 {
2836 // Get the cells (in newMesh labels) per old patch face (in mesh
2837 // labels)
2838 const labelList& fCells = addedCells[oldPatchFacei];
2839
2840 if (cellsUseFace(newMesh, fCells, wrongFaces))
2841 {
2842 // Unmark points on old mesh
2843 if
2844 (
2845 unmarkExtrusion
2846 (
2847 pp.localFaces()[oldPatchFacei],
2848 patchDisp,
2849 patchNLayers,
2850 extrudeStatus
2851 )
2852 )
2853 {
2854 if (additionalReporting && (nChanged < nReportMax))
2855 {
2856 disabledFaceCentres.append
2857 (
2858 pp.faceCentres()[oldPatchFacei]
2859 );
2860 }
2861
2862 nChanged++;
2863 }
2864 }
2865 }
2866
2867
2868 label nChangedTotal = returnReduce(nChanged, sumOp<label>());
2869
2870 if (additionalReporting)
2871 {
2872 // Limit the number of points to be printed so that
2873 // not too many points are reported when running in parallel
2874 // Not accurate, i.e. not always nReportMax points are written,
2875 // but this estimation avoid some communication here.
2876 // The important thing, however, is that when only a few faces
2877 // are disabled, their coordinates are printed, and this should be
2878 // the case
2879 label nReportLocal = nChanged;
2880 if (nChangedTotal > nReportMax)
2881 {
2882 nReportLocal = min
2883 (
2884 max(nChangedTotal / Pstream::nProcs(), 1),
2885 min
2886 (
2887 nChanged,
2888 max(nReportMax / Pstream::nProcs(), 1)
2889 )
2890 );
2891 }
2892
2893 if (nReportLocal)
2894 {
2895 Pout<< "Checked mesh with layers. Disabled extrusion at " << endl;
2896 for (label i=0; i < nReportLocal; i++)
2897 {
2898 Pout<< " " << disabledFaceCentres[i] << endl;
2899 }
2900 }
2901
2902 label nReportTotal = returnReduce(nReportLocal, sumOp<label>());
2903
2904 if (nReportTotal < nChangedTotal)
2905 {
2906 Info<< "Suppressed disabled extrusion message for other "
2907 << nChangedTotal - nReportTotal << " faces." << endl;
2908 }
2909 }
2910
2911 return nChangedTotal;
2912}
2913
2914
2915//- Count global number of extruded faces
2916Foam::label Foam::snappyLayerDriver::countExtrusion
2917(
2918 const indirectPrimitivePatch& pp,
2919 const List<extrudeMode>& extrudeStatus
2920)
2921{
2922 // Count number of extruded patch faces
2923 label nExtruded = 0;
2924 {
2925 const faceList& localFaces = pp.localFaces();
2926
2927 forAll(localFaces, i)
2928 {
2929 const face& localFace = localFaces[i];
2930
2931 forAll(localFace, fp)
2932 {
2933 if (extrudeStatus[localFace[fp]] != NOEXTRUDE)
2934 {
2935 nExtruded++;
2936 break;
2937 }
2938 }
2939 }
2940 }
2941
2942 return returnReduce(nExtruded, sumOp<label>());
2943}
2944
2945
2946Foam::List<Foam::labelPair> Foam::snappyLayerDriver::getBafflesOnAddedMesh
2947(
2948 const polyMesh& mesh,
2949 const labelList& newToOldFaces,
2950 const List<labelPair>& baffles
2951)
2952{
2953 // The problem is that the baffle faces are now inside the
2954 // mesh (addPatchCellLayer modifies original boundary faces and
2955 // adds new ones. So 2 pass:
2956 // - find the boundary face for all faces originating from baffle
2957 // - use the boundary face for the new baffles
2958
2959 Map<label> baffleSet(4*baffles.size());
2960 forAll(baffles, bafflei)
2961 {
2962 baffleSet.insert(baffles[bafflei][0], bafflei);
2963 baffleSet.insert(baffles[bafflei][1], bafflei);
2964 }
2965
2966
2967 List<labelPair> newBaffles(baffles.size(), labelPair(-1, -1));
2968 for
2969 (
2970 label facei = mesh.nInternalFaces();
2971 facei < mesh.nFaces();
2972 facei++
2973 )
2974 {
2975 label oldFacei = newToOldFaces[facei];
2976
2977 const auto faceFnd = baffleSet.find(oldFacei);
2978 if (faceFnd.found())
2979 {
2980 label bafflei = faceFnd();
2981 labelPair& p = newBaffles[bafflei];
2982 if (p[0] == -1)
2983 {
2984 p[0] = facei;
2985 }
2986 else if (p[1] == -1)
2987 {
2988 p[1] = facei;
2989 }
2990 else
2991 {
2993 << "Problem:" << facei << " at:"
2994 << mesh.faceCentres()[facei]
2995 << " is on same baffle as " << p[0]
2996 << " at:" << mesh.faceCentres()[p[0]]
2997 << " and " << p[1]
2998 << " at:" << mesh.faceCentres()[p[1]]
2999 << exit(FatalError);
3000 }
3001 }
3002 }
3003 return newBaffles;
3004}
3005
3006
3007// Collect layer faces and layer cells into mesh fields for ease of handling
3008void Foam::snappyLayerDriver::getLayerCellsFaces
3009(
3010 const polyMesh& mesh,
3011 const addPatchCellLayer& addLayer,
3012 const scalarField& oldRealThickness,
3013
3014 labelList& cellNLayers,
3015 scalarField& faceRealThickness
3016)
3017{
3018 cellNLayers.setSize(mesh.nCells());
3019 cellNLayers = 0;
3020 faceRealThickness.setSize(mesh.nFaces());
3021 faceRealThickness = 0;
3022
3023 // Mark all faces in the layer
3024 const labelListList& layerFaces = addLayer.layerFaces();
3025
3026 // Mark all cells in the layer.
3027 labelListList addedCells(addPatchCellLayer::addedCells(mesh, layerFaces));
3028
3029 forAll(addedCells, oldPatchFacei)
3030 {
3031 const labelList& added = addedCells[oldPatchFacei];
3032
3033 const labelList& layer = layerFaces[oldPatchFacei];
3034
3035 if (layer.size())
3036 {
3037 // Leave out original internal face
3038 forAll(added, i)
3039 {
3040 cellNLayers[added[i]] = layer.size()-1;
3041 }
3042 }
3043 }
3044
3045 forAll(layerFaces, oldPatchFacei)
3046 {
3047 const labelList& layer = layerFaces[oldPatchFacei];
3048 const scalar realThickness = oldRealThickness[oldPatchFacei];
3049
3050 if (layer.size())
3051 {
3052 // Layer contains both original boundary face and new boundary
3053 // face so is nLayers+1. Leave out old internal face.
3054 for (label i = 1; i < layer.size(); i++)
3055 {
3056 faceRealThickness[layer[i]] = realThickness;
3057 }
3058 }
3059 }
3060}
3061
3062
3063void Foam::snappyLayerDriver::printLayerData
3064(
3065 const fvMesh& mesh,
3066 const labelList& patchIDs,
3067 const labelList& cellNLayers,
3068 const scalarField& faceWantedThickness,
3069 const scalarField& faceRealThickness
3070) const
3071{
3072 const polyBoundaryMesh& pbm = mesh.boundaryMesh();
3073
3074 const int oldPrecision = Info.stream().precision();
3075
3076 // Find maximum length of a patch name, for a nicer output
3077 label maxPatchNameLen = 0;
3078 forAll(patchIDs, i)
3079 {
3080 label patchi = patchIDs[i];
3081 word patchName = pbm[patchi].name();
3082 maxPatchNameLen = max(maxPatchNameLen, label(patchName.size()));
3083 }
3084
3085 Info<< nl
3086 << setf(ios_base::left) << setw(maxPatchNameLen) << "patch"
3087 << setw(0) << " faces layers overall thickness" << nl
3088 << setf(ios_base::left) << setw(maxPatchNameLen) << " "
3089 << setw(0) << " [m] [%]" << nl
3090 << setf(ios_base::left) << setw(maxPatchNameLen) << "-----"
3091 << setw(0) << " ----- ------ --- ---" << endl;
3092
3093
3094 forAll(patchIDs, i)
3095 {
3096 label patchi = patchIDs[i];
3097 const polyPatch& pp = pbm[patchi];
3098
3099 label sumSize = pp.size();
3100
3101 // Number of layers
3102 const labelList& faceCells = pp.faceCells();
3103 label sumNLayers = 0;
3104 forAll(faceCells, i)
3105 {
3106 sumNLayers += cellNLayers[faceCells[i]];
3107 }
3108
3109 // Thickness
3110 scalarField::subField patchWanted = pbm[patchi].patchSlice
3111 (
3112 faceWantedThickness
3113 );
3114 scalarField::subField patchReal = pbm[patchi].patchSlice
3115 (
3116 faceRealThickness
3117 );
3118
3119 scalar sumRealThickness = sum(patchReal);
3120 scalar sumFraction = 0;
3121 forAll(patchReal, i)
3122 {
3123 if (patchWanted[i] > VSMALL)
3124 {
3125 sumFraction += (patchReal[i]/patchWanted[i]);
3126 }
3127 }
3128
3129
3130 reduce(sumSize, sumOp<label>());
3131 reduce(sumNLayers, sumOp<label>());
3132 reduce(sumRealThickness, sumOp<scalar>());
3133 reduce(sumFraction, sumOp<scalar>());
3134
3135
3136 scalar avgLayers = 0;
3137 scalar avgReal = 0;
3138 scalar avgFraction = 0;
3139 if (sumSize > 0)
3140 {
3141 avgLayers = scalar(sumNLayers)/sumSize;
3142 avgReal = sumRealThickness/sumSize;
3143 avgFraction = sumFraction/sumSize;
3144 }
3145
3146 Info<< setf(ios_base::left) << setw(maxPatchNameLen)
3147 << pbm[patchi].name() << setprecision(3)
3148 << " " << setw(8) << sumSize
3149 << " " << setw(8) << avgLayers
3150 << " " << setw(8) << avgReal
3151 << " " << setw(8) << 100*avgFraction
3152 << endl;
3153 }
3154 Info<< setprecision(oldPrecision) << endl;
3155}
3156
3157
3158bool 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
3221bool 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,
3252 false
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,
3292 false
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,
3320 false
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;
3363}
3364
3365
3366void Foam::snappyLayerDriver::dupFaceZonePoints
3367(
3368 const labelList& patchIDs, // patch indices
3369 const labelList& numLayers, // number of layers per patch
3370 List<labelPair> baffles, // pairs of baffles (input & updated)
3371 labelList& pointToMaster // -1 or index of original point (duplicated
3372 // point)
3373)
3374{
3375 fvMesh& mesh = meshRefiner_.mesh();
3376
3377 // Check outside of baffles for non-manifoldness
3378
3379 // Points that are candidates for duplication
3380 labelList candidatePoints;
3381 {
3382 // Do full analysis to see if we need to extrude points
3383 // so have to duplicate them
3384 autoPtr<indirectPrimitivePatch> pp
3385 (
3387 (
3388 mesh,
3389 patchIDs
3390 )
3391 );
3392
3393 // Displacement for all pp.localPoints. Set to large value to
3394 // avoid truncation in syncPatchDisplacement because of
3395 // minThickness.
3396 vectorField patchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT));
3397 labelList patchNLayers(pp().nPoints(), Zero);
3398 label nIdealTotAddedCells = 0;
3399 List<extrudeMode> extrudeStatus(pp().nPoints(), EXTRUDE);
3400 // Get number of layers per point from number of layers per patch
3401 setNumLayers
3402 (
3403 numLayers, // per patch the num layers
3404 patchIDs, // patches that are being moved
3405 *pp, // indirectpatch for all faces moving
3406
3407 patchNLayers,
3408 extrudeStatus,
3409 nIdealTotAddedCells
3410 );
3411 // Make sure displacement is equal on both sides of coupled patches.
3412 // Note that we explicitly disable the minThickness truncation
3413 // of the patchDisp here.
3414 syncPatchDisplacement
3415 (
3416 *pp,
3417 scalarField(patchDisp.size(), Zero), //minThickness,
3418 patchDisp,
3419 patchNLayers,
3420 extrudeStatus
3421 );
3422
3423
3424 // Do duplication only if all patch points decide to extrude. Ignore
3425 // contribution from non-patch points. Note that we need to
3426 // apply this to all mesh points
3427 labelList minPatchState(mesh.nPoints(), labelMax);
3428 forAll(extrudeStatus, patchPointi)
3429 {
3430 label pointi = pp().meshPoints()[patchPointi];
3431 minPatchState[pointi] = extrudeStatus[patchPointi];
3432 }
3433
3435 (
3436 mesh,
3437 minPatchState,
3438 minEqOp<label>(), // combine op
3439 labelMax // null value
3440 );
3441
3442 // So now minPatchState:
3443 // - labelMax on non-patch points
3444 // - NOEXTRUDE if any patch point was not extruded
3445 // - EXTRUDE or EXTRUDEREMOVE if all patch points are extruded/
3446 // extrudeRemove.
3447
3448 label n = 0;
3449 forAll(minPatchState, pointi)
3450 {
3451 label state = minPatchState[pointi];
3452 if (state == EXTRUDE || state == EXTRUDEREMOVE)
3453 {
3454 n++;
3455 }
3456 }
3457 candidatePoints.setSize(n);
3458 n = 0;
3459 forAll(minPatchState, pointi)
3460 {
3461 label state = minPatchState[pointi];
3462 if (state == EXTRUDE || state == EXTRUDEREMOVE)
3463 {
3464 candidatePoints[n++] = pointi;
3465 }
3466 }
3467 }
3468
3469 // Not duplicate points on either side of baffles that don't get any
3470 // layers
3471 labelPairList nonDupBaffles;
3472
3473 {
3474 // faceZones that are not being duplicated
3475 DynamicList<label> nonDupZones(mesh.faceZones().size());
3476
3477 labelHashSet layerIDs(patchIDs);
3478 forAll(mesh.faceZones(), zonei)
3479 {
3480 label mpi, spi;
3482 bool hasInfo = meshRefiner_.getFaceZoneInfo
3483 (
3484 mesh.faceZones()[zonei].name(),
3485 mpi,
3486 spi,
3487 fzType
3488 );
3489 if (hasInfo && !layerIDs.found(mpi) && !layerIDs.found(spi))
3490 {
3491 nonDupZones.append(zonei);
3492 }
3493 }
3494 nonDupBaffles = meshRefinement::subsetBaffles
3495 (
3496 mesh,
3497 nonDupZones,
3499 );
3500 }
3501
3502
3503 const localPointRegion regionSide(mesh, nonDupBaffles, candidatePoints);
3504
3505 autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldPoints
3506 (
3507 regionSide
3508 );
3509
3510 if (map)
3511 {
3512 // Store point duplication
3513 pointToMaster.setSize(mesh.nPoints(), -1);
3514
3515 const labelList& pointMap = map().pointMap();
3516 const labelList& reversePointMap = map().reversePointMap();
3517
3518 forAll(pointMap, pointi)
3519 {
3520 label oldPointi = pointMap[pointi];
3521 label newMasterPointi = reversePointMap[oldPointi];
3522
3523 if (newMasterPointi != pointi)
3524 {
3525 // Found slave. Mark both master and slave
3526 pointToMaster[pointi] = newMasterPointi;
3527 pointToMaster[newMasterPointi] = newMasterPointi;
3528 }
3529 }
3530
3531 // Update baffle numbering
3532 {
3533 const labelList& reverseFaceMap = map().reverseFaceMap();
3534 forAll(baffles, i)
3535 {
3536 label f0 = reverseFaceMap[baffles[i].first()];
3537 label f1 = reverseFaceMap[baffles[i].second()];
3538 baffles[i] = labelPair(f0, f1);
3539 }
3540 }
3541
3542
3544 {
3545 const_cast<Time&>(mesh.time())++;
3546 Info<< "Writing point-duplicate mesh to time "
3547 << meshRefiner_.timeName() << endl;
3548
3549 meshRefiner_.write
3550 (
3553 (
3556 ),
3557 mesh.time().path()/meshRefiner_.timeName()
3558 );
3559
3560 OBJstream str
3561 (
3562 mesh.time().path()
3563 / "duplicatePoints_"
3564 + meshRefiner_.timeName()
3565 + ".obj"
3566 );
3567 Info<< "Writing point-duplicates to " << str.name() << endl;
3568 const pointField& p = mesh.points();
3569 forAll(pointMap, pointi)
3570 {
3571 label newMasteri = reversePointMap[pointMap[pointi]];
3572
3573 if (newMasteri != pointi)
3574 {
3575 str.write(linePointRef(p[pointi], p[newMasteri]));
3576 }
3577 }
3578 }
3579 }
3580}
3581
3582
3583void Foam::snappyLayerDriver::mergeFaceZonePoints
3584(
3585 const labelList& pointToMaster, // -1 or index of duplicated point
3586 labelList& cellNLayers,
3587 scalarField& faceRealThickness,
3588 scalarField& faceWantedThickness
3589)
3590{
3591 // Near opposite of dupFaceZonePoints : merge points and baffles introduced
3592 // for internal faceZones
3593
3594 fvMesh& mesh = meshRefiner_.mesh();
3595
3596 // Count duplicate points
3597 label nPointPairs = 0;
3598 forAll(pointToMaster, pointi)
3599 {
3600 label otherPointi = pointToMaster[pointi];
3601 if (otherPointi != -1)
3602 {
3603 nPointPairs++;
3604 }
3605 }
3606 reduce(nPointPairs, sumOp<label>());
3607 if (nPointPairs > 0)
3608 {
3609 // Merge any duplicated points
3610 Info<< "Merging " << nPointPairs << " duplicated points ..." << endl;
3611
3613 {
3614 OBJstream str
3615 (
3616 mesh.time().path()
3617 / "mergePoints_"
3618 + meshRefiner_.timeName()
3619 + ".obj"
3620 );
3621 Info<< "Points to be merged to " << str.name() << endl;
3622 forAll(pointToMaster, pointi)
3623 {
3624 label otherPointi = pointToMaster[pointi];
3625 if (otherPointi != -1)
3626 {
3627 const point& pt = mesh.points()[pointi];
3628 const point& otherPt = mesh.points()[otherPointi];
3629 str.write(linePointRef(pt, otherPt));
3630 }
3631 }
3632 }
3633
3634
3635 autoPtr<mapPolyMesh> map = meshRefiner_.mergePoints(pointToMaster);
3636 if (map)
3637 {
3638 inplaceReorder(map().reverseCellMap(), cellNLayers);
3639
3640 const labelList& reverseFaceMap = map().reverseFaceMap();
3641 inplaceReorder(reverseFaceMap, faceWantedThickness);
3642 inplaceReorder(reverseFaceMap, faceRealThickness);
3643
3644 Info<< "Merged points in = "
3645 << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
3646 }
3647 }
3648
3649 if (mesh.faceZones().size() > 0)
3650 {
3651 // Merge any baffles
3652 Info<< "Converting baffles back into zoned faces ..."
3653 << endl;
3654
3655 autoPtr<mapPolyMesh> map = meshRefiner_.mergeZoneBaffles
3656 (
3657 true, // internal zones
3658 false // baffle zones
3659 );
3660 if (map)
3661 {
3662 inplaceReorder(map().reverseCellMap(), cellNLayers);
3663
3664 const labelList& faceMap = map().faceMap();
3665
3666 // Make sure to keep the max since on two patches only one has
3667 // layers.
3668 scalarField newFaceRealThickness(mesh.nFaces(), Zero);
3669 scalarField newFaceWantedThickness(mesh.nFaces(), Zero);
3670 forAll(newFaceRealThickness, facei)
3671 {
3672 label oldFacei = faceMap[facei];
3673 if (oldFacei >= 0)
3674 {
3675 scalar& realThick = newFaceRealThickness[facei];
3676 realThick = max(realThick, faceRealThickness[oldFacei]);
3677 scalar& wanted = newFaceWantedThickness[facei];
3678 wanted = max(wanted, faceWantedThickness[oldFacei]);
3679 }
3680 }
3681 faceRealThickness.transfer(newFaceRealThickness);
3682 faceWantedThickness.transfer(newFaceWantedThickness);
3683 }
3684
3685 Info<< "Converted baffles in = "
3686 << meshRefiner_.mesh().time().cpuTimeIncrement()
3687 << " s\n" << nl << endl;
3688 }
3689}
3690
3691
3692Foam::label Foam::snappyLayerDriver::setPointNumLayers
3693(
3694 const layerParameters& layerParams,
3695
3696 const labelList& numLayers,
3697 const labelList& patchIDs,
3698 const indirectPrimitivePatch& pp,
3699 const labelListList& edgeGlobalFaces,
3700
3701 vectorField& patchDisp,
3702 labelList& patchNLayers,
3703 List<extrudeMode>& extrudeStatus
3704) const
3705{
3706 fvMesh& mesh = meshRefiner_.mesh();
3707
3708 patchDisp.setSize(pp.nPoints());
3709 patchDisp = vector(GREAT, GREAT, GREAT);
3710
3711 // Number of layers for all pp.localPoints. Note: only valid if
3712 // extrudeStatus = EXTRUDE.
3713 patchNLayers.setSize(pp.nPoints());
3714 patchNLayers = Zero;
3715
3716 // Ideal number of cells added
3717 label nIdealTotAddedCells = 0;
3718
3719 // Whether to add edge for all pp.localPoints.
3720 extrudeStatus.setSize(pp.nPoints());
3721 extrudeStatus = EXTRUDE;
3722
3723
3724 // Get number of layers per point from number of layers per patch
3725 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3726
3727 setNumLayers
3728 (
3729 numLayers, // per patch the num layers
3730 patchIDs, // patches that are being moved
3731 pp, // indirectpatch for all faces moving
3732
3733 patchNLayers,
3734 extrudeStatus,
3735 nIdealTotAddedCells
3736 );
3737
3738 // Precalculate mesh edge labels for patch edges
3739 labelList meshEdges(pp.meshEdges(mesh.edges(), mesh.pointEdges()));
3740
3741
3742 // Disable extrusion on split strings of common points
3743 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3744
3745 handleNonStringConnected
3746 (
3747 pp,
3748 patchDisp,
3749 patchNLayers,
3750 extrudeStatus
3751 );
3752
3753
3754 // Disable extrusion on non-manifold points
3755 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3756
3757 handleNonManifolds
3758 (
3759 pp,
3760 meshEdges,
3761 edgeGlobalFaces,
3762
3763 patchDisp,
3764 patchNLayers,
3765 extrudeStatus
3766 );
3767
3768 // Disable extrusion on feature angles
3769 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3770
3771 handleFeatureAngle
3772 (
3773 pp,
3774 meshEdges,
3775 layerParams.featureAngle(),
3776
3777 patchDisp,
3778 patchNLayers,
3779 extrudeStatus
3780 );
3781
3782 // Disable extrusion on warped faces
3783 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3784 // It is hard to calculate some length scale if not in relative
3785 // mode so disable this check.
3786 if (!layerParams.relativeSizes().found(false))
3787 {
3788 // Undistorted edge length
3789 const scalar edge0Len =
3790 meshRefiner_.meshCutter().level0EdgeLength();
3791 const labelList& cellLevel = meshRefiner_.meshCutter().cellLevel();
3792
3793 handleWarpedFaces
3794 (
3795 pp,
3796 layerParams.maxFaceThicknessRatio(),
3797 layerParams.relativeSizes(),
3798 edge0Len,
3799 cellLevel,
3800
3801 patchDisp,
3802 patchNLayers,
3803 extrudeStatus
3804 );
3805 }
3806
3809 //
3810 //handleMultiplePatchFaces
3811 //(
3812 // pp,
3813 //
3814 // patchDisp,
3815 // patchNLayers,
3816 // extrudeStatus
3817 //);
3818
3819 addProfiling(grow, "snappyHexMesh::layers::grow");
3820
3821 // Grow out region of non-extrusion
3822 for (label i = 0; i < layerParams.nGrow(); i++)
3823 {
3824 growNoExtrusion
3825 (
3826 pp,
3827 patchDisp,
3828 patchNLayers,
3829 extrudeStatus
3830 );
3831 }
3832 return nIdealTotAddedCells;
3833}
3834
3835
3837Foam::snappyLayerDriver::makeMeshMover
3838(
3839 const layerParameters& layerParams,
3840 const dictionary& motionDict,
3841 const labelList& internalFaceZones,
3842 const scalarIOField& minThickness,
3843 pointVectorField& displacement
3844) const
3845{
3846 // Allocate run-time selectable mesh mover
3847
3848 fvMesh& mesh = meshRefiner_.mesh();
3849
3850
3851 // Set up controls for meshMover
3852 dictionary combinedDict(layerParams.dict());
3853 // Add mesh quality constraints
3854 combinedDict.merge(motionDict);
3855 // Where to get minThickness from
3856 combinedDict.add("minThicknessName", minThickness.name());
3857
3858 const List<labelPair> internalBaffles
3859 (
3861 (
3862 mesh,
3863 internalFaceZones,
3865 )
3866 );
3867
3868 // Take over patchDisp as boundary conditions on displacement
3869 // pointVectorField
3870 autoPtr<Foam::externalDisplacementMeshMover> medialAxisMoverPtr
3871 (
3873 (
3874 layerParams.meshShrinker(),
3875 combinedDict,
3876 internalBaffles,
3877 displacement
3878 )
3879 );
3880
3881
3882 if (dryRun_)
3883 {
3884 string errorMsg(FatalError.message());
3885 string IOerrorMsg(FatalIOError.message());
3886
3887 if (errorMsg.size() || IOerrorMsg.size())
3888 {
3889 //errorMsg = "[dryRun] " + errorMsg;
3890 //errorMsg.replaceAll("\n", "\n[dryRun] ");
3891 //IOerrorMsg = "[dryRun] " + IOerrorMsg;
3892 //IOerrorMsg.replaceAll("\n", "\n[dryRun] ");
3893
3894 IOWarningInFunction(combinedDict)
3895 << nl
3896 << "Missing/incorrect required dictionary entries:"
3897 << nl << nl
3898 << IOerrorMsg.c_str() << nl
3899 << errorMsg.c_str() << nl << nl
3900 << "Exiting dry-run" << nl << endl;
3901
3902 if (Pstream::parRun())
3903 {
3904 Perr<< "\nFOAM parallel run exiting\n" << endl;
3905 Pstream::exit(0);
3906 }
3907 else
3908 {
3909 Perr<< "\nFOAM exiting\n" << endl;
3910 std::exit(0);
3911 }
3912 }
3913 }
3914 return medialAxisMoverPtr;
3915}
3916
3917
3919(
3920 const layerParameters& layerParams,
3921 const label nLayerIter,
3922
3923 // Mesh quality provision
3924 const dictionary& motionDict,
3925 const label nRelaxedIter,
3926 const label nAllowableErrors,
3927
3928 const labelList& patchIDs,
3929 const labelList& internalFaceZones,
3930 const List<labelPair>& baffles,
3931 const labelList& numLayers,
3932 const label nIdealTotAddedCells,
3933
3934 const globalIndex& globalFaces,
3936
3937 const labelListList& edgeGlobalFaces,
3938 const labelList& edgePatchID,
3939 const labelList& edgeZoneID,
3940 const boolList& edgeFlip,
3941 const labelList& inflateFaceID,
3942
3943 const scalarField& thickness,
3944 const scalarIOField& minThickness,
3945 const scalarField& expansionRatio,
3946
3947 // Displacement for all pp.localPoints. Set to large value so does
3948 // not trigger the minThickness truncation (see syncPatchDisplacement below)
3949 vectorField& patchDisp,
3950
3951 // Number of layers for all pp.localPoints. Note: only valid if
3952 // extrudeStatus = EXTRUDE.
3953 labelList& patchNLayers,
3954
3955 // Whether to add edge for all pp.localPoints.
3956 List<extrudeMode>& extrudeStatus,
3957
3958
3959 polyTopoChange& savedMeshMod,
3960
3961
3962 // Per cell 0 or number of layers in the cell column it is part of
3963 labelList& cellNLayers,
3964 // Per face actual overall layer thickness
3965 scalarField& faceRealThickness
3966)
3967{
3968 fvMesh& mesh = meshRefiner_.mesh();
3969
3970 // Overall displacement field
3971 pointVectorField displacement
3972 (
3973 makeLayerDisplacementField
3974 (
3976 numLayers
3977 )
3978 );
3979
3980 // Allocate run-time selectable mesh mover
3982 (
3983 makeMeshMover
3984 (
3985 layerParams,
3986 motionDict,
3987 internalFaceZones,
3988 minThickness,
3989 displacement
3990 )
3991 );
3992
3993
3994 // Saved old points
3995 const pointField oldPoints(mesh.points());
3996
3997 for (label iteration = 0; iteration < nLayerIter; iteration++)
3998 {
3999 Info<< nl
4000 << "Layer addition iteration " << iteration << nl
4001 << "--------------------------" << endl;
4002
4003
4004 // Unset the extrusion at the pp.
4005 const dictionary& meshQualityDict =
4006 (
4007 iteration < nRelaxedIter
4008 ? motionDict
4009 : motionDict.subDict("relaxed")
4010 );
4011
4012 if (iteration >= nRelaxedIter)
4013 {
4014 Info<< "Switched to relaxed meshQuality constraints." << endl;
4015 }
4016
4017
4018
4019 // Make sure displacement is equal on both sides of coupled patches.
4020 // Note that this also does the patchDisp < minThickness truncation
4021 // so for the first pass make sure the patchDisp is larger than
4022 // that.
4023 syncPatchDisplacement
4024 (
4025 pp,
4026 minThickness,
4027 patchDisp,
4028 patchNLayers,
4029 extrudeStatus
4030 );
4031
4032 // Displacement acc. to pointnormals
4033 getPatchDisplacement
4034 (
4035 pp,
4036 thickness,
4037 minThickness,
4038 expansionRatio,
4039
4040 patchDisp,
4041 patchNLayers,
4042 extrudeStatus
4043 );
4044
4045 // Shrink mesh by displacement value first.
4046 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4047
4048 {
4049 const pointField oldPatchPos(pp.localPoints());
4050
4051 // We have patchDisp which is the outwards pointing
4052 // extrusion distance. Convert into an inwards pointing
4053 // shrink distance
4054 patchDisp = -patchDisp;
4055
4056 // Take over patchDisp into pointDisplacement field and
4057 // adjust both for multi-patch constraints
4059 (
4060 patchIDs,
4061 pp,
4062 patchDisp,
4063 displacement
4064 );
4065
4066
4067 // Move mesh
4068 // ~~~~~~~~~
4069
4070 // Set up controls for meshMover
4071 dictionary combinedDict(layerParams.dict());
4072 // Add standard quality constraints
4073 combinedDict.merge(motionDict);
4074 // Add relaxed constraints (overrides standard ones)
4075 combinedDict.merge(meshQualityDict);
4076 // Where to get minThickness from
4077 combinedDict.add("minThicknessName", minThickness.name());
4078
4079 labelList checkFaces(identity(mesh.nFaces()));
4080 medialAxisMoverPtr().move
4081 (
4082 combinedDict,
4083 nAllowableErrors,
4084 checkFaces
4085 );
4086
4087 pp.movePoints(mesh.points());
4088
4089 // Unset any moving state
4090 mesh.moving(false);
4091
4092 // Update patchDisp (since not all might have been honoured)
4093 patchDisp = oldPatchPos - pp.localPoints();
4094 }
4095
4096 // Truncate displacements that are too small (this will do internal
4097 // ones, coupled ones have already been truncated by
4098 // syncPatchDisplacement)
4099 faceSet dummySet(mesh, "wrongPatchFaces", 0);
4100 truncateDisplacement
4101 (
4102 globalFaces,
4103 edgeGlobalFaces,
4104 pp,
4105 minThickness,
4106 dummySet,
4107 patchDisp,
4108 patchNLayers,
4109 extrudeStatus
4110 );
4111
4112
4113 // Dump to .obj file for debugging.
4115 {
4116 dumpDisplacement
4117 (
4118 mesh.time().path()/"layer_" + meshRefiner_.timeName(),
4119 pp,
4120 patchDisp,
4121 extrudeStatus
4122 );
4123
4124 const_cast<Time&>(mesh.time())++;
4125 Info<< "Writing shrunk mesh to time "
4126 << meshRefiner_.timeName() << endl;
4127
4128 // See comment in snappySnapDriver why we should not remove
4129 // meshPhi using mesh.clearOut().
4130
4131 meshRefiner_.write
4132 (
4135 (
4138 ),
4139 mesh.time().path()/meshRefiner_.timeName()
4140 );
4141 }
4142
4143
4144 // Mesh topo change engine. Insert current mesh.
4145 polyTopoChange meshMod(mesh);
4146
4147 // Grow layer of cells on to patch. Handles zero sized displacement.
4148 addPatchCellLayer addLayer(mesh);
4149
4150 // Determine per point/per face number of layers to extrude. Also
4151 // handles the slow termination of layers when going switching
4152 // layers
4153
4154 labelList nPatchPointLayers(pp.nPoints(), -1);
4155 labelList nPatchFaceLayers(pp.size(), -1);
4156 setupLayerInfoTruncation
4157 (
4158 pp,
4159 patchNLayers,
4160 extrudeStatus,
4161 layerParams.nBufferCellsNoExtrude(),
4162 nPatchPointLayers,
4163 nPatchFaceLayers
4164 );
4165
4166 // Calculate displacement for final layer for addPatchLayer.
4167 // (layer of cells next to the original mesh)
4168 vectorField finalDisp(patchNLayers.size(), Zero);
4169
4170 forAll(nPatchPointLayers, i)
4171 {
4173 (
4174 nPatchPointLayers[i],
4175 expansionRatio[i]
4176 );
4177 finalDisp[i] = ratio*patchDisp[i];
4178 }
4179
4180
4181 const scalarField invExpansionRatio(1.0/expansionRatio);
4182
4183 // Add topo regardless of whether extrudeStatus is extruderemove.
4184 // Not add layer if patchDisp is zero.
4185 addLayer.setRefinement
4186 (
4187 globalFaces,
4188 edgeGlobalFaces,
4189
4190 invExpansionRatio,
4191 pp,
4192 bitSet(pp.size()), // no flip
4193
4194 edgePatchID, // boundary patch for extruded boundary edges
4195 edgeZoneID, // zone for extruded edges
4196 edgeFlip,
4197 inflateFaceID,
4198
4199
4200 labelList(0), // exposed patchIDs, not used for adding layers
4201 nPatchFaceLayers, // layers per face
4202 nPatchPointLayers, // layers per point
4203 finalDisp, // thickness of layer nearest internal mesh
4204 meshMod
4205 );
4206
4207 if (debug)
4208 {
4209 const_cast<Time&>(mesh.time())++;
4210 }
4211
4212 // Compact storage
4213 meshMod.shrink();
4214
4215 // Store mesh changes for if mesh is correct.
4216 savedMeshMod = meshMod;
4217
4218
4219 // With the stored topo changes we create a new mesh so we can
4220 // undo if necessary.
4221
4222 autoPtr<fvMesh> newMeshPtr;
4223 autoPtr<mapPolyMesh> mapPtr = meshMod.makeMesh
4224 (
4225 newMeshPtr,
4226 IOobject
4227 (
4228 //mesh.name()+"_layer",
4229 mesh.name(),
4230 static_cast<polyMesh&>(mesh).instance(),
4231 mesh.time(), // register with runTime
4232 IOobject::READ_IF_PRESENT, // read fv* if present
4233 static_cast<polyMesh&>(mesh).writeOpt()
4234 ), // io params from original mesh but new name
4235 mesh, // original mesh
4236 true // parallel sync
4237 );
4238 fvMesh& newMesh = *newMeshPtr;
4239 mapPolyMesh& map = *mapPtr;
4240
4241 // Get timing, but more importantly get memory information
4242 addProfiling(grow, "snappyHexMesh::layers::updateMesh");
4243
4244 //?necessary? Update fields
4245 newMesh.updateMesh(map);
4246
4247 // Move mesh if in inflation mode
4248 if (map.hasMotionPoints())
4249 {
4250 newMesh.movePoints(map.preMotionPoints());
4251 }
4252 else
4253 {
4254 // Delete mesh volumes.
4255 newMesh.clearOut();
4256 }
4257
4258 newMesh.setInstance(meshRefiner_.timeName());
4259
4260 // Update numbering on addLayer:
4261 // - cell/point labels to be newMesh.
4262 // - patchFaces to remain in oldMesh order.
4263 addLayer.updateMesh
4264 (
4265 map,
4266 identity(pp.size()),
4267 identity(pp.nPoints())
4268 );
4269
4270 // Collect layer faces and cells for outside loop.
4271 getLayerCellsFaces
4272 (
4273 newMesh,
4274 addLayer,
4275 avgPointData(pp, mag(patchDisp))(), // current thickness
4276
4277 cellNLayers,
4278 faceRealThickness
4279 );
4280
4281
4282 // Count number of added cells
4283 label nAddedCells = 0;
4284 forAll(cellNLayers, celli)
4285 {
4286 if (cellNLayers[celli] > 0)
4287 {
4288 nAddedCells++;
4289 }
4290 }
4291
4292
4293 if (debug&meshRefinement::MESH)
4294 {
4295 Info<< "Writing layer mesh to time " << meshRefiner_.timeName()
4296 << endl;
4297 newMesh.write();
4298 writeLayerSets(newMesh, cellNLayers, faceRealThickness);
4299
4300 // Reset the instance of the original mesh so next iteration
4301 // it dumps a complete mesh. This is just so that the inbetween
4302 // newMesh does not upset e.g. paraFoam cycling through the
4303 // times.
4304 mesh.setInstance(meshRefiner_.timeName());
4305 }
4306
4307
4308 //- Get baffles in newMesh numbering. Note that we cannot detect
4309 // baffles here since the points are duplicated
4310 List<labelPair> internalBaffles;
4311 {
4312 // From old mesh face to corresponding newMesh boundary face
4313 labelList meshToNewMesh(mesh.nFaces(), -1);
4314 for
4315 (
4316 label facei = newMesh.nInternalFaces();
4317 facei < newMesh.nFaces();
4318 facei++
4319 )
4320 {
4321 label newMeshFacei = map.faceMap()[facei];
4322 if (newMeshFacei != -1)
4323 {
4324 meshToNewMesh[newMeshFacei] = facei;
4325 }
4326 }
4327
4328 List<labelPair> newMeshBaffles(baffles.size());
4329 label newi = 0;
4330 forAll(baffles, i)
4331 {
4332 const labelPair& p = baffles[i];
4333 labelPair newMeshBaffle
4334 (
4335 meshToNewMesh[p[0]],
4336 meshToNewMesh[p[1]]
4337 );
4338 if (newMeshBaffle[0] != -1 && newMeshBaffle[1] != -1)
4339 {
4340 newMeshBaffles[newi++] = newMeshBaffle;
4341 }
4342 }
4343 newMeshBaffles.setSize(newi);
4344
4345 internalBaffles = meshRefinement::subsetBaffles
4346 (
4347 newMesh,
4348 internalFaceZones,
4349 newMeshBaffles
4350 );
4351
4352 Info<< "Detected "
4353 << returnReduce(internalBaffles.size(), sumOp<label>())
4354 << " baffles across faceZones of type internal" << nl
4355 << endl;
4356 }
4357
4358 label nTotChanged = checkAndUnmark
4359 (
4360 addLayer,
4361 meshQualityDict,
4362 layerParams.additionalReporting(),
4363 internalBaffles,
4364 pp,
4365 newMesh,
4366
4367 patchDisp,
4368 patchNLayers,
4369 extrudeStatus
4370 );
4371
4372 label nTotExtruded = countExtrusion(pp, extrudeStatus);
4373 label nTotFaces = returnReduce(pp.size(), sumOp<label>());
4374 label nTotAddedCells = returnReduce(nAddedCells, sumOp<label>());
4375
4376 Info<< "Extruding " << nTotExtruded
4377 << " out of " << nTotFaces
4378 << " faces (" << 100.0*nTotExtruded/nTotFaces << "%)."
4379 << " Removed extrusion at " << nTotChanged << " faces."
4380 << endl
4381 << "Added " << nTotAddedCells << " out of "
4382 << nIdealTotAddedCells
4383 << " cells (" << 100.0*nTotAddedCells/nIdealTotAddedCells
4384 << "%)." << endl;
4385
4386 if (nTotChanged == 0)
4387 {
4388 break;
4389 }
4390
4391 // Reset mesh points and start again
4392 mesh.movePoints(oldPoints);
4393 pp.movePoints(mesh.points());
4394 medialAxisMoverPtr().movePoints(mesh.points());
4395
4396 // Unset any moving state
4397 mesh.moving(false);
4398
4399 // Grow out region of non-extrusion
4400 for (label i = 0; i < layerParams.nGrow(); i++)
4401 {
4402 growNoExtrusion
4403 (
4404 pp,
4405 patchDisp,
4406 patchNLayers,
4407 extrudeStatus
4408 );
4409 }
4410
4411 Info<< endl;
4412 }
4413}
4414
4415
4416void Foam::snappyLayerDriver::mapFaceZonePoints
4417(
4418 const mapPolyMesh& map,
4419 labelPairList& baffles,
4420 labelList& pointToMaster
4421) const
4422{
4423 fvMesh& mesh = meshRefiner_.mesh();
4424
4425 // Use geometric detection of points-to-be-merged
4426 // - detect any boundary face created from a duplicated face (=baffle)
4427 // - on these mark any point created from a duplicated point
4428 if (returnReduce(pointToMaster.size(), sumOp<label>()))
4429 {
4430 // Estimate number of points-to-be-merged
4431 DynamicList<label> candidates(baffles.size()*4);
4432
4433 // The problem is that all the internal layer faces also
4434 // have reverseFaceMap pointing to the old baffle face. So instead
4435 // loop over all the boundary faces and see which pair of new boundary
4436 // faces corresponds to the old baffles.
4437
4438
4439 // Mark whether old face was on baffle
4440 Map<label> oldFaceToBaffle(2*baffles.size());
4441 forAll(baffles, i)
4442 {
4443 const labelPair& baffle = baffles[i];
4444 oldFaceToBaffle.insert(baffle[0], i);
4445 oldFaceToBaffle.insert(baffle[1], i);
4446 }
4447
4448 labelPairList newBaffles(baffles.size(), labelPair(-1, -1));
4449
4450 for
4451 (
4452 label facei = mesh.nInternalFaces();
4453 facei < mesh.nFaces();
4454 facei++
4455 )
4456 {
4457 const label oldFacei = map.faceMap()[facei];
4458 const auto iter = oldFaceToBaffle.find(oldFacei);
4459 if (oldFacei != -1 && iter.found())
4460 {
4461 const label bafflei = iter();
4462 auto& newBaffle = newBaffles[bafflei];
4463 if (newBaffle[0] == -1)
4464 {
4465 newBaffle[0] = facei;
4466 }
4467 else if (newBaffle[1] == -1)
4468 {
4469 newBaffle[1] = facei;
4470 }
4471 else
4472 {
4473 FatalErrorInFunction << "face:" << facei
4474 << " at:" << mesh.faceCentres()[facei]
4475 << " already maps to baffle faces:"
4476 << newBaffle[0]
4477 << " at:" << mesh.faceCentres()[newBaffle[0]]
4478 << " and " << newBaffle[1]
4479 << " at:" << mesh.faceCentres()[newBaffle[1]]
4480 << exit(FatalError);
4481 }
4482
4483 const face& f = mesh.faces()[facei];
4484 forAll(f, fp)
4485 {
4486 label pointi = f[fp];
4487 label oldPointi = map.pointMap()[pointi];
4488
4489 if (pointToMaster[oldPointi] != -1)
4490 {
4491 candidates.append(pointi);
4492 }
4493 }
4494 }
4495 }
4496
4497
4498 // Compact newBaffles
4499 {
4500 label n = 0;
4501 forAll(newBaffles, i)
4502 {
4503 const labelPair& newBaffle = newBaffles[i];
4504 if (newBaffle[0] != -1 && newBaffle[1] != -1)
4505 {
4506 newBaffles[n++] = newBaffle;
4507 }
4508 }
4509
4510 newBaffles.setSize(n);
4511 baffles.transfer(newBaffles);
4512 }
4513
4514
4515 // Do geometric merge. Ideally we'd like to use a topological
4516 // merge but we've thrown away all layer-wise addressing when
4517 // throwing away addPatchCellLayer engine. Also the addressing
4518 // is extremely complicated. There is no problem with merging
4519 // too many points; the problem would be if merging baffles.
4520 // Trust mergeZoneBaffles to do sufficient checks.
4521 labelList oldToNew;
4522 label nNew = Foam::mergePoints
4523 (
4524 UIndirectList<point>(mesh.points(), candidates),
4525 meshRefiner_.mergeDistance(),
4526 false,
4527 oldToNew
4528 );
4529
4530 // Extract points to be merged (i.e. multiple points originating
4531 // from a single one)
4532
4533 labelListList newToOld(invertOneToMany(nNew, oldToNew));
4534
4535 // Extract points with more than one old one
4536 pointToMaster.setSize(mesh.nPoints());
4537 pointToMaster = -1;
4538
4539 forAll(newToOld, newi)
4540 {
4541 const labelList& oldPoints = newToOld[newi];
4542 if (oldPoints.size() > 1)
4543 {
4544 labelList meshPoints
4545 (
4546 labelUIndList(candidates, oldPoints)
4547 );
4548 label masteri = min(meshPoints);
4549 forAll(meshPoints, i)
4550 {
4551 pointToMaster[meshPoints[i]] = masteri;
4552 }
4553 }
4554 }
4555 }
4556}
4557
4558
4559void Foam::snappyLayerDriver::updatePatch
4560(
4561 const labelList& patchIDs,
4562 const mapPolyMesh& map,
4563 autoPtr<indirectPrimitivePatch>& pp,
4564 labelList& newToOldPatchPoints
4565) const
4566{
4567 // Update the pp to be consistent with the new mesh
4568
4569 fvMesh& mesh = meshRefiner_.mesh();
4570
4571 autoPtr<indirectPrimitivePatch> newPp
4572 (
4574 (
4575 mesh,
4576 patchIDs
4577 )
4578 );
4579
4580 // Map from new back to old patch points
4581 newToOldPatchPoints.setSize(newPp().nPoints());
4582 newToOldPatchPoints = -1;
4583 {
4584 const Map<label>& baseMap = pp().meshPointMap();
4585 const labelList& newMeshPoints = newPp().meshPoints();
4586
4587 forAll(newMeshPoints, newPointi)
4588 {
4589 const label newMeshPointi = newMeshPoints[newPointi];
4590 const label oldMeshPointi =
4591 map.pointMap()[newMeshPointi];
4592 const auto iter = baseMap.find(oldMeshPointi);
4593 if (iter.found())
4594 {
4595 newToOldPatchPoints[newPointi] = iter();
4596 }
4597 }
4598 }
4599
4600
4601 // Reset pp. Note: make sure to use std::move - otherwise it
4602 // will release the pointer before copying and you get
4603 // memory error. Same if using autoPtr::reset.
4604 pp = std::move(newPp);
4605
4606 // Make sure pp has adressing cached
4607 (void)pp().meshPoints();
4608 (void)pp().meshPointMap();
4609}
4610
4611
4612// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
4613
4615(
4616 meshRefinement& meshRefiner,
4617 const labelList& globalToMasterPatch,
4618 const labelList& globalToSlavePatch,
4619 const bool dryRun
4620)
4621:
4622 meshRefiner_(meshRefiner),
4623 globalToMasterPatch_(globalToMasterPatch),
4624 globalToSlavePatch_(globalToSlavePatch),
4625 dryRun_(dryRun)
4626{}
4627
4628
4629// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
4630
4632(
4633 const layerParameters& layerParams,
4634 const dictionary& motionDict,
4635 const meshRefinement::FaceMergeType mergeType
4636)
4637{
4638 // Clip to 30 degrees. Not helpful!
4639 //scalar planarAngle = min(30.0, layerParams.featureAngle());
4640 scalar planarAngle = layerParams.mergePatchFacesAngle();
4641 scalar minCos = Foam::cos(degToRad(planarAngle));
4642
4643 scalar concaveCos = Foam::cos(degToRad(layerParams.concaveAngle()));
4644
4645 Info<< nl
4646 << "Merging all faces of a cell" << nl
4647 << "---------------------------" << nl
4648 << " - which are on the same patch" << nl
4649 << " - which make an angle < " << planarAngle
4650 << " degrees"
4651 << " (cos:" << minCos << ')' << nl
4652 << " - as long as the resulting face doesn't become concave"
4653 << " by more than "
4654 << layerParams.concaveAngle() << " degrees" << nl
4655 << " (0=straight, 180=fully concave)" << nl
4656 << endl;
4657
4658 const fvMesh& mesh = meshRefiner_.mesh();
4659
4661
4662 labelList duplicateFace(mesh.nFaces(), -1);
4663 forAll(couples, i)
4664 {
4665 const labelPair& cpl = couples[i];
4666 duplicateFace[cpl[0]] = cpl[1];
4667 duplicateFace[cpl[1]] = cpl[0];
4668 }
4669
4670 label nChanged = meshRefiner_.mergePatchFacesUndo
4671 (
4672 minCos,
4673 concaveCos,
4674 meshRefiner_.meshedPatches(),
4675 motionDict,
4676 duplicateFace,
4677 mergeType // How to merge co-planar patch faces
4678 );
4679
4680 nChanged += meshRefiner_.mergeEdgesUndo(minCos, motionDict);
4681}
4682
4683
4685(
4686 const layerParameters& layerParams,
4687 const dictionary& motionDict,
4688 const labelList& patchIDs,
4689 const label nAllowableErrors,
4690 decompositionMethod& decomposer,
4691 fvMeshDistribute& distributor
4692)
4693{
4694 fvMesh& mesh = meshRefiner_.mesh();
4695
4696 // Undistorted edge length
4697 const scalar edge0Len = meshRefiner_.meshCutter().level0EdgeLength();
4698
4699 // faceZones of type internal or baffle (for merging points across)
4700 labelList internalOrBaffleFaceZones;
4701 {
4703 fzTypes[0] = surfaceZonesInfo::INTERNAL;
4704 fzTypes[1] = surfaceZonesInfo::BAFFLE;
4705 internalOrBaffleFaceZones = meshRefiner_.getZones(fzTypes);
4706 }
4707
4708 // faceZones of type internal (for checking mesh quality across and
4709 // merging baffles)
4710 const labelList internalFaceZones
4711 (
4712 meshRefiner_.getZones
4713 (
4715 (
4716 1,
4718 )
4719 )
4720 );
4721
4722 // Create baffles (pairs of faces that share the same points)
4723 // Baffles stored as owner and neighbour face that have been created.
4724 List<labelPair> baffles;
4725 {
4726 labelList originatingFaceZone;
4727 meshRefiner_.createZoneBaffles
4728 (
4730 baffles,
4731 originatingFaceZone
4732 );
4733
4735 {
4736 const_cast<Time&>(mesh.time())++;
4737 Info<< "Writing baffled mesh to time "
4738 << meshRefiner_.timeName() << endl;
4739 meshRefiner_.write
4740 (
4743 (
4746 ),
4747 mesh.time().path()/meshRefiner_.timeName()
4748 );
4749 }
4750 }
4751
4752
4753 // Duplicate points on faceZones of type boundary. Should normally already
4754 // be done by snapping phase
4755 {
4756 autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldBoundaryPoints();
4757 if (map)
4758 {
4759 const labelList& reverseFaceMap = map->reverseFaceMap();
4760 forAll(baffles, i)
4761 {
4762 label f0 = reverseFaceMap[baffles[i].first()];
4763 label f1 = reverseFaceMap[baffles[i].second()];
4764 baffles[i] = labelPair(f0, f1);
4765 }
4766 }
4767 }
4768
4769
4770
4771 // Now we have all patches determine the number of layer per patch
4772 // This will be layerParams.numLayers() except for faceZones where one
4773 // side does get layers and the other not in which case we want to
4774 // suppress movement by explicitly setting numLayers 0
4775 labelList numLayers(layerParams.numLayers());
4776 {
4777 labelHashSet layerIDs(patchIDs);
4778 forAll(mesh.faceZones(), zonei)
4779 {
4780 label mpi, spi;
4782 bool hasInfo = meshRefiner_.getFaceZoneInfo
4783 (
4784 mesh.faceZones()[zonei].name(),
4785 mpi,
4786 spi,
4787 fzType
4788 );
4789 if (hasInfo)
4790 {
4791 const polyBoundaryMesh& pbm = mesh.boundaryMesh();
4792 if (layerIDs.found(mpi) && !layerIDs.found(spi))
4793 {
4794 // Only layers on master side. Fix points on slave side
4795 Info<< "On faceZone " << mesh.faceZones()[zonei].name()
4796 << " adding layers to master patch " << pbm[mpi].name()
4797 << " only. Freezing points on slave patch "
4798 << pbm[spi].name() << endl;
4799 numLayers[spi] = 0;
4800 }
4801 else if (!layerIDs.found(mpi) && layerIDs.found(spi))
4802 {
4803 // Only layers on slave side. Fix points on master side
4804 Info<< "On faceZone " << mesh.faceZones()[zonei].name()
4805 << " adding layers to slave patch " << pbm[spi].name()
4806 << " only. Freezing points on master patch "
4807 << pbm[mpi].name() << endl;
4808 numLayers[mpi] = 0;
4809 }
4810 }
4811 }
4812 }
4813
4814
4815 // Duplicate points on faceZones that layers are added to
4816 labelList pointToMaster;
4817 dupFaceZonePoints
4818 (
4819 patchIDs, // patch indices
4820 numLayers, // number of layers per patch
4821 baffles,
4822 pointToMaster
4823 );
4824
4825
4826 // Add layers to patches
4827 // ~~~~~~~~~~~~~~~~~~~~~
4828
4829 // Now we have
4830 // - mesh with optional baffles and duplicated points for faceZones
4831 // where layers are to be added
4832 // - pointToMaster : correspondence for duplicated points
4833 // - baffles : list of pairs of faces
4834
4835
4836 // Calculate 'base' point extrusion
4838 (
4840 (
4841 mesh,
4842 patchIDs
4843 )
4844 );
4845 // Make sure pp has adressing cached before changing mesh later on
4846 (void)pp().meshPoints();
4847 (void)pp().meshPointMap();
4848
4849 // Global face indices engine
4850 globalIndex globalFaces(mesh.nFaces());
4851
4852 // Determine extrudePatch.edgeFaces in global numbering (so across
4853 // coupled patches). This is used only to string up edges between coupled
4854 // faces (all edges between same (global)face indices get extruded).
4855 labelListList edgeGlobalFaces
4856 (
4858 (
4859 mesh,
4860 globalFaces,
4861 *pp
4862 )
4863 );
4864
4865
4866 // Point-wise extrusion data
4867 // ~~~~~~~~~~~~~~~~~~~~~~~~~
4868
4869 // Displacement for all pp.localPoints. Set to large value so does
4870 // not trigger the minThickness truncation (see syncPatchDisplacement below)
4871 vectorField basePatchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT));
4872
4873 // Number of layers for all pp.localPoints. Note: only valid if
4874 // extrudeStatus = EXTRUDE.
4875 labelList basePatchNLayers(pp().nPoints(), Zero);
4876
4877 // Whether to add edge for all pp.localPoints.
4878 List<extrudeMode> baseExtrudeStatus(pp().nPoints(), EXTRUDE);
4879
4880 // Ideal number of cells added
4881 const label nIdealTotAddedCells = setPointNumLayers
4882 (
4883 layerParams,
4884
4885 numLayers,
4886 patchIDs,
4887 pp(),
4888 edgeGlobalFaces,
4889
4890 basePatchDisp,
4891 basePatchNLayers,
4892 baseExtrudeStatus
4893 );
4894
4895 // Overall thickness of all layers
4896 scalarField baseThickness(pp().nPoints());
4897 // Truncation thickness - when to truncate layers
4898 scalarIOField baseMinThickness
4899 (
4900 IOobject
4901 (
4902 "minThickness",
4903 meshRefiner_.timeName(),
4904 mesh,
4906 ),
4907 pp().nPoints()
4908 );
4909 // Expansion ratio
4910 scalarField baseExpansionRatio(pp().nPoints());
4911 calculateLayerThickness
4912 (
4913 pp(),
4914 patchIDs,
4915 layerParams,
4916 meshRefiner_.meshCutter().cellLevel(),
4917 basePatchNLayers,
4918 edge0Len,
4919
4920 baseThickness,
4921 baseMinThickness,
4922 baseExpansionRatio
4923 );
4924
4925
4926 // Per cell 0 or number of layers in the cell column it is part of
4927 labelList cellNLayers;
4928 // Per face actual overall layer thickness
4929 scalarField faceRealThickness;
4930 // Per face wanted overall layer thickness
4931 scalarField faceWantedThickness(mesh.nFaces(), Zero);
4932 {
4933 UIndirectList<scalar>(faceWantedThickness, pp().addressing()) =
4934 avgPointData(pp(), baseThickness);
4935 }
4936
4937
4938 // Per patch point the number of layers to add. Is basePatchNLayers
4939 // for nOuterIter = 1.
4940 labelList deltaNLayers
4941 (
4942 (basePatchNLayers+layerParams.nOuterIter()-1)
4943 /layerParams.nOuterIter()
4944 );
4945
4946 // Per patch point the sum of added layers so far
4947 labelList nAddedLayers(basePatchNLayers.size(), 0);
4948
4949
4950 for (label layeri = 0; layeri < layerParams.nOuterIter(); layeri++)
4951 {
4952 // Divide layer addition into outer iterations. E.g. if
4953 // nOutIter = 2, numLayers is 20 for patchA and 1 for patchB
4954 // this will
4955 // - iter0:
4956 // - add 10 layers to patchA and 1 to patchB
4957 // - layers are finalLayerThickness down to the number of layers
4958 // - iter1 : add 10 layer to patchA and 0 to patchB
4959
4960
4961 // Exit if nothing to be added
4962 const label nToAdd = gSum(deltaNLayers);
4963 if (debug)
4964 {
4965 Info<< "Outer iteration : " << layeri
4966 << " to add in current iteration : " << nToAdd << endl;
4967 }
4968 if (nToAdd == 0)
4969 {
4970 break;
4971 }
4972
4973
4974 // Determine patches for extruded boundary edges. Calculates if any
4975 // additional processor patches need to be constructed.
4976
4977 labelList edgePatchID;
4978 labelList edgeZoneID;
4979 boolList edgeFlip;
4980 labelList inflateFaceID;
4981 determineSidePatches
4982 (
4983 globalFaces,
4984 edgeGlobalFaces,
4985 *pp,
4986
4987 edgePatchID,
4988 edgeZoneID,
4989 edgeFlip,
4990 inflateFaceID
4991 );
4992
4993
4994 // Point-wise extrusion data
4995 // ~~~~~~~~~~~~~~~~~~~~~~~~~
4996
4997 // Displacement for all pp.localPoints. Set to large value so does
4998 // not trigger the minThickness truncation (see syncPatchDisplacement
4999 // below)
5000 vectorField patchDisp(basePatchDisp);
5001
5002 // Number of layers for all pp.localPoints. Note: only valid if
5003 // extrudeStatus = EXTRUDE.
5004 labelList patchNLayers(deltaNLayers);
5005
5006 // Whether to add edge for all pp.localPoints.
5007 List<extrudeMode> extrudeStatus(baseExtrudeStatus);
5008
5009 // At this point
5010 // - patchDisp is either zero or a large value
5011 // - patchNLayers is the overall number of layers
5012 // - extrudeStatus is EXTRUDE or NOEXTRUDE
5013
5014 // Determine (wanted) point-wise overall layer thickness and expansion
5015 // ratio for this deltaNLayers slice of the overall layers
5016 scalarField sliceThickness(pp().nPoints());
5017 {
5018 forAll(baseThickness, pointi)
5019 {
5020 sliceThickness[pointi] = layerParameters::layerThickness
5021 (
5022 basePatchNLayers[pointi], // overall number of layers
5023 baseThickness[pointi], // overall thickness
5024 baseExpansionRatio[pointi], // expansion ratio
5025 basePatchNLayers[pointi]
5026 -nAddedLayers[pointi]
5027 -patchNLayers[pointi], // start index
5028 patchNLayers[pointi] // nLayers to add
5029 );
5030 }
5031 }
5032
5033
5034 // Current set of topology changes. (changing mesh clears out
5035 // polyTopoChange)
5036 polyTopoChange meshMod(mesh.boundaryMesh().size());
5037
5038 // Shrink mesh, add layers. Returns with any mesh changes in meshMod
5039 labelList sliceCellNLayers;
5040 scalarField sliceFaceRealThickness;
5041
5042 addLayers
5043 (
5044 layerParams,
5045 layerParams.nLayerIter(),
5046
5047 // Mesh quality
5048 motionDict,
5049 layerParams.nRelaxedIter(),
5050 nAllowableErrors,
5051
5052 patchIDs,
5053 internalFaceZones,
5054 baffles,
5055 numLayers,
5056 nIdealTotAddedCells,
5057
5058 // Patch information
5059 globalFaces,
5060 pp(),
5061 edgeGlobalFaces,
5062 edgePatchID,
5063 edgeZoneID,
5064 edgeFlip,
5065 inflateFaceID,
5066
5067 // Per patch point the wanted thickness
5068 sliceThickness,
5069 baseMinThickness,
5070 baseExpansionRatio,
5071
5072 // Per patch point the wanted&obtained layers and thickness
5073 patchDisp,
5074 patchNLayers,
5075 extrudeStatus,
5076
5077 // Complete mesh changes
5078 meshMod,
5079
5080 // Stats on new mesh
5081 sliceCellNLayers, //cellNLayers,
5082 sliceFaceRealThickness //faceRealThickness
5083 );
5084
5085
5086 // Exit if nothing added
5087 const label nTotalAdded = gSum(patchNLayers);
5088 if (debug)
5089 {
5090 Info<< "Outer iteration : " << layeri
5091 << " added in current iteration : " << nTotalAdded
5092 << " out of : " << gSum(deltaNLayers) << endl;
5093 }
5094 if (nTotalAdded == 0)
5095 {
5096 break;
5097 }
5098
5099
5100 // Update wanted layer statistics
5101 forAll(patchNLayers, pointi)
5102 {
5103 nAddedLayers[pointi] += patchNLayers[pointi];
5104
5105 if (patchNLayers[pointi] == 0)
5106 {
5107 // No layers were added. Make sure that overall extrusion
5108 // gets reset as well
5109 unmarkExtrusion
5110 (
5111 pointi,
5112 basePatchDisp,
5113 basePatchNLayers,
5114 baseExtrudeStatus
5115 );
5116 basePatchNLayers[pointi] = nAddedLayers[pointi];
5117 deltaNLayers[pointi] = 0;
5118 }
5119 else
5120 {
5121 // Adjust the number of layers for the next iteration.
5122 // Can never be
5123 // higher than the adjusted overall number of layers.
5124 // Note: is min necessary?
5125 deltaNLayers[pointi] = max
5126 (
5127 0,
5128 min
5129 (
5130 deltaNLayers[pointi],
5131 basePatchNLayers[pointi] - nAddedLayers[pointi]
5132 )
5133 );
5134 }
5135 }
5136
5137
5138 // At this point we have a (shrunk) mesh and a set of topology changes
5139 // which will make a valid mesh with layer. Apply these changes to the
5140 // current mesh.
5141
5142 {
5143 // Apply the stored topo changes to the current mesh.
5144 autoPtr<mapPolyMesh> mapPtr = meshMod.changeMesh(mesh, false);
5145 mapPolyMesh& map = *mapPtr;
5146
5147 // Hack to remove meshPhi - mapped incorrectly. TBD.
5148 mesh.moving(false);
5149 mesh.clearOut();
5150
5151 // Update fields
5152 mesh.updateMesh(map);
5153
5154 // Move mesh (since morphing does not do this)
5155 if (map.hasMotionPoints())
5156 {
5158 }
5159 else
5160 {
5161 // Delete mesh volumes.
5162 mesh.clearOut();
5163 }
5164
5165 // Reset the instance for if in overwrite mode
5166 mesh.setInstance(meshRefiner_.timeName());
5167
5168 meshRefiner_.updateMesh(map, labelList(0));
5169
5170 // Update numbering of accumulated cells
5171 cellNLayers.setSize(map.nOldCells(), 0);
5173 (
5174 map.cellMap(),
5175 label(0),
5176 cellNLayers
5177 );
5178 forAll(cellNLayers, i)
5179 {
5180 cellNLayers[i] += sliceCellNLayers[i];
5181 }
5182
5183 faceRealThickness.setSize(map.nOldFaces(), scalar(0));
5185 (
5186 map.faceMap(),
5187 scalar(0),
5188 faceRealThickness
5189 );
5190 faceRealThickness += sliceFaceRealThickness;
5191
5193 (
5194 map.faceMap(),
5195 scalar(0),
5196 faceWantedThickness
5197 );
5198
5199 // Print data now that we still have patches for the zones
5200 //if (meshRefinement::outputLevel() & meshRefinement::OUTPUTLAYERINFO)
5201 printLayerData
5202 (
5203 mesh,
5204 patchIDs,
5205 cellNLayers,
5206 faceWantedThickness,
5207 faceRealThickness
5208 );
5209
5210
5211 // Dump for debugging
5213 {
5214 const_cast<Time&>(mesh.time())++;
5215 Info<< "Writing mesh with layers but disconnected to time "
5216 << meshRefiner_.timeName() << endl;
5217 meshRefiner_.write
5218 (
5221 (
5224 ),
5225 mesh.time().path()/meshRefiner_.timeName()
5226 );
5227 }
5228
5229
5230 // Map baffles, pointToMaster
5231 mapFaceZonePoints(map, baffles, pointToMaster);
5232
5233 // Map patch and layer settings
5234 labelList newToOldPatchPoints;
5235 updatePatch(patchIDs, map, pp, newToOldPatchPoints);
5236
5237 // Global face indices engine
5238 globalFaces.reset(mesh.nFaces());
5239
5240 // Patch-edges to global faces using them
5241 edgeGlobalFaces = addPatchCellLayer::globalEdgeFaces
5242 (
5243 mesh,
5244 globalFaces,
5245 pp()
5246 );
5247
5248 // Map patch-point based data
5250 (
5251 newToOldPatchPoints,
5252 vector::uniform(-1),
5253 basePatchDisp
5254 );
5256 (
5257 newToOldPatchPoints,
5258 label(0),
5259 basePatchNLayers
5260 );
5262 (
5263 newToOldPatchPoints,
5264 extrudeMode::NOEXTRUDE,
5265 baseExtrudeStatus
5266 );
5268 (
5269 newToOldPatchPoints,
5270 scalar(0),
5271 baseThickness
5272 );
5274 (
5275 newToOldPatchPoints,
5276 scalar(0),
5277 baseMinThickness
5278 );
5280 (
5281 newToOldPatchPoints,
5282 GREAT,
5283 baseExpansionRatio
5284 );
5286 (
5287 newToOldPatchPoints,
5288 label(0),
5289 deltaNLayers
5290 );
5292 (
5293 newToOldPatchPoints,
5294 label(0),
5295 nAddedLayers
5296 );
5297 }
5298 }
5299
5300
5301 // Merge baffles
5302 mergeFaceZonePoints
5303 (
5304 pointToMaster, // per new mesh point : -1 or index of duplicated point
5305 cellNLayers, // per new cell : number of layers
5306 faceRealThickness, // per new face : actual thickness
5307 faceWantedThickness // per new face : wanted thickness
5308 );
5309
5310
5311 // Do final balancing
5312 // ~~~~~~~~~~~~~~~~~~
5313
5314 if (Pstream::parRun())
5315 {
5316 Info<< nl
5317 << "Doing final balancing" << nl
5318 << "---------------------" << nl
5319 << endl;
5320
5321 if (debug)
5322 {
5323 const_cast<Time&>(mesh.time())++;
5324 }
5325
5326 // Balance. No restriction on face zones and baffles.
5327 autoPtr<mapDistributePolyMesh> map = meshRefiner_.balance
5328 (
5329 false,
5330 false,
5331 scalarField(mesh.nCells(), 1.0),
5332 decomposer,
5333 distributor
5334 );
5335
5336 // Re-distribute flag of layer faces and cells
5337 map().distributeCellData(cellNLayers);
5338 map().distributeFaceData(faceWantedThickness);
5339 map().distributeFaceData(faceRealThickness);
5340 }
5341
5342
5343 // Write mesh data
5344 // ~~~~~~~~~~~~~~~
5345
5346 if (!dryRun_)
5347 {
5348 writeLayerData
5349 (
5350 mesh,
5351 patchIDs,
5352 cellNLayers,
5353 faceWantedThickness,
5354 faceRealThickness
5355 );
5356 }
5357}
5358
5359
5361(
5362 const dictionary& shrinkDict,
5363 const dictionary& motionDict,
5364 const layerParameters& layerParams,
5365 const meshRefinement::FaceMergeType mergeType,
5366 const bool preBalance,
5367 decompositionMethod& decomposer,
5368 fvMeshDistribute& distributor
5369)
5370{
5371 addProfiling(layers, "snappyHexMesh::layers");
5372 const fvMesh& mesh = meshRefiner_.mesh();
5373
5374 Info<< nl
5375 << "Shrinking and layer addition phase" << nl
5376 << "----------------------------------" << nl
5377 << endl;
5378
5379
5380 Info<< "Using mesh parameters " << motionDict << nl << endl;
5381
5382 // Merge coplanar boundary faces
5383 if
5384 (
5387 )
5388 {
5389 mergePatchFacesUndo(layerParams, motionDict, mergeType);
5390 }
5391
5392
5393 // Per patch the number of layers (-1 or 0 if no layer)
5394 const labelList& numLayers = layerParams.numLayers();
5395
5396 // Patches that need to get a layer
5397 DynamicList<label> patchIDs(numLayers.size());
5398 label nFacesWithLayers = 0;
5399 forAll(numLayers, patchi)
5400 {
5401 if (numLayers[patchi] > 0)
5402 {
5403 const polyPatch& pp = mesh.boundaryMesh()[patchi];
5404
5405 if (!pp.coupled())
5406 {
5407 patchIDs.append(patchi);
5408 nFacesWithLayers += mesh.boundaryMesh()[patchi].size();
5409 }
5410 else
5411 {
5413 << "Ignoring layers on coupled patch " << pp.name()
5414 << endl;
5415 }
5416 }
5417 }
5418
5419 // Add contributions from faceZones that get layers
5420 const faceZoneMesh& fZones = mesh.faceZones();
5421 forAll(fZones, zonei)
5422 {
5423 label mpi, spi;
5425 meshRefiner_.getFaceZoneInfo(fZones[zonei].name(), mpi, spi, fzType);
5426
5427 if (numLayers[mpi] > 0)
5428 {
5429 nFacesWithLayers += fZones[zonei].size();
5430 }
5431 if (numLayers[spi] > 0)
5432 {
5433 nFacesWithLayers += fZones[zonei].size();
5434 }
5435 }
5436
5437
5438 patchIDs.shrink();
5439
5440 if (returnReduce(nFacesWithLayers, sumOp<label>()) == 0)
5441 {
5442 Info<< nl << "No layers to generate ..." << endl;
5443 }
5444 else
5445 {
5446 // Check that outside of mesh is not multiply connected.
5447 checkMeshManifold();
5448
5449 // Check initial mesh
5450 Info<< "Checking initial mesh ..." << endl;
5451 labelHashSet wrongFaces(mesh.nFaces()/100);
5452 motionSmoother::checkMesh(false, mesh, motionDict, wrongFaces, dryRun_);
5453 const label nInitErrors = returnReduce
5454 (
5455 wrongFaces.size(),
5456 sumOp<label>()
5457 );
5458
5459 Info<< "Detected " << nInitErrors << " illegal faces"
5460 << " (concave, zero area or negative cell pyramid volume)"
5461 << endl;
5462
5463
5464 bool faceZoneOnCoupledFace = false;
5465
5466 if (!preBalance)
5467 {
5468 // Check if there are faceZones on processor boundaries. This
5469 // requires balancing to move them off the processor boundaries.
5470
5471 // Is face on a faceZone
5472 bitSet isExtrudedZoneFace(mesh.nFaces());
5473 {
5474 // Add contributions from faceZones that get layers
5475 const faceZoneMesh& fZones = mesh.faceZones();
5476 forAll(fZones, zonei)
5477 {
5478 const faceZone& fZone = fZones[zonei];
5479 const word& fzName = fZone.name();
5480
5481 label mpi, spi;
5483 meshRefiner_.getFaceZoneInfo(fzName, mpi, spi, fzType);
5484
5485 if (numLayers[mpi] > 0 || numLayers[spi])
5486 {
5487 isExtrudedZoneFace.set(fZone);
5488 }
5489 }
5490 }
5491
5492 bitSet intOrCoupled
5493 (
5495 );
5496
5497 for
5498 (
5499 label facei = mesh.nInternalFaces();
5500 facei < mesh.nFaces();
5501 facei++
5502 )
5503 {
5504 if (intOrCoupled[facei] && isExtrudedZoneFace.test(facei))
5505 {
5506 faceZoneOnCoupledFace = true;
5507 break;
5508 }
5509 }
5510
5511 reduce(faceZoneOnCoupledFace, orOp<bool>());
5512 }
5513
5514
5515
5516
5517 // Balance
5518 if (Pstream::parRun() && (preBalance || faceZoneOnCoupledFace))
5519 {
5520 Info<< nl
5521 << "Doing initial balancing" << nl
5522 << "-----------------------" << nl
5523 << endl;
5524
5525 scalarField cellWeights(mesh.nCells(), 1);
5526 forAll(numLayers, patchi)
5527 {
5528 if (numLayers[patchi] > 0)
5529 {
5530 const polyPatch& pp = mesh.boundaryMesh()[patchi];
5531 forAll(pp.faceCells(), i)
5532 {
5533 cellWeights[pp.faceCells()[i]] += numLayers[patchi];
5534 }
5535 }
5536 }
5537
5538 // Add contributions from faceZones that get layers
5539 const faceZoneMesh& fZones = mesh.faceZones();
5540 forAll(fZones, zonei)
5541 {
5542 const faceZone& fZone = fZones[zonei];
5543 const word& fzName = fZone.name();
5544
5545 label mpi, spi;
5547 meshRefiner_.getFaceZoneInfo(fzName, mpi, spi, fzType);
5548
5549 if (numLayers[mpi] > 0)
5550 {
5551 // Get the owner side for unflipped faces, neighbour side
5552 // for flipped ones
5553 const labelList& cellIDs = fZone.slaveCells();
5554 forAll(cellIDs, i)
5555 {
5556 if (cellIDs[i] >= 0)
5557 {
5558 cellWeights[cellIDs[i]] += numLayers[mpi];
5559 }
5560 }
5561 }
5562 if (numLayers[spi] > 0)
5563 {
5564 const labelList& cellIDs = fZone.masterCells();
5565 forAll(cellIDs, i)
5566 {
5567 if (cellIDs[i] >= 0)
5568 {
5569 cellWeights[cellIDs[i]] += numLayers[mpi];
5570 }
5571 }
5572 }
5573 }
5574
5575 // Balance mesh (and meshRefinement). Restrict faceZones to
5576 // be on internal faces only since they will be converted into
5577 // baffles.
5578 autoPtr<mapDistributePolyMesh> map = meshRefiner_.balance
5579 (
5580 true, // keepZoneFaces
5581 false,
5582 cellWeights,
5583 decomposer,
5584 distributor
5585 );
5586 }
5587
5588
5589 // Do all topo changes
5590 if (layerParams.nOuterIter() == -1)
5591 {
5592 // For testing. Can be removed once addLayers below works
5593 addLayersSinglePass
5594 (
5595 layerParams,
5596 motionDict,
5597 patchIDs,
5598 nInitErrors,
5599 decomposer,
5600 distributor
5601 );
5602 }
5603 else
5604 {
5605 addLayers
5606 (
5607 layerParams,
5608 motionDict,
5609 patchIDs,
5610 nInitErrors,
5611 decomposer,
5612 distributor
5613 );
5614 }
5615 }
5616}
5617
5618
5619// ************************************************************************* //
Istream and Ostream manipulators taking arguments.
label n
Info<< nl<< "Wrote faMesh in vtk format: "<< writer.output().name()<< nl;}{ vtk::lineWriter writer(aMesh.points(), aMesh.edges(), fileName(aMesh.mesh().time().globalPath()/"finiteArea-edges"));writer.writeGeometry();writer.beginCellData(4);writer.writeProcIDs();{ Field< scalar > fld(faMeshTools::flattenEdgeField(aMesh.magLe(), true))
labelList cellIDs
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:72
DynamicList< T, SizeMin > & shrink()
Shrink the allocated space to the number of elements used.
Definition: DynamicListI.H:434
void append(const T &val)
Copy append an element to the end of this list.
Definition: DynamicListI.H:503
SubField< scalar > subField
Declare type of subField.
Definition: Field.H:89
void setSize(const label n)
Dummy function, to make FixedList consistent with List.
Definition: FixedList.H:304
GeometricBoundaryField< scalar, fvPatchField, volMesh > Boundary
Type of boundary fields.
bool found(const Key &key) const
Return true if hashed entry is found in table.
Definition: HashTableI.H:100
label size() const noexcept
The number of elements in table.
Definition: HashTableI.H:52
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:170
const word & name() const noexcept
Return the object name.
Definition: IOobjectI.H:65
const fileName & instance() const noexcept
Read access to instance path component.
Definition: IOobjectI.H:196
writeOption writeOpt() const noexcept
The write option.
Definition: IOobjectI.H:179
void exit()
Job end with "exit" termination.
Definition: JobInfo.C:234
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: List.H:77
void transfer(List< T > &list)
Definition: List.C:447
void setSize(const label n)
Alias for resize()
Definition: List.H:218
void append(const T &val)
Append an element at the end of the list.
Definition: ListI.H:175
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:116
A HashTable to objects of type <T> with a label key.
Definition: Map.H:60
virtual const fileName & name() const
Get the name of the stream.
Definition: OSstream.H:107
virtual int precision() const
Get precision of output field.
Definition: OSstream.C:326
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
A list of faces which address into the list of points.
label nPoints() const
Number of points supporting patch faces.
const Field< point_type > & localPoints() const
Return pointField of points in patch.
virtual void movePoints(const Field< point_type > &)
Correct patch after moving points.
label nProcs() const noexcept
Number of ranks associated with PstreamBuffers.
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:80
static autoPtr< Time > New()
Construct (dummy) Time - no functionObjects or libraries.
Definition: Time.C:717
fileName path() const
Return path.
Definition: Time.H:358
static word timeName(const scalar t, const int precision=precision_)
Definition: Time.C:780
A List with indirect addressing. Like IndirectList but does not store addressing.
Definition: IndirectList.H:79
T & first()
Return the first element of the list.
Definition: UListI.H:202
label find(const T &val, label pos=0) const
Find index of the first occurrence of the value.
Definition: UList.C:212
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
label fcIndex(const label i) const noexcept
Definition: UListI.H:60
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:433
label size() const noexcept
The number of elements in the list.
Definition: UPtrListI.H:106
Adds layers of cells to outside of polyPatch. Can optionally create stand-alone extruded mesh (addToM...
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.
void updateMesh(const mapPolyMesh &, const labelList &faceMap, const labelList &pointMap)
Update any locally stored mesh information. Gets additional.
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:
labelListList addedCells() const
Added cells given current mesh & layerfaces.
static labelListList globalEdgeFaces(const polyMesh &, const globalIndex &globalFaces, const indirectPrimitivePatch &pp)
Per patch edge the pp faces (in global indices) using it.
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition: autoPtr.H:66
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
Definition: bitSet.H:66
void set(const bitSet &bitset)
Set specified bits from another bitset.
Definition: bitSetI.H:590
bool test(const label pos) const
Test value at specified position, never auto-vivify entries.
Definition: bitSetI.H:521
double cpuTimeIncrement() const
Return CPU time (in seconds) since last call to cpuTimeIncrement()
Definition: cpuTimeCxx.C:75
virtual const vectorField & pointNormals() const
Return point unit normals.
Abstract base class for domain decomposition.
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
const dictionary & subDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a sub-dictionary.
Definition: dictionary.C:460
bool merge(const dictionary &dict)
Merge entries from the given dictionary.
Definition: dictionary.C:812
entry * add(entry *entryPtr, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:640
Particle-size distribution model wherein random samples are drawn from the doubly-truncated uniform p...
Definition: uniform.H:164
string message() const
The accumulated error message.
Definition: error.C:319
A list of face labels.
Definition: faceSet.H:54
A subset of mesh faces organised as a primitive patch.
Definition: faceZone.H:67
const labelList & masterCells() const
Definition: faceZone.C:389
const labelList & slaveCells() const
Return labels of slave cells.
Definition: faceZone.C:400
Sends/receives parts of mesh+fvfields to neighbouring processors. Used in load balancing.
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:91
virtual bool write(const bool valid=true) const
Write mesh using IO settings from time.
Definition: fvMesh.C:1079
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:290
const word & name() const
Return reference to name.
Definition: fvMesh.H:310
virtual void updateMesh(const mapPolyMesh &mpm)
Update mesh corresponding to the given map.
Definition: fvMesh.C:971
virtual void movePoints(const pointField &)
Move points, returns volumes swept by faces in motion.
Definition: fvMesh.C:895
void clearOut()
Clear all geometry and addressing.
Definition: fvMesh.C:239
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:68
void reset(const label localSize, const label comm=UPstream::worldComm, const bool parallel=UPstream::parRun())
Definition: globalIndex.C:207
void checkMesh() const
Debug: Check coupled mesh for correctness.
Definition: hexRef8.C:4544
Simple container to keep together layer specific information.
scalar mergePatchFacesAngle() const
label nRelaxedIter() const
Number of iterations after which relaxed motion rules.
bool additionalReporting() const
Any additional reporting requested?
thicknessModelType
Enumeration defining the layer specification:
scalar concaveAngle() const
label nGrow() const
If points get not extruded do nGrow layers of connected faces.
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.
label nOuterIter() const
Outer loop to add layer by layer. Can be set to >= max layers.
label nBufferCellsNoExtrude() const
Create buffer region for new layer terminations.
label nLayerIter() const
Number of overall layer addition iterations.
static scalar finalLayerThicknessRatio(const label nLayers, const scalar expansionRatio)
Determine ratio of final layer thickness to.
const dictionary & dict() const
const labelList & numLayers() const
How many layers to add.
static labelPairList findDuplicateFacePairs(const polyMesh &)
Helper routine to find all baffles (two boundary faces.
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
Definition: mapPolyMesh.H:162
label nOldCells() const
Number of old cells.
Definition: mapPolyMesh.H:387
const labelList & faceMap() const
Old face map.
Definition: mapPolyMesh.H:410
const pointField & preMotionPoints() const
Pre-motion point positions.
Definition: mapPolyMesh.H:613
const labelList & cellMap() const
Old cell map.
Definition: mapPolyMesh.H:435
label nOldFaces() const
Number of old faces.
Definition: mapPolyMesh.H:381
const labelList & pointMap() const
Old point map.
Definition: mapPolyMesh.H:396
bool hasMotionPoints() const
Has valid preMotionPoints?
Definition: mapPolyMesh.H:619
Helper class which maintains intersections of (changing) mesh with (static) surfaces.
static List< labelPair > subsetBaffles(const polyMesh &mesh, const labelList &zoneIDs, const List< labelPair > &baffles)
Subset baffles according to zones.
writeType
Enumeration for what to write. Used as a bit-pattern.
debugType
Enumeration for what to debug. Used as a bit-pattern.
FaceMergeType
Enumeration for what to do with co-planar patch faces on a single.
static autoPtr< indirectPrimitivePatch > makePatch(const polyMesh &, const labelList &)
Create patch from set of patches.
static void updateList(const labelList &newToOld, const T &nullValue, List< T > &elems)
Helper: reorder list according to map.
static writeType writeLevel()
Get/set write level.
OSstream & stream(OSstream *alternative=nullptr)
Definition: messageStream.C:71
static void setDisplacement(const labelList &patchIDs, const indirectPrimitivePatch &pp, pointField &patchDisp, pointVectorField &displacement)
Set displacement field from displacement on patch points.
static const complex rootMax
complex (ROOTVGREAT, ROOTVGREAT)
Definition: complex.H:295
static const complex max
complex (VGREAT,VGREAT)
Definition: complex.H:293
const word & name() const noexcept
The patch name.
A polyBoundaryMesh is a polyPatch list with additional search methods and registered IO.
labelRange range() const noexcept
The face range for all boundary faces.
const labelList & patchID() const
Per boundary face label the patch index.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:81
const faceZoneMesh & faceZones() const noexcept
Return face zone mesh.
Definition: polyMesh.H:498
void setInstance(const fileName &instance, const IOobject::writeOption wOpt=IOobject::AUTO_WRITE)
Set the instance for mesh files.
Definition: polyMeshIO.C:36
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1108
bool moving() const noexcept
Is mesh moving.
Definition: polyMesh.H:534
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1121
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:456
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1083
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:75
virtual bool coupled() const
Return true if this patch is geometrically coupled (i.e. faces and.
Definition: polyPatch.H:380
const labelUList & faceCells() const
Return face-cell addressing.
Definition: polyPatch.C:371
Direct mesh changes based on v1.3 polyTopoChange syntax.
autoPtr< mapPolyMesh > changeMesh(polyMesh &mesh, const labelUList &patchMap, const bool inflate, const bool syncParallel=true, const bool orderCells=false, const bool orderPoints=false)
Inplace changes mesh without change of patches.
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.
bool isInternalFace(const label faceIndex) const noexcept
Return true if given face label is internal to the mesh.
const labelListList & pointEdges() const
const edgeList & edges() const
Return mesh edges. Uses calcEdges.
const vectorField & faceCentres() const
label nInternalFaces() const noexcept
Number of internal faces.
label nPoints() const noexcept
Number of mesh points.
label nCells() const noexcept
Number of mesh cells.
label nFaces() const noexcept
Number of mesh faces.
const cellList & cells() const
label nEdges() const
Number of mesh edges.
int myProcNo() const noexcept
Return processor number.
static word newName(const label myProcNo, const label neighbProcNo)
Return the name of a processorPolyPatch.
All to do with adding layers.
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)
void mergePatchFacesUndo(const layerParameters &layerParams, const dictionary &motionDict, const meshRefinement::FaceMergeType mergeType)
Merge patch faces on same cell.
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.
faceZoneType
What to do with faceZone faces.
static bitSet getInternalOrCoupledFaces(const polyMesh &mesh)
Get per face whether it is internal or coupled.
Definition: syncTools.C:176
static bitSet getMasterPoints(const polyMesh &mesh)
Definition: syncTools.C:68
static void syncPointList(const polyMesh &mesh, List< T > &pointValues, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh points.
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.
A class for managing temporary objects.
Definition: tmp.H:65
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
A class for handling words, derived from Foam::string.
Definition: word.H:68
const word & name() const noexcept
The zone name.
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition: className.H:121
volScalarField & p
const volScalarField & p0
Definition: EEqn.H:36
const polyBoundaryMesh & patches
dynamicFvMesh & mesh
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
const label nPatches
const pointField & points
label nPoints
surfaceVectorField faceNormals(mesh.Sf()/mesh.magSf())
#define IOWarningInFunction(ios)
Report an IO warning using Foam::Warning.
#define WarningInFunction
Report a warning using Foam::Warning.
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:37
Namespace for OpenFOAM.
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:57
List< word > wordList
A List of words.
Definition: fileName.H:63
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
Smanip< ios_base::fmtflags > setf(const ios_base::fmtflags flags)
Definition: IOmanip.H:169
List< labelPair > labelPairList
List of labelPairs.
Definition: labelPair.H:64
Type gSum(const FieldField< Field, Type > &f)
const dimensionSet dimless
Dimensionless.
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i)
Definition: labelList.C:38
List< label > labelList
A List of labels.
Definition: List.H:66
const dimensionSet dimLength(0, 1, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:52
line< point, const point & > linePointRef
A line using referred points.
Definition: linePointRef.H:47
constexpr label labelMin
Definition: label.H:60
prefixOSstream Perr
OSstream wrapped stderr (std::cerr) with parallel prefix.
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:82
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
messageStream Info
Information stream (stdout output on master, null elsewhere)
IOField< scalar > scalarIOField
scalarField with IO.
Definition: scalarIOField.H:44
GeometricField< vector, pointPatchField, pointMesh > pointVectorField
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:44
vector point
Point is a vector.
Definition: point.H:43
Omanip< int > setprecision(const int i)
Definition: IOmanip.H:205
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
constexpr label labelMax
Definition: label.H:61
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
label mergePoints(const PointList &points, labelList &pointToUnique, labelList &uniquePoints, const scalar mergeTol=SMALL, const bool verbose=false)
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:349
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:342
constexpr scalar degToRad() noexcept
Multiplication factor for degrees to radians conversion.
void inplaceReorder(const labelUList &oldToNew, ListType &input, const bool prune=false)
Inplace reorder the elements of a list.
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:56
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:33
pointField vertices(const blockVertexList &bvl)
errorManip< error > abort(error &err)
Definition: errorManip.H:144
dimensioned< vector > dimensionedVector
Dimensioned vector obtained from generic dimensioned type.
Field< vector > vectorField
Specialisation of Field<T> for vector.
IOerror FatalIOError
List< bool > boolList
A List of bools.
Definition: List.H:64
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition: HashSet.H:85
error FatalError
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
T returnReduce(const T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Reduce (copy) and return value.
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
labelListList invertOneToMany(const label len, const labelUList &map)
Invert one-to-many map. Unmapped elements will be size 0.
Definition: ListOps.C:114
Vector< scalar > vector
Definition: vector.H:61
List< face > faceList
A List of faces.
Definition: faceListFwd.H:47
PrimitivePatch< IndirectList< face >, const pointField & > indirectPrimitivePatch
A PrimitivePatch with an IndirectList for the faces, const reference for the point field.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:356
Type gMax(const FieldField< Field, Type > &f)
UIndirectList< label > labelUIndList
UIndirectList of labels.
Definition: IndirectList.H:68
dimensionedScalar cos(const dimensionedScalar &ds)
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
#define addProfiling(name, descr)
Define profiling trigger with specified name and description string.
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
label newPointi
Definition: readKivaGrid.H:496
labelList f(nPoints)
volScalarField & e
Definition: createFields.H:11
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
#define forAllConstIters(container, iter)
Iterate across all elements of the container object with const access.
Definition: stdFoam.H:278
static const char *const typeName
The type name used in ensight case files.
Unit conversion functions.