1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
11 OpenFOAM is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
16 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 Shrinking mesh (part of adding cell layers)
27 \*----------------------------------------------------------------------------*/
29 #include "autoLayerDriver.H"
32 #include "pointFields.H"
33 #include "motionSmoother.H"
34 #include "pointData.H"
35 #include "PointEdgeWave.H"
37 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
39 // Calculate inverse sum of edge weights (currently always 1.0)
40 void Foam::autoLayerDriver::sumWeights
42 const PackedBoolList& isMasterEdge,
43 const labelList& meshEdges,
44 const labelList& meshPoints,
45 const edgeList& edges,
46 scalarField& invSumWeight
53 if (isMasterEdge.get(meshEdges[edgeI]) == 1)
55 const edge& e = edges[edgeI];
56 //scalar eWeight = edgeWeights[edgeI];
59 invSumWeight[e[0]] += eWeight;
60 invSumWeight[e[1]] += eWeight;
64 syncTools::syncPointList
70 scalar(0.0) // null value
73 forAll(invSumWeight, pointI)
75 scalar w = invSumWeight[pointI];
79 invSumWeight[pointI] = 1.0/w;
85 // Smooth field on moving patch
86 void Foam::autoLayerDriver::smoothField
88 const motionSmoother& meshMover,
89 const PackedBoolList& isMasterEdge,
90 const labelList& meshEdges,
91 const scalarField& fieldMin,
92 const label nSmoothDisp,
96 const indirectPrimitivePatch& pp = meshMover.patch();
97 const edgeList& edges = pp.edges();
98 const labelList& meshPoints = pp.meshPoints();
100 scalarField invSumWeight(pp.nPoints());
110 // Get smoothly varying patch field.
111 Info<< "shrinkMeshDistance : Smoothing field ..." << endl;
113 for (label iter = 0; iter < nSmoothDisp; iter++)
115 scalarField average(pp.nPoints());
129 forAll(field, pointI)
131 //full smoothing neighbours + point value
132 average[pointI] = 0.5*(field[pointI]+average[pointI]);
134 // perform monotonic smoothing
137 average[pointI] < field[pointI]
138 && average[pointI] >= fieldMin[pointI]
141 field[pointI] = average[pointI];
145 // Do residual calculation every so often.
146 if ((iter % 10) == 0)
148 Info<< " Iteration " << iter << " residual "
149 << gSum(mag(field-average))
150 /returnReduce(average.size(), sumOp<label>())
157 // Smooth normals on moving patch.
158 void Foam::autoLayerDriver::smoothPatchNormals
160 const motionSmoother& meshMover,
161 const PackedBoolList& isMasterEdge,
162 const labelList& meshEdges,
163 const label nSmoothDisp,
167 const indirectPrimitivePatch& pp = meshMover.patch();
168 const edgeList& edges = pp.edges();
169 const labelList& meshPoints = pp.meshPoints();
171 // Calculate inverse sum of weights
173 scalarField invSumWeight(pp.nPoints());
183 // Get smoothly varying internal normals field.
184 Info<< "shrinkMeshDistance : Smoothing normals ..." << endl;
186 for (label iter = 0; iter < nSmoothDisp; iter++)
188 vectorField average(pp.nPoints());
201 // Do residual calculation every so often.
202 if ((iter % 10) == 0)
204 Info<< " Iteration " << iter << " residual "
205 << gSum(mag(normals-average))
206 /returnReduce(average.size(), sumOp<label>())
210 // Transfer to normals vector field
211 forAll(average, pointI)
213 // full smoothing neighbours + point value
214 average[pointI] = 0.5*(normals[pointI]+average[pointI]);
215 normals[pointI] = average[pointI];
216 normals[pointI] /= mag(normals[pointI]) + VSMALL;
222 // Smooth normals in interior.
223 void Foam::autoLayerDriver::smoothNormals
225 const label nSmoothDisp,
226 const PackedBoolList& isMasterEdge,
227 const labelList& fixedPoints,
228 pointVectorField& normals
231 // Get smoothly varying internal normals field.
232 Info<< "shrinkMeshDistance : Smoothing normals ..." << endl;
234 const fvMesh& mesh = meshRefiner_.mesh();
235 const edgeList& edges = mesh.edges();
237 // Points that do not change.
238 PackedBoolList isFixedPoint(mesh.nPoints());
240 // Internal points that are fixed
241 forAll(fixedPoints, i)
243 label meshPointI = fixedPoints[i];
244 isFixedPoint.set(meshPointI, 1);
247 // Correspondence between local edges/points and mesh edges/points
248 const labelList meshEdges(identity(mesh.nEdges()));
249 const labelList meshPoints(identity(mesh.nPoints()));
251 // Calculate inverse sum of weights
253 scalarField invSumWeight(mesh.nPoints(), 0);
263 Info<< "shrinkMeshDistance : Smoothing normals in interior ..." << endl;
265 for (label iter = 0; iter < nSmoothDisp; iter++)
267 vectorField average(mesh.nPoints());
280 // Do residual calculation every so often.
281 if ((iter % 10) == 0)
283 Info<< " Iteration " << iter << " residual "
284 << gSum(mag(normals-average))
285 /returnReduce(average.size(), sumOp<label>())
290 // Transfer to normals vector field
291 forAll(average, pointI)
293 if (isFixedPoint.get(pointI) == 0)
295 //full smoothing neighbours + point value
296 average[pointI] = 0.5*(normals[pointI]+average[pointI]);
297 normals[pointI] = average[pointI];
298 normals[pointI] /= mag(normals[pointI]) + VSMALL;
305 // Tries and find a medial axis point. Done by comparing vectors to nearest
306 // wall point for both vertices of edge.
307 bool Foam::autoLayerDriver::isMaxEdge
309 const List<pointData>& pointWallDist,
314 const fvMesh& mesh = meshRefiner_.mesh();
315 const pointField& points = mesh.points();
317 // Do not mark edges with one side on moving wall.
319 const edge& e = mesh.edges()[edgeI];
321 vector v0(points[e[0]] - pointWallDist[e[0]].origin());
322 scalar magV0(mag(v0));
329 vector v1(points[e[1]] - pointWallDist[e[1]].origin());
330 scalar magV1(mag(v1));
341 if ((v0 & v1) < minCos)
352 // Stop layer growth where mesh wraps around edge with a
353 // large feature angle
354 void Foam::autoLayerDriver::handleFeatureAngleLayerTerminations
356 const indirectPrimitivePatch& pp,
358 List<extrudeMode>& extrudeStatus,
359 pointField& patchDisp,
360 labelList& patchNLayers,
364 // Mark faces that have all points extruded
365 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
367 boolList extrudedFaces(pp.size(), true);
369 forAll(pp.localFaces(), faceI)
371 const face& f = pp.localFaces()[faceI];
375 if (extrudeStatus[f[fp]] == NOEXTRUDE)
377 extrudedFaces[faceI] = false;
384 // Detect situation where two featureedge-neighbouring faces are partly or
385 // not extruded and the edge itself is extruded. In this case unmark the
386 // edge for extrusion.
388 forAll(pp.edgeFaces(), edgeI)
390 const labelList& eFaces = pp.edgeFaces()[edgeI];
392 if (eFaces.size() == 2)
394 const edge& e = pp.edges()[edgeI];
400 extrudeStatus[v0] != NOEXTRUDE
401 || extrudeStatus[v1] != NOEXTRUDE
404 if (!extrudedFaces[eFaces[0]] || !extrudedFaces[eFaces[1]])
406 const vector& n0 = pp.faceNormals()[eFaces[0]];
407 const vector& n1 = pp.faceNormals()[eFaces[1]];
409 if ((n0 & n1) < minCos)
445 // Find isolated islands (points, edges and faces and layer terminations)
446 // in the layer mesh and stop any layer growth at these points.
447 void Foam::autoLayerDriver::findIsolatedRegions
449 const indirectPrimitivePatch& pp,
450 const PackedBoolList& isMasterEdge,
451 const labelList& meshEdges,
452 const scalar minCosLayerTermination,
454 List<extrudeMode>& extrudeStatus,
455 pointField& patchDisp,
456 labelList& patchNLayers
459 const fvMesh& mesh = meshRefiner_.mesh();
461 Info<< "shrinkMeshDistance : Removing isolated regions ..." << endl;
463 // Keep count of number of points unextruded
464 label nPointCounter = 0;
468 // Stop layer growth where mesh wraps around edge with a
469 // large feature angle
470 handleFeatureAngleLayerTerminations
473 minCosLayerTermination,
482 // Do not extrude from point where all neighbouring
483 // faces are not grown
484 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
485 boolList extrudedFaces(pp.size(), true);
486 forAll(pp.localFaces(), faceI)
488 const face& f = pp.localFaces()[faceI];
491 if (extrudeStatus[f[fp]] == NOEXTRUDE)
493 extrudedFaces[faceI] = false;
499 const labelListList& pointFaces = pp.pointFaces();
501 boolList keptPoints(pp.nPoints(), false);
502 forAll(keptPoints, patchPointI)
504 const labelList& pFaces = pointFaces[patchPointI];
508 label faceI = pFaces[i];
509 if (extrudedFaces[faceI])
511 keptPoints[patchPointI] = true;
517 syncTools::syncPointList
528 forAll(keptPoints, patchPointI)
530 if (!keptPoints[patchPointI])
550 if (returnReduce(nChanged, sumOp<label>()) == 0)
556 const edgeList& edges = pp.edges();
559 // Count number of mesh edges using a point
560 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
562 labelList isolatedPoint(pp.nPoints(),0);
566 if (isMasterEdge.get(meshEdges[edgeI]) == 1)
568 const edge& e = edges[edgeI];
573 if (extrudeStatus[v1] != NOEXTRUDE)
575 isolatedPoint[v0] += 1;
577 if (extrudeStatus[v0] != NOEXTRUDE)
579 isolatedPoint[v1] += 1;
584 syncTools::syncPointList
593 // stop layer growth on isolated faces
594 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
597 const face& f = pp.localFaces()[faceI];
601 if (isolatedPoint[f[fp]] > 2)
607 bool allPointsExtruded = true;
612 if (extrudeStatus[f[fp]] == NOEXTRUDE)
614 allPointsExtruded = false;
619 if (allPointsExtruded)
642 reduce(nPointCounter, sumOp<label>());
643 Info<< "Number isolated points extrusion stopped : "<< nPointCounter<< endl;
647 // Calculates medial axis fields:
648 // dispVec : normal of nearest wall. Where this normal changes direction
649 // defines the medial axis
650 // medialDist : distance to medial axis
651 // medialRatio : ratio of medial distance to wall distance.
652 // (1 at wall, 0 at medial axis)
653 void Foam::autoLayerDriver::medialAxisSmoothingInfo
655 const motionSmoother& meshMover,
656 const label nSmoothNormals,
657 const label nSmoothSurfaceNormals,
658 const scalar minMedianAxisAngleCos,
660 pointVectorField& dispVec,
661 pointScalarField& medialRatio,
662 pointScalarField& medialDist
666 Info<< "medialAxisSmoothingInfo :"
667 << " Calculate distance to Medial Axis ..." << endl;
669 const polyMesh& mesh = meshMover.mesh();
670 const pointField& points = mesh.points();
672 const indirectPrimitivePatch& pp = meshMover.patch();
673 const vectorField& faceNormals = pp.faceNormals();
674 const labelList& meshPoints = pp.meshPoints();
676 // Predetermine mesh edges
677 // ~~~~~~~~~~~~~~~~~~~~~~~
679 // Precalulate master edge (only relevant for shared edges)
680 PackedBoolList isMasterEdge(syncTools::getMasterEdges(mesh));
681 // Precalculate meshEdge per pp edge
682 labelList meshEdges(pp.nEdges());
684 forAll(meshEdges, patchEdgeI)
686 const edge& e = pp.edges()[patchEdgeI];
688 label v0 = pp.meshPoints()[e[0]];
689 label v1 = pp.meshPoints()[e[1]];
690 meshEdges[patchEdgeI] = meshTools::findEdge
693 mesh.pointEdges()[v0],
700 // Determine pointNormal
701 // ~~~~~~~~~~~~~~~~~~~~~
703 pointField pointNormals(pp.nPoints(), vector::zero);
705 labelList nPointFaces(pp.nPoints(), 0);
707 forAll(faceNormals, faceI)
709 const face& f = pp.localFaces()[faceI];
713 pointNormals[f[fp]] += faceNormals[faceI];
714 nPointFaces[f[fp]] ++;
718 syncTools::syncPointList
724 vector::zero // null value
727 syncTools::syncPointList
736 forAll(pointNormals, i)
738 pointNormals[i] /= nPointFaces[i];
742 // Smooth patch normal vectors
748 nSmoothSurfaceNormals,
753 // Calculate distance to pp points
754 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
757 List<pointData> pointWallDist(mesh.nPoints());
759 // Dummy additional info for PointEdgeWave
760 int dummyTrackData = 0;
763 // 1. Calculate distance to points where displacement is specified.
766 List<pointData> wallInfo(meshPoints.size());
768 forAll(meshPoints, patchPointI)
770 label pointI = meshPoints[patchPointI];
771 wallInfo[patchPointI] = pointData
775 pointI, // passive scalar
776 pointNormals[patchPointI] // surface normals
780 // Do all calculations
781 List<pointData> edgeWallDist(mesh.nEdges());
782 PointEdgeWave<pointData> wallDistCalc
789 mesh.globalData().nTotalPoints(), // max iterations
794 // 2. Find points with max distance and transport information back to
797 List<pointData> pointMedialDist(mesh.nPoints());
798 List<pointData> edgeMedialDist(mesh.nEdges());
801 DynamicList<pointData> maxInfo(meshPoints.size());
802 DynamicList<label> maxPoints(meshPoints.size());
804 // 1. Medial axis points
806 const edgeList& edges = mesh.edges();
810 if (isMaxEdge(pointWallDist, edgeI, minMedianAxisAngleCos))
812 // Both end points of edge have very different nearest wall
813 // point. Mark both points as medial axis points.
814 const edge& e = edges[edgeI];
818 label pointI = e[ep];
820 if (!pointMedialDist[pointI].valid(dummyTrackData))
822 maxPoints.append(pointI);
829 pointI, // passive data
830 vector::zero // passive data
833 pointMedialDist[pointI] = maxInfo.last();
840 // 2. Seed non-adapt patches
841 const polyBoundaryMesh& patches = mesh.boundaryMesh();
843 labelHashSet adaptPatches(meshMover.adaptPatchIDs());
845 forAll(patches, patchI)
847 const polyPatch& pp = patches[patchI];
852 && !isA<emptyPolyPatch>(pp)
853 && !adaptPatches.found(patchI)
856 Info<< "Inserting points on patch " << pp.name() << endl;
858 const labelList& meshPoints = pp.meshPoints();
860 forAll(meshPoints, i)
862 label pointI = meshPoints[i];
864 if (!pointMedialDist[pointI].valid(dummyTrackData))
866 maxPoints.append(pointI);
873 pointI, // passive data
874 vector::zero // passive data
877 pointMedialDist[pointI] = maxInfo.last();
886 // Do all calculations
887 PointEdgeWave<pointData> medialDistCalc
895 mesh.globalData().nTotalPoints(), // max iterations
899 // Extract medial axis distance as pointScalarField
900 forAll(pointMedialDist, pointI)
902 medialDist[pointI] = Foam::sqrt(pointMedialDist[pointI].distSqr());
906 // Extract transported surface normals as pointVectorField
909 dispVec[i] = pointWallDist[i].v();
912 // Smooth normal vectors. Do not change normals on pp.meshPoints
913 smoothNormals(nSmoothNormals, isMasterEdge, meshPoints, dispVec);
915 // Calculate ratio point medial distance to point wall distance
916 forAll(medialRatio, pointI)
918 scalar wDist2 = pointWallDist[pointI].distSqr();
919 scalar mDist = medialDist[pointI];
921 if (wDist2 < sqr(SMALL) && mDist < SMALL)
923 medialRatio[pointI] = 0.0;
927 medialRatio[pointI] = mDist / (Foam::sqrt(wDist2) + mDist);
933 Info<< "medialAxisSmoothingInfo :"
935 << " " << dispVec.name()
936 << " : normalised direction of nearest displacement" << nl
937 << " " << medialDist.name()
938 << " : distance to medial axis" << nl
939 << " " << medialRatio.name()
940 << " : ratio of medial distance to wall distance" << nl
949 void Foam::autoLayerDriver::shrinkMeshMedialDistance
951 motionSmoother& meshMover,
952 const dictionary& meshQualityDict,
953 const List<labelPair>& baffles,
954 const label nSmoothThickness,
955 const scalar maxThicknessToMedialRatio,
956 const label nAllowableErrors,
958 const scalar minCosLayerTermination,
960 const scalarField& layerThickness,
961 const scalarField& minThickness,
963 const pointVectorField& dispVec,
964 const pointScalarField& medialRatio,
965 const pointScalarField& medialDist,
967 List<extrudeMode>& extrudeStatus,
968 pointField& patchDisp,
969 labelList& patchNLayers
972 Info<< "shrinkMeshMedialDistance : Smoothing using Medial Axis ..." << endl;
974 const polyMesh& mesh = meshMover.mesh();
976 const indirectPrimitivePatch& pp = meshMover.patch();
977 const labelList& meshPoints = pp.meshPoints();
979 // Precalulate master edge (only relevant for shared edges)
980 PackedBoolList isMasterEdge(syncTools::getMasterEdges(mesh));
981 // Precalculate meshEdge per pp edge
982 labelList meshEdges(pp.nEdges());
984 forAll(meshEdges, patchEdgeI)
986 const edge& e = pp.edges()[patchEdgeI];
988 label v0 = pp.meshPoints()[e[0]];
989 label v1 = pp.meshPoints()[e[1]];
990 meshEdges[patchEdgeI] = meshTools::findEdge
993 mesh.pointEdges()[v0],
1000 scalarField thickness(layerThickness.size());
1002 thickness = mag(patchDisp);
1004 forAll(thickness, patchPointI)
1006 if (extrudeStatus[patchPointI] == NOEXTRUDE)
1008 thickness[patchPointI] = 0.0;
1012 label numThicknessRatioExclude = 0;
1014 // reduce thickness where thickness/medial axis distance large
1015 forAll(meshPoints, patchPointI)
1017 if (extrudeStatus[patchPointI] != NOEXTRUDE)
1019 label pointI = meshPoints[patchPointI];
1021 scalar mDist = medialDist[pointI];
1023 scalar thicknessRatio = thickness[patchPointI]/(mDist+VSMALL);
1025 if (thicknessRatio > maxThicknessToMedialRatio)
1027 // Truncate thickness.
1028 thickness[patchPointI] =
1029 0.5*(minThickness[patchPointI]+thickness[patchPointI]);
1031 patchDisp[patchPointI] =
1032 thickness[patchPointI]
1033 * patchDisp[patchPointI]
1034 / (mag(patchDisp[patchPointI]) + VSMALL);
1035 numThicknessRatioExclude++;
1040 reduce(numThicknessRatioExclude, sumOp<label>());
1041 Info<< "shrinkMeshMedialDistance : Reduce layer thickness at "
1042 << numThicknessRatioExclude
1043 << " nodes where thickness to medial axis distance is large " << endl;
1045 // find points where layer growth isolated to a lone point, edge or face
1052 minCosLayerTermination,
1059 // smooth layer thickness on moving patch
1070 List<pointData> pointWallDist(mesh.nPoints());
1072 const pointField& points = mesh.points();
1073 // 1. Calculate distance to points where displacement is specified.
1074 // This wave is used to transport layer thickness
1076 // Distance to wall and medial axis on edges.
1077 List<pointData> edgeWallDist(mesh.nEdges());
1078 labelList wallPoints(meshPoints.size());
1081 List<pointData> wallInfo(meshPoints.size());
1083 forAll(meshPoints, patchPointI)
1085 label pointI = meshPoints[patchPointI];
1086 wallPoints[patchPointI] = pointI;
1087 wallInfo[patchPointI] = pointData
1091 thickness[patchPointI], // transport layer thickness
1092 vector::zero // passive vector
1096 // Do all calculations
1097 PointEdgeWave<pointData> wallDistCalc
1104 mesh.globalData().nTotalPoints() // max iterations
1108 // Calculate scaled displacement vector
1109 pointVectorField& displacement = meshMover.displacement();
1111 forAll(displacement, pointI)
1113 // 1) displacement on nearest wall point, scaled by medialRatio
1114 // (wall distance / medial distance)
1115 // 2) pointWallDist[pointI].s() is layer thickness transported
1116 // from closest wall point.
1117 // 3) shrink in opposite direction of addedPoints
1118 displacement[pointI] =
1119 -medialRatio[pointI]
1120 *pointWallDist[pointI].s()
1124 // Current faces to check. Gets modified in meshMover.scaleMesh
1125 labelList checkFaces(identity(mesh.nFaces()));
1127 Info<< "shrinkMeshMedialDistance : Moving mesh ..." << endl;
1128 scalar oldErrorReduction = -1;
1130 for (label iter = 0; iter < 2*nSnap ; iter++)
1132 Info<< "Iteration " << iter << endl;
1135 Info<< "Displacement scaling for error reduction set to 0." << endl;
1136 oldErrorReduction = meshMover.setErrorReduction(0.0);
1145 meshMover.paramDict(),
1152 Info<< "shrinkMeshMedialDistance : Successfully moved mesh" << endl;
1157 if (oldErrorReduction >= 0)
1159 meshMover.setErrorReduction(oldErrorReduction);
1162 Info<< "shrinkMeshMedialDistance : Finished moving mesh ..." << endl;
1166 // ************************************************************************* //