// Find a good edge
            forAll(singleEdges, edgeI)
            {
                SLList<label> pointChain;

                bool blockHead = false;
                bool blockTail = false;

                if (!singleEdgeUsage[edgeI])
                {
                    // found a new edge
                    singleEdgeUsage[edgeI] = true;

                    label newEdgeStart = singleEdges[edgeI].start();
                    label newEdgeEnd = singleEdges[edgeI].end();

                    pointChain.insert(newEdgeStart);
                    pointChain.append(newEdgeEnd);

#                   ifdef DEBUG_CHAIN
                    Info<< "found edge to start with: "
                        << singleEdges[edgeI] << endl;
#                   endif

                    // Check if head or tail are blocked
                    forAll(cellPoints, pointI)
                    {
                        if (cellPoints[pointI] == newEdgeStart)
                        {
                            if (pointUsage[pointI] > 2)
                            {
#                               ifdef DEBUG_CHAIN
                                Info<< "start head blocked" << endl;
#                               endif

                                blockHead = true;
                            }
                        }
                        else if (cellPoints[pointI] == newEdgeEnd)
                        {
                            if (pointUsage[pointI] > 2)
                            {
#                               ifdef DEBUG_CHAIN
                                Info<< "start tail blocked" << endl;
#                               endif

                                blockTail = true;
                            }
                        }
                    }

                    bool stopSearching = false;

                    // Go through the unused edges and try to chain them up
                    do
                    {
                        stopSearching = false;

                        forAll(singleEdges, addEdgeI)
                        {
                            if (!singleEdgeUsage[addEdgeI])
                            {
                                // Grab start and end of the candidate
                                label addStart =
                                    singleEdges[addEdgeI].start();

                                label addEnd =
                                    singleEdges[addEdgeI].end();

#                               ifdef DEBUG_CHAIN
                                Info<< "Trying candidate "
                                    << singleEdges[addEdgeI] << endl;
#                               endif

                                // Try to add the edge onto the head
                                if (!blockHead)
                                {
                                    if (pointChain.first() == addStart)
                                    {
                                        // Added at start mark as used
                                        pointChain.insert(addEnd);

                                        singleEdgeUsage[addEdgeI] = true;
                                    }
                                    else if (pointChain.first() == addEnd)
                                    {
                                        pointChain.insert(addStart);

                                        singleEdgeUsage[addEdgeI] = true;
                                    }
                                }

                                // Try the other end only if the first end
                                // did not add it
                                if (!blockTail && !singleEdgeUsage[addEdgeI])
                                {
                                    if (pointChain.last() == addStart)
                                    {
                                        // Added at start mark as used
                                        pointChain.append(addEnd);

                                        singleEdgeUsage[addEdgeI] = true;
                                    }
                                    else if (pointChain.last() == addEnd)
                                    {
                                        pointChain.append(addStart);

                                        singleEdgeUsage[addEdgeI] = true;
                                    }
                                }

                                // check if the new head or tail are blocked
                                label curEdgeStart = pointChain.first();
                                label curEdgeEnd = pointChain.last();

#                               ifdef DEBUG_CHAIN
                                Info<< "curEdgeStart: " << curEdgeStart
                                    << " curEdgeEnd: " << curEdgeEnd << endl;
#                               endif

                                forAll(cellPoints, pointI)
                                {
                                    if (cellPoints[pointI] == curEdgeStart)
                                    {
                                        if (pointUsage[pointI] > 2)
                                        {
#                                           ifdef DEBUG_CHAIN
                                            Info<< "head blocked" << endl;
#                                           endif

                                            blockHead = true;
                                        }
                                    }
                                    else if (cellPoints[pointI] == curEdgeEnd)
                                    {
                                        if (pointUsage[pointI] > 2)
                                        {
#                                           ifdef DEBUG_CHAIN
                                            Info<< "tail blocked" << endl;
#                                           endif

                                            blockTail = true;
                                        }
                                    }
                                }

                                // Check if the loop is closed
                                if (curEdgeStart == curEdgeEnd)
                                {
#                                   ifdef DEBUG_CHAIN
                                    Info<< "closed loop" << endl;
#                                   endif

                                    pointChain.removeHead();

                                    blockHead = true;
                                    blockTail = true;

                                    stopSearching = true;
                                }

#                               ifdef DEBUG_CHAIN
                                Info<< "current pointChain: " << pointChain
                                    << endl;
#                               endif

                                if (stopSearching) break;
                            }
                        }
                    } while (stopSearching);
                }
void Foam::immersedBoundaryFvPatch::makeTriAddressing() const
{
    if (debug)
    {
        InfoIn("void immersedBoundaryFvPatch::makeTriAddressing() const")
            << "creating tri addressing for immersed boundary " << name()
            << endl;
    }

    // It is an error to attempt to recalculate
    // if the pointer is already set
    if (cellsToTriAddrPtr_ || cellsToTriWeightsPtr_)
    {
        FatalErrorIn("immersedBoundaryFvPatch::makeTriAddressing() const")
            << "tri addressing already exist"
            << "for immersed boundary" << name()
            << abort(FatalError);
    }

    // Get reference to tri patch and hit faces
    const triSurface& triPatch = ibPolyPatch_.ibMesh();
    const vectorField& triCentres = triPatch.faceCentres();

    const labelList& hf = hitFaces();
    const vectorField& ibp = ibPoints();

    // Create a markup field and mark all tris containing an ib point with its
    // index
    labelList hitTris(triPatch.size(), -1);

    forAll (hf, hfI)
    {
        hitTris[hf[hfI]] = hfI;
    }

    // Allocate storage
    cellsToTriAddrPtr_ = new labelListList(triPatch.size());
    labelListList& addr = *cellsToTriAddrPtr_;

    cellsToTriWeightsPtr_ = new scalarListList(triPatch.size());
    scalarListList& w = *cellsToTriWeightsPtr_;

    // Algorithm:
    // For each tri face, check if it contains an IB point
    // - if so, set the addressing to the index of IB point and weight to 1
    // - if not, search the neighbouring faces of the visited faces until
    //   at least 3 IB points are found, or the neighbourhood is exhausted.
    //   When a sufficient number of points is found, calculate the weights
    //   using inverse distance weighting

    // Get addressing from the triangular patch
    const labelListList& pf = triPatch.pointFaces();

    forAll (triPatch, triI)
    {
        if (hitTris[triI] > -1)
        {
            // Triangle contains IB point
            addr[triI].setSize(1);
            w[triI].setSize(1);

            addr[triI] = hitTris[triI];
            w[triI] = 1;
        }
        else
        {
            // No direct hit.  Start a neighbourhood search

            // Record already visited faces
            labelHashSet visited;

            // Collect new faces to visit
            SLList<label> nextToVisit;

            // Collect IB points for interpolation
            labelHashSet ibPointsToUse;

            // Initialise with the original tri
            nextToVisit.insert(triI);

            do
            {
                const label curTri = nextToVisit.removeHead();

                // Discard tri if already visited
                if (visited[curTri]) continue;

                visited.insert(curTri);

                const triFace& curTriPoints = triPatch[curTri];

                // For all current points of face, pick up neighbouring faces
                forAll (curTriPoints, tpI)
                {
                    const labelList curNbrs = pf[curTriPoints[tpI]];

                    forAll (curNbrs, nbrI)
                    {
                        if (!visited.found(curNbrs[nbrI]))
                        {
                            // Found a face which is not visited.  Add it to
                            // the list of faces to visit
                            nextToVisit.append(curNbrs[nbrI]);

                            if (hitTris[curNbrs[nbrI]] > -1)
                            {
                                // Found a neighbour with a hit: use this
                                // IB point
                                ibPointsToUse.insert(hitTris[curNbrs[nbrI]]);
                            }
                        }
                    }
                }
            } while
            (
                ibPointsToUse.size() < 3
             && !nextToVisit.empty()
            );

            // Found neighbourhood: collect addressing and weights
            addr[triI] = ibPointsToUse.toc();
            w[triI].setSize(addr[triI].size());

            labelList& curAddr = addr[triI];
            scalarList& curW = w[triI];

            vector curTriCentre = triCentres[triI];

            scalar sumW = 0;

            forAll (curAddr, ibI)
            {
                curW[ibI] = 1/mag(curTriCentre - ibp[curAddr[ibI]]);
                sumW += curW[ibI];
            }

            // Divide weights by sum distance
            forAll (curW, ibI)
            {
                curW[ibI] /= sumW;
            }
        }
    }