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