FreeFOAM The Cross-Platform CFD Toolkit
Hosted by SourceForge:
Get FreeFOAM at SourceForge.net.
            Fast, secure and Free Open Source software downloads

collapseEdges.C

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------------*\
00002   =========                 |
00003   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
00004    \\    /   O peration     |
00005     \\  /    A nd           | Copyright (C) 1991-2010 OpenCFD Ltd.
00006      \\/     M anipulation  |
00007 -------------------------------------------------------------------------------
00008 License
00009     This file is part of OpenFOAM.
00010 
00011     OpenFOAM is free software: you can redistribute it and/or modify it
00012     under the terms of the GNU General Public License as published by
00013     the Free Software Foundation, either version 3 of the License, or
00014     (at your option) any later version.
00015 
00016     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
00017     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00019     for more details.
00020 
00021     You should have received a copy of the GNU General Public License
00022     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
00023 
00024 Application
00025     collapseEdges
00026 
00027 Description
00028     Collapse short edges and combines edges that are in line.
00029 
00030     - collapse short edges. Length of edges to collapse provided as argument.
00031     - merge two edges if they are in line. Maximum angle provided as argument.
00032     - remove unused points.
00033 
00034     Cannot remove cells. Can remove faces and points but does not check
00035     for nonsense resulting topology.
00036 
00037     When collapsing an edge with one point on the boundary it will leave
00038     the boundary point intact. When both points inside it chooses random. When
00039     both points on boundary random again. Note: it should in fact use features
00040     where if one point is on a feature it collapses to that one. Alas we don't
00041     have features on a polyMesh.
00042 
00043 Usage
00044 
00045     - collapseEdges [OPTIONS] <edge length [m]> <merge angle [degrees]>
00046 
00047     @param <edge length [m]> \n
00048     @todo Detailed description of argument.
00049 
00050     @param <merge angle [degrees]> \n
00051     @todo Detailed description of argument.
00052 
00053     @param -overwrite \n
00054     Overwrite existing data.
00055 
00056     @param -case <dir>\n
00057     Case directory.
00058 
00059     @param -help \n
00060     Display help message.
00061 
00062     @param -doc \n
00063     Display Doxygen API documentation page for this application.
00064 
00065     @param -srcDoc \n
00066     Display Doxygen source documentation page for this application.
00067 
00068 \*---------------------------------------------------------------------------*/
00069 
00070 #include <OpenFOAM/argList.H>
00071 #include <OpenFOAM/Time.H>
00072 #include <dynamicMesh/edgeCollapser.H>
00073 #include <dynamicMesh/polyTopoChange.H>
00074 #include <dynamicMesh/polyTopoChanger.H>
00075 #include <OpenFOAM/polyMesh.H>
00076 #include <OpenFOAM/mapPolyMesh.H>
00077 #include <OpenFOAM/mathematicalConstants.H>
00078 #include <OpenFOAM/PackedBoolList.H>
00079 #include <OpenFOAM/SortableList.H>
00080 
00081 using namespace Foam;
00082 
00083 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
00084 
00085 // Get faceEdges in order of face points, i.e. faceEdges[0] is between
00086 // f[0] and f[1]
00087 labelList getSortedEdges
00088 (
00089     const edgeList& edges,
00090     const labelList& f,
00091     const labelList& edgeLabels
00092 )
00093 {
00094     labelList faceEdges(edgeLabels.size(), -1);
00095 
00096     // Find starting pos in f for every edgeLabels
00097     forAll(edgeLabels, i)
00098     {
00099         label edgeI = edgeLabels[i];
00100 
00101         const edge& e = edges[edgeI];
00102 
00103         label fp = findIndex(f, e[0]);
00104         label fp1 = f.fcIndex(fp);
00105 
00106         if (f[fp1] == e[1])
00107         {
00108             // EdgeI between fp -> fp1
00109             faceEdges[fp] = edgeI;
00110         }
00111         else
00112         {
00113             // EdgeI between fp-1 -> fp
00114             faceEdges[f.rcIndex(fp)] = edgeI;
00115         }
00116     }
00117 
00118     return faceEdges;
00119 }
00120 
00121 
00122 // Merges edges which are in straight line. I.e. edge split by point.
00123 label mergeEdges
00124 (
00125     const polyMesh& mesh,
00126     const scalar maxCos,
00127     edgeCollapser& collapser
00128 )
00129 {
00130     const pointField& points = mesh.points();
00131     const edgeList& edges = mesh.edges();
00132     const labelListList& pointEdges = mesh.pointEdges();
00133     const labelList& region = collapser.pointRegion();
00134     const labelList& master = collapser.pointRegionMaster();
00135 
00136     label nCollapsed = 0;
00137 
00138     forAll(pointEdges, pointI)
00139     {
00140         const labelList& pEdges = pointEdges[pointI];
00141 
00142         if (pEdges.size() == 2)
00143         {
00144             const edge& leftE = edges[pEdges[0]];
00145             const edge& rightE = edges[pEdges[1]];
00146 
00147             // Get the two vertices on both sides of the point
00148             label leftV = leftE.otherVertex(pointI);
00149             label rightV = rightE.otherVertex(pointI);
00150 
00151             // Collapse only if none of the points part of merge network
00152             // or all of networks with different masters.
00153             label midMaster = -1;
00154             if (region[pointI] != -1)
00155             {
00156                 midMaster = master[region[pointI]];
00157             }
00158 
00159             label leftMaster = -2;
00160             if (region[leftV] != -1)
00161             {
00162                 leftMaster = master[region[leftV]];
00163             }
00164 
00165             label rightMaster = -3;
00166             if (region[rightV] != -1)
00167             {
00168                 rightMaster = master[region[rightV]];
00169             }
00170 
00171             if
00172             (
00173                 midMaster != leftMaster
00174              && midMaster != rightMaster
00175              && leftMaster != rightMaster
00176             )
00177             {
00178                 // Check if the two edge are in line
00179                 vector leftVec = points[pointI] - points[leftV];
00180                 leftVec /= mag(leftVec) + VSMALL;
00181 
00182                 vector rightVec = points[rightV] - points[pointI];
00183                 rightVec /= mag(rightVec) + VSMALL;
00184 
00185                 if ((leftVec & rightVec) > maxCos)
00186                 {
00187                     // Collapse one (left) side of the edge. Make left vertex
00188                     // the master.
00189                     //if (collapser.unaffectedEdge(pEdges[0]))
00190                     {
00191                         collapser.collapseEdge(pEdges[0], leftV);
00192                         nCollapsed++;
00193                     }
00194                 }
00195             }
00196         }
00197     }
00198 
00199     return nCollapsed;
00200 }
00201 
00202 
00203 // Return master point edge needs to be collapsed to (or -1)
00204 label edgeMaster(const PackedBoolList& boundaryPoint, const edge& e)
00205 {
00206     label masterPoint = -1;
00207 
00208     // Collapse edge to boundary point.
00209     if (boundaryPoint.get(e[0]))
00210     {
00211         if (boundaryPoint.get(e[1]))
00212         {
00213             // Both points on boundary. Choose one to collapse to.
00214             // Note: should look at feature edges/points!
00215             masterPoint = e[0];
00216         }
00217         else
00218         {
00219             masterPoint = e[0];
00220         }
00221     }
00222     else
00223     {
00224         if (boundaryPoint.get(e[1]))
00225         {
00226             masterPoint = e[1];
00227         }
00228         else
00229         {
00230             // None on boundary. Choose arbitrary.
00231             // Note: should look at geometry?
00232             masterPoint = e[0];
00233         }
00234     }
00235     return masterPoint;
00236 }
00237 
00238 
00239 label collapseSmallEdges
00240 (
00241     const polyMesh& mesh,
00242     const PackedBoolList& boundaryPoint,
00243     const scalar minLen,
00244     edgeCollapser& collapser
00245 )
00246 {
00247     const pointField& points = mesh.points();
00248     const edgeList& edges = mesh.edges();
00249 
00250     // Collapse all edges that are too small. Choose intelligently which
00251     // point to collapse edge to.
00252 
00253     label nCollapsed = 0;
00254 
00255     forAll(edges, edgeI)
00256     {
00257         const edge& e = edges[edgeI];
00258 
00259         if (e.mag(points) < minLen)
00260         {
00261             label master = edgeMaster(boundaryPoint, e);
00262 
00263             if (master != -1) // && collapser.unaffectedEdge(edgeI))
00264             {
00265                 collapser.collapseEdge(edgeI, master);
00266                 nCollapsed++;
00267             }
00268         }
00269     }
00270     return nCollapsed;
00271 }
00272 
00273 
00274 // Faces which have edges just larger than collapse length but faces which
00275 // are very small. This one tries to collapse them if it can be done with
00276 // edge collapse. For faces where a face gets replace by two edges use
00277 // collapseFaces
00278 label collapseHighAspectFaces
00279 (
00280     const polyMesh& mesh,
00281     const PackedBoolList& boundaryPoint,
00282     const scalar areaFac,
00283     const scalar edgeRatio,
00284     edgeCollapser& collapser
00285 )
00286 {
00287     const pointField& points = mesh.points();
00288     const edgeList& edges = mesh.edges();
00289     const faceList& faces = mesh.faces();
00290     const labelListList& faceEdges = mesh.faceEdges();
00291 
00292     scalarField magArea(mag(mesh.faceAreas()));
00293 
00294     label maxIndex = findMax(magArea);
00295 
00296     scalar minArea = areaFac * magArea[maxIndex];
00297 
00298     Info<< "Max face area:" << magArea[maxIndex] << endl
00299         << "Collapse area factor:" << areaFac << endl
00300         << "Collapse area:" << minArea << endl;
00301 
00302     label nCollapsed = 0;
00303 
00304     forAll(faces, faceI)
00305     {
00306         if (magArea[faceI] < minArea)
00307         {
00308             const face& f = faces[faceI];
00309 
00310             // Get the edges in face point order
00311             labelList fEdges(getSortedEdges(edges, f, faceEdges[faceI]));
00312 
00313             SortableList<scalar> lengths(fEdges.size());
00314             forAll(fEdges, i)
00315             {
00316                 lengths[i] = edges[fEdges[i]].mag(points);
00317             }
00318             lengths.sort();
00319 
00320 
00321             label edgeI = -1;
00322 
00323             if (f.size() == 4)
00324             {
00325                 // Compare second largest to smallest
00326                 if (lengths[2] > edgeRatio*lengths[0])
00327                 {
00328                     // Collapse smallest only. Triangle should be cleared
00329                     // next time around.
00330                     edgeI = fEdges[lengths.indices()[0]];
00331                 }
00332             }
00333             else if (f.size() == 3)
00334             {
00335                 // Compare second largest to smallest
00336                 if (lengths[1] > edgeRatio*lengths[0])
00337                 {
00338                     edgeI = fEdges[lengths.indices()[0]];
00339                 }
00340             }
00341 
00342 
00343             if (edgeI != -1)
00344             {
00345                 label master = edgeMaster(boundaryPoint, edges[edgeI]);
00346 
00347                 if (master != -1)// && collapser.unaffectedEdge(edgeI))
00348                 {
00349                     collapser.collapseEdge(edgeI, master);
00350                     nCollapsed++;
00351                 }
00352             }
00353         }
00354     }
00355 
00356     return nCollapsed;
00357 }
00358 
00359 
00360 void set(const labelList& elems, const bool val, boolList& status)
00361 {
00362     forAll(elems, i)
00363     {
00364         status[elems[i]] = val;
00365     }
00366 }
00367 
00368 
00369 // Tries to simplify polygons to face of minSize (4=quad, 3=triangle)
00370 label simplifyFaces
00371 (
00372     const polyMesh& mesh,
00373     const PackedBoolList& boundaryPoint,
00374     const label minSize,
00375     const scalar lenGap,
00376     edgeCollapser& collapser
00377 )
00378 {
00379     const pointField& points = mesh.points();
00380     const edgeList& edges = mesh.edges();
00381     const faceList& faces = mesh.faces();
00382     const cellList& cells = mesh.cells();
00383     const labelListList& faceEdges = mesh.faceEdges();
00384     const labelList& faceOwner = mesh.faceOwner();
00385     const labelList& faceNeighbour = mesh.faceNeighbour();
00386     const labelListList& pointCells = mesh.pointCells();
00387     const labelListList& cellEdges = mesh.cellEdges();
00388 
00389     label nCollapsed = 0;
00390 
00391     boolList protectedEdge(mesh.nEdges(), false);
00392 
00393     forAll(faces, faceI)
00394     {
00395         const face& f = faces[faceI];
00396 
00397         if
00398         (
00399             f.size() > minSize
00400          && cells[faceOwner[faceI]].size() >= 6
00401          && (
00402                 mesh.isInternalFace(faceI)
00403              && cells[faceNeighbour[faceI]].size() >= 6
00404             )
00405         )
00406         {
00407             // Get the edges in face point order
00408             labelList fEdges(getSortedEdges(edges, f, faceEdges[faceI]));
00409 
00410             SortableList<scalar> lengths(fEdges.size());
00411             forAll(fEdges, i)
00412             {
00413                 lengths[i] = edges[fEdges[i]].mag(points);
00414             }
00415             lengths.sort();
00416 
00417 
00418             // Now find a gap in length between consecutive elements greater
00419             // than lenGap.
00420 
00421             label gapPos = -1;
00422 
00423             for (label i = f.size()-1-minSize; i >= 0; --i)
00424             {
00425                 if (lengths[i+1] > lenGap*lengths[i])
00426                 {
00427                     gapPos = i;
00428 
00429                     break;
00430                 }
00431             }
00432 
00433             if (gapPos != -1)
00434             {
00435                 //for (label i = gapPos; i >= 0; --i)
00436                 label i = 0;  // Hack: collapse smallest edge only.
00437                 {
00438                     label edgeI = fEdges[lengths.indices()[i]];
00439 
00440                     if (!protectedEdge[edgeI])
00441                     {
00442                         const edge& e = edges[edgeI];
00443 
00444                         label master = edgeMaster(boundaryPoint, e);
00445 
00446                         if (master != -1)
00447                         {
00448                             collapser.collapseEdge(edgeI, master);
00449 
00450                             // Protect all other edges on all cells using edge
00451                             // points.
00452 
00453                             const labelList& pCells0 = pointCells[e[0]];
00454 
00455                             forAll(pCells0, i)
00456                             {
00457                                 set(cellEdges[pCells0[i]], true, protectedEdge);
00458                             }
00459                             const labelList& pCells1 = pointCells[e[1]];
00460 
00461                             forAll(pCells1, i)
00462                             {
00463                                 set(cellEdges[pCells1[i]], true, protectedEdge);
00464                             }
00465 
00466                             nCollapsed++;
00467                         }
00468                     }
00469                 }
00470             }
00471         }
00472     }
00473 
00474     return nCollapsed;
00475 }
00476 
00477 
00478 // Main program:
00479 
00480 int main(int argc, char *argv[])
00481 {
00482     argList::noParallel();
00483     argList::validOptions.insert("overwrite", "");
00484     argList::validArgs.append("edge length [m]");
00485     argList::validArgs.append("merge angle (degrees)");
00486 
00487 #   include <OpenFOAM/setRootCase.H>
00488 #   include <OpenFOAM/createTime.H>
00489     runTime.functionObjects().off();
00490 #   include <OpenFOAM/createPolyMesh.H>
00491     const word oldInstance = mesh.pointsInstance();
00492 
00493     scalar minLen(readScalar(IStringStream(args.additionalArgs()[0])()));
00494     scalar angle(readScalar(IStringStream(args.additionalArgs()[1])()));
00495     bool overwrite = args.optionFound("overwrite");
00496 
00497     scalar maxCos = Foam::cos(angle*mathematicalConstant::pi/180.0);
00498 
00499     Info<< "Merging:" << nl
00500         << "    edges with length less than " << minLen << " meters" << nl
00501         << "    edges split by a point with edges in line to within " << angle
00502         << " degrees" << nl
00503         << endl;
00504 
00505 
00506     bool meshChanged = false;
00507 
00508     while (true)
00509     {
00510         const faceList& faces = mesh.faces();
00511 
00512         // Get all points on the boundary
00513         PackedBoolList boundaryPoint(mesh.nPoints());
00514 
00515         label nIntFaces = mesh.nInternalFaces();
00516         for (label faceI = nIntFaces; faceI < mesh.nFaces(); faceI++)
00517         {
00518             const face& f = faces[faceI];
00519 
00520             forAll(f, fp)
00521             {
00522                 boundaryPoint.set(f[fp], 1);
00523             }
00524         }
00525 
00526         // Edge collapsing engine
00527         edgeCollapser collapser(mesh);
00528 
00529 
00530         // Collapse all edges that are too small.
00531         label nCollapsed =
00532             collapseSmallEdges
00533             (
00534                 mesh,
00535                 boundaryPoint,
00536                 minLen,
00537                 collapser
00538             );
00539         Info<< "Collapsing " << nCollapsed << " small edges" << endl;
00540 
00541 
00542         // Remove midpoints on straight edges.
00543         if (nCollapsed == 0)
00544         {
00545             nCollapsed = mergeEdges(mesh, maxCos, collapser);
00546             Info<< "Collapsing " << nCollapsed << " in line edges" << endl;
00547         }
00548 
00549 
00550         // Remove small sliver faces that can be collapsed to single edge
00551         if (nCollapsed == 0)
00552         {
00553             nCollapsed =
00554                 collapseHighAspectFaces
00555                 (
00556                     mesh,
00557                     boundaryPoint,
00558                     1E-9,       // factor of largest face area
00559                     5,          // factor between smallest and largest edge on
00560                                 // face
00561                     collapser
00562                 );
00563             Info<< "Collapsing " << nCollapsed
00564                 << " small high aspect ratio faces" << endl;
00565         }
00566 
00567         // Simplify faces to quads wherever possible
00568         //if (nCollapsed == 0)
00569         //{
00570         //    nCollapsed =
00571         //        simplifyFaces
00572         //        (
00573         //            mesh,
00574         //            boundaryPoint,
00575         //            4,              // minimum size of face
00576         //            0.2,            // gap in edge lengths on face
00577         //            collapser
00578         //        );
00579         //    Info<< "Collapsing " << nCollapsed << " polygonal faces" << endl;
00580         //}
00581 
00582 
00583         if (nCollapsed == 0)
00584         {
00585             break;
00586         }
00587 
00588         polyTopoChange meshMod(mesh);
00589 
00590         // Insert mesh refinement into polyTopoChange.
00591         collapser.setRefinement(meshMod);
00592 
00593         // Do all changes
00594         Info<< "Morphing ..." << endl;
00595 
00596         autoPtr<mapPolyMesh> morphMap = meshMod.changeMesh(mesh, false);
00597 
00598         collapser.updateMesh(morphMap());
00599 
00600         if (morphMap().hasMotionPoints())
00601         {
00602             mesh.movePoints(morphMap().preMotionPoints());
00603         }
00604 
00605         meshChanged = true;
00606     }
00607 
00608     if (meshChanged)
00609     {
00610         // Write resulting mesh
00611         if (!overwrite)
00612         {
00613             runTime++;
00614         }
00615         else
00616         {
00617             mesh.setInstance(oldInstance);
00618         }
00619 
00620         Info<< "Writing collapsed mesh to time " << runTime.timeName() << endl;
00621 
00622         mesh.write();
00623     }
00624 
00625     Info << "End\n" << endl;
00626 
00627     return 0;
00628 }
00629 
00630 
00631 // ************************ vim: set sw=4 sts=4 et: ************************ //
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines