Skip to content
Snippets Groups Projects
meshOctreeModifierRefineSelectedBoxes.C 19.7 KiB
Newer Older
franjo's avatar
franjo committed
/*---------------------------------------------------------------------------*\
  =========                 |
franjo's avatar
franjo committed
  \\      /  F ield         | cfMesh: A library for mesh generation
franjo's avatar
franjo committed
   \\    /   O peration     |
franjo's avatar
franjo committed
    \\  /    A nd           | Author: Franjo Juretic (franjo.juretic@c-fields.com)
     \\/     M anipulation  | Copyright (C) Creative Fields, Ltd.
franjo's avatar
franjo committed
-------------------------------------------------------------------------------
License
franjo's avatar
franjo committed
    This file is part of cfMesh.
franjo's avatar
franjo committed

franjo's avatar
franjo 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
franjo's avatar
franjo committed
    Free Software Foundation; either version 3 of the License, or (at your
franjo's avatar
franjo committed
    option) any later version.

franjo's avatar
franjo 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
franjo's avatar
franjo committed
    along with cfMesh.  If not, see <http://www.gnu.org/licenses/>.
franjo's avatar
franjo committed

Description

\*---------------------------------------------------------------------------*/

#include "meshOctreeModifier.H"
#include "triSurf.H"
#include "HashSet.H"
#include "helperFunctions.H"
#include "meshOctreeCubeCoordinatesScalar.H"
franjo's avatar
franjo committed

franjo's avatar
franjo committed
# ifdef USE_OMP
franjo's avatar
franjo committed
#include <omp.h>
franjo's avatar
franjo committed
# endif

franjo's avatar
franjo committed
#include <sys/stat.h>

//#define OCTREETiming
//#define DEBUGSearch

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

namespace Foam
{

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

void meshOctreeModifier::markAdditionalLayers
(
    labelList& refineBox,
    const label nLayers
franjo's avatar
franjo committed
) const
{
    const LongList<meshOctreeCube*>& leaves = octree_.leaves_;

    //- this is needed for parallel runs to reduce the communication messages
franjo's avatar
franjo committed
    labelHashSet transferCoordinates;

    DynList<label> neiLeaves;
franjo's avatar
franjo committed

    for(label i=1;i<=nLayers;++i)
franjo's avatar
franjo committed
    {
        LongList<meshOctreeCubeCoordinates> processorChecks;

        transferCoordinates.clear();

        labelLongList activeLeaves;
        forAll(leaves, leafI)
            if( refineBox[leafI] == i )
                activeLeaves.append(leafI);

franjo's avatar
franjo committed
        # ifdef USE_OMP
        # pragma omp parallel for private(neiLeaves) schedule(dynamic, 20)
franjo's avatar
franjo committed
        # endif
        forAll(activeLeaves, lI)
franjo's avatar
franjo committed
        {
            const label leafI = activeLeaves[lI];
franjo's avatar
franjo committed

            const meshOctreeCubeCoordinates& oc = leaves[leafI]->coordinates();
franjo's avatar
franjo committed

            octree_.findAllLeafNeighbours(oc, neiLeaves);
franjo's avatar
franjo committed

            forAll(neiLeaves, posI)
            {
                const label neiLabel = neiLeaves[posI];
franjo's avatar
franjo committed

                if( neiLabel == meshOctreeCubeBasic::OTHERPROC )
franjo's avatar
franjo committed
                {
franjo's avatar
franjo committed
                    # ifdef USE_OMP
franjo's avatar
franjo committed
                    # pragma omp critical
franjo's avatar
franjo committed
                    # endif
franjo's avatar
franjo committed
                    {
                        if( !transferCoordinates.found(leafI) )
                        {
                            processorChecks.append(oc);
franjo's avatar
franjo committed
                            transferCoordinates.insert(leafI);
                        }
                    }
franjo's avatar
franjo committed
                }

                if( neiLabel < 0 )
                    continue;
franjo's avatar
franjo committed

                if( !refineBox[neiLabel] )
                    refineBox[neiLabel] = i+1;
franjo's avatar
franjo committed
            }
        }

        if( octree_.neiProcs().size() )
        {
            LongList<meshOctreeCubeCoordinates> receivedCoords;
            octree_.exchangeRequestsWithNeighbourProcessors
            (
                processorChecks,
                receivedCoords
            );

            //- check consistency with received cube coordinates
franjo's avatar
franjo committed
            # ifdef USE_OMP
franjo's avatar
franjo committed
            # pragma omp parallel for if( receivedCoords.size() > 1000 ) \
            schedule(dynamic, 20) private(neiLeaves)
franjo's avatar
franjo committed
            # endif
franjo's avatar
franjo committed
            forAll(receivedCoords, ccI)
            {
                octree_.findAllLeafNeighbours(receivedCoords[ccI], neiLeaves);
franjo's avatar
franjo committed

                forAll(neiLeaves, posI)
                {
                    if( neiLeaves[posI] < 0 )
                        continue;
franjo's avatar
franjo committed

                    if( !refineBox[neiLeaves[posI]] )
                        refineBox[neiLeaves[posI]] = i+1;
void meshOctreeModifier::markAdditionalLayersOfFaceNeighbours
(
    labelList& refineBox,
    const label nLayers
) const
{
    const LongList<meshOctreeCube*>& leaves = octree_.leaves_;

    //- this is needed for parallel runs to reduce the communication messages
    labelHashSet transferCoordinates;

    DynList<label> neiLeaves;

    for(label i=1;i<=nLayers;++i)
    {
        LongList<meshOctreeCubeCoordinates> processorChecks;

        transferCoordinates.clear();

        labelLongList activeLeaves;
        forAll(leaves, leafI)
            if( refineBox[leafI] == i )
                activeLeaves.append(leafI);

        # ifdef USE_OMP
        # pragma omp parallel for private(neiLeaves) schedule(dynamic, 20)
        # endif
        forAll(activeLeaves, lI)
        {
            const label leafI = activeLeaves[lI];

            const meshOctreeCubeCoordinates& oc = leaves[leafI]->coordinates();

            neiLeaves.clear();
            octree_.findNeighboursForLeaf(oc, neiLeaves);

            forAll(neiLeaves, posI)
            {
                const label neiLabel = neiLeaves[posI];

                if( neiLabel == meshOctreeCubeBasic::OTHERPROC )
                {
                    # ifdef USE_OMP
                    # pragma omp critical
                    # endif
                    {
                        if( !transferCoordinates.found(leafI) )
                        {
                            processorChecks.append(oc);
                            transferCoordinates.insert(leafI);
                        }
                    }

                    continue;
                }

                if( neiLabel < 0 )
                    continue;

                if( !refineBox[neiLabel] )
                    refineBox[neiLabel] = i+1;
            }
        }

        if( octree_.neiProcs().size() )
        {
            LongList<meshOctreeCubeCoordinates> receivedCoords;
            octree_.exchangeRequestsWithNeighbourProcessors
            (
                processorChecks,
                receivedCoords
            );

            //- check consistency with received cube coordinates
            # ifdef USE_OMP
            # pragma omp parallel for if( receivedCoords.size() > 1000 ) \
            schedule(dynamic, 20) private(neiLeaves)
            # endif
            forAll(receivedCoords, ccI)
            {
                neiLeaves.clear();
                octree_.findNeighboursForLeaf(receivedCoords[ccI], neiLeaves);

                forAll(neiLeaves, posI)
                {
                    if( neiLeaves[posI] < 0 )
                        continue;

                    if( !refineBox[neiLeaves[posI]] )
                        refineBox[neiLeaves[posI]] = i+1;
                }
            }
        }
    }
}

# ifdef DEBUGSearch
void writeLeaves
(
    const fileName& fName,
    const meshOctree& octree,
    const labelList& markedBoxes,
    const label layer
)
{
    labelLongList activeLeaves;

    forAll(markedBoxes, leafI)
        if( markedBoxes[leafI] == layer )
            activeLeaves.append(leafI);

    OFstream file(fName);

    //- write the header
    file << "# vtk DataFile Version 3.0\n";
    file << "vtk output\n";
    file << "ASCII\n";
    file << "DATASET POLYDATA\n";

    //- write points
    file << "POINTS " << (8 * activeLeaves.size()) << " float\n";
    forAll(activeLeaves, i)
    {
        const label leafI = activeLeaves[i];
        FixedList<point, 8> vertices;
        octree.returnLeaf(leafI).vertices(octree.rootBox(), vertices);

        forAll(vertices, vI)
        {
            const point& p = vertices[vI];

            file << p.x() << ' ' << p.y() << ' ' << p.z() << nl;
        }
    }

    //- write lines
    file << "\nPOLYGONS " << (6*activeLeaves.size())
         << " " << 30*activeLeaves.size() << nl;
    forAll(activeLeaves, i)
    {
        const label startNode = 8 * i;
        for(label fI=0;fI<6;++fI)
        {
            file << 4;

            for(label pI=0;pI<4;++pI)
                file << " " << (startNode+meshOctreeCube::faceNodes_[fI][pI]);

            file << nl;
        }
    }

    file << "\n";
}
# endif

label meshOctreeModifier::markAdditionalLayers
(
    labelList& refineBox,
    labelList& nLayers,
    List<direction>& targetRefLevel
) const
{
    const LongList<meshOctreeCube*>& leaves = octree_.leaves_;

    # ifdef DEBUGSearch
    Info << "Marking additional layers " << endl;
    # endif

    //- sort leaves based on the number of additional layers
Franjo's avatar
Franjo committed
    label maxLevel = Foam::max(nLayers);
    reduce(maxLevel, maxOp<label>());
Franjo's avatar
Franjo committed
    List<labelLongList> leavesForLayer(maxLevel+1);
    forAll(nLayers, leafI)
Franjo's avatar
Franjo committed
            continue;

        leavesForLayer[nLayers[leafI]].append(leafI);
Franjo's avatar
Franjo committed
    //- set refinement flag to additional boxes marked separately
    label nMarked(0);

    forAllReverse(leavesForLayer, layerI)
Franjo's avatar
Franjo committed
        const labelLongList& activeLeaves = leavesForLayer[layerI];
Franjo's avatar
Franjo committed
        if( returnReduce(activeLeaves.size(), sumOp<label>()) == 0 )
            continue;
Franjo's avatar
Franjo committed
        //- find the max required refinement level
        direction maxLevel(0);
Franjo's avatar
Franjo committed
        # ifdef USE_OMP
        # pragma omp parallel
        # endif
Franjo's avatar
Franjo committed
            direction localMax(0);
Franjo's avatar
Franjo committed
            # ifdef USE_OMP
            # pragma omp for schedule(dynamic, 50)
            # endif
            forAll(activeLeaves, i)
                localMax = Foam::max(localMax, targetRefLevel[activeLeaves[i]]);
Franjo's avatar
Franjo committed
            # ifdef USE_OMP
            # pragma omp critical
            # endif
            maxLevel = Foam::max(localMax, maxLevel);
        }
Franjo's avatar
Franjo committed
        label ml = maxLevel;
        reduce(ml, maxOp<label>());
        maxLevel = ml;
Franjo's avatar
Franjo committed
        //- mark additional boxes for refinement
        for(direction levelI=maxLevel;levelI>0;--levelI)
        {
            labelList markedBoxes(leaves.size(), 0);
Franjo's avatar
Franjo committed
            label counter(0);
            # ifdef USE_OMP
            # pragma omp parallel for reduction(+:counter)
            # endif
            forAll(activeLeaves, lI)
Franjo's avatar
Franjo committed
                const label leafI = activeLeaves[lI];
                if( targetRefLevel[leafI] == levelI )
Franjo's avatar
Franjo committed
                    markedBoxes[leafI] = 1;
                    ++counter;
Franjo's avatar
Franjo committed
            if( returnReduce(counter, sumOp<label>()) == 0 )
                continue;
Franjo's avatar
Franjo committed
            //- mark additional cells at this refinement level
            markAdditionalLayersOfFaceNeighbours(markedBoxes, layerI);

            # ifdef DEBUGSearch
            for(label i=1;i<(layerI+1);++i)
            {
                const fileName fName("leaves_"+help::labelToText(i)+".vtk");
                writeLeaves(fName, octree_, markedBoxes, i);
            }

            Info << "LayerI " << layerI << endl;
            # endif
Franjo's avatar
Franjo committed
            //- update the main list
            # ifdef USE_OMP
            # pragma omp parallel for schedule(dynamic, 100) \
            reduction(+:nMarked)
            # endif
            forAll(markedBoxes, leafI)
Franjo's avatar
Franjo committed
                    continue;
Franjo's avatar
Franjo committed

Franjo's avatar
Franjo committed
                if( leaves[leafI]->level() >= levelI )
                    continue;
Franjo's avatar
Franjo committed
                if( !refineBox[leafI] )
                {
                    refineBox[leafI] |= 1;
                    ++nMarked;
    reduce(nMarked, sumOp<label>());

    return nMarked;
}

franjo's avatar
franjo committed
void meshOctreeModifier::refineSelectedBoxes
(
    labelList& refineBox,
franjo's avatar
franjo committed
    const bool hexRefinement
)
{
    # ifdef OCTREETiming
    const scalar startTime = omp_get_wtime();
    # endif

    //- ensure that refinement will produce 1-irregular octree
    do
    {
        ensureCorrectRegularity(refineBox);
    } while( hexRefinement && ensureCorrectRegularitySons(refineBox) );

    # ifdef OCTREETiming
    const scalar regTime = omp_get_wtime();
    Info << "Time for ensuring regularity " << (regTime-startTime) << endl;
    # endif

franjo's avatar
franjo committed
    const triSurf& surface = octree_.surface();
franjo's avatar
franjo committed
    const boundBox& rootBox = octree_.rootBox();
    const LongList<meshOctreeCube*>& leaves = octree_.leaves_;

    //- this is needed for thread safety
    //- such solutions make me a sad bunny :(
franjo's avatar
franjo committed
    surface.facetEdges();
    surface.edgeFacets();
franjo's avatar
franjo committed
    surface.edges();

franjo's avatar
franjo committed
    # ifdef USE_OMP
franjo's avatar
franjo committed
    # pragma omp parallel num_threads(octree_.dataSlots_.size())
franjo's avatar
franjo committed
    # endif
franjo's avatar
franjo committed
    {
franjo's avatar
franjo committed
        # ifdef USE_OMP
franjo's avatar
franjo committed
        meshOctreeSlot* slotPtr = &octree_.dataSlots_[omp_get_thread_num()];
franjo's avatar
franjo committed
        # else
        meshOctreeSlot* slotPtr = &octree_.dataSlots_[0];
        # endif
franjo's avatar
franjo committed

franjo's avatar
franjo committed
        if( !octree_.isQuadtree() )
franjo's avatar
franjo committed
        {
franjo's avatar
franjo committed
            # ifdef USE_OMP
            # pragma omp for schedule(dynamic, 100)
            # endif
            forAll(leaves, leafI)
            {
                if( refineBox[leafI] )
                    leaves[leafI]->refineCube(surface, rootBox, slotPtr);
            }
        }
        else
        {
            # ifdef USE_OMP
            # pragma omp for schedule(dynamic, 100)
            # endif
            forAll(leaves, leafI)
            {
                if( refineBox[leafI] )
                    leaves[leafI]->refineCube2D(surface, rootBox, slotPtr);
            }
franjo's avatar
franjo committed
        }
    }

    createListOfLeaves();

    # ifdef OCTREETiming
    Info << "Time for actual refinement " << (omp_get_wtime()-regTime) << endl;
    # endif
}

void meshOctreeModifier::refineSelectedBoxesAndAdditionalLayers
(
    labelList& refineBox,
Franjo's avatar
Franjo committed
    const scalarList& refThickness
)
{
    const LongList<meshOctreeCube*>& leaves = octree_.leaves_;

Franjo's avatar
Franjo committed
    typedef std::pair<direction, label> mapKey;
    typedef std::map<mapKey, LongList<meshOctreeCube*> > lMap;
Franjo's avatar
Franjo committed
    Info << "Refining leaves and additional layers" << endl;
Franjo's avatar
Franjo committed
    //- find the maximum refinement level of leaves marked for refinement
Franjo's avatar
Franjo committed
    direction maxLevel(0);

Franjo's avatar
Franjo committed
    # ifdef USE_OMP
    # pragma omp parallel
    {
        direction localMax(0);

        forAll(refineBox, leafI)
        {
            if( refineBox[leafI] )
                localMax = Foam::max(localMax, leaves[leafI]->level());
        }

        # pragma omp critical
        maxLevel = Foam::max(maxLevel, localMax);
    }
    # else
Franjo's avatar
Franjo committed
    forAll(refineBox, leafI)
Franjo's avatar
Franjo committed
    {
Franjo's avatar
Franjo committed
        if( refineBox[leafI] )
            maxLevel = Foam::max(maxLevel, leaves[leafI]->level());
Franjo's avatar
Franjo committed
    }
    # endif
Franjo's avatar
Franjo committed

    label ml = maxLevel;
    reduce(ml, maxOp<label>());
    maxLevel = ml;

    //- sort leaves based on the current level
    List<labelLongList> leavesForLevel(maxLevel+1);
Franjo's avatar
Franjo committed
    forAll(refineBox, leafI)
Franjo's avatar
Franjo committed
        if( !refineBox[leafI] )
            continue;

        leavesForLevel[leaves[leafI]->level()].append(leafI);
Franjo's avatar
Franjo committed
    //- find leaves with the same number of additional layers
    forAllReverse(leavesForLevel, levelI)
    {
        const labelLongList& activeLeaves = leavesForLevel[levelI];

        if( returnReduce(activeLeaves.size(), sumOp<label>()) == 0 )
            continue;

Franjo's avatar
Franjo committed
        labelLongList nLayersForActive(activeLeaves.size());
        label maxNLayers(0);
Franjo's avatar
Franjo committed
        # ifdef USE_OMP
        # pragma omp parallel
        {
            label localMaxNLayers(0);

            forAll(activeLeaves, i)
            {
                const label leafI = activeLeaves[i];

                const scalar cs = leaves[leafI]->size(octree_.rootBox());

                nLayersForActive[i] = Foam::max(ceil(refThickness[leafI]/cs), 1);

                localMaxNLayers =
                    Foam::max(localMaxNLayers, nLayersForActive[i]);
            }

            # pragma omp critical
            maxNLayers = Foam::max(maxNLayers, localMaxNLayers);
        }
        # else
Franjo's avatar
Franjo committed
        forAll(activeLeaves, i)
        {
            const label leafI = activeLeaves[i];

            const scalar cs = leaves[leafI]->size(octree_.rootBox());

            nLayersForActive[i] = Foam::max(ceil(refThickness[leafI]/cs), 1);

            maxNLayers = Foam::max(maxNLayers, nLayersForActive[i]);
        }
Franjo's avatar
Franjo committed
        # endif
Franjo's avatar
Franjo committed

        for(label layerI=0;layerI<=maxNLayers;++layerI)
        {
            mapKey key(levelI, layerI);
            LongList<meshOctreeCube*>& currLeaves = leavesMap[key];
            currLeaves.clear();
        }
Franjo's avatar
Franjo committed
        {
            mapKey key(levelI, nLayersForActive[i]);
            leavesMap[key].append(leaves[activeLeaves[i]]);
        }
Franjo's avatar
Franjo committed
    //- refine leaves
    forAllConstIter(lMap, leavesMap, it)
    {
Franjo's avatar
Franjo committed
        const label nLayers = it->first.second;
        const direction levelI = it->first.first;

        const LongList<meshOctreeCube*>& selectedLeaves = it->second;
Franjo's avatar
Franjo committed

        if( returnReduce(selectedLeaves.size(), sumOp<label>()) == 0 )
            continue;

Franjo's avatar
Franjo committed
        # ifdef DEBUGSearch
Franjo's avatar
Franjo committed
        Info << "Target level " << label(levelI) << endl;
        Info << "Target num layer " << nLayers << endl;
        Info << "Num selected leaves " << selectedLeaves.size() << endl;
Franjo's avatar
Franjo committed
        # endif
Franjo's avatar
Franjo committed
            //- mark current leaves for refinement
            labelList markedLeaves(leaves.size(), 0);

            # ifdef USE_OMP
Franjo's avatar
Franjo committed
            # pragma omp parallel for schedule(dynamic, 50) reduction(+:nMarked)
Franjo's avatar
Franjo committed
                if( !selectedLeaves[i]->isLeaf() )
                    continue;
Franjo's avatar
Franjo committed
                markedLeaves[selectedLeaves[i]->cubeLabel()] = 1;
                ++nMarked;
            }
Franjo's avatar
Franjo committed
            reduce(nMarked, sumOp<label>());
Franjo's avatar
Franjo committed
            //- get out of the do-while loop if there are no selected leaves
            if( nMarked == 0 )
                break;
Franjo's avatar
Franjo committed
            //- mark additional boxes for refinement
            markAdditionalLayers(markedLeaves, nLayers);
Franjo's avatar
Franjo committed
            //- find the leaves in the additional layers
            labelLongList activeLeaves;
            forAll(markedLeaves, leafI)
Franjo's avatar
Franjo committed
                if( markedLeaves[leafI] )
                    activeLeaves.append(leafI);
            }
Franjo's avatar
Franjo committed
            //- check if there exist leaves at lower refinement level
            bool hasLowerLevel(false);
Franjo's avatar
Franjo committed
            # ifdef USE_OMP
            # pragma omp parallel for schedule(guided)
            # endif
            forAll(activeLeaves, i)
            {
Franjo's avatar
Franjo committed
                const direction level = leaves[activeLeaves[i]]->level();
                if( level < levelI )
                {
                    //- found a neighbour at a lower refinement level
Franjo's avatar
Franjo committed
                    hasLowerLevel = true;
Franjo's avatar
Franjo committed
                }
                else if( level > levelI )
                {
                    //- do not allow refinement of leaves at higher
                    //- refinement level
                    markedLeaves[activeLeaves[i]] = 0;
                }
Franjo's avatar
Franjo committed
            }
Franjo's avatar
Franjo committed
            reduce(hasLowerLevel, maxOp<bool>());

            //- deselect leaves at the current level
Franjo's avatar
Franjo committed
            if( hasLowerLevel )
            {
Franjo's avatar
Franjo committed
                # pragma omp parallel for schedule(guided)
Franjo's avatar
Franjo committed
                forAll(activeLeaves, i)
Franjo's avatar
Franjo committed
                    if( leaves[activeLeaves[i]]->level() == levelI )
                        markedLeaves[activeLeaves[i]] = 0;
Franjo's avatar
Franjo committed
            //- refine selected octree boxes
            refineSelectedBoxes(markedLeaves);
        } while( nMarked );
    }
}

franjo's avatar
franjo committed
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

} // End namespace Foam

// ************************************************************************* //