1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd.
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 the
13 Free Software Foundation; either version 2 of the License, or (at your
14 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, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 Checks for multiple patch faces on same cell and combines them. These
27 result from e.g. refined neighbouring cells getting removed, leaving 4
28 exposed faces with same owner.
31 - only boundary faces (since multiple internal faces between two cells
33 - faces have to have same owner
34 - faces have to be connected via edge which are not features (so angle
35 between them < feature angle)
36 - outside of faces has to be single loop
37 - outside of face should not be (or just slightly) concave (so angle
38 between consecutive edges < concaveangle
40 E.g. to allow all faces on same patch to be merged:
42 combinePatchFaces .. cavity 180 -concaveAngle 90
44 \*---------------------------------------------------------------------------*/
46 #include "PstreamReduceOps.H"
49 #include "polyTopoChange.H"
50 #include "polyModifyFace.H"
51 #include "polyAddFace.H"
52 #include "combineFaces.H"
53 #include "removePoints.H"
55 #include "mapPolyMesh.H"
56 #include "mathematicalConstants.H"
60 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
62 // Sin of angle between two consecutive edges on a face. If sin(angle) larger
63 // than this the face will be considered concave.
64 const scalar defaultConcaveAngle = 30;
67 // Same check as snapMesh
72 labelHashSet& wrongFaces
87 // Max nonorthogonality allowed
88 scalar maxNonOrtho(readScalar(snapDict.lookup("maxNonOrtho")));
89 // Max concaveness allowed.
90 scalar maxConcave(readScalar(snapDict.lookup("maxConcave")));
91 // Min volume allowed (factor of minimum cellVolume)
92 scalar relMinVol(readScalar(snapDict.lookup("minVol")));
93 const scalar minCellVol = min(mesh.cellVolumes());
94 const scalar minPyrVol = relMinVol*minCellVol;
96 scalar minArea(readScalar(snapDict.lookup("minArea")));
98 if (maxNonOrtho < 180.0-SMALL)
100 Pout<< "Checking non orthogonality" << endl;
102 label nOldSize = wrongFaces.size();
103 mesh.setNonOrthThreshold(maxNonOrtho);
104 mesh.checkFaceOrthogonality(false, &wrongFaces);
106 Pout<< "Detected " << wrongFaces.size() - nOldSize
107 << " faces with non-orthogonality > " << maxNonOrtho << " degrees"
111 if (minPyrVol > -GREAT)
113 Pout<< "Checking face pyramids" << endl;
115 label nOldSize = wrongFaces.size();
116 mesh.checkFacePyramids(false, minPyrVol, &wrongFaces);
117 Pout<< "Detected additional " << wrongFaces.size() - nOldSize
118 << " faces with illegal face pyramids" << endl;
121 if (maxConcave < 180.0-SMALL)
123 Pout<< "Checking face angles" << endl;
125 label nOldSize = wrongFaces.size();
126 mesh.checkFaceAngles(false, maxConcave, &wrongFaces);
127 Pout<< "Detected additional " << wrongFaces.size() - nOldSize
128 << " faces with concavity > " << maxConcave << " degrees"
132 if (minArea > -SMALL)
134 Pout<< "Checking face areas" << endl;
136 label nOldSize = wrongFaces.size();
138 const scalarField magFaceAreas = mag(mesh.faceAreas());
140 forAll(magFaceAreas, faceI)
142 if (magFaceAreas[faceI] < minArea)
144 wrongFaces.insert(faceI);
147 Pout<< "Detected additional " << wrongFaces.size() - nOldSize
148 << " faces with area < " << minArea << " m^2" << endl;
153 // Merge faces on the same patch (usually from exposing refinement)
154 // Can undo merges if these cause problems.
155 label mergePatchFaces
158 const scalar concaveSin,
159 const bool snapMeshDict,
164 // Patch face merging engine
165 combineFaces faceCombiner(mesh);
167 // Get all sets of faces that can be merged
168 labelListList allFaceSets(faceCombiner.getMergeSets(minCos, concaveSin));
170 label nFaceSets = returnReduce(allFaceSets.size(), sumOp<label>());
172 Info<< "Merging " << nFaceSets << " sets of faces." << endl;
176 // Store the faces of the face sets
177 List<faceList> allFaceSetsFaces(allFaceSets.size());
178 forAll(allFaceSets, setI)
180 allFaceSetsFaces[setI] = UIndirectList<face>
187 autoPtr<mapPolyMesh> map;
189 // Topology changes container
190 polyTopoChange meshMod(mesh);
192 // Merge all faces of a set into the first face of the set.
193 faceCombiner.setRefinement(allFaceSets, meshMod);
195 // Change the mesh (no inflation)
196 map = meshMod.changeMesh(mesh, false, true);
199 mesh.updateMesh(map);
201 // Move mesh (since morphing does not do this)
202 if (map().hasMotionPoints())
204 mesh.movePoints(map().preMotionPoints());
208 // Delete mesh volumes. No other way to do this?
214 // Check for errors and undo
215 // ~~~~~~~~~~~~~~~~~~~~~~~~~
218 labelHashSet errorFaces;
222 checkSnapMesh(runTime, mesh, errorFaces);
226 mesh.checkFacePyramids(false, -SMALL, &errorFaces);
229 // Sets where the master is in error
230 labelHashSet errorSets;
232 forAll(allFaceSets, setI)
234 label newMasterI = map().reverseFaceMap()[allFaceSets[setI][0]];
236 if (errorFaces.found(newMasterI))
238 errorSets.insert(setI);
241 label nErrorSets = returnReduce(errorSets.size(), sumOp<label>());
243 Info<< "Detected " << nErrorSets
244 << " error faces on boundaries that have been merged."
245 << " These will be restored to their original faces."
250 // Renumber stored faces to new vertex numbering.
251 forAllConstIter(labelHashSet, errorSets, iter)
253 label setI = iter.key();
255 faceList& setFaceVerts = allFaceSetsFaces[setI];
257 forAll(setFaceVerts, i)
259 inplaceRenumber(map().reversePointMap(), setFaceVerts[i]);
261 // Debug: check that all points are still there.
262 forAll(setFaceVerts[i], j)
264 label newVertI = setFaceVerts[i][j];
268 FatalErrorIn("mergePatchFaces")
269 << "In set:" << setI << " old face labels:"
270 << allFaceSets[setI] << " new face vertices:"
271 << setFaceVerts[i] << " are unmapped vertices!"
272 << abort(FatalError);
279 // Topology changes container
280 polyTopoChange meshMod(mesh);
284 forAllConstIter(labelHashSet, errorSets, iter)
286 label setI = iter.key();
288 const labelList& setFaces = allFaceSets[setI];
289 const faceList& setFaceVerts = allFaceSetsFaces[setI];
291 label newMasterI = map().reverseFaceMap()[setFaces[0]];
293 // Restore. Get face properties.
295 label own = mesh.faceOwner()[newMasterI];
296 label zoneID = mesh.faceZones().whichZone(newMasterI);
297 bool zoneFlip = false;
300 const faceZone& fZone = mesh.faceZones()[zoneID];
301 zoneFlip = fZone.flipMap()[fZone.whichFace(newMasterI)];
303 label patchI = mesh.boundaryMesh().whichPatch(newMasterI);
306 Pout<< "Restoring new master face " << newMasterI
307 << " to vertices " << setFaceVerts[0] << endl;
309 // Modify the master face.
314 setFaceVerts[0], // original face
315 newMasterI, // label of face
319 patchI, // patch for face
320 false, // remove from zone
321 zoneID, // zone for face
322 zoneFlip // face flip in zone
327 // Add the previously removed faces
328 for (label i = 1; i < setFaces.size(); i++)
330 Pout<< "Restoring removed face " << setFaces[i]
331 << " with vertices " << setFaceVerts[i] << endl;
337 setFaceVerts[i], // vertices
340 -1, // masterPointID,
342 newMasterI, // masterFaceID,
343 false, // flipFaceFlux,
352 // Change the mesh (no inflation)
353 map = meshMod.changeMesh(mesh, false, true);
356 mesh.updateMesh(map);
358 // Move mesh (since morphing does not do this)
359 if (map().hasMotionPoints())
361 mesh.movePoints(map().preMotionPoints());
365 // Delete mesh volumes. No other way to do this?
372 Info<< "No faces merged ..." << endl;
379 // Remove points not used by any face or points used by only two faces where
380 // the edges are in line
381 label mergeEdges(const scalar minCos, polyMesh& mesh)
383 Info<< "Merging all points on surface that" << nl
384 << "- are used by only two boundary faces and" << nl
385 << "- make an angle with a cosine of more than " << minCos
386 << "." << nl << endl;
388 // Point removal analysis engine
389 removePoints pointRemover(mesh);
391 // Count usage of points
392 boolList pointCanBeDeleted;
393 label nRemove = pointRemover.countPointUsage(minCos, pointCanBeDeleted);
397 Info<< "Removing " << nRemove
398 << " straight edge points ..." << endl;
400 // Topology changes container
401 polyTopoChange meshMod(mesh);
403 pointRemover.setRefinement(pointCanBeDeleted, meshMod);
405 // Change the mesh (no inflation)
406 autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, false, true);
409 mesh.updateMesh(map);
411 // Move mesh (since morphing does not do this)
412 if (map().hasMotionPoints())
414 mesh.movePoints(map().preMotionPoints());
418 // Delete mesh volumes. No other way to do this?
424 Info<< "No straight edges simplified and no points removed ..." << endl;
433 int main(int argc, char *argv[])
435 argList::validArgs.append("feature angle [0..180]");
436 argList::validOptions.insert("concaveAngle", "[0..180]");
437 argList::validOptions.insert("snapMesh", "");
438 argList::validOptions.insert("overwrite", "");
440 # include "setRootCase.H"
441 # include "createTime.H"
442 runTime.functionObjects().off();
443 # include "createPolyMesh.H"
444 const word oldInstance = mesh.pointsInstance();
446 scalar featureAngle(readScalar(IStringStream(args.additionalArgs()[0])()));
448 scalar minCos = Foam::cos(featureAngle*mathematicalConstant::pi/180.0);
450 scalar concaveAngle = defaultConcaveAngle;
451 args.optionReadIfPresent("concaveAngle", concaveAngle);
453 scalar concaveSin = Foam::sin(concaveAngle*mathematicalConstant::pi/180.0);
455 bool snapMeshDict = args.optionFound("snapMesh");
456 bool overwrite = args.optionFound("overwrite");
458 Info<< "Merging all faces of a cell" << nl
459 << " - which are on the same patch" << nl
460 << " - which make an angle < " << featureAngle << " degrees"
462 << " (cos:" << minCos << ')' << nl
463 << " - even when resulting face becomes concave by more than "
464 << concaveAngle << " degrees" << nl
465 << " (sin:" << concaveSin << ')' << nl
473 // Merge faces on same patch
474 label nChanged = mergePatchFaces
483 // Merge points on straight edges and remove unused points
486 Info<< "Merging all 'loose' points on surface edges"
487 << ", regardless of the angle they make." << endl;
489 // Surface bnound to be used to extrude. Merge all loose points.
490 nChanged += mergeEdges(-1, mesh);
494 nChanged += mergeEdges(minCos, mesh);
501 mesh.setInstance(oldInstance);
504 Info<< "Writing morphed mesh to time " << runTime.timeName() << endl;
510 Info<< "Mesh unchanged." << endl;
513 Info << "End\n" << endl;
519 // ************************************************************************* //