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

surfaceCheck.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     surfaceCheck
00026 
00027 Description
00028     Performs various checks on surface.
00029 
00030 Usage
00031 
00032     - surfaceCheck [OPTIONS] <Foam surface file>
00033 
00034     @param <Foam surface file> \n
00035     @todo Detailed description of argument.
00036 
00037     @param -noSelfIntersection \n
00038     Check for self-intersection.
00039 
00040     @param -case <dir>\n
00041     Case directory.
00042 
00043     @param -help \n
00044     Display help message.
00045 
00046     @param -doc \n
00047     Display Doxygen API documentation page for this application.
00048 
00049     @param -srcDoc \n
00050     Display Doxygen source documentation page for this application.
00051 
00052 \*---------------------------------------------------------------------------*/
00053 
00054 #include <OpenFOAM/triangle.H>
00055 #include <triSurface/triSurface.H>
00056 #include <meshTools/triSurfaceTools.H>
00057 #include <meshTools/triSurfaceSearch.H>
00058 #include <OpenFOAM/argList.H>
00059 #include <OpenFOAM/OFstream.H>
00060 #include <meshTools/surfaceIntersection.H>
00061 #include <OpenFOAM/SortableList.H>
00062 #include <OpenFOAM/PatchTools.H>
00063 
00064 using namespace Foam;
00065 
00066 // Does face use valid vertices?
00067 bool validTri(const bool verbose, const triSurface& surf, const label faceI)
00068 {
00069     // Simple check on indices ok.
00070 
00071     const labelledTri& f = surf[faceI];
00072 
00073     if
00074     (
00075         (f[0] < 0) || (f[0] >= surf.points().size())
00076      || (f[1] < 0) || (f[1] >= surf.points().size())
00077      || (f[2] < 0) || (f[2] >= surf.points().size())
00078     )
00079     {
00080         WarningIn("validTri(const triSurface&, const label)")
00081             << "triangle " << faceI << " vertices " << f
00082             << " uses point indices outside point range 0.."
00083             << surf.points().size()-1 << endl;
00084 
00085         return false;
00086     }
00087 
00088     if ((f[0] == f[1]) || (f[0] == f[2]) || (f[1] == f[2]))
00089     {
00090         WarningIn("validTri(const triSurface&, const label)")
00091             << "triangle " << faceI
00092             << " uses non-unique vertices " << f
00093             << " coords:" << f.points(surf.points())
00094             << endl;
00095         return false;
00096     }
00097 
00098     // duplicate triangle check
00099 
00100     const labelList& fFaces = surf.faceFaces()[faceI];
00101 
00102     // Check if faceNeighbours use same points as this face.
00103     // Note: discards normal information - sides of baffle are merged.
00104     forAll(fFaces, i)
00105     {
00106         label nbrFaceI = fFaces[i];
00107 
00108         if (nbrFaceI <= faceI)
00109         {
00110             // lower numbered faces already checked
00111             continue;
00112         }
00113 
00114         const labelledTri& nbrF = surf[nbrFaceI];
00115 
00116         if
00117         (
00118             ((f[0] == nbrF[0]) || (f[0] == nbrF[1]) || (f[0] == nbrF[2]))
00119          && ((f[1] == nbrF[0]) || (f[1] == nbrF[1]) || (f[1] == nbrF[2]))
00120          && ((f[2] == nbrF[0]) || (f[2] == nbrF[1]) || (f[2] == nbrF[2]))
00121         )
00122         {
00123             WarningIn("validTri(const triSurface&, const label)")
00124                 << "triangle " << faceI << " vertices " << f
00125                 << " has the same vertices as triangle " << nbrFaceI
00126                 << " vertices " << nbrF
00127                 << " coords:" << f.points(surf.points())
00128                 << endl;
00129 
00130             return false;
00131         }
00132     }
00133     return true;
00134 }
00135 
00136 
00137 labelList countBins
00138 (
00139     const scalar min,
00140     const scalar max,
00141     const label nBins,
00142     const scalarField& vals
00143 )
00144 {
00145     scalar dist = nBins/(max - min);
00146 
00147     labelList binCount(nBins, 0);
00148 
00149     forAll(vals, i)
00150     {
00151         scalar val = vals[i];
00152 
00153         label index = -1;
00154 
00155         if (Foam::mag(val - min) < SMALL)
00156         {
00157             index = 0;
00158         }
00159         else if (val >= max - SMALL)
00160         {
00161             index = nBins - 1;
00162         }
00163         else
00164         {
00165             index = label((val - min)*dist);
00166 
00167             if ((index < 0) || (index >= nBins))
00168             {
00169                 WarningIn
00170                 (
00171                     "countBins(const scalar, const scalar, const label"
00172                     ", const scalarField&)"
00173                 )   << "value " << val << " at index " << i
00174                     << " outside range " << min << " .. " << max << endl;
00175 
00176                 if (index < 0)
00177                 {
00178                     index = 0;
00179                 }
00180                 else
00181                 {
00182                     index = nBins - 1;
00183                 }
00184             }
00185         }
00186         binCount[index]++;
00187     }
00188 
00189     return binCount;
00190 }
00191 
00192 
00193 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
00194 // Main program:
00195 
00196 int main(int argc, char *argv[])
00197 {
00198     argList::noParallel();
00199 
00200     argList::validArgs.clear();
00201     argList::validArgs.append("surface file");
00202     argList::validOptions.insert("checkSelfIntersection", "");
00203     argList::validOptions.insert("verbose", "");
00204     argList args(argc, argv);
00205 
00206     bool checkSelfIntersection = args.optionFound("checkSelfIntersection");
00207     bool verbose = args.optionFound("verbose");
00208 
00209     fileName surfFileName(args.additionalArgs()[0]);
00210     Pout<< "Reading surface from " << surfFileName << " ..." << nl << endl;
00211 
00212 
00213     // Read
00214     // ~~~~
00215 
00216     triSurface surf(surfFileName);
00217 
00218 
00219     Pout<< "Statistics:" << endl;
00220     surf.writeStats(Pout);
00221     Pout<< endl;
00222 
00223 
00224     // Region sizes
00225     // ~~~~~~~~~~~~
00226 
00227     {
00228         labelList regionSize(surf.patches().size(), 0);
00229 
00230         forAll(surf, faceI)
00231         {
00232             label region = surf[faceI].region();
00233 
00234             if (region < 0 || region >= regionSize.size())
00235             {
00236                 WarningIn(args.executable())
00237                     << "Triangle " << faceI << " vertices " << surf[faceI]
00238                     << " has region " << region << " which is outside the range"
00239                     << " of regions 0.." << surf.patches().size()-1
00240                     << endl;
00241             }
00242             else
00243             {
00244                 regionSize[region]++;
00245             }
00246         }
00247 
00248         Pout<< "Region\tSize" << nl
00249             << "------\t----" << nl;
00250         forAll(surf.patches(), patchI)
00251         {
00252             Pout<< surf.patches()[patchI].name() << '\t'
00253                 << regionSize[patchI] << nl;
00254         }
00255         Pout<< nl << endl;
00256     }
00257 
00258 
00259     // Check triangles
00260     // ~~~~~~~~~~~~~~~
00261 
00262     {
00263         DynamicList<label> illegalFaces(surf.size()/100 + 1);
00264 
00265         forAll(surf, faceI)
00266         {
00267             if (!validTri(verbose, surf, faceI))
00268             {
00269                 illegalFaces.append(faceI);
00270             }
00271         }
00272 
00273         if (illegalFaces.size())
00274         {
00275             Pout<< "Surface has " << illegalFaces.size()
00276                 << " illegal triangles." << endl;
00277 
00278             OFstream str("illegalFaces");
00279             Pout<< "Dumping conflicting face labels to " << str.name() << endl
00280                 << "Paste this into the input for surfaceSubset" << endl;
00281             str << illegalFaces;
00282         }
00283         else
00284         {
00285             Pout<< "Surface has no illegal triangles." << endl;
00286         }
00287         Pout<< endl;
00288     }
00289 
00290 
00291 
00292     // Triangle quality
00293     // ~~~~~~~~~~~~~~~~
00294 
00295     {
00296         scalarField triQ(surf.size(), 0);
00297         forAll(surf, faceI)
00298         {
00299             const labelledTri& f = surf[faceI];
00300 
00301             if (f[0] == f[1] || f[0] == f[2] || f[1] == f[2])
00302             {
00303                 //WarningIn(args.executable())
00304                 //    << "Illegal triangle " << faceI << " vertices " << f
00305                 //    << " coords " << f.points(surf.points()) << endl;
00306             }
00307             else
00308             {
00309                 triPointRef tri
00310                 (
00311                     surf.points()[f[0]],
00312                     surf.points()[f[1]],
00313                     surf.points()[f[2]]
00314                 );
00315 
00316                 vector ba(tri.b() - tri.a());
00317                 ba /= mag(ba) + VSMALL;
00318 
00319                 vector ca(tri.c() - tri.a());
00320                 ca /= mag(ca) + VSMALL;
00321 
00322                 if (mag(ba&ca) > 1-1E-3)
00323                 {
00324                     triQ[faceI] = SMALL;
00325                 }
00326                 else
00327                 {
00328                     triQ[faceI] = triPointRef
00329                     (
00330                         surf.points()[f[0]],
00331                         surf.points()[f[1]],
00332                         surf.points()[f[2]]
00333                     ).quality();
00334                 }
00335             }
00336         }
00337 
00338         labelList binCount = countBins(0, 1, 20, triQ);
00339 
00340         Pout<< "Triangle quality (equilateral=1, collapsed=0):"
00341             << endl;
00342 
00343 
00344         OSstream& os = Pout;
00345         os.width(4);
00346 
00347         scalar dist = (1.0 - 0.0)/20.0;
00348         scalar min = 0;
00349         forAll(binCount, binI)
00350         {
00351             Pout<< "    " << min << " .. " << min+dist << "  : "
00352                 << 1.0/surf.size() * binCount[binI]
00353                 << endl;
00354             min += dist;
00355         }
00356         Pout<< endl;
00357 
00358         label minIndex = findMin(triQ);
00359         label maxIndex = findMax(triQ);
00360 
00361         Pout<< "    min " << triQ[minIndex] << " for triangle " << minIndex
00362             << nl
00363             << "    max " << triQ[maxIndex] << " for triangle " << maxIndex
00364             << nl
00365             << endl;
00366 
00367 
00368         if (triQ[minIndex] < SMALL)
00369         {
00370             WarningIn(args.executable()) << "Minimum triangle quality is "
00371                 << triQ[minIndex] << ". This might give problems in"
00372                 << " self-intersection testing later on." << endl;
00373         }
00374 
00375         // Dump for subsetting
00376         {
00377             DynamicList<label> problemFaces(surf.size()/100+1);
00378 
00379             forAll(triQ, faceI)
00380             {
00381                 if (triQ[faceI] < 1E-11)
00382                 {
00383                     problemFaces.append(faceI);
00384                 }
00385             }
00386             OFstream str("badFaces");
00387 
00388             Pout<< "Dumping bad quality faces to " << str.name() << endl
00389                 << "Paste this into the input for surfaceSubset" << nl
00390                 << nl << endl;
00391 
00392             str << problemFaces;
00393         }
00394     }
00395 
00396 
00397 
00398     // Edges
00399     // ~~~~~
00400     {
00401         const edgeList& edges = surf.edges();
00402         const pointField& localPoints = surf.localPoints();
00403 
00404         scalarField edgeMag(edges.size());
00405 
00406         forAll(edges, edgeI)
00407         {
00408             edgeMag[edgeI] = edges[edgeI].mag(localPoints);
00409         }
00410 
00411         label minEdgeI = findMin(edgeMag);
00412         label maxEdgeI = findMax(edgeMag);
00413 
00414         const edge& minE = edges[minEdgeI];
00415         const edge& maxE = edges[maxEdgeI];
00416 
00417 
00418         Pout<< "Edges:" << nl
00419             << "    min " << edgeMag[minEdgeI] << " for edge " << minEdgeI
00420             << " points " << localPoints[minE[0]] << localPoints[minE[1]]
00421             << nl
00422             << "    max " << edgeMag[maxEdgeI] << " for edge " << maxEdgeI
00423             << " points " << localPoints[maxE[0]] << localPoints[maxE[1]]
00424             << nl
00425             << endl;
00426     }
00427 
00428 
00429 
00430     // Close points
00431     // ~~~~~~~~~~~~
00432     {
00433         const edgeList& edges = surf.edges();
00434         const pointField& localPoints = surf.localPoints();
00435 
00436         const boundBox bb(localPoints);
00437         scalar smallDim = 1E-6 * bb.mag();
00438 
00439         Pout<< "Checking for points less than 1E-6 of bounding box ("
00440             << bb.span() << " meter) apart."
00441             << endl;
00442 
00443         // Sort points
00444         SortableList<scalar> sortedMag(mag(localPoints));
00445 
00446         label nClose = 0;
00447 
00448         for (label i = 1; i < sortedMag.size(); i++)
00449         {
00450             label ptI = sortedMag.indices()[i];
00451 
00452             label prevPtI = sortedMag.indices()[i-1];
00453 
00454             if (mag(localPoints[ptI] - localPoints[prevPtI]) < smallDim)
00455             {
00456                 // Check if neighbours.
00457                 const labelList& pEdges = surf.pointEdges()[ptI];
00458 
00459                 label edgeI = -1;
00460 
00461                 forAll(pEdges, i)
00462                 {
00463                     const edge& e = edges[pEdges[i]];
00464 
00465                     if (e[0] == prevPtI || e[1] == prevPtI)
00466                     {
00467                         // point1 and point0 are connected through edge.
00468                         edgeI = pEdges[i];
00469 
00470                         break;
00471                     }
00472                 }
00473 
00474                 nClose++;
00475 
00476                 if (edgeI == -1)
00477                 {
00478                     Pout<< "    close unconnected points "
00479                         << ptI << ' ' << localPoints[ptI]
00480                         << " and " << prevPtI << ' '
00481                         << localPoints[prevPtI]
00482                         << " distance:"
00483                         << mag(localPoints[ptI] - localPoints[prevPtI])
00484                         << endl;
00485                 }
00486                 else
00487                 {
00488                     Pout<< "    small edge between points "
00489                         << ptI << ' ' << localPoints[ptI]
00490                         << " and " << prevPtI << ' '
00491                         << localPoints[prevPtI]
00492                         << " distance:"
00493                         << mag(localPoints[ptI] - localPoints[prevPtI])
00494                         << endl;
00495                 }
00496             }
00497         }
00498 
00499         Pout<< "Found " << nClose << " nearby points." << nl
00500             << endl;
00501     }
00502 
00503 
00504 
00505     // Check manifold
00506     // ~~~~~~~~~~~~~~
00507 
00508     DynamicList<label> problemFaces(surf.size()/100 + 1);
00509 
00510     const labelListList& eFaces = surf.edgeFaces();
00511 
00512     label nSingleEdges = 0;
00513     forAll(eFaces, edgeI)
00514     {
00515         const labelList& myFaces = eFaces[edgeI];
00516 
00517         if (myFaces.size() == 1)
00518         {
00519             problemFaces.append(myFaces[0]);
00520 
00521             nSingleEdges++;
00522         }
00523     }
00524 
00525     label nMultEdges = 0;
00526     forAll(eFaces, edgeI)
00527     {
00528         const labelList& myFaces = eFaces[edgeI];
00529 
00530         if (myFaces.size() > 2)
00531         {
00532             forAll(myFaces, myFaceI)
00533             {
00534                 problemFaces.append(myFaces[myFaceI]);
00535             }
00536 
00537             nMultEdges++;
00538         }
00539     }
00540     problemFaces.shrink();
00541 
00542     if ((nSingleEdges != 0) || (nMultEdges != 0))
00543     {
00544         Pout<< "Surface is not closed since not all edges connected to "
00545             << "two faces:" << endl
00546             << "    connected to one face : " << nSingleEdges << endl
00547             << "    connected to >2 faces : " << nMultEdges << endl;
00548 
00549         Pout<< "Conflicting face labels:" << problemFaces.size() << endl;
00550 
00551         OFstream str("problemFaces");
00552 
00553         Pout<< "Dumping conflicting face labels to " << str.name() << endl
00554             << "Paste this into the input for surfaceSubset" << endl;
00555 
00556         str << problemFaces;
00557     }
00558     else
00559     {
00560         Pout<< "Surface is closed. All edges connected to two faces." << endl;
00561     }
00562     Pout<< endl;
00563 
00564 
00565 
00566     // Check singly connected domain
00567     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00568 
00569     labelList faceZone;
00570     label numZones = surf.markZones(boolList(surf.nEdges(), false), faceZone);
00571 
00572     Pout<< "Number of unconnected parts : " << numZones << endl;
00573 
00574     if (numZones > 1)
00575     {
00576         Pout<< "Splitting surface into parts ..." << endl << endl;
00577 
00578         fileName surfFileNameBase(surfFileName.name());
00579 
00580         for(label zone = 0; zone < numZones; zone++)
00581         {
00582             boolList includeMap(surf.size(), false);
00583 
00584             forAll(faceZone, faceI)
00585             {
00586                 if (faceZone[faceI] == zone)
00587                 {
00588                     includeMap[faceI] = true;
00589                 }
00590             }
00591 
00592             labelList pointMap;
00593             labelList faceMap;
00594 
00595             triSurface subSurf
00596             (
00597                 surf.subsetMesh
00598                 (
00599                     includeMap,
00600                     pointMap,
00601                     faceMap
00602                 )
00603             );
00604 
00605             fileName subFileName
00606             (
00607                 surfFileNameBase.lessExt()
00608               + "_"
00609               + name(zone)
00610               + ".ftr"
00611             );
00612 
00613             Pout<< "writing part " << zone << " size " << subSurf.size()
00614                 << " to " << subFileName << endl;
00615 
00616             subSurf.write(subFileName);
00617         }
00618 
00619         return 0;
00620     }
00621 
00622 
00623 
00624     // Check orientation
00625     // ~~~~~~~~~~~~~~~~~
00626 
00627     labelHashSet borderEdge(surf.size()/1000);
00628     PatchTools::checkOrientation(surf, false, &borderEdge);
00629 
00630     //
00631     // Colour all faces into zones using borderEdge
00632     //
00633     labelList normalZone;
00634     label numNormalZones = PatchTools::markZones(surf, borderEdge, normalZone);
00635 
00636     Pout<< endl
00637         << "Number of zones (connected area with consistent normal) : "
00638         << numNormalZones << endl;
00639 
00640     if (numNormalZones > 1)
00641     {
00642         Pout<< "More than one normal orientation." << endl;
00643     }
00644     Pout<< endl;
00645 
00646 
00647 
00648     // Check self-intersection
00649     // ~~~~~~~~~~~~~~~~~~~~~~~
00650 
00651     if (checkSelfIntersection)
00652     {
00653         Pout<< "Checking self-intersection." << endl;
00654 
00655         triSurfaceSearch querySurf(surf);
00656         surfaceIntersection inter(querySurf);
00657 
00658         if (inter.cutEdges().empty() && inter.cutPoints().empty())
00659         {
00660             Pout<< "Surface is not self-intersecting" << endl;
00661         }
00662         else
00663         {
00664             Pout<< "Surface is self-intersecting" << endl;
00665             Pout<< "Writing edges of intersection to selfInter.obj" << endl;
00666 
00667             OFstream intStream("selfInter.obj");
00668             forAll(inter.cutPoints(), cutPointI)
00669             {
00670                 const point& pt = inter.cutPoints()[cutPointI];
00671 
00672                 intStream << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z()
00673                     << endl;
00674             }
00675             forAll(inter.cutEdges(), cutEdgeI)
00676             {
00677                 const edge& e = inter.cutEdges()[cutEdgeI];
00678 
00679                 intStream << "l " << e.start()+1 << ' ' << e.end()+1 << endl;
00680             }
00681         }
00682         Pout<< endl;
00683     }
00684 
00685 
00686     Pout<< "End\n" << endl;
00687 
00688     return 0;
00689 }
00690 
00691 
00692 // ************************ vim: set sw=4 sts=4 et: ************************ //
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines