Skip to content
Snippets Groups Projects
meshRefinementGapRefine.C 51 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*---------------------------------------------------------------------------*\
      =========                 |
      \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
       \\    /   O peration     |
        \\  /    A nd           | Copyright (C) 2015 OpenFOAM Foundation
    
         \\/     M anipulation  | Copyright (C) 2015-2016 OpenCFD Ltd.
    
    -------------------------------------------------------------------------------
    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 3 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, see <http://www.gnu.org/licenses/>.
    
    \*---------------------------------------------------------------------------*/
    
    #include "meshRefinement.H"
    #include "Time.H"
    #include "refinementSurfaces.H"
    #include "refinementFeatures.H"
    #include "shellSurfaces.H"
    #include "triSurfaceMesh.H"
    #include "treeDataCell.H"
    #include "searchableSurfaces.H"
    #include "DynamicField.H"
    
    #include "transportData.H"
    #include "FaceCellWave.H"
    #include "volFields.H"
    #include "zeroGradientFvPatchFields.H"
    
    
    // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
    
    Foam::label Foam::meshRefinement::markSurfaceGapRefinement
    (
        const scalar planarCos,
    
        const label nAllowRefine,
        const labelList& neiLevel,
        const pointField& neiCc,
    
        labelList& refineCell,
        label& nRefine
    ) const
    {
        const labelList& cellLevel = meshCutter_.cellLevel();
        const pointField& cellCentres = mesh_.cellCentres();
    
        // Get the gap level for the shells
        const labelList maxLevel(shells_.maxGapLevel());
    
        label oldNRefine = nRefine;
    
        if (max(maxLevel) > 0)
        {
            // Use cached surfaceIndex_ to detect if any intersection. If so
            // re-intersect to determine level wanted.
    
            // Collect candidate faces
            // ~~~~~~~~~~~~~~~~~~~~~~~
    
            labelList testFaces(getRefineCandidateFaces(refineCell));
    
            // Collect segments
            // ~~~~~~~~~~~~~~~~
    
            pointField start(testFaces.size());
            pointField end(testFaces.size());
    
            {
                labelList minLevel(testFaces.size());
                calcCellCellRays
                (
                    neiCc,
                    neiLevel,
                    testFaces,
                    start,
                    end,
                    minLevel
                );
            }
    
    
    
            // Collect cells to test for inside/outside in shell
            labelList cellToCompact(mesh_.nCells(), -1);
            labelList bFaceToCompact(mesh_.nFaces()-mesh_.nInternalFaces(), -1);
    
            List<FixedList<label, 3>> shellGapInfo;
    
            List<volumeType> shellGapMode;
            {
                DynamicField<point> compactToCc(mesh_.nCells()/10);
                DynamicList<label> compactToLevel(compactToCc.capacity());
                forAll(testFaces, i)
                {
                    label faceI = testFaces[i];
                    label own = mesh_.faceOwner()[faceI];
                    if (cellToCompact[own] == -1)
                    {
                        cellToCompact[own] = compactToCc.size();
                        compactToCc.append(cellCentres[own]);
                        compactToLevel.append(cellLevel[own]);
                    }
                    if (mesh_.isInternalFace(faceI))
                    {
                        label nei = mesh_.faceNeighbour()[faceI];
                        if (cellToCompact[nei] == -1)
                        {
                            cellToCompact[nei] = compactToCc.size();
                            compactToCc.append(cellCentres[nei]);
                            compactToLevel.append(cellLevel[nei]);
                        }
                    }
                    else
                    {
                        label bFaceI = faceI - mesh_.nInternalFaces();
                        if (bFaceToCompact[bFaceI] == -1)
                        {
                            bFaceToCompact[bFaceI] = compactToCc.size();
                            compactToCc.append(neiCc[bFaceI]);
                            compactToLevel.append(neiLevel[bFaceI]);
                        }
                    }
                }
    
                shells_.findHigherGapLevel
                (
                    compactToCc,
                    compactToLevel,
                    shellGapInfo,
                    shellGapMode
                );
            }
    
    
    
            const List<FixedList<label, 3>>& extendedGapLevel =
    
                surfaces_.extendedGapLevel();
            const List<volumeType>& extendedGapMode =
                surfaces_.extendedGapMode();
    
    
            labelList ccSurface1;
            List<pointIndexHit> ccHit1;
            labelList ccRegion1;
            vectorField ccNormal1;
    
                labelList ccSurface2;
                List<pointIndexHit> ccHit2;
                labelList ccRegion2;
                vectorField ccNormal2;
    
    
                surfaces_.findNearestIntersection
                (
                    identity(surfaces_.surfaces().size()),
                    start,
                    end,
    
    
                    ccSurface1,
                    ccHit1,
                    ccRegion1,
                    ccNormal1,
    
            start.clear();
            end.clear();
    
            DynamicField<point> rayStart(2*ccSurface1.size());
            DynamicField<point> rayEnd(2*ccSurface1.size());
            DynamicField<scalar> gapSize(2*ccSurface1.size());
    
            DynamicField<point> rayStart2(2*ccSurface1.size());
            DynamicField<point> rayEnd2(2*ccSurface1.size());
            DynamicField<scalar> gapSize2(2*ccSurface1.size());
    
            DynamicList<label> cellMap(2*ccSurface1.size());
            DynamicList<label> compactMap(2*ccSurface1.size());
    
                label surfI = ccSurface1[i];
    
                    label globalRegionI =
                        surfaces_.globalRegion(surfI, ccRegion1[i]);
    
                    const point& surfPt = ccHit1[i].hitPoint();
    
    
                    label own = mesh_.faceOwner()[faceI];
    
                    if
                    (
                        cellToCompact[own] != -1
                     && shellGapInfo[cellToCompact[own]][2] > 0
                    )
    
                    {
                        // Combine info from shell and surface
                        label compactI = cellToCompact[own];
                        FixedList<label, 3> gapInfo;
                        volumeType gapMode;
                        mergeGapInfo
                        (
                            shellGapInfo[compactI],
                            shellGapMode[compactI],
                            extendedGapLevel[globalRegionI],
                            extendedGapMode[globalRegionI],
                            gapInfo,
                            gapMode
                        );
    
                        const point& cc = cellCentres[own];
    
                            surfPt+((cc-surfPt)&ccNormal1[i])*ccNormal1[i],
    
                            rayStart2,
                            rayEnd2,
                            gapSize2
                        );
                        for (label j = 0; j < nRays; j++)
    
                            cellMap.append(own);
                            compactMap.append(i);
    
                        }
                    }
                    if (mesh_.isInternalFace(faceI))
                    {
                        label nei = mesh_.faceNeighbour()[faceI];
    
                        if
                        (
                            cellToCompact[nei] != -1
                         && shellGapInfo[cellToCompact[nei]][2] > 0
                        )
    
                        {
                            // Combine info from shell and surface
                            label compactI = cellToCompact[nei];
                            FixedList<label, 3> gapInfo;
                            volumeType gapMode;
                            mergeGapInfo
                            (
                                shellGapInfo[compactI],
                                shellGapMode[compactI],
                                extendedGapLevel[globalRegionI],
                                extendedGapMode[globalRegionI],
                                gapInfo,
                                gapMode
                            );
    
                            const point& cc = cellCentres[nei];
    
                                surfPt+((cc-surfPt)&ccNormal1[i])*ccNormal1[i],
    
                                rayStart2,
                                rayEnd2,
                                gapSize2
                            );
                            for (label j = 0; j < nRays; j++)
    
                                cellMap.append(nei);
                                compactMap.append(i);
    
                        // Note: on coupled face. What cell are we going to
                        // refine? We've got the neighbouring cell centre
                        // and level but we cannot mark it for refinement on
                        // this side...
    
                        label bFaceI = faceI - mesh_.nInternalFaces();
    
    
                        if
                        (
                            bFaceToCompact[bFaceI] != -1
                         && shellGapInfo[bFaceToCompact[bFaceI]][2] > 0
                        )
    
                        {
                            // Combine info from shell and surface
                            label compactI = bFaceToCompact[bFaceI];
                            FixedList<label, 3> gapInfo;
                            volumeType gapMode;
                            mergeGapInfo
                            (
                                shellGapInfo[compactI],
                                shellGapMode[compactI],
                                extendedGapLevel[globalRegionI],
                                extendedGapMode[globalRegionI],
                                gapInfo,
                                gapMode
                            );
    
                            const point& cc = neiCc[bFaceI];
    
                                surfPt+((cc-surfPt)&ccNormal1[i])*ccNormal1[i],
    
                                rayStart2,
                                rayEnd2,
                                gapSize2
                            );
                            for (label j = 0; j < nRays; j++)
    
                                cellMap.append(-1); // See above.
                                compactMap.append(i);
    
            Info<< "Shooting " << returnReduce(rayStart.size(), sumOp<label>())
                << " rays from " << returnReduce(testFaces.size(), sumOp<label>())
                << " intersected faces" << endl;
    
            rayStart.shrink();
            rayEnd.shrink();
            gapSize.shrink();
    
            rayStart2.shrink();
            rayEnd2.shrink();
            gapSize2.shrink();
    
            cellMap.shrink();
            compactMap.shrink();
    
            testFaces.clear();
            ccSurface1.clear();
            ccHit1.clear();
            ccRegion1.clear();
            ccNormal1 = UIndirectList<vector>(ccNormal1, compactMap)();
    
    
            // Do intersections in pairs
            labelList surf1;
            List<pointIndexHit> hit1;
            vectorField normal1;
    
            surfaces_.findNearestIntersection
            (
    
            labelList surf2;
            List<pointIndexHit> hit2;
            vectorField normal2;
            surfaces_.findNearestIntersection
            (
                rayStart2,
                rayEnd2,
                surf2,
                hit2,
                normal2
            );
    
                if (surf1[i] != -1 && surf2[i] != -1)
    
                {
                    // Found intersection with surface. Check opposite normal.
                    label cellI = cellMap[i];
    
    
                    if
                    (
                        cellI != -1
                     && (mag(normal1[i]&normal2[i]) > planarCos)
                     && (
                            magSqr(hit1[i].hitPoint()-hit2[i].hitPoint())
                          < Foam::sqr(gapSize[i])
                        )
                    )
    
                                nAllowRefine,
                                refineCell[cellI],
                                nRefine
                            )
                        )
                        {
                            break;
                        }
                    }
                }
            }
    
            if
            (
                returnReduce(nRefine, sumOp<label>())
              > returnReduce(nAllowRefine, sumOp<label>())
            )
            {
                Info<< "Reached refinement limit." << endl;
            }
        }
    
        return returnReduce(nRefine-oldNRefine, sumOp<label>());
    }
    
    
    //Foam::meshRefinement::findNearestOppositeOp::findNearestOppositeOp
    //(
    //    const indexedOctree<treeDataTriSurface>& tree,
    //    const point& oppositePoint,
    //    const vector& oppositeNormal,
    //    const scalar minCos
    //)
    //:
    //    tree_(tree),
    //    oppositePoint_(oppositePoint),
    //    oppositeNormal_(oppositeNormal),
    //    minCos_(minCos)
    //{}
    //
    //
    //void Foam::meshRefinement::findNearestOppositeOp::operator()
    //(
    //    const labelUList& indices,
    //    const point& sample,
    //    scalar& nearestDistSqr,
    //    label& minIndex,
    //    point& nearestPoint
    //) const
    //{
    //    const treeDataTriSurface& shape = tree_.shapes();
    //    const triSurface& patch = shape.patch();
    //    const pointField& points = patch.points();
    //
    //    forAll(indices, i)
    //    {
    //        const label index = indices[i];
    //        const labelledTri& f = patch[index];
    //
    //        pointHit nearHit = f.nearestPoint(sample, points);
    //        scalar distSqr = sqr(nearHit.distance());
    //
    //        if (distSqr < nearestDistSqr)
    //        {
    //            // Nearer. Check if
    //            // - a bit way from other hit
    //            // - in correct search cone
    //            vector d(nearHit.rawPoint()-oppositePoint_);
    //            scalar normalDist(d&oppositeNormal_);
    //
    //            if (normalDist > Foam::sqr(SMALL) && normalDist/mag(d) > minCos_)
    //            {
    //                nearestDistSqr = distSqr;
    //                minIndex = index;
    //                nearestPoint = nearHit.rawPoint();
    //            }
    //        }
    //    }
    //}
    //
    //
    //void Foam::meshRefinement::searchCone
    //(
    //    const label surfI,
    //    labelList& nearMap,                 // cells
    //    scalarField& nearGap,               // gap size
    //    List<pointIndexHit>& nearInfo,      // nearest point on surface
    //    List<pointIndexHit>& oppositeInfo   // detected point on gap (or miss)
    //) const
    //{
    //    const labelList& cellLevel = meshCutter_.cellLevel();
    //    const pointField& cellCentres = mesh_.cellCentres();
    //    const scalar edge0Len = meshCutter_.level0EdgeLength();
    //
    //    const labelList& surfaceIndices = surfaces_.surfaces();
    
    //    const List<FixedList<label, 3>>& extendedGapLevel =
    
    //        surfaces_.extendedGapLevel();
    //    const List<volumeType>& extendedGapMode = surfaces_.extendedGapMode();
    //
    //
    //    label geomI = surfaceIndices[surfI];
    //    const searchableSurface& geom = surfaces_.geometry()[geomI];
    //
    //    const triSurfaceMesh& s = refCast<const triSurfaceMesh>(geom);
    //    const indexedOctree<treeDataTriSurface>& tree = s.tree();
    //
    //
    
    //    const scalar searchCos = Foam::cos(30.0_deg);
    
    //
    //    // Normals for ray shooting and inside/outside detection
    //    vectorField nearNormal;
    //    geom.getNormal(nearInfo, nearNormal);
    //    // Regions
    //    labelList nearRegion;
    //    geom.getRegion(nearInfo, nearRegion);
    //
    //
    //    // Now loop over all near points and search in the half cone
    //    labelList map(nearInfo.size());
    //    label compactI = 0;
    //
    //    oppositeInfo.setSize(nearInfo.size());
    //
    //    forAll(nearInfo, i)
    //    {
    //        label globalRegionI =
    //            surfaces_.globalRegion(surfI, nearRegion[i]);
    //
    //        // Get updated gap information now we have the region
    //        label nGapCells = extendedGapLevel[globalRegionI][0];
    //        label minLevel = extendedGapLevel[globalRegionI][1];
    //        label maxLevel = extendedGapLevel[globalRegionI][2];
    //        volumeType mode = extendedGapMode[globalRegionI];
    //
    //        label cellI = nearMap[i];
    //        label cLevel = cellLevel[cellI];
    //
    //        if (cLevel >= minLevel && cLevel < maxLevel)
    //        {
    
    //            scalar cellSize = edge0Len/pow(2.0, cLevel);
    
    //
    //            // Update gap size
    //            nearGap[i] = nGapCells*cellSize;
    //
    //            const point& nearPt = nearInfo[i].hitPoint();
    //            vector v(cellCentres[cellI]-nearPt);
    //            scalar magV = mag(v);
    //
    //            // Like with ray shooting we want to
    //            // - find triangles up to nearGap away on the wanted side of the
    //            //   surface
    //            // - find triangles up to 0.5*cellSize away on the unwanted side
    //            //   of the surface. This is for cells straddling the surface
    //            //   where
    //            //   the cell centre might be on the wrong side of the surface
    //
    //            // Tbd: check that cell centre is inbetween the gap hits
    //            // (only if the cell is far enough away)
    //
    //            scalar posNormalSize = 0.0;
    //            scalar negNormalSize = 0.0;
    //
    //            if (mode == volumeType::OUTSIDE)
    //            {
    //                posNormalSize = nearGap[i];
    //                if (magV < 0.5*cellSize)
    //                {
    //                    negNormalSize = 0.5*cellSize;
    //                }
    //            }
    //            else if (mode == volumeType::INSIDE)
    //            {
    //                if (magV < 0.5*cellSize)
    //                {
    //                    posNormalSize = 0.5*cellSize;
    //                }
    //                negNormalSize = nearGap[i];
    //            }
    //            else
    //            {
    //                posNormalSize = nearGap[i];
    //                negNormalSize = nearGap[i];
    //            }
    //
    //            // Test with positive normal
    //            oppositeInfo[compactI] = tree.findNearest
    //            (
    //                nearPt,
    //                sqr(posNormalSize),
    //                findNearestOppositeOp
    //                (
    //                    tree,
    //                    nearPt,
    //                    nearNormal[i],
    //                    searchCos
    //                )
    //            );
    //
    //            if (oppositeInfo[compactI].hit())
    //            {
    //                map[compactI++] = i;
    //            }
    //            else
    //            {
    //                // Test with negative normal
    //                oppositeInfo[compactI] = tree.findNearest
    //                (
    //                    nearPt,
    //                    sqr(negNormalSize),
    //                    findNearestOppositeOp
    //                    (
    //                        tree,
    //                        nearPt,
    //                        -nearNormal[i],
    //                        searchCos
    //                    )
    //                );
    //
    //                if (oppositeInfo[compactI].hit())
    //                {
    //                    map[compactI++] = i;
    //                }
    //            }
    //        }
    //    }
    //
    //    Info<< "Selected " << returnReduce(compactI, sumOp<label>())
    //        << " hits on the correct side out of "
    //        << returnReduce(map.size(), sumOp<label>()) << endl;
    //    map.setSize(compactI);
    //    oppositeInfo.setSize(compactI);
    //
    //    nearMap = UIndirectList<label>(nearMap, map)();
    //    nearGap = UIndirectList<scalar>(nearGap, map)();
    //    nearInfo = UIndirectList<pointIndexHit>(nearInfo, map)();
    //    nearNormal = UIndirectList<vector>(nearNormal, map)();
    //
    //    // Exclude hits which aren't opposite enough. E.g. you might find
    //    // a point on a perpendicular wall - but this does not consistute a gap.
    //    vectorField oppositeNormal;
    //    geom.getNormal(oppositeInfo, oppositeNormal);
    //
    //    compactI = 0;
    //    forAll(oppositeInfo, i)
    //    {
    //        if ((nearNormal[i] & oppositeNormal[i]) < -0.707)
    //        {
    //            map[compactI++] = i;
    //        }
    //    }
    //
    //    Info<< "Selected " << returnReduce(compactI, sumOp<label>())
    //        << " hits opposite the nearest out of "
    //        << returnReduce(map.size(), sumOp<label>()) << endl;
    //    map.setSize(compactI);
    //
    //    nearMap = UIndirectList<label>(nearMap, map)();
    //    nearGap = UIndirectList<scalar>(nearGap, map)();
    //    nearInfo = UIndirectList<pointIndexHit>(nearInfo, map)();
    //    oppositeInfo = UIndirectList<pointIndexHit>(oppositeInfo, map)();
    //}
    
    
    
    Foam::label Foam::meshRefinement::generateRays
    (
        const point& nearPoint,
        const vector& nearNormal,
        const FixedList<label, 3>& gapInfo,
        const volumeType& mode,
    
        const label cLevel,
    
        DynamicField<point>& start,
        DynamicField<point>& end
    ) const
    {
        label nOldRays = start.size();
    
    
        if (cLevel >= gapInfo[1] && cLevel < gapInfo[2] && gapInfo[0] > 0)
    
            scalar cellSize = meshCutter_.level0EdgeLength()/pow(2.0, cLevel);
    
    
            // Calculate gap size
            scalar nearGap = gapInfo[0]*cellSize;
    
            const vector& n = nearNormal;
    
            // Situation 'C' above: cell too close. Use surface
            // -normal and -point to shoot rays
    
            if (mode == volumeType::OUTSIDE)
            {
                start.append(nearPoint+1e-6*n);
                end.append(nearPoint+nearGap*n);
            }
            else if (mode == volumeType::INSIDE)
            {
                start.append(nearPoint-1e-6*n);
                end.append(nearPoint-nearGap*n);
            }
            else if (mode == volumeType::MIXED)
            {
                start.append(nearPoint+1e-6*n);
                end.append(nearPoint+nearGap*n);
    
                start.append(nearPoint-1e-6*n);
                end.append(nearPoint-nearGap*n);
            }
        }
    
        return start.size()-nOldRays;
    }
    
    
    Foam::label Foam::meshRefinement::generateRays
    
    (
        const bool useSurfaceNormal,
    
        const point& nearPoint,
        const vector& nearNormal,
        const FixedList<label, 3>& gapInfo,
        const volumeType& mode,
    
        const point& cc,
        const label cLevel,
    
    
        DynamicField<point>& start,
        DynamicField<point>& end,
        DynamicField<scalar>& gapSize,
    
        DynamicField<point>& start2,
        DynamicField<point>& end2,
        DynamicField<scalar>& gapSize2
    
    ) const
    {
        // We want to handle the following cases:
        // - surface: small gap (marked with 'surface'). gap might be
        //            on inside or outside of surface.
        // - A: cell well inside the gap.
        // - B: cell well outside the gap.
        // - C: cell straddling the gap. cell centre might be inside
        //      or outside
        //
        //       +---+
        //       | B |
        //       +---+
        //
        //            +------+
        //            |      |
        //            |   C  |
        //    --------|------|----surface
        //            +------+
        //
        //        +---+
        //        | A |
        //        +---+
        //
        //
        //    --------------------surface
        //
        // So:
        // - find nearest point on surface
        // - in situation A,B decide if on wanted side of surface
        // - detect if locally a gap (and the cell inside the gap) by
        //   shooting a ray from the point on the surface in the direction
        //   of
        //   - A,B: the cell centre
        //   - C: the surface normal and/or negative surface normal
        //   and see we hit anything
        //
        // Variations of this scheme:
        // - always shoot in the direction of the surface normal. This needs
        //   then an additional check to make sure the cell centre is
        //   somewhere inside the gap
        // - instead of ray shooting use a 'constrained' nearest search
        //   by e.g. looking inside a search cone (implemented in searchCone).
        //   The problem with this constrained nearest is that it still uses
        //   the absolute nearest point on each triangle and only afterwards
        //   checks if it is inside the search cone.
    
    
        // Decide which near points are good:
        // - with updated minLevel and maxLevel and nearGap make sure
        //   the cell is still a candidate
        //   NOTE: inside the gap the nearest point on the surface will
        //         be HALF the gap size - otherwise we would have found
        //         a point on the opposite side
        // - if the mode is both sides
        // - or if the hit is inside the current cell (situation 'C',
        //   magV < 0.5cellSize)
        // - or otherwise if on the correct side
    
    
        label nOldRays = start.size();
    
        if (cLevel >= gapInfo[1] && cLevel < gapInfo[2] && gapInfo[0] > 0)
    
            scalar cellSize = meshCutter_.level0EdgeLength()/pow(2.0, cLevel);
    
    
            // Calculate gap size
            scalar nearGap = gapInfo[0]*cellSize;
    
            // Distance to nearest
            vector v(cc-nearPoint);
            scalar magV = mag(v);
    
            if (useSurfaceNormal || magV < 0.5*cellSize)
            {
                const vector& n = nearNormal;
    
                // Situation 'C' above: cell too close. Use surface
    
                // -normal and -point to shoot rays
    
                    start.append(nearPoint+1e-6*n);
                    end.append(nearPoint+nearGap*n);
                    gapSize.append(nearGap);
                    // Second vector so we get pairs of intersections
                    start2.append(nearPoint+1e-6*n);
                    end2.append(nearPoint-1e-6*n);
                    gapSize2.append(gapSize.last());
    
                }
                else if (mode == volumeType::INSIDE)
                {
    
                    start.append(nearPoint-1e-6*n);
                    end.append(nearPoint-nearGap*n);
                    gapSize.append(nearGap);
                    // Second vector so we get pairs of intersections
                    start2.append(nearPoint-1e-6*n);
                    end2.append(nearPoint+1e-6*n);
                    gapSize2.append(gapSize.last());
    
                }
                else if (mode == volumeType::MIXED)
                {
    
                    // Do both rays:
                    // Outside
                    {
                        start.append(nearPoint+1e-6*n);
                        end.append(nearPoint+nearGap*n);
                        gapSize.append(nearGap);
                        // Second vector so we get pairs of intersections
                        start2.append(nearPoint+1e-6*n);
                        end2.append(nearPoint-1e-6*n);
                        gapSize2.append(gapSize.last());
                    }
                    // Inside
                    {
                        start.append(nearPoint-1e-6*n);
                        end.append(nearPoint-nearGap*n);
                        gapSize.append(nearGap);
                        // Second vector so we get pairs of intersections
                        start2.append(nearPoint-1e-6*n);
                        end2.append(nearPoint+1e-6*n);
                        gapSize2.append(gapSize.last());
                    }
    
                }
            }
            else
            {
                // Situation 'A' or 'B' above: cell well away. Test if
                // cell on correct side of surface and shoot ray through
                // cell centre. Note: no need to shoot ray in other
                // direction since we're trying to detect cell inside
                // the gap.
    
                scalar s = (v&nearNormal);
    
                if
                (
                    (mode == volumeType::MIXED)
                 || (mode == volumeType::OUTSIDE && s > SMALL)
                 || (mode == volumeType::INSIDE && s < -SMALL)
                )
                {
    
                    //// Use single vector through cell centre
    
                    //vector n(v/(magV+ROOTVSMALL));
                    //
                    //start.append(cc);
                    //end.append(cc+nearGap*n);
                    //gapSize.append(nearGap);
                    //
                    //start2.append(cc);
                    //end2.append(cc-nearGap*n);
                    //gapSize2.append(nearGap);
    
    
    
                    //// Shoot some rays through the cell centre
                    //// X-direction:
                    //start.append(cc);
                    //end.append(cc+nearGap*vector(1, 0, 0));
                    //gapSize.append(nearGap);
                    //
                    //start2.append(cc);
                    //end2.append(cc-nearGap*vector(1, 0, 0));
                    //gapSize2.append(nearGap);
                    //
                    //// Y-direction:
                    //start.append(cc);
                    //end.append(cc+nearGap*vector(0, 1, 0));
                    //gapSize.append(nearGap);
                    //
                    //start2.append(cc);
                    //end2.append(cc-nearGap*vector(0, 1, 0));
                    //gapSize2.append(nearGap);
                    //
                    //// Z-direction:
                    //start.append(cc);
                    //end.append(cc+nearGap*vector(0, 0, 1));
                    //gapSize.append(nearGap);
                    //
                    //start2.append(cc);
                    //end2.append(cc-nearGap*vector(0, 0, 1));
                    //gapSize2.append(nearGap);
    
    
                    // 3 axes aligned with normal
    
                    // Use vector through cell centre
                    vector n(v/(magV+ROOTVSMALL));
    
                    // Get second vector. Make sure it is sufficiently perpendicular
                    vector e2(1, 0, 0);
                    scalar s = (e2 & n);
                    if (mag(s) < 0.9)
                    {
                        e2 -= s*n;
                    }
                    else
                    {
                        e2 = vector(0, 1, 0);
                        e2 -= (e2 & n)*n;
                    }
                    e2 /= mag(e2);
    
                    // Third vector
                    vector e3 = n ^ e2;
    
    
                    // Rays in first direction
    
                    gapSize.append(nearGap);
    
                    start2.append(cc);
    
                    gapSize.append(nearGap);
    
                    start2.append(cc);
    
                    gapSize.append(nearGap);
    
                    start2.append(cc);
    
        return start.size()-nOldRays;
    
    }
    
    
    void Foam::meshRefinement::selectGapCandidates
    (
        const labelList& refineCell,
        const label nRefine,
    
        labelList& cellMap,
    
        List<FixedList<label, 3>>& shellGapInfo,