Skip to content
Snippets Groups Projects
optimizeMeshFV.C 18.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • franjo's avatar
    franjo committed
    /*---------------------------------------------------------------------------*\
      =========                 |
    
    Tomislav Lugaric's avatar
    Tomislav Lugaric committed
      \\      /  F ield         | cfMesh: A library for mesh generation
    
    franjo's avatar
    franjo committed
       \\    /   O peration     |
    
    Tomislav Lugaric's avatar
    Tomislav Lugaric committed
        \\  /    A nd           | Author: Franjo Juretic (franjo.juretic@c-fields.com)
         \\/     M anipulation  | Copyright (C) Creative Fields, Ltd.
    
    franjo's avatar
    franjo committed
    -------------------------------------------------------------------------------
    License
    
    Tomislav Lugaric's avatar
    Tomislav Lugaric committed
        This file is part of cfMesh.
    
    franjo's avatar
    franjo committed
    
    
    Tomislav Lugaric's avatar
    Tomislav Lugaric committed
        cfMesh is free software; you can redistribute it and/or modify it
    
    franjo's avatar
    franjo committed
        under the terms of the GNU General Public License as published by the
    
    Tomislav Lugaric's avatar
    Tomislav Lugaric committed
        Free Software Foundation; either version 3 of the License, or (at your
    
    franjo's avatar
    franjo committed
        option) any later version.
    
    
    Tomislav Lugaric's avatar
    Tomislav Lugaric committed
        cfMesh is distributed in the hope that it will be useful, but WITHOUT
    
    franjo's avatar
    franjo committed
        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
    
    Tomislav Lugaric's avatar
    Tomislav Lugaric committed
        along with cfMesh.  If not, see <http://www.gnu.org/licenses/>.
    
    franjo's avatar
    franjo committed
    
    Description
    
    \*---------------------------------------------------------------------------*/
    
    
    franjo's avatar
    franjo committed
    #include "demandDrivenData.H"
    #include "meshOptimizer.H"
    #include "polyMeshGenAddressing.H"
    
    #include "polyMeshGenChecks.H"
    
    franjo's avatar
    franjo committed
    #include "partTetMesh.H"
    #include "HashSet.H"
    
    #include "tetMeshOptimisation.H"
    
    #include "boundaryLayerOptimisation.H"
    
    #include "refineBoundaryLayers.H"
    
    #include "meshSurfaceEngine.H"
    
    franjo's avatar
    franjo committed
    
    //#define DEBUGSmooth
    
    # ifdef DEBUGSmooth
    #include "helperFunctions.H"
    #include "polyMeshGenModifier.H"
    # endif
    
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    
    namespace Foam
    {
    
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    
    
    Franjo's avatar
    Franjo committed
    void meshOptimizer::untangleMeshFV
    (
        const label maxNumGlobalIterations,
        const label maxNumIterations,
    
        const label maxNumSurfaceIterations,
        const bool relaxedCheck
    
    Franjo's avatar
    Franjo committed
    )
    
    franjo's avatar
    franjo committed
    {
    
        Info << "Starting untangling the mesh" << endl;
    
        # ifdef DEBUGSmooth
        partTetMesh tm(mesh_);
        forAll(tm.tets(), tetI)
            if( tm.tets()[tetI].mag(tm.points()) < 0.0 )
                Info << "Tet " << tetI << " is inverted!" << endl;
        polyMeshGen tetPolyMesh(mesh_.returnTime());
        tm.createPolyMesh(tetPolyMesh);
    
    franjo's avatar
    franjo committed
        polyMeshGenModifier(tetPolyMesh).removeUnusedVertices();
    
        forAll(tm.smoothVertex(), pI)
            if( !tm.smoothVertex()[pI] )
                Info << "Point " << pI << " cannot be moved!" << endl;
    
    franjo's avatar
    franjo committed
        const VRWGraph& pTets = tm.pointTets();
        forAll(pTets, pointI)
        {
            const LongList<partTet>& tets = tm.tets();
            forAllRow(pTets, pointI, i)
                if( tets[pTets(pointI, i)].whichPosition(pointI) < 0 )
                    FatalError << "Wrong partTet" << abort(FatalError);
    
    franjo's avatar
    franjo committed
            partTetMeshSimplex simplex(tm, pointI);
        }
    
        boolList boundaryVertex(tetPolyMesh.points().size(), false);
        const labelList& neighbour = tetPolyMesh.neighbour();
        forAll(neighbour, faceI)
            if( neighbour[faceI] == -1 )
            {
                const face& f = tetPolyMesh.faces()[faceI];
    
                forAll(f, pI)
                    boundaryVertex[f[pI]] = true;
            }
    
        forAll(boundaryVertex, pI)
        {
            if( boundaryVertex[pI] && tm.smoothVertex()[pI] )
                FatalErrorIn
                (
                    "void meshOptimizer::untangleMeshFV()"
                ) << "Boundary vertex should not be moved!" << abort(FatalError);
        }
        # endif
    
        label nBadFaces, nGlobalIter(0), nIter;
    
        const faceListPMG& faces = mesh_.faces();
    
        boolList changedFace(faces.size(), true);
    
    Franjo's avatar
    Franjo committed
        //- check if any points in the tet mesh shall not move
        labelLongList lockedPoints;
        forAll(vertexLocation_, pointI)
        {
            if( vertexLocation_[pointI] & LOCKED )
                lockedPoints.append(pointI);
        }
    
    franjo's avatar
    franjo committed
        labelHashSet badFaces;
    
    franjo's avatar
    franjo committed
            label minNumBadFaces(10 * faces.size()), minIter(-1);
    
                if( !relaxedCheck )
                {
                    nBadFaces =
                        polyMeshGenChecks::findBadFaces
                        (
                            mesh_,
                            badFaces,
                            false,
                            &changedFace
                        );
                }
                else
                {
                    nBadFaces =
                        polyMeshGenChecks::findBadFacesRelaxed
                        (
                            mesh_,
                            badFaces,
                            false,
                            &changedFace
                        );
                }
    
    franjo's avatar
    franjo committed
                Info << "Iteration " << nIter
                    << ". Number of bad faces is " << nBadFaces << endl;
    
                //- perform optimisation
                if( nBadFaces == 0 )
                    break;
    
    franjo's avatar
    franjo committed
                if( nBadFaces < minNumBadFaces )
                {
                    minNumBadFaces = nBadFaces;
                    minIter = nIter;
                }
    
                //- create a tet mesh from the mesh and the labels of bad faces
    
    Franjo's avatar
    Franjo committed
                partTetMesh tetMesh
                (
                    mesh_,
                    lockedPoints,
                    badFaces,
                    (nGlobalIter / 2) + 1
                );
    
    franjo's avatar
    franjo committed
    
    
                //- construct tetMeshOptimisation and improve positions of
                //- points in the tet mesh
    
    franjo's avatar
    franjo committed
                tetMeshOptimisation tmo(tetMesh);
    
    franjo's avatar
    franjo committed
                tmo.optimiseUsingKnuppMetric();
    
    franjo's avatar
    franjo committed
                tmo.optimiseUsingMeshUntangler();
    
    franjo's avatar
    franjo committed
                tmo.optimiseUsingVolumeOptimizer();
    
                //- update points in the mesh from the coordinates in the tet mesh
    
                tetMesh.updateOrigMesh(&changedFace);
    
    Franjo's avatar
    Franjo committed
            } while( (nIter < minIter+5) && (++nIter < maxNumIterations) );
    
    franjo's avatar
    franjo committed
    
    
            if( (nBadFaces == 0) || (++nGlobalIter >= maxNumGlobalIterations) )
                break;
    
    franjo's avatar
    franjo committed
            // move boundary vertices
            nIter = 0;
    
    Franjo's avatar
    Franjo committed
            while( nIter++ < maxNumSurfaceIterations );
    
                if( !relaxedCheck )
                {
                    nBadFaces =
                        polyMeshGenChecks::findBadFaces
                        (
                            mesh_,
                            badFaces,
                            false,
                            &changedFace
                        );
                }
                else
                {
                    nBadFaces =
                        polyMeshGenChecks::findBadFacesRelaxed
                        (
                            mesh_,
                            badFaces,
                            false,
                            &changedFace
                        );
                }
    
    franjo's avatar
    franjo committed
                Info << "Iteration " << nIter
                    << ". Number of bad faces is " << nBadFaces << endl;
    
                //- perform optimisation
                if( nBadFaces == 0 )
    
                }
                else if( enforceConstraints_ )
                {
                    const label subsetId =
                        mesh_.addPointSubset(badPointsSubsetName_);
    
                    forAllConstIter(labelHashSet, badFaces, it)
                    {
                        const face& f = faces[it.key()];
                        forAll(f, pI)
                            mesh_.addPointToSubset(subsetId, f[pI]);
                    }
    
                    WarningIn
                    (
                        "void meshOptimizer::untangleMeshFV()"
                    ) << "Writing mesh with " << badPointsSubsetName_
                      << " subset. These points cannot be untangled"
                      << " without sacrificing geometry constraints. Exitting.."
                      << endl;
    
                    returnReduce(1, sumOp<label>());
                    throw std::logic_error
                    (
                        "void meshOptimizer::untangleMeshFV()"
                        "Cannot untangle mesh!!"
                    );
                }
    
    Franjo's avatar
    Franjo committed
                //- create tethrahedral mesh from the cells which shall be smoothed
                partTetMesh tetMesh(mesh_, lockedPoints, badFaces, 0);
    
    
                //- contruct tetMeshOptimisation
    
    franjo's avatar
    franjo committed
                tetMeshOptimisation tmo(tetMesh);
    
    franjo's avatar
    franjo committed
                if( nGlobalIter < 2 )
                {
                    //- the point stays in the plane determined by the point normal
                    tmo.optimiseBoundaryVolumeOptimizer(true);
                }
                else if( nGlobalIter < 5 )
                {
                    //- move points without any constraints on the movement
                    tmo.optimiseBoundarySurfaceLaplace();
                }
                else
                {
                    //- move boundary points without any constraints
                    tmo.optimiseBoundaryVolumeOptimizer(false);
                }
    
                tetMesh.updateOrigMesh(&changedFace);
    
    Franjo's avatar
    Franjo committed
            }
    
    
        } while( nBadFaces );
    
        if( nBadFaces != 0 )
        {
            label subsetId = mesh_.faceSubsetIndex("badFaces");
            if( subsetId >= 0 )
                mesh_.removeFaceSubset(subsetId);
            subsetId = mesh_.addFaceSubset("badFaces");
    
            forAllConstIter(labelHashSet, badFaces, it)
                mesh_.addFaceToSubset(subsetId, it.key());
        }
    
    
        Info << "Finished untangling the mesh" << endl;
    
    franjo's avatar
    franjo committed
    }
    
    
    void meshOptimizer::optimizeBoundaryLayer(const bool addBufferLayer)
    
        if( mesh_.returnTime().foundObject<IOdictionary>("meshDict") )
        {
            const dictionary& meshDict =
                mesh_.returnTime().lookupObject<IOdictionary>("meshDict");
    
    
            bool smoothLayer(false);
    
    
            if( meshDict.found("boundaryLayers") )
            {
                const dictionary& layersDict = meshDict.subDict("boundaryLayers");
    
                if( layersDict.found("optimiseLayer") )
    
                    smoothLayer = readBool(layersDict.lookup("optimiseLayer"));
    
            if( !smoothLayer )
                return;
    
    
            {
                //- create a buffer layer which will not be modified by the smoother
                refineBoundaryLayers refLayers(mesh_);
    
                refineBoundaryLayers::readSettings(meshDict, refLayers);
    
                refLayers.activateSpecialMode();
    
                refLayers.refineLayers();
    
                clearSurface();
                calculatePointLocations();
            }
    
    
            Info << "Starting optimising boundary layer" << endl;
    
            const meshSurfaceEngine& mse = meshSurface();
            const labelList& faceOwner = mse.faceOwners();
    
            boundaryLayerOptimisation optimiser(mesh_, mse);
    
    Franjo's avatar
    Franjo committed
    
    
            boundaryLayerOptimisation::readSettings(meshDict, optimiser);
    
            optimiser.optimiseLayer();
    
            //- check if the bnd layer is tangled somewhere
    
            labelLongList bndLayerCells;
    
            const boolList& baseFace = optimiser.isBaseFace();
    
    
            # ifdef DEBUGSmooth
            const label blCellsId = mesh_.addCellSubset("blCells");
            # endif
    
    
            forAll(baseFace, bfI)
    
    Franjo's avatar
    Franjo committed
            {
    
                if( baseFace[bfI] )
    
                    bndLayerCells.append(faceOwner[bfI]);
    
    
                    # ifdef DEBUGSmooth
                    mesh_.addCellToSubset(blCellsId, faceOwner[bfI]);
                    # endif
                }
    
    Franjo's avatar
    Franjo committed
            }
    
            clearSurface();
            mesh_.clearAddressingData();
    
            //- lock boundary layer points, faces and cells
            lockCells(bndLayerCells);
    
    Franjo's avatar
    Franjo committed
    
    
            # ifdef DEBUGSmooth
            pointField origPoints(mesh_.points().size());
            forAll(origPoints, pI)
                origPoints[pI] = mesh_.points()[pI];
            # endif
    
            //- optimize mesh quality
    
            optimizeMeshFV(5, 1, 50, 0);
    
            //- untangle remaining faces and lock the boundary layer cells
            untangleMeshFV(2, 50, 0);
    
            # ifdef DEBUGSmooth
            forAll(vertexLocation_, pI)
            {
                if( vertexLocation_[pI] & LOCKED )
                {
                    if( mag(origPoints[pI] - mesh_.points()[pI]) > SMALL )
                        FatalError << "Locked points were moved"
                                   << abort(FatalError);
                }
            }
            # endif
    
    
            //- unlock bnd layer points
            removeUserConstraints();
    
            Info << "Finished optimising boundary layer" << endl;
        }
    
    void meshOptimizer::untangleBoundaryLayer()
    {
        bool untangleLayer(true);
        if( mesh_.returnTime().foundObject<IOdictionary>("meshDict") )
        {
            const dictionary& meshDict =
                mesh_.returnTime().lookupObject<IOdictionary>("meshDict");
    
            if( meshDict.found("boundaryLayers") )
            {
                const dictionary& layersDict = meshDict.subDict("boundaryLayers");
    
                if( layersDict.found("untangleLayers") )
                {
                    untangleLayer =
                        readBool(layersDict.lookup("untangleLayers"));
                }
            }
        }
    
        if( !untangleLayer )
        {
            labelHashSet badFaces;
    
            polyMeshGenChecks::checkFacePyramids(mesh_, false, VSMALL, &badFaces);
    
    
            const label nInvalidFaces =
                returnReduce(badFaces.size(), sumOp<label>());
    
            if( nInvalidFaces != 0 )
            {
                const labelList& owner = mesh_.owner();
                const labelList& neighbour = mesh_.neighbour();
    
                const label badBlCellsId =
                    mesh_.addCellSubset("invalidBoundaryLayerCells");
    
                forAllConstIter(labelHashSet, badFaces, it)
                {
                    mesh_.addCellToSubset(badBlCellsId, owner[it.key()]);
    
                    if( neighbour[it.key()] < 0 )
                        continue;
    
                    mesh_.addCellToSubset(badBlCellsId, neighbour[it.key()]);
                }
    
                returnReduce(1, sumOp<label>());
    
                (
                    "void meshOptimizer::untangleBoundaryLayer()"
    
                    "Found invalid faces in the boundary layer."
                    " Cannot untangle mesh!!"
                );
    
            optimizeLowQualityFaces();
    
            untangleMeshFV(2, 50, 1, true);
    
    Franjo's avatar
    Franjo committed
    void meshOptimizer::optimizeLowQualityFaces(const label maxNumIterations)
    
    franjo's avatar
    franjo committed
    {
        label nBadFaces, nIter(0);
    
        const faceListPMG& faces = mesh_.faces();
        boolList changedFace(faces.size(), true);
    
    Franjo's avatar
    Franjo committed
        //- check if any points in the tet mesh shall not move
        labelLongList lockedPoints;
        forAll(vertexLocation_, pointI)
        {
            if( vertexLocation_[pointI] & LOCKED )
                lockedPoints.append(pointI);
        }
    
    franjo's avatar
    franjo committed
        do
        {
            labelHashSet lowQualityFaces;
    
            nBadFaces =
                polyMeshGenChecks::findLowQualityFaces
                (
                    mesh_,
                    lowQualityFaces,
                    false,
                    &changedFace
                );
    
    franjo's avatar
    franjo committed
            changedFace = false;
            forAllConstIter(labelHashSet, lowQualityFaces, it)
    
                changedFace[it.key()] = true;
    
    franjo's avatar
    franjo committed
            Info << "Iteration " << nIter
                << ". Number of bad faces is " << nBadFaces << endl;
    
    franjo's avatar
    franjo committed
            //- perform optimisation
            if( nBadFaces == 0 )
                break;
    
    Franjo's avatar
    Franjo committed
            partTetMesh tetMesh(mesh_, lockedPoints, lowQualityFaces, 2);
    
    
            //- construct tetMeshOptimisation and improve positions
            //- of points in the tet mesh
    
    franjo's avatar
    franjo committed
            tetMeshOptimisation tmo(tetMesh);
    
    franjo's avatar
    franjo committed
            tmo.optimiseUsingVolumeOptimizer();
    
    
            //- update points in the mesh from the new coordinates in the tet mesh
    
    franjo's avatar
    franjo committed
            tetMesh.updateOrigMesh(&changedFace);
    
    
    Franjo's avatar
    Franjo committed
        } while( ++nIter < maxNumIterations );
    
    Franjo's avatar
    Franjo committed
    }
    
    void meshOptimizer::optimizeMeshNearBoundaries
    (
        const label maxNumIterations,
        const label numLayersOfCells
    )
    {
        label nIter(0);
    
        const faceListPMG& faces = mesh_.faces();
        boolList changedFace(faces.size(), true);
    
    
    Franjo's avatar
    Franjo committed
        //- check if any points in the tet mesh shall not move
        labelLongList lockedPoints;
        forAll(vertexLocation_, pointI)
        {
            if( vertexLocation_[pointI] & LOCKED )
                lockedPoints.append(pointI);
        }
    
        partTetMesh tetMesh(mesh_, lockedPoints, numLayersOfCells);
    
    Franjo's avatar
    Franjo committed
        tetMeshOptimisation tmo(tetMesh);
        Info << "Iteration:" << flush;
        do
        {
    
            tmo.optimiseUsingVolumeOptimizer(1);
    
    Franjo's avatar
    Franjo committed
    
            tetMesh.updateOrigMesh(&changedFace);
    
            Info << "." << flush;
    
        } while( ++nIter < maxNumIterations );
    
        Info << endl;
    
    franjo's avatar
    franjo committed
    }
    
    void meshOptimizer::optimizeMeshFV
    (
        const label numLaplaceIterations,
        const label maxNumGlobalIterations,
        const label maxNumIterations,
        const label maxNumSurfaceIterations
    )
    
    franjo's avatar
    franjo committed
    {
        Info << "Starting smoothing the mesh" << endl;
    
    franjo's avatar
    franjo committed
        laplaceSmoother lps(mesh_, vertexLocation_);
    
        lps.optimizeLaplacianPC(numLaplaceIterations);
    
        untangleMeshFV
        (
            maxNumGlobalIterations,
            maxNumIterations,
            maxNumSurfaceIterations
        );
    
    franjo's avatar
    franjo committed
    
        Info << "Finished smoothing the mesh" << endl;
    }
    
    
    void meshOptimizer::optimizeMeshFVBestQuality
    (
    
    Franjo's avatar
    Franjo committed
        const label maxNumIterations,
        const scalar threshold
    
    Franjo's avatar
    Franjo committed
        label minIter(-1);
    
    
        const faceListPMG& faces = mesh_.faces();
        boolList changedFace(faces.size(), true);
    
        //- check if any points in the tet mesh shall not move
        labelLongList lockedPoints;
        forAll(vertexLocation_, pointI)
        {
            if( vertexLocation_[pointI] & LOCKED )
                lockedPoints.append(pointI);
        }
    
    
    Franjo's avatar
    Franjo committed
        label minNumBadFaces(10 * faces.size());
    
        do
        {
            labelHashSet lowQualityFaces;
            nBadFaces =
                polyMeshGenChecks::findWorstQualityFaces
                (
                    mesh_,
                    lowQualityFaces,
                    false,
                    &changedFace,
    
    Franjo's avatar
    Franjo committed
                    threshold
    
                );
    
            changedFace = false;
            forAllConstIter(labelHashSet, lowQualityFaces, it)
                changedFace[it.key()] = true;
    
            Info << "Iteration " << nIter
                << ". Number of worst quality faces is " << nBadFaces << endl;
    
            //- perform optimisation
            if( nBadFaces == 0 )
                break;
    
            if( nBadFaces < minNumBadFaces )
            {
                minNumBadFaces = nBadFaces;
    
    Franjo's avatar
    Franjo committed
    
                //- update the iteration number when the minimum is achieved
    
                minIter = nIter;
            }
    
            partTetMesh tetMesh(mesh_, lockedPoints, lowQualityFaces, 2);
    
            //- construct tetMeshOptimisation and improve positions
            //- of points in the tet mesh
            tetMeshOptimisation tmo(tetMesh);
    
    
    Franjo's avatar
    Franjo committed
            tmo.optimiseUsingVolumeOptimizer(20);
    
    
            //- update points in the mesh from the new coordinates in the tet mesh
            tetMesh.updateOrigMesh(&changedFace);
    
    
        } while( (nIter < minIter+5) && (++nIter < maxNumIterations) );
    
    franjo's avatar
    franjo committed
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    
    } // End namespace Foam
    
    // ************************************************************************* //