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 All to do with snapping to the surface
27 \*----------------------------------------------------------------------------*/
29 #include "autoSnapDriver.H"
30 #include "motionSmoother.H"
31 #include "polyTopoChange.H"
33 #include "syncTools.H"
37 #include "mapPolyMesh.H"
38 #include "pointEdgePoint.H"
39 #include "PointEdgeWave.H"
40 #include "mergePoints.H"
41 #include "snapParameters.H"
42 #include "refinementSurfaces.H"
43 #include "unitConversion.H"
45 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
50 defineTypeNameAndDebug(autoSnapDriver, 0);
52 } // End namespace Foam
55 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
57 // Calculate geometrically collocated points, Requires PackedList to be
58 // sized and initalised!
59 Foam::label Foam::autoSnapDriver::getCollocatedPoints
62 const pointField& points,
63 PackedBoolList& isCollocatedPoint
68 bool hasMerged = mergePoints
77 if (!returnReduce(hasMerged, orOp<bool>()))
82 // Determine which newPoints are referenced more than once
83 label nCollocated = 0;
85 // Per old point the newPoint. Or -1 (not set yet) or -2 (already seen
87 labelList firstOldPoint(newPoints.size(), -1);
88 forAll(pointMap, oldPointI)
90 label newPointI = pointMap[oldPointI];
92 if (firstOldPoint[newPointI] == -1)
94 // First use of oldPointI. Store.
95 firstOldPoint[newPointI] = oldPointI;
97 else if (firstOldPoint[newPointI] == -2)
99 // Third or more reference of oldPointI -> non-manifold
100 isCollocatedPoint.set(oldPointI, 1u);
105 // Second reference of oldPointI -> non-manifold
106 isCollocatedPoint.set(firstOldPoint[newPointI], 1u);
109 isCollocatedPoint.set(oldPointI, 1u);
112 // Mark with special value to save checking next time round
113 firstOldPoint[newPointI] = -2;
116 return returnReduce(nCollocated, sumOp<label>());
120 // Calculate displacement as average of patch points.
121 Foam::pointField Foam::autoSnapDriver::smoothPatchDisplacement
123 const motionSmoother& meshMover,
124 const List<labelPair>& baffles
127 const indirectPrimitivePatch& pp = meshMover.patch();
129 // Calculate geometrically non-manifold points on the patch to be moved.
130 PackedBoolList nonManifoldPoint(pp.nPoints());
131 label nNonManifoldPoints = getCollocatedPoints
137 Info<< "Found " << nNonManifoldPoints << " non-mainfold point(s)."
144 // We determine three points:
145 // - average of (centres of) connected patch faces
146 // - average of (centres of) connected internal mesh faces
147 // - as fallback: centre of any connected cell
148 // so we can do something moderately sensible for non/manifold points.
150 // Note: the averages are calculated properly parallel. This is
151 // necessary to get the points shared by processors correct.
154 const labelListList& pointFaces = pp.pointFaces();
155 const labelList& meshPoints = pp.meshPoints();
156 const pointField& points = pp.points();
157 const polyMesh& mesh = meshMover.mesh();
159 // Get labels of faces to count (master of coupled faces and baffle pairs)
160 PackedBoolList isMasterFace(syncTools::getMasterFaces(mesh));
165 label f0 = baffles[i].first();
166 label f1 = baffles[i].second();
168 if (isMasterFace.get(f0))
171 isMasterFace.unset(f1);
173 else if (isMasterFace.get(f1))
175 isMasterFace.unset(f0);
179 FatalErrorIn("autoSnapDriver::smoothPatchDisplacement(..)")
180 << "Both sides of baffle consisting of faces " << f0
181 << " and " << f1 << " are already slave faces."
182 << abort(FatalError);
188 // Get average position of boundary face centres
189 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
191 vectorField avgBoundary(pointFaces.size(), vector::zero);
192 labelList nBoundary(pointFaces.size(), 0);
194 forAll(pointFaces, patchPointI)
196 const labelList& pFaces = pointFaces[patchPointI];
200 label faceI = pFaces[pfI];
202 if (isMasterFace.get(pp.addressing()[faceI]))
204 avgBoundary[patchPointI] += pp[faceI].centre(points);
205 nBoundary[patchPointI]++;
210 syncTools::syncPointList
215 plusEqOp<point>(), // combine op
216 vector::zero // null value
218 syncTools::syncPointList
223 plusEqOp<label>(), // combine op
227 forAll(avgBoundary, i)
229 avgBoundary[i] /= nBoundary[i];
233 // Get average position of internal face centres
234 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
236 vectorField avgInternal;
239 vectorField globalSum(mesh.nPoints(), vector::zero);
240 labelList globalNum(mesh.nPoints(), 0);
242 // Note: no use of pointFaces
243 const faceList& faces = mesh.faces();
245 for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++)
247 const face& f = faces[faceI];
248 const point& fc = mesh.faceCentres()[faceI];
252 globalSum[f[fp]] += fc;
257 // Count coupled faces as internal ones (but only once)
258 const polyBoundaryMesh& patches = mesh.boundaryMesh();
260 forAll(patches, patchI)
264 patches[patchI].coupled()
265 && refCast<const coupledPolyPatch>(patches[patchI]).owner()
268 const coupledPolyPatch& pp =
269 refCast<const coupledPolyPatch>(patches[patchI]);
271 const vectorField::subField faceCentres = pp.faceCentres();
275 const face& f = pp[i];
276 const point& fc = faceCentres[i];
280 globalSum[f[fp]] += fc;
287 syncTools::syncPointList
291 plusEqOp<vector>(), // combine op
292 vector::zero // null value
294 syncTools::syncPointList
298 plusEqOp<label>(), // combine op
302 avgInternal.setSize(meshPoints.size());
303 nInternal.setSize(meshPoints.size());
305 forAll(avgInternal, patchPointI)
307 label meshPointI = meshPoints[patchPointI];
309 nInternal[patchPointI] = globalNum[meshPointI];
311 if (nInternal[patchPointI] == 0)
313 avgInternal[patchPointI] = globalSum[meshPointI];
317 avgInternal[patchPointI] =
318 globalSum[meshPointI]
319 / nInternal[patchPointI];
325 // Precalculate any cell using mesh point (replacement of pointCells()[])
326 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
328 labelList anyCell(mesh.nPoints(), -1);
329 forAll(mesh.faceNeighbour(), faceI)
331 label own = mesh.faceOwner()[faceI];
332 const face& f = mesh.faces()[faceI];
336 anyCell[f[fp]] = own;
339 for (label faceI = mesh.nInternalFaces(); faceI < mesh.nFaces(); faceI++)
341 label own = mesh.faceOwner()[faceI];
343 const face& f = mesh.faces()[faceI];
347 anyCell[f[fp]] = own;
352 // Displacement to calculate.
353 pointField patchDisp(meshPoints.size(), vector::zero);
355 forAll(pointFaces, i)
357 label meshPointI = meshPoints[i];
358 const point& currentPos = pp.points()[meshPointI];
360 // Now we have the two average points: avgBoundary and avgInternal
361 // and how many boundary/internal faces connect to the point
362 // (nBoundary, nInternal)
363 // Do some blending between the two.
364 // Note: the following section has some reasoning behind it but the
365 // blending factors can be experimented with.
369 if (!nonManifoldPoint.get(i))
371 // Points that are manifold. Weight the internal and boundary
372 // by their number of faces and blend with
373 scalar internalBlend = 0.1;
378 internalBlend*nInternal[i]*avgInternal[i]
379 +(1-internalBlend)*nBoundary[i]*avgBoundary[i]
381 / (internalBlend*nInternal[i]+(1-internalBlend)*nBoundary[i]);
383 newPos = (1-blend)*avgPos + blend*currentPos;
385 else if (nInternal[i] == 0)
387 // Non-manifold without internal faces. Use any connected cell
388 // as internal point instead. Use precalculated any cell to avoid
389 // e.g. pointCells()[meshPointI][0]
391 const point& cc = mesh.cellCentres()[anyCell[meshPointI]];
393 scalar cellCBlend = 0.8;
396 point avgPos = (1-cellCBlend)*avgBoundary[i] + cellCBlend*cc;
398 newPos = (1-blend)*avgPos + blend*currentPos;
402 // Non-manifold point with internal faces connected to them
403 scalar internalBlend = 0.9;
407 internalBlend*avgInternal[i]
408 + (1-internalBlend)*avgBoundary[i];
410 newPos = (1-blend)*avgPos + blend*currentPos;
413 patchDisp[i] = newPos - currentPos;
420 Foam::tmp<Foam::scalarField> Foam::autoSnapDriver::edgePatchDist
422 const pointMesh& pMesh,
423 const indirectPrimitivePatch& pp
426 const polyMesh& mesh = pMesh();
428 // Set initial changed points to all the patch points
429 List<pointEdgePoint> wallInfo(pp.nPoints());
431 forAll(pp.localPoints(), ppI)
433 wallInfo[ppI] = pointEdgePoint(pp.localPoints()[ppI], 0.0);
436 // Current info on points
437 List<pointEdgePoint> allPointInfo(mesh.nPoints());
439 // Current info on edges
440 List<pointEdgePoint> allEdgeInfo(mesh.nEdges());
442 PointEdgeWave<pointEdgePoint> wallCalc
450 mesh.globalData().nTotalPoints() // max iterations
453 // Copy edge values into scalarField
454 tmp<scalarField> tedgeDist(new scalarField(mesh.nEdges()));
455 scalarField& edgeDist = tedgeDist();
457 forAll(allEdgeInfo, edgeI)
459 edgeDist[edgeI] = Foam::sqrt(allEdgeInfo[edgeI].distSqr());
464 // // For debugging: dump to file
465 // pointScalarField pointDist
470 // meshRefiner_.timeName(),
472 // IOobject::NO_READ,
473 // IOobject::AUTO_WRITE
476 // dimensionedScalar("pointDist", dimless, 0.0)
479 // forAll(allEdgeInfo, edgeI)
481 // scalar d = Foam::sqrt(allEdgeInfo[edgeI].distSqr());
483 // const edge& e = mesh.edges()[edgeI];
485 // pointDist[e[0]] += d;
486 // pointDist[e[1]] += d;
488 // forAll(pointDist, pointI)
490 // pointDist[pointI] /= mesh.pointEdges()[pointI].size();
492 // Info<< "Writing patch distance to " << pointDist.name()
493 // << " at time " << meshRefiner_.timeName() << endl;
495 // pointDist.write();
502 void Foam::autoSnapDriver::dumpMove
504 const fileName& fName,
505 const pointField& meshPts,
506 const pointField& surfPts
509 // Dump direction of growth into file
510 Pout<< nl << "Dumping move direction to " << fName << endl;
512 OFstream nearestStream(fName);
518 meshTools::writeOBJ(nearestStream, meshPts[ptI]);
521 meshTools::writeOBJ(nearestStream, surfPts[ptI]);
524 nearestStream<< "l " << vertI-1 << ' ' << vertI << nl;
529 // Check whether all displacement vectors point outwards of patch. Return true
531 bool Foam::autoSnapDriver::outwardsDisplacement
533 const indirectPrimitivePatch& pp,
534 const vectorField& patchDisp
537 const vectorField& faceNormals = pp.faceNormals();
538 const labelListList& pointFaces = pp.pointFaces();
540 forAll(pointFaces, pointI)
542 const labelList& pFaces = pointFaces[pointI];
544 vector disp(patchDisp[pointI]);
546 scalar magDisp = mag(disp);
552 bool outwards = meshTools::visNormal(disp, faceNormals, pFaces);
556 Warning<< "Displacement " << patchDisp[pointI]
557 << " at mesh point " << pp.meshPoints()[pointI]
558 << " coord " << pp.points()[pp.meshPoints()[pointI]]
559 << " points through the surrounding patch faces" << endl;
565 //? Displacement small but in wrong direction. Would probably be ok.
572 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
574 Foam::autoSnapDriver::autoSnapDriver
576 meshRefinement& meshRefiner,
577 const labelList& globalToPatch
580 meshRefiner_(meshRefiner),
581 globalToPatch_(globalToPatch)
585 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
587 Foam::autoPtr<Foam::mapPolyMesh> Foam::autoSnapDriver::mergeZoneBaffles
589 const List<labelPair>& baffles
592 labelList zonedSurfaces = meshRefiner_.surfaces().getNamedSurfaces();
594 autoPtr<mapPolyMesh> map;
596 // No need to sync; all processors will have all same zonedSurfaces.
597 label nBaffles = returnReduce(baffles.size(), sumOp<label>());
598 if (zonedSurfaces.size() && nBaffles > 0)
601 Info<< "Converting " << nBaffles << " baffles back into zoned faces ..."
604 map = meshRefiner_.mergeBaffles(baffles);
606 Info<< "Converted baffles in = "
607 << meshRefiner_.mesh().time().cpuTimeIncrement()
608 << " s\n" << nl << endl;
615 Foam::scalarField Foam::autoSnapDriver::calcSnapDistance
617 const snapParameters& snapParams,
618 const indirectPrimitivePatch& pp
621 const edgeList& edges = pp.edges();
622 const labelListList& pointEdges = pp.pointEdges();
623 const pointField& localPoints = pp.localPoints();
624 const fvMesh& mesh = meshRefiner_.mesh();
626 scalarField maxEdgeLen(localPoints.size(), -GREAT);
628 forAll(pointEdges, pointI)
630 const labelList& pEdges = pointEdges[pointI];
632 forAll(pEdges, pEdgeI)
634 const edge& e = edges[pEdges[pEdgeI]];
636 scalar len = e.mag(localPoints);
638 maxEdgeLen[pointI] = max(maxEdgeLen[pointI], len);
642 syncTools::syncPointList
647 maxEqOp<scalar>(), // combine op
651 return scalarField(snapParams.snapTol()*maxEdgeLen);
655 void Foam::autoSnapDriver::preSmoothPatch
657 const snapParameters& snapParams,
658 const label nInitErrors,
659 const List<labelPair>& baffles,
660 motionSmoother& meshMover
663 const fvMesh& mesh = meshRefiner_.mesh();
665 labelList checkFaces;
667 Info<< "Smoothing patch points ..." << endl;
670 label smoothIter = 0;
671 smoothIter < snapParams.nSmoothPatch();
675 Info<< "Smoothing iteration " << smoothIter << endl;
676 checkFaces.setSize(mesh.nFaces());
677 forAll(checkFaces, faceI)
679 checkFaces[faceI] = faceI;
682 pointField patchDisp(smoothPatchDisplacement(meshMover, baffles));
684 // The current mesh is the starting mesh to smooth from.
685 meshMover.setDisplacement(patchDisp);
689 scalar oldErrorReduction = -1;
691 for (label snapIter = 0; snapIter < 2*snapParams.nSnap(); snapIter++)
693 Info<< nl << "Scaling iteration " << snapIter << endl;
695 if (snapIter == snapParams.nSnap())
697 Info<< "Displacement scaling for error reduction set to 0."
699 oldErrorReduction = meshMover.setErrorReduction(0.0);
702 // Try to adapt mesh to obtain displacement by smoothly
703 // decreasing displacement at error locations.
704 if (meshMover.scaleMesh(checkFaces, baffles, true, nInitErrors))
706 Info<< "Successfully moved mesh" << endl;
711 if (oldErrorReduction >= 0)
713 meshMover.setErrorReduction(oldErrorReduction);
719 // The current mesh is the starting mesh to smooth from.
724 const_cast<Time&>(mesh.time())++;
725 Pout<< "Writing patch smoothed mesh to time "
726 << meshRefiner_.timeName() << '.' << endl;
730 mesh.time().path()/meshRefiner_.timeName()
732 Pout<< "Dumped mesh in = "
733 << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
736 Info<< "Patch points smoothed in = "
737 << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
741 // Get (pp-local) indices of points that are both on zone and on patched surface
742 Foam::labelList Foam::autoSnapDriver::getZoneSurfacePoints
744 const indirectPrimitivePatch& pp,
748 const fvMesh& mesh = meshRefiner_.mesh();
750 label zoneI = mesh.faceZones().findZoneID(zoneName);
756 "autoSnapDriver::getZoneSurfacePoints"
757 "(const indirectPrimitivePatch&, const word&)"
758 ) << "Cannot find zone " << zoneName
762 const faceZone& fZone = mesh.faceZones()[zoneI];
765 // Could use PrimitivePatch & localFaces to extract points but might just
766 // as well do it ourselves.
768 boolList pointOnZone(pp.nPoints(), false);
772 const face& f = mesh.faces()[fZone[i]];
776 label meshPointI = f[fp];
778 Map<label>::const_iterator iter =
779 pp.meshPointMap().find(meshPointI);
781 if (iter != pp.meshPointMap().end())
783 label pointI = iter();
784 pointOnZone[pointI] = true;
789 return findIndices(pointOnZone, true);
793 Foam::vectorField Foam::autoSnapDriver::calcNearestSurface
795 const scalarField& snapDist,
796 motionSmoother& meshMover
799 Info<< "Calculating patchDisplacement as distance to nearest surface"
800 << " point ..." << endl;
802 const indirectPrimitivePatch& pp = meshMover.patch();
803 const pointField& localPoints = pp.localPoints();
804 const refinementSurfaces& surfaces = meshRefiner_.surfaces();
805 const fvMesh& mesh = meshRefiner_.mesh();
807 // Displacement per patch point
808 vectorField patchDisp(localPoints.size(), vector::zero);
810 if (returnReduce(localPoints.size(), sumOp<label>()) > 0)
812 // Current surface snapped to
813 labelList snapSurf(localPoints.size(), -1);
815 // Divide surfaces into zoned and unzoned
816 labelList zonedSurfaces =
817 meshRefiner_.surfaces().getNamedSurfaces();
818 labelList unzonedSurfaces =
819 meshRefiner_.surfaces().getUnnamedSurfaces();
822 // 1. All points to non-interface surfaces
823 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
826 List<pointIndexHit> hitInfo;
827 labelList hitSurface;
832 sqr(snapDist), // sqr of attract distance
837 forAll(hitInfo, pointI)
839 if (hitInfo[pointI].hit())
842 hitInfo[pointI].hitPoint()
843 - localPoints[pointI];
845 snapSurf[pointI] = hitSurface[pointI];
852 // 2. All points on zones to their respective surface
853 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
855 // Surfaces with zone information
856 const wordList& faceZoneNames = surfaces.faceZoneNames();
858 // Current best snap distance
859 scalarField minSnapDist(snapDist);
861 forAll(zonedSurfaces, i)
863 label zoneSurfI = zonedSurfaces[i];
865 const labelList surfacesToTest(1, zoneSurfI);
867 // Get indices of points both on faceZone and on pp.
868 labelList zonePointIndices
873 faceZoneNames[zoneSurfI]
877 // Find nearest for points both on faceZone and pp.
878 List<pointIndexHit> hitInfo;
879 labelList hitSurface;
882 labelList(1, zoneSurfI),
883 pointField(localPoints, zonePointIndices),
884 sqr(scalarField(minSnapDist, zonePointIndices)),
891 label pointI = zonePointIndices[i];
893 if (hitInfo[i].hit())
896 hitInfo[i].hitPoint()
897 - localPoints[pointI];
899 minSnapDist[pointI] = min
902 mag(patchDisp[pointI])
905 snapSurf[pointI] = zoneSurfI;
910 // Check if all points are being snapped
911 forAll(snapSurf, pointI)
913 if (snapSurf[pointI] == -1)
915 WarningIn("autoSnapDriver::calcNearestSurface(..)")
916 << "For point:" << pointI
917 << " coordinate:" << localPoints[pointI]
918 << " did not find any surface within:"
919 << minSnapDist[pointI]
920 << " meter." << endl;
925 scalarField magDisp(mag(patchDisp));
927 Info<< "Wanted displacement : average:"
928 << gSum(magDisp)/returnReduce(patchDisp.size(), sumOp<label>())
929 << " min:" << gMin(magDisp)
930 << " max:" << gMax(magDisp) << endl;
934 Info<< "Calculated surface displacement in = "
935 << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
938 // Limit amount of movement.
939 forAll(patchDisp, patchPointI)
941 scalar magDisp = mag(patchDisp[patchPointI]);
943 if (magDisp > snapDist[patchPointI])
945 patchDisp[patchPointI] *= snapDist[patchPointI] / magDisp;
947 Pout<< "Limiting displacement for " << patchPointI
948 << " from " << magDisp << " to " << snapDist[patchPointI]
953 // Points on zones in one domain but only present as point on other
954 // will not do condition 2 on all. Sync explicitly.
955 syncTools::syncPointList
960 minMagSqrEqOp<point>(), // combine op
961 vector(GREAT, GREAT, GREAT) // null value (note: cannot use VGREAT)
968 void Foam::autoSnapDriver::smoothDisplacement
970 const snapParameters& snapParams,
971 motionSmoother& meshMover
974 const fvMesh& mesh = meshRefiner_.mesh();
975 const indirectPrimitivePatch& pp = meshMover.patch();
977 Info<< "Smoothing displacement ..." << endl;
979 // Set edge diffusivity as inverse of distance to patch
980 scalarField edgeGamma(1.0/(edgePatchDist(meshMover.pMesh(), pp) + SMALL));
981 //scalarField edgeGamma(mesh.nEdges(), 1.0);
982 //scalarField edgeGamma(wallGamma(mesh, pp, 10, 1));
984 // Get displacement field
985 pointVectorField& disp = meshMover.displacement();
987 for (label iter = 0; iter < snapParams.nSmoothDispl(); iter++)
989 if ((iter % 10) == 0)
991 Info<< "Iteration " << iter << endl;
993 pointVectorField oldDisp(disp);
994 meshMover.smooth(oldDisp, edgeGamma, disp);
996 Info<< "Displacement smoothed in = "
997 << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
1001 const_cast<Time&>(mesh.time())++;
1002 Pout<< "Writing smoothed mesh to time " << meshRefiner_.timeName()
1005 // Moving mesh creates meshPhi. Can be cleared out by a mesh.clearOut
1006 // but this will also delete all pointMesh but not pointFields which
1007 // gives an illegal situation.
1011 Pout<< "Writing displacement field ..." << endl;
1013 tmp<pointScalarField> magDisp(mag(disp));
1016 Pout<< "Writing actual patch displacement ..." << endl;
1017 vectorField actualPatchDisp(disp, pp.meshPoints());
1020 mesh.time().path()/"actualPatchDisplacement.obj",
1022 pp.localPoints() + actualPatchDisp
1028 bool Foam::autoSnapDriver::scaleMesh
1030 const snapParameters& snapParams,
1031 const label nInitErrors,
1032 const List<labelPair>& baffles,
1033 motionSmoother& meshMover
1036 const fvMesh& mesh = meshRefiner_.mesh();
1038 // Relax displacement until correct mesh
1039 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1040 labelList checkFaces(identity(mesh.nFaces()));
1042 scalar oldErrorReduction = -1;
1044 bool meshOk = false;
1046 Info<< "Moving mesh ..." << endl;
1047 for (label iter = 0; iter < 2*snapParams.nSnap(); iter++)
1049 Info<< nl << "Iteration " << iter << endl;
1051 if (iter == snapParams.nSnap())
1053 Info<< "Displacement scaling for error reduction set to 0." << endl;
1054 oldErrorReduction = meshMover.setErrorReduction(0.0);
1057 meshOk = meshMover.scaleMesh(checkFaces, baffles, true, nInitErrors);
1061 Info<< "Successfully moved mesh" << endl;
1066 const_cast<Time&>(mesh.time())++;
1067 Pout<< "Writing scaled mesh to time " << meshRefiner_.timeName()
1071 Pout<< "Writing displacement field ..." << endl;
1072 meshMover.displacement().write();
1073 tmp<pointScalarField> magDisp(mag(meshMover.displacement()));
1078 if (oldErrorReduction >= 0)
1080 meshMover.setErrorReduction(oldErrorReduction);
1082 Info<< "Moved mesh in = "
1083 << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
1089 // After snapping: correct patching according to nearest surface.
1090 // Code is very similar to calcNearestSurface.
1091 // - calculate face-wise snap distance as max of point-wise
1092 // - calculate face-wise nearest surface point
1093 // - repatch face according to patch for surface point.
1094 Foam::autoPtr<Foam::mapPolyMesh> Foam::autoSnapDriver::repatchToSurface
1096 const snapParameters& snapParams,
1097 const labelList& adaptPatchIDs
1100 const fvMesh& mesh = meshRefiner_.mesh();
1101 const refinementSurfaces& surfaces = meshRefiner_.surfaces();
1103 Info<< "Repatching faces according to nearest surface ..." << endl;
1105 // Get the labels of added patches.
1106 autoPtr<indirectPrimitivePatch> ppPtr
1108 meshRefinement::makePatch
1114 indirectPrimitivePatch& pp = ppPtr();
1116 // Divide surfaces into zoned and unzoned
1117 labelList zonedSurfaces = surfaces.getNamedSurfaces();
1118 labelList unzonedSurfaces = surfaces.getUnnamedSurfaces();
1121 // Faces that do not move
1122 PackedBoolList isZonedFace(mesh.nFaces());
1124 // 1. All faces on zoned surfaces
1125 const wordList& faceZoneNames = surfaces.faceZoneNames();
1126 const faceZoneMesh& fZones = mesh.faceZones();
1128 forAll(zonedSurfaces, i)
1130 const label zoneSurfI = zonedSurfaces[i];
1131 const faceZone& fZone = fZones[faceZoneNames[zoneSurfI]];
1135 isZonedFace.set(fZone[i], 1);
1141 // Determine per pp face which patch it should be in
1142 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1144 // Patch that face should be in
1145 labelList closestPatch(pp.size(), -1);
1147 // face snap distance as max of point snap distance
1148 scalarField faceSnapDist(pp.size(), -GREAT);
1150 // Distance to attract to nearest feature on surface
1151 const scalarField snapDist(calcSnapDistance(snapParams, pp));
1153 const faceList& localFaces = pp.localFaces();
1155 forAll(localFaces, faceI)
1157 const face& f = localFaces[faceI];
1161 faceSnapDist[faceI] = max
1163 faceSnapDist[faceI],
1170 pointField localFaceCentres(mesh.faceCentres(), pp.addressing());
1172 // Get nearest surface and region
1173 labelList hitSurface;
1174 labelList hitRegion;
1175 surfaces.findNearestRegion
1179 sqr(faceSnapDist), // sqr of attract distance
1187 label faceI = pp.addressing()[i];
1189 if (hitSurface[i] != -1 && !isZonedFace.get(faceI))
1191 closestPatch[i] = globalToPatch_
1193 surfaces.globalRegion
1204 // Change those faces for which there is a different closest patch
1205 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1207 labelList ownPatch(mesh.nFaces(), -1);
1208 labelList neiPatch(mesh.nFaces(), -1);
1210 const polyBoundaryMesh& patches = mesh.boundaryMesh();
1212 forAll(patches, patchI)
1214 const polyPatch& pp = patches[patchI];
1218 ownPatch[pp.start()+i] = patchI;
1219 neiPatch[pp.start()+i] = patchI;
1224 forAll(closestPatch, i)
1226 label faceI = pp.addressing()[i];
1228 if (closestPatch[i] != -1 && closestPatch[i] != ownPatch[faceI])
1230 ownPatch[faceI] = closestPatch[i];
1231 neiPatch[faceI] = closestPatch[i];
1236 Info<< "Repatched " << returnReduce(nChanged, sumOp<label>())
1237 << " faces in = " << mesh.time().cpuTimeIncrement() << " s\n" << nl
1240 return meshRefiner_.createBaffles(ownPatch, neiPatch);
1244 void Foam::autoSnapDriver::doSnap
1246 const dictionary& snapDict,
1247 const dictionary& motionDict,
1248 const scalar featureCos,
1249 const snapParameters& snapParams
1252 fvMesh& mesh = meshRefiner_.mesh();
1255 << "Morphing phase" << nl
1256 << "--------------" << nl
1259 // Get the labels of added patches.
1260 labelList adaptPatchIDs(meshRefiner_.meshedPatches());
1262 // Create baffles (pairs of faces that share the same points)
1263 // Baffles stored as owner and neighbour face that have been created.
1264 List<labelPair> baffles;
1265 meshRefiner_.createZoneBaffles(globalToPatch_, baffles);
1268 bool doFeatures = false;
1269 label nFeatIter = 1;
1270 if (snapParams.nFeatureSnap() > 0)
1273 nFeatIter = snapParams.nFeatureSnap();
1275 Info<< "Snapping to features in " << nFeatIter
1276 << " iterations ..." << endl;
1280 bool meshOk = false;
1283 autoPtr<indirectPrimitivePatch> ppPtr
1285 meshRefinement::makePatch
1292 // Distance to attract to nearest feature on surface
1293 const scalarField snapDist(calcSnapDistance(snapParams, ppPtr()));
1296 // Construct iterative mesh mover.
1297 Info<< "Constructing mesh displacer ..." << endl;
1298 Info<< "Using mesh parameters " << motionDict << nl << endl;
1300 const pointMesh& pMesh = pointMesh::New(mesh);
1302 motionSmoother meshMover
1307 meshRefinement::makeDisplacementField(pMesh, adaptPatchIDs),
1312 // Check initial mesh
1313 Info<< "Checking initial mesh ..." << endl;
1314 labelHashSet wrongFaces(mesh.nFaces()/100);
1315 motionSmoother::checkMesh(false, mesh, motionDict, wrongFaces);
1316 const label nInitErrors = returnReduce
1322 Info<< "Detected " << nInitErrors << " illegal faces"
1323 << " (concave, zero area or negative cell pyramid volume)"
1327 Info<< "Checked initial mesh in = "
1328 << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
1330 // Pre-smooth patch vertices (so before determining nearest)
1331 preSmoothPatch(snapParams, nInitErrors, baffles, meshMover);
1334 for (label iter = 0; iter < nFeatIter; iter++)
1337 << "Morph iteration " << iter << nl
1338 << "-----------------" << endl;
1340 // Calculate displacement at every patch point. Insert into
1342 vectorField disp = calcNearestSurface(snapDist, meshMover);
1344 // Override displacement with feature edge attempt
1347 disp = calcNearestSurfaceFeature
1351 scalar(iter+1)/nFeatIter,
1358 // Check for displacement being outwards.
1359 outwardsDisplacement(ppPtr(), disp);
1361 // Set initial distribution of displacement field (on patches)
1362 // from patchDisp and make displacement consistent with b.c.
1363 // on displacement pointVectorField.
1364 meshMover.setDisplacement(disp);
1367 if (debug&meshRefinement::OBJINTERSECTIONS)
1372 / "patchDisplacement_" + name(iter) + ".obj",
1373 ppPtr().localPoints(),
1374 ppPtr().localPoints() + disp
1378 // Get smoothly varying internal displacement field.
1379 smoothDisplacement(snapParams, meshMover);
1381 // Apply internal displacement to mesh.
1392 Info<< "Did not succesfully snap mesh. Giving up."
1395 // Use current mesh as base mesh
1396 meshMover.correct();
1403 const_cast<Time&>(mesh.time())++;
1404 Pout<< "Writing scaled mesh to time "
1405 << meshRefiner_.timeName() << endl;
1409 mesh.time().path()/meshRefiner_.timeName()
1411 Pout<< "Writing displacement field ..." << endl;
1412 meshMover.displacement().write();
1413 tmp<pointScalarField> magDisp(mag(meshMover.displacement()));
1417 // Use current mesh as base mesh
1418 meshMover.correct();
1422 // Merge any introduced baffles.
1423 mergeZoneBaffles(baffles);
1425 // Repatch faces according to nearest.
1426 repatchToSurface(snapParams, adaptPatchIDs);
1428 // Repatching might have caused faces to be on same patch and hence
1429 // mergeable so try again to merge coplanar faces
1430 label nChanged = meshRefiner_.mergePatchFacesUndo
1432 featureCos, // minCos
1433 featureCos, // concaveCos
1434 meshRefiner_.meshedPatches(),
1438 nChanged += meshRefiner_.mergeEdgesUndo
1444 if (nChanged > 0 && debug)
1446 const_cast<Time&>(mesh.time())++;
1447 Pout<< "Writing patchFace merged mesh to time "
1448 << meshRefiner_.timeName() << endl;
1452 meshRefiner_.timeName()
1458 // ************************************************************************* //