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 Checks for multiple patch faces on same cell and combines them. These
26 result from e.g. refined neighbouring cells getting removed, leaving 4
27 exposed faces with same owner.
30 - only boundary faces (since multiple internal faces between two cells
32 - faces have to have same owner
33 - faces have to be connected via edge which are not features (so angle
34 between them < feature angle)
35 - outside of faces has to be single loop
36 - outside of face should not be (or just slightly) concave (so angle
37 between consecutive edges < concaveangle
39 E.g. to allow all faces on same patch to be merged:
41 combinePatchFaces 180 -concaveAngle 90
43 \*---------------------------------------------------------------------------*/
45 #include "PstreamReduceOps.H"
48 #include "polyTopoChange.H"
49 #include "polyModifyFace.H"
50 #include "polyAddFace.H"
51 #include "combineFaces.H"
52 #include "removePoints.H"
54 #include "mapPolyMesh.H"
55 #include "unitConversion.H"
59 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
62 // Same check as snapMesh
67 labelHashSet& wrongFaces
77 IOobject::MUST_READ_IF_MODIFIED,
82 // Max nonorthogonality allowed
83 scalar maxNonOrtho(readScalar(snapDict.lookup("maxNonOrtho")));
84 // Max concaveness allowed.
85 scalar maxConcave(readScalar(snapDict.lookup("maxConcave")));
86 // Min volume allowed (factor of minimum cellVolume)
87 scalar relMinVol(readScalar(snapDict.lookup("minVol")));
88 const scalar minCellVol = min(mesh.cellVolumes());
89 const scalar minPyrVol = relMinVol*minCellVol;
91 scalar minArea(readScalar(snapDict.lookup("minArea")));
93 if (maxNonOrtho < 180.0-SMALL)
95 Pout<< "Checking non orthogonality" << endl;
97 label nOldSize = wrongFaces.size();
98 mesh.setNonOrthThreshold(maxNonOrtho);
99 mesh.checkFaceOrthogonality(false, &wrongFaces);
101 Pout<< "Detected " << wrongFaces.size() - nOldSize
102 << " faces with non-orthogonality > " << maxNonOrtho << " degrees"
106 if (minPyrVol > -GREAT)
108 Pout<< "Checking face pyramids" << endl;
110 label nOldSize = wrongFaces.size();
111 mesh.checkFacePyramids(false, minPyrVol, &wrongFaces);
112 Pout<< "Detected additional " << wrongFaces.size() - nOldSize
113 << " faces with illegal face pyramids" << endl;
116 if (maxConcave < 180.0-SMALL)
118 Pout<< "Checking face angles" << endl;
120 label nOldSize = wrongFaces.size();
121 mesh.checkFaceAngles(false, maxConcave, &wrongFaces);
122 Pout<< "Detected additional " << wrongFaces.size() - nOldSize
123 << " faces with concavity > " << maxConcave << " degrees"
127 if (minArea > -SMALL)
129 Pout<< "Checking face areas" << endl;
131 label nOldSize = wrongFaces.size();
133 const scalarField magFaceAreas(mag(mesh.faceAreas()));
135 forAll(magFaceAreas, faceI)
137 if (magFaceAreas[faceI] < minArea)
139 wrongFaces.insert(faceI);
142 Pout<< "Detected additional " << wrongFaces.size() - nOldSize
143 << " faces with area < " << minArea << " m^2" << endl;
148 // Merge faces on the same patch (usually from exposing refinement)
149 // Can undo merges if these cause problems.
150 label mergePatchFaces
153 const scalar concaveSin,
154 const bool snapMeshDict,
159 // Patch face merging engine
160 combineFaces faceCombiner(mesh);
162 // Get all sets of faces that can be merged
163 labelListList allFaceSets(faceCombiner.getMergeSets(minCos, concaveSin));
165 label nFaceSets = returnReduce(allFaceSets.size(), sumOp<label>());
167 Info<< "Merging " << nFaceSets << " sets of faces." << endl;
171 // Store the faces of the face sets
172 List<faceList> allFaceSetsFaces(allFaceSets.size());
173 forAll(allFaceSets, setI)
175 allFaceSetsFaces[setI] = UIndirectList<face>
182 autoPtr<mapPolyMesh> map;
184 // Topology changes container
185 polyTopoChange meshMod(mesh);
187 // Merge all faces of a set into the first face of the set.
188 faceCombiner.setRefinement(allFaceSets, meshMod);
190 // Change the mesh (no inflation)
191 map = meshMod.changeMesh(mesh, false, true);
194 mesh.updateMesh(map);
196 // Move mesh (since morphing does not do this)
197 if (map().hasMotionPoints())
199 mesh.movePoints(map().preMotionPoints());
203 // Delete mesh volumes. No other way to do this?
209 // Check for errors and undo
210 // ~~~~~~~~~~~~~~~~~~~~~~~~~
213 labelHashSet errorFaces;
217 checkSnapMesh(runTime, mesh, errorFaces);
221 mesh.checkFacePyramids(false, -SMALL, &errorFaces);
224 // Sets where the master is in error
225 labelHashSet errorSets;
227 forAll(allFaceSets, setI)
229 label newMasterI = map().reverseFaceMap()[allFaceSets[setI][0]];
231 if (errorFaces.found(newMasterI))
233 errorSets.insert(setI);
236 label nErrorSets = returnReduce(errorSets.size(), sumOp<label>());
238 Info<< "Detected " << nErrorSets
239 << " error faces on boundaries that have been merged."
240 << " These will be restored to their original faces."
245 // Renumber stored faces to new vertex numbering.
246 forAllConstIter(labelHashSet, errorSets, iter)
248 label setI = iter.key();
250 faceList& setFaceVerts = allFaceSetsFaces[setI];
252 forAll(setFaceVerts, i)
254 inplaceRenumber(map().reversePointMap(), setFaceVerts[i]);
256 // Debug: check that all points are still there.
257 forAll(setFaceVerts[i], j)
259 label newVertI = setFaceVerts[i][j];
263 FatalErrorIn("mergePatchFaces")
264 << "In set:" << setI << " old face labels:"
265 << allFaceSets[setI] << " new face vertices:"
266 << setFaceVerts[i] << " are unmapped vertices!"
267 << abort(FatalError);
274 // Topology changes container
275 polyTopoChange meshMod(mesh);
279 forAllConstIter(labelHashSet, errorSets, iter)
281 label setI = iter.key();
283 const labelList& setFaces = allFaceSets[setI];
284 const faceList& setFaceVerts = allFaceSetsFaces[setI];
286 label newMasterI = map().reverseFaceMap()[setFaces[0]];
288 // Restore. Get face properties.
290 label own = mesh.faceOwner()[newMasterI];
291 label zoneID = mesh.faceZones().whichZone(newMasterI);
292 bool zoneFlip = false;
295 const faceZone& fZone = mesh.faceZones()[zoneID];
296 zoneFlip = fZone.flipMap()[fZone.whichFace(newMasterI)];
298 label patchID = mesh.boundaryMesh().whichPatch(newMasterI);
300 Pout<< "Restoring new master face " << newMasterI
301 << " to vertices " << setFaceVerts[0] << endl;
303 // Modify the master face.
308 setFaceVerts[0], // original face
309 newMasterI, // label of face
313 patchID, // patch for face
314 false, // remove from zone
315 zoneID, // zone for face
316 zoneFlip // face flip in zone
321 // Add the previously removed faces
322 for (label i = 1; i < setFaces.size(); i++)
324 Pout<< "Restoring removed face " << setFaces[i]
325 << " with vertices " << setFaceVerts[i] << endl;
331 setFaceVerts[i], // vertices
334 -1, // masterPointID,
336 newMasterI, // masterFaceID,
337 false, // flipFaceFlux,
346 // Change the mesh (no inflation)
347 map = meshMod.changeMesh(mesh, false, true);
350 mesh.updateMesh(map);
352 // Move mesh (since morphing does not do this)
353 if (map().hasMotionPoints())
355 mesh.movePoints(map().preMotionPoints());
359 // Delete mesh volumes. No other way to do this?
366 Info<< "No faces merged ..." << endl;
373 // Remove points not used by any face or points used by only two faces where
374 // the edges are in line
375 label mergeEdges(const scalar minCos, polyMesh& mesh)
377 Info<< "Merging all points on surface that" << nl
378 << "- are used by only two boundary faces and" << nl
379 << "- make an angle with a cosine of more than " << minCos
380 << "." << nl << endl;
382 // Point removal analysis engine
383 removePoints pointRemover(mesh);
385 // Count usage of points
386 boolList pointCanBeDeleted;
387 label nRemove = pointRemover.countPointUsage(minCos, pointCanBeDeleted);
391 Info<< "Removing " << nRemove
392 << " straight edge points ..." << endl;
394 // Topology changes container
395 polyTopoChange meshMod(mesh);
397 pointRemover.setRefinement(pointCanBeDeleted, meshMod);
399 // Change the mesh (no inflation)
400 autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, false, true);
403 mesh.updateMesh(map);
405 // Move mesh (since morphing does not do this)
406 if (map().hasMotionPoints())
408 mesh.movePoints(map().preMotionPoints());
412 // Delete mesh volumes. No other way to do this?
418 Info<< "No straight edges simplified and no points removed ..." << endl;
427 int main(int argc, char *argv[])
429 # include "addOverwriteOption.H"
431 argList::validArgs.append("featureAngle [0..180]");
436 "specify concave angle [0..180] (default: 30 degrees)"
438 argList::addBoolOption
441 "use system/snapMeshDict"
444 # include "setRootCase.H"
445 # include "createTime.H"
446 runTime.functionObjects().off();
447 # include "createPolyMesh.H"
448 const word oldInstance = mesh.pointsInstance();
450 const scalar featureAngle = args.argRead<scalar>(1);
451 const scalar minCos = Foam::cos(degToRad(featureAngle));
453 // Sin of angle between two consecutive edges on a face.
454 // If sin(angle) larger than this the face will be considered concave.
455 scalar concaveAngle = args.optionLookupOrDefault("concaveAngle", 30.0);
456 scalar concaveSin = Foam::sin(degToRad(concaveAngle));
458 const bool snapMeshDict = args.optionFound("snapMesh");
459 const bool overwrite = args.optionFound("overwrite");
461 Info<< "Merging all faces of a cell" << nl
462 << " - which are on the same patch" << nl
463 << " - which make an angle < " << featureAngle << " degrees"
465 << " (cos:" << minCos << ')' << nl
466 << " - even when resulting face becomes concave by more than "
467 << concaveAngle << " degrees" << nl
468 << " (sin:" << concaveSin << ')' << nl
476 // Merge faces on same patch
477 label nChanged = mergePatchFaces
486 // Merge points on straight edges and remove unused points
489 Info<< "Merging all 'loose' points on surface edges, "
490 << "regardless of the angle they make." << endl;
492 // Surface bnound to be used to extrude. Merge all loose points.
493 nChanged += mergeEdges(-1, mesh);
497 nChanged += mergeEdges(minCos, mesh);
504 mesh.setInstance(oldInstance);
507 Info<< "Writing morphed mesh to time " << runTime.timeName() << endl;
513 Info<< "Mesh unchanged." << endl;
516 Info<< "\nEnd\n" << endl;
522 // ************************************************************************* //