Program Listing for File TreeCellFinder.C

Return to documentation for file (cellFinders/TreeCellFinder/TreeCellFinder.C)

/*---------------------------------------------------------------------------* \
License
    This file is part of libWallModelledLES.

    libWallModelledLES 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 3 of the License, or
    (at your option) any later version.

    libWallModelledLES 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 libWallModelledLES.
    If not, see <http://www.gnu.org/licenses/>.

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

#include "TreeCellFinder.H"
#include "volFields.H"
#include "surfaceFields.H"
#include "codeRules.H"
#include "treeDataCell.H"
#include "treeDataFace.H"
#include "treeBoundBox.H"
#include "indexedOctree.H"


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

#if !defined(DOXYGEN_SHOULD_SKIP_THIS)
namespace Foam
{
    defineTypeNameAndDebug(TreeCellFinder, 0);
    addToRunTimeSelectionTable(CellFinder, TreeCellFinder, Patch);
}
#endif


// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //



// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //

Foam::TreeCellFinder::TreeCellFinder
(
    const fvPatch & p
)
:
    CellFinder(p)
{
    if (debug)
    {
        Info << "TreeCellFinder: Constructing from patch" << nl;
    }

    createFields();
}

Foam::TreeCellFinder::TreeCellFinder
(
    const word & cellFinderName,
    const fvPatch & p
)
:
   CellFinder(p)
{
}

// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //


void Foam::TreeCellFinder::findCellIndices
(
    labelList & indexList,
    const scalarField & h
) const
{
    scalar maxH = max(h);

    if (debug)
    {
        Info<< "TreeCellSampler: Constructing mesh bounding box" << nl;
    }

    treeBoundBox boundBox(mesh().bounds());
    Random rndGen(261782);
#ifdef FOAM_TREEBOUNDBOX_DOES_NOT_ACCEPT_RNG
    boundBox.extend(1e-4);
#else
    boundBox.extend(rndGen, 1e-4);
#endif
    boundBox.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
    boundBox.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);

    tmp<Foam::volScalarField> distField(distanceField());

    const scalarField & dist =
#ifdef FOAM_NEW_GEOMFIELD_RULES
        distField().primitiveField();
#else
        distField().internalField();
#endif


    tmp<labelField> tSearchCellLabels = findCandidateCellLabels(dist, h);
    const labelField & searchCellLabels = tSearchCellLabels();

    autoPtr<indexedOctree<treeDataCell> > treePtr
    (
        new indexedOctree<treeDataCell>
        (
            treeDataCell
            (
                false,
                mesh_,
                searchCellLabels,
                polyMesh::CELL_TETS
            ),
            boundBox,
            8,
            10,
            3.0
        )
    );

    if (treePtr->nodes().empty() && (maxH != 0) && (patch().size()))
    {
        Warning
            << "TreeCellFinder: max(h) is " << maxH << " but no cell centres within "
            << "distance 2*max(h) were found. "
            << "Will sample from wall-adjacent cells." << nl;
    }


    if (debug)
    {
        Info<< "TreeCellFinder: Constructing face octree" << nl;
    }

    labelList bndFaces(mesh().nFaces() - mesh().nInternalFaces());
    forAll(bndFaces, i)
    {
        bndFaces[i] = mesh().nInternalFaces() + i;
    }

    autoPtr<indexedOctree<treeDataFace> > boundaryTreePtr
    (
        new indexedOctree<treeDataFace>
        (
            treeDataFace
            (
                false,
                mesh(),
                bndFaces
            ),
            boundBox,
            8,
            10,
            3.0
        )
    );


    // Grab face centres, normal and adjacent cells' centres to each patch face
    const vectorField & faceCentres = patch().Cf();
    const tmp<vectorField> tfaceNormals = patch().nf();
    const vectorField faceNormals = tfaceNormals();
    const tmp<vectorField> tcellCentres = patch().Cn();
    const vectorField cellCentres = tcellCentres();

    // Grab the global indices of adjacent cells
    const UList<label> & faceCells = patch().faceCells();

    vector point;
    pointIndexHit pih;

    if (debug)
    {
        Info << "TreeCellFinder: Starting search for sampling cells" << nl;
    }

    forAll(faceCentres, i)
    {
        // Grab the point h away along the face normal
        point = faceCentres[i] - faceNormals[i]*h[i];

        // If h is zero, or the point is outside the domain,
        // grab the wall-adjacent cell
        bool inside =
#ifdef FOAM_VOLUMETYPE_NOT_CAPITAL
            boundaryTreePtr->getVolumeType(point) == volumeType::inside;
#else
            boundaryTreePtr->getVolumeType(point) == volumeType::INSIDE;
#endif

        if (h[i] < 0)
        {
            Warning
                << "TreeCellFinder: " << h[i]
                << " is negative and thus not a valid distance. "
                << "Will fall back to wall-adjacent cell for face "
                <<  i << " on patch " << patch().name() << nl;
                indexList[i] = faceCells[i];
        }
        else if ( (h[i] == 0) || (treePtr->nodes().empty()) )
        {
            indexList[i] = faceCells[i];
        }
        else if (!inside)
        {
            Warning
                << "TreeCellFinder: the point " << h[i]
                << " away from the wall is outside the domain. "
                << "Will fall back to wall-adjacent cell for face "
                <<  i << " on patch " << patch().name() << nl;
                indexList[i] = faceCells[i];
        }
        else
        {

            pih = treePtr->findNearest(point, treePtr->bb().mag());
            indexList[i] = searchCellLabels[pih.index()];
        }
    }
    if (debug)
    {
        Info << "TreeCellFinder: Done" << nl;
    }
}

void Foam::TreeCellFinder::findCellIndices
(
    labelListList & indexList,
    const scalarField & h,
    const bool excludeWallAdjacent
) const
{
    scalar maxH = max(h);

    if (debug)
    {
        Info<< "TreeCellFinder: Constructing mesh bounding box" << nl;
    }

    treeBoundBox boundBox(mesh_.bounds());
    Random rndGen(261782);
#ifdef FOAM_TREEBOUNDBOX_DOES_NOT_ACCEPT_RNG
    boundBox.extend(1e-4);
#else
    boundBox.extend(rndGen, 1e-4);
#endif
    boundBox.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
    boundBox.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);

    tmp<Foam::volScalarField> distField(distanceField());

    const scalarField & dist =
#ifdef FOAM_NEW_GEOMFIELD_RULES
        distField().primitiveField();
#else
        distField().internalField();
#endif


    tmp<labelField> tSearchCellLabels = findCandidateCellLabels(dist, h);
    const labelField & searchCellLabels = tSearchCellLabels();

    autoPtr<indexedOctree<treeDataCell> > treePtr
    (
        new indexedOctree<treeDataCell>
        (
            treeDataCell
            (
                false,
                mesh_,
                searchCellLabels,
                polyMesh::CELL_TETS
            ),
            boundBox,
            8,
            10,
            3.0
        )
    );

    if (treePtr->nodes().empty() && (maxH != 0))
    {
        Warning
            << "TreeCellFinder: max(h) is " << maxH << " but no cell centres within "
            << "distance 2*max(h) were found. "
            << "Will sample from wall-adjacent cells." << nl;
    }


    if (debug)
    {
        Info<< "TreeCellFinder: Constructing face octree" << nl;
    }

    labelList bndFaces(mesh_.nFaces() - mesh_.nInternalFaces());
    forAll(bndFaces, i)
    {
        bndFaces[i] = mesh_.nInternalFaces() + i;
    }

    autoPtr<indexedOctree<treeDataFace> > boundaryTreePtr
    (
        new indexedOctree<treeDataFace>
        (
            treeDataFace
            (
                false,
                mesh_,
                bndFaces
            ),
            boundBox,
            8,
            10,
            3.0
        )
    );

    // Grab face centres, normal and adjacent cells' centres to each patch face
    const vectorField & faceCentres = patch().Cf();
    const tmp<vectorField> tfaceNormals = patch().nf();
    const vectorField & faceNormals = tfaceNormals();
    const volVectorField & C = mesh_.C();

    // Grab the global indices of adjacent cells
    const UList<label> & faceCells = patch().faceCells();

    // Point 2h away from the face
    vector p;
    pointIndexHit pih;

    if (debug)
    {
        Info << "TreeCellFinder: Starting search for sampling cells" << nl;
    }

    forAll(faceCentres, i)
    {
        // Grab the point 2h away along the face normal
        // This just sets the direction of the ray cast
        p = faceCentres[i] - 2*faceNormals[i]*h[i];

        vector tolVector = (p - faceCentres[i])*1e-6;
        point startP = faceCentres[i] + tolVector;
        point endP = p + tolVector;

        // If h is zero, or the point is outside the domain,
        // set it to distance to adjacent cell's centre
        // Set the cellIndexList component accordingly

        if (h[i] < 0)
        {
            Warning
                << "TreeCellFinder: " << h[i]
                << " is negative and thus not a valid distance. "
                << "Will fall back to wall-adjacent cell for face "
                <<  i << " on patch " << patch().name() << nl;
            indexList[i] = labelList(1, faceCells[i]);
        }
        else if ((h[i] == 0) || (treePtr->nodes().empty()))
        {
            indexList[i] = labelList(1, faceCells[i]);
        }
        else
        {
            // Allocate list for the sampling cell indices for face i
            indexList[i] = labelList(50);

            // Amount of cells sampled from for this face
            label n = 0;

            while (true)
            {
                if (debug > 1)
                {
                    Info << "Searching faces along line from: " << startP << " to " << endP << nl;
                }
                pih = treePtr->findLine(startP, endP);

                if (pih.hit())
                {
                    const point hitP = pih.hitPoint();
                    const label cellI =
                        treePtr->findNearest(hitP - tolVector, treePtr->bb().mag()).index();

                    indexList[i][n] = searchCellLabels[cellI];

                    if (debug > 1)
                    {
                        Info<< "Hit face: " << hitP << nl;
                        Info<< "CC: " << C[searchCellLabels[cellI]] << nl;
                    }

                    n++;

                    // If we are now inside the last cell
                    if (mag(hitP - faceCentres[i]) >= h[i])
                    {
                        if (debug > 1)
                        {
                            Info<< "This is the last cell, n = " << n << nl;
                        }
                        indexList[i].setSize(n);
                        break;
                    }

                    startP = hitP + tolVector;
                }
                else
                {
                    if (n == 0)
                    {
                        // Not a single face intersected
                        // revert to wall-adjacent cell
                        Info << "No faces were intersected, reverting to "
                             << "wall-adjacent cell" << nl;
                        indexList[i].setSize(1, faceCells[i]);
                        break;

                    }
                    else
                    {
                        // We went outside the domain
                        indexList[i].setSize(n);
                        if (debug > 1)
                        {
                            Info<< "This is the last cell, n = " << n << nl;
                        }
                        break;
                    }
                }
           }
        }

        // Remove wall adjacent cell from list if applicable
        if ((indexList[i].size() != 1) && (excludeWallAdjacent))
        {
            for(int j=0; j<indexList[i].size()-1; j++)
            {
                indexList[i][j] = indexList[i][j+1];
            }
            indexList[i].setSize(indexList[i].size()-1);
        }
    }
    if (debug)
    {
        Info << "TreeCellFinder: Done" << nl;
    }
}

Foam::tmp<Foam::labelField>
Foam::TreeCellFinder::findCandidateCellLabels
(
    const scalarField & dist,
    const scalarField & h
) const
{
    scalar maxH = max(h);
    if (debug)
    {
        Info << "TreeCellFinder: The maximum h value is " << maxH << nl;
    }

    if (debug)
    {
        Info<< "TreeCellFinderSampler: Constructing mesh bounding box" << nl;
    }

    tmp<labelField> tCandidates(new labelField(mesh().nCells()));
#ifdef FOAM_NEW_TMP_RULES
    labelField & candidates = tCandidates.ref();
#else
    labelField & candidates = tCandidates();
#endif

    label nCandidates = 0;

    if (debug)
    {
        Info<< "TreeCellFinder: Searching for cells closer to 2maxH to the wall"
            << nl;
    }

    forAll(candidates, i)
    {
        if (dist[i] < 2*maxH)
        {
            candidates[nCandidates] = i;
            nCandidates++;
        }
    }

    candidates.resize(nCandidates);

    if (debug)
    {
        Info<< "TreeCellFinder: Found " << candidates.size() << " candidate "
            << "cells" << nl;
    }

    return tCandidates;
}

Foam::tmp<Foam::volScalarField> Foam::TreeCellFinder::distanceField() const
{


    word hName;

    // Grab h for the current patch
    if (mesh_.foundObject<volScalarField>("hSampler"))
    {
        hName = "hSampler";
    }
    else
    {
        hName = "h";
    }
    const volScalarField & h = mesh_.lookupObject<volScalarField>(hName);


    if (debug)
    {
        Info<< "CellFinder: Creating dist field" << nl;
    }

    tmp<volScalarField> dist
    (
        new volScalarField
        (
            IOobject
            (
                "dist",
                mesh_.time().timeName(),
                mesh_,
                IOobject::READ_IF_PRESENT,
                IOobject::NO_WRITE
            ),
            mesh_,
            dimensionedScalar("dist", dimLength, 0),
            h.boundaryField().types()
        )
    );

    bool precomputedDist =
    #ifdef FOAM_NEW_GEOMFIELD_RULES
        mag(max(dist().primitiveField())) > VSMALL;
    #else
        mag(max(dist().internalField())) > VSMALL;
    #endif

    if (debug)
    {
        Info<<"CellFinder: using precumputed distance field" << nl;
    }

    if (!precomputedDist)
    {
        labelHashSet patchIDs(1);
        patchIDs.insert(patch().index());

        dictionary methodDict = dictionary();
        methodDict.lookupOrAddDefault(word("method"), word("meshWave"));

        if (debug)
        {
            Info<< "Initializing patchDistanceMethod" << nl;
        }

        autoPtr<patchDistMethod> pdm
        (
            patchDistMethod::New
            (
                methodDict,
                mesh_,
                patchIDs
            )
        );

        if (debug)
        {
            Info<< "CellFinder: Computing dist field" << nl;
        }
#ifdef FOAM_NEW_TMP_RULES
        pdm->correct(dist.ref());
#else
        pdm->correct(dist());
#endif
    }

    return dist;
}


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