Skip to content
Snippets Groups Projects
surfaceCheck.C 18.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*---------------------------------------------------------------------------*\
      =========                 |
      \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
       \\    /   O peration     |
    
    Mark Olesen's avatar
    Mark Olesen committed
        \\  /    A nd           | Copyright (C) 1991-2009 OpenCFD Ltd.
    
         \\/     M anipulation  |
    -------------------------------------------------------------------------------
    License
        This file is part of OpenFOAM.
    
        OpenFOAM is free software; you can redistribute it and/or modify it
        under the terms of the GNU General Public License as published by the
        Free Software Foundation; either version 2 of the License, or (at your
        option) any later version.
    
        OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
        ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
        FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        for more details.
    
        You should have received a copy of the GNU General Public License
        along with OpenFOAM; if not, write to the Free Software Foundation,
        Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
    
    \*---------------------------------------------------------------------------*/
    
    #include "triangle.H"
    #include "triSurface.H"
    #include "triSurfaceTools.H"
    #include "triSurfaceSearch.H"
    #include "argList.H"
    #include "OFstream.H"
    #include "surfaceIntersection.H"
    #include "SortableList.H"
    
    mattijs's avatar
    mattijs committed
    #include "PatchTools.H"
    
    
    using namespace Foam;
    
    // Does face use valid vertices?
    
    bool validTri
    (
        const bool verbose,
        const triSurface& surf,
        const label faceI
    )
    
    {
        // Simple check on indices ok.
    
        const labelledTri& f = surf[faceI];
    
        if
        (
            (f[0] < 0) || (f[0] >= surf.points().size())
         || (f[1] < 0) || (f[1] >= surf.points().size())
         || (f[2] < 0) || (f[2] >= surf.points().size())
        )
        {
    
    mattijs's avatar
    mattijs committed
            WarningIn("validTri(const triSurface&, const label)")
                << "triangle " << faceI << " vertices " << f
                << " uses point indices outside point range 0.."
                << surf.points().size()-1 << endl;
    
    
            return false;
        }
    
        if ((f[0] == f[1]) || (f[0] == f[2]) || (f[1] == f[2]))
        {
    
    mattijs's avatar
    mattijs committed
            WarningIn("validTri(const triSurface&, const label)")
                << "triangle " << faceI
                << " uses non-unique vertices " << f
                << " coords:" << f.points(surf.points())
                << endl;
    
            return false;
        }
    
        // duplicate triangle check
    
        const labelList& fFaces = surf.faceFaces()[faceI];
    
        // Check if faceNeighbours use same points as this face.
        // Note: discards normal information - sides of baffle are merged.
        forAll(fFaces, i)
        {
            label nbrFaceI = fFaces[i];
    
            if (nbrFaceI <= faceI)
            {
                // lower numbered faces already checked
                continue;
            }
    
            const labelledTri& nbrF = surf[nbrFaceI];
    
            if
            (
                ((f[0] == nbrF[0]) || (f[0] == nbrF[1]) || (f[0] == nbrF[2]))
             && ((f[1] == nbrF[0]) || (f[1] == nbrF[1]) || (f[1] == nbrF[2]))
             && ((f[2] == nbrF[0]) || (f[2] == nbrF[1]) || (f[2] == nbrF[2]))
            )
            {
    
    mattijs's avatar
    mattijs committed
                WarningIn("validTri(const triSurface&, const label)")
                    << "triangle " << faceI << " vertices " << f
                    << " has the same vertices as triangle " << nbrFaceI
                    << " vertices " << nbrF
                    << " coords:" << f.points(surf.points())
                    << endl;
    
    
                return false;
            }
        }
        return true;
    }
    
    
    labelList countBins
    (
        const scalar min,
        const scalar max,
        const label nBins,
        const scalarField& vals
    )
    {
        scalar dist = nBins/(max - min);
    
        labelList binCount(nBins, 0);
    
        forAll(vals, i)
        {
            scalar val = vals[i];
    
            label index = -1;
    
            if (Foam::mag(val - min) < SMALL)
            {
                index = 0;
            }
            else if (val >= max - SMALL)
            {
                index = nBins - 1;
            }
            else
            {
                index = label((val - min)*dist);
    
                if ((index < 0) || (index >= nBins))
                {
                    WarningIn
                    (
                        "countBins(const scalar, const scalar, const label"
                        ", const scalarField&)"
                    )   << "value " << val << " at index " << i
                        << " outside range " << min << " .. " << max << endl;
    
                    if (index < 0)
                    {
                        index = 0;
                    }
                    else
                    {
                        index = nBins - 1;
                    }
                }
            }
            binCount[index]++;
        }
    
        return binCount;
    }
    
    
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Main program:
    
    int main(int argc, char *argv[])
    {
        argList::noParallel();
    
        argList::validArgs.clear();
        argList::validArgs.append("surface file");
    
    
        argList::addBoolOption("checkSelfIntersection");
        argList::addBoolOption("verbose");
    
        argList::addBoolOption
        (
            "blockMesh",
            "write vertices/blocks for blockMeshDict"
        );
    
        argList args(argc, argv);
    
    
        bool checkSelfIntersection = args.optionFound("checkSelfIntersection");
        bool verbose = args.optionFound("verbose");
    
    
        fileName surfFileName(args.additionalArgs()[0]);
    
        Info<< "Reading surface from " << surfFileName << " ..." << nl << endl;
    
        Info<< "Statistics:" << endl;
        surf.writeStats(Info);
        Info<< endl;
    
        // write bounding box corners
        if (args.optionFound("blockMesh"))
        {
            pointField cornerPts = boundBox(surf.points()).corners();
    
            Info<<"// blockMeshDict info" << nl;
    
            Info<<"vertices\n(" << nl;
            forAll(cornerPts, ptI)
            {
                Info << "    " << cornerPts[ptI] << nl;
            }
    
            // number of divisions needs adjustment later
            Info<<");\n" << nl
                <<"blocks\n"
                <<"(\n"
                <<"    hex (0 1 2 3 4 5 6 7) (10 10 10) simpleGrading (1 1 1)\n"
                <<");\n" << nl;
    
            Info<<"edges();" << nl
                <<"patches();" << endl;
        }
    
    
    
        // Region sizes
        // ~~~~~~~~~~~~
    
        {
            labelList regionSize(surf.patches().size(), 0);
    
            forAll(surf, faceI)
            {
                label region = surf[faceI].region();
    
                if (region < 0 || region >= regionSize.size())
                {
                    WarningIn(args.executable())
                        << "Triangle " << faceI << " vertices " << surf[faceI]
                        << " has region " << region << " which is outside the range"
                        << " of regions 0.." << surf.patches().size()-1
                        << endl;
                }
                else
                {
                    regionSize[region]++;
                }
            }
    
    
            Info<< "Region\tSize" << nl
    
                << "------\t----" << nl;
            forAll(surf.patches(), patchI)
            {
    
                Info<< surf.patches()[patchI].name() << '\t'
    
                    << regionSize[patchI] << nl;
            }
    
            Info<< nl << endl;
    
        }
    
    
        // Check triangles
        // ~~~~~~~~~~~~~~~
    
        {
            DynamicList<label> illegalFaces(surf.size()/100 + 1);
    
            forAll(surf, faceI)
            {
    
    mattijs's avatar
    mattijs committed
                if (!validTri(verbose, surf, faceI))
    
                Info<< "Surface has " << illegalFaces.size()
    
                    << " illegal triangles." << endl;
    
                OFstream str("illegalFaces");
    
                Info<< "Dumping conflicting face labels to " << str.name() << endl
    
                    << "Paste this into the input for surfaceSubset" << endl;
                str << illegalFaces;
            }
            else
            {
    
                Info<< "Surface has no illegal triangles." << endl;
    
            Info<< endl;
    
        }
    
    
    
        // Triangle quality
        // ~~~~~~~~~~~~~~~~
    
        {
            scalarField triQ(surf.size(), 0);
            forAll(surf, faceI)
            {
                const labelledTri& f = surf[faceI];
    
                if (f[0] == f[1] || f[0] == f[2] || f[1] == f[2])
                {
    
    Mattijs Janssens's avatar
    Mattijs Janssens committed
                    //WarningIn(args.executable())
                    //    << "Illegal triangle " << faceI << " vertices " << f
                    //    << " coords " << f.points(surf.points()) << endl;
    
                }
                else
                {
                    triPointRef tri
                    (
                        surf.points()[f[0]],
                        surf.points()[f[1]],
                        surf.points()[f[2]]
                    );
    
                    vector ba(tri.b() - tri.a());
                    ba /= mag(ba) + VSMALL;
    
                    vector ca(tri.c() - tri.a());
                    ca /= mag(ca) + VSMALL;
    
                    if (mag(ba&ca) > 1-1E-3)
                    {
                        triQ[faceI] = SMALL;
                    }
                    else
                    {
                        triQ[faceI] = triPointRef
                        (
                            surf.points()[f[0]],
                            surf.points()[f[1]],
                            surf.points()[f[2]]
                        ).quality();
                    }
                }
            }
    
            labelList binCount = countBins(0, 1, 20, triQ);
    
    
            Info<< "Triangle quality (equilateral=1, collapsed=0):"
    
            OSstream& os = Info;
    
            os.width(4);
    
            scalar dist = (1.0 - 0.0)/20.0;
            scalar min = 0;
            forAll(binCount, binI)
            {
    
                Info<< "    " << min << " .. " << min+dist << "  : "
    
                    << 1.0/surf.size() * binCount[binI]
                    << endl;
    
            Info<< endl;
    
    
            label minIndex = findMin(triQ);
            label maxIndex = findMax(triQ);
    
    
            Info<< "    min " << triQ[minIndex] << " for triangle " << minIndex
    
                << nl
                << "    max " << triQ[maxIndex] << " for triangle " << maxIndex
                << nl
                << endl;
    
    
            if (triQ[minIndex] < SMALL)
            {
                WarningIn(args.executable()) << "Minimum triangle quality is "
                    << triQ[minIndex] << ". This might give problems in"
                    << " self-intersection testing later on." << endl;
            }
    
            // Dump for subsetting
            {
                DynamicList<label> problemFaces(surf.size()/100+1);
    
                forAll(triQ, faceI)
                {
                    if (triQ[faceI] < 1E-11)
                    {
                        problemFaces.append(faceI);
                    }
                }
                OFstream str("badFaces");
    
    
                Info<< "Dumping bad quality faces to " << str.name() << endl
    
                    << "Paste this into the input for surfaceSubset" << nl
                    << nl << endl;
    
                str << problemFaces;
            }
        }
    
    
    
        // Edges
        // ~~~~~
        {
            const edgeList& edges = surf.edges();
            const pointField& localPoints = surf.localPoints();
    
            scalarField edgeMag(edges.size());
    
            forAll(edges, edgeI)
            {
                edgeMag[edgeI] = edges[edgeI].mag(localPoints);
            }
    
            label minEdgeI = findMin(edgeMag);
            label maxEdgeI = findMax(edgeMag);
    
            const edge& minE = edges[minEdgeI];
            const edge& maxE = edges[maxEdgeI];
    
    
    
            Info<< "Edges:" << nl
    
                << "    min " << edgeMag[minEdgeI] << " for edge " << minEdgeI
                << " points " << localPoints[minE[0]] << localPoints[minE[1]]
                << nl
                << "    max " << edgeMag[maxEdgeI] << " for edge " << maxEdgeI
                << " points " << localPoints[maxE[0]] << localPoints[maxE[1]]
                << nl
                << endl;
        }
    
    
    
        // Close points
        // ~~~~~~~~~~~~
        {
            const edgeList& edges = surf.edges();
            const pointField& localPoints = surf.localPoints();
    
            const boundBox bb(localPoints);
    
            scalar smallDim = 1E-6 * bb.mag();
    
            Info<< "Checking for points less than 1E-6 of bounding box ("
    
                << bb.span() << " meter) apart."
    
                << endl;
    
            // Sort points
            SortableList<scalar> sortedMag(mag(localPoints));
    
            label nClose = 0;
    
            for (label i = 1; i < sortedMag.size(); i++)
            {
                label ptI = sortedMag.indices()[i];
    
                label prevPtI = sortedMag.indices()[i-1];
    
                if (mag(localPoints[ptI] - localPoints[prevPtI]) < smallDim)
                {
                    // Check if neighbours.
                    const labelList& pEdges = surf.pointEdges()[ptI];
    
                    label edgeI = -1;
    
                    forAll(pEdges, i)
                    {
                        const edge& e = edges[pEdges[i]];
    
                        if (e[0] == prevPtI || e[1] == prevPtI)
                        {
                            // point1 and point0 are connected through edge.
                            edgeI = pEdges[i];
    
                            break;
                        }
                    }
    
                    nClose++;
    
                    if (edgeI == -1)
                    {
    
                        Info<< "    close unconnected points "
    
                            << ptI << ' ' << localPoints[ptI]
                            << " and " << prevPtI << ' '
                            << localPoints[prevPtI]
                            << " distance:"
                            << mag(localPoints[ptI] - localPoints[prevPtI])
                            << endl;
                    }
                    else
                    {
    
                        Info<< "    small edge between points "
    
                            << ptI << ' ' << localPoints[ptI]
                            << " and " << prevPtI << ' '
                            << localPoints[prevPtI]
                            << " distance:"
                            << mag(localPoints[ptI] - localPoints[prevPtI])
                            << endl;
                    }
                }
            }
    
    
            Info<< "Found " << nClose << " nearby points." << nl
    
                << endl;
        }
    
    
    
        // Check manifold
        // ~~~~~~~~~~~~~~
    
        DynamicList<label> problemFaces(surf.size()/100 + 1);
    
        const labelListList& eFaces = surf.edgeFaces();
    
        label nSingleEdges = 0;
        forAll(eFaces, edgeI)
        {
            const labelList& myFaces = eFaces[edgeI];
    
            if (myFaces.size() == 1)
            {
                problemFaces.append(myFaces[0]);
    
                nSingleEdges++;
            }
        }
    
        label nMultEdges = 0;
        forAll(eFaces, edgeI)
        {
            const labelList& myFaces = eFaces[edgeI];
    
            if (myFaces.size() > 2)
            {
                forAll(myFaces, myFaceI)
                {
                    problemFaces.append(myFaces[myFaceI]);
                }
    
                nMultEdges++;
            }
        }
        problemFaces.shrink();
    
        if ((nSingleEdges != 0) || (nMultEdges != 0))
        {
    
            Info<< "Surface is not closed since not all edges connected to "
    
                << "two faces:" << endl
                << "    connected to one face : " << nSingleEdges << endl
                << "    connected to >2 faces : " << nMultEdges << endl;
    
    
            Info<< "Conflicting face labels:" << problemFaces.size() << endl;
    
    
            OFstream str("problemFaces");
    
    
            Info<< "Dumping conflicting face labels to " << str.name() << endl
    
                << "Paste this into the input for surfaceSubset" << endl;
    
            str << problemFaces;
        }
        else
        {
    
            Info<< "Surface is closed. All edges connected to two faces." << endl;
    
        Info<< endl;
    
    
    
    
        // Check singly connected domain
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
        labelList faceZone;
        label numZones = surf.markZones(boolList(surf.nEdges(), false), faceZone);
    
    
        Info<< "Number of unconnected parts : " << numZones << endl;
    
            Info<< "Splitting surface into parts ..." << endl << endl;
    
    
            fileName surfFileNameBase(surfFileName.name());
    
    
            for (label zone = 0; zone < numZones; zone++)
    
            {
                boolList includeMap(surf.size(), false);
    
                forAll(faceZone, faceI)
                {
                    if (faceZone[faceI] == zone)
                    {
                        includeMap[faceI] = true;
                    }
                }
    
                labelList pointMap;
                labelList faceMap;
    
                triSurface subSurf
                (
                    surf.subsetMesh
                    (
                        includeMap,
                        pointMap,
                        faceMap
                    )
                );
    
                fileName subFileName
                (
                    surfFileNameBase.lessExt()
                  + "_"
                  + name(zone)
                  + ".ftr"
                );
    
    
                Info<< "writing part " << zone << " size " << subSurf.size()
    
                    << " to " << subFileName << endl;
    
                subSurf.write(subFileName);
            }
    
            return 0;
        }
    
    
    
        // Check orientation
        // ~~~~~~~~~~~~~~~~~
    
    
    mattijs's avatar
    mattijs committed
        labelHashSet borderEdge(surf.size()/1000);
        PatchTools::checkOrientation(surf, false, &borderEdge);
    
    
        //
        // Colour all faces into zones using borderEdge
        //
        labelList normalZone;
    
    mattijs's avatar
    mattijs committed
        label numNormalZones = PatchTools::markZones(surf, borderEdge, normalZone);
    
        Info<< endl
    
            << "Number of zones (connected area with consistent normal) : "
            << numNormalZones << endl;
    
        if (numNormalZones > 1)
        {
    
            Info<< "More than one normal orientation." << endl;
    
        Info<< endl;
    
    
    
    
        // Check self-intersection
        // ~~~~~~~~~~~~~~~~~~~~~~~
    
        if (checkSelfIntersection)
        {
    
            Info<< "Checking self-intersection." << endl;
    
    
            triSurfaceSearch querySurf(surf);
            surfaceIntersection inter(querySurf);
    
    
            if (inter.cutEdges().empty() && inter.cutPoints().empty())
    
                Info<< "Surface is not self-intersecting" << endl;
    
                Info<< "Surface is self-intersecting" << endl;
                Info<< "Writing edges of intersection to selfInter.obj" << endl;
    
    
                OFstream intStream("selfInter.obj");
                forAll(inter.cutPoints(), cutPointI)
                {
                    const point& pt = inter.cutPoints()[cutPointI];
    
                    intStream << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z()
                        << endl;
                }
                forAll(inter.cutEdges(), cutEdgeI)
                {
                    const edge& e = inter.cutEdges()[cutEdgeI];
    
                    intStream << "l " << e.start()+1 << ' ' << e.end()+1 << endl;
                }
            }
    
            Info<< endl;
    
        Info<< "\nEnd\n" << endl;
    
    
        return 0;
    }
    
    
    // ************************************************************************* //