1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
4 \\ / O peration | Version: 3.2
5 \\ / A nd | Web: http://www.foam-extend.org
6 \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend 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 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
25 Calculate the dual of a polyMesh. Adheres to all the feature and patch
30 - polyDualMesh featureAngle
32 Detects any boundary edge > angle and creates multiple boundary faces
33 for it. Normal behaviour is to have each point become a cell
36 @param -concaveMultiCells
37 Creates multiple cells for each point on a concave edge. Might limit
38 the amount of distortion on some meshes.
41 Normally only constructs a single face between two cells. This single face
42 might be too distorted. splitAllFaces will create a single face for every
43 original cell the face passes through. The mesh will thus have
44 multiple faces inbetween two cells! (so is not strictly upper-triangular
45 anymore - checkMesh will complain)
47 @param -doNotPreserveFaceZones:
48 By default all faceZones are preserved by marking all faces, edges and
49 points on them as features. The -doNotPreserveFaceZones disables this
52 Note: is just a driver for meshDualiser. Substitute your own
53 simpleMarkFeatures to have different behaviour.
55 \*---------------------------------------------------------------------------*/
58 #include "objectRegistry.H"
60 #include "timeSelector.H"
62 #include "mathematicalConstants.H"
63 #include "directTopoChange.H"
64 #include "mapPolyMesh.H"
65 #include "PackedBoolList.H"
66 #include "meshTools.H"
68 #include "meshDualiser.H"
69 #include "ReadFields.H"
70 #include "volFields.H"
71 #include "surfaceFields.H"
75 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
77 // Naive feature detection. All boundary edges with angle > featureAngle become
78 // feature edges. All points on feature edges become feature points. All
79 // boundary faces become feature faces.
80 void simpleMarkFeatures
83 const PackedBoolList& isBoundaryEdge,
84 const scalar featureAngle,
85 const bool concaveMultiCells,
86 const bool doNotPreserveFaceZones,
88 labelList& featureFaces,
89 labelList& featureEdges,
90 labelList& singleCellFeaturePoints,
91 labelList& multiCellFeaturePoints
94 scalar minCos = Foam::cos(featureAngle * mathematicalConstant::pi/180.0);
96 const polyBoundaryMesh& patches = mesh.boundaryMesh();
99 labelHashSet featureEdgeSet;
100 labelHashSet singleCellFeaturePointSet;
101 labelHashSet multiCellFeaturePointSet;
104 // 1. Mark all edges between patches
105 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
107 forAll(patches, patchI)
109 const polyPatch& pp = patches[patchI];
110 const labelList& meshEdges = pp.meshEdges();
112 // All patch corner edges. These need to be feature points & edges!
113 for (label edgeI = pp.nInternalEdges(); edgeI < pp.nEdges(); edgeI++)
115 label meshEdgeI = meshEdges[edgeI];
116 featureEdgeSet.insert(meshEdgeI);
117 singleCellFeaturePointSet.insert(mesh.edges()[meshEdgeI][0]);
118 singleCellFeaturePointSet.insert(mesh.edges()[meshEdgeI][1]);
124 // 2. Mark all geometric feature edges
125 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
126 // Make distinction between convex features where the boundary point becomes
127 // a single cell and concave features where the boundary point becomes
128 // multiple 'half' cells.
130 // Addressing for all outside faces
131 primitivePatch allBoundary
136 mesh.nFaces()-mesh.nInternalFaces(),
137 mesh.nInternalFaces()
142 // Check for non-manifold points (surface pinched at point)
143 allBoundary.checkPointManifold(false, &singleCellFeaturePointSet);
145 // Check for non-manifold edges (surface pinched at edge)
146 const labelListList& edgeFaces = allBoundary.edgeFaces();
147 const labelList& meshPoints = allBoundary.meshPoints();
149 forAll(edgeFaces, edgeI)
151 const labelList& eFaces = edgeFaces[edgeI];
153 if (eFaces.size() > 2)
155 const edge& e = allBoundary.edges()[edgeI];
157 //Info<< "Detected non-manifold boundary edge:" << edgeI
159 // << allBoundary.points()[meshPoints[e[0]]]
160 // << allBoundary.points()[meshPoints[e[1]]] << endl;
162 singleCellFeaturePointSet.insert(meshPoints[e[0]]);
163 singleCellFeaturePointSet.insert(meshPoints[e[1]]);
167 // Check for features.
168 forAll(edgeFaces, edgeI)
170 const labelList& eFaces = edgeFaces[edgeI];
172 if (eFaces.size() == 2)
174 label f0 = eFaces[0];
175 label f1 = eFaces[1];
178 const vector& n0 = allBoundary.faceNormals()[f0];
179 const vector& n1 = allBoundary.faceNormals()[f1];
181 if ((n0 & n1) < minCos)
183 const edge& e = allBoundary.edges()[edgeI];
184 label v0 = meshPoints[e[0]];
185 label v1 = meshPoints[e[1]];
187 label meshEdgeI = meshTools::findEdge(mesh, v0, v1);
188 featureEdgeSet.insert(meshEdgeI);
190 // Check if convex or concave by looking at angle
191 // between face centres and normal
194 allBoundary[f1].centre(allBoundary.points())
195 - allBoundary[f0].centre(allBoundary.points())
198 if (concaveMultiCells && (c1c0 & n0) > SMALL)
200 // Found concave edge. Make into multiCell features
201 Info<< "Detected concave feature edge:" << edgeI
202 << " cos:" << (c1c0 & n0)
204 << allBoundary.points()[v0]
205 << allBoundary.points()[v1]
208 singleCellFeaturePointSet.erase(v0);
209 multiCellFeaturePointSet.insert(v0);
210 singleCellFeaturePointSet.erase(v1);
211 multiCellFeaturePointSet.insert(v1);
215 // Convex. singleCell feature.
216 if (!multiCellFeaturePointSet.found(v0))
218 singleCellFeaturePointSet.insert(v0);
220 if (!multiCellFeaturePointSet.found(v1))
222 singleCellFeaturePointSet.insert(v1);
230 // 3. Mark all feature faces
231 // ~~~~~~~~~~~~~~~~~~~~~~~~~
233 // Face centres that need inclusion in the dual mesh
234 labelHashSet featureFaceSet(mesh.nFaces()-mesh.nInternalFaces());
235 // A. boundary faces.
236 for (label faceI = mesh.nInternalFaces(); faceI < mesh.nFaces(); faceI++)
238 featureFaceSet.insert(faceI);
242 const faceZoneMesh& faceZones = mesh.faceZones();
244 if (doNotPreserveFaceZones)
246 if (faceZones.size() > 0)
248 WarningIn("simpleMarkFeatures(..)")
249 << "Detected " << faceZones.size()
250 << " faceZones. These will not be preserved."
256 if (faceZones.size() > 0)
258 Info<< "Detected " << faceZones.size()
259 << " faceZones. Preserving these by marking their"
260 << " points, edges and faces as features." << endl;
263 forAll(faceZones, zoneI)
265 const faceZone& fz = faceZones[zoneI];
267 Info<< "Inserting all faces in faceZone " << fz.name()
268 << " as features." << endl;
273 const face& f = mesh.faces()[faceI];
274 const labelList& fEdges = mesh.faceEdges()[faceI];
276 featureFaceSet.insert(faceI);
279 // Mark point as multi cell point (since both sides of
280 // face should have different cells)
281 singleCellFeaturePointSet.erase(f[fp]);
282 multiCellFeaturePointSet.insert(f[fp]);
284 // Make sure there are points on the edges.
285 featureEdgeSet.insert(fEdges[fp]);
291 // Transfer to arguments
292 featureFaces = featureFaceSet.toc();
293 featureEdges = featureEdgeSet.toc();
294 singleCellFeaturePoints = singleCellFeaturePointSet.toc();
295 multiCellFeaturePoints = multiCellFeaturePointSet.toc();
299 // Dump features to .obj files
302 const polyMesh& mesh,
303 const labelList& featureFaces,
304 const labelList& featureEdges,
305 const labelList& singleCellFeaturePoints,
306 const labelList& multiCellFeaturePoints
310 OFstream str("featureFaces.obj");
311 Info<< "Dumping centres of featureFaces to obj file " << str.name()
313 forAll(featureFaces, i)
315 meshTools::writeOBJ(str, mesh.faceCentres()[featureFaces[i]]);
319 OFstream str("featureEdges.obj");
320 Info<< "Dumping featureEdges to obj file " << str.name() << endl;
323 forAll(featureEdges, i)
325 const edge& e = mesh.edges()[featureEdges[i]];
326 meshTools::writeOBJ(str, mesh.points()[e[0]]);
328 meshTools::writeOBJ(str, mesh.points()[e[1]]);
330 str<< "l " << vertI-1 << ' ' << vertI << nl;
334 OFstream str("singleCellFeaturePoints.obj");
335 Info<< "Dumping featurePoints that become a single cell to obj file "
336 << str.name() << endl;
337 forAll(singleCellFeaturePoints, i)
339 meshTools::writeOBJ(str, mesh.points()[singleCellFeaturePoints[i]]);
343 OFstream str("multiCellFeaturePoints.obj");
344 Info<< "Dumping featurePoints that become multiple cells to obj file "
345 << str.name() << endl;
346 forAll(multiCellFeaturePoints, i)
348 meshTools::writeOBJ(str, mesh.points()[multiCellFeaturePoints[i]]);
354 int main(int argc, char *argv[])
356 argList::noParallel();
357 timeSelector::addOptions(true, false);
359 argList::validArgs.append("feature angle[0-180]");
360 argList::validOptions.insert("splitAllFaces", "");
361 argList::validOptions.insert("concaveMultiCells", "");
362 argList::validOptions.insert("doNotPreserveFaceZones", "");
363 argList::validOptions.insert("overwrite", "");
365 # include "setRootCase.H"
366 # include "createTime.H"
368 instantList timeDirs = timeSelector::select0(runTime, args);
370 # include "createMesh.H"
372 const word oldInstance = mesh.pointsInstance();
374 // Mark boundary edges and points.
375 // (Note: in 1.4.2 we can use the built-in mesh point ordering
377 PackedBoolList isBoundaryEdge(mesh.nEdges());
378 for (label faceI = mesh.nInternalFaces(); faceI < mesh.nFaces(); faceI++)
380 const labelList& fEdges = mesh.faceEdges()[faceI];
384 isBoundaryEdge.set(fEdges[i], 1);
388 scalar featureAngle(readScalar(IStringStream(args.additionalArgs()[0])()));
390 scalar minCos = Foam::cos(featureAngle * mathematicalConstant::pi/180.0);
392 Info<< "Feature:" << featureAngle << endl
393 << "minCos :" << minCos << endl
397 const bool splitAllFaces = args.optionFound("splitAllFaces");
400 Info<< "Splitting all internal faces to create multiple faces"
401 << " between two cells." << nl
405 const bool overwrite = args.optionFound("overwrite");
406 const bool doNotPreserveFaceZones = args.optionFound
408 "doNotPreserveFaceZones"
410 const bool concaveMultiCells = args.optionFound("concaveMultiCells");
411 if (concaveMultiCells)
413 Info<< "Generating multiple cells for points on concave feature edges."
418 // Face(centre)s that need inclusion in the dual mesh
419 labelList featureFaces;
421 labelList featureEdges;
422 // Points (that become a single cell) that need inclusion in the dual mesh
423 labelList singleCellFeaturePoints;
424 // Points (that become a multiple cells) ,,
425 labelList multiCellFeaturePoints;
427 // Sample implementation of feature detection.
434 doNotPreserveFaceZones,
438 singleCellFeaturePoints,
439 multiCellFeaturePoints
442 // If we want to split all polyMesh faces into one dualface per cell
443 // we are passing through we also need a point
444 // at the polyMesh facecentre and edgemid of the faces we want to
448 featureEdges = identity(mesh.nEdges());
449 featureFaces = identity(mesh.nFaces());
452 // Write obj files for debugging
458 singleCellFeaturePoints,
459 multiCellFeaturePoints
464 // Read objects in time directory
465 IOobjectList objects(mesh, runTime.timeName());
468 PtrList<volScalarField> vsFlds;
469 ReadFields(mesh, objects, vsFlds);
471 PtrList<volVectorField> vvFlds;
472 ReadFields(mesh, objects, vvFlds);
474 PtrList<volSphericalTensorField> vstFlds;
475 ReadFields(mesh, objects, vstFlds);
477 PtrList<volSymmTensorField> vsymtFlds;
478 ReadFields(mesh, objects, vsymtFlds);
480 PtrList<volTensorField> vtFlds;
481 ReadFields(mesh, objects, vtFlds);
483 // Read surface fields.
484 PtrList<surfaceScalarField> ssFlds;
485 ReadFields(mesh, objects, ssFlds);
487 PtrList<surfaceVectorField> svFlds;
488 ReadFields(mesh, objects, svFlds);
490 PtrList<surfaceSphericalTensorField> sstFlds;
491 ReadFields(mesh, objects, sstFlds);
493 PtrList<surfaceSymmTensorField> ssymtFlds;
494 ReadFields(mesh, objects, ssymtFlds);
496 PtrList<surfaceTensorField> stFlds;
497 ReadFields(mesh, objects, stFlds);
500 // Topo change container
501 directTopoChange meshMod(mesh.boundaryMesh().size());
503 // Mesh dualiser engine
504 meshDualiser dualMaker(mesh);
506 // Insert all commands into directTopoChange to create dual of mesh.
507 // This does all the hard work.
508 dualMaker.setRefinement
513 singleCellFeaturePoints,
514 multiCellFeaturePoints,
518 // Create mesh, return map from old to new mesh.
519 autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, false);
522 mesh.updateMesh(map);
524 // Optionally inflate mesh
525 if (map().hasMotionPoints())
527 mesh.movePoints(map().preMotionPoints());
536 mesh.setInstance(oldInstance);
539 Info<< "Writing dual mesh to " << runTime.timeName() << endl;
543 Info<< "End\n" << endl;
549 // ************************************************************************* //