From 2b9b4c1aee532d8d7487d0328e286e432b267e1a Mon Sep 17 00:00:00 2001
From: Andrew Heather <a.heather@opencfd.co.uk>
Date: Tue, 5 Sep 2017 09:32:21 +0100
Subject: [PATCH] ENH: Reinstated local tet-based intersection

---
 .../primitiveShapes/tetrahedron/tetPoints.H   | 113 ++++
 .../primitiveShapes/tetrahedron/tetrahedron.H |  96 +++-
 .../tetrahedron/tetrahedronI.H                | 543 ++++++++++++++++++
 .../tetOverlapVolume/tetOverlapVolume.C       | 341 ++---------
 .../tetOverlapVolume/tetOverlapVolume.H       | 162 ++++--
 .../tetOverlapVolumeTemplates.C               | 259 +++++++++
 6 files changed, 1177 insertions(+), 337 deletions(-)
 create mode 100644 src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetPoints.H
 create mode 100644 src/meshTools/tetOverlapVolume/tetOverlapVolumeTemplates.C

diff --git a/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetPoints.H b/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetPoints.H
new file mode 100644
index 0000000000..8ac906ca62
--- /dev/null
+++ b/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetPoints.H
@@ -0,0 +1,113 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011-2012 OpenFOAM Foundation
+     \\/     M anipulation  | Copyright (C) 2017 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/>.
+
+Class
+    Foam::tetPoints
+
+Description
+    Tet storage. Null constructable (unfortunately tetrahedron<point, point>
+    is not)
+
+SourceFiles
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef tetPoints_H
+#define tetPoints_H
+
+#include "tetrahedron.H"
+#include "FixedList.H"
+#include "treeBoundBox.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class tetPoints Declaration
+\*---------------------------------------------------------------------------*/
+
+class tetPoints
+:
+    public FixedList<point, 4>
+{
+public:
+
+    // Constructors
+
+        //- Construct null
+        inline tetPoints()
+        {}
+
+        //- Construct from four points
+        inline tetPoints
+        (
+            const point& a,
+            const point& b,
+            const point& c,
+            const point& d
+        )
+        {
+            operator[](0) = a;
+            operator[](1) = b;
+            operator[](2) = c;
+            operator[](3) = d;
+        }
+
+    // Member Functions
+
+        //- Return the tetrahedron
+        inline tetPointRef tet() const
+        {
+            return tetPointRef
+            (
+                operator[](0),
+                operator[](1),
+                operator[](2),
+                operator[](3)
+            );
+        }
+
+        //- Calculate the bounding box
+        inline treeBoundBox bounds() const
+        {
+            treeBoundBox bb(operator[](0));
+            for (label i = 1; i < size(); ++i)
+            {
+                bb.add(operator[](i));
+            }
+            return bb;
+        }
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedron.H b/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedron.H
index 6114f4fc41..1006382339 100644
--- a/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedron.H
+++ b/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedron.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011-2017 OpenFOAM Foundation
+    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -56,6 +56,8 @@ namespace Foam
 
 class Istream;
 class Ostream;
+class tetPoints;
+class plane;
 
 // Forward declaration of friend functions and operators
 
@@ -75,6 +77,8 @@ inline Ostream& operator<<
     const tetrahedron<Point, PointRef>&
 );
 
+typedef tetrahedron<point, const point&> tetPointRef;
+
 /*---------------------------------------------------------------------------*\
                            class tetrahedron Declaration
 \*---------------------------------------------------------------------------*/
@@ -82,12 +86,78 @@ inline Ostream& operator<<
 template<class Point, class PointRef>
 class tetrahedron
 {
+public:
+
+    // Public typedefs
+
+        //- Storage type for tets originating from intersecting tets.
+        //  (can possibly be smaller than 200)
+        typedef FixedList<tetPoints, 200> tetIntersectionList;
+
+
+        // Classes for use in sliceWithPlane. What to do with decomposition
+        // of tet.
+
+            //- Dummy
+            class dummyOp
+            {
+            public:
+                inline void operator()(const tetPoints&);
+            };
+
+            //- Sum resulting volumes
+            class sumVolOp
+            {
+            public:
+                scalar vol_;
+
+                inline sumVolOp();
+
+                inline void operator()(const tetPoints&);
+            };
+
+            //- Store resulting tets
+            class storeOp
+            {
+                tetIntersectionList& tets_;
+                label& nTets_;
+
+            public:
+                inline storeOp(tetIntersectionList&, label&);
+
+                inline void operator()(const tetPoints&);
+            };
+
 private:
 
     // Private data
 
         PointRef a_, b_, c_, d_;
 
+        inline static point planeIntersection
+        (
+            const FixedList<scalar, 4>&,
+            const tetPoints&,
+            const label,
+            const label
+        );
+
+        template<class TetOp>
+        inline static void decomposePrism
+        (
+            const FixedList<point, 6>& points,
+            TetOp& op
+        );
+
+        template<class AboveTetOp, class BelowTetOp>
+        inline static void tetSliceWithPlane
+        (
+            const plane& pl,
+            const tetPoints& tet,
+            AboveTetOp& aboveOp,
+            BelowTetOp& belowOp
+        );
+
 
 public:
 
@@ -170,10 +240,6 @@ public:
             //  uniform distribution
             inline Point randomPoint(Random& rndGen) const;
 
-            //- Return a random point in the tetrahedron from a
-            //  uniform distribution
-            inline Point randomPoint(cachedRandom& rndGen) const;
-
             //- Calculate the point from the given barycentric coordinates.
             inline Point barycentricToPoint(const barycentric& bary) const;
 
@@ -195,6 +261,26 @@ public:
             //- Return true if point is inside tetrahedron
             inline bool inside(const point& pt) const;
 
+            //- Decompose tet into tets above and below plane
+            template<class AboveTetOp, class BelowTetOp>
+            inline void sliceWithPlane
+            (
+                const plane& pl,
+                AboveTetOp& aboveOp,
+                BelowTetOp& belowOp
+            ) const;
+
+            //- Decompose tet into tets inside and outside other tet
+            inline void tetOverlap
+            (
+                const tetrahedron<Point, PointRef>& tetB,
+                tetIntersectionList& insideTets,
+                label& nInside,
+                tetIntersectionList& outsideTets,
+                label& nOutside
+            ) const;
+
+
             //- Return (min)containment sphere, i.e. the smallest sphere with
             //  all points inside. Returns pointHit with:
             //  - hit         : if sphere is equal to circumsphere
diff --git a/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedronI.H b/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedronI.H
index 0cbee519e3..2ed07ccf7d 100644
--- a/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedronI.H
+++ b/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedronI.H
@@ -25,6 +25,7 @@ License
 
 #include "triangle.H"
 #include "IOstreams.H"
+#include "tetPoints.H"
 #include "plane.H"
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
@@ -485,6 +486,548 @@ bool Foam::tetrahedron<Point, PointRef>::inside(const point& pt) const
 }
 
 
+template<class Point, class PointRef>
+inline void Foam::tetrahedron<Point, PointRef>::dummyOp::operator()
+(
+    const tetPoints&
+)
+{}
+
+
+template<class Point, class PointRef>
+inline Foam::tetrahedron<Point, PointRef>::sumVolOp::sumVolOp()
+:
+    vol_(0.0)
+{}
+
+
+template<class Point, class PointRef>
+inline void Foam::tetrahedron<Point, PointRef>::sumVolOp::operator()
+(
+    const tetPoints& tet
+)
+{
+    vol_ += tet.tet().mag();
+}
+
+
+template<class Point, class PointRef>
+inline Foam::tetrahedron<Point, PointRef>::storeOp::storeOp
+(
+    tetIntersectionList& tets,
+    label& nTets
+)
+:
+    tets_(tets),
+    nTets_(nTets)
+{}
+
+
+template<class Point, class PointRef>
+inline void Foam::tetrahedron<Point, PointRef>::storeOp::operator()
+(
+    const tetPoints& tet
+)
+{
+    tets_[nTets_++] = tet;
+}
+
+
+template<class Point, class PointRef>
+inline Foam::point Foam::tetrahedron<Point, PointRef>::planeIntersection
+(
+    const FixedList<scalar, 4>& d,
+    const tetPoints& t,
+    const label negI,
+    const label posI
+)
+{
+    return
+        (d[posI]*t[negI] - d[negI]*t[posI])
+      / (-d[negI]+d[posI]);
+}
+
+
+template<class Point, class PointRef>
+template<class TetOp>
+inline void Foam::tetrahedron<Point, PointRef>::decomposePrism
+(
+    const FixedList<point, 6>& points,
+    TetOp& op
+)
+{
+    op(tetPoints(points[1], points[3], points[2], points[0]));
+    op(tetPoints(points[1], points[2], points[3], points[4]));
+    op(tetPoints(points[4], points[2], points[3], points[5]));
+}
+
+
+template<class Point, class PointRef>
+template<class AboveTetOp, class BelowTetOp>
+inline void Foam::tetrahedron<Point, PointRef>::tetSliceWithPlane
+(
+    const plane& pl,
+    const tetPoints& tet,
+    AboveTetOp& aboveOp,
+    BelowTetOp& belowOp
+)
+{
+    // Distance to plane
+    FixedList<scalar, 4> d;
+    label nPos = 0;
+    forAll(tet, i)
+    {
+        d[i] = ((tet[i] - pl.refPoint()) & pl.normal());
+        if (d[i] > 0)
+        {
+            nPos++;
+        }
+    }
+
+    if (nPos == 4)
+    {
+        aboveOp(tet);
+    }
+    else if (nPos == 3)
+    {
+        // Sliced into below tet and above prism. Prism gets split into
+        // two tets.
+
+        // Find the below tet
+        label i0 = -1;
+        forAll(d, i)
+        {
+            if (d[i] <= 0)
+            {
+                i0 = i;
+                break;
+            }
+        }
+
+        label i1 = d.fcIndex(i0);
+        label i2 = d.fcIndex(i1);
+        label i3 = d.fcIndex(i2);
+
+        point p01(planeIntersection(d, tet, i0, i1));
+        point p02(planeIntersection(d, tet, i0, i2));
+        point p03(planeIntersection(d, tet, i0, i3));
+
+        // i0 = tetCell vertex 0: p01,p02,p03 outwards pointing triad
+        //          ,,         1 :     ,,     inwards pointing triad
+        //          ,,         2 :     ,,     outwards pointing triad
+        //          ,,         3 :     ,,     inwards pointing triad
+
+        //Pout<< "Split 3pos tet " << tet << " d:" << d << " into" << nl;
+
+        if (i0 == 0 || i0 == 2)
+        {
+            tetPoints t(tet[i0], p01, p02, p03);
+            //Pout<< "    belowtet:" << t << " around i0:" << i0 << endl;
+            //checkTet(t, "nPos 3, belowTet i0==0 or 2");
+            belowOp(t);
+
+            // Prism
+            FixedList<point, 6> p
+            (
+                {
+                    tet[i1],
+                    tet[i3],
+                    tet[i2],
+                    p01,
+                    p03,
+                    p02
+                }
+            );
+
+            //Pout<< "    aboveprism:" << p << endl;
+            decomposePrism(p, aboveOp);
+        }
+        else
+        {
+            tetPoints t(p01, p02, p03, tet[i0]);
+            //Pout<< "    belowtet:" << t << " around i0:" << i0 << endl;
+            //checkTet(t, "nPos 3, belowTet i0==1 or 3");
+            belowOp(t);
+
+            // Prism
+            FixedList<point, 6> p
+            (
+                {
+                    tet[i3],
+                    tet[i1],
+                    tet[i2],
+                    p03,
+                    p01,
+                    p02
+                }
+            );
+            //Pout<< "    aboveprism:" << p << endl;
+            decomposePrism(p, aboveOp);
+        }
+    }
+    else if (nPos == 2)
+    {
+        // Tet cut into two prisms. Determine the positive one.
+        label pos0 = -1;
+        label pos1 = -1;
+        forAll(d, i)
+        {
+            if (d[i] > 0)
+            {
+                if (pos0 == -1)
+                {
+                    pos0 = i;
+                }
+                else
+                {
+                    pos1 = i;
+                }
+            }
+        }
+
+        //Pout<< "Split 2pos tet " << tet << " d:" << d
+        //    << " around pos0:" << pos0 << " pos1:" << pos1
+        //    << " neg0:" << neg0 << " neg1:" << neg1 << " into" << nl;
+
+        const edge posEdge(pos0, pos1);
+
+        if (posEdge == edge(0, 1))
+        {
+            point p02(planeIntersection(d, tet, 0, 2));
+            point p03(planeIntersection(d, tet, 0, 3));
+            point p12(planeIntersection(d, tet, 1, 2));
+            point p13(planeIntersection(d, tet, 1, 3));
+            // Split the resulting prism
+            {
+                FixedList<point, 6> p
+                (
+                    {
+                        tet[0],
+                        p02,
+                        p03,
+                        tet[1],
+                        p12,
+                        p13
+                    }
+                );
+
+                //Pout<< "    01 aboveprism:" << p << endl;
+                decomposePrism(p, aboveOp);
+            }
+            {
+                FixedList<point, 6> p
+                (
+                    {
+                        tet[2],
+                        p02,
+                        p12,
+                        tet[3],
+                        p03,
+                        p13
+                    }
+                );
+
+                //Pout<< "    01 belowprism:" << p << endl;
+                decomposePrism(p, belowOp);
+            }
+        }
+        else if (posEdge == edge(1, 2))
+        {
+            point p01(planeIntersection(d, tet, 0, 1));
+            point p13(planeIntersection(d, tet, 1, 3));
+            point p02(planeIntersection(d, tet, 0, 2));
+            point p23(planeIntersection(d, tet, 2, 3));
+            // Split the resulting prism
+            {
+                FixedList<point, 6> p
+                (
+                    {
+                        tet[1],
+                        p01,
+                        p13,
+                        tet[2],
+                        p02,
+                        p23
+                    }
+                );
+
+                //Pout<< "    12 aboveprism:" << p << endl;
+                decomposePrism(p, aboveOp);
+            }
+            {
+                FixedList<point, 6> p
+                (
+                    {
+                        tet[3],
+                        p23,
+                        p13,
+                        tet[0],
+                        p02,
+                        p01
+                    }
+                );
+
+                //Pout<< "    12 belowprism:" << p << endl;
+                decomposePrism(p, belowOp);
+            }
+        }
+        else if (posEdge == edge(2, 0))
+        {
+            point p01(planeIntersection(d, tet, 0, 1));
+            point p03(planeIntersection(d, tet, 0, 3));
+            point p12(planeIntersection(d, tet, 1, 2));
+            point p23(planeIntersection(d, tet, 2, 3));
+            // Split the resulting prism
+            {
+                FixedList<point, 6> p
+                (
+                    {
+                        tet[2],
+                        p12,
+                        p23,
+                        tet[0],
+                        p01,
+                        p03
+                    }
+                );
+
+                //Pout<< "    20 aboveprism:" << p << endl;
+                decomposePrism(p, aboveOp);
+            }
+            {
+                FixedList<point, 6> p
+                (
+                    {
+                        tet[1],
+                        p12,
+                        p01,
+                        tet[3],
+                        p23,
+                        p03
+                    }
+                );
+
+                //Pout<< "    20 belowprism:" << p << endl;
+                decomposePrism(p, belowOp);
+            }
+        }
+        else if (posEdge == edge(0, 3))
+        {
+            point p01(planeIntersection(d, tet, 0, 1));
+            point p02(planeIntersection(d, tet, 0, 2));
+            point p13(planeIntersection(d, tet, 1, 3));
+            point p23(planeIntersection(d, tet, 2, 3));
+            // Split the resulting prism
+            {
+                FixedList<point, 6> p
+                (
+                    {
+                        tet[3],
+                        p23,
+                        p13,
+                        tet[0],
+                        p02,
+                        p01
+                    }
+                );
+
+                //Pout<< "    03 aboveprism:" << p << endl;
+                decomposePrism(p, aboveOp);
+            }
+            {
+                FixedList<point, 6> p
+                (
+                    {
+                        tet[2],
+                        p23,
+                        p02,
+                        tet[1],
+                        p13,
+                        p01
+                    }
+                );
+
+                //Pout<< "    03 belowprism:" << p << endl;
+                decomposePrism(p, belowOp);
+            }
+        }
+        else if (posEdge == edge(1, 3))
+        {
+            point p01(planeIntersection(d, tet, 0, 1));
+            point p12(planeIntersection(d, tet, 1, 2));
+            point p03(planeIntersection(d, tet, 0, 3));
+            point p23(planeIntersection(d, tet, 2, 3));
+            // Split the resulting prism
+            {
+                FixedList<point, 6> p
+                (
+                    {
+                        tet[1],
+                        p12,
+                        p01,
+                        tet[3],
+                        p23,
+                        p03
+                    }
+                );
+
+                //Pout<< "    13 aboveprism:" << p << endl;
+                decomposePrism(p, aboveOp);
+            }
+            {
+                FixedList<point, 6> p
+                (
+                    {
+                        tet[2],
+                        p12,
+                        p23,
+                        tet[0],
+                        p01,
+                        p03
+                    }
+                );
+
+                //Pout<< "    13 belowprism:" << p << endl;
+                decomposePrism(p, belowOp);
+            }
+        }
+        else if (posEdge == edge(2, 3))
+        {
+            point p02(planeIntersection(d, tet, 0, 2));
+            point p12(planeIntersection(d, tet, 1, 2));
+            point p03(planeIntersection(d, tet, 0, 3));
+            point p13(planeIntersection(d, tet, 1, 3));
+            // Split the resulting prism
+            {
+                FixedList<point, 6> p
+                (
+                    {
+                        tet[2],
+                        p02,
+                        p12,
+                        tet[3],
+                        p03,
+                        p13
+                    }
+                );
+
+                //Pout<< "    23 aboveprism:" << p << endl;
+                decomposePrism(p, aboveOp);
+            }
+            {
+                FixedList<point, 6> p
+                (
+                    {
+                        tet[0],
+                        p02,
+                        p03,
+                        tet[1],
+                        p12,
+                        p13
+                    }
+                );
+
+                //Pout<< "    23 belowprism:" << p << endl;
+                decomposePrism(p, belowOp);
+            }
+        }
+        else
+        {
+            FatalErrorInFunction
+                << "Missed edge:" << posEdge
+                << abort(FatalError);
+        }
+    }
+    else if (nPos == 1)
+    {
+        // Find the positive tet
+        label i0 = -1;
+        forAll(d, i)
+        {
+            if (d[i] > 0)
+            {
+                i0 = i;
+                break;
+            }
+        }
+
+        label i1 = d.fcIndex(i0);
+        label i2 = d.fcIndex(i1);
+        label i3 = d.fcIndex(i2);
+
+        point p01(planeIntersection(d, tet, i0, i1));
+        point p02(planeIntersection(d, tet, i0, i2));
+        point p03(planeIntersection(d, tet, i0, i3));
+
+        //Pout<< "Split 1pos tet " << tet << " d:" << d << " into" << nl;
+
+        if (i0 == 0 || i0 == 2)
+        {
+            tetPoints t(tet[i0], p01, p02, p03);
+            //Pout<< "    abovetet:" << t << " around i0:" << i0 << endl;
+            //checkTet(t, "nPos 1, aboveTets i0==0 or 2");
+            aboveOp(t);
+
+            // Prism
+            FixedList<point, 6> p
+            (
+                {
+                    tet[i1],
+                    tet[i3],
+                    tet[i2],
+                    p01,
+                    p03,
+                    p02
+                }
+            );
+
+            //Pout<< "    belowprism:" << p << endl;
+            decomposePrism(p, belowOp);
+        }
+        else
+        {
+            tetPoints t(p01, p02, p03, tet[i0]);
+            //Pout<< "    abovetet:" << t << " around i0:" << i0 << endl;
+            //checkTet(t, "nPos 1, aboveTets i0==1 or 3");
+            aboveOp(t);
+
+            // Prism
+            FixedList<point, 6> p
+            (
+                {
+                    tet[i3],
+                    tet[i1],
+                    tet[i2],
+                    p03,
+                    p01,
+                    p02
+                }
+            );
+
+            //Pout<< "    belowprism:" << p << endl;
+            decomposePrism(p, belowOp);
+        }
+    }
+    else    // nPos == 0
+    {
+        belowOp(tet);
+    }
+}
+
+
+template<class Point, class PointRef>
+template<class AboveTetOp, class BelowTetOp>
+inline void Foam::tetrahedron<Point, PointRef>::sliceWithPlane
+(
+    const plane& pl,
+    AboveTetOp& aboveOp,
+    BelowTetOp& belowOp
+) const
+{
+    tetSliceWithPlane(pl, tetPoints(a_, b_, c_, d_), aboveOp, belowOp);
+}
+
+
 // * * * * * * * * * * * * * * * Ostream Operator  * * * * * * * * * * * * * //
 
 template<class Point, class PointRef>
diff --git a/src/meshTools/tetOverlapVolume/tetOverlapVolume.C b/src/meshTools/tetOverlapVolume/tetOverlapVolume.C
index dcae8ae427..73a5b67fbd 100644
--- a/src/meshTools/tetOverlapVolume/tetOverlapVolume.C
+++ b/src/meshTools/tetOverlapVolume/tetOverlapVolume.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2012-2017 OpenFOAM Foundation
+    \\  /    A nd           | Copyright (C) 2012-2015 OpenFOAM Foundation
      \\/     M anipulation  | Copyright (C) 2016-2017 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
@@ -24,12 +24,13 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "tetOverlapVolume.H"
+#include "tetrahedron.H"
+#include "tetPoints.H"
 #include "polyMesh.H"
 #include "OFstream.H"
 #include "treeBoundBox.H"
 #include "indexedOctree.H"
 #include "treeDataCell.H"
-#include "cut.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -47,67 +48,6 @@ Foam::tetOverlapVolume::tetOverlapVolume()
 
 // * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * * * //
 
-Foam::scalar Foam::tetOverlapVolume::tetTetOverlapVol
-(
-    const tetPointRef& tetA,
-    const tetPointRef& tetB
-) const
-{
-    // A maximum of three cuts are made (the tets that result from the final cut
-    // are not stored), and each cut can create at most three tets. The
-    // temporary storage must therefore extend to 3^3 = 27 tets.
-    typedef cutTetList<27> tetListType;
-    static tetListType cutTetList1, cutTetList2;
-
-    // face 0
-    const plane pl0(tetB.b(), tetB.d(), tetB.c());
-    const FixedList<point, 4> t({tetA.a(), tetA.b(), tetA.c(), tetA.d()});
-    cutTetList1.clear();
-    tetCut(t, pl0, cut::appendOp<tetListType>(cutTetList1), cut::noOp());
-    if (cutTetList1.size() == 0)
-    {
-        return 0;
-    }
-
-    // face 1
-    const plane pl1(tetB.a(), tetB.c(), tetB.d());
-    cutTetList2.clear();
-    for (label i = 0; i < cutTetList1.size(); i++)
-    {
-        const FixedList<point, 4>& t = cutTetList1[i];
-        tetCut(t, pl1, cut::appendOp<tetListType>(cutTetList2), cut::noOp());
-    }
-    if (cutTetList2.size() == 0)
-    {
-        return 0;
-    }
-
-    // face 2
-    const plane pl2(tetB.a(), tetB.d(), tetB.b());
-    cutTetList1.clear();
-    for (label i = 0; i < cutTetList2.size(); i++)
-    {
-        const FixedList<point, 4>& t = cutTetList2[i];
-        tetCut(t, pl2, cut::appendOp<tetListType>(cutTetList1), cut::noOp());
-    }
-    if (cutTetList1.size() == 0)
-    {
-        return 0;
-    }
-
-    // face 3
-    const plane pl3(tetB.a(), tetB.b(), tetB.c());
-    scalar v = 0;
-    for (label i = 0; i < cutTetList1.size(); i++)
-    {
-        const FixedList<point, 4>& t = cutTetList1[i];
-        v += tetCut(t, pl3, cut::volumeOp(), cut::noOp());
-    }
-
-    return v;
-}
-
-
 Foam::treeBoundBox Foam::tetOverlapVolume::pyrBb
 (
     const pointField& points,
@@ -134,122 +74,43 @@ bool Foam::tetOverlapVolume::cellCellOverlapMinDecomp
     const scalar threshold
 ) const
 {
-    const cell& cFacesA = meshA.cells()[cellAI];
-    const point& ccA = meshA.cellCentres()[cellAI];
-
-    const cell& cFacesB = meshB.cells()[cellBI];
-    const point& ccB = meshB.cellCentres()[cellBI];
-
-    scalar vol = 0.0;
-
-    forAll(cFacesA, cFA)
-    {
-        label faceAI = cFacesA[cFA];
-
-        const face& fA = meshA.faces()[faceAI];
-        const treeBoundBox pyrA = pyrBb(meshA.points(), fA, ccA);
-        if (!pyrA.overlaps(cellBbB))
-        {
-            continue;
-        }
-
-        bool ownA = (meshA.faceOwner()[faceAI] == cellAI);
-
-        label tetBasePtAI = 0;
-
-        const point& tetBasePtA = meshA.points()[fA[tetBasePtAI]];
-
-        for (label tetPtI = 1; tetPtI < fA.size() - 1; tetPtI++)
-        {
-            label facePtAI = (tetPtI + tetBasePtAI) % fA.size();
-            label otherFacePtAI = fA.fcIndex(facePtAI);
-
-            label pt0I = -1;
-            label pt1I = -1;
-
-            if (ownA)
-            {
-                pt0I = fA[facePtAI];
-                pt1I = fA[otherFacePtAI];
-            }
-            else
-            {
-                pt0I = fA[otherFacePtAI];
-                pt1I = fA[facePtAI];
-            }
-
-            const tetPointRef tetA
-            (
-                ccA,
-                tetBasePtA,
-                meshA.points()[pt0I],
-                meshA.points()[pt1I]
-            );
-            const treeBoundBox tetABb(tetA.bounds());
-
-
-            // Loop over tets of cellB
-            forAll(cFacesB, cFB)
-            {
-                label faceBI = cFacesB[cFB];
-
-                const face& fB = meshB.faces()[faceBI];
-                const treeBoundBox pyrB = pyrBb(meshB.points(), fB, ccB);
-                if (!pyrB.overlaps(pyrA))
-                {
-                    continue;
-                }
-
-                bool ownB = (meshB.faceOwner()[faceBI] == cellBI);
-
-                label tetBasePtBI = 0;
-
-                const point& tetBasePtB = meshB.points()[fB[tetBasePtBI]];
-
-                for (label tetPtI = 1; tetPtI < fB.size() - 1; tetPtI++)
-                {
-                    label facePtBI = (tetPtI + tetBasePtBI) % fB.size();
-                    label otherFacePtBI = fB.fcIndex(facePtBI);
-
-                    label pt0I = -1;
-                    label pt1I = -1;
-
-                    if (ownB)
-                    {
-                        pt0I = fB[facePtBI];
-                        pt1I = fB[otherFacePtBI];
-                    }
-                    else
-                    {
-                        pt0I = fB[otherFacePtBI];
-                        pt1I = fB[facePtBI];
-                    }
-
-                    const tetPointRef tetB
-                    (
-                        ccB,
-                        tetBasePtB,
-                        meshB.points()[pt0I],
-                        meshB.points()[pt1I]
-                    );
-
-                    if (!tetB.bounds().overlaps(tetABb))
-                    {
-                        continue;
-                    }
-
-                    vol += tetTetOverlapVol(tetA, tetB);
-
-                    if (vol > threshold)
-                    {
-                        return true;
-                    }
-                }
-            }
-        }
-    }
-
-    return false;
+    hasOverlapOp overlapCheckOp(threshold);
+    cellCellOverlapMinDecomp<hasOverlapOp>
+    (
+        meshA,
+        cellAI,
+        meshB,
+        cellBI,
+        cellBbB,
+        overlapCheckOp
+    );
+
+    return overlapCheckOp.ok_;
+}
+
+
+Foam::scalar Foam::tetOverlapVolume::cellCellOverlapVolumeMinDecomp
+(
+    const primitiveMesh& meshA,
+    const label cellAI,
+
+    const primitiveMesh& meshB,
+    const label cellBI,
+    const treeBoundBox& cellBbB
+) const
+{
+    sumOverlapOp overlapSumOp;
+    cellCellOverlapMinDecomp<sumOverlapOp>
+    (
+        meshA,
+        cellAI,
+        meshB,
+        cellBI,
+        cellBbB,
+        overlapSumOp
+    );
+
+    return overlapSumOp.iop_.vol_;
 }
 
 
@@ -264,116 +125,18 @@ Foam::tetOverlapVolume::cellCellOverlapMomentMinDecomp
     const treeBoundBox& cellBbB
 ) const
 {
-    const cell& cFacesA = meshA.cells()[cellAI];
-    const point& ccA = meshA.cellCentres()[cellAI];
-
-    const cell& cFacesB = meshB.cells()[cellBI];
-    const point& ccB = meshB.cellCentres()[cellBI];
-
-    scalar vol = 0.0;
-
-    forAll(cFacesA, cFA)
-    {
-        label faceAI = cFacesA[cFA];
-
-        const face& fA = meshA.faces()[faceAI];
-        const treeBoundBox pyrA = pyrBb(meshA.points(), fA, ccA);
-        if (!pyrA.overlaps(cellBbB))
-        {
-            continue;
-        }
-
-        bool ownA = (meshA.faceOwner()[faceAI] == cellAI);
-
-        label tetBasePtAI = 0;
-
-        const point& tetBasePtA = meshA.points()[fA[tetBasePtAI]];
-
-        for (label tetPtI = 1; tetPtI < fA.size() - 1; tetPtI++)
-        {
-            label facePtAI = (tetPtI + tetBasePtAI) % fA.size();
-            label otherFacePtAI = fA.fcIndex(facePtAI);
-
-            label pt0I = -1;
-            label pt1I = -1;
-
-            if (ownA)
-            {
-                pt0I = fA[facePtAI];
-                pt1I = fA[otherFacePtAI];
-            }
-            else
-            {
-                pt0I = fA[otherFacePtAI];
-                pt1I = fA[facePtAI];
-            }
-
-            const tetPointRef tetA
-            (
-                ccA,
-                tetBasePtA,
-                meshA.points()[pt0I],
-                meshA.points()[pt1I]
-            );
-            const treeBoundBox tetABb(tetA.bounds());
-
-
-            // Loop over tets of cellB
-            forAll(cFacesB, cFB)
-            {
-                label faceBI = cFacesB[cFB];
-
-                const face& fB = meshB.faces()[faceBI];
-                const treeBoundBox pyrB = pyrBb(meshB.points(), fB, ccB);
-                if (!pyrB.overlaps(pyrA))
-                {
-                    continue;
-                }
-
-                bool ownB = (meshB.faceOwner()[faceBI] == cellBI);
-
-                label tetBasePtBI = 0;
-
-                const point& tetBasePtB = meshB.points()[fB[tetBasePtBI]];
-
-                for (label tetPtI = 1; tetPtI < fB.size() - 1; tetPtI++)
-                {
-                    label facePtBI = (tetPtI + tetBasePtBI) % fB.size();
-                    label otherFacePtBI = fB.fcIndex(facePtBI);
-
-                    label pt0I = -1;
-                    label pt1I = -1;
-
-                    if (ownB)
-                    {
-                        pt0I = fB[facePtBI];
-                        pt1I = fB[otherFacePtBI];
-                    }
-                    else
-                    {
-                        pt0I = fB[otherFacePtBI];
-                        pt1I = fB[facePtBI];
-                    }
-
-                    const tetPointRef tetB
-                    (
-                        ccB,
-                        tetBasePtB,
-                        meshB.points()[pt0I],
-                        meshB.points()[pt1I]
-                    );
-                    if (!tetB.bounds().overlaps(tetABb))
-                    {
-                        continue;
-                    }
-
-                    vol += tetTetOverlapVol(tetA, tetB);
-                }
-            }
-        }
-    }
-
-    return vol;
+    sumOverlapMomentOp overlapSumOp;
+    cellCellOverlapMinDecomp<sumOverlapMomentOp>
+    (
+        meshA,
+        cellAI,
+        meshB,
+        cellBI,
+        cellBbB,
+        overlapSumOp
+    );
+
+    return overlapSumOp.iop_.vol_;
 }
 
 
diff --git a/src/meshTools/tetOverlapVolume/tetOverlapVolume.H b/src/meshTools/tetOverlapVolume/tetOverlapVolume.H
index 06b315d47f..a77e608c6e 100644
--- a/src/meshTools/tetOverlapVolume/tetOverlapVolume.H
+++ b/src/meshTools/tetOverlapVolume/tetOverlapVolume.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2012-2017 OpenFOAM Foundation
+    \\  /    A nd           | Copyright (C) 2012-2014 OpenFOAM Foundation
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -29,6 +29,7 @@ Description
 
 SourceFiles
     tetOverlapVolume.C
+    tetOverlapVolumeTemplates.C
 
 \*---------------------------------------------------------------------------*/
 
@@ -38,7 +39,8 @@ SourceFiles
 #include "FixedList.H"
 #include "labelList.H"
 #include "treeBoundBox.H"
-#include "tetPointRef.H"
+#include "Tuple2.H"
+#include "tetrahedron.H"
 
 namespace Foam
 {
@@ -52,69 +54,126 @@ class polyMesh;
 
 class tetOverlapVolume
 {
-    // Private member functions
-
-        //- Tet overlap volume
-        scalar tetTetOverlapVol
-        (
-            const tetPointRef& tetA,
-            const tetPointRef& tetB
-        ) const;
-
-        //- Return a const treeBoundBox
-        treeBoundBox pyrBb
-        (
-            const pointField& points,
-            const face& f,
-            const point& fc
-        ) const;
-
-
     // Private classes
 
-        //- A fixed list of tets which simulates a dynamic list by incrementing
-        //  a counter whenever its append method is called. This is used as an
-        //  optimisation for the tetTetOverlapVol method.
-        template<unsigned Size>
-        class cutTetList
-        :
-            public FixedList<FixedList<point, 4>, Size>
+        //- tetPoints handling : sum resulting volumes
+        class sumMomentOp
         {
-        private:
+        public:
+            Tuple2<scalar, point> vol_;
 
-            //- The number of stored elements
-            label n_;
+            inline sumMomentOp()
+            :
+                vol_(0.0, Zero)
+            {}
 
+            inline void operator()(const tetPoints& tet)
+            {
+                const tetPointRef t(tet.tet());
+                scalar tetVol = t.mag();
+                vol_.first() += tetVol;
+                vol_.second() += (tetVol*t.centre());
+            }
+        };
 
+        //- tetPoints combining : check for overlap
+        class hasOverlapOp
+        {
         public:
 
-            //- Construct null
-            cutTetList()
+            const scalar threshold_;
+            tetPointRef::sumVolOp iop_;
+            bool ok_;
+
+            inline hasOverlapOp(const scalar threshold)
             :
-                n_(0)
+                threshold_(threshold),
+                iop_(),
+                ok_(false)
             {}
 
-            //- Clear the array
-            void clear()
+            //- Overlap two tets
+            inline bool operator()(const tetPoints& A, const tetPoints& B)
             {
-                n_ = 0;
+                tetTetOverlap<tetPointRef::sumVolOp>(A, B, iop_);
+                ok_ = (iop_.vol_ > threshold_);
+                return ok_;
             }
+        };
+
+        //- tetPoints combining : sum overlap volume
+        class sumOverlapOp
+        {
+        public:
+
+            tetPointRef::sumVolOp iop_;
 
-            //- Get the current size
-            label size() const
+            inline sumOverlapOp()
+            :
+                iop_()
+            {}
+
+            //- Overlap two tets
+            inline bool operator()(const tetPoints& A, const tetPoints& B)
             {
-                return n_;
+                tetTetOverlap<tetPointRef::sumVolOp>(A, B, iop_);
+                return false;
             }
+        };
+
+        //- tetPoints combining : sum overlap volume
+        class sumOverlapMomentOp
+        {
+        public:
+
+            sumMomentOp iop_;
 
-            //- Add a new tet to the end of the array
-            void append(const FixedList<point, 4>& t)
+            inline sumOverlapMomentOp()
+            :
+                iop_()
+            {}
+
+            //- Overlap two tets
+            inline bool operator()(const tetPoints& A, const tetPoints& B)
             {
-                this->operator[](n_) = t;
-                ++ n_;
+                tetTetOverlap<sumMomentOp>(A, B, iop_);
+                return false;
             }
         };
 
 
+    // Private member functions
+
+        //- Tet overlap calculation
+        template<class tetPointsOp>
+        static void tetTetOverlap
+        (
+            const tetPoints& tetA,
+            const tetPoints& tetB,
+            tetPointsOp& insideOp
+        );
+
+        //- Cell overlap calculation
+        template<class tetsOp>
+        static void cellCellOverlapMinDecomp
+        (
+            const primitiveMesh& meshA,
+            const label cellAI,
+            const primitiveMesh& meshB,
+            const label cellBI,
+            const treeBoundBox& cellBbB,
+            tetsOp& combineTetsOp
+        );
+
+        //- Return a const treeBoundBox
+        static treeBoundBox pyrBb
+        (
+            const pointField& points,
+            const face& f,
+            const point& fc
+        );
+
+
 public:
 
     //- Runtime type information
@@ -159,6 +218,17 @@ public:
             const label cellBI,
             const treeBoundBox& cellBbB
         ) const;
+
+        //- Calculates the overlap volume and moment
+        Tuple2<scalar, point> cellCellOverlapMomentMinDecomp
+        (
+            const primitiveMesh& meshA,
+            const label cellAI,
+
+            const primitiveMesh& meshB,
+            const label cellBI,
+            const treeBoundBox& cellBbB
+        ) const;
 };
 
 
@@ -168,6 +238,12 @@ public:
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+#ifdef NoRepository
+#   include "tetOverlapVolumeTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
 #endif
 
 // ************************************************************************* //
diff --git a/src/meshTools/tetOverlapVolume/tetOverlapVolumeTemplates.C b/src/meshTools/tetOverlapVolume/tetOverlapVolumeTemplates.C
new file mode 100644
index 0000000000..34eded1ff3
--- /dev/null
+++ b/src/meshTools/tetOverlapVolume/tetOverlapVolumeTemplates.C
@@ -0,0 +1,259 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2015 OpenFOAM Foundation
+     \\/     M anipulation  | Copyright (C) 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 "tetOverlapVolume.H"
+#include "primitiveMesh.H"
+
+// * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * * * //
+
+template<class tetPointsOp>
+void Foam::tetOverlapVolume::tetTetOverlap
+(
+    const tetPoints& tetA,
+    const tetPoints& tetB,
+    tetPointsOp& insideOp
+)
+{
+    static tetPointRef::tetIntersectionList insideTets;
+    label nInside = 0;
+    static tetPointRef::tetIntersectionList cutInsideTets;
+    label nCutInside = 0;
+
+    tetPointRef::storeOp inside(insideTets, nInside);
+    tetPointRef::storeOp cutInside(cutInsideTets, nCutInside);
+    tetPointRef::dummyOp outside;
+
+    // Precompute the tet face areas and exit early if any face area is
+    // too small
+    static FixedList<vector, 4> tetAFaceAreas;
+    static FixedList<scalar, 4> tetAMag2FaceAreas;
+    tetPointRef tetATet = tetA.tet();
+    for (label facei = 0; facei < 4; ++facei)
+    {
+        tetAFaceAreas[facei] = -tetATet.tri(facei).normal();
+        tetAMag2FaceAreas[facei] = magSqr(tetAFaceAreas[facei]);
+        if (tetAMag2FaceAreas[facei] < ROOTVSMALL)
+        {
+            return;
+        }
+    }
+
+    static FixedList<vector, 4> tetBFaceAreas;
+    static FixedList<scalar, 4> tetBMag2FaceAreas;
+    tetPointRef tetBTet = tetB.tet();
+    for (label facei = 0; facei < 4; ++facei)
+    {
+        tetBFaceAreas[facei] = -tetBTet.tri(facei).normal();
+        tetBMag2FaceAreas[facei] = magSqr(tetBFaceAreas[facei]);
+        if (tetBMag2FaceAreas[facei] < ROOTVSMALL)
+        {
+            return;
+        }
+    }
+
+
+    // Face 0
+    {
+        vector n = tetBFaceAreas[0]/Foam::sqrt(tetBMag2FaceAreas[0]);
+        plane pl0(tetBTet.tri(0).a(), n, false);
+
+        tetA.tet().sliceWithPlane(pl0, cutInside, outside);
+        if (nCutInside == 0)
+        {
+            return;
+        }
+    }
+
+    // Face 1
+    {
+        vector n = tetBFaceAreas[1]/Foam::sqrt(tetBMag2FaceAreas[1]);
+        plane pl1(tetBTet.tri(1).a(), n, false);
+
+        nInside = 0;
+        for (label i = 0; i < nCutInside; i++)
+        {
+            const tetPointRef t = cutInsideTets[i].tet();
+            t.sliceWithPlane(pl1, inside, outside);
+        }
+        if (nInside == 0)
+        {
+            return;
+        }
+    }
+
+    // Face 2
+    {
+        vector n = tetBFaceAreas[2]/Foam::sqrt(tetBMag2FaceAreas[2]);
+        plane pl2(tetBTet.tri(2).a(), n, false);
+
+        nCutInside = 0;
+        for (label i = 0; i < nInside; i++)
+        {
+            const tetPointRef t = insideTets[i].tet();
+            t.sliceWithPlane(pl2, cutInside, outside);
+        }
+        if (nCutInside == 0)
+        {
+            return;
+        }
+    }
+
+    // Face 3
+    {
+        vector n = tetBFaceAreas[3]/Foam::sqrt(tetBMag2FaceAreas[3]);
+        plane pl3(tetBTet.tri(3).a(), n, false);
+        for (label i = 0; i < nCutInside; i++)
+        {
+            const tetPointRef t = cutInsideTets[i].tet();
+            t.sliceWithPlane(pl3, insideOp, outside);
+        }
+    }
+}
+
+
+template<class tetsOp>
+void Foam::tetOverlapVolume::cellCellOverlapMinDecomp
+(
+    const primitiveMesh& meshA,
+    const label cellAI,
+
+    const primitiveMesh& meshB,
+    const label cellBI,
+    const treeBoundBox& cellBbB,
+    tetsOp& combineTetsOp
+)
+{
+    const cell& cFacesA = meshA.cells()[cellAI];
+    const point& ccA = meshA.cellCentres()[cellAI];
+
+    const cell& cFacesB = meshB.cells()[cellBI];
+    const point& ccB = meshB.cellCentres()[cellBI];
+
+    forAll(cFacesA, cFA)
+    {
+        label faceAI = cFacesA[cFA];
+
+        const face& fA = meshA.faces()[faceAI];
+        const treeBoundBox pyrA = pyrBb(meshA.points(), fA, ccA);
+        if (!pyrA.overlaps(cellBbB))
+        {
+            continue;
+        }
+
+        bool ownA = (meshA.faceOwner()[faceAI] == cellAI);
+
+        label tetBasePtAI = 0;
+
+        const point& tetBasePtA = meshA.points()[fA[tetBasePtAI]];
+
+        for (label tetPtI = 1; tetPtI < fA.size() - 1; tetPtI++)
+        {
+            label facePtAI = (tetPtI + tetBasePtAI) % fA.size();
+            label otherFacePtAI = fA.fcIndex(facePtAI);
+
+            label pt0I = -1;
+            label pt1I = -1;
+
+            if (ownA)
+            {
+                pt0I = fA[facePtAI];
+                pt1I = fA[otherFacePtAI];
+            }
+            else
+            {
+                pt0I = fA[otherFacePtAI];
+                pt1I = fA[facePtAI];
+            }
+
+            const tetPoints tetA
+            (
+                ccA,
+                tetBasePtA,
+                meshA.points()[pt0I],
+                meshA.points()[pt1I]
+            );
+            const treeBoundBox tetABb(tetA.bounds());
+
+            // Loop over tets of cellB
+            forAll(cFacesB, cFB)
+            {
+                label faceBI = cFacesB[cFB];
+
+                const face& fB = meshB.faces()[faceBI];
+                const treeBoundBox pyrB = pyrBb(meshB.points(), fB, ccB);
+                if (!pyrB.overlaps(pyrA))
+                {
+                    continue;
+                }
+
+                bool ownB = (meshB.faceOwner()[faceBI] == cellBI);
+
+                label tetBasePtBI = 0;
+
+                const point& tetBasePtB = meshB.points()[fB[tetBasePtBI]];
+
+                for (label tetPtI = 1; tetPtI < fB.size() - 1; tetPtI++)
+                {
+                    label facePtBI = (tetPtI + tetBasePtBI) % fB.size();
+                    label otherFacePtBI = fB.fcIndex(facePtBI);
+
+                    label pt0I = -1;
+                    label pt1I = -1;
+
+                    if (ownB)
+                    {
+                        pt0I = fB[facePtBI];
+                        pt1I = fB[otherFacePtBI];
+                    }
+                    else
+                    {
+                        pt0I = fB[otherFacePtBI];
+                        pt1I = fB[facePtBI];
+                    }
+
+                    const tetPoints tetB
+                    (
+                        ccB,
+                        tetBasePtB,
+                        meshB.points()[pt0I],
+                        meshB.points()[pt1I]
+                    );
+                    if (!tetB.bounds().overlaps(tetABb))
+                    {
+                        continue;
+                    }
+
+                    if (combineTetsOp(tetA, tetB))
+                    {
+                        return;
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+// ************************************************************************* //
-- 
GitLab