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 Collapse short edges and combines edges that are in line.
28 - collapse short edges. Length of edges to collapse provided as argument.
29 - merge two edges if they are in line. Maximum angle provided as argument.
30 - remove unused points.
32 Cannot remove cells. Can remove faces and points but does not check
33 for nonsense resulting topology.
35 When collapsing an edge with one point on the boundary it will leave
36 the boundary point intact. When both points inside it chooses random. When
37 both points on boundary random again. Note: it should in fact use features
38 where if one point is on a feature it collapses to that one. Alas we don't
39 have features on a polyMesh.
41 \*---------------------------------------------------------------------------*/
45 #include "edgeCollapser.H"
46 #include "polyTopoChange.H"
47 #include "polyTopoChanger.H"
49 #include "mapPolyMesh.H"
50 #include "mathematicalConstants.H"
51 #include "PackedBoolList.H"
52 #include "SortableList.H"
56 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
58 // Get faceEdges in order of face points, i.e. faceEdges[0] is between
60 labelList getSortedEdges
62 const edgeList& edges,
64 const labelList& edgeLabels
67 labelList faceEdges(edgeLabels.size(), -1);
69 // Find starting pos in f for every edgeLabels
72 label edgeI = edgeLabels[i];
74 const edge& e = edges[edgeI];
76 label fp = findIndex(f, e[0]);
77 label fp1 = f.fcIndex(fp);
81 // EdgeI between fp -> fp1
82 faceEdges[fp] = edgeI;
86 // EdgeI between fp-1 -> fp
87 faceEdges[f.rcIndex(fp)] = edgeI;
95 // Merges edges which are in straight line. I.e. edge split by point.
100 edgeCollapser& collapser
103 const pointField& points = mesh.points();
104 const edgeList& edges = mesh.edges();
105 const labelListList& pointEdges = mesh.pointEdges();
106 const labelList& region = collapser.pointRegion();
107 const labelList& master = collapser.pointRegionMaster();
109 label nCollapsed = 0;
111 forAll(pointEdges, pointI)
113 const labelList& pEdges = pointEdges[pointI];
115 if (pEdges.size() == 2)
117 const edge& leftE = edges[pEdges[0]];
118 const edge& rightE = edges[pEdges[1]];
120 // Get the two vertices on both sides of the point
121 label leftV = leftE.otherVertex(pointI);
122 label rightV = rightE.otherVertex(pointI);
124 // Collapse only if none of the points part of merge network
125 // or all of networks with different masters.
126 label midMaster = -1;
127 if (region[pointI] != -1)
129 midMaster = master[region[pointI]];
132 label leftMaster = -2;
133 if (region[leftV] != -1)
135 leftMaster = master[region[leftV]];
138 label rightMaster = -3;
139 if (region[rightV] != -1)
141 rightMaster = master[region[rightV]];
146 midMaster != leftMaster
147 && midMaster != rightMaster
148 && leftMaster != rightMaster
151 // Check if the two edge are in line
152 vector leftVec = points[pointI] - points[leftV];
153 leftVec /= mag(leftVec) + VSMALL;
155 vector rightVec = points[rightV] - points[pointI];
156 rightVec /= mag(rightVec) + VSMALL;
158 if ((leftVec & rightVec) > maxCos)
160 // Collapse one (left) side of the edge. Make left vertex
162 //if (collapser.unaffectedEdge(pEdges[0]))
164 collapser.collapseEdge(pEdges[0], leftV);
176 // Return master point edge needs to be collapsed to (or -1)
177 label edgeMaster(const PackedBoolList& boundaryPoint, const edge& e)
179 label masterPoint = -1;
181 // Collapse edge to boundary point.
182 if (boundaryPoint.get(e[0]))
184 if (boundaryPoint.get(e[1]))
186 // Both points on boundary. Choose one to collapse to.
187 // Note: should look at feature edges/points!
197 if (boundaryPoint.get(e[1]))
203 // None on boundary. Choose arbitrary.
204 // Note: should look at geometry?
212 label collapseSmallEdges
214 const polyMesh& mesh,
215 const PackedBoolList& boundaryPoint,
217 edgeCollapser& collapser
220 const pointField& points = mesh.points();
221 const edgeList& edges = mesh.edges();
223 // Collapse all edges that are too small. Choose intelligently which
224 // point to collapse edge to.
226 label nCollapsed = 0;
230 const edge& e = edges[edgeI];
232 if (e.mag(points) < minLen)
234 label master = edgeMaster(boundaryPoint, e);
236 if (master != -1) // && collapser.unaffectedEdge(edgeI))
238 collapser.collapseEdge(edgeI, master);
247 // Faces which have edges just larger than collapse length but faces which
248 // are very small. This one tries to collapse them if it can be done with
249 // edge collapse. For faces where a face gets replace by two edges use
251 label collapseHighAspectFaces
253 const polyMesh& mesh,
254 const PackedBoolList& boundaryPoint,
255 const scalar areaFac,
256 const scalar edgeRatio,
257 edgeCollapser& collapser
260 const pointField& points = mesh.points();
261 const edgeList& edges = mesh.edges();
262 const faceList& faces = mesh.faces();
263 const labelListList& faceEdges = mesh.faceEdges();
265 scalarField magArea(mag(mesh.faceAreas()));
267 label maxIndex = findMax(magArea);
269 scalar minArea = areaFac * magArea[maxIndex];
271 Info<< "Max face area:" << magArea[maxIndex] << endl
272 << "Collapse area factor:" << areaFac << endl
273 << "Collapse area:" << minArea << endl;
275 label nCollapsed = 0;
279 if (magArea[faceI] < minArea)
281 const face& f = faces[faceI];
283 // Get the edges in face point order
284 labelList fEdges(getSortedEdges(edges, f, faceEdges[faceI]));
286 SortableList<scalar> lengths(fEdges.size());
289 lengths[i] = edges[fEdges[i]].mag(points);
298 // Compare second largest to smallest
299 if (lengths[2] > edgeRatio*lengths[0])
301 // Collapse smallest only. Triangle should be cleared
303 edgeI = fEdges[lengths.indices()[0]];
306 else if (f.size() == 3)
308 // Compare second largest to smallest
309 if (lengths[1] > edgeRatio*lengths[0])
311 edgeI = fEdges[lengths.indices()[0]];
318 label master = edgeMaster(boundaryPoint, edges[edgeI]);
320 if (master != -1)// && collapser.unaffectedEdge(edgeI))
322 collapser.collapseEdge(edgeI, master);
333 void set(const labelList& elems, const bool val, boolList& status)
337 status[elems[i]] = val;
342 // Tries to simplify polygons to face of minSize (4=quad, 3=triangle)
345 const polyMesh& mesh,
346 const PackedBoolList& boundaryPoint,
349 edgeCollapser& collapser
352 const pointField& points = mesh.points();
353 const edgeList& edges = mesh.edges();
354 const faceList& faces = mesh.faces();
355 const cellList& cells = mesh.cells();
356 const labelListList& faceEdges = mesh.faceEdges();
357 const labelList& faceOwner = mesh.faceOwner();
358 const labelList& faceNeighbour = mesh.faceNeighbour();
359 const labelListList& pointCells = mesh.pointCells();
360 const labelListList& cellEdges = mesh.cellEdges();
362 label nCollapsed = 0;
364 boolList protectedEdge(mesh.nEdges(), false);
368 const face& f = faces[faceI];
373 && cells[faceOwner[faceI]].size() >= 6
375 mesh.isInternalFace(faceI)
376 && cells[faceNeighbour[faceI]].size() >= 6
380 // Get the edges in face point order
381 labelList fEdges(getSortedEdges(edges, f, faceEdges[faceI]));
383 SortableList<scalar> lengths(fEdges.size());
386 lengths[i] = edges[fEdges[i]].mag(points);
391 // Now find a gap in length between consecutive elements greater
396 for (label i = f.size()-1-minSize; i >= 0; --i)
398 if (lengths[i+1] > lenGap*lengths[i])
408 //for (label i = gapPos; i >= 0; --i)
409 label i = 0; // Hack: collapse smallest edge only.
411 label edgeI = fEdges[lengths.indices()[i]];
413 if (!protectedEdge[edgeI])
415 const edge& e = edges[edgeI];
417 label master = edgeMaster(boundaryPoint, e);
421 collapser.collapseEdge(edgeI, master);
423 // Protect all other edges on all cells using edge
426 const labelList& pCells0 = pointCells[e[0]];
430 set(cellEdges[pCells0[i]], true, protectedEdge);
432 const labelList& pCells1 = pointCells[e[1]];
436 set(cellEdges[pCells1[i]], true, protectedEdge);
453 int main(int argc, char *argv[])
455 argList::noParallel();
456 argList::validOptions.insert("overwrite", "");
457 argList::validArgs.append("edge length [m]");
458 argList::validArgs.append("merge angle (degrees)");
460 # include "setRootCase.H"
461 # include "createTime.H"
462 runTime.functionObjects().off();
463 # include "createPolyMesh.H"
464 const word oldInstance = mesh.pointsInstance();
466 scalar minLen(readScalar(IStringStream(args.additionalArgs()[0])()));
467 scalar angle(readScalar(IStringStream(args.additionalArgs()[1])()));
468 bool overwrite = args.optionFound("overwrite");
470 scalar maxCos = Foam::cos(angle*180/mathematicalConstant::pi);
472 Info<< "Merging:" << nl
473 << " edges with length less than " << minLen << " meters" << nl
474 << " edges split by a point with edges in line to within " << angle
479 bool meshChanged = false;
483 const faceList& faces = mesh.faces();
485 // Get all points on the boundary
486 PackedBoolList boundaryPoint(mesh.nPoints());
488 label nIntFaces = mesh.nInternalFaces();
489 for (label faceI = nIntFaces; faceI < mesh.nFaces(); faceI++)
491 const face& f = faces[faceI];
495 boundaryPoint.set(f[fp], 1);
499 // Edge collapsing engine
500 edgeCollapser collapser(mesh);
503 // Collapse all edges that are too small.
512 Info<< "Collapsing " << nCollapsed << " small edges" << endl;
515 // Remove midpoints on straight edges.
518 nCollapsed = mergeEdges(mesh, maxCos, collapser);
519 Info<< "Collapsing " << nCollapsed << " in line edges" << endl;
523 // Remove small sliver faces that can be collapsed to single edge
527 collapseHighAspectFaces
531 1E-9, // factor of largest face area
532 5, // factor between smallest and largest edge on
536 Info<< "Collapsing " << nCollapsed
537 << " small high aspect ratio faces" << endl;
540 // Simplify faces to quads wherever possible
541 //if (nCollapsed == 0)
548 // 4, // minimum size of face
549 // 0.2, // gap in edge lengths on face
552 // Info<< "Collapsing " << nCollapsed << " polygonal faces" << endl;
561 polyTopoChange meshMod(mesh);
563 // Insert mesh refinement into polyTopoChange.
564 collapser.setRefinement(meshMod);
567 Info<< "Morphing ..." << endl;
569 autoPtr<mapPolyMesh> morphMap = meshMod.changeMesh(mesh, false);
571 collapser.updateMesh(morphMap());
573 if (morphMap().hasMotionPoints())
575 mesh.movePoints(morphMap().preMotionPoints());
583 // Write resulting mesh
590 mesh.setInstance(oldInstance);
593 Info<< "Writing collapsed mesh to time " << runTime.timeName() << endl;
598 Info << "End\n" << endl;
604 // ************************************************************************* //